diff -urN linux-4.1.52-orig/arch/arm/boot/compressed/head.S linux-4.1.52/arch/arm/boot/compressed/head.S --- linux-4.1.52-orig/arch/arm/boot/compressed/head.S 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/boot/compressed/head.S 2020-02-07 01:47:53.000000000 +0800 @@ -130,7 +130,11 @@ .endr ARM( mov r0, r0 ) ARM( b 1f ) +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + THUMB( badr r12, 1f ) +#else THUMB( adr r12, BSYM(1f) ) +#endif THUMB( bx r12 ) .word _magic_sig @ Magic numbers to help the loader @@ -447,7 +451,11 @@ bl cache_clean_flush +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + badr r0, restart +#else adr r0, BSYM(restart) +#endif add r0, r0, r6 mov pc, r0 diff -urN linux-4.1.52-orig/arch/arm/boot/compressed/Makefile linux-4.1.52/arch/arm/boot/compressed/Makefile --- linux-4.1.52-orig/arch/arm/boot/compressed/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/boot/compressed/Makefile 2020-02-07 01:47:53.000000000 +0800 @@ -111,6 +111,10 @@ ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj) asflags-y := -DZIMAGE +ifdef BCM_KF # defined(CONFIG_BCM_KF_ARM_BCM963XX) +EXTRA_CFLAGS += -I$(INC_BRCMDRIVER_PUB_PATH)/$(BRCM_BOARD) -I$(INC_BRCMSHARED_PUB_PATH)/$(BRCM_BOARD) +endif # BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) + # Supply kernel BSS size to the decompressor via a linker symbol. KBSS_SZ = $(shell $(CROSS_COMPILE)size $(obj)/../../../../vmlinux | \ awk 'END{print $$3}') diff -urN linux-4.1.52-orig/arch/arm/common/mcpm_head.S linux-4.1.52/arch/arm/common/mcpm_head.S --- linux-4.1.52-orig/arch/arm/common/mcpm_head.S 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/common/mcpm_head.S 2020-02-07 01:47:53.000000000 +0800 @@ -49,7 +49,11 @@ ENTRY(mcpm_entry_point) ARM_BE8(setend be) +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + THUMB( badr r12, 1f ) +#else THUMB( adr r12, BSYM(1f) ) +#endif THUMB( bx r12 ) THUMB( .thumb ) 1: diff -urN linux-4.1.52-orig/arch/arm/configs/bcm963138_defconfig linux-4.1.52/arch/arm/configs/bcm963138_defconfig --- linux-4.1.52-orig/arch/arm/configs/bcm963138_defconfig 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/configs/bcm963138_defconfig 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,1141 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 3.4.11 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y +CONFIG_KTIME_SCALAR=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_NEED_MACH_MEMORY_H=y +CONFIG_GENERIC_BUG=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="arm-unknown-linux-uclibcgnueabi-" +CONFIG_LOCALVERSION="brcmarm" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_KERNEL_GZIP=y +# CONFIG_KERNEL_LZMA is not set +# CONFIG_KERNEL_XZ is not set +# CONFIG_KERNEL_LZO is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_FHANDLE is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_IRQ_DOMAIN=y +# CONFIG_IRQ_DOMAIN_DEBUG is not set +CONFIG_IRQ_FORCED_THREADING=y + +# +# RCU Subsystem +# +CONFIG_TREE_RCU=y +# CONFIG_PREEMPT_RCU is not set +CONFIG_RCU_FANOUT=32 +# CONFIG_RCU_FANOUT_EXACT is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=17 +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="initramfs-base-files.txt" +CONFIG_INITRAMFS_ROOT_UID=0 +CONFIG_INITRAMFS_ROOT_GID=0 +# CONFIG_RD_GZIP is not set +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +CONFIG_INITRAMFS_COMPRESSION_NONE=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_EXPERT=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +# CONFIG_ELF_CORE is not set +# CONFIG_BASE_FULL is not set +CONFIG_FUTEX=y +# CONFIG_EPOLL is not set +# CONFIG_SIGNALFD is not set +# CONFIG_TIMERFD is not set +# CONFIG_EVENTFD is not set +CONFIG_SHMEM=y +# CONFIG_AIO is not set +CONFIG_EMBEDDED=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +# CONFIG_VM_EVENT_COUNTERS is not set +CONFIG_COMPAT_BRK=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_JUMP_LABEL=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_USE_GENERIC_SMP_HELPERS=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=1 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SRCVERSION_ALL=y +CONFIG_STOP_MACHINE=y +CONFIG_BLOCK=y +# CONFIG_LBDAF is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +CONFIG_INLINE_READ_UNLOCK=y +# CONFIG_INLINE_READ_UNLOCK_BH is not set +CONFIG_INLINE_READ_UNLOCK_IRQ=y +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +CONFIG_INLINE_WRITE_UNLOCK=y +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +CONFIG_MUTEX_SPIN_ON_OWNER=y +# CONFIG_FREEZER is not set + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BCMRING is not set +# CONFIG_ARCH_HIGHBANK is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CNS3XXX is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_PRIMA2 is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_MXS is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_PICOXCELL is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_MSM is not set +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C24XX is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P64X0 is not set +# CONFIG_ARCH_S5PC100 is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_EXYNOS is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_PLAT_SPEAR is not set +# CONFIG_ARCH_VT8500 is not set +# CONFIG_ARCH_ZYNQ is not set +# CONFIG_ARCH_BCM5301X is not set +CONFIG_ARCH_BCM63XX=y +# CONFIG_ARCH_KONA is not set + +# +# System MMU +# +CONFIG_BOARD_ZRELADDR=0x00008000 +CONFIG_BOARD_PARAMS_PHYS=0x00000000 +CONFIG_DEBUG_UART_ADDR=0x8001E000 +CONFIG_HZ=100 +CONFIG_MACH_BCM963138=y +# CONFIG_PLAT_MPCORE is not set +# CONFIG_CACHE_L310 is not set +CONFIG_PLAT_SMP=y +# CONFIG_ARCH_HAS_HEAD_FIXUP is not set +CONFIG_PLAT_CA9_MPCORE=y +CONFIG_PLAT_BCM63138=y + +# +# Processor Type +# +CONFIG_CPU_V7=y +CONFIG_CPU_32v6K=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +# CONFIG_ARM_LPAE is not set +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +CONFIG_ARM_THUMB=y +# CONFIG_ARM_THUMBEE is not set +# CONFIG_SWP_EMULATE is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_OUTER_CACHE=y +CONFIG_OUTER_CACHE_SYNC=y +CONFIG_CACHE_L2X0=y +CONFIG_CACHE_PL310=y +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +CONFIG_ARM_NR_BANKS=8 +CONFIG_CPU_HAS_PMU=y +CONFIG_MULTI_IRQ_HANDLER=y +# CONFIG_ARM_ERRATA_430973 is not set +# CONFIG_ARM_ERRATA_458693 is not set +# CONFIG_ARM_ERRATA_460075 is not set +# CONFIG_ARM_ERRATA_742230 is not set +# CONFIG_ARM_ERRATA_742231 is not set +# CONFIG_PL310_ERRATA_588369 is not set +# CONFIG_ARM_ERRATA_720789 is not set +# CONFIG_PL310_ERRATA_727915 is not set +# CONFIG_ARM_ERRATA_743622 is not set +# CONFIG_ARM_ERRATA_751472 is not set +# CONFIG_PL310_ERRATA_753970 is not set +# CONFIG_ARM_ERRATA_754322 is not set +# CONFIG_ARM_ERRATA_754327 is not set +# CONFIG_ARM_ERRATA_764369 is not set +# CONFIG_PL310_ERRATA_769419 is not set +CONFIG_ARM_GIC=y + +# +# Bus support +# +CONFIG_ARM_AMBA=y +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +# CONFIG_NO_HZ is not set +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_HAVE_SMP=y +CONFIG_SMP=y +CONFIG_SMP_ON_UP=y +CONFIG_ARM_CPU_TOPOLOGY=y +# CONFIG_SCHED_MC is not set +# CONFIG_SCHED_SMT is not set +CONFIG_HAVE_ARM_SCU=y +CONFIG_HAVE_ARM_TWD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_NR_CPUS=2 +# CONFIG_HOTPLUG_CPU is not set +# CONFIG_LOCAL_TIMERS is not set +CONFIG_ARCH_NR_GPIO=0 +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT__LL is not set +# CONFIG_PREEMPT_RTB is not set +# CONFIG_PREEMPT_RT_FULL is not set +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +CONFIG_OABI_COMPAT=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +# CONFIG_HIGHMEM is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +# CONFIG_CLEANCACHE is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_SECCOMP is not set +# CONFIG_CC_STACKPROTECTOR is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set + +# +# Boot options +# +# CONFIG_USE_OF is not set +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="console=ttyAMA0,115200 earlyprintk debug" +# CONFIG_CMDLINE_FROM_BOOTLOADER is not set +# CONFIG_CMDLINE_EXTEND is not set +CONFIG_CMDLINE_FORCE=y +# CONFIG_XIP_KERNEL is not set +# CONFIG_CRASH_DUMP is not set +# CONFIG_AUTO_ZRELADDR is not set + +# +# CPU Power Management +# +# CONFIG_CPU_IDLE is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +# CONFIG_FPE_NWFPE is not set +CONFIG_FPE_FASTFPE=y +# CONFIG_VFP is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +# CONFIG_SUSPEND is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +# CONFIG_ARM_CPU_SUSPEND is not set +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +# CONFIG_UNIX_DIAG is not set +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_BLOG is not set +# CONFIG_BLOG_IPV6 is not set +# CONFIG_BLOG_MCAST is not set +# CONFIG_BLOG_FEATURE is not set +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +# CONFIG_INET_DIAG is not set +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +# CONFIG_IPV6 is not set +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +# CONFIG_NETFILTER is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +# CONFIG_BRIDGE is not set +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +CONFIG_RPS=y +CONFIG_RFS_ACCEL=y +CONFIG_XPS=y +CONFIG_BQL=y +CONFIG_HAVE_BPF_JIT=y +# CONFIG_BPF_JIT is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set +# CONFIG_WIRELESS is not set +# CONFIG_WIMAX is not set +# CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set +# CONFIG_NFC is not set + +# +# Runner +# +# CONFIG_BRCM_DRIVER_RUNNER is not set +# CONFIG_BRCM_RUNNER_GPON is not set +# CONFIG_BRCM_RUNNER_RG is not set +# CONFIG_BRCM_RUNNER_BR is not set +CONFIG_BRCM_RUNNER_BR_IMPL=1 +CONFIG_BRCM_RUNNER_RG_IMPL=1 +# CONFIG_BRCM_GMP is not set +CONFIG_BRCM_GMP_IMPL=1 +# CONFIG_BRCM_GMP_GPL is not set +CONFIG_BRCM_GMP_GPL_IMPL=1 +# CONFIG_BRCM_GMP_MW is not set +CONFIG_BRCM_GMP_MW_IMPL=1 +# CONFIG_BCM_GMP_OS_SHIM is not set +CONFIG_BCM_GMP_OS_SHIM_IMPL=1 +# CONFIG_BCM_GMP_SHELL is not set +CONFIG_BCM_GMP_SHELL_IMPL=1 +# CONFIG_BCM_GMP_LOGGER is not set +CONFIG_BCM_GMP_LOGGER_IMPL=1 + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_STANDALONE=y +# CONFIG_PREVENT_FIRMWARE_BUILD is not set +# CONFIG_FW_LOADER is not set +CONFIG_DEBUG_DRIVER=y +CONFIG_DEBUG_DEVRES=y +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +# CONFIG_DMA_SHARED_BUFFER is not set +# CONFIG_CONNECTOR is not set +# CONFIG_MTD is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=1 +# CONFIG_BLK_DEV_CRYPTOLOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=1 +CONFIG_BLK_DEV_RAM_SIZE=32768 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_BLK_DEV_RBD is not set + +# +# Misc devices +# +# CONFIG_ATMEL_PWM is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_93CX6 is not set + +# +# Texas Instruments shared transport line discipline +# + +# +# Altera FPGA firmware download module +# + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +CONFIG_NET_CORE=y +# CONFIG_BONDING is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +# CONFIG_MII is not set +# CONFIG_NET_TEAM is not set +# CONFIG_MACVLAN is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set + +# +# CAIF transport drivers +# +CONFIG_ETHERNET=y +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_CALXEDA_XGMAC is not set +# CONFIG_NET_VENDOR_CHELSIO is not set +# CONFIG_NET_VENDOR_CIRRUS is not set +# CONFIG_DM9000 is not set +# CONFIG_DNET is not set +# CONFIG_NET_VENDOR_FARADAY is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_ETHOC is not set +# CONFIG_NET_VENDOR_SEEQ is not set +# CONFIG_NET_VENDOR_SMSC is not set +# CONFIG_NET_VENDOR_STMICRO is not set +# CONFIG_PHYLIB is not set +CONFIG_PPP=y +# CONFIG_PPP_BSDCOMP is not set +# CONFIG_PPP_DEFLATE is not set +# CONFIG_PPP_FILTER is not set +# CONFIG_PPP_MPPE is not set +# CONFIG_PPP_MULTILINK is not set +CONFIG_PPPOE=y +# CONFIG_PPP_ASYNC is not set +# CONFIG_PPP_SYNC_TTY is not set +# CONFIG_SLIP is not set +CONFIG_SLHC=y +# CONFIG_WLAN is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +# CONFIG_INPUT is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set +# CONFIG_TRACE_SINK is not set +# CONFIG_DEVKMEM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_AMBA_PL010 is not set +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +# CONFIG_TTY_PRINTK is not set +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_RAMOOPS is not set +# CONFIG_I2C is not set +# CONFIG_SPI is not set +# CONFIG_HSI is not set + +# +# PPS support +# +# CONFIG_PPS is not set + +# +# PPS generators support +# + +# +# PTP clock support +# + +# +# Enable Device Drivers -> PPS to see the PTP clock options. +# +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_REGULATOR is not set +# CONFIG_MEDIA_SUPPORT is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +# CONFIG_FB is not set +# CONFIG_EXYNOS_VIDEO is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +# CONFIG_SOUND is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB_ARCH_HAS_XHCI is not set +# CONFIG_USB_SUPPORT is not set +# CONFIG_MMC is not set +# CONFIG_MEMSTICK is not set +# CONFIG_NEW_LEDS is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +# CONFIG_RTC_CLASS is not set +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set + +# +# Virtio drivers +# +# CONFIG_VIRTIO_BALLOON is not set +# CONFIG_VIRTIO_MMIO is not set + +# +# Microsoft Hyper-V guest support +# +# CONFIG_STAGING is not set +CONFIG_CLKDEV_LOOKUP=y +CONFIG_HAVE_MACH_CLKDEV=y + +# +# Hardware Spinlock drivers +# +CONFIG_IOMMU_SUPPORT=y + +# +# Remoteproc drivers (EXPERIMENTAL) +# + +# +# Rpmsg drivers (EXPERIMENTAL) +# +# CONFIG_VIRT_DRIVERS is not set +# CONFIG_PM_DEVFREQ is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +CONFIG_FS_POSIX_ACL=y +# CONFIG_FILE_LOCKING is not set +# CONFIG_FSNOTIFY is not set +# CONFIG_DNOTIFY is not set +# CONFIG_INOTIFY_USER is not set +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set +CONFIG_GENERIC_ACL=y + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +# CONFIG_PROC_PAGE_MONITOR is not set +CONFIG_SYSFS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_TMPFS_XATTR=y +# CONFIG_HUGETLB_PAGE is not set +CONFIG_CONFIGFS_FS=y +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_LOGFS is not set +# CONFIG_CRAMFS is not set +CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_XATTR is not set +CONFIG_SQUASHFS_ZLIB=y +# CONFIG_SQUASHFS_LZO is not set +CONFIG_SQUASHFS_XZ=y +# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set +CONFIG_SQUASHFS_EMBEDDED=y +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_PSTORE is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +# CONFIG_NETWORK_FILESYSTEMS is not set +# CONFIG_NLS is not set +# CONFIG_DLM is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=2048 +CONFIG_MAGIC_SYSRQ=y +CONFIG_STRIP_ASM_SYMS=y +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +CONFIG_SCHED_DEBUG=y +# CONFIG_SCHEDSTATS is not set +# CONFIG_BCM_SCHEDAUDIT is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_INFO_REDUCED is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +CONFIG_RCU_CPU_STALL_TIMEOUT=60 +# CONFIG_RCU_CPU_STALL_INFO is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_DEBUG_PER_CPU_MAPS is not set +# CONFIG_LKDTM is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_DMA_API_DEBUG is not set +CONFIG_ATOMIC64_SELFTEST=y +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_ARM_UNWIND=y +CONFIG_DEBUG_USER=y +CONFIG_DEBUG_LL=y +CONFIG_DEBUG_LL_UART_NONE=y +# CONFIG_DEBUG_ICEDCC is not set +# CONFIG_DEBUG_SEMIHOSTING is not set +CONFIG_EARLY_PRINTK=y +# CONFIG_OC_ETM is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +# CONFIG_CRYPTO_FIPS is not set +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG=y +CONFIG_CRYPTO_RNG2=y +# CONFIG_CRYPTO_MANAGER is not set +# CONFIG_CRYPTO_MANAGER2 is not set +# CONFIG_CRYPTO_USER is not set +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_PCRYPT is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +CONFIG_CRYPTO_ANSI_CPRNG=y +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +CONFIG_CRYPTO_HW=y +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +# CONFIG_NO_GENERIC_PCI_IOPORT_MAP is not set +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +# CONFIG_CRC_CCITT is not set +# CONFIG_CRC16 is not set +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +CONFIG_LIBCRC32C=y +# CONFIG_CRC8 is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_XZ_DEC=y +CONFIG_XZ_DEC_X86=y +CONFIG_XZ_DEC_POWERPC=y +CONFIG_XZ_DEC_IA64=y +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +CONFIG_XZ_DEC_SPARC=y +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_CPU_RMAP=y +CONFIG_DQL=y +CONFIG_NLATTR=y +# CONFIG_AVERAGE is not set +# CONFIG_CORDIC is not set diff -urN linux-4.1.52-orig/arch/arm/configs/bcm963138_sim_defconfig linux-4.1.52/arch/arm/configs/bcm963138_sim_defconfig --- linux-4.1.52-orig/arch/arm/configs/bcm963138_sim_defconfig 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/configs/bcm963138_sim_defconfig 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,978 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 3.4.11 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_HAVE_PWM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y +CONFIG_KTIME_SCALAR=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_NEED_MACH_MEMORY_H=y +CONFIG_GENERIC_BUG=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="brcmarm" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_KERNEL_GZIP=y +# CONFIG_KERNEL_LZMA is not set +# CONFIG_KERNEL_XZ is not set +# CONFIG_KERNEL_LZO is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_FHANDLE is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_IRQ_DOMAIN=y +# CONFIG_IRQ_DOMAIN_DEBUG is not set +CONFIG_IRQ_FORCED_THREADING=y + +# +# RCU Subsystem +# +CONFIG_TREE_RCU=y +# CONFIG_PREEMPT_RCU is not set +CONFIG_RCU_FANOUT=32 +# CONFIG_RCU_FANOUT_EXACT is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=17 +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +# CONFIG_RD_GZIP is not set +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SYSCTL=y +CONFIG_EXPERT=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +# CONFIG_EPOLL is not set +# CONFIG_SIGNALFD is not set +# CONFIG_TIMERFD is not set +# CONFIG_EVENTFD is not set +CONFIG_SHMEM=y +# CONFIG_AIO is not set +CONFIG_EMBEDDED=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +# CONFIG_VM_EVENT_COUNTERS is not set +CONFIG_COMPAT_BRK=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_JUMP_LABEL=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_USE_GENERIC_SMP_HELPERS=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_STOP_MACHINE=y +CONFIG_BLOCK=y +# CONFIG_LBDAF is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +CONFIG_INLINE_READ_UNLOCK=y +# CONFIG_INLINE_READ_UNLOCK_BH is not set +CONFIG_INLINE_READ_UNLOCK_IRQ=y +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +CONFIG_INLINE_WRITE_UNLOCK=y +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +CONFIG_MUTEX_SPIN_ON_OWNER=y +# CONFIG_FREEZER is not set + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BCMRING is not set +# CONFIG_ARCH_HIGHBANK is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CNS3XXX is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_PRIMA2 is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_MXS is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_PICOXCELL is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_MSM is not set +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C24XX is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P64X0 is not set +# CONFIG_ARCH_S5PC100 is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_EXYNOS is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_PLAT_SPEAR is not set +# CONFIG_ARCH_VT8500 is not set +# CONFIG_ARCH_ZYNQ is not set +# CONFIG_ARCH_BCM5301X is not set +CONFIG_ARCH_BCM63XX=y +# CONFIG_ARCH_KONA is not set + +# +# System MMU +# +CONFIG_BOARD_ZRELADDR=0x00008000 +CONFIG_BOARD_PARAMS_PHYS=0x00000000 +CONFIG_DEBUG_UART_ADDR=0x80019000 +CONFIG_HZ=100 +CONFIG_MACH_BCM963138=y +CONFIG_BCM63138_SIM=y +# CONFIG_PLAT_MPCORE is not set +# CONFIG_CACHE_L310 is not set +CONFIG_PLAT_SMP=y +# CONFIG_ARCH_HAS_HEAD_FIXUP is not set +CONFIG_PLAT_CA9_MPCORE=y +CONFIG_PLAT_BCM63138=y + +# +# Processor Type +# +CONFIG_CPU_V7=y +CONFIG_CPU_32v6K=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +# CONFIG_ARM_LPAE is not set +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +CONFIG_ARM_THUMB=y +# CONFIG_ARM_THUMBEE is not set +# CONFIG_SWP_EMULATE is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_OUTER_CACHE=y +CONFIG_OUTER_CACHE_SYNC=y +CONFIG_CACHE_L2X0=y +CONFIG_CACHE_PL310=y +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +CONFIG_ARM_NR_BANKS=8 +CONFIG_CPU_HAS_PMU=y +CONFIG_MULTI_IRQ_HANDLER=y +# CONFIG_ARM_ERRATA_430973 is not set +# CONFIG_ARM_ERRATA_458693 is not set +# CONFIG_ARM_ERRATA_460075 is not set +# CONFIG_ARM_ERRATA_742230 is not set +# CONFIG_ARM_ERRATA_742231 is not set +# CONFIG_PL310_ERRATA_588369 is not set +# CONFIG_ARM_ERRATA_720789 is not set +# CONFIG_PL310_ERRATA_727915 is not set +# CONFIG_ARM_ERRATA_743622 is not set +# CONFIG_ARM_ERRATA_751472 is not set +# CONFIG_PL310_ERRATA_753970 is not set +# CONFIG_ARM_ERRATA_754322 is not set +# CONFIG_ARM_ERRATA_754327 is not set +# CONFIG_ARM_ERRATA_764369 is not set +# CONFIG_PL310_ERRATA_769419 is not set +CONFIG_ARM_GIC=y + +# +# Bus support +# +CONFIG_ARM_AMBA=y +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +# CONFIG_NO_HZ is not set +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_HAVE_SMP=y +CONFIG_SMP=y +CONFIG_SMP_ON_UP=y +CONFIG_ARM_CPU_TOPOLOGY=y +# CONFIG_SCHED_MC is not set +# CONFIG_SCHED_SMT is not set +CONFIG_HAVE_ARM_SCU=y +CONFIG_HAVE_ARM_TWD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_NR_CPUS=2 +# CONFIG_HOTPLUG_CPU is not set +# CONFIG_LOCAL_TIMERS is not set +CONFIG_ARCH_NR_GPIO=0 +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT__LL is not set +# CONFIG_PREEMPT_RTB is not set +# CONFIG_PREEMPT_RT_FULL is not set +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +CONFIG_OABI_COMPAT=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +# CONFIG_HIGHMEM is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +# CONFIG_CLEANCACHE is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_SECCOMP is not set +# CONFIG_CC_STACKPROTECTOR is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set + +# +# Boot options +# +# CONFIG_USE_OF is not set +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="console=ttyAMA0,115200n8 debug earlyprintk mem=32M bootmemheap initrd=0x00c00000,524288 nosmp" +# CONFIG_CMDLINE_FROM_BOOTLOADER is not set +# CONFIG_CMDLINE_EXTEND is not set +CONFIG_CMDLINE_FORCE=y +# CONFIG_XIP_KERNEL is not set +# CONFIG_CRASH_DUMP is not set +# CONFIG_AUTO_ZRELADDR is not set + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_IDLE is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +# CONFIG_FPE_NWFPE is not set +CONFIG_FPE_FASTFPE=y +# CONFIG_VFP is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +# CONFIG_SUSPEND is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +# CONFIG_ARM_CPU_SUSPEND is not set +# CONFIG_NET is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_STANDALONE=y +# CONFIG_PREVENT_FIRMWARE_BUILD is not set +# CONFIG_FW_LOADER is not set +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +# CONFIG_DMA_SHARED_BUFFER is not set +# CONFIG_MTD is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=1 +# CONFIG_BLK_DEV_CRYPTOLOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=1 +CONFIG_BLK_DEV_RAM_SIZE=2048 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_ATMEL_PWM is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_93CX6 is not set + +# +# Texas Instruments shared transport line discipline +# + +# +# Altera FPGA firmware download module +# + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_VT_HW_CONSOLE_BINDING is not set +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_TRACE_SINK is not set +CONFIG_DEVKMEM=y + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_AMBA_PL010 is not set +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +# CONFIG_TTY_PRINTK is not set +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_RAMOOPS is not set +# CONFIG_I2C is not set +# CONFIG_SPI is not set +# CONFIG_HSI is not set + +# +# PPS support +# +# CONFIG_PPS is not set + +# +# PPS generators support +# + +# +# PTP clock support +# + +# +# Enable Device Drivers -> PPS to see the PTP clock options. +# +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_REGULATOR is not set +# CONFIG_MEDIA_SUPPORT is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +# CONFIG_FB is not set +# CONFIG_EXYNOS_VIDEO is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Console display driver support +# +CONFIG_DUMMY_CONSOLE=y +# CONFIG_SOUND is not set +# CONFIG_HID_SUPPORT is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB_ARCH_HAS_XHCI is not set +# CONFIG_USB_SUPPORT is not set +# CONFIG_MMC is not set +# CONFIG_MEMSTICK is not set +# CONFIG_NEW_LEDS is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +# CONFIG_RTC_CLASS is not set +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set + +# +# Virtio drivers +# +# CONFIG_VIRTIO_BALLOON is not set +# CONFIG_VIRTIO_MMIO is not set + +# +# Microsoft Hyper-V guest support +# +# CONFIG_STAGING is not set +CONFIG_CLKDEV_LOOKUP=y +CONFIG_HAVE_MACH_CLKDEV=y + +# +# Hardware Spinlock drivers +# +CONFIG_IOMMU_SUPPORT=y + +# +# Remoteproc drivers (EXPERIMENTAL) +# + +# +# Rpmsg drivers (EXPERIMENTAL) +# +# CONFIG_VIRT_DRIVERS is not set +# CONFIG_PM_DEVFREQ is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +CONFIG_FS_POSIX_ACL=y +# CONFIG_FILE_LOCKING is not set +# CONFIG_FSNOTIFY is not set +# CONFIG_DNOTIFY is not set +# CONFIG_INOTIFY_USER is not set +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set +CONFIG_GENERIC_ACL=y + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +# CONFIG_PROC_PAGE_MONITOR is not set +CONFIG_SYSFS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_TMPFS_XATTR=y +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +# CONFIG_MISC_FILESYSTEMS is not set +# CONFIG_NLS is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=2048 +CONFIG_MAGIC_SYSRQ=y +CONFIG_STRIP_ASM_SYMS=y +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +CONFIG_SCHED_DEBUG=y +# CONFIG_SCHEDSTATS is not set +# CONFIG_BCM_SCHEDAUDIT is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_INFO_REDUCED is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +CONFIG_RCU_CPU_STALL_TIMEOUT=60 +# CONFIG_RCU_CPU_STALL_INFO is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_DEBUG_PER_CPU_MAPS is not set +# CONFIG_LKDTM is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_ARM_UNWIND=y +# CONFIG_DEBUG_USER is not set +CONFIG_DEBUG_LL=y +CONFIG_DEBUG_LL_UART_NONE=y +# CONFIG_DEBUG_ICEDCC is not set +# CONFIG_DEBUG_SEMIHOSTING is not set +CONFIG_EARLY_PRINTK=y +# CONFIG_OC_ETM is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +# CONFIG_CRYPTO_FIPS is not set +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG=y +CONFIG_CRYPTO_RNG2=y +# CONFIG_CRYPTO_MANAGER is not set +# CONFIG_CRYPTO_MANAGER2 is not set +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_PCRYPT is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_CRYPTO_HW=y +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +# CONFIG_NO_GENERIC_PCI_IOPORT_MAP is not set +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +# CONFIG_CRC_CCITT is not set +# CONFIG_CRC16 is not set +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +CONFIG_LIBCRC32C=y +# CONFIG_CRC8 is not set +CONFIG_XZ_DEC=y +CONFIG_XZ_DEC_X86=y +CONFIG_XZ_DEC_POWERPC=y +CONFIG_XZ_DEC_IA64=y +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +CONFIG_XZ_DEC_SPARC=y +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +# CONFIG_AVERAGE is not set +# CONFIG_CORDIC is not set diff -urN linux-4.1.52-orig/arch/arm/include/asm/arch_timer.h linux-4.1.52/arch/arm/include/asm/arch_timer.h --- linux-4.1.52-orig/arch/arm/include/asm/arch_timer.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/include/asm/arch_timer.h 2020-02-07 01:47:53.000000000 +0800 @@ -87,13 +87,34 @@ return cval; } +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(LVCNT) +static u64 lvcnt=0; +#endif + static inline u64 arch_counter_get_cntvct(void) { +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(LVCNT) + u64 cval, snap_lvcnt; + snap_lvcnt = lvcnt; + isb(); + asm volatile("mrrc p15, 1, %Q0, %R0, c14" : "=r" (lvcnt)); + cval = lvcnt; + if(snap_lvcnt > cval) + { + if((snap_lvcnt - cval) < (u64)(1ULL << 63)) + { + lvcnt = snap_lvcnt; + cval = snap_lvcnt; + } + } + return cval; +#else u64 cval; isb(); asm volatile("mrrc p15, 1, %Q0, %R0, c14" : "=r" (cval)); return cval; +#endif } static inline u32 arch_timer_get_cntkctl(void) diff -urN linux-4.1.52-orig/arch/arm/include/asm/assembler.h linux-4.1.52/arch/arm/include/asm/assembler.h --- linux-4.1.52-orig/arch/arm/include/asm/assembler.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/include/asm/assembler.h 2020-02-07 01:47:53.000000000 +0800 @@ -177,6 +177,23 @@ restore_irqs_notrace \oldcpsr .endm +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +/* + * Assembly version of "adr rd, BSYM(sym)". This should only be used to + * reference local symbols in the same assembly file which are to be + * resolved by the assembler. Other usage is undefined. + */ + .irp c,,eq,ne,cs,cc,mi,pl,vs,vc,hi,ls,ge,lt,gt,le,hs,lo + .macro badr\c, rd, sym +#ifdef CONFIG_THUMB2_KERNEL + adr\c \rd, \sym + 1 +#else + adr\c \rd, \sym +#endif + .endm + .endr +#endif + /* * Get current thread_info. */ @@ -326,7 +343,11 @@ THUMB( orr \reg , \reg , #PSR_T_BIT ) bne 1f orr \reg, \reg, #PSR_A_BIT +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + badr lr, 2f +#else adr lr, BSYM(2f) +#endif msr spsr_cxsf, \reg __MSR_ELR_HYP(14) __ERET @@ -422,6 +443,16 @@ .size \name , . - \name .endm +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + .macro csdb +#ifdef CONFIG_THUMB2_KERNEL + .inst.w 0xf3af8014 +#else + .inst 0xe320f014 +#endif + .endm +#endif + .macro check_uaccess, addr:req, size:req, limit:req, tmp:req, bad:req #ifndef CONFIG_CPU_USE_DOMAINS adds \tmp, \addr, #\size - 1 diff -urN linux-4.1.52-orig/arch/arm/include/asm/barrier.h linux-4.1.52/arch/arm/include/asm/barrier.h --- linux-4.1.52-orig/arch/arm/include/asm/barrier.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/include/asm/barrier.h 2020-02-07 01:47:53.000000000 +0800 @@ -2,7 +2,10 @@ #define __ASM_BARRIER_H #ifndef __ASSEMBLY__ +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +#else #include +#endif #define nop() __asm__ __volatile__("mov\tr0,r0\t@ nop\n\t"); @@ -13,6 +16,21 @@ #define wfi() __asm__ __volatile__ ("wfi" : : : "memory") #endif +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +#if __LINUX_ARM_ARCH__ >= 7 +#ifdef CONFIG_THUMB2_KERNEL +#define CSDB ".inst.w 0xf3af8014" +#else +#define CSDB ".inst 0xe320f014" +#endif /* CONFIG_THUMB2_KERNEL */ +#define csdb() __asm__ __volatile__(CSDB : : : "memory") +#endif +#endif /* CONFIG_BCM_KF_SPECTRE_PATCH && CONFIG_BCM_SPECTRE_PATCH_ENABLE */ + +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && !defined(CONFIG_BCM947189) && !defined(CONFIG_BCM96846) && !defined(CONFIG_BCM947622) && !defined(CONFIG_BCM963178) && !defined(CONFIG_BCM96878) +#include +#else /*!(defined(CONFIG_BCM_KF_ARM_BCM963XX)*/ + #if __LINUX_ARM_ARCH__ >= 7 #define isb(option) __asm__ __volatile__ ("isb " #option : : : "memory") #define dsb(option) __asm__ __volatile__ ("dsb " #option : : : "memory") @@ -37,12 +55,34 @@ #define dmb(x) __asm__ __volatile__ ("" : : : "memory") #endif +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +#ifndef CSDB +#define CSDB +#endif +#ifndef csdb +#define csdb() +#endif + +#ifdef CONFIG_ARM_HEAVY_MB +extern void arm_heavy_mb(void); +#define __arm_heavy_mb(x...) do { dsb(x); arm_heavy_mb(); } while (0) +#else +#define __arm_heavy_mb(x...) dsb(x) +#endif +#endif /* CONFIG_BCM_KF_SPECTRE_PATCH && CONFIG_BCM_SPECTRE_PATCH_ENABLE */ + #ifdef CONFIG_ARCH_HAS_BARRIERS #include #elif defined(CONFIG_ARM_DMA_MEM_BUFFERABLE) || defined(CONFIG_SMP) +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +#define mb() __arm_heavy_mb() +#define rmb() dsb() +#define wmb() __arm_heavy_mb(st) +#else #define mb() do { dsb(); outer_sync(); } while (0) #define rmb() dsb() #define wmb() do { dsb(st); outer_sync(); } while (0) +#endif /* CONFIG_BCM_KF_SPECTRE_PATCH && CONFIG_BCM_SPECTRE_PATCH_ENABLE */ #define dma_rmb() dmb(osh) #define dma_wmb() dmb(oshst) #else @@ -62,6 +102,7 @@ #define smp_rmb() smp_mb() #define smp_wmb() dmb(ishst) #endif +#endif #define smp_store_release(p, v) \ do { \ @@ -86,5 +127,26 @@ #define smp_mb__before_atomic() smp_mb() #define smp_mb__after_atomic() smp_mb() +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +#ifdef CONFIG_CPU_SPECTRE +static inline unsigned long array_index_mask_nospec(unsigned long idx, + unsigned long sz) +{ + unsigned long mask; + + asm volatile( + "cmp %1, %2\n" + " sbc %0, %1, %1\n" + CSDB + : "=r" (mask) + : "r" (idx), "Ir" (sz) + : "cc"); + + return mask; +} +#define array_index_mask_nospec array_index_mask_nospec +#endif +#endif /* CONFIG_BCM_KF_SPECTRE_PATCH && CONFIG_BCM_SPECTRE_PATCH_ENABLE */ + #endif /* !__ASSEMBLY__ */ #endif /* __ASM_BARRIER_H */ diff -urN linux-4.1.52-orig/arch/arm/include/asm/bugs.h linux-4.1.52/arch/arm/include/asm/bugs.h --- linux-4.1.52-orig/arch/arm/include/asm/bugs.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/include/asm/bugs.h 2020-02-07 01:47:53.000000000 +0800 @@ -10,6 +10,16 @@ #ifndef __ASM_BUGS_H #define __ASM_BUGS_H +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +extern void check_writebuffer_bugs(void); +#ifdef CONFIG_MMU +extern void check_bugs(void); +extern void check_other_bugs(void); +#else +#define check_bugs() do { } while (0) +#define check_other_bugs() do { } while (0) +#endif +#else #ifdef CONFIG_MMU extern void check_writebuffer_bugs(void); @@ -17,5 +27,6 @@ #else #define check_bugs() do { } while (0) #endif +#endif /* CONFIG_BCM_KF_SPECTRE_PATCH && CONFIG_BCM_SPECTRE_PATCH_ENABLE*/ #endif diff -urN linux-4.1.52-orig/arch/arm/include/asm/cacheflush.h linux-4.1.52/arch/arm/include/asm/cacheflush.h --- linux-4.1.52-orig/arch/arm/include/asm/cacheflush.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/include/asm/cacheflush.h 2020-02-07 01:47:53.000000000 +0800 @@ -167,6 +167,21 @@ #endif +#if defined(CONFIG_BCM_KF_NBUFF) +#ifdef CONFIG_CPU_CACHE_V7 +#define __cpuc_flush_line(_addr) \ + __asm__ __volatile__("mcr p15, 0, %0, c7, c14, 1" : : "r" (_addr)) +#define __cpuc_clean_line(_addr) \ + __asm__ __volatile__("mcr p15, 0, %0, c7, c10, 1" : : "r" (_addr)) +#define __cpuc_inv_line(_addr) \ + __asm__ __volatile__("mcr p15, 0, %0, c7, c6, 1" : : "r" (_addr)) +#else +#define __cpuc_flush_line(_addr) do {} while(0) +#define __cpuc_clean_line(_addr) do {} while(0) +#define __cpuc_inv_line(_addr) do {} while(0) +#endif +#endif + /* * Copy user data from/to a page which is mapped into a different * processes address space. Really, we want to allow our "user diff -urN linux-4.1.52-orig/arch/arm/include/asm/cp15.h linux-4.1.52/arch/arm/include/asm/cp15.h --- linux-4.1.52-orig/arch/arm/include/asm/cp15.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/include/asm/cp15.h 2020-02-07 01:47:53.000000000 +0800 @@ -49,6 +49,26 @@ #ifdef CONFIG_CPU_CP15 +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +#define __ACCESS_CP15(CRn, Op1, CRm, Op2) \ + "mrc", "mcr", __stringify(p15, Op1, %0, CRn, CRm, Op2), u32 +#define __ACCESS_CP15_64(Op1, CRm) \ + "mrrc", "mcrr", __stringify(p15, Op1, %Q0, %R0, CRm), u64 + +#define __read_sysreg(r, w, c, t) ({ \ + t __val; \ + asm volatile(r " " c : "=r" (__val)); \ + __val; \ +}) +#define read_sysreg(...) __read_sysreg(__VA_ARGS__) + +#define __write_sysreg(v, r, w, c, t) asm volatile(w " " c : : "r" ((t)(v))) +#define write_sysreg(v, ...) __write_sysreg(v, __VA_ARGS__) + +#define BPIALL __ACCESS_CP15(c7, 0, c5, 6) +#define ICIALLU __ACCESS_CP15(c7, 0, c5, 0) +#endif /* CONFIG_BCM_KF_SPECTRE_PATCH && CONFIG_BCM_SPECTRE_PATCH_ENABLE */ + extern unsigned long cr_alignment; /* defined in entry-armv.S */ static inline unsigned long get_cr(void) diff -urN linux-4.1.52-orig/arch/arm/include/asm/cputype.h linux-4.1.52/arch/arm/include/asm/cputype.h --- linux-4.1.52-orig/arch/arm/include/asm/cputype.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/include/asm/cputype.h 2020-02-07 01:47:53.000000000 +0800 @@ -74,8 +74,31 @@ #define ARM_CPU_PART_CORTEX_A12 0x4100c0d0 #define ARM_CPU_PART_CORTEX_A17 0x4100c0e0 #define ARM_CPU_PART_CORTEX_A15 0x4100c0f0 +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +#define ARM_CPU_PART_CORTEX_A53 0x4100d030 +#define ARM_CPU_PART_CORTEX_A57 0x4100d070 +#define ARM_CPU_PART_CORTEX_A72 0x4100d080 +#define ARM_CPU_PART_CORTEX_A73 0x4100d090 +#define ARM_CPU_PART_CORTEX_A75 0x4100d0a0 +#endif #define ARM_CPU_PART_MASK 0xff00fff0 +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +/* Broadcom cores */ +#define ARM_CPU_PART_BRAHMA_B15 0x420000f0 + +/* DEC implemented cores */ +#define ARM_CPU_PART_SA1100 0x4400a110 + +/* Intel implemented cores */ +#define ARM_CPU_PART_SA1110 0x6900b110 +#define ARM_CPU_REV_SA1110_A0 0 +#define ARM_CPU_REV_SA1110_B0 4 +#define ARM_CPU_REV_SA1110_B1 5 +#define ARM_CPU_REV_SA1110_B2 6 +#define ARM_CPU_REV_SA1110_B4 8 +#endif + #define ARM_CPU_XSCALE_ARCH_MASK 0xe000 #define ARM_CPU_XSCALE_ARCH_V1 0x2000 #define ARM_CPU_XSCALE_ARCH_V2 0x4000 diff -urN linux-4.1.52-orig/arch/arm/include/asm/entry-macro-multi.S linux-4.1.52/arch/arm/include/asm/entry-macro-multi.S --- linux-4.1.52-orig/arch/arm/include/asm/entry-macro-multi.S 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/include/asm/entry-macro-multi.S 2020-02-07 01:47:53.000000000 +0800 @@ -10,7 +10,11 @@ @ @ routine called with r0 = irq number, r1 = struct pt_regs * @ +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + badrne lr, 1b +#else adrne lr, BSYM(1b) +#endif bne asm_do_IRQ #ifdef CONFIG_SMP @@ -23,7 +27,11 @@ ALT_SMP(test_for_ipi r0, r2, r6, lr) ALT_UP_B(9997f) movne r1, sp +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + badrne lr, 1b +#else adrne lr, BSYM(1b) +#endif bne do_IPI #endif 9997: diff -urN linux-4.1.52-orig/arch/arm/include/asm/io.h linux-4.1.52/arch/arm/include/asm/io.h --- linux-4.1.52-orig/arch/arm/include/asm/io.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/include/asm/io.h 2020-02-07 01:47:53.000000000 +0800 @@ -131,6 +131,9 @@ #define MT_DEVICE_NONSHARED 1 #define MT_DEVICE_CACHED 2 #define MT_DEVICE_WC 3 +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_PLAT_BCM63XX_ACP) +#define MT_DEVICE_NONSECURED 15 +#endif /* CONFIG_BCM_KF_ARM_BCM963XX && CONFIG_PLAT_BCM63XX_ACP */ /* * types 4 onwards can be found in asm/mach/map.h and are undefined * for ioremap @@ -336,6 +339,9 @@ #define ioremap_nocache(cookie,size) __arm_ioremap((cookie), (size), MT_DEVICE) #define ioremap_cache(cookie,size) __arm_ioremap((cookie), (size), MT_DEVICE_CACHED) #define ioremap_wc(cookie,size) __arm_ioremap((cookie), (size), MT_DEVICE_WC) +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_PLAT_BCM63XX_ACP) +#define ioremap_nonsecured(cookie,size) __arm_ioremap((cookie), (size), MT_DEVICE_NONSECURED) +#endif /* CONFIG_BCM_KF_ARM_BCM963XX && CONFIG_PLAT_BCM63XX_ACP */ #define iounmap __arm_iounmap /* diff -urN linux-4.1.52-orig/arch/arm/include/asm/mach/map.h linux-4.1.52/arch/arm/include/asm/mach/map.h --- linux-4.1.52-orig/arch/arm/include/asm/mach/map.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/include/asm/mach/map.h 2020-02-07 01:47:53.000000000 +0800 @@ -36,6 +36,9 @@ MT_MEMORY_RWX_ITCM, MT_MEMORY_RW_SO, MT_MEMORY_DMA_READY, +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_PLAT_BCM63XX_ACP) + MT_MEMORY_NONSECURED, +#endif /* CONFIG_BCM_KF_ARM_BCM963XX && CONFIG_PLAT_BCM63XX_ACP */ }; #ifdef CONFIG_MMU diff -urN linux-4.1.52-orig/arch/arm/include/asm/module.h linux-4.1.52/arch/arm/include/asm/module.h --- linux-4.1.52-orig/arch/arm/include/asm/module.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/include/asm/module.h 2020-02-07 01:47:53.000000000 +0800 @@ -5,6 +5,7 @@ struct unwind_table; +#ifdef CONFIG_BCM_KF_MISC_BACKPORTS #ifdef CONFIG_ARM_UNWIND enum { ARM_SEC_INIT, @@ -16,10 +17,38 @@ ARM_SEC_UNLIKELY, ARM_SEC_MAX, }; +#endif struct mod_arch_specific { +#ifdef CONFIG_ARM_UNWIND struct unwind_table *unwind[ARM_SEC_MAX]; +#endif +#ifdef CONFIG_ARM_MODULE_PLTS + struct elf32_shdr *core_plt; + struct elf32_shdr *init_plt; + int core_plt_count; + int init_plt_count; +#endif }; + +u32 get_module_plt(struct module *mod, unsigned long loc, Elf32_Addr val); +#else +#ifdef CONFIG_ARM_UNWIND +enum { + ARM_SEC_INIT, + ARM_SEC_DEVINIT, + ARM_SEC_CORE, + ARM_SEC_EXIT, + ARM_SEC_DEVEXIT, + ARM_SEC_HOT, + ARM_SEC_UNLIKELY, + ARM_SEC_MAX, +}; + +struct mod_arch_specific { + struct unwind_table *unwind[ARM_SEC_MAX]; +}; +#endif #endif /* diff -urN linux-4.1.52-orig/arch/arm/include/asm/outercache.h linux-4.1.52/arch/arm/include/asm/outercache.h --- linux-4.1.52-orig/arch/arm/include/asm/outercache.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/include/asm/outercache.h 2020-02-07 01:47:53.000000000 +0800 @@ -39,6 +39,13 @@ /* This is an ARM L2C thing */ void (*write_sec)(unsigned long, unsigned); void (*configure)(const struct l2x0_regs *); +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) + void (*spin_lock_irqsave)(void); + void (*spin_unlock_irqrestore)(void); + void (*sync_no_lock)(void); + void (*flush_line_no_lock)(unsigned long); + void (*inv_line_no_lock)(unsigned long); +#endif }; extern struct outer_cache_fns outer_cache; @@ -115,6 +122,32 @@ outer_cache.resume(); } +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +static inline void outer_spin_lock_irqsave(void) +{ + outer_cache.spin_lock_irqsave(); +} + +static inline void outer_spin_unlock_irqrestore(void) +{ + outer_cache.spin_unlock_irqrestore(); +} + +static inline void outer_sync_no_lock(void) +{ + outer_cache.sync_no_lock(); +} + +static inline void outer_flush_line_no_lock(phys_addr_t addr) +{ + outer_cache.flush_line_no_lock(addr); +} + +static inline void outer_inv_line_no_lock(phys_addr_t addr) +{ + outer_cache.inv_line_no_lock(addr); +} +#endif #else static inline void outer_inv_range(phys_addr_t start, phys_addr_t end) @@ -129,6 +162,8 @@ #endif +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +#else #ifdef CONFIG_OUTER_CACHE_SYNC /** * outer_sync - perform a sync point for outer cache @@ -145,5 +180,6 @@ static inline void outer_sync(void) { } #endif +#endif /* CONFIG_BCM_KF_SPECTRE_PATCH && CONFIG_BCM_SPECTRE_PATCH_ENABLE */ #endif /* __ASM_OUTERCACHE_H */ diff -urN linux-4.1.52-orig/arch/arm/include/asm/pgtable-2level-hwdef.h linux-4.1.52/arch/arm/include/asm/pgtable-2level-hwdef.h --- linux-4.1.52-orig/arch/arm/include/asm/pgtable-2level-hwdef.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/include/asm/pgtable-2level-hwdef.h 2020-02-07 01:47:53.000000000 +0800 @@ -24,6 +24,9 @@ #define PMD_BIT4 (_AT(pmdval_t, 1) << 4) #define PMD_DOMAIN(x) (_AT(pmdval_t, (x)) << 5) #define PMD_PROTECTION (_AT(pmdval_t, 1) << 9) /* v5 */ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_PLAT_BCM63XX_ACP) +#define PMD_NONSECURE (_AT(pmdval_t, 1) << 3) +#endif /* CONFIG_BCM_KF_ARM_BCM963XX && CONFIG_PLAT_BCM63XX_ACP */ /* * - section */ @@ -38,6 +41,9 @@ #define PMD_SECT_S (_AT(pmdval_t, 1) << 16) /* v6 */ #define PMD_SECT_nG (_AT(pmdval_t, 1) << 17) /* v6 */ #define PMD_SECT_SUPER (_AT(pmdval_t, 1) << 18) /* v6 */ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_PLAT_BCM63XX_ACP) +#define PMD_SECT_NS (_AT(pmdval_t, 1) << 19) +#endif /* CONFIG_BCM_KF_ARM_BCM963XX && CONFIG_PLAT_BCM63XX_ACP */ #define PMD_SECT_AF (_AT(pmdval_t, 0)) #define PMD_SECT_UNCACHED (_AT(pmdval_t, 0)) diff -urN linux-4.1.52-orig/arch/arm/include/asm/proc-fns.h linux-4.1.52/arch/arm/include/asm/proc-fns.h --- linux-4.1.52-orig/arch/arm/include/asm/proc-fns.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/include/asm/proc-fns.h 2020-02-07 01:47:53.000000000 +0800 @@ -36,6 +36,12 @@ * Set up any processor specifics */ void (*_proc_init)(void); +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + /* + * Check for processor bugs + */ + void (*check_bugs)(void); +#endif /* * Disable any processor specifics */ diff -urN linux-4.1.52-orig/arch/arm/include/asm/spinlock.h linux-4.1.52/arch/arm/include/asm/spinlock.h --- linux-4.1.52-orig/arch/arm/include/asm/spinlock.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/include/asm/spinlock.h 2020-02-07 01:47:53.000000000 +0800 @@ -76,8 +76,11 @@ wfe(); lockval.tickets.owner = ACCESS_ONCE(lock->tickets.owner); } - +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_B15_MEGA_BARRIER) + smp_rmb(); +#else smp_mb(); +#endif } static inline int arch_spin_trylock(arch_spinlock_t *lock) @@ -99,7 +102,11 @@ } while (res); if (!contended) { +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_B15_MEGA_BARRIER) + smp_rmb(); +#else smp_mb(); +#endif return 1; } else { return 0; @@ -108,7 +115,11 @@ static inline void arch_spin_unlock(arch_spinlock_t *lock) { +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_B15_MEGA_BARRIER) + smp_rmb(); +#else smp_mb(); +#endif lock->tickets.owner++; dsb_sev(); } @@ -154,7 +165,11 @@ : "r" (&rw->lock), "r" (0x80000000) : "cc"); +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_B15_MEGA_BARRIER) + smp_rmb(); +#else smp_mb(); +#endif } static inline int arch_write_trylock(arch_rwlock_t *rw) @@ -174,7 +189,11 @@ } while (res); if (!contended) { +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_B15_MEGA_BARRIER) + smp_rmb(); +#else smp_mb(); +#endif return 1; } else { return 0; @@ -183,7 +202,11 @@ static inline void arch_write_unlock(arch_rwlock_t *rw) { +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_B15_MEGA_BARRIER) + smp_rmb(); +#else smp_mb(); +#endif __asm__ __volatile__( "str %1, [%0]\n" @@ -225,7 +248,11 @@ : "r" (&rw->lock) : "cc"); +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_B15_MEGA_BARRIER) + smp_rmb(); +#else smp_mb(); +#endif } static inline void arch_read_unlock(arch_rwlock_t *rw) @@ -267,7 +294,11 @@ /* If the lock is negative, then it is already held for write. */ if (contended < 0x80000000) { +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_B15_MEGA_BARRIER) + smp_rmb(); +#else smp_mb(); +#endif return 1; } else { return 0; diff -urN linux-4.1.52-orig/arch/arm/include/asm/system_misc.h linux-4.1.52/arch/arm/include/asm/system_misc.h --- linux-4.1.52-orig/arch/arm/include/asm/system_misc.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/include/asm/system_misc.h 2020-02-07 01:47:53.000000000 +0800 @@ -7,6 +7,9 @@ #include #include #include +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +#include +#endif extern void cpu_init(void); @@ -14,6 +17,22 @@ extern void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd); extern void (*arm_pm_idle)(void); +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +#ifdef CONFIG_HARDEN_BRANCH_PREDICTOR +typedef void (*harden_branch_predictor_fn_t)(void); +DECLARE_PER_CPU(harden_branch_predictor_fn_t, harden_branch_predictor_fn); +static inline void harden_branch_predictor(void) +{ + harden_branch_predictor_fn_t fn = per_cpu(harden_branch_predictor_fn, + smp_processor_id()); + if (fn) + fn(); +} +#else +#define harden_branch_predictor() do { } while (0) +#endif /* CONFIG_HARDEN_BRANCH_PREDICTOR */ +#endif /* CONFIG_BCM_KF_SPECTRE_PATCH && CONFIG_BCM_SPECTRE_PATCH_ENABLE */ + #define UDBG_UNDEFINED (1 << 0) #define UDBG_SYSCALL (1 << 1) #define UDBG_BADABORT (1 << 2) diff -urN linux-4.1.52-orig/arch/arm/include/asm/unified.h linux-4.1.52/arch/arm/include/asm/unified.h --- linux-4.1.52-orig/arch/arm/include/asm/unified.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/include/asm/unified.h 2020-02-07 01:47:53.000000000 +0800 @@ -45,7 +45,10 @@ #define THUMB(x...) x #ifdef __ASSEMBLY__ #define W(instr) instr.w +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +#else #define BSYM(sym) sym + 1 +#endif #else #define WASM(instr) #instr ".w" #endif @@ -59,7 +62,10 @@ #define THUMB(x...) #ifdef __ASSEMBLY__ #define W(instr) instr +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +#else #define BSYM(sym) sym +#endif #else #define WASM(instr) #instr #endif diff -urN linux-4.1.52-orig/arch/arm/include/uapi/asm/setup.h linux-4.1.52/arch/arm/include/uapi/asm/setup.h --- linux-4.1.52-orig/arch/arm/include/uapi/asm/setup.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/include/uapi/asm/setup.h 2020-02-07 01:47:53.000000000 +0800 @@ -143,6 +143,30 @@ __u32 fmemclk; }; +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* BCM63xx, for passing board param from boot loader */ +#define ATAG_BLPARM 0x41000601 + +struct tag_blparm { + char blparm[1]; /* this is the minimum size */ +}; + +#define ATAG_RDPSIZE 0x41000602 +struct tag_rdpsize { + __u32 tm_size; + __u32 mc_size; +}; + +#define ATAG_DHDSIZE 0x41000603 +struct tag_dhdparm { + __u32 dhd_size[3]; +}; + + + +#endif /* CONFIG_BCM_KF_ARM_BCM963XX */ + + struct tag { struct tag_header hdr; union { @@ -165,6 +189,16 @@ * DC21285 specific */ struct tag_memclk memclk; +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) + + /* + * BCM63xx specific + */ + struct tag_blparm blparm; + struct tag_rdpsize rdpsize; + struct tag_dhdparm dhdparm; +#endif /* CONFIG_BCM_KF_ARM_BCM963XX */ + } u; }; diff -urN linux-4.1.52-orig/arch/arm/Kconfig linux-4.1.52/arch/arm/Kconfig --- linux-4.1.52-orig/arch/arm/Kconfig 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/Kconfig 2020-02-07 01:47:53.000000000 +0800 @@ -60,7 +60,7 @@ select HAVE_KPROBES if !XIP_KERNEL select HAVE_KRETPROBES if (HAVE_KPROBES) select HAVE_MEMBLOCK - select HAVE_MOD_ARCH_SPECIFIC if ARM_UNWIND + select HAVE_MOD_ARCH_SPECIFIC if ARM_UNWIND || BCM_KF_MISC_BACKPORTS select HAVE_OPROFILE if (HAVE_PERF_EVENTS) select HAVE_OPTPROBES if !THUMB2_KERNEL select HAVE_PERF_EVENTS @@ -758,8 +758,49 @@ help Support for older TI OMAP1 (omap7xx, omap15xx or omap16xx) +config ARCH_BCM63XX + bool "Broadcom 63XX Architecture" + depends on BCM_KF_ARM_BCM963XX + select NO_GENERIC_PCI_IOPORT_MAP + select USE_OF + help + Support for Broadcome 63XX series architecture + endchoice +if BCM_KF_ARM_BCM963XX + +config CPU_LITTLE_ENDIAN + bool "Build little-endian kernel" + +config CPU_BIG_ENDIAN + bool "Build big-endian kernel" + +config BUZZZ + bool "Broadcom BUZZZ Tool Framework" + default n + help + Enable kernel tools framework: event/performance/function call tracing + +config BUZZZ_FUNC + bool "Broadcom BUZZZ Func Tool" + default n + help + Enable Function tracing tool + +config BUZZZ_PMON + bool "Broadcom BUZZZ PMON Tool" + default n + help + Enable performance monitoring tool + +config BUZZZ_KEVT + bool "Broadcom BUZZZ Kernel Event Tracing Tool" + default n + help + Enable Kernel Event Tracing tool +endif + menu "Multiple platform selection" depends on ARCH_MULTIPLATFORM @@ -836,6 +877,10 @@ source "arch/arm/mach-bcm/Kconfig" +if BCM_KF_ARM_BCM963XX +source "arch/arm/plat-bcm63xx/Kconfig" +endif + source "arch/arm/mach-berlin/Kconfig" source "arch/arm/mach-clps711x/Kconfig" @@ -1682,6 +1727,23 @@ config ARCH_WANT_GENERAL_HUGETLB def_bool y +if BCM_KF_MISC_BACKPORTS +config ARM_MODULE_PLTS + bool "Use PLTs to allow module memory to spill over into vmalloc area" + depends on MODULES + help + Allocate PLTs when loading modules so that jumps and calls whose + targets are too far away for their relative offsets to be encoded + in the instructions themselves can be bounced via veneers in the + module's PLT. This allows modules to be allocated in the generic + vmalloc area after the dedicated module memory area has been + exhausted. The modules will use slightly more memory, but after + rounding up to page size, the actual memory footprint is usually + the same. + + Say y if you are getting out of memory errors while loading modules + +endif source "mm/Kconfig" config FORCE_MAX_ZONEORDER diff -urN linux-4.1.52-orig/arch/arm/kernel/armksyms.c linux-4.1.52/arch/arm/kernel/armksyms.c --- linux-4.1.52-orig/arch/arm/kernel/armksyms.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/kernel/armksyms.c 2020-02-07 01:47:53.000000000 +0800 @@ -16,6 +16,9 @@ #include #include #include +#if defined(CONFIG_BCM_KF_OPTEE) +#include +#endif #include #include @@ -169,3 +172,10 @@ EXPORT_SYMBOL(__pv_phys_pfn_offset); EXPORT_SYMBOL(__pv_offset); #endif + +#if defined(CONFIG_BCM_KF_OPTEE) +#ifdef CONFIG_HAVE_ARM_SMCCC +EXPORT_SYMBOL(__arm_smccc_smc); +EXPORT_SYMBOL(__arm_smccc_hvc); +#endif +#endif /* CONFIG_BCM_KF_OPTEE */ diff -urN linux-4.1.52-orig/arch/arm/kernel/atags_parse.c linux-4.1.52/arch/arm/kernel/atags_parse.c --- linux-4.1.52-orig/arch/arm/kernel/atags_parse.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/kernel/atags_parse.c 2020-02-07 01:47:53.000000000 +0800 @@ -140,6 +140,26 @@ __tagtable(ATAG_CMDLINE, parse_tag_cmdline); +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +static int __init parse_tag_blparm(const struct tag *tag) +{ + /* a dummy function that it has been handled in + * bcm63xx's machine setup code */ + return 0; +} + +__tagtable(ATAG_BLPARM, parse_tag_blparm); + +static int __init parse_tag_rdpsize(const struct tag *tag) +{ + /* a dummy function that it has been handled in + * bcm63xx's machine setup code */ + return 0; +} + +__tagtable(ATAG_RDPSIZE, parse_tag_rdpsize); +#endif /* (CONFIG_BCM_KF_ARM_BCM963XX) */ + /* * Scan the tag table for this tag, and call its parse function. * The tag table is built by the linker from all the __tagtable diff -urN linux-4.1.52-orig/arch/arm/kernel/bugs.c linux-4.1.52/arch/arm/kernel/bugs.c --- linux-4.1.52-orig/arch/arm/kernel/bugs.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/kernel/bugs.c 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,20 @@ +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +// SPDX-Identifier: GPL-2.0 +#include +#include +#include + +void check_other_bugs(void) +{ +#ifdef MULTI_CPU + if (processor.check_bugs) + processor.check_bugs(); +#endif +} + +void __init check_bugs(void) +{ + check_writebuffer_bugs(); + check_other_bugs(); +} +#endif diff -urN linux-4.1.52-orig/arch/arm/kernel/entry-armv.S linux-4.1.52/arch/arm/kernel/entry-armv.S --- linux-4.1.52-orig/arch/arm/kernel/entry-armv.S 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/kernel/entry-armv.S 2020-02-07 01:47:53.000000000 +0800 @@ -40,7 +40,11 @@ #ifdef CONFIG_MULTI_IRQ_HANDLER ldr r1, =handle_arch_irq mov r0, sp +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + badr lr, 9997f +#else adr lr, BSYM(9997f) +#endif ldr pc, [r1] #else arch_irq_handler_default @@ -273,7 +277,11 @@ str r4, [sp, #S_PC] orr r0, r9, r0, lsl #16 #endif +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + badr r9, __und_svc_finish +#else adr r9, BSYM(__und_svc_finish) +#endif mov r2, r4 bl call_fpe @@ -469,7 +477,11 @@ @ instruction, or the more conventional lr if we are to treat @ this as a real undefined instruction @ +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + badr r9, ret_from_exception +#else adr r9, BSYM(ret_from_exception) +#endif @ IRQs must be enabled before attempting to read the instruction from @ user space since that could cause a page/translation fault if the @@ -486,7 +498,11 @@ @ r2 = PC value for the following instruction (:= regs->ARM_pc) @ r4 = PC value for the faulting instruction @ lr = 32-bit undefined instruction function +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + badr lr, __und_usr_fault_32 +#else adr lr, BSYM(__und_usr_fault_32) +#endif b call_fpe __und_usr_thumb: @@ -522,7 +538,11 @@ add r2, r2, #2 @ r2 is PC + 2, make it PC + 4 str r2, [sp, #S_PC] @ it's a 2x16bit instr, update orr r0, r0, r5, lsl #16 +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + badr lr, __und_usr_fault_32 +#else adr lr, BSYM(__und_usr_fault_32) +#endif @ r0 = the two 16-bit Thumb instructions which caused the exception @ r2 = PC value for the following Thumb instruction (:= regs->ARM_pc) @ r4 = PC value for the first 16-bit Thumb instruction @@ -716,7 +736,11 @@ __und_usr_fault_16: mov r1, #2 1: mov r0, sp +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + badr lr, ret_from_exception +#else adr lr, BSYM(ret_from_exception) +#endif b __und_fault ENDPROC(__und_usr_fault_32) ENDPROC(__und_usr_fault_16) diff -urN linux-4.1.52-orig/arch/arm/kernel/entry-common.S linux-4.1.52/arch/arm/kernel/entry-common.S --- linux-4.1.52-orig/arch/arm/kernel/entry-common.S 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/kernel/entry-common.S 2020-02-07 01:47:53.000000000 +0800 @@ -90,7 +90,11 @@ bl schedule_tail cmp r5, #0 movne r0, r4 +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + badrne lr, 1f +#else adrne lr, BSYM(1f) +#endif retne r5 1: get_thread_info tsk b ret_slow_syscall @@ -197,9 +201,13 @@ tst r10, #_TIF_SYSCALL_WORK @ are we tracing syscalls? bne __sys_trace +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + invoke_syscall tbl, scno, r10, ret_fast_syscall +#else cmp scno, #NR_syscalls @ check upper syscall limit adr lr, BSYM(ret_fast_syscall) @ return address ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine +#endif add r1, sp, #S_OFF 2: cmp scno, #(__ARM_NR_BASE - __NR_SYSCALL_BASE) @@ -233,6 +241,10 @@ add r0, sp, #S_OFF bl syscall_trace_enter +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + mov scno, r0 + invoke_syscall tbl, scno, r10, __sys_trace_return, reload=1 +#else adr lr, BSYM(__sys_trace_return) @ return address mov scno, r0 @ syscall number (possibly new) add r1, sp, #S_R0 + S_OFF @ pointer to regs @@ -240,6 +252,7 @@ ldmccia r1, {r0 - r6} @ have to reload r0 - r6 stmccia sp, {r4, r5} @ and update the stack args ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine +#endif cmp scno, #-1 @ skip the syscall? bne 2b add sp, sp, #S_OFF @ restore stack @@ -285,6 +298,12 @@ bic scno, r0, #__NR_OABI_SYSCALL_BASE cmp scno, #__NR_syscall - __NR_SYSCALL_BASE cmpne scno, #NR_syscalls @ check range +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +#ifdef CONFIG_CPU_SPECTRE + movhs scno, #0 + csdb +#endif +#endif /* CONFIG_BCM_KF_SPECTRE_PATCH && CONFIG_BCM_SPECTRE_PATCH_ENABLE */ stmloia sp, {r5, r6} @ shuffle args movlo r0, r1 movlo r1, r2 diff -urN linux-4.1.52-orig/arch/arm/kernel/entry-ftrace.S linux-4.1.52/arch/arm/kernel/entry-ftrace.S --- linux-4.1.52-orig/arch/arm/kernel/entry-ftrace.S 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/kernel/entry-ftrace.S 2020-02-07 01:47:53.000000000 +0800 @@ -87,7 +87,11 @@ 1: mcount_get_lr r1 @ lr of instrumented func mcount_adjust_addr r0, lr @ instrumented function +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + badr lr, 2f +#else adr lr, BSYM(2f) +#endif mov pc, r2 2: mcount_exit .endm diff -urN linux-4.1.52-orig/arch/arm/kernel/entry-header.S linux-4.1.52/arch/arm/kernel/entry-header.S --- linux-4.1.52-orig/arch/arm/kernel/entry-header.S 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/kernel/entry-header.S 2020-02-07 01:47:53.000000000 +0800 @@ -385,6 +385,33 @@ #endif .endm +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + .macro invoke_syscall, table, nr, tmp, ret, reload=0 +#ifdef CONFIG_CPU_SPECTRE + mov \tmp, \nr + cmp \tmp, #NR_syscalls @ check upper syscall limit + movcs \tmp, #0 + csdb + badr lr, \ret @ return address + .if \reload + add r1, sp, #S_R0 + S_OFF @ pointer to regs + ldmccia r1, {r0 - r6} @ reload r0-r6 + stmccia sp, {r4, r5} @ update stack arguments + .endif + ldrcc pc, [\table, \tmp, lsl #2] @ call sys_* routine +#else + cmp \nr, #NR_syscalls @ check upper syscall limit + badr lr, \ret @ return address + .if \reload + add r1, sp, #S_R0 + S_OFF @ pointer to regs + ldmccia r1, {r0 - r6} @ reload r0-r6 + stmccia sp, {r4, r5} @ update stack arguments + .endif + ldrcc pc, [\table, \nr, lsl #2] @ call sys_* routine +#endif + .endm +#endif /* CONFIG_BCM_KF_SPECTRE_PATCH && CONFIG_BCM_SPECTRE_PATCH_ENABLE */ + /* * These are the registers used in the syscall handler, and allow us to * have in theory up to 7 arguments to a function - r0 to r6. diff -urN linux-4.1.52-orig/arch/arm/kernel/head-nommu.S linux-4.1.52/arch/arm/kernel/head-nommu.S --- linux-4.1.52-orig/arch/arm/kernel/head-nommu.S 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/kernel/head-nommu.S 2020-02-07 01:47:53.000000000 +0800 @@ -46,7 +46,11 @@ .arm ENTRY(stext) +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + THUMB( badr r9, 1f ) @ Kernel is always entered in ARM. +#else THUMB( adr r9, BSYM(1f) ) @ Kernel is always entered in ARM. +#endif THUMB( bx r9 ) @ If this is a Thumb-2 kernel, THUMB( .thumb ) @ switch to Thumb now. THUMB(1: ) @@ -79,7 +83,11 @@ #endif ldr r13, =__mmap_switched @ address to jump to after @ initialising sctlr +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + badr lr, 1f @ return (PIC) address +#else adr lr, BSYM(1f) @ return (PIC) address +#endif ldr r12, [r10, #PROCINFO_INITFUNC] add r12, r12, r10 ret r12 @@ -115,7 +123,11 @@ bl __setup_mpu @ Initialize the MPU #endif +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + badr lr, __after_proc_init @ return address +#else adr lr, BSYM(__after_proc_init) @ return address +#endif mov r13, r12 @ __secondary_switched address ldr r12, [r10, #PROCINFO_INITFUNC] add r12, r12, r10 diff -urN linux-4.1.52-orig/arch/arm/kernel/head.S linux-4.1.52/arch/arm/kernel/head.S --- linux-4.1.52-orig/arch/arm/kernel/head.S 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/kernel/head.S 2020-02-07 01:47:53.000000000 +0800 @@ -80,7 +80,11 @@ ENTRY(stext) ARM_BE8(setend be ) @ ensure we are in BE8 mode +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + THUMB( badr r9, 1f ) @ Kernel is always entered in ARM. +#else THUMB( adr r9, BSYM(1f) ) @ Kernel is always entered in ARM. +#endif THUMB( bx r9 ) @ If this is a Thumb-2 kernel, THUMB( .thumb ) @ switch to Thumb now. THUMB(1: ) @@ -136,7 +140,11 @@ */ ldr r13, =__mmap_switched @ address to jump to after @ mmu has been enabled +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + badr lr, 1f @ return (PIC) address +#else adr lr, BSYM(1f) @ return (PIC) address +#endif mov r8, r4 @ set TTBR1 to swapper_pg_dir ldr r12, [r10, #PROCINFO_INITFUNC] add r12, r12, r10 @@ -348,7 +356,11 @@ .text ENTRY(secondary_startup_arm) .arm +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + THUMB( badr r9, 1f ) @ Kernel is entered in ARM. +#else THUMB( adr r9, BSYM(1f) ) @ Kernel is entered in ARM. +#endif THUMB( bx r9 ) @ If this is a Thumb-2 kernel, THUMB( .thumb ) @ switch to Thumb now. THUMB(1: ) @@ -384,7 +396,11 @@ ldr r4, [r7, lr] @ get secondary_data.pgdir add r7, r7, #4 ldr r8, [r7, lr] @ get secondary_data.swapper_pg_dir +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + badr lr, __enable_mmu @ return address +#else adr lr, BSYM(__enable_mmu) @ return address +#endif mov r13, r12 @ __secondary_switched address ldr r12, [r10, #PROCINFO_INITFUNC] add r12, r12, r10 @ initialise processor diff -urN linux-4.1.52-orig/arch/arm/kernel/irq.c linux-4.1.52/arch/arm/kernel/irq.c --- linux-4.1.52-orig/arch/arm/kernel/irq.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/kernel/irq.c 2020-02-07 01:47:53.000000000 +0800 @@ -39,10 +39,16 @@ #include #include +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +#include +#endif #include #include #include #include +#if defined CONFIG_BCM_KF_CPU_AFFINITY_HINT && defined CONFIG_HOTPLUG_CPU +#include +#endif unsigned long irq_err_count; @@ -116,6 +122,9 @@ if (ret) pr_err("L2C: failed to init: %d\n", ret); } +#if defined CONFIG_BCM_KF_CPU_AFFINITY_HINT && defined CONFIG_HOTPLUG_CPU + hotcpu_notifier(bcm63xx_rehint_irqaffinity, 0); +#endif } #ifdef CONFIG_MULTI_IRQ_HANDLER @@ -152,6 +161,13 @@ return false; if (cpumask_any_and(affinity, cpu_online_mask) >= nr_cpu_ids) { +#if defined CONFIG_BCM_KF_CPU_AFFINITY_HINT + // affine cpu is offline; any hinted cpu online? + if (desc->affinity_hint && + cpumask_any_and(desc->affinity_hint, cpu_online_mask) < nr_cpu_ids) + affinity = desc->affinity_hint; + else +#endif affinity = cpu_online_mask; ret = true; } diff -urN linux-4.1.52-orig/arch/arm/kernel/Makefile linux-4.1.52/arch/arm/kernel/Makefile --- linux-4.1.52-orig/arch/arm/kernel/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/kernel/Makefile 2020-02-07 01:47:53.000000000 +0800 @@ -13,6 +13,9 @@ CFLAGS_REMOVE_return_address.o = -pg +CFLAGS_irq.o := -I${INC_BRCMSHARED_PUB_PATH}/bcm963xx\ + -I${INC_BRCMDRIVER_PUB_PATH}/bcm963xx + # Object file lists. obj-y := elf.o entry-common.o irq.o opcodes.o \ @@ -30,10 +33,16 @@ obj-y += entry-armv.o endif +ifdef BCM_KF # defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +obj-$(CONFIG_MMU) += bugs.o +endif obj-$(CONFIG_CPU_IDLE) += cpuidle.o obj-$(CONFIG_ISA_DMA_API) += dma.o obj-$(CONFIG_FIQ) += fiq.o fiqasm.o obj-$(CONFIG_MODULES) += armksyms.o module.o +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_BACKPORTS) +obj-$(CONFIG_ARM_MODULE_PLTS) += module-plts.o +endif obj-$(CONFIG_ISA_DMA) += dma-isa.o obj-$(CONFIG_PCI) += bios32.o isa.o obj-$(CONFIG_ARM_CPU_SUSPEND) += sleep.o suspend.o @@ -90,4 +99,8 @@ obj-$(CONFIG_SMP) += psci_smp.o endif +ifdef BCM_KF +obj-$(CONFIG_HAVE_ARM_SMCCC) += smccc-call.o +endif + extra-y := $(head-y) vmlinux.lds diff -urN linux-4.1.52-orig/arch/arm/kernel/module.c linux-4.1.52/arch/arm/kernel/module.c --- linux-4.1.52-orig/arch/arm/kernel/module.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/kernel/module.c 2020-02-07 01:47:53.000000000 +0800 @@ -40,7 +40,23 @@ #ifdef CONFIG_MMU void *module_alloc(unsigned long size) { +#ifdef CONFIG_BCM_KF_MISC_BACKPORTS + gfp_t gfp_mask = GFP_KERNEL; + void *p; + + /* Silence the initial allocation */ + if (IS_ENABLED(CONFIG_ARM_MODULE_PLTS)) + gfp_mask |= __GFP_NOWARN; + + p = __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END, + gfp_mask, PAGE_KERNEL_EXEC, 0, NUMA_NO_NODE, + __builtin_return_address(0)); + if (!IS_ENABLED(CONFIG_ARM_MODULE_PLTS) || p) + return p; + return __vmalloc_node_range(size, 1, VMALLOC_START, VMALLOC_END, +#else return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END, +#endif GFP_KERNEL, PAGE_KERNEL_EXEC, 0, NUMA_NO_NODE, __builtin_return_address(0)); } @@ -110,6 +126,22 @@ offset -= 0x04000000; offset += sym->st_value - loc; +#ifdef CONFIG_BCM_KF_MISC_BACKPORTS + + /* + * Route through a PLT entry if 'offset' exceeds the + * supported range. Note that 'offset + loc + 8' + * contains the absolute jump target, i.e., + * @sym + addend, corrected for the +8 PC bias. + */ + if (IS_ENABLED(CONFIG_ARM_MODULE_PLTS) && + (offset <= (s32)0xfe000000 || + offset >= (s32)0x02000000)) + offset = get_module_plt(module, loc, + offset + loc + 8) + - loc - 8; + +#endif if (offset <= (s32)0xfe000000 || offset >= (s32)0x02000000) { pr_err("%s: section %u reloc %u sym '%s': relocation %u out of range (%#lx -> %#x)\n", @@ -203,6 +235,19 @@ offset -= 0x02000000; offset += sym->st_value - loc; +#ifdef CONFIG_BCM_KF_MISC_BACKPORTS + /* + * Route through a PLT entry if 'offset' exceeds the + * supported range. + */ + if (IS_ENABLED(CONFIG_ARM_MODULE_PLTS) && + (offset <= (s32)0xff000000 || + offset >= (s32)0x01000000)) + offset = get_module_plt(module, loc, + offset + loc + 4) + - loc - 4; + +#endif if (offset <= (s32)0xff000000 || offset >= (s32)0x01000000) { pr_err("%s: section %u reloc %u sym '%s': relocation %u out of range (%#lx -> %#x)\n", diff -urN linux-4.1.52-orig/arch/arm/kernel/module.lds linux-4.1.52/arch/arm/kernel/module.lds --- linux-4.1.52-orig/arch/arm/kernel/module.lds 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/kernel/module.lds 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,4 @@ +SECTIONS { + .core.plt : { BYTE(0) } + .init.plt : { BYTE(0) } +} diff -urN linux-4.1.52-orig/arch/arm/kernel/module-plts.c linux-4.1.52/arch/arm/kernel/module-plts.c --- linux-4.1.52-orig/arch/arm/kernel/module-plts.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/kernel/module-plts.c 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,183 @@ +#ifdef CONFIG_BCM_KF_MISC_BACKPORTS +/* + * Copyright (C) 2014 Linaro Ltd. + * + * 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. + */ + +#include +#include +#include + +#include +#include + +#define PLT_ENT_STRIDE L1_CACHE_BYTES +#define PLT_ENT_COUNT (PLT_ENT_STRIDE / sizeof(u32)) +#define PLT_ENT_SIZE (sizeof(struct plt_entries) / PLT_ENT_COUNT) + +#ifdef CONFIG_THUMB2_KERNEL +#define PLT_ENT_LDR __opcode_to_mem_thumb32(0xf8dff000 | \ + (PLT_ENT_STRIDE - 4)) +#else +#define PLT_ENT_LDR __opcode_to_mem_arm(0xe59ff000 | \ + (PLT_ENT_STRIDE - 8)) +#endif + +struct plt_entries { + u32 ldr[PLT_ENT_COUNT]; + u32 lit[PLT_ENT_COUNT]; +}; + +static bool in_init(const struct module *mod, u32 addr) +{ + return addr - (u32)mod->module_init < mod->init_size; +} + +u32 get_module_plt(struct module *mod, unsigned long loc, Elf32_Addr val) +{ + struct plt_entries *plt, *plt_end; + int c, *count; + + if (in_init(mod, loc)) { + plt = (void *)mod->arch.init_plt->sh_addr; + plt_end = (void *)plt + mod->arch.init_plt->sh_size; + count = &mod->arch.init_plt_count; + } else { + plt = (void *)mod->arch.core_plt->sh_addr; + plt_end = (void *)plt + mod->arch.core_plt->sh_size; + count = &mod->arch.core_plt_count; + } + + /* Look for an existing entry pointing to 'val' */ + for (c = *count; plt < plt_end; c -= PLT_ENT_COUNT, plt++) { + int i; + + if (!c) { + /* Populate a new set of entries */ + *plt = (struct plt_entries){ + { [0 ... PLT_ENT_COUNT - 1] = PLT_ENT_LDR, }, + { val, } + }; + ++*count; + return (u32)plt->ldr; + } + for (i = 0; i < PLT_ENT_COUNT; i++) { + if (!plt->lit[i]) { + plt->lit[i] = val; + ++*count; + } + if (plt->lit[i] == val) + return (u32)&plt->ldr[i]; + } + } + BUG(); +} + +static int duplicate_rel(Elf32_Addr base, const Elf32_Rel *rel, int num, + u32 mask) +{ + u32 *loc1, *loc2; + int i; + + for (i = 0; i < num; i++) { + if (rel[i].r_info != rel[num].r_info) + continue; + + /* + * Identical relocation types against identical symbols can + * still result in different PLT entries if the addend in the + * place is different. So resolve the target of the relocation + * to compare the values. + */ + loc1 = (u32 *)(base + rel[i].r_offset); + loc2 = (u32 *)(base + rel[num].r_offset); + if (((*loc1 ^ *loc2) & mask) == 0) + return 1; + } + return 0; +} + +/* Count how many PLT entries we may need */ +static unsigned int count_plts(Elf32_Addr base, const Elf32_Rel *rel, int num) +{ + unsigned int ret = 0; + int i; + + /* + * Sure, this is order(n^2), but it's usually short, and not + * time critical + */ + for (i = 0; i < num; i++) + switch (ELF32_R_TYPE(rel[i].r_info)) { + case R_ARM_CALL: + case R_ARM_PC24: + case R_ARM_JUMP24: + if (!duplicate_rel(base, rel, i, + __opcode_to_mem_arm(0x00ffffff))) + ret++; + break; + case R_ARM_THM_CALL: + case R_ARM_THM_JUMP24: + if (!duplicate_rel(base, rel, i, + __opcode_to_mem_thumb32(0x07ff2fff))) + ret++; + } + return ret; +} + +int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, + char *secstrings, struct module *mod) +{ + unsigned long core_plts = 0, init_plts = 0; + Elf32_Shdr *s, *sechdrs_end = sechdrs + ehdr->e_shnum; + + /* + * To store the PLTs, we expand the .text section for core module code + * and the .init.text section for initialization code. + */ + for (s = sechdrs; s < sechdrs_end; ++s) + if (strcmp(".core.plt", secstrings + s->sh_name) == 0) + mod->arch.core_plt = s; + else if (strcmp(".init.plt", secstrings + s->sh_name) == 0) + mod->arch.init_plt = s; + + if (!mod->arch.core_plt || !mod->arch.init_plt) { + pr_err("%s: sections missing\n", mod->name); + return -ENOEXEC; + } + + for (s = sechdrs + 1; s < sechdrs_end; ++s) { + const Elf32_Rel *rels = (void *)ehdr + s->sh_offset; + int numrels = s->sh_size / sizeof(Elf32_Rel); + Elf32_Shdr *dstsec = sechdrs + s->sh_info; + + if (s->sh_type != SHT_REL) + continue; + + if (strstr(secstrings + s->sh_name, ".init")) + init_plts += count_plts(dstsec->sh_addr, rels, numrels); + else + core_plts += count_plts(dstsec->sh_addr, rels, numrels); + } + + mod->arch.core_plt->sh_type = SHT_NOBITS; + mod->arch.core_plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC; + mod->arch.core_plt->sh_addralign = L1_CACHE_BYTES; + mod->arch.core_plt->sh_size = round_up(core_plts * PLT_ENT_SIZE, + sizeof(struct plt_entries)); + mod->arch.core_plt_count = 0; + + mod->arch.init_plt->sh_type = SHT_NOBITS; + mod->arch.init_plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC; + mod->arch.init_plt->sh_addralign = L1_CACHE_BYTES; + mod->arch.init_plt->sh_size = round_up(init_plts * PLT_ENT_SIZE, + sizeof(struct plt_entries)); + mod->arch.init_plt_count = 0; + pr_debug("%s: core.plt=%x, init.plt=%x\n", __func__, + mod->arch.core_plt->sh_size, mod->arch.init_plt->sh_size); + return 0; +} +#endif diff -urN linux-4.1.52-orig/arch/arm/kernel/psci.c linux-4.1.52/arch/arm/kernel/psci.c --- linux-4.1.52-orig/arch/arm/kernel/psci.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/kernel/psci.c 2020-02-07 01:47:53.000000000 +0800 @@ -80,6 +80,17 @@ return err; } +#if defined(CONFIG_BCM_KF_OPTEE) && defined (CONFIG_BCM_KERNEL_CODE_PROTECTION) +static int psci_mem_info(void) +{ + int err; + extern char _stext[], _etext[], __init_begin[]; + + err = invoke_psci_fn(PSCI_0_2_FN_MEM_INFO, (u32)_stext, (u32)_etext, (u32)__init_begin); + return err; +} +#endif + static int psci_cpu_suspend(struct psci_power_state state, unsigned long entry_point) { @@ -232,7 +243,9 @@ arm_pm_restart = psci_sys_reset; pm_power_off = psci_sys_poweroff; - +#if defined(CONFIG_BCM_KF_OPTEE) && defined (CONFIG_BCM_KERNEL_CODE_PROTECTION) + psci_mem_info(); +#endif out_put_node: of_node_put(np); return err; diff -urN linux-4.1.52-orig/arch/arm/kernel/sleep.S linux-4.1.52/arch/arm/kernel/sleep.S --- linux-4.1.52-orig/arch/arm/kernel/sleep.S 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/kernel/sleep.S 2020-02-07 01:47:53.000000000 +0800 @@ -81,7 +81,11 @@ mov r1, r4 @ size of save block add r0, sp, #8 @ pointer to save block bl __cpu_suspend_save +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + badr lr, cpu_suspend_abort +#else adr lr, BSYM(cpu_suspend_abort) +#endif ldmfd sp!, {r0, pc} @ call suspend fn ENDPROC(__cpu_suspend) .ltorg diff -urN linux-4.1.52-orig/arch/arm/kernel/smccc-call.S linux-4.1.52/arch/arm/kernel/smccc-call.S --- linux-4.1.52-orig/arch/arm/kernel/smccc-call.S 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/kernel/smccc-call.S 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,66 @@ +#if defined(CONFIG_BCM_KF_OPTEE_414_BACKPORTS) +/* + * Copyright (c) 2015, Linaro Limited + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ +#include + +#include +#include +#include + + /* + * Wrap c macros in asm macros to delay expansion until after the + * SMCCC asm macro is expanded. + */ + .macro SMCCC_SMC + __SMC(0) + .endm + + .macro SMCCC_HVC + __HVC(0) + .endm + + .macro SMCCC instr +UNWIND( .fnstart) + mov r12, sp + push {r4-r7} +UNWIND( .save {r4-r7}) + ldm r12, {r4-r7} + \instr + pop {r4-r7} + ldr r12, [sp, #(4 * 4)] + stm r12, {r0-r3} + bx lr +UNWIND( .fnend) + .endm + +/* + * void smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2, + * unsigned long a3, unsigned long a4, unsigned long a5, + * unsigned long a6, unsigned long a7, struct arm_smccc_res *res, + * struct arm_smccc_quirk *quirk) + */ +ENTRY(__arm_smccc_smc) + SMCCC SMCCC_SMC +ENDPROC(__arm_smccc_smc) + +/* + * void smccc_hvc(unsigned long a0, unsigned long a1, unsigned long a2, + * unsigned long a3, unsigned long a4, unsigned long a5, + * unsigned long a6, unsigned long a7, struct arm_smccc_res *res, + * struct arm_smccc_quirk *quirk) + */ +ENTRY(__arm_smccc_hvc) + SMCCC SMCCC_HVC +ENDPROC(__arm_smccc_hvc) +#endif /* CONFIG_BCM_KF_OPTEE_414_BACKPORTS */ diff -urN linux-4.1.52-orig/arch/arm/kernel/smp.c linux-4.1.52/arch/arm/kernel/smp.c --- linux-4.1.52-orig/arch/arm/kernel/smp.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/kernel/smp.c 2020-02-07 01:47:53.000000000 +0800 @@ -28,6 +28,9 @@ #include #include +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +#include +#endif #include #include #include @@ -379,6 +382,9 @@ * before we continue - which happens after __cpu_up returns. */ set_cpu_online(cpu, true); +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + check_other_bugs(); +#endif complete(&cpu_running); local_irq_enable(); diff -urN linux-4.1.52-orig/arch/arm/kernel/smp_tlb.c linux-4.1.52/arch/arm/kernel/smp_tlb.c --- linux-4.1.52-orig/arch/arm/kernel/smp_tlb.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/kernel/smp_tlb.c 2020-02-07 01:47:53.000000000 +0800 @@ -92,6 +92,19 @@ unsigned int midr = read_cpuid_id(); unsigned int revidr = read_cpuid(CPUID_REVIDR); +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) + /* specifically for the B15 used in BCM63148 */ + if (midr == 0x420f00f3) { + if ((revidr & 0x210) == 0x210) + return; + if (revidr & 0x10) + erratum_a15_798181_handler = erratum_a15_798181_partial; + else + erratum_a15_798181_handler = erratum_a15_798181_broadcast; + return; + } +#endif + /* Brahma-B15 r0p0..r0p2 affected * Cortex-A15 r0p0..r3p2 w/o ECO fix affected */ if ((midr & 0xff0ffff0) == 0x420f00f0 && midr <= 0x420f00f2) diff -urN linux-4.1.52-orig/arch/arm/kernel/suspend.c linux-4.1.52/arch/arm/kernel/suspend.c --- linux-4.1.52-orig/arch/arm/kernel/suspend.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/kernel/suspend.c 2020-02-07 01:47:53.000000000 +0800 @@ -1,6 +1,9 @@ #include #include +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +#include +#endif #include #include #include @@ -34,6 +37,9 @@ cpu_switch_mm(mm->pgd, mm); local_flush_bp_all(); local_flush_tlb_all(); +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + check_other_bugs(); +#endif } return ret; diff -urN linux-4.1.52-orig/arch/arm/lib/call_with_stack.S linux-4.1.52/arch/arm/lib/call_with_stack.S --- linux-4.1.52-orig/arch/arm/lib/call_with_stack.S 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/lib/call_with_stack.S 2020-02-07 01:47:53.000000000 +0800 @@ -35,7 +35,11 @@ mov r2, r0 mov r0, r1 +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + badr lr, 1f +#else adr lr, BSYM(1f) +#endif ret r2 1: ldr lr, [sp] diff -urN linux-4.1.52-orig/arch/arm/lib/memset.S linux-4.1.52/arch/arm/lib/memset.S --- linux-4.1.52-orig/arch/arm/lib/memset.S 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/lib/memset.S 2020-02-07 01:47:53.000000000 +0800 @@ -18,6 +18,9 @@ ENTRY(memset) UNWIND( .fnstart ) +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) + and r1, r1, #255 @ mask upper bits, per c99 definition +#endif ands r3, r0, #3 @ 1 unaligned? mov ip, r0 @ preserve r0 as return value bne 6f @ 1 diff -urN linux-4.1.52-orig/arch/arm/mach-bcm963xx/board_6846.c linux-4.1.52/arch/arm/mach-bcm963xx/board_6846.c --- linux-4.1.52-orig/arch/arm/mach-bcm963xx/board_6846.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/mach-bcm963xx/board_6846.c 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,45 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + +#include +#include +#include +#include + +static const char * const bcm63xx_dt_compat[] = { + "brcm,bcm96846", + NULL +}; + +MACHINE_START(BCM96846, "BCM96846") + .dt_compat = bcm63xx_dt_compat, +MACHINE_END + +#endif //CONFIG_BCM_KF_ARM_BCM963XX diff -urN linux-4.1.52-orig/arch/arm/mach-bcm963xx/board_963xx.c linux-4.1.52/arch/arm/mach-bcm963xx/board_963xx.c --- linux-4.1.52-orig/arch/arm/mach-bcm963xx/board_963xx.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/mach-bcm963xx/board_963xx.c 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,547 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:GPL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +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 (the "GPL"). + +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. + + +A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by +writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + +:> +*/ + +/* + * Generic board routine for Broadcom 963xx ARM boards + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#if defined(CONFIG_BCM963138) +#include +#elif defined(CONFIG_BCM963148) +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_OF +extern int __init bcm_scan_fdt(void); +extern int __init bcm_reserve_memory(void); +extern int __init bcm_dt_postinit(void); +#endif + +#define BOOT_ATAGS 0 +#define BOOT_FDT 1 + +static int boot_param_status = BOOT_ATAGS; + +#ifdef CONFIG_ATAGS + +#define MB_ALIGNED(__val) ALIGN(__val, 0x100000) + +#if IS_ENABLED(CONFIG_BCM_ADSL) +#include "softdsl/AdslCoreDefs.h" +/* Reserve memory for DSL */ +#define ADSL_SDRAM_RESERVE_SIZE MB_ALIGNED(ADSL_SDRAM_IMAGE_SIZE) +#endif + + +#define SO_MEMORY_SIZE_BYTES SECTION_SIZE +#if IS_ENABLED(CONFIG_BCM_RDPA) +unsigned long tm_size = 0, mc_size = 0; +#endif + +#if IS_ENABLED(CONFIG_BCM_DHD_RUNNER) +unsigned long dhd_pool_size[DHD_RESERVE_MEM_NUM]; +#endif + +#ifdef CONFIG_BCM_CFE_XARGS_EARLY +extern void __init bl_xparms_setup(const unsigned char* blparms, unsigned int size); +#endif + +extern void check_if_rootfs_is_set(char *cmdline); +extern unsigned long memsize; +extern bool is_rootfs_set; +/* Pointers to memory buffers allocated for the DSP module */ +void *dsp_core; +void *dsp_init; +EXPORT_SYMBOL(dsp_core); +EXPORT_SYMBOL(dsp_init); + +#endif /* CONFIG_ATAGS */ + +extern unsigned long getMemorySize(void); + +#if defined(CONFIG_BCM_B15_MEGA_BARRIER) +static uint32_t preSOmemScratch; +static void *so_memory_virt_addr=&preSOmemScratch; +#endif + + +/* +***************************************************************************** +** FUNCTION: allocDspModBuffers +** +** PURPOSE: Allocates buffers for the init and core sections of the DSP +** module. This module is special since it has to be allocated +** in the 0x800.. memory range which is not mapped by the TLB. +** +** PARAMETERS: None +** RETURNS: Nothing +***************************************************************************** +*/ +void __init allocDspModBuffers(void) +{ +} + +#ifdef CONFIG_BCM_B15_MEGA_BARRIER +void BcmMegaBarrier(void) +{ + __asm__("dsb"); + writel_relaxed(so_memory_virt_addr, so_memory_virt_addr); + __asm__("dsb"); +} +EXPORT_SYMBOL(BcmMegaBarrier); +#endif /*CONFIG_BCM_B15_MEGA_BARRIER*/ + +void __init board_map_io(void) +{ + struct map_desc desc[TOTAL_RESERVE_MEM_NUM+1]; + int i = 0; + + /* Map SoC specific I/O */ + soc_map_io(); + + for(i = 0; i < rsvd_mem_cnt; i++ ) { + desc[i].virtual = (unsigned long)phys_to_virt(reserve_mem[i].phys_addr); + desc[i].pfn = __phys_to_pfn(reserve_mem[i].phys_addr); + desc[i].length = reserve_mem[i].size; +#if defined (CONFIG_BCM_B15_MEGA_BARRIER) + if (strcmp(B15_MEGA_BARRIER, reserve_mem[i].name)==0) { + so_memory_virt_addr = (void*)desc[i].virtual; + desc[i].type = MT_MEMORY_RW_SO; + } else { + desc[i].type = MT_MEMORY_RWX_NONCACHED; + } +#else + desc[i].type = MT_MEMORY_RWX_NONCACHED; +#endif + reserve_mem[i].virt_addr = (void*)desc[i].virtual; + printk(KERN_INFO "creating a %s device at physical " + "address of 0x%08lx to virtual address at " + "0x%08lx with size of 0x%lx byte for %s\n", + (desc[i].type == MT_MEMORY_RWX_NONCACHED)?"MT_MEMORY_NONCACHED":"MT_MEMORY", + (unsigned long)reserve_mem[i].phys_addr, + desc[i].virtual, desc[i].length, + reserve_mem[i].name); + } + + if( i > 0 ) + iotable_init(desc, i); + + if (getMemorySize() <= SZ_32M) + printk("WARNING! System is with 0x%0lx memory, might not " + "boot successfully.\n" + "\tcheck ATAG or CMDLINE\n", getMemorySize()); +} + +static void __init bcm_setup(void) +{ + if(kerSysIsIkosBootSet() == 0) + { + kerSysEarlyFlashInit(); + kerSysFlashInit(); + bcm_extirq_init(); + } + bcm_init_pinmux(); +} + +void __init board_init_early(void) +{ + soc_init_early(); + + bcm_setup(); +} + + +void __init board_init_irq(void) +{ + soc_init_irq(); + +#if !defined (CONFIG_OF) +#ifdef CONFIG_CACHE_L2X0 + /* cache initialization */ + soc_l2_cache_init(); +#endif +#endif +} + +static void __init dt_fixup(void) +{ + boot_param_status = BOOT_FDT; +} + +void __init board_init_machine(void) +{ + /* + * Add common platform devices that do not have board dependent HW + * configurations + */ + soc_add_devices(); + +#if defined (CONFIG_OF) + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); +#endif + + return; +} + +#ifdef CONFIG_ATAGS + +static void __init set_memsize_from_cmdline(char *cmdline) +{ + char *cmd_ptr, *end_ptr; + + cmd_ptr = strstr(cmdline, "mem="); + if (cmd_ptr != NULL) { + cmd_ptr += 4; + memsize = (unsigned long)memparse(cmd_ptr, &end_ptr); + } +} + +static void __init reserve_system_mem(char* name, unsigned long addr, unsigned long size) +{ + if (rsvd_mem_cnt >= TOTAL_RESERVE_MEM_NUM) { + printk("reserved memory count %d reached the total memory reserve count %d!!!", rsvd_mem_cnt, TOTAL_RESERVE_MEM_NUM); + return; + } + memblock_remove(addr, size); + strcpy(reserve_mem[rsvd_mem_cnt].name, name); + reserve_mem[rsvd_mem_cnt].size = size; + reserve_mem[rsvd_mem_cnt].phys_addr = (uint32_t)addr; + rsvd_mem_cnt++; + + return; +} + + +/* in ARM, there are two ways of passing in memory size. + * one is by setting it in ATAG_MEM, and the other one is by setting the + * size in CMDLINE. The first appearance of mem=nn[KMG] in CMDLINE is the + * value that has the highest priority. And if there is no memory size set + * in CMDLINE, then it will use the value in ATAG_MEM. If there is no ATAG + * given from boot loader, then a default ATAG with memory size set to 16MB + * will be taken effect. + * Assuming CONFIG_CMDLINE_EXTEND is set. The logic doesn't work if + * CONFIG_CMDLINE_FROM_BOOTLOADER is set. */ +static void __init board_fixup(struct tag *t, char **cmdline) +{ + soc_fixup(); + /* obtaining info passing down from boot loader */ + for (; t->hdr.size; t = tag_next(t)) { + if ((t->hdr.tag == ATAG_CORE) && (t->u.core.rootdev != 0xff)) + is_rootfs_set = true; + + if (t->hdr.tag == ATAG_MEM) + memsize = t->u.mem.size; + +#if IS_ENABLED(CONFIG_BCM_RDPA) + if (t->hdr.tag == ATAG_RDPSIZE) { + tm_size = t->u.rdpsize.tm_size * SZ_1M; + mc_size = t->u.rdpsize.mc_size * SZ_1M; + } +#endif + +#if IS_ENABLED(CONFIG_BCM_DHD_RUNNER) + dhd_pool_size[0] = 0; + dhd_pool_size[1] = 0; + dhd_pool_size[2] = 0; + if (t->hdr.tag == ATAG_DHDSIZE) { + /* if the kernel is still running on old cfe */ + if (t->u.dhdparm.dhd_size[0] != 0xff && + t->u.dhdparm.dhd_size[1] != 0xff && + t->u.dhdparm.dhd_size[2] != 0xff ) { + dhd_pool_size[0] = t->u.dhdparm.dhd_size[0] * SZ_1M; + dhd_pool_size[1] = t->u.dhdparm.dhd_size[1] * SZ_1M; + dhd_pool_size[2] = t->u.dhdparm.dhd_size[2] * SZ_1M; + } + } +#endif + + if (t->hdr.tag == ATAG_BLPARM ){ + const unsigned char* bcm_blparms_buf = bcm_get_blparms(); + if (bcm_blparms_buf) { + memcpy((unsigned char*)bcm_blparms_buf, t->u.blparm.blparm, bcm_get_blparms_size()); +#ifdef CONFIG_BCM_CFE_XARGS_EARLY + bl_xparms_setup(bcm_blparms_buf, bcm_get_blparms_size()); +#endif + } else { + printk(KERN_ERR "%s:%d Unable to get BCM blparms buffer\n",__func__,__LINE__); + } + } + + if (t->hdr.tag == ATAG_CMDLINE) { + set_memsize_from_cmdline(t->u.cmdline.cmdline); + check_if_rootfs_is_set(t->u.cmdline.cmdline); + } + if ((t->hdr.tag == ATAG_INITRD2) || (t->hdr.tag == ATAG_INITRD)) + is_rootfs_set = true; + } + set_memsize_from_cmdline(*cmdline); + check_if_rootfs_is_set(*cmdline); +} + +static void __init board_reserve_atag(void) +{ + /* used for reserve mem blocks */ + unsigned long mem_end = getMemorySize(); + unsigned long rsrv_mem_required = SZ_8M; +#if IS_ENABLED(CONFIG_BCM_DHD_RUNNER) + int j = 0; +#endif + rsvd_mem_cnt = 0; + /* both reserved memory for RDP and DSL have to be within first + * 256MB */ + if (mem_end > SZ_256M) + mem_end = SZ_256M; + +#if IS_ENABLED(CONFIG_BCM_RDPA) + /* Make sure the input values are larger than minimum required */ + if (tm_size < TM_DEF_DDR_SIZE) + tm_size = TM_DEF_DDR_SIZE; + + if (mc_size < TM_MC_DEF_DDR_SIZE) + mc_size = TM_MC_DEF_DDR_SIZE; + + /* both TM and MC reserved memory size has to be multiple of 2MB */ + if (tm_size & SZ_1M) + tm_size += SZ_1M; + if (mc_size & SZ_1M) + mc_size += SZ_1M; + + rsrv_mem_required += tm_size + mc_size; +#endif + +#if IS_ENABLED(CONFIG_BCM_DHD_RUNNER) + /* Make sure the input values are larger than minimum required */ + rsrv_mem_required += dhd_pool_size[0]; + rsrv_mem_required += dhd_pool_size[1]; + rsrv_mem_required += dhd_pool_size[2]; +#endif + +#if IS_ENABLED(CONFIG_BCM_ADSL) + rsrv_mem_required += ADSL_SDRAM_RESERVE_SIZE; +#endif + +#if defined(CONFIG_BCM_B15_MEGA_BARRIER) + rsrv_mem_required += SO_MEMORY_SIZE_BYTES; +#endif + +#if IS_ENABLED(CONFIG_BCM_ADSL) || IS_ENABLED(CONFIG_BCM_RDPA) || IS_ENABLED(CONFIG_BCM_DHD_RUNNER) || defined(CONFIG_BCM_B15_MEGA_BARRIER) + /* check if those configured memory sizes are over what + * system has */ + + if (getMemorySize() < rsrv_mem_required) { + +#if IS_ENABLED(CONFIG_BCM_DHD_RUNNER) + rsrv_mem_required -= (dhd_pool_size[0] + dhd_pool_size[1] + dhd_pool_size[2]); + dhd_pool_size[0] = dhd_pool_size[1] = dhd_pool_size[2] = 0x0; +#endif + +#if IS_ENABLED(CONFIG_BCM_RDPA) + /* If RDP is enabled, try to use the default + * TM and MC reserved memory size and try again */ + rsrv_mem_required -= tm_size + mc_size; + tm_size = TM_DEF_DDR_SIZE; + mc_size = TM_MC_DEF_DDR_SIZE; + rsrv_mem_required += tm_size + mc_size; +#endif + + if (getMemorySize() < rsrv_mem_required) + return; + } +#endif + +#if IS_ENABLED(CONFIG_BCM_ADSL) + /* reserve memory for DSL. We use memblock_remove + IO_MAP the removed + * memory block to MT_MEMORY_NONCACHED here because ADSL driver code + * will need to access the memory. Another option is to use + * memblock_reserve where the kernel still sees the memory, but I could + * not find a function to make the reserved memory noncacheable. */ + mem_end -= ADSL_SDRAM_RESERVE_SIZE; + reserve_system_mem(ADSL_BASE_ADDR_STR, mem_end, ADSL_SDRAM_RESERVE_SIZE); +#endif + +#if IS_ENABLED(CONFIG_BCM_RDPA) + mem_end -= tm_size; + /* TM reserved memory has to be 2MB-aligned */ + if (mem_end & SZ_1M) + mem_end -= SZ_1M; + reserve_system_mem(TM_BASE_ADDR_STR, mem_end, tm_size); + + mem_end -= mc_size; + /* MC reserved memory has to be 2MB-aligned */ + if (unlikely(mem_end & SZ_1M)) + mem_end -= SZ_1M; + reserve_system_mem(TM_MC_BASE_ADDR_STR, mem_end, mc_size); +#endif + +#if IS_ENABLED(CONFIG_BCM_DHD_RUNNER) + for( j = 0; j < 3; j++ ) { + if(dhd_pool_size[j] != 0) { + char name[16]; + mem_end -= dhd_pool_size[j]; + /* DHD reserved memory has to be 2MB-aligned */ + if (unlikely(mem_end & SZ_1M)) + mem_end -= SZ_1M; + sprintf(name, "%s%d", "dhd", j); + reserve_system_mem(name, mem_end, dhd_pool_size[j]); + } + } +#endif + +#if defined(CONFIG_BCM_B15_MEGA_BARRIER) + mem_end -= SO_MEMORY_SIZE_BYTES; + reserve_system_mem(B15_MEGA_BARRIER, mem_end, SO_MEMORY_SIZE_BYTES); +#endif + + if( rsvd_mem_cnt ) + is_memory_reserved = true; +} + +#endif /*CONFIG_ATAGS*/ + +static void board_restart(enum reboot_mode mode, const char *cmd) +{ + if(kerSysIsIkosBootSet() == 0) + kerSysSoftReset(); +} + + +static void __init board_reserve(void) +{ + if (boot_param_status == BOOT_FDT){ + bcm_scan_fdt(); + bcm_reserve_memory(); + bcm_dt_postinit(); + } else { +#if defined(CONFIG_ATAGS) + board_reserve_atag(); +#endif + } +} + + +#if defined (CONFIG_BCM963138) + +#if defined (CONFIG_OF) +static const char * const bcm63xx_dt_compat[] = { + "brcm,bcm963138", + NULL +}; +#endif + +MACHINE_START(BCM963138, "BCM963138") + /* Maintainer: Broadcom */ + .reserve = board_reserve, + .map_io = board_map_io, + .init_early = board_init_early, +#if !defined (CONFIG_OF) + .init_irq = board_init_irq, +#endif + .init_machine = board_init_machine, + .restart = board_restart, +#ifdef CONFIG_ZONE_DMA + /* If enable CONFIG_ZONE_DMA, it will reserve the given size of + * memory from SDRAM and use it exclusively for DMA purpose. + * This ensures the device driver can allocate enough memory. */ + .dma_zone_size = SZ_16M, /* must be multiple of 2MB */ +#endif + +#if defined (CONFIG_ATAGS) + .fixup = board_fixup, +#endif + +#if defined (CONFIG_OF) + .dt_compat = bcm63xx_dt_compat, + .dt_fixup = dt_fixup, + .l2c_aux_val = BCM_L2C_AUX_VAL, + .l2c_aux_mask = BCM_L2C_AUX_MSK, +#endif +MACHINE_END + +#endif + + +#if defined (CONFIG_BCM963148) + +#ifdef CONFIG_OF +static const char * const bcm63xx_dt_compat[] = { + "brcm,bcm963148", + NULL +}; +#endif + + +MACHINE_START(BCM963148, "BCM963148") + /* Maintainer: Broadcom */ + .reserve = board_reserve, + .map_io = board_map_io, + .init_early = board_init_early, +#if !defined (CONFIG_OF) + .init_irq = board_init_irq, +#endif + .init_machine = board_init_machine, + .restart = board_restart, +#ifdef CONFIG_ZONE_DMA + /* If enable CONFIG_ZONE_DMA, it will reserve the given size of + * memory from SDRAM and use it exclusively for DMA purpose. + * This ensures the device driver can allocate enough memory. */ + .dma_zone_size = SZ_16M, /* must be multiple of 2MB and within 16MB for DSL PHY */ +#endif +#if defined (CONFIG_ATAGS) + .fixup = board_fixup, +#endif +#if defined (CONFIG_OF) + .dt_compat = bcm63xx_dt_compat, + .dt_fixup = dt_fixup, +#endif +MACHINE_END + +#endif + +#endif /* CONFIG_BCM_KF_ARM_BCM963XX */ diff -urN linux-4.1.52-orig/arch/arm/mach-bcm963xx/include/mach/barriers.h linux-4.1.52/arch/arm/mach-bcm963xx/include/mach/barriers.h --- linux-4.1.52-orig/arch/arm/mach-bcm963xx/include/mach/barriers.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/mach-bcm963xx/include/mach/barriers.h 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,112 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +#ifndef _MACH_BCM963XX_BARRIERS_H +#define _MACH_BCM963XX_BARRIERS_H + +#include + +#ifdef CONFIG_BCM_B15_MEGA_BARRIER +void BcmMegaBarrier(void); /*Implemented in board_963xx.c*/ +#endif + +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +extern void arm_heavy_mb(void); +#endif + +#if defined(CONFIG_BCM_KF_ARM_ERRATA_798181) + /*WARNING: don't use isb()/dsb()/dmb() macros as a Write-Memory-Memory Barrier. Correctness is not guaranteed. + *If you need a write memory barrier, use the wmb()/smp_wmb() macros below.*/ + #define isb(option) __asm__ __volatile__ ("isb " #option : : : "memory") + #define dsb(option) __asm__ __volatile__ ("dsb " #option : : : "memory") + #define dmb(option) __asm__ __volatile__ ("dmb " #option : : : "memory") +#else + #define isb() __asm__ __volatile__ ("isb" : : : "memory") + #define dsb() __asm__ __volatile__ ("dsb" : : : "memory") + #define dmb() __asm__ __volatile__ ("dmb" : : : "memory") +#endif + +#if defined(CONFIG_ARM_DMA_MEM_BUFFERABLE) || defined(CONFIG_SMP) + #if defined(CONFIG_BCM_B15_MEGA_BARRIER) + #if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + #define mb() do { BcmMegaBarrier(); arm_heavy_mb(); } while (0) + #define wmb() do { BcmMegaBarrier(); arm_heavy_mb(); } while (0) + #else + #define mb() BcmMegaBarrier() + #define wmb() BcmMegaBarrier() + #endif + #elif defined(CONFIG_BCM_KF_ARM_ERRATA_798181) + #if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + #define mb() do { dsb(); arm_heavy_mb(); } while (0) + #define wmb() do { dsb(st); arm_heavy_mb(); } while (0) + #else + #define mb() do { dsb(); outer_sync(); } while (0) + #define wmb() do { dsb(st); outer_sync(); } while (0) + #endif + #else + #if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + #define mb() do { dsb(); arm_heavy_mb(); } while (0) + #else + #define mb() do { dsb(); outer_sync(); } while (0) + #endif + #define wmb() mb() + #endif + #define rmb() dsb() +#else + #include + #define mb() do { if (arch_is_coherent()) dmb(); else barrier(); } while (0) + #define rmb() do { if (arch_is_coherent()) dmb(); else barrier(); } while (0) + #define wmb() do { if (arch_is_coherent()) dmb(); else barrier(); } while (0) +#endif + +#ifndef CONFIG_SMP + #ifdef CONFIG_BCM_B15_MEGA_BARRIER + #define smp_mb() BcmMegaBarrier() + #else + #define smp_mb() barrier() + #endif + #define smp_rmb() barrier() + #define smp_wmb() smp_mb() +#else /*CONFIG_SMP:*/ + #if defined(CONFIG_BCM_B15_MEGA_BARRIER) + #define smp_mb() BcmMegaBarrier() + #define smp_rmb() dmb(ish) + #define smp_wmb() BcmMegaBarrier() + #elif defined(CONFIG_BCM_KF_ARM_ERRATA_798181) + #define smp_mb() dmb(ish) + #define smp_rmb() smp_mb() + #define smp_wmb() dmb(ishst) + #else + #define smp_mb() dmb() + #define smp_rmb() dmb() + #define smp_wmb() dmb() + #endif +#endif /*CONFIG_SMP*/ +#endif /*_MACH_BCM963XX_BARRIERS_H*/ +#endif /*(CONFIG_BCM_KF_ARM_BCM963XX)*/ diff -urN linux-4.1.52-orig/arch/arm/mach-bcm963xx/include/mach/clkdev.h linux-4.1.52/arch/arm/mach-bcm963xx/include/mach/clkdev.h --- linux-4.1.52-orig/arch/arm/mach-bcm963xx/include/mach/clkdev.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/mach-bcm963xx/include/mach/clkdev.h 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,59 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +#ifndef __ASM_MACH_CLKDEV_H +#define __ASM_MACH_CLKDEV_H __FILE__ + +#include +#include + +/* FIXME! the following is based on bcm5301x, and it will need to + * be modified based on the clk implementation */ +struct clk { + const struct clk_ops *ops; + const char *name; + atomic_t ena_cnt; + atomic_t use_cnt; + unsigned long rate; + unsigned gated :1; + unsigned fixed :1; + unsigned chan :6; + void __iomem *regs_base; + struct clk *parent; + /* TBD: could it have multiple parents to select from ? */ + enum { + CLK_XTAL, CLK_GATE, CLK_PLL, CLK_DIV, CLK_PHA, CLK_UART, CLK_DMAC + } type; +}; + +int __clk_get(struct clk *clk); +void __clk_put(struct clk *clk); + +#endif /* __ASM_MACH_CLKDEV_H */ +#endif /* CONFIG_BCM_KF_ARM_BCM963XX */ diff -urN linux-4.1.52-orig/arch/arm/mach-bcm963xx/include/mach/debug-macro.S linux-4.1.52/arch/arm/mach-bcm963xx/include/mach/debug-macro.S --- linux-4.1.52-orig/arch/arm/mach-bcm963xx/include/mach/debug-macro.S 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/mach-bcm963xx/include/mach/debug-macro.S 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,85 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +/* FIXME!! this one needs to be adjusted */ + +/* + * Macros used for EARLY_PRINTK, in low-level UART debug console + */ +#include +//#include + +#define DEBUG_UART_VA IO_ADDRESS(CONFIG_DEBUG_UART_ADDR) + + .macro addruart, rp, rv, tmp + ldr \rv, =DEBUG_UART_VA @ virtual + ldr \rp, =CONFIG_DEBUG_UART_ADDR @ physical + .endm + +/* FIXME!! try to use a defined value for the address below */ +#if CONFIG_DEBUG_UART_ADDR==0x80019000 +//#if CONFIG_DEBUG_UART_ADDR==ARM_UART_PHYS_BASE + #ifdef CONFIG_PLAT_BCM63XX_AMBA_PL011 + #include + #else + #ifdef EARLY_PRINTK + /* FIXME! Print a compiling warning message saying */ + /* that there is no device for early_printk */ + #endif /* EARLY_PRINTK */ + #endif /* CONFIG_PLAT_BCM63XX_AMBA_PL011 */ +#else +/* using PERIPH Uart for debug.S */ +#define BCM63XX_UART_CTRL 0x00 +#define BCM63XX_UART_BDWD 0x04 +#define BCM63XX_UART_MCCTL 0x08 +#define BCM63XX_UART_EIPT 0x0C +#define BCM63XX_UART_INT 0x10 +#define BCM63XX_UART_DATA 0x14 + +#define BCM63XX_UART_TXFIFOEMP 0x0020 +#define BCM63XX_UART_TXFIFOTHOLD 0x0008 + + .macro senduart, rd, rx + /* byte access doesn't work, has to write word */ + strb \rd, [\rx, #BCM63XX_UART_DATA] + .endm + + .macro waituart, rd, rx +1001: ldr \rd, [\rx, #BCM63XX_UART_INT] + tst \rd, #BCM63XX_UART_TXFIFOEMP + beq 1001b + .endm + + .macro busyuart, rd, rx +1002: ldr \rd, [\rx, #BCM63XX_UART_INT] + tst \rd, #BCM63XX_UART_TXFIFOEMP + beq 1002b + .endm +#endif /* CONFIG_DEBUG_UART_ADDR=0x8001900 */ +#endif /* CONFIG_BCM_KF_ARM_BCM963XX */ diff -urN linux-4.1.52-orig/arch/arm/mach-bcm963xx/include/mach/hardware.h linux-4.1.52/arch/arm/mach-bcm963xx/include/mach/hardware.h --- linux-4.1.52-orig/arch/arm/mach-bcm963xx/include/mach/hardware.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/mach-bcm963xx/include/mach/hardware.h 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,61 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +#ifndef __ARCH_HARDWARE_H +#define __ARCH_HARDWARE_H + +#include + +/* macro to get at IO space when running virtually */ +#if defined(CONFIG_MMU) && !defined(CONFIG_BCM96846) +/* in 63138, we have two memory space area defined for registers. One starts + * from 0x8000 0000 to roughly 0x80800000. And the other one is for PERIPH. + * It starts from 0xfffc 0000 to 0xffff 0100. In addition, SPI boot is from + * 0xffd0 0000 to 0xfff0 0000. Therefore, we can define the following macro + * of address translation that combines the three different areas into one + * contiguous virtual address area. They will be mapped to 0xfc00 0000, + * where + * 0xfc00 0000 - 0xfc80 0000 -> 0x8000 0000 - 0x8080 0000 + * 0xfcd0 0000 - 0xfcf0 0000 -> 0xffd0 0000 - 0xfff0 0000 + * 0xfcfc 0000 - 0xfcff 0100 -> 0xfffc 0000 - 0xffff 0100 */ + +#define IO_ADDRESS(x) (((x) & 0x00ffffff) + 0xfc000000) + +#else +#define IO_ADDRESS(x) (x) +#endif + +#define __io_address(n) IOMEM(IO_ADDRESS(n)) + +#ifdef CONFIG_PLAT_BCM63XX_ACP +#define ACP_ADDRESS(x) ((x) | 0xe0000000) +#endif + +#endif /* __ARCH_HARDWARE_H */ +#endif /* CONFIG_BCM_KF_ARM_BCM963XX */ diff -urN linux-4.1.52-orig/arch/arm/mach-bcm963xx/include/mach/io.h linux-4.1.52/arch/arm/mach-bcm963xx/include/mach/io.h --- linux-4.1.52-orig/arch/arm/mach-bcm963xx/include/mach/io.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/mach-bcm963xx/include/mach/io.h 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,43 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +#ifndef __ASM_ARM_ARCH_IO_H +#define __ASM_ARM_ARCH_IO_H __FILE__ + +/* + * This file is required by arch/arm/include/asm/io.h + * and is only used to satisfy obscure compile-time dependencies. + */ + +#define __io(a) __typesafe_io(a) + +#define IO_SPACE_LIMIT 0xffffffff + +#endif +#endif /* CONFIG_BCM_KF_ARM_BCM963XX */ diff -urN linux-4.1.52-orig/arch/arm/mach-bcm963xx/include/mach/irqs.h linux-4.1.52/arch/arm/mach-bcm963xx/include/mach/irqs.h --- linux-4.1.52-orig/arch/arm/mach-bcm963xx/include/mach/irqs.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/mach-bcm963xx/include/mach/irqs.h 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,36 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +/* + * This is the size of a static IRQ handers array + */ +#ifndef NR_IRQS +#define NR_IRQS 256 +#endif +#endif /* CONFIG_BCM_KF_ARM_BCM963XX */ diff -urN linux-4.1.52-orig/arch/arm/mach-bcm963xx/include/mach/memory.h linux-4.1.52/arch/arm/mach-bcm963xx/include/mach/memory.h --- linux-4.1.52-orig/arch/arm/mach-bcm963xx/include/mach/memory.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/mach-bcm963xx/include/mach/memory.h 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,46 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +/* + * Platform memory layout definitions + * + * Note: due to dependencies in common architecture code + * some mappings go in other header files. + */ +#ifndef __ASM_ARCH_MEMORY_H +#define __ASM_ARCH_MEMORY_H + +/* + * Main memory base address and size + * are defined from the board-level configuration file + */ + + +#endif +#endif /* CONFIG_BCM_KF_ARM_BCM963XX */ diff -urN linux-4.1.52-orig/arch/arm/mach-bcm963xx/include/mach/smp.h linux-4.1.52/arch/arm/mach-bcm963xx/include/mach/smp.h --- linux-4.1.52-orig/arch/arm/mach-bcm963xx/include/mach/smp.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/mach-bcm963xx/include/mach/smp.h 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,56 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +/* + * derived from arch/arm/mach-realview/include/mach/smp.h + * + * This file is required from common architecture code, + * in arch/arm/include/asm/smp.h + */ + +#ifndef __ASM_ARCH_SMP_H +#define __ASM_ARCH_SMP_H __FILE__ + +extern void platform_secondary_startup(void); + +int platform_cpu_disable(unsigned int cpu); +void platform_cpu_die(unsigned int cpu); +int platform_cpu_kill(unsigned int cpu); + +/* Used in hotplug.c */ +#define hard_smp_processor_id() \ + ({ \ + unsigned int cpunum; \ + __asm__("mrc p15, 0, %0, c0, c0, 5" \ + : "=r" (cpunum)); \ + cpunum &= 0x0F; \ + }) + +#endif /* __ASM_ARCH_SMP_H */ +#endif /* CONFIG_BCM_KF_ARM_BCM963XX */ diff -urN linux-4.1.52-orig/arch/arm/mach-bcm963xx/include/mach/system.h linux-4.1.52/arch/arm/mach-bcm963xx/include/mach/system.h --- linux-4.1.52-orig/arch/arm/mach-bcm963xx/include/mach/system.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/mach-bcm963xx/include/mach/system.h 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,36 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +#ifndef __ASM_ARCH_SYSTEM_H +#define __ASM_ARCH_SYSTEM_H + +#include + +#endif +#endif /* CONFIG_BCM_KF_ARM_BCM963XX */ diff -urN linux-4.1.52-orig/arch/arm/mach-bcm963xx/include/mach/timex.h linux-4.1.52/arch/arm/mach-bcm963xx/include/mach/timex.h --- linux-4.1.52-orig/arch/arm/mach-bcm963xx/include/mach/timex.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/mach-bcm963xx/include/mach/timex.h 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,44 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +/* + * This file exists to satisfy compile-time dependency from: + * arch/arm/include/asm/timex.h + * It must be a value known at compile time for + * but its value is never used in the resulting code. + * If "get_cycles()" inline function in is rewritten, + * then in combination with this constant it could be used to measure + * microsecond elapsed time using the global timer clock-source. + * -LR + */ + +/* FIXME!! when knowing the real clock tick rate */ +#define CLOCK_TICK_RATE (1000000) + +#endif /* CONFIG_BCM_KF_ARM_BCM963XX */ diff -urN linux-4.1.52-orig/arch/arm/mach-bcm963xx/include/mach/uncompress.h linux-4.1.52/arch/arm/mach-bcm963xx/include/mach/uncompress.h --- linux-4.1.52-orig/arch/arm/mach-bcm963xx/include/mach/uncompress.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/mach-bcm963xx/include/mach/uncompress.h 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,85 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +#include +#include + +#define AMBA_UART_DR(base) (*(volatile unsigned char *)((base) + 0x00)) +#define AMBA_UART_LCRH(base) (*(volatile unsigned char *)((base) + 0x2c)) +#define AMBA_UART_CR(base) (*(volatile unsigned char *)((base) + 0x30)) +#define AMBA_UART_FR(base) (*(volatile unsigned char *)((base) + 0x18)) + +#if defined(ARM_UART_PHYS_BASE) && (CONFIG_DEBUG_UART_ADDR==ARM_UART_PHYS_BASE) +/* + * This does not append a newline + */ +static inline void putc(int c) +{ + unsigned long base = CONFIG_DEBUG_UART_ADDR; + + while (AMBA_UART_FR(base) & (1 << 5)) + barrier(); + + AMBA_UART_DR(base) = c; +} + +static inline void flush(void) +{ + unsigned long base = CONFIG_DEBUG_UART_ADDR; + + while (AMBA_UART_FR(base) & (1 << 3)) + barrier(); +} +#elif CONFIG_DEBUG_UART_ADDR==UART0_PHYS_BASE + +#define PERIPH_UART_DATA(base) (*(volatile unsigned char *)((base) + 0x14)) +#define PERIPH_UART_STS(base) (*(volatile unsigned long *)((base) + 0x10)) + +static inline void putc(int c) +{ +#if 0 /* FIXME! TXFIFOTHOLD might not work */ + while (!(PERIPH_UART_STS(UART0_PHYS_BASE) & TXFIFOTHOLD)) + barrier(); +#endif + PERIPH_UART_DATA(UART0_PHYS_BASE) = (unsigned char)c; +} + +static inline void flush(void) +{ + while (!(PERIPH_UART_STS(UART0_PHYS_BASE) & TXFIFOEMT)) + barrier(); +} +#endif + +/* + * nothing to do + */ +#define arch_decomp_setup() +#define arch_decomp_wdog() +#endif /* CONFIG_BCM_KF_ARM_BCM963XX */ diff -urN linux-4.1.52-orig/arch/arm/mach-bcm963xx/include/mach/vmalloc.h linux-4.1.52/arch/arm/mach-bcm963xx/include/mach/vmalloc.h --- linux-4.1.52-orig/arch/arm/mach-bcm963xx/include/mach/vmalloc.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/mach-bcm963xx/include/mach/vmalloc.h 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,38 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +/* + * This file is included from architecture common code in: + * arch/arm/include/asm/pgtable.h + */ + +#ifndef VMALLOC_END +#define VMALLOC_END (PAGE_OFFSET + 0x30000000) +#endif +#endif /* CONFIG_BCM_KF_ARM_BCM963XX */ diff -urN linux-4.1.52-orig/arch/arm/mach-bcm963xx/Makefile linux-4.1.52/arch/arm/mach-bcm963xx/Makefile --- linux-4.1.52-orig/arch/arm/mach-bcm963xx/Makefile 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/mach-bcm963xx/Makefile 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,36 @@ +ifdef BCM_KF # defined(CONFIG_BCM_KF_ARM_BCM963XX) +# +# Board-level BSP +# Preliminary +# + +ifeq ($(strip $(CONFIG_BCM96846)),) +obj-$(CONFIG_MACH_BCM963138) += board_963xx.o +obj-$(CONFIG_MACH_BCM963148) += board_963xx.o + +SRCBASE := $(TOPDIR) +EXTRA_CFLAGS += -I$(INC_BRCMBOARDPARMS_PATH)/$(BRCM_BOARD) -I$(SRCBASE)/include -I$(INC_BRCMDRIVER_PUB_PATH)/$(BRCM_BOARD) -I$(INC_BRCMSHARED_PUB_PATH)/$(BRCM_BOARD) +#EXTRA_CFLAGS += -I$(INC_ADSLDRV_PATH) -DDBG +EXTRA_CFLAGS += -I$(INC_ADSLDRV_PATH) +EXTRA_CFLAGS += -g +EXTRA_CFLAGS += $(BRCM_WERROR_CFLAGS) + +ifeq ($(strip $(BRCM_PHY_GFASTCOMBO)),y) +EXTRA_CFLAGS += -DCONFIG_BCM_DSL_GFASTCOMBO +endif + +ifeq "$(ADSL)" "ANNEX_B" +EXTRA_CFLAGS += -DADSL_ANNEXB +endif + +ifneq ($(strip $(BUILD_SWMDK)),) +EXTRA_CFLAGS += -DSUPPORT_SWMDK +endif +ifneq ($(strip $(CONFIG_MACH_BCM963138)),) +EXTRA_CFLAGS += -I$(INC_BRCMSHARED_PUB_PATH)/pmc +endif +else +obj-y += board_6846.o +endif +endif # BCM_KF # defined(CONFIG_BCM_KF_ARM_BCM963XX) + diff -urN linux-4.1.52-orig/arch/arm/mach-bcm963xx/Makefile.boot linux-4.1.52/arch/arm/mach-bcm963xx/Makefile.boot --- linux-4.1.52-orig/arch/arm/mach-bcm963xx/Makefile.boot 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/mach-bcm963xx/Makefile.boot 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,10 @@ +ifdef BCM_KF # defined(CONFIG_BCM_KF_ARM_BCM963XX) +# +# SDRAM location for decompressor +# +zreladdr-y := $(CONFIG_BOARD_ZRELADDR) +# +# Where boot monitor is expected to leave parameters +# +params_phys-y := $(CONFIG_BOARD_PARAMS_PHYS) +endif # BCM_KF # defined(CONFIG_BCM_KF_ARM_BCM963XX) \ No newline at end of file diff -urN linux-4.1.52-orig/arch/arm/mach-bcm963xx/prom.c linux-4.1.52/arch/arm/mach-bcm963xx/prom.c --- linux-4.1.52-orig/arch/arm/mach-bcm963xx/prom.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/mach-bcm963xx/prom.c 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,257 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + +// FIXME!! the following ifdef will be redesigned for ARM, at this point, +// it is commented out for compilation purpose. ARM has different way of +// setting up boot param rather than using PROM library. +/* + * prom.c: PROM library initialization code. + */ +#include +#include +#include +#include +#include +#include +#if 0 +#include +#include +#include +#endif + +#include +#include +#include +#include + +// FIXME!! I am just putting this piece of code here. +// It will be needed; however, as for now, we don't use it. +extern int do_syslog(int, char *, int); + +unsigned char g_blparms_buf[1024]; + +#if 0 +static void __init create_cmdline(char *cmdline); +#endif +UINT32 __init calculateCpuSpeed(void); +void __init retrieve_boot_loader_parameters(void); + +/* -------------------------------------------------------------------------- + Name: prom_init + -------------------------------------------------------------------------- */ + +void __init prom_init(void) +{ +#if 0 + int argc = fw_arg0; + u32 *argv = (u32 *)CKSEG0ADDR(fw_arg1); + int i; + + kerSysEarlyFlashInit(); + + // too early in bootup sequence to acquire spinlock, not needed anyways + // only the kernel is running at this point + kerSysNvRamGetBoardIdLocked(promBoardIdStr); + printk( "%s prom init\n", promBoardIdStr ); + + PERF->IrqControl[0].IrqMask=0; + + arcs_cmdline[0] = '\0'; + + create_cmdline(arcs_cmdline); + + strcat(arcs_cmdline, " "); + + for (i = 1; i < argc; i++) { + strcat(arcs_cmdline, (char *)CKSEG0ADDR(argv[i])); + if (i < (argc - 1)) + strcat(arcs_cmdline, " "); + } + + + /* Count register increments every other clock */ + mips_hpt_frequency = calculateCpuSpeed() / 2; + + retrieve_boot_loader_parameters(); +#endif +} + + +/* -------------------------------------------------------------------------- + Name: prom_free_prom_memory +Abstract: + -------------------------------------------------------------------------- */ +void __init prom_free_prom_memory(void) +{ + +} + +#if defined(CONFIG_ROOT_NFS) && defined(SUPPORT_SWMDK) + /* We can't use gendefconfig to automatically fix this, so instead we will + raise an error here */ + #error "Kernel cannot be configured for both SWITCHMDK and NFS." +#endif + +#define HEXDIGIT(d) ((d >= '0' && d <= '9') ? (d - '0') : ((d | 0x20) - 'W')) +#define HEXBYTE(b) (HEXDIGIT((b)[0]) << 4) + HEXDIGIT((b)[1]) + +#ifndef CONFIG_ROOT_NFS_DIR +#define CONFIG_ROOT_NFS_DIR "h:/" +#endif + +#ifdef CONFIG_BLK_DEV_RAM_SIZE +#define RAMDISK_SIZE CONFIG_BLK_DEV_RAM_SIZE +#else +#define RAMDISK_SIZE 0x800000 +#endif + +/* + * This function reads in a line that looks something like this from NvRam: + * + * CFE bootline=bcmEnet(0,0)host:vmlinux e=192.169.0.100:ffffff00 h=192.169.0.1 + * + * and retuns in the cmdline parameter based on the boot_type that CFE sets up. + * + * for boot from flash, it will use the definition in CONFIG_ROOT_FLASHFS + * + * for boot from NFS, it will look like below: + * CONFIG_CMDLINE="root=/dev/nfs nfsroot=192.168.0.1:/opt/targets/96345R/fs + * ip=192.168.0.100:192.168.0.1::255.255.255.0::eth0:off rw" + * + * for boot from tftp, it will look like below: + * CONFIG_CMDLINE="root=/dev/ram rw rd_start=0x81000000 rd_size=0x1800000" + */ +#if 0 +static void __init create_cmdline(char *cmdline) +{ + char boot_type = '\0', mask[16] = ""; + char bootline[NVRAM_BOOTLINE_LEN] = ""; + char *localip = NULL, *hostip = NULL, *p = bootline, *rdaddr = NULL; + + /* + * too early in bootup sequence to acquire spinlock, not needed anyways + * only the kernel is running at this point + */ + kerSysNvRamGetBootlineLocked(bootline); + + while (*p) { + if (p[0] == 'e' && p[1] == '=') { + /* Found local ip address */ + p += 2; + localip = p; + while (*p && *p != ' ' && *p != ':') + p++; + if (*p == ':') { + /* Found network mask (eg FFFFFF00 */ + *p++ = '\0'; + sprintf(mask, "%u.%u.%u.%u", HEXBYTE(p), + HEXBYTE(p + 2), + HEXBYTE(p + 4), HEXBYTE(p + 6)); + p += 4; + } else if (*p == ' ') + *p++ = '\0'; + } else if (p[0] == 'h' && p[1] == '=') { + /* Found host ip address */ + p += 2; + hostip = p; + while (*p && *p != ' ') + p++; + if (*p == ' ') + *p++ = '\0'; + } else if (p[0] == 'r' && p[1] == '=') { + /* Found boot type */ + p += 2; + boot_type = *p; + while (*p && *p != ' ') + p++; + if (*p == ' ') + *p++ = '\0'; + } else if (p[0] == 'a' && p[1] == '=') { + p += 2; + rdaddr = p; + while (*p && *p != ' ') + p++; + if (*p == ' ') + *p++ = '\0'; + } else + p++; + } + + if (boot_type == 'h' && localip && hostip) { + /* Boot from NFS with proper IP addresses */ + sprintf(cmdline, "root=/dev/nfs nfsroot=%s:" CONFIG_ROOT_NFS_DIR + " ip=%s:%s::%s::eth0:off rw", + hostip, localip, hostip, mask); + } else if (boot_type == 'c') { + /* boot from tftp */ + sprintf(cmdline, "root=/dev/ram0 ro rd_start=%s rd_size=0x%x", + rdaddr, RAMDISK_SIZE << 10); + } else { + /* go with the default, boot from flash */ +#ifdef CONFIG_ROOT_FLASHFS + strcpy(cmdline, CONFIG_ROOT_FLASHFS); +#endif + } +} +#endif + +/* Retrieve a buffer of paramters passed by the boot loader. Functions in + * board.c can return requested parameter values to a calling Linux function. + */ +void __init retrieve_boot_loader_parameters(void) +{ +#if 0 + extern unsigned char _text; + unsigned long blparms_magic = *(unsigned long *) (&_text - 8); + unsigned long blparms_buf = *(unsigned long *) (&_text - 4); + unsigned char *src = (unsigned char *) blparms_buf; + unsigned char *dst = g_blparms_buf; + + if( blparms_magic != BLPARMS_MAGIC ) + { + /* Subtract four more bytes for NAND flash images. */ + blparms_magic = *(unsigned long *) (&_text - 12); + blparms_buf = *(unsigned long *) (&_text - 8); + src = (unsigned char *) blparms_buf; + } + + if( blparms_magic == BLPARMS_MAGIC ) + { + do + { + *dst++ = *src++; + } while( (src[0] != '\0' || src[1] != '\0') && + (unsigned long) (dst - g_blparms_buf) < sizeof(g_blparms_buf) - 2); + } + + dst[0] = dst[1] = '\0'; +#endif +} +#endif /* CONFIG_BCM_KF_ARM_BCM963XX */ diff -urN linux-4.1.52-orig/arch/arm/mach-mmp/pm-pxa910.c linux-4.1.52/arch/arm/mach-mmp/pm-pxa910.c --- linux-4.1.52-orig/arch/arm/mach-mmp/pm-pxa910.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/mach-mmp/pm-pxa910.c 2020-02-07 01:47:53.000000000 +0800 @@ -18,6 +18,9 @@ #include #include #include +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +#include +#endif #include #include #include diff -urN linux-4.1.52-orig/arch/arm/mach-prima2/pm.c linux-4.1.52/arch/arm/mach-prima2/pm.c --- linux-4.1.52-orig/arch/arm/mach-prima2/pm.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/mach-prima2/pm.c 2020-02-07 01:47:53.000000000 +0800 @@ -16,6 +16,9 @@ #include #include #include +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +#include +#endif #include #include diff -urN linux-4.1.52-orig/arch/arm/mach-ux500/cache-l2x0.c linux-4.1.52/arch/arm/mach-ux500/cache-l2x0.c --- linux-4.1.52-orig/arch/arm/mach-ux500/cache-l2x0.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/mach-ux500/cache-l2x0.c 2020-02-07 01:47:53.000000000 +0800 @@ -7,6 +7,9 @@ #include #include +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +#include +#endif #include #include "db8500-regs.h" diff -urN linux-4.1.52-orig/arch/arm/Makefile linux-4.1.52/arch/arm/Makefile --- linux-4.1.52-orig/arch/arm/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/Makefile 2020-02-07 01:47:53.000000000 +0800 @@ -19,6 +19,12 @@ LDFLAGS_MODULE += --be8 endif +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_BACKPORTS) +ifeq ($(CONFIG_ARM_MODULE_PLTS),y) +LDFLAGS_MODULE += -T $(srctree)/arch/arm/kernel/module.lds +endif + +endif # BCM_KF # defined(CONFIG_BCM_KF_MISC_BACKPORTS) OBJCOPYFLAGS :=-O binary -R .comment -S GZFLAGS :=-9 #KBUILD_CFLAGS +=-pipe @@ -129,6 +135,25 @@ 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_AFLAGS +=$(CFLAGS_ABI) $(AFLAGS_ISA) $(arch-y) $(tune-y) -include asm/unified.h -msoft-float +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) +# Copy from bigisland's Makefile, but most likely it does apply to BCM63138 +# ifeq ($(CONFIG_VFP),y) +# ifeq ($(CONFIG_VFPv3),y) +# # v3 vfp +# KBUILD_CFLAGS +=-mfpu=vfp3 -mfloat-abi=softfp +# KBUILD_AFLAGS +=-mfpu=vfp3 -mfloat-abi=softfp +# else +# #non-v3 vfp +# KBUILD_CFLAGS +=-mfloat-abi=softfp +# KBUILD_AFLAGS +=-mfloat-abi=softfp +# endif +# else +# #no vfp +# KBUILD_CFLAGS +=-mfloat-abi=soft +# KBUILD_AFLAGS +=-mfloat-abi=soft +# endif +endif # BCM_KF # CONFIG_BCM_KF_MISC_MAKEFILE + CHECKFLAGS += -D__arm__ #Default value @@ -147,6 +172,11 @@ textofs-$(CONFIG_ARCH_MSM8X60) := 0x00208000 textofs-$(CONFIG_ARCH_MSM8960) := 0x00208000 textofs-$(CONFIG_ARCH_AXXIA) := 0x00308000 +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) +textofs-$(CONFIG_ARCH_BCM63XX) := 0x00018000 +textofs-$(CONFIG_ARCH_BCM947XXX) := 0x00018000 +textofs-$(CONFIG_ARCH_MULTI_V7) := 0x00018000 +endif # BCM_KF # Machine directory name. This list is sorted alphanumerically # by CONFIG_* macro name. @@ -154,6 +184,9 @@ machine-$(CONFIG_ARCH_AT91) += at91 machine-$(CONFIG_ARCH_AXXIA) += axxia machine-$(CONFIG_ARCH_BCM) += bcm +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) +machine-$(CONFIG_ARCH_BCM63XX) := bcm963xx +endif # BCM_KF machine-$(CONFIG_ARCH_BERLIN) += berlin machine-$(CONFIG_ARCH_CLPS711X) += clps711x machine-$(CONFIG_ARCH_CNS3XXX) += cns3xxx @@ -217,6 +250,12 @@ # Platform directory name. This list is sorted alphanumerically # by CONFIG_* macro name. +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) +plat-$(CONFIG_ARCH_BCM63XX) := bcm63xx +ifdef CONFIG_BCM96878 +plat-y := bcm63xx +endif +endif # BCM_KF plat-$(CONFIG_ARCH_EXYNOS) += samsung plat-$(CONFIG_ARCH_OMAP) += omap plat-$(CONFIG_ARCH_S3C64XX) += samsung diff -urN linux-4.1.52-orig/arch/arm/mm/alignment.c linux-4.1.52/arch/arm/mm/alignment.c --- linux-4.1.52-orig/arch/arm/mm/alignment.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/mm/alignment.c 2020-02-07 01:47:53.000000000 +0800 @@ -759,6 +759,9 @@ int isize = 4; int thumb2_32b = 0; +#if defined(CONFIG_BCM_KF_KERN_WARNING) + offset.un = 0; +#endif /* CONFIG_BCM_KF_KERN_WARNING */ if (interrupts_enabled(regs)) local_irq_enable(); diff -urN linux-4.1.52-orig/arch/arm/mm/cache-l2x0.c linux-4.1.52/arch/arm/mm/cache-l2x0.c --- linux-4.1.52-orig/arch/arm/mm/cache-l2x0.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/mm/cache-l2x0.c 2020-02-07 01:47:53.000000000 +0800 @@ -166,6 +166,20 @@ l2c_enable(l2x0_base, l2x0_saved_regs.aux_ctrl, l2x0_data->num_lock); } +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +static unsigned long gb_flags; + +static void l2c_spin_lock_irqsave(void) +{ + raw_spin_lock_irqsave(&l2x0_lock, gb_flags); +} + +static void l2c_spin_unlock_irqrestore(void) +{ + raw_spin_unlock_irqrestore(&l2x0_lock, gb_flags); +} +#endif + /* * L2C-210 specific code. * @@ -246,6 +260,23 @@ __l2c210_cache_sync(l2x0_base); } + +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* no debug write workaround is needed for these function because lc310 controller in + 63138 is r3p3 and does not have these erratum */ +static void l2c210_flush_line_no_lock(unsigned long addr) +{ + void __iomem *base = l2x0_base; + writel_relaxed(addr, base + L2X0_CLEAN_INV_LINE_PA); +} + +static void l2c210_inv_line_no_lock(unsigned long addr) +{ + void __iomem *base = l2x0_base; + writel_relaxed(addr, base + L2X0_INV_LINE_PA); +} +#endif + static const struct l2c_init_data l2c210_data __initconst = { .type = "L2C-210", .way_size_0 = SZ_8K, @@ -771,6 +802,13 @@ .disable = l2c310_disable, .sync = l2c210_sync, .resume = l2c310_resume, +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) + .spin_lock_irqsave = l2c_spin_lock_irqsave, + .spin_unlock_irqrestore = l2c_spin_unlock_irqrestore, + .sync_no_lock = l2c210_sync, + .flush_line_no_lock = l2c210_flush_line_no_lock, + .inv_line_no_lock = l2c210_inv_line_no_lock, +#endif }, }; @@ -1219,6 +1257,13 @@ .disable = l2c310_disable, .sync = l2c210_sync, .resume = l2c310_resume, +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) + .spin_lock_irqsave = l2c_spin_lock_irqsave, + .spin_unlock_irqrestore = l2c_spin_unlock_irqrestore, + .sync_no_lock = l2c210_sync, + .flush_line_no_lock = l2c210_flush_line_no_lock, + .inv_line_no_lock = l2c210_inv_line_no_lock, +#endif }, }; diff -urN linux-4.1.52-orig/arch/arm/mm/cache-v7.S linux-4.1.52/arch/arm/mm/cache-v7.S --- linux-4.1.52-orig/arch/arm/mm/cache-v7.S 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/mm/cache-v7.S 2020-02-07 01:47:53.000000000 +0800 @@ -335,8 +335,12 @@ add r0, r0, r2 cmp r0, r1 blo 1b +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_B15_MEGA_BARRIER) + issue_mega_barrier +#else dsb st - ret lr +#endif + ret lr ENDPROC(v7_flush_kern_dcache_area) /* @@ -390,7 +394,11 @@ add r0, r0, r2 cmp r0, r1 blo 1b +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_B15_MEGA_BARRIER) + issue_mega_barrier +#else dsb st +#endif ret lr ENDPROC(v7_dma_clean_range) @@ -412,7 +420,11 @@ add r0, r0, r2 cmp r0, r1 blo 1b +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_B15_MEGA_BARRIER) + issue_mega_barrier +#else dsb st +#endif ret lr ENDPROC(v7_dma_flush_range) diff -urN linux-4.1.52-orig/arch/arm/mm/dma-mapping.c linux-4.1.52/arch/arm/mm/dma-mapping.c --- linux-4.1.52-orig/arch/arm/mm/dma-mapping.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/mm/dma-mapping.c 2020-02-07 01:47:53.000000000 +0800 @@ -371,7 +371,11 @@ goto destroy_genpool; gen_pool_set_algo(atomic_pool, +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) + gen_pool_best_fit, +#else gen_pool_first_fit_order_align, +#endif (void *)PAGE_SHIFT); pr_info("DMA: preallocated %zd KiB pool for atomic coherent allocations\n", atomic_pool_size / 1024); @@ -594,6 +598,14 @@ #endif /* CONFIG_MMU */ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +static void dmac_flush_area(const void * addr, size_t len, int dir) +{ + dmac_flush_range(addr, addr + len); +} + +#endif + static void *__alloc_simple_buffer(struct device *dev, size_t size, gfp_t gfp, struct page **ret_page) { @@ -629,7 +641,11 @@ if (!mask) return NULL; +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_ZONE_ACP) + if ((mask < 0xffffffffULL) && !(gfp & GFP_ACP)) +#else if (mask < 0xffffffffULL) +#endif gfp |= GFP_DMA; /* @@ -900,6 +916,29 @@ } } +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +void ___dma_page_cpu_to_dev_flush(struct page *page, unsigned long off, + size_t size, enum dma_data_direction dir) +{ +#ifdef CONFIG_OUTER_CACHE + unsigned long paddr; + + dma_cache_maint_page(page, off, size, dir, dmac_map_area); + + paddr = page_to_phys(page) + off; + if (dir == DMA_FROM_DEVICE) { + outer_inv_range(paddr, paddr + size); + } else { + outer_flush_range(paddr, paddr + size); + } +#endif + + dma_cache_maint_page(page, off, size, dir, &dmac_flush_area); +} +EXPORT_SYMBOL(___dma_page_cpu_to_dev_flush); + +#endif + /** * arm_dma_map_sg - map a set of SG buffers for streaming mode DMA * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices @@ -2146,6 +2185,9 @@ set_dma_ops(dev, dma_ops); } +#if defined(CONFIG_BCM_KF_GLB_COHERENCY) && defined(CONFIG_BCM_GLB_COHERENCY) +EXPORT_SYMBOL(arch_setup_dma_ops); +#endif void arch_teardown_dma_ops(struct device *dev) { diff -urN linux-4.1.52-orig/arch/arm/mm/fault.c linux-4.1.52/arch/arm/mm/fault.c --- linux-4.1.52-orig/arch/arm/mm/fault.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/mm/fault.c 2020-02-07 01:47:53.000000000 +0800 @@ -163,6 +163,11 @@ { struct siginfo si; +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + if (addr > TASK_SIZE) + harden_branch_predictor(); +#endif + #ifdef CONFIG_DEBUG_USER if (((user_debug & UDBG_SEGV) && (sig == SIGSEGV)) || ((user_debug & UDBG_BUS) && (sig == SIGBUS))) { diff -urN linux-4.1.52-orig/arch/arm/mm/flush.c linux-4.1.52/arch/arm/mm/flush.c --- linux-4.1.52-orig/arch/arm/mm/flush.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/mm/flush.c 2020-02-07 01:47:53.000000000 +0800 @@ -21,6 +21,21 @@ #include "mm.h" +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +#ifdef CONFIG_ARM_HEAVY_MB +void arm_heavy_mb(void) +{ +#ifdef CONFIG_OUTER_CACHE_SYNC + if (outer_cache.sync) + outer_cache.sync(); +#endif +} +#else +void arm_heavy_mb(void) {} +#endif +EXPORT_SYMBOL(arm_heavy_mb); +#endif /* CONFIG_BCM_KF_SPECTRE_PATCH && CONFIG_BCM_SPECTRE_PATCH_ENABLE */ + #ifdef CONFIG_CPU_CACHE_VIPT static void flush_pfn_alias(unsigned long pfn, unsigned long vaddr) diff -urN linux-4.1.52-orig/arch/arm/mm/init.c linux-4.1.52/arch/arm/mm/init.c --- linux-4.1.52-orig/arch/arm/mm/init.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/mm/init.c 2020-02-07 01:47:53.000000000 +0800 @@ -121,6 +121,30 @@ } #endif +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_ZONE_ACP) +static void __init arm_adjust_acp_zone(unsigned long *size, unsigned long *hole) +{ + unsigned long total_size = size[ZONE_NORMAL]; +#ifndef CONFIG_ZONE_DMA + total_size += size[0]; +#endif + + if (total_size <= (CONFIG_BCM_ACP_MEM_SIZE << (20 - PAGE_SHIFT))) + return; + +#ifdef CONFIG_ZONE_DMA + size[ZONE_NORMAL] -= CONFIG_BCM_ACP_MEM_SIZE << (20 - PAGE_SHIFT); +#else + size[ZONE_NORMAL] = size[0] - (CONFIG_BCM_ACP_MEM_SIZE << (20 - PAGE_SHIFT)); +#endif + size[ZONE_ACP] = CONFIG_BCM_ACP_MEM_SIZE << (20 - PAGE_SHIFT); +#ifndef CONFIG_ZONE_DMA + hole[ZONE_NORMAL] = hole[0]; +#endif + hole[ZONE_ACP] = 0; +} +#endif + void __init setup_dma_zone(const struct machine_desc *mdesc) { #ifdef CONFIG_ZONE_DMA @@ -185,6 +209,10 @@ arm_dma_zone_size >> PAGE_SHIFT); #endif +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_ZONE_ACP) + arm_adjust_acp_zone(zone_size, zhole_size); +#endif + free_area_init_node(0, zone_size, min, zhole_size); } diff -urN linux-4.1.52-orig/arch/arm/mm/Kconfig linux-4.1.52/arch/arm/mm/Kconfig --- linux-4.1.52-orig/arch/arm/mm/Kconfig 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/mm/Kconfig 2020-02-07 01:47:53.000000000 +0800 @@ -384,6 +384,7 @@ select CPU_TLB_V6 if MMU # ARMv7 +if CONFIG_BCM_KF_SPECTRE_PATCH config CPU_V7 bool "Support ARM V7 processor" if (!ARCH_MULTIPLATFORM || ARCH_MULTI_V7) && (ARCH_INTEGRATOR || MACH_REALVIEW_EB || MACH_REALVIEW_PBX) select CPU_32v6K @@ -396,7 +397,25 @@ select CPU_CP15_MPU if !MMU select CPU_HAS_ASID if MMU select CPU_PABRT_V7 + select CPU_SPECTRE if MMU select CPU_TLB_V7 if MMU +endif + +if !CONFIG_BCM_KF_SPECTRE_PATCH +config CPU_V7 + bool "Support ARM V7 processor" if (!ARCH_MULTIPLATFORM || ARCH_MULTI_V7) && (ARCH_INTEGRATOR || MACH_REALVIEW_EB || MACH_REALVIEW_PBX) + select CPU_32v6K + select CPU_32v7 + select CPU_ABRT_EV7 + select CPU_CACHE_V7 + select CPU_CACHE_VIPT + select CPU_COPY_V6 if MMU + select CPU_CP15_MMU if MMU + select CPU_CP15_MPU if !MMU + select CPU_HAS_ASID if MMU + select CPU_PABRT_V7 + select CPU_TLB_V7 if MMU +endif # ARMv7M config CPU_V7M @@ -777,6 +796,30 @@ help Say Y here to disable branch prediction. If unsure, say N. +if CONFIG_BCM_KF_SPECTRE_PATCH +config CPU_SPECTRE + bool + +config HARDEN_BRANCH_PREDICTOR + bool "Harden the branch predictor against aliasing attacks" if EXPERT + depends on CPU_SPECTRE + default n + help + Speculation attacks against some high-performance processors rely + on being able to manipulate the branch predictor for a victim + context by executing aliasing branches in the attacker context. + Such attacks can be partially mitigated against by clearing + internal branch predictor state and limiting the prediction + logic in some situations. + + This config option will take CPU-specific actions to harden + the branch predictor against aliasing attacks and may rely on + specific instruction sequences or control bits being set by + the system firmware. + + If unsure, say Y. +endif + config TLS_REG_EMUL bool select NEED_KUSER_HELPERS @@ -861,11 +904,22 @@ config OUTER_CACHE bool +if CONFIG_BCM_KF_SPECTRE_PATCH +config OUTER_CACHE_SYNC + bool + select ARM_HEAVY_MB + help + The outer cache has a outer_cache_fns.sync function pointer + that can be used to drain the write buffer of the outer cache. +endif + +if !CONFIG_BCM_KF_SPECTRE_PATCH config OUTER_CACHE_SYNC bool help The outer cache has a outer_cache_fns.sync function pointer that can be used to drain the write buffer of the outer cache. +endif config CACHE_FEROCEON_L2 bool "Enable the Feroceon L2 cache controller" @@ -973,11 +1027,21 @@ help This option enables the L2 cache on XScale3. +if !BCM_KF_ARM_BCM963XX config ARM_L1_CACHE_SHIFT_6 bool default y if CPU_V7 help Setting ARM L1 cache line size to 64 Bytes. +endif + +if BCM_KF_ARM_BCM963XX +config ARM_L1_CACHE_SHIFT_6 + bool + default y if CPU_V7 && !ARCH_BCM63XX + help + Setting ARM L1 cache line size to 64 Bytes. +endif config ARM_L1_CACHE_SHIFT int @@ -1011,6 +1075,11 @@ This option allows the use of custom mandatory barriers included via the mach/barriers.h file. +if CONFIG_BCM_KF_SPECTRE_PATCH +config ARM_HEAVY_MB + bool +endif + config ARCH_SUPPORTS_BIG_ENDIAN bool help diff -urN linux-4.1.52-orig/arch/arm/mm/Makefile linux-4.1.52/arch/arm/mm/Makefile --- linux-4.1.52-orig/arch/arm/mm/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/mm/Makefile 2020-02-07 01:47:53.000000000 +0800 @@ -89,7 +89,9 @@ obj-$(CONFIG_CPU_FEROCEON) += proc-feroceon.o obj-$(CONFIG_CPU_V6) += proc-v6.o obj-$(CONFIG_CPU_V6K) += proc-v6.o -obj-$(CONFIG_CPU_V7) += proc-v7.o +ifdef BCM_KF # defined (CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +obj-$(CONFIG_CPU_V7) += proc-v7.o proc-v7-bugs.o +endif obj-$(CONFIG_CPU_V7M) += proc-v7m.o AFLAGS_proc-v6.o :=-Wa,-march=armv6 diff -urN linux-4.1.52-orig/arch/arm/mm/mmu.c linux-4.1.52/arch/arm/mm/mmu.c --- linux-4.1.52-orig/arch/arm/mm/mmu.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/mm/mmu.c 2020-02-07 01:47:53.000000000 +0800 @@ -281,6 +281,15 @@ .prot_sect = PMD_TYPE_SECT | PMD_SECT_XN, .domain = DOMAIN_KERNEL, }, +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_PLAT_BCM63XX_ACP) + [MT_DEVICE_NONSECURED] = { + .prot_pte = PROT_PTE_DEVICE | L_PTE_MT_DEV_SHARED | + L_PTE_SHARED, + .prot_l1 = PMD_TYPE_TABLE, + .prot_sect = PROT_SECT_DEVICE | PMD_SECT_S | PMD_SECT_NS, + .domain = DOMAIN_IO, + }, +#endif /* CONFIG_BCM_KF_ARM_BCM963XX && CONFIG_PLAT_BCM63XX_ACP */ #ifndef CONFIG_ARM_LPAE [MT_MINICLEAN] = { .prot_sect = PMD_TYPE_SECT | PMD_SECT_XN | PMD_SECT_MINICACHE, @@ -349,6 +358,14 @@ .prot_l1 = PMD_TYPE_TABLE, .domain = DOMAIN_KERNEL, }, +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_PLAT_BCM63XX_ACP) + [MT_MEMORY_NONSECURED] = { + .prot_pte = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY, + .prot_l1 = PMD_TYPE_TABLE, + .prot_sect = PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_NS, + .domain = DOMAIN_KERNEL, + }, +#endif /* CONFIG_BCM_KF_ARM_BCM963XX && CONFIG_PLAT_BCM63XX_ACP */ }; const struct mem_type *get_mem_type(unsigned int type) @@ -570,6 +587,10 @@ mem_types[MT_MEMORY_DMA_READY].prot_pte |= L_PTE_SHARED; mem_types[MT_MEMORY_RWX_NONCACHED].prot_sect |= PMD_SECT_S; mem_types[MT_MEMORY_RWX_NONCACHED].prot_pte |= L_PTE_SHARED; +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_PLAT_BCM63XX_ACP) + mem_types[MT_MEMORY_NONSECURED].prot_sect |= PMD_SECT_S; + mem_types[MT_MEMORY_NONSECURED].prot_pte |= L_PTE_SHARED; +#endif /* CONFIG_BCM_KF_ARM_BCM963XX && CONFIG_PLAT_BCM63XX_ACP */ } } @@ -633,6 +654,10 @@ mem_types[MT_MEMORY_DMA_READY].prot_pte |= kern_pgprot; mem_types[MT_MEMORY_RWX_NONCACHED].prot_sect |= ecc_mask; mem_types[MT_ROM].prot_sect |= cp->pmd; +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_PLAT_BCM63XX_ACP) + mem_types[MT_MEMORY_NONSECURED].prot_sect |= ecc_mask | cp->pmd; + mem_types[MT_MEMORY_NONSECURED].prot_pte |= kern_pgprot; +#endif /* CONFIG_BCM_KF_ARM_BCM963XX && CONFIG_PLAT_BCM63XX_ACP */ switch (cp->pmd) { case PMD_SECT_WT: @@ -841,7 +866,11 @@ * offsets, and we take full advantage of sections and * supersections. */ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +void __init create_mapping(struct map_desc *md) +#else static void __init create_mapping(struct map_desc *md) +#endif { unsigned long addr, length, end; phys_addr_t phys; diff -urN linux-4.1.52-orig/arch/arm/mm/proc-macros.S linux-4.1.52/arch/arm/mm/proc-macros.S --- linux-4.1.52-orig/arch/arm/mm/proc-macros.S 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/mm/proc-macros.S 2020-02-07 01:47:53.000000000 +0800 @@ -258,13 +258,20 @@ mcr p15, 0, ip, c7, c10, 4 @ data write barrier .endm +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +.macro define_processor_functions name:req, dabort:req, pabort:req, nommu=0, suspend=0, bugs=0 +#else .macro define_processor_functions name:req, dabort:req, pabort:req, nommu=0, suspend=0 +#endif .type \name\()_processor_functions, #object .align 2 ENTRY(\name\()_processor_functions) .word \dabort .word \pabort .word cpu_\name\()_proc_init +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + .word \bugs +#endif .word cpu_\name\()_proc_fin .word cpu_\name\()_reset .word cpu_\name\()_do_idle @@ -359,3 +366,11 @@ orr \dest, \addr, \dest, lsl #1 @ mask in the region size orr \dest, \dest, \enable .endm + +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_B15_MEGA_BARRIER) +.macro issue_mega_barrier + stmfd sp!,{r3,lr} + blx BcmMegaBarrier + ldmfd sp!,{r3,lr} +.endm +#endif diff -urN linux-4.1.52-orig/arch/arm/mm/proc-v7-2level.S linux-4.1.52/arch/arm/mm/proc-v7-2level.S --- linux-4.1.52-orig/arch/arm/mm/proc-v7-2level.S 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/mm/proc-v7-2level.S 2020-02-07 01:47:53.000000000 +0800 @@ -19,12 +19,23 @@ #define TTB_IRGN_WT ((1 << 0) | (0 << 6)) #define TTB_IRGN_WB ((1 << 0) | (1 << 6)) +#if defined(CONFIG_BCM_KF_GLB_COHERENCY) && defined(CONFIG_BCM_GLB_COHERENCY) +/* PTWs cacheable, inner WB shareable, outer WB shareable */ +#define TTB_FLAGS_UP TTB_IRGN_WB|TTB_S|TTB_RGN_OC_WB +#define PMD_FLAGS_UP PMD_SECT_WB|PMD_SECT_S +#else /* PTWs cacheable, inner WB not shareable, outer WB not shareable */ #define TTB_FLAGS_UP TTB_IRGN_WB|TTB_RGN_OC_WB #define PMD_FLAGS_UP PMD_SECT_WB +#endif +#if defined(CONFIG_BCM_KF_GLB_COHERENCY) && defined(CONFIG_BCM_GLB_COHERENCY) +/* PTWs cacheable, inner WBWA shareable, outer WBWA shareable */ +#define TTB_FLAGS_SMP TTB_IRGN_WBWA|TTB_S|TTB_RGN_OC_WBWA +#else /* PTWs cacheable, inner WBWA shareable, outer WBWA not shareable */ #define TTB_FLAGS_SMP TTB_IRGN_WBWA|TTB_S|TTB_NOS|TTB_RGN_OC_WBWA +#endif #define PMD_FLAGS_SMP PMD_SECT_WBWA|PMD_SECT_S /* @@ -37,6 +48,13 @@ * It is assumed that: * - we are not using split page tables */ +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +/* + * Note that we always need to flush BTAC/BTB if IBE is set + * even on Cortex-A8 revisions not affected by 430973. + * If IBE is not set, the flush BTAC/BTB won't do anything. + */ +#else ENTRY(cpu_ca8_switch_mm) #ifdef CONFIG_MMU mov r2, #0 @@ -44,6 +62,7 @@ mcr p15, 0, r2, c7, c5, 6 @ flush BTAC/BTB #endif #endif +#endif /* CONFIG_BCM_KF_SPECTRE_PATCH && CONFIG_BCM_SPECTRE_PATCH_ENABLE */ ENTRY(cpu_v7_switch_mm) #ifdef CONFIG_MMU mmid r1, r1 @ get mm->context.id @@ -64,8 +83,10 @@ #endif bx lr ENDPROC(cpu_v7_switch_mm) +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +#else ENDPROC(cpu_ca8_switch_mm) - +#endif /* * cpu_v7_set_pte_ext(ptep, pte) * @@ -141,8 +162,15 @@ * NS1 = PRRR[19] = 1 - normal shareable property * NOS = PRRR[24+n] = 1 - not outer shareable */ +#if defined(CONFIG_BCM_KF_GLB_COHERENCY) && defined(CONFIG_BCM_GLB_COHERENCY) +/* All NOS - Outer Shareable, Write-Back, Write-Allocate */ +.equ PRRR, 0xa81a8 +.equ NMRR, 0x555540e0 +#else .equ PRRR, 0xff0a81a8 .equ NMRR, 0x40e040e0 +#endif + /* * Macro for setting up the TTBRx and TTBCR registers. diff -urN linux-4.1.52-orig/arch/arm/mm/proc-v7-bugs.c linux-4.1.52/arch/arm/mm/proc-v7-bugs.c --- linux-4.1.52-orig/arch/arm/mm/proc-v7-bugs.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/mm/proc-v7-bugs.c 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,117 @@ +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +// SPDX-License-Identifier: GPL-2.0 +#include +#include + +#include +#include +#include +#include + +#ifdef CONFIG_HARDEN_BRANCH_PREDICTOR +DEFINE_PER_CPU(harden_branch_predictor_fn_t, harden_branch_predictor_fn); + +extern void cpu_v7_iciallu_switch_mm(phys_addr_t pgd_phys, struct mm_struct *mm); +extern void cpu_v7_bpiall_switch_mm(phys_addr_t pgd_phys, struct mm_struct *mm); + +static void harden_branch_predictor_bpiall(void) +{ + write_sysreg(0, BPIALL); +} + +static void harden_branch_predictor_iciallu(void) +{ + write_sysreg(0, ICIALLU); +} + +static void cpu_v7_spectre_init(void) +{ + const char *spectre_v2_method = NULL; + int cpu = smp_processor_id(); + + if (per_cpu(harden_branch_predictor_fn, cpu)) + return; + + switch (read_cpuid_part()) { + case ARM_CPU_PART_CORTEX_A8: + case ARM_CPU_PART_CORTEX_A9: + case ARM_CPU_PART_CORTEX_A12: + case ARM_CPU_PART_CORTEX_A17: + case ARM_CPU_PART_CORTEX_A73: + case ARM_CPU_PART_CORTEX_A75: + if (processor.switch_mm != cpu_v7_bpiall_switch_mm) + goto bl_error; + per_cpu(harden_branch_predictor_fn, cpu) = + harden_branch_predictor_bpiall; + spectre_v2_method = "BPIALL"; + break; + + case ARM_CPU_PART_CORTEX_A15: + case ARM_CPU_PART_BRAHMA_B15: + if (processor.switch_mm != cpu_v7_iciallu_switch_mm) + goto bl_error; + per_cpu(harden_branch_predictor_fn, cpu) = + harden_branch_predictor_iciallu; + spectre_v2_method = "ICIALLU"; + break; + } + if (spectre_v2_method) { + pr_info("CPU%u: Spectre v2: using %s workaround\n", + smp_processor_id(), spectre_v2_method); + } + return; + +bl_error: + pr_err("CPU%u: Spectre v2: incorrect context switching function, system vulnerable\n", + cpu); +} +#else +static void cpu_v7_spectre_init(void) +{ +} +#endif + +static __maybe_unused bool cpu_v7_check_auxcr_set(bool *warned, + u32 mask, const char *msg) +{ + u32 aux_cr; + + asm("mrc p15, 0, %0, c1, c0, 1" : "=r" (aux_cr)); + + if ((aux_cr & mask) != mask) { + if (!*warned) + pr_err("CPU%u: %s", smp_processor_id(), msg); + *warned = true; + return false; + } + return true; +} + +static DEFINE_PER_CPU(bool, spectre_warned); + +static bool check_spectre_auxcr(bool *warned, u32 bit) +{ + return IS_ENABLED(CONFIG_HARDEN_BRANCH_PREDICTOR) && + cpu_v7_check_auxcr_set(warned, bit, "Spectre v2: firmware did not set auxiliary control register IBE bit, system vulnerable\n"); +} + +void cpu_v7_ca8_ibe(void) +{ + if (check_spectre_auxcr(this_cpu_ptr(&spectre_warned), BIT(6))) + cpu_v7_spectre_init(); +} + +void cpu_v7_ca15_ibe(void) +{ + if (check_spectre_auxcr(this_cpu_ptr(&spectre_warned), BIT(0))) + cpu_v7_spectre_init(); + else { + pr_err("Spectre v2: not initialized\n"); + } +} + +void cpu_v7_bugs_init(void) +{ + cpu_v7_spectre_init(); +} +#endif diff -urN linux-4.1.52-orig/arch/arm/mm/proc-v7m.S linux-4.1.52/arch/arm/mm/proc-v7m.S --- linux-4.1.52-orig/arch/arm/mm/proc-v7m.S 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/mm/proc-v7m.S 2020-02-07 01:47:53.000000000 +0800 @@ -98,7 +98,11 @@ str r5, [r0, V7M_SCB_SHPR3] @ set PendSV priority @ SVC to run the kernel in this mode +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + badr r1, 1f +#else adr r1, BSYM(1f) +#endif ldr r5, [r12, #11 * 4] @ read the SVC vector entry str r1, [r12, #11 * 4] @ write the temporary SVC vector entry mov r6, lr @ save LR diff -urN linux-4.1.52-orig/arch/arm/mm/proc-v7.S linux-4.1.52/arch/arm/mm/proc-v7.S --- linux-4.1.52-orig/arch/arm/mm/proc-v7.S 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/mm/proc-v7.S 2020-02-07 01:47:53.000000000 +0800 @@ -87,6 +87,19 @@ ret lr ENDPROC(cpu_v7_dcache_clean_area) +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +ENTRY(cpu_v7_iciallu_switch_mm) + mov r3, #0 + mcr p15, 0, r3, c7, c5, 0 @ ICIALLU + b cpu_v7_switch_mm +ENDPROC(cpu_v7_iciallu_switch_mm) +ENTRY(cpu_v7_bpiall_switch_mm) + mov r3, #0 + mcr p15, 0, r3, c7, c5, 6 @ flush BTAC/BTB + b cpu_v7_switch_mm +ENDPROC(cpu_v7_bpiall_switch_mm) +#endif + string cpu_v7_name, "ARMv7 Processor" .align @@ -152,6 +165,8 @@ ENDPROC(cpu_v7_do_resume) #endif +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +#else /* * Cortex-A8 */ @@ -177,6 +192,7 @@ globl_equ cpu_ca9mp_dcache_clean_area, cpu_v7_dcache_clean_area globl_equ cpu_ca9mp_switch_mm, cpu_v7_switch_mm globl_equ cpu_ca9mp_set_pte_ext, cpu_v7_set_pte_ext +#endif .globl cpu_ca9mp_suspend_size .equ cpu_ca9mp_suspend_size, cpu_v7_suspend_size + 4 * 2 #ifdef CONFIG_ARM_CPU_SUSPEND @@ -262,10 +278,22 @@ __v7_cr7mp_setup: mov r10, #(1 << 0) @ Cache/TLB ops broadcasting b 1f +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +__v7_ca15mp_setup: +__v7_b15mp_setup: +#ifdef CONFIG_HARDEN_BRANCH_PREDICTOR + mrc p15, 0, r0, c1, c0, 1 + orr r0, r0, #1 @ Enable IBE bit + mcr p15, 0, r0, c1, c0, 1 +#endif +#endif /* CONFIG_BCM_KF_SPECTRE_PATCH && CONFIG_BCM_SPECTRE_PATCH_ENABLE */ __v7_ca7mp_setup: __v7_ca12mp_setup: +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +#else __v7_ca15mp_setup: __v7_b15mp_setup: +#endif __v7_ca17mp_setup: mov r10, #0 1: adr r12, __v7_setup_stack @ the local stack @@ -468,12 +496,91 @@ __INITDATA +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + .weak cpu_v7_bugs_init + + @ define struct processor (see and proc-macros.S) + define_processor_functions v7, dabort=v7_early_abort, pabort=v7_pabort, suspend=1, bugs=cpu_v7_bugs_init + +#ifdef CONFIG_HARDEN_BRANCH_PREDICTOR + @ generic v7 bpiall on context switch + globl_equ cpu_v7_bpiall_proc_init, cpu_v7_proc_init + globl_equ cpu_v7_bpiall_proc_fin, cpu_v7_proc_fin + globl_equ cpu_v7_bpiall_reset, cpu_v7_reset + globl_equ cpu_v7_bpiall_do_idle, cpu_v7_do_idle + globl_equ cpu_v7_bpiall_dcache_clean_area, cpu_v7_dcache_clean_area + globl_equ cpu_v7_bpiall_set_pte_ext, cpu_v7_set_pte_ext + globl_equ cpu_v7_bpiall_suspend_size, cpu_v7_suspend_size +#ifdef CONFIG_ARM_CPU_SUSPEND + globl_equ cpu_v7_bpiall_do_suspend, cpu_v7_do_suspend + globl_equ cpu_v7_bpiall_do_resume, cpu_v7_do_resume +#endif + define_processor_functions v7_bpiall, dabort=v7_early_abort, pabort=v7_pabort, suspend=1, bugs=cpu_v7_bugs_init + +#define HARDENED_BPIALL_PROCESSOR_FUNCTIONS v7_bpiall_processor_functions +#else +#define HARDENED_BPIALL_PROCESSOR_FUNCTIONS v7_processor_functions +#endif +#else @ define struct processor (see and proc-macros.S) define_processor_functions v7, dabort=v7_early_abort, pabort=v7_pabort, suspend=1 +#endif /* CONFIG_BCM_KF_SPECTRE_PATCH && CONFIG_BCM_SPECTRE_PATCH_ENABLE */ + +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) +#ifndef CONFIG_ARM_LPAE + @ Cortex-A8 - always needs bpiall switch_mm implementation + globl_equ cpu_ca8_proc_init, cpu_v7_proc_init + globl_equ cpu_ca8_proc_fin, cpu_v7_proc_fin + globl_equ cpu_ca8_reset, cpu_v7_reset + globl_equ cpu_ca8_do_idle, cpu_v7_do_idle + globl_equ cpu_ca8_dcache_clean_area, cpu_v7_dcache_clean_area + globl_equ cpu_ca8_set_pte_ext, cpu_v7_set_pte_ext + globl_equ cpu_ca8_switch_mm, cpu_v7_bpiall_switch_mm + globl_equ cpu_ca8_suspend_size, cpu_v7_suspend_size +#ifdef CONFIG_ARM_CPU_SUSPEND + globl_equ cpu_ca8_do_suspend, cpu_v7_do_suspend + globl_equ cpu_ca8_do_resume, cpu_v7_do_resume +#endif + define_processor_functions ca8, dabort=v7_early_abort, pabort=v7_pabort, suspend=1, bugs=cpu_v7_ca8_ibe + + @ Cortex-A9 - needs more registers preserved across suspend/resume + @ and bpiall switch_mm for hardening + globl_equ cpu_ca9mp_proc_init, cpu_v7_proc_init + globl_equ cpu_ca9mp_proc_fin, cpu_v7_proc_fin + globl_equ cpu_ca9mp_reset, cpu_v7_reset + globl_equ cpu_ca9mp_do_idle, cpu_v7_do_idle + globl_equ cpu_ca9mp_dcache_clean_area, cpu_v7_dcache_clean_area +#ifdef CONFIG_HARDEN_BRANCH_PREDICTOR + globl_equ cpu_ca9mp_switch_mm, cpu_v7_bpiall_switch_mm +#else + globl_equ cpu_ca9mp_switch_mm, cpu_v7_switch_mm +#endif + globl_equ cpu_ca9mp_set_pte_ext, cpu_v7_set_pte_ext + define_processor_functions ca9mp, dabort=v7_early_abort, pabort=v7_pabort, suspend=1, bugs=cpu_v7_bugs_init +#endif + + @ Cortex-A15 - needs iciallu switch_mm for hardening + globl_equ cpu_ca15_proc_init, cpu_v7_proc_init + globl_equ cpu_ca15_proc_fin, cpu_v7_proc_fin + globl_equ cpu_ca15_reset, cpu_v7_reset + globl_equ cpu_ca15_do_idle, cpu_v7_do_idle + globl_equ cpu_ca15_dcache_clean_area, cpu_v7_dcache_clean_area +#ifdef CONFIG_HARDEN_BRANCH_PREDICTOR + globl_equ cpu_ca15_switch_mm, cpu_v7_iciallu_switch_mm +#else + globl_equ cpu_ca15_switch_mm, cpu_v7_switch_mm +#endif + globl_equ cpu_ca15_set_pte_ext, cpu_v7_set_pte_ext + globl_equ cpu_ca15_suspend_size, cpu_v7_suspend_size + globl_equ cpu_ca15_do_suspend, cpu_v7_do_suspend + globl_equ cpu_ca15_do_resume, cpu_v7_do_resume + define_processor_functions ca15, dabort=v7_early_abort, pabort=v7_pabort, suspend=1, bugs=cpu_v7_ca15_ibe +#else #ifndef CONFIG_ARM_LPAE define_processor_functions ca8, dabort=v7_early_abort, pabort=v7_pabort, suspend=1 define_processor_functions ca9mp, dabort=v7_early_abort, pabort=v7_pabort, suspend=1 #endif +#endif /* CONFIG_BCM_KF_SPECTRE_PATCH && CONFIG_BCM_SPECTRE_PATCH_ENABLE */ #ifdef CONFIG_CPU_PJ4B define_processor_functions pj4b, dabort=v7_early_abort, pabort=v7_pabort, suspend=1 #endif @@ -580,7 +687,11 @@ __v7_ca12mp_proc_info: .long 0x410fc0d0 .long 0xff0ffff0 +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + __v7_proc __v7_ca12mp_proc_info, __v7_ca12mp_setup, proc_fns = HARDENED_BPIALL_PROCESSOR_FUNCTIONS +#else __v7_proc __v7_ca12mp_proc_info, __v7_ca12mp_setup +#endif .size __v7_ca12mp_proc_info, . - __v7_ca12mp_proc_info /* @@ -590,9 +701,12 @@ __v7_ca15mp_proc_info: .long 0x410fc0f0 .long 0xff0ffff0 +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + __v7_proc __v7_ca15mp_proc_info, __v7_ca15mp_setup, proc_fns = ca15_processor_functions +#else __v7_proc __v7_ca15mp_proc_info, __v7_ca15mp_setup +#endif .size __v7_ca15mp_proc_info, . - __v7_ca15mp_proc_info - /* * Broadcom Corporation Brahma-B15 processor. */ @@ -600,7 +714,11 @@ __v7_b15mp_proc_info: .long 0x420f00f0 .long 0xff0ffff0 +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + __v7_proc __v7_b15mp_proc_info, __v7_b15mp_setup, proc_fns = ca15_processor_functions +#else __v7_proc __v7_b15mp_proc_info, __v7_b15mp_setup +#endif .size __v7_b15mp_proc_info, . - __v7_b15mp_proc_info /* @@ -610,9 +728,31 @@ __v7_ca17mp_proc_info: .long 0x410fc0e0 .long 0xff0ffff0 +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + __v7_proc __v7_ca17mp_proc_info, __v7_ca17mp_setup, proc_fns = HARDENED_BPIALL_PROCESSOR_FUNCTIONS +#else __v7_proc __v7_ca17mp_proc_info, __v7_ca17mp_setup +#endif .size __v7_ca17mp_proc_info, . - __v7_ca17mp_proc_info +#if defined(CONFIG_BCM_KF_SPECTRE_PATCH) && defined(CONFIG_BCM_SPECTRE_PATCH_ENABLE) + /* ARM Ltd. Cortex A73 processor */ + .type __v7_ca73_proc_info, #object +__v7_ca73_proc_info: + .long 0x410fd090 + .long 0xff0ffff0 + __v7_proc __v7_ca73_proc_info, __v7_setup, proc_fns = HARDENED_BPIALL_PROCESSOR_FUNCTIONS + .size __v7_ca73_proc_info, . - __v7_ca73_proc_info + + /* ARM Ltd. Cortex A75 processor */ + .type __v7_ca75_proc_info, #object +__v7_ca75_proc_info: + .long 0x410fd0a0 + .long 0xff0ffff0 + __v7_proc __v7_ca75_proc_info, __v7_setup, proc_fns = HARDENED_BPIALL_PROCESSOR_FUNCTIONS + .size __v7_ca75_proc_info, . - __v7_ca75_proc_info +#endif + /* * Qualcomm Inc. Krait processors. */ diff -urN linux-4.1.52-orig/arch/arm/plat-bcm63xx/b15_core.c linux-4.1.52/arch/arm/plat-bcm63xx/b15_core.c --- linux-4.1.52-orig/arch/arm/plat-bcm63xx/b15_core.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/plat-bcm63xx/b15_core.c 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,111 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + +/* +* Broadcom ARM based on Cortex A15 Platform base +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void __iomem * scu_base_addr(void) +{ + return __io_address(B15_PHYS_BASE + B15_SCU_OFF); +} + +void __init b15_fixup(void) +{ + /* in case if any fixup that has to be done in really early + * stage of kernel booting */ +} + +/* map_io should be called the first, so we have the register base + * address for the core. */ +void __init b15_map_io(void) +{ + struct map_desc desc; + +#if 0 + /* + * Cortex A9 Architecture Manual specifies this as a way to get + * MPCORE PERHIPHBASE address at run-time + */ + asm("mrc p15,4,%0,c15,c0,0 @ Read Configuration Base Address Register" + : "=&r" (base_addr) : : "cc"); + + printk(KERN_INFO "CA9 MPCORE found at %p\n", (void *)base_addr); +#endif + + /* Fix-map the entire PERIPHBASE 2*4K register block */ + desc.virtual = IO_ADDRESS(B15_PHYS_BASE); + desc.pfn = __phys_to_pfn(B15_PHYS_BASE); + desc.length = SZ_64K; // FIXME! 64K actually cover whole 0x10000 area.. a smaller value once RDB is out + desc.type = MT_DEVICE; + iotable_init(&desc, 1); +} + +void __init b15_init_gic(void) +{ + printk(KERN_INFO "Broadcom B15 CORE GIC init\n"); + printk(KERN_INFO "DIST at %p, CPU_IF at %p\n", + (void *)IO_ADDRESS(B15_PHYS_BASE) + B15_GIC_DIST_OFF, + (void *)IO_ADDRESS(B15_PHYS_BASE) + B15_GIC_CPUIF_OFF); + + // FIXME!! hardcored value below for the interrupt line#, will need to define + // the interrupt line# in a header file for all different chips + gic_init(0, 16, (void *)IO_ADDRESS(B15_PHYS_BASE) + B15_GIC_DIST_OFF, + (void *)IO_ADDRESS(B15_PHYS_BASE) + B15_GIC_CPUIF_OFF); + + //irq_set_handler(B15_IRQ_GLOBALTIMER, handle_percpu_irq); + /* try it.. handle_edge_irq, handle_percpu_irq, or handle_level_irq */ +} + +void __init b15_init_early(void) +{ + /* NOP */ +} + +#endif /* CONFIG_BCM_KF_ARM_BCM963XX */ diff -urN linux-4.1.52-orig/arch/arm/plat-bcm63xx/bcm63138.c linux-4.1.52/arch/arm/plat-bcm63xx/bcm63138.c --- linux-4.1.52-orig/arch/arm/plat-bcm63xx/bcm63138.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/plat-bcm63xx/bcm63138.c 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,421 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + +/* + * BCM63138 SoC main platform file. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_PLAT_BCM63XX_AMBA_PL011 +#include +#include +#endif +#ifdef CONFIG_PLAT_BCM63XX_AMBA_PL081 +#include +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_PLAT_BCM63XX_ACP +#include +#endif + +extern unsigned long getMemorySize(void); + +#ifdef CONFIG_PLAT_BCM63XX_AMBA_PL081 +static struct clk pl081dmac_clk = { + .name = "pl081dmac", + .rate = FREQ_MHZ(50), + .fixed = 1, + .type = CLK_DMAC, +}; +#endif + +#define IO_DESC(pa, sz) { \ + .virtual = IO_ADDRESS(pa), \ + .pfn = __phys_to_pfn(pa), \ + .length = sz, \ + .type = MT_DEVICE, \ + } + +#define MEM_DESC(pa, sz) { \ + .virtual = IO_ADDRESS(pa), \ + .pfn = __phys_to_pfn(pa), \ + .length = sz, \ + .type = MT_MEMORY_RWX_NONCACHED, \ + } +#ifdef CONFIG_PLAT_BCM63XX_ACP +#define ACP_MEM_DESC(pa, sz) { \ + .virtual = ACP_ADDRESS(pa), \ + .pfn = __phys_to_pfn(pa), \ + .length = sz, \ + .type = MT_MEMORY_NONSECURED, \ + } +#endif + + +static struct map_desc bcm63138_io_desc[] __initdata = { + IO_DESC(USB_CTL_PHYS_BASE, SZ_4K), + IO_DESC(MEMC_PHYS_BASE, SZ_4K), + IO_DESC(DDRPHY_PHYS_BASE, SZ_4K), + IO_DESC(SAR_PHYS_BASE, SZ_16K), + IO_DESC(SATA_PHYS_BASE, SZ_16K), + IO_DESC(USBH_PHYS_BASE, SZ_8K), + IO_DESC(ERROR_PORT_PHYS_BASE, SZ_4K), + IO_DESC(AIP_PHYS_BASE, SZ_4K), +#ifdef CONFIG_PLAT_BCM63XX_AMBA_PL011 + IO_DESC(ARM_UART_PHYS_BASE, SZ_4K), +#endif + IO_DESC(L2C_PHYS_BASE, SZ_4K), + IO_DESC(ARMCFG_PHYS_BASE, SZ_4K), + IO_DESC(DECT_PHYS_BASE, SZ_128K), + IO_DESC(SWITCH_PHYS_BASE, SZ_512K), + IO_DESC(APM_PHYS_BASE, SZ_128K), + IO_DESC(RDP_PHYS_BASE, SZ_1M), + IO_DESC(PMC_PHYS_BASE, SZ_512K), + IO_DESC(PROC_MON_PHYS_BASE, SZ_4K), + IO_DESC(DSLPHY_PHYS_BASE, SZ_1M), + IO_DESC(DSLLMEM_PHYS_BASE, SZ_1M), + IO_DESC(PERF_PHYS_BASE, SZ_32K), + IO_DESC(BOOTLUT_PHYS_BASE, SZ_4K), + IO_DESC(SPIFLASH_PHYS_BASE, SZ_128K), + IO_DESC(NANDFLASH_PHYS_BASE, SZ_128K), +}; + +/* any fixup that has to be performed in the early stage of + * kernel booting */ +void __init soc_fixup(void) +{ + ca9mp_fixup(); +} + +/* + * Map fix-mapped I/O that is needed before full MMU operation + */ +void __init soc_map_io(void) +{ +#ifdef CONFIG_PLAT_BCM63XX_ACP + struct map_desc acp_desc; +#endif + ca9mp_map_io(); + + iotable_init(bcm63138_io_desc, ARRAY_SIZE(bcm63138_io_desc)); + +#ifdef CONFIG_PLAT_BCM63XX_ACP + acp_desc.virtual = ACP_ADDRESS(PLAT_PHYS_OFFSET); + acp_desc.pfn = __phys_to_pfn(PLAT_PHYS_OFFSET); + acp_desc.length = getMemorySize(); + acp_desc.type = MT_MEMORY_NONSECURED; + iotable_init(&acp_desc, 1); +#endif +} + +#if 0 +static int soc_abort_handler(unsigned long addr, unsigned int fsr, + struct pt_regs *regs) +{ + /* + * These happen for no good reason + * possibly left over from CFE + */ + printk(KERN_WARNING "External imprecise Data abort at " + "addr=%#lx, fsr=%#x ignored.\n", addr, fsr); + + /* Returning non-zero causes fault display and panic */ + return 0; +} +#endif + +static void soc_aborts_enable(void) +{ +#if 0 + u32 x; + + /* Install our hook */ + hook_fault_code(16 + 6, soc_abort_handler, SIGBUS, 0, + "imprecise external abort"); + + /* Enable external aborts - clear "A" bit in CPSR */ + + /* Read CPSR */ + asm( "mrs %0,cpsr": "=&r" (x) : : ); + + x &= ~ PSR_A_BIT; + + /* Update CPSR, affect bits 8-15 */ + asm( "msr cpsr_x,%0; nop; nop": : "r" (x) : "cc" ); +#endif +} + +/* + * This SoC relies on MPCORE GIC interrupt controller + */ +void __init soc_init_irq(void) +{ + ca9mp_init_gic(); + soc_aborts_enable(); +} + +#if !defined (CONFIG_OF) +#ifdef CONFIG_CACHE_L2X0 +/* + * SoC initialization that need to be done early, + * e.g. L2 cache, clock, I/O pin mux, power management + */ +void __init soc_l2_cache_init(void) +{ + u32 auxctl_val = 0, auxctl_msk = ~0UL; + + auxctl_val = BCM_L2C_AUX_VAL; + auxctl_msk = BCM_L2C_AUX_MSK; + + /* Configure using default aux control value */ + l2x0_init(__io_address(L2C_PHYS_BASE), auxctl_val, auxctl_msk); + + return; +} +#endif +#endif + +void __init soc_init_early(void) +{ + ca9mp_init_early(); + + pmc_init(); + + /* TODO: can we use the following for reserving DMA memory for ACP? + * Will it maintain the same region of memory all the time? */ + //init_consistent_dma_size(SZ_128M); +} + +/* + * Install all other SoC device drivers + * that are not automatically discoverable. + */ + + +#ifdef CONFIG_PLAT_BCM63XX_AMBA_PL081 +struct pl08x_channel_data pl081_dmac0_channeldata [] = +{ + /* HS_UART HOFIFO Channel */ + { + .bus_id = PL081_DMA_CHAN_HS_UART_RX, + .min_signal = 0, + .max_signal = 0, + .muxval = 0, + .circular_buffer = false, + .single = false, + .periph_buses = PL08X_AHB1, + }, + + /* HS_UART HIFIFO Channel */ + { + .bus_id = PL081_DMA_CHAN_HS_UART_TX, + .min_signal = 1, + .max_signal = 1, + .muxval = 0, + .circular_buffer = false, + .single = false, + .periph_buses = PL08X_AHB1, + } +}; + +struct pl08x_channel_data pl081_dmac0_memcp_chdata [] = +{ + { + .bus_id = "DMA_MTOM", + .min_signal = 2, + .max_signal = 2, + .muxval = 0, + .circular_buffer = false, + .single = false, + .periph_buses = PL08X_AHB1, + }, +}; + +struct pl08x_platform_data pl081dmac0_pdata; +static AMBA_AHB_DEVICE(pl081dmac0, "pl08xdmac.0", 0x00041081, PL081_DMA_PHYS_BASE, {INTERRUPT_ID_PL081}, &pl081dmac0_pdata); +#endif + +#ifdef CONFIG_PLAT_BCM63XX_AMBA_PL011 +static AMBA_APB_DEVICE(uart0, "uart0", 0, ARM_UART_PHYS_BASE, { INTERRUPT_ID_UART2 }, NULL); +static struct amba_device *amba_devs[] __initdata = { + &uart0_device, +}; +#endif + +#ifdef CONFIG_PLAT_BCM63XX_AMBA_PL081 +/* + * Get PL081 periperal DMA request signal number + * PL081 has 16 DMA request signals. This function + * returns thhe DMA request signal number associated + * with the specified dma channel + */ +static int get_signal(struct pl08x_dma_chan * dma_chan) +{ + int signal = -1; + + /* Just return min_signal as dma request lines are not muxed */ + if( dma_chan && dma_chan->cd ) + signal = dma_chan->cd->min_signal; + + return signal; +} + +/* + * Release PL081 periperal DMA request signal number + */ +static void put_signal(struct pl08x_dma_chan * dma_chan) +{ + /* Do nothing as dma request lines are not muxed */ +} +#endif + +#ifdef CONFIG_PLAT_BCM63XX_EMMC /* Arasan emmc SD */ +static struct resource bcm63xx_emmc_resources[] = { + [0] = { + .start = EMMC_HOSTIF_PHYS_BASE, + .end = EMMC_HOSTIF_PHYS_BASE + SZ_256 - 1, /* we only need this area */ + /* the memory map actually makes SZ_4K available */ + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = INTERRUPT_ID_EMMC, + .end = INTERRUPT_ID_EMMC, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 bcm63xx_emmc_dmamask = 0xffffffffUL; + +struct platform_device bcm63xx_emmc_device = { + .name = "sdhci-bcm63xx", + .id = 0, + .num_resources = ARRAY_SIZE(bcm63xx_emmc_resources), + .resource = bcm63xx_emmc_resources, + .dev = { + .dma_mask = &bcm63xx_emmc_dmamask, + .coherent_dma_mask = 0xffffffffUL}, +}; +#endif + +void __init soc_add_devices(void) +{ +#ifdef CONFIG_PLAT_BCM63XX_AMBA_PL011 + { + int i; + + /* init uart (AMBA device) here */ + for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { + struct amba_device *d = amba_devs[i]; + int ret; + + ret = amba_device_register(d, &iomem_resource); + if (ret) + printk("%s:%d:amba device[%d] registered failed, err = %d", + __func__, __LINE__, i, ret); + } + } +#endif + +#ifdef CONFIG_PLAT_BCM63XX_AMBA_PL081 + { + pl081dmac0_pdata.slave_channels = &pl081_dmac0_channeldata[0]; + pl081dmac0_pdata.num_slave_channels = 2; +// pl081dmac0_pdata.memcpy_channel = NULL; + pl081dmac0_pdata.get_signal = get_signal; + pl081dmac0_pdata.put_signal = put_signal; + pl081dmac0_pdata.lli_buses = PL08X_AHB1; + pl081dmac0_pdata.mem_buses = PL08X_AHB1; + + /* Register AMBA device */ + amba_device_register(&pl081dmac0_device, &iomem_resource); + } +#endif + +#ifdef CONFIG_PLAT_BCM63XX_EMMC + { + /* Only register EMMC device if NAND i/f is NOT active */ + if ( MISC->miscStrapBus & MISC_STRAP_BUS_SW_BOOT_SPI_SPINAND_EMMC_MASK ) { + platform_device_register(&bcm63xx_emmc_device); + } + } +#endif +} + +static void __init neon_enable(void *data) +{ + u32 access; + (void)data; + + access = get_copro_access(); + + /* + * Enable full access to VFP (cp10 and cp11) + */ + set_copro_access(access | CPACC_FULL(10) | CPACC_FULL(11)); + + /* mov r0, 0x40000000; vmsr fpexc, r0 */ + asm volatile ("mov r0, #0x40000000; .word 0xeee80a10" : : : "r0" ); +} + +static int __init bcm63138_neon_fixup(void) +{ + pmc_neon_power_up(); + smp_call_function_single(0, neon_enable, NULL, 1); + return 0; +} +late_initcall(bcm63138_neon_fixup); + +#endif /* CONFIG_BCM_KF_ARM_BCM963XX */ diff -urN linux-4.1.52-orig/arch/arm/plat-bcm63xx/bcm63148.c linux-4.1.52/arch/arm/plat-bcm63xx/bcm63148.c --- linux-4.1.52-orig/arch/arm/plat-bcm63xx/bcm63148.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/plat-bcm63xx/bcm63148.c 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,275 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + +/* + * BCM63148 SoC main platform file. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FREQ_MHZ(x) ((x)*1000*1000) + +#define IO_DESC(pa, sz) { \ + .virtual = IO_ADDRESS(pa), \ + .pfn = __phys_to_pfn(pa), \ + .length = sz, \ + .type = MT_DEVICE, \ + } + +#define MEM_DESC(pa, sz) { \ + .virtual = IO_ADDRESS(pa), \ + .pfn = __phys_to_pfn(pa), \ + .length = sz, \ + .type = MT_MEMORY_RWX_NONCACHED, \ + } + + +static struct map_desc bcm63148_io_desc[] __initdata = { + IO_DESC(USB_CTL_PHYS_BASE, SZ_4K), + IO_DESC(MEMC_PHYS_BASE, SZ_4K), + IO_DESC(DDRPHY_PHYS_BASE, SZ_4K), + IO_DESC(SAR_PHYS_BASE, SZ_16K), + IO_DESC(SATA_PHYS_BASE, SZ_16K), + IO_DESC(USBH_PHYS_BASE, SZ_8K), + IO_DESC(ERROR_PORT_PHYS_BASE, SZ_4K), +// IO_DESC(L2C_PHYS_BASE, SZ_4K), + IO_DESC(B15_CTRL_PHYS_BASE, SZ_16K), // FIXME! once we know the real RDB + IO_DESC(DECT_PHYS_BASE, SZ_128K), + IO_DESC(SWITCH_PHYS_BASE, SZ_512K), + IO_DESC(APM_PHYS_BASE, SZ_128K), + IO_DESC(RDP_PHYS_BASE, SZ_1M), + IO_DESC(PMC_PHYS_BASE, SZ_512K), + IO_DESC(PROC_MON_PHYS_BASE, SZ_4K), + IO_DESC(DSLPHY_PHYS_BASE, SZ_1M), + IO_DESC(DSLLMEM_PHYS_BASE, SZ_1M), + IO_DESC(PERF_PHYS_BASE, SZ_16K), + IO_DESC(BOOTLUT_PHYS_BASE, SZ_4K), + IO_DESC(SPIFLASH_PHYS_BASE, SZ_128K), + IO_DESC(NANDFLASH_PHYS_BASE, SZ_128K), + /* FIXME!! more!! */ +}; + +/* any fixup that has to be performed in the early stage of + * kernel booting */ +void __init soc_fixup(void) +{ + b15_fixup(); +} + +/* + * Map fix-mapped I/O that is needed before full MMU operation + */ +void __init soc_map_io(void) +{ + b15_map_io(); + + iotable_init(bcm63148_io_desc, ARRAY_SIZE(bcm63148_io_desc)); +} + +static int arm_wfi_allowed = 1; // administratively allowed + +/* power is significantly reduced by re-enabling interrupts + * and looping locally until a reschedule is needed. + * nops would help further but create droops/spikes. + */ +__attribute__ (( aligned(16),hot )) +static void bcm63xx_arm_pm_idle(void) +{ + local_irq_enable(); + while (!need_resched()); +} + +// selective wfi enable/disable based on frequency +void arm_wfi_enable(unsigned int freqHz) +{ + /* enable only if administratively allowed and under 1500MHz */ + if (arm_wfi_allowed && freqHz < FREQ_MHZ(1500)) { + arm_pm_idle = 0; + } else { + arm_pm_idle = bcm63xx_arm_pm_idle; + } +} + +static unsigned int get_arm_core_clk(void) +{ + struct device *cpu_dev = NULL; + struct clk *arm_clk = NULL; + unsigned int clk_rate; + + cpu_dev = get_cpu_device(smp_processor_id()); + if (!cpu_dev) { + return FREQ_MHZ(1500); + } + + arm_clk = clk_get(cpu_dev, "cpuclk"); + if( IS_ERR_OR_NULL(arm_clk) ) { + pr_err("%s: failed to get cpu clk\n", __func__); + return FREQ_MHZ(1500); + } + + clk_rate = clk_get_rate(arm_clk); + + return clk_rate; +} +#if 0 +static int soc_abort_handler(unsigned long addr, unsigned int fsr, + struct pt_regs *regs) +{ + /* + * These happen for no good reason + * possibly left over from CFE + */ + printk(KERN_WARNING "External imprecise Data abort at " + "addr=%#lx, fsr=%#x ignored.\n", addr, fsr); + + /* Returning non-zero causes fault display and panic */ + return 0; +} +#endif + +static void soc_aborts_enable(void) +{ +#if 0 + u32 x; + + /* Install our hook */ + hook_fault_code(16 + 6, soc_abort_handler, SIGBUS, 0, + "imprecise external abort"); + + /* Enable external aborts - clear "A" bit in CPSR */ + + /* Read CPSR */ + asm( "mrs %0,cpsr": "=&r" (x) : : ); + + x &= ~ PSR_A_BIT; + + /* Update CPSR, affect bits 8-15 */ + asm( "msr cpsr_x,%0; nop; nop": : "r" (x) : "cc" ); +#endif +} + +/* + * This SoC relies on MPCORE GIC interrupt controller + */ +void __init soc_init_irq(void) +{ + b15_init_gic(); + soc_aborts_enable(); +} + +#ifdef CONFIG_CACHE_L2X0 +/* 63148 integrate l2 cache controller, no need to init */ +void __init soc_l2_cache_init(void) +{ + return; +} +#endif + +/* + * SoC initialization that need to be done early, + * e.g. L2 cache, clock, I/O pin mux, power management + */ +void __init soc_init_early(void) +{ + b15_init_early(); + + pmc_init(); + + /* + * DMA memory + * + * The PCIe-to-AXI mapping (PAX) has a window of 128 MB alighed at 1MB + * we should make the DMA-able DRAM at least this large. + * Will need to use CONSISTENT_BASE and CONSISTENT_SIZE macros + * to program the PAX inbound mapping registers. + */ + // FIXME!!! + //init_consistent_dma_size(SZ_128M); +} + +/* + * Install all other SoC device drivers + * that are not automatically discoverable. + */ + +void __init soc_add_devices(void) +{ + /* if there is soc specific device */ + + /* to ensure RAC is disabled, due to some known issues with RAC */ + B15CTRL->cpu_ctrl.rac_cfg0 = 0; +} + +/* + * Functions to allow enabling/disabling WAIT instruction + */ +void set_cpu_arm_wait(int enable) +{ + arm_wfi_allowed = enable; + printk("wait instruction: %s\n", enable ? "enabled" : "disabled"); + arm_wfi_enable(get_arm_core_clk()); + kick_all_cpus_sync(); +} +EXPORT_SYMBOL(set_cpu_arm_wait); + +int get_cpu_arm_wait(void) +{ + return arm_wfi_allowed; +} +EXPORT_SYMBOL(get_cpu_arm_wait); + +static int __init bcm963xx_idle_init(void) +{ + arm_wfi_enable(get_arm_core_clk()); + return 0; +} +arch_initcall(bcm963xx_idle_init); +#endif /* CONFIG_BCM_KF_ARM_BCM963XX */ diff -urN linux-4.1.52-orig/arch/arm/plat-bcm63xx/bcm63xx_acp.c linux-4.1.52/arch/arm/plat-bcm63xx/bcm63xx_acp.c --- linux-4.1.52-orig/arch/arm/plat-bcm63xx/bcm63xx_acp.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/plat-bcm63xx/bcm63xx_acp.c 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,1300 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bcm_map_part.h" +#include "pmc_drv.h" +#include "BPCM.h" + +typedef struct { + uint32_t addr_in; + uint32_t addr_out; + uint8_t dst_pid; + uint8_t size_shift; + uint8_t en; + struct proc_dir_entry *proc_dir; +} ubus_cfg_t; + +typedef struct { + ubus_cfg_t ubus_cfg[BCM_UBUS_CFG_MAX]; + int pmb_addr; + uint32_t acp_ctrl; + uint8_t en; + uint8_t name[10]; + struct proc_dir_entry *proc_dir; +} acp_cfg_entry_t; + +acp_cfg_entry_t acp_cfg_tbl[BCM_UBUS_PID_MAX]; +struct proc_dir_entry *proc_acp_dir = NULL; + +static void acp_ctrl_set(uint8_t id, uint8_t enable) +{ + if (enable) + ARMAIPCTRL->acp_ctrl[id] = acp_cfg_tbl[id].acp_ctrl; + else + ARMAIPCTRL->acp_ctrl[id] = 0; +} + +static int ubus_cfg_entry_set(uint8_t ubus_pid, uint8_t entry_id, + uint32_t addr_in, uint32_t addr_out, uint8_t dst_pid, + uint8_t size_shift, uint8_t enable) +{ + BPCM_UBUS_CFG_REG ubus_cfg; + int ret; + + if (acp_cfg_tbl[ubus_pid].pmb_addr == 0) + return -1; + + ret = ReadBPCMRegister(acp_cfg_tbl[ubus_pid].pmb_addr, + UBUSBPCMRegOffset(cfg[entry_id]), + &ubus_cfg.Regs32.word0); + if (ret) + return ret; + + ret = ReadBPCMRegister(acp_cfg_tbl[ubus_pid].pmb_addr, + UBUSBPCMRegOffset(cfg[entry_id]) + 1, + &ubus_cfg.Regs32.word1); + if (ret) + return ret; + + ubus_cfg.Bits.addr_in = addr_in >> 8; + ubus_cfg.Bits.addr_out = addr_out >> 8; + ubus_cfg.Bits.pid = dst_pid; + ubus_cfg.Bits.size = size_shift; + ubus_cfg.Bits.cmddta = 0; + ubus_cfg.Bits.en = enable; + + ret = WriteBPCMRegister(acp_cfg_tbl[ubus_pid].pmb_addr, + UBUSBPCMRegOffset(cfg[entry_id]), + ubus_cfg.Regs32.word0); + if (ret) + return ret; + + ret = WriteBPCMRegister(acp_cfg_tbl[ubus_pid].pmb_addr, + UBUSBPCMRegOffset(cfg[entry_id]) + 1, + ubus_cfg.Regs32.word1); + return ret; +} + +int bcm63xx_acp_ubus_cfg_get_entry(uint8_t ubus_pid, uint8_t idx, + bcm_acp_ubus_cfg_t *acp_ubus_cfg) +{ + if (acp_ubus_cfg == NULL) + return -EINVAL; + + if (idx >= 4) + return -EINVAL; + + acp_ubus_cfg->addr_in = acp_cfg_tbl[ubus_pid].ubus_cfg[idx].addr_in; + acp_ubus_cfg->addr_out = acp_cfg_tbl[ubus_pid].ubus_cfg[idx].addr_out; + acp_ubus_cfg->dst_pid = acp_cfg_tbl[ubus_pid].ubus_cfg[idx].dst_pid; + acp_ubus_cfg->size_shift = acp_cfg_tbl[ubus_pid].ubus_cfg[idx].size_shift; + acp_ubus_cfg->en = acp_cfg_tbl[ubus_pid].ubus_cfg[idx].en; + + return 0; +} + +int bcm63xx_acp_ubus_cfg_get_all(uint8_t ubus_pid, + bcm_acp_ubus_cfg_t *acp_ubus_cfg) +{ + int i; + + for (i = 0; i < 4; i++) { + if (bcm63xx_acp_ubus_cfg_get_entry(ubus_pid, i, &acp_ubus_cfg[i]) != 0) + return -EINVAL; + } + return 0; +} + +int bcm63xx_acp_ubus_cfg_set_entry(uint8_t ubus_pid, uint8_t idx, + bcm_acp_ubus_cfg_t *acp_ubus_cfg) +{ + int ret = 0; + + if (acp_ubus_cfg == NULL) + return -EINVAL; + + if (idx >= 4) + return -EINVAL; + + ret = ubus_cfg_entry_set(ubus_pid, idx, acp_ubus_cfg->addr_in, + acp_ubus_cfg->addr_out, acp_ubus_cfg->dst_pid, + acp_ubus_cfg->size_shift, acp_ubus_cfg->en); + if (ret != 0) + return ret; + + acp_cfg_tbl[ubus_pid].ubus_cfg[idx].addr_in = acp_ubus_cfg->addr_in; + acp_cfg_tbl[ubus_pid].ubus_cfg[idx].addr_out = acp_ubus_cfg->addr_out; + acp_cfg_tbl[ubus_pid].ubus_cfg[idx].dst_pid = acp_ubus_cfg->dst_pid; + acp_cfg_tbl[ubus_pid].ubus_cfg[idx].size_shift = acp_ubus_cfg->size_shift; + acp_cfg_tbl[ubus_pid].ubus_cfg[idx].en = acp_ubus_cfg->en; + + return ret; +} + +int bcm63xx_acp_ubus_cfg_set_all(uint8_t ubus_pid, bcm_acp_ubus_cfg_t *acp_ubus_cfg) +{ + int i; + + for (i = 0; i < 4; i++) { + if (bcm63xx_acp_ubus_cfg_set_entry(ubus_pid, i, &acp_ubus_cfg[i]) != 0) + goto SET_UBUS_CFG_FAIL; + } + return 0; + +SET_UBUS_CFG_FAIL: + bcm63xx_acp_ubus_cfg_reset(ubus_pid); + return -EINVAL; +} + +void bcm63xx_acp_ubus_cfg_reset(uint8_t ubus_pid) +{ + int i; + + for (i = 0; i < 4; i++) { + ubus_cfg_entry_set(ubus_pid, i, 0, 0, 0, 0, 0); + acp_cfg_tbl[ubus_pid].ubus_cfg[i].addr_in = 0; + acp_cfg_tbl[ubus_pid].ubus_cfg[i].addr_out = 0; + acp_cfg_tbl[ubus_pid].ubus_cfg[i].dst_pid = 0; + acp_cfg_tbl[ubus_pid].ubus_cfg[i].size_shift = 0; + acp_cfg_tbl[ubus_pid].ubus_cfg[i].en = 0; + } +} + +int bcm63xx_acp_cache_ctrl_get(uint8_t ubus_pid, bcm_acp_cache_ctrl_t *cache_ctrl) +{ + if (cache_ctrl == NULL) + return -EINVAL; + + cache_ctrl->wcache = (acp_cfg_tbl[ubus_pid].acp_ctrl >> AIPACP_WCACHE_SHIFT) & 0xf; + cache_ctrl->rcache = (acp_cfg_tbl[ubus_pid].acp_ctrl >> AIPACP_RCACHE_SHIFT) & 0xf; + cache_ctrl->wuser = (acp_cfg_tbl[ubus_pid].acp_ctrl >> AIPACP_WUSER_SHIFT) & 0x1f; + cache_ctrl->ruser = (acp_cfg_tbl[ubus_pid].acp_ctrl >> AIPACP_RUSER_SHIFT) & 0x1f; + return 0; +} + +int bcm63xx_acp_cache_ctrl_set(uint8_t ubus_pid, bcm_acp_cache_ctrl_t *cache_ctrl) +{ + if (cache_ctrl == NULL) + return -EINVAL; + + if ((cache_ctrl->ruser > 0x1f) || (cache_ctrl->wuser > 0x1f) || + (cache_ctrl->rcache > 0xf) || (cache_ctrl->wcache > 0xf)) + return -EINVAL; + + acp_cfg_tbl[ubus_pid].acp_ctrl = cache_ctrl->ruser << AIPACP_RUSER_SHIFT | + cache_ctrl->wuser << AIPACP_WUSER_SHIFT | + cache_ctrl->rcache << AIPACP_RCACHE_SHIFT | + cache_ctrl->wcache << AIPACP_WCACHE_SHIFT; + + acp_ctrl_set(ubus_pid, acp_cfg_tbl[ubus_pid].en); + return 0; +} + +int bcm63xx_acp_enable(uint8_t ubus_pid) +{ + uint8_t i; + int ret = 0; + + /* enable ACP ctrl */ + acp_ctrl_set(ubus_pid, 1); + + for (i = 0; i < 4; i++) { + ret |= ubus_cfg_entry_set(ubus_pid, i, + acp_cfg_tbl[ubus_pid].ubus_cfg[i].addr_in, + acp_cfg_tbl[ubus_pid].ubus_cfg[i].addr_out, + acp_cfg_tbl[ubus_pid].ubus_cfg[i].dst_pid, + acp_cfg_tbl[ubus_pid].ubus_cfg[i].size_shift, + acp_cfg_tbl[ubus_pid].ubus_cfg[i].en); + } + if (ret) + goto fail_reset_hw; + + acp_cfg_tbl[ubus_pid].en = 1; + + return 0; + +fail_reset_hw: + for (i = 0; i < 4; i++) + ubus_cfg_entry_set(ubus_pid, i, 0, 0, 0, 0, 0); + acp_ctrl_set(ubus_pid, 0); + + return ret; +} + +int bcm63xx_acp_disable(uint8_t ubus_pid) +{ + int i; + + for (i = 0; i < 4; i++) + ubus_cfg_entry_set(ubus_pid, i, 0, 0, 0, 0, 0); + acp_ctrl_set(ubus_pid, 0); + acp_cfg_tbl[ubus_pid].en = 0; + + return 0; +} + +bool bcm63xx_acp_on(uint8_t ubus_pid) +{ + return acp_cfg_tbl[ubus_pid].en != 0; +} + +static void acp_cfg_tbl_init(int entry_use, uint32_t *addr_in, + uint32_t *addr_out, uint8_t *dst_pid, uint8_t *size_shift) +{ + uint8_t i, j; + + memset(acp_cfg_tbl, 0x0, BCM_UBUS_PID_MAX * sizeof(acp_cfg_entry_t)); + + /* only initialize table for supported device */ +#ifdef CONFIG_BCM963138 + acp_cfg_tbl[BCM_UBUS_PID_PCIE0].pmb_addr = UBUS_CFG_PMB_ADDR_PCIE0; + sprintf((char *)acp_cfg_tbl[BCM_UBUS_PID_PCIE0].name, "pcie0"); + + acp_cfg_tbl[BCM_UBUS_PID_ARMAXIACP].pmb_addr = UBUS_PMB_ADDR_ARM; + sprintf((char *)acp_cfg_tbl[BCM_UBUS_PID_ARMAXIACP].name, "arm"); + + acp_cfg_tbl[BCM_UBUS_PID_PERIPH].pmb_addr = UBUS_CFG_PMB_ADDR_PERIPH; + sprintf((char *)acp_cfg_tbl[BCM_UBUS_PID_PERIPH].name, "periph"); + + acp_cfg_tbl[BCM_UBUS_PID_USBD].pmb_addr = UBUS_CFG_PMB_ADDR_USBD; + sprintf((char *)acp_cfg_tbl[BCM_UBUS_PID_USBD].name, "usbd"); + + acp_cfg_tbl[BCM_UBUS_PID_USBH].pmb_addr = UBUS_CFG_PMB_ADDR_USBH; + sprintf((char *)acp_cfg_tbl[BCM_UBUS_PID_USBH].name, "usbh"); + + acp_cfg_tbl[BCM_UBUS_PID_SATA].pmb_addr = UBUS_CFG_PMB_ADDR_SATA; + sprintf((char *)acp_cfg_tbl[BCM_UBUS_PID_SATA].name, "sata"); + + acp_cfg_tbl[BCM_UBUS_PID_DECT].pmb_addr = UBUS_PMB_ADDR_DECT; + sprintf((char *)acp_cfg_tbl[BCM_UBUS_PID_DECT].name, "dect"); + + acp_cfg_tbl[BCM_UBUS_PID_APM].pmb_addr = UBUS_CFG_PMB_ADDR_APM; + sprintf((char *)acp_cfg_tbl[BCM_UBUS_PID_APM].name, "apm"); + +#if 0 + // FIXME! not sure which PMB_ADDR VDSL_PID uses. + acp_cfg_tbl[BCM_UBUS_PID_VDSL].pmb_addr = UBUS_PMB_ADDR_VDSL3_CORE; + sprintf((char *)acp_cfg_tbl[BCM_UBUS_PID_VDSL].name, "vdsl"); +#endif + + acp_cfg_tbl[BCM_UBUS_PID_SAR].pmb_addr = UBUS_CFG0_PMB_ADDR_SAR; + sprintf((char *)acp_cfg_tbl[BCM_UBUS_PID_SAR].name, "sar0"); + + acp_cfg_tbl[BCM_UBUS_PID_RNR].pmb_addr = UBUS_CFG_PMB_ADDR_DBR; + sprintf((char *)acp_cfg_tbl[BCM_UBUS_PID_RNR].name, "rnr0"); + + acp_cfg_tbl[BCM_UBUS_PID_RNR_RABR].pmb_addr = UBUS_CFG_PMB_ADDR_RABR; + sprintf((char *)acp_cfg_tbl[BCM_UBUS_PID_RNR_RABR].name, "rnr1"); + + acp_cfg_tbl[BCM_UBUS_PID_SF2].pmb_addr = UBUS_CFG_PMB_ADDR_SWITCH; + sprintf((char *)acp_cfg_tbl[BCM_UBUS_PID_SF2].name, "sf2"); + + acp_cfg_tbl[BCM_UBUS_PID_PCIE1].pmb_addr = UBUS_CFG_PMB_ADDR_PCIE1; + sprintf((char *)acp_cfg_tbl[BCM_UBUS_PID_PCIE1].name, "pcie1"); + + acp_cfg_tbl[BCM_UBUS_PID_ARMAIPDAP].pmb_addr = UBUS_PMB_ADDR_DAP; + sprintf((char *)acp_cfg_tbl[BCM_UBUS_PID_ARMAIPDAP].name, "dap"); + + acp_cfg_tbl[BCM_UBUS_PID_SAR2].pmb_addr = UBUS_CFG1_PMB_ADDR_SAR; + sprintf((char *)acp_cfg_tbl[BCM_UBUS_PID_SAR2].name, "sar1"); + + acp_cfg_tbl[BCM_UBUS_PID_RNR_RBBR].pmb_addr = UBUS_CFG_PMB_ADDR_RBBR; + sprintf((char *)acp_cfg_tbl[BCM_UBUS_PID_RNR_RBBR].name, "rnr2"); +#endif + + /* initialize software entry for the first ubus cfg entry */ + for (i = 0; i < BCM_UBUS_PID_MAX; i++) { + if (acp_cfg_tbl[i].pmb_addr != 0) { + acp_cfg_tbl[i].acp_ctrl = (0xf << AIPACP_WCACHE_SHIFT) | + (0xf << AIPACP_RCACHE_SHIFT) | + (0x1 << AIPACP_WUSER_SHIFT) | + (0x1 << AIPACP_RUSER_SHIFT); + for (j = 0; j < entry_use; j++) { + acp_cfg_tbl[i].ubus_cfg[j].addr_in = addr_in[j]; + acp_cfg_tbl[i].ubus_cfg[j].addr_out = addr_out[j]; + acp_cfg_tbl[i].ubus_cfg[j].dst_pid = dst_pid[j]; + acp_cfg_tbl[i].ubus_cfg[j].size_shift = size_shift[j]; + acp_cfg_tbl[i].ubus_cfg[j].en = 1; + } + } + } +} + +/* the following are for the proc file control */ +static uint8_t get_ubus_pid_by_proc_dir(struct proc_dir_entry *proc_dir) +{ + uint8_t i, j; + + for (i = 0; i < BCM_UBUS_PID_MAX; i++) { + if (acp_cfg_tbl[i].pmb_addr != 0) { + if (acp_cfg_tbl[i].proc_dir == proc_dir) + return i; + for (j = 0; j < BCM_UBUS_CFG_MAX; j++) { + if (acp_cfg_tbl[i].ubus_cfg[j].proc_dir + == proc_dir) + return i; + } + } + } + return BCM_UBUS_PID_INVALID; +} + +static inline uint8_t get_cfg_id_by_ubus_pid_proc_dir(uint8_t ubus_pid, + struct proc_dir_entry *proc_dir) +{ + uint8_t i; + + for (i = 0; i < BCM_UBUS_CFG_MAX; i++) { + if (acp_cfg_tbl[ubus_pid].ubus_cfg[i].proc_dir == proc_dir) + return i; + } + return BCM_UBUS_CFG_MAX; + +} + +static ssize_t read_proc_acp_en(struct file *f, char *page, size_t cnt, loff_t *off) +{ + uint8_t ubus_pid; + int len = 0; + void *data; + + if(*off != 0) + return 0; + + data = PDE_DATA(file_inode(f)); + if (data == NULL) + return -EINVAL; + + ubus_pid = get_ubus_pid_by_proc_dir((struct proc_dir_entry *)data); + if (ubus_pid == BCM_UBUS_PID_INVALID) + return -EINVAL; + + len = sprintf(page + len, "%d\n", acp_cfg_tbl[ubus_pid].en); + + *off = len; + return len; +} + +static ssize_t write_proc_acp_en(struct file *f, const char __user *buffer, size_t count, loff_t *off) +{ + uint8_t ubus_pid; + char buf[16]; + unsigned long input_val; + int len, ret; + void *data; + + if (count >= sizeof(buf)) + goto WRITE_PROC_ACP_EN_EXIT; + + len = min((unsigned int)count, (sizeof(buf) - 1)); + + if (copy_from_user(buf, buffer, len)) + goto WRITE_PROC_ACP_EN_EXIT; + + buf[len] = '\0'; + if (strict_strtoul(buf, 0, &input_val)) + goto WRITE_PROC_ACP_EN_EXIT; + + data = PDE_DATA(file_inode(f)); + if (data == NULL) + goto WRITE_PROC_ACP_EN_EXIT; + + ubus_pid = get_ubus_pid_by_proc_dir((struct proc_dir_entry *)data); + if (ubus_pid == BCM_UBUS_PID_INVALID) + goto WRITE_PROC_ACP_EN_EXIT; + + if ((uint8_t)input_val == acp_cfg_tbl[ubus_pid].en) { + printk(KERN_WARNING "Nothing has been done\n"); + return count; + } + + if (input_val == 0) { + ret = bcm63xx_acp_disable(ubus_pid); + if (ret == 0) + printk(KERN_WARNING "Done disabling ACP for %s\n", + acp_cfg_tbl[ubus_pid].name); + } else { + ret = bcm63xx_acp_enable(ubus_pid); + if (ret == 0) + printk(KERN_WARNING "Done enabling ACP for %s\n", + acp_cfg_tbl[ubus_pid].name); + } + if (ret) + printk(KERN_WARNING "Fail to configure\n"); + + return count; + +WRITE_PROC_ACP_EN_EXIT: + printk(KERN_WARNING "invalid input value\n"); + return count; +} + +static ssize_t read_proc_acp_ctrl(struct file *f, char *page, size_t cnt, loff_t *off) +{ + uint8_t ubus_pid; + int len = 0; + void *data; + + if(*off != 0) + return 0; + + data = PDE_DATA(file_inode(f)); + if (data == NULL) + return -EINVAL; + + ubus_pid = get_ubus_pid_by_proc_dir((struct proc_dir_entry *)data); + if (ubus_pid == BCM_UBUS_PID_INVALID) + return -EINVAL; + + len += sprintf(page + len, "bit[3-0] = WCACHE, bit[7-4] = RCACHE, bit[12-8] = WUSER, bit[17-13] = RUSER\n"); + len += sprintf(page + len, "0x%lx\n", (unsigned long)acp_cfg_tbl[ubus_pid].acp_ctrl); + + *off = len; + return len; +} + +static ssize_t write_proc_acp_ctrl(struct file *f, const char __user *buffer, size_t count, loff_t *off) +{ + uint8_t ubus_pid; + char buf[16]; + unsigned long input_val; + int len; + void *data; + + if (count >= sizeof(buf)) + goto WRITE_PROC_ACP_EN_EXIT; + + len = min((unsigned int)count, (sizeof(buf) - 1)); + + if (copy_from_user(buf, buffer, len)) + goto WRITE_PROC_ACP_EN_EXIT; + + buf[len] = '\0'; + if (strict_strtoul(buf, 0, &input_val)) + goto WRITE_PROC_ACP_EN_EXIT; + + data = PDE_DATA(file_inode(f)); + if (data == NULL) + goto WRITE_PROC_ACP_EN_EXIT; + + ubus_pid = get_ubus_pid_by_proc_dir((struct proc_dir_entry *)data); + if (ubus_pid == BCM_UBUS_PID_INVALID) + goto WRITE_PROC_ACP_EN_EXIT; + + if ((uint32_t)input_val == acp_cfg_tbl[ubus_pid].acp_ctrl) { + printk(KERN_WARNING "Nothing has been done\n"); + return count; + } + + acp_cfg_tbl[ubus_pid].acp_ctrl = (uint32_t)input_val; + acp_ctrl_set(ubus_pid, acp_cfg_tbl[ubus_pid].en); + printk(KERN_WARNING "Done setting ACP ctrl for %s\n", + acp_cfg_tbl[ubus_pid].name); + + return count; + +WRITE_PROC_ACP_EN_EXIT: + printk(KERN_WARNING "invalid input value\n"); + return count; +} + +static ssize_t read_proc_addr_in(struct file *f, char *page, size_t cnt, loff_t *off) +{ + uint8_t ubus_pid, cfg_id; + int len = 0; + void *data; + + if(*off != 0) + return 0; + + data = PDE_DATA(file_inode(f)); + if (data == NULL) + return -EINVAL; + + ubus_pid = get_ubus_pid_by_proc_dir((struct proc_dir_entry *)data); + if (unlikely(ubus_pid == BCM_UBUS_PID_INVALID)) + return -EINVAL; + + cfg_id = get_cfg_id_by_ubus_pid_proc_dir(ubus_pid, + (struct proc_dir_entry *)data); + if (unlikely(cfg_id >= BCM_UBUS_CFG_MAX)) + return -EINVAL; + + len = sprintf(page + len, "%p\n", + (void *)acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].addr_in); + + *off = len; + return len; +} + +static ssize_t write_proc_addr_in(struct file *f, const char __user *buffer, size_t count, loff_t *off) +{ + uint8_t ubus_pid, cfg_id; + char buf[16]; + unsigned long input_val; + int len, ret; + void *data; + + if (count >= sizeof(buf)) + goto WRITE_PROC_ADDR_IN_EXIT; + + len = min((unsigned int)count, (sizeof(buf) - 1)); + + if (copy_from_user(buf, buffer, len)) + goto WRITE_PROC_ADDR_IN_EXIT; + + buf[len] = '\0'; + if (strict_strtoul(buf, 0, &input_val)) + goto WRITE_PROC_ADDR_IN_EXIT; + + data = PDE_DATA(file_inode(f)); + if (data == NULL) + goto WRITE_PROC_ADDR_IN_EXIT; + + ubus_pid = get_ubus_pid_by_proc_dir((struct proc_dir_entry *)data); + if (ubus_pid == BCM_UBUS_PID_INVALID) + goto WRITE_PROC_ADDR_IN_EXIT; + + cfg_id = get_cfg_id_by_ubus_pid_proc_dir(ubus_pid, + (struct proc_dir_entry *)data); + if (unlikely(cfg_id >= BCM_UBUS_CFG_MAX)) + goto WRITE_PROC_ADDR_IN_EXIT; + + if ((uint32_t)input_val == acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].addr_in) { + printk(KERN_WARNING "Nothing has been done\n"); + return count; + } + + ret = ubus_cfg_entry_set(ubus_pid, cfg_id, (uint32_t)input_val, + acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].addr_out, + acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].dst_pid, + acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].size_shift, + acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].en); + if (ret) { + printk(KERN_WARNING "Fail to configure\n"); + } else { + printk(KERN_WARNING "Done setting the new value\n"); + acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].addr_in = (uint32_t)input_val; + + } + return count; + +WRITE_PROC_ADDR_IN_EXIT: + printk(KERN_WARNING "invalid input value\n"); + return count; +} + +static ssize_t read_proc_addr_out(struct file *f, char *page, size_t cnt, loff_t *off) +{ + uint8_t ubus_pid, cfg_id; + int len = 0; + void *data; + + if(*off != 0) + return 0; + + data = PDE_DATA(file_inode(f)); + if (data == NULL) + return -EINVAL; + + ubus_pid = get_ubus_pid_by_proc_dir((struct proc_dir_entry *)data); + if (unlikely(ubus_pid == BCM_UBUS_PID_INVALID)) + return -EINVAL; + + cfg_id = get_cfg_id_by_ubus_pid_proc_dir(ubus_pid, + (struct proc_dir_entry *)data); + if (unlikely(cfg_id >= BCM_UBUS_CFG_MAX)) + return -EINVAL; + + len = sprintf(page + len, "%p\n", + (void *)acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].addr_out); + + *off = len; + return len; +} + +static ssize_t write_proc_addr_out(struct file *f, const char __user *buffer, size_t count, loff_t *off) +{ + uint8_t ubus_pid, cfg_id; + char buf[16]; + unsigned long input_val; + int len, ret; + void *data; + + if (count >= sizeof(buf)) + goto WRITE_PROC_ADDR_OUT_EXIT; + + len = min((unsigned int)count, (sizeof(buf) - 1)); + + if (copy_from_user(buf, buffer, len)) + goto WRITE_PROC_ADDR_OUT_EXIT; + + buf[len] = '\0'; + if (strict_strtoul(buf, 0, &input_val)) + goto WRITE_PROC_ADDR_OUT_EXIT; + + data = PDE_DATA(file_inode(f)); + if (data == NULL) + goto WRITE_PROC_ADDR_OUT_EXIT; + + ubus_pid = get_ubus_pid_by_proc_dir((struct proc_dir_entry *)data); + if (ubus_pid == BCM_UBUS_PID_INVALID) + goto WRITE_PROC_ADDR_OUT_EXIT; + + cfg_id = get_cfg_id_by_ubus_pid_proc_dir(ubus_pid, + (struct proc_dir_entry *)data); + if (unlikely(cfg_id >= BCM_UBUS_CFG_MAX)) + goto WRITE_PROC_ADDR_OUT_EXIT; + + if ((uint32_t)input_val == acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].addr_out) { + printk(KERN_WARNING "Nothing has been done\n"); + return count; + } + + ret = ubus_cfg_entry_set(ubus_pid, cfg_id, + acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].addr_in, + (uint32_t)input_val, + acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].dst_pid, + acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].size_shift, + acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].en); + if (ret) { + printk(KERN_WARNING "Fail to configure\n"); + } else { + printk(KERN_WARNING "Done setting the new value\n"); + acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].addr_out = (uint32_t)input_val; + + } + return count; + +WRITE_PROC_ADDR_OUT_EXIT: + printk(KERN_WARNING "invalid input value\n"); + return count; +} + +static ssize_t read_proc_dst_pid(struct file *f, char *page, size_t cnt, loff_t *off) +{ + uint8_t ubus_pid, cfg_id; + int len = 0; + void *data; + + if(*off != 0) + return 0; + + data = PDE_DATA(file_inode(f)); + if (data == NULL) + return -EINVAL; + + ubus_pid = get_ubus_pid_by_proc_dir((struct proc_dir_entry *)data); + if (unlikely(ubus_pid == BCM_UBUS_PID_INVALID)) + return -EINVAL; + + cfg_id = get_cfg_id_by_ubus_pid_proc_dir(ubus_pid, + (struct proc_dir_entry *)data); + if (unlikely(cfg_id >= BCM_UBUS_CFG_MAX)) + return -EINVAL; + + len = sprintf(page + len, "%u\n", + (unsigned)acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].dst_pid); + + *off = len; + return len; +} + +static ssize_t write_proc_dst_pid(struct file *f, const char __user *buffer, size_t count, loff_t *off) +{ + uint8_t ubus_pid, cfg_id; + char buf[16]; + unsigned long input_val; + int len, ret; + void *data; + + if (count >= sizeof(buf)) + goto WRITE_PROC_SIZE_SHIFT_EXIT; + + len = min((unsigned int)count, (sizeof(buf) - 1)); + + if (copy_from_user(buf, buffer, len)) + goto WRITE_PROC_SIZE_SHIFT_EXIT; + + buf[len] = '\0'; + if (strict_strtoul(buf, 0, &input_val)) + goto WRITE_PROC_SIZE_SHIFT_EXIT; + + data = PDE_DATA(file_inode(f)); + if (data == NULL) + goto WRITE_PROC_SIZE_SHIFT_EXIT; + + ubus_pid = get_ubus_pid_by_proc_dir((struct proc_dir_entry *)data); + if (ubus_pid == BCM_UBUS_PID_INVALID) + goto WRITE_PROC_SIZE_SHIFT_EXIT; + + cfg_id = get_cfg_id_by_ubus_pid_proc_dir(ubus_pid, + (struct proc_dir_entry *)data); + if (unlikely(cfg_id >= BCM_UBUS_CFG_MAX)) + goto WRITE_PROC_SIZE_SHIFT_EXIT; + + if (acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].dst_pid == + (uint8_t)input_val) { + printk(KERN_WARNING "Nothing has been done\n"); + return count; + } + + ret = ubus_cfg_entry_set(ubus_pid, cfg_id, + acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].addr_in, + acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].addr_out, + (uint8_t)input_val, + acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].size_shift, + acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].en); + if (ret) { + printk(KERN_WARNING "Fail to configure\n"); + } else { + printk(KERN_WARNING "Done setting the new value\n"); + acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].dst_pid = + (uint8_t)input_val; + + } + return count; + +WRITE_PROC_SIZE_SHIFT_EXIT: + printk(KERN_WARNING "invalid input value\n"); + return count; +} + +static ssize_t read_proc_size_shift(struct file *f, char *page, size_t cnt, loff_t *off) +{ + uint8_t ubus_pid, cfg_id; + int len = 0; + void *data; + + if(*off != 0) + return 0; + + data = PDE_DATA(file_inode(f)); + if (data == NULL) + return -EINVAL; + + ubus_pid = get_ubus_pid_by_proc_dir((struct proc_dir_entry *)data); + if (unlikely(ubus_pid == BCM_UBUS_PID_INVALID)) + return -EINVAL; + + cfg_id = get_cfg_id_by_ubus_pid_proc_dir(ubus_pid, + (struct proc_dir_entry *)data); + if (unlikely(cfg_id >= BCM_UBUS_CFG_MAX)) + return -EINVAL; + + len = sprintf(page + len, "%u\n", + (unsigned)acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].size_shift); + + *off = len; + return len; +} + +static ssize_t write_proc_size_shift(struct file *f, const char __user *buffer, size_t count, loff_t *off) +{ + uint8_t ubus_pid, cfg_id; + char buf[16]; + unsigned long input_val; + int len, ret; + void *data; + + if (count >= sizeof(buf)) + goto WRITE_PROC_SIZE_SHIFT_EXIT; + + len = min((unsigned int)count, (sizeof(buf) - 1)); + + if (copy_from_user(buf, buffer, len)) + goto WRITE_PROC_SIZE_SHIFT_EXIT; + + buf[len] = '\0'; + if (strict_strtoul(buf, 0, &input_val)) + goto WRITE_PROC_SIZE_SHIFT_EXIT; + + data = PDE_DATA(file_inode(f)); + if (data == NULL) + goto WRITE_PROC_SIZE_SHIFT_EXIT; + + ubus_pid = get_ubus_pid_by_proc_dir((struct proc_dir_entry *)data); + if (ubus_pid == BCM_UBUS_PID_INVALID) + goto WRITE_PROC_SIZE_SHIFT_EXIT; + + cfg_id = get_cfg_id_by_ubus_pid_proc_dir(ubus_pid, + (struct proc_dir_entry *)data); + if (unlikely(cfg_id >= BCM_UBUS_CFG_MAX)) + goto WRITE_PROC_SIZE_SHIFT_EXIT; + + if (acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].size_shift == + (uint8_t)input_val) { + printk(KERN_WARNING "Nothing has been done\n"); + return count; + } + + ret = ubus_cfg_entry_set(ubus_pid, cfg_id, + acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].addr_in, + acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].addr_out, + acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].dst_pid, + (uint8_t)input_val, + acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].en); + if (ret) { + printk(KERN_WARNING "Fail to configure\n"); + } else { + printk(KERN_WARNING "Done setting the new value\n"); + acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].size_shift = + (uint8_t)input_val; + + } + return count; + +WRITE_PROC_SIZE_SHIFT_EXIT: + printk(KERN_WARNING "invalid input value\n"); + return count; +} + +static ssize_t read_proc_cfg_en(struct file *f, char *page, size_t cnt, loff_t *off) +{ + uint8_t ubus_pid, cfg_id; + int len = 0; + void *data; + + if(*off != 0) + return 0; + + data = PDE_DATA(file_inode(f)); + if (data == NULL) + return -EINVAL; + + ubus_pid = get_ubus_pid_by_proc_dir((struct proc_dir_entry *)data); + if (unlikely(ubus_pid == BCM_UBUS_PID_INVALID)) + return -EINVAL; + + cfg_id = get_cfg_id_by_ubus_pid_proc_dir(ubus_pid, + (struct proc_dir_entry *)data); + if (unlikely(cfg_id >= BCM_UBUS_CFG_MAX)) + return -EINVAL; + + len = sprintf(page + len, "%u\n", + (unsigned)acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].en); + + *off = len; + return len; +} + +static ssize_t write_proc_cfg_en(struct file *f, const char __user *buffer, size_t count, loff_t *off) +{ + uint8_t ubus_pid, cfg_id; + char buf[16]; + unsigned long input_val; + int len, ret; + void *data; + + if (count >= sizeof(buf)) + goto WRITE_PROC_ENABLE_EXIT; + + len = min((unsigned int)count, (sizeof(buf) - 1)); + + if (copy_from_user(buf, buffer, len)) + goto WRITE_PROC_ENABLE_EXIT; + + buf[len] = '\0'; + if (strict_strtoul(buf, 0, &input_val)) + goto WRITE_PROC_ENABLE_EXIT; + + data = PDE_DATA(file_inode(f)); + if (data == NULL) + goto WRITE_PROC_ENABLE_EXIT; + + ubus_pid = get_ubus_pid_by_proc_dir((struct proc_dir_entry *)data); + if (ubus_pid == BCM_UBUS_PID_INVALID) + goto WRITE_PROC_ENABLE_EXIT; + + cfg_id = get_cfg_id_by_ubus_pid_proc_dir(ubus_pid, + (struct proc_dir_entry *)data); + if (unlikely(cfg_id >= BCM_UBUS_CFG_MAX)) + goto WRITE_PROC_ENABLE_EXIT; + + if (acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].en == (uint8_t)input_val) { + printk(KERN_WARNING "Nothing has been done\n"); + return count; + } + + ret = ubus_cfg_entry_set(ubus_pid, cfg_id, + acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].addr_in, + acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].addr_out, + acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].dst_pid, + acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].size_shift, + (uint8_t)input_val); + if (ret) { + printk(KERN_WARNING "Fail to configure\n"); + } else { + printk(KERN_WARNING "Done setting the new value\n"); + acp_cfg_tbl[ubus_pid].ubus_cfg[cfg_id].en = (uint8_t)input_val; + + } + return count; + +WRITE_PROC_ENABLE_EXIT: + printk(KERN_WARNING "invalid input value\n"); + return count; +} + +static struct file_operations acp_en_proc_fops = { + .read = read_proc_acp_en, + .write = write_proc_acp_en, +}; + +static struct file_operations acp_ctrl_proc_fops = { + .read = read_proc_acp_ctrl, + .write = write_proc_acp_ctrl, +}; + +static struct file_operations addr_in_proc_fops = { + .read = read_proc_addr_in, + .write = write_proc_addr_in, +}; + +static struct file_operations addr_out_proc_fops = { + .read = read_proc_addr_out, + .write = write_proc_addr_out, +}; + +static struct file_operations dst_pid_proc_fops = { + .read = read_proc_dst_pid, + .write = write_proc_dst_pid, +}; + +static struct file_operations size_shift_proc_fops = { + .read = read_proc_size_shift, + .write = write_proc_size_shift, +}; + +static struct file_operations cfg_en_proc_fops = { + .read = read_proc_cfg_en, + .write = write_proc_cfg_en, +}; + +static void create_proc_dir_file(uint8_t ubus_pid) +{ + int i; + char buff[10]; + acp_cfg_entry_t *cur_cfg = &acp_cfg_tbl[ubus_pid]; + struct proc_dir_entry *res; + + if (cur_cfg->proc_dir == NULL) + cur_cfg->proc_dir = proc_mkdir(cur_cfg->name, proc_acp_dir); + + if (cur_cfg->proc_dir == NULL) { + printk(KERN_ERR "fail to create proc dir (%s)\n", + cur_cfg->name); + return; + } + + /* proc file for acp_en */ + res = proc_create_data("acp_enable", S_IRUGO | S_IWUGO, cur_cfg->proc_dir, + &acp_en_proc_fops, (void *)cur_cfg->proc_dir); + + if (!res) { + printk(KERN_ERR "fail to create proc file (%s)" + "->acp_enable\n", cur_cfg->name); + } + + /* proc file for acp_ctrl */ + res = proc_create_data("acp_ctrl", S_IRUGO | S_IWUGO, cur_cfg->proc_dir, + &acp_ctrl_proc_fops, (void *)cur_cfg->proc_dir); + + if (!res) { + printk(KERN_ERR "fail to create proc file (%s)" + "->acp_ctrl\n", cur_cfg->name); + } + + for (i = 0; i < BCM_UBUS_CFG_MAX; i++) { + if (cur_cfg->ubus_cfg[i].proc_dir == NULL) { + sprintf(buff, "cfg%d", i); + cur_cfg->ubus_cfg[i].proc_dir = proc_mkdir(buff, + cur_cfg->proc_dir); + } + + /* supposedly shouldn't happen */ + if (unlikely(cur_cfg->ubus_cfg[i].proc_dir == NULL)) { + printk(KERN_ERR "fail to create proc dir (%s)\n", buff); + return; + } + + /* proc file for addr_in */ + res = proc_create_data("addr_in", S_IRUGO | S_IWUGO, cur_cfg->ubus_cfg[i].proc_dir, + &addr_in_proc_fops, (void *)cur_cfg->ubus_cfg[i].proc_dir); + + if (!res) { + printk(KERN_ERR "fail to create proc file (%s)" + "->addr_in\n", buff); + } + + /* proc file for addr_out */ + res = proc_create_data("addr_out", S_IRUGO | S_IWUGO, cur_cfg->ubus_cfg[i].proc_dir, + &addr_out_proc_fops, (void *)cur_cfg->ubus_cfg[i].proc_dir); + + if (!res) { + printk(KERN_ERR "fail to create proc file (%s)" + "->addr_out\n", buff); + } + + /* proc file for dst_pid */ + res = proc_create_data("dst_pid", S_IRUGO | S_IWUGO, cur_cfg->ubus_cfg[i].proc_dir, + &dst_pid_proc_fops, (void *)cur_cfg->ubus_cfg[i].proc_dir); + + if (!res) { + printk(KERN_ERR "fail to create proc file (%s)" + "->dst_pid\n", buff); + } + + /* proc file for size_shift */ + res = proc_create_data("size_shift", S_IRUGO | S_IWUGO, cur_cfg->ubus_cfg[i].proc_dir, + &size_shift_proc_fops, (void *)cur_cfg->ubus_cfg[i].proc_dir); + + if (!res) { + printk(KERN_ERR "fail to create proc file (%s)" + "->size_shift\n", buff); + } + + /* proc file for enable */ + res = proc_create_data("config_enable", S_IRUGO | S_IWUGO, cur_cfg->ubus_cfg[i].proc_dir, + &cfg_en_proc_fops, (void *)cur_cfg->ubus_cfg[i].proc_dir); + + if (!res) { + printk(KERN_ERR "fail to create proc file (%s)" + "->en\n", buff); + } + } +} + +static void remove_proc_dir_file(uint8_t ubus_pid) +{ + int i; + char buff[10]; + acp_cfg_entry_t *cur_cfg = &acp_cfg_tbl[ubus_pid]; + + for (i = 0; i < BCM_UBUS_CFG_MAX; i++) { + remove_proc_entry("addr_in", cur_cfg->ubus_cfg[i].proc_dir); + remove_proc_entry("addr_out", cur_cfg->ubus_cfg[i].proc_dir); + remove_proc_entry("dst_pid", cur_cfg->ubus_cfg[i].proc_dir); + remove_proc_entry("size_shift", cur_cfg->ubus_cfg[i].proc_dir); + remove_proc_entry("config_enable", cur_cfg->ubus_cfg[i].proc_dir); + sprintf(buff, "cfg%d", i); + remove_proc_entry(buff, cur_cfg->proc_dir); + } + remove_proc_entry("acp_enable", cur_cfg->proc_dir); + remove_proc_entry("acp_ctrl", cur_cfg->proc_dir); + remove_proc_entry(cur_cfg->name, proc_acp_dir); +} + +static void acp_proc_file_init(void) +{ + uint8_t i; + + if (proc_acp_dir == NULL) + proc_acp_dir = proc_mkdir("driver/acp", NULL); + + if (proc_acp_dir == NULL) { + printk(KERN_ERR "fail to create proc dir driver/acp\n"); + return; + } + + for (i = 0; i < BCM_UBUS_PID_MAX; i++) { + if (acp_cfg_tbl[i].pmb_addr != 0) + create_proc_dir_file(i); + } +} + +static void acp_proc_file_deinit(void) +{ + uint8_t i; + for (i = 0; i < BCM_UBUS_PID_MAX; i++) { + if (acp_cfg_tbl[i].pmb_addr != 0) + remove_proc_dir_file(i); + } + + if (proc_acp_dir) + remove_proc_entry("driver/acp", NULL); + proc_acp_dir = NULL; +} + +static void acp_cfg_tbl_deinit(void) +{ + memset(acp_cfg_tbl, 0x0, BCM_UBUS_PID_MAX * sizeof(acp_cfg_entry_t)); +} + +/* size will be in the multiple of MB */ +/* some limitations of the resulted value. For each entry, addr must be in + * multiple of the size. An invalid example is, if addr is 0x800000, and size + * is 0x1600000. HW will not be able to process it. */ +static int ubus_cfg_convert(uint32_t addr_start, uint32_t size, + uint32_t *addr_in, uint32_t *addr_out, + uint8_t *dst_pid, uint8_t *size_shift) +{ + int used = 0, cur_bit_to_add; + uint32_t added_size = 0, extra_added = 0; + + /* first method, go from the least significant bit set in the address + * to add the size into the table. Then go from remaining size to add + * from the most significant bit */ + do { + cur_bit_to_add = ffs((addr_start + added_size)) - 1; + if ((0x1 << cur_bit_to_add) > (size - added_size)) + cur_bit_to_add = fls((size - added_size)) - 1; + addr_in[used] = addr_start + added_size; + addr_out[used] = addr_start + added_size; + dst_pid[used] = BCM_UBUS_PID_ARMAXIACP; + size_shift[used] = cur_bit_to_add; + added_size += 0x1 << cur_bit_to_add; + used++; + } while ((used < 4) && ((size - added_size) != 0)); + + if ((size - added_size) == 0) + return used; + + /* second method, add the total and subtract those that should go DDR */ + added_size = 0; + extra_added = 0; + used = 0; + do { + if (extra_added != 0) { + cur_bit_to_add = fls(extra_added) - 1; + if ((cur_bit_to_add >= 2) && + ((0x1 << cur_bit_to_add) & extra_added) && + ((0x1 << (cur_bit_to_add - 1)) & extra_added) && + ((0x1 << (cur_bit_to_add - 2)) & extra_added)) { + cur_bit_to_add++; + extra_added = 0; + } else { + extra_added -= 0x1 << cur_bit_to_add; + } + added_size -= 0x1 << cur_bit_to_add; + addr_in[used] = added_size; + addr_out[used] = added_size; + dst_pid[used] = BCM_UBUS_PID_DDR; + size_shift[used] = cur_bit_to_add; + used++; + } else { + cur_bit_to_add = fls((addr_start + size - added_size)) - 1; + if (0x1 << (cur_bit_to_add - 1) & (addr_start + size - added_size)) { + cur_bit_to_add++; + extra_added = (0x1 << cur_bit_to_add) + added_size - addr_start - size; + } + addr_in[used] = added_size; + addr_out[used] = added_size; + dst_pid[used] = BCM_UBUS_PID_ARMAXIACP; + size_shift[used] = cur_bit_to_add; + added_size += 0x1 << cur_bit_to_add; + used++; + } + } while ((used < 4) && ((addr_start + size) != added_size)); + + if ((addr_start + size) != added_size) { + printk("BCM63XX ACP ERROR!: please define a new ACP_MEM_SIZE\n"); + return -1; + } + + added_size = 0; + while ((used < 4) && (addr_start - added_size)) { + cur_bit_to_add = fls((addr_start - added_size)) - 1; + addr_in[used] = added_size; + addr_out[used] = added_size; + dst_pid[used] = BCM_UBUS_PID_DDR; + size_shift[used] = cur_bit_to_add; + added_size += 0x1 << cur_bit_to_add; + used++; + } + + if ((addr_start - added_size) == 0) + return used; + + /* TODO: Maybe other way to fill the table entry? */ + + printk("BCM63XX ACP ERROR!: please define a new ACP_MEM_SIZE\n"); + return -1; +} + +int bcm63xx_acp_init(void) +{ + uint32_t addr_in[4], addr_out[4]; + uint8_t size_shift[4], dst_pid[4]; + int entry_use; + struct zone *acp_zone = &NODE_DATA(0)->node_zones[ZONE_ACP]; + + printk("BCM63XX ACP: zone_acp start at 0x%08lx of size %d MB\n", + (acp_zone->zone_start_pfn << PAGE_SHIFT), + CONFIG_BCM_ACP_MEM_SIZE); + + memset(addr_in, 0, sizeof(uint32_t) << 2); + memset(addr_out, 0, sizeof(uint32_t) << 2); + memset(size_shift, 0, sizeof(uint8_t) << 2); + memset(dst_pid, 0, sizeof(uint8_t) << 2); + entry_use = ubus_cfg_convert(acp_zone->zone_start_pfn << PAGE_SHIFT, + CONFIG_BCM_ACP_MEM_SIZE * SZ_1M, addr_in, addr_out, dst_pid, + size_shift); + + if (entry_use == -1) + return -EPERM; + + acp_cfg_tbl_init(entry_use, addr_in, addr_out, dst_pid, size_shift); + acp_proc_file_init(); + + bcm63xx_acp_enable(BCM_UBUS_PID_RNR); + bcm63xx_acp_enable(BCM_UBUS_PID_RNR_RABR); + bcm63xx_acp_enable(BCM_UBUS_PID_RNR_RBBR); + bcm63xx_acp_enable(BCM_UBUS_PID_SAR); + bcm63xx_acp_enable(BCM_UBUS_PID_SAR2); + return 0; +} + +void bcm63xx_acp_exit(void) +{ + bcm63xx_acp_disable(BCM_UBUS_PID_RNR); + bcm63xx_acp_disable(BCM_UBUS_PID_RNR_RABR); + bcm63xx_acp_disable(BCM_UBUS_PID_RNR_RBBR); + bcm63xx_acp_disable(BCM_UBUS_PID_SAR); + bcm63xx_acp_disable(BCM_UBUS_PID_SAR2); + + acp_proc_file_deinit(); + acp_cfg_tbl_deinit(); +} + +module_init(bcm63xx_acp_init); +module_exit(bcm63xx_acp_exit); +#endif /* defined(CONFIG_BCM_KF_ARM_BCM963XX) */ diff -urN linux-4.1.52-orig/arch/arm/plat-bcm63xx/bcm63xx_m2mdma.c linux-4.1.52/arch/arm/plat-bcm63xx/bcm63xx_m2mdma.c --- linux-4.1.52-orig/arch/arm/plat-bcm63xx/bcm63xx_m2mdma.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/plat-bcm63xx/bcm63xx_m2mdma.c 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,488 @@ +/* +<:copyright-BRCM:2015:DUAL/GPL:standard + + Copyright (c) 2015 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bcm_map_part.h" + + +#define M2M_DMA_REG_BASE BCM_IO_ADDR(M2M_DMA_PHYS_BASE) + +typedef struct { + + uint32_t ch0_desc_status; + uint32_t ch1_desc_status; + uint32_t ch2_desc_status; + uint32_t ch3_desc_status; + + uint32_t ch0_src_addr; + uint32_t ch0_dest_addr; + uint32_t ch0_desc_id; + uint32_t ch0_dma_config; + + uint32_t ch1_src_addr; + uint32_t ch1_dest_addr; + uint32_t ch1_desc_id; + uint32_t ch1_dma_config; + + uint32_t ch2_src_addr; + uint32_t ch2_dest_addr; + uint32_t ch2_desc_id; + uint32_t ch2_dma_config; + + uint32_t ch3_src_addr; + uint32_t ch3_dest_addr; + uint32_t ch3_desc_id; + uint32_t ch3_dma_config; + + uint32_t int_clear; + uint32_t control; + uint32_t dma_status; + uint32_t ch_stop; + uint32_t desc_clear; + + uint32_t ch0_ubus_err_debug0; + uint32_t ch0_ubus_err_debug1; + + uint32_t ch1_ubus_err_debug0; + uint32_t ch1_ubus_err_debug1; + + uint32_t ch2_ubus_err_debug0; + uint32_t ch2_ubus_err_debug1; + + uint32_t ch3_ubus_err_debug0; + uint32_t ch3_ubus_err_debug1; + + uint32_t ch0_stop_src_addr; + uint32_t ch0_stop_dest_addr; + uint32_t ch0_stop_addr_msb; + + uint32_t ch1_stop_src_addr; + uint32_t ch1_stop_dest_addr; + uint32_t ch1_stop_addr_msb; + + uint32_t ch2_stop_src_addr; + uint32_t ch2_stop_dest_addr; + uint32_t ch2_stop_addr_msb; + + uint32_t ch3_stop_src_addr; + uint32_t ch3_stop_dest_addr; + uint32_t ch3_stop_addr_msb; + + uint32_t ch0_status_id_fifo; + uint32_t ch1_status_id_fifo; + uint32_t ch2_status_id_fifo; + uint32_t ch3_status_id_fifo; + + uint32_t spare0; + uint32_t spare1; + uint32_t spare2; +} bcm_m2m_dma_reg_t; + +#define M2M_DMA_REG ((volatile bcm_m2m_dma_reg_t * const) M2M_DMA_REG_BASE) + + +#define MAX_ASYNC_DMA_CHNLS 4 +#define MAX_M2M_CHNL_QUEUE_DEPTH 8 + +typedef struct { + volatile uint32_t src_addr; + volatile uint32_t dest_addr; + volatile uint32_t desc_id; + volatile uint32_t dma_config; +}m2m_dma_desc_t; + + +typedef struct { + m2m_dma_desc_t *dma_desc; + volatile uint32_t *desc_status; + uint32_t enable_mask; + uint16_t desc_id; + uint8_t chnl_idx; + uint8_t avail_desc; +}m2m_dma_chanl_t; + +typedef struct { + m2m_dma_chanl_t async_chnls[MAX_ASYNC_DMA_CHNLS]; + spinlock_t async_chnl_lock; + uint8_t cur_async_chnl_idx; +}bcm_m2m_dma_t; + +static bcm_m2m_dma_t bcm_m2m_dma; + + +#define M2M_UBUS_BURST_SIZE_128 0x100000 /*128 bytes*/ +#define M2M_DMA_LEN_MASK 0x0FFFFF + +/* DMA channels enable mask with 1 oustanding UBUS request */ + +#define DMA_CHANL0_ENABLE_MASK 0x01 +#define DMA_CHANL1_ENABLE_MASK 0x02 +#define DMA_CHANL2_ENABLE_MASK 0x004 +#define DMA_CHANL3_ENABLE_MASK 0x008 + +#define M2M_ASYNC_LOCK() spin_lock(&bcm_m2m_dma.async_chnl_lock) +#define M2M_ASYNC_UNLOCK() spin_unlock(&bcm_m2m_dma.async_chnl_lock) + + + +/* + * Find a DMA channel with a free descriptor slot + * caller should acquire lock +*/ +static inline m2m_dma_chanl_t * get_free_dma_channel_async(void) +{ + uint8_t chnl = bcm_m2m_dma.cur_async_chnl_idx; + m2m_dma_chanl_t *m2m_dma_chnl; + int i; + + /* here <= is needed to check the starting channel twice before returning NULL */ + for(i=0; i<=MAX_ASYNC_DMA_CHNLS; i++) + { + chnl = (chnl+1) % MAX_ASYNC_DMA_CHNLS; + m2m_dma_chnl = &bcm_m2m_dma.async_chnls[chnl]; + if(m2m_dma_chnl->avail_desc) + { + //printk("channel=%d, avail_desc=%d\n",chnl, m2m_dma_chnl->avail_desc); + m2m_dma_chnl->avail_desc--; + bcm_m2m_dma.cur_async_chnl_idx = chnl; + return m2m_dma_chnl; + } + + /*read num of free descriptors from HW and update the avil_desc*/ + m2m_dma_chnl->avail_desc = *m2m_dma_chnl->desc_status & 0xFF; + } + return NULL; +} + +/* + * check if a given transcation and all the transactions before it are completed + * + * id: bits 0-15 desc_id + * id: 16-17 channel num + */ +static inline int bcm_m2m_is_async_dma_done(uint32_t id) +{ + volatile uint32_t busy; + int i; + uint16_t cur_desc_id; + uint16_t desc_id; + uint8_t chnl = (id >> 16) & (MAX_ASYNC_DMA_CHNLS-1); + + /* first check if M2M is idle */ + busy = M2M_DMA_REG->dma_status & 0xf; + if(!busy) + return 1; + + + /* here given an id we need to find out if the corresponding transaction and + * all the transcations before it in other channels are completed + * + * each channel maintains it's own desc_id, but since transactions are + * scheduled in round robin fashion among channels, + * for the channels before a given chnl we expect cur_desc_id >= desc_id + * and for the channels after a given chnl we expect cur_desc_id >=desc_id-1 + * + * any holes will be catched by M2M idle check + */ + + busy=0; + for(i=0; i<=MAX_ASYNC_DMA_CHNLS; i++) + { + cur_desc_id = *bcm_m2m_dma.async_chnls[i].desc_status >> 16; + + desc_id = id & 0xFFFF; /* 16 bits */ + if(i > chnl) + { + desc_id--; + } + + if(cur_desc_id < desc_id) + { + busy=1; + break; + } + else if((cur_desc_id + MAX_M2M_CHNL_QUEUE_DEPTH) >= desc_id) + { + /*no rollover */ + busy=1; + break; + } + } + + return (!busy); +} + +#define MAX_WAIT_LOOP_COUNT 50000 + + +/* bcm_m2m_wait_for_complete: + * + * given a transcation this function checks if the corresponding DMA transaction + * and all the transactions before it are completed + * + * desc_id - DMA transaction to check + * + * returns non-zero value if DMA is complete, zero if DMA is still pending + */ +int bcm_m2m_wait_for_complete(uint32_t desc_id) +{ + int i = MAX_WAIT_LOOP_COUNT +1; + + /*dont wait indefinitely */ + while(--i && !bcm_m2m_is_async_dma_done(desc_id)); + + if(i == 0) + { + printk(KERN_WARNING"%s: M2M transaction %x has not yet completed\n", __func__, desc_id); + } + + return i; +} +EXPORT_SYMBOL(bcm_m2m_wait_for_complete); + +static inline void queue_m2m_transfer(m2m_dma_desc_t *dma_desc, uint32_t phys_dest, + uint32_t phys_src, uint32_t desc_id, uint16_t len) +{ + dma_desc->src_addr = phys_src; + dma_desc->dest_addr = phys_dest; + dma_desc->desc_id = desc_id; + dma_desc->dma_config = M2M_UBUS_BURST_SIZE_128 | (len & M2M_DMA_LEN_MASK); +} + +/* caller must ensure len is maximum of 16 bits only */ +/* caller must ensure src & dest are in contiguos physical memory */ +static inline uint32_t __bcm_m2m_dma_memcpy_async(void *dest, void *src, uint16_t len) +{ + m2m_dma_chanl_t *m2m_dma_chnl; + uint32_t phys_src; + uint32_t phys_dest; + uint32_t desc_id; + + phys_src = virt_to_phys(src); + phys_dest = virt_to_phys(dest); + + M2M_ASYNC_LOCK(); + + do{ + + m2m_dma_chnl = get_free_dma_channel_async(); + + if(m2m_dma_chnl) + { + desc_id = m2m_dma_chnl->desc_id++; + + queue_m2m_transfer(m2m_dma_chnl->dma_desc, phys_dest, phys_src, + desc_id<<16, len); + + M2M_ASYNC_UNLOCK(); + } + else + { + /* Instead of waiting fallback to memcpy if cache lines are + * not shared by dest. This check is needed to avoid corruption + * when both DMA & CPU try to use same cache line + */ + if(!(((uint32_t)dest & (L2_CACHE_LINE_SIZE - 1)) || (len % L2_CACHE_LINE_SIZE))) + { + /*get a channel pointer -needed just for a desc_id*/ + m2m_dma_chnl = &bcm_m2m_dma.async_chnls[bcm_m2m_dma.cur_async_chnl_idx]; + desc_id = m2m_dma_chnl->desc_id -1; + + M2M_ASYNC_UNLOCK(); + + + memcpy(dest, src, len); + /*flush dest to make it look like DMA copy to caller */ + dma_map_single(NULL, dest, len, DMA_TO_DEVICE); + } + } + } while(!m2m_dma_chnl); + + return ((m2m_dma_chnl->chnl_idx << 16) | desc_id ); +} + +/* bcm_m2m_dma_memcpy_async: + * use this function with cached memory + * here we flush src & invalidate dest before scheduling the transfer + * + * + * dest - virtual address of destination + * src - virtual address of destination + * len - length of data to be copied + * + * this function expects src & dest to be in contiguos physcial memory + * + * returns a transaction id for the DMA operation, + * copy is not complete on return, caller has to explicitly check if + * transaction is completed + */ +uint32_t bcm_m2m_dma_memcpy_async(void *dest, void *src, uint16_t len) +{ + /* TODO do we need to call dma_unmap_single for NULL device */ + dma_map_single(NULL, src, len, DMA_TO_DEVICE); + dma_map_single(NULL, dest, len, DMA_FROM_DEVICE); + + return __bcm_m2m_dma_memcpy_async(dest, src, len); +} +EXPORT_SYMBOL(bcm_m2m_dma_memcpy_async); + + +/* bcm_m2m_dma_memcpy_async_no_flush: + * use this function with cached memory + * here there is no cache flush of src, use this function + * when you are sure that src is not dirty in cache + * + * dest - virtual address of destination + * src - virtual address of destination + * len - length of data to be copied + * + * this function expects src & dest to be in contiguos physcial memory + * + * returns a transaction id for the DMA operation, + * copy is not complete on return, caller has to explicitly check if + * transaction is completed + */ +uint32_t bcm_m2m_dma_memcpy_async_no_flush(void *dest, void *src, uint16_t len) +{ + /* TODO do we need to call dma_unmap_single for NULL device */ + dma_map_single(NULL, src, len, DMA_TO_DEVICE); + + return __bcm_m2m_dma_memcpy_async(dest, src, len); +} +EXPORT_SYMBOL(bcm_m2m_dma_memcpy_async_no_flush); + +/* bcm_m2m_dma_memcpy_async_no_flush_inv: + * Here there is no cache flush of src,and also there is no invalidate on dest + * use this when you are that src is not dirty in cache & dest is not in cache + * + * dest - virtual address of destination + * src - virtual address of destination + * len - length of data to be copied + * + * this function expects src & dest to be in contiguos physcial memory + * + * returns a transaction id for the DMA operation, + * copy is not complete on return, caller has to explicitly check if + * transaction is completed + */ +uint32_t bcm_m2m_dma_memcpy_async_no_flush_inv(void *dest, void *src, uint16_t len) +{ + return __bcm_m2m_dma_memcpy_async(dest, src, len); +} +EXPORT_SYMBOL(bcm_m2m_dma_memcpy_async_no_flush_inv); + + +/* bcm_m2m_dma_memcpy_async_uncached: + * use with uncached memory, caller has to pass physical addresses + * + * phys_dest - physical address of destination + * phys_src - virtual address of destination + * len - length of data to be copied + * + * + * returns a transaction id for the DMA operation, + * copy is not complete on return, caller has to explicitly check if + * transaction is completed + */ +uint32_t bcm_m2m_dma_memcpy_async_uncached(uint32_t phys_dest, uint32_t phys_src, uint16_t len) +{ + m2m_dma_chanl_t *m2m_dma_chnl; + uint32_t desc_id; + + M2M_ASYNC_LOCK(); + + do + { + m2m_dma_chnl = get_free_dma_channel_async(); + + if(m2m_dma_chnl) + { + desc_id = m2m_dma_chnl->desc_id++; + + queue_m2m_transfer(m2m_dma_chnl->dma_desc, phys_dest, phys_src, + desc_id<<16, len); + + M2M_ASYNC_UNLOCK(); + } + + } while(!m2m_dma_chnl); + + return ((m2m_dma_chnl->chnl_idx << 16) | desc_id ); +} +EXPORT_SYMBOL(bcm_m2m_dma_memcpy_async_uncached); + +static __init int bcm_m2m_dma_init(void) +{ + spin_lock_init(&bcm_m2m_dma.async_chnl_lock); + + bcm_m2m_dma.async_chnls[0].dma_desc = (m2m_dma_desc_t *)&M2M_DMA_REG->ch0_src_addr; + bcm_m2m_dma.async_chnls[0].desc_status = &M2M_DMA_REG->ch0_desc_status; + bcm_m2m_dma.async_chnls[0].chnl_idx = 0; + bcm_m2m_dma.async_chnls[0].desc_id = 0; + bcm_m2m_dma.async_chnls[0].avail_desc = M2M_DMA_REG->ch0_desc_status & 0xFF; + bcm_m2m_dma.async_chnls[0].enable_mask = DMA_CHANL0_ENABLE_MASK; + + bcm_m2m_dma.async_chnls[1].dma_desc = (m2m_dma_desc_t *)&M2M_DMA_REG->ch1_src_addr; + bcm_m2m_dma.async_chnls[1].desc_status = &M2M_DMA_REG->ch1_desc_status; + bcm_m2m_dma.async_chnls[1].chnl_idx = 1; + bcm_m2m_dma.async_chnls[1].desc_id = 0; + bcm_m2m_dma.async_chnls[1].avail_desc = M2M_DMA_REG->ch1_desc_status & 0xFF; + bcm_m2m_dma.async_chnls[1].enable_mask = DMA_CHANL1_ENABLE_MASK; + + bcm_m2m_dma.async_chnls[2].dma_desc = (m2m_dma_desc_t *)&M2M_DMA_REG->ch2_src_addr; + bcm_m2m_dma.async_chnls[2].desc_status = &M2M_DMA_REG->ch2_desc_status; + bcm_m2m_dma.async_chnls[2].chnl_idx = 2; + bcm_m2m_dma.async_chnls[2].desc_id = 0; + bcm_m2m_dma.async_chnls[2].avail_desc = M2M_DMA_REG->ch2_desc_status & 0xFF; + bcm_m2m_dma.async_chnls[2].enable_mask = DMA_CHANL2_ENABLE_MASK; + + bcm_m2m_dma.async_chnls[3].dma_desc = (m2m_dma_desc_t *)&M2M_DMA_REG->ch3_src_addr; + bcm_m2m_dma.async_chnls[3].desc_status = &M2M_DMA_REG->ch3_desc_status; + bcm_m2m_dma.async_chnls[3].chnl_idx = 3; + bcm_m2m_dma.async_chnls[3].desc_id = 0; + bcm_m2m_dma.async_chnls[3].avail_desc = M2M_DMA_REG->ch3_desc_status & 0xFF; + bcm_m2m_dma.async_chnls[3].enable_mask = DMA_CHANL3_ENABLE_MASK; + + bcm_m2m_dma.cur_async_chnl_idx=0; + + M2M_DMA_REG->control = DMA_CHANL0_ENABLE_MASK | DMA_CHANL1_ENABLE_MASK + | DMA_CHANL2_ENABLE_MASK | DMA_CHANL3_ENABLE_MASK; + + printk(KERN_DEBUG "+++ Successfully registered M2M DMA\n"); + return 0; +} + +arch_initcall(bcm_m2m_dma_init); diff -urN linux-4.1.52-orig/arch/arm/plat-bcm63xx/bcm63xx_timer.c linux-4.1.52/arch/arm/plat-bcm63xx/bcm63xx_timer.c --- linux-4.1.52-orig/arch/arm/plat-bcm63xx/bcm63xx_timer.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/plat-bcm63xx/bcm63xx_timer.c 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,271 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + +/* + * BCM63xx SoC timer implementation based on external PERIPH Timer + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * timer implementations for clocksource and clockevent + * We will use 2 PERIPH timers, one for clocksource and one for + * clockevent. + */ +int timer_cs_used = -1; /* for clock source */ +DEFINE_PER_CPU(int, timer_ce_used) = -1;/* for clock event */ +DEFINE_PER_CPU(struct clock_event_device, clockevent_timer); + +uint32_t perclk_freq = 50000; /* in KHz, value is 50MHz */ +/* the below timer value will convert into the larger timercount supported + * in PERIPH_TIMER: 30-bit PERIPH_TIMER at 50MHz support up 0x40000000/50 + * = 21474836us */ +#define PERIPH_TIMER_PERIOD_MAX (TIMER_CNT_MAX/(perclk_freq/1000)) + + +static notrace cycle_t bcm63xx_read_timer_count(struct clocksource *cs) +{ + int ret; + uint64_t count; /* cycle_t is 64 bit integer */ + + if (timer_cs_used == -1) + return 0; + + ret = ext_timer_read_count(timer_cs_used, &count); + if (ret == 0) + return (cycle_t)count; + else + return 0; +} + +notrace u64 bcm63xx_sched_clock_read(void) +{ + int ret; + uint64_t count; + + if (timer_cs_used == -1) + return 0; + + ret = ext_timer_read_count(timer_cs_used, &count); + if (ret == 0) + return count; + else + return 0; +} + +static struct clocksource bcm63xx_clocksource = { + .name = "timer_cs", + .rating = 350, + .read = bcm63xx_read_timer_count, + .mask = CLOCKSOURCE_MASK(30), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, + +}; + +static void __init periph_timer_clocksource_init(void) +{ + + if (timer_cs_used != -1) + return; + + timer_cs_used = ext_timer_alloc(-1, PERIPH_TIMER_PERIOD_MAX, NULL, 0); + + /* cannot allocate timer, just quit. Shouldn't happen! */ + if (timer_cs_used == -1) + return; + + ext_timer_start(timer_cs_used); + + /* bcm63xx_clocksource->shift/mult will be computed by the following + * register function */ + clocksource_register_khz(&bcm63xx_clocksource, perclk_freq); + + /* register with sched clock */ + sched_clock_register(bcm63xx_sched_clock_read, 30, perclk_freq*1000); +} + +static void timer_set_mode(enum clock_event_mode mode, + struct clock_event_device *clk) +{ + int *timer_ce = this_cpu_ptr(&timer_ce_used); + + if (*timer_ce == -1) + return; + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + ext_timer_stop(*timer_ce); + + /* set up timer based on HZ given, unit is microseconds */ + ext_timer_set_period(*timer_ce, 1000000/HZ); + + ext_timer_set_mode(*timer_ce, EXT_TIMER_MODE_PERIODIC); + + ext_timer_start(*timer_ce); + break; + case CLOCK_EVT_MODE_ONESHOT: + /* timer is set and enabled in 'set_next_event' hook */ + break; + case CLOCK_EVT_MODE_RESUME: + ext_timer_start(*timer_ce); + break; + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + ext_timer_stop(*timer_ce); + default: + break; + } +} + +static int timer_set_next_event(unsigned long cycle, + struct clock_event_device *unused) +{ + int *timer_ce = this_cpu_ptr(&timer_ce_used); + + if (*timer_ce == -1) + return -ENODEV; + + /* stop the timer will clear the residual counter */ + ext_timer_stop(*timer_ce); + + ext_timer_set_count(*timer_ce, cycle); + + ext_timer_set_mode(*timer_ce, EXT_TIMER_MODE_ONESHOT); + + ext_timer_start(*timer_ce); + + return 0; +} + +static void clock_event_callback(unsigned int param) +{ + struct clock_event_device *evt = (struct clock_event_device *)param; + evt->event_handler(evt); +} + +static void periph_timer_clockevent_init(void) +{ + int cpu = smp_processor_id(); + int *timer_ce = this_cpu_ptr(&timer_ce_used); + struct clock_event_device *evt = this_cpu_ptr(&clockevent_timer); + int ret = -1; + + if (*timer_ce == -1) + { + *timer_ce = ext_timer_alloc_only(-1, + (ExtTimerHandler)&clock_event_callback, + (unsigned int)evt); + + /* cannot allocate timer, just quit. Shouldn't happen! */ + if (*timer_ce == -1) + return; + } + + /* in cpu1 case, cpu1 is not fully up yet at this port, set force to true + to force irq affinity to this cpu */ + ret = ext_timer_set_affinity(*timer_ce, cpu, true); + + evt->name = "timer_ce"; + evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | + CLOCK_EVT_FEAT_PERCPU; + evt->set_mode = timer_set_mode, + evt->set_next_event = timer_set_next_event, + evt->rating = 250, + evt->cpumask = cpumask_of(cpu); + + /* clockevents_config_and_register(dev, freq, min_delta, max_delta) + * freq is in the unit of Hz + * min_delta: minimum clock tick to program in oneshot mode + * max_delta: maximum clock tick to program in oneshot mode */ + clockevents_config_and_register(evt, + perclk_freq * 1000, 0, 0x3fffffff); +} + +static void periph_timer_clockevent_stop(void) +{ + struct clock_event_device *evt = this_cpu_ptr(&clockevent_timer); + + timer_set_mode(CLOCK_EVT_MODE_UNUSED, evt); +} + +static int periph_timer_cpu_notify(struct notifier_block *self, unsigned long action, + void *hcpu) +{ + switch (action & ~CPU_TASKS_FROZEN) { + case CPU_STARTING: + periph_timer_clockevent_init(); + break; + case CPU_DYING: + periph_timer_clockevent_stop(); + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block periph_timer_cpu_nb = { + .notifier_call = periph_timer_cpu_notify, +}; + +static void __init bcm63xx_timer_of_register(struct device_node *np) +{ + struct clk* clk; + + clk = of_clk_get(np, 0); + if( !IS_ERR_OR_NULL(clk) ) { + clk_prepare_enable(clk); + perclk_freq = clk_get_rate(clk)/1000; + } else + pr_err("bcm63xx_timer_of_register failed to get clk %p, use default perclk rate %d\n", clk, perclk_freq); + + init_hw_timers(); + + register_cpu_notifier(&periph_timer_cpu_nb); + + /* Immediately configure the timer on the boot CPU */ + periph_timer_clocksource_init(); + periph_timer_clockevent_init(); +} + +CLOCKSOURCE_OF_DECLARE(bcm63xx, "brcm,periph-timer", bcm63xx_timer_of_register); + +#endif /* CONFIG_BCM_KF_ARM_BCM963XX */ diff -urN linux-4.1.52-orig/arch/arm/plat-bcm63xx/bcm6846.c linux-4.1.52/arch/arm/plat-bcm63xx/bcm6846.c --- linux-4.1.52-orig/arch/arm/plat-bcm63xx/bcm6846.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/plat-bcm63xx/bcm6846.c 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,65 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void __init neon_enable(void *data) +{ + u32 access; + (void)data; + + access = get_copro_access(); + + /* + * Enable full access to VFP (cp10 and cp11) + */ + set_copro_access(access | CPACC_FULL(10) | CPACC_FULL(11)); + + /* mov r0, 0x40000000; vmsr fpexc, r0 */ + asm volatile ("mov r0, #0x40000000; .word 0xeee80a10" : : : "r0" ); +} + +static int __init bcm6846_neon_fixup(void) +{ + smp_call_function_single(0, neon_enable, NULL, 1); + return 0; +} + +late_initcall(bcm6846_neon_fixup); + +#endif // CONFIG_BCM_KF_ARM_BCM963XX diff -urN linux-4.1.52-orig/arch/arm/plat-bcm63xx/bcm6878.c linux-4.1.52/arch/arm/plat-bcm63xx/bcm6878.c --- linux-4.1.52-orig/arch/arm/plat-bcm63xx/bcm6878.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/plat-bcm63xx/bcm6878.c 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,64 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +static void __init neon_enable(void *data) +{ + u32 access; + (void)data; + + access = get_copro_access(); + + /* + * Enable full access to VFP (cp10 and cp11) + */ + set_copro_access(access | CPACC_FULL(10) | CPACC_FULL(11)); + + /* mov r0, 0x40000000; vmsr fpexc, r0 */ + asm volatile ("mov r0, #0x40000000; .word 0xeee80a10" : : : "r0" ); +} + +static int __init bcm6878_neon_fixup(void) +{ + smp_call_function_single(0, neon_enable, NULL, 1); + return 0; +} + +late_initcall(bcm6878_neon_fixup); + +#endif // CONFIG_BCM_KF_ARM_BCM963XX diff -urN linux-4.1.52-orig/arch/arm/plat-bcm63xx/ca9mp_cache.S linux-4.1.52/arch/arm/plat-bcm63xx/ca9mp_cache.S --- linux-4.1.52-orig/arch/arm/plat-bcm63xx/ca9mp_cache.S 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/plat-bcm63xx/ca9mp_cache.S 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,136 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + +#include +#include + + __CPUINIT + +/* + * v7_l1_cache_invalidate + * + * Invalidate contents of L1 cache without flushing its contents + * into outer cache and memory. This is needed when the contents + * of the cache are unpredictable after power-up. + * + * corrupts r0-r6 + */ + +ENTRY(v7_l1_cache_invalidate) + mov r0, #0 + mcr p15, 2, r0, c0, c0, 0 @ set cache level to 1 + mrc p15, 1, r0, c0, c0, 0 @ read CLIDR + + ldr r1, =0x7fff + and r2, r1, r0, lsr #13 @ get max # of index size + + ldr r1, =0x3ff + and r3, r1, r0, lsr #3 @ NumWays - 1 + add r2, r2, #1 @ NumSets + + and r0, r0, #0x7 + add r0, r0, #4 @ SetShift + + clz r1, r3 @ WayShift + add r4, r3, #1 @ NumWays +1: sub r2, r2, #1 @ NumSets-- + mov r3, r4 @ Temp = NumWays +2: subs r3, r3, #1 @ Temp-- + mov r5, r3, lsl r1 + mov r6, r2, lsl r0 + orr r5, r5, r6 @ Reg = (Temp< +*/ + +/* +* ARM Cortex A9 MPCORE Platform base +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void __iomem * scu_base_addr(void) +{ + return __io_address(SCU_PHYS_BASE + CA9MP_SCU_OFF); +} + +void __init ca9mp_fixup(void) +{ + /* in case of any fixup that needs to be done for processor, such + * as cache invalidation. */ +} + +/* map_io should be called the first, so we have the register base + * address for the core. */ +void __init ca9mp_map_io(void) +{ + struct map_desc desc; + +#if 0 + /* + * Cortex A9 Architecture Manual specifies this as a way to get + * MPCORE PERHIPHBASE address at run-time + */ + asm("mrc p15,4,%0,c15,c0,0 @ Read Configuration Base Address Register" + : "=&r" (base_addr) : : "cc"); + + printk(KERN_INFO "CA9 MPCORE found at %p\n", (void *)base_addr); +#endif + + /* Fix-map the entire PERIPHBASE 2*4K register block */ + desc.virtual = IO_ADDRESS(SCU_PHYS_BASE); + desc.pfn = __phys_to_pfn(SCU_PHYS_BASE); + desc.length = SZ_8K; + desc.type = MT_DEVICE; + iotable_init(&desc, 1); +} + +void __init ca9mp_init_gic(void) +{ + printk(KERN_INFO "Cortex A9 MPCORE GIC init\n"); + printk(KERN_INFO "DIST at %p, CPU_IF at %p\n", + (void *)IO_ADDRESS(SCU_PHYS_BASE) + CA9MP_GIC_DIST_OFF, + (void *)IO_ADDRESS(SCU_PHYS_BASE) + CA9MP_GIC_CPUIF_OFF); + + // FIXME!! hardcored value below for the interrupt line#, will need to define + // the interrupt line# in a header file for all different chips + gic_init(0, 27, (void *)IO_ADDRESS(SCU_PHYS_BASE) + CA9MP_GIC_DIST_OFF, + (void *)IO_ADDRESS(SCU_PHYS_BASE) + CA9MP_GIC_CPUIF_OFF); + + //irq_set_handler(CA9MP_IRQ_GLOBALTIMER, handle_percpu_irq); + /* try it.. handle_edge_irq, handle_percpu_irq, or handle_level_irq */ +} + +void __init ca9mp_init_early(void) +{ + /* NOP */ +} + +#endif /* CONFIG_BCM_KF_ARM_BCM963XX */ diff -urN linux-4.1.52-orig/arch/arm/plat-bcm63xx/ca9mp_timer.c linux-4.1.52/arch/arm/plat-bcm63xx/ca9mp_timer.c --- linux-4.1.52-orig/arch/arm/plat-bcm63xx/ca9mp_timer.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/plat-bcm63xx/ca9mp_timer.c 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,317 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +/* + * The ARM9 MPCORE Global Timer is a continously-running 64-bit timer, + * which is used both as a "clock source" and as a "clock event" - + * there is a banked per-cpu compare and reload registers that are + * used to generated either one-shot or periodic interrupts on the cpu + * that calls the mode_set function. + * + * NOTE: This code does not support dynamic change of the source clock + * frequency. The interrupt interval is only calculated once during + * initialization. + */ + +/* + * Global Timer Registers + */ +#define GTIMER_COUNT_LO 0x00 /* Lower 32 of 64 bits counter */ +#define GTIMER_COUNT_HI 0x04 /* Higher 32 of 64 bits counter */ +#define GTIMER_CTRL 0x08 /* Control (partially banked) */ +#define GTIMER_CTRL_EN (1<<0) /* Timer enable bit */ +#define GTIMER_CTRL_CMP_EN (1<<1) /* Comparator enable */ +#define GTIMER_CTRL_IRQ_EN (1<<2) /* Interrupt enable */ +#define GTIMER_CTRL_AUTO_EN (1<<3) /* Auto-increment enable */ +#define GTIMER_INT_STAT 0x0C /* Interrupt Status (banked) */ +#define GTIMER_COMP_LO 0x10 /* Lower half comparator (banked) */ +#define GTIMER_COMP_HI 0x14 /* Upper half comparator (banked) */ +#define GTIMER_RELOAD 0x18 /* Auto-increment (banked) */ + +#define GTIMER_MIN_RANGE 30 /* Minimum wrap-around time in sec */ + +#define GTIMER_VIRT_ADDR (IO_ADDRESS(SCU_PHYS_BASE) + CA9MP_GTIMER_OFF) +#define LTIMER_PHY_ADDR (SCU_PHYS_BASE + CA9MP_LTIMER_OFF) + +/* Gobal variables */ +static u32 ticks_per_jiffy; + +static cycle_t gptimer_count_read(struct clocksource *cs) +{ + u32 count_hi, count_ho, count_lo; + u64 count; + + /* Avoid unexpected rollover with double-read of upper half */ + do { + count_hi = readl_relaxed(GTIMER_VIRT_ADDR + GTIMER_COUNT_HI); + count_lo = readl_relaxed(GTIMER_VIRT_ADDR + GTIMER_COUNT_LO); + count_ho = readl_relaxed(GTIMER_VIRT_ADDR + GTIMER_COUNT_HI); + } while (count_hi != count_ho); + + count = (u64)count_hi << 32 | count_lo; + return count; +} + +static struct clocksource clocksource_gptimer = { + .name = "ca9mp_gtimer", + .rating = 300, + .read = gptimer_count_read, + .mask = CLOCKSOURCE_MASK(64), +// .shift = 20, + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static notrace u32 brcm_sched_clock_read(void) +{ + return clocksource_gptimer.read(&clocksource_gptimer); +} + +/* + * IRQ handler for the global timer + * This interrupt is banked per CPU so is handled identically + */ +static irqreturn_t gtimer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = *(struct clock_event_device **)dev_id; + + if (evt->mode == CLOCK_EVT_MODE_ONESHOT) { + u32 ctrl = readl_relaxed(GTIMER_VIRT_ADDR + GTIMER_CTRL); + ctrl &= ~GTIMER_CTRL_EN; + writel_relaxed(ctrl, GTIMER_VIRT_ADDR + GTIMER_CTRL); + } + /* clear the interrupt */ + writel_relaxed(1, GTIMER_VIRT_ADDR + GTIMER_INT_STAT); + + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static void gtimer_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + u32 ctrl = 0, period; + u64 count; + + /* By default, when we enter this function, we can just stop + * the timer completely, once a mode is selected, then we + * can start the timer at that point. */ + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + period = ticks_per_jiffy; + count = gptimer_count_read(NULL); + count += period; + writel_relaxed(ctrl, GTIMER_VIRT_ADDR + GTIMER_CTRL); + writel_relaxed(count & 0xffffffffUL, GTIMER_VIRT_ADDR + GTIMER_COMP_LO); + writel_relaxed(count >> 32, GTIMER_VIRT_ADDR + GTIMER_COMP_HI); + writel_relaxed(period, GTIMER_VIRT_ADDR + GTIMER_RELOAD); + ctrl = GTIMER_CTRL_EN | GTIMER_CTRL_CMP_EN | + GTIMER_CTRL_IRQ_EN | GTIMER_CTRL_AUTO_EN; + break; + + case CLOCK_EVT_MODE_ONESHOT: + /* period set, and timer enabled in 'next_event' hook */ + break; + + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + default: + break; + } + /* Apply the new mode */ + writel_relaxed(ctrl, GTIMER_VIRT_ADDR + GTIMER_CTRL); +} + +static int gtimer_set_next_event(unsigned long next, + struct clock_event_device *evt) +{ + u32 ctrl = readl_relaxed(GTIMER_VIRT_ADDR + GTIMER_CTRL); + u64 count = gptimer_count_read(NULL); + + ctrl &= ~GTIMER_CTRL_CMP_EN; + writel_relaxed(ctrl, GTIMER_VIRT_ADDR + GTIMER_CTRL); + + count += next; + + writel_relaxed(count & 0xffffffffUL, GTIMER_VIRT_ADDR + GTIMER_COMP_LO); + writel_relaxed(count >> 32, GTIMER_VIRT_ADDR + GTIMER_COMP_HI); + + /* enable IRQ for the same cpu that loaded comparator */ + ctrl |= GTIMER_CTRL_EN | GTIMER_CTRL_CMP_EN | GTIMER_CTRL_IRQ_EN; + + writel_relaxed(ctrl, GTIMER_VIRT_ADDR + GTIMER_CTRL); + + return 0; +} + +static struct clock_event_device gtimer_clockevent = { + .name = "ca9mp_gtimer", + .shift = 20, + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .set_mode = gtimer_set_mode, + .set_next_event = gtimer_set_next_event, + .rating = 300, +}; + +static union { + struct clock_event_device *evt; + struct clock_event_device __percpu **percpu_evt; +} brcm_evt; + + +static void __init gtimer_clockevents_init(u32 rate) +{ + struct clock_event_device *evt = >imer_clockevent; + int res; + + evt->irq = CA9MP_IRQ_GLOBALTIMER; + evt->cpumask = cpumask_of(0); + +#ifdef CONFIG_BCM63138_SIM + ticks_per_jiffy = DIV_ROUND_CLOSEST(rate, HZ) / 20; +#else + ticks_per_jiffy = DIV_ROUND_CLOSEST(rate, HZ); +#endif + + clockevents_calc_mult_shift(evt, rate, GTIMER_MIN_RANGE); + + evt->max_delta_ns = clockevent_delta2ns(0xffffffff, evt); + evt->min_delta_ns = clockevent_delta2ns(0xf, evt); + + /* Register the device to install handler before enabing IRQ */ + clockevents_register_device(evt); + + brcm_evt.percpu_evt = alloc_percpu(struct clock_event_device *); + if (!brcm_evt.percpu_evt) { + pr_err("alloc_percpu failed for %s\n", evt->name); + } + *__this_cpu_ptr(brcm_evt.percpu_evt) = evt; + res = request_percpu_irq(evt->irq, gtimer_interrupt, evt->name, + brcm_evt.percpu_evt); + if (!res) { + pr_err("request_percpu_irq succeeds for %s\n", evt->name); + enable_percpu_irq(evt->irq, 0); + } else + pr_err("request_percpu_irq fails! for %s\n", evt->name); +} + +static void inline gtimer_clockevents_updatefreq_hz(u32 rate) +{ + struct clock_event_device *evt = >imer_clockevent; + + /* there is an API called clockevents_update_freq which does + * almost identical task as what we do here */ +#ifdef CONFIG_BCM63138_SIM + ticks_per_jiffy = DIV_ROUND_CLOSEST(rate, HZ) / 20; +#else + ticks_per_jiffy = DIV_ROUND_CLOSEST(rate, HZ); +#endif + + clockevents_calc_mult_shift(evt, rate, GTIMER_MIN_RANGE); + + evt->max_delta_ns = clockevent_delta2ns(0xffffffff, evt); + evt->min_delta_ns = clockevent_delta2ns(0xf, evt); +} + +/* + * MPCORE Global Timer initialization function + */ +static void __init ca9mp_gtimer_init(unsigned long rate) +{ + u64 count; + int res; + + printk(KERN_INFO "MPCORE Global Timer Clock %luHz\n", rate); + + /* Register as system timer */ + gtimer_clockevents_init(rate); + + /* Self-test the timer is running */ + count = gptimer_count_read(NULL); + + /* Register as time source */ + res = clocksource_register_hz(&clocksource_gptimer, rate); + if (res) + printk("%s:clocksource_register failed!\n", __func__); + setup_sched_clock(brcm_sched_clock_read, 32, rate); + + count = gptimer_count_read(NULL) - count; + if (count == 0) + printk(KERN_CRIT "MPCORE Global Timer Dead!!\n"); +} + +#ifdef CONFIG_HAVE_ARM_TWD +static DEFINE_TWD_LOCAL_TIMER(twd_local_timer, LTIMER_PHY_ADDR, + CA9MP_IRQ_LOCALTIMER); + +static void __init ca9mp_twd_init(void) +{ + int err = twd_local_timer_register(&twd_local_timer); + if (err) + pr_err("twd_local_timer_register failed %d\n", err); +} +#else +#define ca9mp_twd_init() do {} while(0) +#endif + +void ca9mp_timer_update_freq(unsigned long rate) +{ + printk(KERN_INFO "MPCORE Global Timer Clock update to %luHz\n", rate); + + gtimer_clockevents_updatefreq_hz(rate); + + __clocksource_updatefreq_hz(&clocksource_gptimer, rate); +} + +void __init ca9mp_timer_init(unsigned long rate) +{ + /* init global timer */ + ca9mp_gtimer_init(rate); + + /* init TWD / local timer */ + ca9mp_twd_init(); + +} + +#endif /* CONFIG_BCM_KF_ARM_BCM963XX */ diff -urN linux-4.1.52-orig/arch/arm/plat-bcm63xx/cache-l310.c linux-4.1.52/arch/arm/plat-bcm63xx/cache-l310.c --- linux-4.1.52-orig/arch/arm/plat-bcm63xx/cache-l310.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/plat-bcm63xx/cache-l310.c 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,233 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + +/* + * This L310 Cache controller code is provided with BCM5301x, it is pretty + * similar to what the official kernel has, besides: 1) this one removes + * some spinlock protections over certain atomic access and 2) register + * ISR fo L2 cache (which does nothing just print out the interrupt + * occurs. Therefore, this code is kept here in case there is a performance + * improvement requirement, then we can try this code. + */ +#include +#include +#include +#include + +#include +#include /* Old register offsets */ + +#define CACHE_LINE_SIZE 32 + +static void __iomem *l2x0_base; +static DEFINE_SPINLOCK(l2x0_lock); +static uint32_t l2x0_way_mask; /* Bitmask of active ways */ +int l2x0_irq = 32 ; + +static inline void cache_wait(void __iomem *reg, unsigned long mask) +{ + /* wait for the operation to complete */ + while (readl_relaxed(reg) & mask) + ; +} + +/* + * Atomic operations + * + * The following are atomic operations: + * . Clean Line by PA or by Set/Way. + * . Invalidate Line by PA. + * . Clean and Invalidate Line by PA or by Set/Way. + * . Cache Sync. + * These operations stall the slave ports until they are complete. + * When these registers are read, bit [0], the C flag, + * indicates that a background operation is in progress. + * When written, bit 0 must be zero. + */ +static inline void atomic_cache_sync(void __iomem *base) +{ + writel_relaxed(0, base + L2X0_CACHE_SYNC); +} + +static inline void atomic_clean_line(void __iomem *base, unsigned long addr) +{ + writel_relaxed(addr, base + L2X0_CLEAN_LINE_PA); +} + +static inline void atomic_inv_line(void __iomem *base, unsigned long addr) +{ + writel_relaxed(addr, base + L2X0_INV_LINE_PA); +} + +static inline void atomic_flush_line(void __iomem *base, unsigned long addr) +{ + writel_relaxed(addr, base + L2X0_CLEAN_INV_LINE_PA); +} + +/* + * Atomic operations do not require the use of the spinlock + */ + +static void l2x0_cache_sync(void) +{ + void __iomem *base = l2x0_base; + atomic_cache_sync(base); +} + +static void l2x0_inv_range(unsigned long start, unsigned long end) +{ + void __iomem *base = l2x0_base; + + /* Ramge edges could contain live dirty data */ + if(start & (CACHE_LINE_SIZE - 1)) + atomic_flush_line(base, start & ~(CACHE_LINE_SIZE - 1)); + if(end & (CACHE_LINE_SIZE - 1)) + atomic_flush_line(base, end & ~(CACHE_LINE_SIZE - 1)); + + start &= ~(CACHE_LINE_SIZE - 1); + + while (start < end) { + atomic_inv_line(base, start); + start += CACHE_LINE_SIZE; + } +} + +static void l2x0_clean_range(unsigned long start, unsigned long end) +{ + void __iomem *base = l2x0_base; + + start &= ~(CACHE_LINE_SIZE - 1); + + while (start < end) { + atomic_clean_line(base, start); + start += CACHE_LINE_SIZE; + } + atomic_cache_sync(base); +} + +static void l2x0_flush_range(unsigned long start, unsigned long end) +{ + void __iomem *base = l2x0_base; + + start &= ~(CACHE_LINE_SIZE - 1); + while (start < end) { + atomic_flush_line(base, start); + start += CACHE_LINE_SIZE; + } + atomic_cache_sync(base); +} + +/* + * Invalidate by way is non-atomic, background operation + * has to be protected with the spinlock. + */ +static inline void l2x0_inv_all(void) +{ + void __iomem *base = l2x0_base; + unsigned long flags; + + /* invalidate all ways */ + spin_lock_irqsave(&l2x0_lock, flags); + writel_relaxed(l2x0_way_mask, base + L2X0_INV_WAY); + cache_wait(base + L2X0_INV_WAY, l2x0_way_mask); + atomic_cache_sync(base); + spin_unlock_irqrestore(&l2x0_lock, flags); +} + +static irqreturn_t l2x0_isr(int irq, void * cookie) +{ + u32 reg; + + /* Read pending interrupts */ + reg = readl_relaxed(l2x0_base + L2X0_RAW_INTR_STAT); + /* Acknowledge the interupts */ + writel_relaxed(reg, l2x0_base + L2X0_INTR_CLEAR); + printk(KERN_WARNING "L310: interrupt bits %#x\n", reg); + + return IRQ_HANDLED; +} + +void __init l310_init(void __iomem *base, u32 aux_val, u32 aux_mask, int irq) +{ + __u32 aux; + __u32 cache_id; + int ways; + + l2x0_base = base; + l2x0_irq = irq; + + cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID); + aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL); + + aux &= aux_mask; + aux |= aux_val; + + /* This module unly supports the L310 */ + BUG_ON((cache_id & L2X0_CACHE_ID_PART_MASK) != L2X0_CACHE_ID_PART_L310); + + /* Determine the number of ways */ + if (aux & (1 << 16)) + ways = 16; + else + ways = 8; + + l2x0_way_mask = (1 << ways) - 1; + + /* + * Check if l2x0 controller is already enabled. + * If you are booting from non-secure mode + * accessing the below registers will fault. + */ + if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & 1)) { + + /* l2x0 controller is disabled */ + writel_relaxed(aux, l2x0_base + L2X0_AUX_CTRL); + + l2x0_inv_all(); + + /* enable L2X0 */ + writel_relaxed(1, l2x0_base + L2X0_CTRL); + } + + /* Enable interrupts */ + WARN_ON(request_irq(l2x0_irq, l2x0_isr, 0, "L2C", NULL)); + writel_relaxed(0x00ff, l2x0_base + L2X0_INTR_MASK); + + outer_cache.inv_range = l2x0_inv_range; + outer_cache.clean_range = l2x0_clean_range; + outer_cache.flush_range = l2x0_flush_range; + outer_cache.sync = l2x0_cache_sync; + + printk(KERN_INFO "L310: cache controller enabled %d ways, " + "CACHE_ID 0x%08x, AUX_CTRL 0x%08x\n", + ways, cache_id, aux); +} + +#endif /* CONFIG_BCM_KF_ARM_BCM963XX */ diff -urN linux-4.1.52-orig/arch/arm/plat-bcm63xx/hotplug.c linux-4.1.52/arch/arm/plat-bcm63xx/hotplug.c --- linux-4.1.52-orig/arch/arm/plat-bcm63xx/hotplug.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/plat-bcm63xx/hotplug.c 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,131 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* + * linux/arch/arm/mach-realview/hotplug.c + * + * Copyright (C) 2002 ARM Ltd. + * All Rights Reserved + * + * 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. + */ +#include +#include +#include + +#include +#include +#include + +extern volatile int pen_release; + +static inline void cpu_enter_lowpower(void) +{ + unsigned int v; + + flush_cache_all(); + asm volatile( + " mcr p15, 0, %1, c7, c5, 0\n" + " mcr p15, 0, %1, c7, c10, 4\n" + /* + * Turn off coherency + */ + " mrc p15, 0, %0, c1, c0, 1\n" + " bic %0, %0, #0x20\n" + " mcr p15, 0, %0, c1, c0, 1\n" + " mrc p15, 0, %0, c1, c0, 0\n" + " bic %0, %0, %2\n" + " mcr p15, 0, %0, c1, c0, 0\n" + : "=&r" (v) + : "r" (0), "Ir" (CR_C) + : "cc"); +} + +static inline void cpu_leave_lowpower(void) +{ + unsigned int v; + + asm volatile( "mrc p15, 0, %0, c1, c0, 0\n" + " orr %0, %0, %1\n" + " mcr p15, 0, %0, c1, c0, 0\n" + " mrc p15, 0, %0, c1, c0, 1\n" + " orr %0, %0, #0x20\n" + " mcr p15, 0, %0, c1, c0, 1\n" + : "=&r" (v) + : "Ir" (CR_C) + : "cc"); +} + +static inline void platform_do_lowpower(unsigned int cpu, int *spurious) +{ + /* + * there is no power-control hardware on this platform, so all + * we can do is put the core into WFI; this is safe as the calling + * code will have already disabled interrupts + */ + for (;;) { + /* + * here's the WFI + */ + asm(".word 0xe320f003\n" + : + : + : "memory", "cc"); + + if (pen_release == cpu_logical_map(cpu)) { + /* + * OK, proper wakeup, we're done + */ + break; + } + + /* + * Getting here, means that we have come out of WFI without + * having been woken up - this shouldn't happen + * + * Just note it happening - when we're woken, we can report + * its occurrence. + */ + (*spurious)++; + } +} + +int platform_cpu_kill(unsigned int cpu) +{ + return 1; +} + +/* + * platform-specific code to shutdown a CPU + * + * Called with IRQs disabled + */ +void platform_cpu_die(unsigned int cpu) +{ + int spurious = 0; + + /* + * we're ready for shutdown now, so do it + */ + cpu_enter_lowpower(); + platform_do_lowpower(cpu, &spurious); + + /* + * bring this CPU back into the world of cache + * coherency, and then restore interrupts + */ + cpu_leave_lowpower(); + + if (spurious) + pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious); +} + +int platform_cpu_disable(unsigned int cpu) +{ + /* + * we don't allow CPU 0 to be shutdown (it is still too special + * e.g. clock tick interrupts) + */ + return cpu == 0 ? -EPERM : 0; +} +#endif diff -urN linux-4.1.52-orig/arch/arm/plat-bcm63xx/include/plat/b15core.h linux-4.1.52/arch/arm/plat-bcm63xx/include/plat/b15core.h --- linux-4.1.52-orig/arch/arm/plat-bcm63xx/include/plat/b15core.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/plat-bcm63xx/include/plat/b15core.h 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,83 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + +/* + * Broadcom ARM based on Cortex A15 CORE + * + * Platform hardware information and internal API + */ + +#ifndef __PLAT_B15CORE_H +#define __PLAT_B15CORE_H + +#include + +/* B15 CORE internally-connected IRQs */ +#define B15_IRQ_GLOBALTIMER 27 +#define B15_IRQ_LOCALTIMER 29 +#define B15_IRQ_WDTIMER 30 + +/* + NOTE: B15 CORE physical based ontained at run-time, + while its virtual base address is set at compile-time in memory.h +*/ + +/* B15 CORE register offsets */ +#define B15_SCU_OFF 0x0000 /* Coherency controller */ +#define B15_GTIMER_OFF 0x0200 /* Global timer */ +#define B15_LTIMER_OFF 0x0600 /* Local (private) timers */ +#define B15_GIC_DIST_OFF 0x1000 /* Interrupt distributor registers */ +#define B15_GIC_CPUIF_OFF 0x2000 /* Interrupt controller CPU interface */ + +/* FIXME! the following should be fixed once we verify whether B15 and CA9 share + * the same timer or not */ +#define CA9MP_IRQ_GLOBALTIMER B15_IRQ_GLOBALTIMER +#define CA9MP_IRQ_LOCALTIMER B15_IRQ_LOCALTIMER +#define CA9MP_GTIMER_OFF B15_GTIMER_OFF +#define CA9MP_LTIMER_OFF B15_LTIMER_OFF + +#ifndef __ASSEMBLY__ + +extern void __init b15_fixup(void); +extern void __init b15_map_io(void); +extern void __init b15_init_gic(void); +extern void __init b15_init_early(void); + +/* FIXME! the following should be fixed once we verify whether B15 and CA9 share + * the same timer or not */ +//extern void __init ca9mp_timer_init(unsigned long rate); + +extern void __iomem * scu_base_addr(void); +extern void __cpuinit b15_power_up_cpu(int cpu_id); + +#endif + +#endif /* __PLAT_CA9MPCORE_H */ +#endif /* CONFIG_BCM_KF_ARM_BCM963XX */ diff -urN linux-4.1.52-orig/arch/arm/plat-bcm63xx/include/plat/bcm63xx_acp.h linux-4.1.52/arch/arm/plat-bcm63xx/include/plat/bcm63xx_acp.h --- linux-4.1.52-orig/arch/arm/plat-bcm63xx/include/plat/bcm63xx_acp.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/plat-bcm63xx/include/plat/bcm63xx_acp.h 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,108 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + +#ifndef __PLAT_BCM63XX_ACP_H +#define __PLAT_BCM63XX_ACP_H +#define BCM_UBUS_CFG_MAX 4 + +typedef enum { +#ifdef CONFIG_BCM963138 + BCM_UBUS_PID_PCIE0 = 0x0, + BCM_UBUS_PID_DDR = 0x1, + BCM_UBUS_PID_ARMAXIACP = 0x2, + BCM_UBUS_PID_PERIPH = 0x3, + BCM_UBUS_PID_USBD = 0x4, + BCM_UBUS_PID_USBH = 0x5, + BCM_UBUS_PID_SATA = 0x6, + BCM_UBUS_PID_DECT = 0x7, + BCM_UBUS_PID_APM = 0x8, + BCM_UBUS_PID_VDSL = 0x9, + BCM_UBUS_PID_SAR = 0xa, + BCM_UBUS_PID_RNR = 0xb, + BCM_UBUS_PID_RNR_RABR = 0xc, + BCM_UBUS_PID_SF2 = 0xe, + BCM_UBUS_PID_PMC = 0xf, + BCM_UBUS_PID_PCIE1 = 0x10, + BCM_UBUS_PID_ARMAIPDAP = 0x12, + BCM_UBUS_PID_SAR2 = 0x1a, + BCM_UBUS_PID_RNR_RBBR = 0x1c, + BCM_UBUS_PID_ERROR = 0x1f, +#endif + BCM_UBUS_PID_MAX, +} bcm_ubus_pid_t; +#define BCM_UBUS_PID_INVALID 0xff + +typedef struct { + uint32_t addr_in; + uint32_t addr_out; + uint8_t dst_pid; + uint8_t size_shift; + uint8_t en; +} bcm_acp_ubus_cfg_t; + +typedef struct { + /* L2 cache policy for write, recommend value is 0xf for + * cacheable WBWA, or 0x0 to disable */ + uint8_t wcache; + /* L2 cache policy for read, recommend value is 0xf for + * cacheable WBWA, or 0x0 to disable */ + uint8_t rcache; + /* L1 cache policy for write, recommend value is 0x1 for + * cache invalidation, 0x1f for WBWA, or 0x0 to disable */ + uint8_t wuser; + /* L1 cache policy for read, recommend value is 0x1 for + * cache invalidation, 0x1f for WBWA, or 0x0 to disable */ + uint8_t ruser; +} bcm_acp_cache_ctrl_t; + +/* enable / disable the ACP feature for a specific block */ +int bcm63xx_acp_enable(uint8_t ubus_pid); +int bcm63xx_acp_disable(uint8_t ubus_pid); + +/* check if the ACP is enabled */ +bool bcm63xx_acp_on(uint8_t ubus_pid); + +/* UBUS configuration setting APIs */ +int bcm63xx_acp_ubus_cfg_get_entry(uint8_t ubus_pid, uint8_t idx, + bcm_acp_ubus_cfg_t *acp_ubus_cfg); +int bcm63xx_acp_ubus_cfg_get_all(uint8_t ubus_pid, + bcm_acp_ubus_cfg_t *acp_ubus_cfg); +int bcm63xx_acp_ubus_cfg_set_entry(uint8_t ubus_pid, uint8_t idx, + bcm_acp_ubus_cfg_t *acp_ubus_cfg); +int bcm63xx_acp_ubus_cfg_set_all(uint8_t ubus_pid, + bcm_acp_ubus_cfg_t *acp_ubus_cfg); +void bcm63xx_acp_ubus_cfg_reset(uint8_t ubus_pid); + +/* ACP port control */ +int bcm63xx_acp_cache_ctrl_get(uint8_t ubus_pid, bcm_acp_cache_ctrl_t *cache_ctrl); +int bcm63xx_acp_cache_ctrl_set(uint8_t ubus_pid, bcm_acp_cache_ctrl_t *cache_ctrl); + +#endif /* __PLAT_BCM63XX_ACP_H */ +#endif /* defined(CONFIG_BCM_KF_ARM_BCM963XX) */ diff -urN linux-4.1.52-orig/arch/arm/plat-bcm63xx/include/plat/bsp.h linux-4.1.52/arch/arm/plat-bcm63xx/include/plat/bsp.h --- linux-4.1.52-orig/arch/arm/plat-bcm63xx/include/plat/bsp.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/plat-bcm63xx/include/plat/bsp.h 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,67 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + +/* + * Broadcom ARM BSP + * Internal API declarations + */ + + +#ifndef __PLAT_BSP_H +#define __PLAT_BSP_H + +#include + +struct clk; + +/* 32KB, 16-way cache, Allow non-secure access, Instruction prefetch, Early BRESP */ +#define BCM_L2C_AUX_VAL (L310_AUX_CTRL_ASSOCIATIVITY_16 | \ + (2 << L2C_AUX_CTRL_WAY_SIZE_SHIFT) | \ + L2C_AUX_CTRL_SHARED_OVERRIDE | \ + L310_AUX_CTRL_NS_INT_CTRL | \ + L310_AUX_CTRL_INSTR_PREFETCH | \ + L310_AUX_CTRL_EARLY_BRESP ) + +#define BCM_L2C_AUX_MSK ~( L310_AUX_CTRL_ASSOCIATIVITY_16 | \ + L2C_AUX_CTRL_WAY_SIZE_MASK | \ + L2C_AUX_CTRL_SHARED_OVERRIDE | \ + L310_AUX_CTRL_NS_INT_CTRL | \ + L310_AUX_CTRL_INSTR_PREFETCH | \ + L310_AUX_CTRL_EARLY_BRESP ) + +void __init soc_fixup(void); +void __init soc_map_io(void); +void __init soc_init_irq(void); +void __init soc_init_early(void); +void __init soc_add_devices(void); +void __init soc_l2_cache_init(void); + +#endif /* __PLAT_BSP_H */ +#endif /* CONFIG_BCM_KF_ARM_BCM963XX */ diff -urN linux-4.1.52-orig/arch/arm/plat-bcm63xx/include/plat/ca9mpcore.h linux-4.1.52/arch/arm/plat-bcm63xx/include/plat/ca9mpcore.h --- linux-4.1.52-orig/arch/arm/plat-bcm63xx/include/plat/ca9mpcore.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/plat-bcm63xx/include/plat/ca9mpcore.h 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,75 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + +/* + * ARM A9 MPCORE + * + * Platform hardware information and internal API + */ + +#ifndef __PLAT_CA9MPCORE_H +#define __PLAT_CA9MPCORE_H + +#include + +/* MPCORE internally-connected IRQs */ +#define CA9MP_IRQ_GLOBALTIMER 27 +#define CA9MP_IRQ_LOCALTIMER 29 +#define CA9MP_IRQ_WDTIMER 30 + +/* + NOTE: MPCORE physical based ontained at run-time, + while its virtual base address is set at compile-time in memory.h +*/ + +/* MPCORE register offsets */ +#define CA9MP_SCU_OFF 0x0000 /* Coherency controller */ +#define CA9MP_GIC_CPUIF_OFF 0x0100 /* Interrupt controller CPU interface */ +#define CA9MP_GTIMER_OFF 0x0200 /* Global timer */ +#define CA9MP_LTIMER_OFF 0x0600 /* Local (private) timers */ +#define CA9MP_GIC_DIST_OFF 0x1000 /* Interrupt distributor registers */ + +#ifndef __ASSEMBLY__ + +extern void __init ca9mp_fixup(void); +extern void __init ca9mp_map_io(void); +extern void __init ca9mp_init_gic(void); +extern void __init ca9mp_init_early(void); +extern void __iomem * scu_base_addr(void); + +#ifdef CONFIG_PLAT_CA9_MPCORE_TIMER +extern void __init ca9mp_timer_init(unsigned long rate); +extern void ca9mp_timer_update_freq(unsigned long rate); +#endif + +#endif + +#endif /* __PLAT_CA9MPCORE_H */ +#endif /* CONFIG_BCM_KF_ARM_BCM963XX */ diff -urN linux-4.1.52-orig/arch/arm/plat-bcm63xx/Kconfig linux-4.1.52/arch/arm/plat-bcm63xx/Kconfig --- linux-4.1.52-orig/arch/arm/plat-bcm63xx/Kconfig 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/plat-bcm63xx/Kconfig 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,182 @@ +if (BCM_KF_ARM_BCM963XX) +# Broadcom platforms selection + +config PLAT_CA7_MPCORE + bool "ARM Cortex A7 MPCORE architecture support" + select CPU_V7 + select ARM_GIC + select ARM_L1_CACHE_SHIFT_6 + select HAVE_CLK + select COMMON_CLK + select CLKDEV_LOOKUP + select CLKSRC_OF + select HAVE_ARM_ARCH_TIMER + select SPARSE_IRQ + help + Support for ARM A7 MPCORE subsystem + +config PLAT_CA7_SMP + bool "Enable SMP on ARM Cortex A7 MPCORE" + select HAVE_SMP + select SMP + select SMP_ON_UP + select HOTPLUG_CPU + select HAVE_ARM_SCU + select PLAT_CA7_MPCORE + help + Support for ARM A7 SMP + +config PLAT_CA9_MPCORE + bool "ARM Cortex A9 MPCORE architecture support" + select CPU_V7 + select ARM_GIC + select HAVE_CLK + select CLKDEV_LOOKUP + select CLKSRC_OF + select COMMON_CLK + select GENERIC_CLOCKEVENTS_BUILD + select GENERIC_CLOCKEVENTS + select NEED_MACH_MEMORY_H + select ARM_ERRATA_764369 if SMP && PLAT_BCM63138 +# select ARM_GLOBAL_TIMER +# select CONSTRUCTORS + help + Support for ARM A9 MPCORE subsystem + +config PLAT_CA9_SMP + bool "Enable SMP on ARM Cortex A9 MPCORE" + select HAVE_SMP + select SMP + select SMP_ON_UP + select HOTPLUG_CPU + select HAVE_ARM_SCU + +config PLAT_CA9_MPCORE_TIMER + bool "Enable ARM Cortex A9 MPcore Timer for Clock event" + select HAVE_ARM_TWD if PLAT_CA9_SMP + select LOCAL_TIMERS if PLAT_CA9_SMP + depends on PLAT_CA9_MPCORE + +config PLAT_B15_CORE + bool "Broadcom ARM based on Cortex A15 architecture support" + select CPU_V7 + select ARM_GIC + select HAVE_CLK + select CLKDEV_LOOKUP + select CLKSRC_OF + select COMMON_CLK + select GENERIC_CLOCKEVENTS_BUILD + select GENERIC_CLOCKEVENTS + select NEED_MACH_MEMORY_H + help + Support for Broadcom's ARMv7 processr based on ARM Cortex A15 + +config PLAT_B15_SMP + bool "Enable SMP on Broadcom ARMv7 core" + select HAVE_SMP + select SMP + select SMP_ON_UP + select HOTPLUG_CPU + select HAVE_ARM_TWD + select HAVE_ARM_SCU + select LOCAL_TIMERS + +config PLAT_B15_MPCORE_TIMER + bool "Enable Broadcom B15 MPcore Timer for Clock event" + select HAVE_ARM_TWD if PLAT_B15_SMP + select LOCAL_TIMERS if PLAT_B15_SMP + depends on PLAT_B15_CORE + +config CACHE_L310 + bool "PL310 Level-2 Cache Controller" + select OUTER_CACHE + select OUTER_CACHE_SYNC + depends on PLAT_CA9_MPCORE + +config PLAT_BCM63XX_AMBA_PL011 + bool "Enable AMBA PL011 Serial console" + select ARM_AMBA + select SERIAL_AMBA_PL011 + select SERIAL_AMBA_PL011_CONSOLE + depends on PLAT_BCM63138 + +config PLAT_BCM63XX_AMBA_PL081 + bool "Enable AMBA PL081 DMAC" + select DMADEVICES + select ARM_AMBA + select AMBA_PL08X + depends on PLAT_BCM63138 + +config PLAT_BCM63XX_EMMC + bool "Enable Broadcom EMMC support" + select MMC + select MMC_BLOCK + select MMC_SDHCI + select MMC_SDHCI_PLTFM + select MMC_SDHCI_IO_ACCESSORS + select MMC_SDHCI_BCM63xx + depends on PLAT_BCM63138 + +config PLAT_BCM63XX_UART + bool "Enable Broadcom Serial console" + +config PLAT_BCM63XX_EXT_TIMER + bool "Enable Broadcom External Timer for Clockevent" + depends on BCM_EXT_TIMER + +config PLAT_BCM63XX_ACP + bool "Enable ARM ACP" + default n + select BCM_ZONE_ACP + depends on PLAT_BCM63138 && PLAT_CA9_SMP + +config PLAT_BCM63138 + bool "Broadcom BCM63138 SoC support" +# Based on ARM Cortex-A9 r4p1 and L310 r3p3 +# select PLAT_CA9_SMP # will be chosen by BRCM_SMP_EN + select ARM_ERRATA_754322 + select CACHE_L2X0 + select CACHE_PL310 + select EARLY_PRINTK + select PLAT_BCM63XX_EXT_TIMER +# select PLAT_CA9_MPCORE_TIMER +# select PLAT_BCM63XX_AMBA_PL011 + select PLAT_BCM63XX_UART +# select MIGHT_HAVE_PCI +# select HAVE_PWM # kona has it, Do we? + select ZONE_DMA + select ARCH_SUPPORTS_MSI + select CONFIG_ARCH_HAS_BARRIERS + select HARDEN_BRANCH_PREDICTOR + depends on PLAT_CA9_MPCORE + +config PLAT_BCM63148 + bool "Broadcom BCM63148 SoC support" +# Based on ARM Cortex-A15 r3p2 + select ARM_L1_CACHE_SHIFT_6 # B15 L1 cache line size is 64 bytes +# select PLAT_B15_SMP # will be chosen by BRCM_SMP_EN + select EARLY_PRINTK + select PLAT_BCM63XX_EXT_TIMER + select PLAT_BCM63XX_UART + select ARM_ERRATA_798181 if SMP + select ZONE_DMA + select ARCH_SUPPORTS_MSI + select BCM_B15_MEGA_BARRIER + select CONFIG_ARCH_HAS_BARRIERS + select HARDEN_BRANCH_PREDICTOR + depends on PLAT_B15_CORE + +config BCM_PCIE_PLATFORM + tristate "PCI Express repower module" + +#Apply the mega-barrier prior to DMA operations to work around issue noted +#in HW7445-1301 +config BCM_B15_MEGA_BARRIER + depends on PLAT_B15_CORE + bool + +config HARDEN_BRANCH_PREDICTOR + depends on PLAT_B15_CORE || PLAT_CA9_MPCORE + bool + +endif # BCM_KF_ARM_BCM963XX diff -urN linux-4.1.52-orig/arch/arm/plat-bcm63xx/Makefile linux-4.1.52/arch/arm/plat-bcm63xx/Makefile --- linux-4.1.52-orig/arch/arm/plat-bcm63xx/Makefile 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/plat-bcm63xx/Makefile 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,31 @@ +ifdef BCM_KF # defined(CONFIG_BCM_KF_ARM_BCM963XX) +# SHARED := ../../../../../../../src/shared + +ifdef CONFIG_BCM96878 +obj-y += bcm6878.o +else +ifeq ($(strip $(CONFIG_BCM96846)),) +AFLAGS_plat-head.o := -DTEXT_OFFSET=$(TEXT_OFFSET) +obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o +obj-$(CONFIG_PLAT_CA9_MPCORE) += ca9mp_core.o +obj-$(CONFIG_PLAT_CA9_MPCORE_TIMER) += ca9mp_timer.o +obj-$(CONFIG_PLAT_B15_CORE) += b15_core.o +obj-$(CONFIG_CACHE_L310) += cache-l310.o +obj-$(CONFIG_PLAT_BCM63138) += bcm63138.o +obj-$(CONFIG_PLAT_BCM63148) += bcm63148.o +obj-$(CONFIG_PLAT_BCM63XX_EXT_TIMER) += bcm63xx_timer.o +obj-$(CONFIG_PLAT_BCM63XX_ACP) += bcm63xx_acp.o +obj-$(CONFIG_BCM_M2M_DMA) += bcm63xx_m2mdma.o +else +obj-y += bcm6846.o +obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o +endif +endif + +EXTRA_CFLAGS += -I$(INC_BRCMBOARDPARMS_PATH)/$(BRCM_BOARD) -I$(SRCBASE)/include -I$(INC_BRCMDRIVER_PUB_PATH)/$(BRCM_BOARD) -I$(INC_BRCMSHARED_PUB_PATH)/$(BRCM_BOARD) -I$(INC_BRCMSHARED_PUB_PATH)/pmc + +ifeq ($(strip $(CONFIG_BCM96878)),) +obj-$(CONFIG_SMP) += platsmp.o plat-ca9mp-headsmp.o +endif + +endif # BCM_KF # defined(CONFIG_BCM_KF_ARM_BCM963XX) diff -urN linux-4.1.52-orig/arch/arm/plat-bcm63xx/plat-ca9mp-headsmp.S linux-4.1.52/arch/arm/plat-bcm63xx/plat-ca9mp-headsmp.S --- linux-4.1.52-orig/arch/arm/plat-bcm63xx/plat-ca9mp-headsmp.S 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/plat-bcm63xx/plat-ca9mp-headsmp.S 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,76 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +#include +#include + + __CPUINIT + +/* + * Broadcom specific entry point for secondary CPUs. + * This provides a "holding pen" into which all secondary cores are held + * until we're ready for them to initialise. + */ +ENTRY(platform_secondary_startup) + /* + * Get hardware CPU id of ours + */ + mrc p15, 0, r0, c0, c0, 5 + and r0, r0, #15 + adr r4, 1f + ldmia r4, {r5, r6} + sub r4, r4, r5 + add r6, r6, r4 +pen: ldr r7, [r6] + cmp r7, r0 + bne pen + nop + + /* enable the cpu cycle counter on second core. adsl driver use that */ + mrc p15, 0, r1, c9, c12, 0 + ldr r2, =5 + orr r1, r1, r2 + mcr p15, 0, r1, c9, c12, 0 + ldr r1, =0x80000000 + mcr p15, 0, r1, c9, c12, 1 + + bleq v7_invalidate_l1 + + /* + * we've been released from the holding pen: secondary_stack + * should now contain the SVC stack for this core + */ + b secondary_startup + + .align +1: .long . + .long pen_release +ENDPROC(platform_secondary_startup) + +#endif /* CONFIG_BCM_KF_ARM_BCM963XX */ diff -urN linux-4.1.52-orig/arch/arm/plat-bcm63xx/platsmp.c linux-4.1.52/arch/arm/plat-bcm63xx/platsmp.c --- linux-4.1.52-orig/arch/arm/plat-bcm63xx/platsmp.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/plat-bcm63xx/platsmp.c 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,176 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PLAT_CA9_MPCORE +#include +#endif +#include +#include +#include +#include + +static DEFINE_RAW_SPINLOCK(boot_lock); + +/* write pen_release in a way that is guaranteed to be visible to all + * observers, irrespective of whatever they'are taking part in coherency + * or not. This is necessary for the hotplug code to work reliably. + */ +static void __cpuinit write_pen_release(int val) +{ + pen_release = val; + smp_wmb(); + sync_cache_w(&pen_release); +} + +static void __cpuinit platform_secondary_init(unsigned int cpu) +{ + trace_hardirqs_off(); + + /* + * let the primary processor know we're out of the + * pen, then head off into the C entry point + */ + write_pen_release(-1); + + /* + * Synchronise with the boot thread. + */ + raw_spin_lock(&boot_lock); + raw_spin_unlock(&boot_lock); +} + +static int __cpuinit platform_boot_secondary(unsigned int cpu, struct task_struct *idle) +{ + unsigned long timeout; + + /* + * set synchronisation state between this boot processor + * and the secondary one + */ + raw_spin_lock(&boot_lock); + + /* + * The secondary processor is waiting to be released from + * the holding pen - release it, then wait for it to flag + * that it has been released by resetting pen_release. + * + * Note that "pen_release" is the hardware CPU ID, whereas + * "cpu" is Linux's internal ID. + */ + write_pen_release(cpu_logical_map(cpu)); + + /* + * Send the secondary CPU a soft interrupt, thereby causing + * the boot monitor to read the system wide flags register, + * and branch to the address found there. + */ + arch_send_wakeup_ipi_mask(cpumask_of(cpu)); + + timeout = jiffies + (1 * HZ); + while (time_before(jiffies, timeout)) { + smp_rmb(); + if (pen_release == -1) + break; + + udelay(10); + } + + /* + * now the secondary core is starting up let it run its + * calibrations, then wait for it to finish + */ + raw_spin_unlock(&boot_lock); + + return pen_release != -1 ? -ENOSYS : 0; +} + +#ifdef CONFIG_PLAT_CA9_MPCORE +static void __init platform_smp_init_cpus(void) +{ + void __iomem *scu_base = scu_base_addr(); + unsigned int i, ncores; + + ncores = scu_base ? scu_get_core_count(scu_base) : 1; + + /* sanity check */ + if (ncores > nr_cpu_ids) { + pr_warn("SMP: %u cores greater than maximum (%u), clipping\n", + ncores, nr_cpu_ids); + ncores = nr_cpu_ids; + } + + for (i = 0; i < ncores; i++) + set_cpu_possible(i, true); +} +#endif + +static void __init platform_smp_prepare_cpus(unsigned int max_cpus) +{ + /* + * Initialise the SCU and wake up the secondary core using + * wakeup_secondary(). + */ +#ifdef CONFIG_PLAT_CA9_MPCORE + scu_enable(scu_base_addr()); +#endif + + /* 1) covert the virtual starting address into physical, then + * write it to register for the bootloader to read from */ + GPIO->GeneralPurpose = virt_to_phys(platform_secondary_startup); +} + +struct smp_operations bcm_smp_ops __initdata = { +#ifdef CONFIG_PLAT_CA9_MPCORE + .smp_init_cpus = platform_smp_init_cpus, +#endif + .smp_prepare_cpus = platform_smp_prepare_cpus, + .smp_secondary_init = platform_secondary_init, + .smp_boot_secondary = platform_boot_secondary, +#ifdef CONFIG_HOTPLUG_CPU + .cpu_die = platform_cpu_die, +#endif +}; + +CPU_METHOD_OF_DECLARE(bcm_smp, "brcm,bca-smp", &bcm_smp_ops); + +#endif /* CONFIG_BCM_KF_ARM_BCM963XX */ diff -urN linux-4.1.52-orig/arch/arm/plat-bcm63xx/README linux-4.1.52/arch/arm/plat-bcm63xx/README --- linux-4.1.52-orig/arch/arm/plat-bcm63xx/README 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm/plat-bcm63xx/README 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,11 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +This directory provides support to selected ARM-based SoC from Broadcom. + +The code in this directory is pertinent to the contents of each SoC. +One can choose to enable the SoC in SMP or uni-core mode with the flag. + +This directory also provides the binding information +(i.e. base address and IRQ#) for all the peripherals contained in the SoC. + +All board-level support is in "mach-bcm*'. +#endif diff -urN linux-4.1.52-orig/arch/arm/tools/mach-types linux-4.1.52/arch/arm/tools/mach-types --- linux-4.1.52-orig/arch/arm/tools/mach-types 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm/tools/mach-types 2020-02-07 01:47:53.000000000 +0800 @@ -1006,3 +1006,6 @@ eukrea_cpuimx28sd MACH_EUKREA_CPUIMX28SD EUKREA_CPUIMX28SD 4573 domotab MACH_DOMOTAB DOMOTAB 4574 pfla03 MACH_PFLA03 PFLA03 4575 +bcm963148 MACH_BCM963148 BCM963148 9993 +bcm963138 MACH_BCM963138 BCM963138 9994 +bcm96846 MACH_BCM96846 BCM96846 9995 diff -urN linux-4.1.52-orig/arch/arm64/include/asm/cacheflush.h linux-4.1.52/arch/arm64/include/asm/cacheflush.h --- linux-4.1.52-orig/arch/arm64/include/asm/cacheflush.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm64/include/asm/cacheflush.h 2020-02-07 01:47:54.000000000 +0800 @@ -74,6 +74,9 @@ extern void flush_icache_range(unsigned long start, unsigned long end); extern void __flush_dcache_area(void *addr, size_t len); extern long __flush_cache_user_range(unsigned long start, unsigned long end); +#if defined(CONFIG_BCM_KF_NBUFF) +extern void __inval_cache_range(void *start, void* end); +#endif static inline void flush_cache_mm(struct mm_struct *mm) { diff -urN linux-4.1.52-orig/arch/arm64/include/asm/cputype.h linux-4.1.52/arch/arm64/include/asm/cputype.h --- linux-4.1.52-orig/arch/arm64/include/asm/cputype.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm64/include/asm/cputype.h 2020-02-07 01:47:54.000000000 +0800 @@ -64,6 +64,9 @@ #define ARM_CPU_IMP_ARM 0x41 #define ARM_CPU_IMP_APM 0x50 +#if defined CONFIG_BCM_KF_ARM64_BCM963XX +#define ARM_CPU_IMP_BRCM 0x42 +#endif #define ARM_CPU_PART_AEM_V8 0xD0F #define ARM_CPU_PART_FOUNDATION 0xD00 @@ -71,6 +74,9 @@ #define ARM_CPU_PART_CORTEX_A53 0xD03 #define APM_CPU_PART_POTENZA 0x000 +#if defined CONFIG_BCM_KF_ARM64_BCM963XX +#define ARM_CPU_PART_CORTEX_B53 0x100 +#endif #define ID_AA64MMFR0_BIGENDEL0_SHIFT 16 #define ID_AA64MMFR0_BIGENDEL0_MASK (0xf << ID_AA64MMFR0_BIGENDEL0_SHIFT) diff -urN linux-4.1.52-orig/arch/arm64/include/asm/pgtable-hwdef.h linux-4.1.52/arch/arm64/include/asm/pgtable-hwdef.h --- linux-4.1.52-orig/arch/arm64/include/asm/pgtable-hwdef.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm64/include/asm/pgtable-hwdef.h 2020-02-07 01:47:53.000000000 +0800 @@ -100,7 +100,11 @@ #define PTE_TABLE_BIT (_AT(pteval_t, 1) << 1) #define PTE_USER (_AT(pteval_t, 1) << 6) /* AP[1] */ #define PTE_RDONLY (_AT(pteval_t, 1) << 7) /* AP[2] */ +#if defined(CONFIG_BCM_KF_GLB_COHERENCY) && defined(CONFIG_BCM_GLB_COHERENCY) +#define PTE_SHARED (_AT(pteval_t, 2) << 8) /* SH[1:0], outer shareable */ +#else #define PTE_SHARED (_AT(pteval_t, 3) << 8) /* SH[1:0], inner shareable */ +#endif #define PTE_AF (_AT(pteval_t, 1) << 10) /* Access Flag */ #define PTE_NG (_AT(pteval_t, 1) << 11) /* nG */ #define PTE_PXN (_AT(pteval_t, 1) << 53) /* Privileged XN */ @@ -158,7 +162,12 @@ #define TCR_ORGN_WT ((UL(2) << 10) | (UL(2) << 26)) #define TCR_ORGN_WBnWA ((UL(3) << 10) | (UL(3) << 26)) #define TCR_ORGN_MASK ((UL(3) << 10) | (UL(3) << 26)) +#if defined(CONFIG_BCM_KF_GLB_COHERENCY) && defined(CONFIG_BCM_GLB_COHERENCY) +/* SH0: Outer Shareable, SH1: Outer Shareable */ +#define TCR_SHARED ((UL(2) << 12) | (UL(2) << 28)) +#else #define TCR_SHARED ((UL(3) << 12) | (UL(3) << 28)) +#endif #define TCR_TG0_4K (UL(0) << 14) #define TCR_TG0_64K (UL(1) << 14) #define TCR_TG0_16K (UL(2) << 14) diff -urN linux-4.1.52-orig/arch/arm64/Kconfig linux-4.1.52/arch/arm64/Kconfig --- linux-4.1.52-orig/arch/arm64/Kconfig 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm64/Kconfig 2020-02-07 01:47:54.000000000 +0800 @@ -486,6 +486,11 @@ help Say Y if you plan on running a kernel in big-endian mode. +if BCM_KF_ARM64_BCM963XX +config CPU_LITTLE_ENDIAN + bool "Build little-endian kernel" +endif + config SMP bool "Symmetric Multi-Processing" help @@ -537,6 +542,7 @@ config HZ int + prompt "Timer frequency" if BCM_KF_ARM64_BCM963XX default 100 config ARCH_HAS_HOLES_MEMORYMODEL diff -urN linux-4.1.52-orig/arch/arm64/kernel/asm-offsets.c linux-4.1.52/arch/arm64/kernel/asm-offsets.c --- linux-4.1.52-orig/arch/arm64/kernel/asm-offsets.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm64/kernel/asm-offsets.c 2020-02-07 01:47:53.000000000 +0800 @@ -28,6 +28,9 @@ #include #include #include +#if defined(CONFIG_BCM_KF_OPTEE) +#include +#endif int main(void) { @@ -158,5 +161,13 @@ DEFINE(SLEEP_SAVE_SP_PHYS, offsetof(struct sleep_save_sp, save_ptr_stash_phys)); DEFINE(SLEEP_SAVE_SP_VIRT, offsetof(struct sleep_save_sp, save_ptr_stash)); #endif + +#if defined(CONFIG_BCM_KF_OPTEE) + DEFINE(ARM_SMCCC_RES_X0_OFFS, offsetof(struct arm_smccc_res, a0)); + DEFINE(ARM_SMCCC_RES_X2_OFFS, offsetof(struct arm_smccc_res, a2)); + DEFINE(ARM_SMCCC_QUIRK_ID_OFFS, offsetof(struct arm_smccc_quirk, id)); + DEFINE(ARM_SMCCC_QUIRK_STATE_OFFS, offsetof(struct arm_smccc_quirk, state)); +#endif /* CONFIG_BCM_KF_OPTEE */ + return 0; } diff -urN linux-4.1.52-orig/arch/arm64/kernel/cpu_errata.c linux-4.1.52/arch/arm64/kernel/cpu_errata.c --- linux-4.1.52-orig/arch/arm64/kernel/cpu_errata.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm64/kernel/cpu_errata.c 2020-02-07 01:47:53.000000000 +0800 @@ -23,6 +23,9 @@ #define MIDR_CORTEX_A53 MIDR_CPU_PART(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A53) #define MIDR_CORTEX_A57 MIDR_CPU_PART(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A57) +#if defined CONFIG_BCM_KF_ARM64_BCM963XX +#define MIDR_CORTEX_B53 MIDR_CPU_PART(ARM_CPU_IMP_BRCM, ARM_CPU_PART_CORTEX_B53) +#endif #define CPU_MODEL_MASK (MIDR_IMPLEMENTOR_MASK | MIDR_PARTNUM_MASK | \ MIDR_ARCHITECTURE_MASK) @@ -81,6 +84,14 @@ .capability = ARM64_WORKAROUND_845719, MIDR_RANGE(MIDR_CORTEX_A53, 0x00, 0x04), }, +#if defined CONFIG_BCM_KF_ARM64_BCM963XX + { + /* Cortex-B53 based on r0p4.Need this errata */ + .desc = "ARM erratum 845719", + .capability = ARM64_WORKAROUND_845719, + MIDR_RANGE(MIDR_CORTEX_B53, 0x00, 0x04), + }, +#endif #endif { } diff -urN linux-4.1.52-orig/arch/arm64/kernel/irq.c linux-4.1.52/arch/arm64/kernel/irq.c --- linux-4.1.52-orig/arch/arm64/kernel/irq.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm64/kernel/irq.c 2020-02-07 01:47:53.000000000 +0800 @@ -28,6 +28,9 @@ #include #include #include +#if defined CONFIG_BCM_KF_CPU_AFFINITY_HINT && defined CONFIG_HOTPLUG_CPU +#include +#endif unsigned long irq_err_count; @@ -55,6 +58,9 @@ irqchip_init(); if (!handle_arch_irq) panic("No interrupt controller found."); +#if defined CONFIG_BCM_KF_CPU_AFFINITY_HINT && defined CONFIG_HOTPLUG_CPU + hotcpu_notifier(bcm63xx_rehint_irqaffinity, 0); +#endif } #ifdef CONFIG_HOTPLUG_CPU @@ -73,6 +79,13 @@ return false; if (cpumask_any_and(affinity, cpu_online_mask) >= nr_cpu_ids) { +#if defined CONFIG_BCM_KF_CPU_AFFINITY_HINT + // affine cpu is offline; any hinted cpu online? + if (desc->affinity_hint && + cpumask_any_and(desc->affinity_hint, cpu_online_mask) < nr_cpu_ids) + affinity = desc->affinity_hint; + else +#endif affinity = cpu_online_mask; ret = true; } diff -urN linux-4.1.52-orig/arch/arm64/kernel/Makefile linux-4.1.52/arch/arm64/kernel/Makefile --- linux-4.1.52-orig/arch/arm64/kernel/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm64/kernel/Makefile 2020-02-07 01:47:53.000000000 +0800 @@ -5,6 +5,11 @@ CPPFLAGS_vmlinux.lds := -DTEXT_OFFSET=$(TEXT_OFFSET) AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET) CFLAGS_efi-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET) +CFLAGS_smp_spin_table.o := -I${INC_BRCMSHARED_PUB_PATH}/bcm963xx\ + -I${INC_BRCMDRIVER_PUB_PATH}/bcm963xx\ + -I${INC_BRCMSHARED_PUB_PATH}/pmc +CFLAGS_irq.o := -I${INC_BRCMSHARED_PUB_PATH}/bcm963xx\ + -I${INC_BRCMDRIVER_PUB_PATH}/bcm963xx CFLAGS_armv8_deprecated.o := -I$(src) CFLAGS_REMOVE_ftrace.o = -pg @@ -19,6 +24,10 @@ return_address.o cpuinfo.o cpu_errata.o \ cpufeature.o alternative.o cacheinfo.o +ifdef BCM_KF # defined(CONFIG_BCM_KF_OPTEE_414_BACKPORTS) +arm64-obj-$(CONFIG_HAVE_ARM_SMCCC) += smccc-call.o +endif + arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ sys_compat.o entry32.o \ ../../arm/kernel/opcodes.o diff -urN linux-4.1.52-orig/arch/arm64/kernel/perf_event.c linux-4.1.52/arch/arm64/kernel/perf_event.c --- linux-4.1.52-orig/arch/arm64/kernel/perf_event.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm64/kernel/perf_event.c 2020-02-07 01:47:53.000000000 +0800 @@ -699,6 +699,10 @@ ARMV8_PMUV3_PERFCTR_BUS_ACCESS = 0x19, ARMV8_PMUV3_PERFCTR_MEM_ERROR = 0x1A, ARMV8_PMUV3_PERFCTR_BUS_CYCLES = 0x1D, +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + ARMV8_PMUV3_PERFCTR_STALL_FRONTEND = 0x23, + ARMV8_PMUV3_PERFCTR_STALL_BACKEND = 0x24, +#endif }; /* PMUv3 HW events mapping. */ @@ -707,11 +711,21 @@ [PERF_COUNT_HW_INSTRUCTIONS] = ARMV8_PMUV3_PERFCTR_INSTR_EXECUTED, [PERF_COUNT_HW_CACHE_REFERENCES] = ARMV8_PMUV3_PERFCTR_L1_DCACHE_ACCESS, [PERF_COUNT_HW_CACHE_MISSES] = ARMV8_PMUV3_PERFCTR_L1_DCACHE_REFILL, +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = ARMV8_PMUV3_PERFCTR_PC_WRITE, +#else [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = HW_OP_UNSUPPORTED, +#endif [PERF_COUNT_HW_BRANCH_MISSES] = ARMV8_PMUV3_PERFCTR_PC_BRANCH_MIS_PRED, +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + [PERF_COUNT_HW_BUS_CYCLES] = ARMV8_PMUV3_PERFCTR_BUS_CYCLES, + [PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = ARMV8_PMUV3_PERFCTR_STALL_FRONTEND, + [PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = ARMV8_PMUV3_PERFCTR_STALL_BACKEND, +#else [PERF_COUNT_HW_BUS_CYCLES] = HW_OP_UNSUPPORTED, [PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = HW_OP_UNSUPPORTED, [PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = HW_OP_UNSUPPORTED, +#endif }; static const unsigned armv8_pmuv3_perf_cache_map[PERF_COUNT_HW_CACHE_MAX] @@ -733,8 +747,13 @@ }, [C(L1I)] = { [C(OP_READ)] = { +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + [C(RESULT_ACCESS)] = ARMV8_PMUV3_PERFCTR_L1_ICACHE_ACCESS, + [C(RESULT_MISS)] = ARMV8_PMUV3_PERFCTR_L1_ICACHE_REFILL, +#else [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, +#endif }, [C(OP_WRITE)] = { [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, @@ -747,12 +766,22 @@ }, [C(LL)] = { [C(OP_READ)] = { +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + [C(RESULT_ACCESS)] = ARMV8_PMUV3_PERFCTR_L2_CACHE_ACCESS, + [C(RESULT_MISS)] = ARMV8_PMUV3_PERFCTR_L2_CACHE_REFILL, +#else [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, +#endif }, [C(OP_WRITE)] = { +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + [C(RESULT_ACCESS)] = ARMV8_PMUV3_PERFCTR_L2_CACHE_ACCESS, + [C(RESULT_MISS)] = ARMV8_PMUV3_PERFCTR_L2_CACHE_REFILL, +#else [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, +#endif }, [C(OP_PREFETCH)] = { [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, @@ -762,7 +791,11 @@ [C(DTLB)] = { [C(OP_READ)] = { [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + [C(RESULT_MISS)] = ARMV8_PMUV3_PERFCTR_DTLB_REFILL, +#else [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, +#endif }, [C(OP_WRITE)] = { [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, @@ -776,7 +809,11 @@ [C(ITLB)] = { [C(OP_READ)] = { [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + [C(RESULT_MISS)] = ARMV8_PMUV3_PERFCTR_ITLB_REFILL, +#else [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, +#endif }, [C(OP_WRITE)] = { [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, diff -urN linux-4.1.52-orig/arch/arm64/kernel/psci.c linux-4.1.52/arch/arm64/kernel/psci.c --- linux-4.1.52-orig/arch/arm64/kernel/psci.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm64/kernel/psci.c 2020-02-07 01:47:53.000000000 +0800 @@ -122,6 +122,17 @@ return err; } +#if defined(CONFIG_BCM_KF_OPTEE) && defined (CONFIG_BCM_KERNEL_CODE_PROTECTION) +static int psci_mem_info(void) +{ + int err; + extern char _stext[], _etext[], __init_begin[]; + + err = invoke_psci_fn(PSCI_0_2_FN_MEM_INFO, (u32)(long)_stext, (u32)(long)_etext, (u32)(long)__init_begin); + return err; +} +#endif + static int psci_cpu_suspend(struct psci_power_state state, unsigned long entry_point) { @@ -329,7 +340,9 @@ } psci_0_2_set_functions(); - +#if defined(CONFIG_BCM_KF_OPTEE) && defined (CONFIG_BCM_KERNEL_CODE_PROTECTION) + psci_mem_info(); +#endif return 0; } diff -urN linux-4.1.52-orig/arch/arm64/kernel/setup.c linux-4.1.52/arch/arm64/kernel/setup.c --- linux-4.1.52-orig/arch/arm64/kernel/setup.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm64/kernel/setup.c 2020-02-07 01:47:53.000000000 +0800 @@ -522,7 +522,9 @@ #ifdef CONFIG_SMP seq_printf(m, "processor\t: %d\n", i); #endif - +#ifdef CONFIG_BCM_KF_ARM64_BCM963XX + seq_printf(m, "cpu model\t: ARM\n"); +#endif seq_printf(m, "BogoMIPS\t: %lu.%02lu\n", loops_per_jiffy / (500000UL/HZ), loops_per_jiffy / (5000UL/HZ) % 100); diff -urN linux-4.1.52-orig/arch/arm64/kernel/smccc-call.S linux-4.1.52/arch/arm64/kernel/smccc-call.S --- linux-4.1.52-orig/arch/arm64/kernel/smccc-call.S 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/arm64/kernel/smccc-call.S 2020-02-07 01:47:53.000000000 +0800 @@ -0,0 +1,54 @@ +#if defined(CONFIG_BCM_KF_OPTEE_414_BACKPORTS) +/* + * Copyright (c) 2015, Linaro Limited + * + * 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. + * + */ +#include +#include +#include + + .macro SMCCC instr + .cfi_startproc + \instr #0 + ldr x4, [sp] + stp x0, x1, [x4, #ARM_SMCCC_RES_X0_OFFS] + stp x2, x3, [x4, #ARM_SMCCC_RES_X2_OFFS] + ldr x4, [sp, #8] + cbz x4, 1f /* no quirk structure */ + ldr x9, [x4, #ARM_SMCCC_QUIRK_ID_OFFS] + cmp x9, #ARM_SMCCC_QUIRK_QCOM_A6 + b.ne 1f + str x6, [x4, ARM_SMCCC_QUIRK_STATE_OFFS] +1: ret + .cfi_endproc + .endm + +/* + * void arm_smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2, + * unsigned long a3, unsigned long a4, unsigned long a5, + * unsigned long a6, unsigned long a7, struct arm_smccc_res *res, + * struct arm_smccc_quirk *quirk) + */ +ENTRY(__arm_smccc_smc) + SMCCC smc +ENDPROC(__arm_smccc_smc) + +/* + * void arm_smccc_hvc(unsigned long a0, unsigned long a1, unsigned long a2, + * unsigned long a3, unsigned long a4, unsigned long a5, + * unsigned long a6, unsigned long a7, struct arm_smccc_res *res, + * struct arm_smccc_quirk *quirk) + */ +ENTRY(__arm_smccc_hvc) + SMCCC hvc +ENDPROC(__arm_smccc_hvc) +#endif /* CONFIG_BCM_KF_OPTEE_414_BACKPORTS */ diff -urN linux-4.1.52-orig/arch/arm64/kernel/smp_spin_table.c linux-4.1.52/arch/arm64/kernel/smp_spin_table.c --- linux-4.1.52-orig/arch/arm64/kernel/smp_spin_table.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm64/kernel/smp_spin_table.c 2020-02-07 01:47:53.000000000 +0800 @@ -27,6 +27,9 @@ #include #include #include +#ifdef CONFIG_BCM_KF_OPTEE +#include // FIXME after OPTEE matures +#endif extern void secondary_holding_pen(void); volatile unsigned long secondary_holding_pen_release = INVALID_HWID; @@ -104,6 +107,130 @@ return 0; } +#if defined CONFIG_BCM_KF_ARM64_BCM963XX && defined CONFIG_HOTPLUG_CPU + +# ifdef CONFIG_BCM94908 +# include "bcm_map_part.h" + +static int smp_spin_table_cpu_boot(unsigned int cpu) +{ + if (BIUCTRL->cpu_pwr_zone_ctrl[cpu] & BIU_CPU_CTRL_PWR_ZONE_CTRL_ZONE_RESET) { + BIUCTRL->power_cfg |= BIU_CPU_CTRL_PWR_CFG_CPU0_BPCM_INIT_ON << cpu; + BIUCTRL->cpu_pwr_zone_ctrl[cpu] = BIU_CPU_CTRL_PWR_ZONE_CTRL_PWR_UP_REQ | + (BIUCTRL->cpu_pwr_zone_ctrl[cpu] & ~BIU_CPU_CTRL_PWR_ZONE_CTRL_PWR_DN_REQ); + + udelay(100); // wait for cpu to come out of reset + } + + /* + * Update the pen release flag. + */ + write_pen_release(cpu_logical_map(cpu)); + + /* + * Send an event, causing the secondaries to read pen_release. + */ + sev(); + + return 0; +} + +static void smp_spin_table_cpu_die(unsigned int cpu) +{ + wmb(); + cpu_cache_off(); + flush_cache_all(); + + udelay(10); // delay after cache flush + +#ifdef CONFIG_BCM_KF_OPTEE + write_pen_release(INVALID_HWID); + soft_restart(__pa(secondary_holding_pen)); +#else + BIUCTRL->power_cfg &= ~(BIU_CPU_CTRL_PWR_CFG_CPU0_BPCM_INIT_ON << cpu); + BIUCTRL->cpu_pwr_zone_ctrl[cpu] = BIU_CPU_CTRL_PWR_ZONE_CTRL_PWR_DN_REQ | + (BIUCTRL->cpu_pwr_zone_ctrl[cpu] & ~BIU_CPU_CTRL_PWR_ZONE_CTRL_PWR_UP_REQ); + + while (1) cpu_do_idle(); +#endif + /*NOTREACHED*/ +} + +#define smp_spin_table_cpu_kill 0 + +# elif (defined (CONFIG_BCM96858) || defined(CONFIG_BCM963158)) +# include "pmc_drv.h" +# include "BPCM.h" + +static struct completion cpu_flush[NR_CPUS]; + +static const unsigned int pmb[] = { + PMB_ADDR_ORION_CPU0, PMB_ADDR_ORION_CPU1, + PMB_ADDR_ORION_CPU2, PMB_ADDR_ORION_CPU3, +}; + +static int smp_spin_table_cpu_boot(unsigned int cpu) +{ + BPCM_PWR_ZONE_N_CONTROL zctl; + int rc; + + rc = ReadZoneRegister(pmb[cpu], 0, BPCMZoneOffset(control), &zctl.Reg32); + if (rc == 0 && zctl.Bits.reset_state) { + PowerOnZone(pmb[cpu], 0); + + udelay(100); // wait for cpu to come out of reset + } + + /* + * Update the pen release flag. + */ + write_pen_release(cpu_logical_map(cpu)); + + /* + * Send an event, causing the secondaries to read pen_release. + */ + sev(); + + return 0; +} + +static void smp_spin_table_cpu_die(unsigned int cpu) +{ + init_completion(&cpu_flush[cpu]); + wmb(); + +// cpu_cache_off(); + flush_cache_all(); + + complete(&cpu_flush[cpu]); + wmb(); + +#ifdef CONFIG_BCM_KF_OPTEE + write_pen_release(INVALID_HWID); + soft_restart(__pa(secondary_holding_pen)); +#else + while (1) cpu_do_idle(); +#endif + /*NOTREACHED*/ +} + +static int smp_spin_table_cpu_kill(unsigned int cpu) +{ + if (wait_for_completion_timeout(&cpu_flush[cpu], msecs_to_jiffies(10)) == 0) + return 0; + + udelay(10); // delay after cache flush + +#ifdef CONFIG_BCM_KF_OPTEE + return 1; // do not power off that cpu if OPTEE is configured +#else + return PowerOffZone(pmb[cpu], 0) == kPMC_NO_ERROR; // XXX repower flag ignored +#endif +} + +# endif + +#else static int smp_spin_table_cpu_boot(unsigned int cpu) { /* @@ -118,10 +245,15 @@ return 0; } +#endif const struct cpu_operations smp_spin_table_ops = { .name = "spin-table", .cpu_init = smp_spin_table_cpu_init, .cpu_prepare = smp_spin_table_cpu_prepare, .cpu_boot = smp_spin_table_cpu_boot, +#if defined CONFIG_BCM_KF_ARM64_BCM963XX && defined CONFIG_HOTPLUG_CPU + .cpu_kill = smp_spin_table_cpu_kill, + .cpu_die = smp_spin_table_cpu_die, +#endif }; diff -urN linux-4.1.52-orig/arch/arm64/mm/init.c linux-4.1.52/arch/arm64/mm/init.c --- linux-4.1.52-orig/arch/arm64/mm/init.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/arm64/mm/init.c 2020-02-07 01:47:54.000000000 +0800 @@ -74,7 +74,14 @@ static phys_addr_t max_zone_dma_phys(void) { phys_addr_t offset = memblock_start_of_DRAM() & GENMASK_ULL(63, 32); +#if defined(CONFIG_BCM_KF_ARM64_BCM963XX) && defined(CONFIG_BCM94908) && defined(CONFIG_BCM_HND_EAP) + /* Using ZONE_DMA to work around a 4908 Ethernet packet transmission + * performance issue when packet buffer is located at address + * larger than 1GB. */ + return min(offset + (1ULL << 30), memblock_end_of_DRAM()); +#else return min(offset + (1ULL << 32), memblock_end_of_DRAM()); +#endif } static void __init zone_sizes_init(unsigned long min, unsigned long max) diff -urN linux-4.1.52-orig/arch/microblaze/boot/dts/system.dts linux-4.1.52/arch/microblaze/boot/dts/system.dts --- linux-4.1.52-orig/arch/microblaze/boot/dts/system.dts 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/microblaze/boot/dts/system.dts 1970-01-01 08:00:00.000000000 +0800 @@ -1,366 +0,0 @@ -/* - * Device Tree Generator version: 1.1 - * - * (C) Copyright 2007-2008 Xilinx, Inc. - * (C) Copyright 2007-2009 Michal Simek - * - * Michal SIMEK - * - * 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., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA - * - * CAUTION: This file is automatically generated by libgen. - * Version: Xilinx EDK 10.1.03 EDK_K_SP3.6 - * - * XPS project directory: Xilinx-ML505-ll_temac-sgdma-MMU-FDT-edk101 - */ - -/dts-v1/; -/ { - #address-cells = <1>; - #size-cells = <1>; - compatible = "xlnx,microblaze"; - hard-reset-gpios = <&LEDs_8Bit 2 1>; - model = "testing"; - DDR2_SDRAM: memory@90000000 { - device_type = "memory"; - reg = < 0x90000000 0x10000000 >; - } ; - aliases { - ethernet0 = &Hard_Ethernet_MAC; - serial0 = &RS232_Uart_1; - } ; - chosen { - bootargs = "console=ttyUL0,115200 highres=on"; - linux,stdout-path = "/plb@0/serial@84000000"; - } ; - cpus { - #address-cells = <1>; - #cpus = <0x1>; - #size-cells = <0>; - microblaze_0: cpu@0 { - clock-frequency = <125000000>; - compatible = "xlnx,microblaze-7.10.d"; - d-cache-baseaddr = <0x90000000>; - d-cache-highaddr = <0x9fffffff>; - d-cache-line-size = <0x10>; - d-cache-size = <0x2000>; - device_type = "cpu"; - i-cache-baseaddr = <0x90000000>; - i-cache-highaddr = <0x9fffffff>; - i-cache-line-size = <0x10>; - i-cache-size = <0x2000>; - model = "microblaze,7.10.d"; - reg = <0>; - timebase-frequency = <125000000>; - xlnx,addr-tag-bits = <0xf>; - xlnx,allow-dcache-wr = <0x1>; - xlnx,allow-icache-wr = <0x1>; - xlnx,area-optimized = <0x0>; - xlnx,cache-byte-size = <0x2000>; - xlnx,d-lmb = <0x1>; - xlnx,d-opb = <0x0>; - xlnx,d-plb = <0x1>; - xlnx,data-size = <0x20>; - xlnx,dcache-addr-tag = <0xf>; - xlnx,dcache-always-used = <0x1>; - xlnx,dcache-byte-size = <0x2000>; - xlnx,dcache-line-len = <0x4>; - xlnx,dcache-use-fsl = <0x1>; - xlnx,debug-enabled = <0x1>; - xlnx,div-zero-exception = <0x1>; - xlnx,dopb-bus-exception = <0x0>; - xlnx,dynamic-bus-sizing = <0x1>; - xlnx,edge-is-positive = <0x1>; - xlnx,family = "virtex5"; - xlnx,endianness = <0x1>; - xlnx,fpu-exception = <0x1>; - xlnx,fsl-data-size = <0x20>; - xlnx,fsl-exception = <0x0>; - xlnx,fsl-links = <0x0>; - xlnx,i-lmb = <0x1>; - xlnx,i-opb = <0x0>; - xlnx,i-plb = <0x1>; - xlnx,icache-always-used = <0x1>; - xlnx,icache-line-len = <0x4>; - xlnx,icache-use-fsl = <0x1>; - xlnx,ill-opcode-exception = <0x1>; - xlnx,instance = "microblaze_0"; - xlnx,interconnect = <0x1>; - xlnx,interrupt-is-edge = <0x0>; - xlnx,iopb-bus-exception = <0x0>; - xlnx,mmu-dtlb-size = <0x4>; - xlnx,mmu-itlb-size = <0x2>; - xlnx,mmu-tlb-access = <0x3>; - xlnx,mmu-zones = <0x10>; - xlnx,number-of-pc-brk = <0x1>; - xlnx,number-of-rd-addr-brk = <0x0>; - xlnx,number-of-wr-addr-brk = <0x0>; - xlnx,opcode-0x0-illegal = <0x1>; - xlnx,pvr = <0x2>; - xlnx,pvr-user1 = <0x0>; - xlnx,pvr-user2 = <0x0>; - xlnx,reset-msr = <0x0>; - xlnx,sco = <0x0>; - xlnx,unaligned-exceptions = <0x1>; - xlnx,use-barrel = <0x1>; - xlnx,use-dcache = <0x1>; - xlnx,use-div = <0x1>; - xlnx,use-ext-brk = <0x1>; - xlnx,use-ext-nm-brk = <0x1>; - xlnx,use-extended-fsl-instr = <0x0>; - xlnx,use-fpu = <0x2>; - xlnx,use-hw-mul = <0x2>; - xlnx,use-icache = <0x1>; - xlnx,use-interrupt = <0x1>; - xlnx,use-mmu = <0x3>; - xlnx,use-msr-instr = <0x1>; - xlnx,use-pcmp-instr = <0x1>; - } ; - } ; - mb_plb: plb@0 { - #address-cells = <1>; - #size-cells = <1>; - compatible = "xlnx,plb-v46-1.03.a", "xlnx,plb-v46-1.00.a", "simple-bus"; - ranges ; - FLASH: flash@a0000000 { - bank-width = <2>; - compatible = "xlnx,xps-mch-emc-2.00.a", "cfi-flash"; - reg = < 0xa0000000 0x2000000 >; - xlnx,family = "virtex5"; - xlnx,include-datawidth-matching-0 = <0x1>; - xlnx,include-datawidth-matching-1 = <0x0>; - xlnx,include-datawidth-matching-2 = <0x0>; - xlnx,include-datawidth-matching-3 = <0x0>; - xlnx,include-negedge-ioregs = <0x0>; - xlnx,include-plb-ipif = <0x1>; - xlnx,include-wrbuf = <0x1>; - xlnx,max-mem-width = <0x10>; - xlnx,mch-native-dwidth = <0x20>; - xlnx,mch-plb-clk-period-ps = <0x1f40>; - xlnx,mch-splb-awidth = <0x20>; - xlnx,mch0-accessbuf-depth = <0x10>; - xlnx,mch0-protocol = <0x0>; - xlnx,mch0-rddatabuf-depth = <0x10>; - xlnx,mch1-accessbuf-depth = <0x10>; - xlnx,mch1-protocol = <0x0>; - xlnx,mch1-rddatabuf-depth = <0x10>; - xlnx,mch2-accessbuf-depth = <0x10>; - xlnx,mch2-protocol = <0x0>; - xlnx,mch2-rddatabuf-depth = <0x10>; - xlnx,mch3-accessbuf-depth = <0x10>; - xlnx,mch3-protocol = <0x0>; - xlnx,mch3-rddatabuf-depth = <0x10>; - xlnx,mem0-width = <0x10>; - xlnx,mem1-width = <0x20>; - xlnx,mem2-width = <0x20>; - xlnx,mem3-width = <0x20>; - xlnx,num-banks-mem = <0x1>; - xlnx,num-channels = <0x0>; - xlnx,priority-mode = <0x0>; - xlnx,synch-mem-0 = <0x0>; - xlnx,synch-mem-1 = <0x0>; - xlnx,synch-mem-2 = <0x0>; - xlnx,synch-mem-3 = <0x0>; - xlnx,synch-pipedelay-0 = <0x2>; - xlnx,synch-pipedelay-1 = <0x2>; - xlnx,synch-pipedelay-2 = <0x2>; - xlnx,synch-pipedelay-3 = <0x2>; - xlnx,tavdv-ps-mem-0 = <0x1adb0>; - xlnx,tavdv-ps-mem-1 = <0x3a98>; - xlnx,tavdv-ps-mem-2 = <0x3a98>; - xlnx,tavdv-ps-mem-3 = <0x3a98>; - xlnx,tcedv-ps-mem-0 = <0x1adb0>; - xlnx,tcedv-ps-mem-1 = <0x3a98>; - xlnx,tcedv-ps-mem-2 = <0x3a98>; - xlnx,tcedv-ps-mem-3 = <0x3a98>; - xlnx,thzce-ps-mem-0 = <0x88b8>; - xlnx,thzce-ps-mem-1 = <0x1b58>; - xlnx,thzce-ps-mem-2 = <0x1b58>; - xlnx,thzce-ps-mem-3 = <0x1b58>; - xlnx,thzoe-ps-mem-0 = <0x1b58>; - xlnx,thzoe-ps-mem-1 = <0x1b58>; - xlnx,thzoe-ps-mem-2 = <0x1b58>; - xlnx,thzoe-ps-mem-3 = <0x1b58>; - xlnx,tlzwe-ps-mem-0 = <0x88b8>; - xlnx,tlzwe-ps-mem-1 = <0x0>; - xlnx,tlzwe-ps-mem-2 = <0x0>; - xlnx,tlzwe-ps-mem-3 = <0x0>; - xlnx,twc-ps-mem-0 = <0x2af8>; - xlnx,twc-ps-mem-1 = <0x3a98>; - xlnx,twc-ps-mem-2 = <0x3a98>; - xlnx,twc-ps-mem-3 = <0x3a98>; - xlnx,twp-ps-mem-0 = <0x11170>; - xlnx,twp-ps-mem-1 = <0x2ee0>; - xlnx,twp-ps-mem-2 = <0x2ee0>; - xlnx,twp-ps-mem-3 = <0x2ee0>; - xlnx,xcl0-linesize = <0x4>; - xlnx,xcl0-writexfer = <0x1>; - xlnx,xcl1-linesize = <0x4>; - xlnx,xcl1-writexfer = <0x1>; - xlnx,xcl2-linesize = <0x4>; - xlnx,xcl2-writexfer = <0x1>; - xlnx,xcl3-linesize = <0x4>; - xlnx,xcl3-writexfer = <0x1>; - } ; - Hard_Ethernet_MAC: xps-ll-temac@81c00000 { - #address-cells = <1>; - #size-cells = <1>; - compatible = "xlnx,compound"; - ranges ; - ethernet@81c00000 { - compatible = "xlnx,xps-ll-temac-1.01.b", "xlnx,xps-ll-temac-1.00.a"; - interrupt-parent = <&xps_intc_0>; - interrupts = < 5 2 >; - llink-connected = <&PIM3>; - local-mac-address = [ 00 0a 35 00 00 00 ]; - reg = < 0x81c00000 0x40 >; - xlnx,bus2core-clk-ratio = <0x1>; - xlnx,phy-type = <0x1>; - xlnx,phyaddr = <0x1>; - xlnx,rxcsum = <0x0>; - xlnx,rxfifo = <0x1000>; - xlnx,temac-type = <0x0>; - xlnx,txcsum = <0x0>; - xlnx,txfifo = <0x1000>; - } ; - } ; - IIC_EEPROM: i2c@81600000 { - compatible = "xlnx,xps-iic-2.00.a"; - interrupt-parent = <&xps_intc_0>; - interrupts = < 6 2 >; - reg = < 0x81600000 0x10000 >; - xlnx,clk-freq = <0x7735940>; - xlnx,family = "virtex5"; - xlnx,gpo-width = <0x1>; - xlnx,iic-freq = <0x186a0>; - xlnx,scl-inertial-delay = <0x0>; - xlnx,sda-inertial-delay = <0x0>; - xlnx,ten-bit-adr = <0x0>; - } ; - LEDs_8Bit: gpio@81400000 { - compatible = "xlnx,xps-gpio-1.00.a"; - interrupt-parent = <&xps_intc_0>; - interrupts = < 7 2 >; - reg = < 0x81400000 0x10000 >; - xlnx,all-inputs = <0x0>; - xlnx,all-inputs-2 = <0x0>; - xlnx,dout-default = <0x0>; - xlnx,dout-default-2 = <0x0>; - xlnx,family = "virtex5"; - xlnx,gpio-width = <0x8>; - xlnx,interrupt-present = <0x1>; - xlnx,is-bidir = <0x1>; - xlnx,is-bidir-2 = <0x1>; - xlnx,is-dual = <0x0>; - xlnx,tri-default = <0xffffffff>; - xlnx,tri-default-2 = <0xffffffff>; - #gpio-cells = <2>; - gpio-controller; - } ; - - gpio-leds { - compatible = "gpio-leds"; - - heartbeat { - label = "Heartbeat"; - gpios = <&LEDs_8Bit 4 1>; - linux,default-trigger = "heartbeat"; - }; - - yellow { - label = "Yellow"; - gpios = <&LEDs_8Bit 5 1>; - }; - - red { - label = "Red"; - gpios = <&LEDs_8Bit 6 1>; - }; - - green { - label = "Green"; - gpios = <&LEDs_8Bit 7 1>; - }; - } ; - RS232_Uart_1: serial@84000000 { - clock-frequency = <125000000>; - compatible = "xlnx,xps-uartlite-1.00.a"; - current-speed = <115200>; - device_type = "serial"; - interrupt-parent = <&xps_intc_0>; - interrupts = < 8 0 >; - port-number = <0>; - reg = < 0x84000000 0x10000 >; - xlnx,baudrate = <0x1c200>; - xlnx,data-bits = <0x8>; - xlnx,family = "virtex5"; - xlnx,odd-parity = <0x0>; - xlnx,use-parity = <0x0>; - } ; - SysACE_CompactFlash: sysace@83600000 { - compatible = "xlnx,xps-sysace-1.00.a"; - interrupt-parent = <&xps_intc_0>; - interrupts = < 4 2 >; - reg = < 0x83600000 0x10000 >; - xlnx,family = "virtex5"; - xlnx,mem-width = <0x10>; - } ; - debug_module: debug@84400000 { - compatible = "xlnx,mdm-1.00.d"; - reg = < 0x84400000 0x10000 >; - xlnx,family = "virtex5"; - xlnx,interconnect = <0x1>; - xlnx,jtag-chain = <0x2>; - xlnx,mb-dbg-ports = <0x1>; - xlnx,uart-width = <0x8>; - xlnx,use-uart = <0x1>; - xlnx,write-fsl-ports = <0x0>; - } ; - mpmc@90000000 { - #address-cells = <1>; - #size-cells = <1>; - compatible = "xlnx,mpmc-4.02.a"; - ranges ; - PIM3: sdma@84600180 { - compatible = "xlnx,ll-dma-1.00.a"; - interrupt-parent = <&xps_intc_0>; - interrupts = < 2 2 1 2 >; - reg = < 0x84600180 0x80 >; - } ; - } ; - xps_intc_0: interrupt-controller@81800000 { - #interrupt-cells = <0x2>; - compatible = "xlnx,xps-intc-1.00.a"; - interrupt-controller ; - reg = < 0x81800000 0x10000 >; - xlnx,kind-of-intr = <0x100>; - xlnx,num-intr-inputs = <0x9>; - } ; - xps_timer_1: timer@83c00000 { - compatible = "xlnx,xps-timer-1.00.a"; - interrupt-parent = <&xps_intc_0>; - interrupts = < 3 2 >; - reg = < 0x83c00000 0x10000 >; - xlnx,count-width = <0x20>; - xlnx,family = "virtex5"; - xlnx,gen0-assert = <0x1>; - xlnx,gen1-assert = <0x1>; - xlnx,one-timer-only = <0x0>; - xlnx,trig0-assert = <0x1>; - xlnx,trig1-assert = <0x1>; - } ; - } ; -} ; diff -urN linux-4.1.52-orig/arch/mips/bcm963xx/burstbank.c linux-4.1.52/arch/mips/bcm963xx/burstbank.c --- linux-4.1.52-orig/arch/mips/bcm963xx/burstbank.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/mips/bcm963xx/burstbank.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,453 @@ +/* +<:copyright-BRCM:2015:DUAL/GPL:standard + + Copyright (c) 2015 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + +/* + + * BMIPS 4355 Burstbank API + * $Copyright Open Broadcom Corporation$ + * $Id$ + * + * + */ + +/* + * ----------------------------------------------------------------------------- + * + * File Name : burstbank.c + * + * Provides implementation for + * - initialization (enabling) BMIPS4355 DMA-E (burst bank) feature + * - non-inlined callable versions of Burst Bank APIs + * - debug APIs to dump a burst bank channel or the entire system. + * - performance benchmarking using pmontool + * - unit testing + * + * ----------------------------------------------------------------------------- + */ +#define BB_IMPLEMENTATION /* Non-inlined implementation of Burst Bank APIs */ + +#include +#include + +#if defined(BMIPS4355_DMAE) +bb_global_t bb_g; + +#if defined(CC_BB_DEBUG) +/* + * ============================================================================= + * Section: Implementation of Debug APIs + * ============================================================================= + */ + +/* + * ----------------------------------------------------------------------------- + * bb_dump: Dump a Burst Bank, with data and statistics based on verbosity + * ----------------------------------------------------------------------------- + */ +void +bb_dump(volatile bb_t * const bb_p, int verbose) +{ + uint32_t channel = BB_CHANNEL(bb_p); + bb_state_t * const state_p = &bb_g.ch_state[channel]; + uint32_t status_dmae = bb_DMAE(channel, BB_STATUS_USED); + + BB_PRINT("\nBurstBank %s%p%s channel %s%u%s in_use %u GblDMA-E %u\n" + "\tmemory_p %p burst_words %u RD %u WR %u %sError %u%s\n", + _BBg_, bb_p, _BBn_, _BBg_, channel, _BBn_, + state_p->in_use.counter, status_dmae, + bb_p->memory_p, bb_p->burst_words, + BB_RD_TRANS(bb_p) ? 1 : 0, BB_WR_TRANS(bb_p) ? 1 : 0, + BB_ERR_COND(bb_p) ? _BBr_ : _BBn_, + BB_ERR_COND(bb_p) ? 1 : 0, _BBn_); + + if (verbose == 1) { +# undef _BBD +# define _BBD(i) BB_DATA(bb_p, i) + BB_PRINT("\t\tdata00..03 0x%08x 0x%08x 0x%08x 0x%08x\n" + "\t\tdata04..07 0x%08x 0x%08x 0x%08x 0x%08x\n" + "\t\tdata08..11 0x%08x 0x%08x 0x%08x 0x%08x\n" + "\t\tdata12..15 0x%08x 0x%08x 0x%08x 0x%08x\n", + _BBD(0), _BBD(1), _BBD(2), _BBD(3), + _BBD(4), _BBD(5), _BBD(6), _BBD(7), + _BBD(8), _BBD(9), _BBD(10), _BBD(11), + _BBD(12), _BBD(13), _BBD(14), _BBD(15)); + +#if defined(CC_BB_STATS) + BB_PRINT("\tStats: reserves %u releases %u copies %u\n" + "\tReads: %u polls %u %serrors %u%s\n" + "\tWrites: %u polls %u %serrors %u%s\n", + state_p->reserves, state_p->releases, state_p->copies, + state_p->RD.ops, state_p->RD.polls, + (state_p->RD.errors != 0) ? _BBr_ : _BBn_, + state_p->RD.errors, _BBn_, + state_p->WR.ops, state_p->WR.polls, + (state_p->WR.errors != 0) ? _BBr_ : _BBn_, + state_p->WR.errors, _BBn_); +#endif /* CC_BB_STATS */ + } +} + +/* + * ----------------------------------------------------------------------------- + * Show the entire BursBank system. + * ----------------------------------------------------------------------------- + */ +void +bb_show(int verbose) +{ + int channel_ix; + + for (channel_ix = 0U; channel_ix < BB_MAX_CHANNELS; channel_ix++) { + volatile bb_t * const bb_p = BB_POINTER(channel_ix); + bb_dump(bb_p, verbose); + } +} + +EXPORT_SYMBOL(bb_dump); +EXPORT_SYMBOL(bb_show); +#endif /* CC_BB_DEBUG */ + +#if defined(CC_BB_BENCH) && defined(CONFIG_PMON) + +/* + * ============================================================================= + * Section: Benchmarking Burst bank using pmontool + * ============================================================================= + */ +#include +#include + +uint32_t memory[16]; + +typedef struct bb_dataA +{ + uint32_t u32[BB_SIZE_WORDS]; +} bb_dataA_t ____cacheline_aligned; + +bb_dataA_t data1; /* Burst bank access */ +bb_dataA_t data2; /* Uncached access */ +bb_dataA_t data3; /* Cached access */ +bb_dataA_t data4; + +#define BB_XFER_SZ (256 * 1024) +#define BB_XFER_U32 (BB_XFER_SZ / sizeof(uint32_t)) +uint32_t src_mem[BB_XFER_U32]; +uint32_t dst_mem[BB_XFER_U32]; + +#define DTCM_ADDR 0xb1080000 +void +bb_bench(void) +{ + uint32_t ix, loops; + bb_t * bb_p; + uint32_t ret; + + uint32_t * const from_phy_p = BB_LOG2PHY(memory); + uint32_t * const to_phy_p = BB_LOG2PHY(&data1.u32[0]); + uint32_t * const local_p = BB_LOG2PHY(&data4.u32[0]); + uint32_t * const kseg0_p = (uint32_t*)CKSEG0ADDR(memory); + uint32_t * const kseg1_p = (uint32_t*)CKSEG1ADDR(memory); + + pmon_reg(1, "Burst Bank ASYNC READ 16 words"); + pmon_reg(2, "Burst Bank SYNC READ 16 words"); + + pmon_reg(3, "Burst Bank ASYNC WRITE 16 words"); + pmon_reg(4, "Burst Bank SYNC WRITE 16 words"); + + pmon_reg(5, "Burst Bank SYNC COPY 16 words"); + pmon_reg(6, "Burst Bank SYNC XFER 32K words"); + pmon_reg(7, "Burst Bank SYNC XFER 256K bytes"); + pmon_reg(8, "Burst Bank WRITE TCM 256K bytes"); + pmon_reg(9, "Burst Bank READ TCM 256K bytes"); + pmon_reg(10, "Libc memcpy WRITE TCM 256K bytes"); + pmon_reg(11, "Libc memcpy READ TCM 256K bytes"); + + pmon_reg(12, "UNCACHED SYNC READ 16 words"); + pmon_reg(13, "UNCACHED SYNC WRITE 16 words"); + + pmon_reg(14, "INVALIDATE CACHE Mem 16 words"); + + pmon_reg(15, " CACHED SYNC READ 16 words"); + pmon_reg(16, " CACHED SYNC WRITE 16 words"); + + pmon_reg(17, "FLUSH/INV CACHE Mem 16 words"); + + for (ix = 0; ix < 16; ix++) { + memory[ix] = ix; + data1.u32[ix] = data2.u32[ix] = data3.u32[ix] = data4.u32[ix] = 0; + } + + for (ix = 0; ix < BB_XFER_U32; ix++) { + src_mem[ix] = 0x12345678; + dst_mem[ix] = 0x00000000; + } + + /* Flush memory */ + cache_flush_len(memory, sizeof(uint32_t)*16); + cache_flush_len(&data1, sizeof(uint32_t)*16); + cache_flush_len(&data2, sizeof(uint32_t)*16); + cache_flush_len(&data3, sizeof(uint32_t)*16); + cache_flush_len(&data4, sizeof(uint32_t)*16); + + cache_flush_len(&src_mem, BB_XFER_SZ); + cache_flush_len(&dst_mem, BB_XFER_SZ); + + cache_flush_len(&data4, sizeof(uint32_t)*16); + + bb_p = bb_reserve(bb_channel_copy, bb_alloc_ignore_status); + + ret = 0; + + for (loops = 0; likely(loops < 100); loops++) { + if (unlikely(loops >= 10)) + pmon_bgn(); + + /* Setup and Issue READ: MEMORY ---> BB */ + bb_issue(bb_p, from_phy_p, BB_SIZE_WORDS - 1, bb_read_pending); + pmon_log(1); /* ISSUE READ */ + + /* Sync until READ completes: MEMORY ---> BB */ + ret |= bb_wait(bb_p, bb_read_pending); + pmon_log(2); /* WAIT FOR READ TO COMPLETE */ + + /* Setup and Issue WRITE: BB ---> MEMORY */ + bb_issue(bb_p, to_phy_p, BB_SIZE_WORDS - 1, bb_write_pending); + pmon_log(3); /* ISSUE WRITE */ + + /* Sync until WRITE completes: BB ---> MEMORY */ + ret |= bb_wait(bb_p, bb_write_pending); + pmon_log(4); /* WAIT FOR WRITE TO COMPLETE */ + + /* Copy Memory ---> BB ---> Memory */ + ret |= bb_copy(from_phy_p, local_p, BB_SIZE_WORDS-1, bb_channel_copy); + pmon_log(5); /* Copy 64B */ + + /* Copy Memory ---> BB ---> Memory */ + ret |= bb_xfer(BB_LOG2PHY(src_mem), BB_LOG2PHY(dst_mem), 32*1024); + pmon_log(6); /* XFER 128K Bytes */ + + /* Copy Memory ---> BB ---> Memory */ + ret |= bb_xfer(BB_LOG2PHY(src_mem), BB_LOG2PHY(dst_mem), BB_XFER_U32); + pmon_log(7); /* XFER 256 KBytes */ + + + /* Copy DDR to TCM 256KBytes */ + ret |= bb_xfer(BB_LOG2PHY(src_mem), BB_LOG2PHY(DTCM_ADDR), BB_XFER_U32); + pmon_log(8); + + /* Copy TCM to DDR 256KBytes */ + ret |= bb_xfer(BB_LOG2PHY(DTCM_ADDR), BB_LOG2PHY(dst_mem), BB_XFER_U32); + pmon_log(9); + + memcpy((void*)DTCM_ADDR, (void*)src_mem, BB_XFER_SZ); + pmon_log(10); + + memcpy((void*)src_mem, (void*)DTCM_ADDR, BB_XFER_SZ); + pmon_log(11); + + /* READ from Uncached memory */ + data2.u32[0] = *(kseg1_p + 0); data2.u32[1] = *(kseg1_p + 1); + data2.u32[2] = *(kseg1_p + 2); data2.u32[3] = *(kseg1_p + 3); + data2.u32[4] = *(kseg1_p + 4); data2.u32[5] = *(kseg1_p + 5); + data2.u32[6] = *(kseg1_p + 6); data2.u32[7] = *(kseg1_p + 7); + data2.u32[8] = *(kseg1_p + 8); data2.u32[9] = *(kseg1_p + 9); + data2.u32[10] = *(kseg1_p + 10); data2.u32[11] = *(kseg1_p + 11); + data2.u32[12] = *(kseg1_p + 12); data2.u32[13] = *(kseg1_p + 13); + data2.u32[14] = *(kseg1_p + 14); data2.u32[15] = *(kseg1_p + 15); + pmon_log(12); /* UNCACHED READ ACCESS */ + + /* WRITE to Uncached memory */ + *(kseg1_p + 0) = data2.u32[0]; *(kseg1_p + 1) = data2.u32[1]; + *(kseg1_p + 2) = data2.u32[2]; *(kseg1_p + 3) = data2.u32[3]; + *(kseg1_p + 4) = data2.u32[4]; *(kseg1_p + 5) = data2.u32[5]; + *(kseg1_p + 6) = data2.u32[6]; *(kseg1_p + 7) = data2.u32[7]; + *(kseg1_p + 8) = data2.u32[8]; *(kseg1_p + 9) = data2.u32[9]; + *(kseg1_p + 10) = data2.u32[10]; *(kseg1_p + 11) = data2.u32[11]; + *(kseg1_p + 12) = data2.u32[12]; *(kseg1_p + 13) = data2.u32[13]; + *(kseg1_p + 14) = data2.u32[14]; *(kseg1_p + 15) = data2.u32[15]; + pmon_log(13); /* UNCACHED WRITE ACCESS */ + + /* D-Cache invalidate (not dirty in cache, hence flush ignored */ + cache_flush_len(memory, sizeof(uint32_t) * 16); + pmon_log(14); /* cache invalidate */ + + /* READ from Cached memory, 16B Cacheline */ + data3.u32[0] = *(kseg0_p + 0); data3.u32[1] = *(kseg0_p + 1); + data3.u32[2] = *(kseg0_p + 2); data3.u32[3] = *(kseg0_p + 3); + data3.u32[4] = *(kseg0_p + 4); data3.u32[5] = *(kseg0_p + 5); + data3.u32[6] = *(kseg0_p + 6); data3.u32[7] = *(kseg0_p + 7); + data3.u32[8] = *(kseg0_p + 8); data3.u32[9] = *(kseg0_p + 9); + data3.u32[10] = *(kseg0_p + 10); data3.u32[11] = *(kseg0_p + 11); + data3.u32[12] = *(kseg0_p + 12); data3.u32[13] = *(kseg0_p + 13); + data3.u32[14] = *(kseg0_p + 14); data3.u32[15] = *(kseg0_p + 15); + pmon_log(15); /* CACHED READ ACCESS */ + + /* WRITE from Cached memory, 16B Cacheline, Buffered write */ + *(kseg0_p + 0) = data3.u32[0]; *(kseg0_p + 1) = data3.u32[1]; + *(kseg0_p + 2) = data3.u32[2]; *(kseg0_p + 3) = data3.u32[3]; + *(kseg0_p + 4) = data3.u32[4]; *(kseg0_p + 5) = data3.u32[5]; + *(kseg0_p + 6) = data3.u32[6]; *(kseg0_p + 7) = data3.u32[7]; + *(kseg0_p + 8) = data3.u32[8]; *(kseg0_p + 9) = data3.u32[9]; + *(kseg0_p + 10) = data3.u32[10]; *(kseg0_p + 11) = data3.u32[11]; + *(kseg0_p + 12) = data3.u32[12]; *(kseg0_p + 13) = data3.u32[13]; + *(kseg0_p + 14) = data3.u32[14]; *(kseg0_p + 15) = data3.u32[15]; + pmon_log(16); /* CACHED WRITE ACCESS */ + + /* D-Cache fluash (dirty) and invalidate */ + cache_flush_len(memory, sizeof(uint32_t) * 16); + pmon_log(17); /* cache flush */ + + if (ret == 0) { + pmon_end(17); + } else { + BB_PRINT(_BBr_ "ret error %u" _BBnl_, ret); + pmon_clr(); + ret = 0; + } + } + + BB_DBG( + for (ix = 0; ix < 16; ix++) + BB_PRINT(_BBg_ " %u %u %u" _BBnl_, memory[ix], + data1.u32[ix], data2.u32[ix]); + bb_show(1)); + + bb_release(bb_channel_copy); +} +EXPORT_SYMBOL(bb_bench); + +/* + Evt: Cycles-Count Nano-secs : EventName + 1: 20 50 : Burst Bank ASYNC READ 16 words + 2: 137 342 : Burst Bank SYNC READ 16 words + + 3: 68 170 : Burst Bank ASYNC WRITE 16 words + 4: 74 185 : Burst Bank SYNC WRITE 16 words + + 5: 668 1670 : Burst Bank SYNC COPY 16 words + 6: 1326676 3316690 : Burst Bank SYNC XFER 256K words + + 7: 1678 4195 : UNCACHED SYNC READ 16 words + 8: 561 1402 : UNCACHED SYNC WRITE 16 words + + 9: 84 210 : INVALIDATE CACHE Mem 16 words + + 10: 190 475 : CACHED SYNC READ 16 words + 11: 92 230 : CACHED SYNC WRITE 16 words + + 12: 237 592 : FLUSH/INV CACHE Mem 16 words + */ + +#endif /* CC_BB_BENCH && CONFIG_PMON */ + +#if defined(CC_BB_UNITT) +void bb_unittest(void) +{ + /* 1. Verify burst bank properly enabled */ + /* 2. Verify burst bank reservation scheme */ + /* 3. Verify status clear, by causing an error condition first */ + /* 4. Verify READ transaction with sync */ + /* 5. Verify WRITE transaction with sync */ + /* 6. Verify COPY transaction */ + /* 7. Verify XFER transaction */ + + /* 8. Regress SHOW and DUMP */ + /* 9. Regress STATS, DEBUG, AUDIT builds */ +} +#endif /* CC_BB_UNITT */ + +/* + * ----------------------------------------------------------------------------- + * Enable the BMIPS 4355 Burst Bank System. + * ----------------------------------------------------------------------------- + */ +void +bb_enable(void) +{ + uint32_t channel; + volatile uint32_t * const dma_control_en + = (uint32_t *)BMIPS_DMA_CONTROL_EN_REG; + + memset(&bb_g, 0, sizeof(bb_global_t)); + + BB_DBG(bb_g.debug = (uint32_t)CC_BB_DEBUG); + + /* Enable BMIPS 4350 DMA-Engine */ + *dma_control_en |= 1U; /* Enable */ + + __asm__ volatile ("sync"); /* sync first */ + + /* ( *(0xFF400034) == ( 0xFF500000 | 0x1) ) */ + BB_ASSERTV((*dma_control_en == (BMIPS_DMA_BB_BASE | 0x1))); + + for (channel = 0; channel < BB_MAX_CHANNELS; channel++) { + bb_t * bb_p; + uint32_t word_ix; + + bb_reset(channel); /* clear error condition if any */ + bb_p = BB_POINTER(channel); + for (word_ix = 0U; word_ix < BB_SIZE_WORDS; word_ix++) + BB_DATA(bb_p, word_ix) = 0U; /* clear burst bank memories */ + } + + /* BB_DBG(bb_show(1)); *//* dump all burst banks */ +} + +static int +__init __init_burstbank(void) +{ +#if defined(CONFIG_BCM96838) || defined(CONFIG_BCM96848) + /* Although BCM96368's CPU is BMIPS4350, it does not have burst banks */ + struct cpuinfo_mips *c = & current_cpu_data; /* cpu-info.h */ + + if (likely(c->cputype == CPU_BMIPS4350)) + bb_enable(); + + BB_PRINT(_BBh_ "BMIPS 4350 BurstBank %s" _BBn_ + ": bb_g.debug %p = %d\n\n", + BB_VER_STR, &bb_g.debug, bb_g.debug); + +#if defined(CC_BB_BENCH) && defined(CONFIG_PMON) + pmon_reg(0, (void *)bb_bench); +#endif /* CC_BB_BENCH && CONFIG_PMON */ + +#if defined(CC_BB_UNITT) + bb_unittest(); +#endif /* CC_BB_UNITT */ + +#endif /* CONFIG_BCM96838 || CONFIG_BCM96848 */ + + return 0; +} + +subsys_initcall(__init_burstbank); + +EXPORT_SYMBOL(bb_g); + +#endif /* BMIPS4355_DMAE */ diff -urN linux-4.1.52-orig/arch/mips/bcm963xx/ikos_setup.c linux-4.1.52/arch/mips/bcm963xx/ikos_setup.c --- linux-4.1.52-orig/arch/mips/bcm963xx/ikos_setup.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/mips/bcm963xx/ikos_setup.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,221 @@ +#if defined(CONFIG_BCM_KF_MIPS_BCM963XX) && defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_IKOS) && defined(CONFIG_BRCM_IKOS) + +/* +<:copyright-BRCM:2013:GPL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +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 (the "GPL"). + +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. + + +A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by +writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + +:> +*/ +/* + * Generic setup routines for Broadcom 963xx MIPS IKOS emulation environment + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +//#include +#include +#include + +#include +#include +#include +#include + +#include "shared_utils.h" + +extern unsigned long memsize; +extern unsigned long getMemorySize(void); + +static void __init setMemorySize(void) +{ + memsize = 16 * 1024 * 1024; +} + +void __init plat_mem_setup(void) +{ + /* set up the ddr memory size first */ + setMemorySize(); + + add_memory_region(0, (getMemorySize()), BOOT_MEM_RAM); + { + volatile unsigned long *cr; + uint32 mipsBaseAddr = MIPS_BASE; + + cr = (void *)(mipsBaseAddr + MIPS_RAC_CR0); + *cr = *cr | RAC_D | RAC_PF_D; + +#if defined(MIPS_RAC_CR1) + cr = (void *)(mipsBaseAddr + MIPS_RAC_CR1); + *cr = *cr | RAC_D | RAC_PF_D; +#endif + } +} + +void __init plat_time_init(void) +{ + /* hard code to 320MHz */ + mips_hpt_frequency = 160000000; + // Enable cp0 counter/compare interrupt when + // not using workaround for clock divide + write_c0_status(IE_IRQ5 | read_c0_status()); +} + +extern void stop_other_cpu(void); // in arch/mips/kernel/smp.c + +/* IKOS does not need real restart. Same as halt */ +static void brcm_machine_restart(char *command) +{ +#if defined(CONFIG_SMP) + stop_other_cpu(); +#endif + printk("IKOS restart --> system halted\n"); + local_irq_disable(); + while (1); +} + +static void brcm_machine_halt(void) +{ + /* + * we don't support power off yet. This halt will cause both CPU's to + * spin in a while(1) loop with interrupts disabled. (Used for gathering + * wlan debug dump via JTAG) + */ +#if defined(CONFIG_SMP) + stop_other_cpu(); +#endif + printk("System halted\n"); + local_irq_disable(); + while (1); +} + +/* this funciton implement any necessary hardware related initialization for ikos */ +static int __init ikos_hw_init(void) +{ + return 0; +} +#define bcm63xx_specific_hw_init() ikos_hw_init() + +static int __init bcm63xx_hw_init(void) +{ + return bcm63xx_specific_hw_init(); +} +arch_initcall(bcm63xx_hw_init); + + +static int __init brcm63xx_setup(void) +{ + extern int panic_timeout; + + _machine_restart = brcm_machine_restart; + _machine_halt = brcm_machine_halt; + pm_power_off = brcm_machine_halt; + + panic_timeout = 1; + + return 0; +} + +arch_initcall(brcm63xx_setup); + +int kerSysGetSdramSize( void ) +{ + return getMemorySize(); +} /* kerSysGetSdramSize */ + + +/* return the cmdline for ramdisk boot */ +static void __init create_cmdline(char *cmdline) +{ + +} + +extern struct plat_smp_ops brcm_smp_ops; + +void __init prom_init(void) +{ + int argc = fw_arg0; + u32 *argv = (u32 *)CKSEG0ADDR(fw_arg1); + int i; + + PERF->IrqControl[0].IrqMask=0; + + arcs_cmdline[0] = '\0'; + + create_cmdline(arcs_cmdline); + + strcat(arcs_cmdline, " "); + + for (i = 1; i < argc; i++) { + strcat(arcs_cmdline, (char *)CKSEG0ADDR(argv[i])); + if (i < (argc - 1)) + strcat(arcs_cmdline, " "); + } + +#if defined (CONFIG_SMP) + register_smp_ops(&brcm_smp_ops); +#endif + +} + + +void __init allocDspModBuffers(void); +/* +***************************************************************************** +* stub functions for ikos build. +***************************************************************************** +*/ +void __init allocDspModBuffers(void) +{ +} + +void __init prom_free_prom_memory(void) +{ + +} + +/* ikos does not use external interrupt */ +unsigned int kerSysGetExtIntInfo(unsigned int irq) +{ + return (unsigned int)(-1); +} + +const char *get_system_type(void) +{ + return "ikos emulation system"; +} + + + +#endif diff -urN linux-4.1.52-orig/arch/mips/bcm963xx/irq.c linux-4.1.52/arch/mips/bcm963xx/irq.c --- linux-4.1.52-orig/arch/mips/bcm963xx/irq.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/mips/bcm963xx/irq.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,876 @@ +#if defined(CONFIG_BCM_KF_MIPS_BCM963XX) && defined(CONFIG_MIPS_BCM963XX) +/* +<:copyright-BRCM:2012:DUAL/GPL:standard + + Copyright (c) 2012 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +/* + * Interrupt control functions for Broadcom 963xx MIPS boards + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_BCM_EXT_TIMER) +#include +#endif + +#if defined(CONFIG_SMP) + #define AFFINITY_OF(d) ((d)->affinity) + #define AFFINITY_OF_REF(d) (*(d)->affinity) +#else + #define AFFINITY_OF(d) ((void)(d), CPU_MASK_CPU0) + #define AFFINITY_OF_REF(d) ((void)(d), CPU_MASK_CPU0) +#endif + +#if IRQ_BITS == 64 + #define IRQ_TYPE uint64 +#else + #define IRQ_TYPE uint32 +#endif + + +volatile IrqControl_t * brcm_irq_ctrl[NR_CPUS]; +spinlock_t brcm_irqlock; + +#if defined(CONFIG_SMP) +extern DEFINE_PER_CPU(unsigned int, ipi_pending); +#endif + +static void irq_dispatch_int(void) +{ + int cpu = smp_processor_id(); + IRQ_TYPE pendingIrqs; + static IRQ_TYPE irqBit[NR_CPUS]; + + static uint32 isrNumber[NR_CPUS] = {[0 ... NR_CPUS-1] = (sizeof(IRQ_TYPE) * 8) - 1}; + +#if defined(CONFIG_BCM963268) || defined(CONFIG_BCM96838) || defined(CONFIG_BCM963381) || defined(CONFIG_BCM96848) + IRQ_TYPE pendingExtIrqs; + static IRQ_TYPE extIrqBit[NR_CPUS]; + static uint32 extIsrNumber[NR_CPUS] = {[0 ... NR_CPUS-1] = (sizeof(IRQ_TYPE) * 8) - 1}; +#endif + + spin_lock(&brcm_irqlock); +#if defined(CONFIG_BCM963381) || defined(CONFIG_BCM96848) + pendingIrqs = PERF->IrqStatus & brcm_irq_ctrl[cpu]->IrqMask; + pendingExtIrqs = PERF->ExtIrqStatus & brcm_irq_ctrl[cpu]->ExtIrqMask; +#else + pendingIrqs = brcm_irq_ctrl[cpu]->IrqStatus & brcm_irq_ctrl[cpu]->IrqMask; +#if defined(CONFIG_BCM963268) + pendingExtIrqs = brcm_irq_ctrl[cpu]->ExtIrqStatus & brcm_irq_ctrl[cpu]->ExtIrqMask; +#endif +#if defined(CONFIG_BCM96838) + pendingExtIrqs = PERFEXT->IrqControl[cpu].IrqStatus & PERFEXT->IrqControl[cpu].IrqMask; +#endif +#endif + spin_unlock(&brcm_irqlock); + + if (pendingIrqs) + { + while (1) { + irqBit[cpu] <<= 1; + isrNumber[cpu]++; + if (isrNumber[cpu] == (sizeof(IRQ_TYPE) * 8)) { + isrNumber[cpu] = 0; + irqBit[cpu] = 0x1; + } + if (pendingIrqs & irqBit[cpu]) { + unsigned int irq = isrNumber[cpu] + INTERNAL_ISR_TABLE_OFFSET; +#if defined(CONFIG_BCM96838) + if (irq == INTERRUPT_ID_EXTERNAL) + { + int i; + unsigned int reg = PERF->ExtIrqCfg; + unsigned int status = (reg & EI_STATUS_MASK) >> EI_STATUS_SHFT; + unsigned int mask = (reg & EI_MASK_MASK) >> EI_MASK_SHFT; + status &=mask; + + for(i = 0; i < 6; i++) + { + if (status & (1 << i)) + { + irq = INTERRUPT_ID_EXTERNAL_0 + i; + break; + } + } + spin_lock(&brcm_irqlock); + PERF->ExtIrqCfg |= (1 << (i + EI_CLEAR_SHFT)); // Clear + spin_unlock(&brcm_irqlock); + } +#elif !defined(CONFIG_BCM963381) +#if defined(CONFIG_BCM96848) + if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_7) +#else + if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) +#endif + { spin_lock(&brcm_irqlock); + PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT)); // Clear + spin_unlock(&brcm_irqlock); + } +#endif + do_IRQ(irq); + break; + } + } + } + +#if defined(CONFIG_BCM963268) || defined(CONFIG_BCM96838) || defined(CONFIG_BCM963381) || defined(CONFIG_BCM96848) + if (pendingExtIrqs) + { + while (1) { + extIrqBit[cpu] <<= 1; + extIsrNumber[cpu]++; + if (extIsrNumber[cpu] == (sizeof(IRQ_TYPE) * 8)) { + extIsrNumber[cpu] = 0; + extIrqBit[cpu] = 0x1; + } + if (pendingExtIrqs & extIrqBit[cpu]) { + unsigned int extIrq = extIsrNumber[cpu] + INTERNAL_EXT_ISR_TABLE_OFFSET; +#if defined(CONFIG_BCM963381) + if (extIrq >= INTERRUPT_ID_EXTERNAL_0 && extIrq <= INTERRUPT_ID_EXTERNAL_7) { + spin_lock(&brcm_irqlock); + PERF->ExtIrqCfg |= (1 << (extIrq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT)); // Clear + spin_unlock(&brcm_irqlock); + } +#endif + do_IRQ(extIrq); + break; + } + } + } +#endif +} + +#ifdef CONFIG_BCM_HOSTMIPS_PWRSAVE +extern void BcmPwrMngtResumeFullSpeed (void); +#endif + + +asmlinkage void plat_irq_dispatch(void) +{ + u32 cause; + +#ifdef CONFIG_BCM_HOSTMIPS_PWRSAVE + BcmPwrMngtResumeFullSpeed(); +#endif + + while((cause = (read_c0_cause() & read_c0_status() & CAUSEF_IP))) { + if (cause & CAUSEF_IP7) + do_IRQ(MIPS_TIMER_INT); + else if (cause & CAUSEF_IP0) + do_IRQ(INTERRUPT_ID_SOFTWARE_0); + else if (cause & CAUSEF_IP1) + do_IRQ(INTERRUPT_ID_SOFTWARE_1); +#if defined (CONFIG_SMP) +#if defined(CONFIG_BCM96838) + else if (cause & (CAUSEF_IP3 | CAUSEF_IP4)) +#else + else if (cause & (CAUSEF_IP2 | CAUSEF_IP3)) +#endif +#else +#if defined(CONFIG_BCM96838) + else if (cause & CAUSEF_IP3) +#else + else if (cause & CAUSEF_IP2) +#endif +#endif + irq_dispatch_int(); + } +} + +#if !defined(CONFIG_BCM96838) +// bill +void disable_brcm_irqsave(struct irq_data *data, unsigned long stateSaveArray[]) +{ + int cpu; + unsigned long flags; + unsigned int irq = data->irq; + + // Test for valid interrupt. + if ((irq >= INTERNAL_ISR_TABLE_OFFSET ) && (irq <= INTERRUPT_ID_LAST)) + { + // Disable this processor's interrupts and acquire spinlock. + spin_lock_irqsave(&brcm_irqlock, flags); + + // Loop thru each processor. + for_each_cpu(cpu, AFFINITY_OF(data)) + { + // Save original interrupt's enable state. + stateSaveArray[cpu] = brcm_irq_ctrl[cpu]->IrqMask & (((IRQ_TYPE)1) << (irq - INTERNAL_ISR_TABLE_OFFSET)); + + // Clear each cpu's selected interrupt enable. + brcm_irq_ctrl[cpu]->IrqMask &= ~(((IRQ_TYPE)1) << (irq - INTERNAL_ISR_TABLE_OFFSET)); + +#if defined(CONFIG_BCM963268) || defined (CONFIG_BCM963381) || defined(CONFIG_BCM96848) + // Save original interrupt's enable state. + stateSaveArray[cpu] = brcm_irq_ctrl[cpu]->ExtIrqMask & (((IRQ_TYPE)1) << (irq - INTERNAL_EXT_ISR_TABLE_OFFSET)); + + // Clear each cpu's selected interrupt enable. + brcm_irq_ctrl[cpu]->ExtIrqMask &= ~(((IRQ_TYPE)1) << (irq - INTERNAL_EXT_ISR_TABLE_OFFSET)); +#endif + } + + // Release spinlock and enable this processor's interrupts. + spin_unlock_irqrestore(&brcm_irqlock, flags); + } +} + + +// bill +void restore_brcm_irqsave(struct irq_data *data, unsigned long stateSaveArray[]) +{ + int cpu; + unsigned long flags; + + // Disable this processor's interrupts and acquire spinlock. + spin_lock_irqsave(&brcm_irqlock, flags); + + // Loop thru each processor. + for_each_cpu(cpu, AFFINITY_OF(data)) + { + // Restore cpu's original interrupt enable (off or on). + brcm_irq_ctrl[cpu]->IrqMask |= stateSaveArray[cpu]; +#if defined(CONFIG_BCM963268) || defined (CONFIG_BCM963381) || defined(CONFIG_BCM96848) + brcm_irq_ctrl[cpu]->ExtIrqMask |= stateSaveArray[cpu]; +#endif + } + + // Release spinlock and enable this processor's interrupts. + spin_unlock_irqrestore(&brcm_irqlock, flags); +} +#endif //#if !defined(CONFIG_BCM96838) + + +static __always_inline void enable_brcm_irq_data_locked(unsigned long irq, cpumask_t affinity) +{ + int cpu; + unsigned long flags; + int levelOrEdge = 1; + int detectSense = 0; + int bothEdge = 0; + + spin_lock_irqsave(&brcm_irqlock, flags); + + if(( irq >= INTERNAL_ISR_TABLE_OFFSET ) +#if defined(CONFIG_BCM963268) || defined(CONFIG_BCM96838) || defined (CONFIG_BCM963381) || defined(CONFIG_BCM96848) + && ( irq < (INTERNAL_ISR_TABLE_OFFSET+64) ) +#endif + ) + { + + for_each_cpu(cpu, &affinity) { + brcm_irq_ctrl[cpu]->IrqMask |= (((IRQ_TYPE)1) << (irq - INTERNAL_ISR_TABLE_OFFSET)); + } + } +#if defined(CONFIG_BCM96838) + else if((irq >= INTERRUPT_ID_EXTERNAL_0) && (irq <= INTERRUPT_ID_EXTERNAL_5)) + { + for_each_cpu(cpu, &affinity) { + brcm_irq_ctrl[cpu]->IrqMask |= (((IRQ_TYPE)1) << (INTERRUPT_ID_EXTERNAL - INTERNAL_ISR_TABLE_OFFSET)); + } + } +#endif +#if defined(CONFIG_BCM963268) || defined(CONFIG_BCM96838) || defined (CONFIG_BCM963381) || defined(CONFIG_BCM96848) + else if(( irq >= INTERNAL_EXT_ISR_TABLE_OFFSET ) && + ( irq < (INTERNAL_EXT_ISR_TABLE_OFFSET+64) ) ) + { + for_each_cpu(cpu, &affinity) { +#if defined(CONFIG_BCM963268) || defined (CONFIG_BCM963381) || defined(CONFIG_BCM96848) + brcm_irq_ctrl[cpu]->ExtIrqMask |= (((IRQ_TYPE)1) << (irq - INTERNAL_EXT_ISR_TABLE_OFFSET)); +#else + PERFEXT->IrqControl[cpu].IrqMask |= (((IRQ_TYPE)1) << (irq - INTERNAL_EXT_ISR_TABLE_OFFSET)); +#endif + } + } +#endif + else if ((irq == INTERRUPT_ID_SOFTWARE_0) || (irq == INTERRUPT_ID_SOFTWARE_1)) { + set_c0_status(0x1 << (STATUSB_IP0 + irq - INTERRUPT_ID_SOFTWARE_0)); + } + + /* Determine the type of IRQ trigger required */ + if ( (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) +#if defined(CONFIG_BCM96838) + || (irq >= INTERRUPT_ID_EXTERNAL_4 && irq <= INTERRUPT_ID_EXTERNAL_5) +#elif defined (CONFIG_BCM963381) || defined(CONFIG_BCM96848) + || (irq >= INTERRUPT_ID_EXTERNAL_4 && irq <= INTERRUPT_ID_EXTERNAL_7) +#endif + ) + { + if( IsExtIntrTypeActHigh(kerSysGetExtIntInfo(irq)) ) + detectSense = 1; + else + detectSense = 0; + + if( IsExtIntrTypeSenseLevel(kerSysGetExtIntInfo(irq)) ) + levelOrEdge = 1; + else + levelOrEdge = 0; + + if( IsExtIntrTypeBothEdge(kerSysGetExtIntInfo(irq)) ) + bothEdge = 1; + else + bothEdge = 0; + } +#if defined(CONFIG_BCM96838) + if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_5) { +#elif defined(CONFIG_BCM963381) || defined(CONFIG_BCM96848) + if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_7) { +#else + if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) { +#endif + PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_INSENS_SHFT)); // Edge insesnsitive + if ( levelOrEdge ) { + PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_LEVEL_SHFT)); // Level triggered + } else { + PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_LEVEL_SHFT)); // Edge triggered + } + if ( detectSense ) { + PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_SENSE_SHFT)); // High / Rising triggered + } else { + PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_SENSE_SHFT)); // Low / Falling triggered + } + if ( bothEdge ) { + PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_INSENS_SHFT)); // Both edge triggered + } else { + PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_INSENS_SHFT)); // One edge triggered + } + PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT)); // Clear +#if defined (CONFIG_BCM963381) || defined(CONFIG_BCM96848) + PERF->ExtIrqSts |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT)); // Unmask +#else + PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT)); // Unmask +#endif + } + + spin_unlock_irqrestore(&brcm_irqlock, flags); +} + +__always_inline void enable_brcm_irq_data(struct irq_data *data) +{ + enable_brcm_irq_data_locked(data->irq, AFFINITY_OF_REF(data)); +} + +void enable_brcm_irq_irq(unsigned int irq) +{ +#if defined(CONFIG_SMP) + // Note: for performance, no bounds checks are done on the below two lines + struct irq_desc *desc = irq_desc + irq; + cpumask_var_t affinity; + + cpumask_copy(affinity, desc->irq_data.affinity); + + // sanity check + if (affinity->bits[0] == 0) + { + //WARN_ONCE(1, "irq %d has no affinity!!!\n", irq); + cpumask_copy(affinity, &CPU_MASK_CPU0); + } + + enable_brcm_irq_data_locked(irq, *affinity); +#else + enable_brcm_irq_data_locked(irq, CPU_MASK_CPU0); +#endif +} + +static __always_inline void __disable_ack_brcm_irq(unsigned int irq, cpumask_t affinity) +{ + int cpu; + +#if defined(CONFIG_BCM96838) + if((irq >= INTERRUPT_ID_EXTERNAL_0) && (irq <= INTERRUPT_ID_EXTERNAL_5)) + irq = INTERRUPT_ID_EXTERNAL; +#endif + + if(( irq >= INTERNAL_ISR_TABLE_OFFSET ) + && ( irq < (INTERNAL_ISR_TABLE_OFFSET+64) )) + { + for_each_cpu(cpu, &affinity) { + brcm_irq_ctrl[cpu]->IrqMask &= ~(((IRQ_TYPE)1) << (irq - INTERNAL_ISR_TABLE_OFFSET)); + } + } +#if defined(CONFIG_BCM963268) || defined(CONFIG_BCM96838) || defined(CONFIG_BCM963381) || defined(CONFIG_BCM96848) + else if(( irq >= INTERNAL_EXT_ISR_TABLE_OFFSET ) && + ( irq < (INTERNAL_EXT_ISR_TABLE_OFFSET+64) )) + { + for_each_cpu(cpu, &affinity) { +#if defined(CONFIG_BCM963268) || defined(CONFIG_BCM963381) || defined(CONFIG_BCM96848) + brcm_irq_ctrl[cpu]->ExtIrqMask &= ~(((IRQ_TYPE)1) << (irq - INTERNAL_EXT_ISR_TABLE_OFFSET)); +#else + PERFEXT->IrqControl[cpu].IrqMask &= ~(((IRQ_TYPE)1) << (irq - INTERNAL_EXT_ISR_TABLE_OFFSET)); +#endif + } + } +#endif +} + +static __always_inline void disable_brcm_irq_data_locked(unsigned long irq, cpumask_t affinity) +{ + unsigned long flags; + + spin_lock_irqsave(&brcm_irqlock, flags); + __disable_ack_brcm_irq(irq, affinity); + if ((irq == INTERRUPT_ID_SOFTWARE_0) || (irq == INTERRUPT_ID_SOFTWARE_1)) { + clear_c0_status(0x1 << (STATUSB_IP0 + irq - INTERRUPT_ID_SOFTWARE_0)); + } + spin_unlock_irqrestore(&brcm_irqlock, flags); +} + +void disable_brcm_irq_irq(unsigned int irq) +{ +#if defined(CONFIG_SMP) + struct irq_desc *desc = irq_desc + irq; + cpumask_var_t affinity; + + cpumask_copy(affinity, desc->irq_data.affinity); + + if (unlikely(affinity->bits[0] == 0)) + { + //WARN_ONCE(1, "irq %d has no affinity!!!\n", irq); + cpumask_copy(affinity, &CPU_MASK_CPU0); + } + + disable_brcm_irq_data_locked(irq, *affinity); +#else + disable_brcm_irq_data_locked(irq, CPU_MASK_CPU0); +#endif +} + +void disable_brcm_irq_data(struct irq_data *data) +{ + disable_brcm_irq_data_locked(data->irq, AFFINITY_OF_REF(data)); +} + +void ack_brcm_irq(struct irq_data *data) +{ + unsigned long flags; + unsigned int irq = data->irq; + + spin_lock_irqsave(&brcm_irqlock, flags); + __disable_ack_brcm_irq(irq, AFFINITY_OF_REF(data)); + +#if defined(CONFIG_SMP) + if (irq == INTERRUPT_ID_SOFTWARE_0) { + int this_cpu = smp_processor_id(); + int other_cpu = !this_cpu; + per_cpu(ipi_pending, this_cpu) = 0; + mb(); + clear_c0_cause(1<irq; + + spin_lock_irqsave(&brcm_irqlock, flags); + __disable_ack_brcm_irq(irq, AFFINITY_OF_REF(data)); + +#if defined(CONFIG_SMP) + if (irq == INTERRUPT_ID_SOFTWARE_0) { + int this_cpu = smp_processor_id(); + int other_cpu = !this_cpu; + per_cpu(ipi_pending, this_cpu) = 0; + mb(); + clear_c0_cause(1<irq; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&brcm_irqlock, flags); + +#if defined(CONFIG_BCM96838) + if((irq >= INTERRUPT_ID_EXTERNAL_0) && (irq <= INTERRUPT_ID_EXTERNAL_5)) + irq = INTERRUPT_ID_EXTERNAL; +#endif + + if(( irq >= INTERNAL_ISR_TABLE_OFFSET ) + && ( irq < (INTERNAL_ISR_TABLE_OFFSET+64) ) + ) + { + for_each_online_cpu(cpu) { + if (cpumask_test_cpu(cpu, dest) && !(irqd_irq_disabled(data))) { + brcm_irq_ctrl[cpu]->IrqMask |= (((IRQ_TYPE)1) << (irq - INTERNAL_ISR_TABLE_OFFSET)); + } + else { + brcm_irq_ctrl[cpu]->IrqMask &= ~(((IRQ_TYPE)1) << (irq - INTERNAL_ISR_TABLE_OFFSET)); + } + } + } + +#if defined(CONFIG_BCM963268) || defined(CONFIG_BCM96838) || defined(CONFIG_BCM963381) || defined(CONFIG_BCM96848) + if(( irq >= INTERNAL_EXT_ISR_TABLE_OFFSET ) + && ( irq < (INTERNAL_EXT_ISR_TABLE_OFFSET+64) ) + ) + { + for_each_online_cpu(cpu) { + if (cpumask_test_cpu(cpu, dest) && !(irqd_irq_disabled(data))) { + +#if defined(CONFIG_BCM963268) || defined(CONFIG_BCM963381) || defined(CONFIG_BCM96848) + brcm_irq_ctrl[cpu]->ExtIrqMask |= (((IRQ_TYPE)1) << (irq - INTERNAL_EXT_ISR_TABLE_OFFSET)); +#else + PERFEXT->IrqControl[cpu].IrqMask |= (((IRQ_TYPE)1) << (irq - INTERNAL_EXT_ISR_TABLE_OFFSET)); +#endif + } + else { +#if defined(CONFIG_BCM963268) || defined(CONFIG_BCM963381) || defined(CONFIG_BCM96848) + brcm_irq_ctrl[cpu]->ExtIrqMask &= ~(((IRQ_TYPE)1) << (irq - INTERNAL_EXT_ISR_TABLE_OFFSET)); +#else + PERFEXT->IrqControl[cpu].IrqMask &= ~(((IRQ_TYPE)1) << (irq - INTERNAL_EXT_ISR_TABLE_OFFSET)); +#endif + } + } + } +#endif + + spin_unlock_irqrestore(&brcm_irqlock, flags); + + return ret; +} + + +static struct irq_chip brcm_irq_chip = { + .name = "BCM63xx", + .irq_enable = enable_brcm_irq_data, + .irq_disable = disable_brcm_irq_data, + .irq_ack = ack_brcm_irq, + .irq_mask = disable_brcm_irq_data, + .irq_mask_ack = mask_ack_brcm_irq, + .irq_unmask = enable_brcm_irq_data, + .irq_set_affinity = set_brcm_affinity +}; + +static struct irq_chip brcm_irq_chip_no_unmask = { + .name = "BCM63xx_no_unmask", + .irq_enable = enable_brcm_irq_data, + .irq_disable = disable_brcm_irq_data, + .irq_ack = ack_brcm_irq, + .irq_mask = disable_brcm_irq_data, + .irq_mask_ack = mask_ack_brcm_irq, + .irq_unmask = unmask_brcm_irq_noop, + .irq_set_affinity = set_brcm_affinity +}; + + +void __init arch_init_irq(void) +{ + int i; + + spin_lock_init(&brcm_irqlock); + + for (i = 0; i < NR_CPUS; i++) { + brcm_irq_ctrl[i] = &PERF->IrqControl[i]; + } + + for (i = 0; i < NR_IRQS; i++) { + irq_set_chip_and_handler(i, &brcm_irq_chip, handle_level_irq); + } + +#if defined(CONFIG_BCM96838) + PERF->ExtIrqCfg |= EI_CLEAR_MASK; +#endif + + clear_c0_status(ST0_BEV); +#if defined(CONFIG_SMP) + // make interrupt mask same as TP1, miwang 6/14/10 +#if defined(CONFIG_BCM96838) + change_c0_status(ST0_IM, IE_IRQ1|IE_IRQ2); +#else + change_c0_status(ST0_IM, IE_IRQ0|IE_IRQ1); +#endif +#else +#if defined(CONFIG_BCM96838) + change_c0_status(ST0_IM, IE_IRQ1); +#else + change_c0_status(ST0_IM, IE_IRQ0); +#endif +#endif + + +#ifdef CONFIG_REMOTE_DEBUG + rs_kgdb_hook(0); +#endif +} + + +#define INTR_NAME_MAX_LENGTH 16 + +// This is a wrapper to standand Linux request_irq +// Differences are: +// - The irq won't be renabled after ISR is done and needs to be explicity re-enabled, which is good for NAPI drivers. +// The change is implemented by filling in an no-op unmask function in brcm_irq_chip_no_unmask and set it as the irq_chip +// - IRQ flags and interrupt names are automatically set +// Either request_irq or BcmHalMapInterrupt can be used. Just make sure re-enabling IRQ is handled correctly. + +unsigned int BcmHalMapInterrupt(FN_HANDLER pfunc, void* param, unsigned int irq) +{ + char devname[INTR_NAME_MAX_LENGTH]; + + sprintf(devname, "brcm_%d", irq); + return BcmHalMapInterruptEx(pfunc, param, irq, devname, + INTR_REARM_NO, INTR_AFFINITY_DEFAULT); +} + + +/** Broadcom wrapper to linux request_irq. This version does more stuff. + * + * @param pfunc (IN) interrupt handler function + * @param param (IN) context/cookie that is passed to interrupt handler + * @param irq (IN) interrupt number + * @param interruptName (IN) descriptive name for the interrupt. 15 chars + * or less. This function will make a copy of + * the name. + * @param INTR_REARM_MODE (IN) See bcm_intr.h + * @param INTR_AFFINITY_MODE (IN) See bcm_intr.h + * + * @return 0 on success. + */ +unsigned int BcmHalMapInterruptEx(FN_HANDLER pfunc, + void* param, + unsigned int irq, + const char *interruptName, + INTR_REARM_MODE_ENUM rearmMode, + INTR_AFFINITY_MODE_ENUM affinMode) +{ + char *devname; + unsigned long irqflags; + struct irq_chip *chip; + unsigned int retval; + + BUG_ON(interruptName == NULL); + BUG_ON(strlen(interruptName) >= INTR_NAME_MAX_LENGTH); + + if ((devname = kmalloc(INTR_NAME_MAX_LENGTH, GFP_ATOMIC)) == NULL) + { + printk(KERN_ERR "kmalloc(%d, GFP_ATOMIC) failed for intr name\n", + INTR_NAME_MAX_LENGTH); + return -1; + } + sprintf( devname, "%s", interruptName ); + + /* If this is for the timer interrupt, do not invoke the following code + because doing so kills the timer interrupts that may already be running */ + if (irq != INTERRUPT_ID_TIMER) { + chip = (rearmMode == INTR_REARM_NO) ? &brcm_irq_chip_no_unmask : + &brcm_irq_chip; + irq_set_chip_and_handler(irq, chip, handle_level_irq); + } + + if (rearmMode == INTR_REARM_YES) + { + irq_modify_status(irq, IRQ_NOAUTOEN, 0); + } + + + irqflags = 0; +#if defined(CONFIG_BCM_EXT_TIMER) + /* There are 3 timers with individual control, so the interrupt can be shared */ + if ( (irq >= INTERRUPT_ID_TIMER) && (irq < (INTERRUPT_ID_TIMER+EXT_TIMER_NUM)) ) + irqflags |= IRQF_SHARED; +#endif + /* For external interrupt, check if it is shared */ + if ( (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) +#if defined(CONFIG_BCM96838) + || (irq >= INTERRUPT_ID_EXTERNAL_4 && irq <= INTERRUPT_ID_EXTERNAL_5) +#elif defined(CONFIG_BCM963381) || defined(CONFIG_BCM96848) + || (irq >= INTERRUPT_ID_EXTERNAL_4 && irq <= INTERRUPT_ID_EXTERNAL_7) +#endif + ) + { + if( IsExtIntrShared(kerSysGetExtIntInfo(irq)) ) + irqflags |= IRQF_SHARED; + } + + retval = request_irq(irq, (void*)pfunc, irqflags, devname, (void *) param); + if (retval != 0) + { + printk(KERN_WARNING "request_irq failed for irq=%d (%s) retval=%d\n", + irq, devname, retval); + kfree(devname); + return retval; + } + + // now deal with interrupt affinity requests + if (affinMode != INTR_AFFINITY_DEFAULT) + { + struct cpumask mask; + + cpumask_clear(&mask); + + if (affinMode == INTR_AFFINITY_TP1_ONLY || + affinMode == INTR_AFFINITY_TP1_IF_POSSIBLE) + { + if (cpu_online(1)) + { + cpumask_set_cpu(1, &mask); + irq_set_affinity(irq, &mask); + } + else + { + // TP1 is not on-line but caller insisted on it + if (affinMode == INTR_AFFINITY_TP1_ONLY) + { + printk(KERN_WARNING + "cannot assign intr %d to TP1, not online\n", irq); + retval = request_irq(irq, NULL, 0, NULL, NULL); + kfree(devname); + retval = -1; + } + } + } + else + { + // INTR_AFFINITY_BOTH_IF_POSSIBLE + cpumask_set_cpu(0, &mask); + if (cpu_online(1)) + { + cpumask_set_cpu(1, &mask); + irq_set_affinity(irq, &mask); + } + } + } + + return retval; +} +EXPORT_SYMBOL(BcmHalMapInterruptEx); + + +//*************************************************************************** +// void BcmHalGenerateSoftInterrupt +// +// Triggers a software interrupt. +// +//*************************************************************************** +void BcmHalGenerateSoftInterrupt( unsigned int irq ) +{ + unsigned long flags; + + local_irq_save(flags); + + set_c0_cause(0x1 << (CAUSEB_IP0 + irq - INTERRUPT_ID_SOFTWARE_0)); + + local_irq_restore(flags); +} + +void BcmHalExternalIrqMask(unsigned int irq) +{ + unsigned long flags; + spin_lock_irqsave(&brcm_irqlock, flags); +#if defined(CONFIG_BCM963381) || defined(CONFIG_BCM96848) + PERF->ExtIrqSts &= ~(1 << (EI_MASK_SHFT + irq - INTERRUPT_ID_EXTERNAL_0)); +#else + PERF->ExtIrqCfg &= ~(1 << (EI_MASK_SHFT + irq - INTERRUPT_ID_EXTERNAL_0)); +#endif + spin_unlock_irqrestore(&brcm_irqlock, flags); +} + +void BcmHalExternalIrqUnmask(unsigned int irq) +{ + unsigned long flags; + spin_lock_irqsave(&brcm_irqlock, flags); +#if defined(CONFIG_BCM963381) || defined(CONFIG_BCM96848) + PERF->ExtIrqSts |= (1 << (EI_MASK_SHFT + irq - INTERRUPT_ID_EXTERNAL_0)); +#else + PERF->ExtIrqCfg |= (1 << (EI_MASK_SHFT + irq - INTERRUPT_ID_EXTERNAL_0)); +#endif + spin_unlock_irqrestore(&brcm_irqlock, flags); +} + +EXPORT_SYMBOL(enable_brcm_irq_irq); +EXPORT_SYMBOL(disable_brcm_irq_irq); +EXPORT_SYMBOL(BcmHalMapInterrupt); +EXPORT_SYMBOL(BcmHalGenerateSoftInterrupt); +EXPORT_SYMBOL(BcmHalExternalIrqMask); +EXPORT_SYMBOL(BcmHalExternalIrqUnmask); + +#if !defined(CONFIG_BCM96838) +// bill +EXPORT_SYMBOL(disable_brcm_irqsave); +EXPORT_SYMBOL(restore_brcm_irqsave); +#endif + +#endif //defined(CONFIG_BCM_KF_MIPS_BCM963XX) diff -urN linux-4.1.52-orig/arch/mips/bcm963xx/Makefile linux-4.1.52/arch/mips/bcm963xx/Makefile --- linux-4.1.52-orig/arch/mips/bcm963xx/Makefile 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/mips/bcm963xx/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,52 @@ +ifdef BCM_KF # defined(CONFIG_BCM_KF_MIPS_BCM963XX) +# +# Makefile for generic Broadcom MIPS boards +# +# Copyright (C) 2004 Broadcom Corporation +# +ifeq ($(strip $(CONFIG_BRCM_IKOS)),) +obj-y := irq.o prom.o setup.o +else +obj-y := irq.o ikos_setup.o +endif +obj-$(CONFIG_SMP) += smp-brcm.o + +ifneq ($(strip $(CONFIG_BCM_HOSTMIPS_PWRSAVE)),) +obj-y += pwrmngtclk.o +else +ifneq ($(strip $(CONFIG_BCM_DDR_SELF_REFRESH_PWRSAVE)),) +obj-y += pwrmngtclk.o +endif +endif + +obj-y += burstbank.o + +SRCBASE := $(TOPDIR) +EXTRA_CFLAGS += -I$(INC_BRCMBOARDPARMS_PATH)/$(BRCM_BOARD) -I$(SRCBASE)/include -I$(INC_BRCMDRIVER_PUB_PATH)/$(BRCM_BOARD) -I$(INC_BRCMSHARED_PUB_PATH)/$(BRCM_BOARD) -I$(INC_BRCMSHARED_PUB_PATH)/pmc +#EXTRA_CFLAGS += -I$(INC_ADSLDRV_PATH) -DDBG +EXTRA_CFLAGS += -I$(INC_ADSLDRV_PATH) +EXTRA_CFLAGS += -g +EXTRA_CFLAGS += $(BRCM_WERROR_CFLAGS) + +ifneq ($(strip $(BUILD_SWMDK)),) +EXTRA_CFLAGS += -DSUPPORT_SWMDK +endif + + +ifeq "$(ADSL)" "ANNEX_B" +EXTRA_CFLAGS += -DADSL_ANNEXB +endif +ifeq "$(ADSL)" "SADSL" +EXTRA_CFLAGS += -DADSL_SADSL +endif +ifeq "$(ADSL)" "ANNEX_C" +EXTRA_CFLAGS += -DADSL_ANNEXC +endif +ifeq "$(BRCM_PHY_BONDING)" "y" +EXTRA_CFLAGS += -DSUPPORT_DSL_BONDING +endif +ifeq "$(BRCM_PHY_BONDING5B)" "y" +EXTRA_CFLAGS += -DSUPPORT_DSL_BONDING5B +endif + +endif # BCM_KF # defined(CONFIG_BCM_KF_MIPS_BCM963XX) diff -urN linux-4.1.52-orig/arch/mips/bcm963xx/Platform linux-4.1.52/arch/mips/bcm963xx/Platform --- linux-4.1.52-orig/arch/mips/bcm963xx/Platform 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/mips/bcm963xx/Platform 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,8 @@ +# +# Broadcom BCM963XX boards +# +platform-$(CONFIG_MIPS_BCM963XX) += bcm963xx/ +cflags-$(CONFIG_MIPS_BCM963XX) += -I$(srctree)/arch/mips/include/asm/mach-bcm963xx +cflags-$(CONFIG_MIPS_BCM963XX) += -I$(srctree)/../../bcmdrivers/opensource/include/bcm963xx +cflags-$(CONFIG_MIPS_BCM963XX) += -I$(srctree)/../../shared/opensource/include/bcm963xx +load-$(CONFIG_MIPS_BCM963XX) := 0x80010000 diff -urN linux-4.1.52-orig/arch/mips/bcm963xx/prom.c linux-4.1.52/arch/mips/bcm963xx/prom.c --- linux-4.1.52-orig/arch/mips/bcm963xx/prom.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/mips/bcm963xx/prom.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,406 @@ +#if defined(CONFIG_BCM_KF_MIPS_BCM963XX) && defined(CONFIG_MIPS_BCM963XX) +/* +* <:copyright-BRCM:2004:DUAL/GPL:standard +* +* Copyright (c) 2004 Broadcom +* All Rights Reserved +* +* Unless you and Broadcom execute a separate written software license +* agreement governing use of this software, this software is licensed +* to you under the terms of the GNU General Public License version 2 +* (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +* with the following added to such license: +* +* As a special exception, the copyright holders of this software give +* you permission to link this software with independent modules, and +* to copy and distribute the resulting executable under terms of your +* choice, provided that you also meet, for each linked independent +* module, the terms and conditions of the license of that module. +* An independent module is a module which is not derived from this +* software. The special exception does not apply to any modifications +* of the software. +* +* Not withstanding the above, under no circumstances may you combine +* this software in any way with any other Broadcom software provided +* under a license other than the GPL, without Broadcom's express prior +* written consent. +* +* :> + +*/ +/* + * prom.c: PROM library initialization code. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#if defined(CONFIG_BCM96848) +#include +#endif +#include +#include + + +extern bool early_init_dt_verify(void *params); +extern void unflatten_and_copy_device_tree(void); +extern int __init bcm_scan_fdt(void); +extern int __init bcm_dt_postinit(void); + +void __init device_tree_init(void) +{ + unflatten_and_copy_device_tree(); +} + + +#ifdef CONFIG_BCM_CFE_XARGS_EARLY +extern void __init bl_xparms_setup(const unsigned char* blparms, unsigned int size); +#endif + +extern int do_syslog(int, char *, int); +#ifndef CONFIG_OF +static void __init create_cmdline(char *cmdline); +#endif +UINT32 __init calculateCpuSpeed(void); +void __init retrieve_boot_loader_parameters(void*); + + + +#if defined (CONFIG_BCM963268) +const uint32 cpu_speed_table[0x20] = { + 0, 0, 400, 320, 0, 0, 0, 0, 0, 0, 333, 400, 0, 0, 320, 400, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; +#endif + + +#if defined (CONFIG_BCM960333) +const uint32 cpu_speed_table[0x04] = { + 200, 400, 333, 0 +}; +#endif + + + + + + +#if defined (CONFIG_BCM96838) +const uint32 cpu_speed_table[0x3] = { + 600, 400, 240 +}; +#endif + +#if defined (CONFIG_BCM963381) +const uint32 cpu_speed_table[0x04] = { + 300, 800, 480, 600 +}; +#endif + +#if defined (CONFIG_BCM96848) +const uint32 cpu_speed_table[8] = { + 250, 250, 400, 400, 250, 250, 428, 600 +}; +#endif + +static char promBoardIdStr[NVRAM_BOARD_ID_STRING_LEN]; +const char *get_system_type(void) +{ + kerSysNvRamGetBoardId(promBoardIdStr); + return(promBoardIdStr); +} + + +/* -------------------------------------------------------------------------- + Name: prom_init + -------------------------------------------------------------------------- */ + +#define BOOT_FDT 0 +#define BOOT_LEGACY 1 + +int boot_param_status_legacy = BOOT_LEGACY; + +extern struct plat_smp_ops brcm_smp_ops; + +void __init prom_init(void) +{ + u32 *argv = (u32 *)CKSEG0ADDR(fw_arg1); + int argc = fw_arg0; +#ifdef CONFIG_OF + if (early_init_dt_verify((void*)argv)) { + boot_param_status_legacy = BOOT_FDT; + of_scan_flat_dt(early_init_dt_scan_root, NULL); + of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line); + bcm_scan_fdt(); + bcm_dt_postinit(); + } else +#endif + if (*argv == BLPARMS_MAGIC) { + retrieve_boot_loader_parameters(argv+1); + } else { + printk(KERN_CRIT "ERROR: bootloader params are missing\n"); + BUG(); + return; + } + + kerSysEarlyFlashInit(); + bcm_extirq_init(); + + // too early in bootup sequence to acquire spinlock, not needed anyways + // only the kernel is running at this point + kerSysNvRamGetBoardIdLocked(promBoardIdStr); + printk( "%s prom init\n", promBoardIdStr ); + printk(KERN_DEBUG "bootloader args count %u \n", argc ); + + PERF->IrqControl[0].IrqMask=0; + +#ifdef CONFIG_OF + strncpy(arcs_cmdline, boot_command_line, COMMAND_LINE_SIZE); +#else + { + int i; + arcs_cmdline[0] = '\0'; + create_cmdline(arcs_cmdline); + strcat(arcs_cmdline, " "); + + for (i = 1; i < argc; i++) { + strcat(arcs_cmdline, (char *)CKSEG0ADDR(argv[i])); + if (i < (argc - 1)) + strcat(arcs_cmdline, " "); + } + } +#endif + + /* Count register increments every other clock */ + mips_hpt_frequency = calculateCpuSpeed() / 2; + +#if defined (CONFIG_SMP) + register_smp_ops(&brcm_smp_ops); +#endif +} + + +/* -------------------------------------------------------------------------- + Name: prom_free_prom_memory +Abstract: + -------------------------------------------------------------------------- */ +void __init prom_free_prom_memory(void) +{ + +} + + +#if defined(CONFIG_ROOT_NFS) && defined(SUPPORT_SWMDK) + /* We can't use gendefconfig to automatically fix this, so instead we will + raise an error here */ + #error "Kernel cannot be configured for both SWITCHMDK and NFS." +#endif + +#ifndef CONFIG_OF +#define HEXDIGIT(d) ((d >= '0' && d <= '9') ? (d - '0') : ((d | 0x20) - 'W')) +#define HEXBYTE(b) (HEXDIGIT((b)[0]) << 4) + HEXDIGIT((b)[1]) + +#ifndef CONFIG_ROOT_NFS_DIR +#define CONFIG_ROOT_NFS_DIR "h:/" +#endif + + +#ifdef CONFIG_BLK_DEV_RAM_SIZE +#define RAMDISK_SIZE CONFIG_BLK_DEV_RAM_SIZE +#else +#define RAMDISK_SIZE 0x800000 +#endif + +/* + * This function reads in a line that looks something like this from NvRam: + * + * CFE bootline=bcmEnet(0,0)host:vmlinux e=192.169.0.100:ffffff00 h=192.169.0.1 + * + * and retuns in the cmdline parameter based on the boot_type that CFE sets up. + * + * for boot from flash, it will use the definition in CONFIG_ROOT_FLASHFS + * + * for boot from NFS, it will look like below: + * CONFIG_CMDLINE="root=/dev/nfs nfsroot=192.168.0.1:/opt/targets/96345R/fs + * ip=192.168.0.100:192.168.0.1::255.255.255.0::eth0:off rw" + * + * for boot from tftp, it will look like below: + * CONFIG_CMDLINE="root=/dev/ram rw rd_start=0x81000000 rd_size=0x1800000" + */ +static void __init create_cmdline(char *cmdline) +{ + char boot_type = '\0', mask[16] = ""; + char bootline[NVRAM_BOOTLINE_LEN] = ""; + char *localip = NULL, *hostip = NULL, *p = bootline, *rdaddr = NULL; + + /* + * too early in bootup sequence to acquire spinlock, not needed anyways + * only the kernel is running at this point + */ + kerSysNvRamGetBootlineLocked(bootline); + + while (*p) { + if (p[0] == 'e' && p[1] == '=') { + /* Found local ip address */ + p += 2; + localip = p; + while (*p && *p != ' ' && *p != ':') + p++; + if (*p == ':') { + /* Found network mask (eg FFFFFF00 */ + *p++ = '\0'; + sprintf(mask, "%u.%u.%u.%u", HEXBYTE(p), + HEXBYTE(p + 2), + HEXBYTE(p + 4), HEXBYTE(p + 6)); + p += 4; + } else if (*p == ' ') + *p++ = '\0'; + } else if (p[0] == 'h' && p[1] == '=') { + /* Found host ip address */ + p += 2; + hostip = p; + while (*p && *p != ' ') + p++; + if (*p == ' ') + *p++ = '\0'; + } else if (p[0] == 'r' && p[1] == '=') { + /* Found boot type */ + p += 2; + boot_type = *p; + while (*p && *p != ' ') + p++; + if (*p == ' ') + *p++ = '\0'; + } else if (p[0] == 'a' && p[1] == '=') { + p += 2; + rdaddr = p; + while (*p && *p != ' ') + p++; + if (*p == ' ') + *p++ = '\0'; + } else + p++; + } + + if (boot_type == 'h' && localip && hostip) { + /* Boot from NFS with proper IP addresses */ + sprintf(cmdline, "root=/dev/nfs nfsroot=%s:" CONFIG_ROOT_NFS_DIR + " ip=%s:%s::%s::eth0:off rw", + hostip, localip, hostip, mask); + } else if (boot_type == 'c') { + /* boot from tftp */ + sprintf(cmdline, "root=/dev/ram0 ro rd_start=%s rd_size=0x%x", + rdaddr, RAMDISK_SIZE << 10); + } else { + /* go with the default, boot from flash */ +#ifdef CONFIG_ROOT_FLASHFS + strcpy(cmdline, CONFIG_ROOT_FLASHFS); +#endif + } +} + +#endif /* CONFIG_OF*/ + +/* ********************************************************************* + * calculateCpuSpeed() + * Calculate the BCM63xx CPU speed by reading the PLL Config register + * and applying the following formula: + * Fcpu_clk = (25 * MIPSDDR_NDIV) / MIPS_MDIV + * Input parameters: + * none + * Return value: + * none + ********************************************************************* */ + +#if defined(CONFIG_BCM963268) || defined(CONFIG_BCM963381) +UINT32 __init calculateCpuSpeed(void) +{ + UINT32 mips_pll_fvco; + + mips_pll_fvco = MISC->miscStrapBus & MISC_STRAP_BUS_MIPS_PLL_FVCO_MASK; + mips_pll_fvco >>= MISC_STRAP_BUS_MIPS_PLL_FVCO_SHIFT; + + return cpu_speed_table[mips_pll_fvco] * 1000000; +} +#endif + +#if defined(CONFIG_BCM960333) +UINT32 __init calculateCpuSpeed(void) +{ + UINT32 uiCpuSpeedTableIdx; // Index into the CPU speed table (0 to 3) + + // Get the strapOverrideBus bits to index into teh CPU speed table + uiCpuSpeedTableIdx = STRAP->strapOverrideBus & STRAP_BUS_MIPS_FREQ_MASK; + uiCpuSpeedTableIdx >>= STRAP_BUS_MIPS_FREQ_SHIFT; + + return cpu_speed_table[uiCpuSpeedTableIdx] * 1000000; +} +#endif + +#if defined(CONFIG_BCM96838) +UINT32 __init calculateCpuSpeed(void) +{ +#define OTP_SHADOW_BRCM_BITS_0_31 0x40 +#define OTP_BRCM_VIPER_FREQ_SHIFT 18 +#define OTP_BRCM_VIPER_FREQ_MASK (0x7 << OTP_BRCM_VIPER_FREQ_SHIFT) + + UINT32 otp_shadow_reg = *((volatile UINT32*)(OTP_BASE+OTP_SHADOW_BRCM_BITS_0_31)); + UINT32 uiCpuSpeedTableIdx = (otp_shadow_reg & OTP_BRCM_VIPER_FREQ_MASK) >> OTP_BRCM_VIPER_FREQ_SHIFT; + + return cpu_speed_table[uiCpuSpeedTableIdx] * 1000000; +} +#endif + +#if defined(CONFIG_BCM96848) +UINT32 __init calculateCpuSpeed(void) +{ + UINT32 clock_sel_strap = (MISC->miscStrapBus & MISC_STRAP_CLOCK_SEL_MASK) >> MISC_STRAP_CLOCK_SEL_SHIFT; + UINT32 clock_sel_otp = bcm_otp_get_max_clksel(); + + if (cpu_speed_table[clock_sel_strap] <= cpu_speed_table[clock_sel_otp]) + return cpu_speed_table[clock_sel_strap] * 1000000; + else + return cpu_speed_table[clock_sel_otp] * 1000000; +} +#endif + +/* Retrieve a buffer of paramters passed by the boot loader. Functions in + * board.c can return requested parameter values to a calling Linux function. + */ +void __init retrieve_boot_loader_parameters(void* bl_parm) +{ + unsigned char *src = (unsigned char *) bl_parm; + const unsigned char *dst_buf = bcm_get_blparms(); + unsigned int dst_buf_size = bcm_get_blparms_size(); + unsigned char *dst = (unsigned char*)dst_buf; + unsigned char *dst_end = (unsigned char*)dst + dst_buf_size - 2; + + if (!dst_buf) { + printk(KERN_ERR "%s:%d Unable to get BCM blparms buffer\n",__func__,__LINE__); + return; + } + do + { + *dst++ = *src++; + } while( (src[0] != '\0' || src[1] != '\0') && (dst < dst_end) ); + + dst[0] = dst[1] = '\0'; +#ifdef CONFIG_BCM_CFE_XARGS_EARLY + bl_xparms_setup(dst_buf, dst_buf_size); +#endif +} + +#endif // defined(CONFIG_BCM_KF_MIPS_BCM963XX) + diff -urN linux-4.1.52-orig/arch/mips/bcm963xx/pwrmngtclk.c linux-4.1.52/arch/mips/bcm963xx/pwrmngtclk.c --- linux-4.1.52-orig/arch/mips/bcm963xx/pwrmngtclk.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/mips/bcm963xx/pwrmngtclk.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,511 @@ +#if defined(CONFIG_BCM_KF_MIPS_BCM963XX) && defined(CONFIG_MIPS_BCM963XX) +/*********************************************************** + * + * Copyright (c) 2009 Broadcom Corporation + * All Rights Reserved + * + * <:label-BRCM:2009:DUAL/GPL:standard + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed + * to you under the terms of the GNU General Public License version 2 + * (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, + * with the following added to such license: + * + * As a special exception, the copyright holders of this software give + * you permission to link this software with independent modules, and + * to copy and distribute the resulting executable under terms of your + * choice, provided that you also meet, for each linked independent + * module, the terms and conditions of the license of that module. + * An independent module is a module which is not derived from this + * software. The special exception does not apply to any modifications + * of the software. + * + * Not withstanding the above, under no circumstances may you combine + * this software in any way with any other Broadcom software provided + * under a license other than the GPL, without Broadcom's express prior + * written consent. + * + * :> + * + ************************************************************/ +#include +#include +#include +#include "board.h" + +#if defined(CONFIG_BCM_KF_POWER_SAVE) && defined(CONFIG_BCM_HOSTMIPS_PWRSAVE) +#define CLK_ALIGNMENT_REG 0xff410040 +#define KEEPME_MASK 0x00007F00 // bit[14:8] + +#define RATIO_ONE_SYNC 0x0 /* 0b000 */ +#define RATIO_ONE_ASYNC 0x1 /* 0b001 */ +#define RATIO_ONE_HALF 0x3 /* 0b011 */ +#define RATIO_ONE_QUARTER 0x5 /* 0b101 */ +#define RATIO_ONE_EIGHTH 0x7 /* 0b111 */ + +#define MASK_ASCR_BITS 0x7 +#define MASK_ASCR_SHFT 28 +#define MASK_ASCR (MASK_ASCR_BITS << MASK_ASCR_SHFT) + +unsigned int originalMipsAscr = 0; // To keep track whether MIPS was in Async mode to start with at boot time +unsigned int originalMipsAscrChecked = 0; +unsigned int keepme; +#endif + +#if defined(CONFIG_BCM_PWRMNGT_MODULE) +#if defined(CONFIG_BCM_DDR_SELF_REFRESH_PWRSAVE) +unsigned int self_refresh_enabled = 0; // Wait for the module to control if it is enabled or not +#endif +#if defined(CONFIG_BCM_KF_POWER_SAVE) && defined(CONFIG_BCM_HOSTMIPS_PWRSAVE) +unsigned int clock_divide_enabled = 0; // Wait for the module to control if it is enabled or not +#endif +#else +#if defined(CONFIG_BCM_DDR_SELF_REFRESH_PWRSAVE) +unsigned int self_refresh_enabled = 1; +#endif +#if defined(CONFIG_BCM_KF_POWER_SAVE) && defined(CONFIG_BCM_HOSTMIPS_PWRSAVE) +unsigned int clock_divide_enabled = 1; +#endif +#endif + +unsigned int clock_divide_low_power0 = 0; +unsigned int clock_divide_active0 = 0; +unsigned int wait_count0 = 0; +#if defined(CONFIG_BCM_HOSTMIPS_PWRSAVE_TIMERS) +unsigned int TimerC0Snapshot0 = 0; +unsigned int prevTimerCnt0, newTimerCnt0, TimerAdjust0; +#endif + +#if defined(CONFIG_SMP) +unsigned int clock_divide_low_power1 = 0; +unsigned int clock_divide_active1 = 0; +unsigned int wait_count1 = 0; +#if defined(CONFIG_BCM_HOSTMIPS_PWRSAVE_TIMERS) +unsigned int TimerC0Snapshot1 = 0; +unsigned int prevTimerCnt1, newTimerCnt1, TimerAdjust1; +#endif +#endif + +#if defined(CONFIG_BCM_HOSTMIPS_PWRSAVE_TIMERS) +unsigned int C0divider, C0multiplier, C0ratio, C0adder; +#endif +extern volatile int isVoiceIdle; + +DEFINE_SPINLOCK(pwrmgnt_clk_irqlock); + +#if defined(CONFIG_BCM_KF_POWER_SAVE) && defined(CONFIG_BCM_HOSTMIPS_PWRSAVE) +/* To put CPU in ASYNC mode and change CPU clock speed */ +void __BcmPwrMngtSetASCR(unsigned int freq_div) +{ + register unsigned int temp; + if (freq_div == RATIO_ONE_ASYNC) { + // Gradually bring the processor speed back to 1:1 + // If it is done in one step, CP0 timer interrupts are missed. + + // E/ SYNC instruction // Step E SYNC instruction + asm("sync" : : ); + + // Step F1 change to 1/4 + asm("mfc0 %0,$22,5" : "=d"(temp) :); + temp = ( temp & ~MASK_ASCR) | (RATIO_ONE_QUARTER << MASK_ASCR_SHFT); + asm("mtc0 %0,$22,5" : : "d" (temp)); + + // Step F2 change to 1/2 + temp = ( temp & ~MASK_ASCR) | (RATIO_ONE_HALF << MASK_ASCR_SHFT); + asm("mtc0 %0,$22,5" : : "d" (temp)); + + // Step F3 change to 1/1, high performance memory access + temp = ( temp & ~MASK_ASCR); + asm("mtc0 %0,$22,5" : : "d" (temp)); + + } else { + // E/ SYNC instruction // Step E SYNC instruction + asm("sync" : : ); + + // F/ change to 1/2, or 1/4, or 1/8 by setting cp0 sel 5 bits[30:28] (sel 4 bits[24:22] for single core mips) to 011, 101, or 111 respectively + // Step F change to 1/2, or 1/4, or 1/8 by setting cp0 bits[30:28] + asm("mfc0 %0,$22,5" : "=d"(temp) :); + temp = ( temp & ~MASK_ASCR) | (freq_div << MASK_ASCR_SHFT); + asm("mtc0 %0,$22,5" : : "d" (temp)); + } + + return; +} /* BcmPwrMngtSetASCR */ + +void BcmPwrMngtSetASCR(unsigned int freq_div) +{ + unsigned long flags; + + if (!freq_div) { + // Can't use this function to set to SYNC mode + return; + } + + spin_lock_irqsave(&pwrmgnt_clk_irqlock, flags); + __BcmPwrMngtSetASCR(freq_div); + spin_unlock_irqrestore(&pwrmgnt_clk_irqlock, flags); + return; +} /* BcmPwrMngtSetASCR */ +EXPORT_SYMBOL(BcmPwrMngtSetASCR); + + +/* To put CPU in SYNC mode and change CPU clock speed to 1:1 ratio */ +/* No SYNC mode in newer MIPS core, use the __BcmPwrMngtSetASCR with ratio 1:1 instead */ +void __BcmPwrMngtSetSCR(void) +{ + register unsigned int cp0_ascr_asc; + + // It is important to go back to divide by 1 async mode first, don't jump directly from divided clock back to SYNC mode. + // A/ set cp0 reg 22 sel 5 bits[30:28] (sel 4 bits[24:22] for single core mips) to 001 + asm("mfc0 %0,$22,5" : "=d"(cp0_ascr_asc) :); + if (!originalMipsAscrChecked) { + originalMipsAscr = cp0_ascr_asc & MASK_ASCR; + originalMipsAscrChecked = 1; + } + if (originalMipsAscr) + return; + cp0_ascr_asc = ( cp0_ascr_asc & ~MASK_ASCR) | (RATIO_ONE_ASYNC << MASK_ASCR_SHFT); + asm("mtc0 %0,$22,5" : : "d" (cp0_ascr_asc)); + + // B/ 16 nops // Was 32 nops (wait a while to make sure clk is back to full speed) + asm("nop" : : ); asm("nop" : : ); + asm("nop" : : ); asm("nop" : : ); + asm("nop" : : ); asm("nop" : : ); + asm("nop" : : ); asm("nop" : : ); + asm("nop" : : ); asm("nop" : : ); + asm("nop" : : ); asm("nop" : : ); + asm("nop" : : ); asm("nop" : : ); + asm("nop" : : ); asm("nop" : : ); + + // C/ SYNC instruction + asm("sync" : : ); + + + // H/ set cp0 reg 22 sel 5 bits[30:28] (sel 4 bits[24:22] for single core mips) to 000 + asm("mfc0 %0,$22,5" : "=d"(cp0_ascr_asc) :); + cp0_ascr_asc = ( cp0_ascr_asc & ~MASK_ASCR); + asm("mtc0 %0,$22,5" : : "d" (cp0_ascr_asc)); + + // I/ SYNC instruction + asm("sync" : : ); + + return; +} /* BcmPwrMngtSetSCR */ + +void BcmPwrMngtSetSCR(void) +{ + unsigned long flags; + + spin_lock_irqsave(&pwrmgnt_clk_irqlock, flags); + __BcmPwrMngtSetSCR(); + spin_unlock_irqrestore(&pwrmgnt_clk_irqlock, flags); + + return; +} /* BcmPwrMngtSetSCR */ +EXPORT_SYMBOL(BcmPwrMngtSetSCR); + + +void BcmPwrMngtSetAutoClkDivide(unsigned int enable) +{ + printk("Host MIPS Clock divider pwrsaving is %s\n", enable?"enabled":"disabled"); + clock_divide_enabled = enable; +} +EXPORT_SYMBOL(BcmPwrMngtSetAutoClkDivide); + + +int BcmPwrMngtGetAutoClkDivide(void) +{ + return (clock_divide_enabled); +} +EXPORT_SYMBOL(BcmPwrMngtGetAutoClkDivide); +#endif + +#if defined(CONFIG_BCM_DDR_SELF_REFRESH_PWRSAVE) +void BcmPwrMngtSetDRAMSelfRefresh(unsigned int enable) +{ +#if defined (CONFIG_BCM963381) + if (0xA0 == ((PERF->RevID & REV_ID_MASK) & 0xF0)) { + printk("DDR Self Refresh pwrsaving must not be enabled on 63381A0/A1\n"); + enable = 0; + } +#endif + + printk("DDR Self Refresh pwrsaving is %s\n", enable?"enabled":"disabled"); + self_refresh_enabled = enable; + +} +EXPORT_SYMBOL(BcmPwrMngtSetDRAMSelfRefresh); + + +int BcmPwrMngtGetDRAMSelfRefresh(void) +{ + return (self_refresh_enabled); +} +EXPORT_SYMBOL(BcmPwrMngtGetDRAMSelfRefresh); + +#if defined(CONFIG_BCM_ADSL_MODULE) || defined(CONFIG_BCM_ADSL) +PWRMNGT_DDR_SR_CTRL *pDdrSrCtrl = NULL; +void BcmPwrMngtRegisterLmemAddr(PWRMNGT_DDR_SR_CTRL *pDdrSr) +{ + pDdrSrCtrl = pDdrSr; + + // Initialize tp0 to busy status and tp1 to idle + // for cases where SMP is not compiled in. + if(NULL != pDdrSrCtrl) { + pDdrSrCtrl->word = 0; + pDdrSrCtrl->tp0Busy = 1; + pDdrSrCtrl->tp1Busy = 0; + } +} +EXPORT_SYMBOL(BcmPwrMngtRegisterLmemAddr); +#else +PWRMNGT_DDR_SR_CTRL ddrSrCtl = {{.word=0}}; +PWRMNGT_DDR_SR_CTRL *pDdrSrCtrl = &ddrSrCtl; +#endif +#endif + +// Determine if cpu is busy by checking the number of times we entered the wait +// state in the last milisecond. If we entered the wait state only once or +// twice, then the processor is very likely not busy and we can afford to slow +// it down while on wait state. Otherwise, we don't slow down the processor +// while on wait state in order to avoid affecting the time it takes to +// process interrupts +void BcmPwrMngtCheckWaitCount (void) +{ + int cpu = smp_processor_id(); + + if (cpu == 0) { +#if defined(CONFIG_SMP) && defined(CONFIG_BCM_HOSTMIPS_PWRSAVE_TIMERS) + if (isVoiceIdle && TimerC0Snapshot1) { +#else + if (isVoiceIdle) { +#endif + if (wait_count0 > 0 && wait_count0 < 3) { + clock_divide_low_power0 = 1; + } + else { + clock_divide_low_power0 = 0; + } + } + else { + clock_divide_low_power0 = 0; + } + wait_count0 = 0; + } +#if defined(CONFIG_SMP) + else { +#if defined(CONFIG_BCM_HOSTMIPS_PWRSAVE_TIMERS) + if (TimerC0Snapshot1) { +#else + { +#endif + if (wait_count1 > 0 && wait_count1 < 3) { + clock_divide_low_power1 = 1; + } + else { + clock_divide_low_power1 = 0; + } + } + wait_count1 = 0; + } +#endif +} + +// When entering wait state, consider reducing the MIPS clock speed. +// Clock speed is reduced if it has been determined that the cpu was +// mostly idle in the previous milisecond. Clock speed is reduced only +// once per 1 milisecond interval. +void BcmPwrMngtReduceCpuSpeed (void) +{ + int cpu = smp_processor_id(); + unsigned long flags; + + spin_lock_irqsave(&pwrmgnt_clk_irqlock, flags); + + if (cpu == 0) { + // Slow down the clock when entering wait instruction + // only if the cpu is not busy + if (clock_divide_low_power0) { + if (wait_count0 < 2) { + clock_divide_active0 = 1; +#if defined(CONFIG_BCM_DDR_SELF_REFRESH_PWRSAVE) + if (pDdrSrCtrl && self_refresh_enabled) { + // Communicate TP status to PHY MIPS + pDdrSrCtrl->tp0Busy = 0; + } +#endif + } + } + wait_count0++; + } +#if defined(CONFIG_SMP) + else { + // Slow down the clock when entering wait instruction + // only if the cpu is not busy + if (clock_divide_low_power1) { + if (wait_count1 < 2) { + clock_divide_active1 = 1; +#if defined(CONFIG_BCM_DDR_SELF_REFRESH_PWRSAVE) + if (pDdrSrCtrl && self_refresh_enabled) { + // Communicate TP status to PHY MIPS + pDdrSrCtrl->tp1Busy = 0; + } +#endif + } + } + wait_count1++; + } +#endif + +#if defined(CONFIG_SMP) + if (clock_divide_active0 && clock_divide_active1) { +#else + if (clock_divide_active0) { +#endif +#if defined(CONFIG_BCM_KF_POWER_SAVE) && defined(CONFIG_BCM_HOSTMIPS_PWRSAVE) + if (clock_divide_enabled) { + __BcmPwrMngtSetASCR(RATIO_ONE_EIGHTH); + } +#endif + +#if defined(CONFIG_BCM_DDR_SELF_REFRESH_PWRSAVE) + // Place DDR in self-refresh mode if enabled and other processors are OK with it + if (pDdrSrCtrl && !pDdrSrCtrl->word && self_refresh_enabled) { + // Below defines are CHIP Specific - refer to xxxx_map_part.h +#if defined(DMODE_1_DRAMSLEEP) + DDR->DMODE_1 |= DMODE_1_DRAMSLEEP; +#elif defined(MEMC_SELF_REFRESH) + MEMC->Control |= MEMC_SELF_REFRESH; +#elif defined(CFG_DRAMSLEEP) + MEMC->DRAM_CFG |= CFG_DRAMSLEEP; +#elif defined(SELF_REFRESH_CMD) + MEMC->SDR_CFG.DRAM_CMD[SELF_REFRESH_CMD] = 0; +#else + #error "DDR Self refresh definition missing in xxxx_map_part.h for this chip" +#endif + } +#endif + } + spin_unlock_irqrestore(&pwrmgnt_clk_irqlock, flags); +} + +// Full MIPS clock speed is resumed on the first interrupt following +// the wait instruction. If the clock speed was reduced, the MIPS +// C0 counter was also slowed down and its value needs to be readjusted. +// The adjustments are done based on a reliable timer from the peripheral +// block, timer2. The adjustments are such that C0 will never drift +// but will see minor jitter. +void BcmPwrMngtResumeFullSpeed (void) +{ +#if defined(CONFIG_BCM_HOSTMIPS_PWRSAVE_TIMERS) + unsigned int mult, rem, new; +#endif + int cpu = smp_processor_id(); + unsigned long flags; + + spin_lock_irqsave(&pwrmgnt_clk_irqlock, flags); + +#if defined(CONFIG_BCM_DDR_SELF_REFRESH_PWRSAVE) + if (pDdrSrCtrl) { + // Communicate TP status to PHY MIPS + // Here I don't check if Self-Refresh is enabled because when it is, + // I want PHY MIPS to think the Host MIPS is always busy so it won't assert SR + if (cpu == 0) { + pDdrSrCtrl->tp0Busy = 1; + } else { + pDdrSrCtrl->tp1Busy = 1; + } + } +#endif + + +#if defined(CONFIG_BCM_KF_POWER_SAVE) && defined(CONFIG_BCM_HOSTMIPS_PWRSAVE) + +#if defined(CONFIG_SMP) + if (clock_divide_enabled && clock_divide_active0 && clock_divide_active1) { +#else + if (clock_divide_enabled && clock_divide_active0) { +#endif + // In newer MIPS core, there is no SYNC mode, simply use 1:1 async + __BcmPwrMngtSetASCR(RATIO_ONE_ASYNC); + } +#endif + +#if defined(CONFIG_BCM_HOSTMIPS_PWRSAVE_TIMERS) + if (cpu == 0) { + // Check for TimerCnt2 rollover + newTimerCnt0 = TIMER->TimerCnt2 & 0x3fffffff; + if (newTimerCnt0 < prevTimerCnt0) { + TimerAdjust0 += C0adder; + } + + // fix the C0 counter because it slowed down while on wait state + if (clock_divide_active0) { + mult = newTimerCnt0/C0divider; + rem = newTimerCnt0%C0divider; + new = mult*C0multiplier + ((rem*C0ratio)>>10); + write_c0_count(TimerAdjust0 + TimerC0Snapshot0 + new); + clock_divide_active0 = 0; + } + prevTimerCnt0 = newTimerCnt0; + } +#if defined(CONFIG_SMP) + else { + // Check for TimerCnt2 rollover + newTimerCnt1 = TIMER->TimerCnt2 & 0x3fffffff; + if (newTimerCnt1 < prevTimerCnt1) { + TimerAdjust1 += C0adder; + } + + // fix the C0 counter because it slowed down while on wait state + if (clock_divide_active1) { + mult = newTimerCnt1/C0divider; + rem = newTimerCnt1%C0divider; + new = mult*C0multiplier + ((rem*C0ratio)>>10); + write_c0_count(TimerAdjust1 + TimerC0Snapshot1 + new); + clock_divide_active1 = 0; + } + prevTimerCnt1 = newTimerCnt1; + } +#endif +#else + // On chips not requiring the PERIPH Timers workaround, + // only need to clear the active flags, no need to adjust timers + if (cpu == 0) { + clock_divide_active0 = 0; + } +#if defined(CONFIG_SMP) + else { + clock_divide_active1 = 0; + } +#endif +#endif + spin_unlock_irqrestore(&pwrmgnt_clk_irqlock, flags); +} + + +#if defined(CONFIG_BCM_HOSTMIPS_PWRSAVE_TIMERS) +// These numbers can be precomputed. The values are chosen such that the +// calculations will never overflow as long as the MIPS frequency never +// exceeds 850 MHz (hence mips_hpt_frequency must not exceed 425 MHz) +void BcmPwrMngtInitC0Speed (void) +{ + unsigned int mult, rem; + if (mips_hpt_frequency > 425000000) { + printk("\n\nWarning!!! CPU frequency exceeds limits to support" \ + " Clock Divider feature for Power Management\n"); + } + C0divider = 50000000/128; + C0multiplier = mips_hpt_frequency/128; + C0ratio = ((mips_hpt_frequency/1000000)<<10)/50; + mult = 0x40000000/C0divider; + rem = 0x40000000%C0divider; + // Value below may overflow from 32 bits but that's ok + C0adder = mult*C0multiplier + ((rem*C0ratio)>>10); + spin_lock_init(&pwrmgnt_clk_irqlock); +} +#endif //CONFIG_BCM_HOSTMIPS_PWRSAVE_TIMERS + +#endif //defined(CONFIG_BCM_KF_MIPS_BCM963XX) + diff -urN linux-4.1.52-orig/arch/mips/bcm963xx/setup.c linux-4.1.52/arch/mips/bcm963xx/setup.c --- linux-4.1.52-orig/arch/mips/bcm963xx/setup.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/mips/bcm963xx/setup.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,1159 @@ +#if defined(CONFIG_BCM_KF_MIPS_BCM963XX) && defined(CONFIG_MIPS_BCM963XX) + +/* +<:copyright-BRCM:2002:GPL/GPL:standard + + Copyright (c) 2002 Broadcom + All Rights Reserved + +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 (the "GPL"). + +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. + + +A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by +writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + +:> +*/ +/* + * Generic setup routines for Broadcom 963xx MIPS boards + */ + +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +//#include +#include +#include +#include + +#include + +#include +#include + +extern void check_if_rootfs_is_set(char *cmdline); +extern unsigned long getMemorySize(void); +extern irqreturn_t brcm_timer_interrupt(int irq, void *dev_id); + +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_PCI) +#include +#include +#endif + +#if IS_ENABLED(CONFIG_BCM_ADSL) +#include "softdsl/AdslCoreDefs.h" +#endif + +#include "shared_utils.h" +#include + +extern unsigned long memsize; + +#if IS_ENABLED(CONFIG_BCM_RDPA) || IS_ENABLED(CONFIG_BCM_DHD_RUNNER) +NVRAM_DATA NvramData; +#endif + +static void __init setMemorySize(void) +{ +#if defined(CONFIG_BRCM_IKOS) + memsize = (31 * 1024 * 1024); /* voice DSP is loaded after this amount */ +#elif defined(CONFIG_BRCM_MEMORY_RESTRICTION_16M) + memsize = (16 * 1024 * 1024); +#elif defined(CONFIG_BRCM_MEMORY_RESTRICTION_32M) + memsize = (32 * 1024 * 1024); +#elif defined(CONFIG_BRCM_MEMORY_RESTRICTION_64M) + memsize = (64 * 1024 * 1024); +#elif defined(CONFIG_BCM960333) || defined(CONFIG_BCM963381) + uint32 memCfg; + +#if defined (CONFIG_BCM963381) + memCfg = MEMC->SDR_CFG.SDR_CFG; +#else + memCfg = MEMC->SDR_CFG; +#endif + memCfg = (memCfg&MEMC_SDRAM_SPACE_MASK)>>MEMC_SDRAM_SPACE_SHIFT; + + memsize = 1<<(memCfg+20); +#elif defined(CONFIG_BCM96848) + memsize = 1<<(((MEMC->GLB_GCFG&MEMC_GLB_GCFG_SIZE1_MASK)>>MEMC_GLB_GCFG_SIZE1_SHIFT)+20); +#else +#ifdef CONFIG_BCM_512MB_DDR + memsize = (MEMC->CSEND << 24); +#else + memsize = (((MEMC->CSEND > 16) ? 16 : MEMC->CSEND) << 24); +#endif +#endif +} + +static unsigned long __init get_usable_mem_size(void) +{ + int i; + + for(i = 0; i < boot_mem_map.nr_map; i++) + { + if( boot_mem_map.map[i].type == BOOT_MEM_RAM ) + return boot_mem_map.map[i].size; + } + + return 0; +} + +static void __init inline reserve_system_mem(char* name, unsigned long addr, unsigned long size) +{ + add_memory_region(addr, size, BOOT_MEM_RESERVED); + strcpy(reserve_mem[rsvd_mem_cnt].name, name); + reserve_mem[rsvd_mem_cnt].size = size; + reserve_mem[rsvd_mem_cnt].phys_addr = (uint32_t)boot_mem_map.map[boot_mem_map.nr_map-1].addr; + printk("reserve memory %s phys 0x%x size 0x%x\n", reserve_mem[rsvd_mem_cnt].name, reserve_mem[rsvd_mem_cnt].phys_addr, reserve_mem[rsvd_mem_cnt].size); + rsvd_mem_cnt++; + + return; +} + +static void __init BcmMemReserveInit(void) +{ + unsigned long memsize = getMemorySize(); +#if IS_ENABLED(CONFIG_BCM_RDPA) || IS_ENABLED(CONFIG_BCM_DHD_RUNNER) || IS_ENABLED(CONFIG_BCM_ADSL) + unsigned long size = 0; +#endif +#if IS_ENABLED(CONFIG_BCM_DHD_RUNNER) + int i; +#endif +#ifdef CONFIG_BCM_512MB_DDR + unsigned long highmemsize = 0; + + #define SZ_256M 0x10000000 + if(memsize > SZ_256M) + { + highmemsize = memsize - SZ_256M; + memsize = SZ_256M; + } +#endif + rsvd_mem_cnt = 0; + + if (boot_mem_map.nr_map != 0) + { + unsigned long usr_usable = get_usable_mem_size(); + unsigned long usr_reserved = memsize - usr_usable; + + if( usr_usable == 0 ) + { + printk("Error: No usable memory detected\n"); + BUG_ON(1); + } + + if( memsize < usr_usable ) + { + printk("Error: Detected usable memory greater than physical memory\n"); + BUG_ON(1); + } + + boot_mem_map.nr_map = 0; + if(usr_reserved) + { + memsize -= usr_reserved; + add_memory_region(memsize, usr_reserved, BOOT_MEM_RESERVED); + } + } + +#if IS_ENABLED(CONFIG_BCM_ADSL) + size = ADSL_SDRAM_IMAGE_SIZE; + memsize -= ADSL_SDRAM_IMAGE_SIZE; + reserve_system_mem(ADSL_BASE_ADDR_STR, memsize, size); +#endif + +#if IS_ENABLED(CONFIG_BCM_RDPA) || IS_ENABLED(CONFIG_BCM_DHD_RUNNER) + kerSysNvRamGet((char *)&NvramData, sizeof(NVRAM_DATA), 0); +#endif + +#if IS_ENABLED(CONFIG_BCM_RDPA) + size = NvramData.allocs.alloc_rdp.param1_size; + if (size == 0xff) { + /* Erased NVRAM should be alloc TM_ERASED_NVRAM_DEF_DDR_SIZE to be + * backward compatible */ + size = TM_ERASED_NVRAM_DEF_DDR_SIZE; + } + size = size * 0x100000; + + if(size < TM_DEF_DDR_SIZE) + size = TM_DEF_DDR_SIZE; + + /* TM_BASE_ADDR_STR must be 2MB aligned, reserve unaligned block to heap */ + if ((memsize - size) % (2 * 1024 * 1024) != 0) + { + int tempsize = (memsize - size) % (2 * 1024 * 1024); + + memsize -= tempsize; + add_memory_region(memsize, tempsize, BOOT_MEM_RAM); + } + + memsize -= size; + reserve_system_mem(TM_BASE_ADDR_STR, memsize, size); + + size = NvramData.allocs.alloc_rdp.param2_size; + if (size == 0xff) { + /* Erased NVRAM should be treated as zero */ + size = 0; + } + size = size * 0x100000; + + if(size < TM_MC_DEF_DDR_SIZE) + size = TM_MC_DEF_DDR_SIZE; + + memsize -= size; + reserve_system_mem(TM_MC_BASE_ADDR_STR, memsize, size); +#endif + +#if IS_ENABLED(CONFIG_BCM_DHD_RUNNER) + /* Add memory for DHD offload */ + for( i = 0; i < 3; i++ ) + { + size = NvramData.alloc_dhd.dhd_size[i]; + if (size != 0xff && size != 0) + { + char name[16]; + size = size * 0x100000; + memsize -= size; + sprintf(name, "%s%d", "dhd", i); + reserve_system_mem(name, memsize, size); + } + } +#endif + + if( rsvd_mem_cnt ) + is_memory_reserved = true; + + /* add the linux usable region */ + add_memory_region(0, memsize, BOOT_MEM_RAM); +#ifdef CONFIG_BCM_512MB_DDR + if (highmemsize) + add_memory_region(0x20000000, highmemsize, BOOT_MEM_RAM); +#endif +} +extern int bcm_reserve_memory(void); +extern int boot_param_status_legacy; +void __init plat_mem_setup(void) +{ + /* set up the ddr memory size first */ + if (boot_param_status_legacy) { + setMemorySize(); + BcmMemReserveInit(); +#if IS_ENABLED(CONFIG_BCM_ADSL) + printk("DSL SDRAM reserved: 0x%x at 0x%p\n", ADSL_SDRAM_IMAGE_SIZE, kerSysGetDslPhyMemory()); +#endif + } + else { + bcm_reserve_memory(); + } + + { + volatile unsigned int *cr; + uint32 mipsBaseAddr = MIPS_BASE; + + cr = (void *)(mipsBaseAddr + MIPS_RAC_CR0); + *cr = *cr | RAC_D; + +#if defined(MIPS_RAC_CR1) + cr = (void *)(mipsBaseAddr + MIPS_RAC_CR1); + *cr = *cr | RAC_D; +#endif + } +} + + +extern UINT32 __init calculateCpuSpeed(void); +#ifdef CONFIG_BCM_HOSTMIPS_PWRSAVE_TIMERS +extern void BcmPwrMngtInitC0Speed (void); +#endif + + +void __init plat_time_init(void) +{ + mips_hpt_frequency = calculateCpuSpeed() / 2; +#ifdef CONFIG_BCM_HOSTMIPS_PWRSAVE_TIMERS + BcmPwrMngtInitC0Speed(); +#else + // Enable cp0 counter/compare interrupt when + // not using workaround for clock divide + write_c0_status(IE_IRQ5 | read_c0_status()); +#endif +} + +extern void stop_other_cpu(void); // in arch/mips/kernel/smp.c + +static void brcm_machine_restart(char *command) +{ +#if defined(CONFIG_SMP) + stop_other_cpu(); +#endif + local_irq_disable(); + kerSysSoftReset(); +} + +static void brcm_machine_halt(void) +{ + /* + * we don't support power off yet. This halt will cause both CPU's to + * spin in a while(1) loop with interrupts disabled. (Used for gathering + * wlan debug dump via JTAG) + */ +#if defined(CONFIG_SMP) + stop_other_cpu(); +#endif + printk("System halted\n"); + local_irq_disable(); + while (1); +} + + +#if defined(CONFIG_BCM963268) + +int map_63268_vdsl_override(int val) { + switch (val & ~BP_ACTIVE_MASK) { + case (BP_GPIO_10_AH & BP_GPIO_NUM_MASK): + case (BP_GPIO_11_AH & BP_GPIO_NUM_MASK): + return(GPIO_BASE_VDSL_PHY_OVERRIDE_0); + case (BP_GPIO_12_AH & BP_GPIO_NUM_MASK): + case (BP_GPIO_13_AH & BP_GPIO_NUM_MASK): + return(GPIO_BASE_VDSL_PHY_OVERRIDE_1); + case (BP_GPIO_24_AH & BP_GPIO_NUM_MASK): + case (BP_GPIO_25_AH & BP_GPIO_NUM_MASK): + return(GPIO_BASE_VDSL_PHY_OVERRIDE_2); + case (BP_GPIO_26_AH & BP_GPIO_NUM_MASK): + case (BP_GPIO_27_AH & BP_GPIO_NUM_MASK): + return(GPIO_BASE_VDSL_PHY_OVERRIDE_3); + default: + return(0); + } +} + +int map_63268_misc_misc_override(int val) { + switch (val & ~BP_ACTIVE_MASK) { + case (BP_GPIO_8_AH & BP_GPIO_NUM_MASK): + return(MISC_MISC_DSL_GPIO_8_OVERRIDE); + case (BP_GPIO_9_AH & BP_GPIO_NUM_MASK): + return(MISC_MISC_DSL_GPIO_9_OVERRIDE); + default: + return(0); + } +} + +static int __init bcm63268_hw_init(void) +{ + unsigned int GPIOOverlays, DeviceOptions = 0; + unsigned short gpio; + const ETHERNET_MAC_INFO *EnetInfo; + unsigned char vreg1p8; +#if defined(CONFIG_BCM_1V2REG_AUTO_SHUTDOWN) + uint32 startCount, endCount; + int diff; +#endif + + /* Turn off test bus */ + PERF->blkEnables &= ~TBUS_CLK_EN; + + +#if !(defined(CONFIG_BCM_XTMRT) || defined(CONFIG_BCM_XTMRT_MODULE)) + // Disable SAR if unused + PERF->blkEnables &= ~( SAR_CLK_EN ); + MISC->miscIddqCtrl |= MISC_IDDQ_CTRL_SAR; +#endif + +#if defined(CONFIG_BCM_XTMRT) || defined(CONFIG_BCM_XTMRT_MODULE) + // Phy should always be powered down if XTM is deselected + if (kerSysGetDslPhyEnable()) { +#else + if (0) { +#endif + MISC->miscIddqCtrl &= ~(MISC_IDDQ_CTRL_VDSL_PHY + | MISC_IDDQ_CTRL_VDSL_MIPS + | MISC_IDDQ_CTRL_SAR); + } + else + { + /* If there is no phy support, shut off power */ + PERF->blkEnables &= ~( PHYMIPS_CLK_EN + | VDSL_CLK_EN + | VDSL_AFE_EN | VDSL_QPROC_EN ); + MISC->miscIddqCtrl |= (MISC_IDDQ_CTRL_VDSL_PHY + | MISC_IDDQ_CTRL_VDSL_MIPS); + } + + if( BpGetDeviceOptions(&DeviceOptions) == BP_SUCCESS ) { + if(DeviceOptions&BP_DEVICE_OPTION_DISABLE_LED_INVERSION) + MISC->miscLed_inv = 0; + } + + /* Set LED blink rate for activity LEDs to 80mS */ + LED->ledInit &= ~LED_FAST_INTV_MASK; + LED->ledInit |= (LED_INTERVAL_20MS * 4) << LED_FAST_INTV_SHIFT; + + /* Start with all HW LEDs disabled */ + LED->ledHWDis |= 0xFFFFFF; + + + EnetInfo = BpGetEthernetMacInfoArrayPtr(); + + /* Enable HW to drive LEDs for Ethernet ports in use */ + if (EnetInfo[0].sw.port_map & (1 << 0)) { + LED->ledHWDis &= ~(1 << LED_EPHY0_ACT); + LED->ledHWDis &= ~(1 << LED_EPHY0_SPD); + } + if (EnetInfo[0].sw.port_map & (1 << 1)) { + LED->ledHWDis &= ~(1 << LED_EPHY1_ACT); + LED->ledHWDis &= ~(1 << LED_EPHY1_SPD); + } + if (EnetInfo[0].sw.port_map & (1 << 2)) { + LED->ledHWDis &= ~(1 << LED_EPHY2_ACT); + LED->ledHWDis &= ~(1 << LED_EPHY2_SPD); + } + if (EnetInfo[0].sw.port_map & (1 << 3)) { + LED->ledHWDis &= ~(1 << LED_GPHY0_ACT); + LED->ledHWDis &= ~(1 << LED_GPHY0_SPD0); + LED->ledHWDis &= ~(1 << LED_GPHY0_SPD1); + LED->ledLinkActSelLow |= ((1 << LED_GPHY0_SPD0) << LED_0_LINK_SHIFT); + LED->ledLinkActSelLow |= ((1 << LED_GPHY0_SPD1) << LED_1_LINK_SHIFT); + GPIO->RoboSWLEDControl |= LED_BICOLOR_SPD; + } + + /* UART2 - SDIN and SDOUT are separate for flexibility */ + { + unsigned short Uart2Sdin; + unsigned short Uart2Sdout; + if (BpGetUart2SdinGpio(&Uart2Sdin) == BP_SUCCESS) { + switch (Uart2Sdin & BP_GPIO_NUM_MASK) { + case (BP_GPIO_12_AH & BP_GPIO_NUM_MASK): + GPIO->GPIOMode |= (GPIO_MODE_UART2_SDIN); + break; + case (BP_GPIO_26_AH & BP_GPIO_NUM_MASK): + GPIO->GPIOMode |= (GPIO_MODE_UART2_SDIN2); + break; + } + } + if (BpGetUart2SdoutGpio(&Uart2Sdout) == BP_SUCCESS) { + switch (Uart2Sdout & BP_GPIO_NUM_MASK) { + case (BP_GPIO_13_AH & BP_GPIO_NUM_MASK): + GPIO->GPIOMode |= (GPIO_MODE_UART2_SDOUT); + break; + case (BP_GPIO_27_AH & BP_GPIO_NUM_MASK): + GPIO->GPIOMode |= (GPIO_MODE_UART2_SDOUT2); + break; + } + } + } + + + if( BpGetGPIOverlays(&GPIOOverlays) == BP_SUCCESS ) { + if (GPIOOverlays & BP_OVERLAY_SERIAL_LEDS) { + GPIO->GPIOMode |= (GPIO_MODE_SERIAL_LED_CLK | GPIO_MODE_SERIAL_LED_DATA); + LED->ledInit |= LED_SERIAL_LED_EN; + } + if ( BpGetWanDataLedGpio(&gpio) == BP_SUCCESS ) { + if ((gpio & BP_GPIO_NUM_MASK) == LED_INET_ACT) { + /* WAN Data LED must be LED 8 */ + if (!(gpio & BP_GPIO_SERIAL)) { + /* If LED is not serial, enable corresponding GPIO */ + GPIO->LEDCtrl |= GPIO_NUM_TO_MASK(gpio); + } + } + } + /* Enable LED controller to drive GPIO when LEDs are connected to GPIO pins */ + if (GPIOOverlays & BP_OVERLAY_EPHY_LED_0) { + GPIO->LEDCtrl |= (1 << LED_EPHY0_ACT); + GPIO->LEDCtrl |= (1 << LED_EPHY0_SPD); + } + if (GPIOOverlays & BP_OVERLAY_EPHY_LED_1) { + GPIO->LEDCtrl |= (1 << LED_EPHY1_ACT); + GPIO->LEDCtrl |= (1 << LED_EPHY1_SPD); + } + if (GPIOOverlays & BP_OVERLAY_EPHY_LED_2) { + GPIO->LEDCtrl |= (1 << LED_EPHY2_ACT); + GPIO->LEDCtrl |= (1 << LED_EPHY2_SPD); + } + if (GPIOOverlays & BP_OVERLAY_GPHY_LED_0) { + GPIO->LEDCtrl |= (1 << LED_GPHY0_ACT); + GPIO->LEDCtrl |= (1 << LED_GPHY0_SPD0); + GPIO->LEDCtrl |= (1 << LED_GPHY0_SPD1); + } + if (GPIOOverlays & BP_OVERLAY_PHY) { + unsigned short IntLdMode = 0xffff; + unsigned short IntLdPwr = 0xffff; + unsigned short ExtLdMode = 0xffff; + unsigned short ExtLdPwr = 0xffff; + unsigned short ExtLdClk = 0xffff; + unsigned short ExtLdData = 0xffff; + unsigned int ul; + int ExplicitLdControl ; + ExplicitLdControl = (BpGetIntAFELDModeGpio(&IntLdMode) == BP_SUCCESS) ? 1 : 0; + ExplicitLdControl = ExplicitLdControl + ((BpGetIntAFELDPwrGpio(&IntLdPwr) == BP_SUCCESS) ? 1 : 0); + ExplicitLdControl = ExplicitLdControl + ((BpGetExtAFELDModeGpio(&ExtLdMode) == BP_SUCCESS) ? 1 : 0); + ExplicitLdControl = ExplicitLdControl + ((BpGetExtAFELDPwrGpio(&ExtLdPwr) == BP_SUCCESS) ? 1 : 0); + ExplicitLdControl = ExplicitLdControl + ((BpGetExtAFELDClkGpio(&ExtLdClk) == BP_SUCCESS) ? 1 : 0); + ExplicitLdControl = ExplicitLdControl + ((BpGetExtAFELDDataGpio(&ExtLdData) == BP_SUCCESS) ? 1 : 0); + if (ExplicitLdControl == 0) { + /* default if boardparms doesn't specify a subset */ + GPIO->GPIOBaseMode |= GPIO_BASE_VDSL_PHY_OVERRIDE_0 | GPIO_BASE_VDSL_PHY_OVERRIDE_1; + } else { + GPIO->GPIOBaseMode |= map_63268_vdsl_override(IntLdMode) + | map_63268_vdsl_override(IntLdPwr) + | map_63268_vdsl_override(ExtLdMode) + | map_63268_vdsl_override(ExtLdPwr) + | map_63268_vdsl_override(ExtLdClk) + | map_63268_vdsl_override(ExtLdData) ; + ul = map_63268_misc_misc_override(IntLdMode) + | map_63268_misc_misc_override(IntLdPwr) + | map_63268_misc_misc_override(ExtLdMode) + | map_63268_misc_misc_override(ExtLdPwr) + | map_63268_misc_misc_override(ExtLdClk) + | map_63268_misc_misc_override(ExtLdData) ; + if (ul != 0) { + MISC->miscMisc_ctrl |= ul; + } + } + } + + /* Enable PCIe CLKREQ signal */ + if (GPIOOverlays & BP_OVERLAY_PCIE_CLKREQ) { + GPIO->GPIOMode |= GPIO_MODE_PCIE_CLKREQ_B; + } + + /* Enable VREG CLK signal */ + if (GPIOOverlays & BP_OVERLAY_VREG_CLK) { + GPIO->GPIOMode |= GPIO_MODE_VREG_CLK; + } + + if (GPIOOverlays & BP_OVERLAY_USB_LED) { + LED->ledHWDis &= ~(1 << LED_USB_ACT); + } + /* Enable HS SPI SS Pins */ + if (GPIOOverlays & BP_OVERLAY_HS_SPI_SSB4_EXT_CS) { + GPIO->GPIOMode |= GPIO_MODE_HS_SPI_SS_4; + } + if (GPIOOverlays & BP_OVERLAY_HS_SPI_SSB5_EXT_CS) { + GPIO->GPIOMode |= GPIO_MODE_HS_SPI_SS_5; + } + if (GPIOOverlays & BP_OVERLAY_HS_SPI_SSB6_EXT_CS) { + GPIO->GPIOMode |= GPIO_MODE_HS_SPI_SS_6; + } + if (GPIOOverlays & BP_OVERLAY_HS_SPI_SSB7_EXT_CS) { + GPIO->GPIOMode |= GPIO_MODE_HS_SPI_SS_7; + } + } + + { + unsigned short PhyBaseAddr; + /* clear the base address first. hw does not clear upon soft reset*/ + GPIO->RoboswEphyCtrl &= ~EPHY_PHYAD_BASE_ADDR_MASK; + if( BpGetEphyBaseAddress(&PhyBaseAddr) == BP_SUCCESS ) { + GPIO->RoboswEphyCtrl |= ((PhyBaseAddr >>3) & 0x3) << EPHY_PHYAD_BASE_ADDR_SHIFT; + } + + /* clear the base address first. hw does not clear upon soft reset*/ + GPIO->RoboswGphyCtrl &= ~GPHY_PHYAD_BASE_ADDR_MASK; + if( BpGetGphyBaseAddress(&PhyBaseAddr) == BP_SUCCESS ) { + GPIO->RoboswGphyCtrl |= ((PhyBaseAddr >>3) & 0x3) << GPHY_PHYAD_BASE_ADDR_SHIFT; + } + } + + +#if defined(CONFIG_USB) + PERF->blkEnables |= USBH_CLK_EN; + PERF->softResetB |= SOFT_RST_USBH; + TIMER->ClkRstCtl |= USB_REF_CLKEN; + MISC->miscIddqCtrl &= ~MISC_IDDQ_CTRL_USBH; + mdelay(100); + USBH->SwapControl = EHCI_ENDIAN_SWAP | OHCI_ENDIAN_SWAP; + USBH->Setup |= USBH_IOC; + USBH->Setup &= ~USBH_IPP; + USBH->PllControl1 &= ~(PLLC_PLL_IDDQ_PWRDN | PLLC_PLL_PWRDN_DELAY); +#else + MISC->miscIddqCtrl |= MISC_IDDQ_CTRL_USBH; + PERF->blkEnables &= ~USBH_CLK_EN; +#endif + +#if defined(CONFIG_BCM_KF_FAP) && (defined(CONFIG_BCM_FAP) || defined(CONFIG_BCM_FAP_MODULE)) +#else + PERF->blkEnables &= ~FAP0_CLK_EN; + PERF->blkEnables &= ~FAP1_CLK_EN; +#endif + +#if defined(CONFIG_PCI) + /* Enable WOC */ + PERF->blkEnables |=WLAN_OCP_CLK_EN; + mdelay(10); + PERF->softResetB &= ~(SOFT_RST_WLAN_SHIM_UBUS | SOFT_RST_WLAN_SHIM); + mdelay(1); + PERF->softResetB |= (SOFT_RST_WLAN_SHIM_UBUS | SOFT_RST_WLAN_SHIM); + mdelay(1); + + WLAN_SHIM->ShimMisc = (WLAN_SHIM_FORCE_CLOCKS_ON|WLAN_SHIM_MACRO_SOFT_RESET); + mdelay(1); + WLAN_SHIM->MacControl = (SICF_FGC|SICF_CLOCK_EN); + WLAN_SHIM->ShimMisc = WLAN_SHIM_FORCE_CLOCKS_ON; + WLAN_SHIM->ShimMisc = 0; + WLAN_SHIM->MacControl = SICF_CLOCK_EN; +#endif + +#if defined(CONFIG_BCM_KF_POWER_SAVE) && defined(CONFIG_BCM_DDR_SELF_REFRESH_PWRSAVE) + /* Enable power savings from DDR pads on this chip when DDR goes in Self-Refresh mode */ + MEMC->PhyControl.IDLE_PAD_CONTROL = 0x00000172; + MEMC->PhyByteLane0Control.IDLE_PAD_CONTROL = 0x000fffff; + MEMC->PhyByteLane1Control.IDLE_PAD_CONTROL = 0x000fffff; +#endif + +#if defined(CONFIG_BCM_1V2REG_AUTO_SHUTDOWN) + /* + * Determine if internal VREG is used. + * If not, disable it to improve WLAN performance at 5GHz + * The ring oscillators are affected when varying the 1V2 voltage + * So take a measure of the ring osc count, then raise the internal regulator voltage and remeasure + * If the ring osc count changed as expected than internal regulators are used + */ + printk("Internal 1P2 VREG will be shutdown if unused..."); + + /* Take the first ring osc measurement */ + GPIO->RingOscCtrl1 = RING_OSC_ENABLE_MASK | RING_OSC_COUNT_RESET | RING_OSC_IRQ; + GPIO->RingOscCtrl1 = RING_OSC_ENABLE_MASK | (2 << RING_OSC_SELECT_SHIFT); + GPIO->RingOscCtrl0 = RING_OSC_512_CYCLES; + while (!(GPIO->RingOscCtrl1 & RING_OSC_IRQ)); + startCount = GPIO->RingOscCtrl1 & RING_OSC_COUNT_MASK; + + /* Increase internal 1V2 slightly and see if the ring osc is speeding up */ + MISC->miscVregCtrl1 += 8; + MISC->miscVregCtrl0 |= MISC_VREG_CONTROL0_REG_RESET_B; + + /* Take the second ring osc measurement */ + GPIO->RingOscCtrl1 = RING_OSC_ENABLE_MASK | RING_OSC_COUNT_RESET | RING_OSC_IRQ; + GPIO->RingOscCtrl1 = RING_OSC_ENABLE_MASK | (2 << RING_OSC_SELECT_SHIFT); + GPIO->RingOscCtrl0 = RING_OSC_512_CYCLES; + while (!(GPIO->RingOscCtrl1 & RING_OSC_IRQ)); + endCount = GPIO->RingOscCtrl1 & RING_OSC_COUNT_MASK; + + /* Reset the internal 1V2 to its original value */ + MISC->miscVregCtrl1 -= 8; + + /* + * A negative difference or a small positive difference indicates that an external regulator is used + * This code was calibrated by repeating the measurements thousands of times and looking for a safe value + * Safe means avoiding at all costs being wrong by shutting down the internal regulator when it is in use + * It is better to be wrong by leaving the internal regulator running when an external regulator is used + */ + diff = startCount - endCount; + if (diff < 300) { + printk("Unused, turn it off (%08x-%08x=%d<300)\n", startCount, endCount,diff); + /* Turn off internal 1P2 regulator */ + MISC->miscVregCtrl0 |= MISC_VREG_CONTROL0_REG_RESET_B | MISC_VREG_CONTROL0_POWER_DOWN_1; + } else { + printk("Used, leave it on (%08x-%08x=%d>=300)\n", startCount, endCount, diff); + } +#elif defined(CONFIG_BCM_1V2REG_ALWAYS_SHUTDOWN) + printk("Internal 1P2 VREG is forced to be shutdown\n"); + MISC->miscVregCtrl0 |= MISC_VREG_CONTROL0_REG_RESET_B | MISC_VREG_CONTROL0_POWER_DOWN_1; +#elif defined(CONFIG_BCM_1V2REG_NEVER_SHUTDOWN) + printk("Internal 1P2 VREG is forced to remain enabled\n"); +#endif + + if ( BpGetVreg1P8(&vreg1p8) == BP_SUCCESS ) { + if (vreg1p8 == BP_VREG_EXTERNAL) { + printk("Internal 1P8 VREG is forced by boardparms to be shutdown\n"); + MISC->miscVregCtrl0 |= MISC_VREG_CONTROL0_REG_RESET_B | MISC_VREG_CONTROL0_POWER_DOWN_2; + } + } + + if ( BpGetFemtoResetGpio(&gpio) == BP_SUCCESS ) { + kerSysSetGpioState(gpio, kGpioActive); + } + return 0; +} + +#define bcm63xx_specific_hw_init() bcm63268_hw_init() + +#elif defined(CONFIG_BCM960333) + +static int __init bcm60333_hw_init(void) +{ + /* We can add a minimum GPIO MUX setup here to enable UART TxRx*/ + return 0; +} + +#define bcm63xx_specific_hw_init() bcm60333_hw_init() + +#elif defined(CONFIG_BCM96838) + +static int __init bcm6838_hw_init(void) +{ + unsigned short irq, gpio; + + if( BpGetResetToDefaultExtIntr(&irq) == BP_SUCCESS ) + { + if(BpGetResetToDefaultExtIntrGpio(&gpio) == BP_SUCCESS) + { + int gpio_polarity = gpio & BP_ACTIVE_MASK; + gpio &= BP_GPIO_NUM_MASK; + PERF->ext_irq_muxsel0 |= ( (gpio&EXT_IRQ_MASK_LOW) << (irq*EXT_IRQ_OFF_LOW) ); + DBGPERF->Dbg_extirqmuxsel0_1 |= ( ((gpio&EXT_IRQ_MASK_HIGH)>>EXT_IRQ_OFF_LOW) << (irq*EXT_IRQ_OFF_HIGH) ); + if (gpio_polarity == BP_ACTIVE_HIGH) + PERF->ExtIrqCfg |= (1<ext_irq_muxsel0 |= ( (gpio&EXT_IRQ_MASK_LOW) << (irq*EXT_IRQ_OFF_LOW) ); + DBGPERF->Dbg_extirqmuxsel0_1 |= ( ((gpio&EXT_IRQ_MASK_HIGH)>>EXT_IRQ_OFF_LOW) << (irq*EXT_IRQ_OFF_HIGH) ); + if (gpio_polarity == BP_ACTIVE_HIGH) + PERF->ExtIrqCfg |= (1<ext_irq_muxsel0 |= ( (gpio&EXT_IRQ_MASK_LOW) << (irq*EXT_IRQ_OFF_LOW) ); + DBGPERF->Dbg_extirqmuxsel0_1 |= ( ((gpio&EXT_IRQ_MASK_HIGH)>>EXT_IRQ_OFF_LOW) << (irq*EXT_IRQ_OFF_HIGH) ); + if (gpio_polarity == BP_ACTIVE_HIGH) + { + PERF->ExtIrqCfg |= (1<ExtIrqCfg |= (1<<26); + } + } + } + if( BpGetWanSignalDetectedExtIntr(&irq) == BP_SUCCESS ) + { + if(BpGetWanSignalDetectedExtIntrGpio(&gpio) == BP_SUCCESS) + { + gpio &= BP_GPIO_NUM_MASK; + PERF->ext_irq_muxsel0 |= ( (gpio&EXT_IRQ_MASK_LOW) << (irq*EXT_IRQ_OFF_LOW) ); + DBGPERF->Dbg_extirqmuxsel0_1 |= ( ((gpio&EXT_IRQ_MASK_HIGH)>>EXT_IRQ_OFF_LOW) << (irq*EXT_IRQ_OFF_HIGH) ); + } + } + + if( BpGetTrplxrTxFailExtIntr(&irq) == BP_SUCCESS ) + { + if(BpGetTrplxrTxFailExtIntrGpio(&gpio) == BP_SUCCESS) + { + int gpio_polarity = gpio & BP_ACTIVE_MASK; + gpio &= BP_GPIO_NUM_MASK; + PERF->ext_irq_muxsel0 |= ( (gpio&EXT_IRQ_MASK_LOW) << (irq*EXT_IRQ_OFF_LOW) ); + DBGPERF->Dbg_extirqmuxsel0_1 |= ( ((gpio&EXT_IRQ_MASK_HIGH)>>EXT_IRQ_OFF_LOW) << (irq*EXT_IRQ_OFF_HIGH) ); + if (gpio_polarity == BP_ACTIVE_HIGH) + { + PERF->ExtIrqCfg |= (1<ext_irq_muxsel0 |= ( (gpio&EXT_IRQ_MASK_LOW) << (irq*EXT_IRQ_OFF_LOW) ); + DBGPERF->Dbg_extirqmuxsel0_1 |= ( ((gpio&EXT_IRQ_MASK_HIGH)>>EXT_IRQ_OFF_LOW) << (irq*EXT_IRQ_OFF_HIGH) ); + if (gpio_polarity == BP_ACTIVE_HIGH) + { + PERF->ExtIrqCfg |= (1<ext_irq_muxsel0 |= ( (gpio&EXT_IRQ_MASK_LOW) << (irq*EXT_IRQ_OFF_LOW) ); + DBGPERF->Dbg_extirqmuxsel0_1 |= ( ((gpio&EXT_IRQ_MASK_HIGH)>>EXT_IRQ_OFF_LOW) << (irq*EXT_IRQ_OFF_HIGH) ); + if (gpio_polarity == BP_ACTIVE_HIGH) + { + PERF->ExtIrqCfg |= (1<ext_irq_muxsel0 |= ( (gpio&EXT_IRQ_MASK_LOW) << (irq*EXT_IRQ_OFF_LOW) ); + DBGPERF->Dbg_extirqmuxsel0_1 |= ( ((gpio&EXT_IRQ_MASK_HIGH)>>EXT_IRQ_OFF_LOW) << (irq*EXT_IRQ_OFF_HIGH) ); + if (gpio_polarity == BP_ACTIVE_HIGH) + { + PERF->ExtIrqCfg |= (1<port_block_data1 = 0x0; + if(kerSysGetUsbHostPortEnable(0)) + { + GPIO->port_block_data2 = 0x1045; /*USB0_PWRFLT */ + GPIO->port_command = 0x21; + GPIO->port_block_data2 = 0x1046; /*USB0_PWRON */ + GPIO->port_command = 0x21; + } + if(kerSysGetUsbHostPortEnable(1)) + { + GPIO->port_block_data2 = 0x0047; /*USB1_PWRFLT */ + GPIO->port_command = 0x21; + GPIO->port_block_data2 = 0x0048; /*USB1_PWRON */ + GPIO->port_command = 0x21; + } + mdelay(100); + USBH->SwapControl = EHCI_ENDIAN_SWAP | OHCI_ENDIAN_SWAP; + USBH->Setup |= USB_IOC; + USBH->Setup &= ~USB_IPP; + USBH->PllControl1 &= ~(PLL_IDDQ_PWRDN); + } + else + { /* no USB HOST */ + /* + * Power to USB Host controller is on by default, + * shutdown power to USB Host controller + */ + kerSysSetUsbPower(0, USB_HOST_FUNC); + } + + if(!kerSysGetUsbDeviceEnable()) + { + /* USB device not supported shutdown power to USB device */ + kerSysSetUsbPower(0, USB_DEVICE_FUNC); + } +#endif + return 0; +} + +#define bcm63xx_specific_hw_init() bcm6838_hw_init() + +#elif defined(CONFIG_BCM963381) + +#if defined(CONFIG_USB) + +#define CAP_TYPE_EHCI 0x00 +#define CAP_TYPE_OHCI 0x01 +#define CAP_TYPE_XHCI 0x02 + + +static void bcm63381_manual_usb_ldo_start(void) +{ + USBH_CTRL->pll_ctl &= ~(1 << 30); /*pll_resetb=0*/ + USBH_CTRL->utmi_ctl_1 = 0; + USBH_CTRL->pll_ldo_ctl = 4; /*ldo_ctl=core_rdy */ + USBH_CTRL->pll_ctl |= ( 1 << 31); /*pll_iddq=1*/ + mdelay(10); + USBH_CTRL->pll_ctl &= ~( 1 << 31); /*pll_iddq=0*/ + USBH_CTRL->pll_ldo_ctl |= 1; /*ldo_ctl.AFE_LDO_PWRDWNB=1*/ + USBH_CTRL->pll_ldo_ctl |= 2; /*ldo_ctl.AFE_BG_PWRDWNB=1*/ + mdelay(1); + USBH_CTRL->utmi_ctl_1 = 0x00020002;/* utmi_resetb &ref_clk_sel=0; */ + USBH_CTRL->pll_ctl |= ( 1 << 30); /*pll_resetb=1*/ + mdelay(10); +} + +#define MDIO_USB2 0 +#define MDIO_USB3 (1 << 31) +static void usb_mdio_write(volatile u32 *mdio, u32 reg, u32 val, int mode) +{ + uint32_t data; + data = (reg << 16) | val | mode; + *mdio = data; + data |= (1 << 25); + *mdio = data; + mdelay(1); + data &= ~(1 << 25); + *mdio = data; +} + +static void usb2_eye_fix(void) +{ + /* Updating USB 2.0 PHY registers */ + usb_mdio_write((void *)&USBH_CTRL->mdio, 0x1f, 0x80a0, MDIO_USB2); + usb_mdio_write((void *)&USBH_CTRL->mdio, 0x0a, 0xc6a0, MDIO_USB2); +} + +#if IS_ENABLED(CONFIG_USB_XHCI_PLATFORM) +static struct platform_device *xhci_dev; + +static void usb3_pll_fix(void) +{ + /* Updating USB 3.0 PHY registers */ + usb_mdio_write((void *)&USBH_CTRL->mdio, 0x1f, 0x8000, MDIO_USB3); + usb_mdio_write((void *)&USBH_CTRL->mdio, 0x07, 0x1503, MDIO_USB3); +} + + +static __init struct platform_device *bcm_add_usb_host(int type, int id, + uint32_t mem_base, uint32_t mem_size, int irq, + const char *devname, void *private_data) +{ + struct resource res[2]; + struct platform_device *pdev; + //static const u64 usb_dmamask = ~(u32)0; + static const u64 usb_dmamask = 0xffffffff; + + memset(&res, 0, sizeof(res)); + res[0].start = mem_base; + res[0].end = mem_base + (mem_size -1); + res[0].flags = IORESOURCE_MEM; + + res[1].flags = IORESOURCE_IRQ; + res[1].start = res[1].end = irq; + + pdev = platform_device_alloc(devname, id); + if(!pdev) + { + printk("Error Failed to allocate platform device for devname=%s id=%d\n", + devname, id); + } + + platform_device_add_resources(pdev, res, 2); + + pdev->dev.dma_mask = (u64 *)&usb_dmamask; + pdev->dev.coherent_dma_mask = 0xffffffff; + + if(private_data) + { + pdev->dev.platform_data = private_data; + } + + if( platform_device_add(pdev)) + { + printk(KERN_ERR "Error Failed to add platform device for devname=%s id=%d\n", + devname, id); + } + + return pdev; +} + +static void bcm63381_usb30_init(void) +{ + + /*initialize XHCI settings*/ + //USB30H_CTRL->setup |= (USBH_IPP); + + USB30H_CTRL->usb30_ctl2 = 0x1; /*swap data & control */ + + USB30H_CTRL->usb30_ctl1 |= (1<<30); /*disable over current*/ + USB30H_CTRL->usb30_ctl1 |= USB3_IOC; + //USB30H_CTRL->usb30_ctl1 |= USB3_IPP; + USB30H_CTRL->usb30_ctl1 |= XHC_SOFT_RESETB; + USB30H_CTRL->usb30_ctl1 |= PHY3_PLL_SEQ_START; + + /* work around to avoid USB3.0 issue of contoller being reset when UBUS is loaded */ + USB30H_CTRL->bridge_ctl = (USBH_CTRL->bridge_ctl & 0xFFFF00FF) | (0x1000); + + usb3_pll_fix(); + + xhci_dev = bcm_add_usb_host(CAP_TYPE_XHCI, 0, USB_XHCI_BASE, 0x1000, + INTERRUPT_ID_USBH30, "xhci-hcd", NULL); +} +#endif/*IS_ENABLED(CONFIG_USB_XHCI_PLATFORM)*/ + +#endif + +static int __init bcm63381_hw_init(void) +{ +#if defined(CONFIG_USB) + short usb_gpio; + unsigned int chipRev = UtilGetChipRev(); + if(pmc_usb_power_up(PMC_USB_HOST_20)) + { + printk(KERN_ERR "+++ Failed to Power Up USB20 Host\n"); + return -1; + } + + bcm63381_manual_usb_ldo_start(); + + USBH_CTRL->setup |= (USBH_IOC); + if(BpGetUsbPwrFlt0(&usb_gpio) == BP_SUCCESS) + { + if((usb_gpio & BP_ACTIVE_MASK) != BP_ACTIVE_LOW) + { + USBH_CTRL->setup &= ~(USBH_IOC); + } + } + if(BpGetUsbPwrOn0(&usb_gpio) == BP_SUCCESS) + { + if((usb_gpio & BP_ACTIVE_MASK) != BP_ACTIVE_LOW) + { + USBH_CTRL->setup &= ~(USBH_IPP); + } + else + { + USBH_CTRL->setup |= (USBH_IPP); + } + } + + if ((chipRev & 0xf0) == 0xa0) + { + USBH_CTRL->bridge_ctl |= (EHCI_ENDIAN_SWAP | OHCI_ENDIAN_SWAP); + } else { + USBH_CTRL->bridge_ctl = (USBH_CTRL->bridge_ctl & ~EHCI_SWAP_MODE_MASK & ~OHCI_SWAP_MODE_MASK) + | ((EHCI_SWAP_MODE_BOTH << EHCI_SWAP_MODE_SHIFT) | (OHCI_SWAP_MODE_BOTH << OHCI_SWAP_MODE_SHIFT)); + } + + usb2_eye_fix(); +#if IS_ENABLED(CONFIG_USB_XHCI_PLATFORM) + if(kerSysGetUsb30HostEnable()) + { + if(pmc_usb_power_up(PMC_USB_HOST_30)) + { + printk(KERN_ERR "+++ Failed to Power Up USB30 Host\n"); + return -1; + } + mdelay(10); + bcm63381_usb30_init(); + } +#endif/*IS_ENABLED(CONFIG_USB_XHCI_PLATFORM)*/ + +#endif + return 0; +} + +#define bcm63xx_specific_hw_init() bcm63381_hw_init() + +#elif defined(CONFIG_BCM96848) + +static int __init bcm6848_hw_init(void) +{ +#if defined(CONFIG_USB) + short usb_gpio; + + if (!kerSysGetUsbHostPortEnable(0)) + return 0; + + if(pmc_usb_power_up(PMC_USB_HOST_20)) + { + printk(KERN_ERR "+++ Failed to Power Up USB20 Host\n"); + return -1; + } + + USBH_CTRL->setup |= (USBH_IOC); + if(BpGetUsbPwrFlt0(&usb_gpio) == BP_SUCCESS) + { + if((usb_gpio & BP_ACTIVE_MASK) != BP_ACTIVE_LOW) + { + USBH_CTRL->setup &= ~(USBH_IOC); + } + } + if(BpGetUsbPwrOn0(&usb_gpio) == BP_SUCCESS) + { + if((usb_gpio & BP_ACTIVE_MASK) != BP_ACTIVE_LOW) + { + USBH_CTRL->setup &= ~(USBH_IPP); + } + else + { + USBH_CTRL->setup |= (USBH_IPP); + } + } + + USBH_CTRL->bridge_ctl = (USBH_CTRL->bridge_ctl & ~EHCI_SWAP_MODE_MASK & ~OHCI_SWAP_MODE_MASK) + | ((EHCI_SWAP_MODE_BOTH << EHCI_SWAP_MODE_SHIFT) | (OHCI_SWAP_MODE_BOTH << OHCI_SWAP_MODE_SHIFT)); +#endif + return 0; +} + +#define bcm63xx_specific_hw_init() bcm6848_hw_init() + +#endif + +static int __init bcm63xx_hw_init(void) +{ + int ret; + +#if !defined(CONFIG_BRCM_IKOS) + kerSysFlashInit(); +#endif + + ret = bcm63xx_specific_hw_init(); + + /* Setup external irqs */ + bcm_extirq_init(); +#if defined(CONFIG_BCM96848) || defined(CONFIG_BCM963381) + bcm_init_pinmux(); +#endif + return (ret); +} +early_initcall(bcm63xx_hw_init); + + +static int __init brcm63xx_setup(void) +{ + extern int panic_timeout; + + _machine_restart = brcm_machine_restart; + _machine_halt = brcm_machine_halt; + pm_power_off = brcm_machine_halt; + + panic_timeout = 1; + + check_if_rootfs_is_set(arcs_cmdline); + +#ifdef CONFIG_OF + __dt_register_buses("simple-bus", NULL); +#endif + return 0; +} + +arch_initcall(brcm63xx_setup); + +#endif diff -urN linux-4.1.52-orig/arch/mips/bcm963xx/smp-brcm.c linux-4.1.52/arch/mips/bcm963xx/smp-brcm.c --- linux-4.1.52-orig/arch/mips/bcm963xx/smp-brcm.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/mips/bcm963xx/smp-brcm.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,321 @@ +#if defined(CONFIG_BCM_KF_MIPS_BCM963XX) && defined(CONFIG_MIPS_BCM963XX) +/*********************************************************** + * + * Copyright (c) 2009 Broadcom Corporation + * All Rights Reserved + * + * <:label-BRCM:2011:DUAL/GPL:standard + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed + * to you under the terms of the GNU General Public License version 2 + * (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, + * with the following added to such license: + * + * As a special exception, the copyright holders of this software give + * you permission to link this software with independent modules, and + * to copy and distribute the resulting executable under terms of your + * choice, provided that you also meet, for each linked independent + * module, the terms and conditions of the license of that module. + * An independent module is a module which is not derived from this + * software. The special exception does not apply to any modifications + * of the software. + * + * Not withstanding the above, under no circumstances may you combine + * this software in any way with any other Broadcom software provided + * under a license other than the GPL, without Broadcom's express prior + * written consent. + * + * :> + * + ************************************************************/ + +/*********************************************************** + * + * SMP support for Broadcom 63xx and 68xx chips + * + * 05/2009 Created by Xi Wang + * + ************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +static int cpu_ipi_irq; +DEFINE_PER_CPU(unsigned int, ipi_pending); +DEFINE_PER_CPU(unsigned int, ipi_flags); + +extern spinlock_t brcm_irqlock; + +// boot parameters +struct BootConfig { + unsigned int func_addr; + unsigned int gp; + unsigned int sp; +}; + +static struct BootConfig boot_config; + +void install_smp_boot_ex_handler(void); +static void core_send_ipi_single(int cpu, unsigned int action); +static void core_send_ipi_mask(const struct cpumask *mask, unsigned int action); + + +void install_smp_boot_ex_handler(void) +{ + + asm ( + ".set push\n" + ".set noreorder\n" + "lui $8, 0xa000 \n" + "ori $8, $8, 0x0200 # alternative mips exception vector\n" + "la $9, 2f\n" + "la $10, 3f\n" + "1:\n" + "lw $11, 0($9)\n" + "sw $11, 0($8)\n" + "addiu $9, $9, 4\n" + "bne $9, $10, 1b\n" + "addiu $8, $8, 4\n" + "b 3f\n" + "nop\n" + "2: # begin exception handler code\n" + "mfc0 $26, $13, 0\n" + "li $27, 0x800100 # change back to normal exception vector & ack interrupt\n" + "xor $26, $27\n" + "mtc0 $26, $13, 0\n" + "la $27, %0 # pointer to boot_config structure\n" + "lw $24, 0($27) # func_addr - will be loaded into EPC before eret\n" + "lw $28, 4($27) # gp\n" + "lw $29, 8($27) # sp\n" + "mtc0 $24, $14 # load return address into EPC\n" + "eret\n" + "3:\n" + ".set pop\n" + : + : "X" (&boot_config) + ); +} + +#ifdef CONFIG_BCM_HOSTMIPS_PWRSAVE_TIMERS +extern void bcm_timer_interrupt_handler_TP1(void); +#endif + +static irqreturn_t ipi_interrupt(int irq, void *dev_id) +{ + unsigned int old_ipi_flags; + + spin_lock(&brcm_irqlock); + old_ipi_flags = *this_cpu_ptr(&ipi_flags); + *this_cpu_ptr(&ipi_flags) = 0; + spin_unlock(&brcm_irqlock); + +#ifdef CONFIG_BCM_HOSTMIPS_PWRSAVE_TIMERS + /* Process TIMER related interrupt first */ + if (old_ipi_flags & 1<<2) { + bcm_timer_interrupt_handler_TP1(); + } +#endif + + if (old_ipi_flags & 1<<0) { + scheduler_ipi(); + } + + if (old_ipi_flags & 1<<1) { + smp_call_function_interrupt(); + } + + return IRQ_HANDLED; +} + + +static struct irqaction irq_ipi = { + .handler = ipi_interrupt, + .flags = 0x00|IRQF_PERCPU, + .name = "IPI" +}; + + +static void __init brcm_smp_setup(void) +{ + int i; + + init_cpu_possible(cpu_possible_mask); + + for (i = 0; i < 2; i++) { + set_cpu_possible(i, true); + __cpu_number_map[i] = i; + __cpu_logical_map[i] = i; + } +} + + +static void __init brcm_prepare_cpus(unsigned int max_cpus) +{ + unsigned int c0tmp; + int cpu; + + c0tmp = __read_32bit_c0_register($22, 0); + c0tmp |= CP0_BCM_CFG_NBK; /* non blocking */ + __write_32bit_c0_register($22, 0, c0tmp); + + c0tmp = __read_32bit_c0_register($22, 2); + c0tmp &= ~(CP0_CMT_PRIO_TP0 | CP0_CMT_PRIO_TP1); /* equal (random) D-cache priority */ + __write_32bit_c0_register($22, 2, c0tmp); + + //printk("bcm config0 %08x\n", __read_32bit_c0_register($22, 0)); + //printk("cmt control %08x\n", __read_32bit_c0_register($22, 2)); + //printk("cmt local %08x\n", __read_32bit_c0_register($22, 3)); + //printk("bcm config1 %08x\n", __read_32bit_c0_register($22, 5)); + + for_each_possible_cpu(cpu) { + per_cpu(ipi_pending, cpu) = 0; + per_cpu(ipi_flags, cpu) = 0; + } + + c0tmp = __read_32bit_c0_register($22, 1); + c0tmp |= CP0_CMT_SIR_0; + __write_32bit_c0_register($22, 1, c0tmp); + + cpu_ipi_irq = INTERRUPT_ID_SOFTWARE_0; + setup_irq(cpu_ipi_irq, &irq_ipi); + irq_set_handler(cpu_ipi_irq, handle_percpu_irq); +} + + +// Pass PC, SP, and GP to a secondary core and start it up by sending an inter-core interrupt +static void __cpuinit brcm_boot_secondary(int cpu, struct task_struct *idle) +{ + unsigned int cause; + + boot_config.func_addr = (unsigned long) smp_bootstrap; + boot_config.sp = (unsigned int) __KSTK_TOS(idle); + boot_config.gp = (unsigned int) task_thread_info(idle); + + install_smp_boot_ex_handler(); + + cause = read_c0_cause(); + cause |= CAUSEF_IP0; + write_c0_cause(cause); +} + + +static void __cpuinit brcm_init_secondary(void) +{ + //printk("bcm config0 %08x\n", __read_32bit_c0_register($22, 0)); + //printk("cmt control %08x\n", __read_32bit_c0_register($22, 2)); + //printk("cmt local %08x\n", __read_32bit_c0_register($22, 3)); + //printk("bcm config1 %08x\n", __read_32bit_c0_register($22, 5)); + + clear_c0_status(ST0_BEV); + +#if defined(CONFIG_BCM96838) + +#ifdef CONFIG_BCM_HOSTMIPS_PWRSAVE_TIMERS + // CP0 timer interrupt (IRQ5) is not used for TP1 when pwrsave is enabled + change_c0_status(ST0_IM, IE_SW0 | IE_IRQ1 | IE_IRQ2 /*| IE_IRQ5*/); +#else + change_c0_status(ST0_IM, IE_SW0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ5); +#endif + +#else + +#ifdef CONFIG_BCM_HOSTMIPS_PWRSAVE_TIMERS + // CP0 timer interrupt (IRQ5) is not used for TP1 when pwrsave is enabled + change_c0_status(ST0_IM, IE_SW0 | IE_IRQ0 | IE_IRQ1 /*| IE_IRQ5*/); +#else + change_c0_status(ST0_IM, IE_SW0 | IE_IRQ0 | IE_IRQ1 | IE_IRQ5); +#endif + +#endif +} + + +static void __cpuinit brcm_smp_finish(void) +{ +} + + + +// send inter-core interrupts +static void core_send_ipi_single(int cpu, unsigned int action) +{ + unsigned long flags; + unsigned int cause; + + // printk("== from_cpu %d to_cpu %d action %u\n", smp_processor_id(), cpu, action); + + spin_lock_irqsave(&brcm_irqlock, flags); + + switch (action) { + case SMP_RESCHEDULE_YOURSELF: + per_cpu(ipi_pending, cpu) = 1; + per_cpu(ipi_flags, cpu) |= 1<<0; + break; + case SMP_CALL_FUNCTION: + per_cpu(ipi_pending, cpu) = 1; + per_cpu(ipi_flags, cpu) |= 1<<1; + break; +#if defined(CONFIG_BCM_HOSTMIPS_PWRSAVE_TIMERS) + case SMP_BCM_PWRSAVE_TIMER: + per_cpu(ipi_pending, cpu) = 1; + per_cpu(ipi_flags, cpu) |= 1<<2; + break; +#endif + default: + goto errexit; + } + + mb(); + + cause = read_c0_cause(); + cause |= CAUSEF_IP0; + write_c0_cause(cause); + +errexit: + spin_unlock_irqrestore(&brcm_irqlock, flags); +} + + +static void core_send_ipi_mask(const struct cpumask *mask, unsigned int action) +{ + unsigned int cpu; + + for_each_cpu(cpu, mask) { + core_send_ipi_single(cpu, action); + } +} + + +struct plat_smp_ops brcm_smp_ops = { + .send_ipi_single = core_send_ipi_single, + .send_ipi_mask = core_send_ipi_mask, + .init_secondary = brcm_init_secondary, + .smp_finish = brcm_smp_finish, + .boot_secondary = brcm_boot_secondary, + .smp_setup = brcm_smp_setup, + .prepare_cpus = brcm_prepare_cpus +}; + +#endif // defined(CONFIG_BCM_KF_MIPS_BCM963XX) diff -urN linux-4.1.52-orig/arch/mips/include/asm/checksum.h linux-4.1.52/arch/mips/include/asm/checksum.h --- linux-4.1.52-orig/arch/mips/include/asm/checksum.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/mips/include/asm/checksum.h 2020-02-07 01:47:54.000000000 +0800 @@ -128,6 +128,64 @@ * By Jorge Cwik , adapted for linux by * Arnt Gulbrandsen. */ + +#if defined(CONFIG_BCM_KF_CSUM_UNALIGNED) + +/* Brcm version can handle unaligned data. Merged from brcm 2.6.8 kernel.*/ +static inline __sum16 ip_fast_csum(const void *iph, unsigned int ihl) +{ + if (((__u32)iph&0x3) == 0) { + unsigned int *word = (unsigned int *) iph; + unsigned int *stop = word + ihl; + unsigned int csum; + int carry; + + csum = word[0]; + csum += word[1]; + carry = (csum < word[1]); + csum += carry; + + csum += word[2]; + carry = (csum < word[2]); + csum += carry; + + csum += word[3]; + carry = (csum < word[3]); + csum += carry; + + word += 4; + do { + csum += *word; + carry = (csum < *word); + csum += carry; + word++; + } while ((unsigned int) word < (unsigned int) stop); + + return csum_fold(csum); + } else { + __u16 * buff = (__u16 *) iph; + __u32 sum=0; + __u16 i; + + // make 16 bit words out of every two adjacent 8 bit words in the packet + // and add them up + for (i=0;i>16) + sum = (sum & 0xFFFF)+(sum >> 16); + + // one's complement the result + sum = ~sum; + + return ((__sum16) sum); + } +} + +#else + static inline __sum16 ip_fast_csum(const void *iph, unsigned int ihl) { const unsigned int *word = iph; @@ -160,6 +218,8 @@ } #define ip_fast_csum ip_fast_csum +#endif + static inline __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr, unsigned short len, unsigned short proto, __wsum sum) diff -urN linux-4.1.52-orig/arch/mips/include/asm/cpu-info.h linux-4.1.52/arch/mips/include/asm/cpu-info.h --- linux-4.1.52-orig/arch/mips/include/asm/cpu-info.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/mips/include/asm/cpu-info.h 2020-02-07 01:47:54.000000000 +0800 @@ -94,7 +94,11 @@ } __attribute__((aligned(SMP_CACHE_BYTES))); extern struct cpuinfo_mips cpu_data[]; +#if defined(CONFIG_BCM_KF_CPU_DATA_CPUID) +#define current_cpu_data cpu_data[raw_smp_processor_id()] +#else #define current_cpu_data cpu_data[smp_processor_id()] +#endif #define raw_current_cpu_data cpu_data[raw_smp_processor_id()] #define boot_cpu_data cpu_data[0] diff -urN linux-4.1.52-orig/arch/mips/include/asm/mach-bcm963xx/burstbank.h linux-4.1.52/arch/mips/include/asm/mach-bcm963xx/burstbank.h --- linux-4.1.52-orig/arch/mips/include/asm/mach-bcm963xx/burstbank.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/mips/include/asm/mach-bcm963xx/burstbank.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,927 @@ +#ifndef __BURSTBANK_H_INCLUDED__ +#define __BURSTBANK_H_INCLUDED__ +/* +<:copyright-BRCM:2015:DUAL/GPL:standard + + Copyright (c) 2015 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + +/* + * BMIPS 4355 Burstbank API + * $Copyright Open Broadcom Corporation$ + * $Id$ + * + * + */ + +/* Burstbanks compilation: define BMIPS4355_DMAE */ +#if defined(CONFIG_BCM96838) || defined (CONFIG_BCM96848) +#define BMIPS4355_DMAE /* DSL SoC that uses the BMIPS4355 with DMA-E */ +#endif /* CONFIG_BCM96838 || CONFIG_BCM96848 */ + +#if defined(BMIPS4355_DMAE) +/* + * ----------------------------------------------------------------------------* + * + * File Name : burstbank.h + * + * Provides APIs for Burst Bank manipulation in the BMIPS4355 DMA-Engine + * - Read and Write transactions + * - Copy max 16B and transfer large blocks of data + * - Reservation of burst bank channels + * - Debug, statistics and audit support + * + * History: + * 1.0: Initial version. + * (Note): Derived for use in Host to Device mailbox transactions over + * PCIe between Host 802.3 driver and Device 802.11 driver. + * + * ----------------------------------------------------------------------------- + */ + +/* + * ============================================================================= + * Section: Conditional Compiles for LAB/Design development + * ============================================================================= + * Designer Note: Modifications to Burst Bank must be regressed for performance + * and debug/statistics/audit builds. + * ============================================================================= + */ + +#define CC_BB_FNINL /* Enable burstbank builds with inlined APIs */ +#define CC_BB_COLOR /* Enable build with color coded debug/audit output */ +#define CC_BB_STATS /* Enable build with statistics collection */ +#define CC_BB_DEBUG 5 /* Enable build with debugging at a specific level */ +#define CC_BB_AUDIT /* Enable build with state audit */ +#define CC_BB_BENCH /* Enable build with pmon benchmarking (pmon_enable) */ +#if 0 +#define CC_BB_UNITT /* Enable build with Unit Test section */ +#endif + +/* + * ============================================================================= + * Section: Includes and Defines + * ============================================================================= + */ +#include /* atomic_t */ +#include /* printk, CPHYSADDR() */ + +struct bb; /* A hardware Burst Bank channel */ + +#define BB_VERSION "1.0" +#define BB_VER_STR "v" BB_VERSION + +#define BB_SUCCESS 0 +#define BB_ERROR (~BB_SUCCESS) +#define BB_NULL ((struct bb *)NULL) + +#define BB_NULL_STMT do { /* noop */ } while (0) + +/* Logical to physical address conversion */ +#define BB_LOG2PHY(addr) ((uint32_t *)CPHYSADDR(addr)) + +#define BB_PRINT printk + +/* + * Align an address/len down or up to the next specified alignment boundary + * E.g. + * unsigned long d = BB_ROUND_DN( (unsigned long)addr, L1_CACHE_BYTES ); + * unsigned long u = BB_ROUND_UP( (unsigned long)addr, L1_CACHE_BYTES ); + * + */ +#define BB_ROUND_DN(p, align) ((p) & ~((align) - 1)) +#define BB_ROUND_UP(p, align) (((p) + (align) - 1) & ~((align) - 1)) + +#define BB_U16(bytes) ((bytes) / sizeof(uint16_t)) +#define BB_U32(bytes) ((bytes) / sizeof(uint32_t)) + + +/* + * ============================================================================= + * Section: INLINE Macros + * ============================================================================= + * + * API inline or callable version definitions, example: + * + * static inline void _bb_foo(void) + * { ... do foo statements ... } + * BB_DECLARE_FN( bb_foo, void bb_foo(void), _bb_foo() ) + * + * ============================================================================= + */ +#if defined(CC_BB_FNINL) +# define BB_DECLARE_FN(fn_name, fn_signature, fn_body) \ + static inline fn_signature { fn_body; } /* APIs inlined */ +#else /* ! CC_BB_FNINL */ +# if defined(BB_IMPLEMENTATION) /* pragma in burst bank.c */ +# define BB_DECLARE_FN(fn_name, fn_signature, fn_body) \ + fn_signature { fn_body; } \ + EXPORT_SYMBOL(fn_name); /* APIs declared in burstbank.c */ +# else /* ! BB_IMPLEMENTATION */ +# define BB_DECLARE_FN(fn_name, fn_signature, fn_body) \ + extern fn_signature; /* APIs exported to others */ +# endif /* ! BB_IMPLEMENTATION */ +#endif /* ! CC_BB_FNINL */ + +/* + * ============================================================================= + * Section: COLOR Macros + * ============================================================================= + */ +#if defined(CC_BB_COLOR) +# define BB_COLOR(clr_code) clr_code +#else /* ! CC_BB_COLOR */ +# define BB_COLOR(clr_code) +#endif /* ! CC_BB_COLOR */ + +#define _BBh_ BB_COLOR("\e[0;36;44m") /* Highlight color */ +#define _BBr_ BB_COLOR("\e[0;31m") /* Error color */ +#define _BBg_ BB_COLOR("\e[0;32m") /* Debug color */ +#define _BBn_ BB_COLOR("\e[0m") /* Reset color */ +#define _BBb_ BB_COLOR("\e[0;34m") /* Bold color */ +#define _BBnl_ _BBn_ "\n" /* Reset with newline */ + +/* + * ============================================================================= + * Section: STATISTICS Collection Macros + * ============================================================================= + */ +#if defined(CC_BB_STATS) +# define BB_STATS(code) code +#else +# define BB_STATS(code) BB_NULL_STMT +#endif /* ! CC_BB_STATS */ + +/* + * ============================================================================= + * Section: DEBUG Macros + * BB_DBGL: Enables printk when the burst bank global debug level is greater + * or equal to the requested debug level. + * BB_DBG : Embed debug code segment when burst bank debug builds enabled. + * ============================================================================= + */ +#if defined(CC_BB_DEBUG) +# define BB_DBG(code) code +# define BB_DBGL(lvl, fmt, arg...) \ + if (bb_g.debug >= lvl) \ + BB_PRINT(_BBg_ "BB %pS: " _BBn_ fmt "\n", (void*)_THIS_IP_, ##arg) +#else /* ! CC_BB_DEBUG */ +# define BB_DBG(code) BB_NULL_STMT +# define BB_DBGL(lvl, fmt, arg...) BB_NULL_STMT +#endif /* ! CC_BB_DEBUG */ + +/* + * ============================================================================= + * Section: AUDIT and ASSERT macros + * ============================================================================= + */ +#if defined(CC_BB_AUDIT) +# define BB_AUDIT(code) code +#else /* ! CC_BB_AUDIT */ +# define BB_AUDIT(code) BB_NULL_STMT +#endif /* ! CC_BB_AUDIT */ + +#define BB_ASSERTV(cond) \ + BB_AUDIT(if (!cond) { \ + BB_PRINT(_BBr_ "BB %s ASSERT :" #cond _BBnl_, \ + __FUNCTION__); \ + return; \ + }) + +#define BB_ASSERTR(cond, rtn) \ + BB_AUDIT(if (!cond) { \ + BB_PRINT(_BBr_ "BB %s ASSERT :" #cond _BBnl_, \ + __FUNCTION__); \ + return rtn; \ + }) + +/* + * ============================================================================= + * Section: BMIPS 4355 DMA-Engine Burst Bank Hardware Specification + * + * Designer Note: + * Per shared/include/6xxx_cpu.h, a #define MIPS_BASE 0xff400000 is defined. The + * BMIPS_DMA_CONTROL_EN_REG is essentially (MIPS_BASE + 0x34). + * PS. BCM6368 is BMIPS4350 based and does not include a DMA-E (burst banks). + * + * ============================================================================= + */ + +#define BMIPS_DMA_CONTROL_EN_REG 0xFF400034 /* MIPS_BASE + 0x34 */ +#define BMIPS_DMA_BB_BASE 0xFF500000 + +#define BB_SIZE_WORDS 16U +#define BB_SIZE_SHORT (BB_SIZE_WORDS * sizeof(uint16_t)) +#define BB_SIZE_BYTES (BB_SIZE_WORDS * sizeof(uint32_t)) + +#define BB_MAX_CHANNELS 8U + +#define __bb_aligned __attribute__ ((__aligned__ (4))) + +/* + * ----------------------------------------------------------------------------- + * + * BMIPS4355 Layout of Burst Banks + * + * Addressing DMA_Engine: + * 31 10 9 7 6 0 + * | | | | + * ---------------------------------------------- + * | BMIPS_DMA_BB_BASE | BB# | Field | + * ---------------------------------------------- + * + * Each Burst bank is 32 words in size with 16+3 words. Last bank#7 carries + * 3 extra words of global status of each of the 8 burst banks(4bit status). + * + * Data in a burst bank may only be accessed as an aligned word (lw/sw). + * All burst bank DMA transactions are in multiple of word sizes. + * + * CAUTION: + * 1. A user may only access the data in a burst bank as words (lw/sw). + * 2. The data sections of adjacent burst banks are NOT contiguous. + * 3. Burst Read/Write transactions are not through the L1-Cache. Cache/DDR + * coherency is the callers responsibility. + * 4. What about Read Ahead Cache? + * + * ----------------------------------------------------------------------------- + */ + +union bb_data /* Each burst bank can transfer 16B of 4B aligned data */ +{ + uint8_t u8[BB_SIZE_BYTES]; + uint16_t u16[BB_SIZE_SHORT]; + uint32_t u32[BB_SIZE_WORDS]; +} __bb_aligned; +typedef union bb_data bb_data_t; + + +struct bb_block /* Block of memory to serve as src or dst of a BB transaction */ +{ + uint8_t u8[0]; +} __bb_aligned; +typedef struct bb_block bb_block_t; + + +typedef /* Hardware Specification of a Burst Bank Channel */ +struct bb +{ + bb_data_t data; /* READ/WRITE as u32 ONLY !!! */ + uint32_t * memory_p; /* Destination start address in DMA-M */ + uint32_t burst_words; /* Number of words in transaction */ + uint32_t transaction; /* Type of transaction RD or WR */ + + union + { + int padding06[13]; /* Banks #0 to Bank #6, do not have status */ + + struct + { + /* Global status registers in last burst bank, 4bits per bank */ + uint32_t reg_DMAE; /* Outstanding DMA transactions */ + uint32_t reg_UBUS; /* Outstanding DMA transactions to UBUS */ + uint32_t reg_MISB; /* Outstanding DMA transactions to SMISB */ + uint32_t pad7[10]; /* Bank #7 */ + } status; + }; +} bb_t; + + +/* Macro: Access a burst bank data. Only aligned word access permitted */ +#define BB_DATA(bb_p, i) (bb_p)->data.u32[ i ] + +/* Macro: Access the base pointer of a burst bank from channel number */ +#define BB_POINTER(channel) ((bb_t*)(BMIPS_DMA_BB_BASE) + (channel)) + +/* Macro: Access the channel number from a burst bank pointer */ +#define BB_CHANNEL(pointer) \ + (((uint32_t)(pointer) - BMIPS_DMA_BB_BASE) / sizeof(bb_t)) + +/* Macro: validate a burst bank pointer */ +#define BB_CHECK(pointer) \ + (((bb_t*)(pointer) != BB_NULL) && \ + ((((uint32_t)(pointer) - BMIPS_DMA_BB_BASE) % sizeof(bb_t)) == 0) && \ + (BB_CHANNEL(pointer) < BB_MAX_CHANNELS)) + + +/* + * ----------------------------------------------------------------------------- + * Burst Bank Status (DMAE, UBUS, MISB) (global register in last Burst Bank) + * ----------------------------------------------------------------------------- + */ + +/* A status word contains 4bits per channel */ +#define BB_REG_STATUS_BITS 4 + +#define BB_REG_STATUS(rname) \ + &(((bb_t*)(BMIPS_DMA_BB_BASE)) + (BB_MAX_CHANNELS - 1))->status.reg_##rname + +#define BB_REG_STATUS_DMAE BB_REG_STATUS(DMAE) +#define BB_REG_STATUS_UBUS BB_REG_STATUS(UBUS) +#define BB_REG_STATUS_MISB BB_REG_STATUS(MISB) + +typedef /* Status reported by hardware on a transaction issuance */ +enum bb_status +{ + bb_read_pending = (1 << 0U), /* Outstanding read transaction */ + bb_write_pending = (1 << 1U), /* Outstanding write transaction */ + bb_error_condition = (1 << 2U) /* Error condition */ + /* bit #4 is reserved */ +} bb_status_t; + + +#define BB_RD_TRANS(bb_p) ((bb_p)->transaction & bb_read_pending) +#define BB_WR_TRANS(bb_p) ((bb_p)->transaction & bb_write_pending) +#define BB_ERR_COND(bb_p) ((bb_p)->transaction & bb_error_condition) + +/* Pending read or write transaction */ +#define BB_TRANSACTION (bb_read_pending | bb_write_pending) + +/* Pending read or write transaction, or error constition */ +#define BB_STATUS_USED ((1 << BB_REG_STATUS_BITS) - 1U) + +/* Macro: Access the status 4bits for a channel given a status word */ +#define BB_STATUS_GBL(status_word, channel) \ + ((status_word) >> ((channel) * BB_REG_STATUS_BITS)) + +/* + * ----------------------------------------------------------------------------- + * + * Read a status register and test for pending transaction or error condition + * Returns a 4 bit value containing outstanding transactions + * + * Accessor APIS: + * bb_DMAE(channel, status) + * bb_UBUS(channel, status) + * bb_MISB(channel, status) + * + * channel - bb_channel_t + * status - 3 bit value described in bb_status_t + * + * ----------------------------------------------------------------------------- + */ + +#define __BB_BUILD_STATUS_ACCESSOR(TYPE) \ +static inline uint32_t \ +bb_##TYPE(const uint32_t channel, const uint32_t status) \ +{ \ + register volatile uint32_t status##TYPE; \ + BB_ASSERTR((channel < BB_MAX_CHANNELS), BB_ERROR); \ + BB_ASSERTR((status <= BB_STATUS_USED), BB_ERROR); \ + status##TYPE = (*(volatile uint32_t *)(BB_REG_STATUS_##TYPE)); \ + return (BB_STATUS_GBL(status##TYPE, channel) & status); \ +} +__BB_BUILD_STATUS_ACCESSOR(DMAE) /* function: bb_DMAE(channel, status) */ +__BB_BUILD_STATUS_ACCESSOR(UBUS) /* function: bb_UBUS(channel, status) */ +__BB_BUILD_STATUS_ACCESSOR(MISB) /* function: bb_MISB(channel, status) */ + + +/* + * ============================================================================= + * Section: Pre-assignment of channels usage + * ============================================================================= + * + * Burst Bank DMA Channels are dedicated for specific access function + * Instead of using a reservation mechanism, for datapath channels reserved for + * specific purposes in datapath may be used as an alternative. + * + * Alternatively, reserving 4 banks per CPU core is another option. + * + * While a named reservation of a burst bank breaks the abstraction model, it + * serves the performance requirement for datapath use. + * + * ============================================================================= + */ +typedef /* Named allocation, callers responsibility for mutual access to bank */ +enum bb_channel +{ + bb_channel_xfer0, /* Four banks reserved for xfer of >64B data chunks */ + bb_channel_xfer1, + bb_channel_xfer2, + bb_channel_xfer3, + + bb_channel_copy, /* DMA access to copy <= 64 Bytes */ + + bb_channel_rxbd, /* DMA access to receive buffer descriptors */ + bb_channel_txbd, /* DMA access to transmit buffer descriptors */ + bb_channel_reclbd, /* DMA access to reclaim transmit buffer descriptors */ + + bb_channel_max = BB_MAX_CHANNELS +} bb_channel_t; + +typedef /* Allocation modes supported by API */ +enum bb_alloc_mode +{ + bb_alloc_check_status, + bb_alloc_ignore_status, /* Ignore error or pending transaction, clear */ +} bb_alloc_mode_t; + +/* + * ============================================================================= + * Section: Burst Bank Channel state + * Allocation state and statistics per channel + * ============================================================================= + */ +typedef /* Software state per burst bank channel */ +struct bb_state +{ + atomic_t in_use; /* Allocation via compile time delegation */ +#if defined(CC_BB_STATS) + uint32_t reserves, releases, copies; + struct { uint32_t ops, polls, errors; } RD, WR; +#endif /* CC_BB_STATS */ +} bb_state_t; + + +/* + * ============================================================================= + * Section: Global Burst Bank System State + * ch_state: per channel, in use and statistics + * ============================================================================= + */ +typedef /* Global instance of burst bank software state, debug, statistics */ +struct bb_global +{ + uint32_t debug; + bb_state_t ch_state[BB_MAX_CHANNELS]; +} bb_global_t; + +extern bb_global_t bb_g; /* Burst Bank Global State of all channels */ + +/* + * ============================================================================= + * Section: Channel Reservation + * Burst bank allocation/reservation. + * For datapath, statically reserving channels for specific purposes is + * preferrable for performance. + * ============================================================================= + */ + +/* + * ----------------------------------------------------------------------------- + * API bb_status + * Description: Determine on the requested burst bank, + * if any read or write transaction is pending, or, + * if an error condition exists. + * Parameters: + * bb_p : pointer to a burst bank channel + * transaction: pending read, write and/or error condition test + * ----------------------------------------------------------------------------- + */ +static inline uint32_t +_bb_status(volatile bb_t * const bb_p, const uint32_t transaction) +{ + BB_ASSERTR(BB_CHECK(bb_p), BB_ERROR); + BB_ASSERTR((transaction <= bb_error_condition), BB_ERROR); + + return (bb_p->transaction & transaction); +} +BB_DECLARE_FN( + bb_status, /* Determine the status of a channel */ + uint32_t bb_status(volatile bb_t * const bb_p, const uint32_t transaction), + return _bb_status(bb_p, transaction)) + + +/* + * ----------------------------------------------------------------------------- + * API bb_clear + * Description: Clear error or pending transactions, and memories. + * Parameters: + * bb_p : pointer to a burst bank channel + * + * Design Note: No mention on how to clear error condition. + * ----------------------------------------------------------------------------- + */ +static inline void +_bb_clear(volatile bb_t * const bb_p) +{ + BB_ASSERTV(BB_CHECK(bb_p)); + + bb_p->memory_p = (uint32_t*)0x0; + bb_p->burst_words = 0U; /* Hmmm ... 0 implies at least 1 */ + bb_p->transaction = 0U; /* Force a clear of pending transaction ? */ + + BB_DBG({ + uint32_t wordIx; + for (wordIx = 0U; wordIx < BB_SIZE_WORDS; wordIx++) { + BB_DATA(bb_p, wordIx) = 0U; + } + }); +} +BB_DECLARE_FN( + bb_clear, /* Zero out a burst bank channel */ + void bb_clear(bb_t * const bb_p), + _bb_clear(bb_p)) + +/* + * ----------------------------------------------------------------------------- + * API bb_reset + * Description: Resets status of outstanding transaction or error conditions. + * Parameters: + * channel : index of a burst bank 0..7 + * ----------------------------------------------------------------------------- + */ +static inline void +_bb_reset(const uint32_t channel) +{ + volatile bb_t * bb_p; + + BB_ASSERTV((channel < BB_MAX_CHANNELS)); + + bb_p = BB_POINTER(channel); + + _bb_clear(bb_p); + + BB_DBG(/* udelay(1); Need to wait for the bank to complete transaction ? */ + if (bb_DMAE(channel, BB_STATUS_USED)) { + BB_DBGL(0U, "bb_reset channel %d failure", channel); + }); + + BB_ASSERTV((bb_status(bb_p, BB_TRANSACTION) == 0)); +} +BB_DECLARE_FN( + bb_reset, /* Reset pending transaction|error */ + void bb_reset(const uint32_t channel), + _bb_reset(channel)) + +/* + * ----------------------------------------------------------------------------- + * API bb_reserve + * Description: Reserve a channel for exclusive use. + * Parameters: + * bb_p : pointer to a burst bank channel + * alloc_condition: ignore or check for status before allocation. + * ----------------------------------------------------------------------------- + */ +static inline bb_t * +_bb_reserve(const uint32_t channel, const int alloc_condition) +{ + atomic_t * channel_in_use_p; + volatile bb_t * bb_p; + + BB_STATS(bb_state_t * state_p); + + BB_ASSERTR((channel < BB_MAX_CHANNELS), BB_NULL); + BB_ASSERTR((alloc_condition <= bb_alloc_ignore_status), BB_NULL); + + channel_in_use_p = & bb_g.ch_state[channel].in_use; + BB_ASSERTR((atomic_read(channel_in_use_p) == 0U), BB_NULL); + + bb_p = BB_POINTER(channel); + + BB_STATS(state_p = &bb_g.ch_state[channel]); + + /* Check if reserved by another user */ + if (unlikely(atomic_read(channel_in_use_p))) { + BB_DBGL(0U, "channel<%u> bb_p<0x%08x> in_use transaction<0x%08x>" + "memory_p<0x%08x> burst_words<%u>", + channel, (int)bb_p, (int)bb_p->transaction, + (int)bb_p->memory_p, bb_p->burst_words); + return BB_NULL; + } + + /* Check if outstanding transaction or error condition */ + if (unlikely(bb_DMAE(channel, BB_STATUS_USED))) { + if (unlikely(alloc_condition == bb_alloc_ignore_status)) { + _bb_reset(channel); /* Clear condition */ + } else { + BB_DBGL(0U, "channel<%u> bb_p<0x%08x> pending transaction<0x%08x>" + "memory_p<0x%08x> burst_words<%u>", + channel, (int)bb_p, (int)bb_p->transaction, + (int)bb_p->memory_p, bb_p->burst_words); + return BB_NULL; + } + } + + /* Reserve channel */ + atomic_inc(channel_in_use_p); + BB_ASSERTR((atomic_read(channel_in_use_p) == 1U), BB_NULL); + + BB_STATS(state_p->reserves++); + + return (bb_t *)bb_p; +} +BB_DECLARE_FN( + bb_reserve, /* Reserve a burst bank channel */ + bb_t * bb_reserve(const uint32_t channel, const int alloc_condition), + return _bb_reserve(channel, alloc_condition)) + + +/* + * ----------------------------------------------------------------------------- + * API bb_release + * Description: Release a previously reserved channel. + * Parameters: + * channel : index of a burst bank 0..7 + * ----------------------------------------------------------------------------- + */ +static inline +void _bb_release(const uint32_t channel) +{ + atomic_t * channel_in_use_p; + + BB_STATS(bb_state_t * state_p); + + BB_ASSERTV((channel < BB_MAX_CHANNELS)); + + channel_in_use_p = &bb_g.ch_state[channel].in_use; + BB_ASSERTV((atomic_read(channel_in_use_p) == 1U)); + + BB_STATS(state_p = &bb_g.ch_state[channel]); + + /* Ensure no pending transaction or error condition exists */ + _bb_reset(channel); + + /* Unreserve channel */ + atomic_dec(channel_in_use_p); + + BB_ASSERTV((atomic_read(channel_in_use_p) == 0U)); + + BB_STATS(state_p->releases++); + return; +} +BB_DECLARE_FN( + bb_release, /* Release a burst bank channel */ + void bb_release(const uint32_t channel), + _bb_release(channel)) + + +/* + * ============================================================================= + * Section: Bursting Read & Write transactions, with wait for completion + * ============================================================================= + */ + +/* + * ----------------------------------------------------------------------------- + * API bb_wait + * Description: Wait for a transaction to complete. + * Parameters: + * bb_p : pointer to a burst bank channel + * transaction: read or write transaction + * ----------------------------------------------------------------------------- + */ +static inline uint32_t +_bb_wait(volatile bb_t * const bb_p, const uint32_t transaction) +{ + uint32_t not_done; + BB_STATS(int channel = BB_CHANNEL(bb_p); + bb_state_t * state_p = &bb_g.ch_state[channel]); + + BB_ASSERTR(BB_CHECK(bb_p), BB_ERROR); + BB_ASSERTR(((transaction == bb_read_pending) || + (transaction == bb_write_pending)), BB_ERROR); + + /* Wait on transaction to complete (or error) */ + not_done = bb_p->transaction; + + while (likely(not_done)) { + + if (unlikely(not_done & bb_error_condition)) { + BB_STATS((transaction == bb_read_pending) + ? state_p->RD.errors++ : state_p->WR.errors++); + BB_DBGL(0U, "bb_p<0x%08x> error condition " + "memory_p<0x%08x> burst_words<%u>", + (int)bb_p, (int)bb_p->memory_p, bb_p->burst_words); + return BB_ERROR; + } + + BB_STATS((transaction == bb_read_pending) + ? state_p->RD.polls++ : state_p->WR.polls++); + + not_done = bb_p->transaction; + } + + return BB_SUCCESS; +} +BB_DECLARE_FN( + bb_wait, /* Wait on transaction to complete on a channel */ + uint32_t bb_wait(volatile bb_t * const bb_p, const uint32_t transaction), + return _bb_wait(bb_p, transaction)) + +/* + * ----------------------------------------------------------------------------- + * API bb_issue + * Description: Issue a read or write transaction and return ASYNCHRONOUSLY. + * Parameters: + * bb_p : pointer to a burst bank channel + * memory_p : physical memory address to which the bank will be mapped + * burst_words : number of words 0..15. + * transaction: read or write transaction + * + * CAUTION: memory_p must be a non zero word aligned physical address + * burst_words is total words - 1 + * + * ----------------------------------------------------------------------------- + */ +static inline void +_bb_issue(volatile bb_t * const bb_p, uint32_t * memory_p, + const uint32_t burst_words, const uint32_t transaction) +{ + BB_STATS(int channel = BB_CHANNEL(bb_p); + bb_state_t * state_p = &bb_g.ch_state[channel]); + + BB_ASSERTV(BB_CHECK(bb_p)); + BB_ASSERTV((bb_status(bb_p, BB_TRANSACTION) == 0)); + + BB_ASSERTV((memory_p != (uint32_t*)NULL)); + BB_ASSERTV((((uint32_t)memory_p & 0x3) == 0U)); + BB_ASSERTV(((uint32_t)memory_p == (uint32_t)BB_LOG2PHY(memory_p))); + BB_ASSERTV((burst_words < BB_SIZE_WORDS)); + BB_ASSERTV(((transaction == bb_read_pending) || + (transaction == bb_write_pending))); + + bb_p->memory_p = memory_p; /* physical memory pointer */ + bb_p->burst_words = burst_words; /* num_words - 1 */ + + bb_p->transaction = transaction; /* read or write initiated to HW */ + + BB_STATS((transaction == bb_read_pending) + ? state_p->RD.ops++ : state_p->WR.ops++); +} +BB_DECLARE_FN( + bb_issue, /* Issue a read|write transaction on a channel */ + void bb_issue(volatile bb_t * const bb_p, uint32_t * memory_p, + const uint32_t burst_words , /* num_words - 1 */ + const uint32_t transaction), + _bb_issue(bb_p, memory_p, burst_words, transaction)) + +/* + * ----------------------------------------------------------------------------- + * + * API bb_copy + * Description: A maximum of 64Bytes of data will be copied from a word aligned + * location to a word-aligned location. + * + * Parameters: + * src_p : source physical memory address: read into bank + * dst_p : destination physical memory address: written from bank + * burst_words : number of words 0..15 + * channel : index of a burst bank 0..7 + * + * CAUTION: local_p and memory_p must be non-zero 4B aligned physical addresses + * burst_words is number of words - 1 + * An error on read, results in garbage write. + * ----------------------------------------------------------------------------- + */ +static inline uint32_t +_bb_copy(const uint32_t * src_p, uint32_t * dst_p, + const uint32_t burst_words, const uint32_t channel) +{ + uint32_t ret; + volatile bb_t * bb_p = BB_POINTER(channel); + + BB_STATS(bb_state_t * state_p = &bb_g.ch_state[channel]); + + BB_ASSERTR(BB_CHECK(bb_p), BB_ERROR); + BB_ASSERTR((bb_p->transaction == 0U), BB_ERROR); + + BB_ASSERTR((dst_p != (uint32_t *)NULL), BB_ERROR); + BB_ASSERTR((src_p != (uint32_t *)NULL), BB_ERROR); + BB_ASSERTR((((uint32_t)src_p & 0x3) == 0U), BB_ERROR); + BB_ASSERTR((((uint32_t)dst_p & 0x3) == 0U), BB_ERROR); + BB_ASSERTR((burst_words < BB_SIZE_WORDS), BB_ERROR); + BB_ASSERTR((channel < BB_MAX_CHANNELS), BB_ERROR); + + /* First read from source memory into burst bank */ + _bb_issue(bb_p, (uint32_t *)src_p, burst_words, bb_read_pending); + ret = _bb_wait(bb_p, bb_read_pending); + + /* Then copy to destination memory, "ignoring read error" */ + _bb_issue(bb_p, dst_p, burst_words, bb_write_pending); + ret |= _bb_wait(bb_p, bb_write_pending); + + BB_STATS(state_p->copies++); + + BB_ASSERTR((ret == BB_SUCCESS), ret); + return ret; +} +BB_DECLARE_FN( + bb_copy, /* Copy from|to memory to|from local */ + uint32_t bb_copy(const uint32_t * src_p, uint32_t * dst_p, + const uint32_t burst_words, /* num_words - 1 */ + const uint32_t channel), + return _bb_copy(src_p, dst_p, burst_words, channel)) + +/* + * ----------------------------------------------------------------------------- + * + * API bb_xfer + * Description: Copy data from one location to another (larger than 64B) + * Source and destination location must be word-aligned. + * + * Parameters: + * src_p : physical memory address to be read into bank + * dst_p : physical memory address to be written from bank + * num_words : number of words + * + * CAUTION: local_p and memory_p must be non-zero 4B aligned physical addresses + * An error on read, results in garbage write. + * ----------------------------------------------------------------------------- + */ +static inline uint32_t +_bb_xfer(const uint32_t * src_p, uint32_t * dst_p, const uint32_t num_words) +{ + uint32_t ret, i, bbs; + volatile bb_t * bb_p = BB_POINTER(bb_channel_xfer0); + uint32_t words = (uint32_t) num_words; + + BB_STATS(bb_state_t * state_p = &bb_g.ch_state[bb_channel_xfer0]); + + BB_ASSERTR(BB_CHECK(bb_p), BB_ERROR); + BB_ASSERTR(((bb_p + 0)->transaction == 0U), BB_ERROR); + BB_ASSERTR(((bb_p + 1)->transaction == 0U), BB_ERROR); + BB_ASSERTR(((bb_p + 2)->transaction == 0U), BB_ERROR); + BB_ASSERTR(((bb_p + 3)->transaction == 0U), BB_ERROR); + + BB_ASSERTR((dst_p != (uint32_t *)NULL), BB_ERROR); + BB_ASSERTR((src_p != (uint32_t *)NULL), BB_ERROR); + BB_ASSERTR((((uint32_t)src_p & 0x3) == 0U), BB_ERROR); + BB_ASSERTR((((uint32_t)dst_p & 0x3) == 0U), BB_ERROR); + + while (likely(words > BB_SIZE_WORDS)) { /* Copy using multiple banks */ + + if (words >= (BB_SIZE_WORDS * 4)) + bbs = 4; /* Use 4 banks in parallel */ + else if (words >= (BB_SIZE_WORDS * 3)) + bbs = 3; /* Use 3 banks in parallel */ + else if (words >= (BB_SIZE_WORDS * 2)) + bbs = 2; /* Use 2 banks in parallel */ + else + bbs = 1; + + /* Wait on previous write(s) to complete, then read */ + for (i = 0; likely(i < bbs); i++) { + ret |= _bb_wait(bb_p + i, bb_write_pending); + _bb_issue(bb_p + i, (uint32_t *)src_p, BB_SIZE_WORDS - 1, + bb_read_pending); + src_p += BB_SIZE_WORDS; + } + + /* Wait on previous read(s) to complete, then write */ + for (i = 0; likely(i < bbs); i++) { + ret |= _bb_wait(bb_p + i, bb_read_pending); + _bb_issue(bb_p + i, dst_p, BB_SIZE_WORDS - 1, bb_write_pending); + dst_p += BB_SIZE_WORDS; + } + + words -= BB_SIZE_WORDS * bbs; + BB_STATS(state_p->copies += bbs); + } + + if (likely(words > 0)) { /* Copy leftover words */ + ret |= bb_copy(src_p, dst_p, words - 1, bb_channel_copy); + } + + /* Wait on last set of parralel write(s) to complete */ + for (i = 0; likely(i < 4); i++) { + ret |= _bb_wait(bb_p + i, bb_write_pending); + } + + BB_ASSERTR((ret == BB_SUCCESS), ret); + return ret; +} +BB_DECLARE_FN( + bb_xfer, /* Bulk xfer from|to memory to|from local */ + uint32_t bb_xfer(const uint32_t * src_p, uint32_t * dst_p, + const uint32_t num_words), + return _bb_xfer(src_p, dst_p, num_words)) + +/* + * ============================================================================= + * Section: Debug APIs + * ============================================================================= + */ +#if defined(CC_BB_DEBUG) + extern void bb_dump(volatile bb_t * const bb_p, int verbose); + extern void bb_show(int verbose); +#else /* !CC_BB_DEBUG */ +# define bb_dump(bb_p, verbose) BB_NULL_STMT +# define bb_show(verbose) BB_NULL_STMT +#endif /* !CC_BB_DEBUG */ + +#endif /* BMIPS4355 */ + +#endif /* defined(__BURSTBANK_H_INCLUDED__) */ diff -urN linux-4.1.52-orig/arch/mips/include/asm/mach-bcm963xx/cpu-feature-overrides.h linux-4.1.52/arch/mips/include/asm/mach-bcm963xx/cpu-feature-overrides.h --- linux-4.1.52-orig/arch/mips/include/asm/mach-bcm963xx/cpu-feature-overrides.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/mips/include/asm/mach-bcm963xx/cpu-feature-overrides.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,42 @@ +#if defined(CONFIG_BCM_KF_MIPS_BCM963XX) +#ifndef __ASM_MACH_BCM963XX_CPU_FEATURE_OVERRIDES_H +#define __ASM_MACH_BCM963XX_CPU_FEATURE_OVERRIDES_H + +#define cpu_has_tlb 1 +#define cpu_has_4kex 4 +#define cpu_has_4ktlb 8 +#define cpu_has_4k_cache 1 +#define cpu_has_fpu 0 +#define cpu_has_32fpr 0 +#define cpu_has_counter 0x40 +#define cpu_has_watch 0 +#define cpu_has_mips16 0 +// Use IVEC for TP0 +#define cpu_has_divec 0 +#define cpu_has_vce 0 +#define cpu_has_cache_cdex_p 0 +#define cpu_has_cache_cdex_s 0 +#define cpu_has_prefetch 0 +#define cpu_has_mcheck 0x2000 +#define cpu_has_ejtag 0x4000 +#define cpu_has_llsc 0x10000 +#define cpu_has_vtag_icache 0 +/* #define cpu_has_dc_aliases ? */ +#define cpu_has_ic_fills_f_dc 0 + +#define cpu_has_nofpuex 0 +#define cpu_has_64bits 0 +#define cpu_has_64bit_zero_reg 0 +#define cpu_has_64bit_gp_regs 0 +/* #define cpu_has_inclusive_pcaches ? */ +#define cpu_has_64bit_addresses 0 + +#define cpu_has_subset_pcaches 0 + +#define cpu_dcache_line_size() 16 +#define cpu_icache_line_size() 16 +#define cpu_scache_line_size() 0 +/*#define cpu_icache_snoops_remote_store 1 ? */ + +#endif /* __ASM_MACH_BCM963XX_CPU_FEATURE_OVERRIDES_H */ +#endif diff -urN linux-4.1.52-orig/arch/mips/include/asm/mach-bcm963xx/war.h linux-4.1.52/arch/mips/include/asm/mach-bcm963xx/war.h --- linux-4.1.52-orig/arch/mips/include/asm/mach-bcm963xx/war.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/mips/include/asm/mach-bcm963xx/war.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,27 @@ +#if defined(CONFIG_BCM_KF_MIPS_BCM963XX) +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2002, 2004, 2007 by Ralf Baechle + */ +#ifndef __ASM_MIPS_MACH_BCM47XX_WAR_H +#define __ASM_MIPS_MACH_BCM47XX_WAR_H + +#define R4600_V1_INDEX_ICACHEOP_WAR 0 +#define R4600_V1_HIT_CACHEOP_WAR 0 +#define R4600_V2_HIT_CACHEOP_WAR 0 +#define R5432_CP0_INTERRUPT_WAR 0 +#define BCM1250_M3_WAR 0 +#define SIBYTE_1956_WAR 0 +#define MIPS4K_ICACHE_REFILL_WAR 0 +#define MIPS_CACHE_SYNC_WAR 0 +#define TX49XX_ICACHE_INDEX_INV_WAR 0 +#define RM9000_CDEX_SMP_WAR 0 +#define ICACHE_REFILLS_WORKAROUND_WAR 0 +#define R10000_LLSC_WAR 0 +#define MIPS34K_MISSED_ITLB_WAR 0 + +#endif /* __ASM_MIPS_MACH_BCM47XX_WAR_H */ +#endif diff -urN linux-4.1.52-orig/arch/mips/include/asm/mach-generic/irq.h linux-4.1.52/arch/mips/include/asm/mach-generic/irq.h --- linux-4.1.52-orig/arch/mips/include/asm/mach-generic/irq.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/mips/include/asm/mach-generic/irq.h 2020-02-07 01:47:54.000000000 +0800 @@ -8,6 +8,11 @@ #ifndef __ASM_MACH_GENERIC_IRQ_H #define __ASM_MACH_GENERIC_IRQ_H +#if defined(CONFIG_BCM_KF_MIPS_BCM963XX) && (defined(CONFIG_BCM96838) || defined(CONFIG_BCM96848)) +#ifndef NR_IRQS +#define NR_IRQS 256 +#endif +#endif #ifndef NR_IRQS #define NR_IRQS 128 #endif diff -urN linux-4.1.52-orig/arch/mips/include/asm/mach-generic/spaces.h linux-4.1.52/arch/mips/include/asm/mach-generic/spaces.h --- linux-4.1.52-orig/arch/mips/include/asm/mach-generic/spaces.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/mips/include/asm/mach-generic/spaces.h 2020-02-07 01:47:54.000000000 +0800 @@ -94,11 +94,15 @@ #endif #ifndef FIXADDR_TOP +#if defined(CONFIG_BCM_KF_FIXADDR_TOP) +#define FIXADDR_TOP ((unsigned long)(long)(int)0xff000000) +#else #ifdef CONFIG_KVM_GUEST #define FIXADDR_TOP ((unsigned long)(long)(int)0x7ffe0000) #else #define FIXADDR_TOP ((unsigned long)(long)(int)0xfffe0000) #endif #endif +#endif #endif /* __ASM_MACH_GENERIC_SPACES_H */ diff -urN linux-4.1.52-orig/arch/mips/include/asm/pgtable-32.h linux-4.1.52/arch/mips/include/asm/pgtable-32.h --- linux-4.1.52-orig/arch/mips/include/asm/pgtable-32.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/mips/include/asm/pgtable-32.h 2020-02-07 01:47:54.000000000 +0800 @@ -61,7 +61,11 @@ #define VMALLOC_START MAP_BASE +#if defined(CONFIG_BCM_KF_512MB_DDR) && defined(CONFIG_BCM_512MB_DDR) +#define PKMAP_BASE (0xfc000000UL) +#else #define PKMAP_BASE (0xfe000000UL) +#endif #ifdef CONFIG_HIGHMEM # define VMALLOC_END (PKMAP_BASE-2*PAGE_SIZE) diff -urN linux-4.1.52-orig/arch/mips/include/asm/smp.h linux-4.1.52/arch/mips/include/asm/smp.h --- linux-4.1.52-orig/arch/mips/include/asm/smp.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/mips/include/asm/smp.h 2020-02-07 01:47:54.000000000 +0800 @@ -40,6 +40,11 @@ #define SMP_RESCHEDULE_YOURSELF 0x1 /* XXX braindead */ #define SMP_CALL_FUNCTION 0x2 + +#if defined(CONFIG_BCM_KF_MIPS_BCM963XX) && defined(CONFIG_BCM_HOSTMIPS_PWRSAVE_TIMERS) +#define SMP_BCM_PWRSAVE_TIMER 0x3 +#endif + /* Octeon - Tell another core to flush its icache */ #define SMP_ICACHE_FLUSH 0x4 /* Used by kexec crashdump to save all cpu's state */ diff -urN linux-4.1.52-orig/arch/mips/include/asm/thread_info.h linux-4.1.52/arch/mips/include/asm/thread_info.h --- linux-4.1.52-orig/arch/mips/include/asm/thread_info.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/mips/include/asm/thread_info.h 2020-02-07 01:47:54.000000000 +0800 @@ -64,8 +64,12 @@ /* thread information allocation */ #if defined(CONFIG_PAGE_SIZE_4KB) && defined(CONFIG_32BIT) +#if defined(CONFIG_BCM_KF_THREAD_SIZE_FIX) +#define THREAD_SIZE_ORDER (2) +#else #define THREAD_SIZE_ORDER (1) #endif +#endif #if defined(CONFIG_PAGE_SIZE_4KB) && defined(CONFIG_64BIT) #define THREAD_SIZE_ORDER (2) #endif diff -urN linux-4.1.52-orig/arch/mips/include/uapi/asm/perf_regs.h linux-4.1.52/arch/mips/include/uapi/asm/perf_regs.h --- linux-4.1.52-orig/arch/mips/include/uapi/asm/perf_regs.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/mips/include/uapi/asm/perf_regs.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,41 @@ +#ifndef _ASM_MIPS_PERF_REGS_H +#define _ASM_MIPS_PERF_REGS_H + +enum perf_event_mips_regs { + PERF_REG_MIPS_PC, + PERF_REG_MIPS_R1, + PERF_REG_MIPS_R2, + PERF_REG_MIPS_R3, + PERF_REG_MIPS_R4, + PERF_REG_MIPS_R5, + PERF_REG_MIPS_R6, + PERF_REG_MIPS_R7, + PERF_REG_MIPS_R8, + PERF_REG_MIPS_R9, + PERF_REG_MIPS_R10, + PERF_REG_MIPS_R11, + PERF_REG_MIPS_R12, + PERF_REG_MIPS_R13, + PERF_REG_MIPS_R14, + PERF_REG_MIPS_R15, + PERF_REG_MIPS_R16, + PERF_REG_MIPS_R17, + PERF_REG_MIPS_R18, + PERF_REG_MIPS_R19, + PERF_REG_MIPS_R20, + PERF_REG_MIPS_R21, + PERF_REG_MIPS_R22, + PERF_REG_MIPS_R23, + PERF_REG_MIPS_R24, + PERF_REG_MIPS_R25, + /* + * 26 and 27 are k0 and k1, they are always clobbered thus not + * stored. + */ + PERF_REG_MIPS_R28, + PERF_REG_MIPS_R29, + PERF_REG_MIPS_R30, + PERF_REG_MIPS_R31, + PERF_REG_MIPS_MAX = PERF_REG_MIPS_R31 + 1, +}; +#endif /* _ASM_MIPS_PERF_REGS_H */ diff -urN linux-4.1.52-orig/arch/mips/Kbuild.platforms linux-4.1.52/arch/mips/Kbuild.platforms --- linux-4.1.52-orig/arch/mips/Kbuild.platforms 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/mips/Kbuild.platforms 2020-02-07 01:47:54.000000000 +0800 @@ -7,6 +7,9 @@ platforms += bcm47xx platforms += bcm63xx platforms += bmips +#if BCM_KF # +platforms += bcm963xx +#endif platforms += cavium-octeon platforms += cobalt platforms += dec diff -urN linux-4.1.52-orig/arch/mips/Kconfig linux-4.1.52/arch/mips/Kconfig --- linux-4.1.52-orig/arch/mips/Kconfig 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/mips/Kconfig 2020-02-07 01:47:54.000000000 +0800 @@ -9,6 +9,8 @@ select HAVE_IRQ_EXIT_ON_IRQ_STACK select HAVE_OPROFILE select HAVE_PERF_EVENTS + select HAVE_PERF_REGS + select HAVE_PERF_USER_STACK_DUMP select PERF_USE_VMALLOC select HAVE_ARCH_KGDB select HAVE_ARCH_SECCOMP_FILTER @@ -208,6 +210,24 @@ help Support for BCM63XX based boards +config MIPS_BCM963XX + depends on BCM_KF_MIPS_BCM963XX + bool "Broadcom 96xxx boards (kern)" + select CEVT_R4K + select CSRC_R4K + select IRQ_CPU + select DMA_NONCOHERENT + select SYS_SUPPORTS_32BIT_KERNEL + select SYS_HAS_CPU_MIPS32_R1 + select SYS_SUPPORTS_BIG_ENDIAN + select SYS_SUPPORTS_LITTLE_ENDIAN + select HW_HAS_PCI + select MIPS_L1_CACHE_SHIFT_4 + select USE_OF + help + "Support for BCM963XX boards" + + config MIPS_COBALT bool "Cobalt Server" select CEVT_R4K @@ -987,6 +1007,8 @@ config BOOT_RAW bool + prompt "boot from raw image" if BCM_KF_MIPS_BCM963XX + default n if BCM_KF_MIPS_BCM963XX config CEVT_BCM1480 bool @@ -1734,6 +1756,7 @@ bool select SYS_SUPPORTS_SMP select SYS_SUPPORTS_HOTPLUG_CPU + select MIPS_PERF_SHARED_TC_COUNTERS config CPU_BMIPS4380 bool @@ -1747,6 +1770,7 @@ select MIPS_L1_CACHE_SHIFT_7 select SYS_SUPPORTS_SMP select SYS_SUPPORTS_HOTPLUG_CPU + select MIPS_PERF_SHARED_TC_COUNTERS config SYS_HAS_CPU_LOONGSON3 bool @@ -2782,6 +2806,7 @@ config ZONE_DMA bool + default y if BCM_KF_MIPS_BCM963XX config ZONE_DMA32 bool diff -urN linux-4.1.52-orig/arch/mips/kernel/bcm_tstamp.c linux-4.1.52/arch/mips/kernel/bcm_tstamp.c --- linux-4.1.52-orig/arch/mips/kernel/bcm_tstamp.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/mips/kernel/bcm_tstamp.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,115 @@ +#if defined(CONFIG_BCM_KF_TSTAMP) +/* +<:copyright-BRCM:2011:GPL/GPL:standard + + Copyright (c) 2011 Broadcom + All Rights Reserved + +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 (the "GPL"). + +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. + + +A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by +writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + +:> +*/ +#include +#include +#include +#include + +static u32 _2us_divisor; +static u32 _2ns_shift; +static u32 _2ns_multiplier; + +u32 bcm_tstamp_read(void) +{ + return read_c0_count(); +} +EXPORT_SYMBOL(bcm_tstamp_read); + + +u32 bcm_tstamp_delta(u32 start, u32 end) +{ + // start and end could have been read from different CPU's. + // Typically, I have seen the counters on the CPU's to be within + // 20 cycles of each other. Allow a bit more for a margin of error + if (start <= 100 && ((end > 0xffffffc0) || + (start >= end && end <= 100))) + return 1; + else if (end > start) + return end-start; // simplest case + else if (start > 100 && (start-end < 100)) + return 1; + else + return (0xffffffff - start + end); // simple rollover +} +EXPORT_SYMBOL(bcm_tstamp_delta); + + +u32 bcm_tstamp_elapsed(u32 start) +{ + u32 end = read_c0_count(); + return bcm_tstamp_delta(start, end); +} +EXPORT_SYMBOL(bcm_tstamp_elapsed); + + +u32 bcm_tstamp2us(u32 i) +{ + return (i/_2us_divisor); +} +EXPORT_SYMBOL(bcm_tstamp2us); + + +u64 bcm_tstamp2ns(u32 i) +{ + u64 ns = (u64) i; + ns = (ns * _2ns_multiplier) >> _2ns_shift; + return ns; +} +EXPORT_SYMBOL(bcm_tstamp2ns); + + +int __init init_bcm_tstamp(void) +{ + if (mips_hpt_frequency == 0) + mips_hpt_frequency = 160000000; + + _2us_divisor = mips_hpt_frequency / 1000000; + + if (mips_hpt_frequency == 200000000) { //400MHz + _2ns_multiplier = 5; //5ns + _2ns_shift = 0; + } else if (mips_hpt_frequency == 166500000) { //333MHz + _2ns_multiplier = 6; //6ns + _2ns_shift = 0; + } else if (mips_hpt_frequency == 160000000) { //320MHz + _2ns_multiplier = 25; //6.25ns + _2ns_shift = 2; + } else if (mips_hpt_frequency == 15000000) { //300MHz + _2ns_multiplier = 13; // approximate to 6.5? actual is 6.667ns + _2ns_shift = 1; + } else { + printk("init_bcm_tstamp: unhandled mips_hpt_freq=%d, " + "adjust constants in bcm_tstamp.c\n", mips_hpt_frequency); + } + + printk(KERN_INFO "bcm_tstamp initialized, (hpt_freq=%d 2us_div=%u " + "2ns_mult=%u 2ns_shift=%u)\n", mips_hpt_frequency, + _2us_divisor, _2ns_multiplier, _2ns_shift); + + return 0; +} +__initcall(init_bcm_tstamp); + +#endif /* defined(CONFIG_BCM_KF_TSTAMP) */ + diff -urN linux-4.1.52-orig/arch/mips/kernel/cevt-r4k-bcm-pwr.c linux-4.1.52/arch/mips/kernel/cevt-r4k-bcm-pwr.c --- linux-4.1.52-orig/arch/mips/kernel/cevt-r4k-bcm-pwr.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/mips/kernel/cevt-r4k-bcm-pwr.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,245 @@ +#if defined(CONFIG_BCM_KF_MIPS_BCM963XX) +/*********************************************************** + * + * Copyright (c) 2009 Broadcom Corporation + * All Rights Reserved + * + * <:label-BRCM:2009:DUAL/GPL:standard + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed + * to you under the terms of the GNU General Public License version 2 + * (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, + * with the following added to such license: + * + * As a special exception, the copyright holders of this software give + * you permission to link this software with independent modules, and + * to copy and distribute the resulting executable under terms of your + * choice, provided that you also meet, for each linked independent + * module, the terms and conditions of the license of that module. + * An independent module is a module which is not derived from this + * software. The special exception does not apply to any modifications + * of the software. + * + * Not withstanding the above, under no circumstances may you combine + * this software in any way with any other Broadcom software provided + * under a license other than the GPL, without Broadcom's express prior + * written consent. + * + * :> + * + ************************************************************/ +/*********************************************************** + * + * This file implements clock events for the Broadcom DSL and GPON CPE + * when the power management feature is enabled. When the processor + * is found to be mostly idle, the main CPU clock is slowed down to + * save power. By slowing down the clock, the C0 counter unfortunately + * also slows down. This file replaces the (typical) 1 msec clock tick + * interrupt processing with a reliable timer source which is unaffected + * by the change in MIPS clock changes. + * + * The timer available to replace the C0 timer works differently. + * The design needs to be adjusted accordingly. The C0 counter is a free + * running counter which wraps at 0xFFFFFFFF and which runs at different + * frequencies depending on the MIPS frequency. The C0 compare register + * requires to be programmed to stay ahead of the C0 counter, to generate + * an interrupt in the future. + * + * The peripheral timers (there are 3 of them) wrap at 0x3fffffff and + * run at 50 MHz. When the timer reaches a programmed value, it can generate + * and interrupt and then either stops counting or restarts at 0. + * This difference in behavior between the C0 counter and the peripheral timers + * required to use 2 timers for power management. One to generate the periodic + * interrupts required by the clock events (Timer 0), and one to keep an accurate + * reference when the clock is slowed down for saving power (Timer 2). Timer 1 + * is planned to be used by the second processor to support SMP. + * + ************************************************************/ + + +#include +#include +#include +#include + +#include +#include + +#include +#include + +extern void BcmPwrMngtCheckWaitCount(void); +extern unsigned int TimerC0Snapshot0; +#if defined(CONFIG_SMP) +extern unsigned int TimerC0Snapshot1; +extern unsigned int C0divider, C0multiplier, C0ratio; +#endif + +DEFINE_PER_CPU(struct clock_event_device, bcm_mips_clockevent_device); +int bcm_timer_irq_installed; + +static int bcm_mips_next_event0(unsigned long delta, + struct clock_event_device *evt) +{ + // Timer may be reprogrammed while it is already running, so clear it first + TIMER->TimerCtl0 = 0; + TIMER->TimerCnt0 = 0; + TIMER->TimerCtl0 = TIMERENABLE | RSTCNTCLR | delta; + + return 0; +} + +#if defined(CONFIG_SMP) +static int bcm_mips_next_event1(unsigned long delta, + struct clock_event_device *evt) +{ + // Timer may be reprogrammed while it is already running, so clear it first + TIMER->TimerCtl1 = 0; + TIMER->TimerCnt1 = 0; + TIMER->TimerCtl1 = TIMERENABLE | RSTCNTCLR | delta; + + return 0; +} +#endif + +void bcm_mips_set_clock_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ +} + +void bcm_mips_event_handler(struct clock_event_device *dev) +{ +} + +#if defined(CONFIG_SMP) +extern struct plat_smp_ops *mp_ops; +#endif +irqreturn_t bcm_timer_interrupt_handler_TP0(int irq, void *dev_id) +{ + struct clock_event_device *cd; + irqreturn_t rc = IRQ_NONE; + byte timer_ints = TIMER->TimerInts & (TIMER0|TIMER1); + + if (timer_ints & TIMER0) { + TIMER->TimerCtl0 = 0; + } + if (timer_ints & TIMER1) { + TIMER->TimerCtl1 = 0; + } + TIMER->TimerInts = timer_ints; + + if (timer_ints & TIMER0) { + // Turn off timer + TIMER->TimerCtl0 = 0; + + cd = &per_cpu(bcm_mips_clockevent_device, 0); + cd->event_handler(cd); + + BcmPwrMngtCheckWaitCount(); + + rc = IRQ_HANDLED; + } +#if defined(CONFIG_SMP) + if (timer_ints & TIMER1) { + // Turn off timer + TIMER->TimerCtl1 = 0; + mp_ops->send_ipi_single(1, SMP_BCM_PWRSAVE_TIMER); + + rc = IRQ_HANDLED; + } +#endif + + return rc; +} + +struct irqaction perf_timer_irqaction = { + .handler = bcm_timer_interrupt_handler_TP0, + .flags = IRQF_SHARED, + .name = "Periph Timer", +}; + +#if defined(CONFIG_SMP) +void bcm_timer_interrupt_handler_TP1(void) +{ + struct clock_event_device *cd; + + cd = &per_cpu(bcm_mips_clockevent_device, 1); + cd->event_handler(cd); + + BcmPwrMngtCheckWaitCount(); + + return; +} +#endif + +int __cpuinit r4k_clockevent_init(void) +{ + unsigned int cpu = smp_processor_id(); + struct clock_event_device *cd; + + cd = &per_cpu(bcm_mips_clockevent_device, cpu); + + cd->name = "BCM Periph Timer"; + cd->features = CLOCK_EVT_FEAT_ONESHOT; + + clockevent_set_clock(cd, mips_hpt_frequency); + + /* Calculate the min / max delta */ + cd->max_delta_ns = clockevent_delta2ns(0x3fffffff, cd); + cd->min_delta_ns = clockevent_delta2ns(0x300, cd); + + cd->rating = 300; + cd->irq = INTERRUPT_ID_TIMER; + cd->cpumask = cpumask_of(cpu); + if (cpu == 0) + cd->set_next_event = bcm_mips_next_event0; +#if defined(CONFIG_SMP) + else + cd->set_next_event = bcm_mips_next_event1; +#endif + + cd->set_mode = bcm_mips_set_clock_mode; + cd->event_handler = bcm_mips_event_handler; + + clockevents_register_device(cd); + + if (cpu == 0) { + // Start the BCM Timer interrupt + irq_set_affinity(INTERRUPT_ID_TIMER, cpumask_of(0)); + setup_irq(INTERRUPT_ID_TIMER, &perf_timer_irqaction); + + // Start the BCM Timer0 - keep accurate 1 msec tick count + TIMER->TimerCtl0 = TIMERENABLE | RSTCNTCLR | (50000-1); + TIMER->TimerMask |= TIMER0EN; + + // Take a snapshot of the C0 timer when Timer2 was started + // This will be needed later when having to make adjustments + TimerC0Snapshot0 = read_c0_count(); + + // Start the BCM Timer2 + // to keep an accurate free running high precision counter + // Count up to its maximum value so it can be used by csrc-r4k-bcm-pwr.c + TIMER->TimerCtl2 = TIMERENABLE | 0x3fffffff; + } +#if defined(CONFIG_SMP) + else { + unsigned int newTimerCnt, mult, rem, result; + // Start the BCM Timer1 - keep accurate 1 msec tick count + TIMER->TimerCtl1 = TIMERENABLE | RSTCNTCLR | (50000-1); + TIMER->TimerMask |= TIMER1EN; + + // Take a snapshot of the C0 timer when Timer1 was started + // This will be needed later when having to make adjustments + TimerC0Snapshot1 = read_c0_count(); + newTimerCnt = TIMER->TimerCnt2 & 0x3fffffff; + mult = newTimerCnt/C0divider; + rem = newTimerCnt%C0divider; + result = mult*C0multiplier + ((rem*C0ratio)>>10); + TimerC0Snapshot1 -= result; + } +#endif + + return 0; +} +#endif diff -urN linux-4.1.52-orig/arch/mips/kernel/cevt-r4k.c linux-4.1.52/arch/mips/kernel/cevt-r4k.c --- linux-4.1.52-orig/arch/mips/kernel/cevt-r4k.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/mips/kernel/cevt-r4k.c 2020-02-07 01:47:54.000000000 +0800 @@ -14,6 +14,10 @@ #include #include +#if defined(CONFIG_BCM_KF_POWER_SAVE) && (defined(CONFIG_BCM_HOSTMIPS_PWRSAVE) || defined(CONFIG_BCM_DDR_SELF_REFRESH_PWRSAVE)) +extern void BcmPwrMngtCheckWaitCount(void); +#endif + static int mips_next_event(unsigned long delta, struct clock_event_device *evt) @@ -81,6 +85,9 @@ cd = &per_cpu(mips_clockevent_device, cpu); cd->event_handler(cd); +#if defined(CONFIG_BCM_KF_POWER_SAVE) && (defined(CONFIG_BCM_HOSTMIPS_PWRSAVE) || defined(CONFIG_BCM_DDR_SELF_REFRESH_PWRSAVE)) + BcmPwrMngtCheckWaitCount(); +#endif return IRQ_HANDLED; } diff -urN linux-4.1.52-orig/arch/mips/kernel/csrc-r4k-bcm-pwr.c linux-4.1.52/arch/mips/kernel/csrc-r4k-bcm-pwr.c --- linux-4.1.52-orig/arch/mips/kernel/csrc-r4k-bcm-pwr.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/mips/kernel/csrc-r4k-bcm-pwr.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,62 @@ +#if defined(CONFIG_BCM_KF_MIPS_BCM963XX) +/*********************************************************** + * + * Copyright (c) 2009 Broadcom Corporation + * All Rights Reserved + * + * <:label-BRCM:2009:DUAL/GPL:standard + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed + * to you under the terms of the GNU General Public License version 2 + * (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, + * with the following added to such license: + * + * As a special exception, the copyright holders of this software give + * you permission to link this software with independent modules, and + * to copy and distribute the resulting executable under terms of your + * choice, provided that you also meet, for each linked independent + * module, the terms and conditions of the license of that module. + * An independent module is a module which is not derived from this + * software. The special exception does not apply to any modifications + * of the software. + * + * Not withstanding the above, under no circumstances may you combine + * this software in any way with any other Broadcom software provided + * under a license other than the GPL, without Broadcom's express prior + * written consent. + * + * :> + * + ************************************************************/ +#include +#include + +#include +#include + +static cycle_t timer2_hpt_read(struct clocksource *cs) +{ + return (TIMER->TimerCnt2); +} + +static struct clocksource clocksource_mips = { + .name = "MIPS", + .read = timer2_hpt_read, + .mask = CLOCKSOURCE_MASK(30), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +int __init init_r4k_clocksource(void) +{ + if (!cpu_has_counter || !mips_hpt_frequency) + return -ENXIO; + + /* Calculate a somewhat reasonable rating value */ + clocksource_mips.rating = 300; + + clocksource_register_hz(&clocksource_mips, 50000000); + + return 0; +} +#endif diff -urN linux-4.1.52-orig/arch/mips/kernel/idle.c linux-4.1.52/arch/mips/kernel/idle.c --- linux-4.1.52-orig/arch/mips/kernel/idle.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/mips/kernel/idle.c 2020-02-07 01:47:54.000000000 +0800 @@ -22,6 +22,55 @@ #include #include +#if defined(CONFIG_BCM_KF_MIPS_BCM963XX) && defined(CONFIG_MIPS_BCM963XX) +/* Bcm version minimizes the chance of an irq sneaking in between checking +need_resched and wait instruction, or eliminates it completely (depending on +pipeline design). This avoids delayed processing of softirq. (The delayed +softirq problem can happen when preemption is disabled and softirq runs in +process context.) */ + +extern void BcmPwrMngtReduceCpuSpeed(void); +extern void BcmPwrMngtResumeFullSpeed(void); + +static void bcm_r4k_wait(void) +{ +#if defined(CONFIG_BCM_HOSTMIPS_PWRSAVE) || defined(CONFIG_BCM_DDR_SELF_REFRESH_PWRSAVE) + BcmPwrMngtReduceCpuSpeed(); +#endif + + /* Always try to treat the segment below as an atomic entity and try not + to insert code or move code around */ + /* Begin fixed safe code pattern for the particular MIPS pipleline*/ + raw_local_irq_disable(); + if (!need_resched() && !(read_c0_cause() & read_c0_status())) { + /* Perform SYNC, enable interrupts, then WAIT */ + __asm__ __volatile__ ( + ".set push\n" + ".set noreorder\n" + ".set noat\n" + "sync\n" + "mfc0 $1, $12\n" + "ori $1, $1, 0x1f\n" + "xori $1, $1, 0x1e\n" + "mtc0 $1, $12\n" + "nop\n" // Recommended by MIPS team + "wait\n" + "nop\n" // Needed to ensure next instruction is safe + "nop\n" // When speed is reduced to 1/8, need one more to get DG interrupt + "nop\n" // Safety net... + ".set pop\n"); + } + else { +#if defined(CONFIG_BCM_HOSTMIPS_PWRSAVE) || defined(CONFIG_BCM_DDR_SELF_REFRESH_PWRSAVE) + BcmPwrMngtResumeFullSpeed(); +#endif + raw_local_irq_enable(); + } + /* End fixed code pattern */ +} +#endif /* CONFIG_BCM_KF_MIPS_BCM963XX */ + + /* * Not all of the MIPS CPUs have the "wait" instruction available. Moreover, * the implementation of the "wait" feature differs between CPU families. This @@ -158,7 +207,9 @@ case CPU_25KF: case CPU_PR4450: case CPU_BMIPS3300: +#if !defined(CONFIG_BCM_KF_MIPS_BCM963XX) case CPU_BMIPS4350: +#endif case CPU_BMIPS4380: case CPU_BMIPS5000: case CPU_CAVIUM_OCTEON: @@ -172,6 +223,11 @@ cpu_wait = r4k_wait; break; +#if defined(CONFIG_BCM_KF_MIPS_BCM963XX) && defined(CONFIG_MIPS_BCM963XX) + case CPU_BMIPS4350: + cpu_wait = bcm_r4k_wait; + break; +#endif case CPU_RM7000: cpu_wait = rm7k_wait_irqoff; break; @@ -236,6 +292,33 @@ } } +#if defined(CONFIG_BCM_KF_MIPS_BCM963XX) && defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_POWER_SAVE) +/* for power management */ +static void set_cpu_r4k_wait(int enable) +{ + if(enable) { + cpu_wait = bcm_r4k_wait; + printk("wait instruction: enabled\n"); + } + else { + cpu_wait = NULL; + printk("wait instruction: disabled\n"); + } +} + +static int get_cpu_r4k_wait(void) +{ + if(cpu_wait == bcm_r4k_wait) + return 1; + else + return 0; +} + +#include // just for EXPORT_SYMBOL +EXPORT_SYMBOL(set_cpu_r4k_wait); +EXPORT_SYMBOL(get_cpu_r4k_wait); +#endif + void arch_cpu_idle(void) { if (cpu_wait) diff -urN linux-4.1.52-orig/arch/mips/kernel/Makefile linux-4.1.52/arch/mips/kernel/Makefile --- linux-4.1.52-orig/arch/mips/kernel/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/mips/kernel/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -17,14 +17,40 @@ endif obj-$(CONFIG_CEVT_BCM1480) += cevt-bcm1480.o + +ifdef BCM_KF # defined (CONFIG_BCM_KF_TRACE_CUSTOM) +ifneq ($(strip $(CONFIG_BCM_KF_TRACE_CUSTOM)),) +obj-$(CONFIG_BCM_KF_TRACE_CUSTOM) += bcm_tstamp.o +endif +endif # BCM_KF + +ifdef BCM_KF # defined (CONFIG_BCM_KF_POWER_SAVE) +ifneq ($(strip $(CONFIG_BCM_HOSTMIPS_PWRSAVE_TIMERS)),) +obj-$(CONFIG_CEVT_R4K) += cevt-r4k-bcm-pwr.o +else +obj-$(CONFIG_CEVT_R4K) += cevt-r4k.o +endif +else # BCM_KF obj-$(CONFIG_CEVT_R4K) += cevt-r4k.o +endif # BCM_KF + obj-$(CONFIG_CEVT_DS1287) += cevt-ds1287.o obj-$(CONFIG_CEVT_GT641XX) += cevt-gt641xx.o obj-$(CONFIG_CEVT_SB1250) += cevt-sb1250.o obj-$(CONFIG_CEVT_TXX9) += cevt-txx9.o obj-$(CONFIG_CSRC_BCM1480) += csrc-bcm1480.o obj-$(CONFIG_CSRC_IOASIC) += csrc-ioasic.o + +ifdef BCM_KF # defined (CONFIG_BCM_KF_POWER_SAVE) +ifneq ($(strip $(CONFIG_BCM_HOSTMIPS_PWRSAVE_TIMERS)),) +obj-$(CONFIG_CSRC_R4K) += csrc-r4k-bcm-pwr.o +else obj-$(CONFIG_CSRC_R4K) += csrc-r4k.o +endif +else # BCM_KF +obj-$(CONFIG_CSRC_R4K) += csrc-r4k.o +endif # BCM_KF + obj-$(CONFIG_CSRC_SB1250) += csrc-sb1250.o obj-$(CONFIG_SYNC_R4K) += sync-r4k.o @@ -96,7 +122,7 @@ obj-$(CONFIG_HAVE_STD_PC_SERIAL_PORT) += 8250-platform.o -obj-$(CONFIG_PERF_EVENTS) += perf_event.o +obj-$(CONFIG_PERF_EVENTS) += perf_event.o perf_regs.o obj-$(CONFIG_HW_PERF_EVENTS) += perf_event_mipsxx.o obj-$(CONFIG_JUMP_LABEL) += jump_label.o diff -urN linux-4.1.52-orig/arch/mips/kernel/perf_event_mipsxx.c linux-4.1.52/arch/mips/kernel/perf_event_mipsxx.c --- linux-4.1.52-orig/arch/mips/kernel/perf_event_mipsxx.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/mips/kernel/perf_event_mipsxx.c 2020-02-07 01:47:54.000000000 +0800 @@ -774,7 +774,9 @@ case CPU_R10000: counters = 2; break; - +#ifdef CONFIG_BCM_KF_MIPS_4350 + case CPU_BMIPS4350: +#endif case CPU_R12000: case CPU_R14000: case CPU_R16000: @@ -832,6 +834,16 @@ [PERF_COUNT_HW_BRANCH_MISSES] = { 0x01, CNTR_ODD }, }; +#ifdef CONFIG_BCM_KF_MIPS_4350 +static const struct mips_perf_event mipsxx74Kcore_event_map + [PERF_COUNT_HW_MAX] = { + [PERF_COUNT_HW_CPU_CYCLES] = { 0x00, CNTR_EVEN | CNTR_ODD, P }, + [PERF_COUNT_HW_INSTRUCTIONS] = { 0x01, CNTR_EVEN | CNTR_ODD, T }, + [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = { 0x27, CNTR_EVEN, T }, + [PERF_COUNT_HW_BRANCH_MISSES] = { 0x27, CNTR_ODD, T }, + }; +#endif + static const struct mips_perf_event octeon_event_map[PERF_COUNT_HW_MAX] = { [PERF_COUNT_HW_CPU_CYCLES] = { 0x01, CNTR_ALL }, [PERF_COUNT_HW_INSTRUCTIONS] = { 0x03, CNTR_ALL }, @@ -849,6 +861,15 @@ [PERF_COUNT_HW_BRANCH_MISSES] = { 0x02, CNTR_ODD, T }, }; +#ifdef CONFIG_BCM_KF_MIPS_4350 +static const struct mips_perf_event bmips4350_event_map + [PERF_COUNT_HW_MAX] = { + [PERF_COUNT_HW_CPU_CYCLES] = { 0x00, CNTR_ALL, P }, + [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = { 0x01, CNTR_EVEN, T }, + [PERF_COUNT_HW_BRANCH_MISSES] = { 0x01, CNTR_ODD, T }, +}; +#endif + static const struct mips_perf_event xlp_event_map[PERF_COUNT_HW_MAX] = { [PERF_COUNT_HW_CPU_CYCLES] = { 0x01, CNTR_ALL }, [PERF_COUNT_HW_INSTRUCTIONS] = { 0x18, CNTR_ALL }, /* PAPI_TOT_INS */ @@ -1070,6 +1091,68 @@ }, }; +#ifdef CONFIG_BCM_KF_MIPS_4350 +/* BMIPS4350 */ +static const struct mips_perf_event bmips4350_cache_map + [PERF_COUNT_HW_CACHE_MAX] + [PERF_COUNT_HW_CACHE_OP_MAX] + [PERF_COUNT_HW_CACHE_RESULT_MAX] = { + +[C(L1D)] = { + /* + * Like some other architectures (e.g. ARM), the performance + * counters don't differentiate between read and write + * accesses/misses, so this isn't strictly correct, but it's the + * best we can do. Writes and reads get combined. + */ + [C(OP_READ)] = { + [C(RESULT_ACCESS)] = { 12, CNTR_EVEN, T }, + [C(RESULT_MISS)] = { 12, CNTR_ODD, T }, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] = { 12, CNTR_EVEN, T }, + [C(RESULT_MISS)] = { 12, CNTR_ODD, T }, + }, +}, +[C(L1I)] = { + [C(OP_READ)] = { + [C(RESULT_ACCESS)] = { 10, CNTR_EVEN, T }, + [C(RESULT_MISS)] = { 10, CNTR_ODD, T }, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] = { 10, CNTR_EVEN, T }, + [C(RESULT_MISS)] = { 10, CNTR_ODD, T }, + }, + [C(OP_PREFETCH)] = { + [C(RESULT_ACCESS)] = { 23, CNTR_EVEN, T }, + /* + * Note that MIPS has only "hit" events countable for + * the prefetch operation. + */ + }, +}, +[C(LL)] = { + [C(OP_READ)] = { + [C(RESULT_ACCESS)] = { 28, CNTR_EVEN, P }, + [C(RESULT_MISS)] = { 28, CNTR_ODD, P }, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] = { 28, CNTR_EVEN, P }, + [C(RESULT_MISS)] = { 28, CNTR_ODD, P }, + }, +}, +[C(BPU)] = { + /* Using the same code for *HW_BRANCH* */ + [C(OP_READ)] = { + [C(RESULT_MISS)] = { 0x02, CNTR_ODD, T }, + }, + [C(OP_WRITE)] = { + [C(RESULT_MISS)] = { 0x02, CNTR_ODD, T }, + }, +}, +}; +#endif + /* BMIPS5000 */ static const struct mips_perf_event bmips5000_cache_map [PERF_COUNT_HW_CACHE_MAX] @@ -1750,6 +1833,13 @@ mipspmu.cache_event_map = &octeon_cache_map; mipspmu.map_raw_event = octeon_pmu_map_raw_event; break; +#ifdef CONFIG_BCM_KF_MIPS_4350 + case CPU_BMIPS4350: + mipspmu.name = "CPU_BMIPS4350"; + mipspmu.general_event_map = &mipsxxcore_event_map; + mipspmu.cache_event_map = &mipsxxcore_cache_map; + break; +#endif case CPU_BMIPS5000: mipspmu.name = "BMIPS5000"; mipspmu.general_event_map = &bmips5000_event_map; diff -urN linux-4.1.52-orig/arch/mips/kernel/perf_regs.c linux-4.1.52/arch/mips/kernel/perf_regs.c --- linux-4.1.52-orig/arch/mips/kernel/perf_regs.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/arch/mips/kernel/perf_regs.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,69 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Some parts derived from x86 version of this file. + * + * Copyright (C) 2013 Cavium, Inc. + */ + +#include + +#include + +#ifdef CONFIG_32BIT +u64 perf_reg_abi(struct task_struct *tsk) +{ + return PERF_SAMPLE_REGS_ABI_32; +} +#else /* Must be CONFIG_64BIT */ +u64 perf_reg_abi(struct task_struct *tsk) +{ + if (test_tsk_thread_flag(tsk, TIF_32BIT_REGS)) + return PERF_SAMPLE_REGS_ABI_32; + else + return PERF_SAMPLE_REGS_ABI_64; +} +#endif /* CONFIG_32BIT */ + +int perf_reg_validate(u64 mask) +{ + if (!mask) + return -EINVAL; + if (mask & ~((1ull << PERF_REG_MIPS_MAX) - 1)) + return -EINVAL; + return 0; +} + +u64 perf_reg_value(struct pt_regs *regs, int idx) +{ + long v; + + switch (idx) { + case PERF_REG_MIPS_PC: + v = regs->cp0_epc; + break; + case PERF_REG_MIPS_R1 ... PERF_REG_MIPS_R25: + v = regs->regs[idx - PERF_REG_MIPS_R1 + 1]; + break; + case PERF_REG_MIPS_R28 ... PERF_REG_MIPS_R31: + v = regs->regs[idx - PERF_REG_MIPS_R28 + 28]; + break; + + default: + WARN_ON_ONCE(1); + return 0; + } + + return (s64)v; /* Sign extend if 32-bit. */ +} + +void perf_get_regs_user(struct perf_regs *regs_user, + struct pt_regs *regs, + struct pt_regs *regs_user_copy) +{ + regs_user->regs = task_pt_regs(current); + regs_user->abi = perf_reg_abi(current); +} + diff -urN linux-4.1.52-orig/arch/mips/kernel/setup.c linux-4.1.52/arch/mips/kernel/setup.c --- linux-4.1.52-orig/arch/mips/kernel/setup.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/mips/kernel/setup.c 2020-02-07 01:47:54.000000000 +0800 @@ -75,7 +75,16 @@ * mips_io_port_base is the begin of the address space to which x86 style * I/O ports are mapped. */ +#if !defined(CONFIG_BCM_KF_MIPS_BCM9685XX) && defined(CONFIG_BCM_KF_MIPS_IOPORT_BASE) +/* mips_io_port_base is normally set using set_io_port_base. The + only reason we would need it here is to get around a race condition. + I don't know what the race condition is, but some ivestigation can be done + later to determine if we can remove it. For now, leave it in so it + doesn't hinder the development. */ +const unsigned long mips_io_port_base = KSEG1; +#else const unsigned long mips_io_port_base = -1; +#endif EXPORT_SYMBOL(mips_io_port_base); static struct resource code_resource = { .name = "Kernel code", }; @@ -94,6 +103,11 @@ return; } +#if defined(CONFIG_BCM_KF_MIPS_BCM963XX) && defined(CONFIG_MIPS_BCM963XX) + /* workaround for 6838 which require the two reserved region not merged in order for + runner driver to run*/ + (void)i; +#else /* * Try to merge with existing entry, if any. */ @@ -116,7 +130,7 @@ return; } - +#endif if (boot_mem_map.nr_map == BOOT_MEM_MAP_MAX) { pr_err("Ooops! Too many entries in the memory map!\n"); return; diff -urN linux-4.1.52-orig/arch/mips/kernel/smp.c linux-4.1.52/arch/mips/kernel/smp.c --- linux-4.1.52-orig/arch/mips/kernel/smp.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/mips/kernel/smp.c 2020-02-07 01:47:54.000000000 +0800 @@ -214,8 +214,73 @@ irq_exit(); } +#if defined(CONFIG_BCM_KF_MIPS_BCM963XX) && defined(CONFIG_MIPS_BCM963XX) + +// yeah, I know, this won't work if numcpus>2, but its good enough for now +int other_cpu_stopped=0; +EXPORT_SYMBOL(other_cpu_stopped); + +void stop_other_cpu(void) +{ + int count=0; + smp_send_stop(); + + // make sure the other CPU is really stopped + do + { + udelay(1000); + count++; + if (count % 4000 == 0) + { + printk(KERN_WARNING "still waiting for other cpu to stop, " + "jiffies=%lu\n", jiffies); + } + } while (!other_cpu_stopped); +} +EXPORT_SYMBOL(stop_other_cpu); + +#endif /* CONFIG_MIPS_BCM963XX */ + + static void stop_this_cpu(void *dummy) { +#if defined(CONFIG_BCM_KF_MIPS_BCM963XX) && defined(CONFIG_MIPS_BCM963XX) + printk(KERN_INFO "\nstopping CPU %d\n", smp_processor_id()); + + /* + * Do not allow any more processing of any kind on this CPU. + * interrupts may trigger processing, so disable it. + * Hmm, this may cause us problems. If there are any threads on this + * CPU which is holding a mutex or spinlock which does not block + * interrupts, and this mutex or spinlock is needed by the other + * processor (e.g. to write the firmware image), we will deadlock. + * PROBABLY should be very rare..... + */ + local_irq_disable(); + /* Remove this CPU. Be a bit slow here and + * set the bits for every online CPU so we don't miss + * any IPI whilst taking this VPE down. + */ + + cpumask_copy(&cpu_foreign_map, cpu_online_mask); + + /* Make it visible to every other CPU */ + smp_mb(); + + /* + * Remove this CPU: + */ + set_cpu_online(smp_processor_id(), false); + calculate_cpu_foreign_map(); + other_cpu_stopped=1; + + /* + * just spin, do not call cpu_wait because some implementations, + * namely, brcm_wait, will re-enable interrupts. + */ + for (;;) { + } +#else /* * Remove this CPU. Be a bit slow here and * set the bits for every online CPU so we don't miss @@ -231,6 +296,7 @@ calculate_cpu_foreign_map(); local_irq_disable(); while (1); +#endif /* CONFIG_BCM_KF_MIPS_BCM963XX */ } void smp_send_stop(void) diff -urN linux-4.1.52-orig/arch/mips/kernel/traps.c linux-4.1.52/arch/mips/kernel/traps.c --- linux-4.1.52-orig/arch/mips/kernel/traps.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/mips/kernel/traps.c 2020-02-07 01:47:54.000000000 +0800 @@ -134,6 +134,34 @@ __setup("raw_show_trace", set_raw_show_trace); #endif +#if defined(CONFIG_BCM_KF_FAP) && (defined(CONFIG_BCM_FAP) || defined(CONFIG_BCM_FAP_MODULE)) + +long * traps_fap0DbgVals = NULL; +long * traps_fap1DbgVals = NULL; +EXPORT_SYMBOL(traps_fap0DbgVals); +EXPORT_SYMBOL(traps_fap1DbgVals); + +static void dumpFapInfo(void) +{ + int i; + printk("FAP0: "); + if (traps_fap0DbgVals != NULL) + for (i = 0; i < 10; i++) + { + printk("[%d]:%08lx ", i, traps_fap0DbgVals[i]); + } + printk("\n"); + + printk("FAP1: "); + if (traps_fap1DbgVals != NULL) + for (i = 0; i < 10; i++) + { + printk("[%d]:%08lx ", i, traps_fap1DbgVals[i]); + } + printk("\n"); +} +#endif + static void show_backtrace(struct task_struct *task, const struct pt_regs *regs) { unsigned long sp = regs->regs[29]; @@ -147,12 +175,29 @@ show_raw_backtrace(sp); return; } + +#if defined(CONFIG_BCM_KF_SHOW_RAW_BACKTRACE)&&defined(CONFIG_KALLSYMS) + /* + * Always print the raw backtrace, this will be helpful + * if unwind_stack fails before giving a proper decoded backtrace + */ + show_raw_backtrace(sp); + printk("\n"); +#endif + printk("Call Trace:\n"); do { print_ip_sym(pc); pc = unwind_stack(task, &sp, pc, &ra); } while (pc); printk("\n"); + +#if defined(CONFIG_BCM_KF_FAP) && (defined(CONFIG_BCM_FAP) || defined(CONFIG_BCM_FAP_MODULE)) + printk("FAP Information:\n"); + dumpFapInfo(); + printk("\n"); +#endif + } /* @@ -681,6 +726,29 @@ return -1; } +#if defined(CONFIG_BCM_KF_MIPS_BCM963XX) && defined(CONFIG_MIPS_BCM963XX) +static int simulate_edsp_cfg(struct pt_regs *regs, unsigned int opcode) +{ + /* + * In order to run eDSP from userspace, we need to allow configuration + * of the coprocessor register $22. MIPS does not allow writing to the + * coprocessor from userspace without the ST0_CU0 bit set, but enabling + * this bit would allow userspace to access any CP0 register. Instead, + * we only need to allow the following instructions: + * + * mfc0 at,$22,3 (0x4001b003) + * mtc0 at,$22,3 (0x4081b003) + */ + if(opcode == 0x4001b003) + regs->regs[1] = read_c0_brcm_edsp(); + else if(opcode == 0x4081b003) + write_c0_brcm_edsp(regs->regs[1]); + else + return -1; /* Not ours. */ + + return 0; +} +#endif //defined(CONFIG_BCM_KF_MIPS_BCM963XX) static int simulate_sync(struct pt_regs *regs, unsigned int opcode) { if ((opcode & OPCODE) == SPEC0 && (opcode & FUNC) == SYNC) { @@ -1119,6 +1187,10 @@ if (unlikely(get_user(opcode, epc) < 0)) status = SIGSEGV; +#if defined(CONFIG_BCM_KF_MIPS_BCM963XX) && defined(CONFIG_MIPS_BCM963XX) + if (status < 0) + status = simulate_edsp_cfg(regs, opcode); +#endif //defined(CONFIG_BCM_KF_MIPS_BCM963XX) if (!cpu_has_llsc && status < 0) status = simulate_llsc(regs, opcode); @@ -1372,6 +1444,10 @@ if (unlikely(get_user(opcode, epc) < 0)) status = SIGSEGV; +#if defined(CONFIG_BCM_KF_MIPS_BCM963XX) && defined(CONFIG_MIPS_BCM963XX) + if (status < 0) + status = simulate_edsp_cfg(regs, opcode); +#endif //defined(CONFIG_BCM_KF_MIPS_BCM963XX) if (!cpu_has_llsc && status < 0) status = simulate_llsc(regs, opcode); diff -urN linux-4.1.52-orig/arch/mips/lib/iomap-pci.c linux-4.1.52/arch/mips/lib/iomap-pci.c --- linux-4.1.52-orig/arch/mips/lib/iomap-pci.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/mips/lib/iomap-pci.c 2020-02-07 01:47:54.000000000 +0800 @@ -10,6 +10,7 @@ #include #include +#if !defined(CONFIG_BCM_KF_KERN_WARNING) || defined(CONFIG_NO_GENERIC_PCI_IOPORT_MAP) void __iomem *__pci_ioport_map(struct pci_dev *dev, unsigned long port, unsigned int nr) { @@ -39,6 +40,7 @@ return (void __iomem *) (ctrl->io_map_base + port); } +#endif void pci_iounmap(struct pci_dev *dev, void __iomem * addr) { diff -urN linux-4.1.52-orig/arch/mips/Makefile linux-4.1.52/arch/mips/Makefile --- linux-4.1.52-orig/arch/mips/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/mips/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -48,6 +48,11 @@ endif endif +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) +# removing -ffunction-sections from clfags-y +cflags-y := +endif # BCM_KF # CONFIG_BCM_KF_MISC_MAKEFILE + ifdef CONFIG_FUNCTION_GRAPH_TRACER ifndef KBUILD_MCOUNT_RA_ADDRESS ifeq ($(call cc-option-yn,-mmcount-ra-address), y) @@ -119,11 +124,20 @@ undef-all += -UMIPSEL -U_MIPSEL -U__MIPSEL -U__MIPSEL__ predef-be += -DMIPSEB -D_MIPSEB -D__MIPSEB -D__MIPSEB__ predef-le += -DMIPSEL -D_MIPSEL -D__MIPSEL -D__MIPSEL__ +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) +cflags-$(CONFIG_CPU_BIG_ENDIAN) += $(shell $(CC) -dumpmachine |grep -q 'mips.*el-.*' || echo -EB $(undef-all) $(predef-be)) +cflags-$(CONFIG_CPU_LITTLE_ENDIAN) += $(shell $(CC) -dumpmachine |grep -q 'mips.*el-.*' && echo -EL $(undef-all) $(predef-le)) +else # BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) cflags-$(CONFIG_CPU_BIG_ENDIAN) += $(shell $(CC) -dumpmachine |grep -q 'mips.*el-.*' && echo -EB $(undef-all) $(predef-be)) cflags-$(CONFIG_CPU_LITTLE_ENDIAN) += $(shell $(CC) -dumpmachine |grep -q 'mips.*el-.*' || echo -EL $(undef-all) $(predef-le)) +endif # BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) cflags-$(CONFIG_SB1XXX_CORELIS) += $(call cc-option,-mno-sched-prolog) \ -fno-omit-frame-pointer + +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) +cflags-$(CONFIG_REMOTE_DEBUG) += -ggdb +endif # BCM_KF # CONFIG_BCM_KF_MISC_MAKEFILE # # CPU-dependent compiler/assembler options for optimization. # @@ -366,16 +380,33 @@ # device-trees core-$(CONFIG_BUILTIN_DTB) += arch/mips/boot/dts/ +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) +boot := arch/mips/boot +endif # BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) + %.dtb %.dtb.S %.dtb.o: | scripts +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) + $(Q)$(MAKE) $(build)=$(boot)/dts $(boot)/dts/$@ +else # BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) $(Q)$(MAKE) $(build)=arch/mips/boot/dts arch/mips/boot/dts/$@ +endif # BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) PHONY += dtbs +ifdef BCM_KF #defined (CONFIG_BCM_KF_MISC_MAKEFILE) +dtbs: prepare scripts + $(Q)$(MAKE) $(build)=$(boot)/dts +else # BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) dtbs: scripts $(Q)$(MAKE) $(build)=arch/mips/boot/dts +endif # BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) PHONY += dtbs_install dtbs_install: +ifdef BCM_KF #defined (CONFIG_BCM_KF_MISC_MAKEFILE) + $(Q)$(MAKE) $(dtbinst)=$(boot)/dts +else # BCM_KF #defined (CONFIG_BCM_KF_MISC_MAKEFILE) $(Q)$(MAKE) $(dtbinst)=arch/mips/boot/dts +endif # BCM_KF #defined (CONFIG_BCM_KF_MISC_MAKEFILE) archprepare: ifdef CONFIG_MIPS32_N32 diff -urN linux-4.1.52-orig/arch/mips/mm/c-r4k.c linux-4.1.52/arch/mips/mm/c-r4k.c --- linux-4.1.52-orig/arch/mips/mm/c-r4k.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/mips/mm/c-r4k.c 2020-02-07 01:47:54.000000000 +0800 @@ -639,10 +639,14 @@ static void r4k_flush_data_cache_page(unsigned long addr) { +#if defined(CONFIG_BCM_KF_DCACHE_SHARED) && defined(CONFIG_BCM_DCACHE_SHARED) + local_r4k_flush_data_cache_page((void *) addr); +#else if (in_atomic()) local_r4k_flush_data_cache_page((void *)addr); else r4k_on_each_cpu(local_r4k_flush_data_cache_page, (void *) addr); +#endif } struct flush_icache_range_args { diff -urN linux-4.1.52-orig/arch/mips/mm/dma-default.c linux-4.1.52/arch/mips/mm/dma-default.c --- linux-4.1.52-orig/arch/mips/mm/dma-default.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/mips/mm/dma-default.c 2020-02-07 01:47:54.000000000 +0800 @@ -72,6 +72,11 @@ return !plat_device_is_coherent(dev) && (boot_cpu_type() == CPU_R10000 || boot_cpu_type() == CPU_R12000 || +#if defined(CONFIG_BCM_KF_MIPS_BCM963XX) + boot_cpu_type() == CPU_BMIPS3300 || + boot_cpu_type() == CPU_BMIPS4350 || + boot_cpu_type() == CPU_BMIPS4380 || +#endif boot_cpu_type() == CPU_BMIPS5000); } diff -urN linux-4.1.52-orig/arch/mips/mm/gup.c linux-4.1.52/arch/mips/mm/gup.c --- linux-4.1.52-orig/arch/mips/mm/gup.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/mips/mm/gup.c 2020-02-07 01:47:54.000000000 +0800 @@ -1,3 +1,10 @@ +#if (defined(CONFIG_BCM_KF_MIPS_BCM963XX) && defined(CONFIG_MIPS_BCM963XX)) +/* get_user_pages_fast() is not working properly on BMIPS4350, some times wrong + * data is seen when the pages returned by this fucntion are used. The problem might + * be related to cache flushing. Disabling this architure related function, and + * the kernel will fallback to use of get_user_pages(), see mm/util.c + */ +#else /* * Lockless get_user_pages_fast for MIPS * @@ -314,3 +321,4 @@ } return ret; } +#endif diff -urN linux-4.1.52-orig/arch/mips/pci/Makefile linux-4.1.52/arch/mips/pci/Makefile --- linux-4.1.52-orig/arch/mips/pci/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/mips/pci/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -65,3 +65,9 @@ obj-$(CONFIG_CAVIUM_OCTEON_SOC) += msi-octeon.o obj-$(CONFIG_CPU_XLP) += msi-xlp.o endif + +ifdef BCM_KF # defined(CONFIG_BCM_KF_PCI_FIXUP) +ifeq "$(CONFIG_BCM_PCI)" "y" +EXTRA_CFLAGS += -I$(INC_BRCMDRIVER_PUB_PATH)/$(BRCM_BOARD) -I$(INC_BRCMSHARED_PUB_PATH)/$(BRCM_BOARD) +endif +endif # BCM_KF \ No newline at end of file diff -urN linux-4.1.52-orig/arch/mips/pci/pci.c linux-4.1.52/arch/mips/pci/pci.c --- linux-4.1.52-orig/arch/mips/pci/pci.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/arch/mips/pci/pci.c 2020-02-07 01:47:54.000000000 +0800 @@ -185,8 +185,15 @@ parent = &ioport_resource; if (request_resource(parent, hose->io_resource) < 0) { - release_resource(hose->mem_resource); - goto out; +#if defined(CONFIG_BCM_KF_PCI_FIXUP) + if(!((hose->io_resource->start == 0) && (hose->io_resource->end == 0))) + { +#endif + release_resource(hose->mem_resource); + goto out; +#if defined(CONFIG_BCM_KF_PCI_FIXUP) + } +#endif } *hose_tail = hose; diff -urN linux-4.1.52-orig/autoregs_d11revs_dbg.txt linux-4.1.52/autoregs_d11revs_dbg.txt --- linux-4.1.52-orig/autoregs_d11revs_dbg.txt 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/autoregs_d11revs_dbg.txt 2020-02-07 12:43:06.000000000 +0800 @@ -0,0 +1,11 @@ +D11CONF: 0x40000000 +D11CONF2: 0x20000400 +D11CONF3: 0x00000000 + +D11CONF4: 0x00000000 + +D11CONF5: 0x00000000 + +Rev ID: 30 +Rev ID: 42 +Rev ID: 61 diff -urN linux-4.1.52-orig/block/blk-core.c linux-4.1.52/block/blk-core.c --- linux-4.1.52-orig/block/blk-core.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/block/blk-core.c 2020-02-07 01:47:54.000000000 +0800 @@ -554,7 +554,11 @@ q->queue_lock = &q->__queue_lock; spin_unlock_irq(lock); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + bdi_unregister(&q->backing_dev_info); +#else bdi_destroy(&q->backing_dev_info); +#endif /* @q is and will stay empty, shutdown and put */ blk_put_queue(q); diff -urN linux-4.1.52-orig/block/blk-sysfs.c linux-4.1.52/block/blk-sysfs.c --- linux-4.1.52-orig/block/blk-sysfs.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/block/blk-sysfs.c 2020-02-07 01:47:54.000000000 +0800 @@ -501,6 +501,9 @@ struct request_queue *q = container_of(kobj, struct request_queue, kobj); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + bdi_exit(&q->backing_dev_info); +#endif blkcg_exit_queue(q); if (q->elevator) { diff -urN linux-4.1.52-orig/block/partition-generic.c linux-4.1.52/block/partition-generic.c --- linux-4.1.52-orig/block/partition-generic.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/block/partition-generic.c 2020-02-07 01:47:54.000000000 +0800 @@ -64,6 +64,16 @@ EXPORT_SYMBOL(__bdevname); +#if defined(CONFIG_BCM_KF_EMMC) +static ssize_t part_volname_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct hd_struct *p = dev_to_part(dev); + + return sprintf(buf, "%s\n", &(p->info->volname[0])); +} + +#endif /* CONFIG_BCM_KF_EMMC */ static ssize_t part_partition_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -167,6 +177,9 @@ } #endif +#if defined(CONFIG_BCM_KF_EMMC) +static DEVICE_ATTR(volname, S_IRUGO, part_volname_show, NULL); +#endif /* CONFIG_BCM_KF_EMMC */ static DEVICE_ATTR(partition, S_IRUGO, part_partition_show, NULL); static DEVICE_ATTR(start, S_IRUGO, part_start_show, NULL); static DEVICE_ATTR(size, S_IRUGO, part_size_show, NULL); @@ -182,6 +195,9 @@ #endif static struct attribute *part_attrs[] = { +#if defined(CONFIG_BCM_KF_EMMC) + &dev_attr_volname.attr, +#endif /* CONFIG_BCM_KF_EMMC */ &dev_attr_partition.attr, &dev_attr_start.attr, &dev_attr_size.attr, diff -urN linux-4.1.52-orig/crypto/testmgr.c linux-4.1.52/crypto/testmgr.c --- linux-4.1.52-orig/crypto/testmgr.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/crypto/testmgr.c 2020-02-07 01:47:54.000000000 +0800 @@ -521,7 +521,11 @@ memcpy(key, template[i].key, template[i].klen); ret = crypto_aead_setkey(tfm, key, template[i].klen); +#if defined(CONFIG_BCM_KF_IP) + if ((!ret) == template[i].fail) { +#else if (!ret == template[i].fail) { +#endif pr_err("alg: aead%s: setkey failed on test %d for %s: flags=%x\n", d, j, algo, crypto_aead_get_flags(tfm)); goto out; @@ -622,7 +626,11 @@ memcpy(key, template[i].key, template[i].klen); ret = crypto_aead_setkey(tfm, key, template[i].klen); +#if defined(CONFIG_BCM_KF_IP) + if ((!ret) == template[i].fail) { +#else if (!ret == template[i].fail) { +#endif pr_err("alg: aead%s: setkey failed on chunk test %d for %s: flags=%x\n", d, j, algo, crypto_aead_get_flags(tfm)); goto out; @@ -868,7 +876,11 @@ ret = crypto_cipher_setkey(tfm, template[i].key, template[i].klen); +#if defined(CONFIG_BCM_KF_IP) + if ((!ret) == template[i].fail) { +#else if (!ret == template[i].fail) { +#endif printk(KERN_ERR "alg: cipher: setkey failed " "on test %d for %s: flags=%x\n", j, algo, crypto_cipher_get_flags(tfm)); @@ -976,7 +988,11 @@ ret = crypto_ablkcipher_setkey(tfm, template[i].key, template[i].klen); +#if defined(CONFIG_BCM_KF_IP) + if ((!ret) == template[i].fail) { +#else if (!ret == template[i].fail) { +#endif pr_err("alg: skcipher%s: setkey failed on test %d for %s: flags=%x\n", d, j, algo, crypto_ablkcipher_get_flags(tfm)); goto out; @@ -1043,7 +1059,11 @@ ret = crypto_ablkcipher_setkey(tfm, template[i].key, template[i].klen); +#if defined(CONFIG_BCM_KF_IP) + if ((!ret) == template[i].fail) { +#else if (!ret == template[i].fail) { +#endif pr_err("alg: skcipher%s: setkey failed on chunk test %d for %s: flags=%x\n", d, j, algo, crypto_ablkcipher_get_flags(tfm)); goto out; @@ -3710,7 +3730,9 @@ return rc; notest: +#if !defined(CONFIG_BCM_KF_IP) printk(KERN_INFO "alg: No test for %s (%s)\n", alg, driver); +#endif return 0; non_fips_alg: return -EINVAL; diff -urN linux-4.1.52-orig/crypto/testmgr.h linux-4.1.52/crypto/testmgr.h --- linux-4.1.52-orig/crypto/testmgr.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/crypto/testmgr.h 2020-02-07 01:47:54.000000000 +0800 @@ -28681,7 +28681,11 @@ #define ZLIB_COMP_TEST_VECTORS 2 #define ZLIB_DECOMP_TEST_VECTORS 2 +#if defined(CONFIG_BCM_KF_IP) +static struct { +#else static const struct { +#endif struct nlattr nla; int val; } deflate_comp_params[] = { @@ -28718,7 +28722,11 @@ } }; +#if defined(CONFIG_BCM_KF_IP) +static struct { +#else static const struct { +#endif struct nlattr nla; int val; } deflate_decomp_params[] = { diff -urN linux-4.1.52-orig/Documentation/networking/ip-sysctl.txt linux-4.1.52/Documentation/networking/ip-sysctl.txt --- linux-4.1.52-orig/Documentation/networking/ip-sysctl.txt 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/Documentation/networking/ip-sysctl.txt 2020-02-07 01:47:53.000000000 +0800 @@ -223,6 +223,17 @@ Path MTU discovery (MTU probing). If MTU probing is enabled, this is the initial MSS used by the connection. +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +/* CVE-2019-11479 */ +tcp_min_snd_mss - INTEGER + TCP SYN and SYNACK messages usually advertise an ADVMSS option, + as described in RFC 1122 and RFC 6691. + If this ADVMSS option is smaller than tcp_min_snd_mss, + it is silently capped to tcp_min_snd_mss. + + Default : 48 (at least 8 bytes of payload per segment) +#endif + tcp_congestion_control - STRING Set the congestion control algorithm to be used for new connections. The algorithm "reno" is always available, but @@ -675,6 +686,20 @@ in RFC 5961 (Improving TCP's Robustness to Blind In-Window Attacks) Default: 100 +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +MPTCP variables: + +mptcp_enabled - INTEGER + Enable or disable Multipath TCP for new connections. + Possible values are: + + 0: Multipath TCP is disabled on all TCP-sockets that are newly created. + 1: Multipath TCP is enabled by default on all new TCP-sockets. Note that + existing sockets in LISTEN-state will still use regular TCP. + 2: Enables Multipath TCP only upon the request of the application + throught the socket-option MPTCP_ENABLED. + +#endif UDP variables: udp_mem - vector of 3 INTEGERs: min, pressure, max diff -urN linux-4.1.52-orig/drivers/char/hw_random/iproc-rng200.c linux-4.1.52/drivers/char/hw_random/iproc-rng200.c --- linux-4.1.52-orig/drivers/char/hw_random/iproc-rng200.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/char/hw_random/iproc-rng200.c 2020-02-07 01:47:54.000000000 +0800 @@ -143,9 +143,12 @@ if (!wait) /* Cannot wait, return immediately */ return max - num_remaining; - +#if defined(CONFIG_BCM_KF_HWRNG) + udelay(20); +#else /* Can wait, give others chance to run */ usleep_range(min(num_remaining * 10, 500U), 500); +#endif } } @@ -206,6 +209,9 @@ priv->rng.read = iproc_rng200_read, priv->rng.init = iproc_rng200_init, priv->rng.cleanup = iproc_rng200_cleanup, +#if defined(CONFIG_BCM_KF_HWRNG) + priv->rng.quality = 1024, +#endif /* BCM_KF_HWRNG */ /* Register driver */ ret = devm_hwrng_register(dev, &priv->rng); @@ -214,7 +220,7 @@ return ret; } - dev_info(dev, "hwrng registered\n"); + dev_info(dev,"hwrng registered\n"); return 0; } diff -urN linux-4.1.52-orig/drivers/char/hw_random/Kconfig linux-4.1.52/drivers/char/hw_random/Kconfig --- linux-4.1.52-orig/drivers/char/hw_random/Kconfig 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/char/hw_random/Kconfig 2020-02-07 01:47:54.000000000 +0800 @@ -103,7 +103,7 @@ config HW_RANDOM_IPROC_RNG200 tristate "Broadcom iProc RNG200 support" - depends on ARCH_BCM_IPROC + depends on ARCH_BCM_IPROC || BCM_KF_HWRNG default HW_RANDOM ---help--- This driver provides kernel-side support for the RNG200 diff -urN linux-4.1.52-orig/drivers/char/random.c linux-4.1.52/drivers/char/random.c --- linux-4.1.52-orig/drivers/char/random.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/char/random.c 2020-02-07 01:47:54.000000000 +0800 @@ -661,7 +661,9 @@ if (r == &nonblocking_pool) { prandom_reseed_late(); wake_up_interruptible(&urandom_init_wait); +#if !defined(CONFIG_BCM_KF_SUPPRESS_DEBUG) pr_notice("random: %s pool is initialized\n", r->name); +#endif } } diff -urN linux-4.1.52-orig/drivers/clk/bcm/Makefile linux-4.1.52/drivers/clk/bcm/Makefile --- linux-4.1.52-orig/drivers/clk/bcm/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/clk/bcm/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -1,4 +1,4 @@ obj-$(CONFIG_CLK_BCM_KONA) += clk-kona.o obj-$(CONFIG_CLK_BCM_KONA) += clk-kona-setup.o obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm281xx.o -obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm21664.o +obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm21664.o \ No newline at end of file diff -urN linux-4.1.52-orig/drivers/clocksource/arm_arch_timer.c linux-4.1.52/drivers/clocksource/arm_arch_timer.c --- linux-4.1.52-orig/drivers/clocksource/arm_arch_timer.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/clocksource/arm_arch_timer.c 2020-02-07 01:47:54.000000000 +0800 @@ -8,6 +8,13 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ + +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +#if defined(CONFIG_BCM947189) +#define LVCNT +#endif +#endif + #include #include #include diff -urN linux-4.1.52-orig/drivers/cpufreq/cpufreq.c linux-4.1.52/drivers/cpufreq/cpufreq.c --- linux-4.1.52-orig/drivers/cpufreq/cpufreq.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/cpufreq/cpufreq.c 2020-02-07 01:47:54.000000000 +0800 @@ -526,8 +526,13 @@ return ret; } +#if defined(CONFIG_BCM_KF_BCM63XX_CPUFREQ) +int cpufreq_set_policy(struct cpufreq_policy *policy, + struct cpufreq_policy *new_policy); +#else static int cpufreq_set_policy(struct cpufreq_policy *policy, struct cpufreq_policy *new_policy); +#endif /** * cpufreq_per_cpu_attr_write() / store_##file_name() - sysfs write access @@ -1267,6 +1272,7 @@ policy->user_policy.policy = policy->policy; policy->user_policy.governor = policy->governor; } + up_write(&policy->rwsem); kobject_uevent(&policy->kobj, KOBJ_ADD); @@ -2149,8 +2155,13 @@ * policy : current policy. * new_policy: policy to be set. */ +#if defined(CONFIG_BCM_KF_BCM63XX_CPUFREQ) +int cpufreq_set_policy(struct cpufreq_policy *policy, + struct cpufreq_policy *new_policy) +#else static int cpufreq_set_policy(struct cpufreq_policy *policy, struct cpufreq_policy *new_policy) +#endif { struct cpufreq_governor *old_gov; int ret; @@ -2241,6 +2252,87 @@ return __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS); } +#if defined(CONFIG_BCM_KF_BCM63XX_CPUFREQ) +EXPORT_SYMBOL(cpufreq_set_policy); + +// set governor, and if supported, set speed to specified fraction of max +int cpufreq_set_speed(const char *govstr, int fraction) +{ + struct cpufreq_policy *data, policy; + struct cpufreq_governor *governor; + unsigned cpu; + int rc; + + governor = find_governor(govstr); + if (governor == 0) { + pr_warn("governor %s unavailable\n", govstr); + return -ENOENT; + } + + cpu = get_cpu(); + data = cpufreq_cpu_get(cpu); + put_cpu(); + if (data == 0) { + pr_warn("policy unavailable\n"); + return -ENOENT; + } + policy = *data; + policy.governor = governor; + + down_write(&data->rwsem); + rc = cpufreq_set_policy(data, &policy); + up_write(&data->rwsem); + if (rc == 0) { + data->user_policy.policy = data->policy; + data->user_policy.governor = data->governor; + if (fraction && data->governor->store_setspeed) { + data->governor->store_setspeed(data, data->max / fraction); + } + } + cpufreq_cpu_put(data); + return rc; +} +EXPORT_SYMBOL(cpufreq_set_speed); + +// return current frequency (0 if dynamic) and max frequency +unsigned cpufreq_get_freq_max(unsigned *max) +{ + struct cpufreq_policy *data; + unsigned cpu, freq = 0; + + cpu = get_cpu(); + data = cpufreq_cpu_get(cpu); + put_cpu(); + if (max) *max = data->max; + if (data->governor && data->governor->store_setspeed) { + freq = data->cur; + } + cpufreq_cpu_put(data); + + return freq; +} +EXPORT_SYMBOL(cpufreq_get_freq_max); + +// set maximum frequency for governor +// [ XXX also set current frequency for userspace governor? ] +void cpufreq_set_freq_max(unsigned maxdiv) +{ + struct cpufreq_policy *data; + unsigned cpu; + + cpu = get_cpu(); + data = cpufreq_cpu_get(cpu); + put_cpu(); + + if (data) { + data->user_policy.max = data->cpuinfo.max_freq / (maxdiv ?: 1); + cpufreq_update_policy(data->cpu); + cpufreq_cpu_put(data); + } +} +EXPORT_SYMBOL(cpufreq_set_freq_max); +#endif + /** * cpufreq_update_policy - re-evaluate an existing cpufreq policy * @cpu: CPU which shall be re-evaluated diff -urN linux-4.1.52-orig/drivers/cpufreq/cpufreq_interactive.c linux-4.1.52/drivers/cpufreq/cpufreq_interactive.c --- linux-4.1.52-orig/drivers/cpufreq/cpufreq_interactive.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/cpufreq/cpufreq_interactive.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,1342 @@ +#if defined CONFIG_BCM_KF_INTERACTIVE +/* + * drivers/cpufreq/cpufreq_interactive.c + * + * Copyright (C) 2010 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + * Author: Mike Chan (mike@android.com) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CREATE_TRACE_POINTS +#include + +struct cpufreq_interactive_cpuinfo { + struct timer_list cpu_timer; + struct timer_list cpu_slack_timer; + spinlock_t load_lock; /* protects the next 4 fields */ + u64 time_in_idle; + u64 time_in_idle_timestamp; + u64 cputime_speedadj; + u64 cputime_speedadj_timestamp; + struct cpufreq_policy *policy; + struct cpufreq_frequency_table *freq_table; + spinlock_t target_freq_lock; /*protects target freq */ + unsigned int target_freq; + unsigned int floor_freq; + u64 pol_floor_val_time; /* policy floor_validate_time */ + u64 loc_floor_val_time; /* per-cpu floor_validate_time */ + u64 pol_hispeed_val_time; /* policy hispeed_validate_time */ + u64 loc_hispeed_val_time; /* per-cpu hispeed_validate_time */ + struct rw_semaphore enable_sem; + int governor_enabled; +}; + +static DEFINE_PER_CPU(struct cpufreq_interactive_cpuinfo, cpuinfo); + +/* realtime thread handles frequency scaling */ +static struct task_struct *speedchange_task; +static cpumask_t speedchange_cpumask; +static spinlock_t speedchange_cpumask_lock; +static struct mutex gov_lock; + +/* Target load. Lower values result in higher CPU speeds. */ +#ifndef DEFAULT_TARGET_LOAD +#define DEFAULT_TARGET_LOAD 90 +#endif +static unsigned int default_target_loads[] = {DEFAULT_TARGET_LOAD}; + +#define DEFAULT_TIMER_RATE (20 * USEC_PER_MSEC) +#define DEFAULT_ABOVE_HISPEED_DELAY DEFAULT_TIMER_RATE +static unsigned int default_above_hispeed_delay[] = { + DEFAULT_ABOVE_HISPEED_DELAY }; + +struct cpufreq_interactive_tunables { + int usage_count; + /* Hi speed to bump to from lo speed when load burst (default max) */ + unsigned int hispeed_freq; + /* Go to hi speed when CPU load at or above this value. */ +#define DEFAULT_GO_HISPEED_LOAD 99 + unsigned long go_hispeed_load; + /* Target load. Lower values result in higher CPU speeds. */ + spinlock_t target_loads_lock; + unsigned int *target_loads; + int ntarget_loads; + /* + * The minimum amount of time to spend at a frequency before we can ramp + * down. + */ +#define DEFAULT_MIN_SAMPLE_TIME (80 * USEC_PER_MSEC) + unsigned long min_sample_time; + /* + * The sample rate of the timer used to increase frequency + */ + unsigned long timer_rate; + /* + * Wait this long before raising speed above hispeed, by default a + * single timer interval. + */ + spinlock_t above_hispeed_delay_lock; + unsigned int *above_hispeed_delay; + int nabove_hispeed_delay; + /* Non-zero means indefinite speed boost active */ + int boost_val; + /* Duration of a boot pulse in usecs */ + int boostpulse_duration_val; + /* End time of boost pulse in ktime converted to usecs */ + u64 boostpulse_endtime; + bool boosted; + /* + * Max additional time to wait in idle, beyond timer_rate, at speeds + * above minimum before wakeup to reduce speed, or -1 if unnecessary. + */ +#define DEFAULT_TIMER_SLACK (4 * DEFAULT_TIMER_RATE) + int timer_slack_val; + bool io_is_busy; +}; + +/* For cases where we have single governor instance for system */ +static struct cpufreq_interactive_tunables *common_tunables; + +static struct attribute_group *get_sysfs_attr(void); + +static void cpufreq_interactive_timer_resched( + struct cpufreq_interactive_cpuinfo *pcpu) +{ + struct cpufreq_interactive_tunables *tunables = + pcpu->policy->governor_data; + unsigned long expires; + unsigned long flags; + + spin_lock_irqsave(&pcpu->load_lock, flags); + pcpu->time_in_idle = + get_cpu_idle_time(smp_processor_id(), + &pcpu->time_in_idle_timestamp, + tunables->io_is_busy); + pcpu->cputime_speedadj = 0; + pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp; + expires = jiffies + usecs_to_jiffies(tunables->timer_rate); + mod_timer_pinned(&pcpu->cpu_timer, expires); + + if (tunables->timer_slack_val >= 0 && + pcpu->target_freq > pcpu->policy->min) { + expires += usecs_to_jiffies(tunables->timer_slack_val); + mod_timer_pinned(&pcpu->cpu_slack_timer, expires); + } + + spin_unlock_irqrestore(&pcpu->load_lock, flags); +} + +/* The caller shall take enable_sem write semaphore to avoid any timer race. + * The cpu_timer and cpu_slack_timer must be deactivated when calling this + * function. + */ +static void cpufreq_interactive_timer_start( + struct cpufreq_interactive_tunables *tunables, int cpu) +{ + struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu); + unsigned long expires = jiffies + + usecs_to_jiffies(tunables->timer_rate); + unsigned long flags; + + pcpu->cpu_timer.expires = expires; + add_timer_on(&pcpu->cpu_timer, cpu); + if (tunables->timer_slack_val >= 0 && + pcpu->target_freq > pcpu->policy->min) { + expires += usecs_to_jiffies(tunables->timer_slack_val); + pcpu->cpu_slack_timer.expires = expires; + add_timer_on(&pcpu->cpu_slack_timer, cpu); + } + + spin_lock_irqsave(&pcpu->load_lock, flags); + pcpu->time_in_idle = + get_cpu_idle_time(cpu, &pcpu->time_in_idle_timestamp, + tunables->io_is_busy); + pcpu->cputime_speedadj = 0; + pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp; + spin_unlock_irqrestore(&pcpu->load_lock, flags); +} + +static unsigned int freq_to_above_hispeed_delay( + struct cpufreq_interactive_tunables *tunables, + unsigned int freq) +{ + int i; + unsigned int ret; + unsigned long flags; + + spin_lock_irqsave(&tunables->above_hispeed_delay_lock, flags); + + for (i = 0; i < tunables->nabove_hispeed_delay - 1 && + freq >= tunables->above_hispeed_delay[i+1]; i += 2) + ; + + ret = tunables->above_hispeed_delay[i]; + spin_unlock_irqrestore(&tunables->above_hispeed_delay_lock, flags); + return ret; +} + +static unsigned int freq_to_targetload( + struct cpufreq_interactive_tunables *tunables, unsigned int freq) +{ + int i; + unsigned int ret; + unsigned long flags; + + spin_lock_irqsave(&tunables->target_loads_lock, flags); + + for (i = 0; i < tunables->ntarget_loads - 1 && + freq >= tunables->target_loads[i+1]; i += 2) + ; + + ret = tunables->target_loads[i]; + spin_unlock_irqrestore(&tunables->target_loads_lock, flags); + return ret; +} + +/* + * If increasing frequencies never map to a lower target load then + * choose_freq() will find the minimum frequency that does not exceed its + * target load given the current load. + */ +static unsigned int choose_freq(struct cpufreq_interactive_cpuinfo *pcpu, + unsigned int loadadjfreq) +{ + unsigned int freq = pcpu->policy->cur; + unsigned int prevfreq, freqmin, freqmax; + unsigned int tl; + int index; + + freqmin = 0; + freqmax = UINT_MAX; + + do { + prevfreq = freq; + tl = freq_to_targetload(pcpu->policy->governor_data, freq); + + /* + * Find the lowest frequency where the computed load is less + * than or equal to the target load. + */ + + if (cpufreq_frequency_table_target( + pcpu->policy, pcpu->freq_table, loadadjfreq / tl, + CPUFREQ_RELATION_L, &index)) + break; + freq = pcpu->freq_table[index].frequency; + + if (freq > prevfreq) { + /* The previous frequency is too low. */ + freqmin = prevfreq; + + if (freq >= freqmax) { + /* + * Find the highest frequency that is less + * than freqmax. + */ + if (cpufreq_frequency_table_target( + pcpu->policy, pcpu->freq_table, + freqmax - 1, CPUFREQ_RELATION_H, + &index)) + break; + freq = pcpu->freq_table[index].frequency; + + if (freq == freqmin) { + /* + * The first frequency below freqmax + * has already been found to be too + * low. freqmax is the lowest speed + * we found that is fast enough. + */ + freq = freqmax; + break; + } + } + } else if (freq < prevfreq) { + /* The previous frequency is high enough. */ + freqmax = prevfreq; + + if (freq <= freqmin) { + /* + * Find the lowest frequency that is higher + * than freqmin. + */ + if (cpufreq_frequency_table_target( + pcpu->policy, pcpu->freq_table, + freqmin + 1, CPUFREQ_RELATION_L, + &index)) + break; + freq = pcpu->freq_table[index].frequency; + + /* + * If freqmax is the first frequency above + * freqmin then we have already found that + * this speed is fast enough. + */ + if (freq == freqmax) + break; + } + } + + /* If same frequency chosen as previous then done. */ + } while (freq != prevfreq); + + return freq; +} + +static u64 update_load(int cpu) +{ + struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu); + struct cpufreq_interactive_tunables *tunables = + pcpu->policy->governor_data; + u64 now; + u64 now_idle; + unsigned int delta_idle; + unsigned int delta_time; + u64 active_time; + + now_idle = get_cpu_idle_time(cpu, &now, tunables->io_is_busy); + delta_idle = (unsigned int)(now_idle - pcpu->time_in_idle); + delta_time = (unsigned int)(now - pcpu->time_in_idle_timestamp); + + if (delta_time <= delta_idle) + active_time = 0; + else + active_time = delta_time - delta_idle; + + pcpu->cputime_speedadj += active_time * pcpu->policy->cur; + + pcpu->time_in_idle = now_idle; + pcpu->time_in_idle_timestamp = now; + return now; +} + +static void cpufreq_interactive_timer(unsigned long data) +{ + u64 now; + unsigned int delta_time; + u64 cputime_speedadj; + int cpu_load; + struct cpufreq_interactive_cpuinfo *pcpu = + &per_cpu(cpuinfo, data); + struct cpufreq_interactive_tunables *tunables = + pcpu->policy->governor_data; + unsigned int new_freq; + unsigned int loadadjfreq; + unsigned int index; + unsigned long flags; + u64 max_fvtime; + + if (!down_read_trylock(&pcpu->enable_sem)) + return; + if (!pcpu->governor_enabled) + goto exit; + + spin_lock_irqsave(&pcpu->load_lock, flags); + now = update_load(data); + delta_time = (unsigned int)(now - pcpu->cputime_speedadj_timestamp); + cputime_speedadj = pcpu->cputime_speedadj; + spin_unlock_irqrestore(&pcpu->load_lock, flags); + + if (!delta_time) /* skip WARN_ON_ONCE() for now */ + goto rearm; + + spin_lock_irqsave(&pcpu->target_freq_lock, flags); + do_div(cputime_speedadj, delta_time); + loadadjfreq = (unsigned int)cputime_speedadj * 100; + cpu_load = loadadjfreq / pcpu->policy->cur; + tunables->boosted = tunables->boost_val || now < tunables->boostpulse_endtime; + + if (cpu_load >= tunables->go_hispeed_load || tunables->boosted) { + if (pcpu->policy->cur < tunables->hispeed_freq) { + new_freq = tunables->hispeed_freq; + } else { + new_freq = choose_freq(pcpu, loadadjfreq); + + if (new_freq < tunables->hispeed_freq) + new_freq = tunables->hispeed_freq; + } + } else { + new_freq = choose_freq(pcpu, loadadjfreq); + if (new_freq > tunables->hispeed_freq && + pcpu->policy->cur < tunables->hispeed_freq) + new_freq = tunables->hispeed_freq; + } + + if (pcpu->policy->cur >= tunables->hispeed_freq && + new_freq > pcpu->policy->cur && + now - pcpu->pol_hispeed_val_time < + freq_to_above_hispeed_delay(tunables, pcpu->policy->cur)) { + trace_cpufreq_interactive_notyet( + data, cpu_load, pcpu->target_freq, + pcpu->policy->cur, new_freq); + spin_unlock_irqrestore(&pcpu->target_freq_lock, flags); + goto rearm; + } + + pcpu->loc_hispeed_val_time = now; + + if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, + new_freq, CPUFREQ_RELATION_L, + &index)) { + spin_unlock_irqrestore(&pcpu->target_freq_lock, flags); + goto rearm; + } + + new_freq = pcpu->freq_table[index].frequency; + + /* + * Do not scale below floor_freq unless we have been at or above the + * floor frequency for the minimum sample time since last validated. + */ + max_fvtime = max(pcpu->pol_floor_val_time, pcpu->loc_floor_val_time); + if (new_freq < pcpu->floor_freq && + pcpu->target_freq >= pcpu->policy->cur) { + if (now - max_fvtime < tunables->min_sample_time) { + trace_cpufreq_interactive_notyet( + data, cpu_load, pcpu->target_freq, + pcpu->policy->cur, new_freq); + spin_unlock_irqrestore(&pcpu->target_freq_lock, flags); + goto rearm; + } + } + + /* + * Update the timestamp for checking whether speed has been held at + * or above the selected frequency for a minimum of min_sample_time, + * if not boosted to hispeed_freq. If boosted to hispeed_freq then we + * allow the speed to drop as soon as the boostpulse duration expires + * (or the indefinite boost is turned off). + */ + + if (!tunables->boosted || new_freq > tunables->hispeed_freq) { + pcpu->floor_freq = new_freq; + if (pcpu->target_freq >= pcpu->policy->cur || + new_freq >= pcpu->policy->cur) + pcpu->loc_floor_val_time = now; + } + + if (pcpu->target_freq == new_freq && + pcpu->target_freq <= pcpu->policy->cur) { + trace_cpufreq_interactive_already( + data, cpu_load, pcpu->target_freq, + pcpu->policy->cur, new_freq); + spin_unlock_irqrestore(&pcpu->target_freq_lock, flags); + goto rearm; + } + + trace_cpufreq_interactive_target(data, cpu_load, pcpu->target_freq, + pcpu->policy->cur, new_freq); + + pcpu->target_freq = new_freq; + spin_unlock_irqrestore(&pcpu->target_freq_lock, flags); + spin_lock_irqsave(&speedchange_cpumask_lock, flags); + cpumask_set_cpu(data, &speedchange_cpumask); + spin_unlock_irqrestore(&speedchange_cpumask_lock, flags); + wake_up_process(speedchange_task); + +rearm: + if (!timer_pending(&pcpu->cpu_timer)) + cpufreq_interactive_timer_resched(pcpu); + +exit: + up_read(&pcpu->enable_sem); + return; +} + +static void cpufreq_interactive_idle_end(void) +{ + struct cpufreq_interactive_cpuinfo *pcpu = + &per_cpu(cpuinfo, smp_processor_id()); + + if (!down_read_trylock(&pcpu->enable_sem)) + return; + if (!pcpu->governor_enabled) { + up_read(&pcpu->enable_sem); + return; + } + + /* Arm the timer for 1-2 ticks later if not already. */ + if (!timer_pending(&pcpu->cpu_timer)) { + cpufreq_interactive_timer_resched(pcpu); + } else if (time_after_eq(jiffies, pcpu->cpu_timer.expires)) { + del_timer(&pcpu->cpu_timer); + del_timer(&pcpu->cpu_slack_timer); + cpufreq_interactive_timer(smp_processor_id()); + } + + up_read(&pcpu->enable_sem); +} + +static int cpufreq_interactive_speedchange_task(void *data) +{ + unsigned int cpu; + cpumask_t tmp_mask; + unsigned long flags; + struct cpufreq_interactive_cpuinfo *pcpu; + + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&speedchange_cpumask_lock, flags); + + if (cpumask_empty(&speedchange_cpumask)) { + spin_unlock_irqrestore(&speedchange_cpumask_lock, + flags); + schedule(); + + if (kthread_should_stop()) + break; + + spin_lock_irqsave(&speedchange_cpumask_lock, flags); + } + + set_current_state(TASK_RUNNING); + tmp_mask = speedchange_cpumask; + cpumask_clear(&speedchange_cpumask); + spin_unlock_irqrestore(&speedchange_cpumask_lock, flags); + + for_each_cpu(cpu, &tmp_mask) { + unsigned int j; + unsigned int max_freq = 0; + struct cpufreq_interactive_cpuinfo *pjcpu; + u64 hvt = ~0ULL, fvt = 0; + + pcpu = &per_cpu(cpuinfo, cpu); + if (!down_read_trylock(&pcpu->enable_sem)) + continue; + if (!pcpu->governor_enabled) { + up_read(&pcpu->enable_sem); + continue; + } + + for_each_cpu(j, pcpu->policy->cpus) { + pjcpu = &per_cpu(cpuinfo, j); + + fvt = max(fvt, pjcpu->loc_floor_val_time); + if (pjcpu->target_freq > max_freq) { + max_freq = pjcpu->target_freq; + hvt = pjcpu->loc_hispeed_val_time; + } else if (pjcpu->target_freq == max_freq) { + hvt = min(hvt, pjcpu->loc_hispeed_val_time); + } + } + for_each_cpu(j, pcpu->policy->cpus) { + pjcpu = &per_cpu(cpuinfo, j); + pjcpu->pol_floor_val_time = fvt; + } + + if (max_freq != pcpu->policy->cur) { + __cpufreq_driver_target(pcpu->policy, + max_freq, + CPUFREQ_RELATION_H); + for_each_cpu(j, pcpu->policy->cpus) { + pjcpu = &per_cpu(cpuinfo, j); + pjcpu->pol_hispeed_val_time = hvt; + } + } + trace_cpufreq_interactive_setspeed(cpu, + pcpu->target_freq, + pcpu->policy->cur); + + up_read(&pcpu->enable_sem); + } + } + + return 0; +} + +static void cpufreq_interactive_boost(struct cpufreq_interactive_tunables *tunables) +{ + int i; + int anyboost = 0; + unsigned long flags[2]; + struct cpufreq_interactive_cpuinfo *pcpu; + + tunables->boosted = true; + + spin_lock_irqsave(&speedchange_cpumask_lock, flags[0]); + + for_each_online_cpu(i) { + pcpu = &per_cpu(cpuinfo, i); + if (tunables != pcpu->policy->governor_data) + continue; + + spin_lock_irqsave(&pcpu->target_freq_lock, flags[1]); + if (pcpu->target_freq < tunables->hispeed_freq) { + pcpu->target_freq = tunables->hispeed_freq; + cpumask_set_cpu(i, &speedchange_cpumask); + pcpu->pol_hispeed_val_time = + ktime_to_us(ktime_get()); + anyboost = 1; + } + spin_unlock_irqrestore(&pcpu->target_freq_lock, flags[1]); + } + + spin_unlock_irqrestore(&speedchange_cpumask_lock, flags[0]); + + if (anyboost) + wake_up_process(speedchange_task); +} + +static int cpufreq_interactive_notifier( + struct notifier_block *nb, unsigned long val, void *data) +{ + struct cpufreq_freqs *freq = data; + struct cpufreq_interactive_cpuinfo *pcpu; + int cpu; + unsigned long flags; + + if (val == CPUFREQ_POSTCHANGE) { + pcpu = &per_cpu(cpuinfo, freq->cpu); + if (!down_read_trylock(&pcpu->enable_sem)) + return 0; + if (!pcpu->governor_enabled) { + up_read(&pcpu->enable_sem); + return 0; + } + + for_each_cpu(cpu, pcpu->policy->cpus) { + struct cpufreq_interactive_cpuinfo *pjcpu = + &per_cpu(cpuinfo, cpu); + if (cpu != freq->cpu) { + if (!down_read_trylock(&pjcpu->enable_sem)) + continue; + if (!pjcpu->governor_enabled) { + up_read(&pjcpu->enable_sem); + continue; + } + } + spin_lock_irqsave(&pjcpu->load_lock, flags); + update_load(cpu); + spin_unlock_irqrestore(&pjcpu->load_lock, flags); + if (cpu != freq->cpu) + up_read(&pjcpu->enable_sem); + } + + up_read(&pcpu->enable_sem); + } + return 0; +} + +static struct notifier_block cpufreq_notifier_block = { + .notifier_call = cpufreq_interactive_notifier, +}; + +static unsigned int *get_tokenized_data(const char *buf, int *num_tokens) +{ + const char *cp; + int i; + int ntokens = 1; + unsigned int *tokenized_data; + int err = -EINVAL; + + cp = buf; + while ((cp = strpbrk(cp + 1, " :"))) + ntokens++; + + if (!(ntokens & 0x1)) + goto err; + + tokenized_data = kmalloc(ntokens * sizeof(unsigned int), GFP_KERNEL); + if (!tokenized_data) { + err = -ENOMEM; + goto err; + } + + cp = buf; + i = 0; + while (i < ntokens) { + if (sscanf(cp, "%u", &tokenized_data[i++]) != 1) + goto err_kfree; + + cp = strpbrk(cp, " :"); + if (!cp) + break; + cp++; + } + + if (i != ntokens) + goto err_kfree; + + *num_tokens = ntokens; + return tokenized_data; + +err_kfree: + kfree(tokenized_data); +err: + return ERR_PTR(err); +} + +static ssize_t show_target_loads( + struct cpufreq_interactive_tunables *tunables, + char *buf) +{ + int i; + ssize_t ret = 0; + unsigned long flags; + + spin_lock_irqsave(&tunables->target_loads_lock, flags); + + for (i = 0; i < tunables->ntarget_loads; i++) + ret += sprintf(buf + ret, "%u%s", tunables->target_loads[i], + i & 0x1 ? ":" : " "); + + sprintf(buf + ret - 1, "\n"); + spin_unlock_irqrestore(&tunables->target_loads_lock, flags); + return ret; +} + +static ssize_t store_target_loads( + struct cpufreq_interactive_tunables *tunables, + const char *buf, size_t count) +{ + int ntokens; + unsigned int *new_target_loads = NULL; + unsigned long flags; + + new_target_loads = get_tokenized_data(buf, &ntokens); + if (IS_ERR(new_target_loads)) + return PTR_RET(new_target_loads); + + spin_lock_irqsave(&tunables->target_loads_lock, flags); + if (tunables->target_loads != default_target_loads) + kfree(tunables->target_loads); + tunables->target_loads = new_target_loads; + tunables->ntarget_loads = ntokens; + spin_unlock_irqrestore(&tunables->target_loads_lock, flags); + return count; +} + +static ssize_t show_above_hispeed_delay( + struct cpufreq_interactive_tunables *tunables, char *buf) +{ + int i; + ssize_t ret = 0; + unsigned long flags; + + spin_lock_irqsave(&tunables->above_hispeed_delay_lock, flags); + + for (i = 0; i < tunables->nabove_hispeed_delay; i++) + ret += sprintf(buf + ret, "%u%s", + tunables->above_hispeed_delay[i], + i & 0x1 ? ":" : " "); + + sprintf(buf + ret - 1, "\n"); + spin_unlock_irqrestore(&tunables->above_hispeed_delay_lock, flags); + return ret; +} + +static ssize_t store_above_hispeed_delay( + struct cpufreq_interactive_tunables *tunables, + const char *buf, size_t count) +{ + int ntokens; + unsigned int *new_above_hispeed_delay = NULL; + unsigned long flags; + + new_above_hispeed_delay = get_tokenized_data(buf, &ntokens); + if (IS_ERR(new_above_hispeed_delay)) + return PTR_RET(new_above_hispeed_delay); + + spin_lock_irqsave(&tunables->above_hispeed_delay_lock, flags); + if (tunables->above_hispeed_delay != default_above_hispeed_delay) + kfree(tunables->above_hispeed_delay); + tunables->above_hispeed_delay = new_above_hispeed_delay; + tunables->nabove_hispeed_delay = ntokens; + spin_unlock_irqrestore(&tunables->above_hispeed_delay_lock, flags); + return count; + +} + +static ssize_t show_hispeed_freq(struct cpufreq_interactive_tunables *tunables, + char *buf) +{ + return sprintf(buf, "%u\n", tunables->hispeed_freq); +} + +static ssize_t store_hispeed_freq(struct cpufreq_interactive_tunables *tunables, + const char *buf, size_t count) +{ + int ret; + long unsigned int val; + + ret = kstrtoul(buf, 0, &val); + if (ret < 0) + return ret; + tunables->hispeed_freq = val; + return count; +} + +static ssize_t show_go_hispeed_load(struct cpufreq_interactive_tunables + *tunables, char *buf) +{ + return sprintf(buf, "%lu\n", tunables->go_hispeed_load); +} + +static ssize_t store_go_hispeed_load(struct cpufreq_interactive_tunables + *tunables, const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = kstrtoul(buf, 0, &val); + if (ret < 0) + return ret; + tunables->go_hispeed_load = val; + return count; +} + +static ssize_t show_min_sample_time(struct cpufreq_interactive_tunables + *tunables, char *buf) +{ + return sprintf(buf, "%lu\n", tunables->min_sample_time); +} + +static ssize_t store_min_sample_time(struct cpufreq_interactive_tunables + *tunables, const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = kstrtoul(buf, 0, &val); + if (ret < 0) + return ret; + tunables->min_sample_time = val; + return count; +} + +static ssize_t show_timer_rate(struct cpufreq_interactive_tunables *tunables, + char *buf) +{ + return sprintf(buf, "%lu\n", tunables->timer_rate); +} + +static ssize_t store_timer_rate(struct cpufreq_interactive_tunables *tunables, + const char *buf, size_t count) +{ + int ret; + unsigned long val, val_round; + + ret = kstrtoul(buf, 0, &val); + if (ret < 0) + return ret; + + val_round = jiffies_to_usecs(usecs_to_jiffies(val)); + if (val != val_round) + pr_warn("timer_rate not aligned to jiffy. Rounded up to %lu\n", + val_round); + + tunables->timer_rate = val_round; + return count; +} + +static ssize_t show_timer_slack(struct cpufreq_interactive_tunables *tunables, + char *buf) +{ + return sprintf(buf, "%d\n", tunables->timer_slack_val); +} + +static ssize_t store_timer_slack(struct cpufreq_interactive_tunables *tunables, + const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = kstrtol(buf, 10, &val); + if (ret < 0) + return ret; + + tunables->timer_slack_val = val; + return count; +} + +static ssize_t show_boost(struct cpufreq_interactive_tunables *tunables, + char *buf) +{ + return sprintf(buf, "%d\n", tunables->boost_val); +} + +static ssize_t store_boost(struct cpufreq_interactive_tunables *tunables, + const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = kstrtoul(buf, 0, &val); + if (ret < 0) + return ret; + + tunables->boost_val = val; + + if (tunables->boost_val) { + trace_cpufreq_interactive_boost("on"); + if (!tunables->boosted) + cpufreq_interactive_boost(tunables); + } else { + tunables->boostpulse_endtime = ktime_to_us(ktime_get()); + trace_cpufreq_interactive_unboost("off"); + } + + return count; +} + +static ssize_t store_boostpulse(struct cpufreq_interactive_tunables *tunables, + const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = kstrtoul(buf, 0, &val); + if (ret < 0) + return ret; + + tunables->boostpulse_endtime = ktime_to_us(ktime_get()) + + tunables->boostpulse_duration_val; + trace_cpufreq_interactive_boost("pulse"); + if (!tunables->boosted) + cpufreq_interactive_boost(tunables); + return count; +} + +static ssize_t show_boostpulse_duration(struct cpufreq_interactive_tunables + *tunables, char *buf) +{ + return sprintf(buf, "%d\n", tunables->boostpulse_duration_val); +} + +static ssize_t store_boostpulse_duration(struct cpufreq_interactive_tunables + *tunables, const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = kstrtoul(buf, 0, &val); + if (ret < 0) + return ret; + + tunables->boostpulse_duration_val = val; + return count; +} + +static ssize_t show_io_is_busy(struct cpufreq_interactive_tunables *tunables, + char *buf) +{ + return sprintf(buf, "%u\n", tunables->io_is_busy); +} + +static ssize_t store_io_is_busy(struct cpufreq_interactive_tunables *tunables, + const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = kstrtoul(buf, 0, &val); + if (ret < 0) + return ret; + tunables->io_is_busy = val; + return count; +} + +/* + * Create show/store routines + * - sys: One governor instance for complete SYSTEM + * - pol: One governor instance per struct cpufreq_policy + */ +#define show_gov_pol_sys(file_name) \ +static ssize_t show_##file_name##_gov_sys \ +(struct kobject *kobj, struct attribute *attr, char *buf) \ +{ \ + return show_##file_name(common_tunables, buf); \ +} \ + \ +static ssize_t show_##file_name##_gov_pol \ +(struct cpufreq_policy *policy, char *buf) \ +{ \ + return show_##file_name(policy->governor_data, buf); \ +} + +#define store_gov_pol_sys(file_name) \ +static ssize_t store_##file_name##_gov_sys \ +(struct kobject *kobj, struct attribute *attr, const char *buf, \ + size_t count) \ +{ \ + return store_##file_name(common_tunables, buf, count); \ +} \ + \ +static ssize_t store_##file_name##_gov_pol \ +(struct cpufreq_policy *policy, const char *buf, size_t count) \ +{ \ + return store_##file_name(policy->governor_data, buf, count); \ +} + +#define show_store_gov_pol_sys(file_name) \ +show_gov_pol_sys(file_name); \ +store_gov_pol_sys(file_name) + +show_store_gov_pol_sys(target_loads); +show_store_gov_pol_sys(above_hispeed_delay); +show_store_gov_pol_sys(hispeed_freq); +show_store_gov_pol_sys(go_hispeed_load); +show_store_gov_pol_sys(min_sample_time); +show_store_gov_pol_sys(timer_rate); +show_store_gov_pol_sys(timer_slack); +show_store_gov_pol_sys(boost); +store_gov_pol_sys(boostpulse); +show_store_gov_pol_sys(boostpulse_duration); +show_store_gov_pol_sys(io_is_busy); + +#define gov_sys_attr_rw(_name) \ +static struct global_attr _name##_gov_sys = \ +__ATTR(_name, 0644, show_##_name##_gov_sys, store_##_name##_gov_sys) + +#define gov_pol_attr_rw(_name) \ +static struct freq_attr _name##_gov_pol = \ +__ATTR(_name, 0644, show_##_name##_gov_pol, store_##_name##_gov_pol) + +#define gov_sys_pol_attr_rw(_name) \ + gov_sys_attr_rw(_name); \ + gov_pol_attr_rw(_name) + +gov_sys_pol_attr_rw(target_loads); +gov_sys_pol_attr_rw(above_hispeed_delay); +gov_sys_pol_attr_rw(hispeed_freq); +gov_sys_pol_attr_rw(go_hispeed_load); +gov_sys_pol_attr_rw(min_sample_time); +gov_sys_pol_attr_rw(timer_rate); +gov_sys_pol_attr_rw(timer_slack); +gov_sys_pol_attr_rw(boost); +gov_sys_pol_attr_rw(boostpulse_duration); +gov_sys_pol_attr_rw(io_is_busy); + +static struct global_attr boostpulse_gov_sys = + __ATTR(boostpulse, 0200, NULL, store_boostpulse_gov_sys); + +static struct freq_attr boostpulse_gov_pol = + __ATTR(boostpulse, 0200, NULL, store_boostpulse_gov_pol); + +/* One Governor instance for entire system */ +static struct attribute *interactive_attributes_gov_sys[] = { + &target_loads_gov_sys.attr, + &above_hispeed_delay_gov_sys.attr, + &hispeed_freq_gov_sys.attr, + &go_hispeed_load_gov_sys.attr, + &min_sample_time_gov_sys.attr, + &timer_rate_gov_sys.attr, + &timer_slack_gov_sys.attr, + &boost_gov_sys.attr, + &boostpulse_gov_sys.attr, + &boostpulse_duration_gov_sys.attr, + &io_is_busy_gov_sys.attr, + NULL, +}; + +static struct attribute_group interactive_attr_group_gov_sys = { + .attrs = interactive_attributes_gov_sys, + .name = "interactive", +}; + +/* Per policy governor instance */ +static struct attribute *interactive_attributes_gov_pol[] = { + &target_loads_gov_pol.attr, + &above_hispeed_delay_gov_pol.attr, + &hispeed_freq_gov_pol.attr, + &go_hispeed_load_gov_pol.attr, + &min_sample_time_gov_pol.attr, + &timer_rate_gov_pol.attr, + &timer_slack_gov_pol.attr, + &boost_gov_pol.attr, + &boostpulse_gov_pol.attr, + &boostpulse_duration_gov_pol.attr, + &io_is_busy_gov_pol.attr, + NULL, +}; + +static struct attribute_group interactive_attr_group_gov_pol = { + .attrs = interactive_attributes_gov_pol, + .name = "interactive", +}; + +static struct attribute_group *get_sysfs_attr(void) +{ + if (have_governor_per_policy()) + return &interactive_attr_group_gov_pol; + else + return &interactive_attr_group_gov_sys; +} + +static int cpufreq_interactive_idle_notifier(struct notifier_block *nb, + unsigned long val, + void *data) +{ + if (val == IDLE_END) + cpufreq_interactive_idle_end(); + + return 0; +} + +static struct notifier_block cpufreq_interactive_idle_nb = { + .notifier_call = cpufreq_interactive_idle_notifier, +}; + +static int cpufreq_governor_interactive(struct cpufreq_policy *policy, + unsigned int event) +{ + int rc; + unsigned int j; + struct cpufreq_interactive_cpuinfo *pcpu; + struct cpufreq_frequency_table *freq_table; + struct cpufreq_interactive_tunables *tunables; + unsigned long flags; + + if (have_governor_per_policy()) + tunables = policy->governor_data; + else + tunables = common_tunables; + + WARN_ON(!tunables && (event != CPUFREQ_GOV_POLICY_INIT)); + + switch (event) { + case CPUFREQ_GOV_POLICY_INIT: + if (have_governor_per_policy()) { + WARN_ON(tunables); + } else if (tunables) { + tunables->usage_count++; + policy->governor_data = tunables; + return 0; + } + + tunables = kzalloc(sizeof(*tunables), GFP_KERNEL); + if (!tunables) { + pr_err("%s: POLICY_INIT: kzalloc failed\n", __func__); + return -ENOMEM; + } + + tunables->usage_count = 1; + tunables->above_hispeed_delay = default_above_hispeed_delay; + tunables->nabove_hispeed_delay = + ARRAY_SIZE(default_above_hispeed_delay); + tunables->go_hispeed_load = DEFAULT_GO_HISPEED_LOAD; + tunables->target_loads = default_target_loads; + tunables->ntarget_loads = ARRAY_SIZE(default_target_loads); + tunables->min_sample_time = DEFAULT_MIN_SAMPLE_TIME; + tunables->timer_rate = DEFAULT_TIMER_RATE; + tunables->boostpulse_duration_val = DEFAULT_MIN_SAMPLE_TIME; + tunables->timer_slack_val = DEFAULT_TIMER_SLACK; + + spin_lock_init(&tunables->target_loads_lock); + spin_lock_init(&tunables->above_hispeed_delay_lock); + + policy->governor_data = tunables; + if (!have_governor_per_policy()) { + common_tunables = tunables; + WARN_ON(cpufreq_get_global_kobject()); + } + + rc = sysfs_create_group(get_governor_parent_kobj(policy), + get_sysfs_attr()); + if (rc) { + kfree(tunables); + policy->governor_data = NULL; + if (!have_governor_per_policy()) { + common_tunables = NULL; + cpufreq_put_global_kobject(); + } + return rc; + } + + if (!policy->governor->initialized) { + idle_notifier_register(&cpufreq_interactive_idle_nb); + cpufreq_register_notifier(&cpufreq_notifier_block, + CPUFREQ_TRANSITION_NOTIFIER); + } + + break; + + case CPUFREQ_GOV_POLICY_EXIT: + if (!--tunables->usage_count) { + if (policy->governor->initialized == 1) { + cpufreq_unregister_notifier(&cpufreq_notifier_block, + CPUFREQ_TRANSITION_NOTIFIER); + idle_notifier_unregister(&cpufreq_interactive_idle_nb); + } + + sysfs_remove_group(get_governor_parent_kobj(policy), + get_sysfs_attr()); + + if (!have_governor_per_policy()) + cpufreq_put_global_kobject(); + + kfree(tunables); + common_tunables = NULL; + } + + policy->governor_data = NULL; + break; + + case CPUFREQ_GOV_START: + mutex_lock(&gov_lock); + + freq_table = cpufreq_frequency_get_table(policy->cpu); + if (!tunables->hispeed_freq) + tunables->hispeed_freq = policy->max; + + for_each_cpu(j, policy->cpus) { + pcpu = &per_cpu(cpuinfo, j); + pcpu->policy = policy; + pcpu->target_freq = policy->cur; + pcpu->freq_table = freq_table; + pcpu->floor_freq = pcpu->target_freq; + pcpu->pol_floor_val_time = + ktime_to_us(ktime_get()); + pcpu->loc_floor_val_time = pcpu->pol_floor_val_time; + pcpu->pol_hispeed_val_time = pcpu->pol_floor_val_time; + pcpu->loc_hispeed_val_time = pcpu->pol_floor_val_time; + down_write(&pcpu->enable_sem); + del_timer_sync(&pcpu->cpu_timer); + del_timer_sync(&pcpu->cpu_slack_timer); + cpufreq_interactive_timer_start(tunables, j); + pcpu->governor_enabled = 1; + up_write(&pcpu->enable_sem); + } + + mutex_unlock(&gov_lock); + break; + + case CPUFREQ_GOV_STOP: + mutex_lock(&gov_lock); + for_each_cpu(j, policy->cpus) { + pcpu = &per_cpu(cpuinfo, j); + down_write(&pcpu->enable_sem); + pcpu->governor_enabled = 0; + del_timer_sync(&pcpu->cpu_timer); + del_timer_sync(&pcpu->cpu_slack_timer); + up_write(&pcpu->enable_sem); + } + + mutex_unlock(&gov_lock); + break; + + case CPUFREQ_GOV_LIMITS: + if (policy->max < policy->cur) + __cpufreq_driver_target(policy, + policy->max, CPUFREQ_RELATION_H); + else if (policy->min > policy->cur) + __cpufreq_driver_target(policy, + policy->min, CPUFREQ_RELATION_L); + for_each_cpu(j, policy->cpus) { + pcpu = &per_cpu(cpuinfo, j); + + down_read(&pcpu->enable_sem); + if (pcpu->governor_enabled == 0) { + up_read(&pcpu->enable_sem); + continue; + } + + spin_lock_irqsave(&pcpu->target_freq_lock, flags); + if (policy->max < pcpu->target_freq) + pcpu->target_freq = policy->max; + else if (policy->min > pcpu->target_freq) + pcpu->target_freq = policy->min; + + spin_unlock_irqrestore(&pcpu->target_freq_lock, flags); + up_read(&pcpu->enable_sem); + } + break; + } + return 0; +} + +#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE +static +#endif +struct cpufreq_governor cpufreq_gov_interactive = { + .name = "interactive", + .governor = cpufreq_governor_interactive, + .max_transition_latency = 10000000, + .owner = THIS_MODULE, +}; + +static void cpufreq_interactive_nop_timer(unsigned long data) +{ +} + +static int __init cpufreq_interactive_init(void) +{ + unsigned int i; + struct cpufreq_interactive_cpuinfo *pcpu; + struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; + + /* Initalize per-cpu timers */ + for_each_possible_cpu(i) { + pcpu = &per_cpu(cpuinfo, i); + init_timer_deferrable(&pcpu->cpu_timer); + pcpu->cpu_timer.function = cpufreq_interactive_timer; + pcpu->cpu_timer.data = i; + init_timer(&pcpu->cpu_slack_timer); + pcpu->cpu_slack_timer.function = cpufreq_interactive_nop_timer; + spin_lock_init(&pcpu->load_lock); + spin_lock_init(&pcpu->target_freq_lock); + init_rwsem(&pcpu->enable_sem); + } + + spin_lock_init(&speedchange_cpumask_lock); + mutex_init(&gov_lock); + speedchange_task = + kthread_create(cpufreq_interactive_speedchange_task, NULL, + "cfinteractive"); + if (IS_ERR(speedchange_task)) + return PTR_ERR(speedchange_task); + + sched_setscheduler_nocheck(speedchange_task, SCHED_FIFO, ¶m); + get_task_struct(speedchange_task); + + /* NB: wake up so the thread does not look hung to the freezer */ + wake_up_process(speedchange_task); + + return cpufreq_register_governor(&cpufreq_gov_interactive); +} + +#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE +fs_initcall(cpufreq_interactive_init); +#else +module_init(cpufreq_interactive_init); +#endif + +static void __exit cpufreq_interactive_exit(void) +{ + cpufreq_unregister_governor(&cpufreq_gov_interactive); + kthread_stop(speedchange_task); + put_task_struct(speedchange_task); +} + +module_exit(cpufreq_interactive_exit); + +MODULE_AUTHOR("Mike Chan "); +MODULE_DESCRIPTION("'cpufreq_interactive' - A cpufreq governor for " + "Latency sensitive workloads"); +MODULE_LICENSE("GPL"); +#endif diff -urN linux-4.1.52-orig/drivers/cpufreq/cpufreq_ondemand.c linux-4.1.52/drivers/cpufreq/cpufreq_ondemand.c --- linux-4.1.52-orig/drivers/cpufreq/cpufreq_ondemand.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/cpufreq/cpufreq_ondemand.c 2020-02-07 01:47:54.000000000 +0800 @@ -19,10 +19,19 @@ #include "cpufreq_governor.h" /* On-demand governor macros */ +#if defined(CONFIG_BCM_KF_ONDEMAND) +#define DEF_FREQUENCY_UP_THRESHOLD (20) +#define DEF_SAMPLING_DOWN_FACTOR (10) +#else #define DEF_FREQUENCY_UP_THRESHOLD (80) #define DEF_SAMPLING_DOWN_FACTOR (1) +#endif #define MAX_SAMPLING_DOWN_FACTOR (100000) +#if defined(CONFIG_BCM_KF_ONDEMAND) +#define MICRO_FREQUENCY_UP_THRESHOLD (40) +#else #define MICRO_FREQUENCY_UP_THRESHOLD (95) +#endif #define MICRO_FREQUENCY_MIN_SAMPLE_RATE (10000) #define MIN_FREQUENCY_UP_THRESHOLD (11) #define MAX_FREQUENCY_UP_THRESHOLD (100) @@ -33,6 +42,90 @@ #ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND static struct cpufreq_governor cpufreq_gov_ondemand; +#if defined(CONFIG_BCM_KF_ONDEMAND) +// clear current frequency table indices +static void min_request_clr(struct cpufreq_frequency_table *table) +{ + int idx; + + for (idx = 0; table[idx].frequency != CPUFREQ_TABLE_END; idx++) + table[idx].driver_data = 0; +} + +// change governor policy min +static void min_request_chg(struct cpufreq_policy *policy, + struct cpufreq_frequency_table *table) +{ + unsigned freq, max = 0; + int idx = 0; + + // find highest entry whose request count is non-zero + while ((freq = table[idx].frequency) != CPUFREQ_TABLE_END) { + if (table[idx].driver_data && freq > max) + max = freq; + idx++; + } + + // use lowest min freq if no requests + if (max == 0) + max = table[0].frequency; + + // update policy if current min != max + if (policy->min != max) { + struct cpufreq_policy new_policy = *policy; + new_policy.min = max; + + if (policy->min < max) + __cpufreq_driver_target(policy, max, CPUFREQ_RELATION_L); + cpufreq_set_policy(policy, &new_policy); + } +} + +// decrement request count for min freq of 'freq' +static int min_request_dec(struct cpufreq_policy *policy, + struct cpufreq_frequency_table *table, unsigned freqmin) +{ + unsigned freq; + int idx = 0; + + // assume table ordered by frequency; find first entry >= freqmin + // (cpufreq_frequency_table_target() honors current policy->min) + while ((freq = table[idx].frequency) != CPUFREQ_TABLE_END) { + if (freq != CPUFREQ_ENTRY_INVALID && freq >= freqmin) { + if (table[idx].driver_data && --table[idx].driver_data == 0) { + // expired minimum ... update policy + min_request_chg(policy, table); + } + return 0; + } + idx++; + } + return idx; +} + +// increment request count for min freq of 'freq' +static int min_request_inc(struct cpufreq_policy *policy, + struct cpufreq_frequency_table *table, unsigned freqmin) +{ + unsigned freq; + int idx = 0; + + // assume table ordered by frequency; find first entry >= freqmin + // (cpufreq_frequency_table_target() honors current policy->min) + while ((freq = table[idx].frequency) != CPUFREQ_TABLE_END) { + if (freq != CPUFREQ_ENTRY_INVALID && freq >= freqmin) { + if (table[idx].driver_data++ == 0 && freq > policy->min) { + // higher minimum ... update policy + min_request_chg(policy, table); + } + return 0; + } + idx++; + } + return idx; +} +#endif + #endif static unsigned int default_powersave_bias; @@ -42,6 +135,9 @@ struct od_cpu_dbs_info_s *dbs_info = &per_cpu(od_cpu_dbs_info, cpu); dbs_info->freq_table = cpufreq_frequency_get_table(cpu); +#if defined(CONFIG_BCM_KF_ONDEMAND) + min_request_clr(dbs_info->freq_table); +#endif dbs_info->freq_lo = 0; } @@ -225,9 +321,35 @@ } max_delay: +#if defined(CONFIG_BCM_KF_ONDEMAND) + /* + * If we're at the highest frequency we set rate_mult > 1 + * so we don't switch to a lower frequency for multiple + * sampling_rate intervals. + * + * delay_for_sampling_rate() tries to align the next timer + * to the next interval where (jiffy % sampling_rate == 0) + * + * e.g. if sampling_rate*1 is 100 jiffies, we try to align + * the timer expiry on all cpus to when jiffies are evenly + * divisible by 100. + * + * Similarly if sampling_rate*10 is 1000 jiffies, the + * timer expiry would be aligned to multiples of 1000. + * But if the current jiffy is 990 then the next aligned + * interval would be at 1000 which is much sooner than + * requested by rate_mult + * + * align to sampling_rate and add rate_mult-1 intervals + */ + delay = delay_for_sampling_rate(od_tuners->sampling_rate) + + usecs_to_jiffies(od_tuners->sampling_rate + * (core_dbs_info->rate_mult - 1)); +#else if (!delay) delay = delay_for_sampling_rate(od_tuners->sampling_rate * core_dbs_info->rate_mult); +#endif gov_queue_work(dbs_data, dbs_info->cdbs.cur_policy, delay, modify_all); mutex_unlock(&core_dbs_info->cdbs.timer_mutex); @@ -441,7 +563,87 @@ gov_sys_pol_attr_rw(powersave_bias); gov_sys_pol_attr_ro(sampling_rate_min); +#if defined(CONFIG_BCM_KF_ONDEMAND) +static int reservation_update(int freq) +{ + struct cpufreq_policy *policy; + struct od_cpu_dbs_info_s *dbs_info; + int ret, cpu; + + cpu = get_cpu(); + policy = cpufreq_cpu_get(cpu); + put_cpu(); + if (!policy) return -EFAULT; + if (!policy->governor_enabled) return -EINVAL; + dbs_info = &per_cpu(od_cpu_dbs_info, policy->cpu); + cpufreq_cpu_put(policy); + + if (freq > 0) + ret = min_request_inc(policy, dbs_info->freq_table, freq); + else + ret = min_request_dec(policy, dbs_info->freq_table, -freq); + + return ret ? -ENOENT : 0; +} + +// request to reserve minimum frequency +static ssize_t show_reserve(struct kobject *a, struct attribute *b, + char *buf) +{ + struct cpufreq_policy *policy; + struct od_cpu_dbs_info_s *dbs_info; + unsigned count = 0; + unsigned freq; + int idx = 0; + int cpu; + + cpu = get_cpu(); + policy = cpufreq_cpu_get(cpu); + put_cpu(); + if (!policy) return -ENOENT; + dbs_info = &per_cpu(od_cpu_dbs_info, policy->cpu); + cpufreq_cpu_put(policy); + + while ((freq = dbs_info->freq_table[idx].frequency) != CPUFREQ_TABLE_END) { + count += sprintf(buf + count, "%u:%u ", + dbs_info->freq_table[idx].frequency, + dbs_info->freq_table[idx].driver_data); + idx++; + } + count += sprintf(buf + count - 1, "\n") - 1; + return count; +} + +static ssize_t store_reserve(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + int freq; + + if (sscanf(buf, "%d", &freq) != 1) + return -EINVAL; + + return reservation_update(freq) ?: count; +} + +define_one_global_rw(reserve); + +int cpufreq_minimum_reserve(int freq) +{ + return reservation_update(freq); +} +EXPORT_SYMBOL(cpufreq_minimum_reserve); + +int cpufreq_minimum_unreserve(int freq) +{ + return reservation_update(-freq); +} +EXPORT_SYMBOL(cpufreq_minimum_unreserve); +#endif + static struct attribute *dbs_attributes_gov_sys[] = { +#if defined(CONFIG_BCM_KF_ONDEMAND) + &reserve.attr, +#endif &sampling_rate_min_gov_sys.attr, &sampling_rate_gov_sys.attr, &up_threshold_gov_sys.attr, diff -urN linux-4.1.52-orig/drivers/cpufreq/Kconfig linux-4.1.52/drivers/cpufreq/Kconfig --- linux-4.1.52-orig/drivers/cpufreq/Kconfig 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/cpufreq/Kconfig 2020-02-07 01:47:54.000000000 +0800 @@ -82,7 +82,6 @@ config CPU_FREQ_DEFAULT_GOV_ONDEMAND bool "ondemand" select CPU_FREQ_GOV_ONDEMAND - select CPU_FREQ_GOV_PERFORMANCE help Use the CPUFreq governor 'ondemand' as default. This allows you to get a full dynamic frequency capable system by simply @@ -102,6 +101,17 @@ Be aware that not all cpufreq drivers support the conservative governor. If unsure have a look at the help section of the driver. Fallback governor will be the performance governor. + +config CPU_FREQ_DEFAULT_GOV_INTERACTIVE + bool "interactive" + depends on BCM_KF_INTERACTIVE + select CPU_FREQ_GOV_INTERACTIVE + help + Use the CPUFreq governor 'interactive' as default. This allows + you to get a full dynamic cpu frequency capable system by simply + loading your cpufreq low-level hardware driver, using the + 'interactive' governor for latency-sensitive workloads. + endchoice config CPU_FREQ_GOV_PERFORMANCE @@ -157,6 +167,24 @@ For details, take a look at linux/Documentation/cpu-freq. + If in doubt, say N. + +config CPU_FREQ_GOV_INTERACTIVE + tristate "'interactive' cpufreq policy governor" + depends on BCM_KF_INTERACTIVE + help + 'interactive' - This driver adds a dynamic cpufreq policy governor + designed for latency-sensitive workloads. + + This governor attempts to reduce the latency of clock + increases so that the system is more responsive to + interactive workloads. + + To compile this driver as a module, choose M here: the + module will be called cpufreq_interactive. + + For details, take a look at linux/Documentation/cpu-freq. + If in doubt, say N. config CPU_FREQ_GOV_CONSERVATIVE diff -urN linux-4.1.52-orig/drivers/cpufreq/Makefile linux-4.1.52/drivers/cpufreq/Makefile --- linux-4.1.52-orig/drivers/cpufreq/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/cpufreq/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -3,7 +3,7 @@ obj-$(CONFIG_PM_OPP) += cpufreq_opp.o # CPUfreq stats -obj-$(CONFIG_CPU_FREQ_STAT) += cpufreq_stats.o +obj-$(CONFIG_CPU_FREQ_STAT) += cpufreq_stats.o # CPUfreq governors obj-$(CONFIG_CPU_FREQ_GOV_PERFORMANCE) += cpufreq_performance.o @@ -11,7 +11,13 @@ obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE) += cpufreq_userspace.o obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND) += cpufreq_ondemand.o obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o -obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o +ifdef BCM_KF # defined(CONFIG_BCM_KF_INTERACTIVE) +obj-$(CONFIG_CPU_FREQ_GOV_INTERACTIVE) += cpufreq_interactive.o +ifdef BRCM_CPU_FREQ_TARGET_LOAD +CFLAGS_cpufreq_interactive.o = -DDEFAULT_TARGET_LOAD=${BRCM_CPU_FREQ_TARGET_LOAD} +endif +endif +obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o obj-$(CONFIG_CPUFREQ_DT) += cpufreq-dt.o diff -urN linux-4.1.52-orig/drivers/cpuidle/cpuidle.c linux-4.1.52/drivers/cpuidle/cpuidle.c --- linux-4.1.52-orig/drivers/cpuidle/cpuidle.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/cpuidle/cpuidle.c 2020-02-07 01:47:54.000000000 +0800 @@ -34,6 +34,15 @@ static int enabled_devices; static int off __read_mostly; static int initialized __read_mostly; +#if defined(CONFIG_BCM_KF_POWER_SAVE) && defined(CONFIG_BCM963138) +/* + * On 63138, gathering time statistics can affect performance + * Add /sys/module/cpuidle/parameter/time_stats_enable, disabled by default + * This only works if there is only one cpuidle state to chose from since + * this change makes the statistics unavailable (last_residency = 0) + */ +static int time_stats_enable __read_mostly; +#endif int cpuidle_disabled(void) { @@ -171,10 +180,16 @@ return -EBUSY; trace_cpu_idle_rcuidle(index, dev->cpu); +#if defined(CONFIG_BCM_KF_POWER_SAVE) && defined(CONFIG_BCM963138) + if (time_stats_enable) +#endif time_start = ktime_get(); entered_state = target_state->enter(dev, drv, index); +#if defined(CONFIG_BCM_KF_POWER_SAVE) && defined(CONFIG_BCM963138) + if (time_stats_enable) +#endif time_end = ktime_get(); trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); @@ -188,11 +203,19 @@ if (!cpuidle_state_is_coupled(drv, index)) local_irq_enable(); +#if defined(CONFIG_BCM_KF_POWER_SAVE) && defined(CONFIG_BCM963138) + if (time_stats_enable) { +#endif diff = ktime_to_us(ktime_sub(time_end, time_start)); if (diff > INT_MAX) diff = INT_MAX; dev->last_residency = (int) diff; +#if defined(CONFIG_BCM_KF_POWER_SAVE) && defined(CONFIG_BCM963138) + } else { + dev->last_residency = 0; + } +#endif if (entered_state >= 0) { /* Update cpuidle counters */ @@ -631,5 +654,8 @@ return 0; } +#if defined(CONFIG_BCM_KF_POWER_SAVE) && defined(CONFIG_BCM963138) +module_param(time_stats_enable, int, 0644); +#endif module_param(off, int, 0444); core_initcall(cpuidle_init); diff -urN linux-4.1.52-orig/drivers/gpio/gpio-mmio.c linux-4.1.52/drivers/gpio/gpio-mmio.c --- linux-4.1.52-orig/drivers/gpio/gpio-mmio.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/gpio/gpio-mmio.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,737 @@ +/* + * Generic driver for memory-mapped GPIO controllers. + * + * Copyright 2008 MontaVista Software, Inc. + * Copyright 2008,2010 Anton Vorontsov + * + * 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. + * + * ....``.```~~~~````.`.`.`.`.```````'',,,.........`````......`....... + * ...`` ```````.. + * ..The simplest form of a GPIO controller that the driver supports is`` + * `.just a single "data" register, where GPIO state can be read and/or ` + * `,..written. ,,..``~~~~ .....``.`.`.~~.```.`.........``````.``````` + * ````````` + ___ +_/~~|___/~| . ```~~~~~~ ___/___\___ ,~.`.`.`.`````.~~...,,,,... +__________|~$@~~~ %~ /o*o*o*o*o*o\ .. Implementing such a GPIO . +o ` ~~~~\___/~~~~ ` controller in FPGA is ,.` + `....trivial..'~`.```.``` + * ``````` + * .```````~~~~`..`.``.``. + * . The driver supports `... ,..```.`~~~```````````````....````.``,, + * . big-endian notation, just`. .. A bit more sophisticated controllers , + * . register the device with -be`. .with a pair of set/clear-bit registers , + * `.. suffix. ```~~`````....`.` . affecting the data register and the .` + * ``.`.``...``` ```.. output pins are also supported.` + * ^^ `````.`````````.,``~``~``~~`````` + * . ^^ + * ,..`.`.`...````````````......`.`.`.`.`.`..`.`.`.. + * .. The expectation is that in at least some cases . ,-~~~-, + * .this will be used with roll-your-own ASIC/FPGA .` \ / + * .logic in Verilog or VHDL. ~~~`````````..`````~~` \ / + * ..````````......``````````` \o_ + * | + * ^^ / \ + * + * ...`````~~`.....``.`..........``````.`.``.```........``. + * ` 8, 16, 32 and 64 bits registers are supported, and``. + * . the number of GPIOs is determined by the width of ~ + * .. the registers. ,............```.`.`..`.`.~~~.`.`.`~ + * `.......````.``` + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void bgpio_write8(void __iomem *reg, unsigned long data) +{ + writeb(data, reg); +} + +static unsigned long bgpio_read8(void __iomem *reg) +{ + return readb(reg); +} + +static void bgpio_write16(void __iomem *reg, unsigned long data) +{ + writew(data, reg); +} + +static unsigned long bgpio_read16(void __iomem *reg) +{ + return readw(reg); +} + +static void bgpio_write32(void __iomem *reg, unsigned long data) +{ + writel(data, reg); +} + +static unsigned long bgpio_read32(void __iomem *reg) +{ + return readl(reg); +} + +#if BITS_PER_LONG >= 64 +static void bgpio_write64(void __iomem *reg, unsigned long data) +{ + writeq(data, reg); +} + +static unsigned long bgpio_read64(void __iomem *reg) +{ + return readq(reg); +} +#endif /* BITS_PER_LONG >= 64 */ + +static void bgpio_write16be(void __iomem *reg, unsigned long data) +{ + iowrite16be(data, reg); +} + +static unsigned long bgpio_read16be(void __iomem *reg) +{ + return ioread16be(reg); +} + +static void bgpio_write32be(void __iomem *reg, unsigned long data) +{ + iowrite32be(data, reg); +} + +static unsigned long bgpio_read32be(void __iomem *reg) +{ + return ioread32be(reg); +} + +static unsigned long bgpio_pin2mask(struct gpio_chip *gc, unsigned int pin) +{ + return BIT(pin); +} + +static unsigned long bgpio_pin2mask_be(struct gpio_chip *gc, + unsigned int pin) +{ + return BIT(gc->bgpio_bits - 1 - pin); +} + +static int bgpio_get_set(struct gpio_chip *gc, unsigned int gpio) +{ + unsigned long pinmask = gc->pin2mask(gc, gpio); + + if (gc->bgpio_dir & pinmask) + return !!(gc->read_reg(gc->reg_set) & pinmask); + else + return !!(gc->read_reg(gc->reg_dat) & pinmask); +} + +static int bgpio_get(struct gpio_chip *gc, unsigned int gpio) +{ + return !!(gc->read_reg(gc->reg_dat) & gc->pin2mask(gc, gpio)); +} + +static void bgpio_set_none(struct gpio_chip *gc, unsigned int gpio, int val) +{ +} + +static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +{ + unsigned long mask = gc->pin2mask(gc, gpio); + unsigned long flags; + + spin_lock_irqsave(&gc->bgpio_lock, flags); + + if (val) + gc->bgpio_data |= mask; + else + gc->bgpio_data &= ~mask; + + gc->write_reg(gc->reg_dat, gc->bgpio_data); + + spin_unlock_irqrestore(&gc->bgpio_lock, flags); +} + +static void bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio, + int val) +{ + unsigned long mask = gc->pin2mask(gc, gpio); + + if (val) + gc->write_reg(gc->reg_set, mask); + else + gc->write_reg(gc->reg_clr, mask); +} + +static void bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val) +{ + unsigned long mask = gc->pin2mask(gc, gpio); + unsigned long flags; + + spin_lock_irqsave(&gc->bgpio_lock, flags); + + if (val) + gc->bgpio_data |= mask; + else + gc->bgpio_data &= ~mask; + + gc->write_reg(gc->reg_set, gc->bgpio_data); + + spin_unlock_irqrestore(&gc->bgpio_lock, flags); +} + +static void bgpio_multiple_get_masks(struct gpio_chip *gc, + unsigned long *mask, unsigned long *bits, + unsigned long *set_mask, + unsigned long *clear_mask) +{ + int i; + + *set_mask = 0; + *clear_mask = 0; + + for (i = 0; i < gc->bgpio_bits; i++) { + if (*mask == 0) + break; + if (__test_and_clear_bit(i, mask)) { + if (test_bit(i, bits)) + *set_mask |= gc->pin2mask(gc, i); + else + *clear_mask |= gc->pin2mask(gc, i); + } + } +} + +static void bgpio_set_multiple_single_reg(struct gpio_chip *gc, + unsigned long *mask, + unsigned long *bits, + void __iomem *reg) +{ + unsigned long flags; + unsigned long set_mask, clear_mask; + + spin_lock_irqsave(&gc->bgpio_lock, flags); + + bgpio_multiple_get_masks(gc, mask, bits, &set_mask, &clear_mask); + + gc->bgpio_data |= set_mask; + gc->bgpio_data &= ~clear_mask; + + gc->write_reg(reg, gc->bgpio_data); + + spin_unlock_irqrestore(&gc->bgpio_lock, flags); +} + +static void bgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, + unsigned long *bits) +{ + bgpio_set_multiple_single_reg(gc, mask, bits, gc->reg_dat); +} + +static void bgpio_set_multiple_set(struct gpio_chip *gc, unsigned long *mask, + unsigned long *bits) +{ + bgpio_set_multiple_single_reg(gc, mask, bits, gc->reg_set); +} + +static void bgpio_set_multiple_with_clear(struct gpio_chip *gc, + unsigned long *mask, + unsigned long *bits) +{ + unsigned long set_mask, clear_mask; + + bgpio_multiple_get_masks(gc, mask, bits, &set_mask, &clear_mask); + + if (set_mask) + gc->write_reg(gc->reg_set, set_mask); + if (clear_mask) + gc->write_reg(gc->reg_clr, clear_mask); +} + +static int bgpio_simple_dir_in(struct gpio_chip *gc, unsigned int gpio) +{ + return 0; +} + +static int bgpio_dir_out_err(struct gpio_chip *gc, unsigned int gpio, + int val) +{ + return -EINVAL; +} + +static int bgpio_simple_dir_out(struct gpio_chip *gc, unsigned int gpio, + int val) +{ + gc->set(gc, gpio, val); + + return 0; +} + +static int bgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) +{ + unsigned long flags; + + spin_lock_irqsave(&gc->bgpio_lock, flags); + + gc->bgpio_dir &= ~gc->pin2mask(gc, gpio); + gc->write_reg(gc->reg_dir, gc->bgpio_dir); + + spin_unlock_irqrestore(&gc->bgpio_lock, flags); + + return 0; +} + +static int bgpio_get_dir(struct gpio_chip *gc, unsigned int gpio) +{ + /* Return 0 if output, 1 of input */ + return !(gc->read_reg(gc->reg_dir) & gc->pin2mask(gc, gpio)); +} + +static int bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) +{ + unsigned long flags; + + gc->set(gc, gpio, val); + + spin_lock_irqsave(&gc->bgpio_lock, flags); + + gc->bgpio_dir |= gc->pin2mask(gc, gpio); + gc->write_reg(gc->reg_dir, gc->bgpio_dir); + + spin_unlock_irqrestore(&gc->bgpio_lock, flags); + + return 0; +} + +static int bgpio_dir_in_inv(struct gpio_chip *gc, unsigned int gpio) +{ + unsigned long flags; + + spin_lock_irqsave(&gc->bgpio_lock, flags); + + gc->bgpio_dir |= gc->pin2mask(gc, gpio); + gc->write_reg(gc->reg_dir, gc->bgpio_dir); + + spin_unlock_irqrestore(&gc->bgpio_lock, flags); + + return 0; +} + +static int bgpio_dir_out_inv(struct gpio_chip *gc, unsigned int gpio, int val) +{ + unsigned long flags; + + gc->set(gc, gpio, val); + + spin_lock_irqsave(&gc->bgpio_lock, flags); + + gc->bgpio_dir &= ~gc->pin2mask(gc, gpio); + gc->write_reg(gc->reg_dir, gc->bgpio_dir); + + spin_unlock_irqrestore(&gc->bgpio_lock, flags); + + return 0; +} + +static int bgpio_get_dir_inv(struct gpio_chip *gc, unsigned int gpio) +{ + /* Return 0 if output, 1 if input */ + return !!(gc->read_reg(gc->reg_dir) & gc->pin2mask(gc, gpio)); +} + +static int bgpio_setup_accessors(struct device *dev, + struct gpio_chip *gc, + bool bit_be, + bool byte_be) +{ + + switch (gc->bgpio_bits) { + case 8: + gc->read_reg = bgpio_read8; + gc->write_reg = bgpio_write8; + break; + case 16: + if (byte_be) { + gc->read_reg = bgpio_read16be; + gc->write_reg = bgpio_write16be; + } else { + gc->read_reg = bgpio_read16; + gc->write_reg = bgpio_write16; + } + break; + case 32: + if (byte_be) { + gc->read_reg = bgpio_read32be; + gc->write_reg = bgpio_write32be; + } else { + gc->read_reg = bgpio_read32; + gc->write_reg = bgpio_write32; + } + break; +#if BITS_PER_LONG >= 64 + case 64: + if (byte_be) { + dev_err(dev, + "64 bit big endian byte order unsupported\n"); + return -EINVAL; + } else { + gc->read_reg = bgpio_read64; + gc->write_reg = bgpio_write64; + } + break; +#endif /* BITS_PER_LONG >= 64 */ + default: + dev_err(dev, "unsupported data width %u bits\n", gc->bgpio_bits); + return -EINVAL; + } + + gc->pin2mask = bit_be ? bgpio_pin2mask_be : bgpio_pin2mask; + + return 0; +} + +/* + * Create the device and allocate the resources. For setting GPIO's there are + * three supported configurations: + * + * - single input/output register resource (named "dat"). + * - set/clear pair (named "set" and "clr"). + * - single output register resource and single input resource ("set" and + * dat"). + * + * For the single output register, this drives a 1 by setting a bit and a zero + * by clearing a bit. For the set clr pair, this drives a 1 by setting a bit + * in the set register and clears it by setting a bit in the clear register. + * The configuration is detected by which resources are present. + * + * For setting the GPIO direction, there are three supported configurations: + * + * - simple bidirection GPIO that requires no configuration. + * - an output direction register (named "dirout") where a 1 bit + * indicates the GPIO is an output. + * - an input direction register (named "dirin") where a 1 bit indicates + * the GPIO is an input. + */ +static int bgpio_setup_io(struct gpio_chip *gc, + void __iomem *dat, + void __iomem *set, + void __iomem *clr, + unsigned long flags) +{ + + gc->reg_dat = dat; + if (!gc->reg_dat) + return -EINVAL; + + if (set && clr) { + gc->reg_set = set; + gc->reg_clr = clr; + gc->set = bgpio_set_with_clear; + gc->set_multiple = bgpio_set_multiple_with_clear; + } else if (set && !clr) { + gc->reg_set = set; + gc->set = bgpio_set_set; + gc->set_multiple = bgpio_set_multiple_set; + } else if (flags & BGPIOF_NO_OUTPUT) { + gc->set = bgpio_set_none; + gc->set_multiple = NULL; + } else { + gc->set = bgpio_set; + gc->set_multiple = bgpio_set_multiple; + } + + if (!(flags & BGPIOF_UNREADABLE_REG_SET) && + (flags & BGPIOF_READ_OUTPUT_REG_SET)) + gc->get = bgpio_get_set; + else + gc->get = bgpio_get; + + return 0; +} + +static int bgpio_setup_direction(struct gpio_chip *gc, + void __iomem *dirout, + void __iomem *dirin, + unsigned long flags) +{ + if (dirout && dirin) { + return -EINVAL; + } else if (dirout) { + gc->reg_dir = dirout; + gc->direction_output = bgpio_dir_out; + gc->direction_input = bgpio_dir_in; + gc->get_direction = bgpio_get_dir; + } else if (dirin) { + gc->reg_dir = dirin; + gc->direction_output = bgpio_dir_out_inv; + gc->direction_input = bgpio_dir_in_inv; + gc->get_direction = bgpio_get_dir_inv; + } else { + if (flags & BGPIOF_NO_OUTPUT) + gc->direction_output = bgpio_dir_out_err; + else + gc->direction_output = bgpio_simple_dir_out; + gc->direction_input = bgpio_simple_dir_in; + } + + return 0; +} + +static int bgpio_request(struct gpio_chip *chip, unsigned gpio_pin) +{ + if (gpio_pin < chip->ngpio) + return 0; + + return -EINVAL; +} + +#if defined(CONFIG_BCM_KF_GPIO) && defined( CONFIG_OF_GPIO ) +static int bcm_gpio_of_xlate(struct gpio_chip *gc, + const struct of_phandle_args *gpiospec, + u32 *flags) +{ + + if (gpiospec->args[0] >= gc->ngpio) + return -EINVAL; + + if (flags) + *flags = gpiospec->args[1]; + + return gpiospec->args[0] % 32; +} +#endif + +int bgpio_init(struct gpio_chip *gc, struct device *dev, + unsigned long sz, void __iomem *dat, void __iomem *set, + void __iomem *clr, void __iomem *dirout, void __iomem *dirin, + unsigned long flags) +{ + int ret; + + if (!is_power_of_2(sz)) + return -EINVAL; + + gc->bgpio_bits = sz * 8; + if (gc->bgpio_bits > BITS_PER_LONG) + return -EINVAL; + + spin_lock_init(&gc->bgpio_lock); + gc->parent = dev; + gc->label = dev_name(dev); + gc->base = -1; + gc->ngpio = gc->bgpio_bits; + gc->request = bgpio_request; + + ret = bgpio_setup_io(gc, dat, set, clr, flags); + if (ret) + return ret; + + ret = bgpio_setup_accessors(dev, gc, flags & BGPIOF_BIG_ENDIAN, + flags & BGPIOF_BIG_ENDIAN_BYTE_ORDER); + if (ret) + return ret; + + ret = bgpio_setup_direction(gc, dirout, dirin, flags); + if (ret) + return ret; + + gc->bgpio_data = gc->read_reg(gc->reg_dat); + if (gc->set == bgpio_set_set && + !(flags & BGPIOF_UNREADABLE_REG_SET)) + gc->bgpio_data = gc->read_reg(gc->reg_set); + if (gc->reg_dir && !(flags & BGPIOF_UNREADABLE_REG_DIR)) + gc->bgpio_dir = gc->read_reg(gc->reg_dir); + +#if defined(CONFIG_BCM_KF_GPIO) && defined( CONFIG_OF_GPIO ) + gc->of_node = dev->of_node; + gc->of_xlate = bcm_gpio_of_xlate; + gc->of_gpio_n_cells = 2; +#endif + + return ret; +} +EXPORT_SYMBOL_GPL(bgpio_init); + +#if IS_ENABLED(CONFIG_GPIO_GENERIC_PLATFORM) + +static void __iomem *bgpio_map(struct platform_device *pdev, + const char *name, + resource_size_t sane_sz) +{ + struct resource *r; + resource_size_t sz; + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); + if (!r) + return NULL; + + sz = resource_size(r); + if (sz != sane_sz) + return IOMEM_ERR_PTR(-EINVAL); + + return devm_ioremap_resource(&pdev->dev, r); +} + +#ifdef CONFIG_OF +static const struct of_device_id bgpio_of_match[] = { + { .compatible = "brcm,bcm6345-gpio" }, + { .compatible = "wd,mbl-gpio" }, + { } +}; +MODULE_DEVICE_TABLE(of, bgpio_of_match); + +static struct bgpio_pdata *bgpio_parse_dt(struct platform_device *pdev, + unsigned long *flags) +{ + struct bgpio_pdata *pdata; + + if (!of_match_device(bgpio_of_match, &pdev->dev)) + return NULL; + + pdata = devm_kzalloc(&pdev->dev, sizeof(struct bgpio_pdata), + GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + pdata->base = -1; + + if (of_device_is_big_endian(pdev->dev.of_node)) + *flags |= BGPIOF_BIG_ENDIAN_BYTE_ORDER; + + if (of_property_read_bool(pdev->dev.of_node, "no-output")) + *flags |= BGPIOF_NO_OUTPUT; + + return pdata; +} +#else +static struct bgpio_pdata *bgpio_parse_dt(struct platform_device *pdev, + unsigned long *flags) +{ + return NULL; +} +#endif /* CONFIG_OF */ + +static int bgpio_pdev_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *r; + void __iomem *dat; + void __iomem *set; + void __iomem *clr; + void __iomem *dirout; + void __iomem *dirin; + unsigned long sz; + unsigned long flags = 0; + int err; + struct gpio_chip *gc; + struct bgpio_pdata *pdata; + + pdata = bgpio_parse_dt(pdev, &flags); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + + if (!pdata) { + pdata = dev_get_platdata(dev); + flags = pdev->id_entry->driver_data; + } + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat"); + if (!r) + return -EINVAL; + + sz = resource_size(r); + + dat = bgpio_map(pdev, "dat", sz); + if (IS_ERR(dat)) + return PTR_ERR(dat); + + set = bgpio_map(pdev, "set", sz); + if (IS_ERR(set)) + return PTR_ERR(set); + + clr = bgpio_map(pdev, "clr", sz); + if (IS_ERR(clr)) + return PTR_ERR(clr); + + dirout = bgpio_map(pdev, "dirout", sz); + if (IS_ERR(dirout)) + return PTR_ERR(dirout); + + dirin = bgpio_map(pdev, "dirin", sz); + if (IS_ERR(dirin)) + return PTR_ERR(dirin); + + gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL); + if (!gc) + return -ENOMEM; + + err = bgpio_init(gc, dev, sz, dat, set, clr, dirout, dirin, flags); + if (err) + return err; + + if (pdata) { + if (pdata->label) + gc->label = pdata->label; + gc->base = pdata->base; + if (pdata->ngpio > 0) + gc->ngpio = pdata->ngpio; + } + + platform_set_drvdata(pdev, gc); + +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + return gpiochip_add(gc); +#else + return devm_gpiochip_add_data(&pdev->dev, gc, NULL); +#endif +} + +static const struct platform_device_id bgpio_id_table[] = { + { + .name = "basic-mmio-gpio", + .driver_data = 0, + }, { + .name = "basic-mmio-gpio-be", + .driver_data = BGPIOF_BIG_ENDIAN, + }, + { } +}; +MODULE_DEVICE_TABLE(platform, bgpio_id_table); + +static struct platform_driver bgpio_driver = { + .driver = { + .name = "basic-mmio-gpio", + .of_match_table = of_match_ptr(bgpio_of_match), + }, + .id_table = bgpio_id_table, + .probe = bgpio_pdev_probe, +}; + +module_platform_driver(bgpio_driver); + +#endif /* CONFIG_GPIO_GENERIC_PLATFORM */ + +MODULE_DESCRIPTION("Driver for basic memory-mapped GPIO controllers"); +MODULE_AUTHOR("Anton Vorontsov "); +MODULE_LICENSE("GPL"); diff -urN linux-4.1.52-orig/drivers/gpio/Makefile linux-4.1.52/drivers/gpio/Makefile --- linux-4.1.52-orig/drivers/gpio/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/gpio/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -10,7 +10,11 @@ obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o # Device drivers. Generally keep list sorted alphabetically +ifdef BCM_KF +obj-$(CONFIG_GPIO_GENERIC) += gpio-mmio.o +else obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o +endif obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o obj-$(CONFIG_GPIO_74XX_MMIO) += gpio-74xx-mmio.o diff -urN linux-4.1.52-orig/drivers/infiniband/hw/cxgb4/cm.c linux-4.1.52/drivers/infiniband/hw/cxgb4/cm.c --- linux-4.1.52-orig/drivers/infiniband/hw/cxgb4/cm.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/infiniband/hw/cxgb4/cm.c 2020-02-07 01:47:54.000000000 +0800 @@ -3549,7 +3549,11 @@ */ memset(&tmp_opt, 0, sizeof(tmp_opt)); tcp_clear_options(&tmp_opt); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_parse_options(skb, &tmp_opt, 0, NULL); +#else + tcp_parse_options(skb, &tmp_opt, NULL, 0, NULL, NULL); +#endif req = (struct cpl_pass_accept_req *)__skb_push(skb, sizeof(*req)); memset(req, 0, sizeof(*req)); diff -urN linux-4.1.52-orig/drivers/irqchip/irq-gic.c linux-4.1.52/drivers/irqchip/irq-gic.c --- linux-4.1.52-orig/drivers/irqchip/irq-gic.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/irqchip/irq-gic.c 2020-02-07 01:47:54.000000000 +0800 @@ -953,7 +953,14 @@ gic_irqs = 1020; gic->gic_irqs = gic_irqs; +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && (defined(CONFIG_BCM963138) || defined(CONFIG_BCM963148)) + /* To support GIC in device tree for 63138/63148 legacy device, always use legacy way to add + irq domain and allocate irqs first. A lot of devices has not using device tree yet so need to + pre-allocate irq heres otherwise request_irq function return error */ + if (false) { +#else if (node) { /* DT case */ +#endif gic->domain = irq_domain_add_linear(node, gic_irqs, &gic_irq_domain_hierarchy_ops, gic); diff -urN linux-4.1.52-orig/drivers/Kconfig linux-4.1.52/drivers/Kconfig --- linux-4.1.52-orig/drivers/Kconfig 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/Kconfig 2020-02-07 01:47:54.000000000 +0800 @@ -182,4 +182,8 @@ source "drivers/android/Kconfig" +if BCM_KF_OPTEE +source "drivers/tee/Kconfig" +endif + endmenu diff -urN linux-4.1.52-orig/drivers/leds/led-core.c linux-4.1.52/drivers/leds/led-core.c --- linux-4.1.52-orig/drivers/leds/led-core.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/leds/led-core.c 2020-02-07 01:47:54.000000000 +0800 @@ -76,7 +76,11 @@ unsigned long *delay_on, unsigned long *delay_off) { +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + del_timer_sync(&led_cdev->blink_timer); +#else led_stop_software_blink(led_cdev); +#endif led_cdev->flags &= ~LED_BLINK_ONESHOT; led_cdev->flags &= ~LED_BLINK_ONESHOT_STOP; diff -urN linux-4.1.52-orig/drivers/Makefile linux-4.1.52/drivers/Makefile --- linux-4.1.52-orig/drivers/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -167,3 +167,6 @@ obj-$(CONFIG_THUNDERBOLT) += thunderbolt/ obj-$(CONFIG_CORESIGHT) += hwtracing/coresight/ obj-$(CONFIG_ANDROID) += android/ +ifdef BCM_KF # defined(CONFIG_BCM_KF_OPTEE) +obj-$(CONFIG_TEE) += tee/ +endif diff -urN linux-4.1.52-orig/drivers/mmc/host/Kconfig linux-4.1.52/drivers/mmc/host/Kconfig --- linux-4.1.52-orig/drivers/mmc/host/Kconfig 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/mmc/host/Kconfig 2020-02-07 01:47:54.000000000 +0800 @@ -265,6 +265,18 @@ If unsure, say N. +config MMC_SDHCI_BCM63xx + tristate "SDHCI support on Broadcom DSL/PON CPE device" + depends on BCM_KF_EMMC + depends on MMC_SDHCI && MMC_SDHCI_PLTFM + help + This selects the Secure Digital Host Controller Interface (SDHCI) + for the EMMC block on Broadcom DSL/PON SoCs + + If you have a controller with this interface, say Y or M here. + + If unsure, say N. + config MMC_SDHCI_S3C_DMA bool "DMA support on S3C SDHCI" depends on MMC_SDHCI_S3C diff -urN linux-4.1.52-orig/drivers/mmc/host/Makefile linux-4.1.52/drivers/mmc/host/Makefile --- linux-4.1.52-orig/drivers/mmc/host/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/mmc/host/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -74,6 +74,17 @@ obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o obj-$(CONFIG_MMC_SDHCI_ST) += sdhci-st.o +ifdef BCM_KF # defined(CONFIG_BCM_KF_EMMC) +obj-$(CONFIG_MMC_SDHCI_BCM63xx) += sdhci-bcm63xx.o +endif # BCM_KF + + ifeq ($(CONFIG_CB710_DEBUG),y) CFLAGS-cb710-mmc += -DDEBUG endif + +ifdef BCM_KF # defined(CONFIG_BCM_KF_EMMC) +ifeq ($(CONFIG_MMC_SDHCI_BCM63xx),y) + EXTRA_CFLAGS += -I$(INC_BRCMBOARDPARMS_PATH)/$(BRCM_BOARD) -I$(SRCBASE)/include -I$(INC_BRCMDRIVER_PUB_PATH)/$(BRCM_BOARD) -I$(INC_BRCMSHARED_PUB_PATH)/$(BRCM_BOARD) +endif +endif # BCM_KF diff -urN linux-4.1.52-orig/drivers/mmc/host/sdhci-bcm63xx.c linux-4.1.52/drivers/mmc/host/sdhci-bcm63xx.c --- linux-4.1.52-orig/drivers/mmc/host/sdhci-bcm63xx.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/mmc/host/sdhci-bcm63xx.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,219 @@ +#if defined(CONFIG_BCM_KF_EMMC) +/************************************************************** + * sdhci-bcm63xx.c Support for SDHCI on Broadcom DSL/PON CPE SoC's + * + * Author: Farhan Ali + * Based on sdhci-brcmstb.c + * + * Copyright (c) 2014 Broadcom Corporation + * All Rights Reserved + * + * <:label-BRCM:2014:DUAL/GPL:standard + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed + * to you under the terms of the GNU General Public License version 2 + * (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, + * with the following added to such license: + * + * As a special exception, the copyright holders of this software give + * you permission to link this software with independent modules, and + * to copy and distribute the resulting executable under terms of your + * choice, provided that you also meet, for each linked independent + * module, the terms and conditions of the license of that module. + * An independent module is a module which is not derived from this + * software. The special exception does not apply to any modifications + * of the software. + * + * Not withstanding the above, under no circumstances may you combine + * this software in any way with any other Broadcom software provided + * under a license other than the GPL, without Broadcom's express prior + * written consent. + * + * :> + * + ************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sdhci.h" +#include "sdhci-pltfm.h" + +#define SDHCI_BCM63XX_FORCE_LOW_PERFORMANCE 0 +#define SDHCI_BCM63XX_FAKE_TRIGGER_NATIVE_CD 0 +#if defined(CONFIG_BCM96858) +#define SDHCI_BCM63XX_FORCE_PIO_MODE 1 +#else +#define SDHCI_BCM63XX_FORCE_PIO_MODE 0 +#endif + +static unsigned short ext_intr; +static struct sdhci_pltfm_data sdhci_bcm63xx_pdata = { + /* Quirks and ops defined here will be passed to sdhci_host structure */ + .quirks = 0 +#if SDHCI_BCM63XX_FORCE_PIO_MODE + | SDHCI_QUIRK_BROKEN_ADMA + | SDHCI_QUIRK_BROKEN_DMA +#endif +#if SDHCI_BCM63XX_FORCE_LOW_PERFORMANCE + | SDHCI_QUIRK_FORCE_1_BIT_DATA + | SDHCI_QUIRK_BROKEN_ADMA + | SDHCI_QUIRK_NO_MULTIBLOCK +#endif + , + .quirks2 = 0 + | SDHCI_QUIRK2_STOP_WITH_TC +#if SDHCI_BCM63XX_FORCE_LOW_PERFORMANCE + | SDHCI_QUIRK2_NO_1_8_V + | SDHCI_QUIRK2_BROKEN_DDR50 +#endif + , +}; + +static const struct of_device_id sdhci_bcm63xx_of_match[] = { + { .compatible = "brcm,bcm63xx-sdhci"}, + {} +}; +MODULE_DEVICE_TABLE(of, sdhci_bcm63xx_of_match); + +#if defined(CONFIG_BCM963138) +#include +static struct resource bcm63138_emmc_resources[] = { + [0] = { + .start = EMMC_HOSTIF_PHYS_BASE, + .end = EMMC_HOSTIF_PHYS_BASE + SZ_256 - 1, /* we only need this area */ + /* the memory map actually makes SZ_4K available */ + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = INTERRUPT_ID_EMMC, + .end = INTERRUPT_ID_EMMC, + .flags = IORESOURCE_IRQ, + }, +}; +#endif + +#if SDHCI_BCM63XX_FAKE_TRIGGER_NATIVE_CD +static int sdhci_bcm63xx_trigger_native_card_detection_int(void) +{ + /* Placeholder to fake-trigger native card detection interrupt - if required */ + return 0; +} +#endif + +static irqreturn_t sdhci_bcm63xx_card_detection_isr(int irq, void *dev_id) +{ + /* Schedule a card detection after a debounce timeout */ + struct mmc_host *host = dev_id; + +#if SDHCI_BCM63XX_FAKE_TRIGGER_NATIVE_CD + sdhci_bcm63xx_trigger_native_card_detection_int(); +#else + host->trigger_card_event = true; + mmc_detect_change(host, msecs_to_jiffies(200)); +#endif + + BcmHalExternalIrqClear(irq); + return IRQ_HANDLED; +} + +static int sdhci_bcm63xx_init_card_detection_irq(struct mmc_host *host) +{ + int ret = -1; + if( BpGetSDCardDetectExtIntr(&ext_intr) == BP_SUCCESS ) + { + ret = ext_irq_connect(ext_intr, (void *)host, (FN_HANDLER)sdhci_bcm63xx_card_detection_isr); + } + + return ret; +} + +static int sdhci_bcm63xx_probe(struct platform_device *pdev) +{ + int res = 0; + struct sdhci_host *host; + struct sdhci_pltfm_host *pltfm_host; + +#if defined(CONFIG_BCM963138) + /* Get flash type detected by flash API */ + int flash_type = flash_get_flash_type(); + + /* 63138 does not support concurrent NAND and eMMC accesses, therefore + * donot init the emmc driver when we are strapped to boot from NAND + */ + if (flash_type == FLASH_IFC_NAND) + return res; +#endif + + /* Force straps to enable emmc signals - must prevent platform device register if not using emmc */ + AHBSS_CTRL->ahbss_ctrl_cfg |= FORCE_EMMC_BOOT_STRAP; + EMMC_TOP_CFG->emmc_top_cfg_sd_pin_sel = 1; + + /* Check if we are in normal mode, if not then force us in normal mode */ + while( EMMC_BOOT->emmc_boot_status & EMMC_BOOT_MODE_MASK ) + { + EMMC_BOOT->emmc_boot_main_ctl &= ~EMMC_BOOT_ENABLE; + } + +#if defined(CONFIG_BCM963138) + /* For 63138 the interrupt controller is not listed in the device tree + * therefore all interrupt related bindings will fail. We therefore have + * to manually insert the IRQs by redefining the driver resources + */ + pdev->resource = bcm63138_emmc_resources; + pdev->num_resources = ARRAY_SIZE(bcm63138_emmc_resources); +#endif + + host = sdhci_pltfm_init(pdev, &sdhci_bcm63xx_pdata, sizeof(&sdhci_bcm63xx_pdata)); + if (IS_ERR(host)) + return PTR_ERR(host); + + /* Get pltfm host */ + pltfm_host = sdhci_priv(host); + + res = mmc_of_parse(host->mmc); + if (res) + goto err_pltfm_free; + + sdhci_bcm63xx_init_card_detection_irq(host->mmc); + + sdhci_get_of_property(pdev); + + res = sdhci_add_host(host); + +err_pltfm_free: + if (res) + sdhci_pltfm_free(pdev); + + return res; +} + +static int sdhci_bcm63xx_remove(struct platform_device *pdev) +{ + int res; + res = sdhci_pltfm_unregister(pdev); + return res; +} + +static struct platform_driver sdhci_bcm63xx_driver = { + .driver = { + .name = "sdhci-bcm63xx", + .owner = THIS_MODULE, + .of_match_table = sdhci_bcm63xx_of_match, + }, + .probe = sdhci_bcm63xx_probe, + .remove = sdhci_bcm63xx_remove, +}; + +module_platform_driver(sdhci_bcm63xx_driver); + +MODULE_DESCRIPTION("SDHCI driver for Broadcom DSL/PON CPE devices"); +MODULE_AUTHOR("Farhan Ali "); +MODULE_LICENSE("GPL v2"); +#endif /* CONFIG_BCM_KF_EMMC */ diff -urN linux-4.1.52-orig/drivers/mmc/host/sdhci.c linux-4.1.52/drivers/mmc/host/sdhci.c --- linux-4.1.52-orig/drivers/mmc/host/sdhci.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/mmc/host/sdhci.c 2020-02-07 01:47:54.000000000 +0800 @@ -1031,6 +1031,12 @@ WARN_ON(host->cmd); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + /* Force R1b response onto all STOP commands */ + if ((host->quirks2 & SDHCI_QUIRK2_STOP_WITH_TC) && + cmd->opcode == MMC_STOP_TRANSMISSION) + cmd->flags |= MMC_RSP_BUSY; +#endif /* Wait max 10 ms */ timeout = 10; @@ -2426,10 +2432,18 @@ /* The controller does not support the end-of-busy IRQ, * fall through and take the SDHCI_INT_RESPONSE */ +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + /* All STOP commands will have R1b responses forced on them + * therefore any spurious TC before/after CC will be automatically + * handled in sdhci_data_irq + */ + } +#else } else if ((host->quirks2 & SDHCI_QUIRK2_STOP_WITH_TC) && host->cmd->opcode == MMC_STOP_TRANSMISSION && !host->data) { *mask &= ~SDHCI_INT_DATA_END; } +#endif if (intmask & SDHCI_INT_RESPONSE) sdhci_finish_command(host); diff -urN linux-4.1.52-orig/drivers/mtd/brcmnand/bcm63xx-nand.c linux-4.1.52/drivers/mtd/brcmnand/bcm63xx-nand.c --- linux-4.1.52-orig/drivers/mtd/brcmnand/bcm63xx-nand.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/mtd/brcmnand/bcm63xx-nand.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,288 @@ +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +/* + * + * drivers/mtd/brcmnand/bcm7xxx-nand.c + * + <:copyright-BRCM:2011:DUAL/GPL:standard + + Copyright (c) 2011 Broadcom + All Rights Reserved + + Unless you and Broadcom execute a separate written software license + agreement governing use of this software, this software is licensed + to you under the terms of the GNU General Public License version 2 + (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, + with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + + Not withstanding the above, under no circumstances may you combine + this software in any way with any other Broadcom software provided + under a license other than the GPL, without Broadcom's express prior + written consent. + + :> + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "brcmnand_priv.h" +#include +#include + +#define PRINTK(...) +//#define PRINTK printk + +#define DRIVER_NAME "brcmnand" +#define DRIVER_INFO "Broadcom NAND controller" + +extern int setup_mtd_parts(struct mtd_info* mtd); + +static int brcmnanddrv_probe(struct platform_device *pdev); +static int brcmnanddrv_remove(struct platform_device *pdev); + +static struct platform_driver brcmnand_platform_driver = +{ + .probe = brcmnanddrv_probe, + .remove = brcmnanddrv_remove, + .driver = + { + .name = DRIVER_NAME, + }, +}; + +static struct resource brcmnand_resources[] = +{ + [0] = { + .name = DRIVER_NAME, + .flags = IORESOURCE_MEM, + }, +}; + +static struct brcmnand_info { + struct mtd_info mtd; + struct brcmnand_chip brcmnand; + int nr_parts; + struct mtd_partition* parts; +} *gNandInfo[NUM_NAND_CS]; + +int gNandCS[NAND_MAX_CS]; +/* Number of NAND chips, only applicable to v1.0+ NAND controller */ +int gNumNand = 0; +int gClearBBT = 0; +char gClearCET = 0; +uint32_t gNandTiming1[NAND_MAX_CS], gNandTiming2[NAND_MAX_CS]; +uint32_t gAccControl[NAND_MAX_CS], gNandConfig[NAND_MAX_CS]; + +static unsigned long t1[NAND_MAX_CS] = { 0 }; +static int nt1 = 0; +static unsigned long t2[NAND_MAX_CS] = { 0 }; +static int nt2 = 0; +static unsigned long acc[NAND_MAX_CS] = { 0 }; +static int nacc = 0; +static unsigned long nandcfg[NAND_MAX_CS] = { 0 }; +static int ncfg = 0; +static void* gPageBuffer = NULL; + +static int brcmnanddrv_probe(struct platform_device *pdev) +{ + static int csi = 0; // Index into dev/nandInfo array + int cs = 0; // Chip Select + int err = 0; + struct brcmnand_info* info = NULL; + static struct brcmnand_ctrl* ctrl = (struct brcmnand_ctrl*)0; + + if (!gPageBuffer && (gPageBuffer = kmalloc(sizeof(struct brcmnand_buffers), GFP_KERNEL)) == NULL) { + return -ENOMEM; + } + + if ( (ctrl = kmalloc(sizeof(struct brcmnand_ctrl), GFP_KERNEL)) != NULL) { + memset(ctrl, 0, sizeof(struct brcmnand_ctrl)); + ctrl->state = FL_READY; + init_waitqueue_head(&ctrl->wq); + spin_lock_init(&ctrl->chip_lock); + + if ((info = kmalloc(sizeof(struct brcmnand_info), GFP_KERNEL)) != NULL) { + gNandInfo[csi] = info; + memset(info, 0, sizeof(struct brcmnand_info)); + info->brcmnand.ctrl = ctrl; + info->brcmnand.ctrl->numchips = gNumNand = 1; + info->brcmnand.csi = csi; + + /* For now all devices share the same buffer */ + info->brcmnand.ctrl->buffers = (struct brcmnand_buffers*)gPageBuffer; + + info->brcmnand.ctrl->numchips = gNumNand; + info->brcmnand.chip_shift = 0; // Only 1 chip + info->brcmnand.priv = &info->mtd; + info->mtd.name = dev_name(&pdev->dev); + info->mtd.priv = &info->brcmnand; + info->mtd.owner = THIS_MODULE; + + /* Enable the following for a flash based bad block table */ + info->brcmnand.options |= NAND_BBT_USE_FLASH; + + /* Each chip now will have its own BBT (per mtd handle) */ + if (brcmnand_scan(&info->mtd, cs, gNumNand) == 0) { + PRINTK("Master size=%08llx\n", info->mtd.size); + setup_mtd_parts(&info->mtd); + dev_set_drvdata(&pdev->dev, info); + }else + err = -ENXIO; + }else + err = -ENOMEM; + }else + err = -ENOMEM; + + if (err) { + if (gPageBuffer) { + kfree(gPageBuffer); + gPageBuffer = NULL; + } + + if (ctrl) { + kfree(ctrl); + ctrl = NULL; + } + + if (info) { + kfree(info); + info = NULL; + } + } + + return err; +} + +static int brcmnanddrv_remove(struct platform_device *pdev) +{ + struct brcmnand_info *info = dev_get_drvdata(&pdev->dev); + + dev_set_drvdata(&pdev->dev, NULL); + + if (info) { + mtd_device_unregister(&info->mtd); + + brcmnand_release(&info->mtd); + kfree(gPageBuffer); + kfree(info); + } + + return 0; +} + +static int __init brcmnanddrv_init(void) +{ + int ret = 0; + int csi; + int ncsi; + char cmd[32] = "\0"; + struct platform_device *pdev; + + if (flash_get_flash_type() != FLASH_IFC_NAND) + return -ENODEV; + + kerSysBlParmsGetStr(NAND_COMMAND_NAME, cmd, sizeof(cmd)); + PRINTK("%s: brcmnanddrv_init - NANDCMD='%s'\n", __FUNCTION__, cmd); + + if (cmd[0]) { + if (strcmp(cmd, "rescan") == 0) + gClearBBT = 1; + else if (strcmp(cmd, "showbbt") == 0) + gClearBBT = 2; + else if (strcmp(cmd, "eraseall") == 0) + gClearBBT = 8; + else if (strcmp(cmd, "erase") == 0) + gClearBBT = 7; + else if (strcmp(cmd, "clearbbt") == 0) + gClearBBT = 9; + else if (strcmp(cmd, "showcet") == 0) + gClearCET = 1; + else if (strcmp(cmd, "resetcet") == 0) + gClearCET = 2; + else if (strcmp(cmd, "disablecet") == 0) + gClearCET = 3; + else + printk(KERN_WARNING "%s: unknown command '%s'\n", + __FUNCTION__, cmd); + } + + for (csi = 0; csi < NAND_MAX_CS; csi++) { + gNandTiming1[csi] = 0; + gNandTiming2[csi] = 0; + gAccControl[csi] = 0; + gNandConfig[csi] = 0; + } + + if (nacc == 1) + PRINTK("%s: nacc=%d, gAccControl[0]=%08lx, gNandConfig[0]=%08lx\n", + __FUNCTION__, nacc, acc[0], nandcfg[0]); + + if (nacc > 1) + PRINTK("%s: nacc=%d, gAccControl[1]=%08lx, gNandConfig[1]=%08lx\n", + __FUNCTION__, nacc, acc[1], nandcfg[1]); + + for (csi = 0; csi < nacc; csi++) + gAccControl[csi] = acc[csi]; + + for (csi = 0; csi < ncfg; csi++) + gNandConfig[csi] = nandcfg[csi]; + + ncsi = max(nt1, nt2); + + for (csi = 0; csi < ncsi; csi++) { + if (nt1 && csi < nt1) + gNandTiming1[csi] = t1[csi]; + + if (nt2 && csi < nt2) + gNandTiming2[csi] = t2[csi]; + + } + + printk(KERN_INFO DRIVER_INFO " (BrcmNand Controller)\n"); + + if ( (pdev = platform_device_alloc(DRIVER_NAME, 0)) != NULL ) { + platform_device_add(pdev); + platform_device_put(pdev); + ret = platform_driver_register(&brcmnand_platform_driver); + + brcmnand_resources[0].start = BPHYSADDR(BCHP_NAND_REG_START); + brcmnand_resources[0].end = BPHYSADDR(BCHP_NAND_REG_END) + 3; + + if (ret >= 0) + request_resource(&iomem_resource, &brcmnand_resources[0]); + else + printk("brcmnanddrv_init: driver_register failed, err=%d\n", ret); + }else + ret = -ENODEV; + return ret; +} + +static void __exit brcmnanddrv_exit(void) +{ + release_resource(&brcmnand_resources[0]); + platform_driver_unregister(&brcmnand_platform_driver); +} + +module_init(brcmnanddrv_init); +module_exit(brcmnanddrv_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ton Truong "); +MODULE_DESCRIPTION("Broadcom NAND flash driver"); + +#endif //CONFIG_BCM_KF_MTD_BCMNAND diff -urN linux-4.1.52-orig/drivers/mtd/brcmnand/brcmnand_base.c linux-4.1.52/drivers/mtd/brcmnand/brcmnand_base.c --- linux-4.1.52-orig/drivers/mtd/brcmnand/brcmnand_base.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/mtd/brcmnand/brcmnand_base.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,9415 @@ +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +/* + * drivers/mtd/brcmnand/brcmnand_base.c + * + <:copyright-BRCM:2002:GPL/GPL:standard + + Copyright (c) 2002 Broadcom + All Rights Reserved + + 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 (the "GPL"). + + 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. + + + A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by + writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + :> + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "bcm_map_part.h" +#include "board.h" +#include "shared_utils.h" + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +#define NAND_COMPLEX_OOB_WRITE 0x00400000 +#endif + +#define DEBUG(...) do { } while (0) + +//#include "bbm.h" + +#include "brcmnand_priv.h" + +#define PRINTK(...) do {} while (0) +//#define PRINTK printk +//static char brcmNandMsg[1024]; + +//#define DEBUG_HW_ECC + +//#define BRCMNAND_READ_VERIFY +#undef BRCMNAND_READ_VERIFY + +//#ifdef CONFIG_MTD_BRCMNAND_VERIFY_WRITE +//#define BRCMNAND_WRITE_VERIFY +//#endif +#undef BRCMNAND_WRITE_VERIFY + +//#define DEBUG_ISR +#undef DEBUG_ISR +#if defined( DEBUG_ISR ) || defined(BRCMNAND_READ_VERIFY) \ + || defined(BRCMNAND_WRITE_VERIFY) +#if defined(DEBUG_ISR ) || defined(BRCMNAND_READ_VERIFY) +#define EDU_DEBUG_4 +#endif +#if defined(DEBUG_ISR ) || defined(BRCMNAND_WRITE_VERIFY) +#define EDU_DEBUG_5 +#endif +#endif + + +#define PLATFORM_IOFLUSH_WAR() + + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_3_0 +// Block0 +#define BCHP_NAND_ACC_CONTROL_0_BCH_4 (BRCMNAND_ECC_BCH_4 << BCHP_NAND_ACC_CONTROL_ECC_LEVEL_0_SHIFT) + +// Block n > 0 +#define BCHP_NAND_ACC_CONTROL_N_BCH_4 (BRCMNAND_ECC_BCH_4 << BCHP_NAND_ACC_CONTROL_ECC_LEVEL_SHIFT) +#endif + +int gdebug = 0; +extern int edu_debug; + + +// Whether we should clear the BBT to fix a previous error. +/* This will eventually be on the command line, to allow a user to + * clean the flash + */ +extern int gClearBBT; + +/* Number of NAND chips, only applicable to v1.0+ NAND controller */ +extern int gNumNandCS; + +/* The Chip Select [0..7] for the NAND chips from gNumNand above, only applicable to v1.0+ NAND controller */ +extern int gNandCS[]; +extern uint32_t gNandConfig[]; +extern uint32_t gAccControl[]; + +// If wr_preempt_en is enabled, need to disable IRQ during NAND I/O +int wr_preempt_en = 0; + +// Last known good ECC sector offset (512B sector that does not generate ECC error). +// used in HIF_INTR2 WAR. +loff_t gLastKnownGoodEcc; + +#define DRIVER_NAME "brcmnand" + +#define HW_AUTOOOB_LAYOUT_SIZE 32 /* should be enough */ + + +#ifdef CONFIG_MTD_BRCMNAND_CORRECTABLE_ERR_HANDLING +/* Avoid infinite recursion between brcmnand_refresh_blk() and brcmnand_read_ecc() */ +static atomic_t inrefresh = ATOMIC_INIT(0); +static int brcmnand_refresh_blk(struct mtd_info *, loff_t); +static int brcmnand_erase_nolock(struct mtd_info *, struct erase_info *, int); +#endif + +/* + * ID options + */ +#define BRCMNAND_ID_HAS_BYTE3 0x00000001 +#define BRCMNAND_ID_HAS_BYTE4 0x00000002 +#define BRCMNAND_ID_HAS_BYTE5 0x00000004 +#define BRCMNAND_ID_HYNIX_LEGACY 0x00010000 + +// TYPE2 +#define BRCMNAND_ID_HAS_BYTE4_T2 0x00000008 +#define BRCMNAND_ID_HAS_BYTE5_T2 0x00000010 +#define BRCMNAND_ID_HAS_BYTE6_T2 0x00000020 + +#define BRCMNAND_ID_EXT_BYTES \ + (BRCMNAND_ID_HAS_BYTE3 | BRCMNAND_ID_HAS_BYTE4 | BRCMNAND_ID_HAS_BYTE5) + +#define BRCMNAND_ID_EXT_BYTES_TYPE2 \ + (BRCMNAND_ID_HAS_BYTE3 | BRCMNAND_ID_HAS_BYTE4_T2 | \ + BRCMNAND_ID_HAS_BYTE5_T2 | BRCMNAND_ID_HAS_BYTE6_T2) + + +// MICRON M60A is similar to Type 1 with a few exceptions. +#define BRCMNAND_ID_HAS_MICRON_M60A 0x00020000 +#define BRCMNAND_ID_EXT_MICRON_M60A BRCMNAND_ID_EXT_BYTES + +// MICRON M61A ID encoding is a totally different (and dying beast, temporarily until ONFI) +#define BRCMNAND_ID_HAS_MICRON_M61A 0x00040000 + +#define BRCMNAND_ID_EXT_MICRON_M61A (BRCMNAND_ID_HAS_MICRON_M61A) + +#define BRCMNAND_ID_HAS_MICRON_M68A 0x00080000 +#define BRCMNAND_ID_EXT_MICRON_M68A \ + (BRCMNAND_ID_HAS_MICRON_M60A | BRCMNAND_ID_HAS_MICRON_M68A) + + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_4_0 + +#define ONFI_RDPARAM_SIGNATURE_OFS 0 +#define ONFI_NBR_PARAM_PAGE_OFS 14 +#define ONFI_RDPARAM_PAGESIZE_OFS 80 +#define ONFI_RDPARAM_OOBSIZE_OFS 84 +#define ONFI_RDPARAM_ECC_LEVEL_OFS 112 +#define ONFI_NBR_BITS_PER_CELL_OFS 102 + +/* + * The following def is for a dev with 3 replica of the param page + * Need to be adjusted according based on the actual nbr of param pages. + */ + +#define ONFI_EXTPARAM_OFS 768 +#define ONFI_EXTPARAM_SIG1_OFS 768 +#define ONFI_EXTPARAM_SIG2_OFS 772 +#define ONFI_EXTPARAM_EXT_ECC_OFS 800 +#define ONFI_EXTPARAM_CODEWORK_OFS 801 + + + +#define ONFI_SIGNATURE 0x4F4E4649 /* "ONFI" */ +#define ONFI_EXTPARAM_SIG 0x45505053 /* "EPPS" */ + +#endif + +typedef struct brcmnand_chip_Id { + uint8 mafId, chipId; + uint8 chipId345[3]; /* ID bytes 3,4,5: Resolve ambiguity in chipId */ + const char* chipIdStr; + uint32 eccLevel; /* Only for Samsung Type 2 */ + uint32 sectorSize; /* Only for Samsung Type 2 */ + uint32 nbrBlocks; // Only for devices that do not encode Size into ID string. + uint32 options; + uint32 idOptions; // Whether chip has all 5 ID bytes + uint32 timing1, timing2; // Specify a non-zero value to override the default timings. + int nop; // Number of partial writes per page + unsigned int ctrlVersion; // Required controller version if different than 0 +} brcmnand_chip_Id; + +/* + * List of supported chip + */ +static brcmnand_chip_Id brcmnand_chips[] = { + { /* 0a */ + .chipId = SAMSUNG_K9F1G08U0E, + .chipId345 = { 0x00, 0x95, 0x41 }, + .mafId = FLASHTYPE_SAMSUNG, + .chipIdStr = "Samsung K9F1G08U0E", + .options = NAND_BBT_USE_FLASH | NAND_COMPLEX_OOB_WRITE, /* Use BBT on flash */ + //| NAND_COMPLEX_OOB_WRITE /* Write data together with OOB for write_oob */ + .idOptions = BRCMNAND_ID_EXT_BYTES, + .timing1 = 0, + .timing2 = 0, + .nop = 1, + .ctrlVersion = CONFIG_MTD_BRCMNAND_VERS_3_0, + }, + + { /* 0b */ + .chipId = SAMSUNG_K9F1G08U0A, + .mafId = FLASHTYPE_SAMSUNG, + .chipIdStr = "Samsung K9F1G08U0A/B/C/D", + .options = NAND_BBT_USE_FLASH, /* Use BBT on flash */ + .idOptions = 0, + //| NAND_COMPLEX_OOB_WRITE /* Write data together with OOB for write_oob */ + .timing1 = 0, //00070000, + .timing2 = 0, + .nop = 8, + .ctrlVersion = 0, /* THT Verified on data-sheet 7/10/08: Allows 4 on main and 4 on OOB */ + }, + + { /* 1 */ + .chipId = ST_NAND512W3A, + .mafId = FLASHTYPE_ST, + .chipIdStr = "ST ST_NAND512W3A", + .options = NAND_BBT_USE_FLASH, + .idOptions = 0, + .timing1 = 0, //0x6474555f, + .timing2 = 0, //0x00000fc7, + .nop = 8, + .ctrlVersion = 0, + }, + { /* 2 */ + .chipId = ST_NAND256W3A, + .mafId = FLASHTYPE_ST, + .chipIdStr = "ST ST_NAND256W3A", + .options = NAND_BBT_USE_FLASH, + .idOptions = 0, + .timing1 = 0, //0x6474555f, + .timing2 = 0, //0x00000fc7, + .nop = 8, + .ctrlVersion = 0, + }, +#if 0 // EOL + { /* 4 */ + .chipId = HYNIX_HY27UF081G2M, + .mafId = FLASHTYPE_HYNIX, + .chipIdStr = "HYNIX HY27UF081G2M", + .options = NAND_BBT_USE_FLASH + , + }, +#endif + /* This is the new version of HYNIX_HY27UF081G2M which is EOL. + * Both use the same DevID + */ + { /* 3 */ + .chipId = HYNIX_HY27UF081G2A, + .mafId = FLASHTYPE_HYNIX, + .chipIdStr = "Hynix HY27UF081G2A", + .options = NAND_BBT_USE_FLASH, + .idOptions = 0, + .timing1 = 0, + .timing2 = 0, + .nop = 8, + .ctrlVersion = 0, + }, + +#if 0 +/* Obsoleted by the new Micron flashes */ + { /* 4 */ + .chipId = MICRON_MT29F2G08AAB, + .mafId = FLASHTYPE_MICRON, + .chipIdStr = "MICRON_MT29F2G08AAB", + .options = NAND_BBT_USE_FLASH, + .idOptions = 0, + .timing1 = 0, .timing2 = 0, + .nop = 8, + .ctrlVersion = 0, + }, +/* This is just the 16 bit version of the above? + { + .chipId = MICRON_MT29F2G16AAB, + .mafId = FLASHTYPE_MICRON, + .chipIdStr = "MICRON_MT29F2G16AAB", + .options = NAND_BBT_USE_FLASH + , + } + */ +#endif + { /* 5 */ + .chipId = SAMSUNG_K9F2G08U0A, + .mafId = FLASHTYPE_SAMSUNG, + .chipIdStr = "Samsung K9F2G08U0A", + .options = NAND_BBT_USE_FLASH, + .idOptions = 0, + .timing1 = 0, + .timing2 = 0, + .nop = 4, + .ctrlVersion = CONFIG_MTD_BRCMNAND_VERS_2_1, + }, + +#if 0 +/* + * SW3556-862, SWLINUX-1459 + * Samsung replaced this SLC part with a new SLC part, different block size and page size but re-use the same ID + * Side effect: The old flash part can no longer be supported. + */ + { /* 6 */ + .chipId = SAMSUNG_K9K8G08U0A, + .mafId = FLASHTYPE_SAMSUNG, + .chipIdStr = "Samsung K9K8G08U0A", + .options = NAND_BBT_USE_FLASH, + .idOptions = 0, + .timing1 = 0, .timing2 = 0, + .nop = 4, + .ctrlVersion = CONFIG_MTD_BRCMNAND_VERS_2_1, + }, +#else + { /* 6 Same old ID 0xD3, new part, so the old #define macro is kept, but IDstr is changed to reflect new part number */ + .chipId = SAMSUNG_K9K8G08U0A, + .mafId = FLASHTYPE_SAMSUNG, + .chipIdStr = "Samsung K9F8G08U0M", + .options = NAND_BBT_USE_FLASH, + .idOptions = BRCMNAND_ID_EXT_BYTES, /* New Samsung SLC has all 5 ID bytes defined */ + .timing1 = 0, + .timing2 = 0, + .nop = 4, + .ctrlVersion = CONFIG_MTD_BRCMNAND_VERS_2_1, + }, +#endif + + + { /* 7 */ + .chipId = HYNIX_HY27UF082G2A, + .mafId = FLASHTYPE_HYNIX, + .chipIdStr = "Hynix HY27UF082G2A", + .options = NAND_BBT_USE_FLASH, + .idOptions = 0, + .timing1 = 0x00420000, + .timing2 = 0x00000005, + .nop = 8, + .ctrlVersion = 0, + }, + +#if 0 +/* EOL replaced by the following entry, with reduced NOP */ + + { /* 8 */ + .chipId = HYNIX_HY27UF084G2M, + .mafId = FLASHTYPE_HYNIX, + .chipIdStr = "Hynix HY27UF084G2M", + .options = NAND_BBT_USE_FLASH, + .idOptions = 0, + .timing1 = 0, .timing2 = 0, + .nop = 8, + .ctrlVersion = 0, + }, +#endif + + { /* 8 */ + .chipId = HYNIX_HY27U4G8F2D, + .mafId = FLASHTYPE_HYNIX, + .chipIdStr = "Hynix HY27U4G8F2D", + .options = NAND_BBT_USE_FLASH, + .idOptions = BRCMNAND_ID_EXT_BYTES | BRCMNAND_ID_HYNIX_LEGACY, + .timing1 = 0x00420000, + .timing2 = 0x00000005, + .nop = 4, + .ctrlVersion = 0, + }, + + { /* 9 */ + .chipId = SPANSION_S30ML512P_08, + .mafId = FLASHTYPE_SPANSION, + .chipIdStr = "SPANSION S30ML512P_08", + .options = NAND_BBT_USE_FLASH, + .idOptions = 0, + .timing1 = 0, + .timing2 = 0, + .nop = 8, + .ctrlVersion = 0, + }, + + { /* 10 */ + .chipId = SPANSION_S30ML512P_16, + .mafId = FLASHTYPE_SPANSION, + .chipIdStr = "SPANSION S30ML512P_16", + .options = NAND_BBT_USE_FLASH, + .idOptions = 0, + .timing1 = 0, + .timing2 = 0, + .nop = 8, + .ctrlVersion = 0, + }, + + { /* 11 */ + .chipId = SPANSION_S30ML256P_08, + .mafId = FLASHTYPE_SPANSION, + .chipIdStr = "SPANSION S30ML256P_08", + .options = NAND_BBT_USE_FLASH, + .idOptions = 0, + .timing1 = 0, + .timing2 = 0, + .nop = 8, + .ctrlVersion = 0, + }, + + { /* 12 */ + .chipId = SPANSION_S30ML256P_16, + .mafId = FLASHTYPE_SPANSION, + .chipIdStr = "SPANSION S30ML256P_16", + .options = NAND_BBT_USE_FLASH, + .idOptions = 0, + .timing1 = 0, + .timing2 = 0, + .nop = 8, + .ctrlVersion = 0, + }, + + { /* 13 */ + .chipId = SPANSION_S30ML128P_08, + .mafId = FLASHTYPE_SPANSION, + .chipIdStr = "SPANSION S30ML128P_08", + .options = NAND_BBT_USE_FLASH, + .idOptions = 0, + .timing1 = 0, + .timing2 = 0, + .nop = 8, + .ctrlVersion = 0, + }, + + { /* 14 */ + .chipId = SPANSION_S30ML128P_16, + .mafId = FLASHTYPE_SPANSION, + .chipIdStr = "SPANSION S30ML128P_16", + .options = NAND_BBT_USE_FLASH, + .idOptions = 0, + .timing1 = 0, + .timing2 = 0, + .nop = 8, + .ctrlVersion = 0, + }, + + { /* 15 */ + .chipId = SPANSION_S30ML01GP_08, + .mafId = FLASHTYPE_SPANSION, + .chipIdStr = "SPANSION_S30ML01GP_08", + .options = NAND_BBT_USE_FLASH, + .idOptions = 0, + .timing1 = 0, + .timing2 = 0, + .nop = 8, + .ctrlVersion = 0, + }, + + { /* 16 */ + .chipId = SPANSION_S30ML01GP_16, + .mafId = FLASHTYPE_SPANSION, + .chipIdStr = "SPANSION_S30ML01GP_16", + .options = NAND_BBT_USE_FLASH, + .idOptions = 0, + .timing1 = 0, + .timing2 = 0, + .nop = 8, + .ctrlVersion = 0, + }, + + { /* 17 */ + .chipId = SPANSION_S30ML02GP_08, + .mafId = FLASHTYPE_SPANSION, + .chipIdStr = "SPANSION_S30ML02GP_08", + .options = NAND_BBT_USE_FLASH, + .idOptions = 0, + .timing1 = 0, + .timing2 = 0, + .nop = 8, + .ctrlVersion = 0, + }, + + { /* 18 */ + .chipId = SPANSION_S30ML02GP_16, + .mafId = FLASHTYPE_SPANSION, + .chipIdStr = "SPANSION_S30ML02GP_16", + .options = NAND_BBT_USE_FLASH, + .idOptions = 0, + .timing1 = 0, + .timing2 = 0, + .nop = 8, + .ctrlVersion = 0, + }, + + { /* 19 */ + .chipId = SPANSION_S30ML04GP_08, + .mafId = FLASHTYPE_SPANSION, + .chipIdStr = "SPANSION_S30ML04GP_08", + .options = NAND_BBT_USE_FLASH, + .idOptions = 0, + .timing1 = 0, + .timing2 = 0, + .nop = 8, + .ctrlVersion = 0, + }, + + { /* 20 */ + .chipId = SPANSION_S30ML04GP_16, + .mafId = FLASHTYPE_SPANSION, + .chipIdStr = "SPANSION_S30ML04GP_16", + .options = NAND_BBT_USE_FLASH, + .idOptions = 0, + .timing1 = 0, + .timing2 = 0, + .nop = 8, + .ctrlVersion = 0, + }, + + { /* 21 */ + .chipId = ST_NAND128W3A, + .mafId = FLASHTYPE_ST, + .chipIdStr = "ST NAND128W3A", + .options = NAND_BBT_USE_FLASH, + .idOptions = 0, + .timing1 = 0, + .timing2 = 0, + .nop = 8, + .ctrlVersion = 0, + }, + + /* The following 6 ST chips only allow 4 writes per page, and requires version2.1 (4) of the controller or later */ + { /* 22 */ + .chipId = ST_NAND01GW3B, + .mafId = FLASHTYPE_ST, + .chipIdStr = "ST NAND01GW3B2B", + .nbrBlocks = 1024, /* size=128MB, bsize=128K */ + .options = NAND_BBT_USE_FLASH, + .idOptions = 0, + .timing1 = 0, + .timing2 = 0, + .nop = 4, + .ctrlVersion = CONFIG_MTD_BRCMNAND_VERS_2_0, + }, + +#if 0 +//R version = 1.8V + { /* 23 */ + .chipId = ST_NAND01GR3B, + .mafId = FLASHTYPE_ST, + .chipIdStr = "ST NAND01GR3B2B", + .options = NAND_BBT_USE_FLASH, + .idOptions = 0, + .timing1 = 0, .timing2 = 0, + .nop = 4, + .ctrlVersion = CONFIG_MTD_BRCMNAND_VERS_2_0, + }, + + { /* 24 */ + .chipId = ST_NAND02GR3B, + .mafId = FLASHTYPE_ST, + .chipIdStr = "ST NAND02GR3B2C", + .options = NAND_BBT_USE_FLASH, + .idOptions = 0, + .timing1 = 0, .timing2 = 0, + .nop = 4, + .ctrlVersion = CONFIG_MTD_BRCMNAND_VERS_2_0, + }, +#endif + { /* 25 */ + .chipId = ST_NAND02GW3B, + .mafId = FLASHTYPE_ST, + .chipIdStr = "ST NAND02GW3B2C", + .nbrBlocks = 2048, /* size=256MB, bsize=128K */ + .options = NAND_BBT_USE_FLASH, + .idOptions = 0, + .timing1 = 0, + .timing2 = 0, + .nop = 4, + .ctrlVersion = CONFIG_MTD_BRCMNAND_VERS_2_0, + }, + + { /* 26 */ + .chipId = ST_NAND04GW3B, + .mafId = FLASHTYPE_ST, + .chipIdStr = "ST NAND04GW3B2B", + .options = NAND_BBT_USE_FLASH, + .idOptions = 0, + .timing1 = 0, + .timing2 = 0, + .nop = 4, + .ctrlVersion = CONFIG_MTD_BRCMNAND_VERS_2_0, + }, + { /* 27 */ + .chipId = ST_NAND08GW3B, + .mafId = FLASHTYPE_ST, + .chipIdStr = "ST NAND08GW3B2A", + .options = NAND_BBT_USE_FLASH, + .idOptions = 0, + .timing1 = 0, + .timing2 = 0, + .nop = 4, + .ctrlVersion = CONFIG_MTD_BRCMNAND_VERS_2_0, + }, + + { /* 28a */ + .chipId = SAMSUNG_K9LBG08U0M, + .chipId345 = { 0x55, 0xB6, 0x78 }, + .mafId = FLASHTYPE_SAMSUNG, + .chipIdStr = "Samsung K9LBG08U0M", + .options = NAND_BBT_USE_FLASH, /* Use BBT on flash */ + //| NAND_COMPLEX_OOB_WRITE /* Write data together with OOB for write_oob */ + .idOptions = BRCMNAND_ID_EXT_BYTES, + .timing1 = 0, + .timing2 = 0, + .nop = 1, + .ctrlVersion = CONFIG_MTD_BRCMNAND_VERS_3_0, + }, + + { /* 28b */ + .chipId = SAMSUNG_K9LBG08U0D, + .chipId345 = { 0xD5, 0x29, 0x38 }, + .nbrBlocks = 8192, + //.eccLevel = 8, , Will decode from ID string + .mafId = FLASHTYPE_SAMSUNG, + .chipIdStr = "Samsung K9LBG08UD", + .options = NAND_BBT_USE_FLASH, /* Use BBT on flash */ + //| NAND_COMPLEX_OOB_WRITE /* Write data together with OOB for write_oob */ + .idOptions = BRCMNAND_ID_EXT_BYTES_TYPE2, + .timing1 = 0, + .timing2 = 0, + .nop = 1, + .ctrlVersion = CONFIG_MTD_BRCMNAND_VERS_3_0, + }, + + { /* 28c */ + .chipId = SAMSUNG_K9LBG08U0E, + .chipId345 = { 0xC5, 0x72, 0x54 }, /* C5h, 72h, 54h, 42h */ + .nbrBlocks = 4096, /* 4GB flash */ + //.eccLevel = 24 per 1KB, Will decode from ID string + .mafId = FLASHTYPE_SAMSUNG, + .chipIdStr = "Samsung K9LBG08UD", + .options = NAND_BBT_USE_FLASH, /* Use BBT on flash */ + //| NAND_COMPLEX_OOB_WRITE /* Write data together with OOB for write_oob */ + .idOptions = BRCMNAND_ID_EXT_BYTES_TYPE2, + .timing1 = 0, + .timing2 = 0, + .nop = 1, + .ctrlVersion = CONFIG_MTD_BRCMNAND_VERS_5_0, + }, + + { /* 29a */ + .chipId = SAMSUNG_K9GAG08U0D, + .chipId345 = { 0x94, 0x29, 0x34 }, + .mafId = FLASHTYPE_SAMSUNG, + .nbrBlocks = 4096, + //.eccLevel = 8 , Will decode from ID string + .chipIdStr = "Samsung K9GAG08U0D", + .options = NAND_BBT_USE_FLASH, /* Use BBT on flash */ + //| NAND_COMPLEX_OOB_WRITE /* Write data together with OOB for write_oob */ + .idOptions = BRCMNAND_ID_EXT_BYTES_TYPE2, + .timing1 = 0, + .timing2 = 0, + .nop = 1, + .ctrlVersion = CONFIG_MTD_BRCMNAND_VERS_3_0, + }, + + { /* 29b */ + .chipId = SAMSUNG_K9GAG08U0E, + .chipId345 = { 0x84, 0x72, 0x50 }, /* 84h, 72h, 50h, 42h */ + .mafId = FLASHTYPE_SAMSUNG, + .nbrBlocks = 2048, + //.eccLevel = 24 per 1KB, Will decode from ID string + .chipIdStr = "Samsung K9GAG08U0E", + .options = NAND_BBT_USE_FLASH, /* Use BBT on flash */ + //| NAND_COMPLEX_OOB_WRITE /* Write data together with OOB for write_oob */ + .idOptions = BRCMNAND_ID_EXT_BYTES_TYPE2, + .timing1 = 0, + .timing2 = 0, + .nop = 1, + .ctrlVersion = CONFIG_MTD_BRCMNAND_VERS_5_0, + }, + + { /* 30 */ + .chipId = HYNIX_HY27UT088G2A, + .mafId = FLASHTYPE_HYNIX, + .chipIdStr = "HYNIX_HY27UT088G2A", + .options = NAND_BBT_USE_FLASH | NAND_SCAN_BI_3RD_PAGE, /* BBT on flash + BI on (last-2) page */ + //| NAND_COMPLEX_OOB_WRITE /* Write data together with OOB for write_oob */ + .idOptions = BRCMNAND_ID_EXT_BYTES, + .timing1 = 0x00420000, + .timing2 = 0x00000005, + .nop = 1, + .ctrlVersion = CONFIG_MTD_BRCMNAND_VERS_3_0, + }, + + { /* 31 */ + .chipId = HYNIX_HY27UAG8T2M, + .mafId = FLASHTYPE_HYNIX, + .chipIdStr = "HYNIX_HY27UAG8T2M", + .options = NAND_BBT_USE_FLASH | NAND_SCAN_BI_3RD_PAGE, /* BBT on flash + BI on (last-2) page */ + //| NAND_COMPLEX_OOB_WRITE /* Write data together with OOB for write_oob */ + .idOptions = BRCMNAND_ID_EXT_BYTES, + .timing1 = 0, + .timing2 = 0, + .nop = 1, + .ctrlVersion = CONFIG_MTD_BRCMNAND_VERS_3_0, + }, + + { /* 32 */ + .chipId = TOSHIBA_TC58NVG0S3ETA00, + .mafId = FLASHTYPE_TOSHIBA, + .chipIdStr = "TOSHIBA TC58NVG0S3ETA00", + .options = NAND_BBT_USE_FLASH, + .idOptions = BRCMNAND_ID_EXT_BYTES, + .timing1 = 0, + .timing2 = 0, + .nop = 4, + .eccLevel = 1, + .nbrBlocks = 1024, + .ctrlVersion = CONFIG_MTD_BRCMNAND_VERS_2_0, + }, + + { /* 33 */ + .chipId = TOSHIBA_TC58NVG1S3ETAI5, + .mafId = FLASHTYPE_TOSHIBA, + .chipIdStr = "TOSHIBA TC58NVG1S3ETAI5", + .options = NAND_BBT_USE_FLASH, + .idOptions = BRCMNAND_ID_EXT_BYTES, + .timing1 = 0, + .timing2 = 0, + .nop = 4, + .eccLevel = 1, + .nbrBlocks = 2048, + .ctrlVersion = CONFIG_MTD_BRCMNAND_VERS_2_0, + }, + + { /* 34 */ + .chipId = TOSHIBA_TC58NVG3S0ETA00, + .mafId = FLASHTYPE_TOSHIBA, + .chipIdStr = "TOSHIBA TC58NVG3S0ETA00", + .options = NAND_BBT_USE_FLASH, + .idOptions = BRCMNAND_ID_EXT_BYTES, + .timing1 = 0, + .timing2 = 0, + .nop = 4, + .eccLevel = 4, + .nbrBlocks = 4096, + .ctrlVersion = CONFIG_MTD_BRCMNAND_VERS_3_0, + }, + + { /* 35 */ + .chipId = MICRON_MT29F1G08ABA, + .mafId = FLASHTYPE_MICRON, + .chipIdStr = "MICRON MT29F1G08ABA", + .options = NAND_BBT_USE_FLASH, /* Use BBT on flash */ + .idOptions = BRCMNAND_ID_EXT_MICRON_M68A, + .timing1 = 0, + .timing2 = 0, + .nop = 4, + .ctrlVersion = CONFIG_MTD_BRCMNAND_VERS_3_0, + }, + + { /* 36 */ + .chipId = MICRON_MT29F2G08ABA, + .mafId = FLASHTYPE_MICRON, + .chipIdStr = "MICRON MT29F2G08ABA", + .options = NAND_BBT_USE_FLASH, /* Use BBT on flash */ + .idOptions = BRCMNAND_ID_EXT_MICRON_M68A, /* 69A actually */ + .timing1 = 0, + .timing2 = 0, + .nop = 4, + .ctrlVersion = CONFIG_MTD_BRCMNAND_VERS_3_0, + }, + + { /* 37 */ + .chipId = MICRON_MT29F4G08ABA, + .mafId = FLASHTYPE_MICRON, + .chipIdStr = "MICRON MT29F4G08ABA", + .options = NAND_BBT_USE_FLASH, /* Use BBT on flash */ + .idOptions = BRCMNAND_ID_EXT_MICRON_M60A, + .timing1 = 0, + .timing2 = 0, + .nop = 4, + .ctrlVersion = CONFIG_MTD_BRCMNAND_VERS_3_0, + }, + + { /* 38 */ + .chipId = MICRON_MT29F8G08ABA, + .mafId = FLASHTYPE_MICRON, + .chipIdStr = "MICRON MT29F8G08ABA", + .options = NAND_BBT_USE_FLASH, /* Use BBT on flash */ + .idOptions = BRCMNAND_ID_EXT_MICRON_M61A, + .timing1 = 0, + .timing2 = 0, + .nop = 4, + .ctrlVersion = CONFIG_MTD_BRCMNAND_VERS_3_0, + }, + +#if 0 +/* New Chip ID scheme in place and working, but as of 2631-2.5 these do not work yet, for some unknown reason */ + + { /* 37 */ + .mafId = FLASHTYPE_MICRON, + .chipId = MICRON_MT29F16G08ABA, + .chipId345 = { 0x00, 0x26, 0x89 }, + .chipIdStr = "MICRON MT29F16G08ABA SLC", + .options = NAND_BBT_USE_FLASH, /* Use BBT on flash */ + .idOptions = BRCMNAND_ID_EXT_MICRON_M61A, + .timing1 = 0xFFFFFFFF, + .timing2 = 0xFFFFFFFF, + .nop = 4, + .ctrlVersion = CONFIG_MTD_BRCMNAND_VERS_3_0, /* Require BCH-8 only */ + }, + + { /* 38 */ + .mafId = FLASHTYPE_MICRON, + .chipId = MICRON_MT29F16G08CBA, + .chipId345 = { 0x04, 0x46, 0x85 }, + .chipIdStr = "MICRON MT29F16G08CBA MLC", + .options = NAND_BBT_USE_FLASH, /* Use BBT on flash */ + .idOptions = BRCMNAND_ID_EXT_MICRON_M61A, + .timing1 = 0, + .timing2 = 0, + .nop = 4, + .ctrlVersion = CONFIG_MTD_BRCMNAND_VERS_3_3, /* Require BCH-12 */ + }, +#endif + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_4_0 + + { /* ONFI ENTRY */ + .chipId = 0xFF, + .mafId = 0xFF, + .chipIdStr = "ONFI NAND CHIP", + .options = NAND_BBT_USE_FLASH, + .timing1 = 0, .timing2 = 0, + .ctrlVersion = CONFIG_MTD_BRCMNAND_VERS_4_0, /* ONFI capable NAND controllers */ + }, +#endif + + { /* LAST DUMMY ENTRY */ + .chipId = 0, + .mafId = 0, + .chipIdStr = "UNKNOWN NAND CHIP", + .options = NAND_BBT_USE_FLASH, + .timing1 = 0, .timing2 = 0, + .ctrlVersion = 0, + } +}; + + + +// Max chip account for the last dummy entry +#define BRCMNAND_MAX_CHIPS (ARRAY_SIZE(brcmnand_chips) - 1) + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_4_0 + +#define BRCMNAND_ONFI_IDX (BRCMNAND_MAX_CHIPS - 1) +#endif + +#include /* BRCMNAND controller defined OOB */ + +static unsigned char ffchars[BRCMNAND_FCACHE_SIZE]; + +//static unsigned char eccmask[128]; // Will be initialized during probe + + +static uint32_t brcmnand_registerHoles[] = { + + // 3.2 and earlier + 0x1c, + 0x44, 0x4c, + 0x5c, + 0x88, 0x8c, + 0xb8, 0xbc, +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_3_3 + 0xc4, 0xc8, 0xcc, +#ifndef BCHP_NAND_ACC_CONTROL_CS3 + 0xf0, 0xf4, 0xf8, 0xfc, +#endif + #if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_3_4 + 0x100,0x104, 0x108, 0x10c, + #endif + 0x110,0x114, 0x118, 0x11c, + 0x120,0x124, 0x128, 0x12c, +#endif +}; + +static int brcmnand_wait(struct mtd_info *mtd, int state, uint32_t* pStatus, int timeout); +static int brcmnand_read_page(struct mtd_info *mtd, uint8_t *outp_buf, uint8_t* outp_oob, uint64_t page); + +// Is there a register at the location +static int inRegisterHoles(uint32_t reg) +{ + int i; + // Alas, 7420c0 and later register offsets are 0x0044xxxx compared to 0x0000xxxx in earlier versions + uint32_t regOffset = reg - BCHP_NAND_REVISION; + + for (i = 0; i < ARRAY_SIZE(brcmnand_registerHoles); i++) { + if (regOffset == brcmnand_registerHoles[i]) + return 1; // In register hole + } + return 0; // Not in hole +} + + +static uint32_t brcmnand_ctrl_read(uintptr_t nandCtrlReg) +{ + uintptr_t pReg = (BRCMNAND_CTRL_REGS + + nandCtrlReg - BCHP_NAND_REVISION); + + if (nandCtrlReg < BCHP_NAND_REVISION || nandCtrlReg > BCHP_NAND_LAST_REG || + (nandCtrlReg & 0x3) != 0) { + //printk("brcmnand_ctrl_read: Invalid register value %08x\n", nandCtrlReg); + return 0; + } + if (gdebug > 3) printk("%s: CMDREG=%p xval=0x%08x\n", __FUNCTION__, + (void*)nandCtrlReg, (uint32_t)BDEV_RD(pReg)); + + return (uint32_t)BDEV_RD(pReg); +} + + +static void brcmnand_ctrl_write(uintptr_t nandCtrlReg, uint32_t val) +{ + uintptr_t pReg = (uintptr_t)(BRCMNAND_CTRL_REGS + nandCtrlReg - BCHP_NAND_REVISION); + + if (nandCtrlReg < BCHP_NAND_REVISION || nandCtrlReg > BCHP_NAND_LAST_REG || + (nandCtrlReg & 0x3) != 0) { + printk( "brcmnand_ctrl_read: Invalid register value %p\n", (void*)nandCtrlReg); + } + + BDEV_WR(pReg, val); + + if (gdebug > 3) printk("%s: CMDREG=%p val=%08x\n", __FUNCTION__, (void*)nandCtrlReg, val); +} + + +/* + * chip: BRCM NAND handle + * offset: offset from start of mtd, not necessarily the same as offset from chip. + * cmdEndAddr: 1 for CMD_END_ADDRESS, 0 for CMD_ADDRESS + * + * Returns the real ldw of the address w.r.t. the chip. + */ + +#if 0 // CONFIG_MTD_BRCMNAND_VERSION < CONFIG_MTD_BRCMNAND_VERS_3_3 +/* + * Old codes assume all CSes have the same flash + * Here offset is the offset from CS0. + */ +static uint32_t brcmnand_ctrl_writeAddr(struct brcmnand_chip* chip, loff_t offset, int cmdEndAddr) +{ +#if CONFIG_MTD_BRCMNAND_VERSION <= CONFIG_MTD_BRCMNAND_VERS_0_1 + uint32_t pAddr = offset + chip->pbase; + uint32_t ldw = 0; + + chip->ctrl_write(cmdEndAddr ? BCHP_NAND_CMD_END_ADDRESS : BCHP_NAND_CMD_ADDRESS, pAddr); + +#else + uint32_t udw, ldw, cs; + DIunion chipOffset; + +//char msg[24]; + + + // cs is the index into chip->ctrl->CS[] + cs = (uint32_t)(offset >> chip->chip_shift); + // chipOffset is offset into the current CS + + chipOffset.ll = offset & (chip->chipSize - 1); + + if (cs >= chip->ctrl->numchips) { + printk(KERN_ERR "%s: Offset=%0llx outside of chip range cs=%d, chip->ctrl->CS[cs]=%d\n", + __FUNCTION__, offset, cs, chip->ctrl->CS[cs]); + BUG(); + return 0; + } + + if (gdebug) printk("CS=%d, chip->ctrl->CS[cs]=%d\n", cs, chip->ctrl->CS[cs]); + // ldw is lower 32 bit of chipOffset, need to add pbase when on CS0 and XOR is ON. + if (!chip->xor_disable[cs]) { + ldw = chipOffset.s.low + chip->pbase; + }else { + ldw = chipOffset.s.low; + } + + udw = chipOffset.s.high | (chip->ctrl->CS[cs] << 16); + + if (gdebug > 3) printk("%s: offset=%0llx cs=%d ldw = %08x, udw = %08x\n", __FUNCTION__, offset, cs, ldw, udw); + chip->ctrl_write(cmdEndAddr ? BCHP_NAND_CMD_END_ADDRESS : BCHP_NAND_CMD_ADDRESS, ldw); + chip->ctrl_write(BCHP_NAND_CMD_EXT_ADDRESS, udw); + + +#endif + return (ldw); //(ldw ^ 0x1FC00000); +} + +#else +/* + * Controller v3.3 or later allows heterogenous flashes + * Here offset is the offset from the start of the flash (CSn), as each flash has its own mtd handle + */ + +static uint32_t brcmnand_ctrl_writeAddr(struct brcmnand_chip* chip, loff_t offset, int cmdEndAddr) +{ + uint32_t udw, ldw, cs; + DIunion chipOffset; + + chipOffset.ll = offset & (chip->chipSize - 1); + cs = chip->ctrl->CS[chip->csi]; +//if (gdebug) printk("CS=%d, chip->ctrl->CS[cs]=%d\n", cs, chip->ctrl->CS[chip->csi]); + // ldw is lower 32 bit of chipOffset, need to add pbase when on CS0 and XOR is ON. + if (!chip->xor_disable) { + ldw = chipOffset.s.low + chip->pbase; + }else { + ldw = chipOffset.s.low; + } + + udw = chipOffset.s.high | (cs << 16); + + if (gdebug > 3) printk("%s: offset=%0llx cs=%d ldw = %08x, udw = %08x\n", __FUNCTION__, offset, cs, ldw, udw); + chip->ctrl_write(cmdEndAddr ? BCHP_NAND_CMD_END_ADDRESS : BCHP_NAND_CMD_ADDRESS, ldw); + chip->ctrl_write(BCHP_NAND_CMD_EXT_ADDRESS, udw); + + return (ldw); +} + +#endif + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_3_3 +/* + * Workaround until threshold register is replicated for each CS + */ +static void +brcmnand_reset_corr_threshold(struct brcmnand_chip* chip) +{ + static int once[NUM_NAND_CS]; + + if (chip->ecclevel != 0 && chip->ecclevel != BRCMNAND_ECC_HAMMING) { + uint32_t corr_threshold = brcmnand_ctrl_read(BCHP_NAND_CORR_STAT_THRESHOLD) & BCHP_NAND_CORR_STAT_THRESHOLD_CORR_STAT_THRESHOLD_MASK; + uint32_t seventyfivepc; + + seventyfivepc = (chip->ecclevel * 3) / 4; + if (!once[chip->csi]) { + once[chip->csi] = 1; + printk(KERN_INFO "%s: default CORR ERR threshold %d bits for CS%1d\n", + __FUNCTION__, corr_threshold, chip->ctrl->CS[chip->csi]); + PRINTK("ECC level threshold default value is %d bits for CS%1d\n", corr_threshold, chip->ctrl->CS[chip->csi]); + } + if (seventyfivepc != corr_threshold) { + if ((once[chip->csi])++ < 2) { + printk(KERN_INFO "%s: CORR ERR threshold changed to %d bits for CS%1d\n", + __FUNCTION__, seventyfivepc, chip->ctrl->CS[chip->csi]); + } + seventyfivepc <<= BCHP_NAND_CORR_STAT_THRESHOLD_CORR_STAT_THRESHOLD_SHIFT; + seventyfivepc |= (brcmnand_ctrl_read(BCHP_NAND_CORR_STAT_THRESHOLD) & ~BCHP_NAND_CORR_STAT_THRESHOLD_CORR_STAT_THRESHOLD_MASK); + brcmnand_ctrl_write(BCHP_NAND_CORR_STAT_THRESHOLD, seventyfivepc); + } + } +} + +#else +#define brcmnand_reset_corr_threshold(chip) +#endif + +/* + * Disable ECC, and return the original ACC register (for restore) + */ +uint32_t brcmnand_disable_read_ecc(int cs) +{ + uint32_t acc0; + uint32_t acc; + + /* Disable ECC */ + acc0 = brcmnand_ctrl_read(bchp_nand_acc_control(cs)); +#if CONFIG_MTD_BRCMNAND_VERSION < CONFIG_MTD_BRCMNAND_VERS_7_0 + acc = acc0 & ~(BCHP_NAND_ACC_CONTROL_RD_ECC_EN_MASK | BCHP_NAND_ACC_CONTROL_RD_ECC_BLK0_EN_MASK); +#else + acc = acc0 & ~(BCHP_NAND_ACC_CONTROL_RD_ECC_EN_MASK); +#endif + brcmnand_ctrl_write(bchp_nand_acc_control(cs), acc); + + return acc0; +} + + +void brcmnand_restore_ecc(int cs, uint32_t orig_acc0) +{ + brcmnand_ctrl_write(bchp_nand_acc_control(cs), orig_acc0); +} + +// Restore acc + +#if 0 +/* Dont delete, may be useful for debugging */ + +static void __maybe_unused print_diagnostics(struct brcmnand_chip* chip) +{ + uint32_t nand_acc_control = brcmnand_ctrl_read(BCHP_NAND_ACC_CONTROL); + uint32_t nand_select = brcmnand_ctrl_read(BCHP_NAND_CS_NAND_SELECT); + uint32_t nand_config = brcmnand_ctrl_read(BCHP_NAND_CONFIG); + uint32_t flash_id = brcmnand_ctrl_read(BCHP_NAND_FLASH_DEVICE_ID); + uint32_t pageAddr = brcmnand_ctrl_read(BCHP_NAND_PROGRAM_PAGE_ADDR); + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_1_0 + uint32_t pageAddrExt = brcmnand_ctrl_read(BCHP_NAND_PROGRAM_PAGE_EXT_ADDR); +#endif + + + //unsigned long nand_timing1 = brcmnand_ctrl_read(BCHP_NAND_TIMING_1); + //unsigned long nand_timing2 = brcmnand_ctrl_read(BCHP_NAND_TIMING_2); + + printk(KERN_INFO "NAND_SELECT=%08x ACC_CONTROL=%08x, \tNAND_CONFIG=%08x, FLASH_ID=%08x\n", + nand_select, nand_acc_control, nand_config, flash_id); +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_1_0 + printk("PAGE_EXT_ADDR=%08x\n", pageAddrExt); +#endif + if (chip->ctrl->CS[0] == 0) { + uint32_t ebiCSBase0 = BDEV_RD(BCHP_EBI_CS_BASE_0); + printk(KERN_INFO "PAGE_ADDR=%08x, \tCS0_BASE=%08x\n", pageAddr, ebiCSBase0); + }else { + uint32_t csNandBaseN = BDEV_RD(BCHP_EBI_CS_BASE_0 + 8 * chip->ctrl->CS[0]); + + printk(KERN_INFO "PAGE_ADDR=%08x, \tCS%-d_BASE=%08x\n", pageAddr, chip->ctrl->CS[0], csNandBaseN); + printk(KERN_INFO "pbase=%08lx, vbase=%p\n", chip->pbase, chip->vbase); + } +} +#endif + +static void print_config_regs(struct mtd_info* mtd) +{ + struct brcmnand_chip * chip = mtd->priv; + + unsigned int cs = chip->ctrl->CS[chip->csi]; + uint32_t nand_acc_control = brcmnand_ctrl_read(bchp_nand_acc_control(cs)); + uint32_t nand_config = brcmnand_ctrl_read(bchp_nand_config(cs)); + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_7_1 + uint32_t nand_config_ext = brcmnand_ctrl_read(BCHP_NAND_CONFIG_EXT); +#endif + uint32_t flash_id; // = brcmnand_ctrl_read(BCHP_NAND_FLASH_DEVICE_ID); + uint32_t nand_timing1 = brcmnand_ctrl_read(bchp_nand_timing1(cs)); + uint32_t nand_timing2 = brcmnand_ctrl_read(bchp_nand_timing2(cs)); + uint32_t status; + + /* + * Set CS before reading ID, same as in brcmnand_read_id + */ + + /* Wait for CTRL_Ready */ + brcmnand_wait(mtd, BRCMNAND_FL_READY, &status, 10000); + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_1_0 + /* Older version do not have EXT_ADDR registers */ + chip->ctrl_write(BCHP_NAND_CMD_ADDRESS, 0); + chip->ctrl_write(BCHP_NAND_CMD_EXT_ADDRESS, cs << BCHP_NAND_CMD_EXT_ADDRESS_CS_SEL_SHIFT); +#endif // Set EXT address if version >= 1.0 + + /* Send the command for reading device ID from controller */ + chip->ctrl_write(BCHP_NAND_CMD_START, OP_DEVICE_ID_READ); + + /* Wait for CTRL_Ready */ + brcmnand_wait(mtd, BRCMNAND_FL_READY, &status, 10000); + + + flash_id = chip->ctrl_read(BCHP_NAND_FLASH_DEVICE_ID); + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_7_1 + printk(KERN_INFO "\nFound NAND on CS%1d: ACC=%08x, cfg=%08x, cfg_ext=%08x, flashId=%08x, tim1=%08x, tim2=%08x\n", + cs, nand_acc_control, nand_config, nand_config_ext, flash_id, nand_timing1, nand_timing2); +#else + printk(KERN_INFO "\nFound NAND on CS%1d: ACC=%08x, cfg=%08x, flashId=%08x, tim1=%08x, tim2=%08x\n", + cs, nand_acc_control, nand_config, flash_id, nand_timing1, nand_timing2); +#endif +} + +#define NUM_NAND_REGS (1 + ((BCHP_NAND_LAST_REG - BCHP_NAND_REVISION) / 4)) + +static void __maybe_unused print_nand_ctrl_regs(void) +{ + int i; + +/* Avoid garbled output */ + int saveDebug = gdebug; + + gdebug = 0; + + for (i = 0; i < NUM_NAND_REGS; i++) { + uint32_t reg = (uint32_t)(BCHP_NAND_REVISION + (i * 4)); + uint32_t regval; + //uint32_t regoff = reg - BCHP_NAND_REVISION; // i*4 + + if ((i % 4) == 0) { + printk("\n%08x:", reg); + } + + if (inRegisterHoles(reg)) { + regval = 0; + }else { + regval = (uint32_t)brcmnand_ctrl_read(reg); + } + printk(" %08x", regval); + } + gdebug = saveDebug; +} + +void print_NandCtrl_Status(void) +{ +} + +#if 1 +void print_oobbuf(const unsigned char* buf, int len) +{ + int i; + + + if (!buf) { + printk("NULL"); return; + } + for (i = 0; i < len; i++) { + if (i % 16 == 0 && i != 0) printk("\n"); + else if (i % 4 == 0) printk(" "); + printk("%02x", buf[i]); + } + printk("\n"); +} + +void print_databuf(const unsigned char* buf, int len) +{ + int i; + + + for (i = 0; i < len; i++) { + if (i % 32 == 0) printk("\n%04x: ", i); + else if (i % 4 == 0) printk(" "); + printk("%02x", buf[i]); + } + printk("\n"); +} + +void print_oobreg(struct brcmnand_chip* chip) +{ + int i; + + printk("OOB Register:"); + for (i = 0; i < 4; i++) { + printk("%08x ", chip->ctrl_read(BCHP_NAND_SPARE_AREA_READ_OFS_0 + i * 4)); + } + printk("\n"); +} +#endif + +/* + * BRCMNAND controller always copies the data in 4 byte chunk, and in Big Endian mode + * from and to the flash. + * This routine assumes that dest and src are 4 byte aligned, and that len is a multiple of 4 + (Restriction removed) + + * TBD: 4/28/06: Remove restriction on count=512B, but do restrict the read from within a 512B section. + * Change brcmnand_memcpy32 to be 2 functions, one to-flash, and one from-flash, + * enforcing reading from/writing to flash on a 4B boundary, but relaxing on the buffer being on 4 byte boundary. + */ + + +static int brcmnand_from_flash_memcpy32(struct brcmnand_chip* chip, void* dest, loff_t offset, int len) +{ + volatile uint32_t* flash = (volatile uint32_t*)chip->vbase; + volatile uint32_t* pucDest = (volatile uint32_t*)dest; + volatile uint32_t* pucFlash = (volatile uint32_t*)flash; + int i; + +#if 0 + if (unlikely(((unsigned int)dest) & 0x3)) { + printk(KERN_ERR "brcmnand_memcpy32 dest=%p not DW aligned\n", dest); + return -EINVAL; + } +#endif + if (unlikely(((uintptr_t)flash) & 0x3)) { + printk(KERN_ERR "brcmnand_memcpy32 src=%p not DW aligned\n", flash); + return -EINVAL; + } + if (unlikely(len & 0x3)) { + printk(KERN_ERR "brcmnand_memcpy32 len=%d not DW aligned\n", len); + return -EINVAL; + } + + /* THT: 12/04/08. memcpy plays havoc with the NAND controller logic + * We have removed the alignment test, so we rely on the following codes to take care of it + */ + if (unlikely(((unsigned long)dest) & 0x3)) { + for (i = 0; i < (len >> 2); i++) { + // Flash is guaranteed to be DW aligned. This forces the NAND controller + // to read 1-DW at a time, w/o peep-hole optimization allowed. + volatile uint32_t tmp = pucFlash[i]; + u8* pSrc = (u8*)&tmp; + u8* pDest = (u8*)&pucDest[i]; + pDest[0] = pSrc[0]; + pDest[1] = pSrc[1]; + pDest[2] = pSrc[2]; + pDest[3] = pSrc[3]; + } + }else { +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_4_0 + memcpy(dest, (void*)pucFlash, len); +#else + for (i = 0; i < (len >> 2); i++) { + pucDest[i] = pucFlash[i]; + } +#endif + } + + return 0; +} + + +/* + * Write to flash 512 bytes at a time. + * + * Can't just do a simple memcpy, since the HW NAND controller logic do the filtering + * (i.e. ECC correction) on the fly 4 bytes at a time + * This routine also takes care of alignment. + */ +static int brcmnand_to_flash_memcpy32(struct brcmnand_chip* chip, loff_t offset, const void* src, int len) +{ + u_char* flash = chip->vbase; + int i; + volatile uint32_t* pDest = (volatile uint32_t*)flash; + volatile uint32_t* pSrc = (volatile uint32_t*)src; + + + if (unlikely((uintptr_t)flash & 0x3)) { + printk(KERN_ERR "brcmnand_to_flash_memcpy32 dest=%p not DW aligned\n", flash); + BUG(); + } + + if (unlikely(len & 0x3)) { + printk(KERN_ERR "brcmnand_to_flash_memcpy32 len=%d not DW aligned\n", len); + BUG(); + } + + if (gdebug) printk("%s: flash=%p, len=%d, src=%p\n", __FUNCTION__, flash, len, src); + + + /* + * THT: 12/08/08. memcpy plays havoc with the NAND controller logic + * We have removed the alignment test, so we need these codes to take care of it + */ + if (unlikely((unsigned long)pSrc & 0x3)) { + for (i = 0; i < (len >> 2); i++) { + u8 *tmp = (u8*)&pSrc[i]; +#if defined(CONFIG_CPU_LITTLE_ENDIAN) + pDest[i] = ((uint32_t)(tmp[3] << 24) | (uint32_t)(tmp[2] << 16) + | (uint32_t)(tmp[1] << 8) | (uint32_t)(tmp[0] << 0)); + +#else + pDest[i] = ((uint32_t)(tmp[0] << 24) | (uint32_t)(tmp[1] << 16) + | (uint32_t)(tmp[2] << 8) | (uint32_t)(tmp[3] << 0)); +#endif + } + } else { + for (i = 0; i < (len >> 2); i++) { + pDest[i] = pSrc[i]; + } + } + + return 0; +} + +//#define uint8_t unsigned char + + + +/* The BCHP_HIF_INTR2_xxx registers don't exist on DSL chips so the old way of + * verifying ECC is used. + */ +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) && CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_7_0 +/* + * SWLINUX-1584: Use HIF status register to check for errors. + * In the past we rely on the fact that the registers + * BCHP_NAND_ECC_CORR_EXT_ADDR/BCHP_NAND_ECC_UNC_EXT_ADDR + * are not zeroes, but the indicators are ambiguous when the address is 0 + * + * Notes: 2618 still use the old way, because we are reluctant to change codes that + * are already in production. In 2618 this is only called when address==0 + */ +#define HIF_INTR2_ERR_MASK ( \ + BCHP_HIF_INTR2_CPU_STATUS_NAND_CORR_INTR_MASK | \ + BCHP_HIF_INTR2_CPU_STATUS_NAND_UNC_INTR_MASK) + +/* + * Returns 0: BRCMNAND_SUCCESS: No errors + * 1: Correctable error + * -1: Uncorrectable error + */ +static int brcmnand_ctrl_verify_ecc(struct brcmnand_chip* chip, int state, uint32_t notUsed) +{ + uint32_t intr_status = BDEV_RD(BCHP_HIF_INTR2_CPU_STATUS); + + if (gdebug > 3 ) { + printk("%s: intr_status = %08x\n", __FUNCTION__, intr_status); + } + + /* Only make sense on read */ + if (state != BRCMNAND_FL_READING) + return BRCMNAND_SUCCESS; + + if (intr_status & BCHP_HIF_INTR2_CPU_STATUS_NAND_UNC_INTR_MASK) { +#if !defined(CONFIG_BCM_KF_MTD_BCMNAND) + // Clear Status Mask for sector 0 workaround + BDEV_WR(BCHP_HIF_INTR2_CPU_CLEAR, + HIF_INTR2_ERR_MASK | BCHP_HIF_INTR2_CPU_STATUS_NAND_CTLRDY_INTR_MASK); +#endif + +#if 0 /* Already cleared with cpu-clear */ + intr_status &= ~HIF_INTR2_ERR_MASK; + BDEV_WR(BCHP_HIF_INTR2_CPU_STATUS, intr_status); +#endif + return BRCMNAND_UNCORRECTABLE_ECC_ERROR; + }else if (intr_status & BCHP_HIF_INTR2_CPU_STATUS_NAND_CORR_INTR_MASK) { +#if !defined(CONFIG_BCM_KF_MTD_BCMNAND) + BDEV_WR(BCHP_HIF_INTR2_CPU_CLEAR, + HIF_INTR2_ERR_MASK | BCHP_HIF_INTR2_CPU_STATUS_NAND_CTLRDY_INTR_MASK); +#endif + +#if 0 /* Already cleared with cpu-clear */ + intr_status &= ~HIF_INTR2_ERR_MASK; + BDEV_WR(BCHP_HIF_INTR2_CPU_STATUS, intr_status); +#endif + return BRCMNAND_CORRECTABLE_ECC_ERROR; + } + + return BRCMNAND_SUCCESS; +} + + +#else +/* Old ways of doing it: is ambiguous when offset == 0 */ + +/* + * Returns 0: BRCMNAND_SUCCESS: No errors + * 1: Correctable error + * -1: Uncorrectable error + */ +static int brcmnand_ctrl_verify_ecc(struct brcmnand_chip* chip, int state, uint32_t notUsed) +{ + int err = 0; + uint32_t addr; + uint32_t extAddr = 0; + + if (gdebug > 3 ) { + printk("-->%s\n", __FUNCTION__); + } + + /* Only make sense on read */ + if (state != BRCMNAND_FL_READING) + return BRCMNAND_SUCCESS; + + addr = chip->ctrl_read(BCHP_NAND_ECC_CORR_ADDR); + if (addr) { + + extAddr = chip->ctrl_read(BCHP_NAND_ECC_CORR_EXT_ADDR); + // Clear it + chip->ctrl_write(BCHP_NAND_ECC_CORR_EXT_ADDR, 0); + + // Clear it + chip->ctrl_write(BCHP_NAND_ECC_CORR_ADDR, 0); + printk(KERN_WARNING "%s: Correctable ECC error at %08x:%08x\n", __FUNCTION__, extAddr, addr); + + /* Check to see if error occurs in Data or ECC */ + err = BRCMNAND_CORRECTABLE_ECC_ERROR; + } + + addr = chip->ctrl_read(BCHP_NAND_ECC_UNC_ADDR); + if (addr) { + extAddr = chip->ctrl_read(BCHP_NAND_ECC_UNC_EXT_ADDR); + // Clear it + chip->ctrl_write(BCHP_NAND_ECC_UNC_EXT_ADDR, 0); + chip->ctrl_write(BCHP_NAND_ECC_UNC_ADDR, 0); + + /* + * If the block was just erased, and have not yet been written to, this will be flagged, + * so this could be a false alarm + */ + + err = BRCMNAND_UNCORRECTABLE_ECC_ERROR; + } + return err; +} + +#endif + +#if 0 +static int (*brcmnand_verify_ecc) (struct brcmnand_chip* chip, int state, uint32_t intr) = brcmnand_ctrl_verify_ecc; +#endif + + +/** + * brcmnand_wait - [DEFAULT] wait until the command is done + * @param mtd MTD device structure + * @param state state to select the max. timeout value + * + * Wait for command done. This applies to all BrcmNAND command + * Read can take up to 53, erase up to ?s and program up to 30 clk cycle () + * according to general BrcmNAND specs + */ +static int brcmnand_wait(struct mtd_info *mtd, int state, uint32_t* pStatus, int tout) +{ + struct brcmnand_chip * chip = mtd->priv; + unsigned long timeout; + uint32_t ready; + uint32_t wait_for = BRCMNAND_FL_WRITING == state + ? BCHP_NAND_INTFC_STATUS_CTLR_READY_MASK | BCHP_NAND_INTFC_STATUS_FLASH_READY_MASK + : BCHP_NAND_INTFC_STATUS_CTLR_READY_MASK; + + /* The 20 msec is enough */ + timeout = jiffies + msecs_to_jiffies(tout); + while (time_before(jiffies, timeout)) { + PLATFORM_IOFLUSH_WAR(); + ready = chip->ctrl_read(BCHP_NAND_INTFC_STATUS); + + if ((ready & wait_for) == wait_for) { + *pStatus = ready; + return 0; + } + + if (state != BRCMNAND_FL_READING && (!wr_preempt_en) && !in_interrupt()) + cond_resched(); + else + udelay(1); + //touch_softlockup_watchdog(); + } + + /* + * Get here on timeout + */ + return -ETIMEDOUT; +} + + + +/* + * Returns 1: Success, no errors + * 0: Timeout + * -1: Errors + */ +static int brcmnand_spare_is_valid(struct mtd_info* mtd, int state, int raw, int tout) +{ + struct brcmnand_chip * chip = mtd->priv; + unsigned long timeout; + uint32_t ready; + + if (gdebug > 3 ) { + printk("-->%s, raw=%d\n", __FUNCTION__, raw); + } + + + /* The 20 msec is enough */ + timeout = jiffies + msecs_to_jiffies(tout); // 3 sec timeout for now + while (time_before(jiffies, timeout)) { + PLATFORM_IOFLUSH_WAR(); + ready = chip->ctrl_read(BCHP_NAND_INTFC_STATUS); + + if (ready & BCHP_NAND_INTFC_STATUS_CTLR_READY_MASK && + (ready & BCHP_NAND_INTFC_STATUS_SPARE_AREA_VALID_MASK)) { + + +#if 0 +// THT 6/15/09: Reading OOB would not affect ECC + int ecc; + + if (!raw) { + ecc = brcmnand_ctrl_verify_ecc(chip, state, 0); + if (ecc < 0) { +//printk("%s: Uncorrectable ECC error at offset %08x\n", __FUNCTION__, (unsigned long) offset); + return -1; + } + } +#endif + return 1; + } + if (state != BRCMNAND_FL_READING && !wr_preempt_en && !in_interrupt()) + cond_resched(); + else + udelay(1); + } + + return 0; // Timed out +} + + + +/* + * Returns: Good: >= 0 + * Error: < 0 + * + * BRCMNAND_CORRECTABLE_ECC_ERROR (1) + * BRCMNAND_SUCCESS (0) + * BRCMNAND_UNCORRECTABLE_ECC_ERROR (-1) + * BRCMNAND_FLASH_STATUS_ERROR (-2) + * BRCMNAND_TIMED_OUT (-3) + * + * Is_Valid in the sense that the data is valid in the cache. + * It does not means that the data is either correct or correctable. + */ + +static int brcmnand_cache_is_valid(struct mtd_info* mtd, int state, loff_t offset, int tout) +{ + struct brcmnand_chip * chip = mtd->priv; + unsigned long timeout; + uint32_t ready; + + if (gdebug > 3 ) { + printk("%s: offset=%0llx\n", __FUNCTION__, offset); + } + + /* The 20 msec is enough */ + timeout = jiffies + msecs_to_jiffies(tout); // 3 sec timeout for now + while (time_before(jiffies, timeout)) { + PLATFORM_IOFLUSH_WAR(); + ready = chip->ctrl_read(BCHP_NAND_INTFC_STATUS); + + if ((ready & BCHP_NAND_INTFC_STATUS_CTLR_READY_MASK) + && (ready & BCHP_NAND_INTFC_STATUS_CACHE_VALID_MASK)) { + int ecc; + + ecc = brcmnand_ctrl_verify_ecc(chip, state, 0); +// Let caller handle it +//printk("%s: Possible Uncorrectable ECC error at offset %08x\n", __FUNCTION__, (unsigned long) offset); + if (gdebug > 3 && ecc) { + printk("<--%s: ret = %d\n", __FUNCTION__, ecc); + } + return ecc; + + } + if (state != BRCMNAND_FL_READING && (!wr_preempt_en) && !in_interrupt()) + cond_resched(); + else + udelay(1); + + } + + if (gdebug > 3 ) { + printk("<--%s: ret = TIMEOUT\n", __FUNCTION__); + print_nand_ctrl_regs(); + } + return BRCMNAND_TIMED_OUT; // TimeOut +} + + +#if 0 +static int brcmnand_select_cache_is_valid(struct mtd_info* mtd, int state, loff_t offset) +{ + int ret = 0; + + ret = brcmnand_cache_is_valid(mtd, state, offset); + return ret; +} +#endif + + +/* + * Returns 1 on success, + * 0 on error + */ + + +static int brcmnand_ctrl_write_is_complete(struct mtd_info *mtd, int* outp_needBBT) +{ + int err; + uint32_t status; + uint32_t flashStatus = 0; + + *outp_needBBT = 1; + err = brcmnand_wait(mtd, BRCMNAND_FL_WRITING, &status, 10000); + if (!err) { + if (status & BCHP_NAND_INTFC_STATUS_CTLR_READY_MASK) { + flashStatus = status & 0x01; + if (flashStatus) { + printk(KERN_INFO "%s: INTF Status = %08x\n", __FUNCTION__, status); + } + *outp_needBBT = flashStatus; // 0 = write completes with no errors + return 1; + }else { + return 0; + } + } + return 0; +} + + + + + +static int (*brcmnand_write_is_complete) (struct mtd_info*, int*) = brcmnand_ctrl_write_is_complete; + + + +/** + * brcmnand_transfer_oob - [Internal] Transfer oob from chip->oob_poi to client buffer + * @chip: nand chip structure + * @oob: oob destination address + * @ops: oob ops structure + * @len: OOB bytes to transfer + * + * Returns the pointer to the OOB where next byte should be read + */ +uint8_t * +brcmnand_transfer_oob(struct brcmnand_chip *chip, uint8_t *oob, + struct mtd_oob_ops *ops, int len) +{ + //size_t len = ops->ooblen; + + switch (ops->mode) { + + case MTD_OPS_PLACE_OOB: + case MTD_OPS_RAW: + memcpy(oob, chip->oob_poi + ops->ooboffs, len); + return oob + len; + + case MTD_OPS_AUTO_OOB: { + struct nand_oobfree *free = chip->ecclayout->oobfree; + uint32_t boffs = 0, roffs = ops->ooboffs; + size_t bytes = 0; + + for (; free->length && len; free++, len -= bytes) { + /* Read request not from offset 0 ? */ + if (unlikely(roffs)) { + if (roffs >= free->length) { + roffs -= free->length; + continue; + } + boffs = free->offset + roffs; + bytes = min_t(size_t, len, + (free->length - roffs)); + roffs = 0; + } else { + bytes = min_t(size_t, len, free->length); + boffs = free->offset; + } +#ifdef DEBUG_ISR + printk("%s: AUTO: oob=%p, chip->oob_poi=%p, ooboffs=%d, len=%d, bytes=%d, boffs=%d\n", + __FUNCTION__, oob, chip->oob_poi, ops->ooboffs, len, bytes, boffs); +#endif + memcpy(oob, chip->oob_poi + boffs, bytes); + oob += bytes; + } + return oob; + } + default: + BUG(); + } + return NULL; +} + + + + +#undef DEBUG_UNCERR +#ifdef DEBUG_UNCERR +static uint32_t uncErrOob[7]; +static u_char uncErrData[512]; +#endif + + + +void brcmnand_post_mortem_dump(struct mtd_info* mtd, loff_t offset) +{ + int i; + +//Avoid garbled output + int saveDebug = gdebug; + + gdebug = 0; + + printk("%s at offset %llx\n", __FUNCTION__, offset); + dump_stack(); + + printk("NAND registers snapshot \n"); + for (i = 0; i < NUM_NAND_REGS; i++) { + uint32_t reg = BCHP_NAND_REVISION + (i * 4); + uint32_t regval; + + if (inRegisterHoles(reg)) { // No NAND register at 0x281c + regval = 0; + }else { + regval = brcmnand_ctrl_read(reg); + } + if ((i % 4) == 0) { + printk("\n%08x:", reg); + } + printk(" %08x", regval); + } + + gdebug = saveDebug; + +} + + + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_3_4 +/* + * Read the OOB bytes beyond 16B + * + * i: DW index into OOB area + * p32: DW pointer into OOB area + */ +static inline +void read_ext_spare_area(struct brcmnand_chip* chip, int i, uint32_t* p32) +{ + uint32_t dwoob; + int j; + int oobi; /* Byte index into OOB area */ + u_char* p8 = (u_char*)p32; /* Byte pointer into OOB area */ + u_char* q = (u_char*)&dwoob; + + /* If HW support it, copy OOB bytes beyond 16 bytes */ + + /* p8 and oobi index into byte-wise OOB, p32 index into DW-wise OOB */ + oobi = i * 4; + + for (; i < 8 && oobi < chip->eccOobSize; i++, oobi += 4) { + + + /* This takes care of Endian-ness of the registers */ + dwoob = be32_to_cpu(chip->ctrl_read(BCHP_NAND_SPARE_AREA_READ_OFS_10 + (i - 4) * 4)); + if (gdebug > 3) { + printk("%s: dwoob=%08x\n", __FUNCTION__, dwoob); + } + + /* OOB size is an odd 27 bytes */ + if (oobi + 4 <= chip->eccOobSize) { + p32[i] = dwoob; + }else { /* Trailing 3 bytes, column=pgSize+24,25,26*/ + // remember that p8 = (u_char*) &p32[0]; + for (j = 0; oobi + j < chip->eccOobSize; j++) { + p8[oobi + j] = q[j]; + } + break; /* Out of i loop */ + } + } +} + +#else +#define read_ext_spare_area(...) +#endif + + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +static int brcmnand_get_ecc_strength(struct brcmnand_chip *chip) +{ + uint32_t acc; + int ecclevel, eccstrength; + + acc = chip->ctrl_read(bchp_nand_acc_control(chip->ctrl->CS[chip->csi])); + ecclevel = (acc & BCHP_NAND_ACC_CONTROL_ECC_LEVEL_MASK) + >> BCHP_NAND_ACC_CONTROL_ECC_LEVEL_SHIFT; + + if (ecclevel == BRCMNAND_ECC_HAMMING) + eccstrength = 1; + else if (ecclevel == BRCMNAND_ECC_DISABLE) + eccstrength = 0; + else + eccstrength = ecclevel; + + return eccstrength; +} + + +/* + * Check a page to see if it is erased (w/ bitflips) after an uncorrectable ECC + * error + * + * Because the HW ECC signals an ECC error if an erase paged has even a single + * bitflip, we must check each ECC error to see if it is actually an erased + * page with bitflips, not a truly corrupted page. + * + * On a real error, return a negative error code (-EBADMSG for ECC error), and + * buf will contain raw data. + * Otherwise, fill buf with 0xff and return the maximum number of + * bitflips-per-ECC-sector to the caller. + * + */ + +static int brcmnand_handle_false_read_ecc_unc_errors(struct mtd_info* mtd, + void* buffer, u_char* oobarea, loff_t offset) +{ + struct brcmnand_chip *chip = (struct brcmnand_chip*) mtd->priv; + uint32_t acc0; + int i, sas, oob_nbits, data_nbits; + unsigned int max_bitflips = 0; + int ret; + struct nand_ecclayout *ecclayout = chip->ecclayout; + int oobofs_limit = 0, eccpos_idx = 0, check_ecc = 1, ecc_pos; + unsigned long ecc; + u_char* oobs; + + acc0 = brcmnand_disable_read_ecc(chip->ctrl->CS[chip->csi]); + if (buffer == NULL) { + buffer = chip->ctrl->buffers->databuf; + /* Invalidate page cache */ + chip->pagebuf = -1; + } + if (oobarea == NULL) + oobarea = chip->oob_poi; + + ret = brcmnand_read_page(mtd, buffer, oobarea, offset>>chip->page_shift); + brcmnand_restore_ecc(chip->ctrl->CS[chip->csi], acc0); + if (ret) + return ret; + + oobs = oobarea; + sas = mtd->oobsize / chip->eccsteps; + oob_nbits = sizeof(ecc)<<3; + data_nbits = chip->eccsize << 3; + + for (i = 0; i < chip->eccsteps; i++, oobarea += sas) { + unsigned int bitflips = 0; + + oobofs_limit = (i + 1)*sas; + + /* only check for ECC bytes because JFFS2 may already write OOB */ + /* check number ecc bit flip within each ecc step size */ + while ( eccpos_idx < MTD_MAX_ECCPOS_ENTRIES_LARGE && check_ecc ) { + ecc_pos = ecclayout->eccpos[eccpos_idx]; + if (ecc_pos == 0) { + /* no more ecc bytes all done */ + check_ecc = 0; + break; + } else if (ecc_pos < oobofs_limit) { + /* this ecc bytes belong to this subpage, count any bit flip */ + ecc = (unsigned long)oobs[ecc_pos]; + bitflips += 8 - bitmap_weight(&ecc, oob_nbits); /* only lowest 8 bit matters */ + eccpos_idx++; + } else { + /* done with this subpage */ + break; + } + } + + bitflips += data_nbits - bitmap_weight(buffer, data_nbits); + + buffer += chip->eccsize; + offset += chip->eccsize; + + /* Too many bitflips */ + if (bitflips > brcmnand_get_ecc_strength(chip)) + return -EBADMSG; + + max_bitflips = max(max_bitflips, bitflips); + } + + return max_bitflips; + +} + + +/* This function handle the correctable and uncorrect ecc error for page read + * and return the approriate buffer and return code to upper layer + */ +static int +brcmnand_handle_ecc_errors(struct mtd_info *mtd, uint8_t *buf, + uint8_t* oob, loff_t offset, int error) +{ + struct brcmnand_chip *chip = (struct brcmnand_chip*) mtd->priv; + struct nand_ecclayout *ecclayout = chip->ecclayout; + int eccpos, eccpos_idx = 0; + int ret = 0; + + /* verify ECC error in whole page base */ + if (error == BRCMNAND_UNCORRECTABLE_ECC_ERROR) { + ret = brcmnand_handle_false_read_ecc_unc_errors(mtd, buf, oob, offset); + if (ret < 0) { + printk("uncorrectable error at 0x%llx\n", (unsigned long long)offset); + mtd->ecc_stats.failed++; + /* NAND layer expects zero on ECC errors */ + ret = 0; + } else { + /* uncorrectable ecc error caused by erase page and number of bit flip within ecc + * strength. Fill buffer and oob with 0xff + */ + if (buf) + memset(buf, 0xff, chip->eccsize*chip->eccsteps); + if (oob) { + /* only restore 0xff on the ecc bytes only because JFFS2 may already + * write cleanmarker in oob + */ + while ((eccpos = ecclayout->eccpos[eccpos_idx])) { + oob[eccpos] = 0xff; + eccpos_idx++; + } + } + + if (ret) + printk("corrected %d bitflips in blank page at 0x%llx\n", + ret, (unsigned long long)offset); + mtd->ecc_stats.corrected += ret; + ret = 0; + } + } + + if (error == BRCMNAND_CORRECTABLE_ECC_ERROR) { + printk("corrected error at 0x%llx\n", (unsigned long long)offset); + mtd->ecc_stats.corrected++; + ret = 0; + } + + return ret; +} + +#else +/* + * Returns 0 on success + * Expect a controller read was done before hand, and that the OOB data are read into NAND registers. + */ +static int brcmnand_handle_false_read_ecc_unc_errors( + struct mtd_info* mtd, + void* buffer, u_char* oobarea, loff_t offset) +{ + struct brcmnand_chip* chip = mtd->priv; + //int retries = 2; + static uint32_t oobbuf[8]; // Sparea Area to handle ECC workaround, aligned on DW boundary + uint32_t* p32 = (oobarea ? (uint32_t*)oobarea : (uint32_t*)&oobbuf[0]); + u_char* p8 = (u_char*)p32; + int ret = 0; + + /* Flash chip returns errors + + || There is a bug in the controller, where if one reads from an erased block that has NOT been written to, + || this error is raised. + || (Writing to OOB area does not have any effect on this bug) + || The workaround is to also look into the OOB area, to see if they are all 0xFF + + */ + //u_char oobbuf[16]; + int erased, allFF; + int i; + + for (i = 0; i < 4; i++) { + p32[i] = be32_to_cpu(chip->ctrl_read(BCHP_NAND_SPARE_AREA_READ_OFS_0 + i * 4)); + } + + read_ext_spare_area(chip, i, p32); + + if (chip->ecclevel == BRCMNAND_ECC_HAMMING) { + /* + * THT 9/16/10: Also guard against the case where all data bytes are 0x11 or 0x22, + * in which case, this is a bonafide Uncorrectable error + * + * Look at first 4 bytes from the flash, already guaranteed to be 512B aligned + */ + uint32_t* pFirstDW = (uint32_t*)chip->vbase; + + erased = (p8[6] == 0xff && p8[7] == 0xff && p8[8] == 0xff); + /* If first 4 bytes of data are not 0xFFFFFFFF, then this is a real UNC error */ + allFF = (p8[6] == 0x00 && p8[7] == 0x00 && p8[8] == 0x00 && *pFirstDW == 0xFFFFFFFF); + + if (gdebug > 3 ) { + printk("%s: offset=%0llx, erased=%d, allFF=%d\n", + __FUNCTION__, offset, erased, allFF); + print_oobbuf(p8, 16); + } + }else if (chip->ecclevel >= BRCMNAND_ECC_BCH_1 && chip->ecclevel <= BRCMNAND_ECC_BCH_12) { + erased = 1; + allFF = 0; // Not sure for BCH. + // For BCH-n, the ECC bytes are at the end of the OOB area + for (i = chip->eccOobSize - chip->eccbytes; i < min(16, chip->eccOobSize); i++) { + erased = erased && (p8[i] == 0xff); + if (!erased) { + if (gdebug > 3 ) + printk("p8[%d]=%02x\n", i, p8[i]); + break; + } + } + if (gdebug > 3 ) { + printk("%s: offset=%0llx, i=%d from %d to %d, eccOobSize=%d, eccbytes=%d, erased=%d, allFF=%d\n", + __FUNCTION__, offset, i, chip->eccOobSize - chip->eccbytes, chip->eccOobSize, + chip->eccOobSize, chip->eccbytes, erased, allFF); + } + }else { + printk("BUG: Unsupported ECC level %d\n", chip->ecclevel); + BUG(); + } + + if ( erased || allFF) { + /* + * For the first case, the slice is an erased block, and the ECC bytes are all 0xFF, + * for the 2nd, all bytes are 0xFF, so the Hamming Codes for it are all zeroes. + * The current version of the BrcmNAND controller treats these as un-correctable errors. + * For either case, fill data buffer with 0xff and return success. The error has already + * been cleared inside brcmnand_verify_ecc. + * Both case will be handled correctly by the BrcmNand controller in later releases. + */ + p32 = (uint32_t*)buffer; + for (i = 0; i < ECCSIZE(mtd) / 4; i++) { + p32[i] = 0xFFFFFFFF; + } + ret = 0; // Success + }else { + /* Real error: Disturb read returns uncorrectable errors */ + ret = BRCMNAND_UNCORRECTABLE_ECC_ERROR; + if (gdebug > 3 ) { + printk("<-- %s: indeed uncorrectable ecc error\n", __FUNCTION__); + } + +#ifdef DEBUG_UNCERR + + // Copy the data buffer + brcmnand_from_flash_memcpy32(chip, uncErrData, offset, ECCSIZE(mtd)); + for (i = 0; i < 4; i++) { + uncErrOob[i] = p32[i]; + } + + printk("%s: Uncorrectable error at offset %llx\n", __FUNCTION__, offset); + + printk("Data:\n"); + print_databuf(uncErrData, ECCSIZE(mtd)); + printk("Spare Area\n"); + print_oobbuf((unsigned char*)&uncErrOob[0], 16); + + brcmnand_post_mortem_dump(mtd, offset); + +#endif + } + + return ret; +} +#endif + +// THT PR50928: if wr_preempt is disabled, enable it to clear error +int brcmnand_handle_ctrl_timeout(struct mtd_info* mtd, int retry) +{ + uint32_t acc; + struct brcmnand_chip* __maybe_unused chip = mtd->priv; + + // First check to see if WR_PREEMPT is disabled + acc = brcmnand_ctrl_read(bchp_nand_acc_control(chip->ctrl->CS[chip->csi])); + if (retry <= 2 && 0 == (acc & BCHP_NAND_ACC_CONTROL_WR_PREEMPT_EN_MASK)) { + acc |= BCHP_NAND_ACC_CONTROL_WR_PREEMPT_EN_MASK; + brcmnand_ctrl_write(bchp_nand_acc_control(chip->ctrl->CS[chip->csi]), acc); + printk("Turn on WR_PREEMPT_EN\n"); + return 1; + } + +#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) + brcmnand_restore_ecc(chip->ctrl->CS[chip->csi], brcmnand_disable_read_ecc(chip->ctrl->CS[chip->csi])); +#endif + + return 0; +} + +void brcmnand_Hamming_ecc(const uint8_t *data, uint8_t *ecc_code) +{ + + int i, j; + static uint8_t o_ecc[24], temp[10]; + static uint32_t b_din[128]; + uint32_t* i_din = &b_din[0]; + unsigned long pre_ecc; + +#if 0 + // THT Use this block if there is a need for endian swapping + uint32_t i_din[128]; + uint32_t* p32 = (uint32_t*)data; // alignment guaranteed by caller. + + + for (i = 0; i < 128; i++) { + //i_din[i/4] = (long)(data[i+3]<<24 | data[i+2]<<16 | data[i+1]<<8 | data[i]); + i_din[i] = /*le32_to_cpu */ (p32[i]); + //printk( "i_din[%d] = 0x%08.8x\n", i/4, i_din[i/4] ); + } + +#else + if (unlikely((uintptr_t)data & 0x3)) { + memcpy((uint8_t*)i_din, data, 512); + }else { + i_din = (uint32_t*)data; + } +#endif + + memset(o_ecc, 0, sizeof(o_ecc)); + + for (i = 0; i < 128; i++) { + memset(temp, 0, sizeof(temp)); + + for (j = 0; j < 32; j++) { + temp[0] ^= ((i_din[i] & 0x55555555) >> j) & 0x1; + temp[1] ^= ((i_din[i] & 0xAAAAAAAA) >> j) & 0x1; + temp[2] ^= ((i_din[i] & 0x33333333) >> j) & 0x1; + temp[3] ^= ((i_din[i] & 0xCCCCCCCC) >> j) & 0x1; + temp[4] ^= ((i_din[i] & 0x0F0F0F0F) >> j) & 0x1; + temp[5] ^= ((i_din[i] & 0xF0F0F0F0) >> j) & 0x1; + temp[6] ^= ((i_din[i] & 0x00FF00FF) >> j) & 0x1; + temp[7] ^= ((i_din[i] & 0xFF00FF00) >> j) & 0x1; + temp[8] ^= ((i_din[i] & 0x0000FFFF) >> j) & 0x1; + temp[9] ^= ((i_din[i] & 0xFFFF0000) >> j) & 0x1; + } + + for (j = 0; j < 10; j++) + o_ecc[j] ^= temp[j]; + + //o_ecc[0]^=temp[0];//P1' + //o_ecc[1]^=temp[1];//P1 + //o_ecc[2]^=temp[2];//P2' + //o_ecc[3]^=temp[3];//P2 + //o_ecc[4]^=temp[4];//P4' + //o_ecc[5]^=temp[5];//P4 + //o_ecc[6]^=temp[6];//P8' + //o_ecc[7]^=temp[7];//P8 + //o_ecc[8]^=temp[8];//P16' + //o_ecc[9]^=temp[9];//P16 + + if (i % 2) { + for (j = 0; j < 32; j++) + o_ecc[11] ^= (i_din[i] >> j) & 0x1; //P32 + }else { + for (j = 0; j < 32; j++) + o_ecc[10] ^= (i_din[i] >> j) & 0x1; //P32' + } + + if ((i & 0x3) < 2) { + for (j = 0; j < 32; j++) + o_ecc[12] ^= (i_din[i] >> j) & 0x1; //P64' + }else { + for (j = 0; j < 32; j++) + o_ecc[13] ^= (i_din[i] >> j) & 0x1; //P64 + } + + if ((i & 0x7) < 4) { + for (j = 0; j < 32; j++) + o_ecc[14] ^= (i_din[i] >> j) & 0x1; //P128' + }else { + for (j = 0; j < 32; j++) + o_ecc[15] ^= (i_din[i] >> j) & 0x1; //P128 + } + + if ((i & 0xF) < 8) { + for (j = 0; j < 32; j++) + o_ecc[16] ^= (i_din[i] >> j) & 0x1; //P256' + }else { + for (j = 0; j < 32; j++) + o_ecc[17] ^= (i_din[i] >> j) & 0x1; //P256 + } + + if ((i & 0x1F) < 16) { + for (j = 0; j < 32; j++) + o_ecc[18] ^= (i_din[i] >> j) & 0x1; //P512' + }else { + for (j = 0; j < 32; j++) + o_ecc[19] ^= (i_din[i] >> j) & 0x1; //P512 + } + + if ((i & 0x3F) < 32) { + for (j = 0; j < 32; j++) + o_ecc[20] ^= (i_din[i] >> j) & 0x1; //P1024' + }else { + for (j = 0; j < 32; j++) + o_ecc[21] ^= (i_din[i] >> j) & 0x1; //P1024 + } + + if ((i & 0x7F) < 64) { + for (j = 0; j < 32; j++) + o_ecc[22] ^= (i_din[i] >> j) & 0x1; //P2048' + }else { + for (j = 0; j < 32; j++) + o_ecc[23] ^= (i_din[i] >> j) & 0x1; //P2048 + } + // print intermediate value + pre_ecc = 0; + for (j = 23; j >= 0; j--) { + pre_ecc = (pre_ecc << 1) | (o_ecc[j] ? 1 : 0 ); + } +// printf( "pre_ecc[%d] = 0x%06.6x\n", i, pre_ecc ); + } + //xprintf("P16':%x P16:%x P8':%x P8:%x\n",o_ecc[8],o_ecc[9],o_ecc[6],o_ecc[7]); + //xprintf("P1':%x P1:%x P2':%x P2:%x\n",o_ecc[0],o_ecc[1],o_ecc[2],o_ecc[3]); + // ecc_code[0] = ~(o_ecc[13]<<7 | o_ecc[12]<<6 | o_ecc[11]<<5 | o_ecc[10]<<4 | o_ecc[9]<<3 | o_ecc[8]<<2 | o_ecc[7]<<1 | o_ecc[6]); + // ecc_code[1] = ~(o_ecc[21]<<7 | o_ecc[20]<<6 | o_ecc[19]<<5 | o_ecc[18]<<4 | o_ecc[17]<<3 | o_ecc[16]<<2 | o_ecc[15]<<1 | o_ecc[14]); + // ecc_code[2] = ~(o_ecc[5]<<7 | o_ecc[4]<<6 | o_ecc[3]<<5 | o_ecc[2]<<4 | o_ecc[1]<<3 | o_ecc[0]<<2 | o_ecc[23]<<1 | o_ecc[22]); + + ecc_code[0] = (o_ecc[ 7] << 7 | o_ecc[ 6] << 6 | o_ecc[ 5] << 5 | o_ecc[ 4] << 4 | o_ecc[ 3] << 3 | o_ecc[ 2] << 2 | o_ecc[ 1] << 1 | o_ecc[ 0]); + ecc_code[1] = (o_ecc[15] << 7 | o_ecc[14] << 6 | o_ecc[13] << 5 | o_ecc[12] << 4 | o_ecc[11] << 3 | o_ecc[10] << 2 | o_ecc[ 9] << 1 | o_ecc[ 8]); + ecc_code[2] = (o_ecc[23] << 7 | o_ecc[22] << 6 | o_ecc[21] << 5 | o_ecc[20] << 4 | o_ecc[19] << 3 | o_ecc[18] << 2 | o_ecc[17] << 1 | o_ecc[16]); + // printf("BROADCOM ECC:0x%02X 0x%02X 0x%02X \n",ecc_code[0],ecc_code[1],ecc_code[2]); + //xprintf("BROADCOM ECC:0x%02X 0x%02X 0x%02X \n",test[0],test[1],test[2]); +} + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_3_1_1 +/* No workaround needed, fixed in HW */ +#define brcmnand_Hamming_WAR(...) (0) + +#else + +/* + * Workaround for Hamming ECC when correctable error is in the ECC bytes. + * Returns 0 if error was in data (no action needed), 1 if error was in ECC (use uncorrected data instead) + */ +static int brcmnand_Hamming_WAR(struct mtd_info* mtd, loff_t offset, void* buffer, + u_char* inp_hwECC, u_char* inoutp_swECC) +{ + struct brcmnand_chip* chip = mtd->priv; + static uint32_t ucdata[128]; + u_char* uncorr_data = (u_char*)ucdata; + uint32_t acc0; + int valid; + //unsigned long irqflags; + + int ret = 0, retries = 2; + + /* Disable ECC */ + acc0 = brcmnand_disable_read_ecc(chip->ctrl->CS[chip->csi]); + + while (retries >= 0) { + if (wr_preempt_en) { + //local_irq_save(irqflags); + } + +/* This register doesn't exist on DSL chips. */ +#if !defined(CONFIG_BCM_KF_MTD_BCMNAND) + // Mask Interrupt + BDEV_WR(BCHP_HIF_INTR2_CPU_MASK_SET, HIF_INTR2_ERR_MASK); + // Clear Status Mask for sector 0 workaround + BDEV_WR(BCHP_HIF_INTR2_CPU_CLEAR, + HIF_INTR2_ERR_MASK | BCHP_HIF_INTR2_CPU_STATUS_NAND_CTLRDY_INTR_MASK); +#endif + +#if 0 + /* Already cleared with cpu-clear */ + intr_status = BDEV_RD(BCHP_HIF_INTR2_CPU_STATUS); + intr_status &= ~(HIF_INTR2_ERR_MASK); + BDEV_WR(BCHP_HIF_INTR2_CPU_STATUS, intr_status); +#endif + + chip->ctrl_writeAddr(chip, offset, 0); + PLATFORM_IOFLUSH_WAR(); + chip->ctrl_write(BCHP_NAND_CMD_START, OP_PAGE_READ); + + // Wait until cache is filled up + valid = brcmnand_cache_is_valid(mtd, BRCMNAND_FL_READING, offset, 100); + + if (wr_preempt_en) { + //local_irq_restore(irqflags); + } + + if (valid == BRCMNAND_TIMED_OUT) { + //Read has timed out + ret = -ETIMEDOUT; + retries--; + // THT PR50928: if wr_preempt is disabled, enable it to clear error + wr_preempt_en = brcmnand_handle_ctrl_timeout(mtd, retries); + continue; /* Retry */ + }else { + ret = 0; + break; + } + } + + if (retries < 0) { + goto restore_ecc; + } + + // Reread the uncorrected buffer. + brcmnand_from_flash_memcpy32(chip, uncorr_data, offset, ECCSIZE(mtd)); + + // Calculate Hamming Codes + brcmnand_Hamming_ecc(uncorr_data, inoutp_swECC); + + // Compare ecc0 against ECC from HW + if ((inoutp_swECC[0] == inp_hwECC[0] && inoutp_swECC[1] == inp_hwECC[1] && + inoutp_swECC[2] == inp_hwECC[2]) + || (inoutp_swECC[0] == 0x0 && inoutp_swECC[1] == 0x0 && inoutp_swECC[2] == 0x0 && + inp_hwECC[0] == 0xff && inp_hwECC[1] == 0xff && inp_hwECC[2] == 0xff)) { + // Error was in data bytes, correction made by HW is good, + // or block was erased and no data written to it yet, + // send corrected data. + // printk("CORR error was handled properly by HW\n"); + ret = 0; + }else { // Error was in ECC, send uncorrected data + memcpy(buffer, uncorr_data, 512); + + ret = 1; + printk("CORR error was handled by SW at offset %0llx, HW=%02x%02x%02x, SW=%02x%02x%02x\n", + offset, inp_hwECC[0], inp_hwECC[1], inp_hwECC[2], + inoutp_swECC[0], inoutp_swECC[1], inoutp_swECC[2]); + } + + restore_ecc: + // Restore acc + brcmnand_restore_ecc(chip->ctrl->CS[chip->csi], acc0); + return ret; +} +#endif + + + +/** + * brcmnand_posted_read_cache - [BrcmNAND Interface] Read the 512B cache area + * Assuming brcmnand_get_device() has been called to obtain exclusive lock + * @param mtd MTD data structure + * @param oobarea Spare area, pass NULL if not interested + * @param buffer the databuffer to put/get data, pass NULL if only spare area is wanted. + * @param offset offset to read from or write to, must be 512B aligned. + * + * Caller is responsible to pass a buffer that is + * (1) large enough for 512B for data and optionally an oobarea large enough for 16B. + * (2) 4-byte aligned. + * + * Read the cache area into buffer. The size of the cache is mtd-->eccsize and is always 512B. + */ + +//**************************************** +int in_verify; +//**************************************** + +static int brcmnand_ctrl_posted_read_cache(struct mtd_info* mtd, + void* buffer, u_char* oobarea, loff_t offset) +{ + struct brcmnand_chip* chip = mtd->priv; + loff_t sliceOffset = offset & (~(ECCSIZE(mtd) - 1)); + int i, ret = 0; + static uint32_t oob0[4]; // Sparea Area to handle ECC workaround, aligned on DW boundary + uint32_t* p32 = (oobarea ? (uint32_t*)oobarea : (uint32_t*)&oob0[0]); + u_char* __maybe_unused p8 = (u_char*)p32; + + //unsigned long irqflags; + int retries = 5, done = 0; + int valid = 0; +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_7_0 + uint32_t intr_status; +#endif + + if (gdebug > 3 ) { + printk("%s: offset=%0llx, oobarea=%p\n", __FUNCTION__, offset, oobarea); + } + + + if (unlikely(offset - sliceOffset)) { + printk(KERN_ERR "%s: offset %08x is not cache aligned, sliceOffset=%08lx, CacheSize=%d\n", + __FUNCTION__, (unsigned int)offset, (unsigned long)sliceOffset, ECCSIZE(mtd)); + return -EINVAL; + } + + while (retries > 0 && !done) { +/* This register doesn't exist on DSL chips. */ +#if !defined(CONFIG_BCM_KF_MTD_BCMNAND) + uint32_t intr_status; + + if (wr_preempt_en) { + //local_irq_save(irqflags); + } + + // Mask Interrupt + BDEV_WR(BCHP_HIF_INTR2_CPU_MASK_SET, HIF_INTR2_ERR_MASK); + // Clear Status Mask for sector 0 workaround + BDEV_WR(BCHP_HIF_INTR2_CPU_CLEAR, + HIF_INTR2_ERR_MASK | BCHP_HIF_INTR2_CPU_STATUS_NAND_CTLRDY_INTR_MASK); + if (gdebug > 3) { + intr_status = BDEV_RD(BCHP_HIF_INTR2_CPU_STATUS); + printk("%s: before intr_status=%08x\n", __FUNCTION__, intr_status); + } +#endif + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) && CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_7_0 + intr_status = BDEV_RD(BCHP_HIF_INTR2_CPU_STATUS); + intr_status &= ~(HIF_INTR2_ERR_MASK); + BDEV_WR(BCHP_HIF_INTR2_CPU_STATUS, intr_status); +#endif + + chip->ctrl_writeAddr(chip, sliceOffset, 0); + PLATFORM_IOFLUSH_WAR(); + chip->ctrl_write(BCHP_NAND_CMD_START, OP_PAGE_READ); + + // Wait until cache is filled up + valid = brcmnand_cache_is_valid(mtd, BRCMNAND_FL_READING, offset, 100); + + if (wr_preempt_en) { + //local_irq_restore(irqflags); + } + + switch (valid) { + + case BRCMNAND_SUCCESS: /* Success, no errors */ + // Remember last good 512B-sector read. Needed for HIF_INTR2 war. + //if (0 == gLastKnownGoodEcc) + gLastKnownGoodEcc = offset; + + /* FALLTHROUGH */ + + case BRCMNAND_CORRECTABLE_ECC_ERROR: + if (buffer) { + brcmnand_from_flash_memcpy32(chip, buffer, offset, ECCSIZE(mtd)); + } + +#ifndef DEBUG_HW_ECC + if (oobarea || (ret == BRCMNAND_CORRECTABLE_ECC_ERROR)) +#endif + { + PLATFORM_IOFLUSH_WAR(); + for (i = 0; i < 4; i++) { + p32[i] = be32_to_cpu(chip->ctrl_read(BCHP_NAND_SPARE_AREA_READ_OFS_0 + i * 4)); + } + + read_ext_spare_area(chip, i, p32); + + if (gdebug > 3) { + printk("%s: offset=%0llx, oob=\n", __FUNCTION__, sliceOffset); print_oobbuf(oobarea, chip->eccOobSize); + } + } + + #ifndef DEBUG_HW_ECC // Comment out for debugging + + /* Make sure error was not in ECC bytes */ + if (ret == BRCMNAND_CORRECTABLE_ECC_ERROR && + chip->ecclevel == BRCMNAND_ECC_HAMMING) + #endif + + { + + char ecc0[3]; // SW ECC, manually calculated + + if (brcmnand_Hamming_WAR(mtd, offset, buffer, &p8[6], &ecc0[0])) { + /* Error was in ECC, update it from calculated value */ + if (oobarea) { + oobarea[6] = ecc0[0]; + oobarea[7] = ecc0[1]; + oobarea[8] = ecc0[2]; + } + } + + } + + + // SWLINUX-1495: + if (valid == BRCMNAND_CORRECTABLE_ECC_ERROR) + ret = BRCMNAND_CORRECTABLE_ECC_ERROR; + else + ret = 0; + + done = 1; + break; + + case BRCMNAND_UNCORRECTABLE_ECC_ERROR: +#if !defined(CONFIG_BCM_KF_MTD_BCMNAND) + ret = brcmnand_handle_false_read_ecc_unc_errors(mtd, buffer, oobarea, offset); +#else + ret = BRCMNAND_UNCORRECTABLE_ECC_ERROR; +#endif + done = 1; + break; + + case BRCMNAND_FLASH_STATUS_ERROR: + printk(KERN_ERR "brcmnand_cache_is_valid returns 0\n"); + ret = -EBADMSG; + done = 1; + break; + + case BRCMNAND_TIMED_OUT: + //Read has timed out + ret = -ETIMEDOUT; + if (!wr_preempt_en) { + retries--; + // THT PR50928: if wr_preempt is disabled, enable it to clear error + wr_preempt_en = brcmnand_handle_ctrl_timeout(mtd, retries); + continue; /* Retry */ + }else { + done = 1; + break; + } + + default: + BUG_ON(1); + /* Should never gets here */ + ret = -EFAULT; + done = 1; + } + + } + + if (wr_preempt_en) { + uint32_t acc; + + acc = brcmnand_ctrl_read(bchp_nand_acc_control(chip->ctrl->CS[chip->csi])); + + acc &= ~BCHP_NAND_ACC_CONTROL_WR_PREEMPT_EN_MASK; + brcmnand_ctrl_write(bchp_nand_acc_control(chip->ctrl->CS[chip->csi]), acc); + } + + + if (gdebug > 3 ) { + printk("<-- %s: offset=%0llx\n", __FUNCTION__, offset); + print_databuf(buffer, 32); + } + +#if defined( EDU_DEBUG ) || defined (BRCMNAND_READ_VERIFY ) +//if (in_verify <=0) + if (chip->ecclevel == BRCMNAND_ECC_HAMMING) { + u_char edu_sw_ecc[4]; + + brcmnand_Hamming_ecc(buffer, edu_sw_ecc); + + if ((p8[6] != edu_sw_ecc[0] || p8[7] != edu_sw_ecc[1] || p8[8] != edu_sw_ecc[2]) + && !(p8[6] == 0xff && p8[7] == 0xff && p8[8] == 0xff && + edu_sw_ecc[0] == 0x0 && edu_sw_ecc[1] == 0x0 && edu_sw_ecc[2] == 0x0) + ) { + printk("!!!!!!!!! %s: offset=%0llx ECC=%02x%02x%02x, OOB:", + in_verify < 0 ? "WR" : "RD", + offset, edu_sw_ecc[0], edu_sw_ecc[1], edu_sw_ecc[2]); + print_oobbuf(p8, 16); + BUG(); + } + } +#endif + +//gdebug=0; + + return ret; +} + + +/* + * Clear the controller cache by reading at a location we don't normally read + */ +static void __maybe_unused debug_clear_ctrl_cache(struct mtd_info* mtd) +{ + /* clear the internal cache by writing a new address */ + struct brcmnand_chip* chip = mtd->priv; + loff_t offset = chip->chipSize - chip->blockSize; // Start of BBT region + + //uint32_t intr_status; + +/* This register doesn't exist on DSL chips. */ +#if !defined(CONFIG_BCM_KF_MTD_BCMNAND) + // Mask Interrupt + BDEV_WR(BCHP_HIF_INTR2_CPU_MASK_SET, HIF_INTR2_ERR_MASK); + // Clear Status Mask for sector 0 workaround + BDEV_WR(BCHP_HIF_INTR2_CPU_CLEAR, + HIF_INTR2_ERR_MASK | BCHP_HIF_INTR2_CPU_STATUS_NAND_CTLRDY_INTR_MASK); +#endif + +#if 0 + /* Already cleared with cpu-clear */ + intr_status = BDEV_RD(BCHP_HIF_INTR2_CPU_STATUS); + intr_status &= ~(HIF_INTR2_ERR_MASK); + BDEV_WR(BCHP_HIF_INTR2_CPU_STATUS, intr_status); +#endif + + chip->ctrl_writeAddr(chip, offset, 0); + PLATFORM_IOFLUSH_WAR(); + chip->ctrl_write(BCHP_NAND_CMD_START, OP_PAGE_READ); + + // Wait until cache is filled up + (void)brcmnand_cache_is_valid(mtd, BRCMNAND_FL_READING, offset, 100); +} + +static int (*brcmnand_posted_read_cache)(struct mtd_info*, + void*, u_char*, loff_t) = brcmnand_ctrl_posted_read_cache; + +/** + * brcmnand_posted_read_oob - [BrcmNAND Interface] Read the spare area + * @param mtd MTD data structure + * @param oobarea Spare area, pass NULL if not interested + * @param offset offset to read from or write to + * + * This is a little bit faster than brcmnand_posted_read, making this command useful for improving + * the performance of BBT management. + * The 512B flash cache is invalidated. + * + * Read the cache area into buffer. The size of the cache is mtd->writesize and is always 512B, + * for this version of the BrcmNAND controller. + */ +static int brcmnand_posted_read_oob(struct mtd_info* mtd, + u_char* oobarea, loff_t offset, int raw) +{ + struct brcmnand_chip* chip = mtd->priv; + loff_t sliceOffset = offset & (~(ECCSIZE(mtd) - 1)); + int i, ret = 0, valid, done = 0; + int retries = 5; + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_3_0 + uint32_t acc1 = brcmnand_ctrl_read(bchp_nand_acc_control(chip->ctrl->CS[chip->csi])); + //unsigned long irqflags; + +//char msg[20]; + + static uint8_t myBuf2[512 + 31]; // Place holder only. + static uint8_t* myBuf = NULL; + + /* + * Force alignment on 32B boundary + */ + if (!myBuf) { + myBuf = (uint8_t*)((((uintptr_t)&myBuf2[0]) + 31) & (~31)); + } + + #if CONFIG_MTD_BRCMNAND_VERSION == CONFIG_MTD_BRCMNAND_VERS_3_0 + // Revert to cache read if acc is enabled + if (acc1 & BCHP_NAND_ACC_CONTROL_RD_ECC_EN_MASK) { + // PR2516. Not a very good WAR, but the affected chips (3548A0,7443A0) have been EOL'ed + return brcmnand_ctrl_posted_read_cache(mtd, (void*)myBuf, oobarea, offset); + } + + #else /* 3.1 or later */ + // If BCH codes, force full page read to activate ECC correction on OOB bytes. + // relies on the fact that brcmnand_disable_read_ecc() turns off both bllk0 and blkn bits + if ((acc1 & BCHP_NAND_ACC_CONTROL_RD_ECC_EN_MASK) && + chip->ecclevel != BRCMNAND_ECC_HAMMING && + chip->ecclevel != BRCMNAND_ECC_DISABLE) { + return brcmnand_ctrl_posted_read_cache(mtd, (void*)myBuf, oobarea, offset); + } + #endif +#endif + + if (gdebug > 3 ) PRINTK("->%s: offset=%0llx\n", __FUNCTION__, offset); + if (gdebug > 3 ) PRINTK("->%s: sliceOffset=%0llx\n", __FUNCTION__, sliceOffset); + if (gdebug > 3 ) PRINTK("eccsize = %d\n", ECCSIZE(mtd)); + + if (gdebug > 3 ) { + printk("-->%s: offset=%0llx\n", __FUNCTION__, offset); + } + + while (retries > 0 && !done) { + if (unlikely(sliceOffset - offset)) { + printk(KERN_ERR "%s: offset %0llx is not cache aligned\n", + __FUNCTION__, offset); + return -EINVAL; + } + + if (wr_preempt_en) { + //local_irq_save(irqflags); + } + + chip->ctrl_writeAddr(chip, sliceOffset, 0); + chip->ctrl_write(BCHP_NAND_CMD_START, OP_SPARE_AREA_READ); + + // Wait until spare area is filled up + + valid = brcmnand_spare_is_valid(mtd, BRCMNAND_FL_READING, raw, 100); + if (wr_preempt_en) { + //local_irq_restore(irqflags); + } + switch (valid) { + case 1: + if (oobarea) { + uint32_t* p32 = (uint32_t*)oobarea; + + for (i = 0; i < 4; i++) { + p32[i] = be32_to_cpu(chip->ctrl_read(BCHP_NAND_SPARE_AREA_READ_OFS_0 + (i << 2))); + } + + read_ext_spare_area(chip, i, p32); + + if (gdebug > 3) { + printk("%s: offset=%0llx, oob=\n", __FUNCTION__, sliceOffset); + print_oobbuf(oobarea, chip->eccOobSize); + } + + } + + ret = 0; + done = 1; + break; + + case -1: + ret = -EBADMSG; +//if (gdebug > 3 ) + { PRINTK("%s: ret = -EBADMSG\n", __FUNCTION__); } + /* brcmnand_spare_is_valid also clears the error bit, so just retry it */ + + retries--; + break; + + case 0: + //Read has timed out + ret = -ETIMEDOUT; + { PRINTK("%s: ret = -ETIMEDOUT\n", __FUNCTION__); } + retries--; + // THT PR50928: if wr_preempt is disabled, enable it to clear error + wr_preempt_en = brcmnand_handle_ctrl_timeout(mtd, retries); + continue; /* Retry */ + + default: + BUG_ON(1); + /* NOTREACHED */ + ret = -EINVAL; + done = 1; + break; /* Should never gets here */ + } + + } + if (wr_preempt_en) { + uint32_t acc; + + acc = brcmnand_ctrl_read(bchp_nand_acc_control(chip->ctrl->CS[chip->csi])); + + acc &= ~BCHP_NAND_ACC_CONTROL_WR_PREEMPT_EN_MASK; + brcmnand_ctrl_write(bchp_nand_acc_control(chip->ctrl->CS[chip->csi]), acc); + } + +//if (gdebug > 3 ) + if (0) { // == (offset & (mtd->erasesize-1))) + PRINTK("<--%s: offset=%08x\n", __FUNCTION__, (uint32_t)offset); + print_oobbuf(oobarea, 16); + } + return ret; +} + + + + +/** + * brcmnand_posted_write - [BrcmNAND Interface] Write a buffer to the flash cache + * Assuming brcmnand_get_device() has been called to obtain exclusive lock + * + * @param mtd MTD data structure + * @param buffer the databuffer to put/get data + * @param oobarea Spare area, pass NULL if not interested + * @param offset offset to write to, and must be 512B aligned + * + * Write to the cache area TBD 4/26/06 + */ +static int brcmnand_ctrl_posted_write_cache(struct mtd_info *mtd, + const void* buffer, const u_char* oobarea, loff_t offset) +{ + struct brcmnand_chip* chip = mtd->priv; + loff_t sliceOffset = offset & (~(ECCSIZE(mtd) - 1)); + uint32_t* p32; + int i, needBBT = 0; + int ret; + + //char msg[20]; + + + if (gdebug > 3 ) { + printk("--> %s: offset=%0llx\n", __FUNCTION__, offset); + print_databuf(buffer, 32); + } + + if (unlikely(sliceOffset - offset)) { + printk(KERN_ERR "%s: offset %0llx is not cache aligned\n", + __FUNCTION__, offset); + + ret = -EINVAL; + goto out; + } + chip->ctrl_writeAddr(chip, sliceOffset, 0); + + + if (buffer) { + if (gdebug > 3 ) { + print_databuf(buffer, 32); + } + brcmnand_to_flash_memcpy32(chip, offset, buffer, ECCSIZE(mtd)); + } +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + /* Must write data when NAND_COMPLEX_OOB_WRITE */ + else if (chip->options & NAND_COMPLEX_OOB_WRITE) { + brcmnand_to_flash_memcpy32(chip, offset, ffchars, ECCSIZE(mtd)); + } +#endif + + +//printk("30\n"); + if (oobarea) { + p32 = (uint32_t*)oobarea; + if (gdebug > 3) { + printk("%s: oob=\n", __FUNCTION__); print_oobbuf(oobarea, 16); + } + }else { + // Fill with 0xFF if don't want to change OOB + p32 = (uint32_t*)&ffchars[0]; + } + +//printk("40\n"); + for (i = 0; i < 4; i++) { + chip->ctrl_write(BCHP_NAND_SPARE_AREA_WRITE_OFS_0 + i * 4, cpu_to_be32(p32[i])); + } + + PLATFORM_IOFLUSH_WAR(); + chip->ctrl_write(BCHP_NAND_CMD_START, OP_PROGRAM_PAGE); +//printk("50\n"); + + // Wait until flash is ready + if (brcmnand_ctrl_write_is_complete(mtd, &needBBT)) { + if (!needBBT) { + ret = 0; + goto out; + }else { // Need BBT + printk(KERN_WARNING "%s: Flash Status Error @%0llx\n", __FUNCTION__, offset); +//printk("80 block mark bad\n"); + // SWLINUX-1495: Let UBI do it on returning -EIO + ret = -EIO; + chip->block_markbad(mtd, offset); + goto out; + } + } + //Write has timed out or read found bad block. TBD: Find out which is which + printk(KERN_INFO "%s: Timeout\n", __FUNCTION__); + ret = -ETIMEDOUT; + + out: +//printk("99\n"); + + return ret; +} + + + +static int (*brcmnand_posted_write_cache)(struct mtd_info*, + const void*, const u_char*, loff_t) = brcmnand_ctrl_posted_write_cache; + + + + +/** + * brcmnand_posted_write_oob - [BrcmNAND Interface] Write the spare area + * @param mtd MTD data structure + * @param oobarea Spare area, pass NULL if not interested. Must be able to + * hold mtd->oobsize (16) bytes. + * @param offset offset to write to, and must be 512B aligned + * + */ +static int brcmnand_posted_write_oob(struct mtd_info *mtd, + const u_char* oobarea, loff_t offset, int isFromMarkBadBlock) +{ + struct brcmnand_chip* chip = mtd->priv; + loff_t sliceOffset = offset & (~(ECCSIZE(mtd) - 1)); + uint32_t* p32; + int i, needBBT = 0; + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + uint32_t partial_page_wr_dis; +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_4_0 + uint32_t acc; + + acc = chip->ctrl_read(bchp_nand_acc_control(chip->ctrl->CS[chip->csi])); + partial_page_wr_dis = !(acc & BCHP_NAND_ACC_CONTROL_PARTIAL_PAGE_EN_MASK); +#else + partial_page_wr_dis = 0; +#endif +#endif + + if (gdebug > 3 ) { + printk("-->%s, offset=%0llx\n", __FUNCTION__, offset); + print_oobbuf(oobarea, 16); + } + + + if (unlikely(sliceOffset - offset)) { + printk(KERN_ERR "%s: offset %0llx is not cache aligned\n", + __FUNCTION__, offset); + } + + chip->ctrl_writeAddr(chip, sliceOffset, 0); + + // assert oobarea here + BUG_ON(!oobarea); + p32 = (uint32_t*)oobarea; + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + /* Must write data when NAND_COMPLEX_OOB_WRITE option is set. Wite 0xFFs + * to data and ECC locations. + */ + if ((chip->options & NAND_COMPLEX_OOB_WRITE) || partial_page_wr_dis) { + u_char* p8 = (u_char*)p32; + struct nand_ecclayout *oobinfo = chip->ecclayout; + + brcmnand_to_flash_memcpy32(chip, offset, ffchars, ECCSIZE(mtd)); + for (i = 0; i < oobinfo->eccbytes; i++) { + p8[oobinfo->eccpos[i]] = 0xff; + } + } +#endif + + for (i = 0; i < 4; i++) { + chip->ctrl_write(BCHP_NAND_SPARE_AREA_WRITE_OFS_0 + i * 4, cpu_to_be32(p32[i])); + } + + PLATFORM_IOFLUSH_WAR(); +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + if ((chip->options & NAND_COMPLEX_OOB_WRITE) || partial_page_wr_dis) { + + /* Disable ECC so 0xFFs are stored in the ECC offsets. Doing + * this allows the next page write to store the ECC correctly. + * If the ECC is not disabled here, then a ECC value will be + * stored at the ECC offsets. This will cause the ECC value + * on the next write to be stored incorrectly. + */ + uint32_t acc = chip->ctrl_read(BCHP_NAND_ACC_CONTROL); +#if CONFIG_MTD_BRCMNAND_VERSION < CONFIG_MTD_BRCMNAND_VERS_7_0 + chip->ctrl_write(BCHP_NAND_ACC_CONTROL, + (acc & ~(BCHP_NAND_ACC_CONTROL_ECC_LEVEL_MASK | + BCHP_NAND_ACC_CONTROL_ECC_LEVEL_0_MASK))); +#else + chip->ctrl_write(BCHP_NAND_ACC_CONTROL, + (acc & ~(BCHP_NAND_ACC_CONTROL_ECC_LEVEL_MASK))); +#endif + chip->ctrl_write(BCHP_NAND_CMD_START, OP_PROGRAM_PAGE); + + // Wait until flash is ready + if (brcmnand_ctrl_write_is_complete(mtd, &needBBT)) { + chip->ctrl_write(BCHP_NAND_ACC_CONTROL, acc); + return 0; + } + + chip->ctrl_write(BCHP_NAND_ACC_CONTROL, acc); + }else { + chip->ctrl_write(BCHP_NAND_CMD_START, OP_PROGRAM_SPARE_AREA); + + // Wait until flash is ready + if (brcmnand_ctrl_write_is_complete(mtd, &needBBT)) { + return 0; + } + } +#else +#if 0 + if (chip->options & NAND_COMPLEX_OOB_WRITE) { +//printk("****** Workaround, using OP_PROGRAM_PAGE instead of OP_PROGRAM_SPARE_AREA\n"); + chip->ctrl_write(BCHP_NAND_CMD_START, OP_PROGRAM_PAGE); + }else +#endif + { + chip->ctrl_write(BCHP_NAND_CMD_START, OP_PROGRAM_SPARE_AREA); + } + + // Wait until flash is ready + if (brcmnand_ctrl_write_is_complete(mtd, &needBBT)) { + return 0; + } +#endif /* CONFIG_BCM_KF_MTD_BCMNAND */ + + + if (needBBT) { + + int ret; + + printk(KERN_WARNING "%s: Flash Status Error @%0llx\n", __FUNCTION__, offset); + + // SWLINUX-1495: Let UBI do it on returning -EIO + ret = -EIO; + + if (!isFromMarkBadBlock) + chip->block_markbad(mtd, offset); + + return (ret); + } + + return -ETIMEDOUT; + +} + + + +/** + * brcmnand_get_device - [GENERIC] Get chip for selected access + * @param mtd MTD device structure + * @param new_state the state which is requested + * + * Get the device and lock it for exclusive access + */ +static int brcmnand_get_device(struct mtd_info *mtd, int new_state) +{ + struct brcmnand_chip * chip = mtd->priv; + + if (chip) { + DECLARE_WAITQUEUE(wait, current); + + /* + * Grab the lock and see if the device is available + */ + while (1) { + spin_lock(&chip->ctrl->chip_lock); + + if (chip->ctrl->state == BRCMNAND_FL_READY) { + chip->ctrl->state = new_state; + spin_unlock(&chip->ctrl->chip_lock); + break; + } + if (new_state == BRCMNAND_FL_PM_SUSPENDED) { + spin_unlock(&chip->ctrl->chip_lock); + return (chip->ctrl->state == BRCMNAND_FL_PM_SUSPENDED) ? 0 : -EAGAIN; + } + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->ctrl->wq, &wait); + spin_unlock(&chip->ctrl->chip_lock); + if (!wr_preempt_en && !in_interrupt()) + schedule(); + remove_wait_queue(&chip->ctrl->wq, &wait); + } + + return 0; + }else + return -EINVAL; +} + +#if 0 +/* No longer used */ +static struct brcmnand_chip* +brcmnand_get_device_exclusive(void) +{ + struct brcmnand_chip * chip = (struct brcmnand_chip*)get_brcmnand_handle(); + struct mtd_info *mtd; + int ret; + + mtd = (struct mtd_info*)chip->priv; + + if (mtd) { + ret = brcmnand_get_device(mtd, BRCMNAND_FL_XIP); + }else + ret = -1; + if (0 == ret) + return chip; + else + return ((struct brcmnand_chip *)0); +} + + +#endif + +/** + * brcmnand_release_device - [GENERIC] release chip + * @param mtd MTD device structure + * + * Deselect, release chip lock and wake up anyone waiting on the device + */ +static void brcmnand_release_device(struct mtd_info *mtd) +{ + struct brcmnand_chip * chip = mtd->priv; + + /* Release the chip */ + spin_lock(&chip->ctrl->chip_lock); + chip->ctrl->state = BRCMNAND_FL_READY; + wake_up(&chip->ctrl->wq); + spin_unlock(&chip->ctrl->chip_lock); +} + + + +/** + * brcmnand_read_page - {REPLACEABLE] hardware ecc based page read function + * @mtd: mtd info structure + * @chip: nand chip info structure. The OOB buf is stored here on return + * @buf: buffer to store read data + * + * Not for syndrome calculating ecc controllers which need a special oob layout + */ +static int +brcmnand_read_page(struct mtd_info *mtd, + uint8_t *outp_buf, uint8_t* outp_oob, uint64_t page) +{ + struct brcmnand_chip *chip = (struct brcmnand_chip*)mtd->priv; + int eccstep; + int dataRead = 0; + int oobRead = 0; + int ret = 0, error = 0; + uint64_t offset = ((uint64_t)page) << chip->page_shift; + int corrected = 0; // Only update stats once per page + int uncorrected = 0; // Only update stats once per page + + if (gdebug > 3 ) { + printk("-->%s, page=%0llx\n", __FUNCTION__, page); + } + + chip->pagebuf = page; + + for (eccstep = 0; eccstep < chip->eccsteps && ret == 0; eccstep++) { + ret = brcmnand_posted_read_cache(mtd, &outp_buf[dataRead], + outp_oob ? &outp_oob[oobRead] : NULL, + offset + dataRead); + if (gdebug > 3 && ret) printk("%s 1: calling brcmnand_posted_read_cache returns %d\n", + __FUNCTION__, ret); + if (ret == BRCMNAND_CORRECTABLE_ECC_ERROR) { + if ( !corrected) { +#if !defined(CONFIG_BCM_KF_MTD_BCMNAND) + (mtd->ecc_stats.corrected)++; +#else + if (error != BRCMNAND_UNCORRECTABLE_ECC_ERROR) + error = ret; +#endif + corrected = 1; + } + ret = 0; + }else if (ret == BRCMNAND_UNCORRECTABLE_ECC_ERROR) { + if ( !uncorrected) { +#if !defined(CONFIG_BCM_KF_MTD_BCMNAND) + (mtd->ecc_stats.failed)++; +#else + error = ret; +#endif + uncorrected = 1; + } + ret = 0; + }else if (ret < 0) { + printk(KERN_ERR "%s: 3: brcmnand_posted_read_cache failed at offset=%0llx, ret=%d\n", + __FUNCTION__, offset + dataRead, ret); + return ret; + } + + dataRead += chip->eccsize; + oobRead += chip->eccOobSize; + } + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + if (error) + ret = brcmnand_handle_ecc_errors(mtd, outp_buf, outp_oob, offset, error); +#endif + return ret; +} + + + +/** + * brcmnand_read_page_oob - {REPLACABLE] hardware ecc based page read function + * @mtd: mtd info structure + * @chip: nand chip info structure. The OOB buf is stored in the oob_poi ptr on return + * + * Not for syndrome calculating ecc controllers which need a special oob layout + */ +static int +brcmnand_read_page_oob(struct mtd_info *mtd, + uint8_t* outp_oob, uint64_t page) +{ + struct brcmnand_chip *chip = (struct brcmnand_chip*)mtd->priv; + int eccstep; + int dataRead = 0; + int oobRead = 0; + int corrected = 0; // Only update stats once per page + int uncorrected = 0; // Only update stats once per page + int ret = 0, error = 0; + uint64_t offset = page << chip->page_shift; + + + if (gdebug > 3 ) { + printk("-->%s, offset=%0llx\n", __FUNCTION__, offset); + } + + chip->pagebuf = page; + + for (eccstep = 0; eccstep < chip->eccsteps && ret == 0; eccstep++) { +//gdebug=4; + ret = brcmnand_posted_read_oob(mtd, &outp_oob[oobRead], + offset + dataRead, 1); +//gdebug=0; + if (gdebug > 3 && ret) printk("%s 2: calling brcmnand_posted_read_oob returns %d\n", + __FUNCTION__, ret); + if (ret == BRCMNAND_CORRECTABLE_ECC_ERROR) { + if ( !corrected) { +#if !defined(CONFIG_BCM_KF_MTD_BCMNAND) + (mtd->ecc_stats.corrected)++; +#else + if (error != BRCMNAND_UNCORRECTABLE_ECC_ERROR) + error = ret; +#endif + corrected = 1; + } + ret = 0; + }else if (ret == BRCMNAND_UNCORRECTABLE_ECC_ERROR) { + if ( !uncorrected) { +#if !defined(CONFIG_BCM_KF_MTD_BCMNAND) + (mtd->ecc_stats.failed)++; +#else + error = ret; +#endif + uncorrected = 1; + } + ret = 0; + }else if (ret < 0) { + printk(KERN_ERR "%s: 3: posted read oob failed at offset=%0llx, ret=%d\n", + __FUNCTION__, offset + dataRead, ret); + return ret; + } + dataRead += chip->eccsize; + oobRead += chip->eccOobSize; + } + + if (gdebug > 3 && ret) printk("%s returns %d\n", + __FUNCTION__, ret); + + if (gdebug > 3 ) { + printk("<--%s offset=%0llx, ret=%d\n", __FUNCTION__, offset, ret); + print_oobbuf(outp_oob, mtd->oobsize); + } + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + if (error) + ret = brcmnand_handle_ecc_errors(mtd, NULL, outp_oob, offset, error); +#endif + return ret; +} + +#ifdef CONFIG_MTD_BRCMNAND_CORRECTABLE_ERR_HANDLING +static int brcmnand_refresh_blk(struct mtd_info *mtd, loff_t from) +{ + struct brcmnand_chip *chip = mtd->priv; + int i, j, k, numpages, ret, count = 0, nonecccount = 0; + uint8_t *blk_buf; /* Store one block of data (including OOB) */ + unsigned int pg_idx, oob_idx; + uint64_t realpage; + struct erase_info *instr; + //int gdebug = 1; + struct nand_ecclayout *oobinfo; + uint8_t *oobptr; + uint32_t *oobptr32; + loff_t blkbegin; + unsigned int block_size; + + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_1_0 + chip->ctrl_write(BCHP_NAND_ECC_CORR_EXT_ADDR, 0); +#endif + chip->ctrl_write(BCHP_NAND_ECC_CORR_ADDR, 0); + + DEBUG(MTD_DEBUG_LEVEL3, "Inside %s: from=%0llx\n", __FUNCTION__, from); + printk(KERN_INFO "%s: Performing block refresh for correctable ECC error at %0llx\n", + __FUNCTION__, from); + pg_idx = 0; + oob_idx = mtd->writesize; + numpages = mtd->erasesize / mtd->writesize; + block_size = (1 << chip->erase_shift); + blkbegin = (from & (~(mtd->erasesize - 1))); + realpage = blkbegin >> chip->page_shift; + + blk_buf = (uint8_t*)BRCMNAND_malloc(numpages * (mtd->writesize + mtd->oobsize)); + + if (unlikely(blk_buf == NULL)) { + printk(KERN_ERR "%s: buffer allocation failed\n", __FUNCTION__); + return -1; + } + + memset(blk_buf, 0xff, numpages * (mtd->writesize + mtd->oobsize)); + + if (unlikely(gdebug > 0)) { + printk("---> %s: from = %0llx, numpages = %d, realpage = %0llx\n", \ + __FUNCTION__, from, numpages, realpage); + printk(" Locking flash for read ... \n"); + } + + /* Read an entire block */ + brcmnand_get_device(mtd, BRCMNAND_FL_READING); + for (i = 0; i < numpages; i++) { + ret = chip->read_page(mtd, blk_buf + pg_idx, blk_buf + oob_idx, realpage); + if (ret < 0) { + BRCMNAND_free(blk_buf); +// #else re-use for EDU + brcmnand_release_device(mtd); + return -1; + } + //printk("DEBUG -> Reading %d realpage = %x %x ret = %d oob = %x\n", i, realpage, *(blk_buf+pg_idx), ret, *(blk_buf + oob_idx)); + //print_oobbuf(blk_buf+oob_idx, mtd->oobsize); + pg_idx += mtd->writesize + mtd->oobsize; + oob_idx += mtd->oobsize + mtd->writesize; + realpage++; + } + if (unlikely(gdebug > 0)) { + printk("---> %s: Read -> erase\n", __FUNCTION__); + } + chip->ctrl->state = BRCMNAND_FL_ERASING; + + /* Erase the block */ + instr = kmalloc(sizeof(struct erase_info), GFP_KERNEL); + if (instr == NULL) { + printk(KERN_WARNING "kmalloc for erase_info failed\n"); + BRCMNAND_free(blk_buf); +// #else re-use for EDU + brcmnand_release_device(mtd); + return -ENOMEM; + } + memset(instr, 0, sizeof(struct erase_info)); + instr->mtd = mtd; + instr->addr = blkbegin; + instr->len = mtd->erasesize; + if (unlikely(gdebug > 0)) { + printk("DEBUG -> erasing %0llx, %0llx %d\n", instr->addr, instr->len, chip->ctrl->state); + } + ret = brcmnand_erase_nolock(mtd, instr, 0); + if (ret) { + BRCMNAND_free(blk_buf); +// #else re-use for EDU + kfree(instr); + brcmnand_release_device(mtd); + printk(KERN_WARNING " %s Erase failed %d\n", __FUNCTION__, ret); + return ret; + } + kfree(instr); + + /* Write the entire block */ + pg_idx = 0; + oob_idx = mtd->writesize; + realpage = blkbegin >> chip->page_shift; + if (unlikely(gdebug > 0)) { + printk("---> %s: Erase -> write ... %d\n", __FUNCTION__, chip->ctrl->state); + } + oobinfo = chip->ecclayout; + chip->ctrl->state = BRCMNAND_FL_WRITING; + for (i = 0; i < numpages; i++) { + /* Avoid writing empty pages */ + count = 0; + nonecccount = 0; + oobptr = (uint8_t*)(blk_buf + oob_idx); + oobptr32 = (uint32_t*)(blk_buf + oob_idx); + for (j = 0; j < oobinfo->eccbytes; j++) { + if (oobptr[oobinfo->eccpos[j]] == 0xff) { + count++; + } + } + for (k = 0; k < mtd->oobsize / 4; k++) { + if (oobptr32[k] == 0xffffffff) { + nonecccount++; + } + } + /* Skip this page if ECC is 0xff */ + if (count == j && nonecccount == k) { + pg_idx += mtd->writesize + mtd->oobsize; + oob_idx += mtd->oobsize + mtd->writesize; + realpage++; + continue; + } + /* Skip this page, but write the OOB */ + if (count == j && nonecccount != k) { + ret = chip->write_page_oob(mtd, blk_buf + oob_idx, realpage, 0); + if (ret) { + BRCMNAND_free(blk_buf); +// #else re-use for EDU + brcmnand_release_device(mtd); + return ret; + } + pg_idx += mtd->writesize + mtd->oobsize; + oob_idx += mtd->oobsize + mtd->writesize; + realpage++; + continue; + } + for (j = 0; j < oobinfo->eccbytes; j++) { + oobptr[oobinfo->eccpos[j]] = 0xff; + } + ret = chip->write_page(mtd, blk_buf + pg_idx, blk_buf + oob_idx, realpage); + if (ret) { + BRCMNAND_free(blk_buf); +// #else re-use for EDU + brcmnand_release_device(mtd); + return ret; + } + pg_idx += mtd->writesize + mtd->oobsize; + oob_idx += mtd->oobsize + mtd->writesize; + realpage++; + } + brcmnand_release_device(mtd); + BRCMNAND_free(blk_buf); +// #else re-use for EDU + printk(KERN_INFO "%s: block refresh success\n", __FUNCTION__); + + return 0; +} +#endif + + + +/** + * brcmnand_do_read_ops - [Internal] Read data with ECC + * + * @mtd: MTD device structure + * @from: offset to read from + * @ops: oob ops structure + * @raw: read raw data format when TRUE + * + * Internal function. Called with chip held. + */ + +//#define EDU_DEBUG_1 +#undef EDU_DEBUG_1 + +static int brcmnand_do_read_ops(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + unsigned int bytes, col; + uint64_t realpage; + int aligned; + struct brcmnand_chip *chip = mtd->priv; + struct mtd_ecc_stats stats; + //int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; + //int sndcmd = 1; + int ret = 0; + uint32_t readlen = ops->len; + uint8_t *bufpoi, *oob, *buf; + int __maybe_unused numPages; + int __maybe_unused buffer_aligned = 0; + int ooblen; + + + if (ops->mode == MTD_OPS_AUTO_OOB) { + ooblen = mtd->ecclayout->oobavail; + }else { + ooblen = mtd->oobsize; + } +//int nonBatch = 0; + + /* Remember the current CORR error count */ + stats = mtd->ecc_stats; + + // THT: BrcmNAND controller treats multiple chip as one logical chip. + //chipnr = (int)(from >> chip->chip_shift); + //chip->select_chip(mtd, chipnr); + + realpage = (uint64_t)from >> chip->page_shift; + //page = realpage & chip->pagemask; + + col = mtd64_ll_low(from & (mtd->writesize - 1)); + +/* Debugging 12/27/08 */ + chip->oob_poi = BRCMNAND_OOBBUF(chip->ctrl->buffers); + + buf = ops->datbuf; + oob = ops->oobbuf; + +#ifdef CONFIG_MTD_BRCMNAND_ISR_QUEUE + /* + * Group several pages for submission for small page NAND + */ + if (chip->pageSize == chip->eccsize && ops->mode != MTD_OPS_RAW) { + while (1) { +//nonBatch = 0; + bytes = min(mtd->writesize - col, readlen); + // (1) Writing partial or full page + aligned = (bytes == mtd->writesize); + + // If writing full page, use user buffer, otherwise, internal buffer + bufpoi = aligned ? buf : chip->ctrl->buffers->databuf; + + // (2) Buffer satisfies 32B alignment required by EDU? + buffer_aligned = EDU_buffer_OK(bufpoi, EDU_READ); + + // (3) Batch mode if writing more than 1 pages. + numPages = min(MAX_JOB_QUEUE_SIZE, (int)readlen >> chip->page_shift); + + // Only do Batch mode if all 3 conditions are satisfied. + if (!aligned || !buffer_aligned || numPages <= 1) { + /* Submit 1 page at a time */ + + numPages = 1; // We count partial page read + ret = chip->read_page(mtd, bufpoi, chip->oob_poi, realpage); + + if (ret < 0) + break; + + /* Transfer not aligned data */ + if (!aligned) { + chip->pagebuf = realpage; + memcpy(buf, &bufpoi[col], bytes); + } + buf += bytes; + + if (unlikely(oob)) { + /* if (ops->mode != MTD_OPS_RAW) */ + oob = brcmnand_transfer_oob(chip, oob, ops, ooblen); + + } + + }else { + /* + * Batch job possible, all 3 conditions are met + * bufpoi = Data buffer from FS driver + * oob = OOB buffer from FS driver + */ + bytes = numPages * mtd->writesize; + + ret = brcmnand_isr_read_pages(mtd, bufpoi, oob ? &oob : NULL, realpage, numPages, ops); + + if (ret < 0) + break; + + buf += bytes; /* Advance Read pointer */ + + } + + + readlen -= bytes; + + if (!readlen) + break; + + /* For subsequent reads align to page boundary. */ + col = 0; + /* Increment page address */ + realpage += numPages; + } + goto out; + }else +#endif + { + while (1) { + bytes = min(mtd->writesize - col, readlen); + aligned = (bytes == mtd->writesize); + + bufpoi = aligned ? buf : chip->ctrl->buffers->databuf; + + ret = chip->read_page(mtd, bufpoi, oob ? chip->oob_poi : NULL, realpage); + + if (ret < 0) + break; + + /* Transfer not aligned data */ + if (!aligned) { + chip->pagebuf = realpage; + memcpy(buf, &bufpoi[col], bytes); + } + + buf += bytes; + + if (unlikely(oob)) { + /* Raw mode does data:oob:data:oob */ + if (ops->mode != MTD_OPS_RAW) + oob = brcmnand_transfer_oob(chip, oob, ops, ooblen); + else { + buf = brcmnand_transfer_oob(chip, buf, ops, ooblen); + } + } + + + readlen -= bytes; + + if (!readlen) + break; + + /* For subsequent reads align to page boundary. */ + col = 0; + /* Increment page address */ + realpage++; + + } + } + + out: __maybe_unused +//gdebug=0; + + ops->retlen = ops->len - (size_t)readlen; + + + if (ret) + return ret; + + if (mtd->ecc_stats.failed - stats.failed) + return -EBADMSG; + + return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; +} + + + +/** + * brcmnand_read - [MTD Interface] MTD compability function for nand_do_read_ecc + * @mtd: MTD device structure + * @from: offset to read from + * @len: number of bytes to read + * @retlen: pointer to variable to store the number of read bytes + * @buf: the databuffer to put data + * + * Get hold of the chip and call nand_do_read + */ +static int brcmnand_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, uint8_t *buf) +{ + struct brcmnand_chip *chip = mtd->priv; + int ret; + +#ifdef CONFIG_MTD_BRCMNAND_CORRECTABLE_ERR_HANDLING + int status; +#endif + + DEBUG(MTD_DEBUG_LEVEL3, "%s: from=%0llx\n", __FUNCTION__, from); + + if (gdebug > 3 ) { + printk("-->%s, offset=%0llx, len=%08x\n", __FUNCTION__, from, (unsigned int)len); + } + + /* Do not allow reads past end of device */ + + if (unlikely((from + len) > device_size(mtd))) + return -EINVAL; + + if (!len) + return 0; + + brcmnand_get_device(mtd, BRCMNAND_FL_READING); + + chip->ops.mode = MTD_OPS_AUTO_OOB; + chip->ops.len = len; + chip->ops.datbuf = buf; + chip->ops.oobbuf = NULL; + + brcmnand_reset_corr_threshold(chip); + + ret = brcmnand_do_read_ops(mtd, from, &chip->ops); + + *retlen = chip->ops.retlen; + + brcmnand_release_device(mtd); + +#ifdef CONFIG_MTD_BRCMNAND_CORRECTABLE_ERR_HANDLING + /* use atomic_inc_return instead two seperate atomic_read and atomic_inc call because + there is race condition between these two calls if it is preempted after first call but + right before the second atomic call */ + if (unlikely(ret == -EUCLEAN)) { + if (atomic_inc_return(&inrefresh) == 1) { + if (brcmnand_refresh_blk(mtd, from) == 0) { + ret = 0; + } + if (likely(chip->cet)) { + if (likely(chip->cet->flags != BRCMNAND_CET_DISABLED)) { + if (brcmnand_cet_update(mtd, from, &status) == 0) { + +/* + * PR57272: Provide workaround for BCH-n ECC HW bug when # error bits >= 4 + * We will not mark a block bad when the a correctable error already happened on the same page + */ +#if CONFIG_MTD_BRCMNAND_VERSION <= CONFIG_MTD_BRCMNAND_VERS_3_4 + ret = 0; +#else + if (status) { + ret = -EUCLEAN; + } else { + ret = 0; + } +#endif + } + if (gdebug > 3) { + printk(KERN_INFO "DEBUG -> %s ret = %d, status = %d\n", __FUNCTION__, ret, status); + } + } + } + } + atomic_dec(&inrefresh); + } +#endif + return ret; +} + + + +/** + * brcmnand_do_read_oob - [Intern] BRCMNAND read out-of-band + * @mtd: MTD device structure + * @from: offset to read from + * @ops: oob operations description structure + * + * BRCMNAND read out-of-band data from the spare area + */ +static int brcmnand_do_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + int realpage = 1; + struct brcmnand_chip *chip = mtd->priv; + //int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; + int toBeReadlen = ops->ooblen; + int readlen = 0; + int len; /* Number of OOB bytes to read each page */ + uint8_t *buf = ops->oobbuf; + int ret = 0; + + if (gdebug > 3 ) { + printk("-->%s, offset=%0llx, buf=%p, ooblen=%d\n", __FUNCTION__, from, buf, toBeReadlen); + } + + DEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08Lx, len = %i\n", + __FUNCTION__, (unsigned long long)from, toBeReadlen); + + //chipnr = (int)(from >> chip->chip_shift); + //chip->select_chip(mtd, chipnr); + + if (ops->mode == MTD_OPS_AUTO_OOB) + len = chip->ecclayout->oobavail; + else + len = mtd->oobsize; + + if (unlikely(ops->ooboffs >= len)) { + DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt to start read outside oob\n"); + return -EINVAL; + } + + /* Do not allow reads past end of device */ + if (unlikely(from >= mtd->size || + ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) - + (from >> chip->page_shift)) * len)) { + DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt read beyond end of device\n"); + return -EINVAL; + } + + + /* Shift to get page */ + realpage = (int)(from >> chip->page_shift); + //page = realpage & chip->pagemask; + + chip->oob_poi = BRCMNAND_OOBBUF(chip->ctrl->buffers); + brcmnand_reset_corr_threshold(chip); + + while (toBeReadlen > 0) { + ret = chip->read_page_oob(mtd, chip->oob_poi, realpage); + if (ret) { // Abnormal return + ops->oobretlen = readlen; + return ret; + } + + buf = brcmnand_transfer_oob(chip, buf, ops, len); + + toBeReadlen -= len; + readlen += len; + + /* Increment page address */ + realpage++; + + } + + ops->oobretlen = ops->ooblen; + return ret; +} + + +/** + * brcmnand_read_oob - [MTD Interface] NAND read data and/or out-of-band + * @mtd: MTD device structure + * @from: offset to read from + * @ops: oob operation description structure + * + * NAND read data and/or out-of-band data + */ +static int brcmnand_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ +// struct brcmnand_chip *chip = mtd->priv; + int ret = -ENOTSUPP; + + //int raw; + + if (gdebug > 3 ) { + printk("-->%s, offset=%lx len=%x, databuf=%p\n", __FUNCTION__, + (unsigned long)from, (unsigned)ops->len, ops->datbuf); + } + + DEBUG(MTD_DEBUG_LEVEL3, "%s: from=%0llx\n", __FUNCTION__, from); + + ops->retlen = 0; + + /* Do not allow reads past end of device */ + + brcmnand_get_device(mtd, BRCMNAND_FL_READING); + +#if 0 + switch (ops->mode) { + case MTD_OPS_PLACE_OOB: + case MTD_OPS_AUTO_OOB: + raw = 0; + break; + + case MTD_OPS_RAW: + raw = 1; + break; + + default: + goto out; + } +#endif + + if (!ops->datbuf) { + ret = brcmnand_do_read_oob(mtd, from, ops); + } else { + if (unlikely((from + ops->len) > device_size(mtd))) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt read beyond end of device\n", __FUNCTION__); + ret = -EINVAL; + } else { + ret = brcmnand_do_read_ops(mtd, from, ops); + } + } + + +// out: + brcmnand_release_device(mtd); + if (gdebug > 3 ) { + printk("<-- %s: ret=%d\n", __FUNCTION__, ret); + } + return ret; +} + + + + + +#ifdef CONFIG_MTD_BRCMNAND_VERIFY_WRITE + +#if 0 +/* + * Returns 0 on success, + */ +static int brcmnand_verify_pageoob_priv(struct mtd_info *mtd, loff_t offset, + const u_char* fsbuf, int fslen, u_char* oob_buf, int ooblen, struct nand_oobinfo* oobsel, + int autoplace, int raw) +{ + //struct brcmnand_chip * chip = mtd->priv; + int ret = 0; + int complen; + + + if (autoplace) { + + complen = min_t(int, ooblen, fslen); + + /* We may have read more from the OOB area, so just compare the min of the 2 */ + if (complen == fslen) { + ret = memcmp(fsbuf, oob_buf, complen); + if (ret) { + { + printk("Autoplace Comparison failed at %08x, ooblen=%d fslen=%d left=\n", + __ll_low(offset), ooblen, fslen); + print_oobbuf(fsbuf, fslen); + printk("\nRight=\n"); print_oobbuf(oob_buf, ooblen); + dump_stack(); + } + goto comparison_failed; + } + }else { + printk("%s: OOB comparison failed, ooblen=%d is less than fslen=%d\n", + __FUNCTION__, ooblen, fslen); + return -EBADMSG; + } + }else { // No autoplace. Skip over non-freebytes + + /* + * THT: + * WIth YAFFS1, the FS codes overwrite an already written chunks quite a lot + * (without erasing it first, that is!!!!!) + * For those write accesses, it does not make sense to check the write ops + * because they are going to fail every time + */ + + +#if 0 + int i, len; + + for (i = 0; oobsel->oobfree[i][1] && i < ARRAY_SIZE(oobsel->oobfree); i++) { + int from = oobsel->oobfree[i][0]; + int num = oobsel->oobfree[i][1]; + int len1 = num; + + if (num == 0) break; // End of oobsel + + if ((from + num) > fslen) len1 = fslen - from; + ret = memcmp(&fsbuf[from], &oob_buf[from], len1); + if (ret) { + printk(KERN_ERR "%s: comparison at offset=%08x, i=%d from=%d failed., num=%d\n", + __FUNCTION__, i, __ll_low(offset), from, num); + if (gdebug > 3) { + printk("No autoplace Comparison failed at %08x, ooblen=%d fslen=%d left=\n", + __ll_low(offset), ooblen, fslen); + print_oobbuf(&fsbuf[0], fslen); + printk("\nRight=\n"); print_oobbuf(&oob_buf[0], ooblen); + dump_stack(); + } + goto comparison_failed; + } + if ((from + num) >= fslen) break; + len += num; + } +#endif + } + return ret; + + + comparison_failed: + { + //unsigned long nand_timing1 = brcmnand_ctrl_read(BCHP_NAND_TIMING_1); + //unsigned long nand_timing2 = brcmnand_ctrl_read(BCHP_NAND_TIMING_2); + //u_char raw_oob[NAND_MAX_OOBSIZE]; + //int retlen; + //struct nand_oobinfo noauto_oobsel; + + printk("Comparison Failed\n"); + print_diagnostics(chip); + + //noauto_oobsel = *oobsel; + //noauto_oobsel.useecc = MTD_NANDECC_PLACEONLY; + //brcmnand_read_pageoob(mtd, offset, raw_oob, &retlen, &noauto_oobsel, 0, raw); +//if (gdebug) { printk("oob="); print_oobbuf(raw_oob, retlen);} +//printk("<-- %s: comparison failed\n", __FUNCTION__); + + + return -EBADMSG; + } +} +#endif + + +/** + * brcmnand_verify_page - [GENERIC] verify the chip contents after a write + * @param mtd MTD device structure + * @param dbuf the databuffer to verify + * @param dlen the length of the data buffer, and should beequal to mtd->writesize + * @param oobbuf the length of the file system OOB data and should be exactly + * chip->oobavail (for autoplace) or mtd->oobsize otherise + * bytes to verify. (ignored for Hamming) + * @param ooblen + * + * Returns 0 on success, 1 on errors. + * Assumes that lock on. Munges the internal data and OOB buffers. + */ +//#define MYDEBUG +#if 0 +static u_char verify_buf[NAND_MAX_PAGESIZE + 512]; +static u_char v_oob_buf [NAND_MAX_OOBSIZE]; +static int brcmnand_verify_page(struct mtd_info *mtd, loff_t addr, + const u_char *dbuf, int dlen, + const u_char* inp_oob, int ooblen + ) +{ + struct brcmnand_chip * chip = mtd->priv; + + int ret = 0; // Matched + //int ooblen=0, datalen=0; + //int complen; + u_char* oobbuf = v_oob_buf; + uint64_t page; + int eccstep; + // Align Vbuf on 512B + u_char* vbuf = (u_char*)( ((unsigned long)verify_buf + chip->eccsize - 1) + & ~( chip->eccsize - 1)); + + if (gdebug > 3) printk("-->%s: addr=%0llx\n", __FUNCTION__, addr); + + /* + * Only do it for Hamming codes because + * (1) We can't do it for BCH until we can read the full OOB area for BCH-8 + * (2) OOB area is included in ECC calculation for BCH, so no need to check it + * separately. + */ + + +#if 1 + page = ((uint64_t)addr) >> chip->page_shift; + // Must read entire page + ret = chip->read_page(mtd, vbuf, oobbuf, page); + if (ret) { + printk(KERN_ERR "%s: brcmnand_read_page at %08x failed ret=%d\n", + __FUNCTION__, (unsigned int)addr, ret); + brcmnand_post_mortem_dump(mtd, addr); + return ret; + } + +#endif + + if (chip->ecclevel != BRCMNAND_ECC_HAMMING) { + return ret; // We won't verify the OOB if not Hamming + } + + /* + * If there are no Input Buffer, there is nothing to verify. + * Reading the page should be enough. + */ + if (!dbuf || dlen <= 0) + return 0; + + for (eccstep = 0; eccstep < chip->eccsteps; eccstep++) { + int pageOffset = eccstep * chip->eccsize; + int oobOffset = eccstep * chip->eccOobSize; + u_char sw_ecc[4]; // SW ECC + u_char* oobp = &oobbuf[oobOffset]; // returned from read op, contains HW ECC. + + brcmnand_Hamming_ecc(&dbuf[pageOffset], sw_ecc); + + if (sw_ecc[0] != oobp[6] || sw_ecc[1] != oobp[7] || sw_ecc[2] != oobp[8]) { + if (oobp[6] == 0xff && oobp[7] == 0xff && oobp[8] == 0xff + && sw_ecc[0] == 0 && sw_ecc[1] == 0 && sw_ecc[2] == 0) + ; // OK + else { + printk("%s: Verification failed at %0llx. HW ECC=%02x%02x%02x, SW ECC=%02x%02x%02x\n", + __FUNCTION__, addr, + oobp[6], oobp[7], oobp[8], sw_ecc[0], sw_ecc[1], sw_ecc[2]); + ret = 1; + break; + } + } + + // Verify the OOB if not NULL + if (inp_oob) { + if (memcmp(&inp_oob[oobOffset], oobp, 6) || memcmp(&inp_oob[oobOffset + 9], &oobp[9], 7)) { + printk("+++++++++++++++++++++++ %s: OOB comp Hamming failed\n", __FUNCTION__); + printk("In OOB:\n"); print_oobbuf(&inp_oob[oobOffset], 16); + printk("\nVerify OOB:\n"); print_oobbuf(oobp, 16); + ret = (-2); + break; + } + } + } + + return ret; +} +#endif + +#if 1 + +#define brcmnand_verify_pageoob(...) (0) + +#else + +/** + * brcmnand_verify_pageoob - [GENERIC] verify the chip contents after a write + * @param mtd MTD device structure + * @param dbuf the databuffer to verify + * @param dlen the length of the data buffer, and should be less than mtd->writesize + * @param fsbuf the file system OOB data + * @param fslen the length of the file system buffer + * @param oobsel Specify how to write the OOB data + * @param autoplace Specify how to write the OOB data + * @param raw Ignore the Bad Block Indicator when true + * + * Assumes that lock on. Munges the OOB internal buffer. + */ +static int brcmnand_verify_pageoob(struct mtd_info *mtd, loff_t addr, const u_char* fsbuf, int fslen, + struct nand_oobinfo *oobsel, int autoplace, int raw) +{ +// struct brcmnand_chip * chip = mtd->priv; + //u_char* data_buf = chip->data_buf; + u_char oob_buf[NAND_MAX_OOBSIZE]; // = chip->oob_buf; + int ret = 0; + //int complen; + //char tmpfsbuf[NAND_MAX_OOBSIZE]; // Max oob size we support. + int ooblen = 0; + + if (gdebug) printk("-->%s addr=%08x, fslen=%d, autoplace=%d, raw=%d\n", __FUNCTION__, __ll_low(addr), + fslen, autoplace, raw); + + // Must read entire page + ret = brcmnand_read_pageoob(mtd, addr, oob_buf, &ooblen, oobsel, autoplace, raw); + + if (ret) { + printk(KERN_ERR "%s: brcmnand_read_page at %p failed ret=%d\n", + __FUNCTION__, (void*)addr, ret); + return ret; + } + + if (gdebug) printk("%s: Calling verify_pageoob_priv(addr=%08x, fslen=%d, ooblen=%d\n", + __FUNCTION__, __ll_low(addr), fslen, ooblen); + ret = brcmnand_verify_pageoob_priv(mtd, addr, fsbuf, fslen, oob_buf, ooblen, oobsel, autoplace, raw); + + return ret; +} + +#endif + +#else +#define brcmnand_verify_page(...) (0) +#define brcmnand_verify_pageoob(...) (0) +//#define brcmnand_verify_oob(...) (0) +#endif + + + +/** + * brcmnand_write_page - [INTERNAL] write one page + * @mtd: MTD device structure + * @chip: NAND chip descriptor + * @inp_buf: the data to write + * @inp_oob: the spare area to write + * @page: page number to write + * @cached: cached programming [removed] + */ +static int +brcmnand_write_page(struct mtd_info *mtd, + const uint8_t *inp_buf, const uint8_t* inp_oob, uint64_t page) +{ + struct brcmnand_chip *chip = (struct brcmnand_chip*)mtd->priv; + int eccstep; + int dataWritten = 0; + int oobWritten = 0; + int ret = 0; + uint64_t offset = page << chip->page_shift; + + + if (gdebug > 3 ) { + printk("-->%s, offset=%0llx\n", __FUNCTION__, offset); + } + + chip->pagebuf = page; + + for (eccstep = 0; eccstep < chip->eccsteps && ret == 0; eccstep++) { + ret = brcmnand_posted_write_cache(mtd, &inp_buf[dataWritten], + inp_oob ? &inp_oob[oobWritten] : NULL, + offset + dataWritten); + + if (ret < 0) { + printk(KERN_ERR "%s: brcmnand_posted_write_cache failed at offset=%0llx, ret=%d\n", + __FUNCTION__, offset + dataWritten, ret); + // TBD: Return the the number of bytes written at block boundary. + dataWritten = 0; + return ret; + } + dataWritten += chip->eccsize; + oobWritten += chip->eccOobSize; + } + + // TBD +#ifdef BRCMNAND_WRITE_VERIFY + if (0 == ret) { + int vret; +//gdebug = 0; + vret = brcmnand_verify_page(mtd, offset, inp_buf, mtd->writesize, inp_oob, chip->eccOobSize); +//gdebug=save_debug; + if (vret) BUG(); + } +#endif + + + return ret; +} + +#ifdef CONFIG_MTD_BRCMNAND_ISR_QUEUE + +/* + * Queue the entire page, then wait for completion + */ +static int +brcmnand_isr_write_page(struct mtd_info *mtd, + const uint8_t *inp_buf, const uint8_t* inp_oob, uint64_t page) +{ + struct brcmnand_chip *chip = (struct brcmnand_chip*)mtd->priv; + int eccstep; + int dataWritten = 0; + int oobWritten = 0; + int ret = 0; + uint64_t offset = page << chip->page_shift; + + int submitted = 0; + unsigned long flags; + + if (gdebug > 3 ) { + printk("-->%s, page=%0llx\n", __FUNCTION__, page); + } + + +#if 0 // No need to check, we are aligned on a page + if (unlikely(offset - sliceOffset)) { + printk(KERN_ERR "%s: offset %0llx is not cache aligned, sliceOffset=%0llx, CacheSize=%d\n", + __FUNCTION__, offset, sliceOffset, ECCSIZE(mtd)); + ret = -EINVAL; + goto out; + } +#endif + + + if (unlikely(!EDU_buffer_OK((volatile void*)inp_buf, EDU_WRITE))) { + if (gdebug > 3) printk("++++++++++++++++++++++++ %s: buffer not 32B aligned, trying non-EDU read\n", __FUNCTION__); + /* EDU does not work on non-aligned buffers */ + ret = brcmnand_write_page(mtd, inp_buf, inp_oob, page); + return (ret); + } + + chip->pagebuf = page; + + spin_lock_irqsave(&gJobQ.lock, flags); + if (!list_empty(&gJobQ.jobQ)) { + printk("%s: Start read page but job queue not empty\n", __FUNCTION__); + BUG(); + } + gJobQ.cmd = EDU_WRITE; + gJobQ.needWakeUp = 0; + + + for (eccstep = 0; eccstep < chip->eccsteps && ret == 0; eccstep++) { + eduIsrNode_t* req; + /* + * Queue the 512B sector read, then read the EDU pending bit, + * and issue read command, if EDU is available for read. + */ + req = ISR_queue_write_request(mtd, &inp_buf[dataWritten], + inp_oob ? &inp_oob[oobWritten] : NULL, + offset + dataWritten); + + dataWritten += chip->eccsize; + oobWritten += chip->eccOobSize; + } + + + /* + * Kick start it. The ISR will submit the next job + */ + if (!submitted) { + submitted = brcmnand_isr_submit_job(); + } + + while (!list_empty(&gJobQ.jobQ)) { + spin_unlock_irqrestore(&gJobQ.lock, flags); + ret = ISR_wait_for_queue_completion(); + if (ret) { + dataWritten = 0; + } + spin_lock_irqsave(&gJobQ.lock, flags); + } + spin_unlock_irqrestore(&gJobQ.lock, flags); + return ret; + +} + +/* + * Queue the several pages, then wait for completion + * For 512B page sizes only. + */ +static int +brcmnand_isr_write_pages(struct mtd_info *mtd, + const uint8_t *inp_buf, const uint8_t* inp_oob, uint64_t startPage, int numPages) +{ + struct brcmnand_chip *chip = (struct brcmnand_chip*)mtd->priv; + int dataWritten = 0; + int oobWritten = 0; + int ret = 0; + uint64_t offset = startPage << chip->page_shift; + int page; + + int submitted = 0; + unsigned long flags; + +#if 0 + /* Already checked by caller */ + if (unlikely(!EDU_buffer_OK(inp_buf, EDU_WRITE))) { + if (gdebug > 3) printk("++++++++++++++++++++++++ %s: buffer not 32B aligned, trying non-EDU read\n", __FUNCTION__); + /* EDU does not work on non-aligned buffers */ + ret = brcmnand_write_page(mtd, inp_buf, inp_oob, startPage); + return (ret); + } +#endif + /* Paranoia */ + if (chip->pageSize != chip->eccsize) { + printk("%s: Can only be called on small page flash\n", __FUNCTION__); + BUG(); + } + + spin_lock_irqsave(&gJobQ.lock, flags); + if (!list_empty(&gJobQ.jobQ)) { + printk("%s: Start read page but job queue not empty\n", __FUNCTION__); + BUG(); + } + gJobQ.cmd = EDU_WRITE; + gJobQ.needWakeUp = 0; + +//gdebug=4; + for (page = 0; page < numPages && ret == 0; page++) { + eduIsrNode_t* req; + /* + * Queue the 512B sector read, then read the EDU pending bit, + * and issue read command, if EDU is available for read. + */ + + req = ISR_queue_write_request(mtd, &inp_buf[dataWritten], + inp_oob ? &inp_oob[oobWritten] : NULL, + offset + dataWritten); + + dataWritten += chip->eccsize; + oobWritten += chip->eccOobSize; + } +//gdebug=0; + + + /* + * Kick start it. The ISR will submit the next job + * We do it here, in order to avoid having to obtain the queue lock + * inside the ISR, in preparation for an RCU implementation. + */ + if (!submitted) { + submitted = brcmnand_isr_submit_job(); + } + + while (!list_empty(&gJobQ.jobQ)) { + spin_unlock_irqrestore(&gJobQ.lock, flags); + ret = ISR_wait_for_queue_completion(); + if (ret) { + dataWritten = 0; + } + spin_lock_irqsave(&gJobQ.lock, flags); + } + spin_unlock_irqrestore(&gJobQ.lock, flags); + + + return ret; + +} + + +#endif + + + +/** + * brcmnand_fill_oob - [Internal] Transfer client buffer to oob + * @chip: nand chip structure + * @oob: oob data buffer + * @ops: oob ops structure + * + * Returns the pointer to the OOB where next byte should be written to + */ +uint8_t * +brcmnand_fill_oob(struct brcmnand_chip *chip, uint8_t *oob, struct mtd_oob_ops *ops) +{ + // Already written in previous passes, relying on oob being intialized to ops->oobbuf + size_t writtenLen = oob - ops->oobbuf; + size_t len = ops->ooblen - writtenLen; + + + switch (ops->mode) { + + case MTD_OPS_PLACE_OOB: + case MTD_OPS_RAW: + memcpy(chip->oob_poi + ops->ooboffs, oob, len); + return oob + len; + + case MTD_OPS_AUTO_OOB: { + struct nand_oobfree *free = chip->ecclayout->oobfree; + uint32_t boffs = 0, woffs = ops->ooboffs; + size_t bytes = 0; + + memset(chip->oob_poi + ops->ooboffs, 0xff, chip->eccOobSize - ops->ooboffs); + + for (; free->length && len; free++, len -= bytes) { + /* Write request not from offset 0 ? */ + if (unlikely(woffs)) { + if (woffs >= free->length) { + woffs -= free->length; + continue; + } + boffs = free->offset + woffs; + bytes = min_t(size_t, len, + (free->length - woffs)); + woffs = 0; + } else { + bytes = min_t(size_t, len, free->length); + boffs = free->offset; + } + memcpy(chip->oob_poi + boffs, oob, bytes); + oob += bytes; + } + return oob; + } + default: + BUG(); + } + return NULL; +} + + +#define NOTALIGNED(x) ((int)(x & (mtd->writesize - 1)) != 0) + +/** + * brcmnand_do_write_ops - [Internal] BRCMNAND write with ECC + * @mtd: MTD device structure + * @to: offset to write to + * @ops: oob operations description structure + * + * BRCMNAND write with ECC + */ +static int brcmnand_do_write_ops(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + uint64_t realpage; + int blockmask; + struct brcmnand_chip *chip = mtd->priv; + uint32_t writelen = ops->len; + uint8_t *oob = ops->oobbuf; //brcmnand_fill_oob relies on this + uint8_t *buf = ops->datbuf; + int bytes = mtd->writesize; + int ret = 0; + int numPages; + int buffer_aligned = 0; + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + uint8_t oobarea[512]; + int read_oob = 0; + if ( !oob && + ((chip->options & NAND_COMPLEX_OOB_WRITE) != 0 || + (chip->ecclevel >= BRCMNAND_ECC_BCH_1 && + chip->ecclevel <= BRCMNAND_ECC_BCH_12)) ) { + read_oob = 1; + oob = (uint8_t*)((uintptr_t)(oobarea + 0x0f) & ~0x0f); + brcmnand_read_page_oob(mtd, oob, to >> chip->page_shift); + ops->mode = MTD_OPS_PLACE_OOB; + ops->ooboffs = 0; + ops->ooblen = chip->eccsteps * chip->eccOobSize; + ops->oobbuf = oob; + } +#endif + + DEBUG(MTD_DEBUG_LEVEL3, "-->%s, offset=%0llx\n", __FUNCTION__, to); + + ops->retlen = 0; + + /* reject writes, which are not page aligned */ + if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { + printk(KERN_NOTICE "nand_write: " + "Attempt to write not page aligned data\n"); + return -EINVAL; + } + + if (!writelen) + return 0; + +/* BrcmNAND multi-chips are treated as one logical chip * + chipnr = (int)(to >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + */ + + + + realpage = to >> chip->page_shift; + //page = realpage & chip->pagemask; + blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; + + /* Invalidate the page cache, when we write to the cached page */ + if ((chip->pagebuf != -1LL) && + (to <= (chip->pagebuf << chip->page_shift)) && + ((to + ops->len) > (chip->pagebuf << chip->page_shift) )) { + chip->pagebuf = -1LL; + } + + /* THT: Provide buffer for brcmnand_fill_oob */ + if (unlikely(oob)) { + chip->oob_poi = BRCMNAND_OOBBUF(chip->ctrl->buffers); + }else { + chip->oob_poi = NULL; + } + +#ifdef CONFIG_MTD_BRCMNAND_ISR_QUEUE + /* Buffer must be aligned for EDU */ + buffer_aligned = EDU_buffer_OK(buf, EDU_WRITE); + +#else /* Dont care */ + buffer_aligned = 0; +#endif + + while (1) { + +#ifdef CONFIG_MTD_BRCMNAND_ISR_QUEUE + /* + * Group several pages for submission for small page NAND + */ + numPages = min(MAX_JOB_QUEUE_SIZE, (int)writelen >> chip->page_shift); + + // If Batch mode + if (buffer_aligned && numPages > 1 && chip->pageSize == chip->eccsize) { + int j; + + /* Submit min(queueSize, len/512B) at a time */ + //numPages = min(MAX_JOB_QUEUE_SIZE, writelen>>chip->page_shift); + bytes = chip->eccsize * numPages; + + if (unlikely(oob)) { + //u_char* newoob; + for (j = 0; j < numPages; j++) { + oob = brcmnand_fill_oob(chip, oob, ops); + /* THT: oob now points to where to read next, + * chip->oob_poi contains the OOB to be written + */ + /* In batch mode, we advance the OOB pointer to the next OOB slot + * using chip->oob_poi + */ + chip->oob_poi += chip->eccOobSize; + } + // Reset chip->oob_poi to beginning of OOB buffer for submission. + chip->oob_poi = BRCMNAND_OOBBUF(chip->ctrl->buffers); + } + + ret = brcmnand_isr_write_pages(mtd, buf, chip->oob_poi, realpage, numPages); + if (ret) { + ops->retlen = 0; + return ret; + } + + }else /* Else submit one page at a time */ + +#endif + /* Submit one page at a time */ + { + numPages = 1; + bytes = mtd->writesize; + + if (unlikely(oob)) { + chip->oob_poi = BRCMNAND_OOBBUF(chip->ctrl->buffers); + oob = brcmnand_fill_oob(chip, oob, ops); + /* THT: oob now points to where to read next, + * chip->oob_poi contains the OOB to be written + */ + } + + ret = chip->write_page(mtd, buf, chip->oob_poi, realpage); + + } + + if (ret) + break; + + writelen -= bytes; + if (!writelen) + break; + + buf += bytes; + realpage += numPages; + } + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + if ( read_oob ) { + ops->ooboffs = 0; + ops->ooblen = 0; + ops->oobbuf = NULL; + } +#endif + + ops->retlen = ops->len - writelen; + if (unlikely(oob)) + ops->oobretlen = ops->ooblen; + DEBUG(MTD_DEBUG_LEVEL3, "<-- %s\n", __FUNCTION__); + return ret; +} + + +/** + * BRCMnand_write - [MTD Interface] brcmNAND write with ECC + * @mtd: MTD device structure + * @to: offset to write to + * @len: number of bytes to write + * @retlen: pointer to variable to store the number of written bytes + * @buf: the data to write + * + * BRCMNAND write with ECC + */ +static int brcmnand_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const uint8_t *buf) +{ + struct brcmnand_chip *chip = mtd->priv; + int ret; + + DEBUG(MTD_DEBUG_LEVEL3, "%s: to=%0llx\n", __FUNCTION__, to); + + if (gdebug > 3 ) { + printk("-->%s, offset=%0llx\n", __FUNCTION__, to); + } + + if ( kerSysIsDyingGaspTriggered() ) { + printk("system is losing power, abort nand write offset %lx len %x,\n", (unsigned long)to, (unsigned int)len); + return -EINVAL; + } + + + /* Do not allow writes past end of device */ + if (unlikely((to + len) > device_size(mtd))) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to write beyond end of device\n", + __FUNCTION__); + printk("brcmnand_write Attempt to write beyond end of device to 0x%x, len 0x%x, size of device 0x%x\n", (int)to, (unsigned int)len, (int)device_size(mtd) ); + } + if (!len) + return 0; + + brcmnand_get_device(mtd, BRCMNAND_FL_WRITING); + + chip->ops.len = len; + chip->ops.datbuf = (uint8_t*)buf; + chip->ops.oobbuf = NULL; + + ret = brcmnand_do_write_ops(mtd, to, &chip->ops); + + *retlen = chip->ops.retlen; + + brcmnand_release_device(mtd); + return ret; +} + + +/** + * brcmnand_write_page_oob - [INTERNAL] write one page + * @mtd: MTD device structure + * @chip: NAND chip descriptor. The oob_poi pointer points to the OOB buffer. + * @page: page number to write + */ +static int brcmnand_write_page_oob(struct mtd_info *mtd, + const uint8_t* inp_oob, uint64_t page, int isFromMarkBadBlock) +{ + struct brcmnand_chip *chip = (struct brcmnand_chip*)mtd->priv; + int eccstep; + int oobWritten = 0; + int ret = 0; + uint64_t offset = page << chip->page_shift; + + chip->pagebuf = page; + + for (eccstep = 0; eccstep < chip->eccsteps && ret == 0; eccstep++) { + ret = brcmnand_posted_write_oob(mtd, &inp_oob[oobWritten], + offset, isFromMarkBadBlock); +//gdebug=0; + if (ret < 0) { + printk(KERN_ERR "%s: brcmnand_posted_write_oob failed at offset=%0llx, ret=%d\n", + __FUNCTION__, offset, ret); + return ret; + } + offset = offset + chip->eccsize; + oobWritten += chip->eccOobSize; + } + + // TBD + ret = brcmnand_verify_pageoob(); + + if (gdebug > 3 ) { + printk("<--%s offset=%0llx\n", __FUNCTION__, page << chip->page_shift); + print_oobbuf(inp_oob, mtd->oobsize); + } + return ret; +} + + +/** + * brcmnand_do_write_oob - [Internal] BrcmNAND write out-of-band + * @mtd: MTD device structure + * @to: offset to write to + * @ops: oob operation description structure + * + * BrcmNAND write out-of-band, no data. + */ +static int +brcmnand_do_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) +{ +#if !defined(CONFIG_BCM_KF_MTD_BCMNAND) + int numPages; +#endif + int page; + int status = 0; + struct brcmnand_chip *chip = mtd->priv; + u_char* oob = ops->oobbuf;; + + DEBUG(MTD_DEBUG_LEVEL3, "%s: to = 0x%08x, len = %i\n", __FUNCTION__, + (unsigned int)to, (int)ops->len); + if (gdebug > 3 ) { + printk("-->%s, to=%08x, len=%d\n", __FUNCTION__, (uint32_t)to, (int)ops->len); + } + + /* Do not allow write past end of page */ + if ((ops->ooboffs + ops->len) > mtd->oobsize) { + DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " + "Attempt to write past end of page\n"); + return -EINVAL; + } + +/* BrcmNAND treats multiple chips as a single logical chip + chipnr = (int)(to >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + */ + + /* Shift to get page */ + page = to >> chip->page_shift; + + /* Invalidate the page cache, if we write to the cached page */ + if ((int64_t)page == chip->pagebuf) + chip->pagebuf = -1LL; + +/* The #else case executes in an infinite loop. */ +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + if (unlikely(oob)) { + chip->oob_poi = BRCMNAND_OOBBUF(chip->ctrl->buffers); + memset(chip->oob_poi, 0xff, mtd->oobsize); + oob = brcmnand_fill_oob(chip, oob, ops); + /* THT: oob now points to where to read next, + * chip->oob_poi contains the OOB to be written + */ + } + + status = chip->write_page_oob(mtd, chip->oob_poi, page, 0); +#else + while (1) { + /* Submit one page at a time */ + + numPages = 1; + + if (unlikely(oob)) { + chip->oob_poi = BRCMNAND_OOBBUF(chip->ctrl->buffers); + memset(chip->oob_poi, 0xff, mtd->oobsize); + oob = brcmnand_fill_oob(chip, oob, ops); + /* THT: oob now points to where to read next, + * chip->oob_poi contains the OOB to be written + */ + } + + status |= chip->write_page_oob(mtd, chip->oob_poi, page, 0); + + if (status) + break; + + page += numPages; + } // Write 1 page OOB +#endif + + if (status) + return status; + + ops->oobretlen = ops->ooblen; + + return 0; +} + +/** + * brcmnand_write_oob - [MTD Interface] BrcmNAND write data and/or out-of-band + * @mtd: MTD device structure + * @to: offset to write to + * @ops: oob operation description structure + */ +static int +brcmnand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) +{ + struct brcmnand_chip *chip = mtd->priv; + int ret = -ENOTSUPP; + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + if ((chip->nop == 1) && ops->ooblen && !ops->len) { // quit if writing OOB only to NOP=1 parallel NAND device + ops->retlen = 0; + ops->oobretlen = ops->ooblen; + return(0); + } +#endif + DEBUG(MTD_DEBUG_LEVEL3, "%s: to=%0llx\n", __FUNCTION__, to); + + if (gdebug > 3 ) { + printk("-->%s, offset=%0llx, len=%08x\n", __FUNCTION__, to, (int)ops->len); + } + + if ( kerSysIsDyingGaspTriggered() ) { + printk("system is losing power, abort nand write oob offset %lx\n", (unsigned long)to); + return -EINVAL; + } + + ops->retlen = 0; +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + if (!ops->datbuf) { + ops->len = ops->ooblen; + } +#endif + + /* Do not allow writes past end of device */ + + if (unlikely((to + ops->len) > device_size(mtd))) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to write beyond end of device\n", + __FUNCTION__); + printk("brcmnand_write_oob Attempt to write beyond end of device to 0x%x, len 0x%x, size of device 0x%x\n", (int)to, (unsigned int)ops->len, (int)device_size(mtd) ); + return -EINVAL; + } + + brcmnand_get_device(mtd, BRCMNAND_FL_WRITING); + + + if (!ops->datbuf) + ret = brcmnand_do_write_oob(mtd, to, ops); + else + ret = brcmnand_do_write_ops(mtd, to, ops); + +#if 0 + if (unlikely(ops->mode == MTD_OPS_RAW)) + chip->ecc.write_page = write_page; +#endif + + //out: + brcmnand_release_device(mtd); + return ret; +} + +/** + * brcmnand_writev - [MTD Interface] compabilty function for brcmnand_writev_ecc + * @param mtd MTD device structure + * @param vecs the iovectors to write + * @param count number of vectors + * @param to offset to write to + * @param retlen pointer to variable to store the number of written bytes + * + * BrcmNAND write with kvec. + */ +static int brcmnand_writev(struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t *retlen) +{ + int i, len, total_len, ret = -EIO, written = 0, buflen; + uint32_t page; + int numpages = 0; + struct brcmnand_chip * chip = mtd->priv; + //int ppblock = (1 << (chip->phys_erase_shift - chip->page_shift)); + u_char *bufstart = NULL; + //u_char tmp_oob[NAND_MAX_OOBSIZE]; + u_char *data_buf; + + + if (gdebug > 3 ) { + printk("-->%s, offset=%08x\n", __FUNCTION__, (uint32_t)to); + } + + if ( kerSysIsDyingGaspTriggered() ) + return -EINVAL; + + + /* Preset written len for early exit */ + *retlen = 0; + + /* Calculate total length of data */ + total_len = 0; + for (i = 0; i < count; i++) + total_len += vecs[i].iov_len; + + DEBUG(MTD_DEBUG_LEVEL3, "%s: to = 0x%08x, len = %i, count = %ld, eccbuf=%p, total_len=%d\n", + __FUNCTION__, (unsigned int)to, (unsigned int)total_len, count, NULL, total_len); + + /* Do not allow write past end of the device */ + + + if (unlikely((to + total_len) > device_size(mtd))) { + DEBUG(MTD_DEBUG_LEVEL0, "brcmnand_writev_ecc: Attempted write past end of device\n"); + return -EINVAL; + } + + /* Reject writes, which are not page aligned */ + if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(total_len))) { + DEBUG(MTD_DEBUG_LEVEL0, "brcmnand_writev_ecc: Attempt to write data not aligned to page\n"); + return -EINVAL; + } + + /* Grab the lock and see if the device is available */ + brcmnand_get_device(mtd, BRCMNAND_FL_WRITING); + + /* Setup start page, we know that to is aligned on page boundary */ + page = to > chip->page_shift; + + data_buf = BRCMNAND_malloc(sizeof(u_char) * mtd->writesize); + if (unlikely(data_buf == NULL)) { + printk(KERN_ERR "%s: vmalloc failed\n", __FUNCTION__); + return -ENOMEM; + } + /* Loop until all keve's data has been written */ + len = 0; // How many data from current iovec has been written + written = 0; // How many bytes have been written so far in all + buflen = 0; // How many bytes from the buffer has been copied to. + while (count) { + /* If the given tuple is >= pagesize then + * write it out from the iov + */ + // THT: We must also account for the partial buffer left over from previous iovec + if ((buflen + vecs->iov_len - len) >= mtd->writesize) { + /* Calc number of pages we can write + * out of this iov in one go */ + numpages = (buflen + vecs->iov_len - len) >> chip->page_shift; + + + //oob = 0; + for (i = 0; i < numpages; i++) { + if (0 == buflen) { // If we start a new page + bufstart = &((u_char*)vecs->iov_base)[len]; + }else { // Reuse existing partial buffer, partial refill to end of page + memcpy(&bufstart[buflen], &((u_char*)vecs->iov_base)[len], mtd->writesize - buflen); + } + + ret = chip->write_page(mtd, bufstart, NULL, page); + bufstart = NULL; + + if (ret) { + printk("%s: brcmnand_write_page failed, ret=%d\n", __FUNCTION__, ret); + goto out; + } + len += mtd->writesize - buflen; + buflen = 0; + //oob += oobretlen; + page++; + written += mtd->writesize; + } + /* Check, if we have to switch to the next tuple */ + if (len >= (int)vecs->iov_len) { + vecs++; + len = 0; + count--; + } + } else { // (vecs->iov_len - len) < mtd->writesize) + /* + * We must use the internal buffer, read data out of each + * tuple until we have a full page to write + */ + + + /* + * THT: Changed to use memcpy which is more efficient than byte copying, does not work yet + * Here we know that 0 < vecs->iov_len - len < mtd->writesize, and len is not necessarily 0 + */ + // While we have iovec to write and a partial buffer to fill + while (count && (buflen < mtd->writesize)) { + + // Start new buffer? + if (0 == buflen) { + bufstart = data_buf; + } + if (vecs->iov_base != NULL && (vecs->iov_len - len) > 0) { + // We fill up to the page + int fillLen = min_t(int, vecs->iov_len - len, mtd->writesize - buflen); + + memcpy(&data_buf[buflen], &((u_char*)vecs->iov_base)[len], fillLen); + buflen += fillLen; + len += fillLen; + } + /* Check, if we have to switch to the next tuple */ + if (len >= (int)vecs->iov_len) { + vecs++; + len = 0; + count--; + } + + } + // Write out a full page if we have enough, otherwise loop back to the top + if (buflen == mtd->writesize) { + + numpages = 1; + + ret = chip->write_page(mtd, bufstart, NULL, page); + if (ret) { + printk("%s: brcmnand_write_page failed, ret=%d\n", __FUNCTION__, ret); + goto out; + } + page++; + written += mtd->writesize; + + bufstart = NULL; + buflen = 0; + } + } + + /* All done ? */ + if (!count) { + if (buflen) { // Partial page left un-written. Imposible, as we check for totalLen being multiple of pageSize above. + printk("%s: %d bytes left unwritten with writev_ecc at offset %0llx\n", + __FUNCTION__, buflen, ((uint64_t)page) << chip->page_shift); + BUG(); + } + break; + } + + } + ret = 0; + out: + /* Deselect and wake up anyone waiting on the device */ + brcmnand_release_device(mtd); + + BRCMNAND_free(data_buf); + *retlen = written; +//if (*retlen <= 0) printk("%s returns retlen=%d, ret=%d, startAddr=%08x\n", __FUNCTION__, *retlen, ret, startAddr); +//printk("<-- %s: retlen=%d\n", __FUNCTION__, *retlen); + return ret; +} + +#if 0 +/** + * brcmnand_block_bad - [DEFAULT] Read bad block marker from the chip + * @mtd: MTD device structure + * @ofs: offset from device start + * @getchip: 0, if the chip is already selected + * + * Check, if the block is bad. + */ +static int brcmnand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) +{ + int res = 0, ret = 0; + uint32_t page; + struct brcmnand_chip *chip = mtd->priv; + u16 bad; + uint8_t oob[NAND_MAX_OOBSIZE]; + + //uint8_t* saved_poi; + + if (getchip) { + page = __ll_RightShift(ofs, chip->page_shift); + +#if 0 + chipnr = (int)(ofs >> chip->chip_shift); +#endif + + brcmnand_get_device(mtd, BRCMNAND_FL_READING); + +#if 0 + /* Select the NAND device */ + chip->select_chip(mtd, chipnr); +#endif + } + page = __ll_RightShift(ofs, chip->page_shift); + + ret = chip->read_page_oob(mtd, oob, page); + if (ret) { + return 1; + } + + if (chip->options & NAND_BUSWIDTH_16) { + bad = (u16)cpu_to_le16(*(uint16*)(oob[chip->badblockpos])); + if (chip->badblockpos & 0x1) + bad >>= 8; + if ((bad & 0xFF) != 0xff) + res = 1; + } else { + if (oob[chip->badblockpos] != 0xff) + res = 1; + } + + if (getchip) + brcmnand_release_device(mtd); + + return res; +} +#endif + + +/** + * brcmnand_block_checkbad - [GENERIC] Check if a block is marked bad + * @param mtd MTD device structure + * @param ofs offset from device start + * @param getchip 0, if the chip is already selected + * @param allowbbt 1, if its allowed to access the bbt area + * + * Check, if the block is bad. Either by reading the bad block table or + * calling of the scan function. + */ +static int brcmnand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt) +{ + struct brcmnand_chip * chip = mtd->priv; + int res; + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + if (ofs < mtd->erasesize) + return(0); +#endif + if (getchip) { + brcmnand_get_device(mtd, BRCMNAND_FL_READING); + } + + // BBT already initialized + if (chip->isbad_bbt) { + + /* Return info from the table */ + res = chip->isbad_bbt(mtd, ofs, allowbbt); + }else { + res = brcmnand_isbad_raw(mtd, ofs); + } + + if (getchip) { + brcmnand_release_device(mtd); + } + +// if (res) PRINTK("%s: on Block at %0llx, ret=%d\n", __FUNCTION__, ofs, res); + + return res; +} + +#ifdef CONFIG_MTD_BRCMNAND_CORRECTABLE_ERR_HANDLING +/** + * brcmnand_erase_nolock - [Private] erase block(s) + * @param mtd MTD device structure + * @param instr erase instruction + * @allowBBT allow erase of BBT + * + * Erase one ore more blocks + * ** FIXME ** This code does not work for multiple chips that span an address space > 4GB + * Similar to BBT, except does not use locks and no alignment checks + * Assumes lock held by caller + */ +static int brcmnand_erase_nolock(struct mtd_info *mtd, struct erase_info *instr, int allowbbt) +{ + struct brcmnand_chip * chip = mtd->priv; + unsigned int block_size; + loff_t addr; + int len; + int ret = 0; + int needBBT; + + block_size = (1 << chip->erase_shift); + instr->fail_addr = 0xffffffffffffffffULL; + + /* Clear ECC registers */ + chip->ctrl_write(BCHP_NAND_ECC_CORR_ADDR, 0); + chip->ctrl_write(BCHP_NAND_ECC_UNC_ADDR, 0); +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_1_0 + chip->ctrl_write(BCHP_NAND_ECC_CORR_EXT_ADDR, 0); + chip->ctrl_write(BCHP_NAND_ECC_UNC_EXT_ADDR, 0); +#endif + + /* Loop throught the pages */ + len = instr->len; + addr = instr->addr; + instr->state = MTD_ERASING; + + while (len) { + /* Check if we have a bad block, we do not erase bad blocks */ + if (brcmnand_block_checkbad(mtd, addr, 0, allowbbt)) { + printk(KERN_ERR "%s: attempt to erase a bad block at addr 0x%08x\n", __FUNCTION__, (unsigned int)addr); + instr->state = MTD_ERASE_FAILED; + goto erase_one_block; + } + chip->ctrl_writeAddr(chip, addr, 0); + chip->ctrl_write(BCHP_NAND_CMD_START, OP_BLOCK_ERASE); + + /* Wait until flash is ready */ + ret = brcmnand_ctrl_write_is_complete(mtd, &needBBT); + + /* Check, if it is write protected: TBD */ + if (needBBT ) { + if ( !allowbbt) { + printk(KERN_ERR "brcmnand_erase: Failed erase, block %d, flash status=%08x\n", + (unsigned int)(addr >> chip->erase_shift), needBBT); + instr->state = MTD_ERASE_FAILED; + instr->fail_addr = addr; + printk(KERN_WARNING "%s: Marking bad block @%08x\n", __FUNCTION__, (unsigned int)addr); + (void)chip->block_markbad(mtd, addr); + goto erase_one_block; + } + } + erase_one_block: + len -= block_size; + addr = addr + block_size; + } + + instr->state = MTD_ERASE_DONE; + ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; + /* Do call back function */ + if (!ret) { + mtd_erase_callback(instr); + } + + return ret; +} +#endif + + + +/** + * brcmnand_erase_bbt - [Private] erase block(s) + * @param mtd MTD device structure + * @param instr erase instruction + * @allowBBT allow erase of BBT + * @doNotUseBBT Do not look up in BBT + * + * Erase one ore more blocks + * ** FIXME ** This code does not work for multiple chips that span an address space > 4GB + */ +static int +brcmnand_erase_bbt(struct mtd_info *mtd, struct erase_info *instr, int allowbbt, int doNotUseBBT) +{ + struct brcmnand_chip * chip = mtd->priv; + unsigned int block_size; + loff_t addr; + int len; + int ret = 0; + int needBBT; + + + + DEBUG(MTD_DEBUG_LEVEL3, "%s: start = %0llx, len = %08x\n", __FUNCTION__, + instr->addr, (unsigned int)instr->len); +//PRINTK( "%s: start = 0x%08x, len = %08x\n", __FUNCTION__, (unsigned int) instr->addr, (unsigned int) instr->len); + + block_size = (1 << chip->erase_shift); + + + /* Start address must align on block boundary */ + if (unlikely(instr->addr & (block_size - 1))) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Unaligned address\n", __FUNCTION__); +//if (gdebug > 3 ) + { printk( "%s: Unaligned address\n", __FUNCTION__); } + return -EINVAL; + } + + /* Length must align on block boundary */ + if (unlikely(instr->len & (block_size - 1))) { + DEBUG(MTD_DEBUG_LEVEL0, + "%s: Length not block aligned, len=%08x, blocksize=%08x, chip->blkSize=%08x, chip->erase=%d\n", + __FUNCTION__, (unsigned int)instr->len, (unsigned int)block_size, + (unsigned int)chip->blockSize, chip->erase_shift); + PRINTK( + "%s: Length not block aligned, len=%08x, blocksize=%08x, chip->blkSize=%08x, chip->erase=%d\n", + __FUNCTION__, (unsigned int)instr->len, (unsigned int)block_size, + (unsigned int)chip->blockSize, chip->erase_shift); + return -EINVAL; + } + + /* Do not allow erase past end of device */ + if (unlikely((instr->len + instr->addr) > device_size(mtd))) { + + DEBUG(MTD_DEBUG_LEVEL0, "%s: Erase past end of device\n", __FUNCTION__); +//if (gdebug > 3 ) + { printk(KERN_WARNING "%s: Erase past end of device, instr_addr=%016llx, instr->len=%08x, mtd->size=%16llx\n", + __FUNCTION__, (unsigned long long)instr->addr, + (unsigned int)instr->len, device_size(mtd)); } + + return -EINVAL; + } + + + instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; + + /* + * Clear ECC registers + */ + chip->ctrl_write(BCHP_NAND_ECC_CORR_ADDR, 0); + chip->ctrl_write(BCHP_NAND_ECC_UNC_ADDR, 0); + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_1_0 + chip->ctrl_write(BCHP_NAND_ECC_CORR_EXT_ADDR, 0); + chip->ctrl_write(BCHP_NAND_ECC_UNC_EXT_ADDR, 0); +#endif + + /* Loop throught the pages */ + len = instr->len; + addr = instr->addr; + instr->state = MTD_ERASING; + + while (len) { + + +/* THT: We cannot call brcmnand_block_checkbad which just look at the BBT, + // since this code is also called when we create the BBT. + // We must look at the actual bits, or have a flag to tell the driver + // to read the BI directly from the OOB, bypassing the BBT + */ + /* Check if we have a bad block, we do not erase bad blocks */ + if (brcmnand_block_checkbad(mtd, addr, 0, allowbbt)) { + printk(KERN_ERR "%s: attempt to erase a bad block at addr 0x%08x\n", __FUNCTION__, (unsigned int)addr); + // THT I believe we should allow the erase to go on to the next block in this case. + instr->state = MTD_ERASE_FAILED; +//dump_stack(); + goto erase_exit; + } + + //chip->command(mtd, ONENAND_CMD_ERASE, addr, block_size); + + chip->ctrl_writeAddr(chip, addr, 0); + + chip->ctrl_write(BCHP_NAND_CMD_START, OP_BLOCK_ERASE); + + // Wait until flash is ready + ret = brcmnand_ctrl_write_is_complete(mtd, &needBBT); + + /* Check, if it is write protected: TBD */ + if (needBBT ) { + if ( !allowbbt) { + printk(KERN_ERR "brcmnand_erase: Failed erase, block %d, flash status=%08x\n", + (unsigned int)(addr >> chip->erase_shift), needBBT); + + instr->state = MTD_ERASE_FAILED; + instr->fail_addr = addr; + + printk(KERN_WARNING "%s: Marking bad block @%08x\n", __FUNCTION__, (unsigned int)addr); + (void)chip->block_markbad(mtd, addr); + goto erase_exit; + } + } + len -= block_size; + addr = addr + block_size; + } + + instr->state = MTD_ERASE_DONE; + + erase_exit: + + ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; + /* Do call back function */ + if (!ret) { + mtd_erase_callback(instr); + } + + DEBUG(MTD_DEBUG_LEVEL0, "<--%s\n", __FUNCTION__); + return ret; +} + + +/** + * brcmnand_erase - [MTD Interface] erase block(s) + * @param mtd MTD device structure + * @param instr erase instruction + * + * Erase one ore more blocks + */ +static int brcmnand_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct brcmnand_chip * chip = mtd->priv; + int ret = 0; + unsigned int block_size; + int allowbbt = 0; + +#ifdef CONFIG_MTD_BRCMNAND_CORRECTABLE_ERR_HANDLING + loff_t addr; + int len; + + /* save the instr len and addr first because JFFS2 caller free instr when instr->callback is called */ + len = instr->len; + addr = instr->addr; + + +#endif + + if ( kerSysIsDyingGaspTriggered() ) { + printk("system is losing power, abort nand erase offset %lx len %x,\n", (unsigned long)instr->addr, (int)instr->len); + return -EINVAL; + } + + DEBUG(MTD_DEBUG_LEVEL3, "-->%s addr=%08lx, len=%d\n", __FUNCTION__, + (unsigned long)instr->addr, (int)instr->len); + + /* Grab the lock and see if the device is available */ + brcmnand_get_device(mtd, BRCMNAND_FL_ERASING); + + block_size = (1 << chip->erase_shift); + + ret = brcmnand_erase_bbt(mtd, instr, allowbbt, 0); // Do not allow erase of BBT, and use BBT + + /* Deselect and wake up anyone waiting on the device */ + brcmnand_release_device(mtd); + +#ifdef CONFIG_MTD_BRCMNAND_CORRECTABLE_ERR_HANDLING + if (chip->cet) { + if (chip->cet->flags != BRCMNAND_CET_DISABLED && + chip->cet->flags != BRCMNAND_CET_LAZY && allowbbt != 1) { + while (len) { + /* Skip if bad block */ + if (brcmnand_block_checkbad(mtd, addr, 0, allowbbt)) { + printk(KERN_ERR "%s: attempt to erase a bad block at addr 0x%08x\n", __FUNCTION__, (unsigned int)addr); + len -= block_size; + addr = addr + block_size; + continue; + } + if (brcmnand_cet_erasecallback(mtd, addr) < 0) { + printk(KERN_INFO "Error in CET erase callback, disabling CET\n"); + chip->cet->flags = BRCMNAND_CET_DISABLED; + } + len -= block_size; + addr = addr + block_size; + } + } + } +#endif + return ret; +} + +/** + * brcmnand_sync - [MTD Interface] sync + * @param mtd MTD device structure + * + * Sync is actually a wait for chip ready function + */ +static void brcmnand_sync(struct mtd_info *mtd) +{ + DEBUG(MTD_DEBUG_LEVEL3, "brcmnand_sync: called\n"); + + /* Grab the lock and see if the device is available */ + brcmnand_get_device(mtd, BRCMNAND_FL_SYNCING); + + PLATFORM_IOFLUSH_WAR(); + + /* Release it and go back */ + brcmnand_release_device(mtd); +} + + +/** + * brcmnand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad + * @param mtd MTD device structure + * @param ofs offset relative to mtd start + * + * Check whether the block is bad + */ +static int brcmnand_block_isbad(struct mtd_info *mtd, loff_t ofs) +{ + //struct brcmnand_chip * chip = mtd->priv; + + DEBUG(MTD_DEBUG_LEVEL3, "-->%s ofs=%0llx\n", __FUNCTION__, ofs); + + /* Check for invalid offset */ + if (ofs > device_size(mtd)) { + return -EINVAL; + } + + return brcmnand_block_checkbad(mtd, ofs, 1, 0); +} + +/** + * brcmnand_default_block_markbad - [DEFAULT] mark a block bad + * @param mtd MTD device structure + * @param ofs offset from device start + * + * This is the default implementation, which can be overridden by + * a hardware specific driver. + */ +static int brcmnand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct brcmnand_chip * chip = mtd->priv; + //struct bbm_info *bbm = chip->bbm; + // THT: 3/20/07: We restrict ourselves to only support x8. + // Revisit this for x16. + u_char bbmarker[1] = { 0 }; // CFE and BBS uses 0x0F, Linux by default uses 0 + //so we can use this to mark the difference + u_char buf[NAND_MAX_OOBSIZE]; + //size_t retlen; + uint32_t block, page; + int dir; + uint64_t ulofs; + int ret; + + DEBUG(MTD_DEBUG_LEVEL3, "-->%s ofs=%0llx\n", __FUNCTION__, ofs); +//printk("-->%s ofs=%0llx\n", __FUNCTION__, ofs); + + // Page align offset + ulofs = ((uint64_t)ofs) & (~chip->page_mask); + + + /* Get block number. Block is guaranteed to be < 2*32 */ + block = (uint32_t)(ulofs >> chip->erase_shift); + + // Block align offset + ulofs = ((uint64_t)block) << chip->erase_shift; + + if (!NAND_IS_MLC(chip)) { // SLC chip, mark first and 2nd page as bad. + printk(KERN_INFO "Mark SLC flash as bad at offset %0llx, badblockpos=%d\n", ofs, chip->badblockpos); + page = block << (chip->erase_shift - chip->page_shift); + dir = 1; + }else { // MLC chip, mark last and previous page as bad. + printk(KERN_INFO "Mark MLC flash as bad at offset %0llx\n", ofs); + page = ((block + 1) << (chip->erase_shift - chip->page_shift)) - 1; + dir = -1; + } + if (chip->bbt) { + chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); + } + + memcpy(buf, ffchars, sizeof(buf)); + memcpy(&buf[chip->badblockpos], bbmarker, sizeof(bbmarker)); + + // Lock already held by caller, so cant call mtd->write_oob directly + ret = chip->write_page_oob(mtd, buf, page, 1); + if (ret) { + printk(KERN_INFO "Mark bad page %d failed with retval=%d\n", page, ret); + } + + // Mark 2nd page as bad, ignoring last write + page += dir; + // Lock already held by caller, so cant call mtd->write_oob directly + DEBUG(MTD_DEBUG_LEVEL3, "%s Calling chip->write_page(page=%08x)\n", __FUNCTION__, page); + ret = chip->write_page_oob(mtd, buf, page, 1); + if (ret) { + printk(KERN_INFO "Mark bad page %d failed with retval=%d\n", page, ret); + } + + /* + * According to the HW guy, even if the write fails, the controller have written + * a 0 pattern that certainly would have written a non 0xFF value into the BI marker. + * + * Ignoring ret. Even if we fail to write the BI bytes, just ignore it, + * and mark the block as bad in the BBT + */ + DEBUG(MTD_DEBUG_LEVEL3, "%s Calling brcmnand_update_bbt(ulofs=%0llx))\n", __FUNCTION__, ulofs); + (void)brcmnand_update_bbt(mtd, ulofs); + //if (!ret) + mtd->ecc_stats.badblocks++; + return ret; +} + +/** + * brcmnand_block_markbad - [MTD Interface] Mark the block at the given offset as bad + * @param mtd MTD device structure + * @param ofs offset relative to mtd start + * + * Mark the block as bad + */ +static int brcmnand_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct brcmnand_chip * chip = mtd->priv; + int ret; + + DEBUG(MTD_DEBUG_LEVEL3, "-->%s ofs=%08x\n", __FUNCTION__, (unsigned int)ofs); + + if ( kerSysIsDyingGaspTriggered() ) + return -EINVAL; + + ret = brcmnand_block_isbad(mtd, ofs); + if (ret) { + /* If it was bad already, return success and do nothing */ + if (ret > 0) + return 0; + return ret; + } + + return chip->block_markbad(mtd, ofs); +} + +/** + * brcmnand_unlock - [MTD Interface] Unlock block(s) + * @param mtd MTD device structure + * @param ofs offset relative to mtd start + * @param len number of bytes to unlock + * + * Unlock one or more blocks + */ +static int brcmnand_unlock(struct mtd_info *mtd, loff_t llofs, uint64_t len) +{ + +#if 0 +// Only Samsung Small flash uses this. + + struct brcmnand_chip * chip = mtd->priv; + int status; + uint64_t blkAddr, ofs = (uint64_t)llofs; + + DEBUG(MTD_DEBUG_LEVEL3, "-->%s llofs=%08x, len=%d\n", __FUNCTION__, + (unsigned long)llofs, (int)len); + + + + /* Block lock scheme */ + for (blkAddr = ofs; blkAddr < (ofs + len); blkAddr = blkAddr + chip->blockSize) { + + /* The following 2 commands share the same CMD_EXT_ADDR, as the block never cross a CS boundary */ + chip->ctrl_writeAddr(chip, blkAddr, 0); + /* Set end block address */ + chip->ctrl_writeAddr(chip, blkAddr + chip->blockSize - 1, 1); + /* Write unlock command */ + chip->ctrl_write(BCHP_NAND_CMD_START, OP_BLOCKS_UNLOCK); + + + /* There's no return value */ + chip->wait(mtd, BRCMNAND_FL_UNLOCKING, &status); + + if (status & 0x0f) + printk(KERN_ERR "block = %0llx, wp status = 0x%x\n", blkAddr, status); + + /* Check lock status */ + chip->ctrl_writeAddr(chip, blkAddr, 0); + chip->ctrl_write(BCHP_NAND_CMD_START, OP_READ_BLOCKS_LOCK_STATUS); + status = chip->ctrl_read(BCHP_NAND_BLOCK_LOCK_STATUS); + //status = chip->read_word(chip->base + ONENAND_REG_WP_STATUS); + + } +#endif + return 0; +} + + +/** + * brcmnand_print_device_info - Print device ID + * @param device device ID + * + * Print device ID + */ +static void brcmnand_print_device_info(brcmnand_chip_Id* chipId, struct mtd_info* mtd) +{ + struct brcmnand_chip * chip = mtd->priv; + int cs = chip->ctrl->CS[chip->csi]; + + printk(KERN_INFO "BrcmNAND mfg %x %x %s %dMB on CS%d\n", + chipId->mafId, chipId->chipId, chipId->chipIdStr, \ + mtd64_ll_low(chip->chipSize >> 20), cs); + + print_config_regs(mtd); + +} + +/* + * Calculate the bit fields FUL_ADR_BYTES, COL_ADR_BYTES and BLK_ADR_BYTES + * without which, Micron flashes - which do not have traditional decode-ID opcode 90H-00H - + * would not work. + * + * @chip: Structure containing the page size, block size, and device size. + * @nand_config: nand_config register with page size, block size, device size already encoded. + * + * returns the updated nand_config register. + */ +uint32_t +brcmnand_compute_adr_bytes(struct brcmnand_chip* chip, uint32_t nand_config) +{ + + uint32_t nbrPages; + uint32_t fulAdrBytes, colAdrBytes, blkAdrBytes, nbrPagesShift; + + colAdrBytes = 2; + + PRINTK("-->%s, chip->chipSize=%llx\n", __FUNCTION__, chip->chipSize); + + nbrPages = (uint32_t)(chip->chipSize >> chip->page_shift); + nbrPagesShift = ffs(nbrPages) - 1; /* = power of 2*/ + blkAdrBytes = (nbrPagesShift + 7) / 8; + + fulAdrBytes = colAdrBytes + blkAdrBytes; + + nand_config &= ~(BCHP_NAND_CONFIG_FUL_ADR_BYTES_MASK + | BCHP_NAND_CONFIG_COL_ADR_BYTES_MASK + | BCHP_NAND_CONFIG_BLK_ADR_BYTES_MASK); + nand_config |= (fulAdrBytes << BCHP_NAND_CONFIG_FUL_ADR_BYTES_SHIFT) + | (colAdrBytes << BCHP_NAND_CONFIG_COL_ADR_BYTES_SHIFT) + | (blkAdrBytes << BCHP_NAND_CONFIG_BLK_ADR_BYTES_SHIFT); + PRINTK("%s: nbrPages=%x, blkAdrBytes=%d, colAdrBytes=%d, nand_config=%08x\n", + __FUNCTION__, nbrPages, blkAdrBytes, colAdrBytes, nand_config); + return nand_config; +} + +/* + * bit 31: 1 = OTP read-only + * v2.1 and earlier: 30: Page Size: 0 = PG_SIZE_512, 1 = PG_SIZE_2KB version + * 28-29: Block size: 3=512K, 1 = 128K, 0 = 16K, 2 = 8K + * 24-27: Device_Size + * 0: 4MB + * 1: 8MB + * 2: 16MB + * 3: 32MB + * 4: 64MB + * 5: 128MB + * 6: 256MB + * 7: 512MB + * 8: 1GB + * 9: 2GB + * 10: 4GB << Hit limit of MTD struct here. + * 11: 8GB + * 12: 16GB + * 13: 32GB + * 14: 64GB + * 15: 128GB + * 23: Dev_Width 0 = Byte8, 1 = Word16 + * v2.1 and earlier:22-19: Reserved + * v2.2 and later: 21:20 page Size + * 18:16: Full Address Bytes + * 15 Reserved + * 14:12 Col_Adr_Bytes + * 11: Reserved + * 10-08 Blk_Adr_Bytes + * 07-00 Reserved + */ + +void +brcmnand_decode_config(struct brcmnand_chip* chip, uint32_t nand_config) +{ + unsigned int chipSizeShift; + unsigned int blk_size_cfg; + unsigned int page_size_cfg; + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_7_1 + uint32_t nand_config_ext = brcmnand_ctrl_read(BCHP_NAND_CONFIG_EXT); + + blk_size_cfg = (nand_config_ext & BCHP_NAND_CONFIG_BLOCK_SIZE_MASK) + >> BCHP_NAND_CONFIG_BLOCK_SIZE_SHIFT; + page_size_cfg = (nand_config_ext & BCHP_NAND_CONFIG_PAGE_SIZE_MASK) + >> BCHP_NAND_CONFIG_PAGE_SIZE_SHIFT; +#else + blk_size_cfg = (nand_config & BCHP_NAND_CONFIG_BLOCK_SIZE_MASK) + >> BCHP_NAND_CONFIG_BLOCK_SIZE_SHIFT; + page_size_cfg = (nand_config & BCHP_NAND_CONFIG_PAGE_SIZE_MASK) + >> BCHP_NAND_CONFIG_PAGE_SIZE_SHIFT; +#endif + //chip->chipSize = (nand_config & 0x07000000) >> (24 - 20); + +#if CONFIG_MTD_BRCMNAND_VERSION < CONFIG_MTD_BRCMNAND_VERS_2_2 + // Version 2.1 or earlier: 2 bit field 28:29 + switch (blk_size_cfg) { + case BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_512KB: + chip->blockSize = 512 << 10; + break; + case BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_8KB: + chip->blockSize = 8 << 10; + break; + case BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_128KB: + chip->blockSize = 128 << 10; + break; + case BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_16KB: + chip->blockSize = 16 << 10; + break; + } +#else + // Version 2.2 or later: 3 bits 28:30 + if (chip->blockSize != (1 << 20)) { + switch (blk_size_cfg) { + #if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_7_1 + case BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_8192KB: + chip->blockSize = 8192 << 10; + break; + case BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_4096KB: + chip->blockSize = 4096 << 10; + break; + #endif + #if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_4_0 + case BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_1024KB: + chip->blockSize = 1024 << 10; + break; + case BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_2048KB: + chip->blockSize = 2048 << 10; + break; + #endif + case BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_256KB: + chip->blockSize = 256 << 10; + break; + case BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_512KB: + chip->blockSize = 512 << 10; + break; + case BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_8KB: + chip->blockSize = 8 << 10; + break; + case BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_128KB: + chip->blockSize = 128 << 10; + break; + case BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_16KB: + chip->blockSize = 16 << 10; + break; + } + } + /* + * 1MB block size: + * Nothing to do, we have already recorded it + */ +#endif + + + chip->erase_shift = ffs(chip->blockSize) - 1; + printk("Block size=%08x, erase shift=%d\n", chip->blockSize, chip->erase_shift); + +#if CONFIG_MTD_BRCMNAND_VERSION < CONFIG_MTD_BRCMNAND_VERS_2_2 + // Version 2.1 or earlier: Bit 30 + switch (page_size_cfg) { + case BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_512: + chip->pageSize = 512; + break; + case BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_2KB: + chip->pageSize = 2048; + break; + } + +#else + // Version 2.2 or later, bits 20:21 + switch (page_size_cfg) { + case BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_512: + chip->pageSize = 512; + break; + case BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_2KB: + chip->pageSize = 2048; + break; + case BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_4KB: + chip->pageSize = 4096; + break; + #if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_7_0 + case BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_8KB: + chip->pageSize = 8192; + break; + #elif CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_3_4 + case BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_8KB: + { + uint32_t ctrlVersion = brcmnand_ctrl_read(BCHP_NAND_REVISION); + + /* Only if the controller supports it: */ + chip->pageSize = 8192; + if (!(ctrlVersion & BCHP_NAND_REVISION_8KB_PAGE_SUPPORT_MASK)) { + printk(KERN_ERR "Un-supported page size 8KB\n"); + BUG(); + } + } + break; + #else /* Version 3.3 or earlier */ + case 3: + printk(KERN_ERR "Un-supported page size\n"); + chip->pageSize = 0; // Let it crash + BUG(); + break; + #endif + + } +#endif + + chip->page_shift = ffs(chip->pageSize) - 1; + chip->page_mask = (1 << chip->page_shift) - 1; + + chipSizeShift = (nand_config & BCHP_NAND_CONFIG_DEVICE_SIZE_MASK) + >> BCHP_NAND_CONFIG_DEVICE_SIZE_SHIFT; + + chip->chipSize = 4ULL << (20 + chipSizeShift); + + chip->busWidth = 1 + ((nand_config & BCHP_NAND_CONFIG_DEVICE_WIDTH_MASK) + >> BCHP_NAND_CONFIG_DEVICE_WIDTH_SHIFT); + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_7_1 + printk(KERN_INFO "NAND Config: Reg=%08x, Config Ext: Reg=%08x, chipSize=%d MB, blockSize=%dK, erase_shift=%x\n", + nand_config, nand_config_ext, mtd64_ll_low(chip->chipSize >> 20), chip->blockSize >> 10, chip->erase_shift); +#else + printk(KERN_INFO "NAND Config: Reg=%08x, chipSize=%d MB, blockSize=%dK, erase_shift=%x\n", + nand_config, mtd64_ll_low(chip->chipSize >> 20), chip->blockSize >> 10, chip->erase_shift); +#endif + printk(KERN_INFO "busWidth=%d, pageSize=%dB, page_shift=%d, page_mask=%08x\n", + chip->busWidth, chip->pageSize, chip->page_shift, chip->page_mask); + +} + +/* + * Adjust timing pattern if specified in chip ID list + * Also take dummy entries, but no adjustments will be made. + */ +static void brcmnand_adjust_timings(struct brcmnand_chip *this, brcmnand_chip_Id* chip) +{ + int csi = this->csi; + int __maybe_unused cs = this->ctrl->CS[this->csi]; + + unsigned long nand_timing1 = this->ctrl_read(bchp_nand_timing1(cs)); + unsigned long nand_timing1_b4; + unsigned long nand_timing2 = this->ctrl_read(bchp_nand_timing2(cs)); + unsigned long nand_timing2_b4; + extern uint32_t gNandTiming1[]; + extern uint32_t gNandTiming2[]; + + + + /* + * Override database values with kernel command line values + */ + if (0 != gNandTiming1[csi] || 0 != gNandTiming2[csi]) { + if (0 != gNandTiming1[csi] ) { + chip->timing1 = gNandTiming1[csi]; + //this->ctrl_write(BCHP_NAND_TIMING_1, gNandTiming1); + } + if (0 != gNandTiming2[csi]) { + chip->timing2 = gNandTiming2[csi]; + //this->ctrl_write(BCHP_NAND_TIMING_2, gNandTiming2); + } + //return; + } + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_4_0 + if ((chip->timing1 == 0) && (chip->timing2 == 0)) { + // if we don't know better, force max read speed + chip->timing1 = 0x00320000; + chip->timing2 = 0x00000004; + } +#endif + // Adjust NAND timings from database or command line + if (chip->timing1) { + nand_timing1_b4 = nand_timing1; + + if (chip->timing1 & BCHP_NAND_TIMING_1_tWP_MASK) { + nand_timing1 &= ~BCHP_NAND_TIMING_1_tWP_MASK; + nand_timing1 |= (chip->timing1 & BCHP_NAND_TIMING_1_tWP_MASK); + } + if (chip->timing1 & BCHP_NAND_TIMING_1_tWH_MASK) { + nand_timing1 &= ~BCHP_NAND_TIMING_1_tWH_MASK; + nand_timing1 |= (chip->timing1 & BCHP_NAND_TIMING_1_tWH_MASK); + } + if (chip->timing1 & BCHP_NAND_TIMING_1_tRP_MASK) { + nand_timing1 &= ~BCHP_NAND_TIMING_1_tRP_MASK; + nand_timing1 |= (chip->timing1 & BCHP_NAND_TIMING_1_tRP_MASK); + } + if (chip->timing1 & BCHP_NAND_TIMING_1_tREH_MASK) { + nand_timing1 &= ~BCHP_NAND_TIMING_1_tREH_MASK; + nand_timing1 |= (chip->timing1 & BCHP_NAND_TIMING_1_tREH_MASK); + } + if (chip->timing1 & BCHP_NAND_TIMING_1_tCS_MASK) { + nand_timing1 &= ~BCHP_NAND_TIMING_1_tCS_MASK; + nand_timing1 |= (chip->timing1 & BCHP_NAND_TIMING_1_tCS_MASK); + } + if (chip->timing1 & BCHP_NAND_TIMING_1_tCLH_MASK) { + nand_timing1 &= ~BCHP_NAND_TIMING_1_tCLH_MASK; + nand_timing1 |= (chip->timing1 & BCHP_NAND_TIMING_1_tCLH_MASK); + } + if (chip->timing1 & BCHP_NAND_TIMING_1_tALH_MASK) { + nand_timing1 &= ~BCHP_NAND_TIMING_1_tALH_MASK; + nand_timing1 |= (chip->timing1 & BCHP_NAND_TIMING_1_tALH_MASK); + } + if (chip->timing1 & BCHP_NAND_TIMING_1_tADL_MASK) { + nand_timing1 &= ~BCHP_NAND_TIMING_1_tADL_MASK; + nand_timing1 |= (chip->timing1 & BCHP_NAND_TIMING_1_tADL_MASK); + } + + + this->ctrl_write(bchp_nand_timing1(cs), nand_timing1); + + if (gdebug > 3 ) { + printk("Adjust timing1: Was %08lx, changed to %08lx\n", nand_timing1_b4, nand_timing1); + } + }else { + printk("timing1 not adjusted: %08lx\n", nand_timing1); + } + + // Adjust NAND timings: + if (chip->timing2) { + nand_timing2_b4 = nand_timing2; + + if (chip->timing2 & BCHP_NAND_TIMING_2_tWB_MASK) { + nand_timing2 &= ~BCHP_NAND_TIMING_2_tWB_MASK; + nand_timing2 |= (chip->timing2 & BCHP_NAND_TIMING_2_tWB_MASK); + } + if (chip->timing2 & BCHP_NAND_TIMING_2_tWHR_MASK) { + nand_timing2 &= ~BCHP_NAND_TIMING_2_tWHR_MASK; + nand_timing2 |= (chip->timing2 & BCHP_NAND_TIMING_2_tWHR_MASK); + } + if (chip->timing2 & BCHP_NAND_TIMING_2_tREAD_MASK) { + nand_timing2 &= ~BCHP_NAND_TIMING_2_tREAD_MASK; + nand_timing2 |= (chip->timing2 & BCHP_NAND_TIMING_2_tREAD_MASK); + } + + this->ctrl_write(bchp_nand_timing2(cs), nand_timing2); + + if (gdebug > 3 ) { + printk("Adjust timing2: Was %08lx, changed to %08lx\n", nand_timing2_b4, nand_timing2); + } + }else { + printk("timing2 not adjusted: %08lx\n", nand_timing2); + } +} + + +#if CONFIG_MTD_BRCMNAND_VERSION > CONFIG_MTD_BRCMNAND_VERS_2_2 +static int +is_ecc_strong(int registerEcc, int requiredEcc) +{ + if (registerEcc == BRCMNAND_ECC_HAMMING) + registerEcc = 1; + + else if (registerEcc == BRCMNAND_ECC_DISABLE) + return 1; // Internal ECC is always stronger + + if (requiredEcc == BRCMNAND_ECC_HAMMING) + requiredEcc = 1; + + return (registerEcc >= requiredEcc); +} + +static void +brcmnand_set_acccontrol(struct brcmnand_chip * chip, unsigned int chipSelect, + uint32_t pageSize, uint16_t oobSizePerPage, int reqEcc, int codeWorkSize, int nbrBitsPerCell) +{ + int actualReqEcc = reqEcc; + int eccLevel; + uint32_t b1Ksector = 0; + uint32_t acc0, acc; + int oobPerSector = oobSizePerPage / (pageSize / 512); + uint32_t cellinfo; + + + PRINTK("-->%s(oob=%d, ecc=%d, cw=%d)\n", __FUNCTION__, oobSizePerPage, reqEcc, codeWorkSize); + + if (oobPerSector == 28) + oobPerSector = 27; /* Reduce Micron oobsize to match other vendors in order to share codes */ + + if (codeWorkSize == 1024) { + actualReqEcc = reqEcc / 2; + b1Ksector = 1; + } + + acc = acc0 = chip->ctrl_read(bchp_nand_acc_control(chipSelect)); + eccLevel = (acc & BCHP_NAND_ACC_CONTROL_ECC_LEVEL_MASK) + >> BCHP_NAND_ACC_CONTROL_ECC_LEVEL_SHIFT; + if (!is_ecc_strong(eccLevel, actualReqEcc)) { + eccLevel = actualReqEcc; + } + + acc &= ~(BCHP_NAND_ACC_CONTROL_ECC_LEVEL_MASK +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_5_0 + | BCHP_NAND_ACC_CONTROL_SECTOR_SIZE_1K_MASK +#endif + | BCHP_NAND_ACC_CONTROL_SPARE_AREA_SIZE_MASK); + + printk("eccLevel=%d, 1Ksector=%d, oob=%d\n", eccLevel, b1Ksector, oobPerSector); + + acc |= eccLevel << BCHP_NAND_ACC_CONTROL_ECC_LEVEL_SHIFT; +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_5_0 + acc |= b1Ksector << BCHP_NAND_ACC_CONTROL_SECTOR_SIZE_1K_SHIFT; +#endif + acc |= oobPerSector << BCHP_NAND_ACC_CONTROL_SPARE_AREA_SIZE_SHIFT; + + if (chipSelect == 0) { +#if CONFIG_MTD_BRCMNAND_VERSION < CONFIG_MTD_BRCMNAND_VERS_7_0 + acc &= ~(BCHP_NAND_ACC_CONTROL_ECC_LEVEL_0_MASK +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_5_0 + | BCHP_NAND_ACC_CONTROL_SECTOR_SIZE_1K_0_MASK +#endif + | BCHP_NAND_ACC_CONTROL_SPARE_AREA_SIZE_0_MASK); + acc |= eccLevel << BCHP_NAND_ACC_CONTROL_ECC_LEVEL_0_SHIFT; +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_5_0 + acc |= b1Ksector << BCHP_NAND_ACC_CONTROL_SECTOR_SIZE_1K_0_SHIFT; +#endif + acc |= oobPerSector << BCHP_NAND_ACC_CONTROL_SPARE_AREA_SIZE_0_SHIFT; +#endif + } + + /* Clear FAST_PGM_RDIN, PARTIAL_PAGE_EN if MLC */ + cellinfo = ffs(nbrBitsPerCell) - 2; + + PRINTK("cellinfo=%d\n", cellinfo); + + chip->cellinfo = cellinfo << 2; /* Mask is 0x0C */ + + printk("nbrBitsPerCell=%d, cellinfo=%d, chip->cellinfo=%08x\n", nbrBitsPerCell, cellinfo, chip->cellinfo); + if (NAND_IS_MLC(chip)) { + acc &= ~( +#if CONFIG_MTD_BRCMNAND_VERSION < CONFIG_MTD_BRCMNAND_VERS_7_0 + BCHP_NAND_ACC_CONTROL_FAST_PGM_RDIN_MASK | +#endif + BCHP_NAND_ACC_CONTROL_PARTIAL_PAGE_EN_MASK); + } + + + chip->ctrl_write(bchp_nand_acc_control(chipSelect), acc); + printk("<--%s: acc b4: %08x, after: %08x\n", __FUNCTION__, acc0, acc); + +} + + +/* + * Override database values with kernel command line values and + * set internal data structure values - when the flash ID is NOT in the database - + * using the values set by the CFE + */ +static void brcmnand_adjust_acccontrol(struct brcmnand_chip *chip, int isONFI, int inIdTable, int idTableIdx) +{ + int cs = chip->ctrl->CS[chip->csi]; + unsigned long nand_acc_b4 = chip->ctrl_read(bchp_nand_acc_control(cs)); + unsigned long nand_acc = nand_acc_b4; + int eccLevel; + int oobSize; + int updateInternalData = 0; + + PRINTK("%s: gAccControl[CS=%d]=%08x, ACC=%08lx\n", + __FUNCTION__, cs, gAccControl[chip->csi], nand_acc_b4); + + if (cs != 0 && 0 != gAccControl[chip->csi]) { + nand_acc = gAccControl[chip->csi]; + chip->ctrl_write(bchp_nand_acc_control(cs), nand_acc); + printk("NAND ACC CONTROL on CS%1d changed to %08x, from %08lx,\n", cs, + chip->ctrl_read(bchp_nand_acc_control(cs)), nand_acc_b4); + + updateInternalData = 1; + }else if (!inIdTable && !isONFI) { + updateInternalData = 1; + } + /* Update ACC-CONTROL when not on CS0 */ + else if (cs != 0 && inIdTable) { + int oobSizePerPage = chip->eccOobSize * (chip->pageSize / 512); + brcmnand_set_acccontrol(chip, cs, + chip->pageSize, oobSizePerPage, chip->reqEccLevel, chip->eccSectorSize, 2 + NAND_IS_MLC(chip)); + } + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_7_0 + if( chip->pageSize > 512 ) { + nand_acc |= 1 << BCHP_NAND_ACC_CONTROL_PREFETCH_EN_SHIFT; + chip->ctrl_write(bchp_nand_acc_control(cs), nand_acc); + } +#endif + + + /* + * update InternalData is TRUE only when + * (a) We are on CS0, and the chip is not in the database, in which case we use the values + * used by the CFE. + * (b) an ACC value was passed on the command line + */ + + /* Update ECC level */ + if (updateInternalData) { +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_3_3 + eccLevel = (nand_acc & BCHP_NAND_ACC_CONTROL_CS1_ECC_LEVEL_MASK) >> + BCHP_NAND_ACC_CONTROL_CS1_ECC_LEVEL_SHIFT; + oobSize = (nand_acc & BCHP_NAND_ACC_CONTROL_CS1_SPARE_AREA_SIZE_MASK) >> + BCHP_NAND_ACC_CONTROL_CS1_SPARE_AREA_SIZE_SHIFT; + +#else + eccLevel = (nand_acc & BCHP_NAND_ACC_CONTROL_ECC_LEVEL_MASK) >> + BCHP_NAND_ACC_CONTROL_ECC_LEVEL_SHIFT; + oobSize = (nand_acc & BCHP_NAND_ACC_CONTROL_SPARE_AREA_SIZE_MASK) >> + BCHP_NAND_ACC_CONTROL_SPARE_AREA_SIZE_SHIFT; +#endif + + chip->ecclevel = eccLevel; + printk("ECC level changed to %d\n", eccLevel); + + chip->eccOobSize = oobSize; + printk("OOB size changed to %d\n", oobSize); + + /* Assume MLC if both RDIN and PARTIAL_PAGE are disabled */ +#if CONFIG_MTD_BRCMNAND_VERSION < CONFIG_MTD_BRCMNAND_VERS_7_0 + /* this section needs to be commented out because we now turn off partial page writes + * to support NOP=1 devices and this code may trigger MLC=true for an unidentified NAND device, + * causing the NAND device to be accessed improperly + if (0 == (nand_acc & ( + BCHP_NAND_ACC_CONTROL_FAST_PGM_RDIN_MASK | + BCHP_NAND_ACC_CONTROL_PARTIAL_PAGE_EN_MASK))) { + chip->cellinfo = 0x04; // MLC NAND + printk("Flash type changed to MLC\n"); + } + */ +#endif +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_5_0 + if (nand_acc & BCHP_NAND_ACC_CONTROL_CS1_SECTOR_SIZE_1K_MASK) { + chip->eccSectorSize = 1024; + printk("Sector size changed to 1024\n"); + } +#endif + } +} +#endif + + +static void +brcmnand_read_id(struct mtd_info *mtd, unsigned int chipSelect, unsigned long* dev_id) +{ + struct brcmnand_chip * chip = mtd->priv; + uint32_t status; + uint32_t nandConfig = chip->ctrl_read(bchp_nand_config(chipSelect)); + uint32_t csNandSelect = 0; + uint32_t nandSelect = 0; + + if (chipSelect > 0) { // Do not re-initialize when on CS0, Bootloader already done that + + #if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_1_0 + /* Older version do not have EXT_ADDR registers */ + chip->ctrl_write(BCHP_NAND_CMD_ADDRESS, 0); + chip->ctrl_write(BCHP_NAND_CMD_EXT_ADDRESS, chipSelect << BCHP_NAND_CMD_EXT_ADDRESS_CS_SEL_SHIFT); + #endif // + + chip->ctrl_write(BCHP_NAND_CMD_START, + BCHP_NAND_CMD_START_OPCODE_FLASH_RESET << BCHP_NAND_CMD_START_OPCODE_SHIFT); + + } +/* Wait for CTRL_Ready */ + brcmnand_wait(mtd, BRCMNAND_FL_READY, &status, 10000); + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_0_1 + nandSelect = chip->ctrl_read(BCHP_NAND_CS_NAND_SELECT); + + printk("B4: NandSelect=%08x, nandConfig=%08x, chipSelect=%d\n", nandSelect, nandConfig, chipSelect); + + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_1_0 + /* Older version do not have EXT_ADDR registers */ + chip->ctrl_write(BCHP_NAND_CMD_ADDRESS, 0); + chip->ctrl_write(BCHP_NAND_CMD_EXT_ADDRESS, chipSelect << BCHP_NAND_CMD_EXT_ADDRESS_CS_SEL_SHIFT); +#endif // Set EXT address if version >= 1.0 + + // Has CFE initialized the register? + if (0 == (nandSelect & BCHP_NAND_CS_NAND_SELECT_AUTO_DEVICE_ID_CONFIG_MASK)) { + +#if CONFIG_MTD_BRCMNAND_VERSION == CONFIG_MTD_BRCMNAND_VERS_0_1 + csNandSelect = 1 << (BCHP_NAND_CS_NAND_SELECT_EBI_CS_0_SEL_SHIFT + chipSelect); + +// v1.0 does not define it +#elif CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_2_0 + csNandSelect = 1 << (BCHP_NAND_CS_NAND_SELECT_EBI_CS_0_USES_NAND_SHIFT + chipSelect); + +#endif // If brcmNAND Version >= 1.0 + + nandSelect = BCHP_NAND_CS_NAND_SELECT_AUTO_DEVICE_ID_CONFIG_MASK | csNandSelect; + chip->ctrl_write(BCHP_NAND_CS_NAND_SELECT, nandSelect); + } + +#if CONFIG_MTD_BRCMNAND_VERSION == CONFIG_MTD_BRCMNAND_VERS_7_0 + /*NAND controller rev7 perform auto detect again when _AUTO_DEVICE_ID is set on receiving + read id cmd and update the nand config reg. CFE overwrite this config register in case hw + auto detect is wrong(such as MXIC 512Mb MX30LF1208AA) when system boot.Clear the AUTO Dev + Id bit to avoid incorrect config setting */ + nandSelect = chip->ctrl_read(BCHP_NAND_CS_NAND_SELECT); + nandSelect &= ~BCHP_NAND_CS_NAND_SELECT_AUTO_DEVICE_ID_CONFIG_MASK; + chip->ctrl_write(BCHP_NAND_CS_NAND_SELECT, nandSelect); +#endif + + /* Send the command for reading device ID from controller */ + chip->ctrl_write(BCHP_NAND_CMD_START, OP_DEVICE_ID_READ); + + /* Wait for CTRL_Ready */ + brcmnand_wait(mtd, BRCMNAND_FL_READY, &status, 10000); + +#endif // if BrcmNAND Version >= 0.1 + + + *dev_id = chip->ctrl_read(BCHP_NAND_FLASH_DEVICE_ID); + + printk("%s: CS%1d: dev_id=%08x\n", __FUNCTION__, chipSelect, (unsigned int)*dev_id); + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_0_1 + nandSelect = chip->ctrl_read(BCHP_NAND_CS_NAND_SELECT); +#endif + + nandConfig = chip->ctrl_read(bchp_nand_config(chip->ctrl->CS[chip->csi])); + + printk("After: NandSelect=%08x, nandConfig=%08x\n", nandSelect, nandConfig); +} + + +#if (CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_3_0 && CONFIG_MTD_BRCMNAND_VERSION <= CONFIG_MTD_BRCMNAND_VERS_7_0) +/* + * Type-1 ID string, called from brcmnand_probe with the following condition + * if ((brcmnand_chips[i].idOptions & BRCMNAND_ID_HAS_BYTE4) && + * (brcmnand_chips[i].idOptions & BRCMNAND_ID_HAS_BYTE5)) + * + * returns the updated nand_config register value. + * + * This routine will need to set chip->chipSize and chip->page_shift in order to compute + * the address bytes in the NAND_CONFIG register. + */ +static uint32_t +decode_ID_type1(struct brcmnand_chip * chip, + unsigned char brcmnand_maf_id, unsigned char brcmnand_dev_id, uint32_t idOptions, uint32_t nbrBlocks) +{ + uint32_t nand_config = chip->ctrl_read(bchp_nand_config(chip->ctrl->CS[chip->csi])); // returned value + + +/* Read 5th ID byte if MLC type */ +//if (chip->cellinfo) + +/* THT SWLINUX 1459: Some new SLCs have 5th ID byte defined, not just MLC */ +// if (brcmnand_chips[i].idOptions & BRCMNAND_ID_HAS_BYTE5) + + + unsigned long devIdExt = chip->ctrl_read(BCHP_NAND_FLASH_DEVICE_ID_EXT); + unsigned char devId5thByte = (devIdExt & 0xff000000) >> 24; + unsigned int nbrPlanes = 0; + unsigned int planeSizeMB = 0, chipSizeMB, nandConfigChipSize; + unsigned char devId4thdByte = (chip->device_id & 0xff); + unsigned int pageSize = 0, pageSizeBits = 0; + unsigned int blockSize = 0, blockSizeBits = 0; + + //unsigned int oobSize; + + PRINTK("%s: mafID=%02x, devID=%02x, ID4=%02x, ID5=%02x\n", + __FUNCTION__, brcmnand_maf_id, brcmnand_dev_id, + devId4thdByte, devId5thByte); + + // if (brcmnand_chips[i].idOptions & BRCMNAND_ID_HAS_BYTE4) + +/*---------------- 4th ID byte: page size, block size and OOB size ---------------- */ + switch (brcmnand_maf_id) { + case FLASHTYPE_SAMSUNG: + case FLASHTYPE_HYNIX: + case FLASHTYPE_TOSHIBA: + case FLASHTYPE_MICRON: + pageSize = 1024 << (devId4thdByte & SAMSUNG_4THID_PAGESIZE_MASK); + blockSize = (64 * 1024) << ((devId4thdByte & SAMSUNG_4THID_BLKSIZE_MASK) >> 4); + //oobSize = devId4thdByte & SAMSUNG_4THID_OOBSIZE_MASK ? 16 : 8; + chip->page_shift = ffs(pageSize) - 1; + chip->erase_shift = ffs(blockSize) - 1; + + + PRINTK("Updating Config Reg: Block & Page Size: B4: %08x, blockSize=%08x, pageSize=%d\n", + nand_config, blockSize, pageSize); + /* Update Config Register: Block Size */ + switch (blockSize) { + case 512 * 1024: + blockSizeBits = BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_512KB; + break; + case 128 * 1024: + blockSizeBits = BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_128KB; + break; + case 16 * 1024: + blockSizeBits = BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_16KB; + break; + case 256 * 1024: + blockSizeBits = BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_256KB; + break; + } + nand_config &= ~(BCHP_NAND_CONFIG_BLOCK_SIZE_MASK); + nand_config |= (blockSizeBits << BCHP_NAND_CONFIG_BLOCK_SIZE_SHIFT); + + /* Update Config Register: Page Size */ + switch (pageSize) { + case 512: + pageSizeBits = BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_512; + break; + case 2048: + pageSizeBits = BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_2KB; + break; + case 4096: + pageSizeBits = BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_4KB; + break; + } + nand_config &= ~(BCHP_NAND_CONFIG_PAGE_SIZE_MASK); + nand_config |= (pageSizeBits << BCHP_NAND_CONFIG_PAGE_SIZE_SHIFT); + chip->ctrl_write(bchp_nand_config(chip->ctrl->CS[chip->csi]), nand_config); + PRINTK("Updating Config Reg: Block & Page Size: After: %08x\n", nand_config); + break; + + default: + printk(KERN_ERR "4th ID Byte: Device requiring Controller V3.0 in database, but not handled\n"); + //BUG(); + } + + + if (nbrBlocks > 0) { + chip->chipSize = ((uint64_t)nbrBlocks) << chip->erase_shift; + PRINTK("nbrBlocks=%d, blockSize=%d, blkShift=%d, chip Size = %llx\n", nbrBlocks, blockSize, chip->erase_shift, chip->chipSize); + chipSizeMB = (uint32_t)(chip->chipSize >> 20); + }else { /* Use 5th byte plane size & nbrPlanes to compute chip size */ +/*---------------- 5th ID byte ------------------------- */ + switch (brcmnand_maf_id) { + case FLASHTYPE_SAMSUNG: + case FLASHTYPE_HYNIX: + case FLASHTYPE_TOSHIBA: + case FLASHTYPE_MICRON: + PRINTK("5th ID byte = %02x, extID = %08lx\n", devId5thByte, devIdExt); + + switch (devId5thByte & SAMSUNG_5THID_NRPLANE_MASK) { + case SAMSUNG_5THID_NRPLANE_1: + nbrPlanes = 1; + break; + case SAMSUNG_5THID_NRPLANE_2: + nbrPlanes = 2; + break; + case SAMSUNG_5THID_NRPLANE_4: + nbrPlanes = 4; + break; + case SAMSUNG_5THID_NRPLANE_8: + nbrPlanes = 8; + break; + } + PRINTK("nbrPlanes = %d\n", nbrPlanes); + } + + switch (brcmnand_maf_id) { + case FLASHTYPE_SAMSUNG: + case FLASHTYPE_MICRON: + if (idOptions & BRCMNAND_ID_HAS_MICRON_M68A) { + planeSizeMB = 128; + }else { + /* Samsung Plane Size + #define SAMSUNG_5THID_PLANESZ_64Mb 0x00 + #define SAMSUNG_5THID_PLANESZ_128Mb 0x10 + #define SAMSUNG_5THID_PLANESZ_256Mb 0x20 + #define SAMSUNG_5THID_PLANESZ_512Mb 0x30 + #define SAMSUNG_5THID_PLANESZ_1Gb 0x40 + #define SAMSUNG_5THID_PLANESZ_2Gb 0x50 + #define SAMSUNG_5THID_PLANESZ_4Gb 0x60 + #define SAMSUNG_5THID_PLANESZ_8Gb 0x70 + */ + // planeSize starts at (64Mb/8) = 8MB; + planeSizeMB = 8 << ((devId5thByte & SAMSUNG_5THID_PLANESZ_MASK) >> 4); + } + PRINTK("planSizeMB = %dMB\n", planeSizeMB); + break; + + case FLASHTYPE_HYNIX: + if (idOptions & BRCMNAND_ID_HYNIX_LEGACY) { + // planeSize starts at (64Mb/8) = 8MB, same as Samsung + planeSizeMB = 8 << ((devId5thByte & HYNIX_5THID_LEG_PLANESZ_MASK) >> 4); + }else { + /* Hynix Plane Size, Type 2 + #define HYNIX_5THID_PLANESZ_MASK 0x70 + #define HYNIX_5THID_PLANESZ_512Mb 0x00 + #define HYNIX_5THID_PLANESZ_1Gb 0x10 + #define HYNIX_5THID_PLANESZ_2Gb 0x20 + #define HYNIX_5THID_PLANESZ_4Gb 0x30 + #define HYNIX_5THID_PLANESZ_8Gb 0x40 + #define HYNIX_5THID_PLANESZ_RSVD1 0x50 + #define HYNIX_5THID_PLANESZ_RSVD2 0x60 + #define HYNIX_5THID_PLANESZ_RSVD3 0x70 + */ + // planeSize starts at (512Mb/8) = 64MB; + planeSizeMB = 64 << ((devId5thByte & SAMSUNG_5THID_PLANESZ_MASK) >> 4); + } + break; + + case FLASHTYPE_TOSHIBA: + /* No Plane Size defined */ + // THT Nothing to do, size is hardcoded in chip array. + // planeSizeMB = 64; /* hard-coded for TC58NVG0S3ETA00 */ + break; + + /* TBD Add other mfg ID here */ + + } /* End 5th ID byte */ + + chipSizeMB = planeSizeMB * nbrPlanes; + chip->chipSize = ((uint64_t)chipSizeMB) << 20; + PRINTK("planeSizeMB = %d, chipSizeMB=%d,0x%04x, planeSizeMask=%08x\n", planeSizeMB, chipSizeMB, chipSizeMB, devId5thByte & SAMSUNG_5THID_PLANESZ_MASK); + } + + /* NAND Config register starts at 4MB for chip size */ + nandConfigChipSize = ffs(chipSizeMB >> 2) - 1; + + PRINTK("nandConfigChipSize = %04x\n", nandConfigChipSize); + /* Correct chip Size accordingly, bit 24-27 */ + nand_config &= ~(BCHP_NAND_CONFIG_DEVICE_SIZE_MASK); + nand_config |= (nandConfigChipSize << BCHP_NAND_CONFIG_DEVICE_SIZE_SHIFT); + + return nand_config; +} + + +/* + * Type-2 ID string, called from brcmnand_probe with the following condition + * if ((brcmnand_chips[i].idOptions & BRCMNAND_ID_EXT_BYTES_TYPE2) == + * BRCMNAND_ID_EXT_BYTES_TYPE2) + * + * returns the updated nand_config register value. + * + * This routine will need to set chip->chipSize and chip->page_shift in order to compute + * the address bytes in the NAND_CONFIG register. + */ +static uint32_t +decode_ID_type2(struct brcmnand_chip * chip, + unsigned char brcmnand_maf_id, unsigned char brcmnand_dev_id, uint32_t nbrBlocks, + uint32* pEccLevel, uint32* pSectorSize) +{ + uint32_t nand_config = chip->ctrl_read(bchp_nand_config(chip->ctrl->CS[chip->csi])); // returned value + unsigned char devId4thdByte = (chip->device_id & 0xff); + unsigned int pageSize = 0, pageSizeBits = 0; + unsigned int blockSize = 0, blockSizeBits = 0; + //unsigned int oobSize; + unsigned int oobSize, oobSizePerPage = 0; + uint32_t chipSizeMB, nandConfigChipSize; + uint32_t devIdExt, eccShift, reqEcc; + unsigned char devId5thByte; + + PRINTK("%s: mafID=%02x, devID=%02x, ID4=%02x\n", + __FUNCTION__, brcmnand_maf_id, brcmnand_dev_id, + devId4thdByte); + + /*---------------- 4th ID byte: page size, block size and OOB size ---------------- */ + switch (brcmnand_maf_id) { + case FLASHTYPE_SAMSUNG: + case FLASHTYPE_HYNIX: + pageSize = 2048 << (devId4thdByte & SAMSUNG2_4THID_PAGESIZE_MASK); + /* **FIXME**, when Samsung use the Reserved bits */ + blockSize = (128 * 1024) << ((devId4thdByte & SAMSUNG2_4THID_BLKSIZE_MASK) >> 4); + chip->blockSize = blockSize; + switch (devId4thdByte & SAMSUNG2_4THID_OOBSIZE_MASK) { + case SAMSUNG2_4THID_OOBSIZE_PERPAGE_128: + oobSizePerPage = 128; + break; + + case SAMSUNG2_4THID_OOBSIZE_PERPAGE_218: + oobSizePerPage = 218; + break; + + case SAMSUNG2_4THID_OOBSIZE_PERPAGE_400: /* 16 per 512B */ + oobSizePerPage = 400; + break; + + case SAMSUNG2_4THID_OOBSIZE_PERPAGE_436: /* 27.5 per 512B */ + oobSizePerPage = 436; + break; + } + oobSize = oobSizePerPage / (pageSize / 512); + // Record it here, but will check it when we know about the ECC level. + chip->eccOobSize = oobSize; + PRINTK("oobSizePerPage=%d, eccOobSize=%d, pageSize=%u, blockSize=%u\n", + oobSizePerPage, chip->eccOobSize, pageSize, blockSize); + PRINTK("Updating Config Reg T2: Block & Page Size: B4: %08x\n", nand_config); + + chip->page_shift = ffs(pageSize) - 1; + + /* Update Config Register: Block Size */ + switch (blockSize) { + case 512 * 1024: + blockSizeBits = BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_512KB; + break; + case 128 * 1024: + blockSizeBits = BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_128KB; + break; + case 16 * 1024: + blockSizeBits = BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_16KB; + break; + case 256 * 1024: + blockSizeBits = BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_256KB; + break; +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_5_0 + case 1024 * 1024: + blockSizeBits = BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_1024KB; + break; +#endif + } + PRINTK("%s: blockSizeBits=%08x, NANDCONFIG B4=%08x\n", __FUNCTION__, blockSizeBits, nand_config); + nand_config &= ~(BCHP_NAND_CONFIG_BLOCK_SIZE_MASK); + nand_config |= (blockSizeBits << BCHP_NAND_CONFIG_BLOCK_SIZE_SHIFT); + PRINTK("%s: NANDCONFIG After=%08x\n", __FUNCTION__, nand_config); + + /* Update Config Register: Page Size */ + switch (pageSize) { + case 512: + pageSizeBits = BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_512; + break; + case 2048: + pageSizeBits = BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_2KB; + break; + case 4096: + pageSizeBits = BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_4KB; + break; +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_3_4 + case 8192: + pageSizeBits = BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_8KB; + break; +#endif + } + PRINTK("%s: pageSizeBits=%08x, NANDCONFIG B4=%08x\n", __FUNCTION__, pageSizeBits, nand_config); + nand_config &= ~(BCHP_NAND_CONFIG_PAGE_SIZE_MASK); + nand_config |= (pageSizeBits << BCHP_NAND_CONFIG_PAGE_SIZE_SHIFT); + + + break; + + default: + printk(KERN_ERR "4th ID Byte: Device requiring Controller V3.0 in database, but not handled\n"); + //BUG(); + } + +#if 1 +/* + * Now we hard-code the flash size in the ID array, because Samsung type 2 flashes are MLC flashes, + * so tend to be used on CSn, n != 0, and thus the CFE may not configure it properly + */ + PRINTK("nbrBlocks=%d, blockSize=%d\n", nbrBlocks, chip->blockSize); + chip->chipSize = ((uint64_t)nbrBlocks) * chip->blockSize; + chipSizeMB = (uint32_t)(chip->chipSize >> 20); + nandConfigChipSize = ffs(chipSizeMB >> 2) - 1; + + PRINTK("chipSize=%dMB, nandConfigChipSize = %04x\n", chipSizeMB, nandConfigChipSize); + /* Encode chip Size accordingly, bit 24-27 */ + nand_config &= ~(BCHP_NAND_CONFIG_DEVICE_SIZE_MASK); + nand_config |= (nandConfigChipSize << BCHP_NAND_CONFIG_DEVICE_SIZE_SHIFT); + + PRINTK("Updating Config Reg on CS%1d: %08x\n", chip->ctrl->CS[chip->csi], nand_config); + chip->ctrl_write(bchp_nand_config(chip->ctrl->CS[chip->csi]), nand_config); + +/*---------------- 5th ID byte: ECC level and plane number ---------------- */ + devIdExt = chip->ctrl_read(BCHP_NAND_FLASH_DEVICE_ID_EXT); + devId5thByte = (devIdExt & 0xff000000) >> 24; + reqEcc = (devId5thByte & SAMSUNG2_5THID_ECCLVL_MASK); + switch (reqEcc) { + case SAMSUNG2_5THID_ECCLVL_1BIT: /* 0x00 */ + case SAMSUNG2_5THID_ECCLVL_2BIT: /* 0x10 */ + case SAMSUNG2_5THID_ECCLVL_4BIT: /* 0x20 */ + case SAMSUNG2_5THID_ECCLVL_8BIT: /* 0x30 */ + case SAMSUNG2_5THID_ECCLVL_16BIT: /* 0x40 */ + eccShift = reqEcc >> 4; + *pEccLevel = 1 << eccShift; + *pSectorSize = 512; + break; + + case SAMSUNG2_5THID_ECCLVL_24BIT_1KB: /* 0x50 */ + *pEccLevel = 24; + *pSectorSize = 1024; + } + + PRINTK("Required ECC level = %ld, devIdExt=%08x, eccShift=%02x, sector Size=%ld\n", + *pEccLevel, devIdExt, eccShift, *pSectorSize); + +#else + /* For type 2, ID bytes do not yield flash Size, but CONFIG registers have that info */ + + chipSizeShift = (nand_config & BCHP_NAND_CONFIG_DEVICE_SIZE_MASK) >> BCHP_NAND_CONFIG_DEVICE_SIZE_SHIFT; + chip->chipSize = 4ULL << (20 + chipSizeShift); + +#endif + + return nand_config; +} + + +/* + * Type-2 ID string, called from brcmnand_probe with the following condition + * if ((brcmnand_chips[i].idOptions & BRCMNAND_ID_EXT_BYTES_TYPE2) == + * BRCMNAND_ID_EXT_BYTES_TYPE2) + * + * returns the updated nand_config register value. + * + * This routine will need to set chip->chipSize and chip->page_shift in order to compute + * the address bytes in the NAND_CONFIG register. + */ +static uint32_t +decode_ID_M61A(struct brcmnand_chip * chip, + unsigned char brcmnand_maf_id, unsigned char brcmnand_dev_id) +{ + uint32_t nand_config = chip->ctrl_read(bchp_nand_config(chip->ctrl->CS[chip->csi])); // returned value + unsigned char devId4thdByte = (chip->device_id & 0xff); + unsigned int pageSize = 0, pageSizeBits = 0; + unsigned int blockSize = 0, blockSizeBits = 0; + //unsigned int oobSize; + unsigned int oobSize, oobSizePerPage = 0; + uint32_t pagesPerBlock, pagesPerBlockBits; + unsigned long devIdExt = chip->ctrl_read(BCHP_NAND_FLASH_DEVICE_ID_EXT); + unsigned char devId5thByte = (devIdExt & 0xff000000) >> 24; + unsigned int nbrPlanes = 0; + unsigned int chipSizeMB, nandConfigChipSize; + unsigned int blkPerLun, nbrBlksBits; + + PRINTK("%s: mafID=%02x, devID=%02x, ID4=%02x\n", + __FUNCTION__, brcmnand_maf_id, brcmnand_dev_id, + devId4thdByte); + + /* Byte2: Voltage and size are not reliable */ + + /* 3rd ID byte, same as Samsung Type 1 */ + + /*---------------- 4th ID byte: page size, block size and OOB size ---------------- */ + pageSize = 1024 << (devId4thdByte & SAMSUNG_4THID_PAGESIZE_MASK); + chip->page_shift = ffs(pageSize) - 1; + + /* Block Size */ + pagesPerBlockBits = (devId4thdByte & MICRON_M61A_4THID_PGPBLK_MASK) >> 4; + pagesPerBlock = 32 << pagesPerBlockBits; + blockSize = pagesPerBlock * pageSize; + + + switch (devId4thdByte & MICRON_M61A_4THID_OOBSIZE_MASK) { + case MICRON_M61A_4THID_OOBSIZE_28B: + oobSize = 27; /* Use only 27 to conform to other vendors */ + oobSizePerPage = oobSize * (pageSize / 512); + break; + + } + + + // Record it here, but will check it when we know about the ECC level. + chip->eccOobSize = oobSize; + PRINTK("oobSizePerPage=%d, eccOobSize=%d, pageSize=%u, blockSize=%u\n", + oobSizePerPage, chip->eccOobSize, pageSize, blockSize); + + PRINTK("Updating Config Reg M61A: Block & Page Size: B4: %08x\n", nand_config); + + /* Update Config Register: Block Size */ + switch (blockSize) { +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_5_0 + case 1024 * 1024: + blockSizeBits = BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_1024KB; + break; +#else + case 1024 * 1024: + /* For version 3.x controller, we don't have a bit defined for this */ + /* FALLTHROUGH */ +#endif + case 512 * 1024: + blockSizeBits = BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_512KB; + break; + case 128 * 1024: + blockSizeBits = BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_128KB; + break; + case 16 * 1024: + blockSizeBits = BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_16KB; + break; + case 256 * 1024: + blockSizeBits = BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_256KB; + break; + + } + /* Record Block Size, since we can't rely on NAND_CONFIG */ + chip->blockSize = blockSize; + + PRINTK("%s: blockSizeBits=%08x, NANDCONFIG B4=%08x\n", __FUNCTION__, blockSizeBits, nand_config); + nand_config &= ~(BCHP_NAND_CONFIG_BLOCK_SIZE_MASK); + nand_config |= (blockSizeBits << BCHP_NAND_CONFIG_BLOCK_SIZE_SHIFT); + PRINTK("%s: NANDCONFIG After=%08x\n", __FUNCTION__, nand_config); + + /* Update Config Register: Page Size */ + switch (pageSize) { + case 512: + pageSizeBits = BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_512; + break; + case 2048: + pageSizeBits = BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_2KB; + break; + case 4096: + pageSizeBits = BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_4KB; + break; +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_3_4 + case 8192: + pageSizeBits = BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_8KB; + break; +#endif + + } + + +/*---------------- 5th ID byte ------------------------- */ + + PRINTK("5th ID byte = %02x, extID = %08lx\n", devId5thByte, devIdExt); + + + nbrPlanes = 1 << (devId5thByte & MICRON_M61A_5THID_PLN_PER_LUN_MASK); + nbrBlksBits = (devId5thByte & MICRON_M61A_5THID_BLK_PER_LUN_MASK) >> 2; + blkPerLun = 1024 << nbrBlksBits; + + chipSizeMB = (blkPerLun * blockSize) >> 20; + PRINTK("chipSizeMB=%d,0x%04x, planeSizeMask=%08x\n", chipSizeMB, chipSizeMB, devId5thByte & SAMSUNG_5THID_PLANESZ_MASK); + /* NAND Config register starts at 4MB for chip size */ + nandConfigChipSize = ffs(chipSizeMB >> 2) - 1; + + chip->chipSize = ((uint64_t)chipSizeMB) << 20; + + PRINTK("nandConfigChipSize = %04x\n", nandConfigChipSize); + /* Correct chip Size accordingly, bit 24-27 */ + nand_config &= ~(BCHP_NAND_CONFIG_DEVICE_SIZE_MASK); + nand_config |= (nandConfigChipSize << BCHP_NAND_CONFIG_DEVICE_SIZE_SHIFT); + chip->ctrl_write(bchp_nand_config(chip->ctrl->CS[chip->csi]), nand_config); + + + PRINTK("%s: pageSizeBits=%08x, NANDCONFIG B4=%08x\n", __FUNCTION__, pageSizeBits, nand_config); + nand_config &= ~(BCHP_NAND_CONFIG_PAGE_SIZE_MASK); + nand_config |= (pageSizeBits << BCHP_NAND_CONFIG_PAGE_SIZE_SHIFT); + chip->ctrl_write(bchp_nand_config(chip->ctrl->CS[chip->csi]), nand_config); + PRINTK("Updating Config Reg on CS%1d: Block & Page Size: After: %08x\n", chip->ctrl->CS[chip->csi], nand_config); + + return nand_config; +} +#endif + + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_4_0 + +/* Returns the 32bit integer at the aligned offset */ +static inline uint32_t brcmnand_flashcache_read(void* pDest, uint32_t ofs, int size) +{ + uint32_t aligned_ofs = ofs & 0xFFFFFFFC; + volatile uint32_t* p32FCache = (volatile uint32_t*)BVIRTADDR(BCHP_NAND_FLASH_CACHEi_ARRAY_BASE); + uintptr_t pReg = (BCHP_NAND_FLASH_CACHEi_ARRAY_BASE + aligned_ofs); + volatile u_char* p8FCache = (volatile u_char*)p32FCache; + uint32_t u32; + u_char* p8 = (u_char*)&u32; + + if ((size + ofs) > (aligned_ofs + 4)) { + printk("%s: Cannot read across DW boundary ofs=%d, size=%d\n", __FUNCTION__, ofs, size); + return 0; + } + + u32 = be32_to_cpu((uint32_t)BDEV_RD(pReg)); + if (pDest) { + memcpy(pDest, &p8[ofs - aligned_ofs], size); + } + + if (gdebug > 3) { + printk("%s: OFS=%d, EBIAddr=%p val=%08x, p8=%02x%02x%02x%02x\n", __FUNCTION__, + (unsigned int)aligned_ofs, (void*)pReg, (uint32_t)BDEV_RD(pReg), + p8FCache[aligned_ofs], p8FCache[aligned_ofs + 1], p8FCache[aligned_ofs + 2], p8FCache[aligned_ofs + 3]); + } + + return ((uint32_t)BDEV_RD(pReg)); +} + + +static void __maybe_unused +debug_print_flashcache(struct mtd_info *mtd) +{ + struct brcmnand_chip * chip = mtd->priv; + static uint32_t fbuffer[128]; + volatile uint32_t* fcache = (volatile uint32_t*)chip->vbase; + int i; + int saveDebug = gdebug; + + gdebug = 0; + + // Copy the data buffer + + for (i = 0; i < 128; i++) { + fbuffer[i] = (uint32_t)(fcache[i]); + } + + printk("Flash Cache:\n"); + print_databuf((u_char*)&fbuffer[0], 512); + + brcmnand_post_mortem_dump(mtd, 0); + gdebug = saveDebug; +} + +#if 0 +/* + * Return 0 for ready, TIMEOUT for error + */ +static int brcmnand_wait_for_cache_ready(struct brcmnand_chip* chip) +{ + unsigned long timeout; + uint32_t ready; + + //udelay(100000); /* 100 ms */ + /* The 20 msec is enough */ + timeout = jiffies + msecs_to_jiffies(3000); + while (time_before(jiffies, timeout)) { + //PLATFORM_IOFLUSH_WAR(); + ready = chip->ctrl_read(BCHP_NAND_INTFC_STATUS); + + if ((ready & BCHP_NAND_INTFC_STATUS_CTLR_READY_MASK) + && (ready & BCHP_NAND_INTFC_STATUS_CACHE_VALID_MASK)) { + + return BRCMNAND_SUCCESS; + + } + //if ( !in_interrupt()) + // cond_resched(); + //else + // udelay(10000); + } + + if (gdebug > 3 ) { + printk("<--%s: ret = TIMEOUT\n", __FUNCTION__); + print_nand_ctrl_regs(); + } + return BRCMNAND_TIMED_OUT; // TimeOut +} +#endif + +typedef enum { + BRCMNAND_READY, + BRCMNAND_CTRL_BUSY, + BRCMNAND_CTRL_READY, + BRCMNAND_CACHE_VALID +} brcmnand_ctrl_state_t; + +static int brcmnand_monitor_intfc(struct mtd_info *mtd, brcmnand_ctrl_state_t waitfor, uint32_t* pStatus) +{ + struct brcmnand_chip * chip = mtd->priv; + unsigned long timeout; + uint32_t ready; + brcmnand_ctrl_state_t state = BRCMNAND_READY; + int ret = -ETIMEDOUT; + //unsigned long irqflags; + +// Dont want printk to cause missing a transition of INTFC + int save_debug = gdebug; + uint32_t prev_ready; + +//gdebug = 0; + + //local_irq_save(irqflags); + prev_ready = *pStatus = ready = chip->ctrl_read(BCHP_NAND_INTFC_STATUS); + timeout = jiffies + msecs_to_jiffies(2000); // THT: 1000 msec, for now + while (time_before(jiffies, timeout) ) { + switch (state) { + case BRCMNAND_READY: /* Wait for ctrl-busy */ + if (!(ready & BCHP_NAND_INTFC_STATUS_CTLR_READY_MASK)) { + state = BRCMNAND_CTRL_BUSY; + if (save_debug) printk("%s: waitfor=%d, Got ctrl-busy, intfc=%08x\n", __FUNCTION__, waitfor, ready); + } + /* If we cgot cache valid, skip ctrl-busy */ + if ((waitfor == BRCMNAND_CACHE_VALID) + && (ready & BCHP_NAND_INTFC_STATUS_CACHE_VALID_MASK)) { + state = BRCMNAND_CTRL_READY; + ret = BRCMNAND_SUCCESS; + goto exit_monitor; + } + break; + case BRCMNAND_CTRL_BUSY: /* Wait for ctrl-ready */ + if ((waitfor == BRCMNAND_CTRL_READY) && + (ready & BCHP_NAND_INTFC_STATUS_CTLR_READY_MASK)) { + state = BRCMNAND_CTRL_READY; + ret = BRCMNAND_SUCCESS; + goto exit_monitor; + }else if ((waitfor == BRCMNAND_CACHE_VALID) + && (ready & BCHP_NAND_INTFC_STATUS_CACHE_VALID_MASK)) { + state = BRCMNAND_CTRL_READY; + ret = BRCMNAND_SUCCESS; + goto exit_monitor; + } + break; + case BRCMNAND_CTRL_READY: + if (waitfor == BRCMNAND_CTRL_READY) { + ret = BRCMNAND_SUCCESS; + goto exit_monitor; + } + break; + case BRCMNAND_CACHE_VALID: + if (waitfor == BRCMNAND_CACHE_VALID) { + ret = BRCMNAND_SUCCESS; + goto exit_monitor; + } + break; + } + if (prev_ready != ready) printk("prev_ready=%08x, ready=%08x\n", prev_ready, ready); + prev_ready = ready; + *pStatus = ready = chip->ctrl_read(BCHP_NAND_INTFC_STATUS); + } + + exit_monitor: + gdebug = save_debug; + + //local_irq_restore(irqflags); + + if (save_debug) printk("%s: waitfor=%d, return %d, intfc=%08x\n", __FUNCTION__, waitfor, ret, *pStatus); + return ret; +} + + + + +/* + * Decode flash geometry using ONFI + * returns 1 on success, 0 on failure + */ +static int +brcmnand_ONFI_decode(struct mtd_info *mtd, unsigned int chipSelect, + uint32_t* outp_pageSize, uint16_t* outp_oobSize, int* outp_reqEcc, int* outp_codeWorkSize) +{ + int skipDecodeID = 0; /* Returned value */ + struct brcmnand_chip * chip = mtd->priv; + uint32_t u32; + uint8_t eccLevel = 0; + uint32_t nand_config0, nand_config; + uint32_t acc; + int status, retries; + uint32_t nand_select; + int ret; + uint32_t timing2; + uint8_t nbrParamPages, nbrBitsPerCell; + uint32_t extParamOffset, extParamFCacheOffset; + +//gdebug=4; + if (gdebug > 3) printk("-->%s, chipSelect=%d\n", __FUNCTION__, chipSelect); + +#if 1 + /* Skip ONFI if on CS0, Boot loader already done that */ + if (chipSelect == 0) { // Do not re-initialize when on CS0, Bootloader already done that + return 0; + } +#else + /* + * Even though we cannot boot on CS0 on 7422a0, we still need to go through the + * ONFI decode procedure, in order to initialize internal data structure + */ + if (chipSelect == 0) { // Do not re-initialize when on CS0, Bootloader already done that + //TBD + return 0; + } +#endif + + chip->vbase = (void*)BVIRTADDR(BCHP_NAND_FLASH_CACHEi_ARRAY_BASE); + +#if 1 + if (chipSelect != 0) { + uint32_t nand_acc; + + if (gNandConfig[chip->csi] != 0) { + nand_config = gNandConfig[chip->csi]; + chip->ctrl_write(bchp_nand_config(chipSelect), nand_config); + + if (chip->csi == 0) /* No NAND on CS0 */ + chip->ctrl_write(BCHP_NAND_CONFIG, nand_config); + } + + if (0 != gAccControl[chip->csi]) { + nand_acc = gAccControl[chip->csi]; + chip->ctrl_write(bchp_nand_acc_control(chipSelect), nand_acc); + if (chip->csi == 0) + chip->ctrl_write(BCHP_NAND_ACC_CONTROL, nand_acc); + } + } + +#endif + + + + + retries = 1; + while (retries > 0) { + + PRINTK("************ Retries = %d\n", retries); + +#if 1 + nand_config0 = brcmnand_ctrl_read(bchp_nand_config(chipSelect)); + + +#endif + + + /* Setup READ ID and AUTOCONFIG */ + nand_select = chip->ctrl_read(BCHP_NAND_CS_NAND_SELECT); + chip->ctrl_write(BCHP_NAND_CMD_ADDRESS, 0); + chip->ctrl_write(BCHP_NAND_CMD_EXT_ADDRESS, chipSelect << + BCHP_NAND_CMD_EXT_ADDRESS_CS_SEL_SHIFT); + nand_select |= BCHP_NAND_CS_NAND_SELECT_AUTO_DEVICE_ID_CONFIG_MASK; + nand_select &= ~(1 << (chipSelect + BCHP_NAND_CS_NAND_SELECT_EBI_CS_0_SEL_SHIFT)); + chip->ctrl_write(BCHP_NAND_CS_NAND_SELECT, nand_select); + + //udelay(10000); /* 10 ms */ + + + chip->ctrl_write(BCHP_NAND_CMD_ADDRESS, 0); + chip->ctrl_write(BCHP_NAND_CMD_EXT_ADDRESS, chipSelect << + BCHP_NAND_CMD_EXT_ADDRESS_CS_SEL_SHIFT); + + chip->ctrl_write(BCHP_NAND_CMD_START, + BCHP_NAND_CMD_START_OPCODE_NULL << BCHP_NAND_CMD_START_OPCODE_SHIFT); + + /* Wait for controller busy then ready */ + ret = brcmnand_monitor_intfc(mtd, BRCMNAND_CTRL_READY, &status); + + + + // udelay(1000); // 1 sec + + // Change timing to conform to ONFI + timing2 = chip->ctrl_read(bchp_nand_timing2(chipSelect)); + PRINTK("Old timing2 value=%08x\n", timing2); + timing2 &= ~(BCHP_NAND_TIMING_2_tWHR_MASK); + timing2 |= 11 << BCHP_NAND_TIMING_2_tWHR_SHIFT; + PRINTK("New timing2 value=%08x\n", timing2); + chip->ctrl_write(bchp_nand_timing2(chipSelect), timing2); + + + nand_config = brcmnand_ctrl_read(bchp_nand_config(chipSelect)); + PRINTK("B4 status READ, nand_config0=%08x, nand_config1=%08x, ret=%d\n", nand_config0, nand_config, ret); + + + + nand_config = brcmnand_ctrl_read(bchp_nand_config(chipSelect)); + PRINTK("B4 PARAM READ, nand_config0=%08x, nand_config1=%08x, ret=%d\n", nand_config0, nand_config, ret); + + + chip->ctrl_write(BCHP_NAND_CMD_ADDRESS, 0); + chip->ctrl_write(BCHP_NAND_CMD_EXT_ADDRESS, chipSelect << + BCHP_NAND_CMD_EXT_ADDRESS_CS_SEL_SHIFT); + + chip->ctrl_write(BCHP_NAND_CMD_START, OP_PARAMETER_READ); + + /* Wait for controller busy then cache-valid */ + ret = brcmnand_monitor_intfc(mtd, BRCMNAND_CACHE_VALID, &status); + + + /* + * Verify ONFI capability + */ + u32 = brcmnand_flashcache_read(NULL, ONFI_RDPARAM_SIGNATURE_OFS, sizeof(u32)); + + + if (u32 == ONFI_SIGNATURE) { + printk("%s: Found ONFI signature. Looking for %08x found %08x, ret=%d\n", + __FUNCTION__, ONFI_SIGNATURE, u32, ret); + + break; + } + + + retries--; + + /* Flash Reset */ + brcmnand_wait(mtd, BRCMNAND_FL_READING, &status, 10000); + chip->ctrl_write(BCHP_NAND_CMD_ADDRESS, 0); + chip->ctrl_write(BCHP_NAND_CMD_EXT_ADDRESS, chipSelect << + BCHP_NAND_CMD_EXT_ADDRESS_CS_SEL_SHIFT); + chip->ctrl_write(BCHP_NAND_CMD_START, OP_FLASH_RESET); + + brcmnand_wait(mtd, BRCMNAND_FL_READING, &status, 10000); + + } + + + PRINTK("ONFI sig=%08x\n", *((volatile unsigned int*)chip->vbase)); + + + /* + * Verify ONFI capability + */ + u32 = brcmnand_flashcache_read(NULL, ONFI_RDPARAM_SIGNATURE_OFS, sizeof(u32)); + + + if (u32 != ONFI_SIGNATURE) { + printk("%s: Cannot find ONFI signature. Looking for %08x found %08x\n", + __FUNCTION__, ONFI_SIGNATURE, u32); + + // debug_print_flashcache(mtd); + skipDecodeID = 0; + goto onfi_exit; + } + + + // ONFI read-parameter was successful + nand_config = brcmnand_ctrl_read(bchp_nand_config(chipSelect)); + + if (nand_config != nand_config0) { + printk("Original nand_config=%08x, ONFI nand_config=%08x\n", + nand_config0, nand_config); + } + + /* Page Size */ + u32 = brcmnand_flashcache_read(outp_pageSize, ONFI_RDPARAM_PAGESIZE_OFS, sizeof(u32)); + + + /* OOB Size */ + u32 = brcmnand_flashcache_read(outp_oobSize, ONFI_RDPARAM_OOBSIZE_OFS, sizeof(*outp_oobSize)); + //*outp_oobSize = be16_to_cpu(*outp_oobSize); + PRINTK("oobSize = %d, u32=%08x\n", *outp_oobSize, u32); + + /* MLC or SLC */ + u32 = brcmnand_flashcache_read(&nbrBitsPerCell, ONFI_NBR_BITS_PER_CELL_OFS, sizeof(nbrBitsPerCell)); + PRINTK("nbrBitsPerCell = %d, u32=%08x\n", nbrBitsPerCell, u32); + + /* Required ECC level */ + u32 = brcmnand_flashcache_read(&eccLevel, ONFI_RDPARAM_ECC_LEVEL_OFS, sizeof(eccLevel)); + + PRINTK("EccLevel = [%08x], %02x, pageSize=%d, oobSize=%d\n", u32, eccLevel, *outp_pageSize, *outp_oobSize); + + if (eccLevel != 0xFF) { /* Codework is 512B */ + *outp_reqEcc = eccLevel; + *outp_codeWorkSize = 512; + skipDecodeID = 1; + }else { /* Codework is NOT 512B */ + //int offset = 512; + uint32_t extParamSig = 0; + + /* First find out how many param pages there are */ + (void)brcmnand_flashcache_read(&nbrParamPages, ONFI_NBR_PARAM_PAGE_OFS, sizeof(nbrParamPages)); + + + extParamOffset = (256 * nbrParamPages); + extParamFCacheOffset = extParamOffset & ~(512 - 1); // ALign on 512B + PRINTK("nbrParamPages = %d, offset=%d, extCacheOffset=%d\n", nbrParamPages, extParamOffset, extParamFCacheOffset); + +//gdebug=4; + + /* Turn off 1KB sector size */ + acc = chip->ctrl_read(bchp_nand_acc_control(chipSelect)); +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_5_0 + acc &= ~(BCHP_NAND_ACC_CONTROL_CS1_SECTOR_SIZE_1K_MASK); +#endif + //acc &= ~(BCHP_NAND_ACC_CONTROL_CS1_SPARE_AREA_SIZE_MASK); + //acc |= (*oobSize) << BCHP_NAND_ACC_CONTROL_CS1_SPARE_AREA_SIZE_SHIFT; + chip->ctrl_write(bchp_nand_acc_control(chipSelect), acc); + + + /* Bring in next 512B */ + chip->ctrl_write(BCHP_NAND_CMD_ADDRESS, extParamFCacheOffset); + chip->ctrl_write(BCHP_NAND_CMD_EXT_ADDRESS, + chipSelect << BCHP_NAND_CMD_EXT_ADDRESS_CS_SEL_SHIFT); + + chip->ctrl_write(BCHP_NAND_CMD_START, OP_PARAMETER_CHANGE_COL); + + /* Wait for controller busy then ready */ + ret = brcmnand_monitor_intfc(mtd, BRCMNAND_CACHE_VALID, &status); + + /* + * Verify EXT PARAM signature + * Need to adjust offset based on the number of Read-Param pages + */ + + u32 = brcmnand_flashcache_read(NULL, + ONFI_EXTPARAM_SIG1_OFS - ONFI_EXTPARAM_OFS + (extParamOffset - extParamFCacheOffset), 2); + extParamSig = (u32 & 0xFFFF) << 16; + PRINTK("EPPS1: u32=%08x, eppsig=%08x\n", u32, extParamSig); + u32 = brcmnand_flashcache_read(NULL, + ONFI_EXTPARAM_SIG2_OFS - ONFI_EXTPARAM_OFS + (extParamOffset - extParamFCacheOffset), 2); + extParamSig |= (u32 >> 16); + PRINTK("EPPS2: u32=%08x, eppsig=%08x\n", u32, extParamSig); + + if (ONFI_EXTPARAM_SIG != extParamSig) { + printk("%s: EXT PARAM not found, looking for %08x, found %08x\n", + __FUNCTION__, ONFI_EXTPARAM_SIG, extParamSig); + debug_print_flashcache(mtd); + skipDecodeID = 0; + }else { + uint8_t powerOf2 = 0; + uint8_t eccLevel = 0; + + + u32 = brcmnand_flashcache_read(&powerOf2, + ONFI_EXTPARAM_CODEWORK_OFS - ONFI_EXTPARAM_OFS + (extParamOffset - extParamFCacheOffset), + sizeof(powerOf2)); + + //powerOf2 = (u32 & (0x00FF0000)) >> 16; + *outp_codeWorkSize = 1 << powerOf2; + PRINTK("codeWorkSize power = %d, codeWorkSize=%d, u32=%08x\n", powerOf2, *outp_codeWorkSize, u32); + u32 = brcmnand_flashcache_read(&eccLevel, + ONFI_EXTPARAM_EXT_ECC_OFS - ONFI_EXTPARAM_OFS + (extParamOffset - extParamFCacheOffset), + sizeof(eccLevel)); + *outp_reqEcc = eccLevel; + PRINTK("eccLevel=%d, u32=%08x\n", *outp_reqEcc, u32); + skipDecodeID = 1; + + } + + } + + if (skipDecodeID) { + printk("reqEcc=%d, codeWork=%d\n", *outp_reqEcc, *outp_codeWorkSize); + brcmnand_set_acccontrol(chip, chipSelect, + *outp_pageSize, *outp_oobSize, *outp_reqEcc, *outp_codeWorkSize, nbrBitsPerCell); + } +//gdebug = 0; + + onfi_exit: + + //local_irq_restore(irqflags); + + return skipDecodeID; +} + +#else +/* Non-ONFI chips */ + +#define brcmnand_ONFI_decode(...) (0) + +#endif + + +/** + * brcmnand_probe - [BrcmNAND Interface] Probe the BrcmNAND device + * @param mtd MTD device structure + * + * BrcmNAND detection method: + * Compare the the values from command with ones from register + * + * 8/13/08: + * V3.0+: Add celltype probe for MLC + */ +static int brcmnand_probe(struct mtd_info *mtd, unsigned int chipSelect) +{ + struct brcmnand_chip * chip = mtd->priv; + unsigned char brcmnand_maf_id, brcmnand_dev_id; + uint32_t nand_config = 0; + int version_id; + //int density; + int i = BRCMNAND_MAX_CHIPS + 1; + int isONFI = 0; /* Set when chips (flash & ctrl) are ONFI capable */ + int foundInIdTable = 0; /* Set when flash ID found in ID table */ + int skipIdLookup = 0; + uint32_t __maybe_unused pageSize = 0; + uint16_t __maybe_unused oobSize = 0; + int __maybe_unused reqEcc = 0; + uint32_t __maybe_unused codeWork = 0; + + + /* + * Special treatment for Spansion OrNand chips which do not conform to standard ID + */ + + chip->disableECC = 0; + chip->cellinfo = 0; // default to SLC, will read 3rd byte ID later for v3.0+ controller + chip->eccOobSize = 16; // Will fix it if we have a Type2 ID flash (from which we know the actual OOB size */ + + + isONFI = brcmnand_ONFI_decode(mtd, chipSelect, + &pageSize, &oobSize, &reqEcc, &codeWork); + + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_4_0 + if (isONFI) { /* ONFI capable */ + /* NAND CONFIG register already encoded by NAND controller */ + chip->eccSectorSize = codeWork; + chip->eccOobSize = oobSize / (pageSize / BRCMNAND_FCACHE_SIZE); + if (codeWork == BRCMNAND_FCACHE_SIZE) { + chip->reqEccLevel = reqEcc; + }else { + chip->reqEccLevel = (reqEcc * BRCMNAND_FCACHE_SIZE) / codeWork; + } + /* TBD Check for required ECC level here */ + nand_config = chip->ctrl_read(bchp_nand_config(chipSelect)); + i = BRCMNAND_ONFI_IDX; + } + /* Else fallback to Read ID */ + else +#endif + { + /* Read manufacturer and device IDs from Controller */ + brcmnand_read_id(mtd, chipSelect, &chip->device_id); + + if (chip->device_id == 0) { + printk(KERN_ERR "NAND Flash not detected\n"); + return (-EINVAL); + } + + brcmnand_maf_id = (chip->device_id >> 24) & 0xff; + brcmnand_dev_id = (chip->device_id >> 16) & 0xff; + + /* Look up in our table for infos on device */ + for (i = 0; i < BRCMNAND_MAX_CHIPS; i++) { + if (brcmnand_dev_id == brcmnand_chips[i].chipId + && brcmnand_maf_id == brcmnand_chips[i].mafId) { + + /* No ambiguity in ID#3,4,5 */ + if (brcmnand_chips[i].chipId345[0] == 0x0 + && brcmnand_chips[i].chipId345[1] == 0x0 + && brcmnand_chips[i].chipId345[2] == 0x0) { + foundInIdTable = 1; + break; + } + /* Must resolve ambiguity */ + else if (brcmnand_dev_id == brcmnand_chips[i + 1].chipId + && brcmnand_maf_id == brcmnand_chips[i + 1].mafId) { + + uint32_t extID; + uint8_t id3, id4, id5; + + id3 = (chip->device_id >> 8) & 0xff; + id4 = (chip->device_id & 0xff); + + extID = chip->ctrl_read(BCHP_NAND_FLASH_DEVICE_ID_EXT); + id5 = (extID & 0xff000000) >> 24; + + if (brcmnand_chips[i].chipId345[0] == id3 + && brcmnand_chips[i].chipId345[1] == id4 + && brcmnand_chips[i].chipId345[2] == id5) { + + foundInIdTable = 1; + break; + }else if (brcmnand_chips[i + 1].chipId345[0] == id3 + && brcmnand_chips[i + 1].chipId345[1] == id4 + && brcmnand_chips[i + 1].chipId345[2] == id5) { + + i = i + 1; + foundInIdTable = 1; + break; + } + /* Else not match */ + } + } + } + + if (i >= BRCMNAND_MAX_CHIPS) { +#if CONFIG_MTD_BRCMNAND_VERSION == CONFIG_MTD_BRCMNAND_VERS_0_0 + printk(KERN_ERR "DevId %08x may not be supported\n", (unsigned int)chip->device_id); + /* Because of the bug in the controller in the first version, + * if we can't identify the chip, we punt + */ + return (-EINVAL); +#else + printk(KERN_WARNING "DevId %08x may not be supported. Will use config info\n", (unsigned int)chip->device_id); +#endif + }else { + // Record NOP if known + chip->nop = brcmnand_chips[i].nop; + } + + /* + * Check to see if the NAND chip requires any special controller version + */ + if (brcmnand_chips[i].ctrlVersion > CONFIG_MTD_BRCMNAND_VERSION) { + printk(KERN_ERR "#########################################################\n"); + printk(KERN_ERR "DevId %s requires controller version %d or later, but STB is version %d\n", + brcmnand_chips[i].chipIdStr, brcmnand_chips[i].ctrlVersion, CONFIG_MTD_BRCMNAND_VERSION); + printk(KERN_ERR "#########################################################\n"); +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + return (-EINVAL); +#endif + } + + + // If not on CS0 && config is passed as command line, use it and skip decoding ID. + if (chip->csi != 0 && gNandConfig[chip->csi] != 0) { + skipIdLookup = 1; + nand_config = gNandConfig[chip->csi]; + brcmnand_ctrl_write(bchp_nand_config(chip->ctrl->CS[chip->csi]), nand_config); + }else { + nand_config = brcmnand_ctrl_read(bchp_nand_config(chip->ctrl->CS[chip->csi])); + } + + /*------------- 3rd ID byte --------------------*/ +#if !defined(CONFIG_BCM_KF_MTD_BCMNAND) + if (!skipIdLookup && FLASHTYPE_SPANSION == brcmnand_maf_id) { + unsigned char devId3rdByte = (chip->device_id >> 8) & 0xff; + + switch (devId3rdByte) { + case 0x04: + case 0x00: + /* ECC Needed, device with up to 2% bad blocks */ + break; + + case 0x01: + case 0x03: + /* ECC NOT Needed, device is 100% valid blocks */ + chip->disableECC = 1; + break; + } + /* Correct erase Block Size to read 512K for all Spansion OrNand chips */ + nand_config &= ~(0x3 << 28); + nand_config |= (0x3 << 28); // bit 29:28 = 3 ===> 512K erase block + brcmnand_ctrl_write(bchp_nand_config(chip->ctrl->CS[chip->csi]), nand_config); + } + /* Else if NAND is found in suppported table */ + else if (foundInIdTable) { +#else + if (foundInIdTable) { +#endif + + +#if CONFIG_MTD_BRCMNAND_VERSION == CONFIG_MTD_BRCMNAND_VERS_0_0 + // Workaround for bug in 7400A0 returning invalid config + switch (i) { + case 0: /* SamSung NAND 1Gbit */ + case 1: /* ST NAND 1Gbit */ + case 4: + case 5: + /* Large page, 128K erase block + PAGE_SIZE = 0x1 = 1b = PG_SIZE_2KB + BLOCK_SIZE = 0x1 = 01b = BK_SIZE_128KB + DEVICE_SIZE = 0x5 = 101b = DVC_SIZE_128MB + DEVICE_WIDTH = 0x0 = 0b = DVC_WIDTH_8 + FUL_ADR_BYTES = 5 = 101b + COL_ADR_BYTES = 2 = 010b + BLK_ADR_BYTES = 3 = 011b + */ + nand_config &= ~0x30000000; + nand_config |= 0x10000000; // bit 29:28 = 1 ===> 128K erase block + //nand_config = 0x55042200; //128MB, 0x55052300 for 256MB + brcmnand_ctrl_write(bchp_nand_config(chip->ctrl->CS[chip->csi]), nand_config); + + break; + + case 2: + case 3: + /* Small page, 16K erase block + PAGE_SIZE = 0x0 = 0b = PG_SIZE_512B + BLOCK_SIZE = 0x0 = 0b = BK_SIZE_16KB + DEVICE_SIZE = 0x5 = 101b = DVC_SIZE_128MB + DEVICE_WIDTH = 0x0 = 0b = DVC_WIDTH_8 + FUL_ADR_BYTES = 5 = 101b + COL_ADR_BYTES = 2 = 010b + BLK_ADR_BYTES = 3 = 011b + */ + nand_config &= ~0x70000000; + brcmnand_ctrl_write(bchp_nand_config(chip->ctrl->CS[chip->csi]), nand_config); + + break; + + default: + printk(KERN_ERR "%s: DevId %08x not supported\n", __FUNCTION__, (unsigned int)chip->device_id); + BUG(); + break; + } +/* NAND VERSION 7.1 use two config register, need to update all of decode_id_xxx function. But these special chips + should already be supported in 7.1 and no manual id decoding is needed */ +#elif (CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_3_0 && CONFIG_MTD_BRCMNAND_VERSION <= CONFIG_MTD_BRCMNAND_VERS_7_0) + + if ((brcmnand_chips[i].idOptions & BRCMNAND_ID_EXT_BYTES) == + BRCMNAND_ID_EXT_BYTES || + (brcmnand_chips[i].idOptions & BRCMNAND_ID_EXT_BYTES_TYPE2) == + BRCMNAND_ID_EXT_BYTES_TYPE2 || + (brcmnand_chips[i].idOptions & BRCMNAND_ID_EXT_MICRON_M60A) == + BRCMNAND_ID_EXT_MICRON_M60A || + (brcmnand_chips[i].idOptions & BRCMNAND_ID_EXT_MICRON_M61A) == + BRCMNAND_ID_EXT_MICRON_M61A + ) { + unsigned char devId3rdByte = (chip->device_id >> 8) & 0xff; + + chip->cellinfo = devId3rdByte & NAND_CI_CELLTYPE_MSK; + + /* Read 5th ID byte if MLC type */ + //if (chip->cellinfo) + + /* THT SWLINUX 1459: Some new SLCs have 5th ID byte defined, not just MLC */ + /* Type-1 ID string */ + if ((brcmnand_chips[i].idOptions & BRCMNAND_ID_HAS_BYTE4) && + (brcmnand_chips[i].idOptions & BRCMNAND_ID_HAS_BYTE5)) { + nand_config = decode_ID_type1(chip, brcmnand_maf_id, brcmnand_dev_id, + brcmnand_chips[i].idOptions, brcmnand_chips[i].nbrBlocks); + } + /* Type-2 ID string */ + else if ((brcmnand_chips[i].idOptions & BRCMNAND_ID_EXT_BYTES_TYPE2) == + BRCMNAND_ID_EXT_BYTES_TYPE2) { + brcmnand_chips[i].eccLevel = 0; + nand_config = decode_ID_type2(chip, brcmnand_maf_id, brcmnand_dev_id, + brcmnand_chips[i].nbrBlocks, + &brcmnand_chips[i].eccLevel, + &brcmnand_chips[i].sectorSize); + }else if ((brcmnand_chips[i].idOptions & BRCMNAND_ID_EXT_MICRON_M61A) == + BRCMNAND_ID_EXT_MICRON_M61A) { + nand_config = decode_ID_M61A(chip, brcmnand_maf_id, brcmnand_dev_id); + } + + if (!skipIdLookup) { + + /* Make sure that ColAddrBytes bits are correct */ + nand_config = brcmnand_compute_adr_bytes(chip, nand_config); + + chip->ctrl_write(bchp_nand_config(chip->ctrl->CS[chip->csi]), nand_config); + + PRINTK("%s: NAND_CONFIG=%08x\n", __FUNCTION__, nand_config); + } + + + } + + /* Else no 3rd ID byte, rely on NAND controller to identify the chip + else { + } + */ +#endif // V3.0 Controller + if (foundInIdTable && brcmnand_chips[i].eccLevel) { + if (brcmnand_chips[i].sectorSize == 1024) { + chip->reqEccLevel = brcmnand_chips[i].eccLevel; + chip->eccSectorSize = 1024; + }else { + chip->reqEccLevel = brcmnand_chips[i].eccLevel; + chip->eccSectorSize = 512; + } + switch (chip->reqEccLevel) { + case 15: + chip->ecclevel = BRCMNAND_ECC_HAMMING; + break; + case 4: + chip->ecclevel = BRCMNAND_ECC_BCH_4; + break; + case 8: + chip->ecclevel = BRCMNAND_ECC_BCH_8; + break; + case 12: + case 24: + chip->ecclevel = BRCMNAND_ECC_BCH_12; + break; + } + printk("%s: Ecc level set to %d, sectorSize=%d from ID table\n", __FUNCTION__, chip->reqEccLevel, chip->eccSectorSize); + } + } + /* ID not in table, and no CONFIG REG was passed at command line */ + else if (!skipIdLookup && !foundInIdTable) { +#if CONFIG_MTD_BRCMNAND_VERSION > CONFIG_MTD_BRCMNAND_VERS_2_2 + uint32_t acc; + + /* + * else chip ID not found in table, just use what the NAND controller says. + * We operate under the premise that if it goes this far, the controller/CFE may + * have done something right. It is not guaranteed to work, however + */ + /* + * Do nothing, we will decode the controller CONFIG register for + * for flash geometry + */ + + /* + * Also, we need to find out the size of the OOB from ACC_CONTROL reg + */ + acc = brcmnand_ctrl_read(bchp_nand_acc_control(chip->ctrl->CS[chip->csi])); + chip->eccOobSize = + (acc & BCHP_NAND_ACC_CONTROL_SPARE_AREA_SIZE_MASK) >> + BCHP_NAND_ACC_CONTROL_SPARE_AREA_SIZE_SHIFT; + + printk("Spare Area Size = %dB/512B\n", chip->eccOobSize); +#endif + nand_config = chip->ctrl_read(bchp_nand_config(chip->ctrl->CS[chip->csi])); + } + } + + /* + * else ID not in database, but CONFIG reg was passed at command line, already handled + */ + + /* + * For some ID case, the ID decode does not yield all informations, + * so we read it back, making sure that NAND CONFIG register and chip-> struct + * have matching infos. + */ + brcmnand_decode_config(chip, nand_config); + + // Also works for dummy entries, but no adjustments possible + brcmnand_adjust_timings(chip, &brcmnand_chips[i]); + +#if CONFIG_MTD_BRCMNAND_VERSION > CONFIG_MTD_BRCMNAND_VERS_2_2 + // Adjust perchip NAND ACC CONTROL + // updateInternalData = not ONFI .or. not in ID table + brcmnand_adjust_acccontrol(chip, isONFI, foundInIdTable, i); +#endif + + /* Flash device information */ + brcmnand_print_device_info(&brcmnand_chips[i], mtd); + chip->options = brcmnand_chips[i].options; + + /* BrcmNAND page size & block size */ + mtd->writesize = chip->pageSize; + mtd->writebufsize = mtd->writesize; + // OOB size for MLC NAND varies depend on the chip +#if CONFIG_MTD_BRCMNAND_VERSION < CONFIG_MTD_BRCMNAND_VERS_3_0 + mtd->oobsize = mtd->writesize >> 5; // tht - 16 byte OOB for 512B page, 64B for 2K page +#else + chip->eccsteps = chip->pageSize / chip->eccsize; + mtd->oobsize = chip->eccOobSize * chip->eccsteps; +#endif + mtd->erasesize = chip->blockSize; + + /* Fix me: When we have both a NOR and NAND flash on board */ + /* For now, we will adjust the mtd->size for version 0.0 and 0.1 later in scan routine */ + + if (chip->ctrl->numchips == 0) + chip->ctrl->numchips = 1; + +#if 0 +/* This is old codes, now after we switch to support multiple configs, size is per chip size */ + chip->mtdSize = chip->chipSize * chip->ctrl->numchips; + + /* + * THT: This is tricky. We use mtd->size == 0 as an indicator whether the size + * fit inside a uint32_t. In the case it overflow, size is returned by + * the inline function device_size(mtd), which is num_eraseblocks*block_size + */ + if (mtd64_ll_high(chip->mtdSize)) { // Beyond 4GB limit + mtd->size = 0; + }else { + mtd->size = mtd64_ll_low(chip->mtdSize); + } +/* */ +#endif + + mtd->size = chip->mtdSize = chip->chipSize; + + + //mtd->num_eraseblocks = chip->mtdSize >> chip->erase_shift; + + /* Version ID */ + version_id = chip->ctrl_read(BCHP_NAND_REVISION); + + printk(KERN_INFO "BrcmNAND version = 0x%04x %dMB @%08lx\n", + version_id, mtd64_ll_low(chip->chipSize >> 20), chip->pbase); + + gdebug = 0; + + return 0; +} + +/** + * brcmnand_suspend - [MTD Interface] Suspend the BrcmNAND flash + * @param mtd MTD device structure + */ +static int brcmnand_suspend(struct mtd_info *mtd) +{ + DEBUG(MTD_DEBUG_LEVEL3, "-->%s \n", __FUNCTION__); + return brcmnand_get_device(mtd, BRCMNAND_FL_PM_SUSPENDED); +} + +/** + * brcmnand_resume - [MTD Interface] Resume the BrcmNAND flash + * @param mtd MTD device structure + */ +static void brcmnand_resume(struct mtd_info *mtd) +{ + struct brcmnand_chip * chip = mtd->priv; + + DEBUG(MTD_DEBUG_LEVEL3, "-->%s \n", __FUNCTION__); + if (chip->ctrl->state == BRCMNAND_FL_PM_SUSPENDED) + brcmnand_release_device(mtd); + else + printk(KERN_ERR "resume() called for the chip which is not" + "in suspended state\n"); +} + +#if 0 + +static void fill_ecccmp_mask(struct mtd_info *mtd) +{ + struct brcmnand_chip * chip = mtd->priv; + int i, len; + + struct nand_oobfree *free = chip->ecclayout->oobfree; + unsigned char* myEccMask = (unsigned char*)eccmask; // Defeat const + + /* + * Should we rely on eccmask being zeroed out + */ + for (i = 0; i < ARRAY_SIZE(eccmask); i++) { + myEccMask[i] = 0; + } + /* Write 0xFF where there is a free byte */ + for (i = 0, len = 0; + len < chip->oobavail && len < mtd->oobsize && i < MTD_MAX_OOBFREE_ENTRIES; + i++) { + int to = free[i].offset; + int num = free[i].length; + + if (num == 0) break; // End marker reached + memcpy(&myEccMask[to], ffchars, num); + len += num; + } +} +#endif + + +#if CONFIG_MTD_BRCMNAND_VERSION < CONFIG_MTD_BRCMNAND_VERS_3_3 +/* Not needed when version >=3.3, as newer chip allow different NAND */ + +/* + * Make sure that all NAND chips have same ID + */ +static int +brcmnand_validate_cs(struct mtd_info *mtd ) +{ + +#if CONFIG_MTD_BRCMNAND_VERSION < CONFIG_MTD_BRCMNAND_VERS_3_3 + struct brcmnand_chip* chip = (struct brcmnand_chip*)mtd->priv; + int i; + unsigned long dev_id; + + // Now verify that a NAND chip is at the CS + for (i = 0; i < chip->ctrl->numchips; i++) { + brcmnand_read_id(mtd, chip->ctrl->CS[i], &dev_id); + + if (dev_id != chip->device_id) { + printk(KERN_ERR "Device ID for CS[%1d] = %08lx, Device ID for CS[%1d] = %08lx\n", + chip->ctrl->CS[0], chip->device_id, chip->ctrl->CS[i], dev_id); + return 1; + } + + printk("Found NAND flash on Chip Select %d, chipSize=%dMB, usable size=%dMB, base=%lx\n", + chip->ctrl->CS[i], mtd64_ll_low(chip->chipSize >> 20), + mtd64_ll_low(device_size(mtd) >> 20), chip->pbase); + + } + return 0; + +#else + /* Version 3.3 and later allows multiple IDs */ + struct brcmnand_chip* chip = (struct brcmnand_chip*)mtd->priv; + int i; + unsigned long dev_id; + + // Now verify that a NAND chip is at the CS + for (i = 0; i < chip->ctrl->numchips; i++) { + brcmnand_read_id(mtd, chip->ctrl->CS[i], &dev_id); + +/* + if (dev_id != chip->device_id) { + printk(KERN_ERR "Device ID for CS[%1d] = %08lx, Device ID for CS[%1d] = %08lx\n", + chip->ctrl->CS[0], chip->device_id, chip->ctrl->CS[i], dev_id); + return 1; + } + */ + printk("Found NAND flash on Chip Select %d, chipSize=%dMB, usable size=%dMB, base=%lx\n", + chip->ctrl->CS[i], mtd64_ll_low(chip->chipSize >> 20), + mtd64_ll_low(device_size(mtd) >> 20), chip->pbase); + + } + return 0; +#endif +} + +#endif /* Version < 3.3 */ + +#if 0 /* jipeng - avoid undefined variable error in 7408A0 */ +/* + * CS0 reset values are gone by now, since the bootloader disabled CS0 before booting Linux + * in order to give the EBI address space to NAND. + * We will need to read strap_ebi_rom_size in order to reconstruct the CS0 values + * This will not be a problem, since in order to boot with NAND on CSn (n != 0), the board + * must be strapped for NOR. + */ +static unsigned int __maybe_unused +get_rom_size(unsigned long* outp_cs0Base) +{ + volatile unsigned long strap_ebi_rom_size, sun_top_ctrl_strap_value; + uint32_t romSize = 0; + +#if defined(BCHP_SUN_TOP_CTRL_STRAP_VALUE_0_strap_ebi_rom_size_MASK) + sun_top_ctrl_strap_value = (volatile unsigned long)BDEV_RD(BCHP_SUN_TOP_CTRL_STRAP_VALUE_0); + strap_ebi_rom_size = sun_top_ctrl_strap_value & BCHP_SUN_TOP_CTRL_STRAP_VALUE_0_strap_ebi_rom_size_MASK; + strap_ebi_rom_size >>= BCHP_SUN_TOP_CTRL_STRAP_VALUE_0_strap_ebi_rom_size_SHIFT; +#elif defined(BCHP_SUN_TOP_CTRL_STRAP_VALUE_strap_ebi_rom_size_MASK) + sun_top_ctrl_strap_value = (volatile unsigned long)BDEV_RD(BCHP_SUN_TOP_CTRL_STRAP_VALUE); + strap_ebi_rom_size = sun_top_ctrl_strap_value & BCHP_SUN_TOP_CTRL_STRAP_VALUE_strap_ebi_rom_size_MASK; + strap_ebi_rom_size >>= BCHP_SUN_TOP_CTRL_STRAP_VALUE_strap_ebi_rom_size_SHIFT; +#elif defined(BCHP_SUN_TOP_CTRL_STRAP_VALUE_0_strap_bus_mode_MASK) + romSize = 512 << 10; /* 512K */ + *outp_cs0Base = 0x1FC00000; + return romSize; +#elif !defined(CONFIG_BRCM_HAS_NOR) + printk("FIXME: no strap option for rom size on 3548/7408\n"); + BUG(); +#else + /* all new 40nm chips */ + return 64 << 20; +#endif + + // Here we expect these values to remain the same across platforms. + // Some customers want to have a 2MB NOR flash, but I don't see how that is possible. + switch (strap_ebi_rom_size) { + case 0: + romSize = 64 << 20; + *outp_cs0Base = (0x20000000 - romSize) | BCHP_EBI_CS_BASE_0_size_SIZE_64MB; + break; + case 1: + romSize = 16 << 20; + *outp_cs0Base = (0x20000000 - romSize) | BCHP_EBI_CS_BASE_0_size_SIZE_16MB; + break; + case 2: + romSize = 8 << 20; + *outp_cs0Base = (0x20000000 - romSize) | BCHP_EBI_CS_BASE_0_size_SIZE_8MB; + break; + case 3: + romSize = 4 << 20; + *outp_cs0Base = (0x20000000 - romSize) | BCHP_EBI_CS_BASE_0_size_SIZE_4MB; + break; + default: + printk("%s: Impossible Strap Value %08lx for BCHP_SUN_TOP_CTRL_STRAP_VALUE\n", + __FUNCTION__, sun_top_ctrl_strap_value); + BUG(); + } + return romSize; +} +#endif + + +static void brcmnand_prepare_reboot_priv(struct mtd_info *mtd) +{ + /* + * Must set NAND back to Direct Access mode for reboot, but only if NAND is on CS0 + */ + + struct brcmnand_chip* this; + +#ifdef CONFIG_MTD_BRCMNAND_CORRECTABLE_ERR_HANDLING + /* Flush pending in-mem CET to flash before exclusive lock */ + if (mtd) { + brcmnand_cet_prepare_reboot(mtd); + } +#endif + if (mtd) { + this = (struct brcmnand_chip*)mtd->priv; + brcmnand_get_device(mtd, BRCMNAND_FL_XIP); + }else + /* Nothing we can do without an mtd handle */ + return; + +#if 0 +/* No longer used. We now required the mtd handle */ + else { + /* + * Prevent further access to the NAND flash, we are rebooting + */ + this = brcmnand_get_device_exclusive(); + } +#endif + +#if 0 /* jipeng - avoid undefined variable error in 7408A0 */ + // PR41560: Handle boot from NOR but open NAND flash for access in Linux + //if (!is_bootrom_nand()) { + if (0) { + // Restore CS0 in order to allow boot from NOR. + + //int ret = -EFAULT; + int i; + int csNand; // Which CS is NAND + volatile unsigned long cs0Base, cs0Cnfg, cs0BaseAddr, csNandSelect, extAddr; + volatile unsigned long csNandBase[MAX_NAND_CS], csNandCnfg[MAX_NAND_CS]; + unsigned int romSize; + + romSize = get_rom_size((unsigned long*)&cs0Base); +//printk("ROM size is %dMB\n", romSize >>20); + + cs0BaseAddr = cs0Base & BCHP_EBI_CS_BASE_0_base_addr_MASK; + + cs0Cnfg = *(volatile unsigned long*)(0xb0000000 | BCHP_EBI_CS_CONFIG_0); + + // Turn off NAND CS + for (i = 0; i < this->numchips; i++) { + csNand = this->CS[i]; + + if (csNand == 0) { + printk("%s: Call this routine only if NAND is not on CS0\n", __FUNCTION__); + } + +#if CONFIG_MTD_BRCMNAND_VERSION < CONFIG_MTD_BRCMNAND_VERS_1_0 + BUG_ON(csNand > 5); +#else + BUG_ON(csNand > 8); +#endif + csNandBase[i] = *(volatile unsigned long*)(0xb0000000 + BCHP_EBI_CS_BASE_0 + 8 * csNand); + csNandCnfg[i] = *(volatile unsigned long*)(0xb0000000 + BCHP_EBI_CS_CONFIG_0 + 8 * csNand); + + // Turn off NAND, must turn off both NAND_CS_NAND_SELECT and CONFIG. + // We turn off the CS_CONFIG here, and will turn off NAND_CS_NAND_SELECT for all CS at once, + // outside the loop. + *(volatile unsigned long*)(0xb0000000 + BCHP_EBI_CS_CONFIG_0 + 8 * csNand) = + csNandCnfg[i] & (~BCHP_EBI_CS_CONFIG_0_enable_MASK); + + } + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_0_1 + csNandSelect = brcmnand_ctrl_read(BCHP_NAND_CS_NAND_SELECT); + + + csNandSelect &= + ~( +#if CONFIG_MTD_BRCMNAND_VERSION < CONFIG_MTD_BRCMNAND_VERS_1_0 + BCHP_NAND_CS_NAND_SELECT_EBI_CS_5_SEL_MASK + | BCHP_NAND_CS_NAND_SELECT_EBI_CS_4_SEL_MASK + | BCHP_NAND_CS_NAND_SELECT_EBI_CS_3_SEL_MASK + | BCHP_NAND_CS_NAND_SELECT_EBI_CS_2_SEL_MASK + | BCHP_NAND_CS_NAND_SELECT_EBI_CS_1_SEL_MASK + | BCHP_NAND_CS_NAND_SELECT_EBI_CS_0_SEL_MASK +#else + 0x0000003E /* Not documented on V1.0+ */ +#endif // Version < 1.0 + ); +#endif // version >= 0.1 + brcmnand_ctrl_write(BCHP_NAND_CS_NAND_SELECT, csNandSelect); + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_1_0 + // THT from TM/RP: 020609: Clear NAND_CS_NAND_SELECT_AUTO_DEVICE_ID_CONFIG + csNandSelect &= ~(BCHP_NAND_CS_NAND_SELECT_AUTO_DEVICE_ID_CONFIG_MASK); + brcmnand_ctrl_write(BCHP_NAND_CS_NAND_SELECT, csNandSelect); + + // THT from TM/RP: 020609: Clear NAND_CMD_EXT_ADDRESS_CS_SEL + extAddr = brcmnand_ctrl_read(BCHP_NAND_CMD_EXT_ADDRESS); + extAddr &= ~(BCHP_NAND_CMD_EXT_ADDRESS_CS_SEL_MASK); + brcmnand_ctrl_write(BCHP_NAND_CMD_EXT_ADDRESS, extAddr); +#endif + +//printk("Turn on NOR\n"); + // Turn on NOR on CS0 + *(volatile unsigned long*)(0xb0000000 | BCHP_EBI_CS_CONFIG_0) = + cs0Cnfg | BCHP_EBI_CS_CONFIG_0_enable_MASK; + +//printk("returning from reboot\n"); + // We have turned on NOR, just return, leaving NAND locked + // The CFE will straighten out everything. + return; + } +#endif /* 0 */ + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_1_0 + // Otherwise if NAND is on CS0, turn off direct access before rebooting + if (this->ctrl->CS[0] == 0) { // Only if on CS0 + volatile unsigned long nand_select, ext_addr; + + // THT: Set Direct Access bit + nand_select = brcmnand_ctrl_read(BCHP_NAND_CS_NAND_SELECT); + //printk("%s: B4 nand_select = %08x\n", __FUNCTION__, (uint32_t) nand_select); + nand_select |= BCHP_NAND_CS_NAND_SELECT_EBI_CS_0_SEL_MASK; + + // THT from TM/RP: 020609: Clear NAND_CS_NAND_SELECT_AUTO_DEVICE_ID_CONFIG + nand_select &= ~(BCHP_NAND_CS_NAND_SELECT_AUTO_DEVICE_ID_CONFIG_MASK); + brcmnand_ctrl_write(BCHP_NAND_CS_NAND_SELECT, nand_select); + //nand_select = brcmnand_ctrl_read(BCHP_NAND_CS_NAND_SELECT); + //printk("%s: After nand_select = %08x\n", __FUNCTION__, (uint32_t) nand_select); + + // THT from TM/RP: 020609: Clear NAND_CMD_EXT_ADDRESS_CS_SEL + ext_addr = brcmnand_ctrl_read(BCHP_NAND_CMD_EXT_ADDRESS); + ext_addr &= ~(BCHP_NAND_CMD_EXT_ADDRESS_CS_SEL_MASK); + brcmnand_ctrl_write(BCHP_NAND_CMD_EXT_ADDRESS, ext_addr); + } + +#endif //#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_1_0 + + + return; +} + +#if 0 +// In case someone reboot w/o going thru the MTD notifier mechanism. +void brcmnand_prepare_reboot(void) +{ + brcmnand_prepare_reboot_priv(NULL); +} +EXPORT_SYMBOL(brcmnand_prepare_reboot); +#endif + + +static int brcmnand_reboot_cb(struct notifier_block *nb, unsigned long val, void *v) +{ + struct mtd_info *mtd; + + mtd = container_of(nb, struct mtd_info, reboot_notifier); + brcmnand_prepare_reboot_priv(mtd); + return NOTIFY_DONE; +} + +static void initialize_chip(struct brcmnand_chip* chip) +{ + + /* Initialize chip level routines */ + + if (!chip->ctrl_read) + chip->ctrl_read = brcmnand_ctrl_read; + if (!chip->ctrl_write) + chip->ctrl_write = brcmnand_ctrl_write; + if (!chip->ctrl_writeAddr) + chip->ctrl_writeAddr = brcmnand_ctrl_writeAddr; + +#if 0 + if (!chip->read_raw) + chip->read_raw = brcmnand_read_raw; + if (!chip->read_pageoob) + chip->read_pageoob = brcmnand_read_pageoob; +#endif + + if (!chip->write_is_complete) + chip->write_is_complete = brcmnand_write_is_complete; + + if (!chip->wait) + chip->wait = brcmnand_wait; + + if (!chip->block_markbad) + chip->block_markbad = brcmnand_default_block_markbad; + if (!chip->scan_bbt) + chip->scan_bbt = brcmnand_default_bbt; + if (!chip->erase_bbt) + chip->erase_bbt = brcmnand_erase_bbt; + + chip->eccsize = BRCMNAND_FCACHE_SIZE; // Fixed for Broadcom controller + + + /* + * For now initialize ECC read ops using the controller version, will switch to ISR version after + * EDU has been enabled + */ + + if (!chip->read_page) + chip->read_page = brcmnand_read_page; + if (!chip->write_page) + chip->write_page = brcmnand_write_page; + if (!chip->read_page_oob) + chip->read_page_oob = brcmnand_read_page_oob; + if (!chip->write_page_oob) + chip->write_page_oob = brcmnand_write_page_oob; + + if (!chip->read_oob) + chip->read_oob = brcmnand_do_read_ops; + if (!chip->write_oob) + chip->write_oob = brcmnand_do_write_ops; +} + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_2_0 +static void handle_xor(struct brcmnand_chip* chip) +{ + //int i; + uint32_t nand_xor; + uint32_t __maybe_unused nand_select; + + /* + * 2618-7.3: For v2.0 or later, set xor_disable according to NAND_CS_NAND_XOR:00 bit + */ + + nand_xor = brcmnand_ctrl_read(BCHP_NAND_CS_NAND_XOR); + printk("NAND_CS_NAND_XOR=%08x\n", nand_xor); + // +#ifdef CONFIG_MTD_BRCMNAND_DISABLE_XOR +/* Testing 1,2,3: Force XOR disable on CS0, if not done by CFE */ + if (chip->ctrl->CS[0] == 0) { + printk("Disabling XOR: Before: SEL=%08x, XOR=%08x\n", nand_select, nand_xor); + + nand_select &= ~BCHP_NAND_CS_NAND_SELECT_EBI_CS_0_SEL_MASK; + nand_xor &= ~BCHP_NAND_CS_NAND_XOR_EBI_CS_0_ADDR_1FC0_XOR_MASK; + + brcmnand_ctrl_write(BCHP_NAND_CS_NAND_SELECT, nand_select); + brcmnand_ctrl_write(BCHP_NAND_CS_NAND_XOR, nand_xor); + + printk("Disabling XOR: After: SEL=%08x, XOR=%08x\n", nand_select, nand_xor); + } +#endif + /* Translate nand_xor into our internal flag, for brcmnand_writeAddr */ + // for (i=0; ictrl->numchips; i++) + //i = chip->csi; + + + /* Set xor_disable, 1 for each NAND chip */ + if (!(nand_xor & (BCHP_NAND_CS_NAND_XOR_EBI_CS_0_ADDR_1FC0_XOR_MASK << chip->ctrl->CS[chip->csi]))) { + PRINTK("Disabling XOR on CS#%1d\n", chip->ctrl->CS[chip->csi]); + chip->xor_disable = 1; + } + + +} +#endif /* v2.0 or later */ + +#if CONFIG_MTD_BRCMNAND_VERSION <= CONFIG_MTD_BRCMNAND_VERS_0_1 + +/* + * Version 0.1 can only have Hamming, so + * the problem is handle the flash EBI base address + */ +static void handle_ecclevel_v0_1(struct mtd_info *mtd, struct brcmnand_chip* chip, int cs) +{ + if (cs) { + volatile unsigned long wr_protect; + volatile unsigned long acc_control; + + chip->ctrl->numchips = 1; + + /* Set up base, based on flash size */ + if (chip->chipSize >= (256 << 20)) { + chip->pbase = 0x12000000; + mtd->size = 0x20000000 - chip->pbase; // THT: This is different than chip->chipSize + } else { + /* We know that flash endAddr is 0x2000_0000 */ + chip->pbase = 0x20000000 - chip->chipSize; + mtd->size = chip->chipSize; + } + + printk("Found NAND chip on Chip Select %d, chipSize=%dMB, usable size=%dMB, base=%08x\n", + (int)cs, mtd64_ll_low(chip->chipSize >> 20), mtd64_ll_low(device_size(mtd) >> 20), (unsigned int)chip->pbase); + + + + /* + * When NAND is on CS0, it reads the strap values and set up accordingly. + * WHen on CS1, some configurations must be done by SW + */ + + // Set Write-Unprotect. This register is sticky, so if someone already set it, we are out of luck + wr_protect = brcmnand_ctrl_read(BCHP_NAND_BLK_WR_PROTECT); + if (wr_protect) { + printk("Unprotect Register B4: %08x. Please do a hard power recycle to reset\n", (unsigned int)wr_protect); + // THT: Actually we should punt here, as we cannot zero the register. + } + brcmnand_ctrl_write(BCHP_NAND_BLK_WR_PROTECT, 0); // This will not work. + if (wr_protect) { + printk("Unprotect Register after: %08x\n", brcmnand_ctrl_read(BCHP_NAND_BLK_WR_PROTECT)); + } + + // Enable HW ECC. This is another sticky register. + acc_control = brcmnand_ctrl_read(bchp_nand_acc_control(cs)); + printk("ACC_CONTROL B4: %08x\n", (unsigned int)acc_control); + + brcmnand_ctrl_write(bchp_nand_acc_control(cs), acc_control | BCHP_NAND_ACC_CONTROL_RD_ECC_BLK0_EN_MASK); + if (!(acc_control & BCHP_NAND_ACC_CONTROL_RD_ECC_BLK0_EN_MASK)) { + printk("ACC_CONTROL after: %08x\n", brcmnand_ctrl_read(bchp_nand_acc_control(cs))); + } + }else { + /* NAND chip on Chip Select 0 */ + chip->ctrl->CS[0] = 0; + + chip->ctrl->numchips = 1; + + /* Set up base, based on flash size */ + if (chip->chipSize >= (256 << 20)) { + chip->pbase = 0x12000000; + mtd->size = 0x20000000 - chip->pbase; // THT: This is different than chip->chipSize + } else { + /* We know that flash endAddr is 0x2000_0000 */ + chip->pbase = 0x20000000 - chip->chipSize; + mtd->size = chip->chipSize; + } + //mtd->size_hi = 0; + chip->mtdSize = mtd->size; + + printk("Found NAND chip on Chip Select 0, size=%dMB, base=%08x\n", mtd->size >> 20, (unsigned int)chip->pbase); + + } + chip->vbase = (void*)KSEG1ADDR(chip->pbase); +} + +#elif CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_3_0 +/* Version 3.0 or later */ +static uint32_t +handle_acc_control(struct mtd_info *mtd, struct brcmnand_chip* chip, int cs) +{ + volatile unsigned long acc_control, org_acc_control; + int csi = chip->csi; // Index into chip->ctrl->CS array + unsigned long eccLevel = 0, eccLevel_0, eccLevel_n; + uint32_t eccOobSize; + + if (gAccControl[csi] != 0) { + // Already done in brcmnand_adjust_acccontrol() + printk("ECC level from command line=%d\n", chip->ecclevel); + return chip->ecclevel; // Do nothing, take the overwrite value + } + + #if CONFIG_MTD_BRCMNAND_VERSION < CONFIG_MTD_BRCMNAND_VERS_3_3 + + + PRINTK("100 CS=%d, chip->ctrl->CS[%d]=%d\n", cs, chip->csi, chip->ctrl->CS[chip->csi]); + + org_acc_control = acc_control = brcmnand_ctrl_read(bchp_nand_acc_control(cs)); + + /* + * For now, we only support same ECC level for both block0 and other blocks + */ + // Verify BCH-4 ECC: Handle CS0 block0 + + // ECC level for block-0 + eccLevel = eccLevel_0 = (acc_control & BCHP_NAND_ACC_CONTROL_ECC_LEVEL_0_MASK) >> + BCHP_NAND_ACC_CONTROL_ECC_LEVEL_0_SHIFT; + // ECC level for all other blocks. + eccLevel_n = (acc_control & BCHP_NAND_ACC_CONTROL_ECC_LEVEL_MASK) >> + BCHP_NAND_ACC_CONTROL_ECC_LEVEL_SHIFT; + + // make sure that block-0 and block-n use the same ECC level. + if (eccLevel_0 != eccLevel_n) { + // Use eccLevel_0 for eccLevel_n, unless eccLevel_0 is 0. + if (eccLevel_0 == 0) { + eccLevel = eccLevel_n; + } + acc_control &= ~(BCHP_NAND_ACC_CONTROL_ECC_LEVEL_0_MASK | + BCHP_NAND_ACC_CONTROL_ECC_LEVEL_MASK); + acc_control |= (eccLevel << BCHP_NAND_ACC_CONTROL_ECC_LEVEL_0_SHIFT) | + (eccLevel << BCHP_NAND_ACC_CONTROL_ECC_LEVEL_SHIFT); + brcmnand_ctrl_write(bchp_nand_acc_control(cs), acc_control ); + + if (eccLevel == eccLevel_0) { + printk("Corrected ECC on block-n to ECC on block-0: ACC = %08lx from %08lx\n", + acc_control, org_acc_control); + }else { + printk("Corrected ECC on block-0 to ECC on block-n: ACC = %08lx from %08lx\n", + acc_control, org_acc_control); + } + + } + chip->ecclevel = eccLevel; + + + switch (eccLevel) { + case BRCMNAND_ECC_HAMMING: + if (NAND_IS_MLC(chip)) { + printk(KERN_INFO "Only BCH-4 or better is supported on MLC flash\n"); + chip->ecclevel = BRCMNAND_ECC_BCH_4; + acc_control &= ~(BCHP_NAND_ACC_CONTROL_ECC_LEVEL_0_MASK | + BCHP_NAND_ACC_CONTROL_ECC_LEVEL_MASK); + acc_control |= (BRCMNAND_ECC_BCH_4 << BCHP_NAND_ACC_CONTROL_ECC_LEVEL_0_SHIFT) | + (BRCMNAND_ECC_BCH_4 << BCHP_NAND_ACC_CONTROL_ECC_LEVEL_SHIFT); + brcmnand_ctrl_write(bchp_nand_acc_control(cs), acc_control ); + printk("Corrected ECC to BCH-4 for MLC flashes: ACC_CONTROL = %08lx from %08lx\n", acc_control, org_acc_control); + } + break; + + case BRCMNAND_ECC_BCH_4: + case BRCMNAND_ECC_BCH_8: + case BRCMNAND_ECC_BCH_12: + // eccOobSize is initialized to the board strap of ECC-level + eccOobSize = (acc_control & BCHP_NAND_ACC_CONTROL_SPARE_AREA_SIZE_MASK) >> + BCHP_NAND_ACC_CONTROL_SPARE_AREA_SIZE_SHIFT; + printk("ACC: %d OOB bytes per 512B ECC step; from ID probe: %d\n", eccOobSize, chip->eccOobSize); + //Make sure that the OOB size is >= 27 + if (eccLevel == BRCMNAND_ECC_BCH_12 && chip->eccOobSize < 27) { + printk(KERN_INFO "BCH-12 requires >=27 OOB bytes per ECC step.\n"); + printk(KERN_INFO "Please fix your board straps. Aborting to avoid file system damage\n"); + BUG(); + } + // We have recorded chip->eccOobSize during probe, let's compare it against value from straps: + if (chip->eccOobSize < eccOobSize) { + printk("Flash says it has %d OOB bytes, eccLevel=%lu, but board strap says %d bytes, fixing it...\n", + chip->eccOobSize, eccLevel, eccOobSize); + acc_control &= ~(BCHP_NAND_ACC_CONTROL_SPARE_AREA_SIZE_0_MASK \ + | BCHP_NAND_ACC_CONTROL_SPARE_AREA_SIZE_MASK); + acc_control |= (chip->eccOobSize << BCHP_NAND_ACC_CONTROL_SPARE_AREA_SIZE_0_SHIFT) + | (chip->eccOobSize << BCHP_NAND_ACC_CONTROL_SPARE_AREA_SIZE_SHIFT); + printk("ACC_CONTROL adjusted to %08x\n", (unsigned int)acc_control); + brcmnand_ctrl_write(bchp_nand_acc_control(cs), acc_control ); + } + + break; + + default: + printk(KERN_ERR "Unsupported ECC level %lu\n", eccLevel); + BUG(); + + } + + + chip->ecclevel = eccLevel; + //csi++; // Look at next CS + + + /* + * PR57272: Workaround for BCH-n error, + * reporting correctable errors with 4 or more bits as uncorrectable: + */ + if (chip->ecclevel != 0 && chip->ecclevel != BRCMNAND_ECC_HAMMING) { + int corr_threshold; + + if (chip->ecclevel > BRCMNAND_ECC_BCH_4) { + printk(KERN_WARNING "%s: Architecture cannot support ECC level %d\n", __FUNCTION__, chip->ecclevel); + corr_threshold = 3; + }else if ( chip->ecclevel == BRCMNAND_ECC_BCH_4) { + corr_threshold = 3; // Changed from 2, since refresh is costly and vulnerable to AC-ON/OFF tests. + }else { + corr_threshold = 1; // 1 , default for Hamming + } + + printk(KERN_INFO "%s: CORR ERR threshold set to %d bits\n", __FUNCTION__, corr_threshold); + corr_threshold <<= BCHP_NAND_CORR_STAT_THRESHOLD_CORR_STAT_THRESHOLD_SHIFT; + brcmnand_ctrl_write(BCHP_NAND_CORR_STAT_THRESHOLD, corr_threshold); + } + + #else /* NAND version 3.3 or later */ + + PRINTK("100 CS=%d, chip->ctrl->CS[%d]=%d\n", cs, chip->csi, chip->ctrl->CS[chip->csi]); + + org_acc_control = acc_control = brcmnand_ctrl_read(bchp_nand_acc_control(cs)); + + /* + * For now, we only support same ECC level for both block0 and other blocks + */ + // Verify BCH-4 ECC: Handle CS0 block0 + if (chip->ctrl->CS[chip->csi] == 0) { + // ECC level for all other blocks. + eccLevel_n = (acc_control & BCHP_NAND_ACC_CONTROL_ECC_LEVEL_MASK) >> + BCHP_NAND_ACC_CONTROL_ECC_LEVEL_SHIFT; +#if CONFIG_MTD_BRCMNAND_VERSION < CONFIG_MTD_BRCMNAND_VERS_7_0 + // ECC level for block-0 + eccLevel = eccLevel_0 = (acc_control & BCHP_NAND_ACC_CONTROL_ECC_LEVEL_0_MASK) >> + BCHP_NAND_ACC_CONTROL_ECC_LEVEL_0_SHIFT; + + // make sure that block-0 and block-n use the same ECC level. + if (eccLevel_0 != eccLevel_n) { + // Use eccLevel_0 for eccLevel_n, unless eccLevel_0 is 0. + if (eccLevel_0 == 0) { + eccLevel = eccLevel_n; + } + acc_control &= ~(BCHP_NAND_ACC_CONTROL_ECC_LEVEL_0_MASK | + BCHP_NAND_ACC_CONTROL_ECC_LEVEL_MASK); + acc_control |= (eccLevel << BCHP_NAND_ACC_CONTROL_ECC_LEVEL_0_SHIFT) | + (eccLevel << BCHP_NAND_ACC_CONTROL_ECC_LEVEL_SHIFT); + brcmnand_ctrl_write(bchp_nand_acc_control(cs), acc_control ); + + if (eccLevel == eccLevel_0) { + printk("Corrected ECC on block-n to ECC on block-0: ACC = %08lx from %08lx\n", + acc_control, org_acc_control); + }else { + printk("Corrected ECC on block-0 to ECC on block-n: ACC = %08lx from %08lx\n", + acc_control, org_acc_control); + } + + } + chip->ecclevel = eccLevel; +#else + chip->ecclevel = eccLevel_n; + eccLevel = eccLevel_n; +#endif + /* + * Make sure that threshold is set at 75% of #bits the ECC can correct. + * This should be done for each CS!!!!! + */ + if (chip->ecclevel != 0 && chip->ecclevel != BRCMNAND_ECC_HAMMING) { + uint32_t corr_threshold = brcmnand_ctrl_read(BCHP_NAND_CORR_STAT_THRESHOLD) & BCHP_NAND_CORR_STAT_THRESHOLD_CORR_STAT_THRESHOLD_MASK; + uint32_t seventyfivepc; + + seventyfivepc = (chip->ecclevel * 3) / 4; + printk(KERN_INFO "%s: default CORR ERR threshold %d bits\n", __FUNCTION__, corr_threshold); + PRINTK("ECC level threshold set to %d bits\n", corr_threshold); + if (seventyfivepc < corr_threshold) { + printk(KERN_INFO "%s: CORR ERR threshold set to %d bits\n", __FUNCTION__, seventyfivepc); + seventyfivepc <<= BCHP_NAND_CORR_STAT_THRESHOLD_CORR_STAT_THRESHOLD_SHIFT; + seventyfivepc |= (brcmnand_ctrl_read(BCHP_NAND_CORR_STAT_THRESHOLD) & ~BCHP_NAND_CORR_STAT_THRESHOLD_CORR_STAT_THRESHOLD_MASK); + brcmnand_ctrl_write(BCHP_NAND_CORR_STAT_THRESHOLD, seventyfivepc); + } + } + PRINTK("ECC level %d, threshold at %d bits\n", + chip->ecclevel, brcmnand_ctrl_read(BCHP_NAND_CORR_STAT_THRESHOLD)); + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_5_0 + acc_control &= ~( +#if CONFIG_MTD_BRCMNAND_VERSION < CONFIG_MTD_BRCMNAND_VERS_7_0 + BCHP_NAND_ACC_CONTROL_SECTOR_SIZE_1K_0_MASK | +#endif + BCHP_NAND_ACC_CONTROL_SECTOR_SIZE_1K_MASK); + if (chip->eccSectorSize == 1024) { + acc_control |= ( +#if CONFIG_MTD_BRCMNAND_VERSION < CONFIG_MTD_BRCMNAND_VERS_7_0 + BCHP_NAND_ACC_CONTROL_SECTOR_SIZE_1K_0_MASK | +#endif + BCHP_NAND_ACC_CONTROL_SECTOR_SIZE_1K_MASK); + } + brcmnand_ctrl_write(bchp_nand_acc_control(0), acc_control ); +#endif + }else { // CS != 0 + + eccLevel = eccLevel_0 = (acc_control & BCHP_NAND_ACC_CONTROL_CS1_ECC_LEVEL_MASK) >> + BCHP_NAND_ACC_CONTROL_CS1_ECC_LEVEL_SHIFT; + chip->ecclevel = eccLevel; + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_5_0 + acc_control &= ~(BCHP_NAND_ACC_CONTROL_CS1_SECTOR_SIZE_1K_MASK); + if (chip->eccSectorSize == 1024) { + acc_control |= (BCHP_NAND_ACC_CONTROL_CS1_SECTOR_SIZE_1K_MASK); + } + brcmnand_ctrl_write(bchp_nand_acc_control(cs), acc_control ); +#endif + } + + + switch (eccLevel) { + case BRCMNAND_ECC_HAMMING: + if (NAND_IS_MLC(chip)) { + printk(KERN_INFO "Only BCH-4 or better is supported on MLC flash\n"); + eccLevel = chip->ecclevel = BRCMNAND_ECC_BCH_4; +#if CONFIG_MTD_BRCMNAND_VERSION < CONFIG_MTD_BRCMNAND_VERS_7_0 + acc_control &= ~(BCHP_NAND_ACC_CONTROL_ECC_LEVEL_0_MASK | + BCHP_NAND_ACC_CONTROL_ECC_LEVEL_MASK); + acc_control |= (BRCMNAND_ECC_BCH_4 << BCHP_NAND_ACC_CONTROL_ECC_LEVEL_0_SHIFT) | + (BRCMNAND_ECC_BCH_4 << BCHP_NAND_ACC_CONTROL_ECC_LEVEL_SHIFT); +#else + acc_control &= ~(BCHP_NAND_ACC_CONTROL_ECC_LEVEL_MASK); + acc_control |= (BRCMNAND_ECC_BCH_4 << BCHP_NAND_ACC_CONTROL_ECC_LEVEL_SHIFT); +#endif + brcmnand_ctrl_write(bchp_nand_acc_control(cs), acc_control ); + printk("Corrected ECC to BCH-4 for MLC flashes: ACC_CONTROL = %08lx from %08lx\n", acc_control, org_acc_control); + } + break; + + case BRCMNAND_ECC_BCH_4: + case BRCMNAND_ECC_BCH_8: + case BRCMNAND_ECC_BCH_12: + // eccOobSize is initialized to the board strap of ECC-level + eccOobSize = (acc_control & BCHP_NAND_ACC_CONTROL_SPARE_AREA_SIZE_MASK) >> + BCHP_NAND_ACC_CONTROL_SPARE_AREA_SIZE_SHIFT; + printk("ACC: %d OOB bytes per 512B ECC step; from ID probe: %d\n", eccOobSize, chip->eccOobSize); + + /* Temporary workarond. Id probe function does not set the ecc size. Need to implmenent this.*/ + if ( eccOobSize >= 27 && eccOobSize > chip->eccOobSize ) { + chip->eccOobSize = eccOobSize; + mtd->oobsize = chip->eccOobSize * chip->eccsteps; + printk(KERN_INFO "Use strap setting for ecc size %d bytes, mtd->oobsize %d.\n", eccOobSize, mtd->oobsize); + } + + //Make sure that the OOB size is >= 27 + if (eccLevel == BRCMNAND_ECC_BCH_12 && chip->eccOobSize < 27) { + printk(KERN_INFO "BCH-12 requires >=27 OOB bytes per ECC step.\n"); + printk(KERN_INFO "Please use the NAND part with enough spare eara and fix your board straps. Aborting to avoid file system damage\n"); + BUG(); + } + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_7_0 + //NAND 7.0 requires more ECC byte for BCH-8 + if (eccLevel == BRCMNAND_ECC_BCH_8 && chip->eccOobSize < 27) { + printk(KERN_INFO "BCH-8 requires >=27 OOB bytes per ECC step on NAND controller 7.0 or later.\n"); + printk(KERN_INFO "Please use the NAND part with enough spare eara and fix your board straps. Aborting to avoid file system damage\n"); + BUG(); + } +#endif + + // We have recorded chip->eccOobSize during probe, let's compare it against value from straps: + if (chip->eccOobSize < eccOobSize) { + printk("Flash says it has %d OOB bytes, eccLevel=%lu, but board strap says %d bytes, fixing it...\n", + chip->eccOobSize, eccLevel, eccOobSize); + acc_control &= ~( +#if CONFIG_MTD_BRCMNAND_VERSION < CONFIG_MTD_BRCMNAND_VERS_7_0 + BCHP_NAND_ACC_CONTROL_SPARE_AREA_SIZE_0_MASK | +#endif + BCHP_NAND_ACC_CONTROL_SPARE_AREA_SIZE_MASK); + acc_control |= +#if CONFIG_MTD_BRCMNAND_VERSION < CONFIG_MTD_BRCMNAND_VERS_7_0 + (chip->eccOobSize << BCHP_NAND_ACC_CONTROL_SPARE_AREA_SIZE_0_SHIFT) | +#endif + (chip->eccOobSize << BCHP_NAND_ACC_CONTROL_SPARE_AREA_SIZE_SHIFT); + printk("ACC_CONTROL adjusted to %08x\n", (unsigned int)acc_control); + brcmnand_ctrl_write(bchp_nand_acc_control(cs), acc_control); + } + + break; + + default: + printk(KERN_ERR "Unsupported ECC level %lu\n", eccLevel); + BUG(); + + } + + + chip->ecclevel = eccLevel; + +#endif /* else NAND version 3.3 or later */ + + /* + * This is just a warning + */ + PRINTK("reqEccLevel=%d, eccLevel=%d\n", chip->reqEccLevel, chip->ecclevel); + if (chip->reqEccLevel != 0 && chip->ecclevel != BRCMNAND_ECC_DISABLE) { + if (chip->reqEccLevel == BRCMNAND_ECC_HAMMING) { + ; /* Nothing, lowest requirement */ + } + /* BCH */ + else if (chip->reqEccLevel > 0 && chip->reqEccLevel <= BRCMNAND_ECC_BCH_12) { + if (chip->reqEccLevel > chip->ecclevel) { + printk(KERN_WARNING "******* Insufficient ECC level, required=%d, strapped for %d ********\n", + chip->reqEccLevel, chip->ecclevel); + } + } + } + + return eccLevel; + + /* No need to worry about correctable error for V3.3 or later, just take the default */ +} + + + + + +// else nothing to do for v2.x +#endif /* if controller v0.1 else 2.0 or later */ + +#ifdef CONFIG_BCM3548 +/* + * Check to see if this is a 3548L or 3556, + * in which case, disable WR_PREEMPT to avoid data corruption + * + * returns the passed-in acc-control register value with WR_PREEMPT disabled. + */ +static uint32_t check_n_disable_wr_preempt(uint32_t acc_control) +{ + uint32_t otp_option = BDEV_RD(BCHP_SUN_TOP_CTRL_OTP_OPTION_STATUS); + + printk("mcard_disable=%08x\n", otp_option); + // Is there any device on the EBI bus: mcard_disable==0 means there is (a device hanging off the EBI bus) + if (!(otp_option & BCHP_SUN_TOP_CTRL_OTP_OPTION_STATUS_otp_option_mcard_in_disable_MASK)) { + /* THT PR50928: Disable WR_PREEMPT for 3548L and 3556 */ + acc_control &= ~(BCHP_NAND_ACC_CONTROL_WR_PREEMPT_EN_MASK); + brcmnand_ctrl_write(bchp_nand_acc_control(cs), acc_control ); + printk("Disable WR_PREEMPT: ACC_CONTROL = %08x\n", acc_control); + } + return acc_control; +} +#endif + +/** + * brcmnand_scan - [BrcmNAND Interface] Scan for the BrcmNAND device + * @param mtd MTD device structure + * @cs Chip Select number + * @param numchips Number of chips (from CFE or from nandcs= kernel arg) + * @lastChip Start actual scan for bad blocks only on last chip + * + * This fills out all the not initialized function pointers + * with the defaults. + * The flash ID is read and the mtd/chip structures are + * filled with the appropriate values. + * + */ +int brcmnand_scan(struct mtd_info *mtd, int cs, int numchips ) +{ + struct brcmnand_chip* chip = (struct brcmnand_chip*)mtd->priv; + //unsigned char brcmnand_maf_id; + int err, i; + static int __maybe_unused notFirstChip; + volatile unsigned long nand_select; + unsigned int version_id; + unsigned int version_major; + unsigned int version_minor; + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + memset(ffchars, 0xff, sizeof(ffchars)); +#endif + + PRINTK("-->%s: CS=%d, numchips=%d, csi=%d\n", __FUNCTION__, cs, numchips, chip->csi); + + chip->ctrl->CS[chip->csi] = cs; + + + initialize_chip(chip); + chip->ecclevel = BRCMNAND_ECC_HAMMING; + + printk(KERN_INFO "mtd->oobsize=%d, mtd->eccOobSize=%d\n", mtd->oobsize, chip->eccOobSize); + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_2_0 + handle_xor(chip); + +#endif // if version >= 2.0 XOR + +// for (i=0; ictrl->numchips; i++) { +// cs = chip->ctrl->CS[i]; + +//gdebug=4; + PRINTK("brcmnand_scan: Calling brcmnand_probe for CS=%d\n", cs); + if (brcmnand_probe(mtd, cs)) { + return -ENXIO; + } +//gdebug=0; + +/* + * With version 3.3, we allow per-CS mtd handle, so it is handled in bcm7xxx-nand.c + */ +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_1_0 && \ + CONFIG_MTD_BRCMNAND_VERSION < CONFIG_MTD_BRCMNAND_VERS_3_3 + if (chip->ctrl->numchips > 0) { + if (brcmnand_validate_cs(mtd)) + return (-EINVAL); + } +#endif + + PRINTK("brcmnand_scan: Done brcmnand_probe\n"); + + +#if CONFIG_MTD_BRCMNAND_VERSION <= CONFIG_MTD_BRCMNAND_VERS_0_1 + handle_ecclevel_v0_1(mtd, chip, cs); + +#else + /* + * v1.0 controller and after + */ + // This table is in the Architecture Doc + // pbase is the physical address of the "logical" start of flash. Logical means how Linux sees it, + // and is given by the partition table defined in bcm7xxx-nand.c + // The "physical" start of the flash is always at 1FC0_0000 + + + if (chip->chipSize <= (256 << 20)) + chip->pbase = 0x20000000 - chip->chipSize; + else // 512MB and up + chip->pbase = 0; + + // vbase is the address of the flash cache array + chip->vbase = (void*)BVIRTADDR(BCHP_NAND_FLASH_CACHEi_ARRAY_BASE); // Start of Buffer Cache + // Already set in probe mtd->size = chip->chipSize * chip->ctrl->numchips; + // Make sure we use Buffer Array access, not direct access, Clear CS0 + nand_select = brcmnand_ctrl_read(BCHP_NAND_CS_NAND_SELECT); + printk("%s: B4 nand_select = %08x\n", __FUNCTION__, (uint32_t)nand_select); + + nand_select = brcmnand_ctrl_read(BCHP_NAND_CS_NAND_SELECT); + printk("%s: After nand_select = %08x\n", __FUNCTION__, (uint32_t)nand_select); + chip->directAccess = !(nand_select & BCHP_NAND_CS_NAND_SELECT_EBI_CS_0_SEL_MASK); + + + + /* + * Handle RD_ERASED_ECC bit, make sure it is not set + */ +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_2_1 + { + uint32_t acc0 = brcmnand_ctrl_read(bchp_nand_acc_control(cs)); + + if (acc0 & BCHP_NAND_ACC_CONTROL_RD_ERASED_ECC_EN_MASK) { + acc0 &= ~(BCHP_NAND_ACC_CONTROL_RD_ERASED_ECC_EN_MASK); + brcmnand_ctrl_write(bchp_nand_acc_control(cs), acc0); + } + } +#endif + + + + /* Handle Partial Write Enable configuration for MLC + * {FAST_PGM_RDIN, PARTIAL_PAGE_EN} + * {0, 0} = 1 write per page, no partial page writes (required for MLC flash, suitable for SLC flash) + * {1, 1} = 4 partial page writes per 2k page (SLC flash only) + * {0, 1} = 8 partial page writes per 2k page (not recommended) + * {1, 0} = RESERVED, DO NOT USE + */ +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_3_0 + //if (4) + { + /* For MLC, we only support BCH-4 or better */ + /* THT for 2.6.31-2.3: Nowadays, some SLC chips require higher ECC levels */ + + //int eccOobSize; + uint32_t eccLevel, acc_control, org_acc_control; + int nrSectorPP = chip->pageSize / 512; // Number of sectors per page == controller's NOP + + org_acc_control = brcmnand_ctrl_read(bchp_nand_acc_control(cs)); + eccLevel = handle_acc_control(mtd, chip, cs); + acc_control = brcmnand_ctrl_read(bchp_nand_acc_control(cs)); + + + PRINTK("190 eccLevel=%d, chip->ecclevel=%d, acc=%08x\n", eccLevel, chip->ecclevel, acc_control); + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + if (NAND_IS_MLC(chip)) { + printk("Setting NAND_COMPLEX_OOB_WRITE\n"); + chip->options |= NAND_COMPLEX_OOB_WRITE; + } +#endif + +/* + * For 3556 and 3548L, disable WR_PREEMPT + */ +#ifdef CONFIG_BCM3548 + acc_control = check_n_disable_wr_preempt(acc_control); +#endif + + /* + * Some SLC flashes have page size of 4KB, or more, and may need to disable Partial Page Programming + */ + if (NAND_IS_MLC(chip) || ((chip->nop > 0) && (nrSectorPP > chip->nop))) { + /* Set FAST_PGM_RDIN, PARTIAL_PAGE_EN to {0, 0} for NOP=1 */ + acc_control &= ~( +#if CONFIG_MTD_BRCMNAND_VERSION < CONFIG_MTD_BRCMNAND_VERS_7_0 + BCHP_NAND_ACC_CONTROL_FAST_PGM_RDIN_MASK | +#endif + BCHP_NAND_ACC_CONTROL_PARTIAL_PAGE_EN_MASK); + brcmnand_ctrl_write(bchp_nand_acc_control(cs), acc_control ); + printk("Corrected for NOP=1: ACC_CONTROL = %08x\n", acc_control); + } + + } + + +#endif // NAND version 3.0 or later + +#endif // Version 1.0+ + + PRINTK("%s 10\n", __FUNCTION__); + + PRINTK("200 CS=%d, chip->ctrl->CS[%d]=%d\n", cs, chip->csi, chip->ctrl->CS[chip->csi]); + PRINTK("200 chip->ecclevel=%d, acc=%08x\n", chip->ecclevel, + brcmnand_ctrl_read(bchp_nand_acc_control(chip->ctrl->CS[chip->csi]))); + + chip->bbt_erase_shift = ffs(mtd->erasesize) - 1; + + /* Calculate the address shift from the page size */ + chip->page_shift = ffs(mtd->writesize) - 1; + chip->bbt_erase_shift = chip->phys_erase_shift = ffs(mtd->erasesize) - 1; + chip->chip_shift = mtd64_ll_ffs(chip->chipSize) - 1; + + printk(KERN_INFO "page_shift=%d, bbt_erase_shift=%d, chip_shift=%d, phys_erase_shift=%d\n", + chip->page_shift, chip->bbt_erase_shift, chip->chip_shift, chip->phys_erase_shift); + + /* Set the bad block position */ + /* NAND_LARGE_BADBLOCK_POS also holds for MLC NAND */ + chip->badblockpos = mtd->writesize > 512 ? + NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS; + + + + + PRINTK("%s 220\n", __FUNCTION__); + + + + /* The number of bytes available for the filesystem to place fs dependend + * oob data */ +//PRINTK( "Determining chip->oobavail, chip->autooob=%p \n", chip->autooob); + + /* Version ID */ + version_id = chip->ctrl_read(BCHP_NAND_REVISION); + version_major = (version_id & 0xff00) >> 8; + version_minor = (version_id & 0xff); + + printk(KERN_INFO "Brcm NAND controller version = %x.%x NAND flash size %dMB @%08x\n", + version_major, version_minor, mtd64_ll_low(chip->chipSize >> 20), (uint32_t)chip->pbase); + + + PRINTK("%s 230\n", __FUNCTION__); + /* + * Initialize the eccmask array for ease of verifying OOB area. + */ + //fill_ecccmp_mask(mtd); + + + /* Store the number of chips and calc total size for mtd */ + //chip->ctrl->numchips = i; + //mtd->size = i * chip->chipSize; + + /* Preset the internal oob write buffer */ + memset(BRCMNAND_OOBBUF(chip->ctrl->buffers), 0xff, mtd->oobsize); + + /* + * If no default placement scheme is given, select an appropriate one + * We should make a table for this convoluted mess. (TBD) + */ + PRINTK("%s 40, mtd->oobsize=%d, chip->ecclayout=%08x\n", __FUNCTION__, mtd->oobsize, + (unsigned int)chip->ecclayout); + if (!chip->ecclayout) { + PRINTK("%s 42, mtd->oobsize=%d, chip->ecclevel=%d, isMLC=%d, chip->cellinfo=%d\n", __FUNCTION__, + mtd->oobsize, chip->ecclevel, NAND_IS_MLC(chip), chip->cellinfo); + switch (mtd->oobsize) { + case 16: /* Small size NAND */ + if (chip->ecclevel == BRCMNAND_ECC_HAMMING) { + chip->ecclayout = &brcmnand_oob_16; + }else if (chip->ecclevel == BRCMNAND_ECC_BCH_4) { + printk("ECC layout=brcmnand_oob_bch4_512\n"); + chip->ecclayout = &brcmnand_oob_bch4_512; + }else if (chip->ecclevel != BRCMNAND_ECC_DISABLE) { + printk(KERN_ERR "Unsupported ECC level for page size of %d\n", mtd->writesize); + BUG(); + } + break; + + case 64: /* Large page NAND 2K page */ + if (NAND_IS_MLC(chip) || chip->ecclevel == BRCMNAND_ECC_BCH_4 + || chip->ecclevel == BRCMNAND_ECC_BCH_8 + ) { + switch (mtd->writesize) { + case 4096: /* Impossible for 64B OOB per page */ + printk(KERN_ERR "Unsupported page size of %d\n", mtd->writesize); + BUG(); +/* + printk("ECC layout=brcmnand_oob_bch4_4k\n"); + chip->ecclayout = &brcmnand_oob_bch4_4k; + */ + break; + case 2048: + if (chip->ecclevel == BRCMNAND_ECC_BCH_4 ) { + printk("ECC layout=brcmnand_oob_bch4_2k\n"); + chip->ecclayout = &brcmnand_oob_bch4_2k; + }else if (chip->ecclevel == BRCMNAND_ECC_BCH_8 ) { +#if CONFIG_MTD_BRCMNAND_VERSION < CONFIG_MTD_BRCMNAND_VERS_7_0 + if (chip->eccOobSize == 16) { + printk("ECC layout=brcmnand_oob_bch8_16_2k\n"); + chip->ecclayout = &brcmnand_oob_bch8_16_2k; + }else if (chip->eccOobSize >= 27) { + printk("ECC layout=brcmnand_oob_bch8_27_2k\n"); + chip->ecclayout = &brcmnand_oob_bch8_27_2k; + } +#else + printk("ECC layout=brcmnand_oob_bch8_27_2k\n"); + chip->ecclayout = &brcmnand_oob_bch8_27_2k; +#endif + } + + break; + default: + printk(KERN_ERR "Unsupported page size of %d\n", mtd->writesize); + BUG(); + break; + } + }else if (chip->ecclevel == BRCMNAND_ECC_BCH_12) { + printk("ECC layout=brcmnand_oob_bch12_27_2k\n"); + chip->ecclayout = &brcmnand_oob_bch12_27_2k; + }else if (chip->ecclevel == BRCMNAND_ECC_HAMMING) { + printk("ECC layout=brcmnand_oob_bch4_4k\n"); + chip->ecclayout = &brcmnand_oob_64; + }else { + printk(KERN_ERR "Unsupported ECC code %d with only 64B OOB per page\n", chip->ecclevel); + BUG(); + } + break; + + case 128: /* Large page NAND 4K page or MLC */ + if (NAND_IS_MLC(chip)) { + switch (mtd->writesize) { + case 4096: + switch (chip->ecclevel) { + case BRCMNAND_ECC_BCH_4: + printk("ECC layout=brcmnand_oob_bch4_4k\n"); + chip->ecclayout = &brcmnand_oob_bch4_4k; + break; + case BRCMNAND_ECC_BCH_8: + if (chip->eccOobSize == 16) { + printk("ECC layout=brcmnand_oob_bch8_16_4k\n"); + chip->ecclayout = &brcmnand_oob_bch8_16_4k; + } +#if 1 + else if (chip->eccOobSize >= 27) { + printk("ECC layout=brcmnand_oob_bch8_27_4k\n"); + chip->ecclayout = &brcmnand_oob_bch8_27_4k; + } + break; + case BRCMNAND_ECC_BCH_12: + printk("ECC layout=brcmnand_oob_bch12_27_4k\n"); + chip->ecclayout = &brcmnand_oob_bch12_27_4k; + break; +#endif + + default: + printk(KERN_ERR "Unsupported ECC code %d for MLC with pageSize=%d\n", chip->ecclevel, mtd->writesize); + BUG(); + } + break; + default: + printk(KERN_ERR "Unsupported page size of %d\n", mtd->writesize); + BUG(); + break; + } + }else { /* SLC chips, there are now some SLCs that require BCH-4 or better */ + switch (mtd->writesize) { + case 4096: + if (chip->ecclevel == BRCMNAND_ECC_HAMMING) { + printk("ECC layout=brcmnand_oob_128\n"); + chip->ecclayout = &brcmnand_oob_128; + }else if (chip->ecclevel == BRCMNAND_ECC_BCH_4) { + printk("ECC layout=brcmnand_oob_bch4_4k\n"); + chip->ecclayout = &brcmnand_oob_bch4_4k; + }else if (chip->ecclevel == BRCMNAND_ECC_BCH_8) { + if (chip->eccOobSize == 16) { + printk("ECC layout=brcmnand_oob_bch8_16_4k\n"); + chip->ecclayout = &brcmnand_oob_bch8_16_4k; + }else if (chip->eccOobSize >= 27) { + printk("ECC layout=brcmnand_oob_bch8_27_4k\n"); + chip->ecclayout = &brcmnand_oob_bch8_27_4k; + + } + }else if (chip->ecclevel == BRCMNAND_ECC_BCH_12) { + printk("ECC layout=brcmnand_oob_bch12_27_4k\n"); + chip->ecclayout = &brcmnand_oob_bch12_27_4k; + } + break; + + default: + printk(KERN_ERR "Unsupported page size of %d\n", mtd->writesize); + BUG(); + break; + } + } /* else SLC chips */ + break; /* 128B OOB case */ + + default: /* 27.25/28 or greater OOB size */ + PRINTK("27B OOB\n"); + PRINTK("300 chip->ecclevel=%d, acc=%08x\n", chip->ecclevel, brcmnand_ctrl_read(bchp_nand_acc_control(chip->ctrl->CS[chip->csi]))); + if (mtd->writesize == 2048) { + switch (chip->ecclevel) { + case BRCMNAND_ECC_BCH_4: + printk(KERN_INFO "ECC layout=brcmnand_oob_bch4_2k\n"); + chip->ecclayout = &brcmnand_oob_bch4_2k; + break; + case BRCMNAND_ECC_BCH_8: + printk(KERN_INFO "ECC layout=brcmnand_oob_bch8_27_2k\n"); + chip->ecclayout = &brcmnand_oob_bch8_27_2k; + break; + case BRCMNAND_ECC_BCH_12: + printk(KERN_INFO "ECC layout=brcmnand_oob_bch12_27_2k\n"); + chip->ecclayout = &brcmnand_oob_bch12_27_2k; + break; + default: + printk(KERN_ERR "Unsupported ECC code %d with pageSize=%d\n", chip->ecclevel, mtd->writesize); + BUG(); + } + + }else if (mtd->writesize == 4096) { + switch (chip->ecclevel) { + case BRCMNAND_ECC_BCH_4: + printk(KERN_INFO "ECC layout=brcmnand_oob_bch4_4k\n"); + chip->ecclayout = &brcmnand_oob_bch4_4k; + break; + case BRCMNAND_ECC_BCH_8: + if (chip->eccOobSize == 16) { + printk(KERN_INFO "ECC layout=brcmnand_oob_bch8_16_4k\n"); + chip->ecclayout = &brcmnand_oob_bch8_16_4k; + }else if (chip->eccOobSize >= 27) { + printk(KERN_INFO "ECC layout=brcmnand_oob_bch8_27_4k\n"); + chip->ecclayout = &brcmnand_oob_bch8_27_4k; + } + break; + case BRCMNAND_ECC_BCH_12: + printk(KERN_INFO "ECC layout=brcmnand_oob_bch12_27_4k\n"); + chip->ecclayout = &brcmnand_oob_bch12_27_4k; + break; + default: + printk(KERN_ERR "Unsupported ECC code %d with pageSize=%d\n", chip->ecclevel, mtd->writesize); + BUG(); + } + + }else if (mtd->writesize == 8192) { // 8KB page + switch (chip->ecclevel) { + case BRCMNAND_ECC_BCH_4: + printk(KERN_INFO "ECC layout=brcmnand_oob_bch4_8k\n"); + chip->ecclayout = &brcmnand_oob_bch4_8k; + break; + case BRCMNAND_ECC_BCH_8: + if (chip->eccOobSize == 16) { + printk(KERN_INFO "ECC layout=brcmnand_oob_bch8_16_8k\n"); + chip->ecclayout = &brcmnand_oob_bch8_16_8k; + }else if (chip->eccOobSize >= 27) { + printk(KERN_INFO "ECC layout=brcmnand_oob_bch8_27_8k\n"); + chip->ecclayout = &brcmnand_oob_bch8_27_8k; + } + break; + case BRCMNAND_ECC_BCH_12: + printk(KERN_INFO "ECC layout=brcmnand_oob_bch12_27_8k\n"); + chip->ecclayout = &brcmnand_oob_bch12_27_8k; + break; + default: + printk(KERN_ERR "Unsupported ECC code %d for MLC with pageSize=%d\n", chip->ecclevel, mtd->writesize); + BUG(); + } + }else { + printk(KERN_ERR "Unsupported page size of %d and oobsize %d\n", mtd->writesize, mtd->oobsize); + BUG(); + break; + } + break; /* 27B OOB */ + } + } + + + + /* + * The number of bytes available for a client to place data into + * the out of band area + */ + printk(KERN_INFO "%s: mtd->oobsize=%d\n", __FUNCTION__, mtd->oobsize); + chip->ecclayout->oobavail = 0; + for (i = 0; chip->ecclayout->oobfree[i].length; i++) + chip->ecclayout->oobavail += + chip->ecclayout->oobfree[i].length; + + mtd->oobavail = chip->ecclayout->oobavail; + + printk(KERN_INFO "%s: oobavail=%d, eccsize=%d, writesize=%d\n", __FUNCTION__, + chip->ecclayout->oobavail, chip->eccsize, mtd->writesize); + + /* + * Set the number of read / write steps for one page depending on ECC + * mode + */ + + chip->eccsteps = mtd->writesize / chip->eccsize; + chip->eccbytes = brcmnand_eccbytes[chip->ecclevel]; + printk(KERN_INFO "%s, eccsize=%d, writesize=%d, eccsteps=%d, ecclevel=%d, eccbytes=%d\n", __FUNCTION__, + chip->eccsize, mtd->writesize, chip->eccsteps, chip->ecclevel, chip->eccbytes); +//udelay(2000000); + if (chip->eccsteps * chip->eccsize != mtd->writesize) { + printk(KERN_WARNING "Invalid ecc parameters\n"); + +//udelay(2000000); + BUG(); + } + chip->ecctotal = chip->eccsteps * chip->eccbytes; + //ECCSIZE(mtd) = chip->eccsize; + + /* Initialize state */ + chip->ctrl->state = BRCMNAND_FL_READY; + +#if 0 + /* De-select the device */ + chip->select_chip(mtd, -1); +#endif + + /* Invalidate the pagebuffer reference */ + chip->pagebuf = -1LL; + + /* Fill in remaining MTD driver data */ + mtd->type = MTD_NANDFLASH; + + /* + * Now that we know what kind of NAND it is (SLC vs MLC), + * tell the MTD layer how to test it. + * ** 01/23/08: Special case: SLC with BCH ECC will be treated as MLC -- at the MTD level -- + * ** by the high level test MTD_IS_MLC() + * The low level test NAND_IS_MLC() still tells whether the flash is actually SLC or MLC + * (so that BBT codes know where to find the BI marker) + */ + if (NAND_IS_MLC(chip)) { + mtd->flags = MTD_CAP_MLC_NANDFLASH; + } +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_3_0 + /* + * If controller is version 3 or later, allow SLC to have BCH-n ECC, + * -- ONLY IF THE CFE SAYS SO -- + * in which case, it is treated as if it is an MLC flash by file system codes + */ + else if (chip->ecclevel > BRCMNAND_ECC_DISABLE && chip->ecclevel < BRCMNAND_ECC_HAMMING) { + // CFE wants BCH codes on SLC Nand + mtd->flags = MTD_CAP_MLC_NANDFLASH; + } +#endif + else { + mtd->flags = MTD_CAP_NANDFLASH; + } + //mtd->ecctype = MTD_ECC_SW; +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + if (chip->nop == 1) + mtd->flags |= MTD_NAND_NOP1; +#endif + PRINTK("300 CS=%d, chip->ctrl->CS[%d]=%d\n", cs, chip->csi, chip->ctrl->CS[chip->csi]); + + + mtd->_erase = brcmnand_erase; + mtd->_point = NULL; + mtd->_unpoint = NULL; + mtd->_read = brcmnand_read; + mtd->_write = brcmnand_write; + mtd->_read_oob = brcmnand_read_oob; + mtd->_write_oob = brcmnand_write_oob; + + // Not needed? + mtd->_writev = brcmnand_writev; + + mtd->_sync = brcmnand_sync; + mtd->_lock = NULL; + mtd->_unlock = brcmnand_unlock; + mtd->_suspend = brcmnand_suspend; + mtd->_resume = brcmnand_resume; + + mtd->_block_isbad = brcmnand_block_isbad; + mtd->_block_markbad = brcmnand_block_markbad; + + /* propagate ecc.layout to mtd_info */ + mtd->ecclayout = chip->ecclayout; + + mtd->reboot_notifier.notifier_call = brcmnand_reboot_cb; + register_reboot_notifier(&mtd->reboot_notifier); + + mtd->owner = THIS_MODULE; + + + + + + + + /* + * Clear ECC registers + */ + chip->ctrl_write(BCHP_NAND_ECC_CORR_ADDR, 0); + chip->ctrl_write(BCHP_NAND_ECC_UNC_ADDR, 0); + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_1_0 + chip->ctrl_write(BCHP_NAND_ECC_CORR_EXT_ADDR, 0); + chip->ctrl_write(BCHP_NAND_ECC_UNC_EXT_ADDR, 0); +#endif + + +#if 0 + /* Unlock whole block */ + if (mtd->unlock) { + PRINTK("Calling mtd->unlock(ofs=0, MTD Size=%016llx\n", device_size(mtd)); + mtd->unlock(mtd, 0x0, device_size(mtd)); + } +#endif + + + + + +// if (!lastChip) +// return 0; + + + +//gdebug = 4; + PRINTK("500 chip=%p, CS=%d, chip->ctrl->CS[%d]=%d\n", chip, cs, chip->csi, chip->ctrl->CS[chip->csi]); + err = chip->scan_bbt(mtd); +//gdebug = 0; + +// + +#ifdef CONFIG_MTD_BRCMNAND_CORRECTABLE_ERR_HANDLING + + if (brcmnand_create_cet(mtd) < 0) { + printk(KERN_INFO "%s: CET not created\n", __FUNCTION__); + } +#endif + + PRINTK("%s 99\n", __FUNCTION__); + + return err; + +} + + + +#if defined( CONFIG_BCM7401C0 ) || defined( CONFIG_BCM7118A0 ) || defined( CONFIG_BCM7403A0 ) +static DEFINE_SPINLOCK(bcm9XXXX_lock); +static unsigned long misb_war_flags; + +static inline void +HANDLE_MISB_WAR_BEGIN(void) +{ + /* if it is 7401C0, then we need this workaround */ + if (brcm_ebi_war) { + spin_lock_irqsave(&bcm9XXXX_lock, misb_war_flags); + BDEV_WR(0x00400b1c, 0xFFFF); + BDEV_WR(0x00400b1c, 0xFFFF); + BDEV_WR(0x00400b1c, 0xFFFF); + BDEV_WR(0x00400b1c, 0xFFFF); + BDEV_WR(0x00400b1c, 0xFFFF); + BDEV_WR(0x00400b1c, 0xFFFF); + BDEV_WR(0x00400b1c, 0xFFFF); + BDEV_WR(0x00400b1c, 0xFFFF); + } +} + +static inline void +HANDLE_MISB_WAR_END(void) +{ + if (brcm_ebi_war) { + spin_unlock_irqrestore(&bcm9XXXX_lock, misb_war_flags); + } +} + +#else +#define HANDLE_MISB_WAR_BEGIN() +#define HANDLE_MISB_WAR_END() +#endif + + +#if 0 +/* + * @ buff Kernel buffer to hold the data read from the NOR flash, must be able to hold len bytes, + * and aligned on word boundary. + * @ offset Offset of the data from CS0 (on NOR flash), must be on word boundary. + * @ len Number of bytes to be read, must be even number. + * + * returns 0 on success, negative error codes on failure. + * + * The caller thread may block until access to the NOR flash can be granted. + * Further accesses to the NAND flash (from other threads) will be blocked until this routine returns. + * The routine performs the required swapping of CS0/CS1 under the hood. + */ +int brcmnand_readNorFlash(struct mtd_info *mtd, void* buff, unsigned int offset, int len) +{ + struct brcmnand_chip* chip = (struct brcmnand_chip*)mtd->priv; + int ret = -EFAULT; + int i; + int csNand; // Which CS is NAND + volatile unsigned long cs0Base, cs0Cnfg, cs0BaseAddr, csNandSelect; + volatile unsigned long csNandBase[MAX_NAND_CS], csNandCnfg[MAX_NAND_CS]; + unsigned int romSize; + volatile uint16_t* pui16 = (volatile uint16_t*)buff; + volatile uint16_t* fp; + +#if 1 +/* + * THT 03/12/09: This should never be called since the CFE no longer disable CS0 + * when CS1 is on NAND + */ + printk("%s should never be called\n", __FUNCTION__); + BUG(); +#else + + if (!chip) { // When booting from CRAMFS/SQUASHFS using /dev/romblock + chip = brcmnand_get_device_exclusive(); + mtd = (struct mtd_info*)chip->priv; + }else if (brcmnand_get_device(mtd, BRCMNAND_FL_EXCLUSIVE)) + return ret; + + romSize = get_rom_size((unsigned long*)&cs0Base); + + cs0BaseAddr = cs0Base & BCHP_EBI_CS_BASE_0_base_addr_MASK; + + if ((len + offset) > romSize) { + printk("%s; Attempt to read past end of CS0, (len+offset)=%08x, romSize=%dMB\n", + __FUNCTION__, len + offset, romSize >> 20); + ret = (-EINVAL); + goto release_device_and_out; + } + + cs0Cnfg = *(volatile unsigned long*)(0xb0000000 | BCHP_EBI_CS_CONFIG_0); + + // Turn off NAND CS + for (i = 0; i < chip->ctrl->numchips; i++) { + csNand = chip->ctrl->CS[i]; + + if (csNand == 0) { + printk("%s: Call this routine only if NAND is not on CS0\n", __FUNCTION__); + ret = (-EINVAL); + goto release_device_and_out; + } + +#if CONFIG_MTD_BRCMNAND_VERSION < CONFIG_MTD_BRCMNAND_VERS_1_0 + BUG_ON(csNand > 5); +#else + BUG_ON(csNand > 7); +#endif + csNandBase[i] = *(volatile unsigned long*)(0xb0000000 + BCHP_EBI_CS_BASE_0 + 8 * csNand); + csNandCnfg[i] = *(volatile unsigned long*)(0xb0000000 + BCHP_EBI_CS_CONFIG_0 + 8 * csNand); + + // Turn off NAND, must turn off both NAND_CS_NAND_SELECT and CONFIG. + // We turn off the CS_CONFIG here, and will turn off NAND_CS_NAND_SELECT for all CS at once, + // outside the loop. + *(volatile unsigned long*)(0xb0000000 + BCHP_EBI_CS_CONFIG_0 + 8 * csNand) = + csNandCnfg[i] & (~BCHP_EBI_CS_CONFIG_0_enable_MASK); + + } + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_0_1 + csNandSelect = brcmnand_ctrl_read(BCHP_NAND_CS_NAND_SELECT); + + brcmnand_ctrl_write(BCHP_NAND_CS_NAND_SELECT, csNandSelect & + ~( +#if CONFIG_MTD_BRCMNAND_VERSION < CONFIG_MTD_BRCMNAND_VERS_1_0 + BCHP_NAND_CS_NAND_SELECT_EBI_CS_5_SEL_MASK + | BCHP_NAND_CS_NAND_SELECT_EBI_CS_4_SEL_MASK + | BCHP_NAND_CS_NAND_SELECT_EBI_CS_3_SEL_MASK + | BCHP_NAND_CS_NAND_SELECT_EBI_CS_2_SEL_MASK + | BCHP_NAND_CS_NAND_SELECT_EBI_CS_1_SEL_MASK + | BCHP_NAND_CS_NAND_SELECT_EBI_CS_0_SEL_MASK +#else + 0x0000003E /* Not documented on V1.0+ */ +#endif + )); +#endif + + // Turn on NOR on CS0 + *(volatile unsigned long*)(0xb0000000 | BCHP_EBI_CS_CONFIG_0) = + cs0Cnfg | BCHP_EBI_CS_CONFIG_0_enable_MASK; + + // Take care of MISB Bridge bug on 7401c0/7403a0/7118a0 + HANDLE_MISB_WAR_BEGIN(); + + // Read NOR, 16 bits at a time, we have already checked the out-of-bound condition above. + fp = (volatile uint16_t*)(KSEG1ADDR(cs0BaseAddr + offset)); + for (i = 0; i < (len >> 1); i++) { + pui16[i] = fp[i]; + } + + HANDLE_MISB_WAR_END(); + + // Turn Off NOR + *(volatile unsigned long*)(0xb0000000 | BCHP_EBI_CS_CONFIG_0) = + cs0Cnfg & (~BCHP_EBI_CS_CONFIG_0_enable_MASK); + + // Turn NAND back on + for (i = 0; i < chip->ctrl->numchips; i++) { + csNand = chip->ctrl->CS[i]; + if (csNand == 0) { + printk("%s: Call this routine only if NAND is not on CS0\n", __FUNCTION__); + ret = (-EINVAL); + goto release_device_and_out; + } +#if CONFIG_MTD_BRCMNAND_VERSION < CONFIG_MTD_BRCMNAND_VERS_1_0 + BUG_ON(csNand > 5); +#else + BUG_ON(csNand > 7); +#endif + *(volatile unsigned long*)(0xb0000000 + BCHP_EBI_CS_BASE_0 + 8 * csNand) = csNandBase[i]; + *(volatile unsigned long*)(0xb0000000 + BCHP_EBI_CS_CONFIG_0 + 8 * csNand) = csNandCnfg[i]; + } + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_0_1 + // Restore NAND_CS_SELECT + brcmnand_ctrl_write(BCHP_NAND_CS_NAND_SELECT, csNandSelect); +#endif + udelay(10000); // Wait for ID Configuration to stabilize + + release_device_and_out: + brcmnand_release_device(mtd); +//printk("<-- %s\n", __FUNCTION__); + +#endif + return ret; +} +EXPORT_SYMBOL(brcmnand_readNorFlash); +#endif + +/** + * brcmnand_release - [BrcmNAND Interface] Free resources held by the BrcmNAND device + * @param mtd MTD device structure + */ +void brcmnand_release(struct mtd_info *mtd) +{ + //struct brcmnand_chip * chip = mtd->priv; + + /* Unregister reboot notifier */ + brcmnand_prepare_reboot_priv(mtd); + unregister_reboot_notifier(&mtd->reboot_notifier); + mtd->reboot_notifier.notifier_call = NULL; + + /* Deregister the device (unregisters partitions as well) */ + mtd_device_unregister(mtd); + + + +#if 0 + /* Buffer allocated by brcmnand_scan */ + if (chip->options & NAND_DATABUF_ALLOC) + kfree(chip->data_buf); + + /* Buffer allocated by brcmnand_scan */ + if (chip->options & NAND_OOBBUF_ALLOC) + kfree(chip->oob_buf); +#endif + +} + +#endif // CONFIG_BCM_KF_MTD_BCMNAND diff -urN linux-4.1.52-orig/drivers/mtd/brcmnand/brcmnand_bbt.c linux-4.1.52/drivers/mtd/brcmnand/brcmnand_bbt.c --- linux-4.1.52-orig/drivers/mtd/brcmnand/brcmnand_bbt.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/mtd/brcmnand/brcmnand_bbt.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,2266 @@ +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +/* + <:copyright-BRCM:2012:DUAL/GPL:standard + + Copyright (c) 2012 Broadcom + All Rights Reserved + + Unless you and Broadcom execute a separate written software license + agreement governing use of this software, this software is licensed + to you under the terms of the GNU General Public License version 2 + (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, + with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + + Not withstanding the above, under no circumstances may you combine + this software in any way with any other Broadcom software provided + under a license other than the GPL, without Broadcom's express prior + written consent. + + :> + + File: brcmnand_bbt.c + + Description: + NAND driver for Samsung K9F1G08U0A chip with Broadcom NAND controller. + The main difference between the Broadcom controller and OneNAND is that the Broadcom + NAND controller has only a 512B cache (bufferram) regardless of the flash chip, + whereas OneNAND has multiple bufferram's to match the page size. + This complicates this driver quite a bit, because, for large page flash (2K page) + we have to read in all 4 slides before we know for sure whether a page is bad. + + * When brcmnand_scan_bbt is called, then it tries to find the bad block table + * depending on the options in the bbt descriptor(s). If a bbt is found + * then the contents are read and the memory based bbt is created. If a + * mirrored bbt is selected then the mirror is searched too and the + * versions are compared. If the mirror has a greater version number + * than the mirror bbt is used to build the memory based bbt. + * If the tables are not versioned, then we "or" the bad block information. + * If one of the bbt's is out of date or does not exist it is (re)created. + * If no bbt exists at all then the device is scanned for factory marked + * good / bad blocks and the bad block tables are created. + * + * For manufacturer created bbts like the one found on M-SYS DOC devices + * the bbt is searched and read but never created + * + * The autogenerated bad block table is located in the last good blocks + * of the device. The table is mirrored, so it can be updated eventually. + * The table is marked in the oob area with an ident pattern and a version + * number which indicates which of both tables is more up to date. + * + * The table uses 2 bits per block + * 11b: block is good + * 00b: block is factory marked bad + * 01b, 10b: block is marked bad due to wear + * + * The memory bad block table uses the following scheme: + * 00b: block is good + * 01b: block is marked bad due to wear + * 10b: block is reserved (to protect the bbt area) + * 11b: block is factory marked bad + * + * Multichip devices like DOC store the bad block info per floor. + * + * Following assumptions are made: + * - bbts start at a page boundary, if autolocated on a block boundary + * - the space necessary for a bbt in FLASH does not exceed a block boundary + * + + when who what + ----- --- ---- + 070807 tht codings derived from nand_base & brcmnand_bbt implementations. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "brcmnand_priv.h" +#include +#include +#include + + +#define PRINTK(...) do { } while (0) +//#define PRINTK printk +//#define DEBUG_BBT +#define DEBUG(...) do { } while (0) + + +extern int gClearBBT; +extern int gdebug; + +//char brcmNandBBTMsg[1024]; + +/* brcmnand= + * rescan: 1. Rescan for bad blocks, and update existing BBT + * showbbt: 2. Print out the contents of the BBT on boot up. + * + * The following commands are implemented but should be removed for production builds. + * Use userspace flash_eraseall instead. + * These were intended for development debugging only. + * erase: 7. Erase entire flash, except CFE, and rescan for bad blocks + * eraseall: 8. Erase entire flash, and rescan for bad blocks + * clearbbt: 9. Erase BBT and rescan for bad blocks. (DANGEROUS, may lose Mfg's BIs). + */ +#define NANDCMD_RESCAN 1 +#define NANDCMD_SHOWBBT 2 + +#define NANDCMD_ERASE 7 +#define NANDCMD_ERASEALL 8 +#define NANDCMD_CLEARBBT 9 + +int brcmnand_update_bbt(struct mtd_info *mtd, loff_t offs); + + +/** + * check_pattern - [GENERIC] check if a pattern is in the buffer + * @buf: the buffer to search + * @len: the length of buffer to search + * @paglen: the pagelength + * @td: search pattern descriptor + * + * Check for a pattern at the given place. Used to search bad block + * tables and good / bad block identifiers. + * If the SCAN_EMPTY option is set then check, if all bytes except the + * pattern area contain 0xff + * + */ +static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) +{ + int i, end = 0; + uint8_t *p = buf; + + PRINTK("Check_pattern len=%d, pagelen=%d, td->offs-%d, pattern=%c%c%c%c\n", len, paglen, td->offs, + td->pattern[0], td->pattern[1], td->pattern[2], td->pattern[3]); +#ifdef DEBUG_BBT + if (gdebug) { + printk("oobbuf=\n"); print_oobbuf(&buf[paglen], len - paglen); + } +#endif + end = paglen + td->offs; + if (td->options & NAND_BBT_SCANEMPTY) { + for (i = 0; i < end; i++) { + if (p[i] != 0xff) { + PRINTK("check_pattern 1: p[%d] == %02x - expect FF\n", i, p[i]); + return -1; + } + } + } + p += end; + + /* Compare the pattern */ + for (i = 0; i < td->len; i++) { + if (p[i] != td->pattern[i]) { + PRINTK("%s: expect @i=%d td->pat[%d]=%02x, found p[%d]=%02x\n", + __FUNCTION__, i, i, td->pattern[i], td->offs + i, p[td->offs + i]); + return -1; + } + } + + if (td->options & NAND_BBT_SCANEMPTY) { + p += td->len; + end += td->len; + for (i = end; i < len; i++) { + if (*p++ != 0xff) { + PRINTK("check_pattern 2: p[%d] == %02x - expect FF\n", i, p[i]); + return -1; + } + } + } + return 0; +} + +/** + * check_short_pattern - [GENERIC] check if a pattern is in the buffer + * @buf: the buffer to search + * @td: search pattern descriptor + * + * Check for a pattern at the given place. Used to search bad block + * tables and good / bad block identifiers. Same as check_pattern, but + * no optional empty check + * + */ +static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td) +{ + int i; + uint8_t *p = buf; + + /* Compare the pattern */ + for (i = 0; i < td->len; i++) { + if (p[td->offs + i] != td->pattern[i]) { + PRINTK("%s: expect @i=%d td->pat[%d]=%02x, found p[%d]=%02x\n", + __FUNCTION__, i, i, td->pattern[i], td->offs + i, p[td->offs + i]); + return -1; + } + } + return 0; +} + + + +/** + * brcmnand_read_bbt - [GENERIC] Read the bad block table starting from page + * @mtd: MTD device structure + * @buf: temporary buffer + * @page: the starting page + * @num: the number of bbt descriptors to read + * @bits: number of bits per block + * @offs: offset in the memory table + * @reserved_block_code: Pattern to identify reserved blocks + * + * Read the bad block table starting from page. + * + */ +static int brcmnand_read_bbt(struct mtd_info *mtd, uint8_t *buf, int64_t page, int num, + int bits, int offs, int reserved_block_code) +{ + int res, i, j, act = 0; + struct brcmnand_chip *this = mtd->priv; + size_t retlen, len, totlen; + loff_t from; + uint8_t msk = (uint8_t)((1 << bits) - 1); + + totlen = (num * bits) >> 3; + from = ((loff_t)page) << this->page_shift; + + /* + * Clear ECC registers + */ + this->ctrl_write(BCHP_NAND_ECC_CORR_ADDR, 0); + this->ctrl_write(BCHP_NAND_ECC_UNC_ADDR, 0); + + + while (totlen) { + len = min(totlen, (size_t)(1 << this->bbt_erase_shift)); + PRINTK("%s: calling read_ecc len=%d, bits=%d, num=%d, totallen=%d\n", __FUNCTION__, len, bits, num, totlen); + res = mtd_read(mtd, from, len, &retlen, buf); + if (res < 0) { + if (retlen != len) { + printk(KERN_INFO "brcmnand_bbt: Error reading bad block table\n"); + return res; + } + printk(KERN_ERR "%s: ECC error %d while reading bad block table\n", __FUNCTION__, res); + /* THT 11/10/09: If read fails, we should ignore the data, so return w/o analyzing it */ + return res; + } + + /* Analyse data */ + for (i = 0; i < len; i++) { + uint8_t dat = buf[i]; + for (j = 0; j < 8; j += bits, act += 2) { + uint8_t tmp = (dat >> j) & msk; + if (tmp == msk) + continue; + if (reserved_block_code && (tmp == reserved_block_code)) { + printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n", + ((offs << 2) + (act >> 1)) << this->bbt_erase_shift); + this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06); + mtd->ecc_stats.bbtblocks++; + continue; + } + /* Leave it for now, if its matured we can move this + * message to MTD_DEBUG_LEVEL0 */ + printk(KERN_DEBUG "nand_read_bbt: Bad block at 0x%08x\n", + ((offs << 2) + (act >> 1)) << this->bbt_erase_shift); + /* Factory marked bad or worn out ? */ + if (tmp == 0) + this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06); + else + this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06); + mtd->ecc_stats.badblocks++; + } + } + totlen -= len; + from += (loff_t)len; + } + return 0; +} + +/** + * brcmnand_read_abs_bbt - [GENERIC] Read the bad block table starting at a given page + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @chip: read the table for a specific chip, -1 read all chips. + * Applies only if NAND_BBT_PERCHIP option is set + * + * Read the bad block table for all chips starting at a given page + * We assume that the bbt bits are in consecutive order. + */ +static int brcmnand_read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip) +{ + struct brcmnand_chip *this = mtd->priv; + int res = 0, i; + int bits; + + PRINTK("-->%s, numchips=%d, chip=%d\n", __FUNCTION__, this->ctrl->numchips, chip); + bits = td->options & NAND_BBT_NRBITS_MSK; + if (td->options & NAND_BBT_PERCHIP) { + int offs = 0; + for (i = 0; i < this->ctrl->numchips; i++) { + if (chip == -1 || chip == i) + res = brcmnand_read_bbt(mtd, buf, td->pages[i], this->chipSize >> this->bbt_erase_shift, bits, offs, td->reserved_block_code); + if (res) { + PRINTK("<-- brcmnand_read_abs_bbt ret = %d\n", res); + return res; + } + offs += this->chipSize >> (this->bbt_erase_shift + 2); + } + } else { + PRINTK("%s: read BBT at %llx\n", __FUNCTION__, td->pages[0]); + res = brcmnand_read_bbt(mtd, buf, td->pages[0], + (uint32_t)(this->mtdSize >> this->bbt_erase_shift), bits, 0, td->reserved_block_code); + if (res) { + PRINTK("<-- brcmnand_read_abs_bbt 2 ret = %d\n", res); + return res; + } + } + PRINTK("<-- brcmnand_read_abs_bbt ret 0\n"); + return 0; +} + +/* + * Scan read raw data from flash + */ +static int brcmnand_scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs, + size_t len) +{ + struct mtd_oob_ops ops; + int ret; + + ops.mode = MTD_OPS_RAW; + ops.ooboffs = 0; + ops.ooblen = mtd->oobsize; + ops.oobbuf = &buf[mtd->writesize]; + ops.datbuf = buf; + ops.len = len; + + ret = mtd_read_oob(mtd, offs, &ops); + + PRINTK("%s: Reading BBT Sig @%0llx, OOB=\n", __FUNCTION__, offs); +#ifdef DEBUG_BBT + if (gdebug) + print_oobbuf(ops.oobbuf, mtd->oobsize); +#endif + return ret; +} + +/* + * Scan write data with oob to flash + */ +static int brcmnand_scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len, + uint8_t *buf, uint8_t *oob) +{ + struct mtd_oob_ops ops; + struct brcmnand_chip *this = mtd->priv; + + int ret; + + ops.mode = MTD_OPS_PLACE_OOB; + ops.ooboffs = 0; + ops.ooblen = mtd->oobsize; + ops.datbuf = buf; + ops.oobbuf = oob; + ops.len = len; + + + PRINTK("%s: Writing BBT Sig @%0llx, OOB=\n", __FUNCTION__, offs); + if (gdebug) print_oobbuf(oob, mtd->oobsize); + + ret = this->write_oob(mtd, offs, &ops); +//gdebug = 0; + return ret; +} + +/** + * brcmnand_read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @md: descriptor for the bad block table mirror + * + * Read the bad block table(s) for all chips starting at a given page + * We assume that the bbt bits are in consecutive order. + * + */ +static int brcmnand_read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *td, struct nand_bbt_descr *md) +{ + struct brcmnand_chip *this = mtd->priv; + + PRINTK("--> %s\n", __FUNCTION__); + /* Read the primary version, if available */ + if (td->options & NAND_BBT_VERSION) { + PRINTK("read primary version\n"); + brcmnand_scan_read_raw(mtd, buf, td->pages[0] << this->page_shift, + mtd->writesize); + td->version[0] = buf[mtd->writesize + td->veroffs]; + printk(KERN_DEBUG "Bad block table at page %x, version 0x%02X\n", + td->pages[0], td->version[0]); + PRINTK("Main bad block table at page %llx, version 0x%02X\n", + td->pages[0], td->version[0]); + } + + /* Read the mirror version, if available */ + if (md && (md->options & NAND_BBT_VERSION)) { + PRINTK("read mirror version\n"); + brcmnand_scan_read_raw(mtd, buf, md->pages[0] << this->page_shift, + mtd->writesize); + md->version[0] = buf[mtd->writesize + md->veroffs]; + printk(KERN_DEBUG "Bad block table at page %x, version 0x%02X\n", + md->pages[0], md->version[0]); + PRINTK( "Mirror bad block table at page %x, version 0x%02X\n", + md->pages[0], md->version[0]); + } + PRINTK("<-- %s\n", __FUNCTION__); + return 1; +} + +/* + * Scan a given block full + */ +static int brcmnand_scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd, + loff_t offs, uint8_t *buf, size_t readlen, + int scanlen, int len) +{ + int ret, j; + + ret = brcmnand_scan_read_raw(mtd, buf, offs, readlen); + if (ret) + return ret; + + for (j = 0; j < len; j++, buf += scanlen) { + if (check_pattern(buf, scanlen, mtd->writesize, bd)) + return 1; + } + return 0; +} + +/* + * Scan a given block partially + * @offs: Offset of start of block + * @len: Number of pages to scan + * For MLC we need to read backwards from the end of the block + */ +static int brcmnand_scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd, + loff_t offs, uint8_t *buf, int len) +{ + struct mtd_oob_ops ops; + int j, ret; + int dir; + struct brcmnand_chip *this = mtd->priv; + + /* + * THT 8/23/2010 Changed to use low level test. + * Apparently new Micron chips are SLC, but behaves like an MLC flash (requires BCH-4). + * The old high level test would look for the BI indicator at the wrong page. + * + * if (!MTD_IS_MLC(mtd)) { // SLC: First and 2nd page + * dir = 1; + * } + */ + if (!NAND_IS_MLC(this)) { // SLC: First and 2nd page + dir = 1; + }else { // MLC: Read last page (and next to last page). + int pagesPerBlock = mtd->erasesize / mtd->writesize; + + dir = -1; + offs += (loff_t)((pagesPerBlock - 1 ) * mtd->writesize); + } + ops.len = mtd->oobsize; + ops.ooblen = mtd->oobsize; + ops.oobbuf = buf; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.mode = MTD_OPS_PLACE_OOB; + + for (j = 0; j < len; j++) { + ret = mtd_read_oob(mtd, offs, &ops); + if (gdebug && ret != 0) printk("########## %s: read_oob returns %d\n", __FUNCTION__, ret); + + + if (ret == -EBADMSG || ret == -EIO || ret == -ETIMEDOUT) { // Uncorrectable errors + uint32_t acc0; + + // Disable ECC + acc0 = brcmnand_disable_read_ecc(this->ctrl->CS[this->csi]); + + // Re-read the OOB + ret = mtd_read_oob(mtd, offs, &ops); + + // Enable ECC back + brcmnand_restore_ecc(this->ctrl->CS[this->csi], acc0); + } + + if (ret) { + PRINTK("%s: read_oob returns error %d\n", __FUNCTION__, ret); + return ret; + } + + if (check_short_pattern(buf, bd)) + return 1; + + + offs += ((int64_t)dir * mtd->writesize); + } + return 0; +} + +/** + * brcmnand_create_bbt - [GENERIC] Create a bad block table by scanning the device + * @mtd: MTD device structure + * @buf: temporary buffer + * @bd: descriptor for the good/bad block search pattern + * @chip: create the table for a specific chip, -1 read all chips. + * Applies only if NAND_BBT_PERCHIP option is set + * + * Create a bad block table by scanning the device + * for the given good/bad block identify pattern + */ +static int brcmnand_create_bbt(struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *bd, int chip) +{ + struct brcmnand_chip *this = mtd->priv; + int i, len, scanlen; + uint64_t numblocks, startblock; + + loff_t from; + size_t readlen; + + + PRINTK("-->brcmnand_create_bbt, bbt_erase_shift=%d, this->page_shift=%d\n", this->bbt_erase_shift, this->page_shift); + printk(KERN_INFO "Scanning device for bad blocks, options=%08x\n", bd->options); + + if (bd->options & NAND_BBT_SCANALLPAGES) + len = 1 << (this->bbt_erase_shift - this->page_shift); + else { // Also for MLC + if (bd->options & NAND_BBT_SCAN2NDPAGE) { + if (this->options & NAND_SCAN_BI_3RD_PAGE) { + len = 3; // For Hynix MLC chips + }else { + len = 2; + } + }else + len = 1; + } + + if (!(bd->options & NAND_BBT_SCANEMPTY)) { + /* We need only read few bytes from the OOB area */ + scanlen = 0; + readlen = bd->len; + } else { + /* Full page content should be read */ + scanlen = mtd->writesize + mtd->oobsize; + readlen = len * mtd->writesize; + } + + if (chip == -1) { + /* Note that numblocks is 2 * (real numblocks) here, see i+=2 + * below as it makes shifting and masking less painful */ + numblocks = device_size(mtd) >> (this->bbt_erase_shift - 1); + startblock = 0ULL; + from = 0LL; + } else { + if (chip >= this->ctrl->numchips) { + printk(KERN_WARNING "brcmnand_create_bbt(): chipnr (%d) > available chips (%d)\n", + chip + 1, this->ctrl->numchips); + return -EINVAL; + } + numblocks = this->chipSize >> (this->bbt_erase_shift - 1); + startblock = chip * numblocks; + numblocks += startblock; + from = startblock << (this->bbt_erase_shift - 1); + } + +//gdebug=4; + if (gdebug > 3) { + PRINTK("Starting for loop: from=%0llx bd->options=%08x, startblock=%d numblocks=%d\n", + from, bd->options, mtd64_ll_low(startblock), mtd64_ll_low(numblocks)); + } + for (i = startblock; i < numblocks; ) { + int ret; + + if (bd->options & NAND_BBT_SCANALLPAGES) + ret = brcmnand_scan_block_full(mtd, bd, from, buf, readlen, + scanlen, len); + else + ret = brcmnand_scan_block_fast(mtd, bd, from, buf, len); + + /* + * THT 8/24/10: Fall through and mark it as bad if -EBADMSG. + * We want to mark it as bad if we can't read it, but we also + * don't want to mark the block as bad due to a timeout for example + */ + if (ret < 0 && ret != (-EBADMSG) && ret != (-EIO)) { + PRINTK("$$$$$$$$$$$$$$$$$$$$ brcmnand_scan_block_{fast/full} returns %d\n", ret); + // THT 8/24/10: Go to next block instead of returning + // return ret; + } + // -EBADMSG, -EIO and +1 (marked as bad) go here: + else if (ret) { + this->bbt[i >> 3] |= 0x03 << (i & 0x6); + printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", + i >> 1, (unsigned int)from); + PRINTK("$$$$$$$$$$$$$$$ Bad eraseblock %d at 0x%08x, ret=%d\n", + i >> 1, (unsigned int)from, ret); + mtd->ecc_stats.badblocks++; + } + + i += 2; + from += (loff_t)(1 << this->bbt_erase_shift); + } + +//gdebug=0; + return 0; +} + +/** + * brcmnand_search_bbt - [GENERIC] scan the device for a specific bad block table + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * + * Read the bad block table by searching for a given ident pattern. + * Search is preformed either from the beginning up or from the end of + * the device downwards. The search starts always at the start of a + * block. + * If the option NAND_BBT_PERCHIP is given, each chip is searched + * for a bbt, which contains the bad block information of this chip. + * This is necessary to provide support for certain DOC devices. + * + * The bbt ident pattern resides in the oob area of the first page + * in a block. + */ +static int brcmnand_search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td) +{ + struct brcmnand_chip *this = mtd->priv; + int i, chips; + uint32_t bits, startblock, block; + int dir; + int scanlen = mtd->writesize + mtd->oobsize; + int bbtblocks; + int blocktopage = this->bbt_erase_shift - this->page_shift; + int ret = 0; + + PRINTK("-->%s, CS=%d numchips=%d, mtdSize=%llx, mtd->size=%llx\n", __FUNCTION__, this->ctrl->CS[this->csi], this->ctrl->numchips, this->mtdSize, mtd->size); + + /* Search direction top -> down ? */ + if (td->options & NAND_BBT_LASTBLOCK) { + startblock = (uint32_t)(this->mtdSize >> this->bbt_erase_shift) - 1; + dir = -1; + } else { + startblock = 0; + dir = 1; + } + + /* Do we have a bbt per chip ? */ + if (td->options & NAND_BBT_PERCHIP) { + chips = this->ctrl->numchips; + bbtblocks = this->chipSize >> this->bbt_erase_shift; + startblock &= bbtblocks - 1; + } else { + chips = 1; + bbtblocks = (uint32_t)(this->mtdSize >> this->bbt_erase_shift); + + } + + /* Number of bits for each erase block in the bbt */ + bits = td->options & NAND_BBT_NRBITS_MSK; + + PRINTK("%s: startblock=%d, dir=%d, chips=%d\n", __FUNCTION__, (int)startblock, dir, chips); + + for (i = 0; i < chips; i++) { + /* Reset version information */ + td->version[i] = 0; + td->pages[i] = BBT_NULL_PAGE; + /* Scan the maximum number of blocks */ + for (block = 0; block < td->maxblocks; block++) { + + int64_t actblock = startblock + dir * block; + loff_t offs = (uint64_t)actblock << this->bbt_erase_shift; + + /* Read first page */ + ret = brcmnand_scan_read_raw(mtd, buf, offs, mtd->writesize); + + /* Here if the read routine returns -77 then the BBT data is invalid, ignore it */ + + // Ignore BBT if not there. + if (ret) + continue; + + PRINTK("Checking Sig %c%c%c%c against OOB\n", td->pattern[0], td->pattern[1], td->pattern[2], td->pattern[3]); + + /* If scan-auto mode, fish out the useful data from the ECC stuffs */ + if (td->options & BRCMNAND_BBT_AUTO_PLACE) { + u_char abuf[16]; + struct mtd_oob_ops ops; + + memset(abuf, 0, 16); + ops.mode = MTD_OPS_AUTO_OOB; + ops.ooboffs = 0; + ops.ooblen = mtd->oobsize; + ops.oobbuf = abuf; + ops.datbuf = buf; + ops.len = mtd->writesize; + this->oob_poi = &buf[mtd->writesize]; + + (void)brcmnand_transfer_oob(this, abuf, &ops, td->len + 1); +//printk("BCH-8-16 scan: \n"); +//print_oobbuf(abuf, td->len+1); + + /* Look for pattern at the beginning of OOB auto-buffer */ + if (!check_pattern(abuf, mtd->oobsize, 0, td)) { + PRINTK("%s: Found BBT at offset %0llx\n", __FUNCTION__, offs); + td->pages[i] = actblock << blocktopage; + if (td->options & NAND_BBT_VERSION) { + td->version[i] = abuf[td->veroffs]; + } + break; + } + + }else if (!check_pattern(buf, scanlen, mtd->writesize, td)) { + PRINTK("%s: Found BBT at offset %0llx\n", __FUNCTION__, offs); + td->pages[i] = actblock << blocktopage; + if (td->options & NAND_BBT_VERSION) { + td->version[i] = buf[mtd->writesize + td->veroffs]; + } + break; + } + } + startblock += this->chipSize >> this->bbt_erase_shift; + } + /* Check, if we found a bbt for each requested chip */ + for (i = 0; i < chips; i++) { + if (td->pages[i] == BBT_NULL_PAGE) { + printk(KERN_WARNING "Bad block table %c%c%c%c not found for chip on CS%d\n", + td->pattern[0], td->pattern[1], td->pattern[2], td->pattern[3], this->ctrl->CS[this->csi]); + PRINTK( "**************** Bad block table %c%c%c%c not found for chip on CS%d\n", + td->pattern[0], td->pattern[1], td->pattern[2], td->pattern[3], this->ctrl->CS[this->csi]); + }else { + printk(KERN_DEBUG "Bad block table %c%c%c%c found at page %08lx, version 0x%02X for chip on CS%d\n", + td->pattern[0], td->pattern[1], td->pattern[2], td->pattern[3], + (unsigned long)td->pages[i], td->version[i], this->ctrl->CS[this->csi]); + PRINTK( "############# Bad block table %c%c%c%c found at page %08lx, version 0x%02X for chip on CS%d\n", + td->pattern[0], td->pattern[1], td->pattern[2], td->pattern[3], + (unsigned long)td->pages[i], td->version[i], this->ctrl->CS[this->csi]); + } + } + return 0; +} + +/** + * brcmnand_search_read_bbts - [GENERIC] scan the device for bad block table(s) + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @md: descriptor for the bad block table mirror + * + * Search and read the bad block table(s) + */ +static int brcmnand_search_read_bbts(struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *td, struct nand_bbt_descr *md) +{ + PRINTK("-->%s\n", __FUNCTION__); + + /* Search the primary table */ + brcmnand_search_bbt(mtd, buf, td); + + /* Search the mirror table */ + if (md) + brcmnand_search_bbt(mtd, buf, md); + + /* Force result check */ + return 1; +} + + +/** + * brcmnand_write_bbt - [GENERIC] (Re)write the bad block table + * + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @md: descriptor for the bad block table mirror + * @chipsel: selector for a specific chip, -1 for all + * + * (Re)write the bad block table + * THT: 1/16/07: TO DO: Currently, if writing to the block failed, we punt. + * TBD: Use skip block mechanism, and skip over real bad blocks, so we would either start at the 1MB offset from bottom + * and go down, or start from the bottom and go up, skipping over bad blocks until we reach the 1MB partition reserved + * for BBT. + * + */ +static int brcmnand_write_bbt(struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *td, struct nand_bbt_descr *md, + int chipsel) +{ + struct brcmnand_chip *this = mtd->priv; + struct erase_info einfo; + int i, j, res, chip = 0, skip = 0, dir = 0; + uint32_t bits, offs, sft, sftmsk, bbtoffs; + int64_t startblock = 0ULL, numblocks, page = 0ULL, i64; + int nrchips, pageoffs, ooboffs; + uint8_t msk[4]; + uint8_t rcode = td->reserved_block_code; + size_t retlen, len = 0; + loff_t to; + struct mtd_oob_ops ops; + + int save_gdebug = gdebug; + +//gdebug=4; + + DEBUG(MTD_DEBUG_LEVEL3, "-->%s\n", __FUNCTION__); + PRINTK("-->%s, chipsel=%d\n", __FUNCTION__, chipsel); + ops.ooblen = mtd->oobsize; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.mode = MTD_OPS_PLACE_OOB; + + if (!rcode) + rcode = 0xff; + /* Write bad block table per chip rather than per device ? */ + if (td->options & NAND_BBT_PERCHIP) { + numblocks = (this->chipSize >> this->bbt_erase_shift); + /* Full device write or specific chip ? */ + if (chipsel == -1) { + nrchips = this->ctrl->numchips; + } else { + nrchips = chipsel + 1; + chip = chipsel; + } + } else { + numblocks = (this->mtdSize >> this->bbt_erase_shift); + nrchips = 1; + } + + PRINTK("%s Creating %c%c%c%c numblocks=%d, nrchips=%d, td->pages[0]=%llx\n", + __FUNCTION__, td->pattern[0], td->pattern[1], td->pattern[2], td->pattern[3], (int)numblocks, nrchips, td->pages[0]); + + /* Loop through the chips */ + for (; chip < nrchips; chip++) { + + /* There was already a version of the table, reuse the page + * This applies for absolute placement too, as we have the + * page nr. in td->pages. + */ + if (td->pages[chip] != BBT_NULL_PAGE) { + page = td->pages[chip]; + PRINTK("There is already a version of the table, go ahead and write it\n"); + goto write; + } + + /* Automatic placement of the bad block table */ + /* Search direction top -> down ? */ + if (td->options & NAND_BBT_LASTBLOCK) { + startblock = numblocks * (chip + 1) - 1; + dir = -1; + } else { + startblock = chip * numblocks; + dir = 1; + } + skip = 0; + + write_retry: + PRINTK("%s: write_retry: startblock=%0llx, dir=%d, td->maxblocks=%d, skip=%d\n", + __FUNCTION__, startblock, dir, td->maxblocks, skip); + + for (i = skip; i < td->maxblocks; i++) { + uint64_t block = startblock + (int64_t)(dir * i); + // THT One byte contains 4 set of 2-bits, so divide block by 4 to index the BBT byte + uint32_t blockindex = (uint32_t)(block >> 2); + + /* Check, if the block is bad */ + + PRINTK("%s: Checking BBT: i=%d, block=%0llx, BBT=%08x\n", + __FUNCTION__, i, block, this->bbt[blockindex]); + + // THT: bbt[blockindex] is the byte we are looking for, now get the 2 bits that + // is the BBT for the block (Shift (0,1,2,3) *2 positions depending on the block modulo 4) + switch ((this->bbt[blockindex] >> (2 * (block & 0x03))) + & 0x03) { + case 0x01: + case 0x03: + continue; + } + page = block << (this->bbt_erase_shift - this->page_shift); + + PRINTK("%s: Checking BBT2: page=%llx, md->pages[chip]=%llx\n", + __FUNCTION__, page, md->pages[chip]); + + /* Check, if the block is used by the mirror table */ + if (!md || md->pages[chip] != page) + goto write; + } + printk(KERN_ERR "No space left to write bad block table %c%c%c%c\n", + td->pattern[0], td->pattern[1], td->pattern[2], td->pattern[3]); + brcmnand_post_mortem_dump(mtd, page << this->page_shift); + return -ENOSPC; + write: + + /* Set up shift count and masks for the flash table */ + bits = td->options & NAND_BBT_NRBITS_MSK; + PRINTK("%s: bits=%d\n", __FUNCTION__, bits); + msk[2] = ~rcode; + switch (bits) { + case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; + msk[3] = 0x01; + break; + case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; + msk[3] = 0x03; + break; + case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; + msk[3] = 0x0f; + break; + case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; + msk[3] = 0xff; + break; + default: return -EINVAL; + } + + bbtoffs = chip * ((uint32_t)(numblocks >> 2)); + + to = (uint64_t)page << this->page_shift; + + /* Must we save the block contents ? */ + if (td->options & NAND_BBT_SAVECONTENT) { + /* Make it block aligned */ + PRINTK("%s: NAND_BBT_SAVECONTENT\n", __FUNCTION__); + //to &= ~((loff_t) ((1 << this->bbt_erase_shift) - 1)); + to = to & ( ~((1 << this->bbt_erase_shift) - 1)); + len = 1 << this->bbt_erase_shift; + res = mtd_read(mtd, to, len, &retlen, buf); + if (res < 0) { + if (retlen != len) { + printk(KERN_INFO "nand_bbt: Error " + "reading block for writing " + "the bad block table\n"); + return res; + } + printk(KERN_WARNING "nand_bbt: ECC error " + "while reading block for writing " + "bad block table\n"); + } + /* Read oob data */ + ops.len = (len >> this->page_shift) * mtd->oobsize; + ops.oobbuf = &buf[len]; + res = mtd_read_oob(mtd, to + mtd->writesize, &ops); + if (res < 0 || ops.retlen != ops.len) + goto outerr; + + /* Calc the byte offset in the buffer */ + pageoffs = page - (to >> this->page_shift); + + // offs is offset from start of buffer, so it is OK to be 32bit. + offs = pageoffs << this->page_shift; + /* Preset the bbt area with 0xff */ + memset(&buf[offs], 0xff, (size_t)(numblocks >> sft)); + ooboffs = len + (pageoffs * mtd->oobsize); + + } else { + PRINTK("%s: Not NAND_BBT_SAVECONTENT\n", __FUNCTION__); + /* Calc length */ + len = (size_t)(numblocks >> sft); + /* Make it page aligned ! */ + len = (len + (mtd->writesize - 1)) & + ~(mtd->writesize - 1); + /* Preset the buffer with 0xff */ + memset(buf, 0xff, len + + (len >> this->page_shift) * mtd->oobsize); + offs = 0; + ooboffs = len; + + /* Auto-place for BCH-8 on 16B OOB? */ + if (td->options & BRCMNAND_BBT_AUTO_PLACE) { + u_char abuf[8]; + struct mtd_oob_ops ops; + + memcpy(abuf, td->pattern, td->len); + // Write the version number (1 byte) + if (td->options & NAND_BBT_VERSION) { + abuf[td->veroffs] = td->version[0]; + } + + ops.datbuf = NULL; + ops.len = 0; + ops.mode = MTD_OPS_AUTO_OOB; + ops.ooboffs = 0; + ops.ooblen = td->len + 1; /* 5 bytes */ + ops.oobbuf = abuf; /* Source oobbuf */ + this->oob_poi = &buf[ooboffs]; /* Destination oobbuf */ + + /* Copy abuf into OOB free bytes */ + (void)brcmnand_fill_oob(this, abuf, &ops); + + }else { /* IN-PLACE OOB format */ + /* Pattern is located in oob area of first page */ + memcpy(&buf[ooboffs + td->offs], td->pattern, td->len); + + // Write the version number (1 byte) + if (td->options & NAND_BBT_VERSION) { + buf[ooboffs + td->veroffs] = td->version[0]; + } + } + } + + /* walk through the memory table */ + /* + * THT: Right now we are safe, but when numblocks exceed 32bit, + * then we need to look at these codes again, + * as we may need to break the BBT into 2 or more tables that a uint32_t can index. + */ + for (i64 = 0ULL; i64 < numblocks; ) { + uint8_t dat; + uint32_t irs2 = (uint32_t)(i64 >> 2); // Ihdex into BBT + + /* + * Make sure that the cast above for b64i is not lossy + */ + if (mtd64_ll_high(i64 >> 2)) { + printk(KERN_ERR "FIXME: %s: integer index to BBT overflow %0llx\n", __FUNCTION__, i64 >> 2); + } + dat = this->bbt[bbtoffs + irs2]; + for (j = 0; j < 4; j++, i64++) { + uint32_t sftcnt = (uint32_t)((i64 << (3 - sft)) & sftmsk); + /* Do not store the reserved bbt blocks ! */ + buf[offs + (uint32_t)(i64 >> sft)] &= + ~(msk[dat & 0x03] << sftcnt); + dat >>= 2; + } + } + + memset(&einfo, 0, sizeof(einfo)); + einfo.mtd = mtd; + einfo.addr = to; + + einfo.len = 1ULL << this->bbt_erase_shift; + res = this->erase_bbt(mtd, &einfo, 1, 1); // Do not look up BBT + if (res < 0) { + printk(KERN_ERR "brcmnand_bbt: Error during block erase at %0llx: %d\n", to, res); + skip++; + goto write_retry; + } + +//gdebug = 4; + res = brcmnand_scan_write_bbt(mtd, to, len, buf, &buf[len]); +//gdebug = 0; + if (res < 0) { + // THT: If writing reports a bad block, we will skip it, and retry. Eventually may + // run out of td->maxblocks + printk(KERN_INFO "write_bbt returns flash status error at %0llx, skipping and retrying...\n", + to); + skip++; + goto write_retry; + } + + printk(KERN_DEBUG "Bad block table written to 0x%08x, version " + "0x%02X\n", (unsigned int)to, td->version[chip]); + + /* Mark it as used */ + td->pages[chip] = page; + } + gdebug = save_gdebug; + return 0; + + outerr: + gdebug = save_gdebug; + printk(KERN_WARNING + "brcmnand_bbt: Error while writing bad block table %d\n", res); + return res; +} + +/** + * brcmnand_memory_bbt - [GENERIC] create a memory based bad block table + * @mtd: MTD device structure + * @bd: descriptor for the good/bad block search pattern + * + * The function creates a memory based bbt by scanning the device + * for manufacturer / software marked good / bad blocks + */ +static inline int brcmnand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) +{ + struct brcmnand_chip *this = mtd->priv; + + bd->options &= ~NAND_BBT_SCANEMPTY; + return brcmnand_create_bbt(mtd, this->ctrl->buffers->databuf, bd, -1); +} + +/** + * brcmnand_check_create - [GENERIC] create and write bbt(s) if necessary + * @mtd: MTD device structure + * @buf: temporary buffer + * @bd: descriptor for the good/bad block search pattern + * + * The function checks the results of the previous call to brcmnand_read_bbt + * and creates / updates the bbt(s) if necessary + * Creation is necessary if no bbt was found for the chip/device + * Update is necessary if one of the tables is missing or the + * version nr. of one table is less than the other + */ +static int brcmnand_check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd) +{ + int i, chips, writeops, chipsel, res; + struct brcmnand_chip *this = mtd->priv; + struct nand_bbt_descr *td = this->bbt_td; + struct nand_bbt_descr *md = this->bbt_md; + struct nand_bbt_descr *rd, *rd2; + + PRINTK("-->%s, td=%p, md=%p\n", __FUNCTION__, td, md); + /* Do we have a bbt per chip ? */ + if (td->options & NAND_BBT_PERCHIP) + chips = this->ctrl->numchips; + else + chips = 1; + + for (i = 0; i < chips; i++) { + writeops = 0; + rd = NULL; + rd2 = NULL; + /* Per chip or per device ? */ + chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1; + + /* + * THT: Reset version to 0 if 0xff + */ + if ((td->options & NAND_BBT_VERSION) && (td->version[i] == 0xff) && td->pages[i] != BBT_NULL_PAGE) + td->version[i] = 0; + if ((md->options & NAND_BBT_VERSION) && (md->version[i] == 0xff) && md->pages[i] != BBT_NULL_PAGE) + md->version[i] = 0; + + /* Mirrored table available ? */ + if (md) { + if (td->pages[i] == BBT_NULL_PAGE && md->pages[i] == BBT_NULL_PAGE) { + writeops = 0x03; + goto create; + } + + if (td->pages[i] == BBT_NULL_PAGE) { + rd = md; + td->version[i] = md->version[i]; + writeops = 1; + goto writecheck; + } + + if (md->pages[i] == BBT_NULL_PAGE) { + rd = td; + md->version[i] = td->version[i]; + writeops = 2; + goto writecheck; + } + + if (td->version[i] == md->version[i]) { + rd = td; + if (!(td->options & NAND_BBT_VERSION)) + rd2 = md; + goto writecheck; + } + + if (((int8_t)(td->version[i] - md->version[i])) > 0) { + rd = td; + md->version[i] = td->version[i]; + writeops = 2; + } else { + rd = md; + td->version[i] = md->version[i]; + writeops = 1; + } + + goto writecheck; + + } else { + if (td->pages[i] == BBT_NULL_PAGE) { + writeops = 0x01; + goto create; + } + rd = td; + goto writecheck; + } + create: + /* Create the bad block table by scanning the device ? */ + if (!(td->options & NAND_BBT_CREATE)) + continue; + + /* Create the table in memory by scanning the chip(s) */ + brcmnand_create_bbt(mtd, buf, bd, chipsel); + + td->version[i] = 1; + if (md) + md->version[i] = 1; + writecheck: + res = 0; + + PRINTK("%s: writeops=%d, rd=%p, rd2=%p\n", __FUNCTION__, writeops, rd, rd2); + + /* read back first ? */ + if (rd) { + PRINTK("%s: Read rd\n", __FUNCTION__); + res = brcmnand_read_abs_bbt(mtd, buf, rd, chipsel); + } + /* If they weren't versioned, read both. */ + if (rd2) { + if (res != 0) { + int bbtlen = (uint32_t)(this->mtdSize >> (this->bbt_erase_shift + 2)); + /* Clear the in-memory BBT first */ + PRINTK("%s: Discarding previously read BBT %c%c%c%c, res=%d\n", + __FUNCTION__, rd->pattern[0], rd->pattern[1], rd->pattern[2], rd->pattern[3], res); + memset(this->bbt, 0, bbtlen); + } + PRINTK("%s: Read rd2\n", __FUNCTION__); + res = brcmnand_read_abs_bbt(mtd, buf, rd2, chipsel); + if (res != 0) { + PRINTK("%s: Read BBT %c%c%c%c returns res=%d, discarding\n", + __FUNCTION__, rd2->pattern[0], rd2->pattern[1], rd2->pattern[2], rd2->pattern[3], res); + } + } + + /* Write the bad block table to the device ? */ + if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { + res = brcmnand_write_bbt(mtd, buf, td, md, chipsel); + if (res < 0) + return res; + } + + /* Write the mirror bad block table to the device ? */ + if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { + res = brcmnand_write_bbt(mtd, buf, md, td, chipsel); + if (res < 0) + return res; + } + } + return 0; +} + +/** + * mark_bbt_regions - [GENERIC] mark the bad block table regions + * @mtd: MTD device structure + * @td: bad block table descriptor + * + * The bad block table regions are marked as "bad" to prevent + * accidental erasures / writes. The regions are identified by + * the mark 0x02. + */ +static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) +{ + struct brcmnand_chip *this = mtd->priv; + int i, j, update; + uint32_t chips, block, nrblocks; + uint8_t oldval, newval; + + /* Do we have a bbt per chip ? */ + if (td->options & NAND_BBT_PERCHIP) { + chips = this->ctrl->numchips; + nrblocks = (int)(this->chipSize >> this->bbt_erase_shift); + } else { + chips = 1; + nrblocks = (uint32_t)(this->mtdSize >> this->bbt_erase_shift); + } + + for (i = 0; i < chips; i++) { + if ((td->options & NAND_BBT_ABSPAGE) || + !(td->options & NAND_BBT_WRITE)) { + if (td->pages[i] == BBT_NULL_PAGE) + continue; + block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift); + block <<= 1; + oldval = this->bbt[(block >> 3)]; + newval = oldval | (0x2 << (block & 0x06)); + this->bbt[(block >> 3)] = newval; + if ((oldval != newval) && td->reserved_block_code) + brcmnand_update_bbt(mtd, block << (this->bbt_erase_shift - 1)); + continue; + } + update = 0; + if (td->options & NAND_BBT_LASTBLOCK) + block = ((i + 1) * nrblocks) - td->maxblocks; + else + block = i * nrblocks; + block <<= 1; + for (j = 0; j < td->maxblocks; j++) { + oldval = this->bbt[(block >> 3)]; + newval = oldval | (0x2 << (block & 0x06)); + this->bbt[(block >> 3)] = newval; + if (oldval != newval) + update = 1; + block += 2; + } + /* If we want reserved blocks to be recorded to flash, and some + new ones have been marked, then we need to update the stored + bbts. This should only happen once. */ + if (update && td->reserved_block_code) + brcmnand_update_bbt(mtd, (block - 2) << (this->bbt_erase_shift - 1)); + } +} + +/** + * brcmnand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s) + * @mtd: MTD device structure + * @bd: descriptor for the good/bad block search pattern + * + * The function checks, if a bad block table(s) is/are already + * available. If not it scans the device for manufacturer + * marked good / bad blocks and writes the bad block table(s) to + * the selected place. + * + * The bad block table memory is allocated here. It must be freed + * by calling the nand_free_bbt function. + * + */ +int brcmnand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) +{ + struct brcmnand_chip *this = mtd->priv; + int res = 0; + uint32_t len; + uint8_t *buf; + struct nand_bbt_descr *td = this->bbt_td; + struct nand_bbt_descr *md = this->bbt_md; + + PRINTK("-->%s: chip=%p, td->options=%08x, md->options=%08x\n", __FUNCTION__, this, td->options, md->options); + + len = (uint32_t)(this->mtdSize >> (this->bbt_erase_shift + 2)); + /* Allocate memory (2bit per block) */ + + PRINTK("brcmnand_scan_bbt: Allocating %d byte for BBT. mtd->size=%lld, eraseshift=%d\n", + len, this->mtdSize, this->bbt_erase_shift); + + + this->bbt = (uint8_t*)kmalloc(len, GFP_KERNEL); + + if (!this->bbt) { + printk(KERN_ERR "brcmnand_scan_bbt: Out of memory, bbt_erase_shift=%d, len=%d\n", + this->bbt_erase_shift, len); + return -ENOMEM; + + } + /* Clear the memory bad block table */ + memset(this->bbt, 0x00, len); + + /* If no primary table decriptor is given, scan the device + * to build a memory based bad block table + */ + if (!td) { + if ((res = brcmnand_memory_bbt(mtd, bd))) { + printk(KERN_ERR "brcmnand_bbt: Can't scan flash and build the RAM-based BBT\n"); + kfree(this->bbt); + this->bbt = NULL; + } + return res; + } + + /* Allocate a temporary buffer for one eraseblock incl. oob */ + len = (1 << this->bbt_erase_shift); + PRINTK("%s: len before OOB = %08x\n", __FUNCTION__, len); + len += (len >> this->page_shift) * (mtd->oobsize); + PRINTK("%s: Inc OOB - Allocating %08x byte buffer, oobsize=%d\n", __FUNCTION__, len, mtd->oobsize); + buf = kmalloc(len, GFP_KERNEL); + if (!buf) { + printk(KERN_ERR "%s: Out of memory 2, bbt_erase_shift=%d, len=%dx\n", + __FUNCTION__, this->bbt_erase_shift, len ); + + kfree(this->bbt); + + this->bbt = NULL; + return -ENOMEM; + } + + /* Is the bbt at a given page ? */ + if (td->options & NAND_BBT_ABSPAGE) { + res = brcmnand_read_abs_bbts(mtd, buf, td, md); + } else { + /* Search the bad block table using a pattern in oob */ + res = brcmnand_search_read_bbts(mtd, buf, td, md); + } + + if (res) { + res = brcmnand_check_create(mtd, buf, bd); + } + + /* Prevent the bbt regions from erasing / writing */ + mark_bbt_region(mtd, td); + if (md) + mark_bbt_region(mtd, md); + + kfree(buf); + return res; +} +EXPORT_SYMBOL(brcmnand_scan_bbt); + + +/** + * brcmnand_update_bbt - [NAND Interface] update bad block table(s) + * @mtd: MTD device structure + * @offs: the offset of the newly marked block + * + * The function updates the bad block table(s) + */ +int brcmnand_update_bbt(struct mtd_info *mtd, loff_t offs) +{ + struct brcmnand_chip *this = mtd->priv; + int len, res = 0, writeops = 0; + int chip, chipsel; + uint8_t *buf; + struct nand_bbt_descr *td = this->bbt_td; + struct nand_bbt_descr *md = this->bbt_md; + + DEBUG(MTD_DEBUG_LEVEL3, "-->%s offs=%0llx\n", __FUNCTION__, offs); + PRINTK("-->%s offs=%0llx\n", __FUNCTION__, offs); + + if (!this->bbt || !td) + return -EINVAL; + + len = (uint32_t)(this->mtdSize >> (this->bbt_erase_shift + 2)); + /* Allocate a temporary buffer for one eraseblock incl. oob */ + len = (1 << this->bbt_erase_shift); + len += (len >> this->page_shift) * mtd->oobsize; + buf = kmalloc(len, GFP_ATOMIC); + if (!buf) { + printk(KERN_ERR "brcmnand_update_bbt: Out of memory\n"); + return -ENOMEM; + } + + writeops = md != NULL ? 0x03 : 0x01; + + /* Do we have a bbt per chip ? */ + if (td->options & NAND_BBT_PERCHIP) { + chip = (int)(offs >> this->chip_shift); + chipsel = chip; + } else { + chip = 0; + chipsel = -1; + } + + (td->version[chip])++; + // THT Roll over + if (td->version[chip] == 0xff) + td->version[chip] = 1; + if (md) + (md->version[chip])++; + if (md->version[chip] == 0xff) + md->version[chip] = 1; + + /* Write the bad block table to the device ? */ + if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { + res = brcmnand_write_bbt(mtd, buf, td, md, chipsel); + if (res < 0) + goto out; + } + /* Write the mirror bad block table to the device ? */ + if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { + res = brcmnand_write_bbt(mtd, buf, md, td, chipsel); + } + + out: + kfree(buf); + return res; +} + +/* Define some generic bad / good block scan pattern which are used + * while scanning a device for factory marked good / bad blocks. */ +static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; + +static struct nand_bbt_descr smallpage_memorybased = { + .options = NAND_BBT_SCAN2NDPAGE, + .offs = 5, + .len = 1, + .pattern = scan_ff_pattern +}; + +static struct nand_bbt_descr largepage_memorybased = { + .options = NAND_BBT_SCAN2NDPAGE, + .offs = 0, + .len = 2, + .pattern = scan_ff_pattern +}; + +/* + static struct nand_bbt_descr mlc_4kpage_memorybased = { + .options = NAND_BBT_SCAN2NDPAGE, + .offs = 0, + .len = 1, + .pattern = scan_ff_pattern + }; + */ + +static struct nand_bbt_descr smallpage_flashbased = { + .options = NAND_BBT_SCAN2NDPAGE, + .offs = 5, + .len = 1, + .pattern = scan_ff_pattern +}; + +static struct nand_bbt_descr largepage_flashbased = { + .options = NAND_BBT_SCAN2NDPAGE, + .offs = 0, + .len = 2, + .pattern = scan_ff_pattern +}; + +/* 2K & 4K page MLC NAND use same pattern */ +static struct nand_bbt_descr bch4_flashbased = { + .options = NAND_BBT_SCAN2NDPAGE, + .offs = 0, + .len = 1, + .pattern = scan_ff_pattern +}; + +#if 0 +static uint8_t scan_agand_pattern[] = { 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7 }; + +static struct nand_bbt_descr agand_flashbased = { + .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, + .offs = 0x20, + .len = 6, + .pattern = scan_agand_pattern +}; +#endif + +/* Generic flash bbt decriptors + */ +static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' }; +static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' }; + +/* + * THT: We only have 1 chip per device + */ +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE +#if 0 //CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_3_3 + | NAND_BBT_PERCHIP +#endif + | NAND_BBT_2BIT | NAND_BBT_VERSION, + + .offs = 9, /* THT: Changed from 8 */ + .len = 4, + .veroffs = 13, /* THT: Changed from 12 */ + .maxblocks = 4, /* THT: Will update later, based on 1MB partition reserved for BBT */ + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE +#if 0 //CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_3_3 + | NAND_BBT_PERCHIP +#endif + | NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 9, /* THT: Changed from 8 */ + .len = 4, + .veroffs = 13, /* THT: Changed from 12 */ + .maxblocks = 4, + .pattern = mirror_pattern +}; + +/* SLC flash using BCH-4 ECC, SM & Large page use same descriptor template */ +static struct nand_bbt_descr bbt_slc_bch4_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE +#if 0 //CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_3_3 + | NAND_BBT_PERCHIP +#endif + | NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 1, /* THT: Changed from 8 */ + .len = 4, + .veroffs = 6, /* THT: Changed from 12 */ + .maxblocks = 8, + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_slc_bch4_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE +#if 0 //CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_3_3 + | NAND_BBT_PERCHIP +#endif + | NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 1, + .len = 4, + .veroffs = 6, + .maxblocks = 8, + .pattern = mirror_pattern +}; + +/* Also used for bch-8 & bch-12 with 27B OOB */ +static struct nand_bbt_descr bbt_bch4_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 1, + .len = 4, + .veroffs = 5, /* THT: Changed from 12 */ + .maxblocks = 8, /* THT: Will update later, based on 4MB partition reserved for BBT */ + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_bch4_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 1, /* THT: Changed from 8 */ + .len = 4, + .veroffs = 5, /* THT: Changed from 12 */ + .maxblocks = 8, + .pattern = mirror_pattern +}; + + + +/* BCH-8 with only 16B OOB, uses auto-place for the (small) OOB */ +static struct nand_bbt_descr bbt_bch8_16_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION + | BRCMNAND_BBT_AUTO_PLACE, + .offs = 0, /* Signature is at offset 0 in auto-place format */ + .len = 4, + .veroffs = 4, /* Version just follows the signature in auto-place format */ + .maxblocks = 8, /* THT: Will update later, based on 4MB partition reserved for BBT */ + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_bch8_16_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION + | BRCMNAND_BBT_AUTO_PLACE, + .offs = 0, + .len = 4, + .veroffs = 4, + .maxblocks = 8, + .pattern = mirror_pattern +}; + + +static int brcmnand_displayBBT(struct mtd_info* mtd) +{ + struct brcmnand_chip *this = mtd->priv; + loff_t bOffset, bOffsetStart, bOffsetEnd; + int res; + int bbtSize = brcmnand_get_bbt_size(mtd); + + bOffsetStart = 0; + bOffsetEnd = (loff_t)(this->mtdSize - bbtSize); // Skip BBT itself + + printk(KERN_INFO "----- Contents of BBT -----\n"); + for (bOffset = bOffsetStart; bOffset < bOffsetEnd; bOffset = bOffset + mtd->erasesize) { + res = this->isbad_bbt(mtd, bOffset, 1); + if (res) { + printk(KERN_INFO "Bad block at %0llx\n", bOffset); + } + } + printk(KERN_INFO "----- END Contents of BBT -----\n"); + return 0; +} + +#if 1 +// Remove this block in production builds. + +/* + * Process brcmnand= kernel command arg, BEFORE building/reading BBT table. + * Currently, the only accepted command is CLEARBBT, which in itself is a dangerous activity. + * The user is assumed to know what he is doing. + */ +static void brcmnand_preprocessKernelArg(struct mtd_info *mtd) +{ + struct brcmnand_chip *this = mtd->priv; + + int ret, needBBT; + uint64_t bOffset, bOffsetStart = 0, bOffsetEnd = 0; + int bbtSize = brcmnand_get_bbt_size(mtd); + + //int page; + + + PRINTK("%s: gClearBBT=%d, size=%016llx, erasesize=%08x\n", __FUNCTION__, gClearBBT, device_size(mtd), mtd->erasesize); + + + switch (gClearBBT) { + + case NANDCMD_CLEARBBT: // Force rescan of BBT (DANGEROUS, may lose Mfg's BIs). + + bOffsetStart = this->mtdSize - bbtSize; + bOffsetEnd = this->mtdSize - mtd->erasesize; + printk("%s: gClearBBT=clearbbt, start=%0llx, end=%0llx\n", __FUNCTION__, + bOffsetStart, bOffsetEnd); + break; + + case NANDCMD_SHOWBBT: + return; + case NANDCMD_RESCAN: + return; + case NANDCMD_ERASEALL: + return; + case NANDCMD_ERASE: + return; + + default: + BUG_ON("Invalid brcmnand flag"); + break; + } // switch + + printk("Erasing flash from %016llx to %016llx\n", bOffsetStart, bOffsetEnd); + + /* + * Clear ECC registers + */ + this->ctrl_write(BCHP_NAND_ECC_CORR_ADDR, 0); + this->ctrl_write(BCHP_NAND_ECC_UNC_ADDR, 0); + + + for (bOffset = bOffsetStart; bOffset <= bOffsetEnd; bOffset += mtd->erasesize) { + //unsigned long pAddr = this->pbase + bOffset; + //int i; + //int skipBadBlock = 0; + + /* + * Skip erasing bad blocks. If you accidentally/intentionally mark a block as bad, + * and want to clear it, use BBS to clear it + * The exception are the blocks in the BBT area, which are reserved + * Here during pre-processing, there is no BBT, so we cannot assume its existence. + */ + + PRINTK("brcmnand flag=%d: Erasing block at %0llx\n", + gClearBBT, bOffset); + this->ctrl_writeAddr(this, bOffset, 0); + + this->ctrl_write(BCHP_NAND_CMD_START, OP_BLOCK_ERASE); + // Wait until flash is ready + ret = this->write_is_complete(mtd, &needBBT); + if (needBBT) { + printk(KERN_WARNING "%s: Erase failure, marking bad block @%016llx\n", __FUNCTION__, bOffset); + ret = this->block_markbad(mtd, bOffset); + } + } + + return; + +} + + +#else +#define brcmnand_preprocessKernelArg(mtd) + +#endif + +/* + * Process brcmnand= kernel command arg, AFTER building/reading BBT table + */ +static void brcmnand_postprocessKernelArg(struct mtd_info *mtd) +{ + struct brcmnand_chip *this = mtd->priv; + + int ret = 0, needBBT; + //uint64_t bOffset, bOffsetStart=0, bOffsetEnd=0; + uint64_t bOffset, bOffsetStart = 0, bOffsetEnd = 0; + int bbtSize = brcmnand_get_bbt_size(mtd); + + PRINTK("%s: gClearBBT=%d, size=%016llx, erasesize=%08x\n", + __FUNCTION__, gClearBBT, device_size(mtd), mtd->erasesize); + + switch (gClearBBT) { + case NANDCMD_SHOWBBT: + brcmnand_displayBBT(mtd); + return; + + case NANDCMD_CLEARBBT: // already done during pre-processing + brcmnand_displayBBT(mtd); + return; + + case NANDCMD_RESCAN: + printk("rescanning .... \n"); + /* FALLTHROUGH */ + case NANDCMD_ERASEALL: + /* FALLTHROUGH */ + case NANDCMD_ERASE: + // Force erase of entire flash (except BBT), and rescan of BBT: + bOffsetStart = 0LL; + bOffsetEnd = this->mtdSize - bbtSize; // BBT partition is 1MB +//printk("%s: gClearBBT=erase|eraseall, start=%08x, end=%08x\n", __FUNCTION__, bOffsetStart, bOffsetEnd); + break; + + default: + BUG_ON("Invalid clear brcmnand flag"); + break; + } // switch + + // printk("Erasing flash from %08x to %08x\n", bOffsetStart, bOffsetEnd); + + for (bOffset = bOffsetStart; bOffset < bOffsetEnd; + bOffset = bOffset + mtd->erasesize) { +#if CONFIG_MTD_BRCMNAND_VERSION < CONFIG_MTD_BRCMNAND_VERS_1_0 + unsigned long pAddr = this->pbase + bOffset; +#else + uint64_t pAddr = bOffset + this->pbase; +#endif + + int i; + int isBadBlock = 0; + int inCFE = 0; + int cs = this->ctrl->CS[this->csi]; + + if (0 == cs) { + if (this->xor_disable) { + inCFE = pAddr < 0x00300000; + }else { // XOR enabled + inCFE = (pAddr >= 0x1fc00000 && pAddr < 0x1ff00000); + } + } + + /* Skip reserved area, 7MB starting at 0x1f80_0000. + * Reserved area is owned by the bootloader. Linux owns the rootfs and the BBT area + */ + + if (gClearBBT == NANDCMD_ERASE && inCFE) + continue; + + /* Already included in BBT? */ + if (mtd_block_isbad(mtd, bOffset)) { + isBadBlock = 1; + continue; + } + /* + * Finding bad blocks besides the ones already in the BBT. + * If you accidentally/intentionally mark a block as bad, + * and want to clear it, use BBS to clear it, as Linux does not offer a way to do it. + * The exception are the blocks in the BBT area, which are reserved + */ + else { + unsigned char oobbuf[NAND_MAX_OOBSIZE]; + //int autoplace = 0; + //int raw = 1; + //struct nand_oobinfo oobsel; + int numpages; + /* THT: This __can__ be a 36bit integer (NAND controller address space is 48bit wide, minus + * page size of 2*12, therefore 36bit max + */ + uint64_t blockPage = bOffset >> this->page_shift; + int dir; + uint64_t page; + + /* How many pages should we scan */ + if (this->badblock_pattern->options & NAND_BBT_SCAN2NDPAGE) { + if (this->options & NAND_SCAN_BI_3RD_PAGE) { + numpages = 3; + }else { + numpages = 2; + } + } else { + numpages = 1; + } + + if (!NAND_IS_MLC(this)) { // SLC: First and 2nd page + dir = 1; + page = blockPage; // first page of block + }else { // MLC: Read last page + int pagesPerBlock = mtd->erasesize / mtd->writesize; + + dir = -1; + page = blockPage + pagesPerBlock - 1; // last page of block + } + + for (i = 0; i < numpages; i++, page += i * dir) { + int res; + struct mtd_oob_ops ops; + uint64_t offs = page << this->page_shift; + + + ops.len = mtd->oobsize; + ops.ooblen = mtd->oobsize; + ops.oobbuf = oobbuf; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.mode = MTD_OPS_PLACE_OOB; + + + res = mtd_read_oob(mtd, offs, &ops); + if (gdebug && res != 0) printk("########## %s: read_oob returns %d\n", __FUNCTION__, res); + + + if (res == -EBADMSG || res == -EIO || res == -ETIMEDOUT) { // Uncorrectable errors + uint32_t acc0; + + // Disable ECC + acc0 = brcmnand_disable_read_ecc(this->ctrl->CS[this->csi]); + + // Re-read the OOB + res = mtd_read_oob(mtd, offs, &ops); + + // Enable ECC back + brcmnand_restore_ecc(this->ctrl->CS[this->csi], acc0); + // res should be zero here + } + + if (!res) { + if (check_short_pattern(oobbuf, this->badblock_pattern)) { + isBadBlock = 1; + + if (NANDCMD_RESCAN == gClearBBT) + printk(KERN_INFO "Found bad block at offset %0llx\n", offs); + PRINTK("Found bad block at offset %0llx\n", offs); + + break; + } + }else { // We don't want to mark a block as bad otherwise + printk(KERN_DEBUG "brcmnand_read_pageoob returns %d for page %0llx\n", + res, page); + } + } + + } + + switch (gClearBBT) { + case NANDCMD_ERASE: + /* FALLTHROUGH */ +//gdebug=4; + case NANDCMD_ERASEALL: + if (isBadBlock) { + printk(KERN_INFO "Skipping Bad Block at %0llx\n", bOffset); + continue; + } + + //printk("brcmnand flag=%d: Erasing block at %08x\n", gClearBBT, bOffset); + this->ctrl_writeAddr(this, bOffset, 0); + + this->ctrl_write(BCHP_NAND_CMD_START, OP_BLOCK_ERASE); + // Wait until flash is ready + ret = this->write_is_complete(mtd, &needBBT); + if (needBBT) { + printk(KERN_INFO "%s: Marking bad block @%0llx\n", __FUNCTION__, bOffset); + ret = this->block_markbad(mtd, bOffset); + } + break; + + case NANDCMD_RESCAN: + if (isBadBlock) { + printk(KERN_INFO "%s: Marking bad block @%0llx\n", __FUNCTION__, bOffset); + ret = this->block_markbad(mtd, bOffset); + } + break; + + default: + printk(KERN_INFO "Invalid brcmnand argument in %s\n", __FUNCTION__); + BUG(); + } + } + brcmnand_displayBBT(mtd); + return; +} + +/** + * brcmnand_isbad_bbt - [NAND Interface] Check if a block is bad + * @mtd: MTD device structure + * @offs: offset in the device + * @allowbbt: allow access to bad block table region + * + * Each byte in the BBT contains 4 entries, 2 bits each per block. + * So the entry for the block b is: + * bbt[b >> 2] & (0x3 << ((b & 0x3) << 1))) + * + */ +static int brcmnand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) +{ + struct brcmnand_chip *this = mtd->priv; + uint32_t block; // Used as an index, so 32bit. + uint8_t res; + +//printk( "--> %s: bbt info for offs 0x%08x: \n", __FUNCTION__, __ll_low(offs)); + /* + * THT 03/20/07: + * Get block number * 2. It's more convenient to do it in the following way + * but is actually the same thing as in the comment above + */ + block = (uint32_t)(offs >> (this->bbt_erase_shift - 1)); + res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03; + + DEBUG(MTD_DEBUG_LEVEL3, "brcmnand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", + (unsigned int)offs, block >> 1, res); + +//if (res) PRINTK("%s: res=%x, allowbbt=%d at block %08x\n", __FUNCTION__, res, allowbbt, (unsigned int) offs); + + switch ((int)res) { + case 0x00: // Good block +//printk("<-- %s\n", __FUNCTION__); + return 0; + case 0x01: // Marked bad due to wear + return 1; + case 0x02: // Reserved blocks + return allowbbt ? 0 : 1; + case 0x03: + return 1; // Factory marked bad + } + return 1; +} + +/** + * brcmnand_isbad_raw - [NAND Interface] Check if a block is bad in the absence of BBT + * @mtd: MTD device structure + * @offs: offset in the device + * + * Each byte in the BBT contains 4 entries, 2 bits each per block. + * So the entry for the block b is: + * bbt[b >> 2] & (0x3 << ((b & 0x3) << 1))) + * + */ +int brcmnand_isbad_raw(struct mtd_info *mtd, loff_t offs) +{ + struct brcmnand_chip *this = mtd->priv; + //uint32_t block; // Used as an index, so 32bit. + uint8_t isBadBlock = 0; + int i; + + unsigned char oobbuf[NAND_MAX_OOBSIZE]; + int numpages; + /* THT: This __can__ be a 36bit integer (NAND controller address space is 48bit wide, minus + * page size of 2*12, therefore 36bit max + */ + uint64_t blockPage = offs >> this->page_shift; + int dir; + uint64_t page; + + printk("-->%s(offs=%llx\n", __FUNCTION__, offs); + + /* How many pages should we scan */ + if (this->badblock_pattern->options & NAND_BBT_SCAN2NDPAGE) { + if (this->options & NAND_SCAN_BI_3RD_PAGE) { + numpages = 3; + }else { + numpages = 2; + } + } else { + numpages = 1; + } + + PRINTK("%s: 20\n", __FUNCTION__); + + if (!NAND_IS_MLC(this)) { // SLC: First and 2nd page + dir = 1; + page = blockPage; // first page of block + }else { // MLC: Read last page + int pagesPerBlock = mtd->erasesize / mtd->writesize; + + dir = -1; + page = blockPage + pagesPerBlock - 1; // last page of block + } + + PRINTK("%s: 30\n", __FUNCTION__); + + for (i = 0; i < numpages; i++, page += i * dir) { + int res; + //int retlen = 0; + + PRINTK("%s: 50 calling read_page_oob=%p, offset=%llx\n", __FUNCTION__, this->read_page_oob, + page << this->page_shift); + +//gdebug=4; + res = this->read_page_oob(mtd, oobbuf, page); +//gdebug = 0; + if (!res) { + if (check_short_pattern(oobbuf, this->badblock_pattern)) { + isBadBlock = 1; + break; + } + }else { + printk(KERN_DEBUG "brcmnand_read_pageoob returns %d for page %0llx\n", + res, page); + } + } + + return isBadBlock; +} + +static struct nand_bbt_descr* +brcmnand_bbt_desc_init(struct nand_bbt_descr* orig) +{ + struct nand_bbt_descr* td; + + /* Potential memory leak here when used as a module, this is never freed */ + td = kmalloc(sizeof(struct nand_bbt_descr), GFP_KERNEL); + if (!td) { + printk(KERN_ERR "%s: Cannot allocate memory for BBT descriptor\n", __FUNCTION__); + return NULL; + } + *td = *orig; + return td; +} + + + +/** + * brcmnand_default_bbt - [NAND Interface] Select a default bad block table for the device + * @mtd: MTD device structure + * + * This function selects the default bad block table + * support for the device and calls the brcmnand_scan_bbt function + * + */ +int brcmnand_default_bbt(struct mtd_info *mtd) +{ + struct brcmnand_chip *this = mtd->priv; + int res; + + printk("-->%s\n", __FUNCTION__); + + /* Default for AG-AND. We must use a flash based + * bad block table as the devices have factory marked + * _good_ blocks. Erasing those blocks leads to loss + * of the good / bad information, so we _must_ store + * this information in a good / bad table during + * startup + */ +#if 0 + if (this->options & NAND_IS_AND) { + /* Use the default pattern descriptors */ + if (!this->bbt_td) { + this->bbt_td = brcmnand_bbt_desc_init(&bbt_main_descr); + this->bbt_md = brcmnand_bbt_desc_init(&bbt_mirror_descr); + } + this->options |= NAND_BBT_USE_FLASH; + return brcmnand_scan_bbt(mtd, &agand_flashbased); + } +#endif + + /* Is a flash based bad block table requested ? */ + if (this->options & NAND_BBT_USE_FLASH) { + if (this->ecclevel == BRCMNAND_ECC_HAMMING) { + /* Use the default pattern descriptors */ + if (!this->bbt_td) { + this->bbt_td = brcmnand_bbt_desc_init(&bbt_main_descr); + this->bbt_md = brcmnand_bbt_desc_init(&bbt_mirror_descr); + } + if (!this->badblock_pattern) { + this->badblock_pattern = (mtd->writesize > 512) ? &largepage_flashbased : &smallpage_flashbased; + } + printk("%s: bbt_td = bbt_main_descr\n", __FUNCTION__); + } +#if 1 +/* Nowadays, both SLC and MLC can have 4KB page, and more than 16 OOB size */ + else if (NAND_IS_MLC(this)) { // MLC + if (this->ecclevel == BRCMNAND_ECC_BCH_8 && this->eccOobSize == 16) { + /* Use the default pattern descriptors */ + if (!this->bbt_td) { + this->bbt_td = brcmnand_bbt_desc_init(&bbt_bch8_16_main_descr); + this->bbt_md = brcmnand_bbt_desc_init(&bbt_bch8_16_mirror_descr); + } + if (!this->badblock_pattern) { + // 2K & 4K MLC NAND use same pattern + this->badblock_pattern = &bch4_flashbased; + } + printk("%s: bbt_td = bbt_bch8_16_main_descr\n", __FUNCTION__); + }else { + /* Use the default pattern descriptors */ + if (!this->bbt_td) { + this->bbt_td = brcmnand_bbt_desc_init(&bbt_bch4_main_descr); + this->bbt_md = brcmnand_bbt_desc_init(&bbt_bch4_mirror_descr); + } + if (!this->badblock_pattern) { + // 2K & 4K MLC NAND use same pattern + this->badblock_pattern = &bch4_flashbased; + } + } + printk("%s: bbt_td = bbt_bch4_main_descr\n", __FUNCTION__); + } +#endif + else { /* SLC flashes using BCH-4 or higher ECC */ + /* Small & Large SLC NAND use the same template */ + if (this->ecclevel == BRCMNAND_ECC_BCH_4) { + if (!this->bbt_td) { + this->bbt_td = brcmnand_bbt_desc_init(&bbt_slc_bch4_main_descr); + this->bbt_md = brcmnand_bbt_desc_init(&bbt_slc_bch4_mirror_descr); + } + if (!this->badblock_pattern) { + this->badblock_pattern = (mtd->writesize > 512) ? &bch4_flashbased : &smallpage_flashbased; + } + printk("%s: bbt_td = bbt_slc_bch4_main_descr\n", __FUNCTION__); + } + /* Special case for BCH-8 with only 16B OOB */ + else if (this->ecclevel == BRCMNAND_ECC_BCH_8 && this->eccOobSize == 16) { + if (!this->bbt_td) { + this->bbt_td = brcmnand_bbt_desc_init(&bbt_bch8_16_main_descr); + this->bbt_md = brcmnand_bbt_desc_init(&bbt_bch8_16_mirror_descr); + } + if (!this->badblock_pattern) { + // 2K & 4K MLC NAND use same pattern + this->badblock_pattern = &bch4_flashbased; + } + printk("%s: bbt_td = bbt_bch8_16_main_descr\n", __FUNCTION__); + }else if (this->ecclevel >= BRCMNAND_ECC_BCH_8 && this->ecclevel < BRCMNAND_ECC_HAMMING + && this->eccOobSize > 16) { + /* Use the default pattern descriptors */ + if (!this->bbt_td) { + this->bbt_td = brcmnand_bbt_desc_init(&bbt_slc_bch4_main_descr); + this->bbt_md = brcmnand_bbt_desc_init(&bbt_slc_bch4_mirror_descr); + } + if (!this->badblock_pattern) { + this->badblock_pattern = &bch4_flashbased; + } + printk("%s: bbt_td = bbt_slc_bch4_main_descr\n", __FUNCTION__); + } + /* TBD: Use Internal ECC */ + else { + printk(KERN_ERR "***** %s: Unsupported ECC level %d\n", + __FUNCTION__, this->ecclevel); + BUG(); + } + } + } else { + /* MLC memory based not supported */ + this->bbt_td = NULL; + this->bbt_md = NULL; + if (!this->badblock_pattern) { + this->badblock_pattern = (mtd->writesize > 512) ? + &largepage_memorybased : &smallpage_memorybased; + } + } + + + /* + * BBT partition occupies 1 MB at the end of the useable flash, so adjust maxblocks accordingly. + * Only applies to flash with 512MB or less, since we don't have the extra reserved space at the + * end of the flash (1FF0_0000 - 1FFF_FFFF). + */ + if (device_size(mtd) <= ( 512ULL << 20)) { + this->bbt_td->maxblocks = this->bbt_md->maxblocks = (1 << 20) / this->blockSize; + + } + /* + * THT: 8/18/08: For MLC flashes with block size of 512KB, allocate 8 blocks or 4MB, + * (this is possible because this region is outside of the CFE allocated space of 1MB at 1FF0_0000). + */ + else if (this->blockSize == (512 * 1024)) { + this->bbt_td->maxblocks = this->bbt_md->maxblocks = + max(this->bbt_td->maxblocks, (int)((4 << 20) / this->blockSize)); + } + /* Reserve at least 8 blocks */ + else if (this->blockSize >= (1 << 20)) { + this->bbt_td->maxblocks = this->bbt_md->maxblocks = + max(this->bbt_td->maxblocks, 8); + } + + this->bbtSize = this->bbt_td->maxblocks * this->blockSize; + PRINTK("%s: gClearBBT = %d\n", __FUNCTION__, gClearBBT); + if (gClearBBT) { + (void)brcmnand_preprocessKernelArg(mtd); + } + + res = brcmnand_scan_bbt(mtd, this->badblock_pattern); + + /* + * Now that we have scanned the BBT, allow BBT-lookup + */ + this->isbad_bbt = brcmnand_isbad_bbt; + + if (gClearBBT) { + (void)brcmnand_postprocessKernelArg(mtd); + } + + return res; +} +EXPORT_SYMBOL(brcmnand_default_bbt); + +#endif //defined(CONFIG_BCM_KF_MTD_BCMNAND) diff -urN linux-4.1.52-orig/drivers/mtd/brcmnand/brcmnand_cet.c linux-4.1.52/drivers/mtd/brcmnand/brcmnand_cet.c --- linux-4.1.52-orig/drivers/mtd/brcmnand/brcmnand_cet.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/mtd/brcmnand/brcmnand_cet.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,1119 @@ +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +/* + <:copyright-BRCM:2012:DUAL/GPL:standard + + Copyright (c) 2012 Broadcom + All Rights Reserved + + Unless you and Broadcom execute a separate written software license + agreement governing use of this software, this software is licensed + to you under the terms of the GNU General Public License version 2 + (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, + with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + + Not withstanding the above, under no circumstances may you combine + this software in any way with any other Broadcom software provided + under a license other than the GPL, without Broadcom's express prior + written consent. + + :> + + File: brcmnand_cet.c + + Broadcom NAND Correctable Error Table Support + --------------------------------------------- + In case of a single bit correctable error, the block in which correctable error + occured is refreshed (i.e., read->erase->write the entire block). Following a + refresh a success value is returned by brcmnand_read() i.e., the error is + hidden from the file system. The Correctable Error Table (CET) keeps a history + (bit-vector) of per page correctable errors. If a correctable error happens + on the same page twice, an error is returned to the file system. + + The CET starts from the opposite end of BBT with 1-bit per page. The CET is + initialized to all 1's. On the first correctable error the bit corresponding + to a page is reset. On an erase, all the bits of the corresponding block are + set. The CET can span across multiple blocks therefore a signature 'CET#' + where # is the block number is kept in the OOB area of the first page of a + CET block. Also, the total correctable error count is kept in the second + page OOB of the first CET block. + + There is an in-memory correctable error table during runtime which is flushed + to the flash every 10 mins (CET_SYNC_FREQ). + + Description: + when who what + ----- --- ---- + 080519 sidc initial code + 080910 sidc MLC support + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "brcmnand_priv.h" + +#ifdef CONFIG_MTD_BRCMNAND_CORRECTABLE_ERR_HANDLING + +#define PRINTK(...) + +#define BBT_SLC_PARTITION (1 << 20) +#define BBT_MAX_BLKS_SLC 4 +#define CET_START_BLK_SLC(x, y) (uint32_t)(((x) >> ((y)->bbt_erase_shift)) - (BBT_SLC_PARTITION / (y)->blockSize)) + +#define BBT_MLC_PARTITION (4 << 20) +#define BBT_MAX_BLKS_MLC(x) (BBT_MLC_PARTITION >> ((x)->bbt_erase_shift)) +#define CET_START_BLK_MLC(x, y, z) (uint32_t)(((x) >> ((y)->bbt_erase_shift)) - ((z) / (y)->blockSize)) + +#define CET_GOOD_BLK 0x00 +#define CET_BAD_WEAR 0x01 +#define CET_BBT_USE 0x02 +#define CET_BAD_FACTORY 0x03 + +#define CET_SYNC_FREQ (10 * 60 * HZ) + + +static char cet_pattern[] = { 'C', 'E', 'T', 0 }; +static struct brcmnand_cet_descr cet_descr = { + .offs = 9, + .len = 4, + .pattern = cet_pattern +}; + +/* + * This also applies to Large Page SLC flashes with BCH-4 ECC. + * We don't support BCH-4 on Small Page SLCs because there are not + * enough free bytes for the OOB, but we don't enforce it, + * in order to allow page aggregation like in YAFFS2 on small page SLCs. + */ +static struct brcmnand_cet_descr cet_descr_mlc = { + .offs = 1, + .len = 4, + .pattern = cet_pattern +}; +static void sync_cet(struct work_struct *work); +static int search_cet_blks(struct mtd_info *, struct brcmnand_cet_descr *, char); +extern char gClearCET; + +/* + * Private: Read OOB area in RAW mode + */ +static inline int brcmnand_cet_read_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs) +{ + struct mtd_oob_ops ops; + + ops.mode = MTD_OPS_RAW; + ops.len = mtd->oobsize; + ops.ooblen = mtd->oobsize; + ops.datbuf = NULL; + ops.oobbuf = buf; + ops.ooboffs = 0; + + return mtd_read_oob(mtd, offs, &ops); +} + +/* + * Private: Write to the OOB area only + */ +static inline int brcmnand_cet_write_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs) +{ +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + struct mtd_oob_ops ops; + + ops.mode = MTD_OPS_PLACE_OOB; + ops.len = mtd->writesize; + ops.ooblen = mtd->oobsize; + ops.datbuf = NULL; + ops.oobbuf = buf; + ops.ooboffs = 0; + + return mtd_write_oob(mtd, offs, &ops); +#else + struct mtd_oob_ops ops; + uint8_t databuf[mtd->writesize]; + + memset(databuf, 0xff, mtd->writesize); + ops.mode = MTD_OPS_PLACE_OOB; + ops.len = mtd->writesize; + ops.ooblen = mtd->oobsize; + ops.datbuf = databuf; + ops.oobbuf = buf; + ops.ooboffs = 0; + + return mtd_write_oob(mtd, offs, &ops); +#endif +} + +/* + * Private: write one page of data and OOB to flash + */ +static int brcmnand_cet_write(struct mtd_info *mtd, loff_t offs, size_t len, + uint8_t *buf, uint8_t *oob) +{ + struct mtd_oob_ops ops; + int ret; + + ops.mode = MTD_OPS_PLACE_OOB; + ops.ooboffs = 0; + ops.ooblen = mtd->oobsize; + ops.datbuf = buf; + ops.oobbuf = oob; + ops.len = len; + ret = mtd_write_oob(mtd, offs, &ops); + + return ret; +} + +/* + * bitcount - MIT Hackmem count implementation which is O(1) + * http://infolab.stanford.edu/~manku/bitcount/bitcount.html + * Counts the number of 1s in a given unsigned int n + */ +static inline int bitcount(uint32_t n) +{ + uint32_t tmp; + + tmp = n - ((n >> 1) & 033333333333) + - ((n >> 2) & 011111111111); + return ((tmp + (tmp >> 3)) & 030707070707) % 63; +} + +/* + * Private debug function: Print OOBs + */ +static void cet_printpg_oob(struct mtd_info *mtd, struct brcmnand_cet_descr *cet, int count) +{ + uint8_t oobbuf[mtd->oobsize]; + loff_t offs; + int i, gdebug = 0; + struct brcmnand_chip *this = (struct brcmnand_chip *)mtd->priv; + + offs = ((loff_t)cet->startblk) << this->bbt_erase_shift; + if (gdebug) { + printk(KERN_INFO "%s: %x\n", __FUNCTION__, (unsigned int)offs); + } + for (i = 0; i < count; i++) { + memset(oobbuf, 0, mtd->oobsize); + if (brcmnand_cet_read_oob(mtd, oobbuf, offs)) { + return; + } + print_oobbuf((const char*)oobbuf, mtd->oobsize); + offs = offs + cet->sign * this->pageSize; + } + return; +} + +/* + * Private debug function: Prints first OOB area of all blocks + */ +static void cet_printblk_oob(struct mtd_info *mtd, struct brcmnand_cet_descr *cet) +{ + uint8_t *oobbuf; + loff_t offs; + int i; + struct brcmnand_chip *this = (struct brcmnand_chip *)mtd->priv; + + if ((oobbuf = (uint8_t*)vmalloc(sizeof(uint8_t) * mtd->oobsize)) == NULL) { + printk(KERN_ERR "brcmnandCET: %s vmalloc failed\n", __FUNCTION__); + return; + } + for (i = 0; i < this->bbt_td->maxblocks; i++) { + memset(oobbuf, 0, mtd->oobsize); + offs = ((loff_t)cet->startblk + ((cet->sign) * i)) << this->bbt_erase_shift; + if (brcmnand_cet_read_oob(mtd, oobbuf, offs)) { + vfree(oobbuf); + return; + } + print_oobbuf((const char*)oobbuf, mtd->oobsize); + } + vfree(oobbuf); + return; +} + +/* + * Private debug function: erase all blocks belonging to CET + * Use for testing purposes only + */ +static void cet_eraseall(struct mtd_info *mtd, struct brcmnand_cet_descr *cet) +{ + int i, ret; + loff_t from; + struct erase_info einfo; + struct brcmnand_chip *this = (struct brcmnand_chip *)mtd->priv; + int gdebug = 0; + + for (i = 0; i < cet->numblks; i++) { + if (cet->memtbl[i].blk != -1) { + from = (uint64_t)cet->memtbl[i].blk << this->bbt_erase_shift; + if (unlikely(gdebug)) { + printk(KERN_INFO "DEBUG -> Erasing blk %x\n", cet->memtbl[i].blk); + } + memset(&einfo, 0, sizeof(einfo)); + einfo.mtd = mtd; + einfo.addr = from; + einfo.len = mtd->erasesize; + ret = this->erase_bbt(mtd, &einfo, 1, 1); + if (unlikely(ret < 0)) { + printk(KERN_ERR "brcmnandCET: %s Error erasing block %llx\n", __FUNCTION__, einfo.addr); + } + } + } + + return; +} + +/* + * Private: Check if a block is factory marked bad block + * Derived from brcmnand_isbad_bbt() + * Return values: + * 0x00 Good block + * 0x01 Marked bad due to wear + * 0x02 Reserved for BBT + * 0x03 Factory marked bad + */ +static inline int check_badblk(struct mtd_info *mtd, loff_t offs) +{ + struct brcmnand_chip *this = (struct brcmnand_chip *)mtd->priv; + uint32_t blk; + int res; + + blk = (uint32_t)(offs >> (this->bbt_erase_shift - 1)); + res = (this->bbt[blk >> 3] >> blk & 0x06) & 0x03; + + return res; +} + +/* + * Check for CET pattern in the OOB buffer + * return the blk number present in the CET + */ +static inline int found_cet_pattern(struct brcmnand_chip *this, uint8_t *buf) +{ + struct brcmnand_cet_descr *cet = this->cet; + int i; + + for (i = 0; i < cet->len - 1; i++) { + if (buf[cet->offs + i] != cet_pattern[i]) { + return -1; + } + } + return (int)buf[cet->offs + cet->len - 1]; +} + +/* + * Check for BBT/Mirror BBT pattern + * Similar to the implementation in brcmnand_bbt.c + */ +static inline int found_bbt_pattern(uint8_t *buf, struct nand_bbt_descr *bd) +{ + int i; + + for (i = 0; i < bd->len; i++) { + if (buf[bd->offs + i] != bd->pattern[i]) { + return 0; + } + } + return 1; +} + +/* + * Check OOB area to test if the block is erased + */ +static inline int cet_iserased(struct mtd_info *mtd, uint8_t *oobbuf) +{ + struct brcmnand_chip *this = (struct brcmnand_chip *)mtd->priv; + struct nand_ecclayout *oobinfo = this->ecclayout; + int i; + + for (i = 0; i < oobinfo->eccbytes; i++) { + if (oobbuf[oobinfo->eccpos[i]] != 0xff) { + return 0; + } + } + return 1; +} + +/* + * Process kernel command line showcet + * If the CET is loaded, display which blocks of flash the CET is in + */ +static inline void cmdline_showcet(struct mtd_info *mtd, struct brcmnand_cet_descr *cet) +{ + int i; + loff_t offs; + uint8_t oobbuf[mtd->oobsize]; + struct brcmnand_chip *this = (struct brcmnand_chip *)mtd->priv; + + if (cet->flags == BRCMNAND_CET_DISABLED) { + printk(KERN_INFO "brcmnandCET: Disabled\n"); + return; + } + printk(KERN_INFO "brcmnandCET: Correctable error count is 0x%x\n", cet->cerr_count); + if (cet->flags == BRCMNAND_CET_LAZY) { + printk(KERN_INFO "brcmnandCET: Deferred until next correctable error\n"); + return; + } + printk(KERN_INFO "brcmnandCET: Displaying first OOB area of all CET blocks ...\n"); + for (i = 0; i < cet->numblks; i++) { + if (cet->memtbl[i].blk == -1) + continue; + offs = ((loff_t)cet->memtbl[i].blk) << this->bbt_erase_shift; + printk(KERN_INFO "brcmnandCET: Block[%d] @ %x\n", i, (unsigned int)offs); + if (brcmnand_cet_read_oob(mtd, oobbuf, offs)) { + return; + } + print_oobbuf((const char*)oobbuf, mtd->oobsize); + } + return; +} + +/* + * Reset CET to all 0xffs + */ +static inline int cmdline_resetcet(struct mtd_info *mtd, struct brcmnand_cet_descr *cet) +{ + int i; + + cet_eraseall(mtd, cet); + for (i = 0; i < cet->numblks; i++) { + cet->memtbl[i].isdirty = 0; + cet->memtbl[i].blk = -1; + cet->memtbl[i].bitvec = NULL; + } + printk(KERN_INFO "brcmnandCET: Recreating ... \n"); + + return search_cet_blks(mtd, cet, 0); +} + +/* + * Create a CET pattern in the OOB area. + */ +static int create_cet_blks(struct mtd_info *mtd, struct brcmnand_cet_descr *cet) +{ + int i, j, ret, gdebug = 0; + loff_t from; + struct nand_bbt_descr *td, *md; + struct erase_info einfo; + struct brcmnand_chip *this = (struct brcmnand_chip *)mtd->priv; + uint8_t oobbuf[mtd->oobsize]; + char *oobptr, count = 0; + + td = this->bbt_td; + md = this->bbt_md; + if (unlikely(gdebug)) { + printk(KERN_INFO "brcmnandCET: Inside %s\n", __FUNCTION__); + } + for (i = 0; i < td->maxblocks; i++) { + from = ((loff_t)cet->startblk + i * cet->sign) << this->bbt_erase_shift; + /* Skip if bad block */ + ret = check_badblk(mtd, from); + if (ret == CET_BAD_FACTORY || ret == CET_BAD_WEAR) { + continue; + } + memset(oobbuf, 0, mtd->oobsize); + if (brcmnand_cet_read_oob(mtd, oobbuf, from)) { + printk(KERN_INFO "brcmnandCET: %s %d Error reading OOB\n", __FUNCTION__, __LINE__); + return -1; + } + /* If BBT/MBT block found we have no space left */ + if (found_bbt_pattern(oobbuf, td) || found_bbt_pattern(oobbuf, md)) { + printk(KERN_INFO "brcmnandCET: %s blk %x is BBT\n", __FUNCTION__, cet->startblk + i * cet->sign); + return -1; + } + //if (!cet_iserased(mtd, oobbuf)) { + if (unlikely(gdebug)) { + printk(KERN_INFO "brcmnandCET: block %x is erased\n", cet->startblk + i * cet->sign); + } + /* Erase */ + memset(&einfo, 0, sizeof(einfo)); + einfo.mtd = mtd; + einfo.addr = from; + einfo.len = mtd->erasesize; + ret = this->erase_bbt(mtd, &einfo, 1, 1); + if (unlikely(ret < 0)) { + printk(KERN_ERR "brcmnandCET: %s Error erasing block %x\n", __FUNCTION__, cet->startblk + i * cet->sign); + return -1; + } + //} + /* Write 'CET#' pattern to the OOB area */ + memset(oobbuf, 0xff, mtd->oobsize); + if (unlikely(gdebug)) { + printk(KERN_INFO "brcmnandCET: writing CET %d to OOB area\n", (int)count); + } + oobptr = (char*)oobbuf; + for (j = 0; j < cet->len - 1; j++) { + oobptr[cet->offs + j] = cet->pattern[j]; + } + oobptr[cet->offs + j] = count; + if (brcmnand_cet_write_oob(mtd, oobbuf, from)) { + printk(KERN_ERR "brcmnandCET: %s Error writing to OOB# %x\n", __FUNCTION__, (unsigned int)from); + return -1; + } + /* If this is the first CET block, init the correctable erase count to 0 */ + if (count == 0) { + memset(oobbuf, 0xff, mtd->oobsize); + oobptr = (char*)oobbuf; + *((uint32_t*)(oobptr + cet->offs)) = 0x00000000; + from += this->pageSize; + if (unlikely(gdebug)) { + printk(KERN_INFO "DEBUG -> 0: from = %x\n", (unsigned int)from); + printk(KERN_INFO "brcmnandCET: Writing cer_count to page %x\n", (unsigned int)from); + } + if (brcmnand_cet_write_oob(mtd, oobbuf, from)) { + printk(KERN_INFO "brcmnandCET: %s Error writing to OOB# %x\n", __FUNCTION__, (unsigned int)from); + return -1; + } + } + count++; + if (((int)count) == cet->numblks) { + return 0; + } + } + return -1; +} + +/* + * Search for CET blocks + * force => 1 Force creation of tables, do not defer for later + */ +static int search_cet_blks(struct mtd_info *mtd, struct brcmnand_cet_descr *cet, char force) +{ + int i, count = 0, ret; + loff_t from; + struct nand_bbt_descr *td, *md; + uint8_t oobbuf[mtd->oobsize]; + struct brcmnand_chip *this = (struct brcmnand_chip *)mtd->priv; + int gdebug = 0; + + td = this->bbt_td; + md = this->bbt_md; + if (unlikely(gdebug)) { + printk(KERN_INFO "DEBUG -> Inside search_cet_blks\n"); + } + for (i = 0; i < td->maxblocks; i++) { + from = ((loff_t)cet->startblk + i * cet->sign) << this->bbt_erase_shift; + /* Skip if bad block */ + ret = check_badblk(mtd, from); + if (ret == CET_BAD_FACTORY || ret == CET_BAD_WEAR) { + continue; + } + /* Read the OOB area of the first page of the block */ + memset(oobbuf, 0, mtd->oobsize); + if (brcmnand_cet_read_oob(mtd, oobbuf, from)) { + printk(KERN_INFO "brcmnandCET: %s %d Error reading OOB\n", __FUNCTION__, __LINE__); + cet->flags = BRCMNAND_CET_DISABLED; + return -1; + } + if (unlikely(gdebug)) { + print_oobbuf(oobbuf, mtd->oobsize); + } + /* Return -1 if BBT/MBT block => no space left for CET */ + if (found_bbt_pattern(oobbuf, td) || found_bbt_pattern(oobbuf, md)) { + printk(KERN_INFO "brcmnandCET: %s blk %x is BBT\n", __FUNCTION__, cet->startblk + i * cet->sign); + cet->flags = BRCMNAND_CET_DISABLED; + return -1; + } + /* Check for CET pattern */ + ret = found_cet_pattern(this, oobbuf); + if (unlikely(gdebug)) { + print_oobbuf((const char*)oobbuf, mtd->oobsize); + } + if (ret < 0 || ret >= cet->numblks) { + /* No CET pattern found due to + 1. first time being booted => normal so create + 2. Did not find CET pattern when we're supposed to + error => recreate, in either case we call create_cet_blks(); + 3. Found an incorrect > cet->numblks count => error => recreate + */ + printk(KERN_INFO "brcmnandCET: Did not find CET, recreating\n"); + if (create_cet_blks(mtd, cet) < 0) { + cet->flags = BRCMNAND_CET_DISABLED; + return ret; + } + cet->flags = BRCMNAND_CET_LAZY; + return 0; + } + /* Found CET pattern */ + if (unlikely(gdebug)) { + printk(KERN_INFO "brcmnandCET: Found CET block#%d\n", count); + } + /* If this is the first block do some extra stuff ... */ + if (count == 0) { + /* The global cerr_count is in the 2nd page's OOB area */ + from += this->pageSize; + if (brcmnand_cet_read_oob(mtd, oobbuf, from)) { + printk(KERN_ERR "brcmnandCET: %s %d Error reading OOB\n", __FUNCTION__, __LINE__); + cet->flags = BRCMNAND_CET_DISABLED; + return -1; + } + cet->cerr_count = *((uint32_t*)(oobbuf + cet->offs)); + /* TODO - Fix this -> recreate */ + if (cet->cerr_count == 0xffffffff) { + /* Reset it to 0 */ + cet->cerr_count = 0; + cet->memtbl[0].isdirty = 1; + } + if (unlikely(gdebug)) { + printk(KERN_INFO "brcmnandCET: correctable error count = %x\n", cet->cerr_count); + } + /* If force then go thru all CET blks even if cerr_count is 0 */ + if (!force) { + if (cet->cerr_count == 0) { + cet->flags = BRCMNAND_CET_LAZY; + return 0; + } + } + } + cet->memtbl[ret].blk = cet->startblk + i * cet->sign; + count++; +#if 0 + printk(KERN_INFO "DEBUG -> count = %d, nblks = %d blk = %d\n", count, cet->numblks, cet->memtbl[ret].blk); +#endif + if (count == cet->numblks) { + cet->flags = BRCMNAND_CET_LOADED; + return 0; + } + } + /* This should never happen */ + cet->flags = BRCMNAND_CET_DISABLED; + return -1; +} + +/* + * flush pending in-memory CET data to the flash. Called as part of a + * callback function from workqueue that is invoked every SYNC_FREQ seconds + */ +static int flush_memcet(struct mtd_info *mtd) +{ + struct brcmnand_chip *this = (struct brcmnand_chip *)mtd->priv; + struct brcmnand_cet_descr *cet = this->cet; + struct erase_info einfo; + int i, j, k = 0, ret, pg_idx = 0, gdebug = 0; + uint8_t oobbuf[mtd->oobsize]; + loff_t from, to; + char *oobptr, count = 0; + + /* If chip is locked reset timer for a later time */ + if (spin_is_locked(&this->ctrl->chip_lock)) { + printk(KERN_INFO "brcmnandCET: flash locked reseting timer\n"); + return -1; + } + if (unlikely(gdebug)) { + printk(KERN_INFO "brcmnandCET: Inside %s\n", __FUNCTION__); + } + /* For each in-mem dirty block, sync with flash + sync => erase -> write */ + for (i = 0; i < cet->numblks; i++) { + if (cet->memtbl[i].isdirty && cet->memtbl[i].blk != -1) { + /* Erase */ + from = ((loff_t)cet->memtbl[i].blk) << this->bbt_erase_shift; + to = from; + memset(&einfo, 0, sizeof(einfo)); + einfo.mtd = mtd; + einfo.addr = from; + einfo.len = mtd->erasesize; + ret = this->erase_bbt(mtd, &einfo, 1, 1); + if (unlikely(ret < 0)) { + printk(KERN_ERR "brcmnandCET: %s Error erasing block %x\n", __FUNCTION__, cet->memtbl[i].blk); + return -1; + } + if (unlikely(gdebug)) { + printk(KERN_INFO "DEBUG -> brcmnandCET: After erasing ...\n"); + cet_printpg_oob(mtd, cet, 3); + } + pg_idx = 0; + /* Write pages i.e., flush */ + for (j = 0; j < mtd->erasesize / this->pageSize; j++) { + memset(oobbuf, 0xff, mtd->oobsize); + oobptr = (char*)oobbuf; + if (j == 0) { /* Write CET# */ + for (k = 0; k < cet->len - 1; k++) { + oobptr[cet->offs + k] = cet->pattern[k]; + } + oobptr[cet->offs + k] = count; + if (unlikely(gdebug)) { + print_oobbuf((const char*)oobbuf, mtd->oobsize); + } + } + if (j == 1 && count == 0) { /* Write cerr_count */ + *((uint32_t*)(oobptr + cet->offs)) = cet->cerr_count; + } + ret = brcmnand_cet_write(mtd, to, (size_t)this->pageSize, cet->memtbl[i].bitvec + pg_idx, oobbuf); + if (ret < 0) { + printk(KERN_ERR "brcmnandCET: %s Error writing to page %x\n", __FUNCTION__, (unsigned int)to); + return ret; + } + to += mtd->writesize; + pg_idx += mtd->writesize; + } + cet->memtbl[i].isdirty = 0; + if (unlikely(gdebug)) { + printk(KERN_INFO "brcmnandCET: flushing CET block %d\n", i); + } + } + count++; + } + + return 0; +} + +/* + * The callback function for kernel workq task + * Checks if there is any work to be done, if so calls flush_memcet + * Resets timer before returning in any case + */ +static void sync_cet(struct work_struct *work) +{ + int i; + struct delayed_work *d = container_of(work, struct delayed_work, work); + struct brcmnand_cet_descr *cet = container_of(d, struct brcmnand_cet_descr, cet_flush); + struct mtd_info *mtd = cet->mtd; + + /* Check if all blocks are clean */ + for (i = 0; i < cet->numblks; i++) { + if (cet->memtbl[i].isdirty) break; + } + /* Avoid function call cost if there are no dirty blocks */ + if (i != cet->numblks) + flush_memcet(mtd); + schedule_delayed_work(&cet->cet_flush, CET_SYNC_FREQ); + + return; +} + + +/* + * brcmnand_create_cet - Create a CET (Correctable Error Table) + * @param mtd MTD device structure + * + * Called during mtd init. Checks if a CET already exists or needs + * to be created. Initializes in-memory CET. + */ +int brcmnand_create_cet(struct mtd_info *mtd) +{ + struct brcmnand_chip *this = (struct brcmnand_chip *)mtd->priv; + struct brcmnand_cet_descr *cet; + int gdebug = 0, i, ret, rem; + uint64_t tmpdiv; + + if (unlikely(gdebug)) { + printk(KERN_INFO "brcmnandCET: Creating correctable error table ...\n"); + } + + if (NAND_IS_MLC(this) || /* MLC flashes */ + /* SLC w/ BCH-n; We don't check for pageSize, and let it be */ + (this->ecclevel >= BRCMNAND_ECC_BCH_1 && this->ecclevel <= BRCMNAND_ECC_BCH_12)) { + this->cet = cet = &cet_descr_mlc; + if (gdebug) printk("%s: CET = cet_desc_mlc\n", __FUNCTION__); + }else { + this->cet = cet = &cet_descr; + if (gdebug) printk("%s: CET = cet_descr\n", __FUNCTION__); + } + cet->flags = 0x00; + /* Check that BBT table and mirror exist */ + if (unlikely(!this->bbt_td && !this->bbt_md)) { + printk(KERN_INFO "brcmnandCET: BBT tables not found, disabling\n"); + cet->flags = BRCMNAND_CET_DISABLED; + return -1; + } + /* Per chip not supported. We do not use per chip BBT, but this + is just a safety net */ + if (unlikely(this->bbt_td->options & NAND_BBT_PERCHIP)) { + printk(KERN_INFO "brcmnandCET: per chip CET not supported, disabling\n"); + cet->flags = BRCMNAND_CET_DISABLED; + return -1; + } + /* Calculate max blocks based on 1-bit per page */ + tmpdiv = this->mtdSize; + do_div(tmpdiv, this->pageSize); + do_div(tmpdiv, (8 * this->blockSize)); + cet->numblks = (uint32_t)tmpdiv; + //cet->numblks = (this->mtdSize/this->pageSize)/(8*this->blockSize); + tmpdiv = this->mtdSize; + do_div(tmpdiv, this->pageSize); + do_div(tmpdiv, 8); + rem = do_div(tmpdiv, this->blockSize); + //if (((this->mtdSize/this->pageSize)/8)%this->blockSize) { + if (rem) { + cet->numblks++; + } + /* Allocate twice the size in case we have bad blocks */ + cet->maxblks = cet->numblks * 2; + /* Determine the direction of CET based on reverse direction of BBT */ + cet->sign = (this->bbt_td->options & NAND_BBT_LASTBLOCK) ? 1 : -1; + /* For flash size <= 512MB BBT and CET share the last 1MB + for flash size > 512MB CET is at the 512th MB of flash */ +#if 0 + if (NAND_IS_MLC(this)) { + } else { + if (this->mtdSize < (1 << 29)) { + if (cet->maxblks + BBT_MAX_BLKS > get_bbt_partition(this) / this->blockSize) { + printk(KERN_INFO "brcmnandCET: Not enough space to store CET, disabling CET\n"); + cet->flags = BRCMNAND_CET_DISABLED; + return -1; + } + if (cet->sign) { + cet->startblk = CET_START_BLK(this->mtdSize, this); + } else { + cet->startblk = (uint32_t)(this->mtdSize >> this->bbt_erase_shift) - 1; + } + + } else { + if (cet->maxblks > (get_bbt_partition(this)) / this->blockSize) { + printk(KERN_INFO "brcmnandCET: Not enough space to store CET, disabling CET\n"); + cet->flags = BRCMNAND_CET_DISABLED; + return -1; + } + cet->startblk = CET_START_BLK((1 << 29), this); + } + } +#endif + if (NAND_IS_MLC(this)) { + if (this->mtdSize < (1 << 29)) { + if (cet->maxblks + BBT_MAX_BLKS_MLC(this) > BBT_MLC_PARTITION / this->blockSize) { + printk(KERN_INFO "brcmnandCET: Not enough space to store CET, disabling CET\n"); + cet->flags = BRCMNAND_CET_DISABLED; + return -1; + } + /* Reverse direction of BBT */ + if (cet->sign) { + cet->startblk = CET_START_BLK_MLC(this->mtdSize, this, BBT_MLC_PARTITION); + } else { + cet->startblk = (uint32_t)(this->mtdSize >> this->bbt_erase_shift) - 1; + } + } else { + /* 512th MB used by CET */ + if (cet->maxblks > (1 << 29) / this->blockSize) { + printk(KERN_INFO "brcmnandCET: Not enough space to store CET, disabling CET\n"); + cet->flags = BRCMNAND_CET_DISABLED; + return -1; + } +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + cet->startblk = CET_START_BLK_SLC(this->mtdSize, this); +#else + cet->startblk = CET_START_BLK_MLC((1 << 29), this, (1 << 20)); +#endif + } + } else { + if (this->mtdSize < (1 << 29)) { + if (cet->maxblks + BBT_MAX_BLKS_SLC > BBT_SLC_PARTITION / this->blockSize) { + printk(KERN_INFO "brcmnandCET: Not enough space to store CET, disabling CET\n"); + cet->flags = BRCMNAND_CET_DISABLED; + return -1; + } + /* Reverse direction of BBT */ + if (cet->sign) { + cet->startblk = CET_START_BLK_SLC(this->mtdSize, this); + } else { + cet->startblk = (uint32_t)(this->mtdSize >> this->bbt_erase_shift) - 1; + } + } else { + /* 512th MB used by CET */ + if (cet->maxblks > BBT_SLC_PARTITION / this->blockSize) { + printk(KERN_INFO "brcmnandCET: Not enough space to store CET, disabling CET\n"); + cet->flags = BRCMNAND_CET_DISABLED; + return -1; + } +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + cet->startblk = CET_START_BLK_SLC(this->mtdSize, this); +#else + cet->startblk = CET_START_BLK_SLC((1 << 29), this); +#endif + } + } + if (gdebug) { + printk(KERN_INFO "brcmnandCET: start blk = %x, numblks = %x\n", cet->startblk, cet->numblks); + } + + /* Init memory based CET */ + cet->memtbl = (struct brcmnand_cet_memtable *)vmalloc(cet->numblks * sizeof(struct brcmnand_cet_memtable)); + if (cet->memtbl == NULL) { + printk(KERN_ERR "brcmnandCET: vmalloc failed %s\n", __FUNCTION__); + cet->flags = BRCMNAND_CET_DISABLED; + return -1; + } + for (i = 0; i < cet->numblks; i++) { + cet->memtbl[i].isdirty = 0; + cet->memtbl[i].blk = -1; + cet->memtbl[i].bitvec = NULL; + } + ret = search_cet_blks(mtd, cet, 0); + if (unlikely(gClearCET == 1)) { /* kernel cmdline showcet */ + cmdline_showcet(mtd, cet); + } + if (unlikely(gClearCET == 2)) { /* kernel cmdline resetcet */ + if (cmdline_resetcet(mtd, cet) < 0) { + cet->flags = BRCMNAND_CET_DISABLED; + return -1; + } + } + if (unlikely(gClearCET == 3)) { /* kernel cmdline disable */ + cet->flags = BRCMNAND_CET_DISABLED; + ret = -1; + } + //cet_printpg_oob(mtd, cet, 3); + switch (cet->flags) { + case BRCMNAND_CET_DISABLED: + printk(KERN_INFO "brcmnandCET: Status -> Disabled\n"); + break; + case BRCMNAND_CET_LAZY: + printk(KERN_INFO "brcmnandCET: Status -> Deferred\n"); + break; + case BRCMNAND_CET_LOADED: + printk(KERN_INFO "brcmnandCET: Status -> Loaded\n"); + break; + default: + printk(KERN_INFO "brcmnandCET: Status -> Fatal error CET disabled\n"); + cet->flags = BRCMNAND_CET_DISABLED; + break; + } + if (unlikely(gdebug)) { + cet_printpg_oob(mtd, cet, 3); + cet_printblk_oob(mtd, cet); + } + + INIT_DELAYED_WORK(&cet->cet_flush, sync_cet); + cet->mtd = mtd; + schedule_delayed_work(&cet->cet_flush, CET_SYNC_FREQ); + + return ret; +} + +/* + * brcmnand_cet_erasecallback: Called every time there is an erase due to + * userspace activity + * + * @param mtd MTD device structure + * @param addr Address of the block that was erased by fs/userspace + * + * Assumption: cet->flag != BRCMNAND_CET_DISABLED || BRCMNAND_CET_LAZY + * is checked by the caller + * flag == BRCMNAND_CET_DISABLED => CET not being used + * flag == BRCMNAND_CET_LAZY => correctable error count is 0 so need of callback + * + * TODO Optimize, add comments, check all return paths + */ +int brcmnand_cet_erasecallback(struct mtd_info *mtd, u_int32_t addr) +{ + struct brcmnand_chip *this = (struct brcmnand_chip *)mtd->priv; + struct brcmnand_cet_descr *cet = this->cet; + uint32_t page = 0; + int blkbegin, blk, i, ret, retlen, pg_idx = 0, numzeros = 0, byte, gdebug = 0; + uint32_t *ptr; + unsigned int pos; + loff_t origaddr = addr; + + /* Find out which entry in the memtbl does the addr map to */ + page = (uint32_t)(addr >> this->page_shift); + blk = page / (this->blockSize << 3); + if (unlikely(cet->memtbl[blk].blk == -1)) { + printk(KERN_INFO "brcmnandCET: %s invalid block# in CET\n", __FUNCTION__); + return -1; + } + blkbegin = cet->memtbl[blk].blk; + /* Start page of the block */ + addr = ((loff_t)blkbegin) << this->bbt_erase_shift; + if (cet->memtbl[blk].bitvec == NULL) { + if (gdebug) { + printk(KERN_INFO "DEBUG -> brcmnandCET: bitvec is null, reloading\n"); + } + /* using kmalloc as the erase typically are called from image update which has interrupt disabled. We can only + support 32bit addressing 4GB maximum size NAND. These NAND use 128K block and 2048 byte page. So it takes 4GB/2048/8 + = 256KB or 2 block. Our kernel should have enough kmalloc ATMOIC memory for 2x128KB allocations */ + cet->memtbl[blk].bitvec = (char*)kmalloc(this->blockSize, GFP_ATOMIC); + if (cet->memtbl[blk].bitvec == NULL) { + printk(KERN_INFO "brcmnandCET: %s kmalloc failed\n", __FUNCTION__); + return -1; + } + + memset(cet->memtbl[blk].bitvec, 0xff, sizeof(this->blockSize)); + /* Read an entire block */ + for (i = 0; i < mtd->erasesize / mtd->writesize; i++) { + if (gdebug) { + printk(KERN_INFO "DEBUG -> brcmnandCET: Reading page %d\n", i); + } + ret = mtd_read(mtd, addr, this->pageSize, &retlen, (uint8_t*)(cet->memtbl[blk].bitvec + pg_idx)); + if (ret < 0 || (retlen != this->pageSize)) { + kfree(cet->memtbl[blk].bitvec); + return -1; + } + pg_idx += mtd->writesize; + addr += this->pageSize; + } + } + page = (uint32_t)((origaddr & (~(mtd->erasesize - 1))) >> this->page_shift); + pos = page % (this->blockSize << 3); + byte = pos / (1 << 3); + ptr = (uint32_t*)((char*)cet->memtbl[blk].bitvec + byte); + /* numpages/8bits per byte/4byte per uint32 */ + for (i = 0; i < ((mtd->erasesize / mtd->writesize) >> 3) >> 2; i++) { + /* Count the number of 0s for in the bitvec */ + numzeros += bitcount(~ptr[i]); + } + if (likely(numzeros == 0)) { + if (gdebug) { + printk(KERN_INFO "DEBUG -> brcmnandCET: returning 0 numzeros = 0\n"); + } + return 0; + } + if (cet->cerr_count < numzeros) { + if (gdebug) { + printk(KERN_ERR "brcmnandCET: Erroneous correctable error count"); + } + return -1; + } + cet->cerr_count -= numzeros; + /* Make bits corresponding to this block all 1s */ + memset(cet->memtbl[blk].bitvec + byte, 0xff, (mtd->erasesize / mtd->writesize) >> 3); + cet->memtbl[blk].isdirty = 1; + + return 0; +} + +/* + * brcmnand_cet_update: Called every time a single correctable error is + * encountered. + * @param mtd MTD device structure + * @param from Page address at which correctable error occured + * @param status Return status + * 1 => This page had a correctable errror in past, + * therefore, return correctable error to filesystem + * 0 => First occurence of a correctable error for + * this page. return a success to the filesystem + * + * Check the in memory CET bitvector to see if this page (loff_t from) + * had a correctable error in past, if not set this page's bit to '0' + * in the bitvector. + * + */ +int brcmnand_cet_update(struct mtd_info *mtd, loff_t from, int *status) +{ + struct brcmnand_chip *this = (struct brcmnand_chip *)mtd->priv; + struct brcmnand_cet_descr *cet = this->cet; + int gdebug = 0, ret, blk, byte, bit, retlen = 0, blkbegin, i; + uint32_t page = 0; + unsigned int pg_idx = 0, pos = 0; + unsigned char c, mask; + + if (gdebug) { + printk(KERN_INFO "DEBUG -> brcmnandCET: Inside %s\n", __FUNCTION__); + } + if (cet->flags == BRCMNAND_CET_LAZY) { + /* Force creation of the CET and the mem table */ + ret = search_cet_blks(mtd, cet, 1); + if (ret < 0) { + cet->flags = BRCMNAND_CET_DISABLED; + return ret; + } + cet->flags = BRCMNAND_CET_LOADED; + } + /* Find out which entry in memtbl does the from address map to */ + page = (uint32_t)(from >> this->page_shift); + /* each bit is one page << 3 for 8 bits per byte */ + blk = page / (this->blockSize << 3); + if (unlikely(cet->memtbl[blk].blk == -1)) { + printk(KERN_INFO "brcmnandCET: %s invalid block# in CET\n", __FUNCTION__); + return -1; + } + blkbegin = cet->memtbl[blk].blk; + /* Start page of the block */ + from = ((loff_t)blkbegin) << this->bbt_erase_shift; + /* If bitvec == NULL, load the block from flash */ + if (cet->memtbl[blk].bitvec == NULL) { + if (gdebug) { + printk(KERN_INFO "DEBUG -> brcmnandCET: bitvec null .... loading ...\n"); + } + /* using kmalloc to be consisent with erasecallback function. see erasecallback for details */ + cet->memtbl[blk].bitvec = (char*)kmalloc(this->blockSize, GFP_ATOMIC); + if (cet->memtbl[blk].bitvec == NULL) { + printk(KERN_ERR "brcmnandCET: %s kmalloc failed\n", __FUNCTION__); + return -1; + } + memset(cet->memtbl[blk].bitvec, 0xff, this->blockSize); + /* Read an entire block */ + if (gdebug) { + printk(KERN_INFO "DEBUG -> brcmnandCET: Reading pages starting @ %x\n", (unsigned int)from); + } + for (i = 0; i < mtd->erasesize / mtd->writesize; i++) { + ret = mtd_read(mtd, from, this->pageSize, &retlen, (uint8_t*)(cet->memtbl[blk].bitvec + pg_idx)); + if (ret < 0 || (retlen != this->pageSize)) { + kfree(cet->memtbl[blk].bitvec); + return -1; + } + pg_idx += mtd->writesize; + from += this->pageSize; + } + } + pos = page % (this->blockSize << 3); + byte = pos / (1 << 3); + bit = pos % (1 << 3); + c = cet->memtbl[blk].bitvec[byte]; + mask = 1 << bit; + if ((c & mask) == mask) { /* First time error mark it but return a good status */ + *status = 0; + c = (c & ~mask); + cet->memtbl[blk].bitvec[byte] = c; + cet->memtbl[blk].isdirty = 1; + } else { + *status = 1; /* This page had a previous error so return a bad status */ + } + cet->cerr_count++; +#if 0 + printk(KERN_INFO "DEBUG -> count = %d, byte = %d, bit = %d, blk = %x status = %d c = %d addr = %x\n", cet->cerr_count \ + , byte, bit, blk, *status, cet->memtbl[blk].bitvec[byte], cet->memtbl[blk].bitvec + byte); + printk(KERN_INFO "DEBUG -> CET: Exiting %s\n", __FUNCTION__); +#endif + + return 0; +} + +EXPORT_SYMBOL(brcmnand_cet_update); + +/* + * brcmnand_cet_prepare_reboot Call flush_memcet to flush any in-mem dirty data + * + * @param mtd MTD device structure + * + * Flush any pending in-mem CET blocks to flash before reboot + */ +int brcmnand_cet_prepare_reboot(struct mtd_info *mtd) +{ + int gdebug = 0; + struct brcmnand_chip *this = (struct brcmnand_chip *)mtd->priv; + struct brcmnand_cet_descr *cet = this->cet; + +#if 0 + // Disable for MLC + if (NAND_IS_MLC(this)) { + return 0; + } +#endif + if (unlikely(gdebug)) { + printk(KERN_INFO "DEBUG -> brcmnandCET: flushing pending CET\n"); + } + if (unlikely(cet->flags == BRCMNAND_CET_DISABLED)) { + return 0; + } + flush_memcet(mtd); + + return 0; +} + + +#endif + +#endif diff -urN linux-4.1.52-orig/drivers/mtd/brcmnand/brcmnand_priv.h linux-4.1.52/drivers/mtd/brcmnand/brcmnand_priv.h --- linux-4.1.52-orig/drivers/mtd/brcmnand/brcmnand_priv.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/mtd/brcmnand/brcmnand_priv.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,523 @@ +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +/* + <:copyright-BRCM:2015:DUAL/GPL:standard + + Copyright (c) 2015 Broadcom + All Rights Reserved + + Unless you and Broadcom execute a separate written software license + agreement governing use of this software, this software is licensed + to you under the terms of the GNU General Public License version 2 + (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, + with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + + Not withstanding the above, under no circumstances may you combine + this software in any way with any other Broadcom software provided + under a license other than the GPL, without Broadcom's express prior + written consent. + + :> + +*/ + + + +#ifndef _BRCMNAND_PRIV_H_ +#define _BRCMNAND_PRIV_H_ + +#include +#include + +#ifdef CONFIG_MTD_BRCMNAND_USE_ISR +#include +#include +#include +#include +#include + +//#include "edu.h" +#endif + +#define BRCMNAND_CORRECTABLE_ECC_ERROR (1) +#define BRCMNAND_SUCCESS (0) +#define BRCMNAND_UNCORRECTABLE_ECC_ERROR (-1) +#define BRCMNAND_FLASH_STATUS_ERROR (-2) +#define BRCMNAND_TIMED_OUT (-3) + +#ifdef CONFIG_MTD_BRCMNAND_EDU +#define BRCMEDU_CORRECTABLE_ECC_ERROR (4) +#define BRCMEDU_UNCORRECTABLE_ECC_ERROR (-4) + +#define BRCMEDU_MEM_BUS_ERROR (-5) + + +#define BRCMNAND_malloc(size) kmalloc(size, GFP_DMA) +#define BRCMNAND_free(addr) kfree(addr) + +#else +#define BRCMNAND_malloc(size) vmalloc(size) +#define BRCMNAND_free(addr) vfree(addr) +#endif + +#if 0 /* TO */ +typedef u8 uint8; +typedef u16 uint16; +typedef u32 uint32; +#endif + +#define BRCMNAND_FCACHE_SIZE 512 +#define ECCSIZE(chip) BRCMNAND_FCACHE_SIZE /* Always 512B for Brcm NAND controller */ + +#define MTD_OOB_NOT_WRITEABLE 0x8000 +#define MTD_CAP_MLC_NANDFLASH (MTD_WRITEABLE | MTD_OOB_NOT_WRITEABLE) +#define MTD_IS_MLC(mtd) ((((mtd)->flags & MTD_CAP_MLC_NANDFLASH) == MTD_CAP_MLC_NANDFLASH) &&\ + (((mtd)->flags & MTD_OOB_NOT_WRITEABLE) == MTD_OOB_NOT_WRITEABLE)) + + +/* + * NUM_NAND_CS here is strictly based on the number of CS in the NAND registers + * It does not have the same value as NUM_CS in brcmstb/setup.c + * It is not the same as NAND_MAX_CS, the later being the bit fields found in NAND_CS_NAND_SELECT. + */ + +/* + * # number of CS supported by EBI + */ +#ifdef BCHP_NAND_CS_NAND_SELECT_EBI_CS_7_SEL_MASK +/* Version < 3 */ +#define NAND_MAX_CS 8 + +#elif defined(BCHP_NAND_CS_NAND_SELECT_EBI_CS_3_SEL_MASK) +/* 7420Cx */ +#define NAND_MAX_CS 4 +#else +/* 3548 */ +#define NAND_MAX_CS 2 +#endif + +/* + * Number of CS seen by NAND + */ +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_3_3 +#define NUM_NAND_CS 4 + +#else +#define NUM_NAND_CS 2 +#endif + +#ifdef CONFIG_MTD_BRCMNAND_USE_ISR + +//#define BCM_BASE_ADDRESS 0xb0000000 + +/* CP0 hazard avoidance. */ +#define BARRIER __asm__ __volatile__(".set noreorder\n\t" \ + "nop; nop; nop; nop; nop; nop;\n\t" \ + ".set reorder\n\t") + +/* + * Right now we submit a full page Read for queueing, so with a 8KB page, + * and an ECC step of 512B, the queue depth is 16. Add 2 for dummy elements + * during EDU WAR + */ +#if CONFIG_MTD_BRCMNAND_VERSION <= CONFIG_MTD_BRCMNAND_VERS_3_3 +#define MAX_NAND_PAGE_SIZE (4<<10) + +#else +#define MAX_NAND_PAGE_SIZE (8<<10) +#endif + +/* Max queue size is (PageSize/512B_ECCSize)+2 spare for WAR */ +#define MAX_JOB_QUEUE_SIZE ((MAX_NAND_PAGE_SIZE>>9)) + +typedef enum { + ISR_OP_QUEUED = 0, + ISR_OP_SUBMITTED = 1, + ISR_OP_NEED_WAR = 2, + ISR_OP_COMPLETED = 3, + ISR_OP_TIMEDOUT = 4, + ISR_OP_COMP_WITH_ERROR = 5, +} isrOpStatus_t; + +typedef struct eduIsrNode { + struct list_head list; + spinlock_t lock; // per Node update lock + // int cmd; // 1 == Read, 0 == Write + + // ISR stuffs + uint32_t mask; /* Clear status mask */ + uint32_t expect; /* Status on success */ + uint32_t error; /* Status on error */ + uint32_t intr; /* Interrupt bits */ + uint32_t status; /* Status read during ISR. There may be several interrupts before completion */ + isrOpStatus_t opComplete; /* Completion status */ + + /* Controller Level params (for queueing) */ + struct mtd_info* mtd; + void* buffer; + u_char* oobarea; + loff_t offset; + int ret; + int needBBT; + + /* EDU level params (for ISR) */ + uint32_t edu_ldw; + uint32_t physAddr; + uint32_t hif_intr2; + uint32_t edu_status; + + int refCount; /* Marked for re-use when refCount=0 */ + unsigned long expired; /* Time stamp for expiration, 3 secs from submission */ +} eduIsrNode_t; + +/* + * Read/Write Job Q. + * Process one page at a time, and queue 512B sector Read or Write EDU jobs. + * ISR will wake up the process context thread iff + * 1-EDU reports an error, in which case the process context thread need to be awaken + * in order to do WAR + * 2-Q is empty, in which case the page read/write op is complete. + */ +typedef struct jobQ_t { + struct list_head jobQ; /* Nodes queued for EDU jobs */ + struct list_head availList; /* Free Nodes */ + spinlock_t lock; /* Queues guarding spin lock */ + int needWakeUp; /* Wake up Process context thread to do EDU WAR */ + int cmd; /* 1 == Read, 0 == Write */ + int corrected; /* Number of correctable errors */ +} isrJobQ_t; + +extern isrJobQ_t gJobQ; + +void ISR_init(void); + +/* + * Submit the first entry that is in queued state, + * assuming queue lock has been held by caller. + * + * @doubleBuffering indicates whether we need to submit just 1 job or until EDU is full (double buffering) + * Return the number of job submitted for read. + * + * In current version (v3.3 controller), since EDU only have 1 register for EDU_ERR_STATUS, + * we can't really do double-buffering without losing the returned status of the previous read-op. + */ +#undef EDU_DOUBLE_BUFFER_READ + +int brcmnand_isr_submit_job(void); + +eduIsrNode_t* ISR_queue_read_request(struct mtd_info *mtd, + void* buffer, u_char* oobarea, loff_t offset); +eduIsrNode_t* ISR_queue_write_request(struct mtd_info *mtd, + const void* buffer, const u_char* oobarea, loff_t offset); +eduIsrNode_t* ISR_push_request(struct mtd_info *mtd, + void* buffer, u_char* oobarea, loff_t offset); + + +int brcmnand_edu_read_completion(struct mtd_info* mtd, + void* buffer, u_char* oobarea, loff_t offset, uint32_t intr_status); + +int brcmnand_edu_read_comp_intr(struct mtd_info* mtd, + void* buffer, u_char* oobarea, loff_t offset, uint32_t intr_status); + +#ifdef CONFIG_MTD_BRCMNAND_ISR_QUEUE +int brcmnand_edu_write_completion(struct mtd_info *mtd, + const void* buffer, const u_char* oobarea, loff_t offset, uint32_t intr_status, + int needBBT); +int +brcmnand_edu_write_war(struct mtd_info *mtd, + const void* buffer, const u_char* oobarea, loff_t offset, uint32_t intr_status, + int needBBT); +#endif +eduIsrNode_t* ISR_find_request( isrOpStatus_t opStatus); + +uint32_t ISR_wait_for_completion(void); + +/* + * wait for completion with read/write Queue + */ +int ISR_wait_for_queue_completion(void); + +int ISR_cache_is_valid(void); + +static __inline__ uint32_t ISR_volatileRead(uint32_t addr) +{ + + + return (uint32_t) BDEV_RD(addr); +} + +static __inline__ void ISR_volatileWrite(uint32_t addr, uint32_t data) +{ + BDEV_WR(addr, data); +} + +static __inline__ void ISR_enable_irq(eduIsrNode_t* req) +{ + //uint32_t intrMask; + //unsigned long flags; + + //spin_lock_irqsave(&gEduIsrData.lock, flags); + + // Clear status bits + ISR_volatileWrite(BCHP_HIF_INTR2_CPU_CLEAR, req->mask); + + // Enable interrupt + ISR_volatileWrite(BCHP_HIF_INTR2_CPU_MASK_CLEAR, req->intr); + + //spin_unlock_irqrestore(&gEduIsrData.lock, flags); +} + +static __inline__ void ISR_disable_irq(uint32_t mask) +{ + + /* Disable L2 interrupts */ + ISR_volatileWrite(BCHP_HIF_INTR2_CPU_MASK_SET, mask); + +} + +/* + * For debugging + */ + +#ifdef DEBUG_ISR + +static void __inline__ +ISR_print_queue(void) +{ + eduIsrNode_t* req; + //struct list_head* node; + int i = 0; + + list_for_each_entry(req, &gJobQ.jobQ, list) { + + printk("i=%d, cmd=%d, offset=%08llx, flashAddr=%08x, opComp=%d, status=%08x\n", + i, gJobQ.cmd, req->offset, req->edu_ldw,req->opComplete, req->status); + i++; + } +} + +static void __inline__ +ISR_print_avail_list(void) +{ + eduIsrNode_t* req; + //struct list_head* node; + int i = 0; + + printk("AvailList=%p, next=%p\n", &gJobQ.availList, gJobQ.availList.next); + list_for_each_entry(req, &gJobQ.availList, list) { + printk("i=%d, req=%p, list=%p\n", i, req, &req->list); + i++; + } +} +#else +#define IS_print_queue() +#define ISR_print_avail_list() +#endif // DEBUG_ISR + + +#endif // CONFIG_MTD_BRCMNAND_USE_ISR + +static inline u_int64_t device_size(struct mtd_info *mtd) +{ + //return mtd->size == 0 ? (u_int64_t) mtd->num_eraseblocks * mtd->erasesize : (u_int64_t) mtd->size; + return mtd->size; +} + +/** + * brcmnand_scan - [BrcmNAND Interface] Scan for the BrcmNAND device + * @param mtd MTD device structure + * @cs Chip Select number + * @param numchips Number of chips (from CFE or from nandcs= kernel arg) + + * + * This fills out all the not initialized function pointers + * with the defaults. + * The flash ID is read and the mtd/chip structures are + * filled with the appropriate values. + * + */ +extern int brcmnand_scan(struct mtd_info *mtd , int cs, int maxchips); + +/** + * brcmnand_release - [BrcmNAND Interface] Free resources held by the BrcmNAND device + * @param mtd MTD device structure + */ +extern void brcmnand_release(struct mtd_info *mtd); + +/* BrcmNAND BBT interface */ + +/* Auto-format scan layout for BCH-8 with 16B OOB */ +#define BRCMNAND_BBT_AUTO_PLACE 0x80000000 + +extern uint8_t* brcmnand_transfer_oob(struct brcmnand_chip *chip, uint8_t *oob, + struct mtd_oob_ops *ops, int len); +extern uint8_t* brcmnand_fill_oob(struct brcmnand_chip *chip, uint8_t *oob, struct mtd_oob_ops *ops); + +/* Read the OOB bytes and tell whether a block is bad without consulting the BBT */ +extern int brcmnand_isbad_raw (struct mtd_info *mtd, loff_t offs); + +extern int brcmnand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd); +extern int brcmnand_default_bbt(struct mtd_info *mtd); + +extern int brcmnand_update_bbt (struct mtd_info *mtd, loff_t offs); + +//extern void* get_brcmnand_handle(void); + +extern void print_oobbuf(const unsigned char* buf, int len); +extern void print_databuf(const unsigned char* buf, int len); + +#ifdef CONFIG_MTD_BRCMNAND_CORRECTABLE_ERR_HANDLING +extern int brcmnand_cet_update(struct mtd_info *mtd, loff_t from, int *status); +extern int brcmnand_cet_prepare_reboot(struct mtd_info *mtd); +extern int brcmnand_cet_erasecallback(struct mtd_info *mtd, u_int32_t addr); +extern int brcmnand_create_cet(struct mtd_info *mtd); +#endif + +/* + * Disable ECC, and return the original ACC register (for restore) + */ +uint32_t brcmnand_disable_read_ecc(int cs); + +void brcmnand_restore_ecc(int cs, uint32_t orig_acc0); + +void brcmnand_post_mortem_dump(struct mtd_info* mtd, loff_t offset); + +static unsigned int __maybe_unused brcmnand_get_bbt_size(struct mtd_info* mtd) +{ + struct brcmnand_chip * chip = mtd->priv; + + // return ((device_size(mtd) > (512 << 20)) ? 4<<20 : 1<<20); + return chip->bbtSize; +} + + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_3_3 +static inline uintptr_t bchp_nand_acc_control(int cs) +{ + switch (cs) { + case 0: return BCHP_NAND_ACC_CONTROL; + case 1: return BCHP_NAND_ACC_CONTROL_CS1; +#ifdef BCHP_NAND_ACC_CONTROL_CS2 + case 2: return BCHP_NAND_ACC_CONTROL_CS2; +#endif +#ifdef BCHP_NAND_ACC_CONTROL_CS3 + case 3: return BCHP_NAND_ACC_CONTROL_CS3; +#endif + } + return 0; +} + +static inline uintptr_t bchp_nand_config(int cs) +{ + switch (cs) { + case 0: return BCHP_NAND_CONFIG; + case 1: return BCHP_NAND_CONFIG_CS1; +#ifdef BCHP_NAND_CONFIG_CS2 + case 2: return BCHP_NAND_CONFIG_CS2; +#endif +#ifdef BCHP_NAND_CONFIG_CS3 + case 3: return BCHP_NAND_CONFIG_CS3; +#endif + } + return 0; +} + +static inline uintptr_t bchp_nand_timing1(int cs) +{ + switch (cs) { + case 0: return BCHP_NAND_TIMING_1; + case 1: return BCHP_NAND_TIMING_1_CS1; +#ifdef BCHP_NAND_TIMING_1_CS2 + case 2: return BCHP_NAND_TIMING_1_CS2; +#endif +#ifdef BCHP_NAND_TIMING_1_CS3 + case 3: return BCHP_NAND_TIMING_1_CS3; +#endif + } + return 0; +} +static inline uintptr_t bchp_nand_timing2(int cs) +{ + switch (cs) { + case 0: return BCHP_NAND_TIMING_2; + case 1: return BCHP_NAND_TIMING_2_CS1; +#ifdef BCHP_NAND_TIMING_2_CS2 + case 2: return BCHP_NAND_TIMING_2_CS2; +#endif +#ifdef BCHP_NAND_TIMING_2_CS3 + case 3: return BCHP_NAND_TIMING_2_CS3; +#endif + } + return 0; +} + +#else +#define bchp_nand_acc_control(cs) BCHP_NAND_ACC_CONTROL +#define bchp_nand_config(cs) BCHP_NAND_CONFIG +#define bchp_nand_timing1(cs) BCHP_NAND_TIMING_1 +#define bchp_nand_timing2(cs) BCHP_NAND_TIMING_2 +#endif + +/*********************************************************************** + * Register access macros - sample usage: + * + * DEV_RD(0xb0404000) -> reads 0xb0404000 + * BDEV_RD(0x404000) -> reads 0xb0404000 + * BDEV_RD(BCHP_SUN_TOP_CTRL_PROD_REVISION) -> reads 0xb0404000 + * + * _RB means read back after writing. + ***********************************************************************/ +#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) +#define BPHYSADDR(x) (x) +#define BVIRTADDR(x) (x) +#else +#define BPHYSADDR(x) ((x) | 0x10000000) +#define BVIRTADDR(x) KSEG1ADDR(BPHYSADDR(x)) +#endif + +#define DEV_RD(x) (*((volatile unsigned int *)(x))) +#define DEV_WR(x, y) do { *((volatile unsigned int *)(x)) = (y); } while (0) +#define DEV_UNSET(x, y) do { DEV_WR((x), DEV_RD(x) & ~(y)); } while (0) +#define DEV_SET(x, y) do { DEV_WR((x), DEV_RD(x) | (y)); } while (0) + +#define DEV_WR_RB(x, y) do { DEV_WR((x), (y)); DEV_RD(x); } while (0) +#define DEV_SET_RB(x, y) do { DEV_SET((x), (y)); DEV_RD(x); } while (0) +#define DEV_UNSET_RB(x, y) do { DEV_UNSET((x), (y)); DEV_RD(x); } while (0) + +#define BDEV_RD(x) (DEV_RD(BVIRTADDR(x))) +#define BDEV_WR(x, y) do { DEV_WR(BVIRTADDR(x), (y)); } while (0) +#define BDEV_UNSET(x, y) do { BDEV_WR((x), BDEV_RD(x) & ~(y)); } while (0) +#define BDEV_SET(x, y) do { BDEV_WR((x), BDEV_RD(x) | (y)); } while (0) + +#define BDEV_SET_RB(x, y) do { BDEV_SET((x), (y)); BDEV_RD(x); } while (0) +#define BDEV_UNSET_RB(x, y) do { BDEV_UNSET((x), (y)); BDEV_RD(x); } while (0) +#define BDEV_WR_RB(x, y) do { BDEV_WR((x), (y)); BDEV_RD(x); } while (0) + +#define BDEV_RD_F(reg, field) \ + ((BDEV_RD(BCHP_##reg) & BCHP_##reg##_##field##_MASK) >> \ + BCHP_##reg##_##field##_SHIFT) +#define BDEV_WR_F(reg, field, val) do { \ + BDEV_WR(BCHP_##reg, \ + (BDEV_RD(BCHP_##reg) & ~BCHP_##reg##_##field##_MASK) | \ + (((val) << BCHP_##reg##_##field##_SHIFT) & \ + BCHP_##reg##_##field##_MASK)); \ + } while (0) +#define BDEV_WR_F_RB(reg, field, val) do { \ + BDEV_WR(BCHP_##reg, \ + (BDEV_RD(BCHP_##reg) & ~BCHP_##reg##_##field##_MASK) | \ + (((val) << BCHP_##reg##_##field##_SHIFT) & \ + BCHP_##reg##_##field##_MASK)); \ + BDEV_RD(BCHP_##reg); \ + } while (0) + + +#endif +#endif diff -urN linux-4.1.52-orig/drivers/mtd/brcmnand/Kconfig linux-4.1.52/drivers/mtd/brcmnand/Kconfig --- linux-4.1.52-orig/drivers/mtd/brcmnand/Kconfig 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/mtd/brcmnand/Kconfig 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,74 @@ +# +# linux/drivers/mtd/brcmnand/Kconfig +# + +config MTD_BRCMNAND + tristate "Broadcom NAND controller support" + depends on BCM_KF_MTD_BCMNAND + select BRCMNAND_MTD_EXTENSION + default n + help + Say Y to enable the onchip NAND controller. + +config BRCMNAND_MTD_EXTENSION + bool "Enable Broadcom NAND controller extension" + depends on BCM_KF_MTD_BCMNAND + depends on MTD_BRCMNAND + default y + help + Say Y to enable Broadcom NAND extension in order to support + 27Byte Spare Area and 8KB page NAND flashes. + +config MTD_BRCMNAND_VERIFY_WRITE + bool "Verify Broadcom NAND page writes" + default n + depends on BCM_KF_MTD_BCMNAND + depends on MTD_BRCMNAND + help + This adds an extra check when data is written to the flash. The + Broadcom NAND flash device internally checks only bits transitioning + from 1 to 0. There is a rare possibility that even though the + device thinks the write was successful, a bit could have been + flipped accidentally due to device wear or something else. + +config MTD_BRCMNAND_CORRECTABLE_ERR_HANDLING + bool "Refresh a block on a one bit correctable ECC error" + default n + depends on BCM_KF_MTD_BCMNAND + depends on MTD_BRCMNAND + help + If there is a 1-bit correctable error detected during NAND flash + read, the Broadcom NAND flash driver can refresh the corresponding + NAND flash block. Refreshing implies a sequence of + read->erase->write. Refreshing the block drastically reduces the + probability of occurance of a similar (correctable) error. + + Default is N because this is normally handled by UBI. + +config MTD_BRCMNAND_EDU + bool "Enable Broadcom NAND DMA (EDU)" + default y + select MTD_BRCMNAND_USE_ISR + select MTD_BRCMNAND_ISR_QUEUE + depends on BCM_KF_MTD_BCMNAND + depends on MTD_BRCMNAND && BRCM_HAS_EDU + help + Say Y to enable the EBI DMA unit for NAND flash transfers. + Say N to use PIO transfers. + +config BRCMNAND_MAJOR_VERS + int "Broadcom NAND Major Version" + default 2 + depends on BCM_KF_MTD_BCMNAND + depends on MTD_BRCMNAND + help + NAND controller major version. + +config BRCMNAND_MINOR_VERS + int "Broadcom NAND Minor Version" + default 1 + depends on BCM_KF_MTD_BCMNAND + depends on MTD_BRCMNAND + help + NAND controller minor version. + diff -urN linux-4.1.52-orig/drivers/mtd/brcmnand/Makefile linux-4.1.52/drivers/mtd/brcmnand/Makefile --- linux-4.1.52-orig/drivers/mtd/brcmnand/Makefile 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/mtd/brcmnand/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,11 @@ +ifdef BCM_KF # defined(CONFIG_BCM_KF_MTD_BCMNAND) +# +# Makefile for the Broadcom NAND MTD +# + +obj-$(CONFIG_MTD_BRCMNAND) += bcm63xx-nand.o brcmnand_base.o brcmnand_bbt.o +## obj-$(CONFIG_MTD_BRCMNAND_EDU) += edu.o +obj-$(CONFIG_MTD_BRCMNAND_CORRECTABLE_ERR_HANDLING) += brcmnand_cet.o +EXTRA_CFLAGS += -I $(TOPDIR)/include/asm-generic -I$(INC_BRCMDRIVER_PUB_PATH)/$(BRCM_BOARD) -I$(INC_BRCMSHARED_PUB_PATH)/$(BRCM_BOARD) +endif # BCM_KF # defined(CONFIG_BCM_KF_MTD_BCMNAND) + diff -urN linux-4.1.52-orig/drivers/mtd/Kconfig linux-4.1.52/drivers/mtd/Kconfig --- linux-4.1.52-orig/drivers/mtd/Kconfig 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/mtd/Kconfig 2020-02-07 01:47:54.000000000 +0800 @@ -338,4 +338,8 @@ source "drivers/mtd/ubi/Kconfig" +if BCM_KF_MTD_BCMNAND +source "drivers/mtd/brcmnand/Kconfig" +endif + endif # MTD diff -urN linux-4.1.52-orig/drivers/mtd/Makefile linux-4.1.52/drivers/mtd/Makefile --- linux-4.1.52-orig/drivers/mtd/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/mtd/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -32,5 +32,9 @@ obj-y += chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/ +ifdef BCM_KF # defined(CONFIG_BCM_KF_MTD_BCMNAND) +obj-$(CONFIG_MTD_BRCMNAND) += brcmnand/ +endif #BCM_KF # defined(CONFIG_BCM_KF_MTD_BCMNAND) + obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/ obj-$(CONFIG_MTD_UBI) += ubi/ diff -urN linux-4.1.52-orig/drivers/mtd/maps/bcm963xx.c linux-4.1.52/drivers/mtd/maps/bcm963xx.c --- linux-4.1.52-orig/drivers/mtd/maps/bcm963xx.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/mtd/maps/bcm963xx.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,239 @@ +#if defined(CONFIG_BCM_KF_MTD_BCM963XX) +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + +/* + * Flash mapping code for BCM963xx board SPI NOR flash memory + * + * Song Wang (songw@broadcom.com) + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static void bcm63xx_noop(struct mtd_info *mtd); +static int bcm63xx_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); +static int bcm63xx_erase(struct mtd_info *mtd, struct erase_info *instr); +static int bcm63xx_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); + +static struct mtd_info *mtdRootFS; + +#ifdef CONFIG_AUXFS_JFFS2 +static struct mtd_info *mtdAuxFS; +#endif + +static void bcm63xx_noop(struct mtd_info *mtd) +{ +} + +static int bcm63xx_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +{ + unsigned long flash_base; + + flash_base = (unsigned long)mtd->priv; + *retlen = kerSysReadFromFlash(buf, flash_base + from, len); + + return 0; +} + +static int bcm63xx_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + unsigned long flash_base; + + if (instr->addr + instr->len > mtd->size) { + printk("ERROR: bcm63xx_erase( mtd[%s]) invalid region\n", mtd->name); + return (-EINVAL); + } + + flash_base = (unsigned long)mtd->priv; + + if (kerSysEraseFlash( flash_base + instr->addr, instr->len)) + return (-EINVAL); + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + + return 0; +} + +static int bcm63xx_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) +{ + unsigned long flash_base; + int bytesRemaining; + + flash_base = (unsigned long)mtd->priv; + + bytesRemaining = kerSysWriteToFlash(flash_base + to, (char*)buf, len); + *retlen = (len - bytesRemaining); + + return 0; +} + +#ifdef CONFIG_AUXFS_JFFS2 +static int __init create_aux_partition(void) +{ + FLASH_PARTITION_INFO fPartAuxFS; + + /* Read the flash memory partition map. */ + kerSysFlashPartInfoGet(&fPartAuxFS); + + if (fPartAuxFS.mem_length != 0) { + + if ((mtdAuxFS = kzalloc(sizeof(*mtdAuxFS), GFP_KERNEL)) == NULL) + return -ENOMEM; + + /* Read/Write data (Aux) partition */ + mtdAuxFS->name = "data"; + mtdAuxFS->index = -1; + mtdAuxFS->type = MTD_NORFLASH; + mtdAuxFS->flags = MTD_CAP_NORFLASH; + + mtdAuxFS->erasesize = fPartAuxFS.sect_size; + mtdAuxFS->writesize = 1; + mtdAuxFS->numeraseregions = 0; + mtdAuxFS->eraseregions = NULL; + mtdAuxFS->size = fPartAuxFS.mem_length; + + mtdAuxFS->_read = bcm63xx_read; + mtdAuxFS->_erase = bcm63xx_erase; + mtdAuxFS->_write = bcm63xx_write; + mtdAuxFS->_sync = bcm63xx_noop; + mtdAuxFS->owner = THIS_MODULE; + mtdAuxFS->priv = (void*)fPartAuxFS.mem_base; + + if (mtd_device_register(mtdAuxFS, NULL, 0 )) { + printk("Failed to register device mtd:%s\n", mtdAuxFS->name); + return -EIO; + } + + printk("Registered device mtd:%s dev%d Address=0x%08x Size=%llu\n", + mtdAuxFS->name, mtdAuxFS->index, (int)mtdAuxFS->priv, mtdAuxFS->size); + } + + return 0; +} +#endif + +static int __init init_brcm_physmap(void) +{ + uintptr_t rootfs_addr, kernel_addr; + PFILE_TAG pTag = (PFILE_TAG)NULL; + int flash_type = flash_get_flash_type(); + + if ((flash_type != FLASH_IFC_SPI) && (flash_type != FLASH_IFC_HS_SPI )) + return -EIO; + + printk("bcm963xx_mtd driver\n"); + + if (!(pTag = kerSysImageTagGet())) { + printk("Failed to read image tag from flash\n"); + return -EIO; + } + + rootfs_addr = (uintptr_t)simple_strtoul(pTag->rootfsAddress, NULL, 10) + BOOT_OFFSET + IMAGE_OFFSET; + kernel_addr = (uintptr_t)simple_strtoul(pTag->kernelAddress, NULL, 10) + BOOT_OFFSET + IMAGE_OFFSET; + + if ((mtdRootFS = kzalloc(sizeof(*mtdRootFS), GFP_KERNEL)) == NULL) + return -ENOMEM; + + /* RootFS Read only partition */ + mtdRootFS->name = "rootfs"; + mtdRootFS->index = -1; + mtdRootFS->type = MTD_NORFLASH; + mtdRootFS->flags = MTD_CAP_ROM; + + mtdRootFS->erasesize = 0x10000; + mtdRootFS->writesize = 0x10000; + mtdRootFS->numeraseregions = 0; + mtdRootFS->eraseregions = NULL; + + mtdRootFS->_read = bcm63xx_read; + mtdRootFS->_erase = bcm63xx_erase; + mtdRootFS->_write = bcm63xx_write; + mtdRootFS->_sync = bcm63xx_noop; + mtdRootFS->owner = THIS_MODULE; + + if ((mtdRootFS->size = (kernel_addr - rootfs_addr)) <= 0) { + printk("Invalid RootFs size\n"); + kfree(mtdRootFS); + return -EIO; + } + + mtdRootFS->priv = (void*)rootfs_addr; + + if (mtd_device_register(mtdRootFS , NULL, 0)) { + printk("Failed to register device mtd:%s\n", mtdRootFS->name); + return -EIO; + } + + printk("Registered device mtd:%s dev%d Address=%p Size=%llu\n", + mtdRootFS->name, mtdRootFS->index, (void *)mtdRootFS->priv, mtdRootFS->size); + + if (kerSysIsRootfsSet() == 0) { + kerSysSetBootParm("root=", "/dev/mtdblock0"); + kerSysSetBootParm("rootfstype=", "squashfs"); + } + +#ifdef CONFIG_AUXFS_JFFS2 + create_aux_partition(); +#endif + + return 0; +} + +static void __exit cleanup_brcm_physmap(void) +{ + if (mtdRootFS->index >= 0) + mtd_device_unregister(mtdRootFS); + + kfree(mtdRootFS); + +#ifdef CONFIG_AUXFS_JFFS2 + if (mtdAuxFS->index >= 0) + mtd_device_unregister(mtdAuxFS); + + kfree(mtdAuxFS); +#endif +} + +module_init(init_brcm_physmap); +module_exit(cleanup_brcm_physmap); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Song Wang songw@broadcom.com"); +MODULE_DESCRIPTION("MTD Driver for Broadcom NOR Flash"); +#endif diff -urN linux-4.1.52-orig/drivers/mtd/maps/bcm963xx_mtd.c linux-4.1.52/drivers/mtd/maps/bcm963xx_mtd.c --- linux-4.1.52-orig/drivers/mtd/maps/bcm963xx_mtd.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/mtd/maps/bcm963xx_mtd.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,63 @@ +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +#include +#include + +#include + + +#include +#include + +#include + +#include + +#define PRINTK(...) +//#define PRINTK printk + +extern int setup_mtd_parts(struct mtd_info * mtd); +extern int bcmspinand_probe(struct mtd_info * mtd); + +static int __init mtd_init(void) +{ + struct mtd_info * mtd; + struct nand_chip * nand; + + /* If SPI NAND FLASH is present then register the device. Otherwise do nothing */ + if (FLASH_IFC_SPINAND != flash_get_flash_type()) + return -ENODEV; + + if (((mtd = kmalloc(sizeof(struct mtd_info), GFP_KERNEL)) == NULL) || + ((nand = kmalloc(sizeof(struct nand_chip), GFP_KERNEL)) == NULL)) + { + printk("Unable to allocate SPI NAND dev structure.\n"); + return -ENOMEM; + } + + memset(mtd, 0, sizeof(struct mtd_info)); + memset(nand, 0, sizeof(struct nand_chip)); + + mtd->priv = nand; + + bcmspinand_probe(mtd); + + /* Scan to check existence of the nand device */ + if(nand_scan(mtd, 1)) + { + nand->init_size(mtd, nand, NULL); // override possibly incorrect values detected by Linux NAND driver + + kerSysNvRamLoad(mtd); + + setup_mtd_parts(mtd); + } + + return 0; +} + +module_init(mtd_init); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Regan"); +MODULE_DESCRIPTION("MTD map and partitions SPI NAND"); + +#endif /* CONFIG_BCM_KF_MTD_BCMNAND */ diff -urN linux-4.1.52-orig/drivers/mtd/maps/Kconfig linux-4.1.52/drivers/mtd/maps/Kconfig --- linux-4.1.52-orig/drivers/mtd/maps/Kconfig 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/mtd/maps/Kconfig 2020-02-07 01:47:54.000000000 +0800 @@ -74,6 +74,13 @@ physically into the CPU's memory. The mapping description here is taken from OF device tree. +config MTD_BCM963XX + tristate "Broadcom 963xx ADSL board flash memory support" + depends on BCM_KF_MTD_BCM963XX + help + Broadcom 963xx ADSL board flash memory + + config MTD_PMC_MSP_EVM tristate "CFI Flash device mapped on PMC-Sierra MSP" depends on PMC_MSP && MTD_CFI diff -urN linux-4.1.52-orig/drivers/mtd/maps/Makefile linux-4.1.52/drivers/mtd/maps/Makefile --- linux-4.1.52-orig/drivers/mtd/maps/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/mtd/maps/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -43,3 +43,10 @@ obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr-flash.o obj-$(CONFIG_MTD_LATCH_ADDR) += latch-addr-flash.o obj-$(CONFIG_MTD_LANTIQ) += lantiq-flash.o + +ifdef BCM_KF # defined(CONFIG_BCM_KF_MTD_BCM963XX) +obj-$(CONFIG_MTD_BCM963XX) += bcm963xx.o +obj-$(CONFIG_MTD_BCM_SPI_NAND) += bcm963xx_mtd.o +EXTRA_CFLAGS += -I$(INC_BRCMSHARED_PUB_PATH)/$(BRCM_BOARD) -I$(INC_BRCMDRIVER_PUB_PATH)/$(BRCM_BOARD) +endif # BCM_KF + diff -urN linux-4.1.52-orig/drivers/mtd/mtdchar.c linux-4.1.52/drivers/mtd/mtdchar.c --- linux-4.1.52-orig/drivers/mtd/mtdchar.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/mtd/mtdchar.c 2020-02-07 01:47:54.000000000 +0800 @@ -386,8 +386,11 @@ ops.ooboffs = start & (mtd->writesize - 1); ops.datbuf = NULL; ops.mode = (mfi->mode == MTD_FILE_MODE_RAW) ? MTD_OPS_RAW : +#if defined(CONFIG_BCM_KF_MTD_OOB_AUTO) + MTD_OPS_AUTO_OOB; +#else MTD_OPS_PLACE_OOB; - +#endif if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) return -EINVAL; @@ -426,8 +429,11 @@ ops.ooboffs = start & (mtd->writesize - 1); ops.datbuf = NULL; ops.mode = (mfi->mode == MTD_FILE_MODE_RAW) ? MTD_OPS_RAW : +#if defined(CONFIG_BCM_KF_MTD_OOB_AUTO) + MTD_OPS_AUTO_OOB; +#else MTD_OPS_PLACE_OOB; - +#endif if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) return -EINVAL; diff -urN linux-4.1.52-orig/drivers/mtd/nand/bcm63xx_spinand.c linux-4.1.52/drivers/mtd/nand/bcm63xx_spinand.c --- linux-4.1.52-orig/drivers/mtd/nand/bcm63xx_spinand.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/mtd/nand/bcm63xx_spinand.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,2004 @@ +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +/* + * + * drivers/mtd/bcmspinand/bcm63xx-spinand.c + * + <:copyright-BRCM:2011:DUAL/GPL:standard + + Copyright (c) 2011 Broadcom + All Rights Reserved + + Unless you and Broadcom execute a separate written software license + agreement governing use of this software, this software is licensed + to you under the terms of the GNU General Public License version 2 + (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, + with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + + Not withstanding the above, under no circumstances may you combine + this software in any way with any other Broadcom software provided + under a license other than the GPL, without Broadcom's express prior + written consent. + + :> + + + File: bcm63xx-spinand.c + + Description: + This is a device driver for the Broadcom SPINAND flash for bcm63xxx boards. + + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +//#undef DEBUG_NAND +//#define DEBUG_NAND +#if defined(DEBUG_NAND) +#define DBG_PRINTF printk +#else +#define DBG_PRINTF(...) +#endif + + +#define STATUS_DEFAULT NAND_STATUS_TRUE_READY|NAND_STATUS_READY|NAND_STATUS_WP + +// device information bytes required to identify device for SPI NAND +#define SPI_NAND_ID_LENGTH 4 + +// device information bytes required to identify device for Linux NAND +#define NAND_ID_LENGTH 4 + +#define SPI_CONTROLLER_STATE_SET (1 << 31) +#define SPI_CONTROLLER_STATE_CPHA_EXT (1 << 30) +#define SPI_CONTROLLER_STATE_GATE_CLK_SSOFF (1 << 29) +#define SPI_CONTROLLER_STATE_ASYNC_CLOCK (1 << 28) + +#define SPI_CONTROLLER_MAX_SYNC_CLOCK 30000000 + +/* set mode and controller state based on CHIP defaults + these values do not apply to the legacy controller + legacy controller uses SPI_MODE_3 and clock is not + gated */ + +#define SPI_MODE_DEFAULT SPI_MODE_0 +#define SPI_CONTROLLER_STATE_DEFAULT (SPI_CONTROLLER_STATE_GATE_CLK_SSOFF) + + +static unsigned int spi_max_op_len = SPI_BUF_LEN; + +spinlock_t chip_lock; + + +struct SpiNandChip +{ + unsigned char *chip_name; + unsigned char chip_device_id[SPI_NAND_ID_LENGTH]; + unsigned long chip_total_size; + unsigned int chip_num_blocks; + unsigned int chip_block_size; + unsigned int chip_page_size; + unsigned int chip_spare_size; + unsigned int chip_ecc_offset; + struct nand_ecclayout *ecclayout; + unsigned short chip_block_shift; + unsigned short chip_page_shift; + unsigned short chip_num_planes; + unsigned long chip_die_sel; + unsigned char chip_ecc; // ECC bits + unsigned char chip_ecc_corr; // threshold to fix correctable bits + unsigned char chip_ecc_enh; // enhanced bad bit detection by chip + unsigned char chip_subpage_shift; // 2^ shift amount based on number of subpages, typically 4 + unsigned long chip_clock_speed; +}; + +static struct SpiNandChip * pchip; + + +static struct SpiNandChip SpiDevInfo[] = +{ + { // 1Gb + .chip_name = "GigaDevice GD5F1GQ4UA", + .chip_device_id = {GIGADEVPART, ID_GD5F1GQ4UA, 0, 0}, + .chip_page_size = 2048, + .chip_page_shift = 11, + .chip_block_size = 64 * 2048, // 64 pages per block x chip_page_size + .chip_block_shift = 17, // block size of 0x20000 (128KB) + .chip_spare_size = 128, + .chip_ecc_offset = 0x840, // location of ECC bytes + .chip_num_blocks = 1024, // 1024 blocks total + .chip_num_planes = 1, + .chip_die_sel = 0, + .chip_total_size = 64 * 2048 * 1024, // chip_block_size x chip_num_blocks + .ecclayout = &spinand_oob_gigadevice_2k_A, + .chip_ecc = 8, // ECC bits + .chip_ecc_corr = 6, // threshold to fix correctable bits (6/8) + .chip_ecc_enh = 0, // enhanced bad bit detection by chip (none) + .chip_subpage_shift = 2, // 2^ shift amount based on number of subpages (4) + .chip_clock_speed = 120000000, + }, + { // 2Gb + .chip_name = "GigaDevice GD5F2GQ4UA", + .chip_device_id = {GIGADEVPART, ID_GD5F2GQ4UA, 0, 0}, + .chip_page_size = 2048, + .chip_page_shift = 11, + .chip_block_size = 64 * 2048, // 64 pages per block x chip_page_size + .chip_block_shift = 17, // block size of 0x20000 (128KB) + .chip_spare_size = 128, + .chip_ecc_offset = 0x840, // location of ECC bytes + .chip_num_blocks = 2048, // 2048 blocks total + .chip_num_planes = 1, + .chip_die_sel = 0, + .chip_total_size = 64 * 2048 * 2048, // chip_block_size x chip_num_blocks + .ecclayout = &spinand_oob_gigadevice_2k_A, + .chip_ecc = 8, // ECC bits + .chip_ecc_corr = 6, // threshold to fix correctable bits (6/8) + .chip_ecc_enh = 0, // enhanced bad bit detection by chip (none) + .chip_subpage_shift = 2, // 2^ shift amount based on number of subpages (4) + .chip_clock_speed = 120000000, + }, + { // 1Gb + .chip_name = "GigaDevice GD5F1GQ4UB", // also GD5F1GQ4UE with integrated controller instead of seperate controller die + .chip_device_id = {GIGADEVPART, ID_GD5F1GQ4UB, 0, 0}, + .chip_page_size = 2048, + .chip_page_shift = 11, + .chip_block_size = 64 * 2048, // 64 pages per block x chip_page_size + .chip_block_shift = 17, // block size of 0x20000 (128KB) + .chip_spare_size = 128, + .chip_ecc_offset = 0x840, // location of ECC bytes + .chip_num_blocks = 1024, // 1024 blocks total + .chip_num_planes = 1, + .chip_die_sel = 0, + .chip_total_size = 64 * 2048 * 1024, // chip_block_size x chip_num_blocks + .ecclayout = &spinand_oob_gigadevice_2k_B, + .chip_ecc = 8, // ECC bits + .chip_ecc_corr = 6, // threshold to fix correctable bits (6/8) + .chip_ecc_enh = 1, // enhanced bad bit detection by chip (6/8) + .chip_subpage_shift = 2, // 2^ shift amount based on number of subpages (4) + .chip_clock_speed = 120000000, + }, + { // 2Gb + .chip_name = "GigaDevice GD5F2GQ4UB", + .chip_device_id = {GIGADEVPART, ID_GD5F2GQ4UB, 0, 0}, + .chip_page_size = 2048, + .chip_page_shift = 11, + .chip_block_size = 64 * 2048, // 64 pages per block x chip_page_size + .chip_block_shift = 17, // block size of 0x20000 (128KB) + .chip_spare_size = 128, + .chip_ecc_offset = 0x840, // location of ECC bytes + .chip_num_blocks = 2048, // 2048 blocks total + .chip_num_planes = 1, + .chip_die_sel = 0, + .chip_total_size = 64 * 2048 * 2048, // chip_block_size x chip_num_blocks + .ecclayout = &spinand_oob_gigadevice_2k_B, + .chip_ecc = 8, // ECC bits + .chip_ecc_corr = 6, // threshold to fix correctable bits (6/8) + .chip_ecc_enh = 1, // enhanced bad bit detection by chip (6/8) + .chip_subpage_shift = 2, // 2^ shift amount based on number of subpages (4) + .chip_clock_speed = 120000000, + }, + { // 4Gb + .chip_name = "GigaDevice GD5F4GQ4UB", + .chip_device_id = {GIGADEVPART, ID_GD5F4GQ4UB, 0, 0}, + .chip_page_size = 4096, + .chip_page_shift = 12, + .chip_block_size = 64 * 4096, // 64 pages per block x chip_page_size + .chip_block_shift = 18, // block size of 0x40000 (256KB) + .chip_spare_size = 256, + .chip_ecc_offset = 0x1080, // location of ECC bytes + .chip_num_blocks = 2048, // 2048 blocks total + .chip_num_planes = 1, + .chip_die_sel = 0, + .chip_total_size = 64 * 4096 * 2048, // chip_block_size x chip_num_blocks + .ecclayout = &spinand_oob_gigadevice_4k, + .chip_ecc = 8, // ECC bits + .chip_ecc_corr = 6, // threshold to fix correctable bits (6/8) + .chip_ecc_enh = 1, // enhanced bad bit detection by chip (6/8) + .chip_subpage_shift = 3, // 2^ shift amount based on number of subpages (4) + .chip_clock_speed = 120000000, + }, + { // 1Gb + .chip_name = "Micron MT29F1G01AA", + .chip_device_id = {MICRONPART, ID_MT29F1G01AA, 0, 0}, + .chip_page_size = 2048, + .chip_page_shift = 11, + .chip_block_size = 64 * 2048, // 64 pages per block x chip_page_size + .chip_block_shift = 17, // block size of 0x20000 (128KB) + .chip_spare_size = 64, + .chip_ecc_offset = 0x800, // location of ECC bytes + .chip_num_blocks = 1024, // 1024 blocks total + .chip_num_planes = 1, + .chip_die_sel = 0, + .chip_total_size = 64 * 2048 * 1024, // chip_block_size x chip_num_blocks + .ecclayout = &spinand_oob_micron_aa, + .chip_ecc = 4, // ECC bits + .chip_ecc_corr = 3, // threshold to fix correctable bits (3/4) + .chip_ecc_enh = 0, // enhanced bad bit detection by chip (none) + .chip_subpage_shift = 2, // 2^ shift amount based on number of subpages (4) + .chip_clock_speed = 50000000, + }, + { // 2Gb + .chip_name = "Micron MT29F2G01AA", + .chip_device_id = {MICRONPART, ID_MT29F2G01AA, 0, 0}, + .chip_page_size = 2048, + .chip_page_shift = 11, + .chip_block_size = 64 * 2048, // 64 pages per block x chip_page_size + .chip_block_shift = 17, // block size of 0x20000 (128KB) + .chip_spare_size = 64, + .chip_ecc_offset = 0x800, // location of ECC bytes + .chip_num_blocks = 2048, // 2048 blocks total + .chip_num_planes = 2, + .chip_die_sel = 0, + .chip_total_size = 64 * 2048 * 2048, // chip_block_size x chip_num_blocks + .ecclayout = &spinand_oob_micron_aa, + .chip_ecc = 4, // ECC bits + .chip_ecc_corr = 3, // threshold to fix correctable bits (3/4) + .chip_ecc_enh = 0, // enhanced bad bit detection by chip (none) + .chip_subpage_shift = 2, // 2^ shift amount based on number of subpages (4) + .chip_clock_speed = 50000000, + }, + { // 4Gb + .chip_name = "Micron MT29F4G01AA", + .chip_device_id = {MICRONPART, ID_MT29F4G01AA, 0, 0}, + .chip_page_size = 2048, + .chip_page_shift = 11, + .chip_block_size = 64 * 2048, // 64 pages per block x chip_page_size + .chip_block_shift = 17, // block size of 0x20000 (128KB) + .chip_spare_size = 64, + .chip_ecc_offset = 0x800, // location of ECC bytes + .chip_num_blocks = 4096, // 4096 blocks total + .chip_num_planes = 2, + .chip_die_sel = 0, + .chip_total_size = 64 * 2048 * 4096, // chip_block_size x chip_num_blocks + .ecclayout = &spinand_oob_micron_aa, + .chip_ecc = 4, // ECC bits + .chip_ecc_corr = 3, // threshold to fix correctable bits (3/4) + .chip_ecc_enh = 0, // enhanced bad bit detection by chip (none) + .chip_subpage_shift = 2, // 2^ shift amount based on number of subpages (4) + .chip_clock_speed = 50000000, + }, + { // 1Gb + .chip_name = "Micron MT29F1G01AB", + .chip_device_id = {MICRONPART, ID_MT29F1G01AB, 0, 0}, + .chip_page_size = 2048, + .chip_page_shift = 11, + .chip_block_size = 64 * 2048, // 64 pages per block x chip_page_size + .chip_block_shift = 17, // block size of 0x20000 (128KB) + .chip_spare_size = 128, + .chip_ecc_offset = 0x800, // location of ECC bytes + .chip_num_blocks = 1024, // 1024 blocks total + .chip_num_planes = 1, + .chip_die_sel = 0, + .chip_total_size = 64 * 2048 * 1024, // chip_block_size x chip_num_blocks + .ecclayout = &spinand_oob_toshiba_micron_ab, + .chip_ecc = 8, // ECC bits + .chip_ecc_corr = 6, // threshold to fix correctable bits (3/4) + .chip_ecc_enh = 0, // enhanced bad bit detection by chip (none) + .chip_subpage_shift = 2, // 2^ shift amount based on number of subpages (4) + .chip_clock_speed = 130000000, + }, + { // 2Gb + .chip_name = "Micron MT29F2G01AB", + .chip_device_id = {MICRONPART, ID_MT29F2G01AB, 0, 0}, + .chip_page_size = 2048, + .chip_page_shift = 11, + .chip_block_size = 64 * 2048, // 64 pages per block x chip_page_size + .chip_block_shift = 17, // block size of 0x20000 (128KB) + .chip_spare_size = 128, + .chip_ecc_offset = 0x800, // location of ECC bytes + .chip_num_blocks = 2048, // 2048 blocks total + .chip_num_planes = 2, + .chip_die_sel = 0, + .chip_total_size = 64 * 2048 * 2048, // chip_block_size x chip_num_blocks + .ecclayout = &spinand_oob_toshiba_micron_ab, + .chip_ecc = 8, // ECC bits + .chip_ecc_corr = 6, // threshold to fix correctable bits (6/8) + .chip_ecc_enh = 0, // enhanced bad bit detection by chip (none) + .chip_subpage_shift = 2, // 2^ shift amount based on number of subpages (4) + .chip_clock_speed = 130000000, + }, + { // 4Gb + .chip_name = "Micron MT29F4G01AB", + .chip_device_id = {MICRONPART, ID_MT29F4G01AB, 0, 0}, + .chip_page_size = 4096, + .chip_page_shift = 12, + .chip_block_size = 64 * 4096, // 64 pages per block x chip_page_size + .chip_block_shift = 18, // block size of 0x40000 (256KB) + .chip_spare_size = 256, + .chip_ecc_offset = 0x1000, // location of ECC bytes + .chip_num_blocks = 2048, // 2048 blocks total + .chip_num_planes = 1, + .chip_die_sel = 0x10000000, // 2Gb cutoff + .chip_total_size = 64 * 4096 * 2048, // chip_block_size x chip_num_blocks + .ecclayout = &spinand_oob_toshiba_micron_ab, + .chip_ecc = 8, // ECC bits + .chip_ecc_corr = 6, // threshold to fix correctable bits (6/8) + .chip_ecc_enh = 0, // enhanced bad bit detection by chip (none) + .chip_subpage_shift = 2, // 2^ shift amount based on number of subpages (4) + .chip_clock_speed = 130000000, + }, + { // 4Gb + .chip_name = "Micron MT29F4G01AD", + .chip_device_id = {MICRONPART, ID_MT29F4G01AD, 0, 0}, + .chip_page_size = 2048, + .chip_page_shift = 11, + .chip_block_size = 64 * 2048, // 64 pages per block x chip_page_size + .chip_block_shift = 17, // block size of 0x20000 (128KB) + .chip_spare_size = 128, + .chip_ecc_offset = 0x800, // location of ECC bytes + .chip_num_blocks = 4096, // 4096 blocks total + .chip_num_planes = 2, + .chip_die_sel = 0x10000000, // 2Gb cutoff + .chip_total_size = 64 * 2048 * 4096, // chip_block_size x chip_num_blocks + .ecclayout = &spinand_oob_toshiba_micron_ab, + .chip_ecc = 8, // ECC bits + .chip_ecc_corr = 6, // threshold to fix correctable bits (6/8) + .chip_ecc_enh = 0, // enhanced bad bit detection by chip (none) + .chip_subpage_shift = 2, // 2^ shift amount based on number of subpages (4) + .chip_clock_speed = 130000000, + }, + { // 1Gb + .chip_name = "ESMT F50L1G41A", + .chip_device_id = {ESMTPART, ID_F50L1G41A, 0, 0}, + .chip_page_size = 2048, + .chip_page_shift = 11, + .chip_block_size = 64 * 2048, // 64 pages per block x chip_page_size + .chip_block_shift = 17, // block size of 0x20000 (128KB) + .chip_spare_size = 64, + .chip_ecc_offset = 0x800, // location of ECC bytes + .chip_num_blocks = 1024, // 1024 blocks total + .chip_num_planes = 1, + .chip_die_sel = 0, + .chip_total_size = 64 * 2048 * 1024, // chip_block_size x chip_num_blocks + .ecclayout = &spinand_oob_esmt, + .chip_ecc = 1, // ECC bits + .chip_ecc_corr = 1, // threshold to fix correctable bits (1/1) + .chip_ecc_enh = 0, // enhanced bad bit detection by chip (none) + .chip_subpage_shift = 2, // 2^ shift amount based on number of subpages (4) + .chip_clock_speed = 104000000, + }, + { // 1Gb + .chip_name = "ESMT F50L1G41LB", + .chip_device_id = {ESMTPART, ID_F50L1G41LB, 0, 0}, + .chip_page_size = 2048, + .chip_page_shift = 11, + .chip_block_size = 64 * 2048, // 64 pages per block x chip_page_size + .chip_block_shift = 17, // block size of 0x20000 (128KB) + .chip_spare_size = 64, + .chip_ecc_offset = 0x800, // location of ECC bytes + .chip_num_blocks = 1024, // 1024 blocks total + .chip_num_planes = 1, + .chip_die_sel = 0, + .chip_total_size = 64 * 2048 * 1024, // chip_block_size x chip_num_blocks + .ecclayout = &spinand_oob_esmt2, + .chip_ecc = 1, // ECC bits + .chip_ecc_corr = 1, // threshold to fix correctable bits (1/1) + .chip_ecc_enh = 0, // enhanced bad bit detection by chip (none) + .chip_subpage_shift = 2, // 2^ shift amount based on number of subpages (4) + .chip_clock_speed = 104000000, + }, + { // 2Gb + .chip_name = "ESMT F50L2G41LB", + .chip_device_id = {ESMTPART, ID_F50L2G41LB, 0, 0}, + .chip_page_size = 2048, + .chip_page_shift = 11, + .chip_block_size = 64 * 2048, // 64 pages per block x chip_page_size + .chip_block_shift = 17, // block size of 0x20000 (128KB) + .chip_spare_size = 64, + .chip_ecc_offset = 0x800, // location of ECC bytes + .chip_num_blocks = 2048, // 2048 blocks total + .chip_num_planes = 1, + .chip_die_sel = 0x8000000, // 1Gb cutoff + .chip_total_size = 64 * 2048 * 2048, // chip_block_size x chip_num_blocks + .ecclayout = &spinand_oob_esmt2, + .chip_ecc = 1, // ECC bits + .chip_ecc_corr = 1, // threshold to fix correctable bits (1/1) + .chip_ecc_enh = 0, // enhanced bad bit detection by chip (none) + .chip_subpage_shift = 2, // 2^ shift amount based on number of subpages (4) + .chip_clock_speed = 104000000, + }, + { // 512Mb, please note we do not support contiuous read mode xIG parts, only xIR parts + .chip_name = "Winbond W25N512GV", + .chip_device_id = {WINBONDPART, ID_W25N512GV_1, ID_W25N512GV_2, 0}, + .chip_page_size = 2048, + .chip_page_shift = 11, + .chip_block_size = 64 * 2048, // 64 pages per block x chip_page_size + .chip_block_shift = 17, // block size of 0x20000 (128KB) + .chip_spare_size = 64, + .chip_ecc_offset = 0x800, // location of ECC bytes + .chip_num_blocks = 1024, // 1024 blocks total + .chip_num_planes = 1, + .chip_die_sel = 0, + .chip_total_size = 64 * 2048 * 512, // chip_block_size x chip_num_blocks + .ecclayout = &spinand_oob_micron_aa, + .chip_ecc = 1, // ECC bits + .chip_ecc_corr = 1, // threshold to fix correctable bits (1/1) + .chip_ecc_enh = 0, // enhanced bad bit detection by chip (none) + .chip_subpage_shift = 2, // 2^ shift amount based on number of subpages (4) + .chip_clock_speed = 104000000, + }, + { // 1Gb, please note we do not support contiuous read mode xIG parts, only xIR parts + .chip_name = "Winbond W25N01GV", + .chip_device_id = {WINBONDPART, ID_W25N01GV_1, ID_W25N01GV_2, 0}, + .chip_page_size = 2048, + .chip_page_shift = 11, + .chip_block_size = 64 * 2048, // 64 pages per block x chip_page_size + .chip_block_shift = 17, // block size of 0x20000 (128KB) + .chip_spare_size = 64, + .chip_ecc_offset = 0x800, // location of ECC bytes + .chip_num_blocks = 1024, // 1024 blocks total + .chip_num_planes = 1, + .chip_die_sel = 0, + .chip_total_size = 64 * 2048 * 1024, // chip_block_size x chip_num_blocks + .ecclayout = &spinand_oob_micron_aa, + .chip_ecc = 1, // ECC bits + .chip_ecc_corr = 1, // threshold to fix correctable bits (1/1) + .chip_ecc_enh = 0, // enhanced bad bit detection by chip (none) + .chip_subpage_shift = 2, // 2^ shift amount based on number of subpages (4) + .chip_clock_speed = 104000000, + }, + { // 2Gb, please note we do not support contiuous read mode xIG parts, only xIR parts + .chip_name = "Winbond W25M02GV", + .chip_device_id = {WINBONDPART, ID_W25M02GV_1, ID_W25M02GV_2, 0}, + .chip_page_size = 2048, + .chip_page_shift = 11, + .chip_block_size = 64 * 2048, // 64 pages per block x chip_page_size + .chip_block_shift = 17, // block size of 0x20000 (128KB) + .chip_spare_size = 64, + .chip_ecc_offset = 0x800, // location of ECC bytes + .chip_num_blocks = 2048, // 2048 blocks total + .chip_num_planes = 1, + .chip_die_sel = 0x8000000, // 1Gb cutoff + .chip_total_size = 64 * 2048 * 2048, // chip_block_size x chip_num_blocks + .ecclayout = &spinand_oob_micron_aa, + .chip_ecc = 1, // ECC bits + .chip_ecc_corr = 1, // threshold to fix correctable bits (1/1) + .chip_ecc_enh = 0, // enhanced bad bit detection by chip (none) + .chip_subpage_shift = 2, // 2^ shift amount based on number of subpages (4) + .chip_clock_speed = 104000000, + }, + { // 1Gb + .chip_name = "Macronix MX35LF1GE4", + .chip_device_id = {MACRONIXPART, ID_MX35LF1GE4, 0, 0}, + .chip_page_size = 2048, + .chip_page_shift = 11, + .chip_block_size = 64 * 2048, // 64 pages per block x chip_page_size + .chip_block_shift = 17, // block size of 0x20000 (128KB) + .chip_spare_size = 64, + .chip_ecc_offset = 0x800, // location of ECC bytes + .chip_num_blocks = 1024, // 1024 blocks total + .chip_num_planes = 1, + .chip_die_sel = 0, + .chip_total_size = 64 * 2048 * 1024, // chip_block_size x chip_num_blocks + .ecclayout = &spinand_oob_mxic, + .chip_ecc = 4, // ECC bits + .chip_ecc_corr = 3, // threshold to fix correctable bits (3/4) + .chip_ecc_enh = 1, // enhanced bad bit detection by chip + .chip_subpage_shift = 2, // 2^ shift amount based on number of subpages (4) + .chip_clock_speed = 104000000, + }, + { // 2Gb + .chip_name = "Macronix MX35LF2GE4", + .chip_device_id = {MACRONIXPART, ID_MX35LF2GE4, 0, 0}, + .chip_page_size = 2048, + .chip_page_shift = 11, + .chip_block_size = 64 * 2048, // 64 pages per block x chip_page_size + .chip_block_shift = 17, // block size of 0x20000 (128KB) + .chip_spare_size = 64, + .chip_ecc_offset = 0x800, // location of ECC bytes + .chip_num_blocks = 2048, // 2048 blocks total + .chip_num_planes = 1, + .chip_die_sel = 0, + .chip_total_size = 64 * 2048 * 2048, // chip_block_size x chip_num_blocks + .ecclayout = &spinand_oob_mxic, + .chip_ecc = 4, // ECC bits + .chip_ecc_corr = 3, // threshold to fix correctable bits (3/4) + .chip_ecc_enh = 1, // enhanced bad bit detection by chip + .chip_subpage_shift = 2, // 2^ shift amount based on number of subpages (4) + .chip_clock_speed = 104000000, + }, + { // 1Gb + .chip_name = "Toshiba TC58CVG0S", + .chip_device_id = {TOSHIBAPART, ID_TC58CVG0S, 0, 0}, + .chip_page_size = 2048, + .chip_page_shift = 11, + .chip_block_size = 64 * 2048, // 64 pages per block x chip_page_size + .chip_block_shift = 17, // block size of 0x20000 (128KB) + .chip_spare_size = 128, + .chip_ecc_offset = 0x800, // location of ECC bytes + .chip_num_blocks = 1024, // 1024 blocks total + .chip_num_planes = 1, + .chip_die_sel = 0, + .chip_total_size = 64 * 2048 * 1024, // chip_block_size x chip_num_blocks + .ecclayout = &spinand_oob_toshiba_micron_ab, + .chip_ecc = 8, // ECC bits + .chip_ecc_corr = 6, // threshold to fix correctable bits (6/8) + .chip_ecc_enh = 1, // enhanced bad bit detection by chip, must do this for Toshiba because ECC is not visible when enabled + .chip_subpage_shift = 2, // 2^ shift amount based on number of subpages (4) + .chip_clock_speed = 104000000, + }, + { // 2Gb + .chip_name = "Toshiba TC58CVG1S", + .chip_device_id = {TOSHIBAPART, ID_TC58CVG1S, 0, 0}, + .chip_page_size = 2048, + .chip_page_shift = 11, + .chip_block_size = 64 * 2048, // 64 pages per block x chip_page_size + .chip_block_shift = 17, // block size of 0x20000 (128KB) + .chip_spare_size = 128, + .chip_ecc_offset = 0x800, // location of ECC bytes + .chip_num_blocks = 2048, // 2048 blocks total + .chip_num_planes = 1, + .chip_die_sel = 0, + .chip_total_size = 64 * 2048 * 2048, // chip_block_size x chip_num_blocks + .ecclayout = &spinand_oob_toshiba_micron_ab, + .chip_ecc = 8, // ECC bits + .chip_ecc_corr = 6, // threshold to fix correctable bits (6/8) + .chip_ecc_enh = 1, // enhanced bad bit detection by chip, must do this for Toshiba because ECC is not visible when enabled + .chip_subpage_shift = 2, // 2^ shift amount based on number of subpages (4) + .chip_clock_speed = 104000000, + }, + { // 1Gb + .chip_name = "Etron EM73C044SNB", + .chip_device_id = {ETRONPART, ID_EM73C044SNB, 0, 0}, + .chip_page_size = 2048, + .chip_page_shift = 11, + .chip_block_size = 64 * 2048, // 64 pages per block x chip_page_size + .chip_block_shift = 17, // block size of 0x20000 (128KB) + .chip_spare_size = 128, + .chip_ecc_offset = 0x800, // location of ECC bytes + .chip_num_blocks = 1024, // 1024 blocks total + .chip_num_planes = 1, + .chip_die_sel = 0, + .chip_total_size = 64 * 2048 * 1024, // chip_block_size x chip_num_blocks + .ecclayout = &spinand_oob_etron, + .chip_ecc = 4, // ECC bits + .chip_ecc_corr = 3, // threshold to fix correctable bits (3/4) + .chip_ecc_enh = 0, // enhanced bad bit detection by chip + .chip_subpage_shift = 2, // 2^ shift amount based on number of subpages (4) + .chip_clock_speed = 80000000, + }, + { // 1Gb + .chip_name = "Etron EM73C044VCD", + .chip_device_id = {ETRONPART, ID_EM73C044VCD, 0, 0}, + .chip_page_size = 2048, + .chip_page_shift = 11, + .chip_block_size = 64 * 2048, // 64 pages per block x chip_page_size + .chip_block_shift = 17, // block size of 0x20000 (128KB) + .chip_spare_size = 64, + .chip_ecc_offset = 0x800, // location of ECC bytes + .chip_num_blocks = 1024, // 1024 blocks total + .chip_num_planes = 1, + .chip_die_sel = 0, + .chip_total_size = 64 * 2048 * 1024, // chip_block_size x chip_num_blocks + .ecclayout = &spinand_oob_micron_aa, + .chip_ecc = 4, // ECC bits + .chip_ecc_corr = 3, // threshold to fix correctable bits (3/4) + .chip_ecc_enh = 0, // enhanced bad bit detection by chip + .chip_subpage_shift = 2, // 2^ shift amount based on number of subpages (4) + .chip_clock_speed = 80000000, + }, + { // 2Gb + .chip_name = "Etron EM73D044SNF", + .chip_device_id = {ETRONPART, ID_EM73D044SNF, 0, 0}, + .chip_page_size = 2048, + .chip_page_shift = 11, + .chip_block_size = 64 * 2048, // 64 pages per block x chip_page_size + .chip_block_shift = 17, // block size of 0x20000 (128KB) + .chip_spare_size = 128, + .chip_ecc_offset = 0x800, // location of ECC bytes + .chip_num_blocks = 2048, // 2048 blocks total + .chip_num_planes = 1, + .chip_die_sel = 0, + .chip_total_size = 64 * 2048 * 2048, // chip_block_size x chip_num_blocks + .ecclayout = &spinand_oob_etron2, + .chip_ecc = 4, // ECC bits + .chip_ecc_corr = 3, // threshold to fix correctable bits (3/4) + .chip_ecc_enh = 0, // enhanced bad bit detection by chip + .chip_subpage_shift = 2, // 2^ shift amount based on number of subpages (4) + .chip_clock_speed = 80000000, + }, + { // 2Gb + .chip_name = "Etron EM73D044VCG", + .chip_device_id = {ETRONPART, ID_EM73D044VCG, 0, 0}, + .chip_page_size = 2048, + .chip_page_shift = 11, + .chip_block_size = 64 * 2048, // 64 pages per block x chip_page_size + .chip_block_shift = 17, // block size of 0x20000 (128KB) + .chip_spare_size = 64, + .chip_ecc_offset = 0x800, // location of ECC bytes + .chip_num_blocks = 2048, // 2048 blocks total + .chip_num_planes = 1, + .chip_die_sel = 0, + .chip_total_size = 64 * 2048 * 2048, // chip_block_size x chip_num_blocks + .ecclayout = &spinand_oob_micron_aa, + .chip_ecc = 4, // ECC bits + .chip_ecc_corr = 3, // threshold to fix correctable bits (3/4) + .chip_ecc_enh = 0, // enhanced bad bit detection by chip + .chip_subpage_shift = 2, // 2^ shift amount based on number of subpages (4) + .chip_clock_speed = 80000000, + }, + { // 1Gb + .chip_name = "XTX XT26G01A", // fix extended ECC + .chip_device_id = {XTXPART, ID_XT26G01A, 0, 0}, + .chip_page_size = 2048, + .chip_page_shift = 11, + .chip_block_size = 64 * 2048, // 64 pages per block x chip_page_size + .chip_block_shift = 17, // block size of 0x20000 (128KB) + .chip_spare_size = 64, + .chip_ecc_offset = 0x800, // location of ECC bytes + .chip_num_blocks = 1024, // 1024 blocks total + .chip_num_planes = 1, + .chip_die_sel = 0, + .chip_total_size = 64 * 2048 * 1024, // chip_block_size x chip_num_blocks + .ecclayout = &spinand_oob_xtx, + .chip_ecc = 4, // ECC bits + .chip_ecc_corr = 3, // threshold to fix correctable bits (3/4) + .chip_ecc_enh = 0, // enhanced bad bit detection by chip + .chip_subpage_shift = 2, // 2^ shift amount based on number of subpages (4) + .chip_clock_speed = 90000000, + }, + { // 2Gb + .chip_name = "XTX XT26G02A", + .chip_device_id = {XTXPART, ID_XT26G02A, 0, 0}, + .chip_page_size = 2048, + .chip_page_shift = 11, + .chip_block_size = 64 * 2048, // 64 pages per block x chip_page_size + .chip_block_shift = 17, // block size of 0x20000 (128KB) + .chip_spare_size = 64, + .chip_ecc_offset = 0x800, // location of ECC bytes + .chip_num_blocks = 2048, // 2048 blocks total + .chip_num_planes = 1, + .chip_die_sel = 0, + .chip_total_size = 64 * 2048 * 2048, // chip_block_size x chip_num_blocks + .ecclayout = &spinand_oob_xtx, + .chip_ecc = 8, // ECC bits + .chip_ecc_corr = 6, // threshold to fix correctable bits (6/8) + .chip_ecc_enh = 0, // enhanced bad bit detection by chip + .chip_subpage_shift = 2, // 2^ shift amount based on number of subpages (4) + .chip_clock_speed = 90000000, + }, + { // 1Gb + .chip_name = "FM 25S01", + .chip_device_id = {FMPART, ID_FM25S01, 0, 0}, + .chip_page_size = 2048, + .chip_page_shift = 11, + .chip_block_size = 64 * 2048, // 64 pages per block x chip_page_size + .chip_block_shift = 17, // block size of 0x20000 (128KB) + .chip_spare_size = 128, + .chip_ecc_offset = 0x800, // location of ECC bytes + .chip_num_blocks = 1024, // 1024 blocks total + .chip_num_planes = 1, + .chip_die_sel = 0, + .chip_total_size = 64 * 2048 * 1024, // chip_block_size x chip_num_blocks + .ecclayout = &spinand_oob_etron, + .chip_ecc = 4, // ECC bits + .chip_ecc_corr = 3, // threshold to fix correctable bits (3/4) + .chip_ecc_enh = 0, // enhanced bad bit detection by chip + .chip_subpage_shift = 2, // 2^ shift amount based on number of subpages (4) + .chip_clock_speed = 104000000, + }, + { // 1Gb + .chip_name = "!!! UNKNOWN !!! Default mode", + .chip_device_id = {0, 0, 0, 0}, + .chip_page_size = 2048, + .chip_page_shift = 11, + .chip_block_size = 64 * 2048, // 64 pages per block x chip_page_size + .chip_block_shift = 17, // block size of 0x20000 (128KB) + .chip_spare_size = 64, + .chip_ecc_offset = 0x800, // location of ECC bytes + .chip_num_blocks = 1024, // 1024 blocks total + .chip_num_planes = 1, + .chip_die_sel = 0, + .chip_total_size = 64 * 2048 * 1024, // chip_block_size x chip_num_blocks + .ecclayout = &spinand_oob_micron_aa, + .chip_ecc = 1, // ECC bits + .chip_ecc_corr = 1, // threshold to fix correctable bits (1/?) + .chip_ecc_enh = 0, // enhanced bad bit detection by chip (none) + .chip_subpage_shift = 2, // 2^ shift amount based on number of subpages (4) + .chip_clock_speed = 50000000, + } +}; + + +static struct spi_device * pSpiDevice; // handle for SPI NAND device + +static unsigned char * pageBuf; +static unsigned int pageBufI; +static int pageAddr, pageOffset; +static int status = STATUS_DEFAULT; +static bool SpiNandDeviceRegistered = 0; + +/** Prototypes. **/ +static int spi_nand_read_page(unsigned long page_addr, unsigned int page_offset, unsigned char *buffer, int len); +static int spi_nand_write_page(unsigned long page_addr, unsigned int page_offset, unsigned char *buffer, int len); +static int spi_nand_is_blk_bad(unsigned long addr); +static int spi_nand_mark_blk_bad(unsigned long addr); +static int spi_nand_write_enable(void); +static int spi_nand_write_disable(void); +static void spi_nand_row_addr(unsigned int page_addr, unsigned char* buf); +static void spi_nand_col_addr(unsigned int page_addr, unsigned int page_offset, unsigned char* buf); +static void spi_nand_get_device_id(unsigned char * buf, unsigned int len); +static int spi_nand_wel(void); + +static int spiRead( struct spi_transfer *xfer ); +static int spiWrite( unsigned char *msg_buf, int nbytes ); +static void spi_nand_read_cfg(void); + +static int spi_nand_device_reset(void); +static int spi_nand_status(void); +static int spi_nand_ready(void); +static int spi_nand_ecc(void); +static int spi_nand_sector_erase_int(unsigned long addr); + +static int spi_nand_get_cmd(unsigned char command, unsigned char feat_addr); +static void spi_nand_set_feat(unsigned char feat_addr, unsigned char feat_val); + +static void bcm63xx_cmd(struct mtd_info *mtd, unsigned int command, int column, int page); +static unsigned char bcm63xx_read_byte(struct mtd_info *mtd); +static void bcm63xx_read_buf(struct mtd_info *mtd, uint8_t *buf, int len); +static void bcm63xx_write(struct mtd_info *mtd, const uint8_t *buf, int len); +static int bcm63xx_status(struct mtd_info *mtd, struct nand_chip *chip); +static int bcm63xx_block_isbad(struct mtd_info *mtd, loff_t ofs, int getchip); +static int bcm63xx_block_markbad(struct mtd_info *mtd, loff_t ofs); +static void bcm63xx_select(struct mtd_info *mtd, int chip); +static int bcm63xx_scan_bbt(struct mtd_info *mtd); + + +static int spiRead(struct spi_transfer *xfer) +{ + if (!SpiNandDeviceRegistered) + { + printk("ERROR!! SPI NAND read without SPI NAND Linux device registration\n"); + return(0); + } + + { + struct spi_message message; + + spi_message_init(&message); + spi_message_add_tail(xfer, &message); +#if defined(CONFIG_SPI_BCM63XX_HSSPI) + return(spi_sync(pSpiDevice, &message)); +#else + /* the controller does not support asynchronous transfer, + when spi_async returns the transfer will be complete + don't use spi_sync (to avoid the call to schedule), + scheduling will conflict with atomic operations + such as writing image from Linux */ + return(spi_async(pSpiDevice, &message)); +#endif + } +} + + +static int spiWrite(unsigned char *msg_buf, int nbytes) +{ + if (!SpiNandDeviceRegistered) + { + printk("ERROR!! SPI NAND write without SPI NAND Linux device registration\n"); + return(0); + } + + { + struct spi_message message; + struct spi_transfer xfer; + + spi_message_init(&message); + memset(&xfer, 0, (sizeof xfer)); + xfer.prepend_cnt = 0; + xfer.len = nbytes; + xfer.speed_hz = spi_flash_clock; + xfer.rx_buf = NULL; + xfer.tx_buf = msg_buf; + + spi_message_add_tail(&xfer, &message); + +#if defined(CONFIG_SPI_BCM63XX_HSSPI) + return(spi_sync(pSpiDevice, &message)); +#else + /* the controller does not support asynchronous transfer + when spi_async returns the transfer will be complete + don't use spi_sync to avoid the call to schedule */ + return(spi_async(pSpiDevice, &message)); +#endif + } +} + +static void spi_nand_read_cfg(void) +{ // search through SPI NAND devices to find match + unsigned char buf[SPI_NAND_ID_LENGTH]; + int i = 0; + + spi_nand_get_device_id(buf, SPI_NAND_ID_LENGTH); + + do + { + if ((SpiDevInfo[i].chip_device_id[0] == buf[0]) && + (SpiDevInfo[i].chip_device_id[1] == buf[1]) && + ((SpiDevInfo[i].chip_device_id[2] == 0) || (SpiDevInfo[i].chip_device_id[2] == buf[2])) && + ((SpiDevInfo[i].chip_device_id[3] == 0) || (SpiDevInfo[i].chip_device_id[3] == buf[3]))) + break; + i++; + } while((SpiDevInfo[i].chip_device_id[0] != 0) || (SpiDevInfo[i].chip_device_id[1] != 0)); + + pchip = &SpiDevInfo[i]; + + if ((pchip->chip_device_id[0] == 0) && (pchip->chip_device_id[1] == 0)) + { + printk("ERROR!!! Unsupported SPI NAND device ID = 0x"); + for (i = 0; i < SPI_NAND_ID_LENGTH; i++) + printk("%x", pchip->chip_device_id[i]); + + printk(", halting system\n"); + while(1) ; + } + + memcpy(pchip->chip_device_id, buf, SPI_NAND_ID_LENGTH); +} + +/***********************************************************************/ +/* reset SPI NAND device and get configuration information */ +/* some devices such as Micron MT29F1G01 require explicit reset before */ +/* access to the device. */ +/***********************************************************************/ +static int spi_nand_device_reset(void) +{ + unsigned char buf[4]; + unsigned int i; + if(kerSysIsIkosBootSet() == 1) + { + for( i = 0; i < 250; i++); + } + else + udelay(300); + if (!spin_is_locked(&chip_lock)) // show status only if initial reset since Linux NAND code resets chip during every block erase + printk("SPI NAND device reset\n"); + buf[0] = FLASH_RESET; + spiWrite(buf, 1); + + if(kerSysIsIkosBootSet() == 1) { + for( i = 0; i < 3000; i++); + } + else + { + /* device is availabe after 5ms */ + mdelay(5); + } + while(!spi_nand_ready()); // do we need this here?? + + spi_nand_set_feat(FEATURE_PROT_ADDR, FEAT_DISABLE); // disable block locking + + spi_nand_read_cfg(); + + return(FLASH_API_OK); +} + +/*****************************************************************************************/ +/* row address is 24 bit length. so buf must be at least 3 bytes. */ +/* For gigadevcie GD5F1GQ4 part(2K page size, 64 page per block and 1024 blocks) */ +/* Row Address. RA<5:0> selects a page inside a block, and RA<15:6> selects a block and */ +/* first byte is dummy byte */ +/*****************************************************************************************/ +static void spi_nand_row_addr(unsigned int page_addr, unsigned char* buf) +{ + buf[0] = (unsigned char)(page_addr>>(pchip->chip_page_shift+16)); //dummy byte + buf[1] = (unsigned char)(page_addr>>(pchip->chip_page_shift+8)); + buf[2] = (unsigned char)(page_addr>>(pchip->chip_page_shift)); + + return; +} + +/*********************************************************************************************************************/ +/* column address select the offset within the page. For gigadevcie GD5F1GQ4 part(2K page size and 2112 with spare) */ +/* is 12 bit length. so buf must be at least 2 bytes. The 12 bit address is capable of address from 0 to 4095 bytes */ +/* however only byte 0 to 2111 are valid. */ +/*********************************************************************************************************************/ +static void spi_nand_col_addr(unsigned int page_addr, unsigned int page_offset, unsigned char* buf) +{ + page_offset = page_offset&((1<<(pchip->chip_page_shift+1))-1); /* page size + spare area size */ + + /* the upper 4 bits of buf[0] is either wrap bits for gigadevice or dummy bit[3:1] + plane select bit[0] for micron + */ + if( pchip->chip_num_planes > 1 ) + { + /* setup plane bit if more than one plane. otherwise that bit is always 0 */ + buf[0] = (unsigned char)(((page_offset>>8)&0xf)|((page_addr>>pchip->chip_block_shift)&0x1)<<4); //plane bit is the first bit of the block number RowAddr[6] + } + else + { + /* use default wrap option 0, wrap length 2112 */ + buf[0] = (unsigned char)((page_offset>>8)&0xFF); + } + buf[1] = (unsigned char)(page_offset&0xFF); + + return; +} + +/*************************************************************************** + * Function Name: spi_xfr + * Description : Commonly used SPI transfer function. + * Returns : nothing + ***************************************************************************/ +static void spi_xfr(unsigned long page_addr, unsigned int page_offset, unsigned char *buffer, int len) +{ + int maxread; + unsigned char buf[4]; + struct spi_transfer xfer; + + while (len > 0) + { // break up NAND buffer read into SPI buffer sized chunks + /* Random data read (0Bh or 03h) command to read the page data from the cache + The RANDOM DATA READ command requires 4 dummy bits, followed by a 12-bit column + address for the starting byte address and a dummy byte for waiting data. + This is only for 2K page size, the format will change for other page size. + */ + + maxread = (len < spi_max_op_len) ? len : spi_max_op_len; + + buf[0] = FLASH_READ; + spi_nand_col_addr(page_addr, page_offset, buf+1); + buf[3] = 0; //dummy byte + + if ((page_offset < pchip->chip_page_size) && ((maxread + page_offset) > pchip->chip_page_size)) + maxread = pchip->chip_page_size - page_offset; // snap address to OOB boundary to let chip know we want OOB + + if ((page_offset < pchip->chip_ecc_offset) && ((maxread + page_offset) > pchip->chip_ecc_offset)) + maxread = pchip->chip_ecc_offset - page_offset; // snap address to ECC boundary to let chip know we want ECC + + DBG_PRINTF("spi_xfr - spi cmd 0x%x, 0x%x, 0x%x, 0x%x\n", buf[0],buf[1],buf[2],buf[3]); + DBG_PRINTF("spi_xfr - spi read len 0x%x, offset 0x%x, remaining 0x%x\n", maxread, page_offset, len); + + memset(&xfer, 0, sizeof(struct spi_transfer)); + xfer.tx_buf = buf; + xfer.rx_buf = buffer; + xfer.len = maxread; + xfer.speed_hz = spi_flash_clock; + xfer.prepend_cnt = 4; + xfer.addr_len = 3; // length of address field (max 4 bytes) + xfer.addr_offset = 1; // offset of first addr byte in header + xfer.hdr_len = 4; // length of header + xfer.unit_size = 1; // data for each transfer will be divided into multiples of unit_size + spiRead(&xfer); + while (!spi_nand_ready()); + + buffer += maxread; + len -= maxread; + page_offset += maxread; + } +} + +#if defined(COUNT_BAD_BITS) +/*************************************************************************** + * Function Name: count_bits + * Description : Counts the bit differences between two buffers. + * Returns : Bit difference count + ***************************************************************************/ +static int count_bits(unsigned char * buf1, unsigned char * buf2, int len) +{ + int i, count = 0; + unsigned char hold; + + for(i = 0; i < len; i++) + { + hold = buf1[i] ^ buf2[i]; + while(hold) + { + hold &= (hold-1); + count++; + } + } + + return(count); +} +#endif // COUNT_BAD_BITS +/*************************************************************************** + * Function Name: spi_nand_read_page + * Description : Reads up to a NAND block of pages into the specified buffer. + * Returns : FLASH_API_OK or FLASH_API_ERROR or FLASH_API_CORR + ***************************************************************************/ +int bit_flips; + +static int spi_nand_read_page(unsigned long page_addr, unsigned int page_offset, unsigned char *buffer, int len) +{ + int status = FLASH_API_OK; + unsigned char buf[4]; + + bit_flips = 0; + + if ((page_offset + len) > (pchip->chip_page_size + pchip->chip_spare_size)) // check to see if reading within page/OOB boundary + { + printk("spi_nand_read_page(): Attempt to read past page boundary, offset 0x%x, length 0x%x, into page address 0x%x\n", page_offset, len, (unsigned int)page_addr); + return (FLASH_API_ERROR); + } + + if (pchip->chip_die_sel) + { + if ( (pchip->chip_device_id[0] == WINBONDPART) || (pchip->chip_device_id[0] == ESMTPART) ) + { + while(!spi_nand_ready()); + + buf[0] = FLASH_DIE_SELECT; + buf[1] = (pchip->chip_die_sel & page_addr) ? 1 : 0; + spiWrite(buf, 2); + } + + if (pchip->chip_device_id[0] == MICRONPART) + spi_nand_set_feat(FEATURE_DIE_SEL, (pchip->chip_die_sel & page_addr) ? FEAT_OPT_EN : FEAT_DISABLE); + } + + if (len != 1 || (page_offset != pchip->chip_page_size)) + spi_nand_set_feat(FEATURE_FEAT_ADDR, FEAT_ECC_EN); // not reading from bad block marker, enable ECC and even if there's a failure should still fill buffer + else + spi_nand_set_feat(FEATURE_FEAT_ADDR, FEAT_DISABLE); // else reading bad block marker so don't enable ECC + + /* The PAGE READ (13h) command transfers the data from the NAND Flash array to the + * cache register. The PAGE READ command requires a 24-bit address consisting of + * 8 dummy bits followed by a 16-bit block/page address. + */ + buf[0] = FLASH_PREAD; + spi_nand_row_addr(page_addr, buf+1); + DBG_PRINTF("spi_nand_read_page - spi cmd 0x%x, 0x%x, 0x%x, 0x%x\n", buf[0], buf[1], buf[2], buf[3]); + spiWrite(buf, 4); + + /* GET FEATURES (0Fh) command to read the status */ + while(!spi_nand_ready()); + + if (page_offset < pchip->chip_page_size) + status = spi_nand_ecc(); + + if (!len) + return(status); + + spi_xfr(page_addr, page_offset, buffer, len); + + if(status == FLASH_API_CORR) + { // count bad bits to see if we exceed threshold + bit_flips = 1; // there's at least 1 bit flip when we have a correctable status + + if(pchip->chip_ecc_enh) + { // chip has enhanced bad bit detection + if (pchip->chip_device_id[0] == GIGADEVPART) + bit_flips = ((spi_nand_get_cmd(FLASH_GFEAT, FEATURE_STAT_AUX) & STAT_ECC_MASK1) >> 4) | 0x4; + else if (pchip->chip_device_id[0] == MACRONIXPART) + bit_flips = spi_nand_get_cmd(FLASH_SREAD, 0) & STAT_ECC_MASK2; + else if (pchip->chip_device_id[0] == TOSHIBAPART) + bit_flips = (spi_nand_get_cmd(FLASH_GFEAT, FEATURE_STAT_ENH) & STAT_ECC_MASK3) >> 4; + } +#if defined(COUNT_BAD_BITS) + else if (pchip->chip_ecc_corr != 1) // If correctable status and threshold is 1 bit no need to check we are already at correctable reporting threshold + { + unsigned char *buf_ecc = kmalloc(sizeof(unsigned char) * (pchip->chip_page_size + pchip->chip_spare_size), GFP_ATOMIC); + unsigned char *buf_noecc = kmalloc(sizeof(unsigned char) * (pchip->chip_page_size + pchip->chip_spare_size), GFP_ATOMIC); + int i, size, count; + + if((NULL == buf_ecc) || (NULL == buf_noecc)) + { + printk("spi_nand_read_page(): kmalloc failed to allocate memory\n"); + + if(buf_ecc) + kfree(buf_ecc); + + if(buf_noecc) + kfree(buf_noecc); + + return (-ENOMEM); + } + + spi_xfr(page_addr, 0, buf_ecc, pchip->chip_page_size + pchip->chip_spare_size); + + spi_nand_set_feat(FEATURE_FEAT_ADDR, FEAT_DISABLE); // now grab data with ecc turned off + + /* The PAGE READ (13h) command transfers the data from the NAND Flash array to the + * cache register. The PAGE READ command requires a 24-bit address consisting of + * 8 dummy bits followed by a 16-bit block/page address. + */ + buf[0] = FLASH_PREAD; + spi_nand_row_addr(page_addr, buf+1); + spiWrite(buf, 4); + + while(!spi_nand_ready()); + + spi_xfr(page_addr, 0, buf_noecc, pchip->chip_page_size + pchip->chip_spare_size); + + for(i = 0; i < (1 << pchip->chip_subpage_shift); i++) + { + count = 0; + + size = pchip->chip_page_size >> pchip->chip_subpage_shift; + count += count_bits(buf_ecc + (size * i), buf_noecc + (size * i), size); + + size = (pchip->chip_spare_size - (pchip->chip_ecc_offset - pchip->chip_page_size)) >> pchip->chip_subpage_shift; + count += count_bits(buf_ecc + pchip->chip_page_size + (size * i), buf_noecc + pchip->chip_page_size + (size * i), size); + + if(pchip->chip_page_size != pchip->chip_ecc_offset) + count += count_bits(buf_ecc + pchip->chip_ecc_offset + (size * i), buf_noecc + pchip->chip_ecc_offset + (size * i), size); + + if (count > bit_flips) + bit_flips = count; + } + + kfree(buf_ecc); + kfree(buf_noecc); + } +#endif // COUNT_BAD_BITS + + if (bit_flips < pchip->chip_ecc_corr) + status = FLASH_API_OK; + + } + + return(status); +} + +/*********************************************************************/ +/* Flash_status return the feature status byte */ +/*********************************************************************/ +static int spi_nand_status(void) +{ + return spi_nand_get_cmd(FLASH_GFEAT, FEATURE_STAT_ADDR); +} + +/* check device ready bit */ +static int spi_nand_ready(void) +{ + return (spi_nand_status()&STAT_OIP) ? 0 : 1; +} + +/*********************************************************************/ +/* spi_nand_get_cmd return the resultant byte at command, address */ +/*********************************************************************/ +static int spi_nand_get_cmd(unsigned char command, unsigned char feat_addr) +{ + unsigned char buf[4]; + struct spi_transfer xfer; + + /* check device is ready */ + memset(&xfer, 0, sizeof(struct spi_transfer)); + + buf[0] = command; + buf[1] = feat_addr; + xfer.tx_buf = buf; + xfer.rx_buf = buf; + xfer.len = 1; + xfer.speed_hz = spi_flash_clock; + xfer.prepend_cnt = 2; + spiRead(&xfer); + + DBG_PRINTF("spi_nand_get_cmd at 0x%x 0x%x\n", feat_addr, buf[0]); + + return buf[0]; +} + +/*********************************************************************/ +/* spi_nand_set_feat set the feature byte at feat_addr */ +/*********************************************************************/ +static void spi_nand_set_feat(unsigned char feat_addr, unsigned char feat_val) +{ + unsigned char buf[3]; + + /* check device is ready */ + while(!spi_nand_ready()); + + buf[0] = FLASH_SFEAT; + buf[1] = feat_addr; + buf[2] = feat_val; + spiWrite(buf, 3); + + return; +} + +static int spi_nand_ecc(void) +{ + int status; + + status = spi_nand_get_cmd(FLASH_GFEAT, FEATURE_STAT_ADDR); + status = status & STAT_ECC_MASK1; + + if (status == STAT_ECC_GOOD) + return(FLASH_API_OK); + + if (status == STAT_ECC_UNCORR) // uncorrectable errors + return(FLASH_API_ERROR); + + return(FLASH_API_CORR); // everything else is correctable +} + +/*********************************************************************/ +/* Flash_sector__int() wait until the erase is completed before */ +/* returning control to the calling function. This can be used in */ +/* cases which require the program to hold until a sector is erased, */ +/* without adding the wait check external to this function. */ +/*********************************************************************/ +static int spi_nand_sector_erase_int(unsigned long addr) +{ + unsigned char buf[11]; + int status; + + addr &= ~(pchip->chip_block_size - 1); + + DBG_PRINTF("spi_nand_sector_erase_int block at address 0x%lx\n", addr); + + if (spi_nand_is_blk_bad(addr)) + { + printk("spi_nand_sector_erase_int(): Attempt to erase failed due to bad block 0x%lx (address 0x%lx)\n", addr >> pchip->chip_block_shift, addr); + return (FLASH_API_ERROR); + } + + if (pchip->chip_die_sel) + { + if ( (pchip->chip_device_id[0] == WINBONDPART) || (pchip->chip_device_id[0] == ESMTPART) ) + { + while(!spi_nand_ready()); + + buf[0] = FLASH_DIE_SELECT; + buf[1] = (pchip->chip_die_sel & addr) ? 1 : 0; + spiWrite(buf, 2); + } + + if (pchip->chip_device_id[0] == MICRONPART) + spi_nand_set_feat(FEATURE_DIE_SEL, (pchip->chip_die_sel & addr) ? FEAT_OPT_EN : FEAT_DISABLE); + } + + { // erase dirty block + spi_nand_write_enable(); + buf[0] = FLASH_BERASE; + spi_nand_row_addr(addr, buf+1); + spiWrite(buf, 4); + while(!spi_nand_ready()) ; + + status = spi_nand_status(); + if( status & STAT_EFAIL ) + { + printk("spi_nand_sector_erase_int(): Erase block 0x%lx failed, sts 0x%x\n", addr >> pchip->chip_block_shift, status); + return(FLASH_API_ERROR); + } + + spi_nand_write_disable(); + } + + return (FLASH_API_OK); +} + +/************************************************************************/ +/* flash_write_enable() must be called before any change to the */ +/* device such as write, erase. It also unlocks the blocks if they were */ +/* previouly locked. */ +/************************************************************************/ +static int spi_nand_write_enable(void) +{ + unsigned char buf[4], prot; + + /* make sure it is not locked first */ + prot = spi_nand_get_cmd(FLASH_GFEAT, FEATURE_PROT_ADDR); + if( prot != 0 ) + { + prot = 0; + spi_nand_set_feat(FEATURE_PROT_ADDR, prot); + } + + /* send write enable cmd and check feature status WEL latch bit */ + buf[0] = FLASH_WREN; + spiWrite(buf, 1); + while(!spi_nand_ready()); + while(!spi_nand_wel()); + + return(FLASH_API_OK); +} + +static int spi_nand_write_disable(void) +{ + unsigned char buf[4]; + + buf[0] = FLASH_WRDI; + spiWrite(buf, 1); + while(!spi_nand_ready()); + while(spi_nand_wel()); + + return(FLASH_API_OK); +} + +/*************************************************************************** + * Function Name: spi_nand_write_page + * Description : Writes up to a NAND block of pages from the specified buffer. + * Returns : FLASH_API_OK or FLASH_API_ERROR + ***************************************************************************/ +static int spi_nand_write_page(unsigned long page_addr, unsigned int page_offset, unsigned char *buffer, int len) +{ + int maxwrite, status; + unsigned int page_ofs = page_offset; + unsigned char *spi_buf = kmalloc(sizeof(unsigned char) * spi_max_op_len, GFP_ATOMIC); /* HS_SPI_BUFFER_LEN SPI controller fifo size is currently 512 bytes */ + size_t xfer_buf_size = sizeof(unsigned char) * (pchip->chip_page_size + pchip->chip_spare_size); + unsigned char *xfer_buf = kmalloc(xfer_buf_size, GFP_ATOMIC); + + if((NULL == spi_buf) || (NULL == xfer_buf)) + { + printk("spi_nand_write_page(): kmalloc failed to allocate memory\n"); + + if(spi_buf) + kfree(spi_buf); + if(xfer_buf) + kfree(xfer_buf); + + return (-ENOMEM); + } + + if (!len) + { + printk("spi_nand_write_page(): Not writing any data to page addr 0x%x, page_offset 0x%x, len 0x%x\n", (unsigned int)page_addr, page_offset, len); + kfree(spi_buf); + kfree(xfer_buf); + return (FLASH_API_OK); + } + + if ((page_offset + len) > (pchip->chip_page_size + pchip->chip_spare_size)) + { + printk("spi_nand_write_page(): Attempt to write past page boundary, offset 0x%x, length 0x%x, into page address 0x%x\n", page_offset, len, (unsigned int)page_addr); + kfree(spi_buf); + kfree(xfer_buf); + return (FLASH_API_ERROR); + } + + if (pchip->chip_die_sel) + { + if ( (pchip->chip_device_id[0] == WINBONDPART) || (pchip->chip_device_id[0] == ESMTPART) ) + { + while(!spi_nand_ready()); + + spi_buf[0] = FLASH_DIE_SELECT; + spi_buf[1] = pchip->chip_die_sel & page_addr ? 1 : 0; + spiWrite(spi_buf, 2); + } + + if (pchip->chip_device_id[0] == MICRONPART) + spi_nand_set_feat(FEATURE_DIE_SEL, (pchip->chip_die_sel & page_addr) ? FEAT_OPT_EN : FEAT_DISABLE); + } + + if (page_ofs < pchip->chip_page_size) + { /* writing into page area, if writing into spare area is allowed then must read page first to fill write buffer + * because we don't know if JFFS2 clean marker is there or not and this clean marker would initially have + * been written with ECC off, but will now be included in the ECC calculation along with the page data */ + spi_nand_set_feat(FEATURE_FEAT_ADDR, FEAT_ECC_EN); // enable ECC if writing to page + } + else + { // not writing into page area + if (len != 1 || (page_ofs != pchip->chip_page_size)) + { + kfree(spi_buf); + kfree(xfer_buf); + return(FLASH_API_OK); // only allowed write is the bad block marker; return if not that + } + + spi_nand_set_feat(FEATURE_FEAT_ADDR, FEAT_DISABLE); // else don't write ECC + } + + memset(xfer_buf, 0xff, xfer_buf_size); + memcpy(xfer_buf + page_offset, buffer, len); + len = pchip->chip_page_size + pchip->chip_spare_size; + page_offset = 0; + + while (len > 0) + { + /* Send Program Load Random Data Command (0x84) to load data to cache register. + * PROGRAM LOAD consists of an 8-bit Op code, followed by 4 bit dummy and a + * 12-bit column address, then the data bytes to be programmed. */ + spi_buf[0] = FLASH_PROG_RAN; + spi_nand_col_addr(page_addr, page_offset, spi_buf + 1); + + maxwrite = (len > (spi_max_op_len - 5)) ? (spi_max_op_len - 5) : len; + + if ((page_offset < pchip->chip_page_size) && ((maxwrite + page_offset) > pchip->chip_page_size)) + maxwrite = pchip->chip_page_size - page_offset; // snap address to OOB boundary to let chip know we want OOB + + if ((page_offset < pchip->chip_ecc_offset) && ((maxwrite + page_offset) > pchip->chip_ecc_offset)) + maxwrite = pchip->chip_ecc_offset - page_offset; // snap address to ECC boundary to let chip know we want ECC + + memcpy(&spi_buf[3], xfer_buf + page_offset, maxwrite); + DBG_PRINTF("spi_nand_write_page - spi cmd 0x%x, 0x%x, 0x%x\n", spi_buf[0], spi_buf[1], spi_buf[2]); + DBG_PRINTF("spi_nand_write_page - spi write len 0x%x, offset 0x%x, remaining 0x%x\n", maxwrite, offset, len-maxwrite); + + spi_nand_write_enable(); + spiWrite(spi_buf, maxwrite + 3); + + len -= maxwrite; + page_offset += maxwrite; + + while(!spi_nand_ready()); // do we need this here?? + } + + /* Send Program Execute command (0x10) to write cache data to memory array + * Send address (24bit): 8 bit dummy + 16 bit address (page/Block) + */ + /* Send Write enable Command (0x06) */ + spi_nand_write_enable(); + + spi_buf[0] = FLASH_PEXEC; + spi_nand_row_addr(page_addr, spi_buf + 1); + DBG_PRINTF("spi_nand_write_page - spi cmd 0x%x, 0x%x, 0x%x, 0x%x\n", spi_buf[0], spi_buf[1], spi_buf[2], spi_buf[3]); + spiWrite(spi_buf, 4); + while(!spi_nand_ready()); + + status = spi_nand_status(); + spi_nand_write_disable(); + + if(status & STAT_PFAIL) + { + printk("Page program failed at address 0x%x, sts 0x%x\n", (unsigned int)page_addr, status); + kfree(spi_buf); + kfree(xfer_buf); + return(FLASH_API_ERROR); + } + + if (page_ofs < pchip->chip_page_size) + { + unsigned char *buf = kmalloc((sizeof(unsigned char) * pchip->chip_page_size), GFP_ATOMIC); + + if(NULL == buf) + { + printk("spi_nand_write_page(): kmalloc failed to allocate memory\n"); + kfree(spi_buf); + kfree(xfer_buf); + return (-ENOMEM); + } + + + status = spi_nand_read_page(page_addr, 0, buf, pchip->chip_page_size); + + if (status == FLASH_API_ERROR) + { + printk("Write verify failed reading back page at address 0x%lx\n", page_addr); + kfree(spi_buf); + kfree(xfer_buf); + kfree(buf); + return(FLASH_API_ERROR); + } + + if (memcmp(xfer_buf, buf, pchip->chip_page_size)) + { + printk("Write data did not match read data at address 0x%lx\n", page_addr); + kfree(spi_buf); + kfree(xfer_buf); + kfree(buf); + return(FLASH_API_ERROR); + } + + if (status == FLASH_API_CORR) + { + printk("Write verify correctable errors at address 0x%lx\n", page_addr); + kfree(spi_buf); + kfree(xfer_buf); + kfree(buf); + return(FLASH_API_CORR); + } + + kfree(buf); + } + + kfree(spi_buf); + kfree(xfer_buf); + return (FLASH_API_OK); +} + +/* check device write enable latch bit */ +static int spi_nand_wel(void) +{ + return (spi_nand_status() & STAT_WEL) ? 1 : 0; +} + +/*********************************************************************/ +/* flash_get_device_id() return the device id of the component. */ +/*********************************************************************/ +static void spi_nand_get_device_id(unsigned char * buf, unsigned int len) +{ + unsigned char buffer[2]; + struct spi_transfer xfer; + + while(!spi_nand_ready()); + + memset(&xfer, 0, sizeof(struct spi_transfer)); + buffer[0] = FLASH_RDID; + buffer[1] = 0; + xfer.tx_buf = buffer; + xfer.rx_buf = buf; + xfer.len = len; + xfer.speed_hz = spi_flash_clock; + xfer.prepend_cnt = 2; + spiRead(&xfer); + while(!spi_nand_ready()); + + DBG_PRINTF("spi_nand_get_device_id 0x%x 0x%x\n", buf[0], buf[1]); +} + +static int spi_nand_is_blk_bad(unsigned long addr) +{ + unsigned char buf; + + if (addr < pchip->chip_block_size) + return 0; // always return good for block 0, because if it's a bad chip quite possibly the board is useless + + addr &= ~(pchip->chip_block_size - 1); + + spi_nand_read_page(addr, pchip->chip_page_size, &buf, 1); + + if (0xFF != buf) + return(1); + + return(0); +} + +static int spi_nand_mark_blk_bad(unsigned long addr) +{ + int ret1, ret2; + + addr &= ~(pchip->chip_block_size - 1); + + printk("Marking block 0x%lx bad (address 0x%lx)\n", addr >> pchip->chip_block_shift, addr); + + ret1 = spi_nand_write_page(addr, pchip->chip_page_size, "\0", 1); // write bad block marker into first page + ret2 = spi_nand_write_page(addr + pchip->chip_page_size, pchip->chip_page_size, "\0", 1); // write bad block marker into second page + + if ((ret1 != FLASH_API_OK) && (ret2 != FLASH_API_OK)) + { + printk("Unable to mark block 0x%lx bad\n", addr >> pchip->chip_block_shift); + return(FLASH_API_ERROR); + } + + return(FLASH_API_OK); +} + +static void bcm63xx_cmd(struct mtd_info *mtd, unsigned int command, int column, int page) +{ + unsigned long addr = page * mtd->writesize; + + spin_lock(&chip_lock); + + switch(command) + { + case NAND_CMD_READ0: + case NAND_CMD_READ1: // step 1/2 for read, execute SPI NAND read command and transfer SPI NAND data to local buffer + + status = STATUS_DEFAULT; + + if (addr > mtd->size) + { + printk("SPI NAND ERROR!! Trying to read past end of chip\n"); + status |= NAND_STATUS_FAIL; + } + else + { + int temp = spi_nand_read_page(page * mtd->writesize, column, pageBuf, mtd->writesize + mtd->oobsize); + + if (FLASH_API_ERROR == temp) + { + //printk("SPI NAND ERROR Reading page!!\n"); + status |= NAND_STATUS_FAIL; + mtd->ecc_stats.failed++; + } + else if (FLASH_API_CORR == temp) + mtd->ecc_stats.corrected++; + + pageBufI = 0; + } + break; + + case NAND_CMD_READOOB: // step 1/2 for read, execute SPI NAND read command and transfer SPI NAND data to local buffer + + status = STATUS_DEFAULT; + + if (addr > mtd->size) + { + printk("SPI NAND ERROR!! Trying to read past end of chip\n"); + status |= NAND_STATUS_FAIL; + } + else + { + int temp = spi_nand_read_page(page * mtd->writesize, mtd->writesize, pageBuf + mtd->writesize, mtd->oobsize); + + if (FLASH_API_ERROR == temp) + { + //printk("SPI NAND ERROR Reading page OOB!!\n"); + status |= NAND_STATUS_FAIL; + mtd->ecc_stats.failed++; + } + else if (FLASH_API_CORR == temp) + mtd->ecc_stats.corrected++; + + pageBufI = mtd->writesize; + } + break; + + case NAND_CMD_RESET: + status = STATUS_DEFAULT; + + if (FLASH_API_ERROR == spi_nand_device_reset()) + { + printk("ERROR resetting SPI NAND device!!\n"); + status |= NAND_STATUS_FAIL; + } + break; + + case NAND_CMD_READID: + status = STATUS_DEFAULT; + + memset(pageBuf, 0, NAND_ID_LENGTH); // spoof the Linux NAND driver for grabbing the device ID so that function nand_get_flash_type in nand_base.c makes a call to the SPI NAND driver via busw = chip->init_size(mtd, chip, id_data) to initialize the NAND parameters + + pageBufI = 0; + break; + + case NAND_CMD_STATUS: // NAND infrastructure only uses this to determine if write protect is set + *(pageBuf + mtd->writesize + mtd->oobsize - 1) = status; + pageBufI = mtd->writesize + mtd->oobsize - 1; // set pointer to end of buffer so we have a limit to the amount of data read + break; + + case NAND_CMD_SEQIN: // step 1/3 for write, capture address + status = STATUS_DEFAULT; + + if (addr > mtd->size) + { + printk("ERROR!! Trying to program past end of chip\n"); + status |= NAND_STATUS_FAIL; + } + else + { + pageAddr = addr; + pageOffset = column; + pageBufI = 0; + } + break; + + case NAND_CMD_PAGEPROG: // step 3/3 for write, transfer local buffer to SPI NAND device and execute SPI NAND write command + { + int error = 0; + + addr = pageAddr & ~(mtd->erasesize - 1); // block address + + if ((status = spi_nand_write_page(pageAddr, pageOffset, pageBuf, pageBufI)) == FLASH_API_ERROR) + error = 1; + + if (!error && (status == FLASH_API_CORR) && (pageAddr >= mtd->erasesize)) + { // read/erase/write block to see if we can get rid of the bit errors, but only if not block zero + int offset; + unsigned char * buffer; + + printk("Correctible errors, SPI NAND Rewriting block\n"); + + buffer = kmalloc(mtd->erasesize, GFP_ATOMIC); + if (!buffer) + { // unfortunately can't attempt to fix block in this case + printk("Error allocating buffer!!\n"); + error = 1; + } + + // read block + for (offset = 0; !error && (offset < mtd->erasesize); offset += mtd->writesize) + { + status = spi_nand_read_page(addr + offset, 0, buffer + offset, mtd->writesize); + if (status == FLASH_API_ERROR) + error = 1; + } + + // erase block + if (!error) + { + status = spi_nand_sector_erase_int(addr); + if (status == FLASH_API_ERROR) + error = 1; + } + + // write block + if (!error) + { + for (offset = 0; offset < mtd->erasesize; offset += mtd->writesize) + { + status = spi_nand_write_page(addr + offset, 0, buffer + offset, mtd->writesize); + if (status != FLASH_API_OK) + error = 1; // essentially failed, but finish writing out all the data anyway to hopefully be recovered later + } + } + + if(buffer) + kfree(buffer); + } + + status = STATUS_DEFAULT; + + if (error) + { + printk("SPI NAND ERROR Writing page!!\n"); + status |= NAND_STATUS_FAIL; + spi_nand_mark_blk_bad(addr); // JFFS2 will do this automatically + } + + break; + } + + case NAND_CMD_ERASE1: + status = STATUS_DEFAULT; + + if (addr >= mtd->size) + { + printk("ERROR!! Trying to erase past end of chip\n"); + status |= NAND_STATUS_FAIL; + } + else if (FLASH_API_ERROR == spi_nand_sector_erase_int(addr)) + { + printk("SPI NAND ERROR Erasing block!!\n"); + status |= NAND_STATUS_FAIL; + } + case NAND_CMD_ERASE2: + break; + + default: + printk("ERROR!! Unkonwn NAND command 0x%x\n", command); + status |= NAND_STATUS_FAIL; + } + + spin_unlock(&chip_lock); +} + +static unsigned char bcm63xx_read_byte(struct mtd_info *mtd) +{ + unsigned char ret; + + spin_lock(&chip_lock); + + ret = *(pageBuf + pageBufI++); + + spin_unlock(&chip_lock); + + return(ret); +} + +static void bcm63xx_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ // step 2/2 for read, read data from local buffer + spin_lock(&chip_lock); + + if ((pageBufI + len) > (mtd->writesize + mtd->oobsize)) + printk("ERROR!! Trying to read past end of buffer\n"); + else + { + memcpy(buf, pageBuf+pageBufI, len); + pageBufI += len; + } + + spin_unlock(&chip_lock); +} + +// step 2/3 for write, fill local buffer +static void bcm63xx_write(struct mtd_info *mtd, const uint8_t *buf, int len) +{ // write to buffer + spin_lock(&chip_lock); + + if ((pageBufI + len) > (mtd->writesize + mtd->oobsize)) + printk("ERROR!! Trying to write past end of buffer\n"); + else + { + memcpy(pageBuf+pageBufI, buf, len); + pageBufI += len; + } + + spin_unlock(&chip_lock); +} + +static int bcm63xx_status(struct mtd_info *mtd, struct nand_chip *chip) +{ // NAND infrastructure used this to not only determine when a command has finished (spinlocks will take care of that) + // but also to return the status + + spin_lock(&chip_lock); + + spin_unlock(&chip_lock); + + return(status); +} + +static int bcm63xx_init_size(struct mtd_info *mtd, struct nand_chip *chip, unsigned char *id_data) +{ // overwrite possibly incorrectly detected values from Linux NAND driver + static int splash = 0; + int i; + + if (!splash) + { + printk("SPI NAND device %s\n", pchip->chip_name); + printk(" device id = 0x"); + for (i = 0; i < SPI_NAND_ID_LENGTH; i++) + printk("%x", pchip->chip_device_id[i]); + printk("\n"); + printk(" ecc = %x/%x bits\n", pchip->chip_ecc_corr, pchip->chip_ecc); + printk(" page size = 0x%x (%d) bytes\n", pchip->chip_page_size, pchip->chip_page_size); + printk(" block size = 0x%x (%d) bytes\n", pchip->chip_block_size, pchip->chip_block_size); + printk(" total blocks = 0x%x (%d)\n", pchip->chip_num_blocks, pchip->chip_num_blocks); + printk(" total size = 0x%lx (%ld) bytes\n", pchip->chip_total_size, pchip->chip_total_size); + + splash = 1; + } + + mtd->writesize = pchip->chip_page_size; + mtd->oobsize = pchip->chip_spare_size; + mtd->erasesize = pchip->chip_block_size; + chip->chipsize = mtd->size = pchip->chip_total_size; + mtd->name = pchip->chip_name; + + /* no MLC for SPINAND yet */ + chip->bits_per_cell = 1; + + chip->numchips = 1; + + return(chip->options & NAND_BUSWIDTH_16); // busw is set to this value in function nand_get_flash_type in nand_base.c, must match part in nand_ids.c +} + +static int bcm63xx_block_isbad(struct mtd_info *mtd, loff_t ofs, int getchip) +{ + int ret; + + spin_lock(&chip_lock); + + ret = spi_nand_is_blk_bad(ofs); + + spin_unlock(&chip_lock); + + return(ret); +} + +static int bcm63xx_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + int ret; + + spin_lock(&chip_lock); + + ret = spi_nand_mark_blk_bad(ofs); + + spin_unlock(&chip_lock); + + return(ret); +} + +static int bcm63xx_ecc_calculate(struct mtd_info *mtd, const unsigned char *buf, unsigned char *code) +{ // dummy function, ecc calculation was already completed during the read + return 0; +} + +static int bcm63xx_bit_flips(struct mtd_info *mtd, uint8_t *p, uint8_t *ecc_code, uint8_t *ecc_calc) +{ + return bit_flips; +} + +static void bcm63xx_select(struct mtd_info *mtd, int chip) +{ // dummy function, chip is always selected as far as the NAND infrastructure is concerned +} + +static int bcm63xx_scan_bbt(struct mtd_info *mtd) +{ + unsigned long addr; + + for (addr = 0; addr < pchip->chip_total_size; addr += pchip->chip_block_size) + if (spi_nand_is_blk_bad(addr)) + printk("Bad Block 0x%lx found (address 0x%lx)\n", addr >> pchip->chip_block_shift, addr); + + return(1); // this will ultimately be the return value for nand_scan +} + +static struct spi_board_info bcmSpiDevInfo = +{ + .modalias = "bcm_SpiDev", + .chip_select = 0, + .max_speed_hz = 781000, + .bus_num = LEG_SPI_BUS_NUM, + .mode = SPI_MODE_3, +}; + +static struct spi_driver bcmSpiDevDrv = +{ + .driver = + { + .name = "bcm_SpiDev", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, +}; + + +void bcmspinand_probe(struct mtd_info * mtd) +{ + struct nand_chip * nand = mtd->priv; + struct spi_master * pSpiMaster; + unsigned int spiCtrlState; + + printk("SPI NAND Device Linux Registration\n"); + + /* micron MT29F1G01 only support up to 50MHz, update to 50Mhz if it is more than that */ + spi_flash_busnum = HS_SPI_BUS_NUM; + spi_flash_clock = 50000000; + + /* retrieve the maximum read/write transaction length from the SPI controller */ + spi_max_op_len = SPI_BUF_LEN; + + /* set the controller state, spi_mode_0 */ + spiCtrlState = (unsigned int)SPI_CONTROLLER_STATE_DEFAULT; + + if ( spi_flash_clock > SPI_CONTROLLER_MAX_SYNC_CLOCK ) + spiCtrlState |= SPI_CONTROLLER_STATE_ASYNC_CLOCK; + + bcmSpiDevInfo.max_speed_hz = spi_flash_clock; + bcmSpiDevInfo.controller_data = (void *)(uintptr_t)spiCtrlState; + bcmSpiDevInfo.mode = SPI_MODE_DEFAULT; + bcmSpiDevInfo.chip_select = SPI_FLASH_SLAVE_DEV_ID; + bcmSpiDevInfo.bus_num = spi_flash_busnum; + + pSpiMaster = spi_busnum_to_master( spi_flash_busnum ); + pSpiDevice = spi_new_device(pSpiMaster, &bcmSpiDevInfo); + + /* register as SPI device */ + spi_register_driver(&bcmSpiDevDrv); + + SpiNandDeviceRegistered = 1; + + printk("SPI NAND Linux Registration\n"); + + spin_lock_init(&chip_lock); + + spi_nand_device_reset(); // reset and set configuration information + +#if !defined(CONFIG_BCM96878) + spi_flash_clock = pchip->chip_clock_speed; /* switch to the max supported clock speed */ +#endif + + nand->ecc.size = pchip->chip_page_size; + nand->ecc.bytes = 0; + nand->ecc.steps = 1; // must set to something otherwise nand_read_page_hwecc won't report ecc flipped bits + nand->ecc.strength = pchip->chip_ecc; + mtd->ecc_strength = pchip->chip_ecc; + mtd->bitflip_threshold = pchip->chip_ecc_corr; + nand->ecc.layout = pchip->ecclayout; + nand->page_shift = pchip->chip_page_shift; + nand->phys_erase_shift = pchip->chip_block_shift; + nand->chipsize = pchip->chip_total_size; + + pageBuf = kmalloc(pchip->chip_page_size + pchip->chip_spare_size, GFP_KERNEL); + + nand->options |= NAND_NO_SUBPAGE_WRITE; // bus width must match part in nand_ids.c + + nand->chip_delay = 0; + nand->read_byte = bcm63xx_read_byte; + nand->read_buf = bcm63xx_read_buf; + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.correct = bcm63xx_bit_flips; + nand->ecc.calculate = bcm63xx_ecc_calculate; + nand->ecc.hwctl = bcm63xx_select; + + nand->select_chip = bcm63xx_select; + nand->write_buf = bcm63xx_write; + nand->scan_bbt = bcm63xx_scan_bbt; + nand->block_bad = bcm63xx_block_isbad; + nand->block_markbad = bcm63xx_block_markbad; + nand->cmdfunc = bcm63xx_cmd; + nand->waitfunc = bcm63xx_status; + + nand->init_size = bcm63xx_init_size; +} + +#endif //CONFIG_BCM_KF_MTD_BCMNAND diff -urN linux-4.1.52-orig/drivers/mtd/nand/brcmnand/bcm63xx_nand.c linux-4.1.52/drivers/mtd/nand/brcmnand/bcm63xx_nand.c --- linux-4.1.52-orig/drivers/mtd/nand/brcmnand/bcm63xx_nand.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/mtd/nand/brcmnand/bcm63xx_nand.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,163 @@ +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +/* +<:copyright-BRCM:2016:GPL/GPL:standard + + Copyright (c) 2016 Broadcom + All Rights Reserved + +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 (the "GPL"). + +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. + + +A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by +writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + +:> +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "brcmnand.h" +#include "board.h" +#include "flash_api.h" +#include "bcm_intr.h" + +struct bcm63xx_nand_soc_priv { + void __iomem *base; +}; + +#if defined(CONFIG_BCM963268) || defined(CONFIG_BCM96838) +#define BCM63XX_NAND_INT_STATUS 0x00 +#define BCM63XX_NAND_INT_STATUS_MASK 0xfff +#define BCM63XX_NAND_INT_STATUS_CTLRDY (0x1<<4) +#define BCM63XX_NAND_INT_EN 0x00 +#define BCM63XX_NAND_INT_EN_CTLRDY (0x1<<20) +#else +#define BCM63XX_NAND_INT_STATUS 0x00 +#define BCM63XX_NAND_INT_STATUS_MASK 0xfff +#define BCM63XX_NAND_INT_STATUS_CTLRDY (0x1<<4) +#define BCM63XX_NAND_INT_EN 0x04 +#define BCM63XX_NAND_INT_EN_CTLRDY (0x1<<4) +#endif + +static bool bcm63xx_nand_intc_ack(struct brcmnand_soc *soc) +{ + struct bcm63xx_nand_soc_priv *priv = soc->priv; + void __iomem *mmio = priv->base + BCM63XX_NAND_INT_STATUS; + u32 val = brcmnand_readl(mmio); + + if (val & BCM63XX_NAND_INT_STATUS_CTLRDY) { +#if defined(CONFIG_BCM963268) || defined(CONFIG_BCM96838) + /* 63268 int enable and status shared the same register. Don't ack any other interrupt */ + val &= ~BCM63XX_NAND_INT_STATUS_MASK; + val |= BCM63XX_NAND_INT_STATUS_CTLRDY; + + brcmnand_writel(val, mmio); +#else + brcmnand_writel(val & ~BCM63XX_NAND_INT_STATUS_CTLRDY, mmio); +#endif +#if !defined(CONFIG_ARM) && !defined(CONFIG_ARM64) + BcmHalInterruptEnable(INTERRUPT_ID_NAND_FLASH); +#endif + return true; + } + + return false; +} + +static void bcm63xx_nand_intc_set(struct brcmnand_soc *soc, bool en) +{ + struct bcm63xx_nand_soc_priv *priv = soc->priv; + void __iomem *mmio = priv->base + BCM63XX_NAND_INT_EN; + u32 val = brcmnand_readl(mmio); + +#if defined(CONFIG_BCM963268) || defined(CONFIG_BCM96838) + /* 63268 int enable and status shared the same register. Don't ack any interrupt */ + val &= ~BCM63XX_NAND_INT_STATUS_MASK; +#endif + if (en) + val |= BCM63XX_NAND_INT_EN_CTLRDY; + else + val &= ~BCM63XX_NAND_INT_EN_CTLRDY; + + brcmnand_writel(val, mmio); +#if !defined(CONFIG_ARM) && !defined(CONFIG_ARM64) + if (en) + BcmHalInterruptEnable(INTERRUPT_ID_NAND_FLASH); + else + BcmHalInterruptDisable(INTERRUPT_ID_NAND_FLASH); +#endif +} + +static int bcm63xx_check_dying_gasp(struct brcmnand_soc *soc) +{ + return kerSysIsDyingGaspTriggered(); +} + +static int bcm63xx_nand_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct bcm63xx_nand_soc_priv *priv; + struct brcmnand_soc *soc; + struct resource *res; + + if (flash_get_flash_type() != FLASH_IFC_NAND) + return -ENODEV; + + soc = devm_kzalloc(dev, sizeof(*soc), GFP_KERNEL); + if (!soc) + return -ENOMEM; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand-int-base"); + priv->base = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + soc->pdev = pdev; + soc->priv = priv; + soc->ctlrdy_ack = bcm63xx_nand_intc_ack; + soc->ctlrdy_set_enabled = bcm63xx_nand_intc_set; + soc->check_dying_gasp = bcm63xx_check_dying_gasp; + + return brcmnand_probe(pdev, soc); +} + +static const struct of_device_id bcm63xx_nand_of_match[] = { + { .compatible = "brcm,nand-bcm63xx" }, + {}, +}; +MODULE_DEVICE_TABLE(of, bcm63xx_nand_of_match); + +static struct platform_driver bcm63xx_nand_driver = { + .probe = bcm63xx_nand_probe, + .remove = brcmnand_remove, + .driver = { + .name = "bcm63xx_nand", + .pm = &brcmnand_pm_ops, + .of_match_table = bcm63xx_nand_of_match, + } +}; +module_platform_driver(bcm63xx_nand_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Brian Norris"); +MODULE_DESCRIPTION("NAND driver for BCM63XX devices"); +#endif diff -urN linux-4.1.52-orig/drivers/mtd/nand/brcmnand/brcmnand.c linux-4.1.52/drivers/mtd/nand/brcmnand/brcmnand.c --- linux-4.1.52-orig/drivers/mtd/nand/brcmnand/brcmnand.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/mtd/nand/brcmnand/brcmnand.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,2843 @@ +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +/* +<:copyright-BRCM:2016:GPL/GPL:standard + + Copyright (c) 2016 Broadcom + All Rights Reserved + +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 (the "GPL"). + +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. + + +A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by +writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + +:> +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "brcmnand.h" +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +#include "bcm_intr.h" +#endif + +/* + * This flag controls if WP stays on between erase/write commands to mitigate + * flash corruption due to power glitches. Values: + * 0: NAND_WP is not used or not available + * 1: NAND_WP is set by default, cleared for erase/write operations + * 2: NAND_WP is always cleared + */ +static int wp_on = 1; +module_param(wp_on, int, 0444); + +/*********************************************************************** + * Definitions + ***********************************************************************/ + +#define DRV_NAME "brcmnand" + +#define CMD_NULL 0x00 +#define CMD_PAGE_READ 0x01 +#define CMD_SPARE_AREA_READ 0x02 +#define CMD_STATUS_READ 0x03 +#define CMD_PROGRAM_PAGE 0x04 +#define CMD_PROGRAM_SPARE_AREA 0x05 +#define CMD_COPY_BACK 0x06 +#define CMD_DEVICE_ID_READ 0x07 +#define CMD_BLOCK_ERASE 0x08 +#define CMD_FLASH_RESET 0x09 +#define CMD_BLOCKS_LOCK 0x0a +#define CMD_BLOCKS_LOCK_DOWN 0x0b +#define CMD_BLOCKS_UNLOCK 0x0c +#define CMD_READ_BLOCKS_LOCK_STATUS 0x0d +#define CMD_PARAMETER_READ 0x0e +#define CMD_PARAMETER_CHANGE_COL 0x0f +#define CMD_LOW_LEVEL_OP 0x10 + +struct brcm_nand_dma_desc { + u32 next_desc; + u32 next_desc_ext; + u32 cmd_irq; + u32 dram_addr; + u32 dram_addr_ext; + u32 tfr_len; + u32 total_len; + u32 flash_addr; + u32 flash_addr_ext; + u32 cs; + u32 pad2[5]; + u32 status_valid; +} __packed; + +/* Bitfields for brcm_nand_dma_desc::status_valid */ +#define FLASH_DMA_ECC_ERROR (1 << 8) +#define FLASH_DMA_CORR_ERROR (1 << 9) + +/* 512B flash cache in the NAND controller HW */ +#define FC_SHIFT 9U +#define FC_BYTES 512U +#define FC_WORDS (FC_BYTES >> 2) + +#define BRCMNAND_MIN_PAGESIZE 512 +#define BRCMNAND_MIN_BLOCKSIZE (8 * 1024) +#define BRCMNAND_MIN_DEVSIZE (4ULL * 1024 * 1024) + +/* Controller feature flags */ +enum { + BRCMNAND_HAS_1K_SECTORS = BIT(0), + BRCMNAND_HAS_PREFETCH = BIT(1), + BRCMNAND_HAS_CACHE_MODE = BIT(2), + BRCMNAND_HAS_WP = BIT(3), +}; + +struct brcmnand_controller { + struct device *dev; + struct nand_hw_control controller; + void __iomem *nand_base; + void __iomem *nand_fc; /* flash cache */ + void __iomem *flash_dma_base; + unsigned int irq; + unsigned int dma_irq; + int nand_version; + + /* Some SoCs provide custom interrupt status register(s) */ + struct brcmnand_soc *soc; + + int cmd_pending; + bool dma_pending; + struct completion done; + struct completion dma_done; +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + /* polling or interrupt for cmd. For DSL chips with no dma use polling + * better throughput */ + int polling; +#endif + + /* List of NAND hosts (one for each chip-select) */ + struct list_head host_list; + + struct brcm_nand_dma_desc *dma_desc; + dma_addr_t dma_pa; + + /* in-memory cache of the FLASH_CACHE, used only for some commands */ + u32 flash_cache[FC_WORDS]; + + /* Controller revision details */ + const u16 *reg_offsets; + unsigned int reg_spacing; /* between CS1, CS2, ... regs */ + const u8 *cs_offsets; /* within each chip-select */ + const u8 *cs0_offsets; /* within CS0, if different */ + unsigned int max_block_size; + const unsigned int *block_sizes; + unsigned int max_page_size; + const unsigned int *page_sizes; + unsigned int max_oob; +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + unsigned int ecc_req_factor; +#endif + u32 features; + + /* for low-power standby/resume only */ + u32 nand_cs_nand_select; + u32 nand_cs_nand_xor; + u32 corr_stat_threshold; + u32 flash_dma_mode; +}; + +struct brcmnand_cfg { + u64 device_size; + unsigned int block_size; + unsigned int page_size; + unsigned int spare_area_size; + unsigned int device_width; + unsigned int col_adr_bytes; + unsigned int blk_adr_bytes; + unsigned int ful_adr_bytes; + unsigned int sector_size_1k; + unsigned int ecc_level; + /* use for low-power standby/resume only */ + u32 acc_control; + u32 config; + u32 config_ext; + u32 timing_1; + u32 timing_2; +}; + +struct brcmnand_host { + struct list_head node; + struct device_node *of_node; + + struct nand_chip chip; + struct mtd_info mtd; + struct platform_device *pdev; + int cs; + + unsigned int last_cmd; + unsigned int last_byte; + u64 last_addr; + struct brcmnand_cfg hwcfg; + struct brcmnand_controller *ctrl; +}; + +enum brcmnand_reg { + BRCMNAND_CMD_START = 0, + BRCMNAND_CMD_EXT_ADDRESS, + BRCMNAND_CMD_ADDRESS, + BRCMNAND_INTFC_STATUS, + BRCMNAND_CS_SELECT, + BRCMNAND_CS_XOR, + BRCMNAND_LL_OP, + BRCMNAND_CS0_BASE, + BRCMNAND_CS1_BASE, /* CS1 regs, if non-contiguous */ + BRCMNAND_CORR_THRESHOLD, + BRCMNAND_CORR_THRESHOLD_EXT, + BRCMNAND_UNCORR_COUNT, + BRCMNAND_CORR_COUNT, + BRCMNAND_CORR_EXT_ADDR, + BRCMNAND_CORR_ADDR, + BRCMNAND_UNCORR_EXT_ADDR, + BRCMNAND_UNCORR_ADDR, + BRCMNAND_SEMAPHORE, + BRCMNAND_ID, + BRCMNAND_ID_EXT, + BRCMNAND_LL_RDATA, + BRCMNAND_OOB_READ_BASE, + BRCMNAND_OOB_READ_10_BASE, /* offset 0x10, if non-contiguous */ + BRCMNAND_OOB_WRITE_BASE, + BRCMNAND_OOB_WRITE_10_BASE, /* offset 0x10, if non-contiguous */ + BRCMNAND_FC_BASE, +}; + +/* BRCMNAND v4.0 */ +static const u16 brcmnand_regs_v40[] = { + [BRCMNAND_CMD_START] = 0x04, + [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, + [BRCMNAND_CMD_ADDRESS] = 0x0c, + [BRCMNAND_INTFC_STATUS] = 0x6c, + [BRCMNAND_CS_SELECT] = 0x14, + [BRCMNAND_CS_XOR] = 0x18, + [BRCMNAND_LL_OP] = 0x178, + [BRCMNAND_CS0_BASE] = 0x40, + [BRCMNAND_CS1_BASE] = 0xd0, + [BRCMNAND_CORR_THRESHOLD] = 0x84, + [BRCMNAND_CORR_THRESHOLD_EXT] = 0, + [BRCMNAND_UNCORR_COUNT] = 0, + [BRCMNAND_CORR_COUNT] = 0, + [BRCMNAND_CORR_EXT_ADDR] = 0x70, + [BRCMNAND_CORR_ADDR] = 0x74, + [BRCMNAND_UNCORR_EXT_ADDR] = 0x78, + [BRCMNAND_UNCORR_ADDR] = 0x7c, + [BRCMNAND_SEMAPHORE] = 0x58, + [BRCMNAND_ID] = 0x60, + [BRCMNAND_ID_EXT] = 0x64, + [BRCMNAND_LL_RDATA] = 0x17c, + [BRCMNAND_OOB_READ_BASE] = 0x20, + [BRCMNAND_OOB_READ_10_BASE] = 0x130, + [BRCMNAND_OOB_WRITE_BASE] = 0x30, + [BRCMNAND_OOB_WRITE_10_BASE] = 0, + [BRCMNAND_FC_BASE] = 0x200, +}; + +/* BRCMNAND v5.0 */ +static const u16 brcmnand_regs_v50[] = { + [BRCMNAND_CMD_START] = 0x04, + [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, + [BRCMNAND_CMD_ADDRESS] = 0x0c, + [BRCMNAND_INTFC_STATUS] = 0x6c, + [BRCMNAND_CS_SELECT] = 0x14, + [BRCMNAND_CS_XOR] = 0x18, + [BRCMNAND_LL_OP] = 0x178, + [BRCMNAND_CS0_BASE] = 0x40, + [BRCMNAND_CS1_BASE] = 0xd0, + [BRCMNAND_CORR_THRESHOLD] = 0x84, + [BRCMNAND_CORR_THRESHOLD_EXT] = 0, + [BRCMNAND_UNCORR_COUNT] = 0, + [BRCMNAND_CORR_COUNT] = 0, + [BRCMNAND_CORR_EXT_ADDR] = 0x70, + [BRCMNAND_CORR_ADDR] = 0x74, + [BRCMNAND_UNCORR_EXT_ADDR] = 0x78, + [BRCMNAND_UNCORR_ADDR] = 0x7c, + [BRCMNAND_SEMAPHORE] = 0x58, + [BRCMNAND_ID] = 0x60, + [BRCMNAND_ID_EXT] = 0x64, + [BRCMNAND_LL_RDATA] = 0x17c, + [BRCMNAND_OOB_READ_BASE] = 0x20, + [BRCMNAND_OOB_READ_10_BASE] = 0x130, + [BRCMNAND_OOB_WRITE_BASE] = 0x30, + [BRCMNAND_OOB_WRITE_10_BASE] = 0x140, + [BRCMNAND_FC_BASE] = 0x200, +}; + +/* BRCMNAND v6.0 - v7.1 */ +static const u16 brcmnand_regs_v60[] = { + [BRCMNAND_CMD_START] = 0x04, + [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, + [BRCMNAND_CMD_ADDRESS] = 0x0c, + [BRCMNAND_INTFC_STATUS] = 0x14, + [BRCMNAND_CS_SELECT] = 0x18, + [BRCMNAND_CS_XOR] = 0x1c, + [BRCMNAND_LL_OP] = 0x20, + [BRCMNAND_CS0_BASE] = 0x50, + [BRCMNAND_CS1_BASE] = 0, + [BRCMNAND_CORR_THRESHOLD] = 0xc0, + [BRCMNAND_CORR_THRESHOLD_EXT] = 0xc4, + [BRCMNAND_UNCORR_COUNT] = 0xfc, + [BRCMNAND_CORR_COUNT] = 0x100, + [BRCMNAND_CORR_EXT_ADDR] = 0x10c, + [BRCMNAND_CORR_ADDR] = 0x110, + [BRCMNAND_UNCORR_EXT_ADDR] = 0x114, + [BRCMNAND_UNCORR_ADDR] = 0x118, + [BRCMNAND_SEMAPHORE] = 0x150, + [BRCMNAND_ID] = 0x194, + [BRCMNAND_ID_EXT] = 0x198, + [BRCMNAND_LL_RDATA] = 0x19c, + [BRCMNAND_OOB_READ_BASE] = 0x200, + [BRCMNAND_OOB_READ_10_BASE] = 0, + [BRCMNAND_OOB_WRITE_BASE] = 0x280, + [BRCMNAND_OOB_WRITE_10_BASE] = 0, + [BRCMNAND_FC_BASE] = 0x400, +}; + +/* BRCMNAND v7.1 */ +static const u16 brcmnand_regs_v71[] = { + [BRCMNAND_CMD_START] = 0x04, + [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, + [BRCMNAND_CMD_ADDRESS] = 0x0c, + [BRCMNAND_INTFC_STATUS] = 0x14, + [BRCMNAND_CS_SELECT] = 0x18, + [BRCMNAND_CS_XOR] = 0x1c, + [BRCMNAND_LL_OP] = 0x20, + [BRCMNAND_CS0_BASE] = 0x50, + [BRCMNAND_CS1_BASE] = 0, + [BRCMNAND_CORR_THRESHOLD] = 0xdc, + [BRCMNAND_CORR_THRESHOLD_EXT] = 0xe0, + [BRCMNAND_UNCORR_COUNT] = 0xfc, + [BRCMNAND_CORR_COUNT] = 0x100, + [BRCMNAND_CORR_EXT_ADDR] = 0x10c, + [BRCMNAND_CORR_ADDR] = 0x110, + [BRCMNAND_UNCORR_EXT_ADDR] = 0x114, + [BRCMNAND_UNCORR_ADDR] = 0x118, + [BRCMNAND_SEMAPHORE] = 0x150, + [BRCMNAND_ID] = 0x194, + [BRCMNAND_ID_EXT] = 0x198, + [BRCMNAND_LL_RDATA] = 0x19c, + [BRCMNAND_OOB_READ_BASE] = 0x200, + [BRCMNAND_OOB_READ_10_BASE] = 0, + [BRCMNAND_OOB_WRITE_BASE] = 0x280, + [BRCMNAND_OOB_WRITE_10_BASE] = 0, + [BRCMNAND_FC_BASE] = 0x400, +}; + +/* BRCMNAND v7.2 */ +static const u16 brcmnand_regs_v72[] = { + [BRCMNAND_CMD_START] = 0x04, + [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, + [BRCMNAND_CMD_ADDRESS] = 0x0c, + [BRCMNAND_INTFC_STATUS] = 0x14, + [BRCMNAND_CS_SELECT] = 0x18, + [BRCMNAND_CS_XOR] = 0x1c, + [BRCMNAND_LL_OP] = 0x20, + [BRCMNAND_CS0_BASE] = 0x50, + [BRCMNAND_CS1_BASE] = 0, + [BRCMNAND_CORR_THRESHOLD] = 0xdc, + [BRCMNAND_CORR_THRESHOLD_EXT] = 0xe0, + [BRCMNAND_UNCORR_COUNT] = 0xfc, + [BRCMNAND_CORR_COUNT] = 0x100, + [BRCMNAND_CORR_EXT_ADDR] = 0x10c, + [BRCMNAND_CORR_ADDR] = 0x110, + [BRCMNAND_UNCORR_EXT_ADDR] = 0x114, + [BRCMNAND_UNCORR_ADDR] = 0x118, + [BRCMNAND_SEMAPHORE] = 0x150, + [BRCMNAND_ID] = 0x194, + [BRCMNAND_ID_EXT] = 0x198, + [BRCMNAND_LL_RDATA] = 0x19c, + [BRCMNAND_OOB_READ_BASE] = 0x200, + [BRCMNAND_OOB_READ_10_BASE] = 0, + [BRCMNAND_OOB_WRITE_BASE] = 0x400, + [BRCMNAND_OOB_WRITE_10_BASE] = 0, + [BRCMNAND_FC_BASE] = 0x600, +}; + +enum brcmnand_cs_reg { + BRCMNAND_CS_CFG_EXT = 0, + BRCMNAND_CS_CFG, + BRCMNAND_CS_ACC_CONTROL, + BRCMNAND_CS_TIMING1, + BRCMNAND_CS_TIMING2, +}; + +/* Per chip-select offsets for v7.1 */ +static const u8 brcmnand_cs_offsets_v71[] = { + [BRCMNAND_CS_ACC_CONTROL] = 0x00, + [BRCMNAND_CS_CFG_EXT] = 0x04, + [BRCMNAND_CS_CFG] = 0x08, + [BRCMNAND_CS_TIMING1] = 0x0c, + [BRCMNAND_CS_TIMING2] = 0x10, +}; + +/* Per chip-select offsets for pre v7.1, except CS0 on <= v5.0 */ +static const u8 brcmnand_cs_offsets[] = { + [BRCMNAND_CS_ACC_CONTROL] = 0x00, + [BRCMNAND_CS_CFG_EXT] = 0x04, + [BRCMNAND_CS_CFG] = 0x04, + [BRCMNAND_CS_TIMING1] = 0x08, + [BRCMNAND_CS_TIMING2] = 0x0c, +}; + +/* Per chip-select offset for <= v5.0 on CS0 only */ +static const u8 brcmnand_cs_offsets_cs0[] = { + [BRCMNAND_CS_ACC_CONTROL] = 0x00, + [BRCMNAND_CS_CFG_EXT] = 0x08, + [BRCMNAND_CS_CFG] = 0x08, + [BRCMNAND_CS_TIMING1] = 0x10, + [BRCMNAND_CS_TIMING2] = 0x14, +}; + +/* BRCMNAND_INTFC_STATUS */ +enum { + INTFC_FLASH_STATUS = GENMASK(7, 0), + + INTFC_ERASED = BIT(27), + INTFC_OOB_VALID = BIT(28), + INTFC_CACHE_VALID = BIT(29), + INTFC_FLASH_READY = BIT(30), + INTFC_CTLR_READY = BIT(31), +}; + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +/* BRCMNAND_TIMING_1 - Nand Flash Timing Parameters 1 */ +#define BRCMNAND_TIMING_1_tWP_MASK 0xf0000000 +#define BRCMNAND_TIMING_1_tWP_SHIFT 28 +#define BRCMNAND_TIMING_1_tWH_MASK 0x0f000000 +#define BRCMNAND_TIMING_1_tWH_SHIFT 24 +#define BRCMNAND_TIMING_1_tRP_MASK 0x00f00000 +#define BRCMNAND_TIMING_1_tRP_SHIFT 20 +#define BRCMNAND_TIMING_1_tREH_MASK 0x000f0000 +#define BRCMNAND_TIMING_1_tREH_SHIFT 16 +#define BRCMNAND_TIMING_1_tCS_MASK 0x0000f000 +#define BRCMNAND_TIMING_1_tCS_SHIFT 12 +#define BRCMNAND_TIMING_1_tCLH_MASK 0x00000f00 +#define BRCMNAND_TIMING_1_tCLH_SHIFT 8 +#define BRCMNAND_TIMING_1_tALH_MASK 0x000000f0 +#define BRCMNAND_TIMING_1_tALH_SHIFT 4 +#define BRCMNAND_TIMING_1_tADL_MASK 0x0000000f +#define BRCMNAND_TIMING_1_tADL_SHIFT 0 + +/* BRCMNAND_TIMING_2 - Nand Flash Timing Parameters 2 */ +#define BRCMNAND_TIMING_2_CLK_SELECT_MASK 0x80000000 +#define BRCMNAND_TIMING_2_CLK_SELECT_SHIFT 31 +#define BRCMNAND_TIMING_2_tWB_MASK 0x00001e00 +#define BRCMNAND_TIMING_2_tWB_SHIFT 9 +#define BRCMNAND_TIMING_2_tWHR_MASK 0x000001f0 +#define BRCMNAND_TIMING_2_tWHR_SHIFT 4 +#define BRCMNAND_TIMING_2_tREAD_MASK 0x0000000f +#define BRCMNAND_TIMING_2_tREAD_SHIFT 0 +#endif + +static inline u32 nand_readreg(struct brcmnand_controller *ctrl, u32 offs) +{ + return brcmnand_readl(ctrl->nand_base + offs); +} + +static inline void nand_writereg(struct brcmnand_controller *ctrl, u32 offs, + u32 val) +{ + brcmnand_writel(val, ctrl->nand_base + offs); +} + +static int brcmnand_revision_init(struct brcmnand_controller *ctrl) +{ + static const unsigned int block_sizes_v6[] = { 8, 16, 128, 256, 512, 1024, 2048, 0 }; + static const unsigned int block_sizes_v4[] = { 16, 128, 8, 512, 256, 1024, 2048, 0 }; + static const unsigned int page_sizes[] = { 512, 2048, 4096, 8192, 0 }; + + ctrl->nand_version = nand_readreg(ctrl, 0) & 0xffff; + + /* Only support v4.0+? */ + if (ctrl->nand_version < 0x0400) { + dev_err(ctrl->dev, "version %#x not supported\n", + ctrl->nand_version); + return -ENODEV; + } + + /* Register offsets */ + if (ctrl->nand_version >= 0x0702) + ctrl->reg_offsets = brcmnand_regs_v72; + else if (ctrl->nand_version >= 0x0701) + ctrl->reg_offsets = brcmnand_regs_v71; + else if (ctrl->nand_version >= 0x0600) + ctrl->reg_offsets = brcmnand_regs_v60; + else if (ctrl->nand_version >= 0x0500) + ctrl->reg_offsets = brcmnand_regs_v50; + else if (ctrl->nand_version >= 0x0400) + ctrl->reg_offsets = brcmnand_regs_v40; + + /* Chip-select stride */ + if (ctrl->nand_version >= 0x0701) + ctrl->reg_spacing = 0x14; + else + ctrl->reg_spacing = 0x10; + + /* Per chip-select registers */ + if (ctrl->nand_version >= 0x0701) { + ctrl->cs_offsets = brcmnand_cs_offsets_v71; + } else { + ctrl->cs_offsets = brcmnand_cs_offsets; + + /* v5.0 and earlier has a different CS0 offset layout */ + if (ctrl->nand_version <= 0x0500) + ctrl->cs0_offsets = brcmnand_cs_offsets_cs0; + } + + /* Page / block sizes */ + if (ctrl->nand_version >= 0x0701) { + /* >= v7.1 use nice power-of-2 values! */ + ctrl->max_page_size = 16 * 1024; + ctrl->max_block_size = 2 * 1024 * 1024; + } else { + ctrl->page_sizes = page_sizes; + if (ctrl->nand_version >= 0x0600) + ctrl->block_sizes = block_sizes_v6; + else + ctrl->block_sizes = block_sizes_v4; + + if (ctrl->nand_version < 0x0400) { + ctrl->max_page_size = 4096; + ctrl->max_block_size = 512 * 1024; + } + } + + /* Maximum spare area sector size (per 512B) */ + if (ctrl->nand_version >= 0x0702) + ctrl->max_oob = 128; + if (ctrl->nand_version >= 0x0600) + ctrl->max_oob = 64; + else if (ctrl->nand_version >= 0x0500) + ctrl->max_oob = 32; + else + ctrl->max_oob = 16; +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + /* + * CONTROLLER_VERSION: + * < v5.0: ECC_REQ = ceil(BCH_T * 13/8) + * >= v5.0: ECC_REQ = ceil(BCH_T * 14/8) + */ + if (ctrl->nand_version >= 0x0500) + ctrl->ecc_req_factor = 14; + else + ctrl->ecc_req_factor = 13; +#endif + + /* v6.0 and newer (except v6.1) have prefetch support */ + if (ctrl->nand_version >= 0x0600 && ctrl->nand_version != 0x0601) + ctrl->features |= BRCMNAND_HAS_PREFETCH; + + /* + * v6.x has cache mode, but it's implemented differently. Ignore it for + * now. + */ + if (ctrl->nand_version >= 0x0700) + ctrl->features |= BRCMNAND_HAS_CACHE_MODE; + + if (ctrl->nand_version >= 0x0500) + ctrl->features |= BRCMNAND_HAS_1K_SECTORS; + + if (ctrl->nand_version >= 0x0700) + ctrl->features |= BRCMNAND_HAS_WP; + else if (of_property_read_bool(ctrl->dev->of_node, "brcm,nand-has-wp")) + ctrl->features |= BRCMNAND_HAS_WP; + + return 0; +} + +static inline u32 brcmnand_read_reg(struct brcmnand_controller *ctrl, + enum brcmnand_reg reg) +{ + u16 offs = ctrl->reg_offsets[reg]; + + if (offs) + return nand_readreg(ctrl, offs); + else + return 0; +} + +static inline void brcmnand_write_reg(struct brcmnand_controller *ctrl, + enum brcmnand_reg reg, u32 val) +{ + u16 offs = ctrl->reg_offsets[reg]; + + if (offs) + nand_writereg(ctrl, offs, val); +} + +static inline void brcmnand_rmw_reg(struct brcmnand_controller *ctrl, + enum brcmnand_reg reg, u32 mask, unsigned + int shift, u32 val) +{ + u32 tmp = brcmnand_read_reg(ctrl, reg); + + tmp &= ~mask; + tmp |= val << shift; + brcmnand_write_reg(ctrl, reg, tmp); +} + +static inline u32 brcmnand_read_fc(struct brcmnand_controller *ctrl, int word) +{ + return __raw_readl(ctrl->nand_fc + word * 4); +} + +static inline void brcmnand_write_fc(struct brcmnand_controller *ctrl, + int word, u32 val) +{ + __raw_writel(val, ctrl->nand_fc + word * 4); +} + +static inline u16 brcmnand_cs_offset(struct brcmnand_controller *ctrl, int cs, + enum brcmnand_cs_reg reg) +{ + u16 offs_cs0 = ctrl->reg_offsets[BRCMNAND_CS0_BASE]; + u16 offs_cs1 = ctrl->reg_offsets[BRCMNAND_CS1_BASE]; + u8 cs_offs; + + if (cs == 0 && ctrl->cs0_offsets) + cs_offs = ctrl->cs0_offsets[reg]; + else + cs_offs = ctrl->cs_offsets[reg]; + + if (cs && offs_cs1) + return offs_cs1 + (cs - 1) * ctrl->reg_spacing + cs_offs; + + return offs_cs0 + cs * ctrl->reg_spacing + cs_offs; +} + +static inline u32 brcmnand_count_corrected(struct brcmnand_controller *ctrl) +{ + if (ctrl->nand_version < 0x0600) + return 1; + return brcmnand_read_reg(ctrl, BRCMNAND_CORR_COUNT); +} + +static void brcmnand_wr_corr_thresh(struct brcmnand_host *host, u8 val) +{ + struct brcmnand_controller *ctrl = host->ctrl; + unsigned int shift = 0, bits; + enum brcmnand_reg reg = BRCMNAND_CORR_THRESHOLD; + int cs = host->cs; + + if (ctrl->nand_version >= 0x0702) + bits = 7; + if (ctrl->nand_version >= 0x0600) + bits = 6; + else if (ctrl->nand_version >= 0x0500) + bits = 5; + else + bits = 4; + + if (ctrl->nand_version >= 0x0702) { + if (cs >= 4) + reg = BRCMNAND_CORR_THRESHOLD_EXT; + shift = (cs % 4) * bits; + } else if (ctrl->nand_version >= 0x0600) { + if (cs >= 5) + reg = BRCMNAND_CORR_THRESHOLD_EXT; + shift = (cs % 5) * bits; + } + brcmnand_rmw_reg(ctrl, reg, (bits - 1) << shift, shift, val); +} + +static inline int brcmnand_cmd_shift(struct brcmnand_controller *ctrl) +{ + if (ctrl->nand_version < 0x0602) + return 24; + return 0; +} + +/*********************************************************************** + * NAND ACC CONTROL bitfield + * + * Some bits have remained constant throughout hardware revision, while + * others have shifted around. + ***********************************************************************/ + +/* Constant for all versions (where supported) */ +enum { + /* See BRCMNAND_HAS_CACHE_MODE */ + ACC_CONTROL_CACHE_MODE = BIT(22), + + /* See BRCMNAND_HAS_PREFETCH */ + ACC_CONTROL_PREFETCH = BIT(23), + + ACC_CONTROL_PAGE_HIT = BIT(24), + ACC_CONTROL_WR_PREEMPT = BIT(25), + ACC_CONTROL_PARTIAL_PAGE = BIT(26), + ACC_CONTROL_RD_ERASED = BIT(27), + ACC_CONTROL_FAST_PGM_RDIN = BIT(28), + ACC_CONTROL_WR_ECC = BIT(30), + ACC_CONTROL_RD_ECC = BIT(31), +}; + +static inline u32 brcmnand_spare_area_mask(struct brcmnand_controller *ctrl) +{ + if (ctrl->nand_version >= 0x0702) + return GENMASK(7, 0); + else if (ctrl->nand_version >= 0x0600) + return GENMASK(6, 0); + else + return GENMASK(5, 0); +} + +#define NAND_ACC_CONTROL_ECC_SHIFT 16 +#define NAND_ACC_CONTROL_ECC_EXT_SHIFT 13 + +static inline u32 brcmnand_ecc_level_mask(struct brcmnand_controller *ctrl) +{ + u32 mask = (ctrl->nand_version >= 0x0600) ? 0x1f : 0x0f; + + mask <<= NAND_ACC_CONTROL_ECC_SHIFT; + + /* v7.2 includes additional ECC levels */ + if (ctrl->nand_version >= 0x0702) + mask |= 0x7 << NAND_ACC_CONTROL_ECC_EXT_SHIFT; + + return mask; +} + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +static inline int brcmnand_ecc_level_shift(struct brcmnand_controller *ctrl) +{ + if (ctrl->nand_version >= 0x0702) + return NAND_ACC_CONTROL_ECC_EXT_SHIFT; + else + return NAND_ACC_CONTROL_ECC_SHIFT; +} +#endif + +static void brcmnand_set_ecc_enabled(struct brcmnand_host *host, int en) +{ + struct brcmnand_controller *ctrl = host->ctrl; + u16 offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL); + u32 acc_control = nand_readreg(ctrl, offs); + u32 ecc_flags = ACC_CONTROL_WR_ECC | ACC_CONTROL_RD_ECC; + + if (en) { + acc_control |= ecc_flags; /* enable RD/WR ECC */ +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + acc_control &= ~brcmnand_ecc_level_mask(ctrl); + acc_control |= host->hwcfg.ecc_level << brcmnand_ecc_level_shift(ctrl); +#else + acc_control |= host->hwcfg.ecc_level + << NAND_ACC_CONTROL_ECC_SHIFT; +#endif + + } else { + acc_control &= ~ecc_flags; /* disable RD/WR ECC */ + acc_control &= ~brcmnand_ecc_level_mask(ctrl); + } + + nand_writereg(ctrl, offs, acc_control); +} + +static inline int brcmnand_sector_1k_shift(struct brcmnand_controller *ctrl) +{ + if (ctrl->nand_version >= 0x0702) + return 9; + else if (ctrl->nand_version >= 0x0600) + return 7; + else if (ctrl->nand_version >= 0x0500) + return 6; + else + return -1; +} + +static int brcmnand_get_sector_size_1k(struct brcmnand_host *host) +{ + struct brcmnand_controller *ctrl = host->ctrl; + int shift = brcmnand_sector_1k_shift(ctrl); + u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_ACC_CONTROL); + + if (shift < 0) + return 0; + + return (nand_readreg(ctrl, acc_control_offs) >> shift) & 0x1; +} + +static void brcmnand_set_sector_size_1k(struct brcmnand_host *host, int val) +{ + struct brcmnand_controller *ctrl = host->ctrl; + int shift = brcmnand_sector_1k_shift(ctrl); + u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_ACC_CONTROL); + u32 tmp; + + if (shift < 0) + return; + + tmp = nand_readreg(ctrl, acc_control_offs); + tmp &= ~(1 << shift); + tmp |= (!!val) << shift; + nand_writereg(ctrl, acc_control_offs, tmp); +} + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +static int brcmnand_get_spare_size(struct brcmnand_host *host) +{ + struct brcmnand_controller *ctrl = host->ctrl; + u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_ACC_CONTROL); + u32 acc = nand_readreg(ctrl, acc_control_offs); + + return (acc&brcmnand_spare_area_mask(ctrl)); +} + +static int brcmnand_get_ecc_strength(struct brcmnand_host *host) +{ + struct brcmnand_controller *ctrl = host->ctrl; + u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_ACC_CONTROL); + int sector_size_1k = brcmnand_get_sector_size_1k(host); + u32 acc; + int spare_area_size, ecc_level, ecc_strength; + + spare_area_size = brcmnand_get_spare_size(host); + acc = nand_readreg(ctrl, acc_control_offs); + ecc_level = (acc & brcmnand_ecc_level_mask(ctrl)) >> brcmnand_ecc_level_shift(ctrl); + if (sector_size_1k) + ecc_strength = ecc_level<<1; + else if(spare_area_size == 16 && ecc_level == 15 ) + ecc_strength = 1; /* hamming */ + else + ecc_strength = ecc_level; + + return ecc_strength; +} + + +static void brcmnand_adjust_timing(struct brcmnand_host *host, struct brcmnand_cfg *cfg) +{ + struct brcmnand_controller *ctrl = host->ctrl; + u16 t1_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING1); + u16 t2_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING2); + u32 nand_timing_1 = nand_readreg(ctrl, t1_offs); + u32 nand_timing_2 = nand_readreg(ctrl, t2_offs); + + if ((cfg->timing_1 == 0) && (cfg->timing_2 == 0)) { + /* if we don't know better, force max read speed */ + cfg->timing_1 = 0x00320000; + cfg->timing_2 = 0x00000004; + } + else + dev_info(ctrl->dev, "Using timing parameters from Id table\n"); + + if (cfg->timing_1 & BRCMNAND_TIMING_1_tWP_MASK) { + nand_timing_1 &= ~BRCMNAND_TIMING_1_tWP_MASK; + nand_timing_1 |= (cfg->timing_1 & BRCMNAND_TIMING_1_tWP_MASK); + } + if (cfg->timing_1 & BRCMNAND_TIMING_1_tWH_MASK) { + nand_timing_1 &= ~BRCMNAND_TIMING_1_tWH_MASK; + nand_timing_1 |= (cfg->timing_1 & BRCMNAND_TIMING_1_tWH_MASK); + } + if (cfg->timing_1 & BRCMNAND_TIMING_1_tRP_MASK) { + nand_timing_1 &= ~BRCMNAND_TIMING_1_tRP_MASK; + nand_timing_1 |= (cfg->timing_1 & BRCMNAND_TIMING_1_tRP_MASK); + } + if (cfg->timing_1 & BRCMNAND_TIMING_1_tREH_MASK) { + nand_timing_1 &= ~BRCMNAND_TIMING_1_tREH_MASK; + nand_timing_1 |= (cfg->timing_1 & BRCMNAND_TIMING_1_tREH_MASK); + } + if (cfg->timing_1 & BRCMNAND_TIMING_1_tCS_MASK) { + nand_timing_1 &= ~BRCMNAND_TIMING_1_tCS_MASK; + nand_timing_1 |= (cfg->timing_1 & BRCMNAND_TIMING_1_tCS_MASK); + } + if (cfg->timing_1 & BRCMNAND_TIMING_1_tCLH_MASK) { + nand_timing_1 &= ~BRCMNAND_TIMING_1_tCLH_MASK; + nand_timing_1 |= (cfg->timing_1 & BRCMNAND_TIMING_1_tCLH_MASK); + } + if (cfg->timing_1 & BRCMNAND_TIMING_1_tALH_MASK) { + nand_timing_1 &= ~BRCMNAND_TIMING_1_tALH_MASK; + nand_timing_1 |= (cfg->timing_1 & BRCMNAND_TIMING_1_tALH_MASK); + } + if (cfg->timing_1 & BRCMNAND_TIMING_1_tADL_MASK) { + nand_timing_1 &= ~BRCMNAND_TIMING_1_tADL_MASK; + nand_timing_1 |= (cfg->timing_1 & BRCMNAND_TIMING_1_tADL_MASK); + } + + nand_writereg(ctrl, t1_offs, nand_timing_1); + + if (cfg->timing_2 & BRCMNAND_TIMING_2_tWB_MASK) { + nand_timing_2 &= ~BRCMNAND_TIMING_2_tWB_MASK; + nand_timing_2 |= (cfg->timing_2 & BRCMNAND_TIMING_2_tWB_MASK); + } + if (cfg->timing_2 & BRCMNAND_TIMING_2_tWHR_MASK) { + nand_timing_2 &= ~BRCMNAND_TIMING_2_tWHR_MASK; + nand_timing_2 |= (cfg->timing_2 & BRCMNAND_TIMING_2_tWHR_MASK); + } + if (cfg->timing_2 & BRCMNAND_TIMING_2_tREAD_MASK) { + nand_timing_2 &= ~BRCMNAND_TIMING_2_tREAD_MASK; + nand_timing_2 |= (cfg->timing_2 & BRCMNAND_TIMING_2_tREAD_MASK); + } + + nand_writereg(ctrl, t2_offs, nand_timing_2); + + dev_info(ctrl->dev, "Adjust timing_1 to 0x%08x timing_2 to 0x%08x\n", + nand_timing_1, nand_timing_2); + +} +#endif + +/*********************************************************************** + * CS_NAND_SELECT + ***********************************************************************/ + +enum { + CS_SELECT_NAND_WP = BIT(29), + CS_SELECT_AUTO_DEVICE_ID_CFG = BIT(30), +}; + +static inline void brcmnand_set_wp(struct brcmnand_controller *ctrl, bool en) +{ + u32 val = en ? CS_SELECT_NAND_WP : 0; + + brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT, CS_SELECT_NAND_WP, 0, val); +} + +/*********************************************************************** + * Flash DMA + ***********************************************************************/ + +enum flash_dma_reg { + FLASH_DMA_REVISION = 0x00, + FLASH_DMA_FIRST_DESC = 0x04, + FLASH_DMA_FIRST_DESC_EXT = 0x08, + FLASH_DMA_CTRL = 0x0c, + FLASH_DMA_MODE = 0x10, + FLASH_DMA_STATUS = 0x14, + FLASH_DMA_INTERRUPT_DESC = 0x18, + FLASH_DMA_INTERRUPT_DESC_EXT = 0x1c, + FLASH_DMA_ERROR_STATUS = 0x20, + FLASH_DMA_CURRENT_DESC = 0x24, + FLASH_DMA_CURRENT_DESC_EXT = 0x28, +}; + +static inline bool has_flash_dma(struct brcmnand_controller *ctrl) +{ + return ctrl->flash_dma_base; +} + +static inline bool flash_dma_buf_ok(const void *buf) +{ + return buf && !is_vmalloc_addr(buf) && + likely(IS_ALIGNED((uintptr_t)buf, 4)); +} + +static inline void flash_dma_writel(struct brcmnand_controller *ctrl, u8 offs, + u32 val) +{ + brcmnand_writel(val, ctrl->flash_dma_base + offs); +} + +static inline u32 flash_dma_readl(struct brcmnand_controller *ctrl, u8 offs) +{ + return brcmnand_readl(ctrl->flash_dma_base + offs); +} + +/* Low-level operation types: command, address, write, or read */ +enum brcmnand_llop_type { + LL_OP_CMD, + LL_OP_ADDR, + LL_OP_WR, + LL_OP_RD, +}; + +/*********************************************************************** + * Internal support functions + ***********************************************************************/ + +static inline bool is_hamming_ecc(struct brcmnand_controller *ctrl, + struct brcmnand_cfg *cfg) +{ + if (ctrl->nand_version <= 0x0701) + return cfg->sector_size_1k == 0 && cfg->spare_area_size == 16 && + cfg->ecc_level == 15; + else + return cfg->sector_size_1k == 0 && ((cfg->spare_area_size == 16 && + cfg->ecc_level == 15) || + (cfg->spare_area_size == 28 && cfg->ecc_level == 16)); +} + +/* + * Returns a nand_ecclayout strucutre for the given layout/configuration. + * Returns NULL on failure. + */ +static struct nand_ecclayout *brcmnand_create_layout(int ecc_level, + struct brcmnand_host *host) +{ + struct brcmnand_cfg *cfg = &host->hwcfg; + int i, j; + struct nand_ecclayout *layout; + int req; + int sectors; + int sas; + int idx1, idx2; + + layout = devm_kzalloc(&host->pdev->dev, sizeof(*layout), GFP_KERNEL); + if (!layout) + return NULL; + + sectors = cfg->page_size / (512 << cfg->sector_size_1k); + sas = cfg->spare_area_size << cfg->sector_size_1k; + + /* Hamming */ + if (is_hamming_ecc(host->ctrl, cfg)) { + for (i = 0, idx1 = 0, idx2 = 0; i < sectors; i++) { + /* First sector of each page may have BBI */ + if (i == 0) { +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + /* Large-page NAND have two bytes for BBI */ + layout->oobfree[idx2].offset = i * sas + 2; + layout->oobfree[idx2].length = 4; + /* Small-page NAND use one byte at 6 for BBI */ + if (cfg->page_size == 512) { + layout->oobfree[idx2].offset -= 2; + layout->oobfree[idx2].length++; + } +#else + layout->oobfree[idx2].offset = i * sas + 1; + /* Small-page NAND use byte 6 for BBI */ + if (cfg->page_size == 512) + layout->oobfree[idx2].offset--; + layout->oobfree[idx2].length = 5; +#endif + + } else { + layout->oobfree[idx2].offset = i * sas; + layout->oobfree[idx2].length = 6; + } + idx2++; + layout->eccpos[idx1++] = i * sas + 6; + layout->eccpos[idx1++] = i * sas + 7; + layout->eccpos[idx1++] = i * sas + 8; + layout->oobfree[idx2].offset = i * sas + 9; + layout->oobfree[idx2].length = 7; + idx2++; + /* Leave zero-terminated entry for OOBFREE */ + if (idx1 >= MTD_MAX_ECCPOS_ENTRIES_LARGE || + idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1) + break; + } + goto out; + } + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + req = DIV_ROUND_UP(ecc_level * host->ctrl->ecc_req_factor, 8); +#else + /* + * CONTROLLER_VERSION: + * < v5.0: ECC_REQ = ceil(BCH_T * 13/8) + * >= v5.0: ECC_REQ = ceil(BCH_T * 14/8) + * But we will just be conservative. + */ + req = DIV_ROUND_UP(ecc_level * 14, 8); +#endif + if (req >= sas) { + dev_err(&host->pdev->dev, + "error: ECC too large for OOB (ECC bytes %d, spare sector %d)\n", + req, sas); + return NULL; + } + + layout->eccbytes = req * sectors; + for (i = 0, idx1 = 0, idx2 = 0; i < sectors; i++) { + for (j = sas - req; j < sas && idx1 < + MTD_MAX_ECCPOS_ENTRIES_LARGE; j++, idx1++) + layout->eccpos[idx1] = i * sas + j; + + /* First sector of each page may have BBI */ + if (i == 0) { + if (cfg->page_size == 512 && (sas - req >= 6)) { + /* Small-page NAND use byte 6 for BBI */ + layout->oobfree[idx2].offset = 0; + layout->oobfree[idx2].length = 5; + idx2++; + if (sas - req > 6) { + layout->oobfree[idx2].offset = 6; + layout->oobfree[idx2].length = + sas - req - 6; + idx2++; + } + } else if (sas > req + 1) { + layout->oobfree[idx2].offset = i * sas + 1; + layout->oobfree[idx2].length = sas - req - 1; + idx2++; + } + } else if (sas > req) { + layout->oobfree[idx2].offset = i * sas; + layout->oobfree[idx2].length = sas - req; + idx2++; + } + /* Leave zero-terminated entry for OOBFREE */ + if (idx1 >= MTD_MAX_ECCPOS_ENTRIES_LARGE || + idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1) + break; + } +out: + /* Sum available OOB */ + for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE; i++) + layout->oobavail += layout->oobfree[i].length; + return layout; +} + +static struct nand_ecclayout *brcmnand_choose_ecc_layout( + struct brcmnand_host *host) +{ + struct nand_ecclayout *layout; + struct brcmnand_cfg *p = &host->hwcfg; + unsigned int ecc_level = p->ecc_level; + + if (p->sector_size_1k) + ecc_level <<= 1; + + layout = brcmnand_create_layout(ecc_level, host); + if (!layout) { + dev_err(&host->pdev->dev, + "no proper ecc_layout for this NAND cfg\n"); + return NULL; + } + + return layout; +} + +static void brcmnand_wp(struct mtd_info *mtd, int wp) +{ + struct nand_chip *chip = mtd->priv; + struct brcmnand_host *host = chip->priv; + struct brcmnand_controller *ctrl = host->ctrl; + + if ((ctrl->features & BRCMNAND_HAS_WP) && wp_on == 1) { + static int old_wp = -1; + + if (old_wp != wp) { + dev_dbg(ctrl->dev, "WP %s\n", wp ? "on" : "off"); + old_wp = wp; + } + brcmnand_set_wp(ctrl, wp); + } +} + +/* Helper functions for reading and writing OOB registers */ +static inline u8 oob_reg_read(struct brcmnand_controller *ctrl, u32 offs) +{ + u16 offset0, offset10, reg_offs; + + offset0 = ctrl->reg_offsets[BRCMNAND_OOB_READ_BASE]; + offset10 = ctrl->reg_offsets[BRCMNAND_OOB_READ_10_BASE]; + + if (offs >= ctrl->max_oob) + return 0x77; + + if (offs >= 16 && offset10) + reg_offs = offset10 + ((offs - 0x10) & ~0x03); + else + reg_offs = offset0 + (offs & ~0x03); + + return nand_readreg(ctrl, reg_offs) >> (24 - ((offs & 0x03) << 3)); +} + +static inline void oob_reg_write(struct brcmnand_controller *ctrl, u32 offs, + u32 data) +{ + u16 offset0, offset10, reg_offs; + + offset0 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_BASE]; + offset10 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_10_BASE]; + + if (offs >= ctrl->max_oob) + return; + + if (offs >= 16 && offset10) + reg_offs = offset10 + ((offs - 0x10) & ~0x03); + else + reg_offs = offset0 + (offs & ~0x03); + + nand_writereg(ctrl, reg_offs, data); +} + +/* + * read_oob_from_regs - read data from OOB registers + * @ctrl: NAND controller + * @i: sub-page sector index + * @oob: buffer to read to + * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE) + * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal + */ +static int read_oob_from_regs(struct brcmnand_controller *ctrl, int i, u8 *oob, + int sas, int sector_1k) +{ + int tbytes = sas << sector_1k; + int j; + + /* Adjust OOB values for 1K sector size */ + if (sector_1k && (i & 0x01)) + tbytes = max(0, tbytes - (int)ctrl->max_oob); + tbytes = min_t(int, tbytes, ctrl->max_oob); + + for (j = 0; j < tbytes; j++) + oob[j] = oob_reg_read(ctrl, j); + return tbytes; +} + +/* + * write_oob_to_regs - write data to OOB registers + * @i: sub-page sector index + * @oob: buffer to write from + * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE) + * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal + */ +static int write_oob_to_regs(struct brcmnand_controller *ctrl, int i, + const u8 *oob, int sas, int sector_1k) +{ + int tbytes = sas << sector_1k; + int j; + + /* Adjust OOB values for 1K sector size */ + if (sector_1k && (i & 0x01)) + tbytes = max(0, tbytes - (int)ctrl->max_oob); + tbytes = min_t(int, tbytes, ctrl->max_oob); + + for (j = 0; (j + 3) < tbytes; j += 4) + oob_reg_write(ctrl, j, + (((j < tbytes ) ? oob[j] : 0xff) << 24) | + (((j + 1 < tbytes ) ? oob[j + 1] : 0xff) << 16) | + (((j + 2 < tbytes ) ? oob[j + 2] : 0xff) << 8) | + ((j + 3 < tbytes ) ? oob[j + 3] : 0xff)); + + return tbytes; +} + +static irqreturn_t brcmnand_ctlrdy_irq(int irq, void *data) +{ + struct brcmnand_controller *ctrl = data; + + /* Discard all NAND_CTLRDY interrupts during DMA */ + if (ctrl->dma_pending) + return IRQ_HANDLED; + + complete(&ctrl->done); + return IRQ_HANDLED; +} + +/* Handle SoC-specific interrupt hardware */ +static irqreturn_t brcmnand_irq(int irq, void *data) +{ + struct brcmnand_controller *ctrl = data; + + if (ctrl->soc->ctlrdy_ack(ctrl->soc)) + return brcmnand_ctlrdy_irq(irq, data); + + return IRQ_NONE; +} + +static irqreturn_t brcmnand_dma_irq(int irq, void *data) +{ + struct brcmnand_controller *ctrl = data; + + complete(&ctrl->dma_done); + + return IRQ_HANDLED; +} + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +static int brcmnand_wait_cmd(struct brcmnand_host *host, int timeout) +{ + struct brcmnand_controller *ctrl = host->ctrl; + unsigned long timeo = msecs_to_jiffies(timeout); + int ret = 0, ready = 0; + + if (ctrl->polling) { + timeo += jiffies; + while (time_before(jiffies, timeo)) { + ready = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS)&INTFC_CTLR_READY; + if (ready) + return ret; + else + udelay(1); + } + + ret = -1; + + } else { + if (wait_for_completion_timeout(&ctrl->done, timeo) <= 0 ) + ret = -1; + } + + return ret; +} +#endif + +static void brcmnand_send_cmd(struct brcmnand_host *host, int cmd) +{ + struct brcmnand_controller *ctrl = host->ctrl; + u32 intfc; + + dev_dbg(ctrl->dev, "send native cmd %d addr_lo 0x%x\n", cmd, + brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS)); +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + /* + * If we came here through _panic_write and there is a pending + * command, give it 5ms to complete. If it doesn't, rather than + * hitting BUG_ON, just return so we don't crash while crashing. + */ + if (oops_in_progress) { + if (ctrl->cmd_pending && brcmnand_wait_cmd(host, 5)) + return; + } else +#endif + BUG_ON(ctrl->cmd_pending != 0); + ctrl->cmd_pending = cmd; + + intfc = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS); +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + /* same as above for cmd_pending check */ + if (oops_in_progress) { + if ((!(intfc & INTFC_CTLR_READY)) && brcmnand_wait_cmd(host, 5)) + return; + } else +#endif + BUG_ON(!(intfc & INTFC_CTLR_READY)); + + mb(); /* flush previous writes */ + brcmnand_write_reg(ctrl, BRCMNAND_CMD_START, + cmd << brcmnand_cmd_shift(ctrl)); +} + +/*********************************************************************** + * NAND MTD API: read/program/erase + ***********************************************************************/ + +static void brcmnand_cmd_ctrl(struct mtd_info *mtd, int dat, + unsigned int ctrl) +{ + /* intentionally left blank */ +} + +static int brcmnand_waitfunc(struct mtd_info *mtd, struct nand_chip *this) +{ + struct nand_chip *chip = mtd->priv; + struct brcmnand_host *host = chip->priv; + struct brcmnand_controller *ctrl = host->ctrl; + + dev_dbg(ctrl->dev, "wait on native cmd %d\n", ctrl->cmd_pending); +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + if (ctrl->cmd_pending && brcmnand_wait_cmd(host, 100) != 0 ) { +#else + if (ctrl->cmd_pending && + wait_for_completion_timeout(&ctrl->done, msecs_to_jiffies(100)) <= 0) { +#endif + u32 cmd = brcmnand_read_reg(ctrl, BRCMNAND_CMD_START) + >> brcmnand_cmd_shift(ctrl); + + dev_err_ratelimited(ctrl->dev, + "timeout waiting for command %#02x\n", cmd); + dev_err_ratelimited(ctrl->dev, "intfc status %08x\n", + brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS)); + } + ctrl->cmd_pending = 0; + return brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) & + INTFC_FLASH_STATUS; +} + +enum { + LLOP_RE = BIT(16), + LLOP_WE = BIT(17), + LLOP_ALE = BIT(18), + LLOP_CLE = BIT(19), + LLOP_RETURN_IDLE = BIT(31), + + LLOP_DATA_MASK = GENMASK(15, 0), +}; + +static int brcmnand_low_level_op(struct brcmnand_host *host, + enum brcmnand_llop_type type, u32 data, + bool last_op) +{ + struct mtd_info *mtd = &host->mtd; + struct nand_chip *chip = &host->chip; + struct brcmnand_controller *ctrl = host->ctrl; + u32 tmp; + + tmp = data & LLOP_DATA_MASK; + switch (type) { + case LL_OP_CMD: + tmp |= LLOP_WE | LLOP_CLE; + break; + case LL_OP_ADDR: + /* WE | ALE */ + tmp |= LLOP_WE | LLOP_ALE; + break; + case LL_OP_WR: + /* WE */ + tmp |= LLOP_WE; + break; + case LL_OP_RD: + /* RE */ + tmp |= LLOP_RE; + break; + } + if (last_op) + /* RETURN_IDLE */ + tmp |= LLOP_RETURN_IDLE; + + dev_dbg(ctrl->dev, "ll_op cmd %#x\n", tmp); + + brcmnand_write_reg(ctrl, BRCMNAND_LL_OP, tmp); + (void)brcmnand_read_reg(ctrl, BRCMNAND_LL_OP); + + brcmnand_send_cmd(host, CMD_LOW_LEVEL_OP); + return brcmnand_waitfunc(mtd, chip); +} + +static void brcmnand_cmdfunc(struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + struct nand_chip *chip = mtd->priv; + struct brcmnand_host *host = chip->priv; + struct brcmnand_controller *ctrl = host->ctrl; + u64 addr = (u64)page_addr << chip->page_shift; + int native_cmd = 0; + + if (command == NAND_CMD_READID || command == NAND_CMD_PARAM || + command == NAND_CMD_RNDOUT) + addr = (u64)column; + /* Avoid propagating a negative, don't-care address */ + else if (page_addr < 0) + addr = 0; + + dev_dbg(ctrl->dev, "cmd 0x%x addr 0x%llx\n", command, + (unsigned long long)addr); + + host->last_cmd = command; + host->last_byte = 0; + host->last_addr = addr; + + switch (command) { + case NAND_CMD_RESET: + native_cmd = CMD_FLASH_RESET; + break; + case NAND_CMD_STATUS: + native_cmd = CMD_STATUS_READ; + break; + case NAND_CMD_READID: + native_cmd = CMD_DEVICE_ID_READ; + break; + case NAND_CMD_READOOB: + native_cmd = CMD_SPARE_AREA_READ; + break; + case NAND_CMD_ERASE1: +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + if (brcmnand_check_dying_gasp(ctrl->soc)) { + dev_warn(ctrl->dev, "system is losing power, abort nand erase at offset 0x%llx\n", addr); + native_cmd = 0; + } else { + native_cmd = CMD_BLOCK_ERASE; + brcmnand_wp(mtd, 0); + } +#else + native_cmd = CMD_BLOCK_ERASE; + brcmnand_wp(mtd, 0); +#endif + break; + case NAND_CMD_PARAM: + native_cmd = CMD_PARAMETER_READ; + break; + case NAND_CMD_SET_FEATURES: + case NAND_CMD_GET_FEATURES: + brcmnand_low_level_op(host, LL_OP_CMD, command, false); + brcmnand_low_level_op(host, LL_OP_ADDR, column, false); + break; + case NAND_CMD_RNDOUT: + native_cmd = CMD_PARAMETER_CHANGE_COL; + addr &= ~((u64)(FC_BYTES - 1)); + /* + * HW quirk: PARAMETER_CHANGE_COL requires SECTOR_SIZE_1K=0 + * NB: hwcfg.sector_size_1k may not be initialized yet + */ + if (brcmnand_get_sector_size_1k(host)) { + host->hwcfg.sector_size_1k = + brcmnand_get_sector_size_1k(host); + brcmnand_set_sector_size_1k(host, 0); + } + break; + } + + if (!native_cmd) + return; + + brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS, + (host->cs << 16) | ((addr >> 32) & 0xffff)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS); + brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, lower_32_bits(addr)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); + + brcmnand_send_cmd(host, native_cmd); + brcmnand_waitfunc(mtd, chip); + + if (native_cmd == CMD_PARAMETER_READ || + native_cmd == CMD_PARAMETER_CHANGE_COL) { + int i; + + brcmnand_soc_data_bus_prepare(ctrl->soc); + + /* + * Must cache the FLASH_CACHE now, since changes in + * SECTOR_SIZE_1K may invalidate it + */ + for (i = 0; i < FC_WORDS; i++) + ctrl->flash_cache[i] = brcmnand_read_fc(ctrl, i); + + brcmnand_soc_data_bus_unprepare(ctrl->soc); + + /* Cleanup from HW quirk: restore SECTOR_SIZE_1K */ + if (host->hwcfg.sector_size_1k) + brcmnand_set_sector_size_1k(host, + host->hwcfg.sector_size_1k); + } + + /* Re-enable protection is necessary only after erase */ + if (command == NAND_CMD_ERASE1) + brcmnand_wp(mtd, 1); +} + +static uint8_t brcmnand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct brcmnand_host *host = chip->priv; + struct brcmnand_controller *ctrl = host->ctrl; + uint8_t ret = 0; + int addr, offs; + + switch (host->last_cmd) { + case NAND_CMD_READID: + if (host->last_byte < 4) + ret = brcmnand_read_reg(ctrl, BRCMNAND_ID) >> + (24 - (host->last_byte << 3)); + else if (host->last_byte < 8) + ret = brcmnand_read_reg(ctrl, BRCMNAND_ID_EXT) >> + (56 - (host->last_byte << 3)); + break; + + case NAND_CMD_READOOB: + ret = oob_reg_read(ctrl, host->last_byte); + break; + + case NAND_CMD_STATUS: + ret = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) & + INTFC_FLASH_STATUS; + if (wp_on) /* hide WP status */ + ret |= NAND_STATUS_WP; + break; + + case NAND_CMD_PARAM: + case NAND_CMD_RNDOUT: + addr = host->last_addr + host->last_byte; + offs = addr & (FC_BYTES - 1); + + /* At FC_BYTES boundary, switch to next column */ + if (host->last_byte > 0 && offs == 0) + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, addr, -1); +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) && defined(CONFIG_CPU_LITTLE_ENDIAN) + /* flash_cache always same as host endianess in dsl/pon chip */ + ret = ctrl->flash_cache[offs >> 2] >> + ((offs & 0x03) << 3); +#else + ret = ctrl->flash_cache[offs >> 2] >> + (24 - ((offs & 0x03) << 3)); +#endif + break; + case NAND_CMD_GET_FEATURES: + if (host->last_byte >= ONFI_SUBFEATURE_PARAM_LEN) { + ret = 0; + } else { + bool last = host->last_byte == + ONFI_SUBFEATURE_PARAM_LEN - 1; + brcmnand_low_level_op(host, LL_OP_RD, 0, last); + ret = brcmnand_read_reg(ctrl, BRCMNAND_LL_RDATA) & 0xff; + } + } + + dev_dbg(ctrl->dev, "read byte = 0x%02x\n", ret); + host->last_byte++; + + return ret; +} + +static void brcmnand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + + for (i = 0; i < len; i++, buf++) + *buf = brcmnand_read_byte(mtd); +} + +static void brcmnand_write_buf(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + struct brcmnand_host *host = chip->priv; + + switch (host->last_cmd) { + case NAND_CMD_SET_FEATURES: + for (i = 0; i < len; i++) + brcmnand_low_level_op(host, LL_OP_WR, buf[i], + (i + 1) == len); + break; + default: + BUG(); + break; + } +} + +/** + * Construct a FLASH_DMA descriptor as part of a linked list. You must know the + * following ahead of time: + * - Is this descriptor the beginning or end of a linked list? + * - What is the (DMA) address of the next descriptor in the linked list? + */ +static int brcmnand_fill_dma_desc(struct brcmnand_host *host, + struct brcm_nand_dma_desc *desc, u64 addr, + dma_addr_t buf, u32 len, u8 dma_cmd, + bool begin, bool end, + dma_addr_t next_desc) +{ + memset(desc, 0, sizeof(*desc)); + /* Descriptors are written in native byte order (wordwise) */ + desc->next_desc = lower_32_bits(next_desc); + desc->next_desc_ext = upper_32_bits(next_desc); + desc->cmd_irq = (dma_cmd << 24) | + (end ? (0x03 << 8) : 0) | /* IRQ | STOP */ + (!!begin) | ((!!end) << 1); /* head, tail */ +#ifdef CONFIG_CPU_BIG_ENDIAN + desc->cmd_irq |= 0x01 << 12; +#endif + desc->dram_addr = lower_32_bits(buf); + desc->dram_addr_ext = upper_32_bits(buf); + desc->tfr_len = len; + desc->total_len = len; + desc->flash_addr = lower_32_bits(addr); + desc->flash_addr_ext = upper_32_bits(addr); + desc->cs = host->cs; + desc->status_valid = 0x01; + return 0; +} + +/** + * Kick the FLASH_DMA engine, with a given DMA descriptor + */ +static void brcmnand_dma_run(struct brcmnand_host *host, dma_addr_t desc) +{ + struct brcmnand_controller *ctrl = host->ctrl; + unsigned long timeo = msecs_to_jiffies(100); + + flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC, lower_32_bits(desc)); + (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC); + flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC_EXT, upper_32_bits(desc)); + (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC_EXT); + + /* Start FLASH_DMA engine */ + ctrl->dma_pending = true; + mb(); /* flush previous writes */ + flash_dma_writel(ctrl, FLASH_DMA_CTRL, 0x03); /* wake | run */ + + if (wait_for_completion_timeout(&ctrl->dma_done, timeo) <= 0) { + dev_err(ctrl->dev, + "timeout waiting for DMA; status %#x, error status %#x\n", + flash_dma_readl(ctrl, FLASH_DMA_STATUS), + flash_dma_readl(ctrl, FLASH_DMA_ERROR_STATUS)); + } + ctrl->dma_pending = false; + flash_dma_writel(ctrl, FLASH_DMA_CTRL, 0); /* force stop */ +} + +static int brcmnand_dma_trans(struct brcmnand_host *host, u64 addr, u32 *buf, + u32 len, u8 dma_cmd) +{ + struct brcmnand_controller *ctrl = host->ctrl; + dma_addr_t buf_pa; + int dir = dma_cmd == CMD_PAGE_READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + + buf_pa = dma_map_single(ctrl->dev, buf, len, dir); + if (dma_mapping_error(ctrl->dev, buf_pa)) { + dev_err(ctrl->dev, "unable to map buffer for DMA\n"); + return -ENOMEM; + } + + brcmnand_fill_dma_desc(host, ctrl->dma_desc, addr, buf_pa, len, + dma_cmd, true, true, 0); + + brcmnand_dma_run(host, ctrl->dma_pa); + + dma_unmap_single(ctrl->dev, buf_pa, len, dir); + + if (ctrl->dma_desc->status_valid & FLASH_DMA_ECC_ERROR) + return -EBADMSG; + else if (ctrl->dma_desc->status_valid & FLASH_DMA_CORR_ERROR) + return -EUCLEAN; + + return 0; +} + +/* + * Assumes proper CS is already set + */ +static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip, + u64 addr, unsigned int trans, u32 *buf, + u8 *oob, u64 *err_addr) +{ + struct brcmnand_host *host = chip->priv; + struct brcmnand_controller *ctrl = host->ctrl; + int i, j, ret = 0; + + /* Clear error addresses */ + brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_ADDR, 0); + brcmnand_write_reg(ctrl, BRCMNAND_CORR_ADDR, 0); + + brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS, + (host->cs << 16) | ((addr >> 32) & 0xffff)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS); + + for (i = 0; i < trans; i++, addr += FC_BYTES) { + brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, + lower_32_bits(addr)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); + /* SPARE_AREA_READ does not use ECC, so just use PAGE_READ */ + brcmnand_send_cmd(host, CMD_PAGE_READ); + brcmnand_waitfunc(mtd, chip); + + if (likely(buf)) { + brcmnand_soc_data_bus_prepare(ctrl->soc); +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + (void)j; + memcpy((void*)buf, (void*)ctrl->nand_fc, FC_BYTES); + buf += FC_WORDS; +#else + for (j = 0; j < FC_WORDS; j++, buf++) + *buf = brcmnand_read_fc(ctrl, j); +#endif + brcmnand_soc_data_bus_unprepare(ctrl->soc); + } + + if (oob) + oob += read_oob_from_regs(ctrl, i, oob, + mtd->oobsize / trans, + host->hwcfg.sector_size_1k); + + if (!ret) { + *err_addr = brcmnand_read_reg(ctrl, + BRCMNAND_UNCORR_ADDR) | + ((u64)(brcmnand_read_reg(ctrl, + BRCMNAND_UNCORR_EXT_ADDR) + & 0xffff) << 32); + if (*err_addr) + ret = -EBADMSG; + } + + if (!ret) { + *err_addr = brcmnand_read_reg(ctrl, + BRCMNAND_CORR_ADDR) | + ((u64)(brcmnand_read_reg(ctrl, + BRCMNAND_CORR_EXT_ADDR) + & 0xffff) << 32); + if (*err_addr) + ret = -EUCLEAN; + } + } + + return ret; +} + +/* + * Check a page to see if it is erased (w/ bitflips) after an uncorrectable ECC + * error + * + * Because the HW ECC signals an ECC error if an erase paged has even a single + * bitflip, we must check each ECC error to see if it is actually an erased + * page with bitflips, not a truly corrupted page. + * + * On a real error, return a negative error code (-EBADMSG for ECC error), and + * buf will contain raw data. + * Otherwise, fill buf with 0xff and return the maximum number of + * bitflips-per-ECC-sector to the caller. + * + */ +static int brcmnand_verify_erased_page(struct mtd_info *mtd, + struct nand_chip *chip, void *buf, u64 addr) +{ + int i, sas, oob_nbits, data_nbits; + void *oob = chip->oob_poi; + unsigned int max_bitflips = 0; + int page = addr >> chip->page_shift; + int ret; +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + u8 *oobs = chip->oob_poi; + struct nand_ecclayout *ecclayout = chip->ecc.layout; + int oobofs_limit = 0, eccpos_idx = 0, check_ecc = 1, ecc_pos; + unsigned long ecc; +#endif + + if (!buf) { + buf = chip->buffers->databuf; + /* Invalidate page cache */ + chip->pagebuf = -1; + } + + sas = mtd->oobsize / chip->ecc.steps; +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + oob_nbits = sizeof(ecc)<<3; +#else + oob_nbits = sas << 3; +#endif + data_nbits = chip->ecc.size << 3; + + /* read without ecc for verification */ + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); + ret = chip->ecc.read_page_raw(mtd, chip, buf, true, page); + if (ret) + return ret; + + for (i = 0; i < chip->ecc.steps; i++, oob += sas) { + unsigned int bitflips = 0; + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + oobofs_limit = (i + 1)*sas; + /* only check for ECC bytes because JFFS2 may already write OOB */ + /* check number ecc bit flip within each ecc step size */ + while ( eccpos_idx < MTD_MAX_ECCPOS_ENTRIES_LARGE && check_ecc ) { + ecc_pos = ecclayout->eccpos[eccpos_idx]; + if (ecc_pos == 0) { + /* no more ecc bytes all done */ + check_ecc = 0; + break; + } else if (ecc_pos < oobofs_limit) { + /* this ecc bytes belong to this subpage, count any bit flip */ + ecc = (unsigned long)oobs[ecc_pos]; + bitflips += 8 - bitmap_weight(&ecc, oob_nbits); /* only lowest 8 bit matters */ + eccpos_idx++; + } else { + /* done with this subpage */ + break; + } + } +#else + bitflips += oob_nbits - bitmap_weight(oob, oob_nbits); +#endif + bitflips += data_nbits - bitmap_weight(buf, data_nbits); + + buf += chip->ecc.size; + addr += chip->ecc.size; + + /* Too many bitflips */ + if (bitflips > chip->ecc.strength) + return -EBADMSG; + + max_bitflips = max(max_bitflips, bitflips); + } + + return max_bitflips; +} + +static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip, + u64 addr, unsigned int trans, u32 *buf, u8 *oob) +{ + struct brcmnand_host *host = chip->priv; + struct brcmnand_controller *ctrl = host->ctrl; + u64 err_addr = 0; + int err; + bool retry = true; +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + struct nand_ecclayout *ecclayout = chip->ecc.layout; + int eccpos, eccpos_idx = 0; +#endif + dev_dbg(ctrl->dev, "read %llx -> %p\n", (unsigned long long)addr, buf); + +try_dmaread: + brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_COUNT, 0); + + if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) { + err = brcmnand_dma_trans(host, addr, buf, trans * FC_BYTES, + CMD_PAGE_READ); + if (err) { + if (mtd_is_bitflip_or_eccerr(err)) + err_addr = addr; + else + return -EIO; + } + } else { + if (oob) + memset(oob, 0x99, mtd->oobsize); + + err = brcmnand_read_by_pio(mtd, chip, addr, trans, buf, + oob, &err_addr); + } + + if (mtd_is_eccerr(err)) { + int ret; + /* + * On oontroller version >=7.0 if we are doing a DMA read + * after a prior PIO read that reported uncorrectable error, + * the DMA engine captures this error following DMA read + * cleared only on subsequent DMA read, so just retry once + * to clear a possible false error reported for current DMA + * read + */ + if ((ctrl->nand_version >= 0x0700) && retry) { + retry = false; + goto try_dmaread; + } + ret = brcmnand_verify_erased_page(mtd, chip, buf, addr); + if (ret < 0) { + dev_err(ctrl->dev, "uncorrectable error at 0x%llx\n", + (unsigned long long)err_addr); + mtd->ecc_stats.failed++; + /* NAND layer expects zero on ECC errors */ + return 0; + } else { + if (buf) + memset(buf, 0xff, FC_BYTES * trans); + if (oob) { +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + /* only restore 0xff on the ecc bytes only because JFFS2 may already + * write cleanmarker in oob + */ + while ((eccpos = ecclayout->eccpos[eccpos_idx])) { + oob[eccpos] = 0xff; + eccpos_idx++; + } +#else + memset(oob, 0xff, mtd->oobsize); +#endif + } + + if (ret) + dev_info(&host->pdev->dev, + "corrected %d bitflips in blank page at 0x%llx\n", + ret, (unsigned long long)addr); + return ret; + } + } + + if (mtd_is_bitflip(err)) { + unsigned int corrected = brcmnand_count_corrected(ctrl); + + dev_dbg(ctrl->dev, "corrected error at 0x%llx\n", + (unsigned long long)err_addr); + mtd->ecc_stats.corrected += corrected; + /* Always exceed the software-imposed threshold */ + return max(mtd->bitflip_threshold, corrected); + } + + return 0; +} + +static int brcmnand_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct brcmnand_host *host = chip->priv; + u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL; + + return brcmnand_read(mtd, chip, host->last_addr, + mtd->writesize >> FC_SHIFT, (u32 *)buf, oob); +} + +static int brcmnand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct brcmnand_host *host = chip->priv; + u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL; + int ret; + + brcmnand_set_ecc_enabled(host, 0); + ret = brcmnand_read(mtd, chip, host->last_addr, + mtd->writesize >> FC_SHIFT, (u32 *)buf, oob); + brcmnand_set_ecc_enabled(host, 1); + return ret; +} + +static int brcmnand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + return brcmnand_read(mtd, chip, (u64)page << chip->page_shift, + mtd->writesize >> FC_SHIFT, + NULL, (u8 *)chip->oob_poi); +} + +static int brcmnand_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct brcmnand_host *host = chip->priv; + + brcmnand_set_ecc_enabled(host, 0); + brcmnand_read(mtd, chip, (u64)page << chip->page_shift, + mtd->writesize >> FC_SHIFT, + NULL, (u8 *)chip->oob_poi); + brcmnand_set_ecc_enabled(host, 1); + return 0; +} + +static int brcmnand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, + uint32_t data_offs, uint32_t readlen, + uint8_t *bufpoi, int page) +{ + struct brcmnand_host *host = chip->priv; + + return brcmnand_read(mtd, chip, host->last_addr + data_offs, + readlen >> FC_SHIFT, (u32 *)bufpoi, NULL); +} + +static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip, + u64 addr, const u32 *buf, u8 *oob) +{ + struct brcmnand_host *host = chip->priv; + struct brcmnand_controller *ctrl = host->ctrl; + unsigned int i, j, trans = mtd->writesize >> FC_SHIFT; + int status, ret = 0; +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + u8* oob_in = oob; + if (brcmnand_check_dying_gasp(ctrl->soc)) { + dev_warn(ctrl->dev, "system is losing power, abort nand write at offset 0x%llx\n", addr); + return -EIO; + } +#endif + + dev_dbg(ctrl->dev, "write %llx <- %p\n", (unsigned long long)addr, buf); + + if (unlikely((unsigned long)buf & 0x03)) { + dev_warn(ctrl->dev, "unaligned buffer: %p\n", buf); + buf = (u32 *)((unsigned long)buf & ~0x03); + } + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + if ((chip->options & NAND_PAGE_NOP1) && !buf && oob) { // quit if writing OOB only to NOP=1 parallel NAND device + return 0; + } + + if( buf && !oob ) { + u64 err_addr = 0; + oob = (u8 *)chip->oob_poi; + brcmnand_read_by_pio(mtd, chip, addr, trans, NULL, oob, &err_addr); + } +#endif + + brcmnand_wp(mtd, 0); + + for (i = 0; i < ctrl->max_oob; i += 4) + oob_reg_write(ctrl, i, 0xffffffff); + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + if (has_flash_dma(ctrl) && !oob_in && flash_dma_buf_ok(buf)) { +#else + if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) { +#endif + if (brcmnand_dma_trans(host, addr, (u32 *)buf, + mtd->writesize, CMD_PROGRAM_PAGE)) + ret = -EIO; + goto out; + } + + brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS, + (host->cs << 16) | ((addr >> 32) & 0xffff)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS); + + for (i = 0; i < trans; i++, addr += FC_BYTES) { + /* full address MUST be set before populating FC */ + brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, + lower_32_bits(addr)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); + + if (buf) { + brcmnand_soc_data_bus_prepare(ctrl->soc); + + for (j = 0; j < FC_WORDS; j++, buf++) + brcmnand_write_fc(ctrl, j, *buf); + + brcmnand_soc_data_bus_unprepare(ctrl->soc); + } else if (oob) { + for (j = 0; j < FC_WORDS; j++) + brcmnand_write_fc(ctrl, j, 0xffffffff); + } + + if (oob) { + oob += write_oob_to_regs(ctrl, i, oob, + mtd->oobsize / trans, + host->hwcfg.sector_size_1k); + } + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + if( !buf && oob ) { + brcmnand_set_ecc_enabled(host, 0); + } +#endif + /* we cannot use SPARE_AREA_PROGRAM when PARTIAL_PAGE_EN=0 */ + brcmnand_send_cmd(host, CMD_PROGRAM_PAGE); + status = brcmnand_waitfunc(mtd, chip); + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + if( !buf && oob ) + brcmnand_set_ecc_enabled(host, 1); +#endif + if (status & NAND_STATUS_FAIL) { + dev_info(ctrl->dev, "program failed at %llx\n", + (unsigned long long)addr); + ret = -EIO; + goto out; + } + } +out: + brcmnand_wp(mtd, 1); + return ret; +} + +static int brcmnand_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required) +{ + struct brcmnand_host *host = chip->priv; + void *oob = oob_required ? chip->oob_poi : NULL; + + return brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob); +} + +static int brcmnand_write_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf, + int oob_required) +{ + struct brcmnand_host *host = chip->priv; + void *oob = oob_required ? chip->oob_poi : NULL; + int ret; + + brcmnand_set_ecc_enabled(host, 0); + ret = brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob); + brcmnand_set_ecc_enabled(host, 1); + return ret; +} + +static int brcmnand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + return brcmnand_write(mtd, chip, (u64)page << chip->page_shift, + NULL, chip->oob_poi); +} + +static int brcmnand_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct brcmnand_host *host = chip->priv; + int ret; + + brcmnand_set_ecc_enabled(host, 0); + ret = brcmnand_write(mtd, chip, (u64)page << chip->page_shift, NULL, + (u8 *)chip->oob_poi); + brcmnand_set_ecc_enabled(host, 1); + + return ret; +} + +/*********************************************************************** + * Per-CS setup (1 NAND device) + ***********************************************************************/ + +static int brcmnand_set_cfg(struct brcmnand_host *host, + struct brcmnand_cfg *cfg) +{ + struct brcmnand_controller *ctrl = host->ctrl; + struct nand_chip *chip = &host->chip; + u16 cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG); + u16 cfg_ext_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_CFG_EXT); + u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_ACC_CONTROL); + u8 block_size = 0, page_size = 0, device_size = 0; + u32 tmp; + + if (ctrl->block_sizes) { + int i, found; + + for (i = 0, found = 0; ctrl->block_sizes[i]; i++) + if (ctrl->block_sizes[i] * 1024 == cfg->block_size) { + block_size = i; + found = 1; + } + if (!found) { + dev_warn(ctrl->dev, "invalid block size %u\n", + cfg->block_size); + return -EINVAL; + } + } else { + block_size = ffs(cfg->block_size) - ffs(BRCMNAND_MIN_BLOCKSIZE); + } + + if (cfg->block_size < BRCMNAND_MIN_BLOCKSIZE || (ctrl->max_block_size && + cfg->block_size > ctrl->max_block_size)) { + dev_warn(ctrl->dev, "invalid block size %u\n", + cfg->block_size); + block_size = 0; + } + + if (ctrl->page_sizes) { + int i, found; + + for (i = 0, found = 0; ctrl->page_sizes[i]; i++) + if (ctrl->page_sizes[i] == cfg->page_size) { + page_size = i; + found = 1; + } + if (!found) { + dev_warn(ctrl->dev, "invalid page size %u\n", + cfg->page_size); + return -EINVAL; + } + } else { + page_size = ffs(cfg->page_size) - ffs(BRCMNAND_MIN_PAGESIZE); + } + + if (cfg->page_size < BRCMNAND_MIN_PAGESIZE || (ctrl->max_page_size && + cfg->page_size > ctrl->max_page_size)) { + dev_warn(ctrl->dev, "invalid page size %u\n", cfg->page_size); + return -EINVAL; + } + + if (fls64(cfg->device_size) < fls64(BRCMNAND_MIN_DEVSIZE)) { + dev_warn(ctrl->dev, "invalid device size 0x%llx\n", + (unsigned long long)cfg->device_size); + return -EINVAL; + } + device_size = fls64(cfg->device_size) - fls64(BRCMNAND_MIN_DEVSIZE); + + tmp = (cfg->blk_adr_bytes << 8) | + (cfg->col_adr_bytes << 12) | + (cfg->ful_adr_bytes << 16) | + (!!(cfg->device_width == 16) << 23) | + (device_size << 24); + if (cfg_offs == cfg_ext_offs) { + tmp |= (page_size << 20) | (block_size << 28); + nand_writereg(ctrl, cfg_offs, tmp); + } else { + nand_writereg(ctrl, cfg_offs, tmp); + tmp = page_size | (block_size << 4); + nand_writereg(ctrl, cfg_ext_offs, tmp); + } + + tmp = nand_readreg(ctrl, acc_control_offs); + tmp &= ~brcmnand_ecc_level_mask(ctrl); + tmp |= cfg->ecc_level << NAND_ACC_CONTROL_ECC_SHIFT; + tmp &= ~brcmnand_spare_area_mask(ctrl); + tmp |= cfg->spare_area_size; + nand_writereg(ctrl, acc_control_offs, tmp); + + brcmnand_set_sector_size_1k(host, cfg->sector_size_1k); + + /* threshold = ceil(BCH-level * 0.75) */ + brcmnand_wr_corr_thresh(host, DIV_ROUND_UP(chip->ecc.strength * 3, 4)); + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + brcmnand_adjust_timing(host, cfg); +#endif + + return 0; +} + +static void brcmnand_print_cfg(struct brcmnand_host *host, + char *buf, struct brcmnand_cfg *cfg) +{ + buf += sprintf(buf, + "%lluMiB total, %uKiB blocks, %u%s pages, %uB OOB, %u-bit", + (unsigned long long)cfg->device_size >> 20, + cfg->block_size >> 10, + cfg->page_size >= 1024 ? cfg->page_size >> 10 : cfg->page_size, + cfg->page_size >= 1024 ? "KiB" : "B", + cfg->spare_area_size, cfg->device_width); + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + if (host->chip.options & NAND_PAGE_NOP1) + buf += sprintf(buf, ", NOP=1"); +#endif + + /* Account for Hamming ECC and for BCH 512B vs 1KiB sectors */ + if (is_hamming_ecc(host->ctrl, cfg)) + sprintf(buf, ", Hamming ECC"); + else if (cfg->sector_size_1k) + sprintf(buf, ", BCH-%u (1KiB sector)", cfg->ecc_level << 1); + else + sprintf(buf, ", BCH-%u", cfg->ecc_level); +} + +/* + * Minimum number of bytes to address a page. Calculated as: + * roundup(log2(size / page-size) / 8) + * + * NB: the following does not "round up" for non-power-of-2 'size'; but this is + * OK because many other things will break if 'size' is irregular... + */ +static inline int get_blk_adr_bytes(u64 size, u32 writesize) +{ + return ALIGN(ilog2(size) - ilog2(writesize), 8) >> 3; +} + +static int brcmnand_setup_dev(struct brcmnand_host *host) +{ + struct mtd_info *mtd = &host->mtd; + struct nand_chip *chip = &host->chip; + struct brcmnand_controller *ctrl = host->ctrl; + struct brcmnand_cfg *cfg = &host->hwcfg; + char msg[128]; + u32 offs, tmp, oob_sector; + int ret; +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + int sector_size_1k = 0; +#endif + + memset(cfg, 0, sizeof(*cfg)); + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + /* save the timing parameter from nand id table */ + cfg->timing_1 = chip->timing_1; + cfg->timing_2 = chip->timing_2; + + /* set ECC size and strength based on hw configuration from strap + * if dtb does not specify them + */ + sector_size_1k = brcmnand_get_sector_size_1k(host); + if (chip->ecc.size == 0) { + if (sector_size_1k< 0) + chip->ecc.size = 512; + else + chip->ecc.size = 512<ecc.strength = brcmnand_get_ecc_strength(host); + if (chip->ecc.strength == 0) { + dev_err(ctrl->dev, "ECC disable not supported\n"); + return -EINVAL; + } +#endif + + ret = of_property_read_u32(chip->dn, "brcm,nand-oob-sector-size", + &oob_sector); + if (ret) { +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + /* set spare area based on hw configuration from strap + * if dtb does not specify them + */ + cfg->spare_area_size = brcmnand_get_spare_size(host); +#else + /* Use detected size */ + cfg->spare_area_size = mtd->oobsize / + (mtd->writesize >> FC_SHIFT); +#endif + } else { + cfg->spare_area_size = oob_sector; + } + if (cfg->spare_area_size > ctrl->max_oob) + cfg->spare_area_size = ctrl->max_oob; + /* + * Set oobsize to be consistent with controller's spare_area_size, as + * the rest is inaccessible. + */ + mtd->oobsize = cfg->spare_area_size * (mtd->writesize >> FC_SHIFT); + + cfg->device_size = mtd->size; + cfg->block_size = mtd->erasesize; + cfg->page_size = mtd->writesize; + cfg->device_width = (chip->options & NAND_BUSWIDTH_16) ? 16 : 8; + cfg->col_adr_bytes = 2; + cfg->blk_adr_bytes = get_blk_adr_bytes(mtd->size, mtd->writesize); + + switch (chip->ecc.size) { + case 512: + if (chip->ecc.strength == 1) /* Hamming */ + cfg->ecc_level = 15; + else + cfg->ecc_level = chip->ecc.strength; + cfg->sector_size_1k = 0; + break; + case 1024: + if (!(ctrl->features & BRCMNAND_HAS_1K_SECTORS)) { + dev_err(ctrl->dev, "1KB sectors not supported\n"); + return -EINVAL; + } + if (chip->ecc.strength & 0x1) { + dev_err(ctrl->dev, + "odd ECC not supported with 1KB sectors\n"); + return -EINVAL; + } + + cfg->ecc_level = chip->ecc.strength >> 1; + cfg->sector_size_1k = 1; + break; + default: + dev_err(ctrl->dev, "unsupported ECC size: %d\n", + chip->ecc.size); + return -EINVAL; + } + + cfg->ful_adr_bytes = cfg->blk_adr_bytes; + if (mtd->writesize > 512) + cfg->ful_adr_bytes += cfg->col_adr_bytes; + else + cfg->ful_adr_bytes += 1; + + ret = brcmnand_set_cfg(host, cfg); + if (ret) + return ret; + + brcmnand_set_ecc_enabled(host, 1); + + brcmnand_print_cfg(host, msg, cfg); + dev_info(ctrl->dev, "detected %s\n", msg); + + /* Configure ACC_CONTROL */ + offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL); + tmp = nand_readreg(ctrl, offs); + tmp &= ~ACC_CONTROL_PARTIAL_PAGE; + tmp &= ~ACC_CONTROL_RD_ERASED; + + /* We need to turn on Read from erased paged protected by ECC */ + if (ctrl->nand_version >= 0x0702) + tmp |= ACC_CONTROL_RD_ERASED; + tmp &= ~ACC_CONTROL_FAST_PGM_RDIN; + if (ctrl->features & BRCMNAND_HAS_PREFETCH) { + /* + * FIXME: Flash DMA + prefetch may see spurious erased-page ECC + * errors + */ + if (has_flash_dma(ctrl)) + tmp &= ~ACC_CONTROL_PREFETCH; + else + tmp |= ACC_CONTROL_PREFETCH; + } + nand_writereg(ctrl, offs, tmp); + + return 0; +} + +static int brcmnand_init_cs(struct brcmnand_host *host) +{ + struct brcmnand_controller *ctrl = host->ctrl; + struct device_node *dn = host->of_node; + struct platform_device *pdev = host->pdev; + struct mtd_info *mtd; + struct nand_chip *chip; + int ret; +#if !defined(CONFIG_BCM_KF_MTD_BCMNAND) + struct mtd_part_parser_data ppdata = { .of_node = dn }; +#endif + + ret = of_property_read_u32(dn, "reg", &host->cs); + if (ret) { + dev_err(&pdev->dev, "can't get chip-select\n"); + return -ENXIO; + } + + mtd = &host->mtd; + chip = &host->chip; + + chip->dn = dn; + chip->priv = host; + mtd->priv = chip; + mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "brcmnand.%d", + host->cs); + mtd->owner = THIS_MODULE; + mtd->dev.parent = &pdev->dev; + + chip->IO_ADDR_R = (void __iomem *)0xdeadbeef; + chip->IO_ADDR_W = (void __iomem *)0xdeadbeef; + + chip->cmd_ctrl = brcmnand_cmd_ctrl; + chip->cmdfunc = brcmnand_cmdfunc; + chip->waitfunc = brcmnand_waitfunc; + chip->read_byte = brcmnand_read_byte; + chip->read_buf = brcmnand_read_buf; + chip->write_buf = brcmnand_write_buf; + + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.read_page = brcmnand_read_page; + chip->ecc.read_subpage = brcmnand_read_subpage; + chip->ecc.write_page = brcmnand_write_page; + chip->ecc.read_page_raw = brcmnand_read_page_raw; + chip->ecc.write_page_raw = brcmnand_write_page_raw; + chip->ecc.write_oob_raw = brcmnand_write_oob_raw; + chip->ecc.read_oob_raw = brcmnand_read_oob_raw; + chip->ecc.read_oob = brcmnand_read_oob; + chip->ecc.write_oob = brcmnand_write_oob; + + chip->controller = &ctrl->controller; + + if (nand_scan_ident(mtd, 1, NULL)) + return -ENXIO; + + chip->options |= NAND_NO_SUBPAGE_WRITE; + /* + * Avoid (for instance) kmap()'d buffers from JFFS2, which we can't DMA + * to/from, and have nand_base pass us a bounce buffer instead, as + * needed. + */ + chip->options |= NAND_USE_BOUNCE_BUFFER; + + if (of_get_nand_on_flash_bbt(dn)) + chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; + + if (brcmnand_setup_dev(host)) + return -ENXIO; + + chip->ecc.size = host->hwcfg.sector_size_1k ? 1024 : 512; + /* only use our internal HW threshold */ + mtd->bitflip_threshold = 1; + + chip->ecc.layout = brcmnand_choose_ecc_layout(host); + if (!chip->ecc.layout) + return -ENXIO; + + if (nand_scan_tail(mtd)) + return -ENXIO; + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + setup_mtd_parts(mtd); + return 0; +#else + return mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); +#endif +} + +static void brcmnand_save_restore_cs_config(struct brcmnand_host *host, + int restore) +{ + struct brcmnand_controller *ctrl = host->ctrl; + u16 cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG); + u16 cfg_ext_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_CFG_EXT); + u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_ACC_CONTROL); + u16 t1_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING1); + u16 t2_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING2); + + if (restore) { + nand_writereg(ctrl, cfg_offs, host->hwcfg.config); + if (cfg_offs != cfg_ext_offs) + nand_writereg(ctrl, cfg_ext_offs, + host->hwcfg.config_ext); + nand_writereg(ctrl, acc_control_offs, host->hwcfg.acc_control); + nand_writereg(ctrl, t1_offs, host->hwcfg.timing_1); + nand_writereg(ctrl, t2_offs, host->hwcfg.timing_2); + } else { + host->hwcfg.config = nand_readreg(ctrl, cfg_offs); + if (cfg_offs != cfg_ext_offs) + host->hwcfg.config_ext = + nand_readreg(ctrl, cfg_ext_offs); + host->hwcfg.acc_control = nand_readreg(ctrl, acc_control_offs); + host->hwcfg.timing_1 = nand_readreg(ctrl, t1_offs); + host->hwcfg.timing_2 = nand_readreg(ctrl, t2_offs); + } +} + +static int brcmnand_suspend(struct device *dev) +{ + struct brcmnand_controller *ctrl = dev_get_drvdata(dev); + struct brcmnand_host *host; + + list_for_each_entry(host, &ctrl->host_list, node) + brcmnand_save_restore_cs_config(host, 0); + + ctrl->nand_cs_nand_select = brcmnand_read_reg(ctrl, BRCMNAND_CS_SELECT); + ctrl->nand_cs_nand_xor = brcmnand_read_reg(ctrl, BRCMNAND_CS_XOR); + ctrl->corr_stat_threshold = + brcmnand_read_reg(ctrl, BRCMNAND_CORR_THRESHOLD); + + if (has_flash_dma(ctrl)) + ctrl->flash_dma_mode = flash_dma_readl(ctrl, FLASH_DMA_MODE); + + return 0; +} + +static int brcmnand_resume(struct device *dev) +{ + struct brcmnand_controller *ctrl = dev_get_drvdata(dev); + struct brcmnand_host *host; + + if (has_flash_dma(ctrl)) { + flash_dma_writel(ctrl, FLASH_DMA_MODE, ctrl->flash_dma_mode); + flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0); + } + + brcmnand_write_reg(ctrl, BRCMNAND_CS_SELECT, ctrl->nand_cs_nand_select); + brcmnand_write_reg(ctrl, BRCMNAND_CS_XOR, ctrl->nand_cs_nand_xor); + brcmnand_write_reg(ctrl, BRCMNAND_CORR_THRESHOLD, + ctrl->corr_stat_threshold); + if (ctrl->soc) { + /* Clear/re-enable interrupt */ + ctrl->soc->ctlrdy_ack(ctrl->soc); + ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true); + } + + list_for_each_entry(host, &ctrl->host_list, node) { + struct mtd_info *mtd = &host->mtd; + struct nand_chip *chip = mtd->priv; + + brcmnand_save_restore_cs_config(host, 1); + + /* Reset the chip, required by some chips after power-up */ + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + } + + return 0; +} + +const struct dev_pm_ops brcmnand_pm_ops = { + .suspend = brcmnand_suspend, + .resume = brcmnand_resume, +}; +EXPORT_SYMBOL_GPL(brcmnand_pm_ops); + +static const struct of_device_id brcmnand_of_match[] = { + { .compatible = "brcm,brcmnand-v4.0" }, + { .compatible = "brcm,brcmnand-v5.0" }, + { .compatible = "brcm,brcmnand-v6.0" }, + { .compatible = "brcm,brcmnand-v6.1" }, + { .compatible = "brcm,brcmnand-v6.2" }, + { .compatible = "brcm,brcmnand-v7.0" }, + { .compatible = "brcm,brcmnand-v7.1" }, + { .compatible = "brcm,brcmnand-v7.2" }, + {}, +}; +MODULE_DEVICE_TABLE(of, brcmnand_of_match); + +/*********************************************************************** + * Platform driver setup (per controller) + ***********************************************************************/ + +int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc) +{ + struct device *dev = &pdev->dev; + struct device_node *dn = dev->of_node, *child; + struct brcmnand_controller *ctrl; + struct resource *res; + int ret; + + /* We only support device-tree instantiation */ + if (!dn) + return -ENODEV; + + if (!of_match_node(brcmnand_of_match, dn)) + return -ENODEV; + + ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + dev_set_drvdata(dev, ctrl); + ctrl->dev = dev; + + init_completion(&ctrl->done); + init_completion(&ctrl->dma_done); + spin_lock_init(&ctrl->controller.lock); + init_waitqueue_head(&ctrl->controller.wq); + INIT_LIST_HEAD(&ctrl->host_list); + + /* NAND register range */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ctrl->nand_base = devm_ioremap_resource(dev, res); + + if (IS_ERR(ctrl->nand_base)) + return PTR_ERR(ctrl->nand_base); + + /* Initialize NAND revision */ + ret = brcmnand_revision_init(ctrl); + if (ret) + return ret; + + /* + * Most chips have this cache at a fixed offset within 'nand' block. + * Some must specify this region separately. + */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand-cache"); + if (res) { + ctrl->nand_fc = devm_ioremap_resource(dev, res); + + if (IS_ERR(ctrl->nand_fc)) + return PTR_ERR(ctrl->nand_fc); + } else { + ctrl->nand_fc = ctrl->nand_base + + ctrl->reg_offsets[BRCMNAND_FC_BASE]; + } + + /* FLASH_DMA */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "flash-dma"); + if (res) { + ctrl->flash_dma_base = devm_ioremap_resource(dev, res); + if (IS_ERR(ctrl->flash_dma_base)) + return PTR_ERR(ctrl->flash_dma_base); + + flash_dma_writel(ctrl, FLASH_DMA_MODE, 1); /* linked-list */ + flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0); + + /* Allocate descriptor(s) */ + ctrl->dma_desc = dmam_alloc_coherent(dev, + sizeof(*ctrl->dma_desc), + &ctrl->dma_pa, GFP_KERNEL); + if (!ctrl->dma_desc) + return -ENOMEM; + + ctrl->dma_irq = platform_get_irq(pdev, 1); + if ((int)ctrl->dma_irq < 0) { + dev_err(dev, "missing FLASH_DMA IRQ\n"); + return -ENODEV; + } + + ret = devm_request_irq(dev, ctrl->dma_irq, + brcmnand_dma_irq, 0, DRV_NAME, + ctrl); + if (ret < 0) { + dev_err(dev, "can't allocate IRQ %d: error %d\n", + ctrl->dma_irq, ret); + return ret; + } + + dev_info(dev, "enabling FLASH_DMA\n"); + } + + /* Disable automatic device ID config, direct addressing */ + brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT, + CS_SELECT_AUTO_DEVICE_ID_CFG | 0xff, 0, 0); + /* Disable XOR addressing */ + brcmnand_rmw_reg(ctrl, BRCMNAND_CS_XOR, 0xff, 0, 0); + + if (ctrl->features & BRCMNAND_HAS_WP) { + /* Permanently disable write protection */ + if (wp_on == 2) + brcmnand_set_wp(ctrl, false); + } else { + wp_on = 0; + } + + /* IRQ */ + ctrl->irq = platform_get_irq(pdev, 0); + if ((int)ctrl->irq < 0) { +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +#if !defined(CONFIG_BCM947189) + ctrl->irq = INTERRUPT_ID_NAND_FLASH; +#endif + /* switch to polling for better throughput for now */ + ctrl->polling = 1; + ctrl->soc = soc; +#else + return -ENODEV; +#endif + } + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + if (!ctrl->polling) { +#endif + /* + * Some SoCs integrate this controller (e.g., its interrupt bits) in + * interesting ways + */ + if (soc) { + ctrl->soc = soc; + + ret = devm_request_irq(dev, ctrl->irq, brcmnand_irq, 0, + DRV_NAME, ctrl); + + /* Enable interrupt */ + ctrl->soc->ctlrdy_ack(ctrl->soc); + ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true); + } else { + /* Use standard interrupt infrastructure */ + ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0, + DRV_NAME, ctrl); + } + if (ret < 0) { + dev_err(dev, "can't allocate IRQ %d: error %d\n", + ctrl->irq, ret); + return ret; + } +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + } +#endif + + for_each_available_child_of_node(dn, child) { + if (of_device_is_compatible(child, "brcm,nandcs")) { + struct brcmnand_host *host; + + host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); + if (!host) + return -ENOMEM; + host->pdev = pdev; + host->ctrl = ctrl; + host->of_node = child; + + ret = brcmnand_init_cs(host); + if (ret) + continue; /* Try all chip-selects */ + + list_add_tail(&host->node, &ctrl->host_list); + } + } + + /* No chip-selects could initialize properly */ + if (list_empty(&ctrl->host_list)) + return -ENODEV; + + return 0; +} +EXPORT_SYMBOL_GPL(brcmnand_probe); + +int brcmnand_remove(struct platform_device *pdev) +{ + struct brcmnand_controller *ctrl = dev_get_drvdata(&pdev->dev); + struct brcmnand_host *host; + + list_for_each_entry(host, &ctrl->host_list, node) + nand_release(&host->mtd); + + dev_set_drvdata(&pdev->dev, NULL); + + return 0; +} +EXPORT_SYMBOL_GPL(brcmnand_remove); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Kevin Cernekee"); +MODULE_AUTHOR("Brian Norris"); +MODULE_DESCRIPTION("NAND driver for Broadcom chips"); +MODULE_ALIAS("platform:brcmnand"); + +#endif diff -urN linux-4.1.52-orig/drivers/mtd/nand/brcmnand/brcmnand.h linux-4.1.52/drivers/mtd/nand/brcmnand/brcmnand.h --- linux-4.1.52-orig/drivers/mtd/nand/brcmnand/brcmnand.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/mtd/nand/brcmnand/brcmnand.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,109 @@ +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +/* +<:copyright-BRCM:2016:GPL/GPL:standard + + Copyright (c) 2016 Broadcom + All Rights Reserved + +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 (the "GPL"). + +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. + + +A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by +writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + +:> +*/ + +#ifndef __BRCMNAND_H__ +#define __BRCMNAND_H__ + +#include +#include +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +#include +#include + +extern int setup_mtd_parts(struct mtd_info* mtd); +#endif + +struct platform_device; +struct dev_pm_ops; + +struct brcmnand_soc { + struct platform_device *pdev; + void *priv; + bool (*ctlrdy_ack)(struct brcmnand_soc *soc); + void (*ctlrdy_set_enabled)(struct brcmnand_soc *soc, bool en); + void (*prepare_data_bus)(struct brcmnand_soc *soc, bool prepare); +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + int (*check_dying_gasp)(struct brcmnand_soc *soc); +#endif +}; + +static inline void brcmnand_soc_data_bus_prepare(struct brcmnand_soc *soc) +{ + if (soc && soc->prepare_data_bus) + soc->prepare_data_bus(soc, true); +} + +static inline void brcmnand_soc_data_bus_unprepare(struct brcmnand_soc *soc) +{ + if (soc && soc->prepare_data_bus) + soc->prepare_data_bus(soc, false); +} + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +/* Check if system is losing power. Abort any write or erase request if system + * is shutting down to avoid any partial write or erase to the NAND media. Otherwise + * the impacted block or page may be unstable + */ +static inline int brcmnand_check_dying_gasp(struct brcmnand_soc *soc) +{ + int ret = 0; + if (soc && soc->check_dying_gasp) + ret = soc->check_dying_gasp(soc); + return ret; +} +#endif + +static inline u32 brcmnand_readl(void __iomem *addr) +{ + /* + * MIPS endianness is configured by boot strap, which also reverses all + * bus endianness (i.e., big-endian CPU + big endian bus ==> native + * endian I/O). + * + * Other architectures (e.g., ARM) either do not support big endian, or + * else leave I/O in little endian mode. + */ + if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN)) + return __raw_readl(addr); + else + return readl_relaxed(addr); +} + +static inline void brcmnand_writel(u32 val, void __iomem *addr) +{ + /* See brcmnand_readl() comments */ + if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN)) + __raw_writel(val, addr); + else + writel_relaxed(val, addr); +} + +int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc); +int brcmnand_remove(struct platform_device *pdev); + +extern const struct dev_pm_ops brcmnand_pm_ops; + +#endif /* __BRCMNAND_H__ */ + +#endif diff -urN linux-4.1.52-orig/drivers/mtd/nand/brcmnand/brcmstb_nand.c linux-4.1.52/drivers/mtd/nand/brcmnand/brcmstb_nand.c --- linux-4.1.52-orig/drivers/mtd/nand/brcmnand/brcmstb_nand.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/mtd/nand/brcmnand/brcmstb_nand.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,56 @@ +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +/* +<:copyright-BRCM:2016:GPL/GPL:standard + + Copyright (c) 2016 Broadcom + All Rights Reserved + +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 (the "GPL"). + +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. + + +A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by +writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + +:> +*/ + +#include +#include +#include + +#include "brcmnand.h" + +static const struct of_device_id brcmstb_nand_of_match[] = { + { .compatible = "brcm,brcmnand" }, + {}, +}; +MODULE_DEVICE_TABLE(of, brcmstb_nand_of_match); + +static int brcmstb_nand_probe(struct platform_device *pdev) +{ + return brcmnand_probe(pdev, NULL); +} + +static struct platform_driver brcmstb_nand_driver = { + .probe = brcmstb_nand_probe, + .remove = brcmnand_remove, + .driver = { + .name = "brcmstb_nand", + .pm = &brcmnand_pm_ops, + .of_match_table = brcmstb_nand_of_match, + } +}; +module_platform_driver(brcmstb_nand_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Brian Norris"); +MODULE_DESCRIPTION("NAND driver for Broadcom STB chips"); +#endif diff -urN linux-4.1.52-orig/drivers/mtd/nand/brcmnand/iproc_nand.c linux-4.1.52/drivers/mtd/nand/brcmnand/iproc_nand.c --- linux-4.1.52-orig/drivers/mtd/nand/brcmnand/iproc_nand.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/mtd/nand/brcmnand/iproc_nand.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,164 @@ +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +/* +<:copyright-BRCM:2016:GPL/GPL:standard + + Copyright (c) 2016 Broadcom + All Rights Reserved + +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 (the "GPL"). + +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. + + +A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by +writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + +:> +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "brcmnand.h" + +struct iproc_nand_soc_priv { + void __iomem *idm_base; + void __iomem *ext_base; + spinlock_t idm_lock; +}; + +#define IPROC_NAND_CTLR_READY_OFFSET 0x10 +#define IPROC_NAND_CTLR_READY BIT(0) + +#define IPROC_NAND_IO_CTRL_OFFSET 0x00 +#define IPROC_NAND_APB_LE_MODE BIT(24) +#define IPROC_NAND_INT_CTRL_READ_ENABLE BIT(6) + +static bool iproc_nand_intc_ack(struct brcmnand_soc *soc) +{ + struct iproc_nand_soc_priv *priv = soc->priv; + void __iomem *mmio = priv->ext_base + IPROC_NAND_CTLR_READY_OFFSET; + u32 val = brcmnand_readl(mmio); + + if (val & IPROC_NAND_CTLR_READY) { + brcmnand_writel(IPROC_NAND_CTLR_READY, mmio); + return true; + } + + return false; +} + +static void iproc_nand_intc_set(struct brcmnand_soc *soc, bool en) +{ + struct iproc_nand_soc_priv *priv = soc->priv; + void __iomem *mmio = priv->idm_base + IPROC_NAND_IO_CTRL_OFFSET; + u32 val; + unsigned long flags; + + spin_lock_irqsave(&priv->idm_lock, flags); + + val = brcmnand_readl(mmio); + + if (en) + val |= IPROC_NAND_INT_CTRL_READ_ENABLE; + else + val &= ~IPROC_NAND_INT_CTRL_READ_ENABLE; + + brcmnand_writel(val, mmio); + + spin_unlock_irqrestore(&priv->idm_lock, flags); +} + +static void iproc_nand_apb_access(struct brcmnand_soc *soc, bool prepare) +{ + struct iproc_nand_soc_priv *priv = soc->priv; + void __iomem *mmio = priv->idm_base + IPROC_NAND_IO_CTRL_OFFSET; + u32 val; + unsigned long flags; + + spin_lock_irqsave(&priv->idm_lock, flags); + + val = brcmnand_readl(mmio); + + if (prepare) + val |= IPROC_NAND_APB_LE_MODE; + else + val &= ~IPROC_NAND_APB_LE_MODE; + + brcmnand_writel(val, mmio); + + spin_unlock_irqrestore(&priv->idm_lock, flags); +} + +static int iproc_nand_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iproc_nand_soc_priv *priv; + struct brcmnand_soc *soc; + struct resource *res; + + soc = devm_kzalloc(dev, sizeof(*soc), GFP_KERNEL); + if (!soc) + return -ENOMEM; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + spin_lock_init(&priv->idm_lock); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iproc-idm"); + priv->idm_base = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->idm_base)) + return PTR_ERR(priv->idm_base); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iproc-ext"); + priv->ext_base = devm_ioremap_resource(dev, res); +#if !defined(CONFIG_BCM_KF_MTD_BCMNAND) + if (IS_ERR(priv->ext_base)) + return PTR_ERR(priv->ext_base); +#endif + + soc->pdev = pdev; + soc->priv = priv; + soc->ctlrdy_ack = iproc_nand_intc_ack; + soc->ctlrdy_set_enabled = iproc_nand_intc_set; + soc->prepare_data_bus = iproc_nand_apb_access; + + return brcmnand_probe(pdev, soc); +} + +static const struct of_device_id iproc_nand_of_match[] = { + { .compatible = "brcm,nand-iproc" }, + {}, +}; +MODULE_DEVICE_TABLE(of, iproc_nand_of_match); + +static struct platform_driver iproc_nand_driver = { + .probe = iproc_nand_probe, + .remove = brcmnand_remove, + .driver = { + .name = "iproc_nand", + .pm = &brcmnand_pm_ops, + .of_match_table = iproc_nand_of_match, + } +}; +module_platform_driver(iproc_nand_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Brian Norris"); +MODULE_AUTHOR("Ray Jui"); +MODULE_DESCRIPTION("NAND driver for Broadcom IPROC-based SoCs"); +#endif diff -urN linux-4.1.52-orig/drivers/mtd/nand/brcmnand/Makefile linux-4.1.52/drivers/mtd/nand/brcmnand/Makefile --- linux-4.1.52-orig/drivers/mtd/nand/brcmnand/Makefile 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/mtd/nand/brcmnand/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,9 @@ +# link order matters; don't link the more generic brcmstb_nand.o before the +# more specific iproc_nand.o, for instance +obj-$(CONFIG_MTD_NAND_BRCMNAND) += iproc_nand.o +obj-$(CONFIG_MTD_NAND_BRCMNAND) += bcm63xx_nand.o +obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmstb_nand.o +obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand.o +ifdef BCM_KF # defined (CONFIG_BCM_KF_MTD_BCM963XX) +EXTRA_CFLAGS += -I $(TOPDIR)/include/asm-generic -I$(INC_BRCMDRIVER_PUB_PATH)/$(BRCM_BOARD) -I$(INC_BRCMSHARED_PUB_PATH)/$(BRCM_BOARD) -I$(INC_BRCMSHARED_PUB_PATH)/../flash +endif # BCM_KF # defined(CONFIG_BCM_KF_MTD_BCM963XX) diff -urN linux-4.1.52-orig/drivers/mtd/nand/Kconfig linux-4.1.52/drivers/mtd/nand/Kconfig --- linux-4.1.52-orig/drivers/mtd/nand/Kconfig 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/mtd/nand/Kconfig 2020-02-07 01:47:54.000000000 +0800 @@ -394,6 +394,16 @@ block, such as SD card. So pay attention to it when you enable the GPMI. +if BCM_KF_MTD_BCMNAND +config MTD_NAND_BRCMNAND + tristate "Broadcom STB NAND controller" + depends on ARM || MIPS || ARM64 + help + Enables the Broadcom NAND controller driver. The controller was + originally designed for Set-Top Box but is used on various BCM7xxx, + BCM3xxx, BCM63xxx, iProc/Cygnus and more. +endif + config MTD_NAND_BCM47XXNFLASH tristate "Support for NAND flash on BCM4706 BCMA bus" depends on BCMA_NFLASH diff -urN linux-4.1.52-orig/drivers/mtd/nand/Makefile linux-4.1.52/drivers/mtd/nand/Makefile --- linux-4.1.52-orig/drivers/mtd/nand/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/mtd/nand/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -53,4 +53,10 @@ obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o +ifdef BCM_KF # defined (CONFIG_BCM_KF_MTD_BCM963XX) +obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/ +obj-$(CONFIG_MTD_BCM_SPI_NAND) += bcm63xx_spinand.o +EXTRA_CFLAGS += -I $(TOPDIR)/include/asm-generic -I$(INC_BRCMDRIVER_PUB_PATH)/$(BRCM_BOARD) -I$(INC_BRCMSHARED_PUB_PATH)/$(BRCM_BOARD) -I$(INC_BRCMSHARED_PUB_PATH)/../flash +endif # BCM_KF # defined(CONFIG_BCM_KF_MTD_BCM963XX) + nand-objs := nand_base.o nand_bbt.o nand_timings.o diff -urN linux-4.1.52-orig/drivers/mtd/nand/nand_base.c linux-4.1.52/drivers/mtd/nand/nand_base.c --- linux-4.1.52-orig/drivers/mtd/nand/nand_base.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/mtd/nand/nand_base.c 2020-02-07 01:47:54.000000000 +0800 @@ -48,6 +48,9 @@ #include #include #include +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +#include +#endif /* Define default oob placement schemes for large and small page devices */ static struct nand_ecclayout nand_oob_8 = { @@ -3634,7 +3637,10 @@ chip->ecc_step_ds = NAND_ECC_STEP(type); chip->onfi_timing_mode_default = type->onfi_timing_mode_default; - +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + chip->timing_1 = type->timing_1; + chip->timing_2 = type->timing_2; +#endif *busw = type->options & NAND_BUSWIDTH_16; if (!mtd->name) @@ -3809,6 +3815,41 @@ return type; } +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +static int nand_dt_init(struct mtd_info *mtd, struct nand_chip *chip, + struct device_node *dn) +{ + int ecc_mode, ecc_strength, ecc_step; + + if (of_get_nand_bus_width(dn) == 16) + chip->options |= NAND_BUSWIDTH_16; + + if (of_get_nand_on_flash_bbt(dn)) + chip->bbt_options |= NAND_BBT_USE_FLASH; + + ecc_mode = of_get_nand_ecc_mode(dn); + ecc_strength = of_get_nand_ecc_strength(dn); + ecc_step = of_get_nand_ecc_step_size(dn); + + if ((ecc_step >= 0 && !(ecc_strength >= 0)) || + (!(ecc_step >= 0) && ecc_strength >= 0)) { + pr_err("must set both strength and step size in DT\n"); + return -EINVAL; + } + + if (ecc_mode >= 0) + chip->ecc.mode = ecc_mode; + + if (ecc_strength >= 0) + chip->ecc.strength = ecc_strength; + + if (ecc_step > 0) + chip->ecc.size = ecc_step; + + return 0; +} +#endif + /** * nand_scan_ident - [NAND Interface] Scan for the NAND device * @mtd: MTD device structure @@ -3826,7 +3867,15 @@ int i, nand_maf_id, nand_dev_id; struct nand_chip *chip = mtd->priv; struct nand_flash_dev *type; +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + int ret; + if (chip->dn) { + ret = nand_dt_init(mtd, chip, chip->dn); + if (ret) + return ret; + } +#endif /* Set the default functions */ nand_set_defaults(chip, chip->options & NAND_BUSWIDTH_16); @@ -4173,6 +4222,10 @@ mtd->type = nand_is_slc(chip) ? MTD_NANDFLASH : MTD_MLCNANDFLASH; mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM : MTD_CAP_NANDFLASH; +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + if (chip->options & NAND_PAGE_NOP1) + mtd->flags |= MTD_NAND_NOP1; +#endif mtd->_erase = nand_erase; mtd->_point = NULL; mtd->_unpoint = NULL; diff -urN linux-4.1.52-orig/drivers/mtd/nand/nand_ids.c linux-4.1.52/drivers/mtd/nand/nand_ids.c --- linux-4.1.52-orig/drivers/mtd/nand/nand_ids.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/mtd/nand/nand_ids.c 2020-02-07 01:47:54.000000000 +0800 @@ -50,7 +50,20 @@ { .id = {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4} }, SZ_8K, SZ_8K, SZ_2M, 0, 6, 640, NAND_ECC_INFO(40, SZ_1K), 4 }, - +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + /* list all the NOP=1 SLC device here */ + {"K9F1G08U0E 128MiB 3.3V 8-bit", + { .id = {0xec, 0xf1, 0x00, 0x95, 0x41} }, + SZ_2K, SZ_128, SZ_128K, NAND_PAGE_NOP1, 5, 64, NAND_ECC_INFO(1, SZ_512) }, + + /* list all the device here with non default timing parameter */ + {"HY27UF082G2A 256MiB 3.3V 8-bit", + { .id = {0xad, 0xda, 0x80, 0x1d, 0x00} }, + SZ_2K, SZ_256, SZ_128K, 0, 5, 64, NAND_ECC_INFO(1, SZ_512), 0, 0x00420000, 0x00000005 }, + {"HY27U4G8F2D ??512MiB?? 3.3V 8-bit", /* No datasheet available.. best guess */ + { .id = {0xad, 0xdc} }, + SZ_2K, SZ_512, SZ_128K, 0, 2, 64, NAND_ECC_INFO(1, SZ_512), 0, 0x00420000, 0x00000005 }, +#endif LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS), LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS), LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE5, 4, SZ_8K, SP_OPTIONS), @@ -159,7 +172,9 @@ EXTENDED_ID_NAND("NAND 64GiB 3,3V 8-bit", 0x3E, 65536, LP_OPTIONS), EXTENDED_ID_NAND("NAND 64GiB 1,8V 16-bit", 0x2E, 65536, LP_OPTIONS16), EXTENDED_ID_NAND("NAND 64GiB 3,3V 16-bit", 0x4E, 65536, LP_OPTIONS16), - +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + EXTENDED_ID_NAND("; set by driver", 0x00, 0, LP_OPTIONS), // generic SPI NAND device detection just to make Linux NAND driver happy +#endif {NULL} }; diff -urN linux-4.1.52-orig/drivers/mtd/spi-nor/spi-nor.c linux-4.1.52/drivers/mtd/spi-nor/spi-nor.c --- linux-4.1.52-orig/drivers/mtd/spi-nor/spi-nor.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/mtd/spi-nor/spi-nor.c 2020-02-07 01:47:54.000000000 +0800 @@ -1068,6 +1068,9 @@ mtd->_erase = spi_nor_erase; mtd->_read = spi_nor_read; +#ifdef CONFIG_BCM_KF_MTD_OOPS + mtd->_panic_write = spi_nor_write; +#endif /* nor protection support for STmicro chips */ if (JEDEC_MFR(info) == CFI_MFR_ST) { nor->flash_lock = stm_lock; diff -urN linux-4.1.52-orig/drivers/mtd/ubi/build.c linux-4.1.52/drivers/mtd/ubi/build.c --- linux-4.1.52-orig/drivers/mtd/ubi/build.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/mtd/ubi/build.c 2020-02-07 01:47:54.000000000 +0800 @@ -53,6 +53,11 @@ /* Maximum value for the number of bad PEBs per 1024 PEBs */ #define MAX_MTD_UBI_BEB_LIMIT 768 +#if defined(CONFIG_BCM_KF_UBI) +/* Minimum Bad Erase Blocks per MTD partition */ +#define BRCM_MIN_BEB_PER_MTDPART 4 +#endif + #ifdef CONFIG_MTD_UBI_MODULE #define ubi_is_module() 1 #else @@ -599,7 +604,20 @@ if (!max_beb_per1024) return 0; - +#if defined(CONFIG_BCM_KF_UBI) + /* + * Here we are using size of just the MTD partition + * because we believe that it is highly unlikely that + * all of the bad eraseblocks in a chip will end up + * in any one partition. Instead we are saying that the + * larger the partition, the more likely it has a higher + * percentage of bad eraseblocks. We therefore scale the + * reserved peb limit based upon the partition size, + * while enforcing a minimum of BRCM_MIN_BEB_PER_MTDPART + * for smaller partitions. + */ + device_size = ubi->mtd->size; +#else /* * Here we are using size of the entire flash chip and * not just the MTD partition size because the maximum @@ -610,6 +628,7 @@ * the MTD partition we are attaching (ubi->mtd). */ device_size = mtd_get_device_size(ubi->mtd); +#endif device_pebs = mtd_div_by_eb(device_size, ubi->mtd); limit = mult_frac(device_pebs, max_beb_per1024, 1024); @@ -617,7 +636,11 @@ if (mult_frac(limit, 1024, max_beb_per1024) < device_pebs) limit += 1; +#if defined(CONFIG_BCM_KF_UBI) + return max(BRCM_MIN_BEB_PER_MTDPART, limit); +#else return limit; +#endif } /** diff -urN linux-4.1.52-orig/drivers/mtd/ubi/vmt.c linux-4.1.52/drivers/mtd/ubi/vmt.c --- linux-4.1.52-orig/drivers/mtd/ubi/vmt.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/mtd/ubi/vmt.c 2020-02-07 01:47:54.000000000 +0800 @@ -413,6 +413,10 @@ struct ubi_device *ubi = vol->ubi; int i, err, vol_id = vol->vol_id, reserved_pebs = vol->reserved_pebs; +#if defined(CONFIG_BCM_KF_KERN_WARNING) + err = -EPERM; +#endif + dbg_gen("remove device %d, volume %d", ubi->ubi_num, vol_id); ubi_assert(desc->mode == UBI_EXCLUSIVE); ubi_assert(vol == ubi->volumes[vol_id]); @@ -458,6 +462,11 @@ if (!no_vtbl) self_check_volumes(ubi); +#if defined(CONFIG_BCM_KF_KERN_WARNING) + if (err == -EPERM) { + printk("vmt.c: ubi_remove_volume really returned an undefined error\n"); + } +#endif return err; out_err: diff -urN linux-4.1.52-orig/drivers/net/accel-pptp/AUTHORS linux-4.1.52/drivers/net/accel-pptp/AUTHORS --- linux-4.1.52-orig/drivers/net/accel-pptp/AUTHORS 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/net/accel-pptp/AUTHORS 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1 @@ +Kozlov D. diff -urN linux-4.1.52-orig/drivers/net/accel-pptp/COPYING linux-4.1.52/drivers/net/accel-pptp/COPYING --- linux-4.1.52-orig/drivers/net/accel-pptp/COPYING 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/net/accel-pptp/COPYING 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff -urN linux-4.1.52-orig/drivers/net/accel-pptp/Doxyfile linux-4.1.52/drivers/net/accel-pptp/Doxyfile --- linux-4.1.52-orig/drivers/net/accel-pptp/Doxyfile 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/net/accel-pptp/Doxyfile 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,275 @@ +# Doxyfile 1.4.1-KDevelop + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = pptp.kdevelop +PROJECT_NUMBER = 0.1 +OUTPUT_DIRECTORY = +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +USE_WINDOWS_ENCODING = NO +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = NO +STRIP_FROM_PATH = /home/dima/Projects/bg2/sectrr/ +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +DETAILS_AT_TOP = NO +INHERIT_DOCS = YES +DISTRIBUTE_GROUP_DOC = NO +TAB_SIZE = 8 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +SUBGROUPING = YES +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_BY_SCOPE_NAME = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_DIRECTORIES = YES +FILE_VERSION_FILTER = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = /home/dima/Projects/pptp/pptp +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.idl \ + *.odl \ + *.cs \ + *.php \ + *.php3 \ + *.inc \ + *.m \ + *.mm \ + *.dox \ + *.C \ + *.CC \ + *.C++ \ + *.II \ + *.I++ \ + *.H \ + *.HH \ + *.H++ \ + *.CS \ + *.PHP \ + *.PHP3 \ + *.M \ + *.MM \ + *.C \ + *.H \ + *.tlh \ + *.diff \ + *.patch \ + *.moc \ + *.xpm \ + *.dox +RECURSIVE = yes +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +VERBATIM_HEADERS = YES +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = NO +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NO +TREEVIEW_WIDTH = 250 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = YES +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = NO +USE_PDFLATEX = NO +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = yes +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = pptp.tag +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = NO +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +MAX_DOT_GRAPH_WIDTH = 1024 +MAX_DOT_GRAPH_HEIGHT = 1024 +MAX_DOT_GRAPH_DEPTH = 1000 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = NO diff -urN linux-4.1.52-orig/drivers/net/accel-pptp/find_kernel_headers linux-4.1.52/drivers/net/accel-pptp/find_kernel_headers --- linux-4.1.52-orig/drivers/net/accel-pptp/find_kernel_headers 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/net/accel-pptp/find_kernel_headers 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,21 @@ +#!/bin/bash + +if test -n "${KDIR}"; then + if test -f ${KDIR}/include/linux/version.h; then + echo ${KDIR} + exit 0 + else + exit 1 + fi +else + if test -f /usr/src/linux/include/linux/version.h; then + echo /usr/src/linux + exit 0 + elif test -f /lib/modules/`uname -r`/build/include/linux/version.h; then + echo /lib/modules/`uname -r`/build + exit 0 + else + exit 1 + fi +fi + \ No newline at end of file diff -urN linux-4.1.52-orig/drivers/net/accel-pptp/gre.c linux-4.1.52/drivers/net/accel-pptp/gre.c --- linux-4.1.52-orig/drivers/net/accel-pptp/gre.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/net/accel-pptp/gre.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,145 @@ +#if defined(CONFIG_BCM_KF_ACCEL_PPTP) +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gre.h" + +struct gre_protocol *gre_proto[GREPROTO_MAX] ____cacheline_aligned_in_smp; +static DEFINE_SPINLOCK(gre_proto_lock); + +int gre_add_protocol(struct gre_protocol *proto, u8 version) +{ + int ret; + + if (version >= GREPROTO_MAX) + return -EINVAL; + + spin_lock(&gre_proto_lock); + if (gre_proto[version]) { + ret = -EAGAIN; + } else { + rcu_assign_pointer(gre_proto[version], proto); + ret = 0; + } + spin_unlock(&gre_proto_lock); + + return ret; +} + +int gre_del_protocol(struct gre_protocol *proto, u8 version) +{ + if (version >= GREPROTO_MAX) + goto out_err; + + spin_lock(&gre_proto_lock); + if (gre_proto[version] == proto) + rcu_assign_pointer(gre_proto[version], NULL); + else + goto out_err_unlock; + spin_unlock(&gre_proto_lock); + synchronize_rcu(); + return 0; + +out_err_unlock: + spin_unlock(&gre_proto_lock); +out_err: + return -EINVAL; +} + +static int gre_rcv(struct sk_buff *skb) +{ + u8 ver; + int ret; + struct gre_protocol *proto; + + if (!pskb_may_pull(skb, 12)) + goto drop_nolock; + + ver = skb->data[1]&0x7f; + if (ver >= GREPROTO_MAX) + goto drop_nolock; + + rcu_read_lock(); + proto = rcu_dereference(gre_proto[ver]); + if (!proto || !proto->handler) + goto drop; + + ret = proto->handler(skb); + + rcu_read_unlock(); + + return ret; + +drop: + rcu_read_unlock(); +drop_nolock: + kfree_skb(skb); + return NET_RX_DROP; +} + +static void gre_err(struct sk_buff *skb, u32 info) +{ + u8 ver; + struct gre_protocol *proto; + + if (!pskb_may_pull(skb, 12)) + goto drop_nolock; + + ver=skb->data[1]&0x7f; + if (ver>=GREPROTO_MAX) + goto drop_nolock; + + rcu_read_lock(); + proto = rcu_dereference(gre_proto[ver]); + if (!proto || !proto->err_handler) + goto drop; + + proto->err_handler(skb, info); + rcu_read_unlock(); + + return; + +drop: + rcu_read_unlock(); +drop_nolock: + kfree_skb(skb); +} + +static struct net_protocol net_gre_protocol = { + .handler = gre_rcv, + .err_handler = gre_err, + .netns_ok=1, +}; + +static int __init gre_init(void) +{ + printk(KERN_INFO "GRE over IPv4 demultiplexor driver"); + + if (inet_add_protocol(&net_gre_protocol, IPPROTO_GRE) < 0) { + printk(KERN_INFO "gre: can't add protocol\n"); + return -EAGAIN; + } + return 0; +} + +static void __exit gre_exit(void) +{ + inet_del_protocol(&net_gre_protocol, IPPROTO_GRE); +} + +module_init(gre_init); +module_exit(gre_exit); + +MODULE_DESCRIPTION("GRE over IPv4 demultiplexor driver"); +MODULE_AUTHOR("Kozlov D. (xeb@mail.ru)"); +MODULE_LICENSE("GPL"); +EXPORT_SYMBOL_GPL(gre_add_protocol); +EXPORT_SYMBOL_GPL(gre_del_protocol); +#endif diff -urN linux-4.1.52-orig/drivers/net/accel-pptp/gre.h linux-4.1.52/drivers/net/accel-pptp/gre.h --- linux-4.1.52-orig/drivers/net/accel-pptp/gre.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/net/accel-pptp/gre.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,20 @@ +#if defined(CONFIG_BCM_KF_ACCEL_PPTP) +#ifndef __LINUX_GRE_H +#define __LINUX_GRE_H + +#include + +#define GREPROTO_CISCO 0 +#define GREPROTO_PPTP 1 +#define GREPROTO_MAX 2 + +struct gre_protocol { + int (*handler)(struct sk_buff *skb); + void (*err_handler)(struct sk_buff *skb, u32 info); +}; + +int gre_add_protocol(struct gre_protocol *proto, u8 version); +int gre_del_protocol(struct gre_protocol *proto, u8 version); + +#endif +#endif diff -urN linux-4.1.52-orig/drivers/net/accel-pptp/if_pppox.h linux-4.1.52/drivers/net/accel-pptp/if_pppox.h --- linux-4.1.52-orig/drivers/net/accel-pptp/if_pppox.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/net/accel-pptp/if_pppox.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,198 @@ +#if defined(CONFIG_BCM_KF_ACCEL_PPTP) +/*************************************************************************** + * Linux PPP over X - Generic PPP transport layer sockets + * Linux PPP over Ethernet (PPPoE) Socket Implementation (RFC 2516) + * + * This file supplies definitions required by the PPP over Ethernet driver + * (pppox.c). All version information wrt this file is located in pppox.c + * + * License: + * 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. + * + */ + +#ifndef __LINUX_IF_PPPOX_H +#define __LINUX_IF_PPPOX_H + + +#include +#include +#include + +#ifdef __KERNEL__ +#include +#include +#include +#include +#include +#endif /* __KERNEL__ */ + +/* For user-space programs to pick up these definitions + * which they wouldn't get otherwise without defining __KERNEL__ + */ +#ifndef AF_PPPOX +#define AF_PPPOX 24 +#define PF_PPPOX AF_PPPOX +#endif /* !(AF_PPPOX) */ + +/************************************************************************ + * PPPoE addressing definition + */ +typedef __u16 sid_t; +struct pppoe_addr{ + sid_t sid; /* Session identifier */ + unsigned char remote[ETH_ALEN]; /* Remote address */ + char dev[IFNAMSIZ]; /* Local device to use */ +}; + +struct pptp_addr{ + __u16 call_id; + struct in_addr sin_addr; +}; +/************************************************************************ + * Protocols supported by AF_PPPOX + */ +#define PX_PROTO_OE 0 /* Currently just PPPoE */ +#define PX_PROTO_PPTP 2 +#define PX_MAX_PROTO 3 + +struct sockaddr_pppox { + sa_family_t sa_family; /* address family, AF_PPPOX */ + unsigned int sa_protocol; /* protocol identifier */ + union{ + struct pppoe_addr pppoe; + struct pptp_addr pptp; + }sa_addr; +}__attribute__ ((packed)); + + +/********************************************************************* + * + * ioctl interface for defining forwarding of connections + * + ********************************************************************/ + +#define PPPOEIOCSFWD _IOW(0xB1 ,0, size_t) +#define PPPOEIOCDFWD _IO(0xB1 ,1) +/*#define PPPOEIOCGFWD _IOWR(0xB1,2, size_t)*/ + +/* Codes to identify message types */ +#define PADI_CODE 0x09 +#define PADO_CODE 0x07 +#define PADR_CODE 0x19 +#define PADS_CODE 0x65 +#define PADT_CODE 0xa7 +struct pppoe_tag { + __u16 tag_type; + __u16 tag_len; + char tag_data[0]; +} __attribute ((packed)); + +/* Tag identifiers */ +#define PTT_EOL __constant_htons(0x0000) +#define PTT_SRV_NAME __constant_htons(0x0101) +#define PTT_AC_NAME __constant_htons(0x0102) +#define PTT_HOST_UNIQ __constant_htons(0x0103) +#define PTT_AC_COOKIE __constant_htons(0x0104) +#define PTT_VENDOR __constant_htons(0x0105) +#define PTT_RELAY_SID __constant_htons(0x0110) +#define PTT_SRV_ERR __constant_htons(0x0201) +#define PTT_SYS_ERR __constant_htons(0x0202) +#define PTT_GEN_ERR __constant_htons(0x0203) + +struct pppoe_hdr { +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8 ver : 4; + __u8 type : 4; +#elif defined(__BIG_ENDIAN_BITFIELD) + __u8 type : 4; + __u8 ver : 4; +#else +#error "Please fix " +#endif + __u8 code; + __u16 sid; + __u16 length; + struct pppoe_tag tag[0]; +} __attribute__ ((packed)); + + +/* Socket options */ +#define PPTP_SO_TIMEOUT 1 + + +#ifdef __KERNEL__ +struct pppoe_opt { + struct net_device *dev; /* device associated with socket*/ + struct pppoe_addr pa; /* what this socket is bound to*/ + struct sockaddr_pppox relay; /* what socket data will be + relayed to (PPPoE relaying) */ +}; +struct pptp_opt { + struct pptp_addr src_addr; + struct pptp_addr dst_addr; + __u32 ack_sent, ack_recv; + __u32 seq_sent, seq_recv; + int ppp_flags; +}; +#define PPTP_FLAG_PAUSE 0 +#define PPTP_FLAG_PROC 1 + +#include + +struct pppox_sock { + /* struct sock must be the first member of pppox_sock */ + struct sock sk; + struct ppp_channel chan; + struct pppox_sock *next; /* for hash table */ + union { + struct pppoe_opt pppoe; + struct pptp_opt pptp; + } proto; + unsigned short num; +}; +#define pppoe_dev proto.pppoe.dev +#define pppoe_pa proto.pppoe.pa +#define pppoe_relay proto.pppoe.relay + +static inline struct pppox_sock *pppox_sk(struct sock *sk) +{ + return (struct pppox_sock *)sk; +} + +static inline struct sock *sk_pppox(struct pppox_sock *po) +{ + return (struct sock *)po; +} + +struct module; + +struct pppox_proto { + int (*create)(struct net *net, struct socket *sock); + int (*ioctl)(struct socket *sock, unsigned int cmd, + unsigned long arg); + struct module *owner; +}; + +extern int register_pppox_proto(int proto_num, struct pppox_proto *pp); +extern void unregister_pppox_proto(int proto_num); +extern void pppox_unbind_sock(struct sock *sk);/* delete ppp-channel binding */ +extern int pppox_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); + +/* PPPoX socket states */ +enum { + PPPOX_NONE = 0, /* initial state */ + PPPOX_CONNECTED = 1, /* connection established ==TCP_ESTABLISHED */ + PPPOX_BOUND = 2, /* bound to ppp device */ + PPPOX_RELAY = 4, /* forwarding is enabled */ + PPPOX_ZOMBIE = 8, /* dead, but still bound to ppp device */ + PPPOX_DEAD = 16 /* dead, useless, please clean me up!*/ +}; + +#endif /* __KERNEL__ */ + +#endif /* !(__LINUX_IF_PPPOX_H) */ +#endif diff -urN linux-4.1.52-orig/drivers/net/accel-pptp/INSTALL linux-4.1.52/drivers/net/accel-pptp/INSTALL --- linux-4.1.52-orig/drivers/net/accel-pptp/INSTALL 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/net/accel-pptp/INSTALL 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,167 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes a while. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Type `make install' to install the programs and any data files and + documentation. + + 4. You can remove the program binaries and object files from the + source code directory by typing `make clean'. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. + diff -urN linux-4.1.52-orig/drivers/net/accel-pptp/Makefile linux-4.1.52/drivers/net/accel-pptp/Makefile --- linux-4.1.52-orig/drivers/net/accel-pptp/Makefile 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/net/accel-pptp/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,44 @@ +ifdef BCM_KF # defined CONFIG_BCM_KF_ACCEL_PPTP +MDIR = extra + +KDIR ?= $(shell sh find_kernel_headers) + +obj-m += pptp.o +#obj-m += gre.o + +CURRENT = $(shell uname -r) + + +ifndef MAKING_MODULES +all: kernel_headers + make -C $(KDIR) SUBDIRS=$(PWD) modules +endif + +ifneq (,$(findstring 2.4.,$(CURRENT))) +install: + @if test ! -d /lib/modules/$(CURRENT)/extra; then \ + mkdir /lib/modules/$(CURRENT)/extra; \ + fi; \ + cp -v $(TARGET).o /lib/modules/$(CURRENT)/extra/$(TARGET).o && /sbin/depmod -a +else +install: + make -C $(KDIR) M=$(PWD) modules_install +endif + +kernel_headers: + @if test -z "$(KDIR)"; then \ + echo "kernel headers not found"; \ + exit 1; \ + else \ + echo "using \"$(KDIR)\" kernel headers"; \ + fi + +default: all + +clean: + -rm -f *.o *.ko .*.cmd .*.flags *.mod.c + +ifneq (,$(findstring 2.4.,$(CURRENT))) +include $(KDIR)/Rules.make +endif +endif # BCM_KF # defined CONFIG_BCM_KF_ACCEL_PPTP diff -urN linux-4.1.52-orig/drivers/net/accel-pptp/pptp.c linux-4.1.52/drivers/net/accel-pptp/pptp.c --- linux-4.1.52-orig/drivers/net/accel-pptp/pptp.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/net/accel-pptp/pptp.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,1057 @@ +#if defined(CONFIG_BCM_KF_ACCEL_PPTP) +/* + * Point-to-Point Tunneling Protocol for Linux + * + * Authors: Kozlov D. (xeb@mail.ru) + * + * 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 "if_pppox.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#if defined(CONFIG_BLOG) +#include +#endif + +//#define DEBUG +//#define CONFIG_GRE + +#if defined(CONFIG_GRE) || defined(CONFIG_GRE_MODULE) +#include "gre.h" +#endif + +#define PPTP_DRIVER_VERSION "0.8.5" + +static int log_level=0; +static int log_packets=10; + +#define MAX_CALLID 65535 +#define PPP_LCP_ECHOREQ 0x09 +#define PPP_LCP_ECHOREP 0x0A + +static DECLARE_BITMAP(callid_bitmap, MAX_CALLID + 1); +static struct pppox_sock **callid_sock; + +#if defined(CONFIG_BLOG) +void pptp_xmit_update(uint16_t call_id, uint32_t seqNum, uint32_t ackNum, uint32_t daddr); +int pptp_xmit_get(uint16_t call_id, uint32_t* seqNum, uint32_t* ackNum, uint32_t daddr); +int pptp_rcv_check(uint16_t call_id, uint32_t *rcv_pktSeq, uint32_t rcv_pktAck, uint32_t saddr); +#endif + +#define SC_RCV_BITS (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP) + + +static DEFINE_SPINLOCK(chan_lock); +#define SK_STATE(sk) (sk)->sk_state + +static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb); +static int pptp_ppp_ioctl(struct ppp_channel *chan, unsigned int cmd, + unsigned long arg); +static int pptp_rcv_core(struct sock *sk,struct sk_buff *skb); + +static struct ppp_channel_ops pptp_chan_ops= { + .start_xmit = pptp_xmit, + .ioctl=pptp_ppp_ioctl, +}; + + +#define MISSING_WINDOW 20 +#define WRAPPED( curseq, lastseq) \ + ((((curseq) & 0xffffff00) == 0) && \ + (((lastseq) & 0xffffff00 ) == 0xffffff00)) + +/* gre header structure: -------------------------------------------- */ + +#define PPTP_GRE_PROTO 0x880B +#define PPTP_GRE_VER 0x1 + +#define PPTP_GRE_FLAG_C 0x80 +#define PPTP_GRE_FLAG_R 0x40 +#define PPTP_GRE_FLAG_K 0x20 +#define PPTP_GRE_FLAG_S 0x10 +#define PPTP_GRE_FLAG_A 0x80 + +#define PPTP_GRE_IS_C(f) ((f)&PPTP_GRE_FLAG_C) +#define PPTP_GRE_IS_R(f) ((f)&PPTP_GRE_FLAG_R) +#define PPTP_GRE_IS_K(f) ((f)&PPTP_GRE_FLAG_K) +#define PPTP_GRE_IS_S(f) ((f)&PPTP_GRE_FLAG_S) +#define PPTP_GRE_IS_A(f) ((f)&PPTP_GRE_FLAG_A) + +struct pptp_gre_header { + u8 flags; /* bitfield */ + u8 ver; /* should be PPTP_GRE_VER (enhanced GRE) */ + u16 protocol; /* should be PPTP_GRE_PROTO (ppp-encaps) */ + u16 payload_len; /* size of ppp payload, not inc. gre header */ + u16 call_id; /* peer's call_id for this session */ + u32 seq; /* sequence number. Present if S==1 */ + u32 ack; /* seq number of highest packet recieved by */ + /* sender in this session */ +} __packed; +#define PPTP_HEADER_OVERHEAD (2+sizeof(struct pptp_gre_header)) + +static struct pppox_sock * lookup_chan(u16 call_id, __be32 s_addr) +{ + struct pppox_sock *sock; + struct pptp_opt *opt; + +#ifdef DEBUG + if (log_level>=3) + printk(KERN_INFO"lookup_chan: rcv packet, call id =%d, s_addr = %03u.%03u.%03u.%03u\n", call_id, + ((uint8_t*)&s_addr)[0], ((uint8_t*)&s_addr)[1], ((uint8_t*)&s_addr)[2], ((uint8_t*)&s_addr)[3]); +#endif + + rcu_read_lock(); + sock = rcu_dereference(callid_sock[call_id]); + if (sock) { + opt=&sock->proto.pptp; + if (opt->dst_addr.sin_addr.s_addr!=s_addr) sock=NULL; + else sock_hold(sk_pppox(sock)); + } + rcu_read_unlock(); + + return sock; +} + +static int lookup_chan_dst(u16 call_id, __be32 d_addr) +{ + struct pppox_sock *sock; + struct pptp_opt *opt; + int i; + + rcu_read_lock(); + for(i = find_next_bit(callid_bitmap,MAX_CALLID,1); i < MAX_CALLID; + i = find_next_bit(callid_bitmap, MAX_CALLID, i + 1)){ + sock = rcu_dereference(callid_sock[i]); + if (!sock) + continue; + opt = &sock->proto.pptp; + if (opt->dst_addr.call_id == call_id && opt->dst_addr.sin_addr.s_addr == d_addr) break; + } + rcu_read_unlock(); + + return iproto.pptp.src_addr.call_id) + { + call_id=find_next_zero_bit(callid_bitmap,MAX_CALLID,call_id+1); + if (call_id==MAX_CALLID) + call_id=find_next_zero_bit(callid_bitmap,MAX_CALLID,1); + sock->proto.pptp.src_addr.call_id=call_id; + } + else if (test_bit(sock->proto.pptp.src_addr.call_id,callid_bitmap)) + goto exit; + + rcu_assign_pointer(callid_sock[sock->proto.pptp.src_addr.call_id],sock); + set_bit(sock->proto.pptp.src_addr.call_id,callid_bitmap); + res=0; + +exit: + spin_unlock(&chan_lock); + + return res; +} + +static void del_chan(struct pppox_sock *sock) +{ + spin_lock(&chan_lock); + clear_bit(sock->proto.pptp.src_addr.call_id,callid_bitmap); + rcu_assign_pointer(callid_sock[sock->proto.pptp.src_addr.call_id],NULL); + spin_unlock(&chan_lock); + synchronize_rcu(); +} + +static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb) +{ + struct sock *sk = (struct sock *) chan->private; + struct pppox_sock *po = pppox_sk(sk); + struct pptp_opt *opt=&po->proto.pptp; + struct pptp_gre_header *hdr; + unsigned int header_len=sizeof(*hdr); + int err=0; + int islcp; + int len; + unsigned char *data; + u32 seq_recv; + + + struct rtable *rt; /* Route to the other host */ + struct net_device *tdev; /* Device to other host */ + struct iphdr *iph; /* Our new IP header */ + int max_headroom; /* The extra header space needed */ + + if (SK_STATE(sk_pppox(po)) & PPPOX_DEAD) + goto tx_error; + + { + /*ori_accel_pptp_code + * struct flowi fl = { .oif = 0, + .nl_u = { .ip4_u = + { .daddr = opt->dst_addr.sin_addr.s_addr, + .saddr = opt->src_addr.sin_addr.s_addr, + .tos = RT_TOS(0) } + }, + .proto = IPPROTO_GRE }; + */ + struct flowi fl; + fl.flowi_oif = 0; + fl.u.ip4.daddr = opt->dst_addr.sin_addr.s_addr; + fl.u.ip4.saddr = opt->src_addr.sin_addr.s_addr; + fl.flowi_tos = RT_TOS(0); + fl.flowi_proto = IPPROTO_GRE; + //if ((err=ip_route_output_key(&init_net,&rt, &fl))) { + rt = ip_route_output_key(&init_net, (struct flowi4 *)&fl); + //goto tx_error; + //} + } + tdev = rt->dst.dev; + + max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(*iph)+sizeof(*hdr)+2; + + if (skb_headroom(skb) < max_headroom || skb_shared(skb) || + (skb_cloned(skb) && !skb_clone_writable(skb,0))) { + struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom); + if (!new_skb) { + ip_rt_put(rt); + goto tx_error; + } + if (skb->sk) + skb_set_owner_w(new_skb, skb->sk); + kfree_skb(skb); + skb = new_skb; + } + + data=skb->data; + islcp=((data[0] << 8) + data[1])== PPP_LCP && 1 <= data[2] && data[2] <= 7; + + /* compress protocol field */ + if ((opt->ppp_flags & SC_COMP_PROT) && data[0]==0 && !islcp) + skb_pull(skb,1); + + /* + * Put in the address/control bytes if necessary + */ + if ((opt->ppp_flags & SC_COMP_AC) == 0 || islcp) { + data=skb_push(skb,2); + data[0]=PPP_ALLSTATIONS; + data[1]=PPP_UI; + } + + len=skb->len; + + seq_recv = opt->seq_recv; + + if (opt->ack_sent == seq_recv) header_len-=sizeof(hdr->ack); + + // Push down and install GRE header + skb_push(skb,header_len); + hdr=(struct pptp_gre_header *)(skb->data); + + hdr->flags = PPTP_GRE_FLAG_K; + hdr->ver = PPTP_GRE_VER; + hdr->protocol = htons(PPTP_GRE_PROTO); + hdr->call_id = htons(opt->dst_addr.call_id); + + hdr->flags |= PPTP_GRE_FLAG_S; + hdr->seq = htonl(++opt->seq_sent); +#ifdef DEBUG + if (log_level>=3 && opt->seq_sent<=log_packets) + printk(KERN_INFO"PPTP[%i]: send packet: seq=%i",opt->src_addr.call_id,opt->seq_sent); +#endif + if (opt->ack_sent != seq_recv) { + /* send ack with this message */ + hdr->ver |= PPTP_GRE_FLAG_A; + hdr->ack = htonl(seq_recv); + opt->ack_sent = seq_recv; +#ifdef DEBUG + if (log_level>=3 && opt->seq_sent<=log_packets) + printk(" ack=%i",seq_recv); +#endif + } + hdr->payload_len = htons(len); +#ifdef DEBUG + if (log_level>=3 && opt->seq_sent<=log_packets) + printk("\n"); +#endif + + /* + * Push down and install the IP header. + */ + + skb_reset_transport_header(skb); + skb_push(skb, sizeof(*iph)); + skb_reset_network_header(skb); + memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); + IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED | + IPSKB_REROUTED); + + iph = ip_hdr(skb); + iph->version = 4; + iph->ihl = sizeof(struct iphdr) >> 2; + if (ip_dont_fragment(sk, &rt->dst)) + iph->frag_off = htons(IP_DF); + else + iph->frag_off = 0; + iph->protocol = IPPROTO_GRE; + iph->tos = 0; + + iph->daddr = opt->dst_addr.sin_addr.s_addr; + iph->saddr = opt->src_addr.sin_addr.s_addr ; + + //iph->ttl = dst_metric(&rt->dst, RTAX_HOPLIMIT);//ori_accel_pptp_code + iph->ttl = dst_metric(&rt->dst, RTAX_HOPLIMIT-1); + iph->tot_len = htons(skb->len); + + skb_dst_drop(skb); + skb_dst_set(skb,&rt->dst); + + nf_reset(skb); + + skb->ip_summed = CHECKSUM_NONE; + if(skb && sk) + ip_select_ident(skb, sk); + ip_send_check(iph); + + err = ip_local_out(skb); + +tx_error: + return 1; +} + +static int pptp_rcv_core(struct sock *sk,struct sk_buff *skb) +{ + struct pppox_sock *po = pppox_sk(sk); + struct pptp_opt *opt=&po->proto.pptp; + int headersize,payload_len,seq; + u8 *payload; + struct pptp_gre_header *header; + + if (!(SK_STATE(sk) & PPPOX_CONNECTED)) { + if (sock_queue_rcv_skb(sk, skb)) + goto drop; + return NET_RX_SUCCESS; + } + + header = (struct pptp_gre_header *)(skb->data); + + /* test if acknowledgement present */ + if (PPTP_GRE_IS_A(header->ver)){ + u32 ack = (PPTP_GRE_IS_S(header->flags))? + header->ack:header->seq; /* ack in different place if S = 0 */ + + ack = ntohl( ack); + + if (ack > opt->ack_recv) opt->ack_recv = ack; + /* also handle sequence number wrap-around */ + if (WRAPPED(ack,opt->ack_recv)) opt->ack_recv = ack; + } + + /* test if payload present */ + if (!PPTP_GRE_IS_S(header->flags)){ + goto drop; + } + + headersize = sizeof(*header); + payload_len = ntohs(header->payload_len); + seq = ntohl(header->seq); + + /* no ack present? */ + if (!PPTP_GRE_IS_A(header->ver)) headersize -= sizeof(header->ack); + /* check for incomplete packet (length smaller than expected) */ + if (skb->len - headersize < payload_len){ +#ifdef DEBUG + if (log_level>=1) + printk(KERN_INFO"PPTP: discarding truncated packet (expected %d, got %d bytes)\n", + payload_len, skb->len - headersize); +#endif + goto drop; + } + + payload=skb->data+headersize; + +#if defined(CONFIG_BLOG) + if (!blog_gre_tunnel_accelerated()) + { +#endif + /* check for expected sequence number */ + if ( seq < opt->seq_recv + 1 || WRAPPED(opt->seq_recv, seq) ){ + if ( (payload[0] == PPP_ALLSTATIONS) && (payload[1] == PPP_UI) && + (PPP_PROTOCOL(payload) == PPP_LCP) && + ((payload[4] == PPP_LCP_ECHOREQ) || (payload[4] == PPP_LCP_ECHOREP)) ){ +#ifdef DEBUG + if ( log_level >= 1) + printk(KERN_INFO"PPTP[%i]: allowing old LCP Echo packet %d (expecting %d)\n", opt->src_addr.call_id, + seq, opt->seq_recv + 1); +#endif + goto allow_packet; + } +#ifdef DEBUG + if ( log_level >= 1) + printk(KERN_INFO"PPTP[%i]: discarding duplicate or old packet %d (expecting %d)\n",opt->src_addr.call_id, + seq, opt->seq_recv + 1); +#endif + }else{ + opt->seq_recv = seq; +allow_packet: +#ifdef DEBUG + if ( log_level >= 3 && opt->seq_sent<=log_packets) + printk(KERN_INFO"PPTP[%i]: accepting packet %d size=%i (%02x %02x %02x %02x %02x %02x)\n",opt->src_addr.call_id, seq,payload_len, + *(payload +0), + *(payload +1), + *(payload +2), + *(payload +3), + *(payload +4), + *(payload +5)); +#endif + + skb_pull(skb,headersize); + + if (payload[0] == PPP_ALLSTATIONS && payload[1] == PPP_UI){ + /* chop off address/control */ + if (skb->len < 3) + goto drop; + skb_pull(skb,2); + } + + if ((*skb->data) & 1){ + /* protocol is compressed */ + skb_push(skb, 1)[0] = 0; + } + + skb->ip_summed=CHECKSUM_NONE; + skb_set_network_header(skb,skb->head-skb->data); + ppp_input(&po->chan,skb); + + return NET_RX_SUCCESS; + } +#if defined(CONFIG_BLOG) + } + else /* blog_gre_tunnel_accelerated is true, so opt->seq_recv and opt->ack_recv have been ++ by pptp_rcv_check() */ + { + /* check for expected sequence number */ + if ( seq < opt->seq_recv || WRAPPED(opt->seq_recv, seq) ) + { + if ( (payload[0] == PPP_ALLSTATIONS) && (payload[1] == PPP_UI) && (PPP_PROTOCOL(payload) == PPP_LCP) && + ((payload[4] == PPP_LCP_ECHOREQ) || (payload[4] == PPP_LCP_ECHOREP)) ) + goto allow_packet2; + } + else + { +allow_packet2: + //printk(" PPTP: blog_gre_tunnel_accelerated!\n"); +#ifdef DEBUG + if ( log_level >= 3 && opt->seq_sent<=log_packets) + printk(KERN_INFO"PPTP[%i]: accepting packet %d size=%i (%02x %02x %02x %02x %02x %02x)\n",opt->src_addr.call_id, seq,payload_len, + *(payload +0),*(payload +1),*(payload +2),*(payload +3),*(payload +4),*(payload +5)); +#endif + skb_pull(skb,headersize); + + if (payload[0] == PPP_ALLSTATIONS && payload[1] == PPP_UI) + { + /* chop off address/control */ + if (skb->len < 3) + goto drop; + skb_pull(skb,2); + } + + if ((*skb->data) & 1){ + /* protocol is compressed */ + skb_push(skb, 1)[0] = 0; + } + + skb->ip_summed=CHECKSUM_NONE; + skb_set_network_header(skb,skb->head-skb->data); + ppp_input(&po->chan,skb); + return NET_RX_SUCCESS; + } + } +#endif + +drop: + kfree_skb(skb); + return NET_RX_DROP; +} + +static int pptp_rcv(struct sk_buff *skb) +{ + struct pppox_sock *po; + struct pptp_gre_header *header; + struct iphdr *iph; + + if (skb->pkt_type != PACKET_HOST) + goto drop; + + /*if (!pskb_may_pull(skb, 12)) + goto drop;*/ + + iph = ip_hdr(skb); + + header = (struct pptp_gre_header *)skb->data; + + if ( /* version should be 1 */ + ((header->ver & 0x7F) != PPTP_GRE_VER) || + /* PPTP-GRE protocol for PPTP */ + (ntohs(header->protocol) != PPTP_GRE_PROTO)|| + /* flag C should be clear */ + PPTP_GRE_IS_C(header->flags) || + /* flag R should be clear */ + PPTP_GRE_IS_R(header->flags) || + /* flag K should be set */ + (!PPTP_GRE_IS_K(header->flags)) || + /* routing and recursion ctrl = 0 */ + ((header->flags&0xF) != 0)){ + /* if invalid, discard this packet */ + if (log_level>=1) + printk(KERN_INFO"PPTP: Discarding GRE: %X %X %X %X %X %X\n", + header->ver&0x7F, ntohs(header->protocol), + PPTP_GRE_IS_C(header->flags), + PPTP_GRE_IS_R(header->flags), + PPTP_GRE_IS_K(header->flags), + header->flags & 0xF); + goto drop; + } + + + if ((po=lookup_chan(htons(header->call_id),iph->saddr))) { + skb_dst_drop(skb); + nf_reset(skb); + + return sk_receive_skb(sk_pppox(po), skb, 0); + + }else { +#ifdef DEBUG + if (log_level>=1) + printk(KERN_INFO"PPTP: Discarding packet from unknown call_id %i\n",htons(header->call_id)); +#endif + } + +drop: + kfree_skb(skb); + return NET_RX_DROP; +} + +static int pptp_bind(struct socket *sock,struct sockaddr *uservaddr,int sockaddr_len) +{ + struct sock *sk = sock->sk; + struct sockaddr_pppox *sp = (struct sockaddr_pppox *) uservaddr; + struct pppox_sock *po = pppox_sk(sk); + struct pptp_opt *opt=&po->proto.pptp; + int error=0; + +#ifdef DEBUG + if (log_level>=1) + printk(KERN_INFO"PPTP: bind: addr=%X call_id=%i\n",sp->sa_addr.pptp.sin_addr.s_addr, + sp->sa_addr.pptp.call_id); +#endif + lock_sock(sk); + + opt->src_addr=sp->sa_addr.pptp; + if (add_chan(po)) + { + release_sock(sk); + error=-EBUSY; + } +#ifdef DEBUG + if (log_level>=1) + printk(KERN_INFO"PPTP: using call_id %i\n",opt->src_addr.call_id); +#endif + + release_sock(sk); + return error; +} + +static int pptp_connect(struct socket *sock, struct sockaddr *uservaddr, + int sockaddr_len, int flags) +{ + struct sock *sk = sock->sk; + struct sockaddr_pppox *sp = (struct sockaddr_pppox *) uservaddr; + struct pppox_sock *po = pppox_sk(sk); + struct pptp_opt *opt = &po->proto.pptp; + struct rtable *rt; /* Route to the other host */ + int error=0; + struct flowi4 fl4; + + if (sp->sa_protocol != PX_PROTO_PPTP) + return -EINVAL; + +#ifdef DEBUG + if (log_level>=1) + printk(KERN_INFO"PPTP[%i]: connect: addr=%X call_id=%i\n",opt->src_addr.call_id, + sp->sa_addr.pptp.sin_addr.s_addr,sp->sa_addr.pptp.call_id); +#endif + + if (lookup_chan_dst(sp->sa_addr.pptp.call_id,sp->sa_addr.pptp.sin_addr.s_addr)) + return -EALREADY; + + lock_sock(sk); + /* Check for already bound sockets */ + if (SK_STATE(sk) & PPPOX_CONNECTED){ + error = -EBUSY; + goto end; + } + + /* Check for already disconnected sockets, on attempts to disconnect */ + if (SK_STATE(sk) & PPPOX_DEAD){ + error = -EALREADY; + goto end; + } + + if (!opt->src_addr.sin_addr.s_addr || !sp->sa_addr.pptp.sin_addr.s_addr){ + error = -EINVAL; + goto end; + } + + po->chan.private=sk; + po->chan.ops=&pptp_chan_ops; + + + { + rt = ip_route_output_ports(&init_net, &fl4, sk, + opt->dst_addr.sin_addr.s_addr, + opt->src_addr.sin_addr.s_addr, + 0, 0, + IPPROTO_GRE, RT_CONN_FLAGS(sk), 0); + + if (IS_ERR(rt)) + { + error = -EHOSTUNREACH; + goto end; + } + + sk_setup_caps(sk, &rt->dst); + } + + po->chan.mtu=dst_mtu(&rt->dst); + if (!po->chan.mtu) po->chan.mtu=1500; + ip_rt_put(rt); + po->chan.mtu-=PPTP_HEADER_OVERHEAD; + + po->chan.hdrlen=2+sizeof(struct pptp_gre_header); + error = ppp_register_channel(&po->chan); + if (error){ + printk(KERN_ERR "PPTP: failed to register PPP channel (%d)\n",error); + goto end; + } + + opt->dst_addr=sp->sa_addr.pptp; + SK_STATE(sk) = PPPOX_CONNECTED; + + end: + release_sock(sk); + return error; +} + +static int pptp_getname(struct socket *sock, struct sockaddr *uaddr, + int *usockaddr_len, int peer) +{ + int len = sizeof(struct sockaddr_pppox); + struct sockaddr_pppox sp; + + sp.sa_family = AF_PPPOX; + sp.sa_protocol = PX_PROTO_PPTP; + sp.sa_addr.pptp=pppox_sk(sock->sk)->proto.pptp.src_addr; + + memcpy(uaddr, &sp, len); + + *usockaddr_len = len; + + return 0; +} + +static int pptp_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + struct pppox_sock *po; + struct pptp_opt *opt; + int error = 0; + + if (!sk) + return 0; + + lock_sock(sk); + + if (sock_flag(sk, SOCK_DEAD)) + { + release_sock(sk); + return -EBADF; + } + + po = pppox_sk(sk); + opt=&po->proto.pptp; + del_chan(po); + + pppox_unbind_sock(sk); + SK_STATE(sk) = PPPOX_DEAD; + +#ifdef DEBUG + if (log_level>=1) + printk(KERN_INFO"PPTP[%i]: release\n",opt->src_addr.call_id); +#endif + + sock_orphan(sk); + sock->sk = NULL; + + release_sock(sk); + sock_put(sk); + + return error; +} + + +static struct proto pptp_sk_proto = { + .name = "PPTP", + .owner = THIS_MODULE, + .obj_size = sizeof(struct pppox_sock), +}; + +static struct proto_ops pptp_ops = { + .family = AF_PPPOX, + .owner = THIS_MODULE, + .release = pptp_release, + .bind = pptp_bind, + .connect = pptp_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = pptp_getname, + .poll = sock_no_poll, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = sock_no_setsockopt, + .getsockopt = sock_no_getsockopt, + .sendmsg = sock_no_sendmsg, + .recvmsg = sock_no_recvmsg, + .mmap = sock_no_mmap, + .ioctl = pppox_ioctl, +}; + + +static void pptp_sock_destruct(struct sock *sk) +{ + if (!(SK_STATE(sk) & PPPOX_DEAD)){ + del_chan(pppox_sk(sk)); + pppox_unbind_sock(sk); + } + skb_queue_purge(&sk->sk_receive_queue); +} +static int pptp_create(struct net *net, struct socket *sock) +{ + int error = -ENOMEM; + struct sock *sk; + struct pppox_sock *po; + struct pptp_opt *opt; + + sk = sk_alloc(net,PF_PPPOX, GFP_KERNEL, &pptp_sk_proto); + if (!sk) + goto out; + + sock_init_data(sock, sk); + + sock->state = SS_UNCONNECTED; + sock->ops = &pptp_ops; + + sk->sk_backlog_rcv = pptp_rcv_core; + sk->sk_state = PPPOX_NONE; + sk->sk_type = SOCK_STREAM; + sk->sk_family = PF_PPPOX; + sk->sk_protocol = PX_PROTO_PPTP; + sk->sk_destruct = pptp_sock_destruct; + + po = pppox_sk(sk); + opt=&po->proto.pptp; + + opt->seq_sent=0; opt->seq_recv=0; + opt->ack_recv=0; opt->ack_sent=0; + + error = 0; +out: + return error; +} + + +static int pptp_ppp_ioctl(struct ppp_channel *chan, unsigned int cmd, + unsigned long arg) +{ + struct sock *sk = (struct sock *) chan->private; + struct pppox_sock *po = pppox_sk(sk); + struct pptp_opt *opt=&po->proto.pptp; + void __user *argp = (void __user *)arg; + int __user *p = argp; + int err, val; + + err = -EFAULT; + switch (cmd) { + case PPPIOCGFLAGS: + val = opt->ppp_flags; + if (put_user(val, p)) + break; + err = 0; + break; + case PPPIOCSFLAGS: + if (get_user(val, p)) + break; + opt->ppp_flags = val & ~SC_RCV_BITS; + err = 0; + break; + default: + err = -ENOTTY; + } + + return err; +} + + +static struct pppox_proto pppox_pptp_proto = { + .create = pptp_create, + .owner = THIS_MODULE, +}; + +#if defined(CONFIG_GRE) || defined(CONFIG_GRE_MODULE) +static struct gre_protocol gre_pptp_protocol = { + .handler = pptp_rcv, +}; +#else +static struct net_protocol net_pptp_protocol = { + .handler = pptp_rcv, +}; +#endif + +static int __init pptp_init_module(void) +{ + int err=0; + printk(KERN_INFO "PPTP driver version " PPTP_DRIVER_VERSION "\n"); + + callid_sock = __vmalloc((MAX_CALLID + 1) * sizeof(void *), + GFP_KERNEL | __GFP_ZERO, PAGE_KERNEL); + if (!callid_sock) { + printk(KERN_ERR "PPTP: cann't allocate memory\n"); + return -ENOMEM; + } + +#if defined(CONFIG_GRE) || defined(CONFIG_GRE_MODULE) + if (gre_add_protocol(&gre_pptp_protocol, GREPROTO_PPTP) < 0) { + printk(KERN_INFO "PPTP: can't add protocol\n"); + goto out_free_mem; + } +#else + if (inet_add_protocol(&net_pptp_protocol, IPPROTO_GRE) < 0) { + printk(KERN_INFO "PPTP: can't add protocol\n"); + goto out_free_mem; + } +#endif + + err = proto_register(&pptp_sk_proto, 0); + if (err){ + printk(KERN_INFO "PPTP: can't register sk_proto\n"); + goto out_inet_del_protocol; + } + + err = register_pppox_proto(PX_PROTO_PPTP, &pppox_pptp_proto); + if (err){ + printk(KERN_INFO "PPTP: can't register pppox_proto\n"); + goto out_unregister_sk_proto; + } + +#if defined(CONFIG_BLOG) + blog_pptp_xmit_update_fn = (blog_pptp_xmit_upd_t) pptp_xmit_update; + blog_pptp_xmit_get_fn = (blog_pptp_xmit_get_t) pptp_xmit_get; + blog_pptp_rcv_check_fn = (blog_pptp_rcv_check_t) pptp_rcv_check; +#endif + + return 0; +out_unregister_sk_proto: + proto_unregister(&pptp_sk_proto); + +out_inet_del_protocol: + +#if defined(CONFIG_GRE) || defined(CONFIG_GRE_MODULE) + gre_del_protocol(&gre_pptp_protocol, GREPROTO_PPTP); +#else + inet_del_protocol(&net_pptp_protocol, IPPROTO_GRE); +#endif +out_free_mem: + vfree(callid_sock); + + return err; +} + +static void __exit pptp_exit_module(void) +{ + unregister_pppox_proto(PX_PROTO_PPTP); +#if defined(CONFIG_GRE) || defined(CONFIG_GRE_MODULE) + proto_unregister(&pptp_sk_proto); + gre_del_protocol(&gre_pptp_protocol, GREPROTO_PPTP); +#else + proto_unregister(&pptp_sk_proto); + inet_del_protocol(&net_pptp_protocol, IPPROTO_GRE); +#endif + vfree(callid_sock); +} + +#if defined(CONFIG_BLOG) +void pptp_xmit_update(uint16_t call_id, uint32_t seqNum, uint32_t ackNum, uint32_t daddr) +{ + struct pppox_sock *sock; + struct pptp_opt *opt; + int i; + + rcu_read_lock(); + + for(i = find_next_bit(callid_bitmap,MAX_CALLID,1); i < MAX_CALLID; i = find_next_bit(callid_bitmap, MAX_CALLID, i + 1)) + { + sock = rcu_dereference(callid_sock[i]); + if (!sock) + continue; + + opt = &sock->proto.pptp; + if (opt->dst_addr.call_id == call_id && opt->dst_addr.sin_addr.s_addr == daddr) + { + //printk(KERN_INFO "PPTP: find the channel!\n"); + if( opt->seq_sent != seqNum && seqNum > 0) + { + //printk(KERN_INFO "PPTP: update seq_sent!\n"); + opt->seq_sent = seqNum; + } + if( opt->ack_sent != ackNum && ackNum > 0) + { + //printk(KERN_INFO "PPTP: update ack_sent!\n"); + opt->ack_sent = ackNum; + } + break; + } + } + + rcu_read_unlock(); + + return; +} + +int pptp_xmit_get(uint16_t call_id, uint32_t* seqNum, uint32_t* ackNum, uint32_t daddr) +{ + struct pppox_sock *sock; + struct pptp_opt *opt; + int i, ack_flag = PPTP_NOT_ACK; + + rcu_read_lock(); + + for(i = find_next_bit(callid_bitmap,MAX_CALLID,1); i < MAX_CALLID; i = find_next_bit(callid_bitmap, MAX_CALLID, i + 1)) + { + sock = rcu_dereference(callid_sock[i]); + if (!sock) + continue; + + opt = &sock->proto.pptp; + if (opt->dst_addr.call_id == call_id && opt->dst_addr.sin_addr.s_addr == daddr) + { + //printk(KERN_INFO "PPTP: seq_sent = %d, ack_sent = %d \n", opt->seq_sent, opt->ack_sent); + opt->seq_sent += 1; + *seqNum = opt->seq_sent; + *ackNum = opt->ack_sent; + + if (opt->ack_sent != opt->seq_recv) + { + ack_flag = PPTP_WITH_ACK; + *ackNum = opt->ack_sent = opt->seq_recv; + } + break; + } + } + + rcu_read_unlock(); + + return ack_flag; +} + +int pptp_rcv_check(uint16_t call_id, uint32_t *rcv_pktSeq, uint32_t rcv_pktAck, uint32_t saddr) +{ + struct pppox_sock *sock; + struct pptp_opt *opt; + int ret = BLOG_PPTP_RCV_NO_TUNNEL; + + rcu_read_lock(); + sock = rcu_dereference(callid_sock[call_id]); + if (sock) + { + opt=&sock->proto.pptp; + if (opt->dst_addr.sin_addr.s_addr!=saddr) + sock=NULL; + else + { + sock_hold(sk_pppox(sock)); + //printk(KERN_INFO "PPTP: pptp_rcv_check() current seq_recv is %d \n", opt->seq_recv); + if (opt->seq_recv && ((*rcv_pktSeq) > opt->seq_recv)) + { + opt->seq_recv = (*rcv_pktSeq); + ret = BLOG_PPTP_RCV_IN_SEQ; + } else if (opt->seq_recv && ((*rcv_pktSeq) - opt->seq_recv) <= 0) { + printk(KERN_INFO "pptp_rcv_check():[BLOG_PPTP_RCV_OOS_LT] current seq_recv is %d \n", opt->seq_recv); + ret = BLOG_PPTP_RCV_OOS_LT; + } else { + printk(KERN_INFO "pptp_rcv_check():[BLOG_PPTP_RCV_OOS_GT] current seq_recv is %d \n", opt->seq_recv); + opt->seq_recv = (*rcv_pktSeq); + ret = BLOG_PPTP_RCV_OOS_GT; + } + + if (rcv_pktAck > opt->ack_recv) opt->ack_recv = rcv_pktAck; + } + + } + rcu_read_unlock(); + + return ret; +} + +EXPORT_SYMBOL(pptp_xmit_update); +EXPORT_SYMBOL(pptp_xmit_get); +EXPORT_SYMBOL(pptp_rcv_check); +#endif + +module_init(pptp_init_module); +module_exit(pptp_exit_module); + +MODULE_DESCRIPTION("Point-to-Point Tunneling Protocol for Linux"); +MODULE_AUTHOR("Kozlov D. (xeb@mail.ru)"); +MODULE_LICENSE("GPL"); + +module_param(log_level,int,0); +module_param(log_packets,int,0); +MODULE_PARM_DESC(log_level,"Logging level (default=0)"); +#endif diff -urN linux-4.1.52-orig/drivers/net/bonding/bond_3ad.c linux-4.1.52/drivers/net/bonding/bond_3ad.c --- linux-4.1.52-orig/drivers/net/bonding/bond_3ad.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/net/bonding/bond_3ad.c 2020-02-07 01:47:54.000000000 +0800 @@ -352,6 +352,18 @@ return retval; } +#if defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) +static inline u32 __get_agg_async_linkspeed(struct port *port) +{ + struct bonding *bond = __get_bond_by_port(port); + + if (bond == NULL) + return BOND_ASYNC_LINKSPEED_OFF; + + return bond->params.async_linkspeed; +} +#endif /* defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) */ + /* Conversions */ /** @@ -1950,7 +1962,10 @@ */ port->actor_admin_port_key = 0; port->actor_admin_port_key |= __get_duplex(port); - port->actor_admin_port_key |= (__get_link_speed(port) << 1); +#if defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) + if (!__get_agg_async_linkspeed(port)) +#endif /* defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) */ + port->actor_admin_port_key |= (__get_link_speed(port) << 1); port->actor_oper_port_key = port->actor_admin_port_key; /* if the port is not full duplex, then the port should be not * lacp Enabled @@ -2325,7 +2340,10 @@ spin_lock_bh(&slave->bond->mode_lock); port->actor_admin_port_key &= ~AD_SPEED_KEY_MASKS; - port->actor_admin_port_key |= __get_link_speed(port) << 1; +#if defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) + if (!__get_agg_async_linkspeed(port)) +#endif /* defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) */ + port->actor_admin_port_key |= __get_link_speed(port) << 1; port->actor_oper_port_key = port->actor_admin_port_key; netdev_dbg(slave->bond->dev, "Port %d changed speed\n", port->actor_port_number); /* there is no need to reselect a new aggregator, just signal the @@ -2403,8 +2421,13 @@ port->actor_admin_port_key &= ~(AD_DUPLEX_KEY_MASKS|AD_SPEED_KEY_MASKS); if (link == BOND_LINK_UP) { port->is_enabled = true; - port->actor_admin_port_key |= - (__get_link_speed(port) << 1) | __get_duplex(port); +#if defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) + if (__get_agg_async_linkspeed(port)) + port->actor_admin_port_key |= __get_duplex(port); + else +#endif /* defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) */ + port->actor_admin_port_key |= + (__get_link_speed(port) << 1) | __get_duplex(port); if (port->actor_admin_port_key & AD_DUPLEX_KEY_MASKS) port->sm_vars |= AD_PORT_LACP_ENABLED; } else { diff -urN linux-4.1.52-orig/drivers/net/bonding/bond_main.c linux-4.1.52/drivers/net/bonding/bond_main.c --- linux-4.1.52-orig/drivers/net/bonding/bond_main.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/net/bonding/bond_main.c 2020-02-07 01:47:54.000000000 +0800 @@ -84,6 +84,14 @@ #include "bonding_priv.h" +#if defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#include +#endif +#include +#include +#endif + /*---------------------------- Module parameters ----------------------------*/ /* monitor all links that often (in milliseconds). <=0 disables monitoring */ @@ -1152,6 +1160,23 @@ if (unlikely(!skb)) return RX_HANDLER_CONSUMED; +#if defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + if (blog_ptr(skb)) + { + + /* Clarify : bond device shouldn't modify the packet; + also it does not hold its own stats rather gets the cummulative stats from its slave devices [ see:bond_get_stats() ]; + blog->put_stats,clr_stats are also not needed for the same reason; + Do we need to link bond devices ; One usage could be that when bond device goes down ; FC will be able to remove the flows */ + + blog_lock(); + blog_link( IF_DEVICE, blog_ptr(skb), (void*)skb->dev, DIR_RX, skb->len ); + blog_unlock(); + } +#endif /* defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) */ +#endif /* defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) */ + *pskb = skb; slave = bond_slave_get_rcu(skb->dev); @@ -1631,7 +1656,7 @@ goto err_upper_unlink; } - /* If the mode uses primary, then the following is handled by + /* If the mode uses primary, then the following is handled by * bond_change_active_slave(). */ if (!bond_uses_primary(bond)) { @@ -1685,6 +1710,7 @@ /* enslave is successful */ bond_queue_slave_event(new_slave); + return 0; /* Undo stages on error */ @@ -3080,6 +3106,44 @@ .notifier_call = bond_netdev_event, }; +#if defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) + +/* bond_br_stp_event: handle bridge stp notifier chain events. + * + */ +#include +static int bond_br_stp_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct stpPortInfo *pInfo = (struct stpPortInfo *)ptr; + struct net_device *bond_dev; + + bond_dev = dev_get_by_name(&init_net, pInfo->portName); + if (!bond_dev) + return NOTIFY_DONE; + + if ((bond_dev->priv_flags & IFF_BONDING) && + (bond_dev->flags & IFF_MASTER)) { + struct bonding *bond = netdev_priv(bond_dev); + struct slave *slave; + struct list_head *iter; + + bond_for_each_slave(bond, slave, iter) { + /* Propogate the STP event to slaves */ + call_br_stp_notifiers(event, slave->dev, pInfo); + } + + } + + dev_put(bond_dev); + return NOTIFY_DONE; +} + +static struct notifier_block bond_br_stp_notifier = { + .notifier_call = bond_br_stp_event, +}; + +#endif /*---------------------------- Hashing Policies -----------------------------*/ /* L2 hash helper */ @@ -3983,6 +4047,32 @@ !bond_slave_override(bond, skb)) return NETDEV_TX_OK; +#if defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + +/* Only XOR or 802.3AD mode can be accelerated by flow-cache -- these are flow-based moded */ +#define BOND_SUPPORTED_ACCL_MODE(mode) (((mode) == BOND_MODE_XOR) || ((mode) == BOND_MODE_8023AD)) + +/* IS_SKB check not needed unless we support flow-cache acceleration to bond device in other modes; + No need to check for non-NULL blog as well -- blog_skip will take care */ + + if (!BOND_SUPPORTED_ACCL_MODE(bond->params.mode)) + blog_skip(skb, blog_skip_reason_bond); + else if (blog_ptr(skb)) + { + /* Clarify : bond device shouldn't modify the packet; + also it does not hold its own stats rather gets the cummulative stats from its slave devices [ see:bond_get_stats() ]; + blog->put_stats,clr_stats are also not needed for the same reason; + Do we need to link bond devices ?? ; + One usage could be that when bond device goes down -- FC will be able to remove the flows */ + blog_lock(); + blog_link( IF_DEVICE, blog_ptr(skb), (void*)dev, DIR_TX, skb->len ); + blog_unlock(); + } + +#endif /* defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) */ +#endif /* defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) */ + switch (BOND_MODE(bond)) { case BOND_MODE_ROUNDROBIN: return bond_xmit_roundrobin(skb, dev); @@ -4676,6 +4766,35 @@ .size = sizeof(struct bond_net), }; +#if defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) && defined(CONFIG_BCM_KF_LOG) +/* Callback function through bcm_log function pointer; + Given a slave device ; update bond group stat, and clear saved slave stat */ +int bcm_bond_clr_slave_stat(void *ctxt) +{ + struct net_device *slave_dev = ctxt; + + if (netif_is_bond_slave(slave_dev)) { + struct slave *slave = bond_slave_get_rcu(slave_dev); + if (slave) { + rcu_read_lock(); + /* clear slave stats for the next run */ + memset(&slave->slave_stats, 0, sizeof(slave->slave_stats)); + rcu_read_unlock(); + return 0; + } + } + return -1; +} + +/* Callback function through bcm_log function pointer; + Given a RX Handler function pointer for a device ; confirms if it is same as bond RX handler */ +int bcm_bond_is_rx_handler(void *ctxt) +{ + return (ctxt == (void*)bond_handle_frame); +} + +#endif /* defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) */ + static int __init bonding_init(void) { int i; @@ -4704,6 +4823,13 @@ } register_netdevice_notifier(&bond_netdev_notifier); + +#if defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) + register_bridge_stp_notifier(&bond_br_stp_notifier); + bcmFun_reg(BCM_FUN_ID_BOND_CLR_SLAVE_STAT, bcm_bond_clr_slave_stat); + bcmFun_reg(BCM_FUN_ID_BOND_RX_HANDLER, bcm_bond_is_rx_handler); +#endif /* defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) */ + out: return res; err: @@ -4719,6 +4845,12 @@ { unregister_netdevice_notifier(&bond_netdev_notifier); +#if defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) + unregister_bridge_stp_notifier(&bond_br_stp_notifier); + bcmFun_dereg(BCM_FUN_ID_BOND_CLR_SLAVE_STAT); + bcmFun_dereg(BCM_FUN_ID_BOND_RX_HANDLER); +#endif /* defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) */ + bond_destroy_debugfs(); bond_netlink_fini(); diff -urN linux-4.1.52-orig/drivers/net/bonding/bond_options.c linux-4.1.52/drivers/net/bonding/bond_options.c --- linux-4.1.52-orig/drivers/net/bonding/bond_options.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/net/bonding/bond_options.c 2020-02-07 01:47:54.000000000 +0800 @@ -70,6 +70,10 @@ const struct bond_opt_value *newval); static int bond_option_tlb_dynamic_lb_set(struct bonding *bond, const struct bond_opt_value *newval); +#if defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) +static int bond_option_async_linkspeed_set(struct bonding *bond, + const struct bond_opt_value *newval); +#endif /* defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) */ static const struct bond_opt_value bond_mode_tbl[] = { @@ -186,6 +190,14 @@ { NULL, -1, 0} }; +#if defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) +static const struct bond_opt_value bond_async_linkspeed_tbl[] = { + { "off", BOND_ASYNC_LINKSPEED_OFF, BOND_VALFLAG_DEFAULT}, + { "on", BOND_ASYNC_LINKSPEED_ON, 0}, + { NULL, -1, 0}, +}; +#endif /* defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) */ + static const struct bond_option bond_opts[BOND_OPT_LAST] = { [BOND_OPT_MODE] = { .id = BOND_OPT_MODE, @@ -379,6 +391,16 @@ .values = bond_tlb_dynamic_lb_tbl, .flags = BOND_OPTFLAG_IFDOWN, .set = bond_option_tlb_dynamic_lb_set, +#if defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) + }, + [BOND_OPT_ASYNC_LINKSPEED] = { + .id = BOND_OPT_ASYNC_LINKSPEED, + .name = "async_linkspeed", + .desc = "Enable Aggregation of links of different data rates is not prohibited nor required by 802.1ax.", + .unsuppmodes = BOND_MODE_ALL_EX(BIT(BOND_MODE_8023AD)), + .values = bond_async_linkspeed_tbl, + .set = bond_option_async_linkspeed_set +#endif /* defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) */ } }; @@ -1349,3 +1371,15 @@ return 0; } + +#if defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) +static int bond_option_async_linkspeed_set(struct bonding *bond, + const struct bond_opt_value *newval) +{ + netdev_info(bond->dev, "Setting ad_select to %s (%llu)\n", + newval->string, newval->value); + bond->params.async_linkspeed = newval->value; + + return 0; +} +#endif /* defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) */ diff -urN linux-4.1.52-orig/drivers/net/bonding/bond_sysfs.c linux-4.1.52/drivers/net/bonding/bond_sysfs.c --- linux-4.1.52-orig/drivers/net/bonding/bond_sysfs.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/net/bonding/bond_sysfs.c 2020-02-07 01:47:54.000000000 +0800 @@ -692,6 +692,22 @@ static DEVICE_ATTR(packets_per_slave, S_IRUGO | S_IWUSR, bonding_show_packets_per_slave, bonding_sysfs_store_option); +#if defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) +static ssize_t bonding_show_async_linkspeed(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct bonding *bond = to_bond(d); + const struct bond_opt_value *val; + + val = bond_opt_get_val(BOND_OPT_ASYNC_LINKSPEED, bond->params.async_linkspeed); + + return sprintf(buf, "%s %d\n", val->string, bond->params.async_linkspeed); +} +static DEVICE_ATTR(async_linkspeed, S_IRUGO | S_IWUSR, + bonding_show_async_linkspeed, bonding_sysfs_store_option); +#endif /* defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) */ + static struct attribute *per_bond_attrs[] = { &dev_attr_slaves.attr, &dev_attr_mode.attr, @@ -725,6 +741,9 @@ &dev_attr_lp_interval.attr, &dev_attr_packets_per_slave.attr, &dev_attr_tlb_dynamic_lb.attr, +#if defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) + &dev_attr_async_linkspeed.attr, +#endif /* defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) */ NULL, }; diff -urN linux-4.1.52-orig/drivers/net/macvlan.c linux-4.1.52/drivers/net/macvlan.c --- linux-4.1.52-orig/drivers/net/macvlan.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/net/macvlan.c 2020-02-07 01:47:54.000000000 +0800 @@ -410,6 +410,15 @@ int ret; rx_handler_result_t handle_res; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + if (blog_ptr(skb)) + { + blog_lock(); + blog_link( IF_DEVICE, blog_ptr(skb), (void*)skb->dev, DIR_RX, skb->len ); + blog_unlock(); + } +#endif + port = macvlan_port_get_rcu(skb->dev); if (is_multicast_ether_addr(eth->h_dest)) { skb = ip_check_defrag(skb, IP_DEFRAG_MACVLAN); @@ -513,6 +522,14 @@ int ret; struct macvlan_dev *vlan = netdev_priv(dev); +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + if (blog_ptr(skb)) + { + blog_lock(); + blog_link( IF_DEVICE, blog_ptr(skb), (void*)dev, DIR_TX, skb->len ); + blog_unlock(); + } +#endif if (unlikely(netpoll_tx_running(dev))) return macvlan_netpoll_send_skb(vlan, skb); @@ -1060,6 +1077,9 @@ dev->destructor = free_netdev; dev->header_ops = &macvlan_hard_header_ops; dev->ethtool_ops = &macvlan_ethtool_ops; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + dev->blog_stats_flags |= BLOG_DEV_STAT_FLAG_INCLUDE_ALL; +#endif } EXPORT_SYMBOL_GPL(macvlan_common_setup); diff -urN linux-4.1.52-orig/drivers/net/Makefile linux-4.1.52/drivers/net/Makefile --- linux-4.1.52-orig/drivers/net/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/net/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -49,6 +49,9 @@ obj-$(CONFIG_PPPOE) += ppp/ obj-$(CONFIG_PPPOL2TP) += ppp/ obj-$(CONFIG_PPTP) += ppp/ +ifdef BCM_KF # defined CONFIG_BCM_KF_ACCEL_PPTP +#obj-$(CONFIG_ACCEL_PPTP) += accel-pptp/ +endif # BCM_KF # defined CONFIG_BCM_KF_ACCEL_PPTP obj-$(CONFIG_SLIP) += slip/ obj-$(CONFIG_SLHC) += slip/ obj-$(CONFIG_NET_SB1000) += sb1000.o diff -urN linux-4.1.52-orig/drivers/net/ppp/ppp_generic.c linux-4.1.52/drivers/net/ppp/ppp_generic.c --- linux-4.1.52-orig/drivers/net/ppp/ppp_generic.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/net/ppp/ppp_generic.c 2020-02-07 01:47:54.000000000 +0800 @@ -54,6 +54,10 @@ #include #include +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#include +#endif + #define PPP_VERSION "2.4.2" /* @@ -70,6 +74,12 @@ #define MPHDRLEN 6 /* multilink protocol header length */ #define MPHDRLEN_SSN 4 /* ditto with short sequence numbers */ +#if defined(CONFIG_BCM_KF_PPP) +#define FIELD0 4 /* ppp device number ppp0, ppp1, the third digit (max 16) */ +#define FIELD1 8 /* if 0, default mode, 1 vlan mux, 2 msc */ +#define FIELD2 19 /* if FILED1 is 0, add no extension, 1 add vlan id, 2 add conId for msc */ +#endif + /* * An instance of /dev/ppp can be associated with either a ppp * interface unit or a ppp channel. In both cases, file->private_data @@ -134,6 +144,7 @@ unsigned long last_recv; /* jiffies when last pkt rcvd a0 */ struct net_device *dev; /* network interface device a4 */ int closing; /* is device closing down? a8 */ + #ifdef CONFIG_PPP_MULTILINK int nxchan; /* next channel to send something on */ u32 nxseq; /* next sequence number to send */ @@ -494,6 +505,10 @@ goto out; } +#if defined(CONFIG_BCM_KF_PPP) + skb->mark = 7; /* mark with the highest subpriority value */ +#endif + skb_queue_tail(&pf->xq, skb); switch (pf->kind) { @@ -570,8 +585,15 @@ struct npioctl npi; int unit, cflags; struct slcompress *vj; +#if defined(CONFIG_BCM_KF_PPP) && defined(CONFIG_BCM_KF_NETDEV_PATH) + char real_dev_name[IFNAMSIZ]; + struct net_device *real_dev; +#endif void __user *argp = (void __user *)arg; int __user *p = argp; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + BlogStats_t bStats; +#endif mutex_lock(&ppp_mutex); @@ -653,6 +675,38 @@ err = 0; break; +#if defined(CONFIG_BCM_KF_PPP) && defined(CONFIG_BCM_KF_NETDEV_PATH) + case PPPIOCSREALDEV: + /* 64 bit compatibility handling is not needed since the dev_name string + is passed as a pointer in the arg. Compatibility handling is needed + only when a pointer to a structure is passed in the arg field and + the members of the structure require a 32bit<->64 bit conversion */ + copy_from_user(real_dev_name, argp, IFNAMSIZ); + real_dev_name[IFNAMSIZ-1] = '\0'; /* NULL terminate, just in case */ + + real_dev = dev_get_by_name(&init_net, real_dev_name); + if(real_dev == NULL) + { + printk(KERN_ERR "PPP: Invalid Real Device Name : %s\n", real_dev_name); + err = -EINVAL; + break; + } + + err = netdev_path_add(ppp->dev, real_dev); + if(err) + { + printk(KERN_ERR "PPP: Failed to add %s to Interface path (%d)", + ppp->dev->name, err); + dev_put(real_dev); + } + else + { + netdev_path_dump(ppp->dev); + } + + break; +#endif + case PPPIOCSFLAGS: if (get_user(val, p)) break; @@ -700,6 +754,14 @@ break; case PPPIOCGIDLE: +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + memset(&bStats, 0, sizeof(BlogStats_t)); + blog_get_dev_running_stats(ppp->dev, &bStats); + if(bStats.tx_packets) + ppp->last_xmit = jiffies; + if(bStats.rx_packets) + ppp->last_recv = jiffies; +#endif idle.xmit_idle = (jiffies - ppp->last_xmit) / HZ; idle.recv_idle = (jiffies - ppp->last_recv) / HZ; if (copy_to_user(argp, &idle, sizeof(idle))) @@ -889,14 +951,14 @@ } static const struct file_operations ppp_device_fops = { - .owner = THIS_MODULE, - .read = ppp_read, - .write = ppp_write, - .poll = ppp_poll, + .owner = THIS_MODULE, + .read = ppp_read, + .write = ppp_write, + .poll = ppp_poll, .unlocked_ioctl = ppp_ioctl, - .open = ppp_open, - .release = ppp_release, - .llseek = noop_llseek, + .open = ppp_open, + .release = ppp_release, + .llseek = noop_llseek, }; static __net_init int ppp_init_net(struct net *net) @@ -1016,6 +1078,33 @@ return NETDEV_TX_OK; } +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +/* note: BLOG changes for read-only statistic data. */ +static void ppp_dev_clear_stats(struct net_device * dev_p) +{ + struct ppp *ppp; + + if ( dev_p == (struct net_device *)NULL ) + return; + ppp = netdev_priv(dev_p); + + ppp_recv_lock(ppp); + ppp->stats64.rx_packets = 0; + ppp->stats64.rx_bytes = 0; + ppp_recv_unlock(ppp); + + ppp_xmit_lock(ppp); + ppp->stats64.tx_packets = 0; + ppp->stats64.tx_bytes = 0; + ppp_xmit_unlock(ppp); + + memset(&dev_p->stats, 0, sizeof(dev_p->stats)); + + blog_clr_dev_stats(dev_p); + return; +} +#endif /* defined(CONFIG_BLOG) */ + static int ppp_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { @@ -1082,7 +1171,6 @@ return stats64; } - static struct lock_class_key ppp_tx_busylock; static int ppp_dev_init(struct net_device *dev) { @@ -1106,8 +1194,18 @@ dev->tx_queue_len = 3; dev->type = ARPHRD_PPP; dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; +#if defined(CONFIG_BCM_KF_WANDEV) + dev->priv_flags = IFF_WANDEV; +#endif +#if defined(CONFIG_BCM_KF_PPP) + dev->priv_flags |= IFF_PPP; +#endif dev->features |= NETIF_F_NETNS_LOCAL; netif_keep_dst(dev); +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + dev->blog_stats_flags |= BLOG_DEV_STAT_FLAG_INCLUDE_ALL; + dev->clr_stats = ppp_dev_clear_stats; +#endif } /* @@ -1189,6 +1287,54 @@ return new_skb; } +#if defined(CONFIG_BCM_KF_PPP) +/* +brcm_on_demand_filter(...) and ppp_send_frame(...) are protected for SMP+Preempt safety +by ppp_xmit_lock(ppp) => spin_lock_bh(&(ppp)->wlock) and ppp_xmit_unlock(ppp) => spin_unlock_bh(&(ppp)->wlock). +*/ + +/* + * Excluding timestamp for packet generated from ADSL modem + * these include WAN-side RIP,dnsprobe + */ +static int +brcm_on_demand_filter(char *data) +{ + unsigned short udp_port=0; + +#if 0 + char cmd; + + printk("%02x%02x%02x%02x\n%02x%02x%02x%02x\n",data[2],data[3],data[4],data[5],data[6],data[7],data[8],data[9]); + printk("%02x%02x%02x%02x\n%02x%02x%02x%02x\n",data[10],data[11],data[12],data[13],data[14],data[15],data[16],data[17]); + printk("%02x%02x%02x%02x\n",data[18],data[19],data[20],data[21]); +#endif + + if ( data[11] == 0x2 ) /* IGMP */ + return 0; + if ( data[11] == 0x11 ) { /* UDP */ + udp_port= (data[24]<< 8) + data[25]; + if ( udp_port == 123 ) { /* ntpclient */ + return 0; + } + if ( udp_port == 53 ) { + if ( data[45] == 'r' && data[46] == 'o' && data[47] == 'o' && data[48] =='t') + + return 0; + } + else if (udp_port == 520) { /* RIP */ +#if 0 + cmd = data[30]; // 1=request, 2=reply + if ( cmd == 1) +#endif + return 0; + } + } + + return 1; +} +#endif + /* * Compress and send a frame. * The caller should have locked the xmit path, @@ -1201,6 +1347,15 @@ struct sk_buff *new_skb; int len; unsigned char *cp; +#if defined(CONFIG_BCM_KF_PPP) + unsigned char *data; + int timestamp = 1; + + if ( proto == PPP_IP) { + data = skb->data; + timestamp = brcm_on_demand_filter(data); + } +#endif if (proto < 0x8000) { #ifdef CONFIG_PPP_FILTER @@ -1209,7 +1364,7 @@ a four-byte PPP header on each packet */ *skb_push(skb, 2) = 1; if (ppp->pass_filter && - BPF_PROG_RUN(ppp->pass_filter, skb) == 0) { + BPF_PROG_RUN(ppp->pass_filter, skb) == 0) { if (ppp->debug & 1) netdev_printk(KERN_DEBUG, ppp->dev, "PPP: outbound frame " @@ -1219,15 +1374,27 @@ } /* if this packet passes the active filter, record the time */ if (!(ppp->active_filter && - BPF_PROG_RUN(ppp->active_filter, skb) == 0)) + BPF_PROG_RUN(ppp->active_filter, skb) == 0)) +#if defined(CONFIG_BCM_KF_PPP) + if (timestamp) +#endif ppp->last_xmit = jiffies; skb_pull(skb, 2); #else /* for data packets, record the time */ +#if defined(CONFIG_BCM_KF_PPP) + if (timestamp) +#endif ppp->last_xmit = jiffies; #endif /* CONFIG_PPP_FILTER */ } +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_lock(); + blog_link(IF_DEVICE, blog_ptr(skb), (void*)ppp->dev, DIR_TX, skb->len - 2 ); + blog_unlock(); +#endif + ++ppp->stats64.tx_packets; ppp->stats64.tx_bytes += skb->len - 2; @@ -1294,6 +1461,10 @@ if (ppp->flags & SC_LOOP_TRAFFIC) { if (ppp->file.rq.qlen > PPP_MAX_RQLEN) goto drop; +#if defined(CONFIG_BCM_KF_PPP) + if (!timestamp) + goto drop; +#endif skb_queue_tail(&ppp->file.rq, skb); wake_up_interruptible(&ppp->file.rwait); return; @@ -1740,11 +1911,42 @@ slhc_toss(ppp->vj); } +#if defined(CONFIG_BCM_KF_PPP) +/* +note: brcm_mcast_filter(...) and ppp_receive_nonmp_frame(...) are protected for SMP+Preempt safety +by ppp_recv_lock(ppp) => spin_lock_bh(&(ppp)->rlock) and ppp_recv_unlock(ppp) => spin_unlock_bh(&(ppp)->rlock). +*/ + +static int +brcm_mcast_filter(char *data) +{ + struct iphdr *encap; + + encap = (struct iphdr *)(data + 2); + if ( ipv4_is_multicast(encap->daddr)) { + if ( !ipv4_is_local_multicast(encap->daddr)) { // real mcast data + //printk("bcm_mcast_filer: 0x%x \n",encap->daddr); + return 1; // no timestamp + } + else + return 0; + } + else + return 0; +} +#endif + + static void ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb) { struct sk_buff *ns; int proto, len, npi; +#if defined(CONFIG_BCM_KF_PPP) + struct sk_buff *tmp; + int timestamp=0; + unsigned char *data; +#endif /* * Decompress the frame, if compressed. @@ -1759,6 +1961,13 @@ goto err; proto = PPP_PROTO(skb); + +#if defined(CONFIG_BCM_KF_PPP) + if (proto == PPP_IP) { + data = skb->data; + timestamp = brcm_mcast_filter(data); + } +#endif switch (proto) { case PPP_VJC_COMP: /* decompress VJ compressed packets */ @@ -1817,6 +2026,12 @@ break; } +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_lock(); + blog_link(IF_DEVICE, blog_ptr(skb), (void*)ppp->dev, DIR_RX, skb->len - 2 ); + blog_unlock(); +#endif + ++ppp->stats64.rx_packets; ppp->stats64.rx_bytes += skb->len - 2; @@ -1834,6 +2049,37 @@ } else { /* network protocol frame - give it to the kernel */ +#if defined(CONFIG_BCM_KF_PPP) +#ifdef CONFIG_PPP_FILTER + /* check if the packet passes the pass and active filters */ + /* the filter instructions are constructed assuming + a four-byte PPP header on each packet */ + if (skb_headroom(skb) < 2) { + tmp = alloc_skb(skb->len+2,GFP_ATOMIC); + skb_reserve(tmp, 2); + memcpy(skb_put(tmp, skb->len), skb->data, skb->len); + kfree_skb(skb); + skb = tmp; + } + *skb_push(skb, 2) = 0; + if (ppp->pass_filter && + BPF_PROG_RUN(ppp->pass_filter, skb) == 0) { + if (ppp->debug & 1) + printk(KERN_DEBUG "PPP: inbound frame not passed\n"); + kfree_skb(skb); + return; + } + if (!(ppp->active_filter && + BPF_PROG_RUN(ppp->active_filter, skb) == 0)) + if (timestamp) + ppp->last_recv = jiffies; + skb_pull(skb, 2); +#else + if (timestamp) + ppp->last_recv = jiffies; +#endif /* CONFIG_PPP_FILTER */ + +#else #ifdef CONFIG_PPP_FILTER /* check if the packet passes the pass and active filters */ /* the filter instructions are constructed assuming @@ -1859,6 +2105,7 @@ } else #endif /* CONFIG_PPP_FILTER */ ppp->last_recv = jiffies; +#endif /* CONFIG_BCM_KF_PPP */ if ((ppp->dev->flags & IFF_UP) == 0 || ppp->npmode[npi] != NPMODE_PASS) { @@ -2649,6 +2896,18 @@ st->p.ppp_opackets = ppp->stats64.tx_packets; st->p.ppp_oerrors = ppp->dev->stats.tx_errors; st->p.ppp_obytes = ppp->stats64.tx_bytes; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + { + BlogStats_t bStats; + memset(&bStats, 0, sizeof(bStats)); + blog_get_dev_stats(ppp->dev, &bStats); + + st->p.ppp_ipackets += bStats.rx_packets; + st->p.ppp_ibytes += bStats.rx_bytes; + st->p.ppp_opackets += bStats.tx_packets; + st->p.ppp_obytes += bStats.tx_bytes; + } +#endif if (!vj) return; st->vj.vjs_packets = vj->sls_o_compressed + vj->sls_o_uncompressed; @@ -2666,6 +2925,10 @@ * and for initialization. */ +#if defined(CONFIG_BCM_KF_PPP) +/* note: ppp_create_interface(...) is protected by lock_kernel() and unlock_kernel() in ppp_unattached_ioctl(...). */ +#endif + /* * Create a new ppp interface unit. Fails if it can't allocate memory * or if there is already a unit with the requested number. @@ -2740,7 +3003,43 @@ /* Initialize the new ppp unit */ ppp->file.index = unit; + +#if defined(CONFIG_BCM_KF_PPP) + if (unit >= 0) + { + unsigned num[3]={0,0,0}; + unsigned u=unit; + + /* req_name will beused as ifname and for + * num[1] == 0: default connection mdoe: ppp0, ppp1... + * num[1] == 1: vlanMux mode: ppp0.100, ppp1.200... + * num[1] == 2: msc (multiple service mode) ppp0_1, ppp1_3... + * num[1] == 3: pppoa0, pppoa1... + * + */ + num[0] = u<<(32-(FIELD2+FIELD1+FIELD0))>>(32-FIELD0); + num[1] = u<<(32-(FIELD2+FIELD1))>>(32-FIELD1); + num[2] = u<<(32-(FIELD2))>>(32-FIELD2); + if (num[1] == 0) + { + sprintf(dev->name, "ppp%d", num[0]); + } + else if (num[1] == 1) /* vlan mux */ + { + sprintf(dev->name, "ppp%d.%d", num[0], num[2]); + } + else if (num[1] == 2) /* msc */ + { + sprintf(dev->name, "ppp%d_%d", num[0], num[2]); + } + else if (num[1] == 3) /* pppoa */ + { + sprintf(dev->name, "pppoa%d", num[0]); + } + } +#else sprintf(dev->name, "ppp%d", unit); +#endif ret = register_netdev(dev); if (ret != 0) { @@ -2793,6 +3092,20 @@ /* This will call dev_close() for us. */ ppp_lock(ppp); if (!ppp->closing) { +#if defined(CONFIG_BCM_KF_PPP) + int err; + struct net_device *next_dev; + next_dev = netdev_path_next_dev(ppp->dev); + err = netdev_path_remove(ppp->dev); + if(err) + { + printk(KERN_ERR "PPP: Failed to remove %s from Interface path (%d)", + ppp->dev->name, err); + netdev_path_dump(ppp->dev); + } + if(next_dev != NULL) + dev_put(next_dev); +#endif ppp->closing = 1; ppp_unlock(ppp); unregister_netdev(ppp->dev); @@ -3029,6 +3342,32 @@ return idr_find(p, n); } +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +int +ppp_rcv_decomp_run(struct ppp_channel *chan) +{ + struct channel *ch; + struct ppp *ppp; + + if (!chan) + return 0; + ch = chan->ppp; + if (!ch) + return 0; + ppp = ch->ppp; + if (!ppp) + return 0; + + /*pr_info("%s: flags 0x%x xstate 0x%x rstate 0x%x\n", + __FUNCTION__, ppp->flags, ppp->xstate, ppp->rstate);*/ + + if (ppp->rstate & SC_DECOMP_RUN) + return 1; + else + return 0; +} +#endif + /* Module/initialization stuff */ module_init(ppp_init); @@ -3045,6 +3384,9 @@ EXPORT_SYMBOL(ppp_output_wakeup); EXPORT_SYMBOL(ppp_register_compressor); EXPORT_SYMBOL(ppp_unregister_compressor); +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +EXPORT_SYMBOL(ppp_rcv_decomp_run); +#endif MODULE_LICENSE("GPL"); MODULE_ALIAS_CHARDEV(PPP_MAJOR, 0); MODULE_ALIAS("devname:ppp"); diff -urN linux-4.1.52-orig/drivers/net/ppp/pppoe.c linux-4.1.52/drivers/net/ppp/pppoe.c --- linux-4.1.52-orig/drivers/net/ppp/pppoe.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/net/ppp/pppoe.c 2020-02-07 01:47:54.000000000 +0800 @@ -497,7 +497,11 @@ goto abort; ph = pppoe_hdr(skb); +#if defined(CONFIG_BCM_KF_PPP) + if ((ph->code != PADT_CODE) || (ph->sid)) +#else if (ph->code != PADT_CODE) +#endif /* CONFIG_BCM_KF_PPP */ goto abort; pn = pppoe_pernet(dev_net(dev)); diff -urN linux-4.1.52-orig/drivers/net/ppp/pptp.c linux-4.1.52/drivers/net/ppp/pptp.c --- linux-4.1.52-orig/drivers/net/ppp/pptp.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/net/ppp/pptp.c 2020-02-07 01:47:54.000000000 +0800 @@ -41,6 +41,9 @@ #include #include +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#include +#endif #define PPTP_DRIVER_VERSION "0.8.5" @@ -49,6 +52,12 @@ static DECLARE_BITMAP(callid_bitmap, MAX_CALLID + 1); static struct pppox_sock __rcu **callid_sock; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +extern int ppp_rcv_decomp_run(struct ppp_channel *); +void pptp_xmit_update(uint16_t call_id, uint32_t seqNum, uint32_t ackNum, uint32_t daddr); +int pptp_xmit_get(uint16_t call_id, uint32_t* seqNum, uint32_t* ackNum, uint32_t daddr); +int pptp_rcv_check(uint16_t call_id, uint32_t *rcv_pktSeq, uint32_t rcv_pktAck, uint32_t saddr); +#endif static DEFINE_SPINLOCK(chan_lock); static struct proto pptp_sk_proto __read_mostly; @@ -345,6 +354,10 @@ goto drop; payload = skb->data + headersize; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + if (!blog_gre_tunnel_accelerated() || ppp_rcv_decomp_run(&po->chan)) + { +#endif /* check for expected sequence number */ if (seq < opt->seq_recv + 1 || WRAPPED(opt->seq_recv, seq)) { if ((payload[0] == PPP_ALLSTATIONS) && (payload[1] == PPP_UI) && @@ -374,6 +387,40 @@ return NET_RX_SUCCESS; } +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + } + else /* blog_gre_tunnel_accelerated is true, so opt->seq_recv and opt->ack_recv have been ++ by pptp_rcv_check() */ + { + /* check for expected sequence number */ + if ( seq < opt->seq_recv || WRAPPED(opt->seq_recv, seq) ) + { + if ( (payload[0] == PPP_ALLSTATIONS) && (payload[1] == PPP_UI) && (PPP_PROTOCOL(payload) == PPP_LCP) && + ((payload[4] == PPP_LCP_ECHOREQ) || (payload[4] == PPP_LCP_ECHOREP)) ) + goto allow_packet2; + } + else + { +allow_packet2: + //printk(" PPTP: blog_gre_tunnel_accelerated!\n"); + skb_pull(skb,headersize); + if (payload[0] == PPP_ALLSTATIONS && payload[1] == PPP_UI) + { + /* chop off address/control */ + if (skb->len < 3) + goto drop; + skb_pull(skb,2); + } + if ((*skb->data) & 1){ + /* protocol is compressed */ + skb_push(skb, 1)[0] = 0; + } + skb->ip_summed=CHECKSUM_NONE; + skb_set_network_header(skb,skb->head-skb->data); + ppp_input(&po->chan,skb); + return NET_RX_SUCCESS; + } + } +#endif drop: kfree_skb(skb); return NET_RX_DROP; @@ -712,6 +759,12 @@ goto out_unregister_sk_proto; } + +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_pptp_xmit_update_fn = (blog_pptp_xmit_upd_t) pptp_xmit_update; + blog_pptp_xmit_get_fn = (blog_pptp_xmit_get_t) pptp_xmit_get; + blog_pptp_rcv_check_fn = (blog_pptp_rcv_check_t) pptp_rcv_check; +#endif return 0; out_unregister_sk_proto: @@ -732,6 +785,128 @@ vfree(callid_sock); } +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +void pptp_xmit_update(uint16_t call_id, uint32_t seqNum, uint32_t ackNum, uint32_t daddr) +{ + struct pppox_sock *sock; + struct pptp_opt *opt; + int i; + + rcu_read_lock(); + + for(i = find_next_bit(callid_bitmap,MAX_CALLID,1); i < MAX_CALLID; i = find_next_bit(callid_bitmap, MAX_CALLID, i + 1)) + { + sock = rcu_dereference(callid_sock[i]); + if (!sock) + continue; + + opt = &sock->proto.pptp; + if (opt->dst_addr.call_id == call_id && opt->dst_addr.sin_addr.s_addr == daddr) + { + //printk(KERN_INFO "PPTP: find the channel!\n"); + if( opt->seq_sent != seqNum && seqNum > 0) + { + //printk(KERN_INFO "PPTP: update seq_sent!\n"); + opt->seq_sent = seqNum; + } + if( opt->ack_sent != ackNum && ackNum > 0) + { + //printk(KERN_INFO "PPTP: update ack_sent!\n"); + opt->ack_sent = ackNum; + } + break; + } + } + + rcu_read_unlock(); + + return; +} + +int pptp_xmit_get(uint16_t call_id, uint32_t* seqNum, uint32_t* ackNum, uint32_t daddr) +{ + struct pppox_sock *sock; + struct pptp_opt *opt; + int i, ack_flag = PPTP_NOT_ACK; + + rcu_read_lock(); + + for(i = find_next_bit(callid_bitmap,MAX_CALLID,1); i < MAX_CALLID; i = find_next_bit(callid_bitmap, MAX_CALLID, i + 1)) + { + sock = rcu_dereference(callid_sock[i]); + if (!sock) + continue; + + opt = &sock->proto.pptp; + if (opt->dst_addr.call_id == call_id && opt->dst_addr.sin_addr.s_addr == daddr) + { + //printk(KERN_INFO "PPTP: seq_sent = %d, ack_sent = %d \n", opt->seq_sent, opt->ack_sent); + opt->seq_sent += 1; + *seqNum = opt->seq_sent; + *ackNum = opt->ack_sent; + + if (opt->ack_sent != opt->seq_recv) + { + ack_flag = PPTP_WITH_ACK; + *ackNum = opt->ack_sent = opt->seq_recv; + } + break; + } + } + + rcu_read_unlock(); + + return ack_flag; +} + +int pptp_rcv_check(uint16_t call_id, uint32_t *rcv_pktSeq, uint32_t rcv_pktAck, uint32_t saddr) +{ + struct pppox_sock *sock; + struct pptp_opt *opt; + int ret = BLOG_PPTP_RCV_NO_TUNNEL; + + rcu_read_lock(); + sock = rcu_dereference(callid_sock[call_id]); + if (sock) + { + opt=&sock->proto.pptp; + if (opt->dst_addr.sin_addr.s_addr!=saddr) + { + sock=NULL; + } + else + { + sock_hold(sk_pppox(sock)); + //printk(KERN_INFO "PPTP: pptp_rcv_check() current seq_recv is %d \n", opt->seq_recv); + if (ppp_rcv_decomp_run(&sock->chan)) { + ret = BLOG_PPTP_ENCRYPTED; + rcu_read_unlock(); + return ret; + } else if (opt->seq_recv && ((*rcv_pktSeq) > opt->seq_recv)) + { + opt->seq_recv = (*rcv_pktSeq); + ret = BLOG_PPTP_RCV_IN_SEQ; + } else if (opt->seq_recv && ((*rcv_pktSeq) - opt->seq_recv) <= 0) { + printk(KERN_INFO "pptp_rcv_check():[BLOG_PPTP_RCV_OOS_LT] current seq_recv is %d \n", opt->seq_recv); + ret = BLOG_PPTP_RCV_OOS_LT; + } else { + printk(KERN_INFO "pptp_rcv_check():[BLOG_PPTP_RCV_OOS_GT] current seq_recv is %d \n", opt->seq_recv); + opt->seq_recv = (*rcv_pktSeq); + ret = BLOG_PPTP_RCV_OOS_GT; + } + if (rcv_pktAck > opt->ack_recv) opt->ack_recv = rcv_pktAck; + } + + } + rcu_read_unlock(); + return ret; +} + +EXPORT_SYMBOL(pptp_xmit_update); +EXPORT_SYMBOL(pptp_xmit_get); +EXPORT_SYMBOL(pptp_rcv_check); +#endif + module_init(pptp_init_module); module_exit(pptp_exit_module); diff -urN linux-4.1.52-orig/drivers/net/usb/ax88179_178a.c linux-4.1.52/drivers/net/usb/ax88179_178a.c --- linux-4.1.52-orig/drivers/net/usb/ax88179_178a.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/net/usb/ax88179_178a.c 2020-02-07 01:47:54.000000000 +0800 @@ -190,7 +190,12 @@ static const struct { unsigned char ctrl, timer_l, timer_h, size, ifg; } AX88179_BULKIN_SIZE[] = { +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_THREAD)) + /* try to reduce number of interrupts */ + {7, 0x4f, 1, 0x12, 0xff}, +#else {7, 0x4f, 0, 0x12, 0xff}, +#endif {7, 0x20, 3, 0x16, 0xff}, {7, 0xae, 7, 0x18, 0xff}, {7, 0xcc, 0x4c, 0x18, 8}, diff -urN linux-4.1.52-orig/drivers/net/usb/Makefile linux-4.1.52/drivers/net/usb/Makefile --- linux-4.1.52-orig/drivers/net/usb/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/net/usb/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -2,6 +2,10 @@ # Makefile for USB Network drivers # +ifdef BCM_KF # defined(CONFIG_BCM_KF_USBNET) +EXTRA_CFLAGS += -I$(INC_BRCMSHARED_PUB_PATH)/$(BRCM_BOARD) +endif #BCM_KF # defined(CONFIG_BCM_KF_USBNET) + obj-$(CONFIG_USB_CATC) += catc.o obj-$(CONFIG_USB_KAWETH) += kaweth.o obj-$(CONFIG_USB_PEGASUS) += pegasus.o diff -urN linux-4.1.52-orig/drivers/net/usb/usbnet.c linux-4.1.52/drivers/net/usb/usbnet.c --- linux-4.1.52-orig/drivers/net/usb/usbnet.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/net/usb/usbnet.c 2020-02-07 01:47:54.000000000 +0800 @@ -46,6 +46,16 @@ #include #include +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_ACCELERATION) && defined(CONFIG_BLOG)) +#include +#include +#endif + +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_THREAD)) +#include +#include +#endif + #define DRIVER_VERSION "22-Aug-2005" @@ -77,6 +87,13 @@ // between wakeups #define UNLINK_TIMEOUT_MS 3 +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_THREAD)) + +#define USBNET_RX_BUDGET 64 +#define USBNET_PENDING_RX_SKB_THRESH_DEFAULT 64 + +#endif + /*-------------------------------------------------------------------------*/ // randomly generated ethernet address @@ -323,6 +340,30 @@ return; } +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_ACCELERATION) && defined(CONFIG_BLOG)) + if(skb->clone_fc_head == NULL) + { + /* Make sure fcache does not expand the skb->data if clone_fc_head + * is not set by the dongle driver's(ex:rndis_host.c, asix.c etc..) + * we expect dongle/class drivers using fcache to set minumun headroom + * available for all packets in an aggregated skb by calling + * skb_clone_headers_set() before calling usbnet_skb_return. + * + * Ex:rndis based drivers have 8 bytes spacig between 2 packets in an + * aggreated skb. we can call skb_clone_ headers_set(skb, 8) in + * rndis_rx_fixup(); + * By setting this we are telling fcache or enet driver can expand + * skb->data for upto 8 bytes. This is helpful to avoid packet + * copy incase of LAN VLAN's, External Switch tag's etc.. + * + */ + skb_clone_headers_set(skb, 0); + } + if (PKT_DONE == blog_sinit(skb, skb->dev, TYPE_ETH, 0, BLOG_USBPHY)) { + return; + } +#endif + skb->protocol = eth_type_trans (skb, dev->net); dev->net->stats.rx_packets++; dev->net->stats.rx_bytes += skb->len; @@ -334,7 +375,19 @@ if (skb_defer_rx_timestamp(skb)) return; +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_ACCELERATION) && defined(CONFIG_BLOG)) + +#if defined(CONFIG_BCM_USBNET_THREAD) + local_bh_disable(); + status = netif_receive_skb(skb); + local_bh_enable(); +#else + status = netif_receive_skb(skb); +#endif + +#else status = netif_rx (skb); +#endif if (status != NET_RX_SUCCESS) netif_dbg(dev, rx_err, dev->net, "netif_rx status %d\n", status); @@ -363,6 +416,13 @@ default: dev->rx_qlen = dev->tx_qlen = 4; } + +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_THREAD)) + if(dev->rx_qlen < USBNET_RX_BUDGET) + dev->rx_qlen = USBNET_RX_BUDGET; + + dev->pending_rx_skb_thresh = 2 * dev->rx_qlen; +#endif } EXPORT_SYMBOL_GPL(usbnet_update_max_qlen); @@ -411,6 +471,107 @@ entry->state = state; } +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_THREAD)) + + +static inline int bcm_skb_queue_len(struct sk_buff_head *list) +{ + unsigned long flags; + __u32 qlen; + + spin_lock_irqsave(&list->lock, flags); + qlen = list->qlen; + spin_unlock_irqrestore(&list->lock, flags); + return qlen; +} + +static inline struct sk_buff *bcm_skb_done_dequeue(struct usbnet *dev) +{ + unsigned long flags; + struct sk_buff *result; + + spin_lock_irqsave(&dev->done.lock, flags); + result = __skb_dequeue(&dev->done); + + if(result && (((struct skb_data *) result->cb)->state != tx_done)) + dev->pending_rx_skb_count--; + + spin_unlock_irqrestore(&dev->done.lock, flags); + return result; +} + +static void usbnet_bh (unsigned long param); + + +static int usbnet_thread_func(void *thread_data) +{ + struct usbnet *dev =(struct usbnet *)thread_data; + + while (1) { + + wait_event_interruptible(dev->thread_wq, + (bcm_skb_queue_len(&dev->done) | dev->usbnet_thread_resched + | kthread_should_stop())); + + if(unlikely(kthread_should_stop())) + { + return 0; + } + + /*clear all conditions */ + dev->usbnet_thread_resched=0; + + usbnet_bh((unsigned long)dev); + + if (dev->usbnet_thread_resched || (bcm_skb_queue_len(&dev->done) >= 1)) { + //cond_resched(); + yield(); + } + } + return 0; +} + +static inline void usbnet_thread_wakeup(struct usbnet *dev) +{ + wake_up_interruptible(&dev->thread_wq); +} + +static inline void usbnet_thread_schedule(struct usbnet *dev) +{ + dev->usbnet_thread_resched = 1; + usbnet_thread_wakeup(dev); +} + +static void usbnet_bh_timer(unsigned long param) +{ + usbnet_thread_schedule((struct usbnet *)param); +} + +struct task_struct *create_usbnet_thread(void *thread_data) +{ + struct task_struct *tsk; + struct sched_param param; + + /*TODO fix thread name when multiple devices are present + * this is just a cosmetic issue + */ + + tsk = kthread_create(usbnet_thread_func, thread_data, "usbnet_thread"); + + if (IS_ERR(tsk)) { + printk("usbnet_thread_free_task creation failed\n"); + return NULL; + } + + param.sched_priority = BCM_RTPRIO_DATA; + sched_setscheduler(tsk, SCHED_RR, ¶m); + wake_up_process(tsk); + + printk("usbnet_thread created successfully \n"); + return tsk; +} +#endif + /*-------------------------------------------------------------------------*/ /* some LK 2.4 HCDs oopsed if we freed or resubmitted urbs from @@ -431,8 +592,21 @@ spin_unlock(&list->lock); spin_lock(&dev->done.lock); __skb_queue_tail(&dev->done, skb); +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_THREAD)) + /* restrict number of pending urbs/skb's to avoid irq flooding */ + if((state != tx_done)) + { + ++dev->pending_rx_skb_count; + old_state = unlink_start; + } + + if (dev->done.qlen == 1) + usbnet_thread_wakeup(dev); + +#else if (dev->done.qlen == 1) tasklet_schedule(&dev->bh); +#endif spin_unlock_irqrestore(&dev->done.lock, flags); return old_state; } @@ -447,13 +621,21 @@ set_bit (work, &dev->flags); if (!schedule_work (&dev->kevent)) { if (net_ratelimit()) +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + netdev_dbg(dev->net, "kevent %d may have been dropped\n", work); +#else netdev_err(dev->net, "kevent %d may have been dropped\n", work); +#endif } else { netdev_dbg(dev->net, "kevent %d scheduled\n", work); } } EXPORT_SYMBOL_GPL(usbnet_defer_kevent); +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM96838)) +int bcm_usb_hw_align_size = 1024; +#endif + /*-------------------------------------------------------------------------*/ static void rx_complete (struct urb *urb); @@ -472,7 +654,12 @@ return -ENOLINK; } +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM96838)) + skb = __netdev_alloc_skb_ip_align(dev->net, size + bcm_usb_hw_align_size, flags); +#else skb = __netdev_alloc_skb_ip_align(dev->net, size, flags); +#endif + if (!skb) { netif_dbg(dev, rx_err, dev->net, "no rx skb\n"); usbnet_defer_kevent (dev, EVENT_RX_MEMORY); @@ -480,6 +667,14 @@ return -ENOMEM; } +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM96838)) + { + unsigned int aligned_len = (unsigned int)skb->data; + aligned_len = bcm_usb_hw_align_size - (aligned_len & (bcm_usb_hw_align_size - 1)); + skb_reserve(skb, aligned_len); + } +#endif + entry = (struct skb_data *) skb->cb; entry->urb = urb; entry->dev = dev; @@ -511,7 +706,11 @@ default: netif_dbg(dev, rx_err, dev->net, "rx submit, %d\n", retval); - tasklet_schedule (&dev->bh); +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_THREAD)) + usbnet_thread_schedule(dev); +#else + tasklet_schedule(&dev->bh); +#endif break; case 0: __usbnet_queue_skb(&dev->rxq, skb, rx_start); @@ -528,7 +727,6 @@ return retval; } - /*-------------------------------------------------------------------------*/ static inline void rx_process (struct usbnet *dev, struct sk_buff *skb) @@ -556,7 +754,26 @@ } done: +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_THREAD)) +#if 1 + /* there is no need to queue the skb back to done queue just for freeing, + * we can just call free here as we are in thread context + */ + + usb_free_urb (((struct skb_data *)skb->cb)->urb); + dev_kfree_skb (skb); +#else + { + unsigned long flags; + spin_lock_irqsave(&dev->done.lock, flags); + __skb_queue_tail(&dev->done, skb); + dev->pending_rx_skb_count++ ; + spin_unlock_irqrestore(&dev->done.lock, flags); + } +#endif +#else skb_queue_tail(&dev->done, skb); +#endif } /*-------------------------------------------------------------------------*/ @@ -673,7 +890,11 @@ num++; } +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_THREAD)) + usbnet_thread_schedule(dev); +#else tasklet_schedule(&dev->bh); +#endif netif_dbg(dev, rx_status, dev->net, "paused rx queue disabled, %d skbs requeued\n", num); @@ -742,7 +963,11 @@ { if (netif_running(dev->net)) { (void) unlink_urbs (dev, &dev->rxq); +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_THREAD)) + usbnet_thread_schedule(dev); +#else tasklet_schedule(&dev->bh); +#endif } } EXPORT_SYMBOL_GPL(usbnet_unlink_rx_urbs); @@ -817,7 +1042,11 @@ */ dev->flags = 0; del_timer_sync (&dev->delay); +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_THREAD)) + /* no need to do anything here for now*/ +#else tasklet_kill (&dev->bh); +#endif if (!pm) usb_autopm_put_interface(dev->intf); @@ -901,7 +1130,11 @@ clear_bit(EVENT_RX_KILL, &dev->flags); // delay posting reads until we're fully open - tasklet_schedule (&dev->bh); +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_THREAD)) + usbnet_thread_schedule(dev); +#else + tasklet_schedule(&dev->bh); +#endif if (info->manage_power) { retval = info->manage_power(dev, 1); if (retval < 0) { @@ -1043,7 +1276,11 @@ */ } else { /* submitting URBs for reading packets */ +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_THREAD)) + usbnet_thread_schedule(dev); +#else tasklet_schedule(&dev->bh); +#endif } /* hard_mtu or rx_urb_size may change during link change */ @@ -1116,7 +1353,11 @@ status); } else { clear_bit (EVENT_RX_HALT, &dev->flags); - tasklet_schedule (&dev->bh); +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_THREAD)) + usbnet_thread_schedule(dev); +#else + tasklet_schedule(&dev->bh); +#endif } } @@ -1141,7 +1382,11 @@ usb_autopm_put_interface(dev->intf); fail_lowmem: if (resched) - tasklet_schedule (&dev->bh); +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_THREAD)) + usbnet_thread_schedule(dev); +#else + tasklet_schedule(&dev->bh); +#endif } } @@ -1237,7 +1482,11 @@ struct usbnet *dev = netdev_priv(net); unlink_urbs (dev, &dev->txq); - tasklet_schedule (&dev->bh); +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_THREAD)) + usbnet_thread_schedule(dev); +#else + tasklet_schedule(&dev->bh); +#endif /* this needs to be handled individually because the generic layer * doesn't know what is sufficient and could not restore private * information if a remedy of an unconditional reset were used. @@ -1293,6 +1542,26 @@ unsigned long flags; int retval; +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_ACCELERATION) && defined(CONFIG_BLOG)) + if(skb) + { + struct sk_buff *orig_skb = skb; + + if(unlikely(netif_queue_stopped(net))) + skb = NULL; + else + skb = nbuff_xlate((pNBuff_t )skb); + + if (skb == NULL) + { + dev->net->stats.tx_dropped++; + nbuff_free((pNBuff_t) orig_skb); + return NETDEV_TX_OK; + } + blog_emit( skb, net, TYPE_ETH, 0, BLOG_USBPHY ); + } +#endif + if (skb) skb_tx_timestamp(skb); @@ -1309,6 +1578,33 @@ } } +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM963268)) + /* on 63268 if TX buffer is not 4 byte aligned then some times + * USB descriptors are corrupted beacuse of a BUG in hW + * for unaligned buffers copy data to a new aligned buffer + */ + if (((unsigned int)skb->data & 0x3) ) { + struct sk_buff *oskb = skb; + int newheadroom = skb_headroom(oskb); + /* create a new skb from an existing skb + * and adjust the data pointer to be word aligned + */ + newheadroom += newheadroom%4; + skb = skb_copy_expand(oskb, newheadroom, 1, GFP_ATOMIC); + dev_kfree_skb_any(oskb); + + if ( unlikely(!skb) ) + goto drop; + + if (unlikely((unsigned int)skb->data & 0x3)) + { + printk(KERN_WARNING"%s: unaligned skb->data=%p \n", __func__, + skb->data); + goto drop; + } + } +#endif + if (!(urb = usb_alloc_urb (0, GFP_ATOMIC))) { netif_dbg(dev, tx_err, dev->net, "no urb\n"); goto drop; @@ -1453,9 +1749,21 @@ struct usbnet *dev = (struct usbnet *) param; struct sk_buff *skb; struct skb_data *entry; + +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_THREAD)) + int rx_budget = USBNET_RX_BUDGET; + while ((skb = bcm_skb_done_dequeue (dev))) { + entry = (struct skb_data *) skb->cb; + + /*TODO: try to add seperate tx_done queue*/ + if(entry->state != tx_done) + rx_budget--; +#else while ((skb = skb_dequeue (&dev->done))) { entry = (struct skb_data *) skb->cb; + +#endif switch (entry->state) { case rx_done: entry->state = rx_cleanup; @@ -1470,6 +1778,11 @@ default: netdev_dbg(dev->net, "bogus skb state %d\n", entry->state); } + +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_THREAD)) + if(rx_budget == 0) + break; +#endif } /* restart RX again after disabling due to high error rate */ @@ -1490,15 +1803,25 @@ !test_bit (EVENT_RX_HALT, &dev->flags)) { int temp = dev->rxq.qlen; +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_THREAD)) + if ( (dev->pending_rx_skb_count < dev->pending_rx_skb_thresh) && + (temp < RX_QLEN(dev)) ) { + if (rx_alloc_submit(dev, GFP_KERNEL) == -ENOLINK) +#else if (temp < RX_QLEN(dev)) { if (rx_alloc_submit(dev, GFP_ATOMIC) == -ENOLINK) +#endif return; if (temp != dev->rxq.qlen) netif_dbg(dev, link, dev->net, "rxqlen %d --> %d\n", temp, dev->rxq.qlen); if (dev->rxq.qlen < RX_QLEN(dev)) - tasklet_schedule (&dev->bh); +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_THREAD)) + usbnet_thread_schedule(dev); +#else + tasklet_schedule(&dev->bh); +#endif } if (dev->txq.qlen < TX_QLEN (dev)) netif_wake_queue (dev->net); @@ -1546,6 +1869,9 @@ usb_free_urb(dev->interrupt); kfree(dev->padding_pkt); +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_THREAD)) + kthread_stop(dev->usbnet_thread ); +#endif free_netdev(net); } EXPORT_SYMBOL_GPL(usbnet_disconnect); @@ -1625,11 +1951,26 @@ skb_queue_head_init (&dev->txq); skb_queue_head_init (&dev->done); skb_queue_head_init(&dev->rxq_pause); +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_THREAD)) + init_waitqueue_head(&dev->thread_wq); + dev->usbnet_thread = create_usbnet_thread(dev); + if(dev->usbnet_thread == NULL) + goto out1; + dev->usbnet_thread_resched=0; + dev->pending_rx_skb_thresh=USBNET_PENDING_RX_SKB_THRESH_DEFAULT; + dev->pending_rx_skb_count=0; +#else dev->bh.func = usbnet_bh; dev->bh.data = (unsigned long) dev; +#endif + INIT_WORK (&dev->kevent, usbnet_deferred_kevent); init_usb_anchor(&dev->deferred); +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_THREAD)) + dev->delay.function = usbnet_bh_timer; +#else dev->delay.function = usbnet_bh; +#endif dev->delay.data = (unsigned long) dev; init_timer (&dev->delay); mutex_init (&dev->phy_mutex); @@ -1655,6 +1996,9 @@ net->watchdog_timeo = TX_TIMEOUT_JIFFIES; net->ethtool_ops = &usbnet_ethtool_ops; +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM96838)) + printk("+++++ &bcm_usb_hw_align_size =%p \n", &bcm_usb_hw_align_size); +#endif // allow device-specific bind/init procedures // NOTE net->name still not usable ... if (info->bind) { @@ -1761,6 +2105,11 @@ */ cancel_work_sync(&dev->kevent); del_timer_sync(&dev->delay); + +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_THREAD)) + if(dev->usbnet_thread) + kthread_stop(dev->usbnet_thread ); +#endif free_netdev(net); out: return status; @@ -1849,7 +2198,11 @@ if (!(dev->txq.qlen >= TX_QLEN(dev))) netif_tx_wake_all_queues(dev->net); - tasklet_schedule (&dev->bh); +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_THREAD)) + usbnet_thread_schedule(dev); +#else + tasklet_schedule(&dev->bh); +#endif } } @@ -2044,8 +2397,13 @@ urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) { +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + netdev_dbg(dev->net, "Error allocating URB in" + " %s!\n", __func__); +#else netdev_err(dev->net, "Error allocating URB in" " %s!\n", __func__); +#endif goto fail; } diff -urN linux-4.1.52-orig/drivers/net/veth.c linux-4.1.52/drivers/net/veth.c --- linux-4.1.52-orig/drivers/net/veth.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/net/veth.c 2020-02-07 01:47:54.000000000 +0800 @@ -111,6 +111,17 @@ struct net_device *rcv; int length = skb->len; +#if defined(CONFIG_BCM_KF_VETH_IPV6_DISABLE) + const struct ipv6hdr *hdr; + hdr = ipv6_hdr(skb); + + if (hdr->version == 6) + { + kfree_skb(skb); + return NETDEV_TX_OK; + } +#endif + rcu_read_lock(); rcv = rcu_dereference(priv->peer); if (unlikely(!rcv)) { diff -urN linux-4.1.52-orig/drivers/pci/msi.c linux-4.1.52/drivers/pci/msi.c --- linux-4.1.52-orig/drivers/pci/msi.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/pci/msi.c 2020-02-07 01:47:54.000000000 +0800 @@ -121,9 +121,16 @@ int __weak arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) { +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + struct msi_controller *chip = pci_msi_controller(dev); +#endif struct msi_desc *entry; int ret; +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + if (chip && chip->setup_irqs) + return chip->setup_irqs(chip, dev, nvec, type); +#endif /* * If an architecture wants to support multiple MSI, it needs to * override arch_setup_msi_irqs() diff -urN linux-4.1.52-orig/drivers/pci/pci.c linux-4.1.52/drivers/pci/pci.c --- linux-4.1.52-orig/drivers/pci/pci.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/pci/pci.c 2020-02-07 01:47:54.000000000 +0800 @@ -4544,6 +4544,15 @@ return atomic_inc_return(&__domain_nr); } +#ifdef CONFIG_BCM_KF_PCI_RESET_DOMAIN_NR +void pci_reset_domain_nr(void) +{ + atomic_set(&__domain_nr, -1); + return; +} +EXPORT_SYMBOL(pci_reset_domain_nr); +#endif /* CONFIG_BCM_KF_PCI_RESET_DOMAIN_NR */ + #ifdef CONFIG_PCI_DOMAINS_GENERIC void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent) { diff -urN linux-4.1.52-orig/drivers/pci/pcie/aspm.c linux-4.1.52/drivers/pci/pcie/aspm.c --- linux-4.1.52-orig/drivers/pci/pcie/aspm.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/pci/pcie/aspm.c 2020-02-07 01:47:54.000000000 +0800 @@ -76,6 +76,10 @@ #define POLICY_DEFAULT 0 /* BIOS default setting */ #define POLICY_PERFORMANCE 1 /* high performance */ #define POLICY_POWERSAVE 2 /* high power saving */ +#if defined(CONFIG_BCM_KF_POWER_SAVE) +#define POLICY_L0SPOWERSAVE 3 /* Only do L0S */ +#define POLICY_L1POWERSAVE 4 /* Typically same savings as L1+L0s */ +#endif #ifdef CONFIG_PCIEASPM_PERFORMANCE static int aspm_policy = POLICY_PERFORMANCE; @@ -89,6 +93,11 @@ [POLICY_DEFAULT] = "default", [POLICY_PERFORMANCE] = "performance", [POLICY_POWERSAVE] = "powersave" +#if defined(CONFIG_BCM_KF_POWER_SAVE) + , + [POLICY_L0SPOWERSAVE] = "l0s_powersave", + [POLICY_L1POWERSAVE] = "l1_powersave", +#endif }; #define LINK_RETRAIN_TIMEOUT HZ @@ -102,6 +111,14 @@ case POLICY_POWERSAVE: /* Enable ASPM L0s/L1 */ return ASPM_STATE_ALL; +#if defined(CONFIG_BCM_KF_POWER_SAVE) + case POLICY_L0SPOWERSAVE: + /* Enable ASPM L0s */ + return ASPM_STATE_L0S; + case POLICY_L1POWERSAVE: + /* Enable ASPM L1 */ + return ASPM_STATE_L1; +#endif case POLICY_DEFAULT: return link->aspm_default; } @@ -112,9 +129,15 @@ { switch (aspm_policy) { case POLICY_PERFORMANCE: +#if defined(CONFIG_BCM_KF_POWER_SAVE) + case POLICY_L0SPOWERSAVE: +#endif /* Disable ASPM and Clock PM */ return 0; case POLICY_POWERSAVE: +#if defined(CONFIG_BCM_KF_POWER_SAVE) + case POLICY_L1POWERSAVE: +#endif /* Disable Clock PM */ return 1; case POLICY_DEFAULT: @@ -172,7 +195,12 @@ } link->clkpm_enabled = enabled; link->clkpm_default = enabled; +#if defined(CONFIG_BCM_KF_POWER_SAVE) + // Do not enable clkpm for the moment + link->clkpm_capable = 0; +#else link->clkpm_capable = (blacklist) ? 0 : capable; +#endif } /* @@ -510,6 +538,25 @@ dev_info(&child->dev, "disabling ASPM on pre-1.1 PCIe device. You can enable it with 'pcie_aspm=force'\n"); return -EINVAL; } +#if defined(CONFIG_BCM_KF_POWER_SAVE) + // Only enable ASPM where thoroughly tested + dev_printk(KERN_DEBUG, &pdev->dev, "Checking PCIe ASPM for vendor %x device %x\n", child->vendor, child->device); + if ((child->vendor == 0x14e4) && + (child->device != 0x4365) && // 4365 + (child->device != 0x4366) && // 4366 + (child->device != 0xaa90) && // 4366E + (child->device != 0xaa92) && // 4365E + (child->device != 0x4360) && // 4360 + (child->device != 0x43a0) && // 4360 + (child->device != 0x43a1) && // 4360 + (child->device != 0x43a2) && // 4360 + (child->device != 0x43a9) && // 43217 + (child->device != 0xa8db)) // 43217-43227 + { + dev_printk(KERN_DEBUG, &pdev->dev, "Disabling PCIe ASPM for vendor %x device %x\n", child->vendor, child->device); + return -EINVAL; + } +#endif } return 0; } diff -urN linux-4.1.52-orig/drivers/scsi/hosts.c linux-4.1.52/drivers/scsi/hosts.c --- linux-4.1.52-orig/drivers/scsi/hosts.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/scsi/hosts.c 2020-02-07 01:47:54.000000000 +0800 @@ -34,6 +34,10 @@ #include #include +#if defined(CONFIG_BCM_KF_USB_STORAGE) +#include +#endif + #include #include #include @@ -479,6 +483,14 @@ PTR_ERR(shost->ehandler)); goto fail_kfree; } +#if defined(CONFIG_BCM_KF_USB_STORAGE) + /*convert the thread to realtime RR thread */ + { + struct sched_param param; + param.sched_priority = BCM_RTPRIO_DATA; + sched_setscheduler(shost->ehandler, SCHED_RR, ¶m); + } +#endif shost->tmf_work_q = alloc_workqueue("scsi_tmf_%d", WQ_UNBOUND | WQ_MEM_RECLAIM, diff -urN linux-4.1.52-orig/drivers/spi/Kconfig linux-4.1.52/drivers/spi/Kconfig --- linux-4.1.52-orig/drivers/spi/Kconfig 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/spi/Kconfig 2020-02-07 01:47:54.000000000 +0800 @@ -130,7 +130,7 @@ config SPI_BCM63XX_HSSPI tristate "Broadcom BCM63XX HS SPI controller driver" - depends on BCM63XX || COMPILE_TEST + depends on BCM63XX || COMPILE_TEST || BCM_KF_MISC_BACKPORTS help This enables support for the High Speed SPI controller present on newer Broadcom BCM63XX SoCs. diff -urN linux-4.1.52-orig/drivers/spi/spi-bcm63xx-hsspi.c linux-4.1.52/drivers/spi/spi-bcm63xx-hsspi.c --- linux-4.1.52-orig/drivers/spi/spi-bcm63xx-hsspi.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/spi/spi-bcm63xx-hsspi.c 2020-02-07 01:47:54.000000000 +0800 @@ -19,6 +19,11 @@ #include #include #include +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +#include /* for of functions */ +#include +#include +#endif #define HSSPI_GLOBAL_CTRL_REG 0x0 #define GLOBAL_CTRL_CS_POLARITY_SHIFT 0 @@ -54,6 +59,9 @@ #define PINGPONG_CMD_SS_SHIFT 12 #define HSSPI_PINGPONG_STATUS_REG(x) (0x84 + (x) * 0x40) +#ifdef CONFIG_BCM_KF_SPI +#define HSSPI_PINGPONG_STATUS_SRC_BUSY BIT(1) +#endif #define HSSPI_PROFILE_CLK_CTRL_REG(x) (0x100 + (x) * 0x20) #define CLK_CTRL_FREQ_CTRL_MASK 0x0000ffff @@ -76,6 +84,9 @@ #define HSSPI_FIFO_REG(x) (0x200 + (x) * 0x200) +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +#define HSSPI_OP_MULTIBIT BIT(11) +#endif #define HSSPI_OP_CODE_SHIFT 13 #define HSSPI_OP_SLEEP (0 << HSSPI_OP_CODE_SHIFT) #define HSSPI_OP_READ_WRITE (1 << HSSPI_OP_CODE_SHIFT) @@ -90,6 +101,9 @@ #define HSSPI_MAX_SYNC_CLOCK 30000000 +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +#define HSSPI_SPI_MAX_CS 8 +#endif #define HSSPI_BUS_NUM 1 /* 0 is legacy SPI */ struct bcm63xx_hsspi { @@ -105,7 +119,11 @@ u8 cs_polarity; }; +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +static void bcm63xx_hsspi_set_cs(struct bcm63xx_hsspi *bs, unsigned int cs, +#else static void bcm63xx_hsspi_set_cs(struct bcm63xx_hsspi *bs, unsigned cs, +#endif bool active) { u32 reg; @@ -124,7 +142,11 @@ static void bcm63xx_hsspi_set_clk(struct bcm63xx_hsspi *bs, struct spi_device *spi, int hz) { +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + unsigned int profile = spi->chip_select; +#else unsigned profile = spi->chip_select; +#endif u32 reg; reg = DIV_ROUND_UP(2048, DIV_ROUND_UP(bs->speed_hz, hz)); @@ -151,7 +173,11 @@ static int bcm63xx_hsspi_do_txrx(struct spi_device *spi, struct spi_transfer *t) { struct bcm63xx_hsspi *bs = spi_master_get_devdata(spi->master); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + unsigned int chip_select = spi->chip_select; +#else unsigned chip_select = spi->chip_select; +#endif u16 opcode = 0; int pending = t->len; int step_size = HSSPI_BUFFER_LEN; @@ -171,9 +197,18 @@ if (opcode != HSSPI_OP_READ) step_size -= HSSPI_OPCODE_LEN; +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + if ((opcode == HSSPI_OP_READ && t->rx_nbits == SPI_NBITS_DUAL) || + (opcode == HSSPI_OP_WRITE && t->tx_nbits == SPI_NBITS_DUAL)) + opcode |= HSSPI_OP_MULTIBIT; + + __raw_writel(1 << MODE_CTRL_MULTIDATA_WR_SIZE_SHIFT | + 1 << MODE_CTRL_MULTIDATA_RD_SIZE_SHIFT | 0xff, +#else __raw_writel(0 << MODE_CTRL_PREPENDBYTE_CNT_SHIFT | 2 << MODE_CTRL_MULTIDATA_WR_STRT_SHIFT | 2 << MODE_CTRL_MULTIDATA_RD_STRT_SHIFT | 0xff, +#endif bs->regs + HSSPI_PROFILE_MODE_CTRL_REG(chip_select)); while (pending > 0) { @@ -185,7 +220,11 @@ tx += curr_step; } +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + __raw_writew(cpu_to_be16(opcode | curr_step), bs->fifo); +#else __raw_writew(opcode | curr_step, bs->fifo); +#endif /* enable interrupt */ __raw_writel(HSSPI_PINGx_CMD_DONE(0), @@ -197,11 +236,19 @@ PINGPONG_COMMAND_START_NOW, bs->regs + HSSPI_PINGPONG_COMMAND_REG(0)); - if (wait_for_completion_timeout(&bs->done, HZ) == 0) { - dev_err(&bs->pdev->dev, "transfer timed out!\n"); - return -ETIMEDOUT; +#ifdef CONFIG_BCM_KF_SPI + if (!oops_in_progress) { +#endif + if (wait_for_completion_timeout(&bs->done, HZ) == 0) { + dev_err(&bs->pdev->dev, "transfer timed out!\n"); + return -ETIMEDOUT; + } +#ifdef CONFIG_BCM_KF_SPI + } else { + while( __raw_readl(bs->regs + HSSPI_PINGPONG_STATUS_REG(0)) & + HSSPI_PINGPONG_STATUS_SRC_BUSY ); } - +#endif if (rx) { memcpy_fromio(rx, bs->fifo, curr_step); rx += curr_step; @@ -259,6 +306,9 @@ int status = -EINVAL; int dummy_cs; u32 reg; +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + bool restore_polarity = true; +#endif /* This controller does not support keeping CS active during idle. * To work around this, we use the following ugly hack: @@ -287,16 +337,49 @@ if (t->delay_usecs) udelay(t->delay_usecs); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + /* + * cs_change rules: + * (1) cs_change = 0 && last_xfer = 0: + * Do not touch the CS. On to the next xfer. + * (2) cs_change = 1 && last_xfer = 0: + * Set cs = false before the next xfer. + * (3) cs_change = 0 && last_xfer = 1: + * We want CS to be deactivated. So do NOT set cs = false, + * instead just restore the original polarity. This has the + * same effect of deactivating the CS. + * (4) cs_change = 1 && last_xfer = 1: + * We want to keep CS active. So do NOT set cs = false, and + * make sure we do NOT reverse polarity. + */ + if (t->cs_change && !list_is_last(&t->transfer_list, &msg->transfers)) +#else if (t->cs_change) +#endif bcm63xx_hsspi_set_cs(bs, spi->chip_select, false); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + + restore_polarity = !t->cs_change; +#endif } +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + if (restore_polarity) { + mutex_lock(&bs->bus_mutex); + reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG); + reg &= ~GLOBAL_CTRL_CS_POLARITY_MASK; + reg |= bs->cs_polarity; + __raw_writel(reg, bs->regs + HSSPI_GLOBAL_CTRL_REG); + mutex_unlock(&bs->bus_mutex); + } +#else mutex_lock(&bs->bus_mutex); reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG); reg &= ~GLOBAL_CTRL_CS_POLARITY_MASK; reg |= bs->cs_polarity; __raw_writel(reg, bs->regs + HSSPI_GLOBAL_CTRL_REG); mutex_unlock(&bs->bus_mutex); +#endif msg->status = status; spi_finalize_current_message(master); @@ -323,21 +406,46 @@ { struct spi_master *master; struct bcm63xx_hsspi *bs; +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + struct resource res_mem; +#else struct resource *res_mem; +#endif void __iomem *regs; struct device *dev = &pdev->dev; struct clk *clk; int irq, ret; +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + u32 reg, rate, num_cs = HSSPI_SPI_MAX_CS; +#else u32 reg, rate; +#endif +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + irq = irq_of_parse_and_map(pdev->dev.of_node, 0); +#else irq = platform_get_irq(pdev, 0); +#endif if (irq < 0) { +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + dev_err(dev, "no irq: %d\n", irq); + return irq; +#else dev_err(dev, "no irq\n"); return -ENXIO; +#endif } +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + ret = of_address_to_resource(pdev->dev.of_node, 0, &res_mem); + if (ret) + return -EINVAL; + + regs = devm_ioremap_resource(dev, &res_mem); +#else res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = devm_ioremap_resource(dev, res_mem); +#endif if (IS_ERR(regs)) return PTR_ERR(regs); @@ -347,8 +455,21 @@ return PTR_ERR(clk); rate = clk_get_rate(clk); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + if (!rate) { + struct clk *pll_clk = devm_clk_get(dev, "pll"); + + if (IS_ERR(pll_clk)) + return PTR_ERR(pll_clk); + + rate = clk_get_rate(pll_clk); + if (!rate) + return -EINVAL; + } +#else if (!rate) return -EINVAL; +#endif ret = clk_prepare_enable(clk); if (ret) @@ -370,11 +491,30 @@ mutex_init(&bs->bus_mutex); init_completion(&bs->done); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + master->dev.of_node = dev->of_node; + if (!dev->of_node) + master->bus_num = HSSPI_BUS_NUM; + + of_property_read_u32(dev->of_node, "num-cs", &num_cs); + if (num_cs > 8) { + dev_warn(dev, "unsupported number of cs (%i), reducing to 8\n", + num_cs); + num_cs = HSSPI_SPI_MAX_CS; + } + master->num_chipselect = num_cs; +#else master->bus_num = HSSPI_BUS_NUM; master->num_chipselect = 8; +#endif master->setup = bcm63xx_hsspi_setup; master->transfer_one_message = bcm63xx_hsspi_transfer_one; +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | + SPI_RX_DUAL | SPI_TX_DUAL; +#else master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; +#endif master->bits_per_word_mask = SPI_BPW_MASK(8); master->auto_runtime_pm = true; @@ -456,10 +596,21 @@ static SIMPLE_DEV_PM_OPS(bcm63xx_hsspi_pm_ops, bcm63xx_hsspi_suspend, bcm63xx_hsspi_resume); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +static const struct of_device_id bcm63xx_hsspi_of_match[] = { + { .compatible = "brcm,bcm6328-hsspi", }, + { }, +}; +MODULE_DEVICE_TABLE(of, bcm63xx_hsspi_of_match); +#endif + static struct platform_driver bcm63xx_hsspi_driver = { .driver = { .name = "bcm63xx-hsspi", .pm = &bcm63xx_hsspi_pm_ops, +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + .of_match_table = bcm63xx_hsspi_of_match, +#endif }, .probe = bcm63xx_hsspi_probe, .remove = bcm63xx_hsspi_remove, diff -urN linux-4.1.52-orig/drivers/spi/spi.c linux-4.1.52/drivers/spi/spi.c --- linux-4.1.52-orig/drivers/spi/spi.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/spi/spi.c 2020-02-07 01:47:54.000000000 +0800 @@ -1963,6 +1963,24 @@ * which are wrappers around this core asynchronous primitive.) */ int spi_async(struct spi_device *spi, struct spi_message *message) +#if defined(CONFIG_BCM_KF_SPI) && !defined(CONFIG_SPI_BCM63XX_HSSPI) +{ + struct spi_master *master = spi->master; + unsigned long flags; + + /* holding the spinlock and disabling irqs for the duration of the transfer is problematic +   the controller driver manages the locking so call __spi_async without the lock */ + + spin_lock_irqsave(&master->bus_lock_spinlock, flags); + if (master->bus_lock_flag){ + spin_unlock_irqrestore(&master->bus_lock_spinlock, flags); + return -EBUSY; + } + spin_unlock_irqrestore(&master->bus_lock_spinlock, flags); + + return __spi_async(spi, message); +} +#else { struct spi_master *master = spi->master; int ret; @@ -1983,6 +2001,7 @@ return ret; } +#endif EXPORT_SYMBOL_GPL(spi_async); /** diff -urN linux-4.1.52-orig/drivers/tee/Kconfig linux-4.1.52/drivers/tee/Kconfig --- linux-4.1.52-orig/drivers/tee/Kconfig 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/tee/Kconfig 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,27 @@ +if BCM_KF_OPTEE_414_BACKPORTS +# Generic Trusted Execution Environment Configuration +config HAVE_ARM_SMCCC + bool "SMC Call Support" + def_bool n + depends on BCM_KF_OPTEE + +config TEE + tristate "Trusted Execution Environment support" + depends on COMPILE_TEST || BCM_KF_OPTEE + select DMA_SHARED_BUFFER + select GENERIC_ALLOCATOR + select HAVE_ARM_SMCCC + help + This implements a generic interface towards a Trusted Execution + Environment (TEE). + +if TEE + +menu "TEE drivers" + +source "drivers/tee/optee/Kconfig" + +endmenu + +endif +endif # BCM_KF_OPTEE_414_BACKPORTS diff -urN linux-4.1.52-orig/drivers/tee/Makefile linux-4.1.52/drivers/tee/Makefile --- linux-4.1.52-orig/drivers/tee/Makefile 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/tee/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,8 @@ +ifdef BCM_KF # defined(CONFIG_BCM_KF_OPTEE_414_BACKPORTS) +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_TEE) += tee.o +tee-objs += tee_core.o +tee-objs += tee_shm.o +tee-objs += tee_shm_pool.o +obj-$(CONFIG_OPTEE) += optee/ +endif diff -urN linux-4.1.52-orig/drivers/tee/optee/call.c linux-4.1.52/drivers/tee/optee/call.c --- linux-4.1.52-orig/drivers/tee/optee/call.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/tee/optee/call.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,486 @@ +#if defined(CONFIG_BCM_KF_OPTEE_414_BACKPORTS) +/* + * Copyright (c) 2015, Linaro Limited + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "optee_private.h" +#include "optee_smc.h" +#if defined(CONFIG_BCM_KF_OPTEE) +#include +#endif +struct optee_call_waiter { + struct list_head list_node; + struct completion c; +}; + +static void optee_cq_wait_init(struct optee_call_queue *cq, + struct optee_call_waiter *w) +{ + /* + * We're preparing to make a call to secure world. In case we can't + * allocate a thread in secure world we'll end up waiting in + * optee_cq_wait_for_completion(). + * + * Normally if there's no contention in secure world the call will + * complete and we can cleanup directly with optee_cq_wait_final(). + */ + mutex_lock(&cq->mutex); + + /* + * We add ourselves to the queue, but we don't wait. This + * guarantees that we don't lose a completion if secure world + * returns busy and another thread just exited and try to complete + * someone. + */ + init_completion(&w->c); + list_add_tail(&w->list_node, &cq->waiters); + + mutex_unlock(&cq->mutex); +} + +static void optee_cq_wait_for_completion(struct optee_call_queue *cq, + struct optee_call_waiter *w) +{ + wait_for_completion(&w->c); + + mutex_lock(&cq->mutex); + + /* Move to end of list to get out of the way for other waiters */ + list_del(&w->list_node); + reinit_completion(&w->c); + list_add_tail(&w->list_node, &cq->waiters); + + mutex_unlock(&cq->mutex); +} + +static void optee_cq_complete_one(struct optee_call_queue *cq) +{ + struct optee_call_waiter *w; + + list_for_each_entry(w, &cq->waiters, list_node) { + if (!completion_done(&w->c)) { + complete(&w->c); + break; + } + } +} + +static void optee_cq_wait_final(struct optee_call_queue *cq, + struct optee_call_waiter *w) +{ + /* + * We're done with the call to secure world. The thread in secure + * world that was used for this call is now available for some + * other task to use. + */ + mutex_lock(&cq->mutex); + + /* Get out of the list */ + list_del(&w->list_node); + + /* Wake up one eventual waiting task */ + optee_cq_complete_one(cq); + + /* + * If we're completed we've got a completion from another task that + * was just done with its call to secure world. Since yet another + * thread now is available in secure world wake up another eventual + * waiting task. + */ + if (completion_done(&w->c)) + optee_cq_complete_one(cq); + + mutex_unlock(&cq->mutex); +} + +/* Requires the filpstate mutex to be held */ +static struct optee_session *find_session(struct optee_context_data *ctxdata, + u32 session_id) +{ + struct optee_session *sess; + + list_for_each_entry(sess, &ctxdata->sess_list, list_node) + if (sess->session_id == session_id) + return sess; + + return NULL; +} + +/** + * optee_do_call_with_arg() - Do an SMC to OP-TEE in secure world + * @ctx: calling context + * @parg: physical address of message to pass to secure world + * + * Does and SMC to OP-TEE in secure world and handles eventual resulting + * Remote Procedure Calls (RPC) from OP-TEE. + * + * Returns return code from secure world, 0 is OK + */ +u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg) +{ + struct optee *optee = tee_get_drvdata(ctx->teedev); + struct optee_call_waiter w; + struct optee_rpc_param param = { }; + u32 ret; + + param.a0 = OPTEE_SMC_CALL_WITH_ARG; + reg_pair_from_64(¶m.a1, ¶m.a2, parg); + /* Initialize waiter */ + optee_cq_wait_init(&optee->call_queue, &w); + while (true) { + struct arm_smccc_res res; + + optee->invoke_fn(param.a0, param.a1, param.a2, param.a3, + param.a4, param.a5, param.a6, param.a7, + &res); + + if (res.a0 == OPTEE_SMC_RETURN_ETHREAD_LIMIT) { + /* + * Out of threads in secure world, wait for a thread + * become available. + */ + optee_cq_wait_for_completion(&optee->call_queue, &w); + } else if (OPTEE_SMC_RETURN_IS_RPC(res.a0)) { + param.a0 = res.a0; + param.a1 = res.a1; + param.a2 = res.a2; + param.a3 = res.a3; + optee_handle_rpc(ctx, ¶m); + } else { + ret = res.a0; + break; + } + } + + /* + * We're done with our thread in secure world, if there's any + * thread waiters wake up one. + */ + optee_cq_wait_final(&optee->call_queue, &w); + + return ret; +} + +static struct tee_shm *get_msg_arg(struct tee_context *ctx, size_t num_params, + struct optee_msg_arg **msg_arg, + phys_addr_t *msg_parg) +{ + int rc; + struct tee_shm *shm; + struct optee_msg_arg *ma; + + shm = tee_shm_alloc(ctx, OPTEE_MSG_GET_ARG_SIZE(num_params), + TEE_SHM_MAPPED); + if (IS_ERR(shm)) + return shm; + + ma = tee_shm_get_va(shm, 0); + if (IS_ERR(ma)) { + rc = PTR_ERR(ma); + goto out; + } + + rc = tee_shm_get_pa(shm, 0, msg_parg); + if (rc) + goto out; + + memset(ma, 0, OPTEE_MSG_GET_ARG_SIZE(num_params)); + ma->num_params = num_params; + *msg_arg = ma; +out: + if (rc) { + tee_shm_free(shm); + return ERR_PTR(rc); + } + + return shm; +} + +int optee_open_session(struct tee_context *ctx, + struct tee_ioctl_open_session_arg *arg, + struct tee_param *param) +{ + struct optee_context_data *ctxdata = ctx->data; + int rc; + struct tee_shm *shm; + struct optee_msg_arg *msg_arg; + phys_addr_t msg_parg; + struct optee_session *sess = NULL; +#if defined(CONFIG_BCM_KF_OPTEE) + u32 process_id = task_pid_vnr(current); +#endif + + /* +2 for the meta parameters added below */ + shm = get_msg_arg(ctx, arg->num_params + 2, &msg_arg, &msg_parg); + if (IS_ERR(shm)) + return PTR_ERR(shm); + + msg_arg->cmd = OPTEE_MSG_CMD_OPEN_SESSION; + msg_arg->cancel_id = arg->cancel_id; +#if defined(CONFIG_BCM_KF_OPTEE) + msg_arg->pid = process_id; +#endif + /* + * Initialize and add the meta parameters needed when opening a + * session. + */ + msg_arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT | + OPTEE_MSG_ATTR_META; + msg_arg->params[1].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT | + OPTEE_MSG_ATTR_META; + memcpy(&msg_arg->params[0].u.value, arg->uuid, sizeof(arg->uuid)); + memcpy(&msg_arg->params[1].u.value, arg->uuid, sizeof(arg->clnt_uuid)); + msg_arg->params[1].u.value.c = arg->clnt_login; + + rc = optee_to_msg_param(msg_arg->params + 2, arg->num_params, param); + if (rc) + goto out; + + sess = kzalloc(sizeof(*sess), GFP_KERNEL); + if (!sess) { + rc = -ENOMEM; + goto out; + } + + if (optee_do_call_with_arg(ctx, msg_parg)) { + msg_arg->ret = TEEC_ERROR_COMMUNICATION; + msg_arg->ret_origin = TEEC_ORIGIN_COMMS; + } + + if (msg_arg->ret == TEEC_SUCCESS) { + /* A new session has been created, add it to the list. */ + sess->session_id = msg_arg->session; +#if defined(CONFIG_BCM_KF_OPTEE) + sess->session_pid = process_id; +#endif + mutex_lock(&ctxdata->mutex); + list_add(&sess->list_node, &ctxdata->sess_list); + mutex_unlock(&ctxdata->mutex); + } else { + kfree(sess); + } + + if (optee_from_msg_param(param, arg->num_params, msg_arg->params + 2)) { + arg->ret = TEEC_ERROR_COMMUNICATION; + arg->ret_origin = TEEC_ORIGIN_COMMS; + /* Close session again to avoid leakage */ + optee_close_session(ctx, msg_arg->session); + } else { + arg->session = msg_arg->session; + arg->ret = msg_arg->ret; + arg->ret_origin = msg_arg->ret_origin; + } +out: + tee_shm_free(shm); + + return rc; +} + +int optee_close_session(struct tee_context *ctx, u32 session) +{ + struct optee_context_data *ctxdata = ctx->data; + struct tee_shm *shm; + struct optee_msg_arg *msg_arg; + phys_addr_t msg_parg; + struct optee_session *sess; +#if defined(CONFIG_BCM_KF_OPTEE) + u32 process_id = task_pid_vnr(current); +#endif + + /* Check that the session is valid and remove it from the list */ + mutex_lock(&ctxdata->mutex); + sess = find_session(ctxdata, session); + if (sess) + list_del(&sess->list_node); + mutex_unlock(&ctxdata->mutex); + if (!sess) + return -EINVAL; +#if defined(CONFIG_BCM_KF_OPTEE) + if(process_id != sess->session_pid) + return -EINVAL; +#endif + kfree(sess); + + shm = get_msg_arg(ctx, 0, &msg_arg, &msg_parg); + if (IS_ERR(shm)) + return PTR_ERR(shm); + + msg_arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION; + msg_arg->session = session; +#if defined(CONFIG_BCM_KF_OPTEE) + msg_arg->pid = sess->session_pid; +#endif + optee_do_call_with_arg(ctx, msg_parg); + + tee_shm_free(shm); + return 0; +} + +int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, + struct tee_param *param) +{ + struct optee_context_data *ctxdata = ctx->data; + struct tee_shm *shm; + struct optee_msg_arg *msg_arg; + phys_addr_t msg_parg; + struct optee_session *sess; + int rc; +#if defined(CONFIG_BCM_KF_OPTEE) + u32 process_id = task_pid_vnr(current); +#endif + + /* Check that the session is valid */ + mutex_lock(&ctxdata->mutex); + sess = find_session(ctxdata, arg->session); + mutex_unlock(&ctxdata->mutex); + if (!sess) + return -EINVAL; +#if defined(CONFIG_BCM_KF_OPTEE) + if(process_id != sess->session_pid) + return -EINVAL; +#endif + + shm = get_msg_arg(ctx, arg->num_params, &msg_arg, &msg_parg); + if (IS_ERR(shm)) + return PTR_ERR(shm); + msg_arg->cmd = OPTEE_MSG_CMD_INVOKE_COMMAND; + msg_arg->func = arg->func; + msg_arg->session = arg->session; + msg_arg->cancel_id = arg->cancel_id; +#if defined(CONFIG_BCM_KF_OPTEE) + msg_arg->pid = sess->session_pid; +#endif + + rc = optee_to_msg_param(msg_arg->params, arg->num_params, param); + if (rc) + goto out; + + if (optee_do_call_with_arg(ctx, msg_parg)) { + msg_arg->ret = TEEC_ERROR_COMMUNICATION; + msg_arg->ret_origin = TEEC_ORIGIN_COMMS; + } + + if (optee_from_msg_param(param, arg->num_params, msg_arg->params)) { + msg_arg->ret = TEEC_ERROR_COMMUNICATION; + msg_arg->ret_origin = TEEC_ORIGIN_COMMS; + } + + arg->ret = msg_arg->ret; + arg->ret_origin = msg_arg->ret_origin; +out: + tee_shm_free(shm); + return rc; +} + +int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session) +{ + struct optee_context_data *ctxdata = ctx->data; + struct tee_shm *shm; + struct optee_msg_arg *msg_arg; + phys_addr_t msg_parg; + struct optee_session *sess; +#if defined(CONFIG_BCM_KF_OPTEE) + u32 process_id = task_pid_vnr(current); +#endif + + /* Check that the session is valid */ + mutex_lock(&ctxdata->mutex); + sess = find_session(ctxdata, session); + mutex_unlock(&ctxdata->mutex); + if (!sess) + return -EINVAL; +#if defined(CONFIG_BCM_KF_OPTEE) + if(process_id != sess->session_pid) + return -EINVAL; +#endif + + shm = get_msg_arg(ctx, 0, &msg_arg, &msg_parg); + if (IS_ERR(shm)) + return PTR_ERR(shm); + + msg_arg->cmd = OPTEE_MSG_CMD_CANCEL; + msg_arg->session = session; + msg_arg->cancel_id = cancel_id; +#if defined(CONFIG_BCM_KF_OPTEE) + msg_arg->pid = sess->session_pid; +#endif + optee_do_call_with_arg(ctx, msg_parg); + + tee_shm_free(shm); + return 0; +} + +/** + * optee_enable_shm_cache() - Enables caching of some shared memory allocation + * in OP-TEE + * @optee: main service struct + */ +void optee_enable_shm_cache(struct optee *optee) +{ + struct optee_call_waiter w; + + /* We need to retry until secure world isn't busy. */ + optee_cq_wait_init(&optee->call_queue, &w); + while (true) { + struct arm_smccc_res res; + + optee->invoke_fn(OPTEE_SMC_ENABLE_SHM_CACHE, 0, 0, 0, 0, 0, 0, + 0, &res); + if (res.a0 == OPTEE_SMC_RETURN_OK) + break; + optee_cq_wait_for_completion(&optee->call_queue, &w); + } + optee_cq_wait_final(&optee->call_queue, &w); +} + +/** + * optee_disable_shm_cache() - Disables caching of some shared memory allocation + * in OP-TEE + * @optee: main service struct + */ +void optee_disable_shm_cache(struct optee *optee) +{ + struct optee_call_waiter w; + + /* We need to retry until secure world isn't busy. */ + optee_cq_wait_init(&optee->call_queue, &w); + while (true) { + union { + struct arm_smccc_res smccc; + struct optee_smc_disable_shm_cache_result result; + } res; + + optee->invoke_fn(OPTEE_SMC_DISABLE_SHM_CACHE, 0, 0, 0, 0, 0, 0, + 0, &res.smccc); + if (res.result.status == OPTEE_SMC_RETURN_ENOTAVAIL) + break; /* All shm's freed */ + if (res.result.status == OPTEE_SMC_RETURN_OK) { + struct tee_shm *shm; + + shm = reg_pair_to_ptr(res.result.shm_upper32, + res.result.shm_lower32); + tee_shm_free(shm); + } else { + optee_cq_wait_for_completion(&optee->call_queue, &w); + } + } + optee_cq_wait_final(&optee->call_queue, &w); +} +#endif /* CONFIG_BCM_KF_OPTEE_414_BACKPORTS */ diff -urN linux-4.1.52-orig/drivers/tee/optee/core.c linux-4.1.52/drivers/tee/optee/core.c --- linux-4.1.52-orig/drivers/tee/optee/core.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/tee/optee/core.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,909 @@ +#if defined(CONFIG_BCM_KF_OPTEE_414_BACKPORTS) +/* + * Copyright (c) 2015, Linaro Limited + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "optee_private.h" +#include "optee_smc.h" + +#define DRIVER_NAME "optee" + +#define OPTEE_SHM_NUM_PRIV_PAGES 1 + +#if defined(CONFIG_BCM_KF_OPTEE) && defined(CONFIG_OPTEE) +#include +#include /* for meminfo */ +#include +#include +#include +#include /* for high_memory */ +#include +#include +#include +#include +#include + +static int pte_callback(pte_t *pte, unsigned long x, unsigned long y, + struct mm_walk *walk) +{ + const pgprot_t pte_prot = __pgprot(*pte); + const pgprot_t req_prot = *((pgprot_t *)walk->private); +#if defined(CONFIG_ARM64) + const pgprot_t prot_msk = PROT_DEFAULT | PTE_VALID; +#else + const pgprot_t prot_msk = L_PTE_MT_MASK | L_PTE_VALID; +#endif + + return (((pte_prot ^ req_prot) & prot_msk) == 0) ? 0 : -1; +} + +static void *page_to_virt_contig(const struct page *page, unsigned int pg_cnt, + pgprot_t pgprot) +{ + int rc; + struct mm_walk walk; + unsigned long pfn; + unsigned long pfn_start; + unsigned long pfn_end; + unsigned long va_start; + unsigned long va_end; + + if ((page == NULL) || !pg_cnt) + return ERR_PTR(-EINVAL); + + pfn_start = page_to_pfn(page); + pfn_end = pfn_start + pg_cnt; + for (pfn = pfn_start; pfn < pfn_end; pfn++) { + const struct page *cur_pg = pfn_to_page(pfn); + phys_addr_t pa; + + /* Verify range is in low memory only */ + if (PageHighMem(cur_pg)) + return NULL; + + /* Must be mapped */ + pa = page_to_phys(cur_pg); + if (page_address(cur_pg) == NULL) + return NULL; + } + + /* + * Aliased mappings with different cacheability attributes on ARM can + * lead to trouble! + */ + memset(&walk, 0, sizeof(walk)); + walk.pte_entry = &pte_callback; + walk.private = (void *)&pgprot; + walk.mm = current->mm; + va_start = (unsigned long)page_address(page); + va_end = (unsigned long)(page_address(page) + (pg_cnt << PAGE_SHIFT)); + rc = walk_page_range(va_start, + va_end, + &walk); + if (rc) + pr_debug("cacheability mismatch\n"); + + return rc ? NULL : page_address(page); +} + +static struct page **get_pages(struct page *page, int num_pages) +{ + struct page **pages; + long pfn; + int i; + + if (num_pages == 0) { + pr_err("bad count\n"); + return NULL; + } + + if (page == NULL) { + pr_err("bad page\n"); + return NULL; + } + + pages = vmalloc(sizeof(struct page *) * num_pages); + if (pages == NULL) + return NULL; + + pfn = page_to_pfn(page); + for (i = 0; i < num_pages; i++) { + /* + * pfn_to_page() should resolve to simple arithmetic for the + * FLATMEM memory model. + */ + pages[i] = pfn_to_page(pfn++); + } + + return pages; +} + +/* + * Basically just vmap() without checking that count < totalram_pages, + * since we want to be able to map pages that aren't managed by Linux + */ +static void *brcmstb_memory_vmap(struct page **pages, unsigned int count, + unsigned long flags, pgprot_t prot) +{ + struct vm_struct *area; + + might_sleep(); + + area = get_vm_area_caller((count << PAGE_SHIFT), flags, + __builtin_return_address(0)); + if (!area) + return NULL; + + if (map_vm_area(area, prot, pages)) { + vunmap(area->addr); + return NULL; + } + + return area->addr; +} + +/** + * brcmstb_memory_kva_map() - Map page(s) to a kernel virtual address + * + * @page: A struct page * that points to the beginning of a chunk of physical + * contiguous memory. + * @num_pages: Number of pages + * @pgprot: Page protection bits + * + * Return: pointer to mapping, or NULL on failure + */ +void *brcmstb_memory_kva_map(struct page *page, int num_pages, pgprot_t pgprot) +{ + void *va; + + /* get the virtual address for this range if it exists */ + va = page_to_virt_contig(page, num_pages, pgprot); + if (IS_ERR(va)) { + pr_debug("page_to_virt_contig() failed (%ld)\n", PTR_ERR(va)); + return NULL; + } else if (va == NULL || is_vmalloc_addr(va)) { + struct page **pages; + + pages = get_pages(page, num_pages); + if (pages == NULL) { + pr_err("couldn't get pages\n"); + return NULL; + } + + va = brcmstb_memory_vmap(pages, num_pages, 0, pgprot); + + vfree(pages); + + if (va == NULL) { + pr_err("vmap failed (num_pgs=%d)\n", num_pages); + return NULL; + } + } + + return va; +} +EXPORT_SYMBOL(brcmstb_memory_kva_map); + +/** + * brcmstb_memory_kva_map_phys() - map phys range to kernel virtual address + * + * @phys: physical address base + * @size: size of range to map + * @cached: whether to use cached or uncached mapping + * + * Return: NULL on failure, err on success + */ +void *brcmstb_memory_kva_map_phys(phys_addr_t phys, size_t size, bool cached) +{ + void *addr = NULL; + unsigned long pfn = PFN_DOWN(phys); + + if (!cached) { + /* + * This could be supported for MIPS by using ioremap instead, + * but that cannot be done on ARM if you want O_DIRECT support + * because having multiple mappings to the same memory with + * different cacheability will result in undefined behavior. + */ + return NULL; + } + + if (pfn_valid(pfn)) { + addr = brcmstb_memory_kva_map(pfn_to_page(pfn), + size / PAGE_SIZE, PAGE_KERNEL); + } + return addr; +} +EXPORT_SYMBOL(brcmstb_memory_kva_map_phys); + +/** + * brcmstb_memory_kva_unmap() - Unmap a kernel virtual address associated + * to physical pages mapped by brcmstb_memory_kva_map() + * + * @kva: Kernel virtual address previously mapped by brcmstb_memory_kva_map() + * + * Return: 0 on success, negative on failure. + */ +int brcmstb_memory_kva_unmap(const void *kva) +{ + if (kva == NULL) + return -EINVAL; + + if (!is_vmalloc_addr(kva)) { + /* unmapping not necessary for low memory VAs */ + return 0; + } + + vunmap(kva); + + return 0; +} +EXPORT_SYMBOL(brcmstb_memory_kva_unmap); + +extern void *brcmstb_memory_kva_map_phys(phys_addr_t phys, size_t size, bool cached); +extern int brcmstb_memory_kva_unmap(const void *kva); +#endif /* defined(CONFIG_BCM_KF_OPTEE) && defined(CONFIG_OPTEE) */ + +/** + * optee_from_msg_param() - convert from OPTEE_MSG parameters to + * struct tee_param + * @params: subsystem internal parameter representation + * @num_params: number of elements in the parameter arrays + * @msg_params: OPTEE_MSG parameters + * Returns 0 on success or <0 on failure + */ +int optee_from_msg_param(struct tee_param *params, size_t num_params, + const struct optee_msg_param *msg_params) +{ + int rc; + size_t n; + struct tee_shm *shm; + phys_addr_t pa; + + for (n = 0; n < num_params; n++) { + struct tee_param *p = params + n; + const struct optee_msg_param *mp = msg_params + n; + u32 attr = mp->attr & OPTEE_MSG_ATTR_TYPE_MASK; + + switch (attr) { + case OPTEE_MSG_ATTR_TYPE_NONE: + p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE; + memset(&p->u, 0, sizeof(p->u)); + break; + case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT: + case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT: + case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT: + p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT + + attr - OPTEE_MSG_ATTR_TYPE_VALUE_INPUT; + p->u.value.a = mp->u.value.a; + p->u.value.b = mp->u.value.b; + p->u.value.c = mp->u.value.c; + break; + case OPTEE_MSG_ATTR_TYPE_TMEM_INPUT: + case OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT: + case OPTEE_MSG_ATTR_TYPE_TMEM_INOUT: + p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT + + attr - OPTEE_MSG_ATTR_TYPE_TMEM_INPUT; + p->u.memref.size = mp->u.tmem.size; + shm = (struct tee_shm *)(unsigned long) + mp->u.tmem.shm_ref; + if (!shm) { + p->u.memref.shm_offs = 0; + p->u.memref.shm = NULL; + break; + } + rc = tee_shm_get_pa(shm, 0, &pa); + if (rc) + return rc; + p->u.memref.shm_offs = mp->u.tmem.buf_ptr - pa; + p->u.memref.shm = shm; + + /* Check that the memref is covered by the shm object */ + if (p->u.memref.size) { + size_t o = p->u.memref.shm_offs + + p->u.memref.size - 1; + + rc = tee_shm_get_pa(shm, o, NULL); + if (rc) + return rc; + } + break; + default: + return -EINVAL; + } + } + return 0; +} + +/** + * optee_to_msg_param() - convert from struct tee_params to OPTEE_MSG parameters + * @msg_params: OPTEE_MSG parameters + * @num_params: number of elements in the parameter arrays + * @params: subsystem itnernal parameter representation + * Returns 0 on success or <0 on failure + */ +int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params, + const struct tee_param *params) +{ + int rc; + size_t n; + phys_addr_t pa; + + for (n = 0; n < num_params; n++) { + const struct tee_param *p = params + n; + struct optee_msg_param *mp = msg_params + n; + + switch (p->attr) { + case TEE_IOCTL_PARAM_ATTR_TYPE_NONE: + mp->attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE; + memset(&mp->u, 0, sizeof(mp->u)); + break; + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: + mp->attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + p->attr - + TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; + mp->u.value.a = p->u.value.a; + mp->u.value.b = p->u.value.b; + mp->u.value.c = p->u.value.c; + break; + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: + mp->attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT + + p->attr - + TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; + mp->u.tmem.shm_ref = (unsigned long)p->u.memref.shm; + mp->u.tmem.size = p->u.memref.size; + if (!p->u.memref.shm) { + mp->u.tmem.buf_ptr = 0; + break; + } + rc = tee_shm_get_pa(p->u.memref.shm, + p->u.memref.shm_offs, &pa); + if (rc) + return rc; + mp->u.tmem.buf_ptr = pa; + mp->attr |= OPTEE_MSG_ATTR_CACHE_PREDEFINED << + OPTEE_MSG_ATTR_CACHE_SHIFT; + break; + default: + return -EINVAL; + } + } + return 0; +} + +static void optee_get_version(struct tee_device *teedev, + struct tee_ioctl_version_data *vers) +{ + struct tee_ioctl_version_data v = { + .impl_id = TEE_IMPL_ID_OPTEE, + .impl_caps = TEE_OPTEE_CAP_TZ, + .gen_caps = TEE_GEN_CAP_GP, + }; + *vers = v; +} + +static int optee_open(struct tee_context *ctx) +{ + struct optee_context_data *ctxdata; + struct tee_device *teedev = ctx->teedev; + struct optee *optee = tee_get_drvdata(teedev); + + ctxdata = kzalloc(sizeof(*ctxdata), GFP_KERNEL); + if (!ctxdata) + return -ENOMEM; + + if (teedev == optee->supp_teedev) { + bool busy = true; + + mutex_lock(&optee->supp.ctx_mutex); + if (!optee->supp.ctx) { + busy = false; + optee->supp.ctx = ctx; + } + mutex_unlock(&optee->supp.ctx_mutex); + if (busy) { + kfree(ctxdata); + return -EBUSY; + } + } + + mutex_init(&ctxdata->mutex); + INIT_LIST_HEAD(&ctxdata->sess_list); + + ctx->data = ctxdata; + return 0; +} + +static void optee_release(struct tee_context *ctx) +{ + struct optee_context_data *ctxdata = ctx->data; + struct tee_device *teedev = ctx->teedev; + struct optee *optee = tee_get_drvdata(teedev); + struct tee_shm *shm; + struct optee_msg_arg *arg = NULL; + phys_addr_t parg; + struct optee_session *sess; + struct optee_session *sess_tmp; + + if (!ctxdata) + return; + + shm = tee_shm_alloc(ctx, sizeof(struct optee_msg_arg), TEE_SHM_MAPPED); + if (!IS_ERR(shm)) { + arg = tee_shm_get_va(shm, 0); + /* + * If va2pa fails for some reason, we can't call into + * secure world, only free the memory. Secure OS will leak + * sessions and finally refuse more sessions, but we will + * at least let normal world reclaim its memory. + */ + if (!IS_ERR(arg)) + if (tee_shm_va2pa(shm, arg, &parg)) + arg = NULL; /* prevent usage of parg below */ + } + + list_for_each_entry_safe(sess, sess_tmp, &ctxdata->sess_list, + list_node) { + list_del(&sess->list_node); + if (!IS_ERR_OR_NULL(arg)) { + memset(arg, 0, sizeof(*arg)); + arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION; + arg->session = sess->session_id; + optee_do_call_with_arg(ctx, parg); + } + kfree(sess); + } + kfree(ctxdata); + + if (!IS_ERR(shm)) + tee_shm_free(shm); + + ctx->data = NULL; + + if (teedev == optee->supp_teedev) { + mutex_lock(&optee->supp.ctx_mutex); + optee->supp.ctx = NULL; + mutex_unlock(&optee->supp.ctx_mutex); + } +} + +static const struct tee_driver_ops optee_ops = { + .get_version = optee_get_version, + .open = optee_open, + .release = optee_release, + .open_session = optee_open_session, + .close_session = optee_close_session, + .invoke_func = optee_invoke_func, + .cancel_req = optee_cancel_req, +}; + +static const struct tee_desc optee_desc = { + .name = DRIVER_NAME "-clnt", + .ops = &optee_ops, + .owner = THIS_MODULE, +}; + +static const struct tee_driver_ops optee_supp_ops = { + .get_version = optee_get_version, + .open = optee_open, + .release = optee_release, + .supp_recv = optee_supp_recv, + .supp_send = optee_supp_send, +}; + +static const struct tee_desc optee_supp_desc = { + .name = DRIVER_NAME "-supp", + .ops = &optee_supp_ops, + .owner = THIS_MODULE, + .flags = TEE_DESC_PRIVILEGED, +}; + +static bool optee_msg_api_uid_is_optee_api(optee_invoke_fn *invoke_fn) +{ + struct arm_smccc_res res; + + invoke_fn(OPTEE_SMC_CALLS_UID, 0, 0, 0, 0, 0, 0, 0, &res); + + if (res.a0 == OPTEE_MSG_UID_0 && res.a1 == OPTEE_MSG_UID_1 && + res.a2 == OPTEE_MSG_UID_2 && res.a3 == OPTEE_MSG_UID_3) + return true; + return false; +} + +static bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn) +{ + union { + struct arm_smccc_res smccc; + struct optee_smc_calls_revision_result result; + } res; + + invoke_fn(OPTEE_SMC_CALLS_REVISION, 0, 0, 0, 0, 0, 0, 0, &res.smccc); + + if (res.result.major == OPTEE_MSG_REVISION_MAJOR && + (int)res.result.minor >= OPTEE_MSG_REVISION_MINOR) + return true; + return false; +} + +static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn, + u32 *sec_caps) +{ + union { + struct arm_smccc_res smccc; + struct optee_smc_exchange_capabilities_result result; + } res; + u32 a1 = 0; + + /* + * TODO This isn't enough to tell if it's UP system (from kernel + * point of view) or not, is_smp() returns the the information + * needed, but can't be called directly from here. + */ + if (!IS_ENABLED(CONFIG_SMP) || nr_cpu_ids == 1) + a1 |= OPTEE_SMC_NSEC_CAP_UNIPROCESSOR; + + invoke_fn(OPTEE_SMC_EXCHANGE_CAPABILITIES, a1, 0, 0, 0, 0, 0, 0, + &res.smccc); + + if (res.result.status != OPTEE_SMC_RETURN_OK) + return false; + + *sec_caps = res.result.capabilities; + return true; +} + +static struct tee_shm_pool * +optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm) +{ + union { + struct arm_smccc_res smccc; + struct optee_smc_get_shm_config_result result; + } res; + struct tee_shm_pool *pool; + unsigned long vaddr; + phys_addr_t paddr; + size_t size; + phys_addr_t begin; + phys_addr_t end; + void *va; + struct tee_shm_pool_mem_info priv_info; + struct tee_shm_pool_mem_info dmabuf_info; + + invoke_fn(OPTEE_SMC_GET_SHM_CONFIG, 0, 0, 0, 0, 0, 0, 0, &res.smccc); + if (res.result.status != OPTEE_SMC_RETURN_OK) { + pr_info("shm service not available\n"); + return ERR_PTR(-ENOENT); + } + + if (res.result.settings != OPTEE_SMC_SHM_CACHED) { + pr_err("only normal cached shared memory supported\n"); + return ERR_PTR(-EINVAL); + } + + begin = roundup(res.result.start, PAGE_SIZE); + end = rounddown(res.result.start + res.result.size, PAGE_SIZE); + paddr = begin; + size = end - begin; + + if (size < 2 * OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE) { + pr_err("too small shared memory area\n"); + return ERR_PTR(-EINVAL); + } + +#if defined(CONFIG_BCM_KF_OPTEE) && defined(CONFIG_OPTEE) + va = brcmstb_memory_kva_map_phys(paddr, size, true); +#else + va = memremap(paddr, size, MEMREMAP_WB); +#endif + if (!va) { + pr_err("shared memory ioremap failed\n"); + return ERR_PTR(-EINVAL); + } + vaddr = (unsigned long)va; + + priv_info.vaddr = vaddr; + priv_info.paddr = paddr; + priv_info.size = OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE; + dmabuf_info.vaddr = vaddr + OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE; + dmabuf_info.paddr = paddr + OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE; + dmabuf_info.size = size - OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE; + + pool = tee_shm_pool_alloc_res_mem(&priv_info, &dmabuf_info); + if (IS_ERR(pool)) { +#if defined(CONFIG_BCM_KF_OPTEE) && defined(CONFIG_OPTEE) + brcmstb_memory_kva_unmap(va); +#else + memunmap(va); +#endif + goto out; + } + + *memremaped_shm = va; +out: + return pool; +} + +/* Simple wrapper functions to be able to use a function pointer */ +static void optee_smccc_smc(unsigned long a0, unsigned long a1, + unsigned long a2, unsigned long a3, + unsigned long a4, unsigned long a5, + unsigned long a6, unsigned long a7, + struct arm_smccc_res *res) +{ +#if defined(CONFIG_BCM_KF_OPTEE) + extern char _stext[], _etext[]; +#if defined(CONFIG_ARM64) + register long ret_addr asm ("x30"); + + /* Make SMC call and save x0-x3 into res */ + __asm__ volatile( + "smc #0 \n" + "mov x8, %0 \n" + "stp x0, x1, [x8] \n" + "stp x2, x3, [x8, #0x10] \n" + : : "r" (res) : "x8"); +#else + register long ret_addr asm ("r14"); + + /* Make SMC call (opcode: 0xE1600070) and save r0-r3 into res */ + __asm__ volatile( + ".long 0xE1600070 \n" + "mov r8, %0 \n" + "stm r8, {r0-r3} \n" + : : "r" (res) : "r8"); +#endif + /* Make sure, this function was called from signed address range */ + if ( (char*)ret_addr < _stext || (char*)ret_addr > _etext){ + pr_info("ALERT!! System intrution detected by OP-TEE\n"); + res->a0 = OPTEE_SMC_RETURN_UNKNOWN_FUNCTION; + return; + } +#else + arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res); +#endif +} + +static void optee_smccc_hvc(unsigned long a0, unsigned long a1, + unsigned long a2, unsigned long a3, + unsigned long a4, unsigned long a5, + unsigned long a6, unsigned long a7, + struct arm_smccc_res *res) +{ + arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res); +} + +static optee_invoke_fn *get_invoke_func(struct device_node *np) +{ + const char *method; + + pr_info("probing for conduit method from DT.\n"); + + if (of_property_read_string(np, "method", &method)) { + pr_warn("missing \"method\" property\n"); + return ERR_PTR(-ENXIO); + } + + if (!strcmp("hvc", method)) + return optee_smccc_hvc; + else if (!strcmp("smc", method)) + return optee_smccc_smc; + + pr_warn("invalid \"method\" property: %s\n", method); + return ERR_PTR(-EINVAL); +} + +#if defined(CONFIG_BCM_KF_OPTEE) +struct optee *optee_probe(struct device_node *np) +#else +static struct optee *optee_probe(struct device_node *np) +#endif +{ + optee_invoke_fn *invoke_fn; + struct tee_shm_pool *pool; + struct optee *optee = NULL; + void *memremaped_shm = NULL; + struct tee_device *teedev; + u32 sec_caps; + int rc; + + invoke_fn = get_invoke_func(np); + if (IS_ERR(invoke_fn)) + return (void *)invoke_fn; + + if (!optee_msg_api_uid_is_optee_api(invoke_fn)) { + pr_warn("api uid mismatch\n"); + return ERR_PTR(-EINVAL); + } + + if (!optee_msg_api_revision_is_compatible(invoke_fn)) { + pr_warn("api revision mismatch\n"); + return ERR_PTR(-EINVAL); + } + + if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps)) { + pr_warn("capabilities mismatch\n"); + return ERR_PTR(-EINVAL); + } + + /* + * We have no other option for shared memory, if secure world + * doesn't have any reserved memory we can use we can't continue. + */ + if (!(sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM)) + return ERR_PTR(-EINVAL); + + pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm); + if (IS_ERR(pool)) + return (void *)pool; + + optee = kzalloc(sizeof(*optee), GFP_KERNEL); + if (!optee) { + rc = -ENOMEM; + goto err; + } + + optee->invoke_fn = invoke_fn; + + teedev = tee_device_alloc(&optee_desc, NULL, pool, optee); + if (IS_ERR(teedev)) { + rc = PTR_ERR(teedev); + goto err; + } + optee->teedev = teedev; + + teedev = tee_device_alloc(&optee_supp_desc, NULL, pool, optee); + if (IS_ERR(teedev)) { + rc = PTR_ERR(teedev); + goto err; + } + optee->supp_teedev = teedev; + + rc = tee_device_register(optee->teedev); + if (rc) + goto err; + + rc = tee_device_register(optee->supp_teedev); + if (rc) + goto err; + + mutex_init(&optee->call_queue.mutex); + INIT_LIST_HEAD(&optee->call_queue.waiters); + optee_wait_queue_init(&optee->wait_queue); + optee_supp_init(&optee->supp); + optee->memremaped_shm = memremaped_shm; + optee->pool = pool; + + optee_enable_shm_cache(optee); + + pr_info("initialized driver\n"); + return optee; +err: + if (optee) { + /* + * tee_device_unregister() is safe to call even if the + * devices hasn't been registered with + * tee_device_register() yet. + */ + tee_device_unregister(optee->supp_teedev); + tee_device_unregister(optee->teedev); + kfree(optee); + } + if (pool) + tee_shm_pool_free(pool); + if (memremaped_shm) +#if defined(CONFIG_BCM_KF_OPTEE) && defined(CONFIG_OPTEE) + iounmap(memremaped_shm); +#else + memunmap(memremaped_shm); +#endif + return ERR_PTR(rc); +} + +static void optee_remove(struct optee *optee) +{ + /* + * Ask OP-TEE to free all cached shared memory objects to decrease + * reference counters and also avoid wild pointers in secure world + * into the old shared memory range. + */ + optee_disable_shm_cache(optee); + + /* + * The two devices has to be unregistered before we can free the + * other resources. + */ + tee_device_unregister(optee->supp_teedev); + tee_device_unregister(optee->teedev); + + tee_shm_pool_free(optee->pool); + if (optee->memremaped_shm) +#if defined(CONFIG_BCM_KF_OPTEE) && defined(CONFIG_OPTEE) + iounmap(optee->memremaped_shm); +#else + memunmap(optee->memremaped_shm); +#endif + optee_wait_queue_exit(&optee->wait_queue); + optee_supp_uninit(&optee->supp); + mutex_destroy(&optee->call_queue.mutex); + + kfree(optee); +} + +static const struct of_device_id optee_match[] = { + { .compatible = "linaro,optee-tz" }, + {}, +}; + +static struct optee *optee_svc; + +static int __init optee_driver_init(void) +{ + struct device_node *fw_np; + struct device_node *np; + struct optee *optee; + + /* Node is supposed to be below /firmware */ + fw_np = of_find_node_by_name(NULL, "firmware"); + if (!fw_np) + return -ENODEV; + + np = of_find_matching_node(fw_np, optee_match); + of_node_put(fw_np); + if (!np) + return -ENODEV; + + optee = optee_probe(np); + of_node_put(np); + + if (IS_ERR(optee)) + return PTR_ERR(optee); + + optee_svc = optee; + + return 0; +} +module_init(optee_driver_init); + +static void __exit optee_driver_exit(void) +{ + struct optee *optee = optee_svc; + + optee_svc = NULL; + if (optee) + optee_remove(optee); +} +module_exit(optee_driver_exit); + +MODULE_AUTHOR("Linaro"); +MODULE_DESCRIPTION("OP-TEE driver"); +MODULE_SUPPORTED_DEVICE(""); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL v2"); +#endif /* CONFIG_BCM_KF_OPTEE_414_BACKPORTS */ diff -urN linux-4.1.52-orig/drivers/tee/optee/Kconfig linux-4.1.52/drivers/tee/optee/Kconfig --- linux-4.1.52-orig/drivers/tee/optee/Kconfig 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/tee/optee/Kconfig 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,9 @@ +if BCM_KF_OPTEE_414_BACKPORTS +# OP-TEE Trusted Execution Environment Configuration +config OPTEE + tristate "OP-TEE" + depends on HAVE_ARM_SMCCC || BCM_KF_OPTEE + help + This implements the OP-TEE Trusted Execution Environment (TEE) + driver. +endif # BCM_KF_OPTEE_414_BACKPORTS diff -urN linux-4.1.52-orig/drivers/tee/optee/Makefile linux-4.1.52/drivers/tee/optee/Makefile --- linux-4.1.52-orig/drivers/tee/optee/Makefile 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/tee/optee/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,8 @@ +ifdef BCM_KF # defined(CONFIG_BCM_KF_OPTEE_414_BACKPORTS) +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_OPTEE) += optee.o +optee-objs += core.o +optee-objs += call.o +optee-objs += rpc.o +optee-objs += supp.o +endif diff -urN linux-4.1.52-orig/drivers/tee/optee/optee_msg.h linux-4.1.52/drivers/tee/optee/optee_msg.h --- linux-4.1.52-orig/drivers/tee/optee/optee_msg.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/tee/optee/optee_msg.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,422 @@ +#if defined(CONFIG_BCM_KF_OPTEE_414_BACKPORTS) +/* + * Copyright (c) 2015-2016, Linaro Limited + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _OPTEE_MSG_H +#define _OPTEE_MSG_H + +#include +#include + +/* + * This file defines the OP-TEE message protocol used to communicate + * with an instance of OP-TEE running in secure world. + * + * This file is divided into three sections. + * 1. Formatting of messages. + * 2. Requests from normal world + * 3. Requests from secure world, Remote Procedure Call (RPC), handled by + * tee-supplicant. + */ + +/***************************************************************************** + * Part 1 - formatting of messages + *****************************************************************************/ + +#define OPTEE_MSG_ATTR_TYPE_NONE 0x0 +#define OPTEE_MSG_ATTR_TYPE_VALUE_INPUT 0x1 +#define OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT 0x2 +#define OPTEE_MSG_ATTR_TYPE_VALUE_INOUT 0x3 +#define OPTEE_MSG_ATTR_TYPE_RMEM_INPUT 0x5 +#define OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT 0x6 +#define OPTEE_MSG_ATTR_TYPE_RMEM_INOUT 0x7 +#define OPTEE_MSG_ATTR_TYPE_TMEM_INPUT 0x9 +#define OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT 0xa +#define OPTEE_MSG_ATTR_TYPE_TMEM_INOUT 0xb + +#define OPTEE_MSG_ATTR_TYPE_MASK GENMASK(7, 0) + +/* + * Meta parameter to be absorbed by the Secure OS and not passed + * to the Trusted Application. + * + * Currently only used with OPTEE_MSG_CMD_OPEN_SESSION. + */ +#define OPTEE_MSG_ATTR_META BIT(8) + +/* + * The temporary shared memory object is not physically contigous and this + * temp memref is followed by another fragment until the last temp memref + * that doesn't have this bit set. + */ +#define OPTEE_MSG_ATTR_FRAGMENT BIT(9) + +/* + * Memory attributes for caching passed with temp memrefs. The actual value + * used is defined outside the message protocol with the exception of + * OPTEE_MSG_ATTR_CACHE_PREDEFINED which means the attributes already + * defined for the memory range should be used. If optee_smc.h is used as + * bearer of this protocol OPTEE_SMC_SHM_* is used for values. + */ +#define OPTEE_MSG_ATTR_CACHE_SHIFT 16 +#define OPTEE_MSG_ATTR_CACHE_MASK GENMASK(2, 0) +#define OPTEE_MSG_ATTR_CACHE_PREDEFINED 0 + +/* + * Same values as TEE_LOGIN_* from TEE Internal API + */ +#define OPTEE_MSG_LOGIN_PUBLIC 0x00000000 +#define OPTEE_MSG_LOGIN_USER 0x00000001 +#define OPTEE_MSG_LOGIN_GROUP 0x00000002 +#define OPTEE_MSG_LOGIN_APPLICATION 0x00000004 +#define OPTEE_MSG_LOGIN_APPLICATION_USER 0x00000005 +#define OPTEE_MSG_LOGIN_APPLICATION_GROUP 0x00000006 + +/** + * struct optee_msg_param_tmem - temporary memory reference parameter + * @buf_ptr: Address of the buffer + * @size: Size of the buffer + * @shm_ref: Temporary shared memory reference, pointer to a struct tee_shm + * + * Secure and normal world communicates pointers as physical address + * instead of the virtual address. This is because secure and normal world + * have completely independent memory mapping. Normal world can even have a + * hypervisor which need to translate the guest physical address (AKA IPA + * in ARM documentation) to a real physical address before passing the + * structure to secure world. + */ +struct optee_msg_param_tmem { + u64 buf_ptr; + u64 size; + u64 shm_ref; +}; + +/** + * struct optee_msg_param_rmem - registered memory reference parameter + * @offs: Offset into shared memory reference + * @size: Size of the buffer + * @shm_ref: Shared memory reference, pointer to a struct tee_shm + */ +struct optee_msg_param_rmem { + u64 offs; + u64 size; + u64 shm_ref; +}; + +/** + * struct optee_msg_param_value - opaque value parameter + * + * Value parameters are passed unchecked between normal and secure world. + */ +struct optee_msg_param_value { + u64 a; + u64 b; + u64 c; +}; + +/** + * struct optee_msg_param - parameter used together with struct optee_msg_arg + * @attr: attributes + * @tmem: parameter by temporary memory reference + * @rmem: parameter by registered memory reference + * @value: parameter by opaque value + * + * @attr & OPTEE_MSG_ATTR_TYPE_MASK indicates if tmem, rmem or value is used in + * the union. OPTEE_MSG_ATTR_TYPE_VALUE_* indicates value, + * OPTEE_MSG_ATTR_TYPE_TMEM_* indicates tmem and + * OPTEE_MSG_ATTR_TYPE_RMEM_* indicates rmem. + * OPTEE_MSG_ATTR_TYPE_NONE indicates that none of the members are used. + */ +struct optee_msg_param { + u64 attr; + union { + struct optee_msg_param_tmem tmem; + struct optee_msg_param_rmem rmem; + struct optee_msg_param_value value; + } u; +}; + +/** + * struct optee_msg_arg - call argument + * @cmd: Command, one of OPTEE_MSG_CMD_* or OPTEE_MSG_RPC_CMD_* + * @func: Trusted Application function, specific to the Trusted Application, + * used if cmd == OPTEE_MSG_CMD_INVOKE_COMMAND + * @session: In parameter for all OPTEE_MSG_CMD_* except + * OPTEE_MSG_CMD_OPEN_SESSION where it's an output parameter instead + * @cancel_id: Cancellation id, a unique value to identify this request + * @ret: return value + * @ret_origin: origin of the return value + * @num_params: number of parameters supplied to the OS Command + * @params: the parameters supplied to the OS Command + * + * All normal calls to Trusted OS uses this struct. If cmd requires further + * information than what these field holds it can be passed as a parameter + * tagged as meta (setting the OPTEE_MSG_ATTR_META bit in corresponding + * attrs field). All parameters tagged as meta has to come first. + * + * Temp memref parameters can be fragmented if supported by the Trusted OS + * (when optee_smc.h is bearer of this protocol this is indicated with + * OPTEE_SMC_SEC_CAP_UNREGISTERED_SHM). If a logical memref parameter is + * fragmented then has all but the last fragment the + * OPTEE_MSG_ATTR_FRAGMENT bit set in attrs. Even if a memref is fragmented + * it will still be presented as a single logical memref to the Trusted + * Application. + */ +struct optee_msg_arg { + u32 cmd; + u32 func; + u32 session; + u32 cancel_id; + u32 pad; + u32 ret; + u32 ret_origin; + u32 num_params; +#if defined(CONFIG_BCM_KF_OPTEE) + u32 pid; +#endif + /* num_params tells the actual number of element in params */ + struct optee_msg_param params[0]; +}; + +/** + * OPTEE_MSG_GET_ARG_SIZE - return size of struct optee_msg_arg + * + * @num_params: Number of parameters embedded in the struct optee_msg_arg + * + * Returns the size of the struct optee_msg_arg together with the number + * of embedded parameters. + */ +#define OPTEE_MSG_GET_ARG_SIZE(num_params) \ + (sizeof(struct optee_msg_arg) + \ + sizeof(struct optee_msg_param) * (num_params)) + +/***************************************************************************** + * Part 2 - requests from normal world + *****************************************************************************/ + +/* + * Return the following UID if using API specified in this file without + * further extensions: + * 384fb3e0-e7f8-11e3-af63-0002a5d5c51b. + * Represented in 4 32-bit words in OPTEE_MSG_UID_0, OPTEE_MSG_UID_1, + * OPTEE_MSG_UID_2, OPTEE_MSG_UID_3. + */ +#define OPTEE_MSG_UID_0 0x384fb3e0 +#define OPTEE_MSG_UID_1 0xe7f811e3 +#define OPTEE_MSG_UID_2 0xaf630002 +#define OPTEE_MSG_UID_3 0xa5d5c51b +#define OPTEE_MSG_FUNCID_CALLS_UID 0xFF01 + +/* + * Returns 2.0 if using API specified in this file without further + * extensions. Represented in 2 32-bit words in OPTEE_MSG_REVISION_MAJOR + * and OPTEE_MSG_REVISION_MINOR + */ +#define OPTEE_MSG_REVISION_MAJOR 2 +#define OPTEE_MSG_REVISION_MINOR 0 +#define OPTEE_MSG_FUNCID_CALLS_REVISION 0xFF03 + +/* + * Get UUID of Trusted OS. + * + * Used by non-secure world to figure out which Trusted OS is installed. + * Note that returned UUID is the UUID of the Trusted OS, not of the API. + * + * Returns UUID in 4 32-bit words in the same way as + * OPTEE_MSG_FUNCID_CALLS_UID described above. + */ +#define OPTEE_MSG_OS_OPTEE_UUID_0 0x486178e0 +#define OPTEE_MSG_OS_OPTEE_UUID_1 0xe7f811e3 +#define OPTEE_MSG_OS_OPTEE_UUID_2 0xbc5e0002 +#define OPTEE_MSG_OS_OPTEE_UUID_3 0xa5d5c51b +#define OPTEE_MSG_FUNCID_GET_OS_UUID 0x0000 + +/* + * Get revision of Trusted OS. + * + * Used by non-secure world to figure out which version of the Trusted OS + * is installed. Note that the returned revision is the revision of the + * Trusted OS, not of the API. + * + * Returns revision in 2 32-bit words in the same way as + * OPTEE_MSG_CALLS_REVISION described above. + */ +#define OPTEE_MSG_FUNCID_GET_OS_REVISION 0x0001 + +/* + * Do a secure call with struct optee_msg_arg as argument + * The OPTEE_MSG_CMD_* below defines what goes in struct optee_msg_arg::cmd + * + * OPTEE_MSG_CMD_OPEN_SESSION opens a session to a Trusted Application. + * The first two parameters are tagged as meta, holding two value + * parameters to pass the following information: + * param[0].u.value.a-b uuid of Trusted Application + * param[1].u.value.a-b uuid of Client + * param[1].u.value.c Login class of client OPTEE_MSG_LOGIN_* + * + * OPTEE_MSG_CMD_INVOKE_COMMAND invokes a command a previously opened + * session to a Trusted Application. struct optee_msg_arg::func is Trusted + * Application function, specific to the Trusted Application. + * + * OPTEE_MSG_CMD_CLOSE_SESSION closes a previously opened session to + * Trusted Application. + * + * OPTEE_MSG_CMD_CANCEL cancels a currently invoked command. + * + * OPTEE_MSG_CMD_REGISTER_SHM registers a shared memory reference. The + * information is passed as: + * [in] param[0].attr OPTEE_MSG_ATTR_TYPE_TMEM_INPUT + * [| OPTEE_MSG_ATTR_FRAGMENT] + * [in] param[0].u.tmem.buf_ptr physical address (of first fragment) + * [in] param[0].u.tmem.size size (of first fragment) + * [in] param[0].u.tmem.shm_ref holds shared memory reference + * ... + * The shared memory can optionally be fragmented, temp memrefs can follow + * each other with all but the last with the OPTEE_MSG_ATTR_FRAGMENT bit set. + * + * OPTEE_MSG_CMD_UNREGISTER_SHM unregisteres a previously registered shared + * memory reference. The information is passed as: + * [in] param[0].attr OPTEE_MSG_ATTR_TYPE_RMEM_INPUT + * [in] param[0].u.rmem.shm_ref holds shared memory reference + * [in] param[0].u.rmem.offs 0 + * [in] param[0].u.rmem.size 0 + */ +#define OPTEE_MSG_CMD_OPEN_SESSION 0 +#define OPTEE_MSG_CMD_INVOKE_COMMAND 1 +#define OPTEE_MSG_CMD_CLOSE_SESSION 2 +#define OPTEE_MSG_CMD_CANCEL 3 +#define OPTEE_MSG_CMD_REGISTER_SHM 4 +#define OPTEE_MSG_CMD_UNREGISTER_SHM 5 +#define OPTEE_MSG_FUNCID_CALL_WITH_ARG 0x0004 + +/***************************************************************************** + * Part 3 - Requests from secure world, RPC + *****************************************************************************/ + +/* + * All RPC is done with a struct optee_msg_arg as bearer of information, + * struct optee_msg_arg::arg holds values defined by OPTEE_MSG_RPC_CMD_* below + * + * RPC communication with tee-supplicant is reversed compared to normal + * client communication desribed above. The supplicant receives requests + * and sends responses. + */ + +/* + * Load a TA into memory, defined in tee-supplicant + */ +#define OPTEE_MSG_RPC_CMD_LOAD_TA 0 + +/* + * Reserved + */ +#define OPTEE_MSG_RPC_CMD_RPMB 1 + +/* + * File system access, defined in tee-supplicant + */ +#define OPTEE_MSG_RPC_CMD_FS 2 + +/* + * Get time + * + * Returns number of seconds and nano seconds since the Epoch, + * 1970-01-01 00:00:00 +0000 (UTC). + * + * [out] param[0].u.value.a Number of seconds + * [out] param[0].u.value.b Number of nano seconds. + */ +#define OPTEE_MSG_RPC_CMD_GET_TIME 3 + +/* + * Wait queue primitive, helper for secure world to implement a wait queue. + * + * If secure world need to wait for a secure world mutex it issues a sleep + * request instead of spinning in secure world. Conversely is a wakeup + * request issued when a secure world mutex with a thread waiting thread is + * unlocked. + * + * Waiting on a key + * [in] param[0].u.value.a OPTEE_MSG_RPC_WAIT_QUEUE_SLEEP + * [in] param[0].u.value.b wait key + * + * Waking up a key + * [in] param[0].u.value.a OPTEE_MSG_RPC_WAIT_QUEUE_WAKEUP + * [in] param[0].u.value.b wakeup key + */ +#define OPTEE_MSG_RPC_CMD_WAIT_QUEUE 4 +#define OPTEE_MSG_RPC_WAIT_QUEUE_SLEEP 0 +#define OPTEE_MSG_RPC_WAIT_QUEUE_WAKEUP 1 + +/* + * Suspend execution + * + * [in] param[0].value .a number of milliseconds to suspend + */ +#define OPTEE_MSG_RPC_CMD_SUSPEND 5 + +/* + * Allocate a piece of shared memory + * + * Shared memory can optionally be fragmented, to support that additional + * spare param entries are allocated to make room for eventual fragments. + * The spare param entries has .attr = OPTEE_MSG_ATTR_TYPE_NONE when + * unused. All returned temp memrefs except the last should have the + * OPTEE_MSG_ATTR_FRAGMENT bit set in the attr field. + * + * [in] param[0].u.value.a type of memory one of + * OPTEE_MSG_RPC_SHM_TYPE_* below + * [in] param[0].u.value.b requested size + * [in] param[0].u.value.c required alignment + * + * [out] param[0].u.tmem.buf_ptr physical address (of first fragment) + * [out] param[0].u.tmem.size size (of first fragment) + * [out] param[0].u.tmem.shm_ref shared memory reference + * ... + * [out] param[n].u.tmem.buf_ptr physical address + * [out] param[n].u.tmem.size size + * [out] param[n].u.tmem.shm_ref shared memory reference (same value + * as in param[n-1].u.tmem.shm_ref) + */ +#define OPTEE_MSG_RPC_CMD_SHM_ALLOC 6 +/* Memory that can be shared with a non-secure user space application */ +#define OPTEE_MSG_RPC_SHM_TYPE_APPL 0 +/* Memory only shared with non-secure kernel */ +#define OPTEE_MSG_RPC_SHM_TYPE_KERNEL 1 + +/* + * Free shared memory previously allocated with OPTEE_MSG_RPC_CMD_SHM_ALLOC + * + * [in] param[0].u.value.a type of memory one of + * OPTEE_MSG_RPC_SHM_TYPE_* above + * [in] param[0].u.value.b value of shared memory reference + * returned in param[0].u.tmem.shm_ref + * above + */ +#define OPTEE_MSG_RPC_CMD_SHM_FREE 7 + +#endif /* _OPTEE_MSG_H */ +#endif /* CONFIG_BCM_KF_OPTEE_414_BACKPORTS */ diff -urN linux-4.1.52-orig/drivers/tee/optee/optee_private.h linux-4.1.52/drivers/tee/optee/optee_private.h --- linux-4.1.52-orig/drivers/tee/optee/optee_private.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/tee/optee/optee_private.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,188 @@ +#if defined(CONFIG_BCM_KF_OPTEE_414_BACKPORTS) +/* + * Copyright (c) 2015, Linaro Limited + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#ifndef OPTEE_PRIVATE_H +#define OPTEE_PRIVATE_H + +#include +#include +#include +#include +#include "optee_msg.h" + +#define OPTEE_MAX_ARG_SIZE 1024 + +/* Some Global Platform error codes used in this driver */ +#define TEEC_SUCCESS 0x00000000 +#define TEEC_ERROR_BAD_PARAMETERS 0xFFFF0006 +#define TEEC_ERROR_COMMUNICATION 0xFFFF000E +#define TEEC_ERROR_OUT_OF_MEMORY 0xFFFF000C + +#define TEEC_ORIGIN_COMMS 0x00000002 + +typedef void (optee_invoke_fn)(unsigned long, unsigned long, unsigned long, + unsigned long, unsigned long, unsigned long, + unsigned long, unsigned long, + struct arm_smccc_res *); + +struct optee_call_queue { + /* Serializes access to this struct */ + struct mutex mutex; + struct list_head waiters; +}; + +struct optee_wait_queue { + /* Serializes access to this struct */ + struct mutex mu; + struct list_head db; +}; + +/** + * struct optee_supp - supplicant synchronization struct + * @ctx the context of current connected supplicant. + * if !NULL the supplicant device is available for use, + * else busy + * @ctx_mutex: held while accessing @ctx + * @func: supplicant function id to call + * @ret: call return value + * @num_params: number of elements in @param + * @param: parameters for @func + * @req_posted: if true, a request has been posted to the supplicant + * @supp_next_send: if true, next step is for supplicant to send response + * @thrd_mutex: held by the thread doing a request to supplicant + * @supp_mutex: held by supplicant while operating on this struct + * @data_to_supp: supplicant is waiting on this for next request + * @data_from_supp: requesting thread is waiting on this to get the result + */ +struct optee_supp { + struct tee_context *ctx; + /* Serializes access of ctx */ + struct mutex ctx_mutex; + + u32 func; + u32 ret; + size_t num_params; + struct tee_param *param; + + bool req_posted; + bool supp_next_send; + /* Serializes access to this struct for requesting thread */ + struct mutex thrd_mutex; + /* Serializes access to this struct for supplicant threads */ + struct mutex supp_mutex; + struct completion data_to_supp; + struct completion data_from_supp; +}; + +/** + * struct optee - main service struct + * @supp_teedev: supplicant device + * @teedev: client device + * @invoke_fn: function to issue smc or hvc + * @call_queue: queue of threads waiting to call @invoke_fn + * @wait_queue: queue of threads from secure world waiting for a + * secure world sync object + * @supp: supplicant synchronization struct for RPC to supplicant + * @pool: shared memory pool + * @memremaped_shm virtual address of memory in shared memory pool + */ +struct optee { + struct tee_device *supp_teedev; + struct tee_device *teedev; + optee_invoke_fn *invoke_fn; + struct optee_call_queue call_queue; + struct optee_wait_queue wait_queue; + struct optee_supp supp; + struct tee_shm_pool *pool; + void *memremaped_shm; +}; + +struct optee_session { + struct list_head list_node; + u32 session_id; +#if defined(CONFIG_BCM_KF_OPTEE) + u32 session_pid; +#endif +}; + +struct optee_context_data { + /* Serializes access to this struct */ + struct mutex mutex; + struct list_head sess_list; +}; + +struct optee_rpc_param { + u32 a0; + u32 a1; + u32 a2; + u32 a3; + u32 a4; + u32 a5; + u32 a6; + u32 a7; +}; + +void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param); + +void optee_wait_queue_init(struct optee_wait_queue *wq); +void optee_wait_queue_exit(struct optee_wait_queue *wq); + +u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params, + struct tee_param *param); + +int optee_supp_read(struct tee_context *ctx, void __user *buf, size_t len); +int optee_supp_write(struct tee_context *ctx, void __user *buf, size_t len); +void optee_supp_init(struct optee_supp *supp); +void optee_supp_uninit(struct optee_supp *supp); + +int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, + struct tee_param *param); +int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params, + struct tee_param *param); + +u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg); +int optee_open_session(struct tee_context *ctx, + struct tee_ioctl_open_session_arg *arg, + struct tee_param *param); +int optee_close_session(struct tee_context *ctx, u32 session); +int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, + struct tee_param *param); +int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session); + +void optee_enable_shm_cache(struct optee *optee); +void optee_disable_shm_cache(struct optee *optee); + +int optee_from_msg_param(struct tee_param *params, size_t num_params, + const struct optee_msg_param *msg_params); +int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params, + const struct tee_param *params); + +/* + * Small helpers + */ + +static inline void *reg_pair_to_ptr(u32 reg0, u32 reg1) +{ + return (void *)(unsigned long)(((u64)reg0 << 32) | reg1); +} + +static inline void reg_pair_from_64(u32 *reg0, u32 *reg1, u64 val) +{ + *reg0 = val >> 32; + *reg1 = val; +} + +#endif /*OPTEE_PRIVATE_H*/ +#endif /* CONFIG_BCM_KF_OPTEE_414_BACKPORTS */ diff -urN linux-4.1.52-orig/drivers/tee/optee/optee_smc.h linux-4.1.52/drivers/tee/optee/optee_smc.h --- linux-4.1.52-orig/drivers/tee/optee/optee_smc.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/tee/optee/optee_smc.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,452 @@ +#if defined(CONFIG_BCM_KF_OPTEE_414_BACKPORTS) +/* + * Copyright (c) 2015-2016, Linaro Limited + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef OPTEE_SMC_H +#define OPTEE_SMC_H + +#include +#include + +#define OPTEE_SMC_STD_CALL_VAL(func_num) \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL, ARM_SMCCC_SMC_32, \ + ARM_SMCCC_OWNER_TRUSTED_OS, (func_num)) +#define OPTEE_SMC_FAST_CALL_VAL(func_num) \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \ + ARM_SMCCC_OWNER_TRUSTED_OS, (func_num)) + +/* + * Function specified by SMC Calling convention. + */ +#define OPTEE_SMC_FUNCID_CALLS_COUNT 0xFF00 +#define OPTEE_SMC_CALLS_COUNT \ + ARM_SMCCC_CALL_VAL(OPTEE_SMC_FAST_CALL, SMCCC_SMC_32, \ + SMCCC_OWNER_TRUSTED_OS_END, \ + OPTEE_SMC_FUNCID_CALLS_COUNT) + +/* + * Normal cached memory (write-back), shareable for SMP systems and not + * shareable for UP systems. + */ +#define OPTEE_SMC_SHM_CACHED 1 + +/* + * a0..a7 is used as register names in the descriptions below, on arm32 + * that translates to r0..r7 and on arm64 to w0..w7. In both cases it's + * 32-bit registers. + */ + +/* + * Function specified by SMC Calling convention + * + * Return one of the following UIDs if using API specified in this file + * without further extentions: + * 65cb6b93-af0c-4617-8ed6-644a8d1140f8 + * see also OPTEE_SMC_UID_* in optee_msg.h + */ +#define OPTEE_SMC_FUNCID_CALLS_UID OPTEE_MSG_FUNCID_CALLS_UID +#define OPTEE_SMC_CALLS_UID \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \ + ARM_SMCCC_OWNER_TRUSTED_OS_END, \ + OPTEE_SMC_FUNCID_CALLS_UID) + +/* + * Function specified by SMC Calling convention + * + * Returns 2.0 if using API specified in this file without further extentions. + * see also OPTEE_MSG_REVISION_* in optee_msg.h + */ +#define OPTEE_SMC_FUNCID_CALLS_REVISION OPTEE_MSG_FUNCID_CALLS_REVISION +#define OPTEE_SMC_CALLS_REVISION \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \ + ARM_SMCCC_OWNER_TRUSTED_OS_END, \ + OPTEE_SMC_FUNCID_CALLS_REVISION) + +struct optee_smc_calls_revision_result { + unsigned long major; + unsigned long minor; + unsigned long reserved0; + unsigned long reserved1; +}; + +/* + * Get UUID of Trusted OS. + * + * Used by non-secure world to figure out which Trusted OS is installed. + * Note that returned UUID is the UUID of the Trusted OS, not of the API. + * + * Returns UUID in a0-4 in the same way as OPTEE_SMC_CALLS_UID + * described above. + */ +#define OPTEE_SMC_FUNCID_GET_OS_UUID OPTEE_MSG_FUNCID_GET_OS_UUID +#define OPTEE_SMC_CALL_GET_OS_UUID \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_OS_UUID) + +/* + * Get revision of Trusted OS. + * + * Used by non-secure world to figure out which version of the Trusted OS + * is installed. Note that the returned revision is the revision of the + * Trusted OS, not of the API. + * + * Returns revision in a0-1 in the same way as OPTEE_SMC_CALLS_REVISION + * described above. + */ +#define OPTEE_SMC_FUNCID_GET_OS_REVISION OPTEE_MSG_FUNCID_GET_OS_REVISION +#define OPTEE_SMC_CALL_GET_OS_REVISION \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_OS_REVISION) + +/* + * Call with struct optee_msg_arg as argument + * + * Call register usage: + * a0 SMC Function ID, OPTEE_SMC*CALL_WITH_ARG + * a1 Upper 32bit of a 64bit physical pointer to a struct optee_msg_arg + * a2 Lower 32bit of a 64bit physical pointer to a struct optee_msg_arg + * a3 Cache settings, not used if physical pointer is in a predefined shared + * memory area else per OPTEE_SMC_SHM_* + * a4-6 Not used + * a7 Hypervisor Client ID register + * + * Normal return register usage: + * a0 Return value, OPTEE_SMC_RETURN_* + * a1-3 Not used + * a4-7 Preserved + * + * OPTEE_SMC_RETURN_ETHREAD_LIMIT return register usage: + * a0 Return value, OPTEE_SMC_RETURN_ETHREAD_LIMIT + * a1-3 Preserved + * a4-7 Preserved + * + * RPC return register usage: + * a0 Return value, OPTEE_SMC_RETURN_IS_RPC(val) + * a1-2 RPC parameters + * a3-7 Resume information, must be preserved + * + * Possible return values: + * OPTEE_SMC_RETURN_UNKNOWN_FUNCTION Trusted OS does not recognize this + * function. + * OPTEE_SMC_RETURN_OK Call completed, result updated in + * the previously supplied struct + * optee_msg_arg. + * OPTEE_SMC_RETURN_ETHREAD_LIMIT Number of Trusted OS threads exceeded, + * try again later. + * OPTEE_SMC_RETURN_EBADADDR Bad physcial pointer to struct + * optee_msg_arg. + * OPTEE_SMC_RETURN_EBADCMD Bad/unknown cmd in struct optee_msg_arg + * OPTEE_SMC_RETURN_IS_RPC() Call suspended by RPC call to normal + * world. + */ +#define OPTEE_SMC_FUNCID_CALL_WITH_ARG OPTEE_MSG_FUNCID_CALL_WITH_ARG +#define OPTEE_SMC_CALL_WITH_ARG \ + OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_CALL_WITH_ARG) + +/* + * Get Shared Memory Config + * + * Returns the Secure/Non-secure shared memory config. + * + * Call register usage: + * a0 SMC Function ID, OPTEE_SMC_GET_SHM_CONFIG + * a1-6 Not used + * a7 Hypervisor Client ID register + * + * Have config return register usage: + * a0 OPTEE_SMC_RETURN_OK + * a1 Physical address of start of SHM + * a2 Size of of SHM + * a3 Cache settings of memory, as defined by the + * OPTEE_SMC_SHM_* values above + * a4-7 Preserved + * + * Not available register usage: + * a0 OPTEE_SMC_RETURN_ENOTAVAIL + * a1-3 Not used + * a4-7 Preserved + */ +#define OPTEE_SMC_FUNCID_GET_SHM_CONFIG 7 +#define OPTEE_SMC_GET_SHM_CONFIG \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_SHM_CONFIG) + +struct optee_smc_get_shm_config_result { + unsigned long status; + unsigned long start; + unsigned long size; + unsigned long settings; +}; + +/* + * Exchanges capabilities between normal world and secure world + * + * Call register usage: + * a0 SMC Function ID, OPTEE_SMC_EXCHANGE_CAPABILITIES + * a1 bitfield of normal world capabilities OPTEE_SMC_NSEC_CAP_* + * a2-6 Not used + * a7 Hypervisor Client ID register + * + * Normal return register usage: + * a0 OPTEE_SMC_RETURN_OK + * a1 bitfield of secure world capabilities OPTEE_SMC_SEC_CAP_* + * a2-7 Preserved + * + * Error return register usage: + * a0 OPTEE_SMC_RETURN_ENOTAVAIL, can't use the capabilities from normal world + * a1 bitfield of secure world capabilities OPTEE_SMC_SEC_CAP_* + * a2-7 Preserved + */ +/* Normal world works as a uniprocessor system */ +#define OPTEE_SMC_NSEC_CAP_UNIPROCESSOR BIT(0) +/* Secure world has reserved shared memory for normal world to use */ +#define OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM BIT(0) +/* Secure world can communicate via previously unregistered shared memory */ +#define OPTEE_SMC_SEC_CAP_UNREGISTERED_SHM BIT(1) +#define OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES 9 +#define OPTEE_SMC_EXCHANGE_CAPABILITIES \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES) + +struct optee_smc_exchange_capabilities_result { + unsigned long status; + unsigned long capabilities; + unsigned long reserved0; + unsigned long reserved1; +}; + +/* + * Disable and empties cache of shared memory objects + * + * Secure world can cache frequently used shared memory objects, for + * example objects used as RPC arguments. When secure world is idle this + * function returns one shared memory reference to free. To disable the + * cache and free all cached objects this function has to be called until + * it returns OPTEE_SMC_RETURN_ENOTAVAIL. + * + * Call register usage: + * a0 SMC Function ID, OPTEE_SMC_DISABLE_SHM_CACHE + * a1-6 Not used + * a7 Hypervisor Client ID register + * + * Normal return register usage: + * a0 OPTEE_SMC_RETURN_OK + * a1 Upper 32bit of a 64bit Shared memory cookie + * a2 Lower 32bit of a 64bit Shared memory cookie + * a3-7 Preserved + * + * Cache empty return register usage: + * a0 OPTEE_SMC_RETURN_ENOTAVAIL + * a1-7 Preserved + * + * Not idle return register usage: + * a0 OPTEE_SMC_RETURN_EBUSY + * a1-7 Preserved + */ +#define OPTEE_SMC_FUNCID_DISABLE_SHM_CACHE 10 +#define OPTEE_SMC_DISABLE_SHM_CACHE \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_DISABLE_SHM_CACHE) + +struct optee_smc_disable_shm_cache_result { + unsigned long status; + unsigned long shm_upper32; + unsigned long shm_lower32; + unsigned long reserved0; +}; + +/* + * Enable cache of shared memory objects + * + * Secure world can cache frequently used shared memory objects, for + * example objects used as RPC arguments. When secure world is idle this + * function returns OPTEE_SMC_RETURN_OK and the cache is enabled. If + * secure world isn't idle OPTEE_SMC_RETURN_EBUSY is returned. + * + * Call register usage: + * a0 SMC Function ID, OPTEE_SMC_ENABLE_SHM_CACHE + * a1-6 Not used + * a7 Hypervisor Client ID register + * + * Normal return register usage: + * a0 OPTEE_SMC_RETURN_OK + * a1-7 Preserved + * + * Not idle return register usage: + * a0 OPTEE_SMC_RETURN_EBUSY + * a1-7 Preserved + */ +#define OPTEE_SMC_FUNCID_ENABLE_SHM_CACHE 11 +#define OPTEE_SMC_ENABLE_SHM_CACHE \ + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_ENABLE_SHM_CACHE) + +/* + * Resume from RPC (for example after processing a foreign interrupt) + * + * Call register usage: + * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC + * a1-3 Value of a1-3 when OPTEE_SMC_CALL_WITH_ARG returned + * OPTEE_SMC_RETURN_RPC in a0 + * + * Return register usage is the same as for OPTEE_SMC_*CALL_WITH_ARG above. + * + * Possible return values + * OPTEE_SMC_RETURN_UNKNOWN_FUNCTION Trusted OS does not recognize this + * function. + * OPTEE_SMC_RETURN_OK Original call completed, result + * updated in the previously supplied. + * struct optee_msg_arg + * OPTEE_SMC_RETURN_RPC Call suspended by RPC call to normal + * world. + * OPTEE_SMC_RETURN_ERESUME Resume failed, the opaque resume + * information was corrupt. + */ +#define OPTEE_SMC_FUNCID_RETURN_FROM_RPC 3 +#define OPTEE_SMC_CALL_RETURN_FROM_RPC \ + OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_RETURN_FROM_RPC) + +#define OPTEE_SMC_RETURN_RPC_PREFIX_MASK 0xFFFF0000 +#define OPTEE_SMC_RETURN_RPC_PREFIX 0xFFFF0000 +#define OPTEE_SMC_RETURN_RPC_FUNC_MASK 0x0000FFFF + +#define OPTEE_SMC_RETURN_GET_RPC_FUNC(ret) \ + ((ret) & OPTEE_SMC_RETURN_RPC_FUNC_MASK) + +#define OPTEE_SMC_RPC_VAL(func) ((func) | OPTEE_SMC_RETURN_RPC_PREFIX) + +/* + * Allocate memory for RPC parameter passing. The memory is used to hold a + * struct optee_msg_arg. + * + * "Call" register usage: + * a0 This value, OPTEE_SMC_RETURN_RPC_ALLOC + * a1 Size in bytes of required argument memory + * a2 Not used + * a3 Resume information, must be preserved + * a4-5 Not used + * a6-7 Resume information, must be preserved + * + * "Return" register usage: + * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC. + * a1 Upper 32bits of 64bit physical pointer to allocated + * memory, (a1 == 0 && a2 == 0) if size was 0 or if memory can't + * be allocated. + * a2 Lower 32bits of 64bit physical pointer to allocated + * memory, (a1 == 0 && a2 == 0) if size was 0 or if memory can't + * be allocated + * a3 Preserved + * a4 Upper 32bits of 64bit Shared memory cookie used when freeing + * the memory or doing an RPC + * a5 Lower 32bits of 64bit Shared memory cookie used when freeing + * the memory or doing an RPC + * a6-7 Preserved + */ +#define OPTEE_SMC_RPC_FUNC_ALLOC 0 +#define OPTEE_SMC_RETURN_RPC_ALLOC \ + OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_ALLOC) + +/* + * Free memory previously allocated by OPTEE_SMC_RETURN_RPC_ALLOC + * + * "Call" register usage: + * a0 This value, OPTEE_SMC_RETURN_RPC_FREE + * a1 Upper 32bits of 64bit shared memory cookie belonging to this + * argument memory + * a2 Lower 32bits of 64bit shared memory cookie belonging to this + * argument memory + * a3-7 Resume information, must be preserved + * + * "Return" register usage: + * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC. + * a1-2 Not used + * a3-7 Preserved + */ +#define OPTEE_SMC_RPC_FUNC_FREE 2 +#define OPTEE_SMC_RETURN_RPC_FREE \ + OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_FREE) + +/* + * Deliver foreign interrupt to normal world. + * + * "Call" register usage: + * a0 OPTEE_SMC_RETURN_RPC_FOREIGN_INTR + * a1-7 Resume information, must be preserved + * + * "Return" register usage: + * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC. + * a1-7 Preserved + */ +#define OPTEE_SMC_RPC_FUNC_FOREIGN_INTR 4 +#define OPTEE_SMC_RETURN_RPC_FOREIGN_INTR \ + OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_FOREIGN_INTR) + +/* + * Do an RPC request. The supplied struct optee_msg_arg tells which + * request to do and the parameters for the request. The following fields + * are used (the rest are unused): + * - cmd the Request ID + * - ret return value of the request, filled in by normal world + * - num_params number of parameters for the request + * - params the parameters + * - param_attrs attributes of the parameters + * + * "Call" register usage: + * a0 OPTEE_SMC_RETURN_RPC_CMD + * a1 Upper 32bit of a 64bit Shared memory cookie holding a + * struct optee_msg_arg, must be preserved, only the data should + * be updated + * a2 Lower 32bit of a 64bit Shared memory cookie holding a + * struct optee_msg_arg, must be preserved, only the data should + * be updated + * a3-7 Resume information, must be preserved + * + * "Return" register usage: + * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC. + * a1-2 Not used + * a3-7 Preserved + */ +#define OPTEE_SMC_RPC_FUNC_CMD 5 +#define OPTEE_SMC_RETURN_RPC_CMD \ + OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_CMD) + +/* Returned in a0 */ +#define OPTEE_SMC_RETURN_UNKNOWN_FUNCTION 0xFFFFFFFF + +/* Returned in a0 only from Trusted OS functions */ +#define OPTEE_SMC_RETURN_OK 0x0 +#define OPTEE_SMC_RETURN_ETHREAD_LIMIT 0x1 +#define OPTEE_SMC_RETURN_EBUSY 0x2 +#define OPTEE_SMC_RETURN_ERESUME 0x3 +#define OPTEE_SMC_RETURN_EBADADDR 0x4 +#define OPTEE_SMC_RETURN_EBADCMD 0x5 +#define OPTEE_SMC_RETURN_ENOMEM 0x6 +#define OPTEE_SMC_RETURN_ENOTAVAIL 0x7 +#define OPTEE_SMC_RETURN_IS_RPC(ret) __optee_smc_return_is_rpc((ret)) + +static inline bool __optee_smc_return_is_rpc(u32 ret) +{ + return ret != OPTEE_SMC_RETURN_UNKNOWN_FUNCTION && + (ret & OPTEE_SMC_RETURN_RPC_PREFIX_MASK) == + OPTEE_SMC_RETURN_RPC_PREFIX; +} + +#endif /* OPTEE_SMC_H */ +#endif /* CONFIG_BCM_KF_OPTEE_414_BACKPORTS */ diff -urN linux-4.1.52-orig/drivers/tee/optee/rpc.c linux-4.1.52/drivers/tee/optee/rpc.c --- linux-4.1.52-orig/drivers/tee/optee/rpc.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/tee/optee/rpc.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,395 @@ +#if defined(CONFIG_BCM_KF_OPTEE_414_BACKPORTS) +/* + * Copyright (c) 2015-2016, Linaro Limited + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include "optee_private.h" +#include "optee_smc.h" + +struct wq_entry { + struct list_head link; + struct completion c; + u32 key; +}; + +void optee_wait_queue_init(struct optee_wait_queue *priv) +{ + mutex_init(&priv->mu); + INIT_LIST_HEAD(&priv->db); +} + +void optee_wait_queue_exit(struct optee_wait_queue *priv) +{ + mutex_destroy(&priv->mu); +} + +static void handle_rpc_func_cmd_get_time(struct optee_msg_arg *arg) +{ + struct timespec64 ts; + + if (arg->num_params != 1) + goto bad; + if ((arg->params[0].attr & OPTEE_MSG_ATTR_TYPE_MASK) != + OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT) + goto bad; + + getnstimeofday64(&ts); + arg->params[0].u.value.a = ts.tv_sec; + arg->params[0].u.value.b = ts.tv_nsec; + + arg->ret = TEEC_SUCCESS; + return; +bad: + arg->ret = TEEC_ERROR_BAD_PARAMETERS; +} + +static struct wq_entry *wq_entry_get(struct optee_wait_queue *wq, u32 key) +{ + struct wq_entry *w; + + mutex_lock(&wq->mu); + + list_for_each_entry(w, &wq->db, link) + if (w->key == key) + goto out; + + w = kmalloc(sizeof(*w), GFP_KERNEL); + if (w) { + init_completion(&w->c); + w->key = key; + list_add_tail(&w->link, &wq->db); + } +out: + mutex_unlock(&wq->mu); + return w; +} + +static void wq_sleep(struct optee_wait_queue *wq, u32 key) +{ + struct wq_entry *w = wq_entry_get(wq, key); + + if (w) { + wait_for_completion(&w->c); + mutex_lock(&wq->mu); + list_del(&w->link); + mutex_unlock(&wq->mu); + kfree(w); + } +} + +static void wq_wakeup(struct optee_wait_queue *wq, u32 key) +{ + struct wq_entry *w = wq_entry_get(wq, key); + + if (w) + complete(&w->c); +} + +static void handle_rpc_func_cmd_wq(struct optee *optee, + struct optee_msg_arg *arg) +{ + if (arg->num_params != 1) + goto bad; + + if ((arg->params[0].attr & OPTEE_MSG_ATTR_TYPE_MASK) != + OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) + goto bad; + + switch (arg->params[0].u.value.a) { + case OPTEE_MSG_RPC_WAIT_QUEUE_SLEEP: + wq_sleep(&optee->wait_queue, arg->params[0].u.value.b); + break; + case OPTEE_MSG_RPC_WAIT_QUEUE_WAKEUP: + wq_wakeup(&optee->wait_queue, arg->params[0].u.value.b); + break; + default: + goto bad; + } + + arg->ret = TEEC_SUCCESS; + return; +bad: + arg->ret = TEEC_ERROR_BAD_PARAMETERS; +} + +static void handle_rpc_func_cmd_wait(struct optee_msg_arg *arg) +{ + u32 msec_to_wait; + + if (arg->num_params != 1) + goto bad; + + if ((arg->params[0].attr & OPTEE_MSG_ATTR_TYPE_MASK) != + OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) + goto bad; + + msec_to_wait = arg->params[0].u.value.a; + + /* Go to interruptible sleep */ + msleep_interruptible(msec_to_wait); + + arg->ret = TEEC_SUCCESS; + return; +bad: + arg->ret = TEEC_ERROR_BAD_PARAMETERS; +} + +static void handle_rpc_supp_cmd(struct tee_context *ctx, + struct optee_msg_arg *arg) +{ + struct tee_param *params; + + arg->ret_origin = TEEC_ORIGIN_COMMS; + + params = kmalloc_array(arg->num_params, sizeof(struct tee_param), + GFP_KERNEL); + if (!params) { + arg->ret = TEEC_ERROR_OUT_OF_MEMORY; + return; + } + + if (optee_from_msg_param(params, arg->num_params, arg->params)) { + arg->ret = TEEC_ERROR_BAD_PARAMETERS; + goto out; + } + + arg->ret = optee_supp_thrd_req(ctx, arg->cmd, arg->num_params, params); + + if (optee_to_msg_param(arg->params, arg->num_params, params)) + arg->ret = TEEC_ERROR_BAD_PARAMETERS; +out: + kfree(params); +} + +static struct tee_shm *cmd_alloc_suppl(struct tee_context *ctx, size_t sz) +{ + u32 ret; + struct tee_param param; + struct optee *optee = tee_get_drvdata(ctx->teedev); + struct tee_shm *shm; + + param.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT; + param.u.value.a = OPTEE_MSG_RPC_SHM_TYPE_APPL; + param.u.value.b = sz; + param.u.value.c = 0; + + ret = optee_supp_thrd_req(ctx, OPTEE_MSG_RPC_CMD_SHM_ALLOC, 1, ¶m); + if (ret) + return ERR_PTR(-ENOMEM); + + mutex_lock(&optee->supp.ctx_mutex); + /* Increases count as secure world doesn't have a reference */ + shm = tee_shm_get_from_id(optee->supp.ctx, param.u.value.c); + mutex_unlock(&optee->supp.ctx_mutex); + return shm; +} + +static void handle_rpc_func_cmd_shm_alloc(struct tee_context *ctx, + struct optee_msg_arg *arg) +{ + phys_addr_t pa; + struct tee_shm *shm; + size_t sz; + size_t n; + + arg->ret_origin = TEEC_ORIGIN_COMMS; + + if (!arg->num_params || + arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) { + arg->ret = TEEC_ERROR_BAD_PARAMETERS; + return; + } + + for (n = 1; n < arg->num_params; n++) { + if (arg->params[n].attr != OPTEE_MSG_ATTR_TYPE_NONE) { + arg->ret = TEEC_ERROR_BAD_PARAMETERS; + return; + } + } + + sz = arg->params[0].u.value.b; + switch (arg->params[0].u.value.a) { + case OPTEE_MSG_RPC_SHM_TYPE_APPL: + shm = cmd_alloc_suppl(ctx, sz); + break; + case OPTEE_MSG_RPC_SHM_TYPE_KERNEL: + shm = tee_shm_alloc(ctx, sz, TEE_SHM_MAPPED); + break; + default: + arg->ret = TEEC_ERROR_BAD_PARAMETERS; + return; + } + + if (IS_ERR(shm)) { + arg->ret = TEEC_ERROR_OUT_OF_MEMORY; + return; + } + + if (tee_shm_get_pa(shm, 0, &pa)) { + arg->ret = TEEC_ERROR_BAD_PARAMETERS; + goto bad; + } + + arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT; + arg->params[0].u.tmem.buf_ptr = pa; + arg->params[0].u.tmem.size = sz; + arg->params[0].u.tmem.shm_ref = (unsigned long)shm; + arg->ret = TEEC_SUCCESS; + return; +bad: + tee_shm_free(shm); +} + +static void cmd_free_suppl(struct tee_context *ctx, struct tee_shm *shm) +{ + struct tee_param param; + + param.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT; + param.u.value.a = OPTEE_MSG_RPC_SHM_TYPE_APPL; + param.u.value.b = tee_shm_get_id(shm); + param.u.value.c = 0; + + /* + * Match the tee_shm_get_from_id() in cmd_alloc_suppl() as secure + * world has released its reference. + * + * It's better to do this before sending the request to supplicant + * as we'd like to let the process doing the initial allocation to + * do release the last reference too in order to avoid stacking + * many pending fput() on the client process. This could otherwise + * happen if secure world does many allocate and free in a single + * invoke. + */ + tee_shm_put(shm); + + optee_supp_thrd_req(ctx, OPTEE_MSG_RPC_CMD_SHM_FREE, 1, ¶m); +} + +static void handle_rpc_func_cmd_shm_free(struct tee_context *ctx, + struct optee_msg_arg *arg) +{ + struct tee_shm *shm; + + arg->ret_origin = TEEC_ORIGIN_COMMS; + + if (arg->num_params != 1 || + arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) { + arg->ret = TEEC_ERROR_BAD_PARAMETERS; + return; + } + + shm = (struct tee_shm *)(unsigned long)arg->params[0].u.value.b; + switch (arg->params[0].u.value.a) { + case OPTEE_MSG_RPC_SHM_TYPE_APPL: + cmd_free_suppl(ctx, shm); + break; + case OPTEE_MSG_RPC_SHM_TYPE_KERNEL: + tee_shm_free(shm); + break; + default: + arg->ret = TEEC_ERROR_BAD_PARAMETERS; + } + arg->ret = TEEC_SUCCESS; +} + +static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee, + struct tee_shm *shm) +{ + struct optee_msg_arg *arg; + + arg = tee_shm_get_va(shm, 0); + if (IS_ERR(arg)) { + pr_err("%s: tee_shm_get_va %p failed\n", __func__, shm); + return; + } + + switch (arg->cmd) { + case OPTEE_MSG_RPC_CMD_GET_TIME: + handle_rpc_func_cmd_get_time(arg); + break; + case OPTEE_MSG_RPC_CMD_WAIT_QUEUE: + handle_rpc_func_cmd_wq(optee, arg); + break; + case OPTEE_MSG_RPC_CMD_SUSPEND: + handle_rpc_func_cmd_wait(arg); + break; + case OPTEE_MSG_RPC_CMD_SHM_ALLOC: + handle_rpc_func_cmd_shm_alloc(ctx, arg); + break; + case OPTEE_MSG_RPC_CMD_SHM_FREE: + handle_rpc_func_cmd_shm_free(ctx, arg); + break; + default: + handle_rpc_supp_cmd(ctx, arg); + } +} + +/** + * optee_handle_rpc() - handle RPC from secure world + * @ctx: context doing the RPC + * @param: value of registers for the RPC + * + * Result of RPC is written back into @param. + */ +void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param) +{ + struct tee_device *teedev = ctx->teedev; + struct optee *optee = tee_get_drvdata(teedev); + struct tee_shm *shm; + phys_addr_t pa; + + switch (OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0)) { + case OPTEE_SMC_RPC_FUNC_ALLOC: + shm = tee_shm_alloc(ctx, param->a1, TEE_SHM_MAPPED); + if (!IS_ERR(shm) && !tee_shm_get_pa(shm, 0, &pa)) { + reg_pair_from_64(¶m->a1, ¶m->a2, pa); + reg_pair_from_64(¶m->a4, ¶m->a5, + (unsigned long)shm); + } else { + param->a1 = 0; + param->a2 = 0; + param->a4 = 0; + param->a5 = 0; + } + break; + case OPTEE_SMC_RPC_FUNC_FREE: + shm = reg_pair_to_ptr(param->a1, param->a2); + tee_shm_free(shm); + break; + case OPTEE_SMC_RPC_FUNC_FOREIGN_INTR: + /* + * A foreign interrupt was raised while secure world was + * executing, since they are handled in Linux a dummy RPC is + * performed to let Linux take the interrupt through the normal + * vector. + */ + break; + case OPTEE_SMC_RPC_FUNC_CMD: + shm = reg_pair_to_ptr(param->a1, param->a2); + handle_rpc_func_cmd(ctx, optee, shm); + break; + default: + pr_warn("Unknown RPC func 0x%x\n", + (u32)OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0)); + break; + } + + param->a0 = OPTEE_SMC_CALL_RETURN_FROM_RPC; +} +#endif /* CONFIG_BCM_KF_OPTEE_414_BACKPORTS */ diff -urN linux-4.1.52-orig/drivers/tee/optee/supp.c linux-4.1.52/drivers/tee/optee/supp.c --- linux-4.1.52-orig/drivers/tee/optee/supp.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/tee/optee/supp.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,275 @@ +#if defined(CONFIG_BCM_KF_OPTEE_414_BACKPORTS) +/* + * Copyright (c) 2015, Linaro Limited + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ +#include +#include +#include +#include "optee_private.h" + +void optee_supp_init(struct optee_supp *supp) +{ + memset(supp, 0, sizeof(*supp)); + mutex_init(&supp->ctx_mutex); + mutex_init(&supp->thrd_mutex); + mutex_init(&supp->supp_mutex); + init_completion(&supp->data_to_supp); + init_completion(&supp->data_from_supp); +} + +void optee_supp_uninit(struct optee_supp *supp) +{ + mutex_destroy(&supp->ctx_mutex); + mutex_destroy(&supp->thrd_mutex); + mutex_destroy(&supp->supp_mutex); +} + +/** + * optee_supp_thrd_req() - request service from supplicant + * @ctx: context doing the request + * @func: function requested + * @num_params: number of elements in @param array + * @param: parameters for function + * + * Returns result of operation to be passed to secure world + */ +u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params, + struct tee_param *param) +{ + bool interruptable; + struct optee *optee = tee_get_drvdata(ctx->teedev); + struct optee_supp *supp = &optee->supp; + u32 ret; + + /* + * Other threads blocks here until we've copied our answer from + * supplicant. + */ + while (mutex_lock_interruptible(&supp->thrd_mutex)) { + /* See comment below on when the RPC can be interrupted. */ + mutex_lock(&supp->ctx_mutex); + interruptable = !supp->ctx; + mutex_unlock(&supp->ctx_mutex); + if (interruptable) + return TEEC_ERROR_COMMUNICATION; + } + + /* + * We have exclusive access now since the supplicant at this + * point is either doing a + * wait_for_completion_interruptible(&supp->data_to_supp) or is in + * userspace still about to do the ioctl() to enter + * optee_supp_recv() below. + */ + + supp->func = func; + supp->num_params = num_params; + supp->param = param; + supp->req_posted = true; + + /* Let supplicant get the data */ + complete(&supp->data_to_supp); + + /* + * Wait for supplicant to process and return result, once we've + * returned from wait_for_completion(data_from_supp) we have + * exclusive access again. + */ + while (wait_for_completion_interruptible(&supp->data_from_supp)) { + mutex_lock(&supp->ctx_mutex); + interruptable = !supp->ctx; + if (interruptable) { + /* + * There's no supplicant available and since the + * supp->ctx_mutex currently is held none can + * become available until the mutex released + * again. + * + * Interrupting an RPC to supplicant is only + * allowed as a way of slightly improving the user + * experience in case the supplicant hasn't been + * started yet. During normal operation the supplicant + * will serve all requests in a timely manner and + * interrupting then wouldn't make sense. + */ + supp->ret = TEEC_ERROR_COMMUNICATION; + init_completion(&supp->data_to_supp); + } + mutex_unlock(&supp->ctx_mutex); + if (interruptable) + break; + } + + ret = supp->ret; + supp->param = NULL; + supp->req_posted = false; + + /* We're done, let someone else talk to the supplicant now. */ + mutex_unlock(&supp->thrd_mutex); + + return ret; +} + +/** + * optee_supp_recv() - receive request for supplicant + * @ctx: context receiving the request + * @func: requested function in supplicant + * @num_params: number of elements allocated in @param, updated with number + * used elements + * @param: space for parameters for @func + * + * Returns 0 on success or <0 on failure + */ +int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, + struct tee_param *param) +{ + struct tee_device *teedev = ctx->teedev; + struct optee *optee = tee_get_drvdata(teedev); + struct optee_supp *supp = &optee->supp; + int rc; + + /* + * In case two threads in one supplicant is calling this function + * simultaneously we need to protect the data with a mutex which + * we'll release before returning. + */ + mutex_lock(&supp->supp_mutex); + + if (supp->supp_next_send) { + /* + * optee_supp_recv() has been called again without + * a optee_supp_send() in between. Supplicant has + * probably been restarted before it was able to + * write back last result. Abort last request and + * wait for a new. + */ + if (supp->req_posted) { + supp->ret = TEEC_ERROR_COMMUNICATION; + supp->supp_next_send = false; + complete(&supp->data_from_supp); + } + } + + /* + * This is where supplicant will be hanging most of the + * time, let's make this interruptable so we can easily + * restart supplicant if needed. + */ + if (wait_for_completion_interruptible(&supp->data_to_supp)) { + rc = -ERESTARTSYS; + goto out; + } + + /* We have exlusive access to the data */ + + if (*num_params < supp->num_params) { + /* + * Not enough room for parameters, tell supplicant + * it failed and abort last request. + */ + supp->ret = TEEC_ERROR_COMMUNICATION; + rc = -EINVAL; + complete(&supp->data_from_supp); + goto out; + } + + *func = supp->func; + *num_params = supp->num_params; + memcpy(param, supp->param, + sizeof(struct tee_param) * supp->num_params); + + /* Allow optee_supp_send() below to do its work */ + supp->supp_next_send = true; + + rc = 0; +out: + mutex_unlock(&supp->supp_mutex); + return rc; +} + +/** + * optee_supp_send() - send result of request from supplicant + * @ctx: context sending result + * @ret: return value of request + * @num_params: number of parameters returned + * @param: returned parameters + * + * Returns 0 on success or <0 on failure. + */ +int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params, + struct tee_param *param) +{ + struct tee_device *teedev = ctx->teedev; + struct optee *optee = tee_get_drvdata(teedev); + struct optee_supp *supp = &optee->supp; + size_t n; + int rc = 0; + + /* + * We still have exclusive access to the data since that's how we + * left it when returning from optee_supp_read(). + */ + + /* See comment on mutex in optee_supp_read() above */ + mutex_lock(&supp->supp_mutex); + + if (!supp->supp_next_send) { + /* + * Something strange is going on, supplicant shouldn't + * enter optee_supp_send() in this state + */ + rc = -ENOENT; + goto out; + } + + if (num_params != supp->num_params) { + /* + * Something is wrong, let supplicant restart. Next call to + * optee_supp_recv() will give an error to the requesting + * thread and release it. + */ + rc = -EINVAL; + goto out; + } + + /* Update out and in/out parameters */ + for (n = 0; n < num_params; n++) { + struct tee_param *p = supp->param + n; + + switch (p->attr) { + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: + p->u.value.a = param[n].u.value.a; + p->u.value.b = param[n].u.value.b; + p->u.value.c = param[n].u.value.c; + break; + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: + p->u.memref.size = param[n].u.memref.size; + break; + default: + break; + } + } + supp->ret = ret; + + /* Allow optee_supp_recv() above to do its work */ + supp->supp_next_send = false; + + /* Let the requesting thread continue */ + complete(&supp->data_from_supp); +out: + mutex_unlock(&supp->supp_mutex); + return rc; +} +#endif /* CONFIG_BCM_KF_OPTEE_414_BACKPORTS */ diff -urN linux-4.1.52-orig/drivers/tee/tee_core.c linux-4.1.52/drivers/tee/tee_core.c --- linux-4.1.52-orig/drivers/tee/tee_core.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/tee/tee_core.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,900 @@ +#if defined(CONFIG_BCM_KF_OPTEE_414_BACKPORTS) +/* + * Copyright (c) 2015-2016, Linaro Limited + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "tee_private.h" + +#define TEE_NUM_DEVICES 32 + +#define TEE_IOCTL_PARAM_SIZE(x) (sizeof(struct tee_param) * (x)) + +/* + * Unprivileged devices in the lower half range and privileged devices in + * the upper half range. + */ +static DECLARE_BITMAP(dev_mask, TEE_NUM_DEVICES); +static DEFINE_SPINLOCK(driver_lock); + +static struct class *tee_class; +static dev_t tee_devt; + +static int tee_open(struct inode *inode, struct file *filp) +{ + int rc; + struct tee_device *teedev; + struct tee_context *ctx; + + teedev = container_of(inode->i_cdev, struct tee_device, cdev); + if (!tee_device_get(teedev)) + return -EINVAL; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + rc = -ENOMEM; + goto err; + } + + ctx->teedev = teedev; + INIT_LIST_HEAD(&ctx->list_shm); + filp->private_data = ctx; + rc = teedev->desc->ops->open(ctx); + if (rc) + goto err; + + return 0; +err: + kfree(ctx); + tee_device_put(teedev); + return rc; +} + +static int tee_release(struct inode *inode, struct file *filp) +{ + struct tee_context *ctx = filp->private_data; + struct tee_device *teedev = ctx->teedev; + struct tee_shm *shm; + + ctx->teedev->desc->ops->release(ctx); + mutex_lock(&ctx->teedev->mutex); + list_for_each_entry(shm, &ctx->list_shm, link) + shm->ctx = NULL; + mutex_unlock(&ctx->teedev->mutex); + kfree(ctx); + tee_device_put(teedev); + return 0; +} + +static int tee_ioctl_version(struct tee_context *ctx, + struct tee_ioctl_version_data __user *uvers) +{ + struct tee_ioctl_version_data vers; + + ctx->teedev->desc->ops->get_version(ctx->teedev, &vers); + + if (ctx->teedev->desc->flags & TEE_DESC_PRIVILEGED) + vers.gen_caps |= TEE_GEN_CAP_PRIVILEGED; + + if (copy_to_user(uvers, &vers, sizeof(vers))) + return -EFAULT; + + return 0; +} + +static int tee_ioctl_shm_alloc(struct tee_context *ctx, + struct tee_ioctl_shm_alloc_data __user *udata) +{ + long ret; + struct tee_ioctl_shm_alloc_data data; + struct tee_shm *shm; + + if (copy_from_user(&data, udata, sizeof(data))) + return -EFAULT; + + /* Currently no input flags are supported */ + if (data.flags) + return -EINVAL; + + data.id = -1; + + shm = tee_shm_alloc(ctx, data.size, TEE_SHM_MAPPED | TEE_SHM_DMA_BUF); + if (IS_ERR(shm)) + return PTR_ERR(shm); + + data.id = shm->id; + data.flags = shm->flags; + data.size = shm->size; + + if (copy_to_user(udata, &data, sizeof(data))) + ret = -EFAULT; + else + ret = tee_shm_get_fd(shm); + + /* + * When user space closes the file descriptor the shared memory + * should be freed or if tee_shm_get_fd() failed then it will + * be freed immediately. + */ + tee_shm_put(shm); + return ret; +} + +static int params_from_user(struct tee_context *ctx, struct tee_param *params, + size_t num_params, + struct tee_ioctl_param __user *uparams) +{ + size_t n; + + for (n = 0; n < num_params; n++) { + struct tee_shm *shm; + struct tee_ioctl_param ip; + + if (copy_from_user(&ip, uparams + n, sizeof(ip))) + return -EFAULT; + + /* All unused attribute bits has to be zero */ + if (ip.attr & ~TEE_IOCTL_PARAM_ATTR_TYPE_MASK) + return -EINVAL; + + params[n].attr = ip.attr; + switch (ip.attr) { + case TEE_IOCTL_PARAM_ATTR_TYPE_NONE: + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: + break; + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: + params[n].u.value.a = ip.a; + params[n].u.value.b = ip.b; + params[n].u.value.c = ip.c; + break; + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: + /* + * If we fail to get a pointer to a shared memory + * object (and increase the ref count) from an + * identifier we return an error. All pointers that + * has been added in params have an increased ref + * count. It's the callers responibility to do + * tee_shm_put() on all resolved pointers. + */ + shm = tee_shm_get_from_id(ctx, ip.c); + if (IS_ERR(shm)) + return PTR_ERR(shm); + + params[n].u.memref.shm_offs = ip.a; + params[n].u.memref.size = ip.b; + params[n].u.memref.shm = shm; + break; + default: + /* Unknown attribute */ + return -EINVAL; + } + } + return 0; +} + +static int params_to_user(struct tee_ioctl_param __user *uparams, + size_t num_params, struct tee_param *params) +{ + size_t n; + + for (n = 0; n < num_params; n++) { + struct tee_ioctl_param __user *up = uparams + n; + struct tee_param *p = params + n; + + switch (p->attr) { + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: + if (put_user(p->u.value.a, &up->a) || + put_user(p->u.value.b, &up->b) || + put_user(p->u.value.c, &up->c)) + return -EFAULT; + break; + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: + if (put_user((u64)p->u.memref.size, &up->b)) + return -EFAULT; + default: + break; + } + } + return 0; +} + +static bool param_is_memref(struct tee_param *param) +{ + switch (param->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) { + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: + return true; + default: + return false; + } +} + +static int tee_ioctl_open_session(struct tee_context *ctx, + struct tee_ioctl_buf_data __user *ubuf) +{ + int rc; + size_t n; + struct tee_ioctl_buf_data buf; + struct tee_ioctl_open_session_arg __user *uarg; + struct tee_ioctl_open_session_arg arg; + struct tee_ioctl_param __user *uparams = NULL; + struct tee_param *params = NULL; + bool have_session = false; + + if (!ctx->teedev->desc->ops->open_session) + return -EINVAL; + + if (copy_from_user(&buf, ubuf, sizeof(buf))) + return -EFAULT; + + if (buf.buf_len > TEE_MAX_ARG_SIZE || + buf.buf_len < sizeof(struct tee_ioctl_open_session_arg)) + return -EINVAL; + + uarg = u64_to_user_ptr(buf.buf_ptr); + if (copy_from_user(&arg, uarg, sizeof(arg))) + return -EFAULT; + + if (sizeof(arg) + TEE_IOCTL_PARAM_SIZE(arg.num_params) != buf.buf_len) + return -EINVAL; + + if (arg.num_params) { + params = kcalloc(arg.num_params, sizeof(struct tee_param), + GFP_KERNEL); + if (!params) + return -ENOMEM; + uparams = uarg->params; + rc = params_from_user(ctx, params, arg.num_params, uparams); + if (rc) + goto out; + } + + rc = ctx->teedev->desc->ops->open_session(ctx, &arg, params); + if (rc) + goto out; + have_session = true; + + if (put_user(arg.session, &uarg->session) || + put_user(arg.ret, &uarg->ret) || + put_user(arg.ret_origin, &uarg->ret_origin)) { + rc = -EFAULT; + goto out; + } + rc = params_to_user(uparams, arg.num_params, params); +out: + /* + * If we've succeeded to open the session but failed to communicate + * it back to user space, close the session again to avoid leakage. + */ + if (rc && have_session && ctx->teedev->desc->ops->close_session) + ctx->teedev->desc->ops->close_session(ctx, arg.session); + + if (params) { + /* Decrease ref count for all valid shared memory pointers */ + for (n = 0; n < arg.num_params; n++) + if (param_is_memref(params + n) && + params[n].u.memref.shm) + tee_shm_put(params[n].u.memref.shm); + kfree(params); + } + + return rc; +} + +static int tee_ioctl_invoke(struct tee_context *ctx, + struct tee_ioctl_buf_data __user *ubuf) +{ + int rc; + size_t n; + struct tee_ioctl_buf_data buf; + struct tee_ioctl_invoke_arg __user *uarg; + struct tee_ioctl_invoke_arg arg; + struct tee_ioctl_param __user *uparams = NULL; + struct tee_param *params = NULL; + + if (!ctx->teedev->desc->ops->invoke_func) + return -EINVAL; + + if (copy_from_user(&buf, ubuf, sizeof(buf))) + return -EFAULT; + + if (buf.buf_len > TEE_MAX_ARG_SIZE || + buf.buf_len < sizeof(struct tee_ioctl_invoke_arg)) + return -EINVAL; + + uarg = u64_to_user_ptr(buf.buf_ptr); + if (copy_from_user(&arg, uarg, sizeof(arg))) + return -EFAULT; + + if (sizeof(arg) + TEE_IOCTL_PARAM_SIZE(arg.num_params) != buf.buf_len) + return -EINVAL; + + if (arg.num_params) { + params = kcalloc(arg.num_params, sizeof(struct tee_param), + GFP_KERNEL); + if (!params) + return -ENOMEM; + uparams = uarg->params; + rc = params_from_user(ctx, params, arg.num_params, uparams); + if (rc) + goto out; + } + + rc = ctx->teedev->desc->ops->invoke_func(ctx, &arg, params); + if (rc) + goto out; + + if (put_user(arg.ret, &uarg->ret) || + put_user(arg.ret_origin, &uarg->ret_origin)) { + rc = -EFAULT; + goto out; + } + rc = params_to_user(uparams, arg.num_params, params); +out: + if (params) { + /* Decrease ref count for all valid shared memory pointers */ + for (n = 0; n < arg.num_params; n++) + if (param_is_memref(params + n) && + params[n].u.memref.shm) + tee_shm_put(params[n].u.memref.shm); + kfree(params); + } + return rc; +} + +static int tee_ioctl_cancel(struct tee_context *ctx, + struct tee_ioctl_cancel_arg __user *uarg) +{ + struct tee_ioctl_cancel_arg arg; + + if (!ctx->teedev->desc->ops->cancel_req) + return -EINVAL; + + if (copy_from_user(&arg, uarg, sizeof(arg))) + return -EFAULT; + + return ctx->teedev->desc->ops->cancel_req(ctx, arg.cancel_id, + arg.session); +} + +static int +tee_ioctl_close_session(struct tee_context *ctx, + struct tee_ioctl_close_session_arg __user *uarg) +{ + struct tee_ioctl_close_session_arg arg; + + if (!ctx->teedev->desc->ops->close_session) + return -EINVAL; + + if (copy_from_user(&arg, uarg, sizeof(arg))) + return -EFAULT; + + return ctx->teedev->desc->ops->close_session(ctx, arg.session); +} + +static int params_to_supp(struct tee_context *ctx, + struct tee_ioctl_param __user *uparams, + size_t num_params, struct tee_param *params) +{ + size_t n; + + for (n = 0; n < num_params; n++) { + struct tee_ioctl_param ip; + struct tee_param *p = params + n; + + ip.attr = p->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK; + switch (p->attr) { + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: + ip.a = p->u.value.a; + ip.b = p->u.value.b; + ip.c = p->u.value.c; + break; + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: + ip.b = p->u.memref.size; + if (!p->u.memref.shm) { + ip.a = 0; + ip.c = (u64)-1; /* invalid shm id */ + break; + } + ip.a = p->u.memref.shm_offs; + ip.c = p->u.memref.shm->id; + break; + default: + ip.a = 0; + ip.b = 0; + ip.c = 0; + break; + } + + if (copy_to_user(uparams + n, &ip, sizeof(ip))) + return -EFAULT; + } + + return 0; +} + +static int tee_ioctl_supp_recv(struct tee_context *ctx, + struct tee_ioctl_buf_data __user *ubuf) +{ + int rc; + struct tee_ioctl_buf_data buf; + struct tee_iocl_supp_recv_arg __user *uarg; + struct tee_param *params; + u32 num_params; + u32 func; + + if (!ctx->teedev->desc->ops->supp_recv) + return -EINVAL; + + if (copy_from_user(&buf, ubuf, sizeof(buf))) + return -EFAULT; + + if (buf.buf_len > TEE_MAX_ARG_SIZE || + buf.buf_len < sizeof(struct tee_iocl_supp_recv_arg)) + return -EINVAL; + + uarg = u64_to_user_ptr(buf.buf_ptr); + if (get_user(num_params, &uarg->num_params)) + return -EFAULT; + + if (sizeof(*uarg) + TEE_IOCTL_PARAM_SIZE(num_params) != buf.buf_len) + return -EINVAL; + + params = kcalloc(num_params, sizeof(struct tee_param), GFP_KERNEL); + if (!params) + return -ENOMEM; + + rc = ctx->teedev->desc->ops->supp_recv(ctx, &func, &num_params, params); + if (rc) + goto out; + + if (put_user(func, &uarg->func) || + put_user(num_params, &uarg->num_params)) { + rc = -EFAULT; + goto out; + } + + rc = params_to_supp(ctx, uarg->params, num_params, params); +out: + kfree(params); + return rc; +} + +static int params_from_supp(struct tee_param *params, size_t num_params, + struct tee_ioctl_param __user *uparams) +{ + size_t n; + + for (n = 0; n < num_params; n++) { + struct tee_param *p = params + n; + struct tee_ioctl_param ip; + + if (copy_from_user(&ip, uparams + n, sizeof(ip))) + return -EFAULT; + + /* All unused attribute bits has to be zero */ + if (ip.attr & ~TEE_IOCTL_PARAM_ATTR_TYPE_MASK) + return -EINVAL; + + p->attr = ip.attr; + switch (ip.attr) { + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: + /* Only out and in/out values can be updated */ + p->u.value.a = ip.a; + p->u.value.b = ip.b; + p->u.value.c = ip.c; + break; + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: + /* + * Only the size of the memref can be updated. + * Since we don't have access to the original + * parameters here, only store the supplied size. + * The driver will copy the updated size into the + * original parameters. + */ + p->u.memref.shm = NULL; + p->u.memref.shm_offs = 0; + p->u.memref.size = ip.b; + break; + default: + memset(&p->u, 0, sizeof(p->u)); + break; + } + } + return 0; +} + +static int tee_ioctl_supp_send(struct tee_context *ctx, + struct tee_ioctl_buf_data __user *ubuf) +{ + long rc; + struct tee_ioctl_buf_data buf; + struct tee_iocl_supp_send_arg __user *uarg; + struct tee_param *params; + u32 num_params; + u32 ret; + + /* Not valid for this driver */ + if (!ctx->teedev->desc->ops->supp_send) + return -EINVAL; + + if (copy_from_user(&buf, ubuf, sizeof(buf))) + return -EFAULT; + + if (buf.buf_len > TEE_MAX_ARG_SIZE || + buf.buf_len < sizeof(struct tee_iocl_supp_send_arg)) + return -EINVAL; + + uarg = u64_to_user_ptr(buf.buf_ptr); + if (get_user(ret, &uarg->ret) || + get_user(num_params, &uarg->num_params)) + return -EFAULT; + + if (sizeof(*uarg) + TEE_IOCTL_PARAM_SIZE(num_params) > buf.buf_len) + return -EINVAL; + + params = kcalloc(num_params, sizeof(struct tee_param), GFP_KERNEL); + if (!params) + return -ENOMEM; + + rc = params_from_supp(params, num_params, uarg->params); + if (rc) + goto out; + + rc = ctx->teedev->desc->ops->supp_send(ctx, ret, num_params, params); +out: + kfree(params); + return rc; +} + +static long tee_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct tee_context *ctx = filp->private_data; + void __user *uarg = (void __user *)arg; + + switch (cmd) { + case TEE_IOC_VERSION: + return tee_ioctl_version(ctx, uarg); + case TEE_IOC_SHM_ALLOC: + return tee_ioctl_shm_alloc(ctx, uarg); + case TEE_IOC_OPEN_SESSION: + return tee_ioctl_open_session(ctx, uarg); + case TEE_IOC_INVOKE: + return tee_ioctl_invoke(ctx, uarg); + case TEE_IOC_CANCEL: + return tee_ioctl_cancel(ctx, uarg); + case TEE_IOC_CLOSE_SESSION: + return tee_ioctl_close_session(ctx, uarg); + case TEE_IOC_SUPPL_RECV: + return tee_ioctl_supp_recv(ctx, uarg); + case TEE_IOC_SUPPL_SEND: + return tee_ioctl_supp_send(ctx, uarg); + default: + return -EINVAL; + } +} + +static const struct file_operations tee_fops = { + .owner = THIS_MODULE, + .open = tee_open, + .release = tee_release, + .unlocked_ioctl = tee_ioctl, + .compat_ioctl = tee_ioctl, +}; + +static void tee_release_device(struct device *dev) +{ + struct tee_device *teedev = container_of(dev, struct tee_device, dev); + + spin_lock(&driver_lock); + clear_bit(teedev->id, dev_mask); + spin_unlock(&driver_lock); + mutex_destroy(&teedev->mutex); + idr_destroy(&teedev->idr); + kfree(teedev); +} + +/** + * tee_device_alloc() - Allocate a new struct tee_device instance + * @teedesc: Descriptor for this driver + * @dev: Parent device for this device + * @pool: Shared memory pool, NULL if not used + * @driver_data: Private driver data for this device + * + * Allocates a new struct tee_device instance. The device is + * removed by tee_device_unregister(). + * + * @returns a pointer to a 'struct tee_device' or an ERR_PTR on failure + */ +struct tee_device *tee_device_alloc(const struct tee_desc *teedesc, + struct device *dev, + struct tee_shm_pool *pool, + void *driver_data) +{ + struct tee_device *teedev; + void *ret; + int rc; + int offs = 0; + + if (!teedesc || !teedesc->name || !teedesc->ops || + !teedesc->ops->get_version || !teedesc->ops->open || + !teedesc->ops->release || !pool) + return ERR_PTR(-EINVAL); + + teedev = kzalloc(sizeof(*teedev), GFP_KERNEL); + if (!teedev) { + ret = ERR_PTR(-ENOMEM); + goto err; + } + + if (teedesc->flags & TEE_DESC_PRIVILEGED) + offs = TEE_NUM_DEVICES / 2; + + spin_lock(&driver_lock); + teedev->id = find_next_zero_bit(dev_mask, TEE_NUM_DEVICES, offs); + if (teedev->id < TEE_NUM_DEVICES) + set_bit(teedev->id, dev_mask); + spin_unlock(&driver_lock); + + if (teedev->id >= TEE_NUM_DEVICES) { + ret = ERR_PTR(-ENOMEM); + goto err; + } + + snprintf(teedev->name, sizeof(teedev->name), "tee%s%d", + teedesc->flags & TEE_DESC_PRIVILEGED ? "priv" : "", + teedev->id - offs); + + teedev->dev.class = tee_class; + teedev->dev.release = tee_release_device; + teedev->dev.parent = dev; + + teedev->dev.devt = MKDEV(MAJOR(tee_devt), teedev->id); + + rc = dev_set_name(&teedev->dev, "%s", teedev->name); + if (rc) { + ret = ERR_PTR(rc); + goto err_devt; + } + + cdev_init(&teedev->cdev, &tee_fops); + teedev->cdev.owner = teedesc->owner; + teedev->cdev.kobj.parent = &teedev->dev.kobj; + + dev_set_drvdata(&teedev->dev, driver_data); + device_initialize(&teedev->dev); + + /* 1 as tee_device_unregister() does one final tee_device_put() */ + teedev->num_users = 1; + init_completion(&teedev->c_no_users); + mutex_init(&teedev->mutex); + idr_init(&teedev->idr); + + teedev->desc = teedesc; + teedev->pool = pool; + + return teedev; +err_devt: + unregister_chrdev_region(teedev->dev.devt, 1); +err: + pr_err("could not register %s driver\n", + teedesc->flags & TEE_DESC_PRIVILEGED ? "privileged" : "client"); + if (teedev && teedev->id < TEE_NUM_DEVICES) { + spin_lock(&driver_lock); + clear_bit(teedev->id, dev_mask); + spin_unlock(&driver_lock); + } + kfree(teedev); + return ret; +} +EXPORT_SYMBOL_GPL(tee_device_alloc); + +static ssize_t implementation_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tee_device *teedev = container_of(dev, struct tee_device, dev); + struct tee_ioctl_version_data vers; + + teedev->desc->ops->get_version(teedev, &vers); + return scnprintf(buf, PAGE_SIZE, "%d\n", vers.impl_id); +} +static DEVICE_ATTR_RO(implementation_id); + +static struct attribute *tee_dev_attrs[] = { + &dev_attr_implementation_id.attr, + NULL +}; + +static const struct attribute_group tee_dev_group = { + .attrs = tee_dev_attrs, +}; + +/** + * tee_device_register() - Registers a TEE device + * @teedev: Device to register + * + * tee_device_unregister() need to be called to remove the @teedev if + * this function fails. + * + * @returns < 0 on failure + */ +int tee_device_register(struct tee_device *teedev) +{ + int rc; + + if (teedev->flags & TEE_DEVICE_FLAG_REGISTERED) { + dev_err(&teedev->dev, "attempt to register twice\n"); + return -EINVAL; + } + + rc = cdev_add(&teedev->cdev, teedev->dev.devt, 1); + if (rc) { + dev_err(&teedev->dev, + "unable to cdev_add() %s, major %d, minor %d, err=%d\n", + teedev->name, MAJOR(teedev->dev.devt), + MINOR(teedev->dev.devt), rc); + return rc; + } + + rc = device_add(&teedev->dev); + if (rc) { + dev_err(&teedev->dev, + "unable to device_add() %s, major %d, minor %d, err=%d\n", + teedev->name, MAJOR(teedev->dev.devt), + MINOR(teedev->dev.devt), rc); + goto err_device_add; + } + + rc = sysfs_create_group(&teedev->dev.kobj, &tee_dev_group); + if (rc) { + dev_err(&teedev->dev, + "failed to create sysfs attributes, err=%d\n", rc); + goto err_sysfs_create_group; + } + + teedev->flags |= TEE_DEVICE_FLAG_REGISTERED; + return 0; + +err_sysfs_create_group: + device_del(&teedev->dev); +err_device_add: + cdev_del(&teedev->cdev); + return rc; +} +EXPORT_SYMBOL_GPL(tee_device_register); + +void tee_device_put(struct tee_device *teedev) +{ + mutex_lock(&teedev->mutex); + /* Shouldn't put in this state */ + if (!WARN_ON(!teedev->desc)) { + teedev->num_users--; + if (!teedev->num_users) { + teedev->desc = NULL; + complete(&teedev->c_no_users); + } + } + mutex_unlock(&teedev->mutex); +} + +bool tee_device_get(struct tee_device *teedev) +{ + mutex_lock(&teedev->mutex); + if (!teedev->desc) { + mutex_unlock(&teedev->mutex); + return false; + } + teedev->num_users++; + mutex_unlock(&teedev->mutex); + return true; +} + +/** + * tee_device_unregister() - Removes a TEE device + * @teedev: Device to unregister + * + * This function should be called to remove the @teedev even if + * tee_device_register() hasn't been called yet. Does nothing if + * @teedev is NULL. + */ +void tee_device_unregister(struct tee_device *teedev) +{ + if (!teedev) + return; + + if (teedev->flags & TEE_DEVICE_FLAG_REGISTERED) { + sysfs_remove_group(&teedev->dev.kobj, &tee_dev_group); + cdev_del(&teedev->cdev); + device_del(&teedev->dev); + } + + tee_device_put(teedev); + wait_for_completion(&teedev->c_no_users); + + /* + * No need to take a mutex any longer now since teedev->desc was + * set to NULL before teedev->c_no_users was completed. + */ + + teedev->pool = NULL; + + put_device(&teedev->dev); +} +EXPORT_SYMBOL_GPL(tee_device_unregister); + +/** + * tee_get_drvdata() - Return driver_data pointer + * @teedev: Device containing the driver_data pointer + * @returns the driver_data pointer supplied to tee_register(). + */ +void *tee_get_drvdata(struct tee_device *teedev) +{ + return dev_get_drvdata(&teedev->dev); +} +EXPORT_SYMBOL_GPL(tee_get_drvdata); + +static int __init tee_init(void) +{ + int rc; + + tee_class = class_create(THIS_MODULE, "tee"); + if (IS_ERR(tee_class)) { + pr_err("couldn't create class\n"); + return PTR_ERR(tee_class); + } + + rc = alloc_chrdev_region(&tee_devt, 0, TEE_NUM_DEVICES, "tee"); + if (rc) { + pr_err("failed to allocate char dev region\n"); + class_destroy(tee_class); + tee_class = NULL; + } + + return rc; +} + +static void __exit tee_exit(void) +{ + class_destroy(tee_class); + tee_class = NULL; + unregister_chrdev_region(tee_devt, TEE_NUM_DEVICES); +} + +subsys_initcall(tee_init); +module_exit(tee_exit); + +MODULE_AUTHOR("Linaro"); +MODULE_DESCRIPTION("TEE Driver"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL v2"); +#endif /* CONFIG_BCM_KF_OPTEE_414_BACKPORTS */ diff -urN linux-4.1.52-orig/drivers/tee/tee_private.h linux-4.1.52/drivers/tee/tee_private.h --- linux-4.1.52-orig/drivers/tee/tee_private.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/tee/tee_private.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,131 @@ +#if defined(CONFIG_BCM_KF_OPTEE_414_BACKPORTS) +/* + * Copyright (c) 2015-2016, Linaro Limited + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ +#ifndef TEE_PRIVATE_H +#define TEE_PRIVATE_H + +#include +#include +#include +#include +#include +#include + +struct tee_device; + +/** + * struct tee_shm - shared memory object + * @teedev: device used to allocate the object + * @ctx: context using the object, if NULL the context is gone + * @link link element + * @paddr: physical address of the shared memory + * @kaddr: virtual address of the shared memory + * @size: size of shared memory + * @dmabuf: dmabuf used to for exporting to user space + * @flags: defined by TEE_SHM_* in tee_drv.h + * @id: unique id of a shared memory object on this device + */ +struct tee_shm { + struct tee_device *teedev; + struct tee_context *ctx; + struct list_head link; + phys_addr_t paddr; + void *kaddr; + size_t size; + struct dma_buf *dmabuf; + u32 flags; + int id; +}; + +struct tee_shm_pool_mgr; + +/** + * struct tee_shm_pool_mgr_ops - shared memory pool manager operations + * @alloc: called when allocating shared memory + * @free: called when freeing shared memory + */ +struct tee_shm_pool_mgr_ops { + int (*alloc)(struct tee_shm_pool_mgr *poolmgr, struct tee_shm *shm, + size_t size); + void (*free)(struct tee_shm_pool_mgr *poolmgr, struct tee_shm *shm); +}; + +/** + * struct tee_shm_pool_mgr - shared memory manager + * @ops: operations + * @private_data: private data for the shared memory manager + */ +struct tee_shm_pool_mgr { + const struct tee_shm_pool_mgr_ops *ops; + void *private_data; +}; + +/** + * struct tee_shm_pool - shared memory pool + * @private_mgr: pool manager for shared memory only between kernel + * and secure world + * @dma_buf_mgr: pool manager for shared memory exported to user space + * @destroy: called when destroying the pool + * @private_data: private data for the pool + */ +struct tee_shm_pool { + struct tee_shm_pool_mgr private_mgr; + struct tee_shm_pool_mgr dma_buf_mgr; + void (*destroy)(struct tee_shm_pool *pool); + void *private_data; +}; + +#define TEE_DEVICE_FLAG_REGISTERED 0x1 +#define TEE_MAX_DEV_NAME_LEN 32 + +/** + * struct tee_device - TEE Device representation + * @name: name of device + * @desc: description of device + * @id: unique id of device + * @flags: represented by TEE_DEVICE_FLAG_REGISTERED above + * @dev: embedded basic device structure + * @cdev: embedded cdev + * @num_users: number of active users of this device + * @c_no_user: completion used when unregistering the device + * @mutex: mutex protecting @num_users and @idr + * @idr: register of shared memory object allocated on this device + * @pool: shared memory pool + */ +struct tee_device { + char name[TEE_MAX_DEV_NAME_LEN]; + const struct tee_desc *desc; + int id; + unsigned int flags; + + struct device dev; + struct cdev cdev; + + size_t num_users; + struct completion c_no_users; + struct mutex mutex; /* protects num_users and idr */ + + struct idr idr; + struct tee_shm_pool *pool; +}; + +int tee_shm_init(void); + +int tee_shm_get_fd(struct tee_shm *shm); + +bool tee_device_get(struct tee_device *teedev); +void tee_device_put(struct tee_device *teedev); + +#endif /*TEE_PRIVATE_H*/ +#endif /* CONFIG_BCM_KF_OPTEE_414_BACKPORTS */ diff -urN linux-4.1.52-orig/drivers/tee/tee_shm.c linux-4.1.52/drivers/tee/tee_shm.c --- linux-4.1.52-orig/drivers/tee/tee_shm.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/tee/tee_shm.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,365 @@ +#if defined(CONFIG_BCM_KF_OPTEE_414_BACKPORTS) +/* + * Copyright (c) 2015-2016, Linaro Limited + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include "tee_private.h" + +static void tee_shm_release(struct tee_shm *shm) +{ + struct tee_device *teedev = shm->teedev; + struct tee_shm_pool_mgr *poolm; + + mutex_lock(&teedev->mutex); + idr_remove(&teedev->idr, shm->id); + if (shm->ctx) + list_del(&shm->link); + mutex_unlock(&teedev->mutex); + + if (shm->flags & TEE_SHM_DMA_BUF) + poolm = &teedev->pool->dma_buf_mgr; + else + poolm = &teedev->pool->private_mgr; + + poolm->ops->free(poolm, shm); + kfree(shm); + + tee_device_put(teedev); +} + +static struct sg_table *tee_shm_op_map_dma_buf(struct dma_buf_attachment + *attach, enum dma_data_direction dir) +{ + return NULL; +} + +static void tee_shm_op_unmap_dma_buf(struct dma_buf_attachment *attach, + struct sg_table *table, + enum dma_data_direction dir) +{ +} + +static void tee_shm_op_release(struct dma_buf *dmabuf) +{ + struct tee_shm *shm = dmabuf->priv; + + tee_shm_release(shm); +} + +static void *tee_shm_op_map_atomic(struct dma_buf *dmabuf, unsigned long pgnum) +{ + return NULL; +} + +static void *tee_shm_op_map(struct dma_buf *dmabuf, unsigned long pgnum) +{ + return NULL; +} + +static int tee_shm_op_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) +{ + struct tee_shm *shm = dmabuf->priv; + size_t size = vma->vm_end - vma->vm_start; + + return remap_pfn_range(vma, vma->vm_start, shm->paddr >> PAGE_SHIFT, + size, vma->vm_page_prot); +} + +static const struct dma_buf_ops tee_shm_dma_buf_ops = { + .map_dma_buf = tee_shm_op_map_dma_buf, + .unmap_dma_buf = tee_shm_op_unmap_dma_buf, + .release = tee_shm_op_release, +#if defined(CONFIG_BCM_KF_OPTEE) && defined(CONFIG_TEE) + .kmap_atomic = tee_shm_op_map_atomic, + .kmap = tee_shm_op_map, +#else + .map_atomic = tee_shm_op_map_atomic, + .map = tee_shm_op_map, +#endif + .mmap = tee_shm_op_mmap, +}; + +/** + * tee_shm_alloc() - Allocate shared memory + * @ctx: Context that allocates the shared memory + * @size: Requested size of shared memory + * @flags: Flags setting properties for the requested shared memory. + * + * Memory allocated as global shared memory is automatically freed when the + * TEE file pointer is closed. The @flags field uses the bits defined by + * TEE_SHM_* in . TEE_SHM_MAPPED must currently always be + * set. If TEE_SHM_DMA_BUF global shared memory will be allocated and + * associated with a dma-buf handle, else driver private memory. + */ +struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags) +{ + struct tee_device *teedev = ctx->teedev; + struct tee_shm_pool_mgr *poolm = NULL; + struct tee_shm *shm; + void *ret; + int rc; + + if (!(flags & TEE_SHM_MAPPED)) { + dev_err(teedev->dev.parent, + "only mapped allocations supported\n"); + return ERR_PTR(-EINVAL); + } + + if ((flags & ~(TEE_SHM_MAPPED | TEE_SHM_DMA_BUF))) { + dev_err(teedev->dev.parent, "invalid shm flags 0x%x", flags); + return ERR_PTR(-EINVAL); + } + + if (!tee_device_get(teedev)) + return ERR_PTR(-EINVAL); + + if (!teedev->pool) { + /* teedev has been detached from driver */ + ret = ERR_PTR(-EINVAL); + goto err_dev_put; + } + + shm = kzalloc(sizeof(*shm), GFP_KERNEL); + if (!shm) { + ret = ERR_PTR(-ENOMEM); + goto err_dev_put; + } + + shm->flags = flags; + shm->teedev = teedev; + shm->ctx = ctx; + if (flags & TEE_SHM_DMA_BUF) + poolm = &teedev->pool->dma_buf_mgr; + else + poolm = &teedev->pool->private_mgr; + + rc = poolm->ops->alloc(poolm, shm, size); + if (rc) { + ret = ERR_PTR(rc); + goto err_kfree; + } + + mutex_lock(&teedev->mutex); + shm->id = idr_alloc(&teedev->idr, shm, 1, 0, GFP_KERNEL); + mutex_unlock(&teedev->mutex); + if (shm->id < 0) { + ret = ERR_PTR(shm->id); + goto err_pool_free; + } + + if (flags & TEE_SHM_DMA_BUF) { + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); + + exp_info.ops = &tee_shm_dma_buf_ops; + exp_info.size = shm->size; + exp_info.flags = O_RDWR; + exp_info.priv = shm; + + shm->dmabuf = dma_buf_export(&exp_info); + if (IS_ERR(shm->dmabuf)) { + ret = ERR_CAST(shm->dmabuf); + goto err_rem; + } + } + mutex_lock(&teedev->mutex); + list_add_tail(&shm->link, &ctx->list_shm); + mutex_unlock(&teedev->mutex); + + return shm; +err_rem: + mutex_lock(&teedev->mutex); + idr_remove(&teedev->idr, shm->id); + mutex_unlock(&teedev->mutex); +err_pool_free: + poolm->ops->free(poolm, shm); +err_kfree: + kfree(shm); +err_dev_put: + tee_device_put(teedev); + return ret; +} +EXPORT_SYMBOL_GPL(tee_shm_alloc); + +/** + * tee_shm_get_fd() - Increase reference count and return file descriptor + * @shm: Shared memory handle + * @returns user space file descriptor to shared memory + */ +int tee_shm_get_fd(struct tee_shm *shm) +{ + u32 req_flags = TEE_SHM_MAPPED | TEE_SHM_DMA_BUF; + int fd; + + if ((shm->flags & req_flags) != req_flags) + return -EINVAL; + + fd = dma_buf_fd(shm->dmabuf, O_CLOEXEC); + if (fd >= 0) + get_dma_buf(shm->dmabuf); + return fd; +} + +/** + * tee_shm_free() - Free shared memory + * @shm: Handle to shared memory to free + */ +void tee_shm_free(struct tee_shm *shm) +{ + /* + * dma_buf_put() decreases the dmabuf reference counter and will + * call tee_shm_release() when the last reference is gone. + * + * In the case of driver private memory we call tee_shm_release + * directly instead as it doesn't have a reference counter. + */ + if (shm->flags & TEE_SHM_DMA_BUF) + dma_buf_put(shm->dmabuf); + else + tee_shm_release(shm); +} +EXPORT_SYMBOL_GPL(tee_shm_free); + +/** + * tee_shm_va2pa() - Get physical address of a virtual address + * @shm: Shared memory handle + * @va: Virtual address to tranlsate + * @pa: Returned physical address + * @returns 0 on success and < 0 on failure + */ +int tee_shm_va2pa(struct tee_shm *shm, void *va, phys_addr_t *pa) +{ + /* Check that we're in the range of the shm */ + if ((char *)va < (char *)shm->kaddr) + return -EINVAL; + if ((char *)va >= ((char *)shm->kaddr + shm->size)) + return -EINVAL; + + return tee_shm_get_pa( + shm, (unsigned long)va - (unsigned long)shm->kaddr, pa); +} +EXPORT_SYMBOL_GPL(tee_shm_va2pa); + +/** + * tee_shm_pa2va() - Get virtual address of a physical address + * @shm: Shared memory handle + * @pa: Physical address to tranlsate + * @va: Returned virtual address + * @returns 0 on success and < 0 on failure + */ +int tee_shm_pa2va(struct tee_shm *shm, phys_addr_t pa, void **va) +{ + /* Check that we're in the range of the shm */ + if (pa < shm->paddr) + return -EINVAL; + if (pa >= (shm->paddr + shm->size)) + return -EINVAL; + + if (va) { + void *v = tee_shm_get_va(shm, pa - shm->paddr); + + if (IS_ERR(v)) + return PTR_ERR(v); + *va = v; + } + return 0; +} +EXPORT_SYMBOL_GPL(tee_shm_pa2va); + +/** + * tee_shm_get_va() - Get virtual address of a shared memory plus an offset + * @shm: Shared memory handle + * @offs: Offset from start of this shared memory + * @returns virtual address of the shared memory + offs if offs is within + * the bounds of this shared memory, else an ERR_PTR + */ +void *tee_shm_get_va(struct tee_shm *shm, size_t offs) +{ + if (offs >= shm->size) + return ERR_PTR(-EINVAL); + return (char *)shm->kaddr + offs; +} +EXPORT_SYMBOL_GPL(tee_shm_get_va); + +/** + * tee_shm_get_pa() - Get physical address of a shared memory plus an offset + * @shm: Shared memory handle + * @offs: Offset from start of this shared memory + * @pa: Physical address to return + * @returns 0 if offs is within the bounds of this shared memory, else an + * error code. + */ +int tee_shm_get_pa(struct tee_shm *shm, size_t offs, phys_addr_t *pa) +{ + if (offs >= shm->size) + return -EINVAL; + if (pa) + *pa = shm->paddr + offs; + return 0; +} +EXPORT_SYMBOL_GPL(tee_shm_get_pa); + +/** + * tee_shm_get_from_id() - Find shared memory object and increase reference + * count + * @ctx: Context owning the shared memory + * @id: Id of shared memory object + * @returns a pointer to 'struct tee_shm' on success or an ERR_PTR on failure + */ +struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id) +{ + struct tee_device *teedev; + struct tee_shm *shm; + + if (!ctx) + return ERR_PTR(-EINVAL); + + teedev = ctx->teedev; + mutex_lock(&teedev->mutex); + shm = idr_find(&teedev->idr, id); + if (!shm || shm->ctx != ctx) + shm = ERR_PTR(-EINVAL); + else if (shm->flags & TEE_SHM_DMA_BUF) + get_dma_buf(shm->dmabuf); + mutex_unlock(&teedev->mutex); + return shm; +} +EXPORT_SYMBOL_GPL(tee_shm_get_from_id); + +/** + * tee_shm_get_id() - Get id of a shared memory object + * @shm: Shared memory handle + * @returns id + */ +int tee_shm_get_id(struct tee_shm *shm) +{ + return shm->id; +} +EXPORT_SYMBOL_GPL(tee_shm_get_id); + +/** + * tee_shm_put() - Decrease reference count on a shared memory handle + * @shm: Shared memory handle + */ +void tee_shm_put(struct tee_shm *shm) +{ + if (shm->flags & TEE_SHM_DMA_BUF) + dma_buf_put(shm->dmabuf); +} +EXPORT_SYMBOL_GPL(tee_shm_put); +#endif /* CONFIG_BCM_KF_OPTEE_414_BACKPORTS */ diff -urN linux-4.1.52-orig/drivers/tee/tee_shm_pool.c linux-4.1.52/drivers/tee/tee_shm_pool.c --- linux-4.1.52-orig/drivers/tee/tee_shm_pool.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/tee/tee_shm_pool.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,158 @@ +#if defined(CONFIG_BCM_KF_OPTEE_414_BACKPORTS) +/* + * Copyright (c) 2015, Linaro Limited + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ +#include +#include +#include +#include +#include +#include "tee_private.h" + +static int pool_op_gen_alloc(struct tee_shm_pool_mgr *poolm, + struct tee_shm *shm, size_t size) +{ + unsigned long va; + struct gen_pool *genpool = poolm->private_data; + size_t s = roundup(size, 1 << genpool->min_alloc_order); + + va = gen_pool_alloc(genpool, s); + if (!va) + return -ENOMEM; + + memset((void *)va, 0, s); + shm->kaddr = (void *)va; + shm->paddr = gen_pool_virt_to_phys(genpool, va); + shm->size = s; + return 0; +} + +static void pool_op_gen_free(struct tee_shm_pool_mgr *poolm, + struct tee_shm *shm) +{ + gen_pool_free(poolm->private_data, (unsigned long)shm->kaddr, + shm->size); + shm->kaddr = NULL; +} + +static const struct tee_shm_pool_mgr_ops pool_ops_generic = { + .alloc = pool_op_gen_alloc, + .free = pool_op_gen_free, +}; + +static void pool_res_mem_destroy(struct tee_shm_pool *pool) +{ + gen_pool_destroy(pool->private_mgr.private_data); + gen_pool_destroy(pool->dma_buf_mgr.private_data); +} + +static int pool_res_mem_mgr_init(struct tee_shm_pool_mgr *mgr, + struct tee_shm_pool_mem_info *info, + int min_alloc_order) +{ + size_t page_mask = PAGE_SIZE - 1; + struct gen_pool *genpool = NULL; + int rc; + + /* + * Start and end must be page aligned + */ + if ((info->vaddr & page_mask) || (info->paddr & page_mask) || + (info->size & page_mask)) + return -EINVAL; + + genpool = gen_pool_create(min_alloc_order, -1); + if (!genpool) + return -ENOMEM; + + gen_pool_set_algo(genpool, gen_pool_best_fit, NULL); + rc = gen_pool_add_virt(genpool, info->vaddr, info->paddr, info->size, + -1); + if (rc) { + gen_pool_destroy(genpool); + return rc; + } + + mgr->private_data = genpool; + mgr->ops = &pool_ops_generic; + return 0; +} + +/** + * tee_shm_pool_alloc_res_mem() - Create a shared memory pool from reserved + * memory range + * @priv_info: Information for driver private shared memory pool + * @dmabuf_info: Information for dma-buf shared memory pool + * + * Start and end of pools will must be page aligned. + * + * Allocation with the flag TEE_SHM_DMA_BUF set will use the range supplied + * in @dmabuf, others will use the range provided by @priv. + * + * @returns pointer to a 'struct tee_shm_pool' or an ERR_PTR on failure. + */ +struct tee_shm_pool * +tee_shm_pool_alloc_res_mem(struct tee_shm_pool_mem_info *priv_info, + struct tee_shm_pool_mem_info *dmabuf_info) +{ + struct tee_shm_pool *pool = NULL; + int ret; + + pool = kzalloc(sizeof(*pool), GFP_KERNEL); + if (!pool) { + ret = -ENOMEM; + goto err; + } + + /* + * Create the pool for driver private shared memory + */ + ret = pool_res_mem_mgr_init(&pool->private_mgr, priv_info, + 3 /* 8 byte aligned */); + if (ret) + goto err; + + /* + * Create the pool for dma_buf shared memory + */ + ret = pool_res_mem_mgr_init(&pool->dma_buf_mgr, dmabuf_info, + PAGE_SHIFT); + if (ret) + goto err; + + pool->destroy = pool_res_mem_destroy; + return pool; +err: + if (ret == -ENOMEM) + pr_err("%s: can't allocate memory for res_mem shared memory pool\n", __func__); + if (pool && pool->private_mgr.private_data) + gen_pool_destroy(pool->private_mgr.private_data); + kfree(pool); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(tee_shm_pool_alloc_res_mem); + +/** + * tee_shm_pool_free() - Free a shared memory pool + * @pool: The shared memory pool to free + * + * There must be no remaining shared memory allocated from this pool when + * this function is called. + */ +void tee_shm_pool_free(struct tee_shm_pool *pool) +{ + pool->destroy(pool); + kfree(pool); +} +EXPORT_SYMBOL_GPL(tee_shm_pool_free); +#endif /* CONFIG_BCM_KF_OPTEE_414_BACKPORTS */ diff -urN linux-4.1.52-orig/drivers/tty/serial/8250/8250_core.c linux-4.1.52/drivers/tty/serial/8250/8250_core.c --- linux-4.1.52-orig/drivers/tty/serial/8250/8250_core.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/tty/serial/8250/8250_core.c 2020-02-07 01:47:54.000000000 +0800 @@ -17,6 +17,9 @@ */ #if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#if defined(CONFIG_BCM_KF_CHAR_SYSRQ) +static char sysrq_start_char='^'; +#endif #define SUPPORT_SYSRQ #endif @@ -1524,6 +1527,20 @@ else if (lsr & UART_LSR_FE) flag = TTY_FRAME; } +#if defined(CONFIG_BCM_KF_CHAR_SYSRQ) && defined(SUPPORT_SYSRQ) + /* + * Simple hack for substituting a regular ASCII char as the break + * char for the start of the Magic Sysrq sequence. This duplicates + * some of the code in uart_handle_break() in serial_core.h + */ + if (up->port.sysrq == 0) + { + if (ch == sysrq_start_char) { + up->port.sysrq = jiffies + HZ*5; + goto ignore_char; + } + } +#endif if (uart_handle_sysrq_char(port, ch)) goto ignore_char; @@ -1832,6 +1849,9 @@ struct irq_info *i; struct hlist_node *n; struct hlist_head *h; +#if defined(CONFIG_BCM_KF_KERN_WARNING) + i = NULL; +#endif mutex_lock(&hash_mutex); diff -urN linux-4.1.52-orig/drivers/tty/serial/amba-pl011.c linux-4.1.52/drivers/tty/serial/amba-pl011.c --- linux-4.1.52-orig/drivers/tty/serial/amba-pl011.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/tty/serial/amba-pl011.c 2020-02-07 01:47:54.000000000 +0800 @@ -1995,11 +1995,12 @@ { struct uart_amba_port *uap = amba_ports[co->index]; unsigned int status, old_cr, new_cr; +#if !defined(CONFIG_BCM_KF_PRINTK_INT_ENABLED) || !defined(CONFIG_BCM_PRINTK_INT_ENABLED) unsigned long flags; int locked = 1; - +#endif clk_enable(uap->clk); - +#if !defined(CONFIG_BCM_KF_PRINTK_INT_ENABLED) || !defined(CONFIG_BCM_PRINTK_INT_ENABLED) local_irq_save(flags); if (uap->port.sysrq) locked = 0; @@ -2007,7 +2008,7 @@ locked = spin_trylock(&uap->port.lock); else spin_lock(&uap->port.lock); - +#endif /* * First save the CR then disable the interrupts */ @@ -2026,11 +2027,11 @@ status = readw(uap->port.membase + UART01x_FR); } while (status & UART01x_FR_BUSY); writew(old_cr, uap->port.membase + UART011_CR); - +#if !defined(CONFIG_BCM_KF_PRINTK_INT_ENABLED) || !defined(CONFIG_BCM_PRINTK_INT_ENABLED) if (locked) spin_unlock(&uap->port.lock); local_irq_restore(flags); - +#endif clk_disable(uap->clk); } diff -urN linux-4.1.52-orig/drivers/tty/serial/serial_core.c linux-4.1.52/drivers/tty/serial/serial_core.c --- linux-4.1.52-orig/drivers/tty/serial/serial_core.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/tty/serial/serial_core.c 2020-02-07 01:47:54.000000000 +0800 @@ -1890,6 +1890,9 @@ }; static const struct baud_rates baud_rates[] = { +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM63138_SIM) + {3000000, B3000000}, +#endif { 921600, B921600 }, { 460800, B460800 }, { 230400, B230400 }, diff -urN linux-4.1.52-orig/drivers/tty/sysrq.c linux-4.1.52/drivers/tty/sysrq.c --- linux-4.1.52-orig/drivers/tty/sysrq.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/tty/sysrq.c 2020-02-07 01:47:54.000000000 +0800 @@ -47,6 +47,9 @@ #include #include #include +#if defined(CONFIG_BCM_KF_WDT) +#include +#endif #include #include @@ -55,10 +58,17 @@ static int __read_mostly sysrq_enabled = CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE; static bool __read_mostly sysrq_always_enabled; +#if defined(CONFIG_BCM_KF_CHAR_SYSRQ) +bool sysrq_on(void) +#else static bool sysrq_on(void) +#endif { return sysrq_enabled || sysrq_always_enabled; } +#if defined(CONFIG_BCM_KF_CHAR_SYSRQ) +EXPORT_SYMBOL_GPL(sysrq_on); +#endif /* * A value of 1 means 'all', other nonzero values are an op mask: @@ -211,12 +221,23 @@ #define sysrq_showlocks_op (*(struct sysrq_key_op *)NULL) #endif -#ifdef CONFIG_SMP +#if defined(CONFIG_SMP) || defined(CONFIG_BCM_KF_CHAR_SYSRQ) static DEFINE_SPINLOCK(show_lock); static void showacpu(void *dummy) { unsigned long flags; +#if defined(CONFIG_BCM_KF_CHAR_SYSRQ) + struct pt_regs *regs = get_irq_regs(); + + printk("=>entered showacpu on CPU %d: (idle=%d)\n", + smp_processor_id(), idle_cpu(smp_processor_id())); + + if (regs) { + printk(KERN_INFO "=>calling show_regs:\n"); + show_regs(regs); + } +#endif /* Idle CPUs have no interesting backtrace. */ if (idle_cpu(smp_processor_id())) @@ -224,7 +245,18 @@ spin_lock_irqsave(&show_lock, flags); pr_info("CPU%d:\n", smp_processor_id()); +#if defined(CONFIG_BCM_KF_CHAR_SYSRQ) + printk(KERN_INFO "=>calling show_stack, current=%p ", current); + if (current) + printk(" (comm=%s)\n", current->comm); + else + printk("\n"); + + show_stack(current, NULL); +#else show_stack(NULL, NULL); +#endif + spin_unlock_irqrestore(&show_lock, flags); } @@ -237,6 +269,24 @@ static void sysrq_handle_showallcpus(int key) { +#if defined(CONFIG_BCM_KF_CHAR_SYSRQ) + + + showacpu(NULL); + +#ifdef CONFIG_SMP + { + cpumask_t available_cpus; + cpumask_copy(&available_cpus, cpu_present_mask); + int othercpu; + for_each_cpu(othercpu, &available_cpus) { + printk("=== Now call CPU (%d)\n", othercpu); + smp_call_function_single(othercpu, showacpu, (void *) 0xeeee, 0); + } + } +#endif /* CONFIG_SMP */ + +#else /* CONFIG_BCM_KF_CHAR_SYSRQ */ /* * Fall back to the workqueue based printing if the * backtrace printing did not succeed or the @@ -253,6 +303,7 @@ } schedule_work(&sysrq_showallcpus); } +#endif /* CONFIG_BCM_KF_CHAR_SYSRQ */ } static struct sysrq_key_op sysrq_showallcpus_op = { @@ -418,6 +469,92 @@ .enable_mask = SYSRQ_ENABLE_RTNICE, }; +#ifdef CONFIG_BCM_KF_CHAR_SYSRQ + +/* + * This function is a "safe" function to call at anytime. It should only + * read data that do not require a lock and will not cause exceptions, + * i.e. reading statistics but not dereferencing pointers. It should not + * assume interrupts are working on any of the CPU's. + * The 'L' option is also good. + */ +static void sysrq_handle_cpu_summary(int key) +{ + int cpu; + int max_cpu=1; +#ifdef CONFIG_SMP + max_cpu=2; + cpumask_t available_cpus; + + cpumask_copy(&available_cpus, cpu_present_mask); +#endif + + + printk("CPU summary invoked on cpu %d\n", smp_processor_id()); +#ifdef CONFIG_SMP + for_each_cpu(cpu, &available_cpus) { +#else + for (cpu=0; cpu < max_cpu; cpu++) { +#endif + if (cpu_online(cpu)) { + printk(" cpu %d is online.\n", cpu); + } + else { + printk(" WARNING: cpu %d is offline!\n", cpu); + } + } + printk("\n\n"); + // dump interrupt statistics + // print other CPU info that does not require getting a lock or + // interrupts to be enabled on that CPU +} + +static struct sysrq_key_op sysrq_cpu_summary_op = { + .handler = sysrq_handle_cpu_summary, + .help_msg = "BRCM: show summary status on all CPUs(A)", + .action_msg = "CPU Summary Status", + .enable_mask = 0x4000, /* typically all flags will be enabled */ +}; + + +#include +#include + +static char intrs_output_buf[1024]; + +static void sysrq_handle_show_intrs(int key) +{ + struct seq_file intrs_seq_file; + loff_t pos=0; + + // Just initialize the parts of seq_file that seq_printf needs + memset(&intrs_seq_file, 0, sizeof(intrs_seq_file)); + intrs_seq_file.buf = intrs_output_buf; + intrs_seq_file.size = sizeof(intrs_output_buf); + + /* + * Leverage the show_interrupts() function in kernel/mips/irq.c to dump + * to our buffer. + */ + while (pos <= NR_IRQS) + { + memset(intrs_output_buf, 0, sizeof(intrs_output_buf)); + intrs_seq_file.count = 0; + show_interrupts(&intrs_seq_file, &pos); + printk("%s", intrs_output_buf); + pos++; + } +} + +static struct sysrq_key_op sysrq_show_intrs_op = { + .handler = sysrq_handle_show_intrs, + .help_msg = "BRCM: show interrupt counts on all CPUs(Y)", + .action_msg = "Interrupt Counts", + .enable_mask = 0x4000, /* typically all flags will be enabled */ +}; + +#endif /* CONFIG_BCM_KF_CHAR_SYSRQ */ + /* Key Operations table and lock */ static DEFINE_SPINLOCK(sysrq_key_table_lock); @@ -433,11 +570,15 @@ &sysrq_loglevel_op, /* 8 */ &sysrq_loglevel_op, /* 9 */ +#if defined(CONFIG_BCM_KF_CHAR_SYSRQ) + &sysrq_cpu_summary_op, /* a*/ +#else /* * a: Don't use for system provided sysrqs, it is handled specially on * sparc and will never arrive. */ NULL, /* a */ +#endif &sysrq_reboot_op, /* b */ &sysrq_crash_op, /* c & ibm_emac driver debug */ &sysrq_showlocks_op, /* d */ @@ -453,7 +594,7 @@ NULL, /* j */ #endif &sysrq_SAK_op, /* k */ -#ifdef CONFIG_SMP +#if defined(CONFIG_SMP) || defined(CONFIG_BCM_KF_CHAR_SYSRQ) &sysrq_showallcpus_op, /* l */ #else NULL, /* l */ @@ -474,8 +615,12 @@ /* x: May be registered on ppc/powerpc for xmon */ /* x: May be registered on sparc64 for global PMU dump */ NULL, /* x */ +#if defined(CONFIG_BCM_KF_CHAR_SYSRQ) + &sysrq_show_intrs_op, /* y */ +#else /* y: May be registered on sparc64 for global register dump */ NULL, /* y */ +#endif &sysrq_ftrace_dump_op, /* z */ }; @@ -488,6 +633,10 @@ retval = key - '0'; else if ((key >= 'a') && (key <= 'z')) retval = key + 10 - 'a'; +#if defined(CONFIG_BCM_KF_CHAR_SYSRQ) + else if ((key >= 'A') && (key <= 'Z')) + retval = key + 10 - 'A'; +#endif else retval = -1; return retval; @@ -522,8 +671,19 @@ int orig_log_level; int i; +#if defined(CONFIG_BCM_KF_WDT) +#if defined(CONFIG_WATCHDOG_CORE) + /* Kill the watchdog */ + watchdog_force_disable(); +#endif +#endif + rcu_sysrq_start(); rcu_read_lock(); +#if defined(CONFIG_BCM_KF_CHAR_SYSRQ) + printk("SysRq: intr handled on CPU %d\n", smp_processor_id()); +#endif + /* * Raise the apparent loglevel to maximum so that the sysrq header * is shown to provide the user with positive feedback. We do not @@ -563,6 +723,9 @@ } } pr_cont("\n"); +#if defined(CONFIG_BCM_KF_CHAR_SYSRQ) + printk("All commands are case insensitive.\n"); +#endif console_loglevel = orig_log_level; } rcu_read_unlock(); diff -urN linux-4.1.52-orig/drivers/usb/core/hub.c linux-4.1.52/drivers/usb/core/hub.c --- linux-4.1.52-orig/drivers/usb/core/hub.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/usb/core/hub.c 2020-02-07 01:47:54.000000000 +0800 @@ -2767,8 +2767,16 @@ USB_PORT_FEAT_C_BH_PORT_RESET); usb_clear_port_feature(hub->hdev, port1, USB_PORT_FEAT_C_PORT_LINK_STATE); - usb_clear_port_feature(hub->hdev, port1, - USB_PORT_FEAT_C_CONNECTION); + +#if defined(CONFIG_BCM_KF_USB_HOSTS) + /* during reboot some times warm reset is seen and clearing this bit + * is causing device detection issues*/ + if(warm) + printk("+++++ BRCM skipping port_feat_c_connection for warm reset\n"); + else +#endif + usb_clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_CONNECTION); /* * If a USB 3.0 device migrates from reset to an error diff -urN linux-4.1.52-orig/drivers/usb/core/message.c linux-4.1.52/drivers/usb/core/message.c --- linux-4.1.52-orig/drivers/usb/core/message.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/usb/core/message.c 2020-02-07 01:47:54.000000000 +0800 @@ -1590,6 +1590,11 @@ alt->desc.bInterfaceNumber)) return -ENOMEM; +#if defined(CONFIG_BCM_KF_USB_HOSTS) + if (add_uevent_var(env, "USBDEVNAME=%s", dev_name(dev))) + return -ENOMEM; +#endif + return 0; } diff -urN linux-4.1.52-orig/drivers/usb/host/Kconfig linux-4.1.52/drivers/usb/host/Kconfig --- linux-4.1.52-orig/drivers/usb/host/Kconfig 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/usb/host/Kconfig 2020-02-07 01:47:54.000000000 +0800 @@ -33,6 +33,7 @@ config USB_XHCI_PLATFORM tristate + prompt "xHCI platform support" if BCM_KF_USB_HOSTS config USB_XHCI_MVEBU tristate "xHCI support for Marvell Armada 375/38x" diff -urN linux-4.1.52-orig/drivers/usb/host/pci-quirks.c linux-4.1.52/drivers/usb/host/pci-quirks.c --- linux-4.1.52-orig/drivers/usb/host/pci-quirks.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/usb/host/pci-quirks.c 2020-02-07 01:47:54.000000000 +0800 @@ -1075,6 +1075,12 @@ static void quirk_usb_early_handoff(struct pci_dev *pdev) { + +#if (defined(CONFIG_BCM_KF_MIPS_BCM963XX) && defined(CONFIG_MIPS_BCM963XX)) + /*as 963xx chips fake USB controllers as PCI, just return from here*/ + if(pdev->vendor == PCI_VENDOR_ID_BROADCOM) + return; +#endif /* Skip Netlogic mips SoC's internal PCI USB controller. * This device does not need/support EHCI/OHCI handoff */ diff -urN linux-4.1.52-orig/drivers/usb/host/xhci.c linux-4.1.52/drivers/usb/host/xhci.c --- linux-4.1.52-orig/drivers/usb/host/xhci.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/usb/host/xhci.c 2020-02-07 01:47:54.000000000 +0800 @@ -355,6 +355,8 @@ return; } +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +#ifdef CONFIG_PM static void __maybe_unused xhci_msix_sync_irqs(struct xhci_hcd *xhci) { int i; @@ -364,6 +366,18 @@ synchronize_irq(xhci->msix_entries[i].vector); } } +#endif +#else +static void __maybe_unused xhci_msix_sync_irqs(struct xhci_hcd *xhci) +{ + int i; + + if (xhci->msix_entries) { + for (i = 0; i < xhci->msix_count; i++) + synchronize_irq(xhci->msix_entries[i].vector); + } +} +#endif static int xhci_try_enable_msi(struct usb_hcd *hcd) { @@ -506,6 +520,18 @@ { const char *dmi_product_name, *dmi_sys_vendor; +#if defined(CONFIG_BCM_KF_USB_HOSTS) && ( defined(CONFIG_BCM963138) || \ + defined(CONFIG_BCM963148) || defined(CONFIG_BCM96858) || \ + defined(CONFIG_BCM94908) || \ + defined(CONFIG_BCM963158) || defined(CONFIG_BCM96856)) + + /* some bad USB3.0 devices are driving USB3.0 ports into compliance mode + * and makin gthe port unusable(till a reboot), this work around helps to + * avoid reboot by doing a warm reset. + */ + return true; +#endif + dmi_product_name = dmi_get_system_info(DMI_PRODUCT_NAME); dmi_sys_vendor = dmi_get_system_info(DMI_SYS_VENDOR); if (!dmi_product_name || !dmi_sys_vendor) diff -urN linux-4.1.52-orig/drivers/usb/host/xhci-hub.c linux-4.1.52/drivers/usb/host/xhci-hub.c --- linux-4.1.52-orig/drivers/usb/host/xhci-hub.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/usb/host/xhci-hub.c 2020-02-07 01:47:54.000000000 +0800 @@ -1150,6 +1150,13 @@ break; } if ((temp & mask) != 0 || +#ifdef CONFIG_BCM_KF_USB_HOSTS + /* when in compliance mode no change events are generated + * this check is needed for compliance mode quirk to bring + * port out of compliance mode + */ + ((temp & PORT_PLS_MASK) == USB_SS_PORT_LS_COMP_MOD) || +#endif (bus_state->port_c_suspend & 1 << i) || (bus_state->resume_done[i] && time_after_eq( jiffies, bus_state->resume_done[i]))) { @@ -1167,7 +1174,7 @@ return status ? retval : 0; } -#ifdef CONFIG_PM +#if defined(CONFIG_PM) int xhci_bus_suspend(struct usb_hcd *hcd) { diff -urN linux-4.1.52-orig/drivers/usb/host/xhci-mem.c linux-4.1.52/drivers/usb/host/xhci-mem.c --- linux-4.1.52-orig/drivers/usb/host/xhci-mem.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/usb/host/xhci-mem.c 2020-02-07 01:47:54.000000000 +0800 @@ -1725,7 +1725,11 @@ if (!buf) goto fail_sp5; +#if defined(CONFIG_BCM_KF_MIPS_BCM963XX) || defined(CONFIG_BCM_KF_ARM_BCM963XX) + xhci->scratchpad->sp_array[i] = cpu_to_le64(dma); +#else xhci->scratchpad->sp_array[i] = dma; +#endif xhci->scratchpad->sp_buffers[i] = buf; xhci->scratchpad->sp_dma_buffers[i] = dma; } diff -urN linux-4.1.52-orig/drivers/usb/storage/scsiglue.c linux-4.1.52/drivers/usb/storage/scsiglue.c --- linux-4.1.52-orig/drivers/usb/storage/scsiglue.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/usb/storage/scsiglue.c 2020-02-07 01:47:54.000000000 +0800 @@ -570,8 +570,13 @@ /* lots of sg segments can be handled */ .sg_tablesize = SCSI_MAX_SG_CHAIN_SEGMENTS, +#if defined(CONFIG_BCM_KF_USB_STORAGE) + /* limit the total size of a transfer to 256 KB */ + .max_sectors = 512, +#else /* limit the total size of a transfer to 120 KB */ .max_sectors = 240, +#endif /* merge commands... this seems to help performance, but * periodically someone should test to see which setting is more diff -urN linux-4.1.52-orig/drivers/usb/storage/usb.c linux-4.1.52/drivers/usb/storage/usb.c --- linux-4.1.52-orig/drivers/usb/storage/usb.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/usb/storage/usb.c 2020-02-07 01:47:54.000000000 +0800 @@ -58,6 +58,10 @@ #include #include +#if defined(CONFIG_BCM_KF_USB_STORAGE) +#include +#endif + #include #include #include @@ -795,6 +799,14 @@ "Unable to start control thread\n"); return PTR_ERR(th); } +#if defined(CONFIG_BCM_KF_USB_STORAGE) + /*convert the thread to realtime RR thread */ + { + struct sched_param param; + param.sched_priority = BCM_RTPRIO_DATA; + sched_setscheduler(th, SCHED_RR, ¶m); + } +#endif us->ctl_thread = th; return 0; diff -urN linux-4.1.52-orig/drivers/watchdog/bcm96xxx_wdt.c linux-4.1.52/drivers/watchdog/bcm96xxx_wdt.c --- linux-4.1.52-orig/drivers/watchdog/bcm96xxx_wdt.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/drivers/watchdog/bcm96xxx_wdt.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,214 @@ +#if defined(CONFIG_BCM_KF_WDT) +/************************************************************** + * bcm96xxx_wdt.c Watchdog driver for Broadcom BCM96xxx + * + * Author: Farhan Ali + * Based on bcm2835_wdt.c + * + * Copyright (c) 2014 Broadcom Corporation + * All Rights Reserved + * + * <:label-BRCM:2016:DUAL/GPL:standard + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed + * to you under the terms of the GNU General Public License version 2 + * (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, + * with the following added to such license: + * + * As a special exception, the copyright holders of this software give + * you permission to link this software with independent modules, and + * to copy and distribute the resulting executable under terms of your + * choice, provided that you also meet, for each linked independent + * module, the terms and conditions of the license of that module. + * An independent module is a module which is not derived from this + * software. The special exception does not apply to any modifications + * of the software. + * + * Not withstanding the above, under no circumstances may you combine + * this software in any way with any other Broadcom software provided + * under a license other than the GPL, without Broadcom's express prior + * written consent. + * + * :> + * + ************************************************************/ +#include +#include +#include +#include +#include +#include + +/* Watchdog default count register */ +#define WDT_DEFVAL_REG 0x0 + +/* Watchdog control register */ +#define WDT_CTL_REG 0x4 + +/* Watchdog control register constants */ +#define WDT_START_1 (0xff00) +#define WDT_START_2 (0x00ff) +#define WDT_STOP_1 (0xee00) +#define WDT_STOP_2 (0x00ee) + +/* Watchdog reset length register */ +#define WDT_RSTLEN_REG 0x8 + +/* Watchdog soft reset register (BCM6328 only) */ +#define WDT_SOFTRESET_REG 0xc + +#define WDT_HZ 50000000 /* Fclk */ +#define WDT_MAX_TIME_TICKS 0xffffffff +#define WDT_DEFAULT_TIME_SECS 10 + +#define SECS_TO_WDOG_TICKS(x) ((uint32_t)((x) * WDT_HZ)) +#define WDOG_TICKS_TO_SECS(x) ((uint32_t)((x) / WDT_HZ)) + +struct bcm96xxx_wdt { + void __iomem *base; + spinlock_t lock; +}; + +static unsigned int heartbeat; +static bool nowayout = WATCHDOG_NOWAYOUT; + +static int bcm96xxx_wdt_start(struct watchdog_device *wdog) +{ + struct bcm96xxx_wdt *wdt = watchdog_get_drvdata(wdog); + unsigned long flags; + spin_lock_irqsave(&wdt->lock, flags); + + writel_relaxed(SECS_TO_WDOG_TICKS(wdog->timeout), wdt->base + WDT_DEFVAL_REG); + writel_relaxed(WDT_START_1, wdt->base + WDT_CTL_REG); + writel_relaxed(WDT_START_2, wdt->base + WDT_CTL_REG); + + spin_unlock_irqrestore(&wdt->lock, flags); + + return 0; +} + +static int bcm96xxx_wdt_stop(struct watchdog_device *wdog) +{ + struct bcm96xxx_wdt *wdt = watchdog_get_drvdata(wdog); + + writel_relaxed(WDT_STOP_1, wdt->base + WDT_CTL_REG); + writel_relaxed(WDT_STOP_2, wdt->base + WDT_CTL_REG); + + dev_info(wdog->dev, "Watchdog timer stopped"); + return 0; +} + +static int bcm96xxx_wdt_set_timeout(struct watchdog_device *wdog, unsigned int t) +{ + wdog->timeout = t; + return 0; +} + +static unsigned int bcm96xxx_wdt_get_timeleft(struct watchdog_device *wdog) +{ + struct bcm96xxx_wdt *wdt = watchdog_get_drvdata(wdog); + + uint32_t ret = readl_relaxed(wdt->base + WDT_CTL_REG); + return WDOG_TICKS_TO_SECS(ret); +} + +static struct watchdog_ops bcm96xxx_wdt_ops = { + .owner = THIS_MODULE, + .start = bcm96xxx_wdt_start, + .stop = bcm96xxx_wdt_stop, + .set_timeout = bcm96xxx_wdt_set_timeout, + .get_timeleft = bcm96xxx_wdt_get_timeleft, +}; + +static struct watchdog_info bcm96xxx_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | + WDIOF_KEEPALIVEPING, + .identity = "BCM96xxx Watchdog timer", +}; + +static struct watchdog_device bcm96xxx_wdt_wdd = { + .info = &bcm96xxx_wdt_info, + .ops = &bcm96xxx_wdt_ops, + .min_timeout = 1, + .max_timeout = WDOG_TICKS_TO_SECS(WDT_MAX_TIME_TICKS), + .timeout = WDOG_TICKS_TO_SECS(WDT_MAX_TIME_TICKS), +}; + +static int bcm96xxx_wdt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct bcm96xxx_wdt *wdt; + int err; + + wdt = devm_kzalloc(dev, sizeof(struct bcm96xxx_wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + platform_set_drvdata(pdev, wdt); + + spin_lock_init(&wdt->lock); + + wdt->base = of_iomap(np, 0); + if (!wdt->base) { + dev_err(dev, "Failed to remap watchdog regs"); + return -ENODEV; + } + + watchdog_set_drvdata(&bcm96xxx_wdt_wdd, wdt); + watchdog_init_timeout(&bcm96xxx_wdt_wdd, heartbeat, dev); + watchdog_set_nowayout(&bcm96xxx_wdt_wdd, nowayout); + err = watchdog_register_device(&bcm96xxx_wdt_wdd); + if (err) { + dev_err(dev, "Failed to register watchdog device"); + iounmap(wdt->base); + return err; + } + + dev_info(dev, "Broadcom BCM96xxx watchdog timer"); + return 0; +} + +static int bcm96xxx_wdt_remove(struct platform_device *pdev) +{ + struct bcm96xxx_wdt *wdt = platform_get_drvdata(pdev); + bcm96xxx_wdt_stop(&bcm96xxx_wdt_wdd); + watchdog_unregister_device(&bcm96xxx_wdt_wdd); + iounmap(wdt->base); + + return 0; +} + +static void bcm96xxx_wdt_shutdown(struct platform_device *pdev) +{ + bcm96xxx_wdt_stop(&bcm96xxx_wdt_wdd); +} + +static const struct of_device_id bcm96xxx_wdt_of_match[] = { + { .compatible = "brcm,bcm96xxx-wdt", }, + {}, +}; +MODULE_DEVICE_TABLE(of, bcm96xxx_wdt_of_match); + +static struct platform_driver bcm96xxx_wdt_driver = { + .probe = bcm96xxx_wdt_probe, + .remove = bcm96xxx_wdt_remove, + .shutdown = bcm96xxx_wdt_shutdown, + .driver = { + .name = "bcm96xxx-wdt", + .of_match_table = bcm96xxx_wdt_of_match, + }, +}; +module_platform_driver(bcm96xxx_wdt_driver); + +module_param(heartbeat, uint, 0); +MODULE_PARM_DESC(heartbeat, "Initial watchdog heartbeat in seconds"); + +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +MODULE_AUTHOR("Farhan Ali "); +MODULE_DESCRIPTION("Driver for Broadcom BCM96xxx watchdog timer"); +MODULE_LICENSE("GPL"); +#endif diff -urN linux-4.1.52-orig/drivers/watchdog/Kconfig linux-4.1.52/drivers/watchdog/Kconfig --- linux-4.1.52-orig/drivers/watchdog/Kconfig 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/watchdog/Kconfig 2020-02-07 01:47:54.000000000 +0800 @@ -140,6 +140,16 @@ To compile this driver as a module, choose M here: the module will be called of_xilinx_wdt. +config BCM96XXX_WDT + tristate "Broadcom BCM96xxx SOCs hardware watchdog" + depends on BCM_KF_WDT + help + Watchdog driver for the built in watchdog hardware in Broadcom + BCM96xxx based SoCs. + + To compile this driver as a loadable module, choose M here. + The module will be called bcm96xxx_wdt. + # ALPHA Architecture # ARM Architecture diff -urN linux-4.1.52-orig/drivers/watchdog/Makefile linux-4.1.52/drivers/watchdog/Makefile --- linux-4.1.52-orig/drivers/watchdog/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/watchdog/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -186,3 +186,6 @@ obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o +ifdef BCM_KF # defined(CONFIG_BCM_KF_WDT) +obj-$(CONFIG_BCM96XXX_WDT) += bcm96xxx_wdt.o +endif # BCM_KF # defined(CONFIG_BCM_KF_WDT) diff -urN linux-4.1.52-orig/drivers/watchdog/watchdog_core.c linux-4.1.52/drivers/watchdog/watchdog_core.c --- linux-4.1.52-orig/drivers/watchdog/watchdog_core.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/watchdog/watchdog_core.c 2020-02-07 01:47:54.000000000 +0800 @@ -151,7 +151,6 @@ return ret; } } - devno = wdd->cdev.dev; wdd->dev = device_create(watchdog_class, wdd->parent, devno, NULL, "watchdog%d", wdd->id); @@ -185,12 +184,22 @@ ret = watchdog_dev_unregister(wdd); if (ret) pr_err("error unregistering /dev/watchdog (err=%d)\n", ret); + device_destroy(watchdog_class, devno); ida_simple_remove(&watchdog_ida, wdd->id); wdd->dev = NULL; + } EXPORT_SYMBOL_GPL(watchdog_unregister_device); +#if defined(CONFIG_BCM_KF_WDT) +void watchdog_force_disable( void ) +{ + watchdog_dev_force_disable(); +} +EXPORT_SYMBOL_GPL(watchdog_force_disable); +#endif + static int __init watchdog_init(void) { int err; diff -urN linux-4.1.52-orig/drivers/watchdog/watchdog_core.h linux-4.1.52/drivers/watchdog/watchdog_core.h --- linux-4.1.52-orig/drivers/watchdog/watchdog_core.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/watchdog/watchdog_core.h 2020-02-07 01:47:54.000000000 +0800 @@ -33,5 +33,8 @@ */ extern int watchdog_dev_register(struct watchdog_device *); extern int watchdog_dev_unregister(struct watchdog_device *); +#if defined(CONFIG_BCM_KF_WDT) +extern int watchdog_dev_force_disable( void ); +#endif extern int __init watchdog_dev_init(void); extern void __exit watchdog_dev_exit(void); diff -urN linux-4.1.52-orig/drivers/watchdog/watchdog_dev.c linux-4.1.52/drivers/watchdog/watchdog_dev.c --- linux-4.1.52-orig/drivers/watchdog/watchdog_dev.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/drivers/watchdog/watchdog_dev.c 2020-02-07 01:47:54.000000000 +0800 @@ -479,7 +479,11 @@ if (err < 0) { mutex_lock(&wdd->lock); if (!test_bit(WDOG_UNREGISTERED, &wdd->status)) +#if defined(CONFIG_BCM_KF_WDT) + dev_dbg(wdd->dev, "watchdog did not stop!\n"); +#else dev_crit(wdd->dev, "watchdog did not stop!\n"); +#endif mutex_unlock(&wdd->lock); watchdog_ping(wdd); } @@ -571,6 +575,7 @@ mutex_unlock(&watchdog->lock); cdev_del(&watchdog->cdev); + if (watchdog->id == 0) { misc_deregister(&watchdog_miscdev); old_wdd = NULL; @@ -578,6 +583,17 @@ return 0; } +#if defined(CONFIG_BCM_KF_WDT) +int watchdog_dev_force_disable( void ) +{ + if( old_wdd ) + { + watchdog_stop(old_wdd); + } + return 0; +} +#endif + /* * watchdog_dev_init: init dev part of watchdog core * diff -urN linux-4.1.52-orig/fs/compat_binfmt_elf.c linux-4.1.52/fs/compat_binfmt_elf.c --- linux-4.1.52-orig/fs/compat_binfmt_elf.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/fs/compat_binfmt_elf.c 2020-02-07 01:47:54.000000000 +0800 @@ -51,6 +51,12 @@ #define elf_prstatus compat_elf_prstatus #define elf_prpsinfo compat_elf_prpsinfo + +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +static void cputime_to_compat_timeval(const cputime_t cputime, + struct compat_timeval *value) __attribute__((unused)); +#endif + /* * Compat version of cputime_to_compat_timeval, perhaps this * should be an inline in . diff -urN linux-4.1.52-orig/fs/compat_ioctl.c linux-4.1.52/fs/compat_ioctl.c --- linux-4.1.52-orig/fs/compat_ioctl.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/fs/compat_ioctl.c 2020-02-07 01:47:54.000000000 +0800 @@ -1015,6 +1015,9 @@ COMPATIBLE_IOCTL(PPPIOCATTCHAN) COMPATIBLE_IOCTL(PPPIOCGCHAN) COMPATIBLE_IOCTL(PPPIOCGL2TPSTATS) +#if defined(CONFIG_BCM_KF_PPP) && defined(CONFIG_BCM_KF_NETDEV_PATH) +COMPATIBLE_IOCTL(PPPIOCSREALDEV) +#endif /* PPPOX */ COMPATIBLE_IOCTL(PPPOEIOCSFWD) COMPATIBLE_IOCTL(PPPOEIOCDFWD) diff -urN linux-4.1.52-orig/fs/ext4/ext4.h linux-4.1.52/fs/ext4/ext4.h --- linux-4.1.52-orig/fs/ext4/ext4.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/fs/ext4/ext4.h 2020-02-07 01:47:54.000000000 +0800 @@ -2836,9 +2836,11 @@ extern int ext4_inline_data_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, int *has_inline, __u64 start, __u64 len); +#if !defined(CONFIG_BCM_KF_MISC_BACKPORTS) extern int ext4_try_to_evict_inline_data(handle_t *handle, struct inode *inode, int needed); +#endif extern void ext4_inline_data_truncate(struct inode *inode, int *has_inline); extern int ext4_convert_inline_data(struct inode *inode); diff -urN linux-4.1.52-orig/fs/ext4/inline.c linux-4.1.52/fs/ext4/inline.c --- linux-4.1.52-orig/fs/ext4/inline.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/fs/ext4/inline.c 2020-02-07 01:47:54.000000000 +0800 @@ -436,6 +436,11 @@ memset((void *)ext4_raw_inode(&is.iloc)->i_block, 0, EXT4_MIN_INLINE_DATA_SIZE); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +/*CVE-2018-10881*/ + memset(ei->i_data, 0, EXT4_MIN_INLINE_DATA_SIZE); +#endif + if (EXT4_HAS_INCOMPAT_FEATURE(inode->i_sb, EXT4_FEATURE_INCOMPAT_EXTENTS)) { if (S_ISDIR(inode->i_mode) || @@ -884,11 +889,17 @@ flags |= AOP_FLAG_NOFS; if (ret == -ENOSPC) { +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +/*CVE-2018-10883*/ + ext4_journal_stop(handle); +#endif ret = ext4_da_convert_inline_data_to_extent(mapping, inode, flags, fsdata); +#if !defined(CONFIG_BCM_KF_MISC_BACKPORTS) ext4_journal_stop(handle); +#endif if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries)) goto retry_journal; @@ -1857,6 +1868,7 @@ return (error < 0 ? error : 0); } +#if !defined(CONFIG_BCM_KF_MISC_BACKPORTS) /*CVE-2018-10883*/ /* * Called during xattr set, and if we can sparse space 'needed', * just create the extent tree evict the data to the outer block. @@ -1892,6 +1904,7 @@ brelse(iloc.bh); return error; } +#endif // !defined(CONFIG_BCM_KF_MISC_BACKPORTS) void ext4_inline_data_truncate(struct inode *inode, int *has_inline) { diff -urN linux-4.1.52-orig/fs/ext4/xattr.c linux-4.1.52/fs/ext4/xattr.c --- linux-4.1.52-orig/fs/ext4/xattr.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/fs/ext4/xattr.c 2020-02-07 01:47:54.000000000 +0800 @@ -1018,6 +1018,7 @@ if (EXT4_I(inode)->i_extra_isize == 0) return -ENOSPC; error = ext4_xattr_set_entry(i, s); +#if !defined(CONFIG_BCM_KF_MISC_BACKPORTS) if (error) { if (error == -ENOSPC && ext4_has_inline_data(inode)) { @@ -1034,6 +1035,11 @@ if (error) return error; } +#else +/*CVE-2018-10883*/ + if (error) + return error; +#endif header = IHDR(inode, ext4_raw_inode(&is->iloc)); if (!IS_LAST_ENTRY(s->first)) { header->h_magic = cpu_to_le32(EXT4_XATTR_MAGIC); @@ -1364,6 +1370,14 @@ /* Find the entry best suited to be pushed into EA block */ entry = NULL; for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) { +#if defined (CONFIG_BCM_KF_MISC_BACKPORTS) +/*CVE-2018-10880*/ + /* never move system.data out of the inode */ + if ((last->e_name_len == 4) && + (last->e_name_index == EXT4_XATTR_INDEX_SYSTEM) && + !memcmp(last->e_name, "data", 4)) + continue; +#endif total_size = EXT4_XATTR_SIZE(le32_to_cpu(last->e_value_size)) + EXT4_XATTR_LEN(last->e_name_len); diff -urN linux-4.1.52-orig/fs/inode.c linux-4.1.52/fs/inode.c --- linux-4.1.52-orig/fs/inode.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/fs/inode.c 2020-02-07 01:47:54.000000000 +0800 @@ -20,6 +20,10 @@ #include #include #include "internal.h" +#if defined(CONFIG_BCM_KF_512MB_DDR) && defined(CONFIG_BCM_512MB_DDR) +#include "./ubifs/ubifs.h" +#include +#endif /* * Inode locking rules: @@ -169,6 +173,12 @@ mapping->host = inode; mapping->flags = 0; atomic_set(&mapping->i_mmap_writable, 0); +#if defined(CONFIG_BCM_KF_512MB_DDR) && defined(CONFIG_BCM_512MB_DDR) + /* force jffs2 and ubifs to use lowmem */ + if (sb->s_magic == UBIFS_SUPER_MAGIC || sb->s_magic == JFFS2_SUPER_MAGIC || sb->s_magic == SQUASHFS_MAGIC) + mapping_set_gfp_mask(mapping, GFP_HIGHUSER_MOVABLE & (~__GFP_HIGHMEM)); + else +#endif mapping_set_gfp_mask(mapping, GFP_HIGHUSER_MOVABLE); mapping->private_data = NULL; mapping->writeback_index = 0; diff -urN linux-4.1.52-orig/fs/jbd2/transaction.c linux-4.1.52/fs/jbd2/transaction.c --- linux-4.1.52-orig/fs/jbd2/transaction.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/fs/jbd2/transaction.c 2020-02-07 01:47:54.000000000 +0800 @@ -1283,11 +1283,18 @@ * of the transaction. This needs to be done * once a transaction -bzzz */ +#if !defined(CONFIG_BCM_KF_MISC_BACKPORTS) /*CVE-2018-10883*/ jh->b_modified = 1; +#endif if (handle->h_buffer_credits <= 0) { ret = -ENOSPC; goto out_unlock_bh; } + +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +/*CVE-2018-10883*/ + jh->b_modified = 1; +#endif handle->h_buffer_credits--; } diff -urN linux-4.1.52-orig/fs/jffs2/compr.c linux-4.1.52/fs/jffs2/compr.c --- linux-4.1.52-orig/fs/jffs2/compr.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/fs/jffs2/compr.c 2020-02-07 01:47:54.000000000 +0800 @@ -156,6 +156,14 @@ uint32_t orig_slen, orig_dlen; uint32_t best_slen=0, best_dlen=0; +#if defined(CONFIG_BCM_KF_JFFS) + if( (f->inocache->flags & INO_FLAGS_COMPR_NONE) == INO_FLAGS_COMPR_NONE ) + { + ret = JFFS2_COMPR_NONE; + goto out; + } +#endif + if (c->mount_opts.override_compr) mode = c->mount_opts.compr; else @@ -241,6 +249,10 @@ pr_err("unknown compression mode\n"); } +#if defined(CONFIG_BCM_KF_JFFS) +out: +#endif + if (ret == JFFS2_COMPR_NONE) { *cpage_out = data_in; *datalen = *cdatalen; diff -urN linux-4.1.52-orig/fs/jffs2/compr_rtime.c linux-4.1.52/fs/jffs2/compr_rtime.c --- linux-4.1.52-orig/fs/jffs2/compr_rtime.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/fs/jffs2/compr_rtime.c 2020-02-07 01:47:54.000000000 +0800 @@ -39,6 +39,11 @@ memset(positions,0,sizeof(positions)); +#if defined (CONFIG_BCM_KF_JFFS) + if (*dstlen < 2) + return -1; + +#endif while (pos < (*sourcelen) && outpos <= (*dstlen)-2) { int backpos, runlen=0; unsigned char value; diff -urN linux-4.1.52-orig/fs/jffs2/dir.c linux-4.1.52/fs/jffs2/dir.c --- linux-4.1.52-orig/fs/jffs2/dir.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/fs/jffs2/dir.c 2020-02-07 01:47:54.000000000 +0800 @@ -34,8 +34,13 @@ static int jffs2_mkdir (struct inode *,struct dentry *,umode_t); static int jffs2_rmdir (struct inode *,struct dentry *); static int jffs2_mknod (struct inode *,struct dentry *,umode_t,dev_t); +#if defined(CONFIG_BCM_KF_JFFS2_OVERLAY) static int jffs2_rename (struct inode *, struct dentry *, - struct inode *, struct dentry *); + struct inode *, struct dentry *, unsigned int); +#else +static int jffs2_rename (struct inode *, struct dentry *, + struct inode *, struct dentry *); +#endif const struct file_operations jffs2_dir_operations = { @@ -57,7 +62,11 @@ .mkdir = jffs2_mkdir, .rmdir = jffs2_rmdir, .mknod = jffs2_mknod, +#if defined(CONFIG_BCM_KF_JFFS2_OVERLAY) + .rename2 = jffs2_rename, +#else .rename = jffs2_rename, +#endif .get_acl = jffs2_get_acl, .set_acl = jffs2_set_acl, .setattr = jffs2_setattr, @@ -756,21 +765,68 @@ return ret; } +#if defined(CONFIG_BCM_KF_JFFS2_OVERLAY) +static int jffs2_whiteout(struct inode *old_dir, struct dentry *old_dentry) +{ + struct dentry *wh; + int err; + + wh = d_alloc(old_dentry->d_parent, &old_dentry->d_name); + if (!wh) + return -ENOMEM; + + err = jffs2_mknod(old_dir, wh, S_IFCHR | WHITEOUT_MODE, + WHITEOUT_DEV); + if (err) + return err; + + d_rehash(wh); + return 0; +} +#endif + +#if defined(CONFIG_BCM_KF_JFFS2_OVERLAY) static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry, - struct inode *new_dir_i, struct dentry *new_dentry) + struct inode *new_dir_i, struct dentry *new_dentry, unsigned int flags) +#else +static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry, + struct inode *new_dir_i, struct dentry *new_dentry) +#endif { int ret; struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb); struct jffs2_inode_info *victim_f = NULL; uint8_t type; uint32_t now; +#if defined(CONFIG_BCM_KF_JFFS2_OVERLAY) + struct inode *fst_inode = d_inode(old_dentry); + struct inode *snd_inode = d_inode(new_dentry); + + if (flags & ~(RENAME_WHITEOUT | RENAME_EXCHANGE)) + return -EINVAL; + + if ((flags & RENAME_EXCHANGE) && (old_dir_i != new_dir_i)) { + if(S_ISDIR(fst_inode->i_mode) && !S_ISDIR(snd_inode->i_mode)) { + inc_nlink(new_dir_i); + drop_nlink(old_dir_i); + } + else if(!S_ISDIR(fst_inode->i_mode) && S_ISDIR(snd_inode->i_mode)) { + drop_nlink(new_dir_i); + inc_nlink(old_dir_i); + } + } +#endif /* The VFS will check for us and prevent trying to rename a * file over a directory and vice versa, but if it's a directory, * the VFS can't check whether the victim is empty. The filesystem * needs to do that for itself. */ +#if defined(CONFIG_BCM_KF_JFFS2_OVERLAY) + if (d_really_is_positive(new_dentry) && !(flags & RENAME_EXCHANGE)) { +#else if (d_really_is_positive(new_dentry)) { +#endif victim_f = JFFS2_INODE_INFO(d_inode(new_dentry)); if (d_is_dir(new_dentry)) { struct jffs2_full_dirent *fd; @@ -805,7 +861,11 @@ if (ret) return ret; +#if defined(CONFIG_BCM_KF_JFFS2_OVERLAY) + if (victim_f && !(flags & RENAME_EXCHANGE)) { +#else if (victim_f) { +#endif /* There was a victim. Kill it off nicely */ if (d_is_dir(new_dentry)) clear_nlink(d_inode(new_dentry)); @@ -825,12 +885,29 @@ /* If it was a directory we moved, and there was no victim, increase i_nlink on its new parent */ +#if defined(CONFIG_BCM_KF_JFFS2_OVERLAY) + if (d_is_dir(old_dentry) && !victim_f && !(flags & RENAME_EXCHANGE)) + inc_nlink(new_dir_i); +#else if (d_is_dir(old_dentry) && !victim_f) inc_nlink(new_dir_i); - - /* Unlink the original */ - ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i), - old_dentry->d_name.name, old_dentry->d_name.len, NULL, now); +#endif + +#if defined(CONFIG_BCM_KF_JFFS2_OVERLAY) + if (flags & RENAME_WHITEOUT) + /* Replace with whiteout */ + ret = jffs2_whiteout(old_dir_i, old_dentry); + else if (flags & RENAME_EXCHANGE) + /* Replace the original */ + ret = jffs2_do_link(c, JFFS2_INODE_INFO(old_dir_i), + d_inode(new_dentry)->i_ino, type, + old_dentry->d_name.name, old_dentry->d_name.len, + now); + else +#endif + /* Unlink the original */ + ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i), + old_dentry->d_name.name, old_dentry->d_name.len, NULL, now); /* We don't touch inode->i_nlink */ @@ -856,8 +933,12 @@ new_dir_i->i_mtime = new_dir_i->i_ctime = ITIME(now); return ret; } - + +#if defined(CONFIG_BCM_KF_JFFS2_OVERLAY) + if (d_is_dir(old_dentry) && !(flags & RENAME_EXCHANGE)) +#else if (d_is_dir(old_dentry)) +#endif drop_nlink(old_dir_i); new_dir_i->i_mtime = new_dir_i->i_ctime = old_dir_i->i_mtime = old_dir_i->i_ctime = ITIME(now); diff -urN linux-4.1.52-orig/fs/jffs2/erase.c linux-4.1.52/fs/jffs2/erase.c --- linux-4.1.52-orig/fs/jffs2/erase.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/fs/jffs2/erase.c 2020-02-07 01:47:54.000000000 +0800 @@ -75,6 +75,46 @@ ((struct erase_priv_struct *)instr->priv)->jeb = jeb; ((struct erase_priv_struct *)instr->priv)->c = c; +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + if (c->mtd->flags & MTD_NAND_NOP1) + { /* check if block is empty first, may not have to erase. must do this at the time of erasing a block + and cannot put this in jffs2_check_nand_cleanmarker since that routine is called often, even when not + attempting to erase the block and even in non writeable JFFS2 partition. Do this only for NOP=1 device since + device is not allowed to have write to spare area only (JFFS2 clean marker inserted after erase) and thus + there will be an erase attempt at every boot when JFFS2 checks for erased blocks and the missing clean + marker. Do not do erase check for NOP > 1 devices since they are allowed to have a JFFS2 clean marker + inserted and by not doing the erase check this will save time */ + struct mtd_oob_ops ops; + loff_t page_offset; + int i, dirty = 0; + unsigned char buf[c->mtd->writesize + c->mtd->oobsize]; + + for (page_offset = 0; !dirty && (page_offset < c->mtd->erasesize); page_offset += c->mtd->writesize) + { // check to see that ECC is empty to determine if page is erased + ops.mode = MTD_OPS_RAW; + ops.ooblen = c->mtd->oobsize; + ops.oobbuf = buf + c->mtd->writesize; + ops.len = c->mtd->writesize; + ops.ooboffs = ops.retlen = ops.oobretlen = 0; + ops.datbuf = buf; + + i = mtd_read_oob(c->mtd, jeb->offset + page_offset, &ops); + + if (i || (ops.oobretlen != ops.ooblen) || (ops.retlen != ops.len)) + dirty = 1; + + for (i = 0; !dirty && (i < (c->mtd->writesize + c->mtd->oobsize)); i++) + if (buf[i] != 0xFF) + dirty = 1; + } + if (!dirty) + { + instr->state = MTD_ERASE_DONE; + jffs2_erase_callback(instr); + return; + } + } +#endif ret = mtd_erase(c->mtd, instr); if (!ret) diff -urN linux-4.1.52-orig/fs/jffs2/gc.c linux-4.1.52/fs/jffs2/gc.c --- linux-4.1.52-orig/fs/jffs2/gc.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/fs/jffs2/gc.c 2020-02-07 01:47:54.000000000 +0800 @@ -903,6 +903,11 @@ for (raw = f->inocache->nodes; raw != (void *)f->inocache; raw = raw->next_in_ino) { +#if defined(CONFIG_BCM_KF_JFFS) + if (!raw) + break; +#endif + cond_resched(); /* We only care about obsolete ones */ @@ -923,7 +928,20 @@ /* This is an obsolete node belonging to the same directory, and it's of the right length. We need to take a closer look...*/ + #if defined(CONFIG_BCM_KF_JFFS) + /* The lock, erase_free_sem, needs to be unlocked here in order to prevent a possible + * deadlock. Without doing this, the following condition can occur. + * thread 1: brcmnand_erase => brcmnand_get_device (gets and holds chip_lock) => + * jffs2_erase_pending_blocks (blocks trying to get erase_free_sem) + * thread 2: jffs2_garbage_collect_deletion_dirent (gets and holds erase_free_sem) => + * brcmnand_get_device (blocks trying to get chip_lock) + */ + mutex_unlock(&c->erase_free_sem); + ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)rd); + mutex_lock(&c->erase_free_sem); +#else ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)rd); +#endif if (ret) { pr_warn("%s(): Read error (%d) reading obsolete node at %08x\n", __func__, ret, ref_offset(raw)); diff -urN linux-4.1.52-orig/fs/jffs2/nodelist.h linux-4.1.52/fs/jffs2/nodelist.h --- linux-4.1.52-orig/fs/jffs2/nodelist.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/fs/jffs2/nodelist.h 2020-02-07 01:47:54.000000000 +0800 @@ -203,6 +203,10 @@ #define INOCACHE_HASHSIZE_MIN 128 #define INOCACHE_HASHSIZE_MAX 1024 +#if defined(CONFIG_BCM_KF_JFFS) +#define INO_FLAGS_COMPR_NONE 0x80 +#endif + #define write_ofs(c) ((c)->nextblock->offset + (c)->sector_size - (c)->nextblock->free_size) /* diff -urN linux-4.1.52-orig/fs/jffs2/readinode.c linux-4.1.52/fs/jffs2/readinode.c --- linux-4.1.52-orig/fs/jffs2/readinode.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/fs/jffs2/readinode.c 2020-02-07 01:47:54.000000000 +0800 @@ -1318,6 +1318,12 @@ if (f->inocache->state == INO_STATE_READING) jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT); +#if defined(CONFIG_BCM_KF_JFFS) + /* Set a "not compressed" flag so the inode does not get compressed when moved. */ + if( latest_node->compr == JFFS2_COMPR_NONE ) + f->inocache->flags |= INO_FLAGS_COMPR_NONE; +#endif + return 0; } diff -urN linux-4.1.52-orig/fs/overlayfs/copy_up.c linux-4.1.52/fs/overlayfs/copy_up.c --- linux-4.1.52-orig/fs/overlayfs/copy_up.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/fs/overlayfs/copy_up.c 2020-02-07 01:47:54.000000000 +0800 @@ -311,7 +311,9 @@ struct dentry *upperdir; struct dentry *upperdentry; const struct cred *old_cred; +#if !defined(CONFIG_BCM_KF_OVERLAYFS_BACKPORTS) struct cred *override_cred; +#endif char *link = NULL; if (WARN_ON(!workdir)) @@ -330,6 +332,9 @@ return PTR_ERR(link); } +#if defined(CONFIG_BCM_KF_OVERLAYFS_BACKPORTS) + old_cred = ovl_override_creds(dentry->d_sb); +#else err = -ENOMEM; override_cred = prepare_creds(); if (!override_cred) @@ -352,6 +357,7 @@ cap_raise(override_cred->cap_effective, CAP_CHOWN); cap_raise(override_cred->cap_effective, CAP_MKNOD); old_cred = override_creds(override_cred); +#endif err = -EIO; if (lock_rename(workdir, upperdir) != NULL) { @@ -381,9 +387,10 @@ unlock_rename(workdir, upperdir); out_put_cred: revert_creds(old_cred); +#if !defined(CONFIG_BCM_KF_OVERLAYFS_BACKPORTS) put_cred(override_cred); - out_free_link: +#endif if (link) free_page((unsigned long) link); diff -urN linux-4.1.52-orig/fs/overlayfs/dir.c linux-4.1.52/fs/overlayfs/dir.c --- linux-4.1.52-orig/fs/overlayfs/dir.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/fs/overlayfs/dir.c 2020-02-07 01:47:54.000000000 +0800 @@ -405,6 +405,9 @@ err = ovl_create_upper(dentry, inode, &stat, link, hardlink); } else { const struct cred *old_cred; +#if defined(CONFIG_BCM_KF_OVERLAYFS_BACKPORTS) + old_cred = ovl_override_creds(dentry->d_sb); +#else struct cred *override_cred; err = -ENOMEM; @@ -421,12 +424,15 @@ cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE); cap_raise(override_cred->cap_effective, CAP_FOWNER); old_cred = override_creds(override_cred); - +#endif err = ovl_create_over_whiteout(dentry, inode, &stat, link, hardlink); revert_creds(old_cred); +#if !defined(CONFIG_BCM_KF_OVERLAYFS_BACKPORTS) put_cred(override_cred); +#endif + } if (!err) @@ -657,13 +663,15 @@ err = ovl_remove_upper(dentry, is_dir); } else { const struct cred *old_cred; +#if defined(CONFIG_BCM_KF_OVERLAYFS_BACKPORTS) + old_cred = ovl_override_creds(dentry->d_sb); +#else struct cred *override_cred; err = -ENOMEM; override_cred = prepare_creds(); if (!override_cred) goto out_drop_write; - /* * CAP_SYS_ADMIN for setting xattr on whiteout, opaque dir * CAP_DAC_OVERRIDE for create in workdir, rename @@ -677,11 +685,14 @@ cap_raise(override_cred->cap_effective, CAP_FSETID); cap_raise(override_cred->cap_effective, CAP_CHOWN); old_cred = override_creds(override_cred); +#endif err = ovl_remove_and_whiteout(dentry, is_dir); revert_creds(old_cred); +#if !defined(CONFIG_BCM_KF_OVERLAYFS_BACKPORTS) put_cred(override_cred); +#endif } out_drop_write: ovl_drop_write(dentry); @@ -720,7 +731,9 @@ bool new_is_dir = false; struct dentry *opaquedir = NULL; const struct cred *old_cred = NULL; +#if !defined(CONFIG_BCM_KF_OVERLAYFS_BACKPORTS) struct cred *override_cred = NULL; +#endif err = -EINVAL; if (flags & ~(RENAME_EXCHANGE | RENAME_NOREPLACE)) @@ -789,6 +802,10 @@ old_opaque = !OVL_TYPE_PURE_UPPER(old_type); new_opaque = !OVL_TYPE_PURE_UPPER(new_type); +#if defined(CONFIG_BCM_KF_OVERLAYFS_BACKPORTS) + if (old_opaque || new_opaque) + old_cred = ovl_override_creds(old->d_sb); +#else if (old_opaque || new_opaque) { err = -ENOMEM; override_cred = prepare_creds(); @@ -809,6 +826,7 @@ cap_raise(override_cred->cap_effective, CAP_CHOWN); old_cred = override_creds(override_cred); } +#endif if (overwrite && OVL_TYPE_MERGE_OR_LOWER(new_type) && new_is_dir) { opaquedir = ovl_check_empty_and_clear(new); @@ -941,7 +959,9 @@ out_revert_creds: if (old_opaque || new_opaque) { revert_creds(old_cred); +#if !defined(CONFIG_BCM_KF_OVERLAYFS_BACKPORTS) put_cred(override_cred); +#endif } out_drop_write: ovl_drop_write(old); diff -urN linux-4.1.52-orig/fs/overlayfs/inode.c linux-4.1.52/fs/overlayfs/inode.c --- linux-4.1.52-orig/fs/overlayfs/inode.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/fs/overlayfs/inode.c 2020-02-07 01:47:54.000000000 +0800 @@ -393,6 +393,11 @@ ovl_path_upper(dentry, &realpath); } +#if defined(CONFIG_BCM_KF_OVERLAYFS_BACKPORTS) + if (realpath.dentry->d_flags & DCACHE_OP_SELECT_INODE) + return realpath.dentry->d_op->d_select_inode(realpath.dentry, file_flags); +#endif + return d_backing_inode(realpath.dentry); } diff -urN linux-4.1.52-orig/fs/overlayfs/overlayfs.h linux-4.1.52/fs/overlayfs/overlayfs.h --- linux-4.1.52-orig/fs/overlayfs/overlayfs.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/fs/overlayfs/overlayfs.h 2020-02-07 01:47:54.000000000 +0800 @@ -145,6 +145,9 @@ struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry); void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache); struct dentry *ovl_workdir(struct dentry *dentry); +#if defined(CONFIG_BCM_KF_OVERLAYFS_BACKPORTS) +const struct cred *ovl_override_creds(struct super_block *sb); +#endif int ovl_want_write(struct dentry *dentry); void ovl_drop_write(struct dentry *dentry); bool ovl_dentry_is_opaque(struct dentry *dentry); diff -urN linux-4.1.52-orig/fs/overlayfs/readdir.c linux-4.1.52/fs/overlayfs/readdir.c --- linux-4.1.52-orig/fs/overlayfs/readdir.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/fs/overlayfs/readdir.c 2020-02-07 01:47:54.000000000 +0800 @@ -36,6 +36,9 @@ struct ovl_readdir_data { struct dir_context ctx; +#if defined(CONFIG_BCM_KF_OVERLAYFS_BACKPORTS) + struct dentry *dentry; +#endif bool is_merge; struct rb_root root; struct list_head *list; @@ -205,6 +208,9 @@ struct ovl_cache_entry *p; struct dentry *dentry; const struct cred *old_cred; +#if defined(CONFIG_BCM_KF_OVERLAYFS_BACKPORTS) + old_cred = ovl_override_creds(rdd->dentry->d_sb); +#else struct cred *override_cred; override_cred = prepare_creds(); @@ -216,6 +222,7 @@ */ cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE); old_cred = override_creds(override_cred); +#endif err = mutex_lock_killable(&dir->d_inode->i_mutex); if (!err) { @@ -231,7 +238,9 @@ mutex_unlock(&dir->d_inode->i_mutex); } revert_creds(old_cred); +#if !defined(CONFIG_BCM_KF_OVERLAYFS_BACKPORTS) put_cred(override_cred); +#endif return err; } @@ -287,6 +296,9 @@ struct path realpath; struct ovl_readdir_data rdd = { .ctx.actor = ovl_fill_merge, +#if defined(CONFIG_BCM_KF_OVERLAYFS_BACKPORTS) + .dentry = dentry, +#endif .list = list, .root = RB_ROOT, .is_merge = false, diff -urN linux-4.1.52-orig/fs/overlayfs/super.c linux-4.1.52/fs/overlayfs/super.c --- linux-4.1.52-orig/fs/overlayfs/super.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/fs/overlayfs/super.c 2020-02-07 01:47:54.000000000 +0800 @@ -42,6 +42,10 @@ long lower_namelen; /* pathnames of lower and upper dirs, for show_options */ struct ovl_config config; +#if defined(CONFIG_BCM_KF_OVERLAYFS_BACKPORTS) + /* creds of process who forced instantiation of super block */ + const struct cred *creator_cred; +#endif }; struct ovl_dir_cache; @@ -202,6 +206,15 @@ return oe->opaque; } +#if defined(CONFIG_BCM_KF_OVERLAYFS_BACKPORTS) +const struct cred *ovl_override_creds(struct super_block *sb) +{ + struct ovl_fs *ofs = sb->s_fs_info; + + return override_creds(ofs->creator_cred); +} +#endif + void ovl_dentry_set_opaque(struct dentry *dentry, bool opaque) { struct ovl_entry *oe = dentry->d_fsdata; @@ -482,6 +495,9 @@ kfree(ufs->config.lowerdir); kfree(ufs->config.upperdir); kfree(ufs->config.workdir); +#if defined(CONFIG_BCM_KF_OVERLAYFS_BACKPORTS) + put_cred(ufs->creator_cred); +#endif kfree(ufs); } @@ -985,10 +1001,20 @@ sb->s_d_op = &ovl_dentry_operations; +#if defined(CONFIG_BCM_KF_OVERLAYFS_BACKPORTS) + ufs->creator_cred = prepare_creds(); + if (!ufs->creator_cred) + goto out_put_lower_mnt; +#endif + err = -ENOMEM; oe = ovl_alloc_entry(numlower); if (!oe) +#if defined(CONFIG_BCM_KF_OVERLAYFS_BACKPORTS) + goto out_put_cred; +#else goto out_put_lower_mnt; +#endif root_dentry = d_make_root(ovl_new_inode(sb, S_IFDIR, oe)); if (!root_dentry) @@ -1021,6 +1047,10 @@ out_free_oe: kfree(oe); +#if defined(CONFIG_BCM_KF_OVERLAYFS_BACKPORTS) +out_put_cred: + put_cred(ufs->creator_cred); +#endif out_put_lower_mnt: for (i = 0; i < ufs->numlower; i++) mntput(ufs->lower_mnt[i]); diff -urN linux-4.1.52-orig/fs/proc/array.c linux-4.1.52/fs/proc/array.c --- linux-4.1.52-orig/fs/proc/array.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/fs/proc/array.c 2020-02-07 01:47:54.000000000 +0800 @@ -338,6 +338,13 @@ cpumask_pr_args(&task->cpus_allowed)); seq_printf(m, "Cpus_allowed_list:\t%*pbl\n", cpumask_pr_args(&task->cpus_allowed)); +#if defined(CONFIG_BCM_KF_CPU_AFFINITY_HINT) && \ + defined(CONFIG_BCM_PROC_CPU_AFFINITY_HINT) + seq_printf(m, "Cpus_hint:\t%*pb\n", + cpumask_pr_args(&task->cpus_hint)); + seq_printf(m, "Cpus_hint_list:\t%*pbl\n", + cpumask_pr_args(&task->cpus_hint)); +#endif } int proc_pid_status(struct seq_file *m, struct pid_namespace *ns, diff -urN linux-4.1.52-orig/fs/proc/Makefile linux-4.1.52/fs/proc/Makefile --- linux-4.1.52-orig/fs/proc/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/fs/proc/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -30,3 +30,6 @@ proc-$(CONFIG_PROC_VMCORE) += vmcore.o proc-$(CONFIG_PRINTK) += kmsg.o proc-$(CONFIG_PROC_PAGE_MONITOR) += page.o +ifdef BCM_KF # defined(CONFIG_BCM_KF_PROC_BCM) +proc-$(CONFIG_BCM_KF_PROC_BCM) += proc_brcm.o +endif # BCM_KF \ No newline at end of file diff -urN linux-4.1.52-orig/fs/proc/proc_brcm.c linux-4.1.52/fs/proc/proc_brcm.c --- linux-4.1.52-orig/fs/proc/proc_brcm.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/fs/proc/proc_brcm.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,513 @@ +#if defined(CONFIG_BCM_KF_PROC_BCM) +/* + * + <:copyright-BRCM:2011:DUAL/GPL:standard + + Copyright (c) 2011 Broadcom + All Rights Reserved + + Unless you and Broadcom execute a separate written software license + agreement governing use of this software, this software is licensed + to you under the terms of the GNU General Public License version 2 + (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, + with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + + Not withstanding the above, under no circumstances may you combine + this software in any way with any other Broadcom software provided + under a license other than the GPL, without Broadcom's express prior + written consent. + + :> +*/ + +/************************************************************ + proc_brcm.c + + procfs entries like proc/shirkmem, proc/brcm/pagewalk and proc/brcm/cstat + + 9/27/2006 Xi Wang Created + 11/12/2008 Xi Wang Updated for 2.6.21 + + + ************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_BCM_CFE_XARGS +extern int bcm_blxparms_init(struct proc_dir_entry *pentry); +#endif +//extern int proc_calc_metrics(char *page, char **start, off_t off, int count, int *eof, int len); +int proc_calc_metrics(char *page, char **start, off_t off, int count, int *eof, int len) +{ + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + + +#ifdef CONFIG_BCM_CSTAT + + +#define PERF_C_INTERVAL (HZ*1) +#define DIV 1000 +#define N_INST +#define COUNTER_RESET_V 0xffffffffu + +#define BRCM_PERFREG_BASE 0xff420000 +typedef struct { + unsigned global_ctrl; + unsigned ctrl[2]; + unsigned donottouch[1]; + unsigned counters[4]; +} PerformanceControl; +#define BRCM_PERF ((volatile PerformanceControl *) BRCM_PERFREG_BASE) + +struct timer_list cachestat_timer; +int cachestat_interval = 0; + +void static brcm_perf_timer_func(unsigned long data); +static int perf_counters_proc(char *page, char **start, off_t off, int count, int *eof, void *data); + + +static void cachestat_timer_func(unsigned long data) +{ + static int tp = 0; + static int item = 0; + register unsigned long temp; + int i,ratio; + unsigned tdiv = cachestat_interval*DIV; + unsigned counters[4]; + + for (i=0;i<4;i++) { + counters[i] = COUNTER_RESET_V - BRCM_PERF->counters[i]; + BRCM_PERF->counters[i]=COUNTER_RESET_V; + } + + if (item == 0) { + printk("TP %d instruction miss %uk/sec\n", tp, counters[0]/tdiv); + printk("TP %d instruction hit %uk/sec\n", tp, counters[1]/tdiv); + ratio = (counters[0]+counters[1])? counters[0]*1000/(counters[0]+counters[1]) : 0; + printk("TP %d miss ratio %u\n", tp, ratio); + } + + if (item == 1) { + printk("TP %d data miss %uk/sec\n", tp, counters[0]/tdiv); + printk("TP %d data hit %uk/sec\n", tp, counters[1]/tdiv); + ratio = (counters[0]+counters[1])? counters[0]*1000/(counters[0]+counters[1]) : 0; + printk("TP %d miss ratio %u\n", tp, ratio); + } + +#if defined(N_INST) + printk("TP %d number of instructions %uk/sec\n", tp, counters[2]/tdiv); + printk("TP %d number of cycles %uk/sec\n", tp, counters[3]/tdiv); +#endif + + if (tp >= 1) { + printk("\n"); + tp = 0; + if (item >= 1) { + item = 0; + } + else { + item++; + } + } + else { + tp++; + } + + if (tp ==0) { + asm("mfc0 %0,$22,2" : "=d" (temp)); + temp &= 0x3fffffff; + temp |= 0x00000000; + asm("mtc0 %0,$22,2" :: "d" (temp)); + } + else { + asm("mfc0 %0,$22,2" : "=d" (temp)); + temp &= 0x3fffffff; + temp |= 0x40000000; + asm("mtc0 %0,$22,2" :: "d" (temp)); + } + + if (item == 0) { + BRCM_PERF->global_ctrl = 0x0; + BRCM_PERF->global_ctrl = 0x80000018; + if (tp == 0) { + BRCM_PERF->ctrl[0] = 0x80188014; + } + else { + BRCM_PERF->ctrl[0] = 0xa018a014; + } + } + + if (item == 1) { + BRCM_PERF->global_ctrl = 0x0; + BRCM_PERF->global_ctrl = 0x80000011; + if (tp == 0) { + BRCM_PERF->ctrl[0] = 0x80288024; + } + else { + BRCM_PERF->ctrl[0] = 0xa028a024; + } + } + +#if defined(N_INST) + if (tp ==0) { + BRCM_PERF->ctrl[1] = 0x80488044; + } + else { + BRCM_PERF->ctrl[1] = 0xa048a044; + } +#endif + + cachestat_timer.expires = jiffies+cachestat_interval*HZ; + add_timer(&cachestat_timer); +} + + +static void cachestat_start() +{ + int i; + + printk("Starting cache performance counters..\n\n"); + + init_timer(&cachestat_timer); + cachestat_timer.expires = jiffies+HZ; + cachestat_timer.data = 0; + cachestat_timer.function = cachestat_timer_func; + + for (i=0;i<4;i++) { + BRCM_PERF->counters[i]=COUNTER_RESET_V; + } + + BRCM_PERF->global_ctrl = 0x80000018; + BRCM_PERF->global_ctrl = 0x80000011; + + add_timer(&cachestat_timer); +} + +static void cachestat_stop() +{ + del_timer_sync(&cachestat_timer); + printk("Cache performance counting stopped..\n"); +} + +static int cachestat_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len=0; + + len += sprintf(page, "%d\n", cachestat_interval); + + return proc_calc_metrics(page, start, off, count, eof, len); + +} + + +static int cachestat_write_proc(struct file *file, const char *buf, unsigned long count, void *data) +{ + char ibuf[20]; + int arg; + + if (count<1 || count>sizeof(ibuf)) { + return -EFAULT; + } + if (copy_from_user(ibuf, buf, count)) { + return -EFAULT; + } + ibuf[count] = 0; + + if (sscanf(ibuf, "%d\n", &arg) == 1) { + if (arg>=0) { + if (arg && !cachestat_interval) { + cachestat_interval = arg; + cachestat_start(); + } + else if (!arg && cachestat_interval) { + cachestat_interval = arg; + cachestat_stop(); + } + else { + cachestat_interval = arg; + } + } + return count; + } + return -EFAULT; +} + +#endif + +#if defined(CONFIG_BRCM_OLT_FPGA_RESTORE) +/* These functions save and restore the state of the olt fpga */ +static int olt_fpga_restore_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + char *op; + struct pci_dev *dev; + op = page + off; + *op = '\0'; + *eof = 1; + dev = pci_get_device(0x1172, 0x0004, NULL); + if (dev != NULL) { + printk("found fpga\n"); + printk("save fpga config\n"); + (void)pci_save_state(dev); + } else { + printk("no fpga\n"); + return -EFAULT; + } + return 0; +} + +static int olt_fpga_restore_write_proc(struct file *file, const char *buf, unsigned long count, void *data) +{ + struct pci_dev *dev; + dev = pci_get_device(0x1172, 0x0004, NULL); + if (dev != NULL) { + printk("found fpga\n"); + } else { + printk("no fpga\n"); + return -EFAULT; + } + printk("restore fpga config\n"); + (void)pci_restore_state(dev); + return count; +} + +#endif + +#if 0 +static int cp0regs_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + register unsigned long temp; + unsigned long *mips_core_base = NULL; + int len=0; + + len += sprintf(page+len, "Running on processor %d\n", smp_processor_id()); + + len += sprintf(page+len, "Status = %x\n", __read_32bit_c0_register($12, 0)); + len += sprintf(page+len, "Cause = %x\n", __read_32bit_c0_register($13, 0)); + + len += sprintf(page+len, "BRCM Config_0 = %x\n", __read_32bit_c0_register($22, 0)); + len += sprintf(page+len, "CMT Interrupt = %x\n", __read_32bit_c0_register($22, 1)); + len += sprintf(page+len, "CMT Control = %x\n", __read_32bit_c0_register($22, 2)); + len += sprintf(page+len, "CMT Local = %x\n", __read_32bit_c0_register($22, 3)); + len += sprintf(page+len, "BRCM Config_1 = %x\n", __read_32bit_c0_register($22, 5)); + + temp = __read_32bit_c0_register($22, 6); + mips_core_base =(unsigned long *) (temp & 0xfffc0000); + len += sprintf(page+len, "Core Base = %x\n", temp); + len += sprintf(page+len, "RAC Config (%x) = %x\n", mips_core_base, *mips_core_base); + len += sprintf(page+len, "RAC Range (%x) = %x\n", mips_core_base+1, *(mips_core_base+1)); + len += sprintf(page+len, "RAC Config1 (%x) = %x\n", mips_core_base+2, *(mips_core_base+2)); + len += sprintf(page+len, "LMB (%x) = %x\n", mips_core_base+7, *(mips_core_base+7)); + + len += sprintf(page+len, "\n"); + + *(page+len) = 0; + len++; + return proc_calc_metrics(page, start, off, count, eof, len); +} +#endif + + +#ifdef CONFIG_BRCM_VMTOOLS + + +#define ALLPAGES (1024*1024) + +extern int pagewalk(char *print); +extern int proc_calc_metrics(char *page, char **start, off_t off, int count, int *eof, int len); +extern int meminfo_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data); + + +static int shrinkmem_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len; + + len = sprintf(page, "\nTry to free as many pages as possible (most of pages left will be data pages), then show memory info.\n\n"); + + shrink_all_memory(ALLPAGES); + + len += meminfo_read_proc(page+len, start, off, count, eof, data); + + return proc_calc_metrics(page, start, off, count, eof, len); +} + + +static int pagewalk_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len; + + printk("\nList all occupied memory pages in the system and show what they are used for. "); + printk("(output generated by printk - i.e. not for file operations)\n\n"); + + len = pagewalk(page); + + return proc_calc_metrics(page, start, off, count, eof, len); +} + + +static int shrinkpagewalk_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len; + + printk("\nTry to free as many pages as possible (most of pages left will be data pages), then:\n"); + printk("List all occupied memory pages in the system and show what they are used for. "); + printk("(output generated by printk - i.e. not for file operations)\n\n"); + + shrink_all_memory(ALLPAGES); + + len = pagewalk(page); + + return proc_calc_metrics(page, start, off, count, eof, len); +} + +#endif + + +/**************************************************************************** + * This section is for reporting kernel configs + ****************************************************************************/ + + +int bcm_kernel_config_smp=0; +int bcm_kernel_config_preempt=0; +int bcm_kernel_config_debug_spinlock=0; +int bcm_kernel_config_debug_mutexes=0; + +EXPORT_SYMBOL(bcm_kernel_config_smp); +EXPORT_SYMBOL(bcm_kernel_config_preempt); +EXPORT_SYMBOL(bcm_kernel_config_debug_spinlock); +EXPORT_SYMBOL(bcm_kernel_config_debug_mutexes); + +static int bcm_kernel_config_show(struct seq_file *m, void *v) +{ + seq_printf(m, "CONFIG_SMP=%d\n", bcm_kernel_config_smp); + seq_printf(m, "CONFIG_PREEMPT=%d\n", bcm_kernel_config_preempt); + seq_printf(m, "CONFIG_DEBUG_SPINLOCK=%d\n", bcm_kernel_config_debug_spinlock); + seq_printf(m, "CONFIG_DEBUG_MUTEXES=%d\n", bcm_kernel_config_debug_mutexes); + return 0; +} + +static int bcm_kernel_config_open(struct inode *inode, struct file *file) +{ + return single_open(file, bcm_kernel_config_show, NULL); +} + +static const struct file_operations proc_kernel_config_operations = { + .open = bcm_kernel_config_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int __init bcm_kernel_config_init(struct proc_dir_entry *pentry) +{ + +#ifdef CONFIG_SMP + bcm_kernel_config_smp=1; +#endif +#ifdef CONFIG_PREEMPT + bcm_kernel_config_preempt=1; +#endif +#ifdef CONFIG_DEBUG_SPINLOCK + bcm_kernel_config_debug_spinlock=1; +#endif +#ifdef CONFIG_DEBUG_MUTEXES + bcm_kernel_config_debug_mutexes=1; +#endif + + printk(KERN_INFO "--Kernel Config--\n"); + printk(KERN_INFO " SMP=%d\n", bcm_kernel_config_smp); + printk(KERN_INFO " PREEMPT=%d\n", bcm_kernel_config_preempt); + printk(KERN_INFO " DEBUG_SPINLOCK=%d\n", bcm_kernel_config_debug_spinlock); + printk(KERN_INFO " DEBUG_MUTEXES=%d\n", bcm_kernel_config_debug_mutexes); + + proc_create("kernel_config", 0, pentry, &proc_kernel_config_operations); + return 0; +} + + +/**************************************************************************** + * Entry point from proc_root_init + ****************************************************************************/ +void __init proc_brcm_init(struct proc_dir_entry *pentry) +{ + + struct proc_dir_entry *entry __attribute ((unused)); + +#ifdef CONFIG_BRCM_VMTOOLS + create_proc_read_entry("shrinkmem", 0, pentry, shrinkmem_read_proc, NULL); + create_proc_read_entry("pagewalk", 0, pentry, pagewalk_read_proc, NULL); + create_proc_read_entry("shrinkpagewalk", 0, pentry, shrinkpagewalk_read_proc, NULL); +#endif + +#ifdef CONFIG_BCM_CSTAT + entry = create_proc_entry("cstat", 0, pentry); + entry->read_proc = cachestat_read_proc; + entry->write_proc = cachestat_write_proc; +#endif + +#if defined(CONFIG_BRCM_OLT_FPGA_RESTORE) + entry = create_proc_entry("olt_fpga_restore", 0, pentry); + entry->read_proc = olt_fpga_restore_read_proc; + entry->write_proc = olt_fpga_restore_write_proc; +#endif + +#if 0 + create_proc_read_entry("cp0regs", 0, pentry, cp0regs_read_proc, NULL); +#endif + + bcm_kernel_config_init(pentry); +#ifdef CONFIG_BCM_CFE_XARGS + bcm_blxparms_init(pentry); +#endif +} + +#endif diff -urN linux-4.1.52-orig/fs/proc/proc_sysctl.c linux-4.1.52/fs/proc/proc_sysctl.c --- linux-4.1.52-orig/fs/proc/proc_sysctl.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/fs/proc/proc_sysctl.c 2020-02-07 01:47:54.000000000 +0800 @@ -705,6 +705,7 @@ if (!dir_emit_dots(file, ctx)) goto out; + pos = 2; for (first_entry(ctl_dir, &h, &entry); h; next_entry(&h, &entry)) { diff -urN linux-4.1.52-orig/fs/proc/root.c linux-4.1.52/fs/proc/root.c --- linux-4.1.52-orig/fs/proc/root.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/fs/proc/root.c 2020-02-07 01:47:54.000000000 +0800 @@ -23,6 +23,11 @@ #include "internal.h" +#if defined(CONFIG_BCM_KF_PROC_BCM) +struct proc_dir_entry *proc_brcm; +extern void proc_brcm_init(struct proc_dir_entry *pentry); +#endif + static int proc_test_super(struct super_block *sb, void *data) { return sb->s_fs_info == data; @@ -40,11 +45,17 @@ enum { Opt_gid, Opt_hidepid, Opt_err, +#if defined(CONFIG_BCM_KF_PROC_DEFAULT) + Opt_default +#endif }; static const match_table_t tokens = { {Opt_hidepid, "hidepid=%u"}, {Opt_gid, "gid=%u"}, +#if defined(CONFIG_BCM_KF_PROC_DEFAULT) + {Opt_default, "defaults"}, +#endif {Opt_err, NULL}, }; @@ -79,6 +90,10 @@ } pid->hide_pid = option; break; +#if defined(CONFIG_BCM_KF_PROC_DEFAULT) + case Opt_default: + break; +#endif default: pr_err("proc: unrecognized mount option \"%s\" " "or missing value\n", p); @@ -193,6 +208,11 @@ #endif proc_tty_init(); proc_mkdir("bus", NULL); + +#if defined(CONFIG_BCM_KF_PROC_BCM) + proc_brcm = proc_mkdir("brcm", NULL); + proc_brcm_init(proc_brcm); +#endif proc_sys_init(); } diff -urN linux-4.1.52-orig/fs/ubifs/dir.c linux-4.1.52/fs/ubifs/dir.c --- linux-4.1.52-orig/fs/ubifs/dir.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/fs/ubifs/dir.c 2020-02-07 01:47:54.000000000 +0800 @@ -301,6 +301,98 @@ return err; } +#if defined(CONFIG_BCM_KF_UBIFS_OVERLAY_BACKPORTS) +static int do_tmpfile(struct inode *dir, struct dentry *dentry, + umode_t mode, struct inode **whiteout) +{ + struct inode *inode; + struct ubifs_info *c = dir->i_sb->s_fs_info; + struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1}; + struct ubifs_budget_req ino_req = { .dirtied_ino = 1 }; + struct ubifs_inode *ui, *dir_ui = ubifs_inode(dir); + int err, instantiated = 0; + + /* + * Budget request settings: new dirty inode, new direntry, + * budget for dirtied inode will be released via writeback. + */ + + dbg_gen("dent '%pd', mode %#hx in dir ino %lu", + dentry, mode, dir->i_ino); + + err = ubifs_budget_space(c, &req); + if (err) + return err; + + err = ubifs_budget_space(c, &ino_req); + if (err) { + ubifs_release_budget(c, &req); + return err; + } + + inode = ubifs_new_inode(c, dir, mode); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out_budg; + } + ui = ubifs_inode(inode); + + if (whiteout) { + init_special_inode(inode, inode->i_mode, WHITEOUT_DEV); + ubifs_assert(inode->i_op == &ubifs_file_inode_operations); + } + + err = ubifs_init_security(dir, inode, &dentry->d_name); + if (err) + goto out_inode; + + mutex_lock(&ui->ui_mutex); + insert_inode_hash(inode); + + if (whiteout) { + mark_inode_dirty(inode); + drop_nlink(inode); + *whiteout = inode; + } else { + d_tmpfile(dentry, inode); + } + ubifs_assert(ui->dirty); + + instantiated = 1; + mutex_unlock(&ui->ui_mutex); + + mutex_lock(&dir_ui->ui_mutex); + err = ubifs_jnl_update(c, dir, &dentry->d_name, inode, 1, 0); + if (err) + goto out_cancel; + mutex_unlock(&dir_ui->ui_mutex); + + ubifs_release_budget(c, &req); + + return 0; + +out_cancel: + mutex_unlock(&dir_ui->ui_mutex); +out_inode: + make_bad_inode(inode); + if (!instantiated) + iput(inode); +out_budg: + ubifs_release_budget(c, &req); + if (!instantiated) + ubifs_release_budget(c, &ino_req); + ubifs_err(c, "cannot create temporary file, error %d", err); + return err; +} + +static int ubifs_tmpfile(struct inode *dir, struct dentry *dentry, + umode_t mode) +{ + return do_tmpfile(dir, dentry, mode, NULL); +} + +#endif + /** * vfs_dent_type - get VFS directory entry type. * @type: UBIFS directory entry type @@ -934,6 +1026,52 @@ return err; } +#if defined(CONFIG_BCM_KF_UBIFS_OVERLAY_BACKPORTS) +/** + * lock_4_inodes - a wrapper for locking three UBIFS inodes. + * @inode1: first inode + * @inode2: second inode + * @inode3: third inode + * @inode4: forth inode + * + * This function is used for 'ubifs_rename()' and @inode1 may be the same as + * @inode2 whereas @inode3 and @inode4 may be %NULL. + * + * We do not implement any tricks to guarantee strict lock ordering, because + * VFS has already done it for us on the @i_mutex. So this is just a simple + * wrapper function. + */ +static void lock_4_inodes(struct inode *inode1, struct inode *inode2, + struct inode *inode3, struct inode *inode4) +{ + mutex_lock_nested(&ubifs_inode(inode1)->ui_mutex, WB_MUTEX_1); + if (inode2 != inode1) + mutex_lock_nested(&ubifs_inode(inode2)->ui_mutex, WB_MUTEX_2); + if (inode3) + mutex_lock_nested(&ubifs_inode(inode3)->ui_mutex, WB_MUTEX_3); + if (inode4) + mutex_lock_nested(&ubifs_inode(inode4)->ui_mutex, WB_MUTEX_4); +} + +/** + * unlock_4_inodes - a wrapper for unlocking three UBIFS inodes for rename. + * @inode1: first inode + * @inode2: second inode + * @inode3: third inode + * @inode4: forth inode + */ +static void unlock_4_inodes(struct inode *inode1, struct inode *inode2, + struct inode *inode3, struct inode *inode4) +{ + if (inode4) + mutex_unlock(&ubifs_inode(inode4)->ui_mutex); + if (inode3) + mutex_unlock(&ubifs_inode(inode3)->ui_mutex); + if (inode1 != inode2) + mutex_unlock(&ubifs_inode(inode2)->ui_mutex); + mutex_unlock(&ubifs_inode(inode1)->ui_mutex); +} +#else /** * lock_3_inodes - a wrapper for locking three UBIFS inodes. * @inode1: first inode @@ -972,13 +1110,24 @@ mutex_unlock(&ubifs_inode(inode2)->ui_mutex); mutex_unlock(&ubifs_inode(inode1)->ui_mutex); } +#endif +#if defined(CONFIG_BCM_KF_UBIFS_OVERLAY_BACKPORTS) +static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry, + unsigned int flags) +#else static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) +#endif { struct ubifs_info *c = old_dir->i_sb->s_fs_info; struct inode *old_inode = d_inode(old_dentry); struct inode *new_inode = d_inode(new_dentry); +#if defined(CONFIG_BCM_KF_UBIFS_OVERLAY_BACKPORTS) + struct inode *whiteout = NULL; + struct ubifs_inode *whiteout_ui = NULL; +#endif struct ubifs_inode *old_inode_ui = ubifs_inode(old_inode); int err, release, sync = 0, move = (new_dir != old_dir); int is_dir = S_ISDIR(old_inode->i_mode); @@ -1004,12 +1153,14 @@ dbg_gen("dent '%pd' ino %lu in dir ino %lu to dent '%pd' in dir ino %lu", old_dentry, old_inode->i_ino, old_dir->i_ino, new_dentry, new_dir->i_ino); + +#if !defined(CONFIG_BCM_KF_UBIFS_OVERLAY_BACKPORTS) ubifs_assert(mutex_is_locked(&old_dir->i_mutex)); ubifs_assert(mutex_is_locked(&new_dir->i_mutex)); +#endif if (unlink) ubifs_assert(mutex_is_locked(&new_inode->i_mutex)); - if (unlink && is_dir) { err = check_dir_empty(c, new_inode); if (err) @@ -1025,7 +1176,36 @@ return err; } +#if defined(CONFIG_BCM_KF_UBIFS_OVERLAY_BACKPORTS) + if (flags & RENAME_WHITEOUT) { + union ubifs_dev_desc *dev = NULL; + + dev = kmalloc(sizeof(union ubifs_dev_desc), GFP_NOFS); + if (!dev) { + ubifs_release_budget(c, &req); + ubifs_release_budget(c, &ino_req); + return -ENOMEM; + } + + err = do_tmpfile(old_dir, old_dentry, S_IFCHR | WHITEOUT_MODE, &whiteout); + if (err) { + ubifs_release_budget(c, &req); + ubifs_release_budget(c, &ino_req); + kfree(dev); + return err; + } + + whiteout->i_state |= I_LINKABLE; + whiteout_ui = ubifs_inode(whiteout); + whiteout_ui->data = dev; + whiteout_ui->data_len = ubifs_encode_dev(dev, MKDEV(0, 0)); + ubifs_assert(!whiteout_ui->dirty); + } + + lock_4_inodes(old_dir, new_dir, new_inode, whiteout); +#else lock_3_inodes(old_dir, new_dir, new_inode); +#endif /* * Like most other Unix systems, set the @i_ctime for inodes on a @@ -1095,12 +1275,43 @@ if (unlink && IS_SYNC(new_inode)) sync = 1; } + +#if defined(CONFIG_BCM_KF_UBIFS_OVERLAY_BACKPORTS) + if (whiteout) { + struct ubifs_budget_req wht_req = { .dirtied_ino = 1, + .dirtied_ino_d = \ + ALIGN(ubifs_inode(whiteout)->data_len, 8) }; + + err = ubifs_budget_space(c, &wht_req); + if (err) { + ubifs_release_budget(c, &req); + ubifs_release_budget(c, &ino_req); + kfree(whiteout_ui->data); + whiteout_ui->data_len = 0; + iput(whiteout); + return err; + } + + inc_nlink(whiteout); + mark_inode_dirty(whiteout); + whiteout->i_state &= ~I_LINKABLE; + iput(whiteout); + } + + err = ubifs_jnl_rename(c, old_dir, old_dentry, new_dir, new_dentry, whiteout, + sync); +#else err = ubifs_jnl_rename(c, old_dir, old_dentry, new_dir, new_dentry, sync); +#endif if (err) goto out_cancel; +#if defined(CONFIG_BCM_KF_UBIFS_OVERLAY_BACKPORTS) + unlock_4_inodes(old_dir, new_dir, new_inode, whiteout); +#else unlock_3_inodes(old_dir, new_dir, new_inode); +#endif ubifs_release_budget(c, &req); mutex_lock(&old_inode_ui->ui_mutex); @@ -1133,12 +1344,82 @@ inc_nlink(old_dir); } } +#if defined(CONFIG_BCM_KF_UBIFS_OVERLAY_BACKPORTS) + if (whiteout) { + drop_nlink(whiteout); + iput(whiteout); + } + unlock_4_inodes(old_dir, new_dir, new_inode, whiteout); +#else unlock_3_inodes(old_dir, new_dir, new_inode); +#endif ubifs_release_budget(c, &ino_req); ubifs_release_budget(c, &req); return err; } + +#if defined(CONFIG_BCM_KF_UBIFS_OVERLAY_BACKPORTS) +static int ubifs_xrename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + struct ubifs_info *c = old_dir->i_sb->s_fs_info; + struct ubifs_budget_req req = { .new_dent = 1, .mod_dent = 1, + .dirtied_ino = 2 }; + int sync = IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir); + struct inode *fst_inode = d_inode(old_dentry); + struct inode *snd_inode = d_inode(new_dentry); + struct timespec time; + int err; + + ubifs_assert(fst_inode && snd_inode); + + lock_4_inodes(old_dir, new_dir, NULL, NULL); + + time = ubifs_current_time(old_dir); + fst_inode->i_ctime = time; + snd_inode->i_ctime = time; + old_dir->i_mtime = old_dir->i_ctime = time; + new_dir->i_mtime = new_dir->i_ctime = time; + + if (old_dir != new_dir) { + if (S_ISDIR(fst_inode->i_mode) && !S_ISDIR(snd_inode->i_mode)) { + inc_nlink(new_dir); + drop_nlink(old_dir); + } + else if (!S_ISDIR(fst_inode->i_mode) && S_ISDIR(snd_inode->i_mode)) { + drop_nlink(new_dir); + inc_nlink(old_dir); + } + } + + err = ubifs_jnl_xrename(c, old_dir, old_dentry, new_dir, new_dentry, + sync); + + unlock_4_inodes(old_dir, new_dir, NULL, NULL); + ubifs_release_budget(c, &req); + + return err; +} + +static int ubifs_rename2(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry, + unsigned int flags) +{ + if (flags & ~(RENAME_NOREPLACE | RENAME_WHITEOUT | RENAME_EXCHANGE)) + return -EINVAL; + + ubifs_assert(mutex_is_locked(&old_dir->i_mutex)); + ubifs_assert(mutex_is_locked(&new_dir->i_mutex)); + + if (flags & RENAME_EXCHANGE) + return ubifs_xrename(old_dir, old_dentry, new_dir, new_dentry); + + return ubifs_rename(old_dir, old_dentry, new_dir, new_dentry, flags); +} + +#endif + int ubifs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { @@ -1187,13 +1468,20 @@ .mkdir = ubifs_mkdir, .rmdir = ubifs_rmdir, .mknod = ubifs_mknod, +#if defined(CONFIG_BCM_KF_UBIFS_OVERLAY_BACKPORTS) + .rename2 = ubifs_rename2, +#else .rename = ubifs_rename, +#endif .setattr = ubifs_setattr, .getattr = ubifs_getattr, .setxattr = ubifs_setxattr, .getxattr = ubifs_getxattr, .listxattr = ubifs_listxattr, .removexattr = ubifs_removexattr, +#if defined(CONFIG_BCM_KF_UBIFS_OVERLAY_BACKPORTS) + .tmpfile = ubifs_tmpfile, +#endif }; const struct file_operations ubifs_dir_operations = { diff -urN linux-4.1.52-orig/fs/ubifs/journal.c linux-4.1.52/fs/ubifs/journal.c --- linux-4.1.52-orig/fs/ubifs/journal.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/fs/ubifs/journal.c 2020-02-07 01:47:54.000000000 +0800 @@ -907,6 +907,168 @@ return err; } +#if defined(CONFIG_BCM_KF_UBIFS_OVERLAY_BACKPORTS) +/** + * ubifs_jnl_xrename - cross rename two directory entries. + * @c: UBIFS file-system description object + * @fst_dir: parent inode of 1st directory entry to exchange + * @fst_dentry: 1st directory entry to exchange + * @snd_dir: parent inode of 2nd directory entry to exchange + * @snd_dentry: 2nd directory entry to exchange + * @sync: non-zero if the write-buffer has to be synchronized + * + * This function implements the cross rename operation which may involve + * writing 2 inodes and 2 directory entries. It marks the written inodes as clean + * and returns zero on success. In case of failure, a negative error code is + * returned. + */ +int ubifs_jnl_xrename(struct ubifs_info *c, const struct inode *fst_dir, + const struct dentry *fst_dentry, + const struct inode *snd_dir, + const struct dentry *snd_dentry, int sync) +{ + union ubifs_key key; + struct ubifs_dent_node *dent1, *dent2; + int err, dlen1, dlen2, lnum, offs, len, plen = UBIFS_INO_NODE_SZ; + int aligned_dlen1, aligned_dlen2; + int twoparents = (fst_dir != snd_dir); + const struct inode *fst_inode = d_inode(fst_dentry); + const struct inode *snd_inode = d_inode(snd_dentry); + void *p; + + dbg_jnl("dent '%pd' in dir ino %lu between dent '%pd' in dir ino %lu", + fst_dentry, fst_dir->i_ino, snd_dentry, snd_dir->i_ino); + + ubifs_assert(ubifs_inode(fst_dir)->data_len == 0); + ubifs_assert(ubifs_inode(snd_dir)->data_len == 0); + ubifs_assert(mutex_is_locked(&ubifs_inode(fst_dir)->ui_mutex)); + ubifs_assert(mutex_is_locked(&ubifs_inode(snd_dir)->ui_mutex)); + + dlen1 = UBIFS_DENT_NODE_SZ + snd_dentry->d_name.len + 1; + dlen2 = UBIFS_DENT_NODE_SZ + fst_dentry->d_name.len + 1; + aligned_dlen1 = ALIGN(dlen1, 8); + aligned_dlen2 = ALIGN(dlen2, 8); + + len = aligned_dlen1 + aligned_dlen2 + ALIGN(plen, 8); + if (twoparents) + len += plen; + + dent1 = kmalloc(len, GFP_NOFS); + if (!dent1) + return -ENOMEM; + + /* Make reservation before allocating sequence numbers */ + err = make_reservation(c, BASEHD, len); + if (err) + goto out_free; + + /* Make new dent for 1st entry */ + dent1->ch.node_type = UBIFS_DENT_NODE; + dent_key_init_flash(c, &dent1->key, snd_dir->i_ino, &snd_dentry->d_name); + dent1->inum = cpu_to_le64(fst_inode->i_ino); + dent1->type = get_dent_type(fst_inode->i_mode); + dent1->nlen = cpu_to_le16(snd_dentry->d_name.len); + memcpy(dent1->name, snd_dentry->d_name.name, snd_dentry->d_name.len); + dent1->name[snd_dentry->d_name.len] = '\0'; + zero_dent_node_unused(dent1); + ubifs_prep_grp_node(c, dent1, dlen1, 0); + + /* Make new dent for 2nd entry */ + dent2 = (void *)dent1 + aligned_dlen1; + dent2->ch.node_type = UBIFS_DENT_NODE; + dent_key_init_flash(c, &dent2->key, fst_dir->i_ino, &fst_dentry->d_name); + dent2->inum = cpu_to_le64(snd_inode->i_ino); + dent2->type = get_dent_type(snd_inode->i_mode); + dent2->nlen = cpu_to_le16(fst_dentry->d_name.len); + memcpy(dent2->name, fst_dentry->d_name.name, fst_dentry->d_name.len); + dent2->name[fst_dentry->d_name.len] = '\0'; + zero_dent_node_unused(dent2); + ubifs_prep_grp_node(c, dent2, dlen2, 0); + + p = (void *)dent2 + aligned_dlen2; + if (!twoparents) + pack_inode(c, p, fst_dir, 1); + else { + pack_inode(c, p, fst_dir, 0); + p += ALIGN(plen, 8); + pack_inode(c, p, snd_dir, 1); + } + + err = write_head(c, BASEHD, dent1, len, &lnum, &offs, sync); + if (err) + goto out_release; + if (!sync) { + struct ubifs_wbuf *wbuf = &c->jheads[BASEHD].wbuf; + + ubifs_wbuf_add_ino_nolock(wbuf, fst_dir->i_ino); + ubifs_wbuf_add_ino_nolock(wbuf, snd_dir->i_ino); + } + release_head(c, BASEHD); + + dent_key_init(c, &key, snd_dir->i_ino, &snd_dentry->d_name); + err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen1, &snd_dentry->d_name); + if (err) + goto out_ro; + + offs += aligned_dlen1; + dent_key_init(c, &key, fst_dir->i_ino, &fst_dentry->d_name); + err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen2, &fst_dentry->d_name); + if (err) + goto out_ro; + + offs += aligned_dlen2; + + ino_key_init(c, &key, fst_dir->i_ino); + err = ubifs_tnc_add(c, &key, lnum, offs, plen); + if (err) + goto out_ro; + + if (twoparents) { + offs += ALIGN(plen, 8); + ino_key_init(c, &key, snd_dir->i_ino); + err = ubifs_tnc_add(c, &key, lnum, offs, plen); + if (err) + goto out_ro; + } + + finish_reservation(c); + + mark_inode_clean(c, ubifs_inode(fst_dir)); + if (twoparents) + mark_inode_clean(c, ubifs_inode(snd_dir)); + kfree(dent1); + return 0; + +out_release: + release_head(c, BASEHD); +out_ro: + ubifs_ro_mode(c, err); + finish_reservation(c); +out_free: + kfree(dent1); + return err; +} + +/** + * ubifs_jnl_rename - rename a directory entry. + * @c: UBIFS file-system description object + * @old_dir: parent inode of directory entry to rename + * @old_dentry: directory entry to rename + * @new_dir: parent inode of directory entry to rename + * @new_dentry: new directory entry (or directory entry to replace) + * @sync: non-zero if the write-buffer has to be synchronized + * + * This function implements the re-name operation which may involve writing up + * to 4 inodes and 2 directory entries. It marks the written inodes as clean + * and returns zero on success. In case of failure, a negative error code is + * returned. + */ +int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir, + const struct dentry *old_dentry, + const struct inode *new_dir, + const struct dentry *new_dentry, + const struct inode *whiteout, int sync) +#else /** * ubifs_jnl_rename - rename a directory entry. * @c: UBIFS file-system description object @@ -925,6 +1087,7 @@ const struct dentry *old_dentry, const struct inode *new_dir, const struct dentry *new_dentry, int sync) +#endif { void *p; union ubifs_key key; @@ -985,8 +1148,19 @@ dent2->ch.node_type = UBIFS_DENT_NODE; dent_key_init_flash(c, &dent2->key, old_dir->i_ino, &old_dentry->d_name); +#if defined(CONFIG_BCM_KF_UBIFS_OVERLAY_BACKPORTS) + if (whiteout) { + dent2->inum = cpu_to_le64(whiteout->i_ino); + dent2->type = get_dent_type(whiteout->i_mode); + } else { + /* Make deletion dent */ + dent2->inum = 0; + dent2->type = DT_UNKNOWN; + } +#else dent2->inum = 0; dent2->type = DT_UNKNOWN; +#endif dent2->nlen = cpu_to_le16(old_dentry->d_name.len); memcpy(dent2->name, old_dentry->d_name.name, old_dentry->d_name.len); dent2->name[old_dentry->d_name.len] = '\0'; @@ -1035,6 +1209,27 @@ if (err) goto out_ro; +#if defined(CONFIG_BCM_KF_UBIFS_OVERLAY_BACKPORTS) + offs += aligned_dlen1; + if (whiteout) { + dent_key_init(c, &key, old_dir->i_ino, &old_dentry->d_name); + err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen2, &old_dentry->d_name); + if (err) + goto out_ro; + + ubifs_delete_orphan(c, whiteout->i_ino); + } else { + err = ubifs_add_dirt(c, lnum, dlen2); + if (err) + goto out_ro; + + dent_key_init(c, &key, old_dir->i_ino, &old_dentry->d_name); + err = ubifs_tnc_remove_nm(c, &key, &old_dentry->d_name); + if (err) + goto out_ro; + } + offs += aligned_dlen2; +#else err = ubifs_add_dirt(c, lnum, dlen2); if (err) goto out_ro; @@ -1045,6 +1240,8 @@ goto out_ro; offs += aligned_dlen1 + aligned_dlen2; +#endif + if (new_inode) { ino_key_init(c, &key, new_inode->i_ino); err = ubifs_tnc_add(c, &key, lnum, offs, ilen); diff -urN linux-4.1.52-orig/fs/ubifs/ubifs.h linux-4.1.52/fs/ubifs/ubifs.h --- linux-4.1.52-orig/fs/ubifs/ubifs.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/fs/ubifs/ubifs.h 2020-02-07 01:47:54.000000000 +0800 @@ -180,6 +180,9 @@ WB_MUTEX_1 = 0, WB_MUTEX_2 = 1, WB_MUTEX_3 = 2, +#if defined(CONFIG_BCM_KF_UBIFS_OVERLAY_BACKPORTS) + WB_MUTEX_4 = 3, +#endif }; /* @@ -1544,10 +1547,22 @@ const union ubifs_key *key, const void *buf, int len); int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode); int ubifs_jnl_delete_inode(struct ubifs_info *c, const struct inode *inode); +#if defined(CONFIG_BCM_KF_UBIFS_OVERLAY_BACKPORTS) +int ubifs_jnl_xrename(struct ubifs_info *c, const struct inode *fst_dir, + const struct dentry *fst_dentry, + const struct inode *snd_dir, + const struct dentry *snd_dentry, int sync); +int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir, + const struct dentry *old_dentry, + const struct inode *new_dir, + const struct dentry *new_dentry, + const struct inode *whiteout, int sync); +#else int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir, const struct dentry *old_dentry, const struct inode *new_dir, const struct dentry *new_dentry, int sync); +#endif int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode, loff_t old_size, loff_t new_size); int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host, diff -urN linux-4.1.52-orig/include/asm-generic/gcclib.h linux-4.1.52/include/asm-generic/gcclib.h --- linux-4.1.52-orig/include/asm-generic/gcclib.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/asm-generic/gcclib.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,35 @@ +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + +/* gcclib.h -- definitions for various functions 'borrowed' from gcc-2.95.3 */ +/* I Molton 29/07/01 */ + +#include + +#ifndef _GCCLIB_H_ +#define _GCCLIB_H_ + +#define BITS_PER_UNIT 8 +#define SI_TYPE_SIZE (sizeof (SItype) * BITS_PER_UNIT) + +typedef unsigned int UQItype __attribute__ ((mode (QI))); +typedef int SItype __attribute__ ((mode (SI))); +typedef unsigned int USItype __attribute__ ((mode (SI))); +typedef int DItype __attribute__ ((mode (DI))); +typedef int word_type __attribute__ ((mode (__word__))); +typedef unsigned int UDItype __attribute__ ((mode (DI))); + +#ifdef CONFIG_CPU_LITTLE_ENDIAN + struct DIstruct {USItype low; SItype high;}; +#else + struct DIstruct {SItype high; USItype low;}; +#endif + +typedef union +{ + struct DIstruct s; + DItype ll; +} DIunion; + +#endif + +#endif diff -urN linux-4.1.52-orig/include/crypto/hash.h linux-4.1.52/include/crypto/hash.h --- linux-4.1.52-orig/include/crypto/hash.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/crypto/hash.h 2020-02-07 01:47:54.000000000 +0800 @@ -59,6 +59,10 @@ /* This field may only be used by the ahash API code. */ void *priv; +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined (CONFIG_BCM_SPU_MODULE)) && !(defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE)) + int alloc_buff_spu; + int headerLen; +#endif void *__ctx[] CRYPTO_MINALIGN_ATTR; }; diff -urN linux-4.1.52-orig/include/linux/arm-smccc.h linux-4.1.52/include/linux/arm-smccc.h --- linux-4.1.52-orig/include/linux/arm-smccc.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/arm-smccc.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,136 @@ +#if defined(CONFIG_BCM_KF_OPTEE_414_BACKPORTS) +/* + * Copyright (c) 2015, Linaro Limited + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ +#ifndef __LINUX_ARM_SMCCC_H +#define __LINUX_ARM_SMCCC_H + +/* + * This file provides common defines for ARM SMC Calling Convention as + * specified in + * http://infocenter.arm.com/help/topic/com.arm.doc.den0028a/index.html + */ + +#define ARM_SMCCC_STD_CALL 0 +#define ARM_SMCCC_FAST_CALL 1 +#define ARM_SMCCC_TYPE_SHIFT 31 + +#define ARM_SMCCC_SMC_32 0 +#define ARM_SMCCC_SMC_64 1 +#define ARM_SMCCC_CALL_CONV_SHIFT 30 + +#define ARM_SMCCC_OWNER_MASK 0x3F +#define ARM_SMCCC_OWNER_SHIFT 24 + +#define ARM_SMCCC_FUNC_MASK 0xFFFF + +#define ARM_SMCCC_IS_FAST_CALL(smc_val) \ + ((smc_val) & (ARM_SMCCC_FAST_CALL << ARM_SMCCC_TYPE_SHIFT)) +#define ARM_SMCCC_IS_64(smc_val) \ + ((smc_val) & (ARM_SMCCC_SMC_64 << ARM_SMCCC_CALL_CONV_SHIFT)) +#define ARM_SMCCC_FUNC_NUM(smc_val) ((smc_val) & ARM_SMCCC_FUNC_MASK) +#define ARM_SMCCC_OWNER_NUM(smc_val) \ + (((smc_val) >> ARM_SMCCC_OWNER_SHIFT) & ARM_SMCCC_OWNER_MASK) + +#define ARM_SMCCC_CALL_VAL(type, calling_convention, owner, func_num) \ + (((type) << ARM_SMCCC_TYPE_SHIFT) | \ + ((calling_convention) << ARM_SMCCC_CALL_CONV_SHIFT) | \ + (((owner) & ARM_SMCCC_OWNER_MASK) << ARM_SMCCC_OWNER_SHIFT) | \ + ((func_num) & ARM_SMCCC_FUNC_MASK)) + +#define ARM_SMCCC_OWNER_ARCH 0 +#define ARM_SMCCC_OWNER_CPU 1 +#define ARM_SMCCC_OWNER_SIP 2 +#define ARM_SMCCC_OWNER_OEM 3 +#define ARM_SMCCC_OWNER_STANDARD 4 +#define ARM_SMCCC_OWNER_TRUSTED_APP 48 +#define ARM_SMCCC_OWNER_TRUSTED_APP_END 49 +#define ARM_SMCCC_OWNER_TRUSTED_OS 50 +#define ARM_SMCCC_OWNER_TRUSTED_OS_END 63 + +#define ARM_SMCCC_QUIRK_NONE 0 +#define ARM_SMCCC_QUIRK_QCOM_A6 1 /* Save/restore register a6 */ + +#ifndef __ASSEMBLY__ + +#include +#include +/** + * struct arm_smccc_res - Result from SMC/HVC call + * @a0-a3 result values from registers 0 to 3 + */ +struct arm_smccc_res { + unsigned long a0; + unsigned long a1; + unsigned long a2; + unsigned long a3; +}; + +/** + * struct arm_smccc_quirk - Contains quirk information + * @id: quirk identification + * @state: quirk specific information + * @a6: Qualcomm quirk entry for returning post-smc call contents of a6 + */ +struct arm_smccc_quirk { + int id; + union { + unsigned long a6; + } state; +}; + +/** + * __arm_smccc_smc() - make SMC calls + * @a0-a7: arguments passed in registers 0 to 7 + * @res: result values from registers 0 to 3 + * @quirk: points to an arm_smccc_quirk, or NULL when no quirks are required. + * + * This function is used to make SMC calls following SMC Calling Convention. + * The content of the supplied param are copied to registers 0 to 7 prior + * to the SMC instruction. The return values are updated with the content + * from register 0 to 3 on return from the SMC instruction. An optional + * quirk structure provides vendor specific behavior. + */ +asmlinkage void __arm_smccc_smc(unsigned long a0, unsigned long a1, + unsigned long a2, unsigned long a3, unsigned long a4, + unsigned long a5, unsigned long a6, unsigned long a7, + struct arm_smccc_res *res, struct arm_smccc_quirk *quirk); + +/** + * __arm_smccc_hvc() - make HVC calls + * @a0-a7: arguments passed in registers 0 to 7 + * @res: result values from registers 0 to 3 + * @quirk: points to an arm_smccc_quirk, or NULL when no quirks are required. + * + * This function is used to make HVC calls following SMC Calling + * Convention. The content of the supplied param are copied to registers 0 + * to 7 prior to the HVC instruction. The return values are updated with + * the content from register 0 to 3 on return from the HVC instruction. An + * optional quirk structure provides vendor specific behavior. + */ +asmlinkage void __arm_smccc_hvc(unsigned long a0, unsigned long a1, + unsigned long a2, unsigned long a3, unsigned long a4, + unsigned long a5, unsigned long a6, unsigned long a7, + struct arm_smccc_res *res, struct arm_smccc_quirk *quirk); + +#define arm_smccc_smc(...) __arm_smccc_smc(__VA_ARGS__, NULL) + +#define arm_smccc_smc_quirk(...) __arm_smccc_smc(__VA_ARGS__) + +#define arm_smccc_hvc(...) __arm_smccc_hvc(__VA_ARGS__, NULL) + +#define arm_smccc_hvc_quirk(...) __arm_smccc_hvc(__VA_ARGS__) + +#endif /*__ASSEMBLY__*/ +#endif /*__LINUX_ARM_SMCCC_H*/ +#endif /* CONFIG_BCM_KF_OPTEE_414_BACKPORTS */ diff -urN linux-4.1.52-orig/include/linux/backing-dev.h linux-4.1.52/include/linux/backing-dev.h --- linux-4.1.52-orig/include/linux/backing-dev.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/backing-dev.h 2020-02-07 01:47:54.000000000 +0800 @@ -110,12 +110,20 @@ struct backing_dev_info *inode_to_bdi(struct inode *inode); int __must_check bdi_init(struct backing_dev_info *bdi); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +void bdi_exit(struct backing_dev_info *bdi); +#else void bdi_destroy(struct backing_dev_info *bdi); +#endif __printf(3, 4) int bdi_register(struct backing_dev_info *bdi, struct device *parent, const char *fmt, ...); int bdi_register_dev(struct backing_dev_info *bdi, dev_t dev); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +void bdi_unregister(struct backing_dev_info *bdi); +void bdi_destroy(struct backing_dev_info *bdi); +#endif int __must_check bdi_setup_and_register(struct backing_dev_info *, char *); void bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages, enum wb_reason reason); diff -urN linux-4.1.52-orig/include/linux/bcm_dslcpe_wlan_info.h linux-4.1.52/include/linux/bcm_dslcpe_wlan_info.h --- linux-4.1.52-orig/include/linux/bcm_dslcpe_wlan_info.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/bcm_dslcpe_wlan_info.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,26 @@ +#ifndef __BCM_DSLCPE_WLAN_INFO_H_ +#define __BCM_DSLCPE_WLAN_INFO_H_ +#include +#include +#define WLAN_CLIENT_INFO_OK (0) +#define WLAN_CLIENT_INFO_ERR (-1) +typedef enum { + WLAN_CLIENT_TYPE_CPU, + WLAN_CLIENT_TYPE_WFD, + WLAN_CLIENT_TYPE_RUNNER, + WLAN_CLIENT_TYPE_MAX +} wlan_client_type_t; + +typedef struct { + wlan_client_type_t type; + union { + uint32_t wl; + BlogWfd_t wfd; + BlogRnr_t rnr; + }; +} wlan_client_info_t; + + +typedef int (* wlan_client_get_info_t)(struct net_device *dev,char *mac_address_p,int priority, wlan_client_info_t *info_p); + +#endif diff -urN linux-4.1.52-orig/include/linux/bcm_log.h linux-4.1.52/include/linux/bcm_log.h --- linux-4.1.52-orig/include/linux/bcm_log.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/bcm_log.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,324 @@ +#if defined(CONFIG_BCM_KF_LOG) +/* +* <:copyright-BRCM:2012:DUAL/GPL:standard +* +* Copyright (c) 2012 Broadcom +* All Rights Reserved +* +* Unless you and Broadcom execute a separate written software license +* agreement governing use of this software, this software is licensed +* to you under the terms of the GNU General Public License version 2 +* (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +* with the following added to such license: +* +* As a special exception, the copyright holders of this software give +* you permission to link this software with independent modules, and +* to copy and distribute the resulting executable under terms of your +* choice, provided that you also meet, for each linked independent +* module, the terms and conditions of the license of that module. +* An independent module is a module which is not derived from this +* software. The special exception does not apply to any modifications +* of the software. +* +* Not withstanding the above, under no circumstances may you combine +* this software in any way with any other Broadcom software provided +* under a license other than the GPL, without Broadcom's express prior +* written consent. +* +:> +*/ +#ifndef _BCM_LOG_SERVICES_ +#define _BCM_LOG_SERVICES_ + +#if !defined(__KERNEL__) +#include /**< ISO C99 7.18 Integer Types */ +#include +#include +#endif + +#include + +#if !defined(__KERNEL__) +#define bcm_printk printf +#define BUG() do { } while(0) +#define EXPORT_SYMBOL(sym) +#endif + +/********* + ********* + * Private: + ********* + *********/ + + +#define IN /*Input parameters*/ +#define OUT /*Output parameters*/ +#define INOUT /*Input/Output parameters*/ + +/* + * This block of defines selects supported functionality for everything + * that includes bcm_log.h. Selection of functionality will eventually + * be moved to make menuconfig. CONFIG_BRCM_COLORIZE_PRINTS is already + * in make menuconfig, but it is locally disabled here. + */ +#ifdef CONFIG_BCM_LOG +#undef CONFIG_BRCM_COLORIZE_PRINTS +#define BCM_ASSERT_SUPPORTED +#define BCM_LOG_SUPPORTED +#define BCM_DATADUMP_SUPPORTED +#define BCM_ERROR_SUPPORTED +#undef BCM_SNAPSHOT_SUPPORTED +#endif /* CONFIG_BCM_LOG */ + +#include + +#if defined(BCM_ASSERT_SUPPORTED) +#define BCM_ASSERTCODE(code) code +#else +#define BCM_ASSERTCODE(code) +#endif /*defined(BCM_ASSERT_SUPPORTED)*/ + +#if defined(BCM_LOG_SUPPORTED) +#define BCM_LOGCODE(code) code +#else +#define BCM_LOGCODE(code) +#endif /*defined(BCM_LOG_SUPPORTED)*/ + +#if defined(BCM_ERROR_SUPPORTED) +#define BCM_ERRORCODE(code) code +#else +#define BCM_ERRORCODE(code) +#endif /*defined(BCM_ERROR_SUPPORTED)*/ + +#if defined(BCM_DATADUMP_SUPPORTED) +#define BCM_DATADUMPCODE(code) code +#else +#define BCM_DATADUMPCODE(code) +#endif /*defined(BCM_DATADUMP_SUPPORTED)*/ + +#if defined(BCM_SNAPSHOT_SUPPORTED) +#define BCM_SNAPSHOTCODE(code) code +#else +#define BCM_SNAPSHOTCODE(code) +#endif /*defined(BCM_SNAPSHOT_SUPPORTED)*/ + +typedef enum { + BCM_LOG_DD_IMPORTANT=0, + BCM_LOG_DD_INFO, + BCM_LOG_DD_DETAIL, + BCM_LOG_DD_MAX +} bcmLogDataDumpLevel_t; + +typedef void (*bcmLogLevelChangeCallback_t)(bcmLogId_t logId, bcmLogLevel_t level, void *ctx); + +typedef struct { + bcmLogId_t logId; + char *name; + bcmLogLevel_t logLevel; + bcmLogDataDumpLevel_t ddLevel; + bcmLogLevelChangeCallback_t lcCallback; + void * lcCallbackCtx; +} bcmLogModuleInfo_t; + +typedef struct +{ + int (*reserveSlave)(int busNum, int slaveId, int maxFreq); + int (*syncTrans)(unsigned char *txBuf, unsigned char *rxBuf, int prependcnt, int nbytes, int busNum, int slaveId); + int (*kerSysSlaveWrite)(int dev, unsigned int addr, unsigned int data, unsigned int len); + int (*kerSysSlaveRead)(int dev, unsigned int addr, unsigned int *data, unsigned int len); + int (*bpGet6829PortInfo)( unsigned char *portInfo6829 ); + +} bcmLogSpiCallbacks_t; + + +/******** + ******** + * Public: service API offered by LOGdriver to other drivers + ******** + ********/ +#if defined(__KERNEL__) +/* This fucntion is same as printk, but is defined always + * and should be used in binary only modules to avoid + * dependency on CONFIG_PRINTK. It can be used in place of + * regular printk as well. + * + * All driver should use/define macro based on this function and + * should not create new clones. + */ +int bcm_printk(const char *fmt, ...); +#define bcm_print(fmt, ...) bcm_printk(fmt, ##__VA_ARGS__) +#endif + +/** + * Logging API: Activate by #defining BCM_LOG_SUPPORTED + **/ + +#if defined(BCM_LOG_SUPPORTED) +bcmLogModuleInfo_t *bcmLog_logIsEnabled(bcmLogId_t logId, bcmLogLevel_t logLevel); +#endif + +#define BCM_LOG_FUNC(logId) \ + BCM_LOG_DEBUG((logId), " ") + +#define BCM_LOG_DEBUG(logId, fmt, arg...) \ + BCM_LOGCODE( do { bcmLogModuleInfo_t *_pModInfo = bcmLog_logIsEnabled(logId, BCM_LOG_LEVEL_DEBUG); \ + if (_pModInfo) \ + bcm_printk(CLRm "[DBG " "%s" "] %-10s: " fmt CLRnl, \ + _pModInfo->name, __FUNCTION__, ##arg); } while(0) ) + +#define BCM_LOG_INFO(logId, fmt, arg...) \ + BCM_LOGCODE( do { bcmLogModuleInfo_t *_pModInfo = bcmLog_logIsEnabled(logId, BCM_LOG_LEVEL_INFO); \ + if (_pModInfo) \ + bcm_printk(CLRg "[INF " "%s" "] %-10s: " fmt CLRnl, \ + _pModInfo->name, __FUNCTION__, ##arg); } while(0) ) + +#define BCM_LOG_NOTICE(logId, fmt, arg...) \ + BCM_LOGCODE( do { bcmLogModuleInfo_t *_pModInfo = bcmLog_logIsEnabled(logId, BCM_LOG_LEVEL_NOTICE); \ + if (_pModInfo) \ + bcm_printk(CLRb "[NTC " "%s" "] %-10s: " fmt CLRnl, \ + _pModInfo->name, __FUNCTION__, ##arg); } while(0) ) + + +/** + * Error Reporting API: Activate by #defining BCM_ERROR_SUPPORTED + **/ + +#define BCM_LOG_ERROR(logId, fmt, arg...) \ + BCM_ERRORCODE( do { bcmLogModuleInfo_t *_pModInfo = bcmLog_logIsEnabled(logId, BCM_LOG_LEVEL_ERROR); \ + if (_pModInfo) \ + bcm_printk(CLRerr "[ERROR " "%s" "] %-10s,%d: " fmt CLRnl, \ + _pModInfo->name, __FUNCTION__, __LINE__, ##arg); } while(0) ) + + +/** + * Assert API: Activate by #defining BCM_ASSERT_SUPPORTED + **/ + +#define BCM_ASSERT(cond) \ + BCM_ASSERTCODE( if ( !(cond) ) { \ + bcm_printk(CLRerr "[ASSERT " "%s" "] %-10s,%d: " #cond CLRnl, \ + __FILE__, __FUNCTION__, __LINE__); \ + BUG(); \ + } ) + + +/** + * Datadump API: Activate by #defining BCM_DATADUMP_SUPPORTED + **/ + +/* + * Prototype of datadump print functions. + * Note: parse functions must be exported (EXPORT_SYMBOL) + */ +typedef int (Bcm_DataDumpPrintFunc)(uint32_t dataDumpId, IN void* dataPtr, uint32_t numDataBytes, + OUT char* buf, uint32_t bufSize); + +#if defined(BCM_DATADUMP_SUPPORTED) +bcmLogModuleInfo_t *bcmLog_ddIsEnabled(bcmLogId_t logId, bcmLogDataDumpLevel_t ddLevel); +void bcm_dataDumpRegPrinter(uint32_t qId, uint32_t dataDumpId, Bcm_DataDumpPrintFunc *printFun); +void bcm_dataDump(uint32_t qID, uint32_t dataDumpID, const char* dataDumpName, void *ptr, uint32_t numBytes); +uint32_t bcm_dataDumpCreateQ(const char* qName); +void bcm_dataDumpDeleteQ(uint32_t qid); +#endif + +/* + * Create a DataDump queue. Different modules can share a queue. + * Returns a queue ID (uint32_t). + */ +#define BCM_DATADUMP_CREATE_Q(qName) BCM_DATADUMPCODE(bcm_dataDumpCreateQ(qName)) + +/* + * Delete a DataDump queue. + */ +#define BCM_DATADUMP_DELETE_Q(qID) BCM_DATADUMPCODE(bcm_dataDumpDeleteQ(qID)) + +/* + * Dump data + */ +#define BCM_DATADUMP_IMPORTANT(logId, qID, dataDumpID, ptr, numBytes) \ + BCM_DATADUMPCODE( do { bcmLogModuleInfo_t *_pModInfo = bcmLog_ddIsEnabled(logId, BCM_LOG_DD_IMPORTANT); \ + if (_pModInfo) \ + bcm_dataDump(qID, dataDumpID, #dataDumpID, (void*)(ptr), numBytes); } while(0) ) +#define BCM_DATADUMP_INFO(logId, qID, dataDumpID, ptr, numBytes) \ + BCM_DATADUMPCODE( do { bcmLogModuleInfo_t *_pModInfo = bcmLog_ddIsEnabled(logId, BCM_LOG_DD_INFO); \ + if (_pModInfo) \ + bcm_dataDump(qID, dataDumpID, #dataDumpID, (void*)(ptr), numBytes); } while(0) ) +#define BCM_DATADUMP_DETAIL(logId, qID, dataDumpID, ptr, numBytes) \ + BCM_DATADUMPCODE( do { bcmLogModuleInfo_t *_pModInfo = bcmLog_ddIsEnabled(logId, BCM_LOG_DD_DETAIL); \ + if (_pModInfo) \ + bcm_dataDump(qID, dataDumpID, #dataDumpID, (void*)(ptr), numBytes); } while(0) ) +#define BCM_DATADUMP_MAX(logId, qID, dataDumpID, ptr, numBytes) \ + BCM_DATADUMPCODE( do { bcmLogModuleInfo_t *_pModInfo = bcmLog_ddIsEnabled(logId, BCM_LOG_DD_MAX); \ + if (_pModInfo) \ + bcm_dataDump(qID, dataDumpID, #dataDumpID, (void*)(ptr), numBytes); } while(0) ) + +/* + * Register a printer for a certain DataDump ID. + * Datadumps for which no printer is registered will use a default printer. + * The default printer will print the data as an array of bytes. + */ +#define BCM_DATADUMP_REG_PRINTER(qId, dataDumpId, printFun) \ + BCM_DATADUMPCODE(bcm_dataDumpRegPrinter(qId, dataDumpId, printFun)) + +/* A helper macro for datadump printers */ +#define DDPRINTF(buf, len, bufSize, arg...) \ + ({len += snprintf((buf)+(len), max_t(uint32_t, 0, (bufSize)-80-(len)), ##arg); \ + if ((len) >= (bufSize)-80) snprintf((buf)+(len), 80, "---BUFFER FULL---\n");}) + + +/** + * Snapshot API: Commit all logs to the Snapshot queue + **/ + +#define BCM_LOG_SNAPSHOT() BCM_SNAPSHOTCODE() /*TBD*/ + + +/** + * API Function Prototypes + **/ + +#ifdef CONFIG_BCM_LOG + +void bcmLog_setGlobalLogLevel(bcmLogLevel_t logLevel); +bcmLogLevel_t bcmLog_getGlobalLogLevel(void); + +void bcmLog_setLogLevel(bcmLogId_t logId, bcmLogLevel_t logLevel); +bcmLogLevel_t bcmLog_getLogLevel(bcmLogId_t logId); + +char *bcmLog_getModName(bcmLogId_t logId); + +void bcmLog_registerSpiCallbacks(bcmLogSpiCallbacks_t callbacks); + +typedef int (bcmFun_t)(void *); + +/*Register a function with the bcmLog driver*/ +void bcmFun_reg(bcmFunId_t funId, bcmFun_t *f); + +/*De-Register a function with the bcmLog driver*/ +void bcmFun_dereg(bcmFunId_t funId); + +/*Look up a function by FunId. Returns NULL if the function is not + *registered.*/ +bcmFun_t* bcmFun_get(bcmFunId_t funId); + +void bcmLog_registerLevelChangeCallback(bcmLogId_t logId, bcmLogLevelChangeCallback_t callback, void *ctx); + +#else +/* BCM LOG not configured: create empty stubs for all functions */ +static inline bcmLogModuleInfo_t *bcmLog_logIsEnabled(bcmLogId_t logId, bcmLogLevel_t logLevel) { return NULL; } +static inline void bcmLog_setGlobalLogLevel(bcmLogLevel_t loglevel) {} +static inline bcmLogLevel_t bcmLog_getGlobalLogLevel(void) { return 0; } +static inline char *bcmLog_getModName(bcmLogId_t logId) { return NULL; } +static inline void bcmLog_registerSpiCallbacks(bcmLogSpiCallbacks_t callbacks) {} +static inline void bcmLog_setLogLevel(bcmLogId_t logId, bcmLogLevel_t logLevel) {} +static inline bcmLogLevel_t bcmLog_getLogLevel(bcmLogId_t logId) { return 0; } +typedef int (bcmFun_t)(void *); +static inline void bcmFun_reg(bcmFunId_t funId, bcmFun_t f) {} +static inline void bcmFun_dereg(bcmFunId_t funId) {} +static inline bcmFun_t* bcmFun_get(bcmFunId_t funId) { return NULL; } +static inline void bcmLog_registerLevelChangeCallback(bcmLogId_t logId, bcmLogLevelChangeCallback_t callback, void *ctx) {} + + +#endif /* CONFIG_BCM_LOG */ +#endif /*_BCM_LOG_SERVICES_*/ +#endif /* CONFIG_BCM_KF_LOG */ diff -urN linux-4.1.52-orig/include/linux/bcm_log_mod.h linux-4.1.52/include/linux/bcm_log_mod.h --- linux-4.1.52-orig/include/linux/bcm_log_mod.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/bcm_log_mod.h 2020-02-07 01:56:25.000000000 +0800 @@ -0,0 +1,322 @@ +#if defined(CONFIG_BCM_KF_LOG) +/* +* <:copyright-BRCM:2010:DUAL/GPL:standard +* +* Copyright (c) 2010 Broadcom +* All Rights Reserved +* +* Unless you and Broadcom execute a separate written software license +* agreement governing use of this software, this software is licensed +* to you under the terms of the GNU General Public License version 2 +* (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +* with the following added to such license: +* +* As a special exception, the copyright holders of this software give +* you permission to link this software with independent modules, and +* to copy and distribute the resulting executable under terms of your +* choice, provided that you also meet, for each linked independent +* module, the terms and conditions of the license of that module. +* An independent module is a module which is not derived from this +* software. The special exception does not apply to any modifications +* of the software. +* +* Not withstanding the above, under no circumstances may you combine +* this software in any way with any other Broadcom software provided +* under a license other than the GPL, without Broadcom's express prior +* written consent. +* +* :> + +*/ + +#ifndef _BCM_LOG_MODULES_ +#define _BCM_LOG_MODULES_ + +typedef enum { + BCM_LOG_LEVEL_ERROR=0, + BCM_LOG_LEVEL_NOTICE, + BCM_LOG_LEVEL_INFO, + BCM_LOG_LEVEL_DEBUG, + BCM_LOG_LEVEL_MAX +} bcmLogLevel_t; + +/* To support a new module, create a new log ID in bcmLogId_t, + and a new entry in BCM_LOG_MODULE_INFO */ + + +typedef enum { + BCM_LOG_ID_LOG=0, + BCM_LOG_ID_VLAN, + BCM_LOG_ID_GPON, + BCM_LOG_ID_PLOAM, + BCM_LOG_ID_PLOAM_FSM, + BCM_LOG_ID_PLOAM_HAL, + BCM_LOG_ID_PLOAM_PORT, + BCM_LOG_ID_PLOAM_ALARM, + BCM_LOG_ID_OMCI, + BCM_LOG_ID_I2C, + BCM_LOG_ID_ENET, + BCM_LOG_ID_GPON_SERDES, + BCM_LOG_ID_FAP, + BCM_LOG_ID_FAPPROTO, + BCM_LOG_ID_FAP4KE, + BCM_LOG_ID_AE, + BCM_LOG_ID_XTM, + BCM_LOG_ID_IQ, + BCM_LOG_ID_BPM, + BCM_LOG_ID_ARL, + BCM_LOG_ID_EPON, + BCM_LOG_ID_GMAC, + BCM_LOG_ID_RDPA, + BCM_LOG_ID_RDPA_CMD_DRV, + BCM_LOG_ID_PKTRUNNER, + BCM_LOG_ID_SIM_CARD, + BCM_LOG_ID_PMD, + BCM_LOG_ID_TM, + BCM_LOG_ID_SPDSVC, + BCM_LOG_ID_MCAST, + BCM_LOG_ID_DPI, + BCM_LOG_ID_CMDLIST, + BCM_LOG_ID_ARCHER, + BCM_LOG_ID_TOD, + BCM_LOG_ID_PON_PWM, + BCM_LOG_ID_OPTICALDET, + BCM_LOG_ID_WANTYPEDET, + BCM_LOG_ID_XPORT, + BCM_LOG_ID_BCMLIBS_BIT_POOL, + BCM_LOG_ID_MAX +} bcmLogId_t; + +#define BCM_LOG_MODULE_INFO \ + { \ + {.logId = BCM_LOG_ID_LOG, .name = "bcmlog", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_VLAN, .name = "vlan", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_GPON, .name = "gpon", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_PLOAM, .name = "ploam", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_PLOAM_FSM, .name = "ploamFsm", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_PLOAM_HAL, .name = "ploamHal", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_PLOAM_PORT, .name = "ploamPort", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_PLOAM_ALARM, .name = "ploamAlarm", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_OMCI, .name = "omci", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_I2C, .name = "i2c", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_ENET, .name = "enet", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_GPON_SERDES, .name = "gponSerdes", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_FAP, .name = "fap", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_FAPPROTO, .name = "fapProto", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_FAP4KE, .name = "fap4ke", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_AE, .name = "ae", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_XTM, .name = "xtm", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_IQ, .name = "iq", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_BPM, .name = "bpm", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_ARL, .name = "arl", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_EPON, .name = "eponlue", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_GMAC, .name = "gmac", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_RDPA, .name = "rdpa", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_RDPA_CMD_DRV, .name = "rdpadrv", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_PKTRUNNER, .name = "pktrunner", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_SIM_CARD, .name = "sim_card", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_PMD, .name = "pmd", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_TM, .name = "tm", .logLevel = BCM_LOG_LEVEL_ERROR}, \ + {.logId = BCM_LOG_ID_SPDSVC, .name = "spdsvc", .logLevel = BCM_LOG_LEVEL_ERROR}, \ + {.logId = BCM_LOG_ID_MCAST, .name = "mcast", .logLevel = BCM_LOG_LEVEL_ERROR}, \ + {.logId = BCM_LOG_ID_DPI, .name = "dpi", .logLevel = BCM_LOG_LEVEL_ERROR}, \ + {.logId = BCM_LOG_ID_CMDLIST, .name = "cmdlist", .logLevel = BCM_LOG_LEVEL_ERROR}, \ + {.logId = BCM_LOG_ID_ARCHER, .name = "archer", .logLevel = BCM_LOG_LEVEL_ERROR}, \ + {.logId = BCM_LOG_ID_TOD, .name = "tod", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_PON_PWM, .name = "pon_pwm", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_OPTICALDET, .name = "opticaldet", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_WANTYPEDET, .name = "wantypedet", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_XPORT, .name = "xport", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + {.logId = BCM_LOG_ID_BCMLIBS_BIT_POOL, .name = "bitpool", .logLevel = BCM_LOG_LEVEL_NOTICE}, \ + } + +/* To support a new registered function, + * create a new BCM_FUN_ID */ + +typedef enum { + BCM_FUN_ID_RESET_SWITCH=0, + BCM_FUN_ID_ENET_LINK_CHG, + BCM_FUN_ID_ENET_CHECK_SWITCH_LOCKUP, + BCM_FUN_ID_ENET_GET_PORT_BUF_USAGE, + BCM_FUN_ID_GPON_GET_GEM_PID_QUEUE, + BCM_FUN_ID_ENET_HANDLE, + BCM_FUN_ID_EPON_HANDLE, + BCM_FUN_IN_ENET_CLEAR_ARL_ENTRY, +#if defined(CONFIG_BCM_GMAC) + BCM_FUN_ID_ENET_GMAC_ACTIVE, + BCM_FUN_ID_ENET_GMAC_PORT, +#endif + BCM_FUN_ID_ENET_IS_WAN_PORT, /* Take Logical port number as argument */ + BCM_FUN_ID_ENET_IS_SWSWITCH_PORT, + BCM_FUN_ID_ENET_LAG_PORT_GET, + BCM_FUN_ID_ENET_PORT_ROLE_NOTIFY, + BCM_FUN_ID_ENET_BOND_RX_PORT_MAP, + BCM_FUN_ID_ENET_SYSPORT_CONFIG, + BCM_FUN_ID_ENET_SYSPORT_QUEUE_MAP, + BCM_FUN_ID_ENET_REMAP_TX_QUEUE, + BCM_FUN_ID_ENET_PHY_SPEED_SET, + BCM_FUN_ID_ENET_TM_EN_SET, + /* The arguments of the BCM TM functions are defined by bcmTmDrv_arg_t */ + BCM_FUN_ID_TM_REGISTER, + BCM_FUN_ID_TM_PORT_CONFIG, + BCM_FUN_ID_TM_PORT_ENABLE, + BCM_FUN_ID_TM_ARBITER_CONFIG, + BCM_FUN_ID_TM_QUEUE_CONFIG, + BCM_FUN_ID_TM_APPLY, + BCM_FUN_ID_TM_ENQUEUE, + /* The arguments of the Speed Service functions are defined in spdsvc_defs.h */ + BCM_FUN_ID_SPDSVC_TRANSMIT, + BCM_FUN_ID_SPDSVC_RECEIVE, + /* The arguments of the FAP functions are defined in fap.h */ + BCM_FUN_ID_FAP_PERF_ENABLE, + BCM_FUN_ID_FAP_PERF_DISABLE, + BCM_FUN_ID_FAP_PERF_SET_GENERATOR, + BCM_FUN_ID_FAP_PERF_SET_ANALYZER, + BCM_FUN_ID_FAP_PERF_GET_RESULTS, + /* Kernel Bonding Driver related function */ + BCM_FUN_ID_BOND_CLR_SLAVE_STAT, + BCM_FUN_ID_ENET_IS_BONDED_LAN_WAN_PORT, /* Expects Logical port number as argument */ + BCM_FUN_ID_ENET_IS_DEV_IN_SLAVE_PATH, + BCM_FUN_ID_BOND_RX_HANDLER, + /* DSL Runner Hooks */ + BCM_FUN_ID_RUNNER_PREPEND, + /* Archer Hooks */ + BCM_FUN_ID_ARCHER_HOST_BIND, + BCM_FUN_ID_ARCHER_XTMRT_BIND, + BCM_FUN_ID_ARCHER_WFD_BIND, + BCM_FUN_ID_ARCHER_WFD_CONFIG, + BCM_FUN_ID_VLAN_LOOKUP_DP, + BCM_FUN_ID_MAX +} bcmFunId_t; + +/* Structures passed in above function calls */ +typedef struct { + struct net_device *slave_dev; /* Input */ + struct net_device **bond_dev; /* Input/Output */ +}BCM_BondDevInfo; + +/* Structures passed in above function calls */ +typedef struct { + uint16_t gemPortIndex; /* Input */ + uint16_t gemPortId; /* Output */ + uint8_t usQueueIdx; /* Output */ +}BCM_GponGemPidQueueInfo; + +typedef enum { + BCM_ENET_FUN_TYPE_LEARN_CTRL = 0, + BCM_ENET_FUN_TYPE_ARL_WRITE, + BCM_ENET_FUN_TYPE_AGE_PORT, + BCM_ENET_FUN_TYPE_UNI_UNI_CTRL, + BCM_ENET_FUN_TYPE_PORT_RX_CTRL, + BCM_ENET_FUN_TYPE_GET_VPORT_CNT, + BCM_ENET_FUN_TYPE_GET_IF_NAME_OF_VPORT, + BCM_ENET_FUN_TYPE_GET_UNIPORT_MASK, + BCM_ENET_FUN_TYPE_MAX +} bcmFun_Type_t; + +typedef struct { + uint16_t vid; + uint16_t val; + uint8_t mac[6]; +} arlEntry_t; + +typedef struct { + bcmFun_Type_t type; /* Action Needed in Enet Driver */ + union { + uint8_t port; + uint8_t uniport_cnt; + uint16_t portMask; + arlEntry_t arl_entry; + }; + char name[16]; + uint8_t enable; +}BCM_EnetHandle_t; + +typedef enum { + BCM_EPON_FUN_TYPE_UNI_UNI_CTRL = 0, + BCM_EPON_FUN_TYPE_MAX +} bcmEponFun_Type_t; + +typedef struct { + bcmEponFun_Type_t type; /* Action Needed in Epon Driver */ + uint8_t enable; +}BCM_EponHandle_t; + +typedef struct { + uint8_t port; /* switch port */ + uint8_t enable; /* enable/disable the clock */ +}BCM_CmfFfeClk_t; + +#define BCM_RUNNER_PREPEND_SIZE_MAX 32 /* Increasing the size of the prepend data buffer will require + recompiling the pktrunner driver files released as binary */ + +typedef struct { + void *blog_p; /* INPUT: Pointer to the Blog_t structure that triggered the Runner flow creation */ + uint8_t data[BCM_RUNNER_PREPEND_SIZE_MAX]; /* INPUT: The data that will be be prepended to all packets + forwarded by Runner that match the given Blog/Flow. + The data must be stored in NETWWORK BYTE ORDER */ + unsigned int size; /* OUTPUT: Size of the prepend data, up to 32 bytes long. + When no data is to be prepended, specify size = 0 */ +} BCM_runnerPrepend_t; + +typedef struct { + unsigned int sysport; + unsigned int port; + unsigned int is_wan; +} BCM_EnetPortRole_t; + +#define BCM_ENET_SYSPORT_INTF_MAX 2 +#define BCM_ENET_SYSPORT_BLOG_CHNL_MAX 8 + +typedef enum { + BCM_ENET_SYSPORT_MODE_INVALID = 0, + BCM_ENET_SYSPORT_MODE_PORT, + BCM_ENET_SYSPORT_MODE_INTERNAL_BRCM_SW, + BCM_ENET_SYSPORT_MODE_EXTERNAL_BRCM_SW, + BCM_ENET_SYSPORT_MODE_MAX +} bcmSysport_Mode_t; + +typedef struct { + bcmSysport_Mode_t mode; +} bcmSysport_Sysport_t; + +typedef struct { + struct net_device *dev; + int sysport; + int port; + int nbr_of_queues; +} bcmSysport_BlogChnl_t; + +typedef struct { + int nbr_of_sysports; + bcmSysport_Sysport_t sysport[BCM_ENET_SYSPORT_INTF_MAX]; + int nbr_of_blog_channels; + bcmSysport_BlogChnl_t blog_chnl[BCM_ENET_SYSPORT_BLOG_CHNL_MAX]; +} bcmSysport_Config_t; + +#define BCM_ENET_SYSPORT_QUEUE_MAP_PRIORITY_MAX 8 + +typedef struct { + int blog_chnl; + uint8_t priority_to_switch_queue[BCM_ENET_SYSPORT_QUEUE_MAP_PRIORITY_MAX]; +} bcmSysport_QueueMap_t; + +typedef struct { + int blog_chnl; + int blog_chnl_rx; +} bcmEnet_BondRxPortMap_t; + +/* Structure used with BCM_FUN_ID_ENET_REMAP_TX_QUEUE */ +typedef struct { + uint8_t tx_queue; + void *dev; +} bcmEnet_QueueReMap_t; + +typedef struct { + struct net_device *dev; + int kbps; +} bcmSysport_PhySpeed_t; + +#endif /* _BCM_LOG_MODULES_ */ + +#endif diff -urN linux-4.1.52-orig/include/linux/bcm_m2mdma.h linux-4.1.52/include/linux/bcm_m2mdma.h --- linux-4.1.52-orig/include/linux/bcm_m2mdma.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/bcm_m2mdma.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,41 @@ +/* +<:copyright-BRCM:2015:DUAL/GPL:standard + + Copyright (c) 2015 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +#if !defined(__BCM_M2M_DMA_H__) && defined(CONFIG_BCM_KF_M2M_DMA) && defined(CONFIG_BCM_M2M_DMA) +#define __BCM_M2M_DMA_H__ + + +extern uint32_t bcm_m2m_dma_memcpy_async_uncached(uint32_t phys_dest, uint32_t phys_src, uint16_t len); + +extern uint32_t bcm_m2m_dma_memcpy_async(void *dest, void *src, uint16_t len); +extern uint32_t bcm_m2m_dma_memcpy_async_no_flush(void *dest, void *src, uint16_t len); + +extern uint32_t bcm_m2m_dma_memcpy_async_no_flush_inv(void *dest, void *src, uint16_t len); +extern int bcm_m2m_wait_for_complete(uint32_t desc_id); + +#endif /* __BCM_M2M_DMA_H__ */ diff -urN linux-4.1.52-orig/include/linux/bcm_realtime.h linux-4.1.52/include/linux/bcm_realtime.h --- linux-4.1.52-orig/include/linux/bcm_realtime.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/bcm_realtime.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,79 @@ +#if defined(CONFIG_BCM_KF_RTPRIO_DEF) +/* +<:copyright-BRCM:2011:DUAL/GPL:standard + + Copyright (c) 2011 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + +#ifndef _BCM_REALTIME_H_ +#define _BCM_REALTIME_H_ + +/* + * This file defines the real time priority levels used by the various + * threads in the system. It is important that all threads coordinate + * their priority levels so that the desired effect is achieved. + * These priorities are also related cgroups, so check the cgroups + * groupings and cpu allocations (if cgroups is enabled). + */ + +/** highest priority threads in the system. + * + * Threads at this priority require the absolute minium latency. However, + * they should only run very briefly (<2ms per run). + * These threads should also run at sched policy FIFO. + */ +#define BCM_RTPRIO_HIGH 75 + + +/** priority for the voip DSP. + * + * Note this is not for all voip threads, just the DSP thread. + * The other voice threads should be run at the other priorities that are + * defined. + */ +#define BCM_RTPRIO_VOIPDSP 35 + + +/** priority for all data forwarding. + * + * This is for data and video streaming. Not clear if we need to split out + * sub-categories here such as video, versus web data, versus voice. + * Probably need to use cgroups if a system needs to handle many types of + * streams. + * Threads running at this priority should use sched policy Round-Robin. + */ +#define BCM_RTPRIO_DATA 5 + +/** priority for all tasks that handle control messages related to data path. + * + * ex: bpm tasl handling allocation/free of data buffers. + * Threads running at this priority should use sched policy Round-Robin. + */ +#define BCM_RTPRIO_DATA_CONTROL 10 + +#endif /* _BCM_REALTIME_H_ */ + +#endif diff -urN linux-4.1.52-orig/include/linux/bcm_skb_defines.h linux-4.1.52/include/linux/bcm_skb_defines.h --- linux-4.1.52-orig/include/linux/bcm_skb_defines.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/bcm_skb_defines.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,144 @@ +#if defined(CONFIG_BCM_KF_SKB_DEFINES) +/* +* <:copyright-BRCM:2014:DUAL/GPL:standard +* +* Copyright (c) 2014 Broadcom +* All Rights Reserved +* +* Unless you and Broadcom execute a separate written software license +* agreement governing use of this software, this software is licensed +* to you under the terms of the GNU General Public License version 2 +* (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +* with the following added to such license: +* +* As a special exception, the copyright holders of this software give +* you permission to link this software with independent modules, and +* to copy and distribute the resulting executable under terms of your +* choice, provided that you also meet, for each linked independent +* module, the terms and conditions of the license of that module. +* An independent module is a module which is not derived from this +* software. The special exception does not apply to any modifications +* of the software. +* +* Not withstanding the above, under no circumstances may you combine +* this software in any way with any other Broadcom software provided +* under a license other than the GPL, without Broadcom's express prior +* written consent. +* +* :> +*/ + +#ifndef _BCM_SKB_DEFINES_ +#define _BCM_SKB_DEFINES_ + +/* queue = mark[4:0] */ +#define SKBMARK_Q_S 0 +#define SKBMARK_Q_M (0x1F << SKBMARK_Q_S) +#define SKBMARK_GET_Q(MARK) ((MARK & SKBMARK_Q_M) >> SKBMARK_Q_S) +#define SKBMARK_SET_Q(MARK, Q) ((MARK & ~SKBMARK_Q_M) | (Q << SKBMARK_Q_S)) +/* traffic_class_id = mark[10:5] */ +#define SKBMARK_TC_ID_S 5 +#define SKBMARK_TC_ID_M (0x3F << SKBMARK_TC_ID_S) +#define SKBMARK_GET_TC_ID(MARK) ((MARK & SKBMARK_TC_ID_M) >> SKBMARK_TC_ID_S) +#define SKBMARK_SET_TC_ID(MARK, TC) \ + ((MARK & ~SKBMARK_TC_ID_M) | (TC << SKBMARK_TC_ID_S)) +/* flow_id = mark[18:11] */ +#define SKBMARK_FLOW_ID_S 11 +#define SKBMARK_FLOW_ID_M (0xFF << SKBMARK_FLOW_ID_S) +#define SKBMARK_GET_FLOW_ID(MARK) \ + ((MARK & SKBMARK_FLOW_ID_M) >> SKBMARK_FLOW_ID_S) +#define SKBMARK_SET_FLOW_ID(MARK, FLOW) \ + ((MARK & ~SKBMARK_FLOW_ID_M) | (FLOW << SKBMARK_FLOW_ID_S)) +/* iq_prio = mark[19]; for Ingress QoS used when TX is WLAN */ +#define SKBMARK_IQPRIO_MARK_S 19 +#define SKBMARK_IQPRIO_MARK_M (0x01 << SKBMARK_IQPRIO_MARK_S) +#define SKBMARK_GET_IQPRIO_MARK(MARK) \ + ((MARK & SKBMARK_IQPRIO_MARK_M) >> SKBMARK_IQPRIO_MARK_S) +#define SKBMARK_SET_IQPRIO_MARK(MARK, IQPRIO_MARK) \ + ((MARK & ~SKBMARK_IQPRIO_MARK_M) | (IQPRIO_MARK << SKBMARK_IQPRIO_MARK_S)) +/* port = mark[26:20]; for enet driver of gpon port, this is gem_id */ +#define SKBMARK_PORT_S 20 +#define SKBMARK_PORT_M (0x7F << SKBMARK_PORT_S) +#define SKBMARK_GET_PORT(MARK) \ + ((MARK & SKBMARK_PORT_M) >> SKBMARK_PORT_S) +#define SKBMARK_SET_PORT(MARK, PORT) \ + ((MARK & ~SKBMARK_PORT_M) | (PORT << SKBMARK_PORT_S)) +#if defined(CONFIG_BCM_KF_WANDEV) +/* iffwan_mark = mark[27] -- BRCM defined-- */ +#define SKBMARK_IFFWAN_MARK_S 27 +#define SKBMARK_IFFWAN_MARK_M (0x01 << SKBMARK_IFFWAN_MARK_S) +#define SKBMARK_GET_IFFWAN_MARK(MARK) \ + ((MARK & SKBMARK_IFFWAN_MARK_M) >> SKBMARK_IFFWAN_MARK_S) +#define SKBMARK_SET_IFFWAN_MARK(MARK, IFFWAN_MARK) \ + ((MARK & ~SKBMARK_IFFWAN_MARK_M) | (IFFWAN_MARK << SKBMARK_IFFWAN_MARK_S)) +#endif +/* ipsec_mark = mark[28] */ +#define SKBMARK_IPSEC_MARK_S 28 +#define SKBMARK_IPSEC_MARK_M (0x01 << SKBMARK_IPSEC_MARK_S) +#define SKBMARK_GET_IPSEC_MARK(MARK) \ + ((MARK & SKBMARK_IPSEC_MARK_M) >> SKBMARK_IPSEC_MARK_S) +#define SKBMARK_SET_IPSEC_MARK(MARK, IPSEC_MARK) \ + ((MARK & ~SKBMARK_IPSEC_MARK_M) | (IPSEC_MARK << SKBMARK_IPSEC_MARK_S)) +/* policy_routing = mark[31:29] */ +#define SKBMARK_POLICY_RTNG_S 29 +#define SKBMARK_POLICY_RTNG_M (0x07 << SKBMARK_POLICY_RTNG_S) +#define SKBMARK_GET_POLICY_RTNG(MARK) \ + ((MARK & SKBMARK_POLICY_RTNG_M) >> SKBMARK_POLICY_RTNG_S) +#define SKBMARK_SET_POLICY_RTNG(MARK, POLICY) \ + ((MARK & ~SKBMARK_POLICY_RTNG_M) | (POLICY << SKBMARK_POLICY_RTNG_S)) + +/* dpi_queue = mark[31:27] */ +/* Overlaps with SKBMARK_IFFWAN, SKBMARK_IPSEC, and SKBMARK_POLICY_RTNG */ +#define SKBMARK_DPIQ_MARK_S 27 +#define SKBMARK_DPIQ_MARK_M (0x1F << SKBMARK_DPIQ_MARK_S) +#define SKBMARK_GET_DPIQ_MARK(MARK) \ + ((MARK & SKBMARK_DPIQ_MARK_M) >> SKBMARK_DPIQ_MARK_S) +#define SKBMARK_SET_DPIQ_MARK(MARK, DPIQ_MARK) \ + ((MARK & ~SKBMARK_DPIQ_MARK_M) | (DPIQ_MARK << SKBMARK_DPIQ_MARK_S)) + +/* The enet driver subdivides queue field (mark[4:0]) in the skb->mark into + priority and channel */ +/* priority = queue[2:0] (=>mark[2:0]) */ +#define SKBMARK_Q_PRIO_S (SKBMARK_Q_S) +#define SKBMARK_Q_PRIO_M (0x07 << SKBMARK_Q_PRIO_S) +#define SKBMARK_GET_Q_PRIO(MARK) \ + ((MARK & SKBMARK_Q_PRIO_M) >> SKBMARK_Q_PRIO_S) +#define SKBMARK_SET_Q_PRIO(MARK, Q) \ + ((MARK & ~SKBMARK_Q_PRIO_M) | (Q << SKBMARK_Q_PRIO_S)) +/* channel = queue[4:3] (=>mark[4:3]) */ +#define SKBMARK_Q_CH_S (SKBMARK_Q_S + 3) +#define SKBMARK_Q_CH_M (0x03 << SKBMARK_Q_CH_S) +#define SKBMARK_GET_Q_CHANNEL(MARK) ((MARK & SKBMARK_Q_CH_M) >> SKBMARK_Q_CH_S) +#define SKBMARK_SET_Q_CHANNEL(MARK, CH) \ + ((MARK & ~SKBMARK_Q_CH_M) | (CH << SKBMARK_Q_CH_S)) + +#define SKBMARK_ALL_GEM_PORT (0xFF) + +#define WLAN_PRIORITY_BIT_POS (1) +#define WLAN_PRIORITY_MASK (0x7 << WLAN_PRIORITY_BIT_POS) +#define GET_WLAN_PRIORITY(VAL) ((VAL & WLAN_PRIORITY_MASK) >> WLAN_PRIORITY_BIT_POS) +#define SET_WLAN_PRIORITY(ENCODEVAL, PRIO) ((ENCODEVAL & (~WLAN_PRIORITY_MASK)) | (PRIO << WLAN_PRIORITY_BIT_POS)) + +#define WLAN_IQPRIO_BIT_POS (0) +#define WLAN_IQPRIO_MASK (0x1 << WLAN_IQPRIO_BIT_POS) +#define GET_WLAN_IQPRIO(VAL) ((VAL & WLAN_IQPRIO_MASK) >> WLAN_IQPRIO_BIT_POS) +#define SET_WLAN_IQPRIO(ENCODEVAL, IQPRIO) ((ENCODEVAL & (~WLAN_IQPRIO_MASK)) | (IQPRIO << WLAN_IQPRIO_BIT_POS)) + +// LINUX_PRIORITY_BIT_POS_IN_MARK macro must be in sync with PRIO_LOC_NFMARK +// defined in linux_osl_dslcpe.h +#define LINUX_PRIORITY_BIT_POS_IN_MARK 16 +#define LINUX_PRIORITY_BIT_MASK (0x7 << LINUX_PRIORITY_BIT_POS_IN_MARK) +#define LINUX_GET_PRIO_MARK(MARK) ((MARK & LINUX_PRIORITY_BIT_MASK) >> LINUX_PRIORITY_BIT_POS_IN_MARK) +#define LINUX_SET_PRIO_MARK(MARK, PRIO) ((MARK & (~LINUX_PRIORITY_BIT_MASK)) | (PRIO << LINUX_PRIORITY_BIT_POS_IN_MARK)) + +//Encode 3 bits of priority and 1 bit of IQPRIO into 4 bits as follows (3bitPrio:1bitIQPrio) +#define ENCODE_WLAN_PRIORITY_MARK(u8EncodeVal, u32Mark) \ + (u8EncodeVal = SET_WLAN_PRIORITY(u8EncodeVal, LINUX_GET_PRIO_MARK(u32Mark)) | SET_WLAN_IQPRIO(u8EncodeVal, SKBMARK_GET_IQPRIO_MARK(u32Mark))) +#define DECODE_WLAN_PRIORITY_MARK(encodedVal, u32Mark) \ + do { (u32Mark) = LINUX_SET_PRIO_MARK(u32Mark, GET_WLAN_PRIORITY(encodedVal)); \ + (u32Mark) = SKBMARK_SET_IQPRIO_MARK(u32Mark, GET_WLAN_IQPRIO(encodedVal)); \ + } while(0) + + +#endif /* _BCM_SKB_DEFINES_ */ +#endif /* CONFIG_BCM_KF_SKB_DEFINES */ diff -urN linux-4.1.52-orig/include/linux/bcm_tstamp.h linux-4.1.52/include/linux/bcm_tstamp.h --- linux-4.1.52-orig/include/linux/bcm_tstamp.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/bcm_tstamp.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,69 @@ +#if defined(CONFIG_BCM_KF_TSTAMP) +/* +<:copyright-BRCM:2011:GPL/GPL:standard + + Copyright (c) 2011 Broadcom + All Rights Reserved + +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 (the "GPL"). + +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. + + +A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by +writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + +:> +*/ + +#ifndef _BCM_TSTAMP_H_ +#define _BCM_TSTAMP_H_ + +#include + +/* + * This is a just a simple set of utility functions for measuring + * small amounts of time (<20 seconds) using the MIPS c0 counter. + * The main limitation with this implementation is that the c0 counter + * will roll over about every 21 seconds, so measurements of time that + * are longer than 20 seconds will be unreliable. + * These functions maintain no state (other than the initial multipliers + * and divisors based on clock speed), so no SMP locking is needed to + * use these functions. + * It is OK to read a starting timestamp on one CPU, and then read the + * ending timestamp on the other. The c0 counters of both CPU's are + * within about 20 cycles of each other, and bcm_tstamp_delta() tries + * to detect if a migration plus read of a slightly behind end timestamp + * has happened (seems extremely unlikely though). In this case, it + * returns 1 cycle (instead of 4 billion cycles, which is unlikely unless + * you are measuring something that is close to 20 seconds long.) + */ + +/** Get current timestamp + */ +u32 bcm_tstamp_read(void); + +/** Return the number of cycles elapsed between start and end. + */ +u32 bcm_tstamp_delta(u32 start, u32 end); + +/** Return the number of cycles elapsed between start and now. + */ +u32 bcm_tstamp_elapsed(u32 start); + +/** Convert a timestamp to microseconds. + */ +u32 bcm_tstamp2us(u32 i); + +/** Convert a timestamp to nanoseconds. Note 64 bit return val. + */ +u64 bcm_tstamp2ns(u32 i); + +#endif /* _BCM_TSTAMP_H_ */ +#endif /* defined(BCM_KF_TSTAMP) */ diff -urN linux-4.1.52-orig/include/linux/blog.h linux-4.1.52/include/linux/blog.h --- linux-4.1.52-orig/include/linux/blog.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/blog.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,2933 @@ +#if defined(CONFIG_BLOG) + +#ifndef __BLOG_H_INCLUDED__ +#define __BLOG_H_INCLUDED__ + +/*--------------------------------*/ +/* Blog.h and Blog.c for Linux OS */ +/*--------------------------------*/ + +/* +* <:copyright-BRCM:2003:DUAL/GPL:standard +* +* Copyright (c) 2003 Broadcom +* All Rights Reserved +* +* Unless you and Broadcom execute a separate written software license +* agreement governing use of this software, this software is licensed +* to you under the terms of the GNU General Public License version 2 +* (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +* with the following added to such license: +* +* As a special exception, the copyright holders of this software give +* you permission to link this software with independent modules, and +* to copy and distribute the resulting executable under terms of your +* choice, provided that you also meet, for each linked independent +* module, the terms and conditions of the license of that module. +* An independent module is a module which is not derived from this +* software. The special exception does not apply to any modifications +* of the software. +* +* Not withstanding the above, under no circumstances may you combine +* this software in any way with any other Broadcom software provided +* under a license other than the GPL, without Broadcom's express prior +* written consent. +* +:> +*/ + +/* + ******************************************************************************* + * + * File Name : blog.h + * + * Description: + * + * A Blog is an extension of the native OS network stack's packet context. + * In Linux a Blog would be an extension of the Linux socket buffer (aka skbuff) + * or a network device driver level packet context FkBuff. The nbuff layer + * provides a transparent access SHIM to the underlying packet context, may it + * be a skbuff or a fkbuff. In a BSD network stack, a packet context is the + * BSD memory buffer (aka mbuff). + * + * Blog layer provides Blog clients a SHIM to the native OS network stack: + * Blog clients may be implemented to: + * - debug trace a packet as it passes through the network stack, + * - develop traffic generators (loop) at the network device driver level. + * - develop network driver level promiscuous mode bound applications and use + * the Blog SHIM to isolate themselves from the native OS network constructs + * or proprietery network constructs such as Ethernet bridges, VLAN network + * interfaces, IGMP, firewall and connection tracking systems. + * + * As such, Blog provides an extension of the packet context and contains the + * received and transmitted packets data and parsed information. Parsing results + * are saved to describe, the type of layer 1, 2, 3 and 4 headers seen, whether + * the packet was a unicast, broadcast or multicast, a tunnel 4in6 or 6in4 etc. + * + * Blog views a receive or transmit end-point to be any construct that can be + * described by a end point context and a handler op. An end-point could hence + * be a: + * - a network device (Linux net_device with hard start transmit handler), + * - a link or queue in the network stack (e.g. a Linux Traffic Control queue + * or a netlink or raw socket queue), + * - a file system logging interface and its logging handler, + * - a virtual interface to some hardware block that provides some hardware + * assisted functionality (e.g. IPSEC acceleration or checksum offloading + * or GSO block), + * - a raw interface to an external hardware test traffic generator using say + * a DMA mapped packet reception or transmission. + * + * Blog clients are hence applications that provide value added capability by + * binding at such end-points. + * + * A simple Blog client application is a loop traffic generator that simply + * acts as a sink of packets belonging to a specific "l3 flow" and mirrors + * them to another interface or loops them back into the stack by serving as a + * source to a receive network device, while measuring the packet processing + * datapath performance in the native OS network stack/proprietary constructs. + * Such a loop traffic generator could be used to inject N cells/packets + * that cycle through the system endlessly, serving as background traffic while + * a few flows are studied from say a QOS perspective. + * + * Another example of a Blog client is a proxy accelerator (hardware / software) + * that is capable of snooping on specific flows and accelerating them while + * bypassing the native OS network stack and/or proprietery constructs. It is + * however required that the native OS constructs can co-exist. E.g. it may be + * necessary to refresh a network bridge's ARL table, or a connection/session + * tracker, or update statistics, when individual packets bypass such network + * constructs. A proxy accelerator may also reside between a Rx network device + * a hardware IPSEC accelerator block and a Tx network device. + * + * Blog layer provides a logical composite SHIM to the network constructs + * Linux or proprietery, allowing 3rd party network constructs to be seemlesly + * supported in the native OS. E.g a network stack that uses a proprietery + * session tracker with firewalling capability would need to be transparently + * accessed, so that a Blog client may refresh the session tracking object when + * packets bypass the network stack. + * + * For each OS (eCOS, Linux, BSD) a blog.c implementation file is provided that + * implements the OS specific SHIM. Support for 3rd-party network constructs + * would need to be defined in the blog.c . E.g. for Linux, if a proprietery + * session tracker replaces the Linux netfilter connection tracking framework, + * then the void * ct_p and the corresponding query/set operations would need to + * be implemented. The Blog clients SHOULD NOT rely on any function other than + * those specifically defined allowing a coexistence of the Blog client and the + * native construct. In the example of a ct_p, for all practice and purposes, + * the void *, could have been a key or a handle to a connection tracking object + * + * Likewise, the Blog client may save need to save a client key with the + * network constuct. Again a client key may be a pointer to a client object or + * simply a hash key or some handle semantics. + * + * The logical SHIM is defined as follows: + * + * __doc_include_if_linux__ + * + * 1. Extension of a packet context with a logging context: + * ======================================================== + * Explicit APIs to allocate/Free a Blog structure, and bind to the packet + * context, may it be a skbuff or a fkbuff. Support for transferring a + * Blog_t structure from one packet context to another during the course of + * a packet in the network stack involving a packet context clone/copy is + * also included. The release and recycling of Blog_t structures when a + * packet context is freed are also providied. + * Binding is bi-directional: packet context <-- --> Blog_t + * + * + * 2. Associating native OS or 3rd-party network constructs: blog_link() + * ========================================================================== + * Examples of network constructs + * "dev" - Network device + * "ct" - Connection or session tracker + * "fdb" - Network bridge forwarding database entity + * + * Association is pseudo bi-directional, using "void *" binding in a Blog_t to + * a network construct. In the reverse, a network construct will link to a + * Blog client entity using a Key concept. Two types of keys are currently + * employed, a BlogFlowKey and a BlogGroupKey. + * + * A BlogFlowKey would typically refer to a single unidirectional packet + * stream defined by say all packets belonging to a unidirectional IPv4 flow, + * whereas a BlogGroupKey could be used to represent a single downstream + * multicast stream (IP multicast group) that results in replicated streams + * pertaining to multiple clients joining a the IPv4 multicast group. + * + * Likewise, one may represent a single unidirectional IPv4 UDP flow using + * BlogFlowKey, and the reverse direction IPv4 UDP reply flow + * using another BlogFlowKey, and represent the mated pair using a + * BlogGroupKey. + * + * In a Blog traffic generator client, where in several IPv4 UDP flows, each + * represented independently using a BlogFlowKey, allows for a set of them + * (background downstream stress traffic) to be managed as a group using a + * BlogGroupKey. + * + * Designer Note: + * A network construct may be required to save a BlogFlowKey and/or + * BlogGroupKey to complete the reverse binding between a network construct + * and the Blog client application. An alternate approach would be to save + * a pointer to the Blog_t in the network construct with an additional + * dereference through the keys saved within the Blog_t object. + * + * A BlogFlowKey and a BlogGroupKey is a 32bt sized unit and can serve either + * as a pointer (32bit processor) or a index or a hash key or ... + * + * + * 3. Network construct and Blog client co-existence call backs: + * ============================================================= + * + * blog_notify(): + * ============== + * A network construct may notify a Blog client of a change of status and may + * be viewed as a "downcall" from specialized network construct to a Blog client + * E.g. if a connection/session tracking system deems that a flow needs to be + * deleted or say it itself is being destroyed, then it needs to notify the Blog + * client. This would allow the Blog client to cleanup any association with the + * network construct. + * Ability for a Blog client to receive general system wide notifications of + * changes, to include, network interfaces or link state changes, protocol stack + * service access point changes, etc. + * Designer Note: Linux notification list? + * + * blog_request(): + * =============== + * A Blog client may request a change in state in the network construct and may + * be viewed as a "upcall" from the Blog client into the network construct. A + * timer refresh of the bridge fdb or connection tracking object, or a query + * whether the session tracker has successfully established (e.g. a TCP 3-way + * handshake has completed, or a IGMP client was permitted to join a group, or a + * RTSP session was successful) a uni-driectional or bi-directional flow. + * + * + * 4. Network end-point binding of Blog client + * =========================================== + * + * blog_init(), blog_sinit(), blog_finit(): + * ======================================== + * __comment_if_linux__ : This function is invoked by a Linux network device on + * packet reception to pass the packet to a Blog client application. + * + * Pass a packet context to a Blog client at a "RX" network device either using + * a skbuff or a fkbuff packet context. Blog client MAY ONLY ACCESS fkbuff + * fields. As per the nbuff specification, a FkBuff may be considered as a + * base class and a skbuff is a derived class, inheriting the base class members + * of the base class, fkbuff. The basic fields of a packet context are a pointer + * to the received packet's data, data length, a set of reserved fields to carry + * layer 1 information, queue priority, etc, and packet context and or packet + * recycling. The layer 1 information is described in terms of channels and + * and link layer phy preambles. A channel could be an ATM VCI, a DSL queue, a + * PON Gem Port. A Phy could describe the LINK layer type and or a preamble for + * instance a RFC2684 header in the DSL world. + * + * blog_[s|f]init() will setup the L1 coarse key and invokes a Blog + * client's receive hook. A Blog client may consume the packet bypassing the + * native OS network stack, may suggest that the packet context be extended by + * a Blog_t structure or may deem that the packet is of not interest. As such + * the Blog client will return PKT_DONE, PKT_BLOG or PKT_NORM, respectively. In + * case no Blog client has been registered for receiving packets (promiscuous) + * driectly from RX network devices, then the packet will follow a normal data + * path within the network stack (PKT_NORM). + * + * Designer Note: Blog clients MAY NOT use fields not defined in FkBuff. + * + * + * blog_emit(): + * ============ + * __comment_if_linux__ : This function is invoked by a Linux network device + * prior to packet transmission to pass the packet to a Blog client application. + * + * Pass a packet context to a Blog client at a "TX" network device either using + * a skbuff or a fkbuff packet context. The same restrictions on a Blog client + * pertaining to packet field context access as defined in the blog_init() + * variant of APIs is applicable to blog_emit(). A Blog client may also return + * PKT_NORM or PKT_DONE, to indicate normal processing, or packet consumption. + * + * Designer Note: blog_emit() will ONLY pass those packets to Blog clients that + * have a packet context extended with a Blog_t structure. Hence skbuffs or + * fkbuffs that do not have a Blog_t extension will not be handed to the Blog + * client. Do we need blog_semit/blog_femit variants. + * + * + * 5. Binding Blog client applications: blog_bind() + * ================================================ + * blog_bind() enables a "single" client to bind into the network stack by + * specifying a network device packet reception handler, a network device packet + * transmission handler, network stack to blog client notify hook. + * + * + * 6. Miscellanous + * =============== + * - Blog_t management. + * - Data-filling a Blog_t. + * - Protocol Header specifications independent of OS. + * - Debug printing. + * + * + * __end_include_if_linux__ + * + * Version 1.0 SKB based blogging + * Version 2.0 NBuff/FKB based blogging (mbuf) + * Version 2.1 IPv6 Support + * Version 3.0 Restructuring Blog SHIM to support eCOS, Linux and proprietery + * network constructs + * + ******************************************************************************* + */ + +#define BLOG_VERSION "v3.0" + +#if defined(__KERNEL__) /* Kernel space compilation */ +#include /* LINUX ISO C99 7.18 Integer types */ +#else /* User space compilation */ +#include /* C-Lib ISO C99 7.18 Integer types */ +#endif +#include /* IEEE and RFC standard definitions */ +#include /* for IS_SKBUFF_PTR */ +#include + +#ifndef NULL_STMT +#define NULL_STMT do { /* NULL BODY */ } while (0) +#endif + +#undef BLOG_DECL +#define BLOG_DECL(x) x, + +#ifndef BLOG_OFFSETOF +#define BLOG_OFFSETOF(stype, member) ((size_t) &((struct stype *)0)->member) +#endif + +/* Forward declarations */ +struct blog_t; +typedef struct blog_t Blog_t; +#define BLOG_NULL ((Blog_t*)NULL) +#define BLOG_KEY_NONE 0 + +/* __bgn_include_if_linux__ */ + +struct sk_buff; /* linux/skbuff.h */ +struct fkbuff; /* linux/nbuff.h */ + +/* See RFC 4008 */ + + +typedef struct blogCtTimeFlags { + uint32_t unused: 31; + uint32_t valid: 1; /* BlogCtTime has valid values */ +} BlogCtTimeFlags_t; + +/* used to pass timer info between the stack and blog layer */ +typedef struct blogCtTime { + BlogCtTimeFlags_t flags; /* Flags */ + uint8_t unknown; /* unknown proto */ + uint8_t proto; /* known proto TCP, UDP */ + uint8_t intv; /* intv in sec */ + uint8_t idle; /* idle time in sec */ +} BlogCtTime_t; + +/* used to exchange info between fcache and drivers */ +typedef struct { + uint32_t h_proto; /* protocol */ + uint32_t key_match; /* key */ + void *txdev_p; + uint8_t tx_l3_offset; + uint8_t tx_l4_offset; +} BlogFcArgs_t; + +typedef struct { + uint32_t rx_packets; + int32_t rx_rtp_packets_lost; /*TODO chekc why this is defined as signed int */ + aligned_u64 rx_bytes; + uint32_t pollTS_ms; // Poll timestamp in ms +}BlogFcStats_t; + +typedef struct { + u64 packet_count; + u64 byte_count; +} blog_fast_stats_t; + +/* + * Linux Netfilter Conntrack registers it's conntrack refresh function which + * will be invoked to refresh a conntrack when packets belonging to a flow + * managed by Linux conntrack are bypassed by a Blog client. + */ +typedef void (*blog_cttime_upd_t)(void * ct_p, BlogCtTime_t *ct_time_p); +extern blog_cttime_upd_t blog_cttime_update_fn; + +typedef void (*blog_ct_put_stats_t)(void * net_p, uint32_t dir, const BlogFcStats_t * stats); +extern blog_ct_put_stats_t blog_ct_put_stats_fn; + +extern int blog_ct_get_stats(const void *ct, uint32_t blog_key, uint32_t dir, + BlogFcStats_t *stats); +extern int blog_ct_push_stats(void); + +#if defined(CONFIG_NET_IPGRE) || defined(CONFIG_NET_IPGRE_MODULE) +typedef int (*blog_gre_rcv_check_t)(void *dev, BlogIpv4Hdr_t *iph, uint16_t len, + void **tunl_pp, uint32_t *pkt_seqno_p); +extern blog_gre_rcv_check_t blog_gre_rcv_check_fn; + +typedef int (*blog_gre_xmit_upd_t)(void * tunl_p, BlogIpv4Hdr_t *iph, uint16_t len); +extern blog_gre_xmit_upd_t blog_gre_xmit_update_fn; +typedef int (*blog_gre6_rcv_check_t)(void *dev, BlogIpv6Hdr_t *ipv6h, uint16_t len, + void **tunl_pp, uint32_t *pkt_seqno_p); +extern blog_gre6_rcv_check_t blog_gre6_rcv_check_fn; + +typedef int (*blog_gre6_xmit_upd_t)(void * tunl_p, BlogIpv6Hdr_t *ipv6h, uint16_t len); +extern blog_gre6_xmit_upd_t blog_gre6_xmit_update_fn; +#endif + + +#define PPTP_NOT_ACK 0 +#define PPTP_WITH_ACK 1 +#define PPTP_GRE_VER_0 0 +#define PPTP_GRE_VER_1 1 +#define PPTP_GRE_NONE 2 + +typedef int (*blog_pptp_xmit_upd_t)(uint16_t call_id, uint32_t seqNum, + uint32_t ackNum, uint32_t daddr); +extern blog_pptp_xmit_upd_t blog_pptp_xmit_update_fn; + +typedef int (*blog_pptp_xmit_get_t)(uint16_t call_id, uint32_t* seqNum, + uint32_t* ackNum, uint32_t daddr); +extern blog_pptp_xmit_get_t blog_pptp_xmit_get_fn; + +typedef int (*blog_pptp_rcv_check_t)(uint16_t call_id, uint32_t *rcv_pktSeq, + uint32_t rcv_pktAck, uint32_t saddr); +extern blog_pptp_rcv_check_t blog_pptp_rcv_check_fn; + +typedef int (*blog_l2tp_rcv_check_t)(void *dev, uint16_t tunnel_id, + uint16_t session_id); +extern blog_l2tp_rcv_check_t blog_l2tp_rcv_check_fn; + +#if defined(CONFIG_BCM_OVS) +typedef int (* blog_is_ovs_internal_dev_t)(void *dev); +typedef unsigned long (* blog_mega_get_key_t)(void *mega); +typedef void (* blog_mega_set_key_t)(void *mega, unsigned long blog_key); +typedef void (* blog_mega_put_fast_stats_t)(void *net_p, + const blog_fast_stats_t *stats); + +typedef struct { + blog_is_ovs_internal_dev_t is_ovs_internal_dev; + blog_mega_get_key_t mega_get_key; + blog_mega_set_key_t mega_set_key; + blog_mega_put_fast_stats_t mega_put_fast_stats; +} blog_ovs_hooks_t; +void blog_bind_ovs(blog_ovs_hooks_t *blog_ovs_hooks_p); +#endif + +/* __end_include_if_linux__ */ + + + +/* + *------------------------------------------------------------------------------ + * Denotes a Blog client, + *------------------------------------------------------------------------------ + */ +typedef enum { + BLOG_DECL(BlogClient_fcache) +#if defined(CONFIG_BCM_KF_FAP) + BLOG_DECL(BlogClient_fap) +#endif +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) + BLOG_DECL(BlogClient_runner) +#endif /* CONFIG_BCM_RUNNER */ +#endif /* CONFIG_BCM_KF_RUNNER */ + BLOG_DECL(BlogClient_MAX) +} BlogClient_t; + +/* + *------------------------------------------------------------------------------ + * Denotes whether a packet is consumed and freed by a Blog client application, + * whether a packet needs to be processed normally within the network stack or + * whether a packet context is extended with a Blog_t object. + *------------------------------------------------------------------------------ + */ +typedef enum { + BLOG_DECL(PKT_DONE) /* Packet consumed and freed */ + BLOG_DECL(PKT_NORM) /* Continue normal stack processing */ + BLOG_DECL(PKT_BLOG) /* Continue stack with blogging */ + BLOG_DECL(PKT_DROP) /* Drop Packet */ + BLOG_DECL(PKT_TCP4_LOCAL) /* ipv4 tcp packet terminating locally*/ + BLOG_DECL(BLOG_ACTION_MAX) +} BlogAction_t; + +/* + *------------------------------------------------------------------------------ + * Denotes the direction in the network stack when a packet is processed by a + * virtual network interface/network device. + *------------------------------------------------------------------------------ + */ +typedef enum { + BLOG_DECL(DIR_RX) /* Receive path in network stack */ + BLOG_DECL(DIR_TX) /* Transmit path in network stack */ + BLOG_DECL(BLOG_DIR_MAX) +} BlogDir_t; + +/* + *------------------------------------------------------------------------------ + * Denotes the type of Network entity associated with a Blog_t. + * + * BlogNetEntity_t may be linked to a blog using blog_link to make the Blog_t + * point to the BlogNetEntity_t. A reverse linking from the BlogNetEntity_t to + * Blog_t is only possible via a key (if necessary when a one to one association + * between the BlogNetEntity_t and a Blog exists. For instance, there is a + * one to one association between a Flow Connection Tracker and a Blog. In fact + * a Linux Netfilter Connection Tracking object manages a bi-directional flow + * and thus may have 2 keys to reference the corresponding Blog_t. However, a + * network device (physical end device or a virtual device) may have multiple + * Flows passing through it and hence no one-to-one association exists. In this + * can a Blog may have a link to a network device, but the reverse link (via a + * key) is not saved in the network device. + * + * Linking a BlogNetEntity_t to a blog is done via blog_link() whereas saving + * a reference key into a BlogNetEntity_t is done via blog_request() by the + * Blog client application, if needed. + * + *------------------------------------------------------------------------------ + */ + +#define BLOG_CT_PLD 0U +#define BLOG_CT_DEL 1U +#define BLOG_CT_MAX 2U + +/* FLOWTRACK: param1 is ORIG=0 or REPLY=1 direction */ +#define BLOG_PARAM1_DIR_ORIG 0U +#define BLOG_PARAM1_DIR_REPLY 1U +#define BLOG_PARAM1_DIR_MAX 2U + +/* FLOWTRACK: param2 is IPv4=0, IPv6=1, GRE=2, L2TP=3 */ +#define BLOG_PARAM2_IPV4 0U +#define BLOG_PARAM2_IPV6 1U +#define BLOG_PARAM2_GRE_IPV4 2U +#define BLOG_PARAM2_L2TP_IPV4 3U +#define BLOG_PARAM2_MAX 4U +#define BLOG_CT_VER_MAX 2U + +/* MAP_TUPLE: param1 is US=0 or DS=1 direction */ +#define BLOG_PARAM1_MAP_DIR_US BLOG_PARAM1_DIR_ORIG +#define BLOG_PARAM1_MAP_DIR_DS BLOG_PARAM1_DIR_REPLY + +/* BRIDGEFDB: param1 is src|dst */ +#define BLOG_PARAM1_SRCFDB 0U +#define BLOG_PARAM1_DSTFDB 1U + +/* IF_DEVICE: param1 is direction RX or TX, param 2 is minMtu */ + +typedef enum { + BLOG_DECL(FLOWTRACK) /* Flow (connection|session) tracker */ + BLOG_DECL(BRIDGEFDB) /* Bridge Forwarding Database entity */ + BLOG_DECL(MCAST_FDB) /* Multicast Client FDB entity */ + BLOG_DECL(IF_DEVICE) /* Virtual Interface (network device) */ + BLOG_DECL(IF_DEVICE_MCAST) /* Virtual Interface (network device) */ + BLOG_DECL(GRE_TUNL) /* GRE Tunnel */ + BLOG_DECL(TOS_MODE) /* TOS_MODE */ + BLOG_DECL(MAP_TUPLE) /* Flow (MAP-T connection) tracker */ + BLOG_DECL(MEGA) /* Megaflow tracker */ + BLOG_DECL(BLOG_NET_ENTITY_MAX) +} BlogNetEntity_t; + +/* + *------------------------------------------------------------------------------ + * Denotes a type of notification sent from the network stack to the Blog client + * See blog_notify(BlogNotify_t, void *, unsigned long param1, uint32_t param2); + *------------------------------------------------------------------------------ + */ + +/* MCAST_CONTROL_EVT: param1 is add|del, and param2 is IPv4|IPv6 */ +#define BLOG_PARAM1_MCAST_ADD 0U +#define BLOG_PARAM1_MCAST_DEL 1U +#define BLOG_PARAM2_MCAST_IPV4 0U +#define BLOG_PARAM2_MCAST_IPV6 1U + +/* LINK_STATE_CHANGE: param1 */ +#define BLOG_PARAM1_LINK_STATE_UP 0U +#define BLOG_PARAM1_LINK_STATE_DOWN 1U + +typedef enum { + BLOG_DECL(DESTROY_FLOWTRACK) /* Session/connection is deleted */ + BLOG_DECL(DESTROY_BRIDGEFDB) /* Bridge FDB has aged */ + BLOG_DECL(MCAST_CONTROL_EVT) /* Mcast client joins a group event */ + BLOG_DECL(MCAST_SYNC_EVT) /* Topology change for mcast event */ + BLOG_DECL(DESTROY_NETDEVICE) /* Network device going down */ + BLOG_DECL(FETCH_NETIF_STATS) /* Fetch accumulated stats */ + BLOG_DECL(CLEAR_NETIF_STATS) /* Clear accumulated stats */ + BLOG_DECL(DYNAMIC_DSCP_EVENT) /* Dynamic DSCP change event */ + BLOG_DECL(UPDATE_NETDEVICE) /* Netdevice has been modified (MTU, etc) */ + BLOG_DECL(ARP_BIND_CHG) /* ARP IP/MAC binding change event */ + BLOG_DECL(CONFIG_CHANGE) /* Certain configuration change event */ + BLOG_DECL(UP_NETDEVICE) /* network device up */ + BLOG_DECL(DN_NETDEVICE) /* network device down */ + BLOG_DECL(CHANGE_ADDR) /* network device change MAC addr */ + BLOG_DECL(SET_DPI_PARAM) /* Set the DPI parameters */ + BLOG_DECL(DESTROY_MAP_TUPLE) /* MAPT Session/connection is deleted */ + BLOG_DECL(FLUSH) /* Flush flows based on parameters */ + BLOG_DECL(DESTROY_MEGA) /* Megaflow connection is deleted */ + BLOG_DECL(FETCH_MEGA_STATS) /* Fetch megaflow fast stats */ + BLOG_DECL(CLEAR_MEGA_STATS) /* Clear megaflow fast stats */ + BLOG_DECL(BLOG_NOTIFY_MAX) +} BlogNotify_t; + +typedef enum { + BLOG_DECL(QUERY_FLOWTRACK) /* Session/connection time is queried */ + BLOG_DECL(QUERY_BRIDGEFDB) /* Bridge FDB time is queried */ + BLOG_DECL(QUERY_MAP_TUPLE) /* MAP-T connection time is queried */ + BLOG_DECL(QUERY_FLOWTRACK_STATS)/* get stats of flows associated with NPE */ + BLOG_DECL(QUERY_GET_HW_ACCEL) + BLOG_DECL(BLOG_QUERY_MAX) +} BlogQuery_t; + +typedef struct{ + int orig_queue; /* Originating queue index */ + int reply_queue; /* Reply queue index */ + int priority; /* Traffic priority */ +}BlogDpiParams_t; + + +/* Blog Notify FLUSH strucutre */ +typedef int (* BlogFlushMetadataFunc_t)(void *metadata_p, const Blog_t *const blog_p); + +typedef struct { + uint32_t flush_all :1; + uint32_t flush_flow :1; + uint32_t flush_dev :1; + uint32_t flush_dstmac :1; + uint32_t flush_srcmac :1; + uint32_t flush_meta :1; /* Not available through Userspace/CLI */ + uint32_t flush_hw :1; /* Flush all flows from HW */ + uint32_t flush_unused :25; + uint8_t mac[6]; + int devid; + int flowid; + void *metadata_p; /* flush_meta = 1 ; Must set metadata_p, devid & flush_dev */ + BlogFlushMetadataFunc_t func_p; /* flush_meta = 1 ; Must provide callback func */ +}BlogFlushParams_t; + +/* + *------------------------------------------------------------------------------ + * Denotes a type of request from a Blog client to a network stack entity. + *------------------------------------------------------------------------------ + */ + +typedef enum { + BLOG_DECL(FLOWTRACK_KEY_SET) /* Set Client key into Flowtracker */ + BLOG_DECL(FLOWTRACK_KEY_GET) /* Get Client key into Flowtracker */ + BLOG_DECL(FLOWTRACK_DSCP_GET) /* Get DSCP from Flow tracker:DYNDSCP */ + BLOG_DECL(FLOWTRACK_CONFIRMED) /* Test whether session is confirmed */ + BLOG_DECL(FLOWTRACK_ALG_HELPER) /* Test whether flow has an ALG */ + BLOG_DECL(FLOWTRACK_EXCLUDE) /* Clear flow candidacy by Client */ + BLOG_DECL(FLOWTRACK_TIME_SET) /* Set time in a flow tracker */ + BLOG_DECL(FLOWTRACK_PUT_STATS) /* Push accumulated stats to conntrack*/ + BLOG_DECL(NETIF_PUT_STATS) /* Push accumulated stats to devices */ + BLOG_DECL(LINK_XMIT_FN) /* Fetch device link transmit function*/ + BLOG_DECL(LINK_NOCARRIER) /* Fetch device link carrier */ + BLOG_DECL(NETDEV_NAME) /* Network device name */ + BLOG_DECL(MCAST_DFLT_MIPS) /* Delete action in blogRule chain */ + BLOG_DECL(IQPRIO_SKBMARK_SET) /* Set IQOS Prio in skb->mark */ + BLOG_DECL(DPIQ_SKBMARK_SET) /* Set DPIQ in skb->mark */ + BLOG_DECL(BRIDGEFDB_KEY_SET) /* Set Client key into bridge FDB */ + BLOG_DECL(BRIDGEFDB_KEY_GET) /* Get Client key into bridge FDB */ + BLOG_DECL(BRIDGEFDB_TIME_SET) /* Refresh bridge FDB time */ + BLOG_DECL(BRIDGEFDB_IFIDX_GET) /* Get bridge FDB's device ifindex */ + BLOG_DECL(SYS_TIME_GET) /* Get the system time in jiffies */ + BLOG_DECL(GRE_TUNL_XMIT) /* GRE Tunnel tx */ + BLOG_DECL(GRE6_TUNL_XMIT) /* GRE6 Tunnel tx */ + BLOG_DECL(SKB_DST_ENTRY_SET) /* get dst_entry from skb */ + BLOG_DECL(SKB_DST_ENTRY_RELEASE)/* release dst_entry from blog */ + BLOG_DECL(NETDEV_ADDR) /* Device MAC addr */ + BLOG_DECL(FLOW_EVENT_ACTIVATE) /* Flow Activation event */ + BLOG_DECL(FLOW_EVENT_DEACTIVATE)/* Flow Deactivation event */ + BLOG_DECL(CHK_HOST_DEV_MAC) /* Check Dev HostMAC for addition */ + BLOG_DECL(MAP_TUPLE_KEY_SET) /* Set Client key into MAPT Tuple */ + BLOG_DECL(MAP_TUPLE_KEY_GET) /* Get Client key into MAPT Tuple */ + BLOG_DECL(MEGA_KEY_SET) /* Set Client key into megaflow */ + BLOG_DECL(MEGA_KEY_GET) /* Get Client key into megaflow */ + BLOG_DECL(MEGA_PUT_STATS) /* Put the stats in a megaflow */ + BLOG_DECL(BLOG_REQUEST_MAX) +} BlogRequest_t; + +/* + *------------------------------------------------------------------------------ + * Denotes a type of update to an existing Blog flow. + *------------------------------------------------------------------------------ + */ + +typedef enum { + BLOG_DECL(BLOG_UPDATE_DPI_QUEUE) /* DPI Queue assignment has changed */ + BLOG_DECL(BLOG_UPDATE_DPI_PRIORITY) /* DPI Priority assignment has changed */ + BLOG_DECL(BLOG_UPDATE_MAX) +} BlogUpdate_t; + +/* + *------------------------------------------------------------------------------ + * Flow event parameters + *------------------------------------------------------------------------------ + */ + +typedef enum { + BLOG_DECL(FLOW_EVENT_TYPE_FC) /* FCache Flow */ + BLOG_DECL(FLOW_EVENT_TYPE_HW) /* Hardware Flow */ + BLOG_DECL(FLOW_EVENT_TYPE_MAX) +} BlogFlowEventType_t; + + +typedef struct { + void *ct_pld_p; + void *ct_del_p; + union { + struct { + uint32_t is_downstream :1; + uint32_t flow_event_type :2; + uint32_t reserved :21; + uint32_t skb_mark_flow_id :8; + }; + uint32_t u32; + }; +} BlogFlowEventInfo_t; + + +/* + *------------------------------------------------------------------------------ + * Clean this up. + *------------------------------------------------------------------------------ + */ + +#define BLOG_ENCAP_MAX 6 /* Maximum number of L2 encaps */ +#define BLOG_HDRSZ_MAX 32 /* Maximum size of L2 encaps */ + +typedef enum { + BLOG_DECL(GRE_ETH) /* e.g. BLOG_XTMPHY, BLOG_GPONPHY */ + BLOG_DECL(BCM_XPHY) /* e.g. BLOG_XTMPHY, BLOG_GPONPHY */ + BLOG_DECL(BCM_SWC) /* BRCM LAN Switch Tag/Header */ + BLOG_DECL(ETH_802x) /* Ethernet */ + BLOG_DECL(VLAN_8021Q) /* Vlan 8021Q (incld stacked) */ + BLOG_DECL(PPPoE_2516) /* PPPoE RFC 2516 */ + BLOG_DECL(PPP_1661) /* PPP RFC 1661 */ + BLOG_DECL(PLD_IPv4) /* Payload IPv4 */ + BLOG_DECL(PLD_IPv6) /* Payload IPv6 */ + BLOG_DECL(PPTP) /* PPTP Header */ + BLOG_DECL(L2TP) /* L2TP Header */ + BLOG_DECL(GRE) /* GRE Header */ + BLOG_DECL(ESP) /* ESP Header */ + BLOG_DECL(DEL_IPv4) /* Outer IPv4 */ + BLOG_DECL(DEL_IPv6) /* Outer IPv6 */ + BLOG_DECL(DEL_L2) /* L2 DEL */ + BLOG_DECL(PLD_L2) /* L2 PLD */ + BLOG_DECL(HDR0_IPv4) /* IPv4 Inner Header 0 */ + BLOG_DECL(HDR0_IPv6) /* IPv6 Inner Header 0 */ + BLOG_DECL(HDR0_L2) /* L2 Inner Header 0 */ + BLOG_DECL(GREoESP_type) /* GRE over ESP type */ + BLOG_DECL(GREoESP_type_resvd) /* GRE over ESP type */ + BLOG_DECL(GREoESP) /* GRE over ESP */ + BLOG_DECL(unused1) /* unused1 */ + BLOG_DECL(PASS_THRU) /* pass-through */ + BLOG_DECL(unused) /* unused */ + BLOG_DECL(PROTO_MAX) +} BlogEncap_t; + + + + + + +/* CAUTION: Following macros have binary dependencies. Please do not change these + macros without consulting with Broadcom or the subsystem owners + Macro definition START */ +#define BLOG_IS_HWACC_DISABLED_WLAN_EXTRAPHY(rxphy,txphy) ((rxphy == BLOG_EXTRA1PHY) || \ + (txphy == BLOG_EXTRA1PHY)) +#define BLOG_IS_TX_HWACC_ENABLED_WLAN_PHY(txphy) (txphy == BLOG_WLANPHY) +/* Macro definition END */ + +/* + *------------------------------------------------------------------------------ + * Logging of a maximum 4 "virtual" network devices that a flow can traverse. + * Virtual devices are interfaces that do not perform the actual DMA transfer. + * E.g. an ATM interface would be referred to as a physical interface whereas + * a ppp interface would be referred to as a Virtual interface. + *------------------------------------------------------------------------------ + */ +#define MAX_VIRT_DEV 7 + +#define DEV_DIR_MASK 0x3ul +#define DEV_PTR_MASK (~DEV_DIR_MASK) +#define DEV_DIR(ptr) ((uintptr_t)(ptr) & DEV_DIR_MASK) + +#define IS_RX_DIR(ptr) ( DEV_DIR(ptr) == DIR_RX ) +#define IS_TX_DIR(ptr) ( DEV_DIR(ptr) == DIR_TX ) + +/* + *------------------------------------------------------------------------------ + * Device pointer conversion between with and without embeded direction info + *------------------------------------------------------------------------------ + */ +#define DEVP_APPEND_DIR(ptr,dir) ((void *)((uintptr_t)(ptr) | (uintptr_t)(dir))) +#define DEVP_DETACH_DIR(ptr) ((void *)((uintptr_t)(ptr) & (uintptr_t) \ + DEV_PTR_MASK)) +/* + *------------------------------------------------------------------------------ + * Denotes the tos mode. + *------------------------------------------------------------------------------ + */ +typedef enum { + BLOG_DECL(BLOG_TOS_FIXED) + BLOG_DECL(BLOG_TOS_INHERIT) + BLOG_DECL(BLOG_TOS_MAX) +} BlogTos_t; + +/* + *------------------------------------------------------------------------------ + * Blog statistics structure + *------------------------------------------------------------------------------ + */ +typedef struct{ + /* NOTE : All these structure variables should be of same type/size */ + + uint64_t rx_packets; /* total blog packets received */ + uint64_t tx_packets; /* total blog packets transmitted */ + uint64_t rx_bytes; /* total blog bytes received */ + uint64_t tx_bytes; /* total blog bytes transmitted */ + uint64_t multicast; /* total blog multicast packets */ + uint64_t tx_multicast_packets; /* multicast packets transmitted */ + uint64_t rx_multicast_bytes; /* multicast bytes recieved */ + uint64_t tx_multicast_bytes; /* multicast bytes transmitted */ +} BlogStats_t; + +typedef enum { + BLOG_DECL(blog_skip_reason_unknown = 0) /* unknown or customer defined */ + BLOG_DECL(blog_skip_reason_br_flood) + BLOG_DECL(blog_skip_reason_ct_tcp_state_not_est) + BLOG_DECL(blog_skip_reason_ct_tcp_state_ignore) + BLOG_DECL(blog_skip_reason_ct_status_donot_blog) + BLOG_DECL(blog_skip_reason_nf_xt_skiplog) + BLOG_DECL(blog_skip_reason_nf_ebt_skiplog) + BLOG_DECL(blog_skip_reason_scrub_pkt) + BLOG_DECL(blog_skip_reason_sch_htb) + BLOG_DECL(blog_skip_reason_sch_dsmark) + BLOG_DECL(blog_skip_reason_unknown_proto) + BLOG_DECL(blog_skip_reason_unknown_proto_ah4) + BLOG_DECL(blog_skip_reason_unknown_proto_ah6) + BLOG_DECL(blog_skip_reason_unknown_proto_esp6) + BLOG_DECL(blog_skip_reason_esp4_crypto_algo) + BLOG_DECL(blog_skip_reason_esp4_spu_disabled) + BLOG_DECL(blog_skip_reason_spudd_check_failure) + BLOG_DECL(blog_skip_reason_dpi) + BLOG_DECL(blog_skip_reason_bond) + BLOG_DECL(blog_skip_reason_map_tcp) + BLOG_DECL(blog_skip_reason_blog) + BLOG_DECL(blog_skip_reason_l2_local_termination) + BLOG_DECL(blog_skip_reason_mega_multi_output_ports) + BLOG_DECL(blog_skip_reason_mega_attr_mismatch) + BLOG_DECL(blog_skip_reason_mega_field_mismatch) + BLOG_DECL(blog_skip_reason_max) +} blog_skip_reason_t; + +typedef enum { + BLOG_DECL(blog_free_reason_unknown = 0) /* unknown or customer defined */ + BLOG_DECL(blog_free_reason_blog_emit) + BLOG_DECL(blog_free_reason_blog_iq_prio) + BLOG_DECL(blog_free_reason_kfree) + BLOG_DECL(blog_free_reason_ipmr_local) + BLOG_DECL(blog_free_reason_max) +} blog_free_reason_t; + +typedef struct { + uint32_t blog_get; + uint32_t blog_put; + uint32_t blog_skip; + uint32_t blog_free; + uint32_t blog_xfer; + uint32_t blog_clone; + uint32_t blog_copy; + uint32_t blog_min_avail; +} blog_info_stats_t; + +#define BLOG_DUMP_DISABLE 0 +#define BLOG_DUMP_RXBLOG 1 +#define BLOG_DUMP_TXBLOG 2 +#define BLOG_DUMP_RXTXBLOG 3 + +typedef struct blog_ctx { + uint32_t blog_total; + uint32_t blog_avail; + uint32_t blog_mem_fails; + uint32_t blog_extends; + uint32_t blog_extend_fails; + blog_info_stats_t info_stats; + blog_skip_reason_t blog_skip_stats_table[blog_skip_reason_max]; + blog_free_reason_t blog_free_stats_table[blog_free_reason_max]; + uint32_t blog_dump; +} blog_ctx_t; + + +/* + * ----------------------------------------------------------------------------- + * Support accleration of L2, L3 packets. + * + * When acceleration support is enabled system wide, the default to be used may + * be set in CC_BLOG_SUPPORT_ACCEL_MODE which gets saved in blog_support_accel_mode_g. + * One may change the default (at runtime) by invoking blog_support_accel_mode(). + * ----------------------------------------------------------------------------- + */ + + +/* + * ----------------------------------------------------------------------------- + * Acceleration support: + * All the platforms support L3 tuple based acceleatiion. + * But FAP and PON platforns do NOT supports the fcache L2 tuple based acceleation. + * When the acceleation mode is configured as L23, the accelerators decides + * on per packet basis whether to use L2 or L3 tuple based acceleration. + * ----------------------------------------------------------------------------- + */ +#define BLOG_ACCEL_MODE_L3 0 /* Legacy. All platforms support*/ +#define BLOG_ACCEL_MODE_L23 1 /* Not supported on FAP and PON */ + +#define CC_BLOG_SUPPORT_ACCEL_MODE (BLOG_ACCEL_MODE_L3) + +extern int blog_support_accel_mode_g; + +typedef int (*blog_accel_mode_set_t)(uint32_t accel_mode); +extern blog_accel_mode_set_t blog_accel_mode_set_fn; + +extern void blog_support_accel_mode(int accel_mode); +extern int blog_support_get_accel_mode(void); + +extern int blog_support_tcp_ack_mflows_g; + +typedef int (*blog_tcp_ack_mflows_set_t)(int enable); +extern blog_tcp_ack_mflows_set_t blog_tcp_ack_mflows_set_fn; + +extern void blog_support_set_tcp_ack_mflows(int enable); +extern int blog_support_get_tcp_ack_mflows(void); + +/* + * ----------------------------------------------------------------------------- + * Support blogging of multicast packets. + * + * When Multicast support is enabled system wide, the default to be used may + * be set in CC_BLOG_SUPPORT_MCAST which gets saved in blog_support_mcast_g. + * One may change the default (at runtime) by invoking blog_support_mcast(). + * ----------------------------------------------------------------------------- + */ + +/* Multicast Support for IPv4 and IPv6 Control */ +#define BLOG_MCAST_DISABLE 0 +#define BLOG_MCAST_IPV4 1 +#define BLOG_MCAST_IPV6 2 + +#ifdef CONFIG_BLOG_MCAST +#define CC_BLOG_SUPPORT_MCAST BLOG_MCAST_IPV4 + BLOG_MCAST_IPV6 +#else +#define CC_BLOG_SUPPORT_MCAST BLOG_MCAST_DISABLE +#endif + +extern int blog_support_mcast_g; +extern void blog_support_mcast(int enable); + +/* + * ----------------------------------------------------------------------------- + * Support learning of multicast packets. + * + * When Multicast learn support is enabled system wide, the default to be used + * may be set in CC_BLOG_SUPPORT_MCAST_LEARN which gets saved in + * blog_support_mcast_learn_g. One may change the default (at runtime) by + * invoking blog_support_mcast_learn(). + * ----------------------------------------------------------------------------- + */ + +/* Multicast Learning Support Enable/Disable Control */ +#define BLOG_MCAST_LEARN_DISABLE 0 +#define BLOG_MCAST_LEARN_ENABLE 1 +#define BLOG_MCAST_LEARN_ENABLE_1ST_CLIENT 2 + +/* If BRCM MCAST OVS Support is enabled, enable multicast learning + by default to allow learning OVS slowpath actions */ +#if defined(CONFIG_BCM_OVS_MCAST) +#define CONFIG_BLOG_MCAST_LEARN 1 +#endif + +#ifdef CONFIG_BLOG_MCAST_LEARN +#define CC_BLOG_SUPPORT_MCAST_LEARN BLOG_MCAST_LEARN_ENABLE +#else +#define CC_BLOG_SUPPORT_MCAST_LEARN BLOG_MCAST_LEARN_DISABLE +#endif + +extern int blog_support_mcast_learn_g; +extern void blog_support_mcast_learn(int enable); + +/* + * ----------------------------------------------------------------------------- + * Support blogging of IPv6 traffic + * + * When IPv6 support is enabled system wide, the default to be used may + * be set in CC_BLOG_SUPPORT_IPV6 which gets saved in blog_support_ipv6_g. + * One may change the default (at runtime) by invoking blog_support_ipv6(). + * ----------------------------------------------------------------------------- + */ + +/* IPv6 Support Control: see blog_support_ipv6_g and blog_support_ipv6() */ +#define BLOG_IPV6_DISABLE 0 +#define BLOG_IPV6_ENABLE 1 + +#ifdef CONFIG_BLOG_IPV6 +#define CC_BLOG_SUPPORT_IPV6 BLOG_IPV6_ENABLE +#else +#define CC_BLOG_SUPPORT_IPV6 BLOG_IPV6_DISABLE +#endif + +extern int blog_support_ipv6_g; +extern void blog_support_ipv6(int enable); + +/* + * ----------------------------------------------------------------------------- + * Support blogging of 6rd tos + * + * When 6rd is configured, the default to be used may be set in + * CC_BLOG_DEFAULT_TUNL_TOS which gets saved in blog_tunl_tos_g. + * One may change the default (at runtime) by invoking blog_tunl_tos(). + * ----------------------------------------------------------------------------- + */ + +/* GRE Support: enable/disable */ +#define BLOG_GRE_DISABLE 0 +#define BLOG_GRE_ENABLE 1 + +#ifdef CONFIG_BLOG_GRE +#define CC_BLOG_SUPPORT_GRE BLOG_GRE_ENABLE +#else +#define CC_BLOG_SUPPORT_GRE BLOG_GRE_DISABLE +#endif + +extern int blog_gre_tunnel_accelerated_g; +extern int blog_support_gre_g; +extern void blog_support_gre(int enable); + +/* L2TP Support */ +#define BLOG_L2TP_DISABLE 0 +#define BLOG_L2TP_TUNNEL 1 +#define BLOG_L2TP_TUNNEL_WITHCHKSUM 2 + +#ifdef CONFIG_BLOG_L2TP +#define CC_BLOG_SUPPORT_L2TP BLOG_L2TP_TUNNEL +#else +#define CC_BLOG_SUPPORT_L2TP BLOG_L2TP_DISABLE +#endif + +extern int blog_l2tp_tunnel_accelerated_g; +extern int blog_support_l2tp_g; +extern void blog_support_l2tp(int enable); + +/* ESP Support: tunnel and pass-thru modes */ +#define BLOG_ESP_DISABLE 0 +#define BLOG_ESP_TUNNEL 1 +#define BLOG_ESP_PASS_THRU 2 + +#ifdef CONFIG_BLOG_ESP +#define CC_BLOG_SUPPORT_ESP BLOG_ESP_TUNNEL +#else +#define CC_BLOG_SUPPORT_ESP BLOG_ESP_DISABLE +#endif + +/* + * ----------------------------------------------------------------------------- + * Support 4o6 fragmentation enable/disable + * ----------------------------------------------------------------------------- + */ +#define BLOG_4O6_FRAG_DISABLE 0 +#define BLOG_4O6_FRAG_ENABLE 1 + +extern int blog_support_4o6_frag_g; +extern void blog_support_4o6_frag(int enable); + +/* blog notify processing mode */ +typedef enum { + BLOG_DECL(BLOG_NOTIFY_PROC_MODE_NOW) /* processing mode: now/sync */ + BLOG_DECL(BLOG_NOTIFY_PROC_MODE_HYBRID)/* mode: now+deferred */ + BLOG_DECL(BLOG_NOTIFY_PROC_MODE_DFRD)/* processing mode: deferred */ + BLOG_DECL(BLOG_NOTIFY_PROC_MODE_MAX) +} blog_notify_proc_mode_t; + +extern int blog_notify_proc_mode_g; +extern void blog_set_notify_proc_mode(int mode); + +typedef enum { + BLOG_DECL(BLOG_NOTIFY_API_SYNC) /* blog_notify() */ + BLOG_DECL(BLOG_NOTIFY_API_ASYNC)/* blog_notify_async() */ + BLOG_DECL(BLOG_NOTIFY_API_MAX) +} blog_notify_api_t; + +typedef enum +{ + BLOG_DECL(BLOG_NOTIFY_EVT_NONE) + BLOG_DECL(BLOG_NOTIFY_EVT_FLUSH_FDB) + BLOG_DECL(BLOG_NOTIFY_EVT_FLUSH_NPE) + BLOG_DECL(BLOG_NOTIFY_EVT_FLUSH_ARP) + BLOG_DECL(BLOG_NOTIFY_EVT_FLUSH) + BLOG_DECL(BLOG_NOTIFY_EVT_FLUSH_HW) + BLOG_DECL(BLOG_NOTIFY_EVT_FLUSH_DEV) + BLOG_DECL(BLOG_NOTIFY_EVT_FLUSH_PARAMS) + BLOG_DECL(BLOG_NOTIFY_EVT_FETCH_NETIF_STATS) + BLOG_DECL(BLOG_NOTIFY_EVT_CLEAR_NETIF_STATS) + BLOG_DECL(BLOG_NOTIFY_EVT_MAX) +} blog_notify_evt_type_t; + +/* Traffic type */ +typedef enum { + BLOG_DECL(BlogTraffic_IPV4_UCAST) + BLOG_DECL(BlogTraffic_IPV6_UCAST) + BLOG_DECL(BlogTraffic_IPV4_MCAST) + BLOG_DECL(BlogTraffic_IPV6_MCAST) + BLOG_DECL(BlogTraffic_Layer2_Flow) + BLOG_DECL(BlogTraffic_MAX) +} BlogTraffic_t; + +typedef union { + uint32_t word; + struct { + BE_DECL( + uint32_t incarn : 3; /* Allocation instance identification */ + uint32_t self : 29; /* Index into static allocation table */ + ) + LE_DECL( + uint32_t self : 29; /* Index into static allocation table */ + uint32_t incarn : 3; /* Allocation instance identification */ + ) + }; +} BlogKeyFc_t; + +typedef union { + uint32_t word; + struct { + BE_DECL( + uint32_t resvd : 16; + uint32_t intf : 8; + uint32_t client : 8; + ) + LE_DECL( + uint32_t client : 8; + uint32_t intf : 8; + uint32_t resvd : 16; + ) + }; +} BlogKeyMc_t; + +#define BLOG_FDB_KEY_INVALID BLOG_KEY_NONE +#define BLOG_KEY_FC_INVALID BLOG_KEY_NONE +#define BLOG_KEY_FC_TUNNEL_IPV4 0xFFFFFFFE +#define BLOG_KEY_FC_TUNNEL_IPV6 0xFFFFFFFF + +#define BLOG_KEY_INVALID BLOG_KEY_NONE +#define BLOG_KEY_MCAST_INVALID BLOG_KEY_INVALID + +typedef struct { + BE_DECL( + BlogKeyFc_t fc; + BlogKeyMc_t mc; + ) + LE_DECL( + BlogKeyMc_t mc; + BlogKeyFc_t fc; + ) +} BlogActivateKey_t; + +typedef enum +{ + BLOG_DECL(BLOG_L2_KEYMAP_MACSA) + BLOG_DECL(BLOG_L2_KEYMAP_MACDA) + BLOG_DECL(BLOG_L2_KEYMAP_ETHTYPE) + BLOG_DECL(BLOG_L2_KEYMAP_VLAN0) + BLOG_DECL(BLOG_L2_KEYMAP_VLAN1) + BLOG_DECL(BLOG_L2_KEYMAP_TOS) + BLOG_DECL(BLOG_L2_KEYMAP_TCPACK) +} blog_l2_keymap_field_t; + +typedef enum +{ + BLOG_DECL(BLOG_L3_KEYMAP_IPV4SA) + BLOG_DECL(BLOG_L3_KEYMAP_IPV4DA) + BLOG_DECL(BLOG_L3_KEYMAP_IPV6SA) + BLOG_DECL(BLOG_L3_KEYMAP_IPV6DA) + BLOG_DECL(BLOG_L3_KEYMAP_PROTO) + BLOG_DECL(BLOG_L3_KEYMAP_SPORT) + BLOG_DECL(BLOG_L3_KEYMAP_DPORT) + BLOG_DECL(BLOG_L3_KEYMAP_TOS) + BLOG_DECL(BLOG_L3_KEYMAP_TCPACK) +} blog_l3_keymap_field_t; + + +#define BLOG_SET_L2KEYMAP(b, v0, v1, t) \ + b->l2_keymap = (1<l3_keymap = (1<l3_keymap = (1<l3_keymap = (1<l3_keymap = (1<mega_p != (void *)NULL)) + +/* Is the map tuple valid ? */ +#define IS_BLOG_MAP(b) ( MAPT(b) && ((b)->map_p != (void *)NULL) ) + +/* Before the flow is learnt, use CT macros */ +#define IS_BLOG_CT_PLD(b) ( (b)->ct_p[BLOG_CT_PLD] != (void *)NULL ) +#define IS_BLOG_CT_DEL(b) ( (b)->ct_p[BLOG_CT_DEL] != (void *)NULL ) +#define IS_BLOG_CT(b) ( IS_BLOG_CT_PLD((b)) || IS_BLOG_CT_DEL((b)) ) +#define IS_BLOG_NOT_CT(b) ( !IS_BLOG_CT_PLD((b)) && !IS_BLOG_CT_DEL((b)) ) + +#define IS_BLOG_CT4_PLD(b) \ + ( IS_BLOG_CT_PLD((b)) && \ + ((b)->ct_ver[BLOG_CT_PLD] == BLOG_PARAM2_IPV4)) + +#define IS_BLOG_CT4_DEL(b) \ + ( IS_BLOG_CT_DEL((b)) && \ + ((b)->ct_ver[BLOG_CT_DEL] == BLOG_PARAM2_IPV4)) + +#define IS_BLOG_CT6_PLD(b) \ + ( IS_BLOG_CT_PLD((b)) && \ + ((b)->ct_ver[BLOG_CT_PLD] == BLOG_PARAM2_IPV6)) + +#define IS_BLOG_CT6_DEL(b) \ + ( IS_BLOG_CT_DEL((b)) && \ + ((b)->ct_ver[BLOG_CT_DEL] == BLOG_PARAM2_IPV6)) + +#define IS_BLOG_CT4(b) ( IS_BLOG_CT4_PLD(b) || IS_BLOG_CT4_DEL(b) ) +#define IS_BLOG_CT6(b) ( IS_BLOG_CT6_PLD(b) || IS_BLOG_CT6_DEL(b) ) + + +#define BLOG_NPE_PLD 0U +#define BLOG_NPE_DEL 1U +#define BLOG_NPE_MAP 2U +#define BLOG_NPE_MEGA 3U +#define BLOG_NPE_MAX 4U + +#define BLOG_FDB_NPE_SRCFDB 0U +#define BLOG_FDB_NPE_DSTFDB 1U +#define BLOG_FDB_NPE_MAX 2U + +/* Is the map tuple valid ? */ +#define IS_BLOG_NPE_MAP(b) ( MAPT(b) && ((b)->npe_p[BLOG_NPE_MAP] != (void *)NULL) ) +#define IS_BLOG_NPE_MEGA(b) ( ((b)->npe_p[BLOG_NPE_MEGA] != (void *)NULL) ) + +/* After the flow is learnt, use NPE macros instead of CT macros */ +#define IS_BLOG_NPE_PLD(b) ( (b)->npe_p[BLOG_NPE_PLD] != (void *)NULL ) +#define IS_BLOG_NPE_DEL(b) ( (b)->npe_p[BLOG_NPE_DEL] != (void *)NULL ) +#define IS_BLOG_NPE_CT(b) ( IS_BLOG_NPE_PLD((b)) || IS_BLOG_NPE_DEL((b)) ) +#define IS_BLOG_NOT_NPE_CT(b) ( !IS_BLOG_NPE_PLD((b)) && !IS_BLOG_NPE_DEL((b)) ) + +#define IS_BLOG_NPE4_PLD(b) \ + ( IS_BLOG_NPE_PLD((b)) && \ + ((b)->ct_ver[BLOG_NPE_PLD] == BLOG_PARAM2_IPV4)) + +#define IS_BLOG_NPE4_DEL(b) \ + ( IS_BLOG_NPE_DEL((b)) && \ + ((b)->ct_ver[BLOG_NPE_DEL] == BLOG_PARAM2_IPV4)) + +#define IS_BLOG_NPE6_PLD(b) \ + ( IS_BLOG_NPE_PLD((b)) && \ + ((b)->ct_ver[BLOG_NPE_PLD] == BLOG_PARAM2_IPV6)) + +#define IS_BLOG_NPE6_DEL(b) \ + ( IS_BLOG_NPE_DEL((b)) && \ + ((b)->ct_ver[BLOG_NPE_DEL] == BLOG_PARAM2_IPV6)) + +#define IS_BLOG_NPE4(b) ( IS_BLOG_NPE4_PLD(b) || IS_BLOG_NPE4_DEL(b) ) +#define IS_BLOG_NPE6(b) ( IS_BLOG_NPE6_PLD(b) || IS_BLOG_NPE6_DEL(b) ) + +#define BLOG_NPE_NULL ((blog_npe_t*)NULL) + +/* + *------------------------------------------------------------------------------ + * Flow Cache network proxiy entity (npe) Entry: + *------------------------------------------------------------------------------ + */ +struct blog_npe { + struct dll_t node; /* npe Entity list */ + BlogKeyFc_t key; /* linking Linux nwe and npe entity */ + struct blog_npe *chain_p; /* npe Entity Hash list node */ + uint32_t hashix; /* npe Entity hash index */ + + int type; /* npe Entity type */ + void *nwe_p; + Dll_t flow_list[BLOG_PARAM1_DIR_MAX]; /* flow lists */ + uint32_t flow_count[BLOG_PARAM1_DIR_MAX]; /* # of flows */ +} ____cacheline_aligned; +typedef struct blog_npe blog_npe_t; + + +/* + * ============================================================================= + * CAUTION: OS and network stack may be built without CONFIG_BLOG defined. + * ============================================================================= + */ + +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + +/* + *------------------------------------------------------------------------------ + * + * Section: Blog Conditional Compiles CC_BLOG_SUPPORT_... + * + * These conditional compiles are not controlled by a system wide build process. + * E.g. CONFIG_BLOG_MCAST is a system wide build configuration + * CC_BLOG_SUPPORT_MCAST is a blog defined build configuration + * + * Do not use any CONFIG_ or CC_BLOG_SUPPORT_ in Blog_t structure definitions. + * + *------------------------------------------------------------------------------ + */ + +/* LAB ONLY: Design development, uncomment to enable */ +/* #define CC_BLOG_SUPPORT_COLOR */ +/* #define CC_BLOG_SUPPORT_DEBUG */ + + + + +/* To enable user filtering, see blog_filter(), invoked in blog_finit() */ +/* #define CC_BLOG_SUPPORT_USER_FILTER */ + + + +/* + * ----------------------------------------------------------------------------- + * Section: Definition of a Blog_t + * ----------------------------------------------------------------------------- + */ + +#define BLOG_CHAN_INVALID 0xFF + +#define HDR_BMAP_IPV4 1 +#define HDR_BMAP_IPV6 2 +#define HDR_BMAP_L2 3 + +/* GREoESP flag indicates whether it is GRE over ESP, or ESP over GRE */ +#define BLOG_ESPoGRE 0 +#define BLOG_GREoESP 1 + +#define BLOG_GREoESP_44 0 +#define BLOG_GREoESP_64 1 +#define BLOG_GREoESP_46 2 +#define BLOG_GREoESP_66 3 + +#define BLOG_ESPoGRE_44 0 +#define BLOG_ESPoGRE_64 1 +#define BLOG_ESPoGRE_46 2 +#define BLOG_ESPoGRE_66 3 + +typedef struct { + union { + struct { + uint8_t ivsize; /* IPsec cipher Initialization Vector size */ + }; + uint8_t channel; /* e.g. port number, txchannel, ... */ + }; + + union { + struct { + uint8_t phyHdrLen : 4; + uint8_t phyHdrType : 4; + }; + uint8_t phyHdr; + }; + + uint16_t unused; + + union { + struct { + BE_DECL( + uint32_t unused : 6; + uint32_t MAPE : 1; + uint32_t PASS_THRU : 1; + + uint32_t unused1 : 1; + uint32_t GREoESP : 1; + uint32_t GREoESP_type: 2; + uint32_t HDR0_L2 : 1; + uint32_t HDR0_IPv6 : 1; + uint32_t HDR0_IPv4 : 1; + uint32_t PLD_L2 : 1; + + uint32_t DEL_L2 : 1; + uint32_t DEL_IPv6 : 1; + uint32_t DEL_IPv4 : 1; + uint32_t ESP : 1; + uint32_t GRE : 1; + uint32_t L2TP : 1; + uint32_t PPTP : 1; + uint32_t PLD_IPv6 : 1; + + uint32_t PLD_IPv4 : 1; + uint32_t PPP_1661 : 1; + uint32_t PPPoE_2516 : 1; + uint32_t VLAN_8021Q : 1; + uint32_t ETH_802x : 1; + uint32_t BCM_SWC : 1; + uint32_t BCM_XPHY : 1; /* e.g. BCM_XTM */ + uint32_t GRE_ETH : 1; /* Ethernet over GRE */ + ) + LE_DECL( + uint32_t GRE_ETH : 1; /* Ethernet over GRE */ + uint32_t BCM_XPHY : 1; /* e.g. BCM_XTM */ + uint32_t BCM_SWC : 1; + uint32_t ETH_802x : 1; + uint32_t VLAN_8021Q : 1; + uint32_t PPPoE_2516 : 1; + uint32_t PPP_1661 : 1; + uint32_t PLD_IPv4 : 1; + + uint32_t PLD_IPv6 : 1; + uint32_t PPTP : 1; + uint32_t L2TP : 1; + uint32_t GRE : 1; + uint32_t ESP : 1; + uint32_t DEL_IPv4 : 1; + uint32_t DEL_IPv6 : 1; + uint32_t DEL_L2 : 1; + + uint32_t PLD_L2 : 1; + uint32_t HDR0_IPv4 : 1; + uint32_t HDR0_IPv6 : 1; + uint32_t HDR0_L2 : 1; + uint32_t GREoESP_type: 2; + uint32_t GREoESP : 1; + uint32_t unused1 : 1; + + uint32_t PASS_THRU : 1; + uint32_t MAPE : 1; + uint32_t unused : 6; + ) + } bmap;/* as per order of BlogEncap_t enums declaration */ + uint32_t hdrs; + }; +} BlogInfo_t; + +/* + *------------------------------------------------------------------------------ + * Buffer to log IP Tuple. + * Packed: 1 16byte cacheline. + *------------------------------------------------------------------------------ + */ +struct blogTuple_t { + uint32_t saddr; /* IP header saddr */ + uint32_t daddr; /* IP header daddr */ + + union { + struct { + uint16_t source; /* L4 source port */ + uint16_t dest; /* L4 dest port */ + } port; + struct { + uint16_t unused; + uint16_t gre_callid; + }; + uint32_t ports; + uint32_t esp_spi; + }; + + uint8_t ttl; /* IP header ttl */ + uint8_t tos; /* IP header tos */ + uint16_t check; /* checksum: rx tuple=l3, tx tuple=l4 */ + +}; +typedef struct blogTuple_t BlogTuple_t; + +#define NEXTHDR_IPV4 IPPROTO_IPIP +#define GRE_MAX_HDR_LEN (sizeof(struct ipv6hdr) + sizeof(BLOG_HDRSZ_MAX) + BLOG_GRE_HDR_LEN) + +#define HDRS_IPinIP ((1<rx.info.hdrs & HDRS_IPinIP)==HDRS_IP4in6) +#define RX_IP6in4(b) (((b)->rx.info.hdrs & HDRS_IPinIP)==HDRS_IP6in4) +#define TX_IP4in6(b) (((b)->tx.info.hdrs & HDRS_IPinIP)==HDRS_IP4in6) +#define TX_IP6in4(b) (((b)->tx.info.hdrs & HDRS_IPinIP)==HDRS_IP6in4) + +#define RX_IPV4(b) ((b)->rx.info.bmap.PLD_IPv4) +#define TX_IPV4(b) ((b)->tx.info.bmap.PLD_IPv4) +#define RX_IPV6(b) ((b)->rx.info.bmap.PLD_IPv6) +#define TX_IPV6(b) ((b)->tx.info.bmap.PLD_IPv6) +#define RX_IPV4_DEL(b) ((b)->rx.info.bmap.DEL_IPv4) +#define TX_IPV4_DEL(b) ((b)->tx.info.bmap.DEL_IPv4) +#define RX_IPV6_DEL(b) ((b)->rx.info.bmap.DEL_IPv6) +#define TX_IPV6_DEL(b) ((b)->tx.info.bmap.DEL_IPv6) +#define PT(b) ((b)->tx.info.bmap.PASS_THRU) + +#define RX_GRE(b) ((b)->rx.info.bmap.GRE) +#define TX_GRE(b) ((b)->tx.info.bmap.GRE) +#define RX_ESP(b) ((b)->rx.info.bmap.ESP) +#define TX_ESP(b) ((b)->tx.info.bmap.ESP) +#define RX_GRE_ETH(b) ((b)->rx.info.bmap.GRE_ETH) +#define TX_GRE_ETH(b) ((b)->tx.info.bmap.GRE_ETH) + +#define RX_IPV4ONLY(b) (((b)->rx.info.hdrs & HDRS_IPinIP)==(1 << PLD_IPv4)) +#define TX_IPV4ONLY(b) (((b)->tx.info.hdrs & HDRS_IPinIP)==(1 << PLD_IPv4)) +#define RX_IPV6ONLY(b) (((b)->rx.info.hdrs & HDRS_IPinIP)==(1 << PLD_IPv6)) +#define TX_IPV6ONLY(b) (((b)->tx.info.hdrs & HDRS_IPinIP)==(1 << PLD_IPv6)) +#define RX_L2ONLY(b) (((b)->rx.info.hdrs & HDRS_IPinIP)==(1 << PLD_L2)) +#define TX_L2ONLY(b) (((b)->tx.info.hdrs & HDRS_IPinIP)==(1 << PLD_L2)) + +#define CHK_RX_GIPV4(b) (((b)->rx.info.hdrs & ((1 << DEL_IPv4) | (1 << PLD_IPv4))) && RX_GRE(b)) +#define CHK_RX_GIPV6(b) (((b)->rx.info.hdrs & ((1 << DEL_IPv6) | (1 << PLD_IPv6))) && RX_GRE(b)) + + +#define RX_IPV4_OUTER(b) (RX_IPV4ONLY(b) || RX_IPV4_DEL(b)) +#define TX_IPV4_OUTER(b) (TX_IPV4ONLY(b) || TX_IPV4_DEL(b)) +#define PT4(b) (RX_IPV4ONLY(b) && TX_IPV4ONLY(b) && PT(b)) + +#define RX_IPV6_OUTER(b) (RX_IPV6ONLY(b) || RX_IPV6_DEL(b)) +#define TX_IPV6_OUTER(b) (TX_IPV6ONLY(b) || TX_IPV6_DEL(b)) +#define PT6(b) (RX_IPV6ONLY(b) && TX_IPV6ONLY(b) && PT(b)) + +#define HDRS_IPV4 ((1 << PLD_IPv4) | (1 << DEL_IPv4)) +#define HDRS_IPV6 ((1 << PLD_IPv6) | (1 << DEL_IPv6)) + +#define MAPT_UP(b) (RX_IPV4ONLY(b) && TX_IPV6ONLY(b)) +#define MAPT_DN(b) (RX_IPV6ONLY(b) && TX_IPV4ONLY(b)) +#define MAPT(b) (MAPT_DN(b) || MAPT_UP(b)) + +#define T4in6UP(b) (RX_IPV4ONLY(b) && TX_IP4in6(b)) +#define T4in6DN(b) (RX_IP4in6(b) && TX_IPV4ONLY(b)) + +#define T6in4UP(b) (RX_IPV6ONLY(b) && TX_IP6in4(b)) +#define T6in4DN(b) (RX_IP6in4(b) && TX_IPV6ONLY(b)) + +#define CHK4in6(b) (T4in6UP(b) || T4in6DN(b)) +#define CHK6in4(b) (T6in4UP(b) || T6in4DN(b)) +#define CHK4to4(b) (RX_IPV4ONLY(b) && TX_IPV4ONLY(b)) +#define CHK6to6(b) (RX_IPV6ONLY(b) && TX_IPV6ONLY(b)) + +#define HDRS_GIP4in4 ((1<rx.info.hdrs & HDRS_IPinIP)== HDRS_GIP4) +#define TX_GIPV4ONLY(b) (((b)->tx.info.hdrs & HDRS_IPinIP)== HDRS_GIP4) +#define RX_GIPV6ONLY(b) (((b)->rx.info.hdrs & HDRS_IPinIP)== HDRS_GIP6) +#define TX_GIPV6ONLY(b) (((b)->tx.info.hdrs & HDRS_IPinIP)== HDRS_GIP6) +#define RX_GL2ONLY(b) (((b)->rx.info.hdrs & HDRS_IPinIP)== HDRS_GL2) +#define TX_GL2ONLY(b) (((b)->tx.info.hdrs & HDRS_IPinIP)== HDRS_GL2) + +#define RX_GIP4in4(b) (((b)->rx.info.hdrs & HDRS_IPinIP)==HDRS_GIP4in4) +#define TX_GIP4in4(b) (((b)->tx.info.hdrs & HDRS_IPinIP)==HDRS_GIP4in4) +#define RX_GIP6in4(b) (((b)->rx.info.hdrs & HDRS_IPinIP)==HDRS_GIP6in4) +#define TX_GIP6in4(b) (((b)->tx.info.hdrs & HDRS_IPinIP)==HDRS_GIP6in4) +#define RX_GIP2in4(b) (((b)->rx.info.hdrs & HDRS_IPinIP)==HDRS_GIP2in4) +#define TX_GIP2in4(b) (((b)->tx.info.hdrs & HDRS_IPinIP)==HDRS_GIP2in4) +#define RX_GIP46in4(b) (RX_GIP4in4(b) || RX_GIP6in4(b)) +#define TX_GIP46in4(b) (TX_GIP4in4(b) || TX_GIP6in4(b)) + +#define RX_GIP4in6(b) (((b)->rx.info.hdrs & HDRS_IPinIP)==HDRS_GIP4in6) +#define TX_GIP4in6(b) (((b)->tx.info.hdrs & HDRS_IPinIP)==HDRS_GIP4in6) +#define RX_GIP6in6(b) (((b)->rx.info.hdrs & HDRS_IPinIP)==HDRS_GIP6in6) +#define TX_GIP6in6(b) (((b)->tx.info.hdrs & HDRS_IPinIP)==HDRS_GIP6in6) +#define RX_GIP2in6(b) (((b)->rx.info.hdrs & HDRS_IPinIP)==HDRS_GIP2in6) +#define TX_GIP2in6(b) (((b)->tx.info.hdrs & HDRS_IPinIP)==HDRS_GIP2in6) +#define RX_GIP46in6(b) (RX_GIP4in6(b) || RX_GIP6in6(b)) +#define TX_GIP46in6(b) (TX_GIP4in6(b) || TX_GIP6in6(b)) + +#define TG4in4UP(b) (RX_IPV4ONLY(b) && TX_GIP4in4(b)) +#define TG4in4DN(b) (RX_GIP4in4(b) && TX_IPV4ONLY(b)) +#define TG6in4UP(b) (RX_IPV6ONLY(b) && TX_GIP6in4(b)) +#define TG6in4DN(b) (RX_GIP6in4(b) && TX_IPV6ONLY(b)) +#define TG2in4UP(b) (RX_L2ONLY(b) && TX_GIP2in4(b)) +#define TG2in4DN(b) (RX_GIP2in4(b) && TX_L2ONLY(b)) + +#define TGL3_4in4UP(b) (RX_IPV4ONLY(b) && !TX_GRE_ETH(b) && TX_GIP4in4(b)) +#define TGL3_4in4DN(b) (!RX_GRE_ETH(b) && RX_GIP4in4(b) && TX_IPV4ONLY(b)) +#define TGL2_4in4UP(b) (RX_IPV4ONLY(b) && TX_GRE_ETH(b) && TX_GIP4in4(b)) +#define TGL2_4in4DN(b) (RX_GRE_ETH(b) && RX_GIP4in4(b) && TX_IPV4ONLY(b)) +#define TGL2_2in4UP(b) (RX_L2ONLY(b) && TX_GRE_ETH(b) && TX_GIP2in4(b)) +#define TGL2_2in4DN(b) (RX_GRE_ETH(b) && RX_GIP2in4(b) && TX_L2ONLY(b)) + +#define TG4in6UP(b) (RX_IPV4ONLY(b) && TX_GIP4in6(b)) +#define TG4in6DN(b) (RX_GIP4in6(b) && TX_IPV4ONLY(b)) +#define TG6in6UP(b) (RX_IPV6ONLY(b) && TX_GIP6in6(b)) +#define TG6in6DN(b) (RX_GIP6in6(b) && TX_IPV6ONLY(b)) +#define TG2in6UP(b) (RX_L2ONLY(b) && TX_GIP2in6(b)) +#define TG2in6DN(b) (RX_GIP2in6(b) && TX_L2ONLY(b)) + +#define TG24in4UP(b) (TG4in4UP(b) || TG2in4UP(b)) +#define TG24in4DN(b) (TG4in4DN(b) || TG2in4DN(b)) + +#define TG24in6UP(b) (TG4in6UP(b) || TG2in6UP(b)) +#define TG24in6DN(b) (TG4in6DN(b) || TG2in6DN(b)) + +#define CHKG4in4(b) (TG4in4UP(b) || TG4in4DN(b)) +#define CHKG6in4(b) (TG6in4UP(b) || TG6in4DN(b)) +#define CHKG2in4(b) (TG2in4UP(b) || TG2in4DN(b)) +#define CHKG46in4UP(b) (TG4in4UP(b) || TG6in4UP(b)) +#define CHKG46in4DN(b) (TG4in4DN(b) || TG6in4DN(b)) +#define CHKG46in4(b) (CHKG4in4(b) || CHKG6in4(b)) +#define CHKG246in4UP(b) (TG4in4UP(b) || TG6in4UP(b) || TG2in4UP(b)) +#define CHKG246in4DN(b) (TG4in4DN(b) || TG6in4DN(b) || TG2in4DN(b)) +#define CHKG246in4(b) (CHKG4in4(b) || CHKG6in4(b) || CHKG2in4(b)) + +#define CHKG4in6(b) (TG4in6UP(b) || TG4in6DN(b)) +#define CHKG6in6(b) (TG6in6UP(b) || TG6in6DN(b)) +#define CHKG2in6(b) (TG2in6UP(b) || TG2in6DN(b)) +#define CHKG46in6UP(b) (TG4in6UP(b) || TG6in6UP(b)) +#define CHKG46in6DN(b) (TG4in6DN(b) || TG6in6DN(b)) +#define CHKG46in6(b) (CHKG4in6(b) || CHKG6in6(b)) +#define CHKG246in6UP(b) (TG4in6UP(b) || TG6in6UP(b) || TG2in6UP(b)) +#define CHKG246in6DN(b) (TG4in6DN(b) || TG6in6DN(b) || TG2in6DN(b)) +#define CHKG246in6(b) (CHKG4in6(b) || CHKG6in6(b) || CHKG2in6(b)) + +#define PTG4(b) (RX_GIPV4ONLY(b) && TX_GIPV4ONLY(b) && PT(b)) +#define PTG6(b) (RX_GIPV6ONLY(b) && TX_GIPV6ONLY(b) && PT(b)) +#define TOTG4(b) (!PT(b) && ((RX_GIP4in4(b) && TX_GIP4in4(b)) || \ + (RX_GIP6in4(b) && TX_GIP6in4(b)))) +#define TOTG6(b) (!PT(b) && ((RX_GIP4in6(b) && TX_GIP4in6(b)) || \ + (RX_GIP6in6(b) && TX_GIP6in6(b)))) + +#define L2ACCEL_PTG(b) (RX_GL2ONLY(b) && TX_GL2ONLY(b)) + +#define HDRS_EIP4in4 ((1<rx.info.hdrs & HDRS_IPinIP)== HDRS_EIP4) +#define TX_EIPV4ONLY(b) (((b)->tx.info.hdrs & HDRS_IPinIP)== HDRS_EIP4) + +#define RX_EIP4in4(b) (((b)->rx.info.hdrs & HDRS_IPinIP)==HDRS_EIP4in4) +#define TX_EIP4in4(b) (((b)->tx.info.hdrs & HDRS_IPinIP)==HDRS_EIP4in4) +#define RX_EIP6in4(b) (((b)->rx.info.hdrs & HDRS_IPinIP)==HDRS_EIP6in4) +#define TX_EIP6in4(b) (((b)->tx.info.hdrs & HDRS_IPinIP)==HDRS_EIP6in4) + +#define TE4in4UP(b) (RX_IPV4ONLY(b) && TX_EIP4in4(b)) +#define TE4in4DN(b) (RX_EIP4in4(b) && TX_IPV4ONLY(b)) +#define TE6in4UP(b) (RX_IPV6ONLY(b) && TX_EIP6in4(b)) +#define TE6in4DN(b) (RX_EIP6in4(b) && TX_IPV6ONLY(b)) + +#define CHKE4in4(b) (TE4in4UP(b) || TE4in4DN(b)) +#define CHKE6in4(b) (TE6in4UP(b) || TE6in4DN(b)) +#define CHKE46in4(b) (CHKE4in4(b) || CHKE6in4(b)) + +#define PTE4(b) (RX_EIPV4ONLY(b) && TX_EIPV4ONLY(b)) + +#define HDRS_404 (1<rx.info.hdrs & HDRS_IPinIP)==HDRS_404) +#define RX_464(b) (((b)->rx.info.hdrs & HDRS_IPinIP)==HDRS_464) +#define RX_606(b) (((b)->rx.info.hdrs & HDRS_IPinIP)==HDRS_606) +#define RX_646(b) (((b)->rx.info.hdrs & HDRS_IPinIP)==HDRS_646) + + +#define HDRS_444 (1<rx.info.hdrs & HDRS_IPinIP)==HDRS_4oG4oE4) +#define TX_4oG4oE4(b) (((b)->tx.info.hdrs & HDRS_IPinIP)==HDRS_4oG4oE4) +#define RX_6oG4oE4(b) (((b)->rx.info.hdrs & HDRS_IPinIP)==HDRS_6oG4oE4) +#define TX_6oG4oE4(b) (((b)->tx.info.hdrs & HDRS_IPinIP)==HDRS_6oG4oE4) +#define RX_2oG4oE4(b) (((b)->rx.info.hdrs & HDRS_IPinIP)==HDRS_2oG4oE4) +#define TX_2oG4oE4(b) (((b)->tx.info.hdrs & HDRS_IPinIP)==HDRS_2oG4oE4) +#define RX_GoEo2(b) (((b)->rx.info.hdrs & HDRS_IPinIP)==HDRS_GE2) +#define TX_GoEo2(b) (((b)->tx.info.hdrs & HDRS_IPinIP)==HDRS_GE2) + +#define T4oG4oE4UP(b) (RX_IPV4ONLY(b) && TX_4oG4oE4(b)) +#define T4oG4oE4DN(b) (RX_4oG4oE4(b) && TX_IPV4ONLY(b)) +#define T6oG4oE4UP(b) (RX_IPV6ONLY(b) && TX_6oG4oE4(b)) +#define T6oG4oE4DN(b) (RX_6oG4oE4(b) && TX_IPV6ONLY(b)) +#define T2oG4oE4UP(b) (RX_L2ONLY(b) && (TX_2oG4oE4(b) || TX_GoEo2(b))) +#define T2oG4oE4DN(b) ((RX_2oG4oE4(b) || RX_GoEo2(b)) && TX_L2ONLY(b)) + +#define CHK4oG4oE4UP(b) (T4oG4oE4UP(b)) +#define CHK4oG4oE4DN(b) (T4oG4oE4DN(b)) +#define CHK4oG4oE4(b) (CHK4oG4oE4UP(b) || CHK4oG4oE4DN(b)) + +#define CHK6oG4oE4UP(b) (T6oG4oE4UP(b)) +#define CHK6oG4oE4DN(b) (T6oG4oE4DN(b)) +#define CHK6oG4oE4(b) (CHK6oG4oE4UP(b) || CHK6oG4oE4DN(b)) + +#define CHK46oG4oE4DN(b) (CHK4oG4oE4DN(b) || CHK6oG4oE4DN(b)) +#define CHK46oG4oE4UP(b) (CHK4oG4oE4UP(b) || CHK6oG4oE4UP(b)) +#define CHK46oG4oE4(b) (CHK4oG4oE4(b) || CHK6oG4oE4(b)) + +#define CHK2oG4oE4UP(b) (T2oG4oE4UP(b)) +#define CHK2oG4oE4DN(b) (T2oG4oE4DN(b)) +#define CHK2oG4oE4(b) (CHK2oG4oE4UP(b) || CHK2oG4oE4DN(b)) + + +#define CHK246oG4oE4UP(b) (CHK4oG4oE4UP(b) || CHK6oG4oE4UP(b) || CHK2oG4oE4UP(b)) +#define CHK246oG4oE4DN(b) (CHK4oG4oE4DN(b) || CHK6oG4oE4DN(b) || CHK2oG4oE4DN(b)) +#define CHK246oG4oE4(b) (CHK4oG4oE4(b) || CHK6oG4oE4(b) || CHK2oG4oE4(b)) + + +#define RX_PPTP(b) ((b)->rx.info.bmap.PPTP) +#define TX_PPTP(b) ((b)->tx.info.bmap.PPTP) + +#define RX_L2TP(b) ((b)->rx.info.bmap.L2TP) +#define TX_L2TP(b) ((b)->tx.info.bmap.L2TP) + +#define TX_IPV6_MAPE(b) ((b)->tx.info.bmap.MAPE) + +#define RX_PPPOE(b) ((b)->rx.info.bmap.PPPoE_2516) +#define TX_PPPOE(b) ((b)->tx.info.bmap.PPPoE_2516) +#define PT_PPPOE(b) (RX_PPPOE(b) && TX_PPPOE(b)) + + +#define PKT_IPV6_GET_TOS_WORD(word) \ + ((ntohl(word) & 0x0FF00000) >> 20) + +#define PKT_IPV6_SET_TOS_WORD(word, tos) \ + (word = htonl((ntohl(word) & 0xF00FFFFF) | ((tos << 20) & 0x0FF00000))) + +/* BLOG_LOCK Definitions */ +extern spinlock_t blog_lock_g; +#define BLOG_LOCK_BH() spin_lock_bh( &blog_lock_g ) +#define BLOG_UNLOCK_BH() spin_unlock_bh( &blog_lock_g ) + +typedef struct ip6_addr { + union { + uint8_t p8[16]; + uint16_t p16[8]; + uint32_t p32[4]; + }; +} ip6_addr_t; + +/* + *------------------------------------------------------------------------------ + * Buffer to log IPv6 Tuple. + * Packed: 3 16byte cachelines + *------------------------------------------------------------------------------ + */ +struct blogTupleV6_t { + union { + uint32_t word0; + }; + + union { + uint32_t word1; + struct { + uint16_t length; + uint8_t next_hdr; + uint8_t rx_hop_limit; + }; + }; + + ip6_addr_t saddr; + ip6_addr_t daddr; + + union { + struct { + uint16_t source; /* L4 source port */ + uint16_t dest; /* L4 dest port */ + } port; + uint32_t ports; + }; + + union { + struct { + uint8_t exthdrs:6; /* Bit field of IPv6 extension headers */ + uint8_t fragflag:1; /* 6in4 Upstream IPv4 fragmentation flag */ + uint8_t tunnel:1; /* Indication of IPv6 tunnel */ + uint8_t tx_hop_limit; + uint16_t ipid; /* 6in4 Upstream IPv4 identification */ + }; + uint32_t word2; + }; + + union { + struct { + uint8_t nextHdr; uint8_t hdrLen; uint16_t data16; + uint32_t data32; + }; + uint64_t ip6_ExtHdr; + }; + +} ____cacheline_aligned; +typedef struct blogTupleV6_t BlogTupleV6_t; + +typedef union blogGreFlags { + uint16_t u16; + struct { + BE_DECL( + uint16_t csumIe : 1; + uint16_t rtgIe : 1; + uint16_t keyIe : 1; + uint16_t seqIe : 1; + uint16_t srcRtIe: 1; + uint16_t recurIe: 3; + uint16_t ackIe : 1; + + uint16_t flags : 4; + uint16_t ver : 3; + ) + LE_DECL( + uint16_t ver : 3; + uint16_t flags : 4; + + uint16_t ackIe : 1; + uint16_t recurIe: 3; + uint16_t srcRtIe: 1; + uint16_t seqIe : 1; + uint16_t keyIe : 1; + uint16_t rtgIe : 1; + uint16_t csumIe : 1; + ) + }; +} BlogGreFlags_t; + +struct blogGre_t { + uint8_t l2hdr[ BLOG_HDRSZ_MAX ]; /* Data of all L2 headers */ + BlogGreFlags_t gre_flags; + union { + uint16_t u16; + struct { + BE_DECL( + uint16_t reserved : 10; + uint16_t fragflag : 1; + uint16_t hlen : 5; + ) + LE_DECL( + uint16_t hlen : 5; + uint16_t fragflag : 1; + uint16_t reserved : 10; + ) + }; + }; + uint16_t ipid; + uint16_t l2_hlen; + + union { //pptp + struct { + uint16_t keyLen; + uint16_t keyId; + }; + uint32_t key; + }; + uint32_t seqNum; + uint32_t ackNum; + uint16_t pppInfo; + uint16_t pppProto; +}; + +typedef struct blogGre_t BlogGre_t; + +typedef union blogL2tpFlags { + uint16_t u16; + struct { + BE_DECL( + uint16_t type : 1; + uint16_t lenBit : 3; + uint16_t seqBit : 2; + uint16_t offsetBit : 1; + uint16_t priority : 1; + uint16_t reserved : 4; + uint16_t version : 4; + ) + LE_DECL( + uint16_t version : 4; + uint16_t reserved : 4; + uint16_t priority : 1; + int16_t offsetBit : 1; + uint16_t seqBit : 2; + uint16_t lenBit : 3; + uint16_t type : 1; + ) + }; +} BlogL2tpFlags_t; + +struct blogL2tp_t { + BlogL2tpFlags_t l2tp_flags; + uint16_t length; + uint16_t tunnelId; + uint16_t sessionId; + uint16_t seqNum; + uint16_t expSeqNum; + uint16_t offsetSize; + uint16_t offsetPad; + union { + uint16_t u16; + struct { + BE_DECL( + uint16_t reserved : 10; + uint16_t fragflag : 1; + uint16_t hlen : 5; + ) + LE_DECL( + uint16_t hlen : 5; + uint16_t fragflag : 1; + uint16_t reserved : 10; + ) + }; + }; + uint16_t ipid; + uint16_t unused; + uint16_t udpLen; + uint16_t udpCheck; + uint16_t pppInfo; + uint16_t pppProto; + +}; +typedef struct blogL2tp_t BlogL2tp_t; + +#define BLOG_L2TP_PPP_LEN 4 +#define BLOG_L2TP_PORT 1701 + +#define BLOG_PPTP_PPP_LEN 4 +#define BLOG_PPTP_NOAC_PPPINFO 0X2145 /* pptp packet without ppp address control field 0xff03 */ + +#define BLOG_ESP_SPI_LEN 4 +#define BLOG_ESP_SEQNUM_LEN 4 +#define BLOG_ESP_PADLEN_LEN 1 +#define BLOG_ESP_NEXT_PROTO_LEN 1 +#define BLOG_ESP_ICV_LEN 12 + +struct blogEsp_t { + uint32_t u32; + union { + uint16_t u16; + struct { + BE_DECL( + uint16_t reserved : 7; + uint16_t pmtudiscen : 1; + uint16_t ipv6 : 1; + uint16_t ipv4 : 1; + uint16_t fragflag : 1; + uint16_t ivsize : 5; + ) + LE_DECL( + uint16_t ivsize : 5; + uint16_t fragflag : 1; + uint16_t ipv4 : 1; + uint16_t ipv6 : 1; + uint16_t pmtudiscen : 1; + uint16_t reserved : 7; + ) + }; + }; + uint16_t ipid; + void *dst_p; + void *secPath_p; +}; +typedef struct blogEsp_t BlogEsp_t; + + +/* + *------------------------------------------------------------------------------ + * Buffer to log Layer 2 and IP Tuple headers. + * Packed: 4 16byte cachelines + *------------------------------------------------------------------------------ + */ +struct blogHeader_t { + + BlogTuple_t tuple; /* L3+L4 IP Tuple log */ + + union { + BlogInfo_t info; + union { + struct { + uint32_t word1; /* channel, count, rfc2684, bmap */ + uint32_t word; /* channel, count, rfc2684, bmap */ + }; + uint32_t pktlen; /* stats info */ + }; + }; + + struct { + uint8_t vlan_8021ad :1; /* 8021AD stacked */ + uint8_t wan_qdisc :1; /* device type */ + uint8_t multicast :1; /* multicast flag */ + uint8_t fkbInSkb :1; /* fkb from skb */ + uint8_t count :4; /* # of L2 encapsulations */ + }; + uint8_t length; /* L2 header total length */ + uint8_t /*BlogEncap_t*/ encap[ BLOG_ENCAP_MAX ];/* All L2 header types */ + + uint8_t l2hdr[ BLOG_HDRSZ_MAX ]; /* Data of all L2 headers */ + +} ____cacheline_aligned; + +typedef struct blogHeader_t BlogHeader_t; /* L2 and L3+4 tuple */ + +/* Coarse hash key: L1, L3, L4 hash */ +union blogHash_t { + uint32_t match; + struct { + union { + struct { + uint8_t tcp_pure_ack : 1; + uint8_t unused : 7; + }; + uint8_t ext_match; + }; + uint8_t protocol; /* IP protocol */ + + struct { + union { + struct { + uint8_t ivsize; /* IPsec cipher Initialization Vector size */ + }; + uint8_t channel; + }; + + union { + struct { + uint8_t phyLen : 4; + uint8_t phyType : 4; + }; + uint8_t phy; + }; + } l1_tuple; + }; +}; + +typedef union blogHash_t BlogHash_t; + + +/* flow priority - used for packet buffer reseravtion */ +typedef enum { + BLOG_DECL(flow_prio_normal) + BLOG_DECL(flow_prio_high) + BLOG_DECL(flow_prio_exclusive) +} BlogFlowPrio_t; + + +/* TBD : Rearrange following bit positions for optimization. */ +union blogWfd_t { + uint32_t u32; + struct { + BE_DECL( + uint32_t is_rx_hw_acc_en : 1;/* =1 if WLAN Receive is capable of HW Acceleration */ + uint32_t is_tx_hw_acc_en : 1;/* =1 if WLAN Transmit is capable of HW Acceleartion */ + uint32_t is_wfd : 1;/* is_wfd=1 */ + uint32_t is_chain : 1;/* is_chain=1 */ + uint32_t reserved1 : 4;/* unused */ + uint32_t wfd_prio : 1;/* 0=high, 1=low */ + uint32_t wfd_idx : 2;/* WFD idx */ + uint32_t reserved0 : 1;/* unused */ + uint32_t priority : 4;/* Tx Priority */ + uint32_t chain_idx : 16;/* Tx chain index */ + ) + LE_DECL( + uint32_t chain_idx : 16;/* Tx chain index */ + uint32_t priority : 4;/* Tx Priority */ + uint32_t reserved0 : 1;/* unused */ + uint32_t wfd_idx : 2;/* WFD idx */ + uint32_t wfd_prio : 1;/* 0=high, 1=low */ + uint32_t reserved1 : 4;/* unused */ + uint32_t is_chain : 1;/* is_chain=1 */ + uint32_t is_wfd : 1;/* is_wfd=1 */ + uint32_t is_tx_hw_acc_en : 1;/* =1 if WLAN Transmit is capable of HW Acceleartion */ + uint32_t is_rx_hw_acc_en : 1;/* =1 if WLAN Receive is capable of HW Acceleration */ + ) + } nic_ucast; + + struct { + BE_DECL( + uint32_t is_rx_hw_acc_en : 1;/* =1 if WLAN Receive is capable of HW Acceleration */ + uint32_t is_tx_hw_acc_en : 1;/* =1 if WLAN Transmit is capable of HW Acceleartion */ + uint32_t is_wfd : 1;/* is_wfd=1 */ + uint32_t is_chain : 1;/* is_chain=0 */ + uint32_t wfd_prio : 1;/* 0=high, 1=low */ + uint32_t ssid : 4;/* SSID for WLAN */ + uint32_t reserved1 : 8;/* unused */ + uint32_t wfd_idx : 2;/* WFD idx */ + uint32_t priority : 3;/* Tx Priority */ + uint32_t flowring_idx :10;/* Tx flowring index */ + ) + LE_DECL( + uint32_t flowring_idx :10;/* Tx flowring index */ + uint32_t priority : 3;/* Tx Priority */ + uint32_t wfd_idx : 2;/* WFD idx */ + uint32_t reserved1 : 8;/* unused */ + uint32_t ssid : 4;/* SSID for WLAN */ + uint32_t wfd_prio : 1;/* 0=high, 1=low */ + uint32_t is_chain : 1;/* is_chain=0 */ + uint32_t is_wfd : 1;/* is_wfd=1 */ + uint32_t is_tx_hw_acc_en : 1;/* =1 if WLAN Transmit is capable of HW Acceleartion */ + uint32_t is_rx_hw_acc_en : 1;/* =1 if WLAN Receive is capable of HW Acceleration */ + ) + } dhd_ucast; + + struct { + BE_DECL( + uint32_t is_rx_hw_acc_en : 1;/* =1 if WLAN Receive is capable of HW Acceleration */ + uint32_t is_tx_hw_acc_en : 1;/* =1 if WLAN Transmit is capable of HW Acceleartion */ + uint32_t is_wfd : 1;/* is_wfd=1 */ + uint32_t is_chain : 1;/* is_chain=0 */ + uint32_t wfd_idx : 2;/* WFD idx */ + uint32_t wfd_prio : 1;/* 0=high, 1=low */ + uint32_t reserved1 : 2;/* unused */ + uint32_t ssid : 4;/* SSID */ + uint32_t reserved0 :19;/* unused */ + ) + LE_DECL( + uint32_t reserved0 :19;/* unused */ + uint32_t ssid : 4;/* SSID */ + uint32_t reserved1 : 2;/* unused */ + uint32_t wfd_prio : 1;/* 0=high, 1=low */ + uint32_t wfd_idx : 2;/* WFD idx */ + uint32_t is_chain : 1;/* is_chain=0 */ + uint32_t is_wfd : 1;/* is_wfd=1 */ + uint32_t is_tx_hw_acc_en : 1;/* =1 if WLAN Transmit is capable of HW Acceleartion */ + uint32_t is_rx_hw_acc_en : 1;/* =1 if WLAN Receive is capable of HW Acceleration */ + ) + } mcast; +}; +typedef union blogWfd_t BlogWfd_t; + +struct blogRnr_t { + BE_DECL( + uint32_t is_rx_hw_acc_en : 1;/* =1 if WLAN Receive is capable of HW Acceleration */ + uint32_t is_tx_hw_acc_en : 1;/* =1 if WLAN Transmit is capable of HW Acceleartion */ + uint32_t is_wfd : 1;/* rnr (is_wfd=0) */ + uint32_t radio_idx : 2;/* Radio index */ + uint32_t llcsnap_flag : 1;/* llcsnap_flag */ + uint32_t priority : 3;/* Tx Priority */ + uint32_t ssid : 4;/* SSID */ + uint32_t reserved1 : 7;/* unused */ + uint32_t flow_prio : 2;/* flow priority (normal,high, exclusive) - used for packet buffer reservation */ + uint32_t flowring_idx :10;/* Tx flowring index */ + ) + LE_DECL( + uint32_t flowring_idx :10;/* Tx flowring index */ + uint32_t flow_prio : 2;/* flow priority (normal,high, exclusive) - used for packet buffer reservation */ + uint32_t reserved1 : 7;/* unused */ + uint32_t ssid : 4;/* SSID */ + uint32_t priority : 3;/* Tx Priority */ + uint32_t llcsnap_flag : 1;/* llcsnap_flag */ + uint32_t radio_idx : 2;/* Radio index */ + uint32_t is_wfd : 1;/* rnr (is_wfd=0) */ + uint32_t is_tx_hw_acc_en : 1;/* =1 if WLAN Transmit is capable of HW Acceleartion */ + uint32_t is_rx_hw_acc_en : 1;/* =1 if WLAN Receive is capable of HW Acceleration */ + ) +}; + +typedef struct blogRnr_t BlogRnr_t; + +#define MAX_NUM_VLAN_TAG 2 + +/* Blog ingress priority derived from IQOS */ +typedef enum { + BLOG_DECL(BLOG_IQ_PRIO_LOW) + BLOG_DECL(BLOG_IQ_PRIO_HIGH) +} BlogIqPrio_t; + +/* + *------------------------------------------------------------------------------ + * Buffer log structure. + * ARM 32: 704 bytes + * ARM 64: 896 bytes + * MIPS : 704 bytes + * Marked the cacheline boundaries in the below structure. + * Be cautious when adding new members to this structure. + *------------------------------------------------------------------------------ + */ +struct blog_t { + + union { + void * void_p; + struct blog_t * blog_p; /* Free list of Blog_t */ + struct sk_buff * skb_p; /* Associated sk_buff */ + }; + BlogHash_t key; /* Coarse hash search key */ + uint32_t hash; /* hash */ + union { + uint32_t wl; + struct { + BE_DECL( + uint32_t is_rx_hw_acc_en : 1;/* =1 if WLAN Receive is capable of HW Acceleration */ + uint32_t is_tx_hw_acc_en : 1;/* =1 if WLAN Transmit is capable of HW Acceleartion */ + uint32_t reserved : 30; + ) + LE_DECL( + uint32_t reserved : 30; + uint32_t is_tx_hw_acc_en : 1;/* =1 if WLAN Transmit is capable of HW Acceleartion */ + uint32_t is_rx_hw_acc_en : 1;/* =1 if WLAN Receive is capable of HW Acceleration */ + ) + } wl_hw_support; + BlogWfd_t wfd; + BlogRnr_t rnr; + }; + uint32_t fc_context; + struct blog_t * vblog_p; /* vertical list of Blog_t */ + void * mc_fdb; /* physical rx network device */ + + void *map_p; + /* --- [ARM32]32 byte cacheline boundary --- */ + BlogEthAddr_t src_mac; /* Flow src MAC */ + BlogEthAddr_t dst_mac; /* Flow dst MAC */ + /* --- [ARM64]64 byte cacheline boundary --- */ + + void * fdb[2]; /* fdb_src and fdb_dst */ + int8_t delta[MAX_VIRT_DEV]; /* octet delta info */ + int8_t tx_dev_delta; /* octet delta of TX dev */ + uint8_t l2_dirty_offset; + uint8_t vtag_num: 4; + uint8_t vtag_tx_num: 4; + uint16_t eth_type; + /* --- [ARM32]32 byte cacheline boundary --- */ + + union { + uint32_t flags; + struct { + BE_DECL( + uint32_t unused: 2; + uint32_t is_routed: 1; + uint32_t fc_hybrid: 1; /* hybrid flow accelarate in HW and SW */ + uint32_t l2_mode: 1; + uint32_t is_ssm: 1; + uint32_t mcast_learn: 1; + + uint32_t l2_pppoe: 1; /* L2 packet is PPPoE */ + uint32_t l2_ipv6: 1; /* L2 packet is IPv6 */ + uint32_t l2_ipv4: 1; /* L2 packet is IPv4 */ + uint32_t is_mapt_us: 1; /* MAP-T Upstream flow */ + uint32_t is_df: 1; /* IPv4 DF flag set */ + uint32_t ptm_us_bond: 1; /* PTM US Bonding Mode */ + uint32_t lag_port: 2; /* LAG port when trunking is done by internal switch/runner */ + + uint32_t tos_mode_us: 1; /* ToS mode for US: fixed, inherit */ + uint32_t tos_mode_ds: 1; /* ToS mode for DS: fixed, inherit */ + uint32_t has_pppoe: 1; + uint32_t ack_done: 1; /* TCP ACK prio decision made */ + uint32_t ack_cnt: 4; /* back to back TCP ACKs for prio */ + + uint32_t nf_dir_pld: 1; + uint32_t nf_dir_del: 1; + uint32_t nf_ct_skip_ref_dec: 1; /* when set don't decrement ct refcnt */ + uint32_t pop_pppoa: 1; + uint32_t insert_eth: 1; + uint32_t iq_prio: 1; + uint32_t mc_sync: 1; + uint32_t rtp_seq_chk: 1; /* RTP sequence check enable */ + uint32_t incomplete: 1; + ) + LE_DECL( + uint32_t incomplete: 1; + uint32_t rtp_seq_chk: 1; /* RTP sequence check enable */ + uint32_t mc_sync: 1; + uint32_t iq_prio: 1; + uint32_t insert_eth: 1; + uint32_t pop_pppoa: 1; + uint32_t nf_ct_skip_ref_dec: 1; /* when set don't decrement ct refcnt */ + uint32_t nf_dir_del: 1; + uint32_t nf_dir_pld: 1; + + uint32_t ack_cnt: 4; /* back to back TCP ACKs for prio */ + uint32_t ack_done: 1; /* TCP ACK prio decision made */ + uint32_t has_pppoe: 1; + uint32_t tos_mode_ds: 1; /* ToS mode for DS: fixed, inherit */ + uint32_t tos_mode_us: 1; /* ToS mode for US: fixed, inherit */ + + uint32_t lag_port: 2; /* LAG port when trunking is done by internal switch/runner */ + uint32_t ptm_us_bond: 1; /* PTM US Bonding Mode */ + uint32_t is_df: 1; /* IPv4 DF flag set */ + uint32_t is_mapt_us: 1; /* MAP-T Upstream flow */ + uint32_t l2_ipv4: 1; /* L2 packet is IPv4 */ + uint32_t l2_ipv6: 1; /* L2 packet is IPv6 */ + uint32_t l2_pppoe: 1; /* L2 packet is PPPoE */ + + uint32_t mcast_learn: 1; + uint32_t is_ssm: 1; + uint32_t l2_mode: 1; + uint32_t fc_hybrid: 1; /* hybrid flow accelarate in HW and SW */ + uint32_t is_routed: 1; + uint32_t unused: 2; + ) + }; + }; + union { + /* only the lower 32 bit in mark is used in 64 bit system + * but we declare it as unsigned long for the ease of blog + * to handle it in different architecture, since it part + * of union with a dst_entry pointer */ + unsigned long mark; /* NF mark value on tx */ + void *dst_entry; /* skb dst_entry for local_in */ + }; + + union { + uint32_t priority; /* Tx priority */ + uint32_t flowid; /* used only for local in */ + }; + + void * blogRule_p; /* List of Blog Rules */ + + union { + struct { + uint32_t dosAttack : 16; + uint32_t lenPrior : 1; + uint32_t vlanPrior : 1; + uint32_t dscpMangl : 1; + uint32_t tosMangl : 1; + uint32_t preMod : 1; + uint32_t postMod : 1; + uint32_t dscp2pbit : 1; + uint32_t dscp2q : 1; + uint32_t reserved : 8; + }; + uint32_t feature; /* Feature set for per-packet modification */ + }; + union { + struct { + uint8_t vlanout_offset; /* Outer VLAN header offset */ + uint8_t vlanin_offset; /* Inner VLAN header offset */ + uint8_t vpass_tx_offset; /* Passthru VLAN headers tx offset */ + uint8_t vpass_len; /* Passthru VLAN headers length */ + uint8_t pppoe_offset; /* PPPoE header offset */ + uint8_t ip_offset; /* IPv4 header offset */ + uint8_t ip6_offset; /* IPv6 header offset */ + uint8_t l4_offset; /* Layer 4 header offset */ + uint8_t isWan; /* Receiving by WAN interface */ + uint8_t reserved8_3[3]; + }; + uint32_t offsets[3]; + }; + /* --- [ARM32][ARM64] cacheline boundary --- */ + int (*preHook)(Blog_t *blog_p, void *nbuff_p); /* Pre-modify hook */ + int (*postHook)(Blog_t *blog_p, void *nbuff_p); /* Post-modify hook */ + /* vtag[] stored in network order to improve fcache performance */ + uint32_t vtag[MAX_NUM_VLAN_TAG]; + /* pointers to the devices which the flow goes thru */ + void * virt_dev_p[MAX_VIRT_DEV]; + /* --- [ARM32][ARM64]cacheline boundary --- */ + void *fdb_npe_p[BLOG_FDB_NPE_MAX]; /* FDB NPEs */ + void *npe_p[BLOG_NPE_MAX]; /* non-FDB NPEs*/ + /* --- [ARM32][ARM64]cacheline boundary --- */ + + //BlogTupleV6_t and BlogHeader_t is cacheline_aligned structure, be + //be cautious when adding new variables around these memeber as it + //will create unwanted holes in the structure. + BlogTupleV6_t tupleV6; /* L3+L4 IP Tuple log */ + BlogTupleV6_t del_tupleV6; /* Del GRE L3+L4 IPV6 Tuple log */ + + BlogHeader_t tx; /* Transmit path headers */ + BlogHeader_t rx; /* Receive path headers */ + /* --- [ARM32][ARM64]cacheline boundary --- */ + + void *rx_dev_p; /* RX physical network device */ + void *tx_dev_p; /* TX physical network device */ + + unsigned long dev_xmit; + int (*dev_xmit_blog)(void * nbuff_p,const Blog_t * const blog_p); /* Xmit with blog */ + + /* Flow connection/session tracker */ + void *ct_p[BLOG_CT_MAX]; + uint32_t ct_ver[BLOG_CT_VER_MAX]; + /* --- [ARM32]32 byte cacheline boundary --- */ + void *rx_tunl_p; + /* --- [ARM64]64 byte cacheline boundary --- */ + void *tx_tunl_p; + BlogActivateKey_t activate_key; + uint8_t tx_l4_offset; /*offset to inner most L4 header*/ + uint8_t tx_l3_offset; /*offset to inner most L3 header*/ + uint16_t mcast_port_map; + uint16_t mcast_excl_udp_port; + + uint16_t minMtu; + uint8_t dpi_queue; + uint8_t tuple_offset; /* offset of flow tuple header */ + uint8_t hw_pathstat_idx; /* HWACC Pathstat index */ + uint8_t host_mac_hashix; + uint8_t spdtst; + + /* To make cache alignment inserting dummy_ptr */ + uint8_t dummy_data2[3]; + /* --- [ARM32]32 byte cacheline boundary --- */ + BlogTuple_t *grerx_tuple_p; /* gre proto RX Tuple pointer */ + BlogTuple_t *gretx_tuple_p; /* gre proto TX Tuple pointer */ + union { + struct { + BlogGre_t grerx; + /* --- [ARM32][ARM64]cacheline boundary --- */ + BlogGre_t gretx; + }; + struct { + BlogL2tp_t l2tptx; + }; + }; + /* --- [ARM32]32 byte cacheline boundary (was 24 bytes ago)--- */ + BlogTuple_t *esprx_tuple_p; /* ESP proto RX Tuple pointer */ + BlogTuple_t *esptx_tuple_p; /* ESP proto TX Tuple pointer */ + /* --- [ARM32]32 byte cacheline boundary --- */ + struct { + BlogEsp_t esprx; + /* --- [ARM64]64 byte cacheline boundary --- */ + BlogEsp_t esptx; + }; + /* --- [ARM32]32 byte cacheline boundary --- */ + BlogTuple_t delrx_tuple; /* Del proto RX L3+L4 IP Tuple log */ + BlogTuple_t deltx_tuple; /* Del proto TX L3+L4 IP Tuple log */ + /* --- [ARM32][ARM64]cacheline boundary --- */ + BlogTuple_t rx_tuple[1]; /* RX L3+L4 IP Tuple log */ + BlogTuple_t tx_tuple[1]; /* TX L3+L4 IP Tuple log */ + void *mega_p; + + uint32_t l2_keymap; + uint32_t l3_keymap; +} ____cacheline_aligned; + +/* + * ----------------------------------------------------------------------------- + * Engineering constants: Pre-allocated pool size 400 blogs Ucast+Mcast + * + * Extensions done in #blogs carved from a 2x4K page (external fragmentation) + * Blog size = 240, 8192/240 = 34 extension 32bytes internal fragmentation + * + * Number of extensions engineered to permit approximately max # of flows + * (assuming one blog per flow). + * ----------------------------------------------------------------------------- + */ +#define CC_BLOG_SUPPORT_EXTEND /* Conditional compile */ +#define BLOG_POOL_SIZE_ENGG 400 /* Pre-allocated pool size */ +/* Number of Blog_t per extension */ +#define BLOG_EXTEND_SIZE_ENGG (8192/sizeof(Blog_t)) +/* Maximum extensions allowed including 4K flows */ +#define BLOG_EXTEND_MAX_ENGG (16384/BLOG_EXTEND_SIZE_ENGG) + + + +extern const char * strBlogAction[]; +extern const char * strBlogEncap[]; +extern const char * strRfc2684[]; +extern const uint8_t rfc2684HdrLength[]; +extern const uint8_t rfc2684HdrData[][16]; + + +#else +struct blog_t {void * blogRule_p;}; +#define BLOG_LOCK_BH() +#define BLOG_UNLOCK_BH() +#endif /* defined(CONFIG_BLOG) */ + +/* + * ----------------------------------------------------------------------------- + * Blog functional interface + * ----------------------------------------------------------------------------- + */ + + +/* + * ----------------------------------------------------------------------------- + * Section 1. Extension of a packet context with a logging context + * ----------------------------------------------------------------------------- + */ + +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#define blog_ptr(skb_p) skb_p->blog_p +#else +#define blog_ptr(skb_p) BLOG_NULL +#endif + +/* Allocate or deallocate a Blog_t */ +Blog_t * blog_get(void); +void blog_put(Blog_t * blog_p); + +/* Allocate a Blog_t and associate with sk_buff or fkbuff */ +extern Blog_t * blog_skb(struct sk_buff * skb_p); +extern Blog_t * blog_fkb(struct fkbuff * fkb_p); + +/* Clear association of Blog_t with sk_buff */ +extern Blog_t * blog_snull(struct sk_buff * skb_p); +extern Blog_t * blog_fnull(struct fkbuff * fkb_p); + +/* Clear association of Blog_t with sk_buff and free Blog_t object */ +extern void blog_free( struct sk_buff * skb_p, blog_skip_reason_t reason ); +/* increment refcount of ct's associated with blog */ +extern void blog_ct_get(Blog_t * blog_p); +/* decrement refcount of ct's associated with blog */ +extern void blog_ct_put(Blog_t * blog_p); + +/* Disable further logging. Dis-associate with skb and free Blog object */ +extern void blog_skip(struct sk_buff * skb_p, blog_skip_reason_t reason); + +/* Transfer association of a Blog_t object between two sk_buffs. */ +extern void blog_xfer(struct sk_buff * skb_p, const struct sk_buff * prev_p); + +/* Duplicate a Blog_t object for another skb. */ +extern void blog_clone(struct sk_buff * skb_p, const struct blog_t * prev_p); + +/* Copy a Blog_t object another blog object. */ +extern void blog_copy(struct blog_t * new_p, const struct blog_t * prev_p); + +/* get the Ingress QoS Prio from the blog */ +extern int blog_iq(const struct sk_buff * skb_p); + +/* get the flow cache status */ +extern int blog_fc_enabled(void); + +/* get the GRE tunnel accelerated status */ +extern int blog_gre_tunnel_accelerated(void); + +#define BLOG_PTM_US_BONDING_DISABLED 0 +#define BLOG_PTM_US_BONDING_ENABLED 1 + +extern void blog_ptm_us_bonding( struct sk_buff *skb_p, int mode ); + +typedef int (*blog_dhd_flow_update_t)(void*, char*, char*, int); +extern blog_dhd_flow_update_t blog_dhd_flow_update_fn; +extern int blog_is_config_netdev_mac(void *dev_p, unsigned long incl_vmacs); +extern int blog_preemptible_task(void); + +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +/* gets an NWE from blog based on the NPE type */ +static inline void *_blog_get_nwe(Blog_t *blog_p, uint32_t npe_type) +{ + blog_npe_t *npe_p = (blog_npe_t *)blog_p->npe_p[npe_type]; + return (npe_p ? npe_p->nwe_p : (void *) NULL); +} +#endif + +/* + *------------------------------------------------------------------------------ + * Section 2. Associating native OS or 3rd-party network constructs + *------------------------------------------------------------------------------ + */ + +extern void blog_link(BlogNetEntity_t entity_type, Blog_t * blog_p, + void * net_p, uint32_t param1, uint32_t param2); + +/* + *------------------------------------------------------------------------------ + * Section 3. Network construct and Blog client co-existence call backs + *------------------------------------------------------------------------------ + */ + +extern unsigned long blog_request(BlogRequest_t event, void * net_p, + unsigned long param1, unsigned long param2); + +extern int blog_query(BlogQuery_t query, void * net_p, + uint32_t param1, uint32_t param2, unsigned long param3); + +/* + * blog_notify(): + * blog_notify() is a synchrounous notification from an entity to blog/flow_cache + * and will return once the notification/event has been completed. + * This API should be called only when it is known that the event processing time + * is going to be short (process or interrupt context). + * + * Interrupt Context: It is NOT recommended to call blog_notify() from an + * interrupt context (like softirq/timer) because it will block all other + * processes/threads/softirq from running until the notification processing + * has been completed. Instead use blog_notify_async() from an interrupt + * context. + */ +extern void blog_notify(BlogNotify_t event, void *net_p, + unsigned long param1, unsigned long param2); + +/* + * fc_evt task will invoke this callback function asynchronously once the + * event processing has been completed. It is upto each entity to decide what + * it wants to do within this callback but it should not hold the fc_evt task + * for too long. + */ +typedef void (*blog_notify_async_cb_fn_t)(void *notify_cb_data_p); + +/* + * blog_notify_async(): + * It is same as blog_notify() except two new parameters (notify_cb_fn and + * notify_cb_data_p) have been added, and it is asynchronous call. notify_cb_fn + * function will be called with notify_cb_data_p parameter on completion of + * the event processing. If an caller does not want to wait for the completion + * of the event it can pass NULL values for notify_cb_fn and notify_cb_data_p. + * + * blog_notify_async() can be called from process or interrupt context, + * it will not block/sleep in this call. If blocking is needed it should be + * done outside this API. + * + * CAUTION: Responsibility of the calling entity: + * - Serialization and/or locking, reference count of entry + * - the entry (e.g. flowring, FDB, etc.) for which flows are being flused + * is NOT freed or reallocated before the callback function is invoked. + * + * return: Caller MUST check the return value. + * 1 : caller's notify callback function will be called + * 0 : caller's notify callback function will NOT be called. + * Situations where fc_evt task is not running. + */ +extern int blog_notify_async(BlogNotify_t event, void *net_p, + unsigned long param1, unsigned long param2, + blog_notify_async_cb_fn_t notify_cb_fn, void *notify_cb_data_p); + +/* + *------------------------------------------------------------------------------ + * blog_notify_async_wait + * Calls blog_notify_async() and then waits for completion of event. No callback + * function needed from the caller, this function uses its own callback function. + * Note : If called from NOT preempt-safe context, this function will change + * blog_notify_async() to blog_notify(), which means the event is + * processed synchronously. + * Caller should not call blog_lock()/blog_unlock() as this fucntion + * internally calls blog_lock before calling blog_notify/_async() APIs, + * and blog_unlock after calling. + *------------------------------------------------------------------------------ + */ +extern void blog_notify_async_wait(BlogNotify_t event, void *net_p, + unsigned long param1, unsigned long param2); + +/* blog notify event enqueue function type */ +typedef int (*blog_notify_evt_enqueue_hook_t)(blog_notify_evt_type_t evt_type, + void *net_p, unsigned long param1, unsigned long param2, + blog_notify_async_cb_fn_t notify_cb_fn, void *notify_cb_data_p); + +void blog_bind_notify_evt_enqueue( + blog_notify_evt_enqueue_hook_t blog_notify_evt_enqueue_fn ); +/* + *------------------------------------------------------------------------------ + * Section 4. Network end-point binding of Blog client + * + * If rx hook is defined, + * blog_sinit(): initialize a fkb from skb, and pass to hook + * if packet is consumed, skb is released. + * if packet is blogged, the blog is associated with skb. + * blog_finit(): pass to hook + * if packet is to be blogged, the blog is associated with fkb. + * + * If tx hook is defined, invoke tx hook, dis-associate and free Blog_t + *------------------------------------------------------------------------------ + */ +extern BlogAction_t blog_sinit(struct sk_buff *skb_p, void * dev_p, + uint32_t encap, uint32_t channel, uint32_t phyHdr); + +extern BlogAction_t blog_finit(struct fkbuff *fkb_p, void * dev_p, + uint32_t encap, uint32_t channel, uint32_t phyHdr); + +extern BlogAction_t blog_finit_args(struct fkbuff *fkb_p, void * dev_p, + uint32_t encap, uint32_t channel, uint32_t phyHdr, + BlogFcArgs_t *fc_args); + +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +extern BlogAction_t _blog_emit(void * nbuff_p, void * dev_p, + uint32_t encap, uint32_t channel, uint32_t phyHdr); + +static inline BlogAction_t blog_emit(void * nbuff_p, void * dev_p, + uint32_t encap, uint32_t channel, uint32_t phyHdr) +{ + if ( nbuff_p == NULL ) return PKT_NORM; + if ( !IS_SKBUFF_PTR(nbuff_p) ) return PKT_NORM; + // OK, this is something worth looking at, call real function + return ( _blog_emit(nbuff_p, dev_p, encap, channel, phyHdr) ); +} +#else +BlogAction_t blog_emit( void * nbuff_p, void * dev_p, + uint32_t encap, uint32_t channel, uint32_t phyHdr ); +#endif + +/* + * blog_iq_prio determines the Ingress QoS priority of the packet + */ +extern int blog_iq_prio(struct sk_buff * skb_p, void * dev_p, + uint32_t encap, uint32_t channel, uint32_t phyHdr); +/* + *------------------------------------------------------------------------------ + * blog_activate(): static configuration function of blog application + * pass a filled blog to the hook for configuration + *------------------------------------------------------------------------------ + */ +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +extern BlogActivateKey_t *blog_activate( Blog_t * blog_p, BlogTraffic_t traffic, + BlogClient_t client ); +#else +extern uint32_t blog_activate( Blog_t * blog_p, BlogTraffic_t traffic, + BlogClient_t client ); +#endif + +/* + *------------------------------------------------------------------------------ + * blog_deactivate(): static deconfiguration function of blog application + *------------------------------------------------------------------------------ + */ +extern Blog_t * blog_deactivate( BlogActivateKey_t key, BlogTraffic_t traffic, + BlogClient_t client ); + +/* + * ----------------------------------------------------------------------------- + * User defined filter invoked invoked in the rx hook. A user may override the + * Blog action defined by the client. To enable the invocation of this API + * in blog_finit, ensure that CC_BLOG_SUPPORT_USER_FILTER is enabled. Also, a + * network device driver may directly invoke blog_filter() to override PKT_BLOG + * and return PKT_NORM (by releasing the associated Blog_t). + * ----------------------------------------------------------------------------- + */ +extern BlogAction_t blog_filter(Blog_t * blog_p); + +/* + * ----------------------------------------------------------------------------- + * Section 5. Binding Blog client applications: + * + * Blog defines three hooks: + * + * RX Hook: If this hook is defined then blog_init() will pass the packet to + * the Rx Hook using the FkBuff_t context. L1 and encap information + * are passed to the receive hook. The private network device context + * may be extracted using the passed net_device object, if needed. + * + * TX Hook: If this hook is defined then blog_emit() will check to see whether + * the NBuff has a Blog_t, and if so pass the NBuff and Blog to the + * bound Tx hook. + * + * NotifHook: When blog_notify is invoked, the bound hook is invoked. Based on + * event type the bound Blog client may perform a custom action. + * + * SC Hook: If this hook is defined, blog_activate() will pass a blog with + * necessary information for statical configuration. + * + * SD Hook: If this hook is defined, blog_deactivate() will pass a pointer + * to a network object with BlogActivateKey information. The + * respective flow entry will be deleted. + * + * QueryHook: When blog_query is invoked, the bound hook is invoked. Based on + * query type the bound Blog client will return result of query. + * ----------------------------------------------------------------------------- + */ +typedef union { + struct { + uint16_t QR_HOOK : 1; + uint16_t RX_HOOK : 1; + uint16_t TX_HOOK : 1; + uint16_t XX_HOOK : 1; + uint16_t SC_HOOK : 1; + uint16_t SD_HOOK : 1; + uint16_t FA_HOOK : 1; + uint16_t FD_HOOK : 1; + uint16_t PA_HOOK : 1; + uint16_t reserved : 7; + } bmap; + uint16_t hook_info; +} BlogBind_t; + +typedef BlogAction_t (* BlogDevRxHook_t)(struct fkbuff *fkb_p, void * dev_p, + BlogFcArgs_t * args); + +typedef BlogAction_t (* BlogDevTxHook_t)(struct sk_buff *skb_p, void * dev_p, + uint32_t encap, uint32_t blogHash); + +typedef int (* BlogNotifyHook_t)(blog_notify_api_t blog_notify_api, + BlogNotify_t notification, + void * net_p, unsigned long param1, unsigned long param2, + blog_notify_async_cb_fn_t notify_cb_fn, void *notify_cb_data_p); + +typedef int (* BlogQueryHook_t)(BlogQuery_t query, void * net_p, + uint32_t param1, uint32_t param2, unsigned long param3); + +typedef BlogActivateKey_t * (* BlogScHook_t)(Blog_t * blog_p, BlogTraffic_t traffic); + +typedef Blog_t * (* BlogSdHook_t)(BlogActivateKey_t key, BlogTraffic_t traffic); + +typedef void (* BlogFaHook_t)(void *ct_p, BlogFlowEventInfo_t info, BlogFlowEventType_t type); + +typedef void (* BlogFdHook_t)(void *ct_p, BlogFlowEventInfo_t info, BlogFlowEventType_t type); + +typedef BlogAction_t (* BlogPaHook_t)(struct fkbuff * fkb_p, void * dev_p, + uint32_t encap, uint32_t channel, uint32_t phyHdr); + +extern int blog_get_hw_accel(void); +extern void blog_bind(BlogDevRxHook_t rx_hook, /* Client Rx netdevice handler*/ + BlogDevTxHook_t tx_hook, /* Client Tx netdevice handler*/ + BlogNotifyHook_t xx_hook, /* Client notification handler*/ + BlogQueryHook_t qr_hook, /* Client query handler */ + BlogBind_t bind + ); + +extern int hw_accelerator_client_get(void); +extern int sw_accelerator_client_get(void); + +extern void blog_bind_config(BlogScHook_t sc_hook, /* Client static config handler*/ + BlogSdHook_t sd_hook, /* Client static deconf handler*/ + BlogClient_t client, /* Static configuration Client */ + BlogBind_t bind + ); + +void blog_bind_packet_accelerator( BlogPaHook_t blog_pa, BlogBind_t bind ); +int blog_flowevent_register_notifier(struct notifier_block *nb); +int blog_flowevent_unregister_notifier(struct notifier_block *nb); + +/* + *------------------------------------------------------------------------------ + * blog notify event + *------------------------------------------------------------------------------ + */ +typedef struct { + struct dll_t node; /* First element implements dll */ + blog_notify_evt_type_t evt_type; + void *net_p; + unsigned long param1; + unsigned long param2; + blog_notify_async_cb_fn_t notify_cb_fn; + void *notify_cb_data_p; +} ____cacheline_aligned blog_notify_evt_t; + +/* + * ----------------------------------------------------------------------------- + * Section 6. Miscellanous + * ----------------------------------------------------------------------------- + */ + +/* Logging of L2|L3 headers */ +extern void blog(struct sk_buff * skb_p, BlogDir_t dir, BlogEncap_t encap, + size_t len, void * data_p); + +/* Dump a Blog_t object */ +extern void blog_dump(Blog_t * blog_p); + +/* Get the minimum Tx MTU for a blog */ +uint16_t blog_getTxMtu(Blog_t * blog_p); + +/* + * Lock and unlock the blog layer. This is used to reduce the number of + * times the blog lock must be acquired and released during bulk rx processing. + * See also blog_finit_locked. + */ +extern void blog_lock(void); +extern void blog_unlock(void); + +/* + * Per packet basis modification feature + */ +#define BLOG_MAX_FEATURES 8 + +#define BLOG_LEN_PARAM_INDEX 0 +#define BLOG_DSCP_PARAM_INDEX 1 +#define BLOG_TOS_PARAM_INDEX 2 + +#define BLOG_MAX_LEN_TBLSZ 8 +#define BLOG_MAX_DSCP_TBLSZ 64 +#define BLOG_MAX_TOS_TBLSZ 256 + +#define BLOG_LEN_PARAM_NUM 4 +#define BLOG_MAX_PARAM_NUM 4 + +#define BLOG_MIN_LEN_INDEX 0 +#define BLOG_MAX_LEN_INDEX 1 +#define BLOG_ORIGINAL_MARK_INDEX 2 +#define BLOG_TARGET_MARK_INDEX 3 + +#define BLOG_MATCH_DSCP_INDEX 0 +#define BLOG_TARGET_DSCP_INDEX 1 + +#define BLOG_MATCH_TOS_INDEX 0 +#define BLOG_TARGET_TOS_INDEX 1 + +#define BLOG_INVALID_UINT8 ((uint8_t)(-1)) +#define BLOG_INVALID_UINT16 ((uint16_t)(-1)) +#define BLOG_INVALID_UINT32 ((uint32_t)(-1)) + +extern int blog_set_ack_tbl(uint32_t val[]); +extern int blog_clr_ack_tbl(void); +extern int blog_set_len_tbl(uint32_t val[]); +extern int blog_clr_len_tbl(void); +extern int blog_set_dscp_tbl(uint8_t idx, uint8_t val); +extern int blog_clr_dscp_tbl(void); +extern int blog_set_tos_tbl(uint8_t idx, uint8_t val); +extern int blog_clr_tos_tbl(void); +extern int blog_pre_mod_hook(Blog_t *blog_p, void *nbuff_p); +extern int blog_post_mod_hook(Blog_t *blog_p, void *nbuff_p); + +#if defined(CONFIG_NET_IPGRE) || defined(CONFIG_NET_IPGRE_MODULE) +#define BLOG_GRE_RCV_NOT_GRE 2 +#define BLOG_GRE_RCV_NO_SEQNO 1 +#define BLOG_GRE_RCV_IN_SEQ 0 +#define BLOG_GRE_RCV_NO_TUNNEL -1 +#define BLOG_GRE_RCV_FLAGS_MISSMATCH -2 +#define BLOG_GRE_RCV_CHKSUM_ERR -3 +#define BLOG_GRE_RCV_OOS_LT -4 +#define BLOG_GRE_RCV_OOS_GT -5 + +extern int blog_gre_rcv( struct fkbuff *fkb_p, void * dev_p, uint32_t h_proto, + void **tunl_pp, uint32_t *pkt_seqno_p); +extern void blog_gre_xmit( struct sk_buff *skb_p, uint32_t h_proto ); +#endif + +#if defined(CONFIG_ACCEL_PPTP) +#define BLOG_PPTP_ENCRYPTED 3 +#define BLOG_PPTP_RCV_NOT_PPTP 2 +#define BLOG_PPTP_RCV_NO_SEQNO 1 +#define BLOG_PPTP_RCV_IN_SEQ 0 +#define BLOG_PPTP_RCV_NO_TUNNEL -1 +#define BLOG_PPTP_RCV_FLAGS_MISSMATCH -2 +#define BLOG_PPTP_RCV_CHKSUM_ERR -3 +#define BLOG_PPTP_RCV_OOS_LT -4 +#define BLOG_PPTP_RCV_OOS_GT -5 +extern int blog_pptp_rcv( struct fkbuff *fkb_p, uint32_t h_proto, + uint32_t *rcv_pktSeq); +extern void blog_pptp_xmit( struct sk_buff *skb_p, uint32_t h_proto ); +#endif + +#define BLOG_L2TP_RCV_TUNNEL_FOUND 1 +#define BLOG_L2TP_RCV_NO_TUNNEL 0 + +#define BLOG_INCLUDE_VIRTUAL_DEVS 1 + +void blog_get_dev_stats(void *dev_p, void *bStats_p); +void blog_clr_dev_stats(void *dev_p); +void blog_get_dev_running_stats(void *dev_p, void * const bStats_p); +void blog_get_dev_running_stats_wlan(void *dev_p, void * const bStats_p); /* Remove once WLAN falls in place */ +void blog_add_dev_accelerated_stats(void *dev_p, void *stats64_p); + +typedef struct { + wait_queue_head_t wqh; + unsigned long work_avail; +#define BLOG_WORK_AVAIL (1<<0) +} wq_info_t; + +#define BLOG_WAKEUP_WORKER_THREAD(x, mask) \ +do { \ + if ( !((x)->work_avail & mask) ) { \ + (x)->work_avail |= mask; \ + wake_up_interruptible(&((x)->wqh)); \ + } \ +} while (0) + + +#endif /* defined(__BLOG_H_INCLUDED__) */ + +#endif /* CONFIG_BCM_KF_BLOG */ diff -urN linux-4.1.52-orig/include/linux/blog_net.h linux-4.1.52/include/linux/blog_net.h --- linux-4.1.52-orig/include/linux/blog_net.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/blog_net.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,827 @@ +#if defined(CONFIG_BCM_KF_BLOG) +#ifndef __BLOG_NET_H_INCLUDED__ +#define __BLOG_NET_H_INCLUDED__ + +/* +<:copyright-BRCM:2003:DUAL/GPL:standard + + Copyright (c) 2003 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + +/* + ******************************************************************************* + * + * File Name : blog_net.h + * + * Description: + * + * Global definitions and declaration of Protocol Headers independent of OS as + * per IEEE and RFC standards. Inlined utilities for header access. + * + * CAUTION: All protocol header structures are declared for Big Endian access + * and are not compatible for a Little Endian machine. + * + * CAUTION: It is also assumed that the Headers are AT LEAST 16bit aligned. + * + ******************************************************************************* + */ + +#if defined(CONFIG_CPU_BIG_ENDIAN) +#define BE_DECL(declarations) declarations +#define BE_CODE(statements) do { statements } while (0) +#define LE_DECL(declarations) +#define LE_CODE(statements) NULL_STMT +#elif defined(CONFIG_CPU_LITTLE_ENDIAN) || defined(CONFIG_ARM) +#define BE_DECL(declarations) +#define BE_CODE(statements) NULL_STMT +#define LE_DECL(declarations) declarations +#define LE_CODE(statements) do { statements } while (0) +#else +#error "Compile: fix endianess in platform.h" +#endif + + +/*TODO rename these PHY types to BCM_XXXPHY */ +#undef BLOG_DECL +#define BLOG_DECL(x) x, +/* + *------------------------------------------------------------------------------ + * Denotes the type of physical interface and the presence of a preamble. + *------------------------------------------------------------------------------ + */ +typedef enum { + BLOG_DECL(BLOG_NOPHY) /* index 0 placeholder for non-bloggable interface */ + BLOG_DECL(BLOG_XTMPHY) + BLOG_DECL(BLOG_ENETPHY) + BLOG_DECL(BLOG_GPONPHY) + BLOG_DECL(BLOG_EPONPHY) + BLOG_DECL(BLOG_USBPHY) + BLOG_DECL(BLOG_WLANPHY) + BLOG_DECL(BLOG_MOCAPHY) + BLOG_DECL(BLOG_EXTRA1PHY) + BLOG_DECL(BLOG_LTEPHY) + BLOG_DECL(BLOG_SIDPHY) + BLOG_DECL(BLOG_TCP4_LOCALPHY) + BLOG_DECL(BLOG_SPU_DS) + BLOG_DECL(BLOG_SPU_US) + BLOG_DECL(BLOG_NETXLPHY) + BLOG_DECL(BLOG_SPDTST) + BLOG_DECL(BLOG_MAXPHY) +} BlogPhy_t; + +/* + *------------------------------------------------------------------------------ + * RFC 2684 header logging. + * CAUTION: 0'th enum corresponds to either header was stripped or zero length + * header. VC_MUX_PPPOA and VC_MUX_IPOA have 0 length RFC2684 header. + * PTM does not have an rfc2684 header. + *------------------------------------------------------------------------------ + */ +typedef enum { + BLOG_DECL(RFC2684_NONE) /* */ + BLOG_DECL(LLC_SNAP_ETHERNET) /* AA AA 03 00 80 C2 00 07 00 00 */ + BLOG_DECL(LLC_SNAP_ROUTE_IP) /* AA AA 03 00 00 00 08 00 */ + BLOG_DECL(LLC_ENCAPS_PPP) /* FE FE 03 CF */ + BLOG_DECL(VC_MUX_ETHERNET) /* 00 00 */ + BLOG_DECL(VC_MUX_IPOA) /* */ + BLOG_DECL(VC_MUX_PPPOA) /* */ + BLOG_DECL(PTM) /* */ + BLOG_DECL(RFC2684_MAX) +} Rfc2684_t; + +/*----- LinkType: First header type ------------------------------------------*/ +/* Used by network drivers to determine the Layer 1 encapsulation or LinkType */ +typedef enum { + BLOG_DECL(TYPE_ETH) /* LAN: ETH, WAN: EoA, MER, PPPoE */ + BLOG_DECL(TYPE_PPP) /* WAN: PPPoA */ + BLOG_DECL(TYPE_IP) /* WAN: IPoA */ +} BlogLinkType_t; + +#define BLOG_SET_PHYHDR(a, b) ( (((a) & 0xf) << 4) | ((b) & 0xf) ) +#define BLOG_GET_PHYTYPE(a) ( (a) & 0xf ) +#define BLOG_GET_PHYLEN(a) ( (a) >> 4 ) + +#define BLOG_PHYHDR_MASK 0xff +#define BLOG_SET_HW_ACT(a) ( ((a) & 0xf) << 8 ) +#define BLOG_GET_HW_ACT(a) ( (a) >> 8 ) + +/*----- ETH_TYPE: Standard well-defined Ethernet Encapsulations --------------*/ +#define BLOG_ETH_P_ETH_BRIDGING 0x6558 /* Transparent Ethernet bridging */ +#define BLOG_ETH_P_IPV4 0x0800 /* IPv4 in Ethernet */ +#define BLOG_ETH_P_ARP 0x0806 /* Address Resolution packet */ +#define BLOG_ETH_P_RARP 0x8035 /* Reverse ARP */ +#define BLOG_ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */ +#define BLOG_ETH_P_8021AD 0x88A8 /* VLAN Stacking 802.1ad */ +#define BLOG_ETH_P_IPV6 0x86DD /* Internet Protocol Version 6 */ +#define BLOG_ETH_P_MPLS_UC 0x8847 /* MPLS - Unicast */ +#define BLOG_ETH_P_MPLS_MC 0x8848 /* MPLS - Multicast */ +#define BLOG_ETH_P_ATMMPOA 0x884c /* MultiProtocol Over ATM */ +#define BLOG_ETH_P_PPP_DIS 0x8863 /* PPPoE Discovery */ +#define BLOG_ETH_P_PPP_SES 0x8864 /* PPPoE Session */ +#define BLOG_ETH_JUMBO_FRAME 0x8870 /* Jumbo frame indicator */ +#define BLOG_ETH_P_BRCM6TAG 0x8874 /* BRCM Switch Hdr : 6 byte */ +#define BLOG_ETH_P_BRCM4TAG 0x888A /* BRCM Switch Hdr : 4 byte */ +#define BLOG_ETH_P_PAUSE 0x8808 /* IEEE Pause frames. 802.3 31B */ +#define BLOG_ETH_P_SLOW 0x8809 /* Slow Protocol. See 802.3ad 43B */ +#define BLOG_ETH_P_8021AG 0x8902 /* 802.1ag Connectivity FaultMgmt */ + /* ITU-T recomm Y.1731 (OAM) */ +#define BLOG_ETH_FCOE 0x8906 /* Fibre Channel over Ethernet */ +#define BLOG_ETH_FCOE_INIT 0x8914 /* FCoE Initialization Protocol */ +#define BLOG_ETH_QINQ1 0x9100 /* 802.1Q in Q, alternate 1 */ +#define BLOG_ETH_QINQ2 0x9200 /* 802.1Q in Q, alternate 2 */ + +/*----- PPP_TYPE: Standard well-defined PPP Encapsulations -------------------*/ +#define BLOG_PPP_IPV4 0x0021 /* IPv4 in PPP */ +#define BLOG_PPP_IPCP 0x8021 /* IP Control Protocol */ +#define BLOG_PPP_LCP 0xC021 /* Link Control Protocol */ +#define BLOG_PPP_MP 0x003D /* Multilink protocol */ +#define BLOG_PPP_IPV6 0x0057 /* IPv6 in PPP */ +#define BLOG_PPP_IPV6CP 0x8057 /* IPv6 Control Protocol */ +#define BLOG_PPP_MPLSCP 0x80FD /* MPLS Control Protocol??? */ +#define BLOG_PPP_MPLS_UC 0x0281 /* MPLS - Unicast */ +#define BLOG_PPP_MPLS_MC 0x0283 /* MPLS - Multicast */ + +#define BLOG_GRE_PPP 0x880B /* PPTP: PPP in GRE Tunnel */ + +/*----- IPPROTO: Standard well-defined IP Encapsulations ---------------------*/ +#define BLOG_IPPROTO_HOPOPTV6 0 /* IPv6 ext: Hop-by-Hop Option Header */ +#define BLOG_IPPROTO_ICMP 1 /* Internet Control Message Protocol */ +#define BLOG_IPPROTO_IGMP 2 /* Internet Group Management Protocol */ +#define BLOG_IPPROTO_IPIP 4 /* IPIP tunnels e.g. 4in6 */ +#define BLOG_IPPROTO_TCP 6 /* Transmission Control Protocol */ +#define BLOG_IPPROTO_EGP 8 /* Exterior Gateway Protocol */ +#define BLOG_IPPROTO_UDP 17 /* User Datagram Protocol */ +#define BLOG_IPPROTO_IPV6 41 /* IPv6-in-IPv4 tunnelling */ +#define BLOG_IPPROTO_ROUTING 43 /* IPv6 ext: Routing Header */ +#define BLOG_IPPROTO_FRAGMENT 44 /* IPv6 ext: Fragmentation Header */ +#define BLOG_IPPROTO_RSVP 46 /* RSVP Protocol */ +#define BLOG_IPPROTO_GRE 47 /* Cisco GRE tunnels (rfc 1701,1702) */ +#define BLOG_IPPROTO_ESP 50 /* Encapsulation Security Payload */ +#define BLOG_IPPROTO_AH 51 /* Authentication Header Protocol */ +#define BLOG_IPPROTO_ICMPV6 58 /* IPv6 ext: ICMPv6 Header */ +#define BLOG_IPPROTO_NONE 59 /* IPv6 ext: NONE */ +#define BLOG_IPPROTO_DSTOPTS 60 /* IPv6 ext: Destination Options Hdr */ +#define BLOG_IPPROTO_ANY_HOST_INTERNAL_PROTO 61 /* Any host internel proto */ +#define BLOG_IPPROTO_MTP 92 /* IPv6 ext: Mcast Transport Protocol */ +#define BLOG_IPPROTO_ENCAP 98 /* IPv6 ext: Encapsulation Header */ +#define BLOG_IPPROTO_PIM 103 /* Protocol Independent Multicast */ +#define BLOG_IPPROTO_COMP 108 /* Compression Header Protocol */ +#define BLOG_IPPROTO_ANY_0HOP 114 /* Any Zero HOP */ +#define BLOG_IPPROTO_SCTP 132 /* Stream Control Transport Protocol */ +#define BLOG_IPPROTO_UDPLITE 136 /* UDP-Lite (RFC 3828) */ + +#define BLOG_IPPROTO_UNASSIGN_B 141 /* Begin of unassigned range */ +#define BLOG_IPPROTO_UNASSIGN_E 252 /* End of unassigned range */ +#define BLOG_IPPROTO_RSVD_EXPT1 253 /* Reserved for experimentation */ +#define BLOG_IPPROTO_RSVD_EXPT2 254 /* Reserved for experimentation */ +#define BLOG_IPPROTO_RAW 255 /* Raw IP Packets */ + + +/* IGRS/UPnP using Simple Service Discovery Protocol SSDP over HTTPMU */ +#define BLOG_HTTP_MCAST_UDP_DSTPORT 1900 + +/* Known L4 Ports */ +#define BLOG_DNS_SERVER_PORT 53 +#define BLOG_DHCP_SERVER_PORT 67 +#define BLOG_DHCP_CLIENT_PORT 68 + +/*----- Ethernet IEEE 802.3 definitions ------------------------------------- */ +#define BLOG_LLC_SAP_SNAP (0xAA) +#define BLOG_LLC_SNAP_8023_DSAP (BLOG_LLC_SAP_SNAP) +#define BLOG_LLC_SNAP_8023_SSAP (BLOG_LLC_SAP_SNAP) +#define BLOG_LLC_SNAP_8023_Ctrl (0x3) +#define BLOG_LLC_SNAP_8023_LEN 6 + +#define BLOG_ETH_ADDR_LEN 6 +#define BLOG_ETH_TYPE_LEN sizeof(uint16_t) +#define BLOG_ETH_HDR_LEN ((BLOG_ETH_ADDR_LEN * 2) + BLOG_ETH_TYPE_LEN) +#define BLOG_ETH_TYPE_MIN 1536 + +#define BLOG_ETH_MIN_LEN 60 +#define BLOG_ETH_FCS_LEN 4 +#define BLOG_ETH_MTU_LEN 0xFFFF /* Initial minMtu value */ + +#define BLOG_ETH_ADDR_FMT "[%02X:%02X:%02X:%02X:%02X:%02X]" +#define BLOG_ETH_ADDR(e) e.u8[0],e.u8[1],e.u8[2],e.u8[3],e.u8[4],e.u8[5] + +typedef union BlogEthAddr { + uint8_t u8[BLOG_ETH_ADDR_LEN]; + uint16_t u16[BLOG_ETH_ADDR_LEN/sizeof(uint16_t)]; +} BlogEthAddr_t; + +typedef struct BlogEthHdr { + union { + uint8_t u8[BLOG_ETH_HDR_LEN]; + uint16_t u16[BLOG_ETH_HDR_LEN/sizeof(uint16_t)]; + struct { + BlogEthAddr_t macDa; + BlogEthAddr_t macSa; + /* + * CAUTION: Position of ethType field of an Ethernet header depends on + * the presence and the number of VLAN Tags + * E.g. A single tagged Ethernet frame will have the ethType at offset 16. + */ + uint16_t ethType; /* or length */ + }; + }; +} BlogEthHdr_t; + +/* 16bit aligned access MAC Address functgions */ +static inline int blog_is_zero_eth_addr(uint8_t * addr_p) +{ + uint16_t * u16_p = (uint16_t *)addr_p; /* assert u16_p is 16bit aligned */ + return ( (u16_p[0] & u16_p[1] & u16_p[2]) == 0x0000 ); +} + +static inline int blog_is_bcast_eth_addr(uint8_t * addr_p) +{ + uint16_t * u16_p = (uint16_t *)addr_p; /* assert u16_p is 16bit aligned */ + return ( (u16_p[0] & u16_p[1] & u16_p[2]) == 0xFFFF ); +} + +/* Caution an IP mcast over PPPoE need not have a mcast MacDA */ +static inline int blog_is_mcast_eth_addr(uint8_t * addr_p) +{ +#if 1 + return *(addr_p+0) & 0x01; +#else /* Multicast (e.g. over PPPoE) may use unicast MacDA */ + uint16_t * u16_p = (uint16_t *)addr_p; /* assert u16_p is 16bit aligned */ + if ( ((u16_p[0] == 0x0100) /* IPv4: 01:00:5E:`1b0 */ + && (*(addr_p+2) == 0x5e) && ((*(addr_p+3) & 0x80) == 0) ) + || ( u16_p[0] == 0x3333) /* IPv6: 33:33 */ + ) + return 1; + else + return 0; +#endif +} + +static inline int blog_cmp_eth_addr(uint8_t * addr1_p, uint8_t * addr2_p) +{ + uint16_t *a1 = (uint16_t *)addr1_p; + uint16_t *a2 = (uint16_t *)addr2_p; + return ( ((a1[0] ^ a2[0]) | (a1[1] ^ a2[1]) | (a1[2] ^ a2[2])) != 0 ); +} + + +/*----- 6Byte Brcm6Hdr layout for 5397/98 Switch Management Port Tag ---------*/ +#define BLOG_BRCM6_HDR_LEN 6 + +typedef struct BlogBrcm6Hdr { + union { + uint8_t u8[BLOG_BRCM6_HDR_LEN]; + uint16_t u16[BLOG_BRCM6_HDR_LEN/sizeof(uint16_t)]; + /* + * egress: opcode:3, fbcount:14, rsvd:11, srcPortId:4 + * ingress_port opcode:3, rsvd:25, dstPortId:4 + * ingress_map opcode:3, rsvd:20, fwdMap:9 + */ + }; +} BlogBrcm6Hdr_t; + + +/*----- 4Byte Brcm4Hdr layout for 53115 Switch Management Port Tag -----------*/ +#define BLOG_BRCM4_HDR_LEN 4 + +typedef struct BlogBrcm4Hdr { + union { + uint8_t u8[BLOG_BRCM4_HDR_LEN]; + uint16_t u16[BLOG_BRCM4_HDR_LEN/sizeof(uint16_t)]; + /* + * egress opcode:3, rsvd:13, rsvd2:2, + * flooding:1, snooping:1, protocol:1, switching:1 + * learning:1, mirroring:1, tclass:3, srcpid:5 + * ingress opcode:3, tclass:3, + * tagenforce:2, rsvd:1, dstmap:23 + */ + }; +} BlogBrcm4Hdr_t; + +/*----- Composite Ethernet with BRCM Tag -------------------------------------*/ + +#define BLOG_ETHBRCM6_HDR_LEN (BLOG_ETH_HDR_LEN + BLOG_BRCM6_HDR_LEN) +#define BLOG_ETHBRCM4_HDR_LEN (BLOG_ETH_HDR_LEN + BLOG_BRCM4_HDR_LEN) + +typedef struct BlogEthBrcm6Hdr { + union { + uint8_t u8[BLOG_ETHBRCM6_HDR_LEN]; + uint16_t u16[BLOG_ETHBRCM6_HDR_LEN/sizeof(uint16_t)]; + struct { + BlogEthAddr_t macDa; + BlogEthAddr_t macSa; + BlogBrcm6Hdr_t brcm6; + uint16_t ethType; + }; + }; +} BlogEthBrcm6Hdr_t; + +typedef struct BlogEthBrcm4Hdr { + union { + uint8_t u8[BLOG_ETHBRCM4_HDR_LEN]; + uint16_t u16[BLOG_ETHBRCM4_HDR_LEN/sizeof(uint16_t)]; + struct { + BlogEthAddr_t macDa; + BlogEthAddr_t macSa; + BlogBrcm4Hdr_t brcm4; + uint16_t ethType; + }; + }; +} BlogEthBrcm4Hdr_t; + + +/*----- Vlan IEEE 802.1Q definitions -----------------------------------------*/ +#define BLOG_VLAN_HDR_LEN 4 +#define BLOG_VLAN_HDR_FMT "[0x%08X] tpid<0x%04X> tci<0x%04X> "\ + "pbit<%u> dei<%u> vid<0x%03X>" +#define BLOG_VLAN_HDR(v) v.u32[0], v.tpid, v.tci.u16[0], \ + v.tci.pbits, v.tci.dei, v.tci.vid + +typedef struct BlogVlanTci { + union { + uint8_t u8[sizeof(uint16_t)]; + uint16_t u16[1]; + struct { + BE_DECL( uint16_t pbits:3; uint16_t dei:1; uint16_t vid:12; ) + LE_DECL( uint16_t vid:12; uint16_t dei:1; uint16_t pbits:3; ) + }; + }; +} BlogVlanTci_t; + +typedef struct BlogVlanHdr { + union { + uint8_t u8[BLOG_VLAN_HDR_LEN]; + uint16_t u16[BLOG_VLAN_HDR_LEN/sizeof(uint16_t)]; + uint32_t u32[BLOG_VLAN_HDR_LEN/sizeof(uint32_t)]; + struct { + uint16_t tpid; BlogVlanTci_t tci; /* u8[ 88, A8, EA, AA ] */ + }; + }; +} BlogVlanHdr_t; + + +/*----- PPPoE + PPP Header layout. PPPoE RFC 2516, PPP RFC 1661 --------------*/ +#define BLOG_PPPOE_HDR_LEN 8 /* Including PPP Header "PPP Type" */ +#define BLOG_PPP_HDR_LEN sizeof(uint16_t) +#define BLOG_PPPOE_HDR_FMT "[0x%08X 0x%08X] ver<%u> type<%u> code<0x%02X>"\ + " sId<0x%04X> len<%u> pppType<0x%04X>" +#define BLOG_PPPOE_HDR(p) p.u32[0], p.u32[1], p.ver, p.type, p.code,\ + p.sId, p.len, p.pppType + +typedef uint16_t BlogPppHdr_t; + +typedef struct BlogPppoeHdr { /* includes 2 byte PPP Type */ + union { + uint8_t u8[BLOG_PPPOE_HDR_LEN]; + uint16_t u16[BLOG_PPPOE_HDR_LEN/sizeof(uint16_t)]; + uint32_t u32[BLOG_PPPOE_HDR_LEN/sizeof(uint32_t)]; + struct { + BE_DECL( uint16_t ver:4; uint16_t type:4; uint16_t code:8; ) + LE_DECL( uint16_t code:8; uint16_t type:4; uint16_t ver:4; ) + uint16_t sId; uint16_t len; BlogPppHdr_t pppType; + }; + }; +} BlogPppoeHdr_t; + + +/*----- Multi Protocol Label Switiching Architecture: RFC 3031 ----------------- + * + * 20b-label, 3b-tos, 1b-Stack, 8b-TTL + * StackBit==1? if label==0 then next is IPV4, if label==1 then next is IPV6 + *------------------------------------------------------------------------------ + */ +#define BLOG_MPLS_HDR_LEN 4 + +typedef struct BlogMplsHdr { + union { + uint8_t u8[BLOG_MPLS_HDR_LEN]; + uint16_t u16[BLOG_MPLS_HDR_LEN/sizeof(uint16_t)]; + uint32_t u32[BLOG_MPLS_HDR_LEN/sizeof(uint32_t)]; + struct { + BE_DECL( uint32_t label:20; uint32_t cos:3; uint32_t sbit:1; uint32_t ttl:8; ) + LE_DECL( uint32_t ttl:8; uint32_t sbit:1; uint32_t cos:3; uint32_t label:20; ) + }; + }; +} BlogMplsHdr_t; + + +/*----- IPv4: RFC 791 definitions --------------------------------------------*/ +#define BLOG_IPV4_HDR_LEN 20 /* Not including IP Options */ +#define BLOG_IPV4_ADDR_LEN 4 +#define BLOG_IPV4_HDR_FMT "[0x%08X] ver<%u> ihl<%u> tos<0x%02X> len<%u> "\ + "[0x%08X] id<%u> df<%u> mf<%u> "\ + "fragOffset<0x%04X> [0x%08X] "\ + "ttl<%u> proto<%u> chkSum<0x%04X> "\ +#define BLOG_IPV4_HDR(i) i.u32[0], i.ver, i.ihl, i.tos, i.len, \ + i.u32[1], i.id, i.df, i.mf, i.fragOffset,\ + i.u32[2], i.ttl, i.proto, i.chkSum, +#define BLOG_IPTOS2DSCP(tos) ((tos) >> 2) +#define BLOG_IPDSCP2TOS(dscp) ((dscp) << 2) + +#define BLOG_IPV4_ADDR_FMT "<%03u.%03u.%03u.%03u>" +#define BLOG_IPV4_ADDR_PORT_FMT "<%03u.%03u.%03u.%03u:%u>" +#define BLOG_IPV4_ADDR(ip) ((uint8_t*)&ip)[0], ((uint8_t*)&ip)[1], \ + ((uint8_t*)&ip)[2], ((uint8_t*)&ip)[3] + +#if defined(CONFIG_CPU_BIG_ENDIAN) +#define BLOG_IPV4_ADDR_HOST(ip) ((uint8_t*)&ip)[0], ((uint8_t*)&ip)[1], \ + ((uint8_t*)&ip)[2], ((uint8_t*)&ip)[3] +#elif defined(CONFIG_CPU_LITTLE_ENDIAN) || defined(CONFIG_ARM) +#define BLOG_IPV4_ADDR_HOST(ip) ((uint8_t*)&ip)[3], ((uint8_t*)&ip)[2], \ + ((uint8_t*)&ip)[1], ((uint8_t*)&ip)[0] +#endif + +typedef union BlogIpv4Addr { + uint8_t u8[BLOG_IPV4_ADDR_LEN]; + uint16_t u16[BLOG_IPV4_ADDR_LEN/sizeof(uint16_t)]; + uint32_t u32[BLOG_IPV4_ADDR_LEN/sizeof(uint32_t)]; +} BlogIpv4Addr_t; + +#define BLOG_IP_FLAG_CE 0x8000 /* Congestion */ +#define BLOG_IP_FLAG_DF 0x4000 /* Do Not Fragment */ +#define BLOG_IP_FLAG_MF 0x2000 /* More Fragment */ +#define BLOG_IP_FRAG_OFFSET 0x1FFF + +typedef struct BlogIpv4Hdr { + union { + uint8_t u8[BLOG_IPV4_HDR_LEN]; + uint16_t u16[BLOG_IPV4_HDR_LEN/sizeof(uint16_t)]; + uint32_t u32[BLOG_IPV4_HDR_LEN/sizeof(uint32_t)]; + struct { + union { + struct { + BE_DECL( uint8_t ver:4; uint8_t ihl:4; ) + LE_DECL( uint8_t ihl:4; uint8_t ver:4; ) + }; + uint8_t ver_ihl; + }; + uint8_t tos; uint16_t len; + uint16_t id; + union { + uint16_t flagsFrag; + struct { + BE_DECL( uint16_t cong:1; uint16_t df:1; + uint16_t moreFrag:1; uint16_t fragOffset:13; ) + LE_DECL( uint16_t fragOffset:13; uint16_t moreFrag:1; + uint16_t df:1; uint16_t cong:1; ) + }; + }; + uint8_t ttl; uint8_t proto; uint16_t chkSum; + BlogIpv4Addr_t sAddr; + BlogIpv4Addr_t dAddr; + }; + }; +} BlogIpv4Hdr_t; + + +/*----- IPv6: RFC 2460 RFC 3513 definitions ----------------------------------*/ +/* + * Well know IPv6 Address prefixes + * Multicast: FFXX:: + * Site local: FEC0:: + * Link Local: FE80:: + * Ucast 6to4: 2002:: + */ +#define BLOG_IPV6_HDR_LEN 40 +#define BLOG_IPV6_ADDR_LEN 16 + +#define BLOG_IPV6_ADDR_FMT "<%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x>" +#define BLOG_IPV6_ADDR_PORT_FMT "<%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%u>" +#define BLOG_IPV6_ADDR(ip) \ + ntohs(((uint16_t*)&ip)[0]), ntohs(((uint16_t*)&ip)[1]), \ + ntohs(((uint16_t*)&ip)[2]), ntohs(((uint16_t*)&ip)[3]), \ + ntohs(((uint16_t*)&ip)[4]), ntohs(((uint16_t*)&ip)[5]), \ + ntohs(((uint16_t*)&ip)[6]), ntohs(((uint16_t*)&ip)[7]) + +typedef union BlogIpv6Addr { + uint8_t u8[BLOG_IPV6_ADDR_LEN]; + uint16_t u16[BLOG_IPV6_ADDR_LEN/sizeof(uint16_t)]; + uint32_t u32[BLOG_IPV6_ADDR_LEN/sizeof(uint32_t)]; +} BlogIpv6Addr_t; + +typedef struct BlogIpv6Hdr { + union { + uint8_t u8[BLOG_IPV6_HDR_LEN]; + uint16_t u16[BLOG_IPV6_HDR_LEN/sizeof(uint16_t)]; + uint32_t u32[BLOG_IPV6_HDR_LEN/sizeof(uint32_t)]; + struct { + /* ver_tos bits -> ver 4: tos 8: flowlblHi 4 + using bit field results in unaligned access */ + uint16_t ver_tos; uint16_t flowLblLo; + uint16_t len; uint8_t nextHdr; uint8_t hopLmt; + BlogIpv6Addr_t sAddr; + BlogIpv6Addr_t dAddr; + }; + }; +} BlogIpv6Hdr_t; + +#define BLOG_IPV6EXT_HDR_LEN 8 /* multiple of 8 octets */ +typedef struct BlogIpv6ExtHdr { + union { + uint8_t u8[BLOG_IPV6EXT_HDR_LEN]; + uint16_t u16[BLOG_IPV6EXT_HDR_LEN/sizeof(uint16_t)]; + uint32_t u32[BLOG_IPV6EXT_HDR_LEN/sizeof(uint32_t)]; + struct { + uint8_t nextHdr; uint8_t hdrLen; uint16_t data16; + uint32_t data32; + }; + }; +} BlogIpv6ExtHdr_t; + + +/*----- Transmission Control Protocol: RFC 793 definitions -------------------*/ + +#define BLOG_TCP_HDR_LEN 20 + +#define TCPH_DOFF(t) (((htons(t->offFlags.u16)) >> 12) & 0xF) +#define TCPH_CWR(t) (((htons(t->offFlags.u16)) >> 7) & 0x1) +#define TCPH_ECE(t) (((htons(t->offFlags.u16)) >> 6) & 0x1) +#define TCPH_URG(t) (((htons(t->offFlags.u16)) >> 5) & 0x1) +#define TCPH_ACK(t) (((htons(t->offFlags.u16)) >> 4) & 0x1) +#define TCPH_PSH(t) (((htons(t->offFlags.u16)) >> 3) & 0x1) +#define TCPH_RST(t) (((htons(t->offFlags.u16)) >> 2) & 0x1) +#define TCPH_SYN(t) (((htons(t->offFlags.u16)) >> 1) & 0x1) +#define TCPH_FIN(t) (((htons(t->offFlags.u16)) >> 0) & 0x1) + +typedef struct BlogTcpOffFlags { + union { + uint16_t u16; + struct { uint8_t off; uint8_t flags; }; + struct { + BE_DECL( + uint16_t dOff: 4; + uint16_t res1: 4; + uint16_t cwr : 1; + uint16_t ece : 1; + uint16_t urg : 1; + uint16_t ack : 1; + uint16_t psh : 1; + uint16_t rst : 1; + uint16_t syn : 1; + uint16_t fin : 1; + ) + LE_DECL( + uint16_t fin : 1; + uint16_t syn : 1; + uint16_t rst : 1; + uint16_t psh : 1; + uint16_t ack : 1; + uint16_t urg : 1; + uint16_t ece : 1; + uint16_t cwr : 1; + uint16_t res1: 4; + uint16_t dOff: 4; + ) + }; + }; +} BlogTcpOffFlags_t; + +typedef struct BlogTcpHdr { + union { + uint8_t u8[BLOG_TCP_HDR_LEN]; + uint16_t u16[BLOG_TCP_HDR_LEN/sizeof(uint16_t)]; + uint32_t u32[BLOG_TCP_HDR_LEN/sizeof(uint32_t)]; + struct { + uint16_t sPort; uint16_t dPort; + uint32_t seq; + uint32_t ackSeq; + BlogTcpOffFlags_t offFlags; uint16_t window; + uint16_t chkSum; uint16_t urgPtr; + }; + }; +} BlogTcpHdr_t; + + +/*----- User Datagram Protocol: RFC 768 definitions --------------------------*/ +#define BLOG_UDP_HDR_LEN 8 + +typedef struct BlogUdpHdr { + union { + uint8_t u8[BLOG_UDP_HDR_LEN]; + uint16_t u16[BLOG_UDP_HDR_LEN/sizeof(uint16_t)]; + uint32_t u32[BLOG_UDP_HDR_LEN/sizeof(uint32_t)]; + struct { + uint16_t sPort; uint16_t dPort; + uint16_t len; uint16_t chkSum; + }; + }; +} BlogUdpHdr_t; + + +/*----- L2TP: RFC 2661 definitions -------------------------------------------*/ +#define BLOG_L2TP_HDR_LEN 8 + +typedef struct BlogL2tpIeFlagsVer { + union { + uint16_t u16; + struct { + BE_DECL( + uint16_t type : 1; + uint16_t lenIe : 1; + uint16_t rsvd2 : 2; + uint16_t seqIe : 1; + uint16_t rsvd1 : 1; + uint16_t offIe : 1; + uint16_t prio : 1; + uint16_t rsvd4 : 4; + uint16_t ver : 4; + ) + LE_DECL( + uint16_t ver : 4; + uint16_t rsvd4 : 4; + uint16_t prio : 1; + uint16_t offIe : 1; + uint16_t rsvd1 : 1; + uint16_t seqIe : 1; + uint16_t rsvd2 : 2; + uint16_t lenIe : 1; + uint16_t type : 1; + ) + }; + }; +} BlogL2tpIeFlagsVer_t; + +typedef struct BlogL2tpHdr { + union { + uint8_t u8[BLOG_L2TP_HDR_LEN]; + uint16_t u16[BLOG_L2TP_HDR_LEN/sizeof(uint16_t)]; + uint32_t u32[BLOG_L2TP_HDR_LEN/sizeof(uint32_t)]; + struct { + BlogL2tpIeFlagsVer_t ieFlagsVer; uint16_t len; + uint16_t tId; uint16_t sId; + /* uint16_t ns; uint16_t nr; + uint16_t offSz; uint16_t offPad; */ + }; + }; +} BlogL2tpHdr_t; + + +/*----- Generic Routing Encapsulation: RFC 2637, PPTP session, RFC 2784 ------*/ +#define BLOG_GRE_HDR_LEN 8 + +typedef struct BlogGreIeFlagsVer { + union { + uint16_t u16; + struct { + BE_DECL( + uint16_t csumIe : 1; + uint16_t rtgIe : 1; + uint16_t keyIe : 1; + uint16_t seqIe : 1; + uint16_t srcRtIe: 1; + uint16_t recurIe: 3; + uint16_t ackIe : 1; + uint16_t flags : 4; + uint16_t ver : 3; + ) + LE_DECL( + uint16_t ver : 3; + uint16_t flags : 4; + uint16_t ackIe : 1; + uint16_t recurIe: 3; + uint16_t srcRtIe: 1; + uint16_t seqIe : 1; + uint16_t keyIe : 1; + uint16_t rtgIe : 1; + uint16_t csumIe : 1; + ) + }; + }; +} BlogGreIeFlagsVer_t; + +typedef struct BlogGreHdr { + union { + uint8_t u8[BLOG_GRE_HDR_LEN]; + uint16_t u16[BLOG_GRE_HDR_LEN/sizeof(uint16_t)]; + uint32_t u32[BLOG_GRE_HDR_LEN/sizeof(uint32_t)]; + struct { + BlogGreIeFlagsVer_t ieFlagsVer; uint16_t proto; + /* RFC2784 specifies csum instead of len, for GRE ver = 0 */ + /* RFC2637 specifies len, for GRE ver=1 used with PPTP */ + uint16_t len; uint16_t callId; + /* uint32_t seqNum; present if seqIe = 1 */ + /* uint32_t ackNum; present if ackIe = 1 */ + }; + }; +} BlogGreHdr_t; + +/* + *------------------------------------------------------------------------------ + * Assert that headers are properly packed (without using attribute packed) + * + * #include + * #include + * #include "blog_net.h" + * int main() { + * printf("blog_net_audit_hdrs %d\n", blog_net_audit_hdrs() ); + * return blog_net_audit_hdrs(); + * } + *------------------------------------------------------------------------------ + */ +static inline int blog_net_audit_hdrs(void) +{ +#define BLOG_NET_AUDIT(hdrlen,hdrtype) \ + if (hdrlen != sizeof(hdrtype)) \ + return (-1) + + BLOG_NET_AUDIT( BLOG_ETH_ADDR_LEN, BlogEthAddr_t ); + BLOG_NET_AUDIT( BLOG_ETH_HDR_LEN, BlogEthHdr_t ); + BLOG_NET_AUDIT( BLOG_BRCM6_HDR_LEN, BlogBrcm6Hdr_t ); + BLOG_NET_AUDIT( BLOG_BRCM4_HDR_LEN, BlogBrcm4Hdr_t ); + BLOG_NET_AUDIT( BLOG_ETHBRCM6_HDR_LEN, BlogEthBrcm6Hdr_t ); + BLOG_NET_AUDIT( BLOG_ETHBRCM4_HDR_LEN, BlogEthBrcm4Hdr_t ); + BLOG_NET_AUDIT( BLOG_VLAN_HDR_LEN, BlogVlanHdr_t ); + BLOG_NET_AUDIT( BLOG_PPPOE_HDR_LEN, BlogPppoeHdr_t ); + BLOG_NET_AUDIT( BLOG_MPLS_HDR_LEN, BlogMplsHdr_t ); + BLOG_NET_AUDIT( BLOG_IPV4_ADDR_LEN, BlogIpv4Addr_t ); + BLOG_NET_AUDIT( BLOG_IPV4_HDR_LEN, BlogIpv4Hdr_t ); + BLOG_NET_AUDIT( BLOG_IPV6_ADDR_LEN, BlogIpv6Addr_t ); + BLOG_NET_AUDIT( BLOG_IPV6_HDR_LEN, BlogIpv6Hdr_t ); + BLOG_NET_AUDIT( BLOG_TCP_HDR_LEN, BlogTcpHdr_t ); + BLOG_NET_AUDIT( BLOG_UDP_HDR_LEN, BlogUdpHdr_t ); + BLOG_NET_AUDIT( BLOG_L2TP_HDR_LEN, BlogL2tpHdr_t ); + BLOG_NET_AUDIT( BLOG_GRE_HDR_LEN, BlogGreHdr_t ); + + return 0; +} + + +/* + *------------------------------------------------------------------------------ + * Network Utilities : 16bit aligned + *------------------------------------------------------------------------------ + */ +#if defined(CONFIG_CPU_LITTLE_ENDIAN) || defined(CONFIG_ARM) +/* + *------------------------------------------------------------------------------ + * Function : blog_read32_align16 + * Description : Read a 32bit value from a 16 byte aligned data stream + *------------------------------------------------------------------------------ + */ +static inline uint32_t blog_read32_align16( uint16_t * from ) +{ + return (uint32_t)( (from[1] << 16) | from[0] ); +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_write32_align16 + * Description : Write a 32bit value to a 16bit aligned data stream + *------------------------------------------------------------------------------ + */ +static inline void blog_write32_align16( uint16_t * to, uint32_t from ) +{ + to[1] = (uint16_t)htons(from >> 16); + to[0] = (uint16_t)htons(from >> 0); +} + +#elif defined(CONFIG_CPU_BIG_ENDIAN) + +/* + *------------------------------------------------------------------------------ + * Function : blog_read32_align16 + * Description : Read a 32bit value from a 16 byte aligned data stream + *------------------------------------------------------------------------------ + */ +static inline uint32_t blog_read32_align16( uint16_t * from ) +{ + return (uint32_t)( (from[0] << 16) | (from[1]) ); +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_write32_align16 + * Description : Write a 32bit value to a 16bit aligned data stream + *------------------------------------------------------------------------------ + */ +static inline void blog_write32_align16( uint16_t * to, uint32_t from ) +{ + to[0] = (uint16_t)(from >> 16); + to[1] = (uint16_t)(from >> 0); +} +#endif /* defined(CONFIG_CPU_BIG_ENDIAN) */ + +#endif /* defined(__BLOG_NET_H_INCLUDED__) */ +#endif diff -urN linux-4.1.52-orig/include/linux/blog_rule.h linux-4.1.52/include/linux/blog_rule.h --- linux-4.1.52-orig/include/linux/blog_rule.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/blog_rule.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,256 @@ +#if defined(CONFIG_BLOG) +#ifndef __BLOG_RULE_H_INCLUDED__ +#define __BLOG_RULE_H_INCLUDED__ + +/* +* <:copyright-BRCM:2010:DUAL/GPL:standard +* +* Copyright (c) 2010 Broadcom +* All Rights Reserved +* +* Unless you and Broadcom execute a separate written software license +* agreement governing use of this software, this software is licensed +* to you under the terms of the GNU General Public License version 2 +* (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +* with the following added to such license: +* +* As a special exception, the copyright holders of this software give +* you permission to link this software with independent modules, and +* to copy and distribute the resulting executable under terms of your +* choice, provided that you also meet, for each linked independent +* module, the terms and conditions of the license of that module. +* An independent module is a module which is not derived from this +* software. The special exception does not apply to any modifications +* of the software. +* +* Not withstanding the above, under no circumstances may you combine +* this software in any way with any other Broadcom software provided +* under a license other than the GPL, without Broadcom's express prior +* written consent. +* +:> +*/ + +/* + ******************************************************************************* + * + * File Name : blog_rule.h + * + * Description: Blog rules are extensions to a Blog structure that can be used + * to specify additional fiters and modifications. + * + ******************************************************************************* + */ + +#define CC_CONFIG_BLOG_RULE_DEBUG + +#define BLOG_RULE_VERSION "v1.0" + +#define BLOG_RULE_VLAN_TAG_MAX 3 + +#define BLOG_RULE_ACTION_MAX 16 + +#define BLOG_RULE_PBITS_MASK 0xE000 +#define BLOG_RULE_PBITS_SHIFT 13 +#define BLOG_RULE_DEI_MASK 0x1000 +#define BLOG_RULE_DEI_SHIFT 12 +#define BLOG_RULE_VID_MASK 0x0FFF +#define BLOG_RULE_VID_SHIFT 0 + +#define BLOG_RULE_GET_TCI_PBITS(_tci) \ + ( ((_tci) & BLOG_RULE_PBITS_MASK) >> BLOG_RULE_PBITS_SHIFT ) + +#define BLOG_RULE_GET_TCI_DEI(_tci) \ + ( ((_tci) & BLOG_RULE_DEI_MASK) >> BLOG_RULE_DEI_SHIFT ) + +#define BLOG_RULE_GET_TCI_VID(_tci) \ + ( (_tci) & BLOG_RULE_VID_MASK ) + +#define BLOG_RULE_DSCP_IN_TOS_MASK 0xFC +#define BLOG_RULE_DSCP_IN_TOS_SHIFT 2 + +#define BLOG_RULE_IP_PROTO_MASK 0xFF +#define BLOG_RULE_IP_PROTO_SHIFT 0 +#define BLOG_RULE_IP6_NXT_HDR_MASK 0xFF +#define BLOG_RULE_IP6_NXT_HDR_SHIFT 0 + +#define blog_rule_filterInUse(_filter) \ + ({ \ + char *_filter_p = (char *)(&_filter); \ + int _i, _val; \ + for(_i=0; _i +*/ + +#if !defined(_envelope_of) +/* derived from container_of, without "const", for gcc -Wcast-qual compile */ +#define _envelope_of(ptr, type, member) \ +({ \ + typeof(((type *)0)->member) *__mptr = (ptr); \ + (type *)((char *)__mptr - offsetof(type, member)); \ +}) +#endif /* _envelope_of */ + + +typedef struct dll_t dll_t; /* common to wlan bcmutils.h, pktHdr.h */ +typedef struct dll_t { + dll_t * next_p; + dll_t * prev_p; +} Dll_t, * PDll_t; + +#define dll dll_t + +#define DLL_STRUCT_INITIALIZER(struct_name, dll_name) \ + { .next_p = &(struct_name).dll_name, .prev_p = &(struct_name).dll_name } + +#define dll_init(node_p) ((node_p)->next_p = (node_p)->prev_p = (node_p)) + +/* dll macros returing a "dll_t *" */ +#define dll_head_p(list_p) ((list_p)->next_p) +#define dll_tail_p(list_p) ((list_p)->prev_p) + +#define dll_next_p(node_p) ((node_p)->next_p) +#define dll_prev_p(node_p) ((node_p)->prev_p) + +#define dll_empty(list_p) ((list_p)->next_p == (list_p)) +#define dll_end(list_p, node_p) ((list_p) == (node_p)) + +/* inserts the node new_p "after" the node at_p */ +#define dll_insert(new_p, at_p) \ +({ \ + (new_p)->next_p = (at_p)->next_p; \ + (new_p)->prev_p = (at_p); \ + (at_p)->next_p = (new_p); \ + (new_p)->next_p->prev_p = (new_p); \ +}) + +#define dll_append(list_p, node_p) dll_insert((node_p), dll_tail_p(list_p)) +#define dll_prepend(list_p, node_p) dll_insert((node_p), (list_p)) + +/* deletes a node from any list that it "may" be in, if at all. */ +#define dll_delete(node_p) \ +({ \ + (node_p)->prev_p->next_p = (node_p)->next_p; \ + (node_p)->next_p->prev_p = (node_p)->prev_p; \ +}) + +/** + * dll_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + * + * Iterator "pos" may not be moved. + * + * If you need to delete the iterator, then use the below sample. + * dll_t * iter_p, * next_p; + * for (iter_p = dll_head_p(&someList); ! dll_end(&someList, iter_p); + * iter_p = next_p) + * { + * next_p = dll_next_p(iter_p); + * ... use iter_p at will, including removing it from list ... + * } + * + */ +#define dll_for_each(pos, head) \ + for (pos = (head)->next_p; pos != (head); pos = pos->next_p) + +/** + * Take all elements of list A and join them to the tail of list B. + * List A must not be empty and list A will be returned as an empty list. + */ +#define dll_join(listA_p, listB_p) \ +({ \ + dll_t *_listB_p = (listB_p); \ + dll_t *headA_p = dll_head_p(listA_p); \ + dll_t *tailA_p = dll_tail_p(listA_p); \ + dll_t *tailB_p = dll_tail_p(listB_p); \ + /* Link up list B's tail to list A's head */ \ + headA_p->prev_p = tailB_p; \ + tailB_p->next_p = headA_p; \ + /* Make list A's tail to be list B's new tail */ \ + tailA_p->next_p = (listB_p); \ + _listB_p->prev_p = tailA_p; \ + dll_init(listA_p); \ +}) + +#endif /* ! defined(_dll_t_) */ diff -urN linux-4.1.52-orig/include/linux/bug.h linux-4.1.52/include/linux/bug.h --- linux-4.1.52-orig/include/linux/bug.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/bug.h 2020-02-07 01:47:54.000000000 +0800 @@ -81,7 +81,17 @@ * build time, you should use BUILD_BUG to detect if it is * unexpectedly used. */ +#if defined(CONFIG_BCM_KF_BUZZZ) && defined(CONFIG_BUZZZ_FUNC) && defined(BUZZZ_O0) +/** + * When compiled with -O0 to disable inlining, compile time constant evaluation + * (of size parameter) in several inline kernel function will result in a + * switch default case implemented as a BUILD_BUG(). + * E.g. include/asm percpu.h __percpi_##op(), cmpxchg.h __xchg(), ... + */ +#define BUILD_BUG() do { } while (0) +#else #define BUILD_BUG() BUILD_BUG_ON_MSG(1, "BUILD_BUG failed") +#endif #endif /* __CHECKER__ */ diff -urN linux-4.1.52-orig/include/linux/buzzz_kevt.h linux-4.1.52/include/linux/buzzz_kevt.h --- linux-4.1.52-orig/include/linux/buzzz_kevt.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/buzzz_kevt.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,97 @@ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +#ifndef __buzzz_kevt_h_included__ +#define __buzzz_kevt_h_included__ + +#if defined(CONFIG_BUZZZ_KEVT) || defined(CONFIG_BUZZZ_FUNC) +/* + * +---------------------------------------------------------------------------- + * + * BCM BUZZZ ARM Cortex A9 Router Kernel events + * + * $Copyright Open Broadcom Corporation$ + * $Id$ + * + * vim: set ts=4 noet sw=4 tw=80: + * -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- + * + * +---------------------------------------------------------------------------- + */ + +#include + +#undef BUZZZ_KEVT +#define BUZZZ_KEVT(event) BUZZZ_KEVT__ ## event, + +#undef _B_ +#undef _H_ +#undef _N_ +#undef _FAIL_ +#define _B_ "\e[0;34m" +#define _H_ "\e[0;31m;40m" +#define _N_ "\e[0m" +#define _FAIL_ _H_ " === FAILURE ===" _N_ + +/* Expected events : Font = Normal */ +#define BUZZZ_KEVTN(event, format) \ + buzzz_event_reg(BUZZZ_KEVT__## event, "\t\t" format); + +/* Unexpected events: Font = bold2 highlighted */ +#define BUZZZ_KEVTH(event, format) \ + buzzz_event_reg(BUZZZ_KEVT__## event, _H_ "\t\t" format _N_); + +/** + * DO NOT SUBMIT USER EVENTS + * + * For private debug (not for submission), a user event may be added by: + * 1. Add an enum entry to buzzz_rtr_dpid, + * e.g. BUZZZ_KEVT(HELLO_WORLD) + * + * 2. Add an entry to buzzz_dp_init(), + * e.g. BUZZZ_KEVTN(HELLO_WORLD, "hello world at %pS from %pS") + * + * 3. In source code base, insert instrumentation: + * e.g. BUZZZ_DPL3(HELLO_WORLD, 2, + * BUZZZ32(BUZZZ_CUR_IP_), BUZZZ32(BUZZZ_RET_IP_)); + * + * See uapi/linux/buzzz.h, where BUZZZ_DPL tracing level is set to 3, thereby + * enabling all instrumentations BUZZZ_DPL1(), BUZZZ_DPL2() and BUZZZ_DPL3() + * Instrumentation with BUZZZ_DPL4() and BUZZZ_DPL5() are compiled out. + * + * Second parameter to BUZZZ_DPL#() specifies the number of arguments to be + * logged, in the above example, it is 2 arguments (maximum 3 arguments). + * - First argument in example is current instruction address, and + * - Second argument is return address. + * Arguments are 32bit values. [Gets messy on 64b aarch] + * + * Do not forget to invoke, buzzz_dp_init() once ... say in a module init. + */ +typedef +enum buzzz_rtr_dpid +{ + BUZZZ_KEVT__DATAPATH_START = 100, + + BUZZZ_KEVT(SAMPLE) + /* Define user events here */ + +} buzzz_rtr_dpid_t; + + +/* Invoke this once in a datapath module's init */ +static inline int +buzzz_dp_init(void) +{ + BUZZZ_KEVTN(SAMPLE, "sample pkt<%p>") + /* Add user event logs here */ + + return 0; +} +#else /* ! CONFIG_BUZZZ */ +#define BUZZZ_DPL1(ID, N, ARG...) do {} while (0) +#define BUZZZ_DPL2(ID, N, ARG...) do {} while (0) +#define BUZZZ_DPL3(ID, N, ARG...) do {} while (0) +#define BUZZZ_DPL4(ID, N, ARG...) do {} while (0) +#define BUZZZ_DPL5(ID, N, ARG...) do {} while (0) +#endif /* ! CONFIG_BUZZZ */ + +#endif /* __buzzz_kevt_h_included__ */ +#endif diff -urN linux-4.1.52-orig/include/linux/compiler-gcc.h linux-4.1.52/include/linux/compiler-gcc.h --- linux-4.1.52-orig/include/linux/compiler-gcc.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/compiler-gcc.h 2020-02-07 01:47:54.000000000 +0800 @@ -65,6 +65,7 @@ * Force always-inline if the user requests it so via the .config, * or if gcc is too old: */ + #if !defined(CONFIG_ARCH_SUPPORTS_OPTIMIZED_INLINING) || \ !defined(CONFIG_OPTIMIZE_INLINING) || (__GNUC__ < 4) # define inline inline __attribute__((always_inline)) notrace @@ -77,6 +78,7 @@ # define __inline __inline notrace #endif + #define __deprecated __attribute__((deprecated)) #define __packed __attribute__((packed)) #define __weak __attribute__((weak)) diff -urN linux-4.1.52-orig/include/linux/compiler.h linux-4.1.52/include/linux/compiler.h --- linux-4.1.52-orig/include/linux/compiler.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/compiler.h 2020-02-07 01:47:54.000000000 +0800 @@ -404,6 +404,10 @@ # define __compiletime_error_fallback(condition) do { } while (0) #endif +#if defined(CONFIG_BCM_KF_BUZZZ) && defined(CONFIG_BUZZZ_FUNC) && defined(BUZZZ_O0) +#define __compiletime_assert(condition, msg, prefix, suffix) \ + do { /* no-op */ } while (0) +#else #define __compiletime_assert(condition, msg, prefix, suffix) \ do { \ bool __cond = !(condition); \ @@ -412,6 +416,7 @@ prefix ## suffix(); \ __compiletime_error_fallback(__cond); \ } while (0) +#endif #define _compiletime_assert(condition, msg, prefix, suffix) \ __compiletime_assert(condition, msg, prefix, suffix) diff -urN linux-4.1.52-orig/include/linux/cpufreq.h linux-4.1.52/include/linux/cpufreq.h --- linux-4.1.52-orig/include/linux/cpufreq.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/cpufreq.h 2020-02-07 01:47:54.000000000 +0800 @@ -155,6 +155,14 @@ u64 get_cpu_idle_time(unsigned int cpu, u64 *wall, int io_busy); int cpufreq_get_policy(struct cpufreq_policy *policy, unsigned int cpu); +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) +int cpufreq_set_policy(struct cpufreq_policy *data, struct cpufreq_policy *policy); +int cpufreq_set_speed(const char *govstr, int fraction); +unsigned cpufreq_get_freq_max(unsigned *max_out); +void cpufreq_set_freq_max(unsigned int fraction); +int cpufreq_minimum_reserve(int freq); +int cpufreq_minimum_unreserve(int freq); +#endif int cpufreq_update_policy(unsigned int cpu); bool have_governor_per_policy(void); struct kobject *get_governor_parent_kobj(struct cpufreq_policy *policy); @@ -271,7 +279,9 @@ /* Will be called after the driver is fully initialized */ void (*ready)(struct cpufreq_policy *policy); - +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) + int (*init_sysfs) (struct cpufreq_policy *policy); +#endif struct freq_attr **attr; /* platform specific boost support code */ diff -urN linux-4.1.52-orig/include/linux/cpu.h linux-4.1.52/include/linux/cpu.h --- linux-4.1.52-orig/include/linux/cpu.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/cpu.h 2020-02-07 01:47:54.000000000 +0800 @@ -291,4 +291,13 @@ bool cpu_report_death(void); #endif /* #ifdef CONFIG_HOTPLUG_CPU */ +#if defined CONFIG_BCM_KF_INTERACTIVE && defined CONFIG_CPU_FREQ_GOV_INTERACTIVE +#define IDLE_START 1 +#define IDLE_END 2 + +void idle_notifier_register(struct notifier_block *n); +void idle_notifier_unregister(struct notifier_block *n); +void idle_notifier_call_chain(unsigned long val); +#endif + #endif /* _LINUX_CPU_H_ */ diff -urN linux-4.1.52-orig/include/linux/crypto.h linux-4.1.52/include/linux/crypto.h --- linux-4.1.52-orig/include/linux/crypto.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/crypto.h 2020-02-07 01:47:54.000000000 +0800 @@ -100,6 +100,10 @@ */ #define CRYPTO_ALG_INTERNAL 0x00002000 +#if defined(CONFIG_BLOG) && defined(CONFIG_BCM_KF_BLOG) +#define CRYPTO_ALG_BLOG 0x80000000 +#endif + /* * Transform masks and values (for crt_flags). */ @@ -109,6 +113,9 @@ #define CRYPTO_TFM_REQ_WEAK_KEY 0x00000100 #define CRYPTO_TFM_REQ_MAY_SLEEP 0x00000200 #define CRYPTO_TFM_REQ_MAY_BACKLOG 0x00000400 +#if defined(CONFIG_BLOG) && defined(CONFIG_BCM_KF_BLOG) +#define CRYPTO_TFM_REQ_MAY_BLOG 0x00080000 +#endif #define CRYPTO_TFM_RES_WEAK_KEY 0x00100000 #define CRYPTO_TFM_RES_BAD_KEY_LEN 0x00200000 #define CRYPTO_TFM_RES_BAD_KEY_SCHED 0x00400000 @@ -197,7 +204,15 @@ struct scatterlist *assoc; struct scatterlist *src; struct scatterlist *dst; - +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) + unsigned int data_offset; + u8 next_hdr; +#else + int alloc_buff_spu; + int headerLen; +#endif +#endif void *__ctx[] CRYPTO_MINALIGN_ATTR; }; diff -urN linux-4.1.52-orig/include/linux/dpi.h linux-4.1.52/include/linux/dpi.h --- linux-4.1.52-orig/include/linux/dpi.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/dpi.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,108 @@ +#if defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_MODULE) +#ifndef _LINUX_DPI_H +#define _LINUX_DPI_H + +#include +#include + +#define DPI_APPID_ONGOING_BIT 0 +#define DPI_APPID_IDENTIFIED_BIT 1 +#define DPI_APPID_FINAL_BIT 2 +#define DPI_APPID_STOP_CLASSIFY_BIT 3 +#define DPI_APPID_RESYNC_BIT 4 +#define DPI_DEVID_ONGOING_BIT 5 +#define DPI_DEVID_IDENTIFIED_BIT 6 +#define DPI_DEVID_FINAL_BIT 7 +#define DPI_DEVID_STOP_CLASSIFY_BIT 8 +#define DPI_URL_STOP_CLASSIFY_BIT 9 +#define DPI_CLASSIFICATION_STOP_BIT 14 +#define DPI_CT_INIT_FROM_WAN_BIT 15 +#define DPI_CT_BLOCK_BIT 31 + +#define DPI_NL_CHANGE_MASK (1 << DPI_CT_BLOCK_BIT) + +#define DPI_URLINFO_MAX_HOST_LEN 64 + +#define dpi_ct_init_from_wan(ct) \ + test_bit(DPI_CT_INIT_FROM_WAN_BIT, &(ct)->dpi.flags) + +struct dpi_ct_stats { + u64 pkts; + u64 bytes; +}; + +struct dpi_app { + u32 app_id; + atomic_t refcount; + struct hlist_node node; +}; + +struct dpi_dev { + u8 mac[ETH_ALEN]; + + u32 dev_id; + u16 category; + u16 family; + u16 vendor; + u16 os; + u16 os_class; + + struct dpi_ct_stats us; + struct dpi_ct_stats ds; + + u16 classified; + atomic_t refcount; + struct hlist_node node; +}; + +struct dpi_appinst { + struct dpi_app *app; + struct dpi_dev *dev; + struct dpi_ct_stats us; + struct dpi_ct_stats ds; + atomic_t refcount; + struct hlist_node node; +}; + +struct dpi_url { + u32 len; + char hostname[DPI_URLINFO_MAX_HOST_LEN]; + atomic_t refcount; + struct hlist_node node; +}; + +struct dpi_info { + struct dpi_dev *dev; + struct dpi_app *app; + struct dpi_appinst *appinst; + struct dpi_url *url; + unsigned long flags; +}; + +struct dpi_hooks { + int (*cpu_enqueue)(pNBuff_t pNBuff, struct net_device *dev); +}; + +struct nf_conn; + +/* ----- dpicore driver functions ----- */ +struct dpi_info *dpi_info_get(struct nf_conn *conn); +u32 dpi_app_id(struct dpi_app *app); +u32 dpi_dev_id(struct dpi_dev *dev); +u8 *dpi_mac(struct dpi_dev *dev); +int dpi_url_len(struct dpi_url *url); +char *dpi_url(struct dpi_url *url); +struct dpi_ct_stats *dpi_appinst_stats(struct nf_conn *ct, int dir); +struct dpi_ct_stats *dpi_dev_stats(struct nf_conn *ct, int dir); +void dpi_print_flow(struct seq_file *s, struct nf_conn *ct); +int dpi_bind(struct dpi_hooks *hooks); +void dpi_block(struct nf_conn *conn); +int dpi_cpu_enqueue(pNBuff_t pNBuff, struct net_device *dev); +void dpi_ct_evicting(struct nf_conn *ct); + +/* ----- dpicore driver variables ----- */ +extern struct proc_dir_entry *dpi_dir; +extern int dpi_enabled; + +#endif /* _LINUX_DPI_H */ +#endif /* defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_MODULE) */ diff -urN linux-4.1.52-orig/include/linux/fpm_skbuff.h linux-4.1.52/include/linux/fpm_skbuff.h --- linux-4.1.52-orig/include/linux/fpm_skbuff.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/fpm_skbuff.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,121 @@ +/* +* <:copyright-BRCM:2017:DUAL/GPL:standard +* +* Copyright (c) 2017 Broadcom +* All Rights Reserved +* +* Unless you and Broadcom execute a separate written software license +* agreement governing use of this software, this software is licensed +* to you under the terms of the GNU General Public License version 2 +* (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +* with the following added to such license: +* +* As a special exception, the copyright holders of this software give +* you permission to link this software with independent modules, and +* to copy and distribute the resulting executable under terms of your +* choice, provided that you also meet, for each linked independent +* module, the terms and conditions of the license of that module. +* An independent module is a module which is not derived from this +* software. The special exception does not apply to any modifications +* of the software. +* +* Not withstanding the above, under no circumstances may you combine +* this software in any way with any other Broadcom software provided +* under a license other than the GPL, without Broadcom's express prior +* written consent. +* +* :> +* +* Created on: May 2017 +* Author: Kosta Sopov, kosta.sopov@broadcom.com +*/ + +#include +#include + +/* + FPM data buffer: + + skb->head skb->data skb->tail skb->end + +--------------+------------+-----------------+-----------+---------+ + | pre-headroom | headroom | data | tailroom | endroom | + +-------------------------------------------------------------------+ + \-------------- vanila skb ----------------/ +*/ + + +#define FPM_BUFFER_SIZE (2048) +#define SKB_SHARED_INFO_SIZE_REDUCED SKB_DATA_ALIGN(offsetof(struct skb_shared_info, frags)) /* no fragments allowed */ +#define FPM_DATA_PREHEADROOM SKB_DATA_ALIGN(sizeof(struct sk_buff)) +#define MAX_DATALEN (FPM_BUFFER_SIZE - FPM_DATA_PREHEADROOM - SKB_SHARED_INFO_SIZE_REDUCED) + +int skb_is_fpm_data(struct sk_buff *skb) +{ + return skb->recycle_flags & SKB_RECYCLE_FPM_DATA; +} + +/* Refer to the scheme above */ +int skb_fpm_preheadroom(struct sk_buff *skb) +{ + return FPM_DATA_PREHEADROOM; +} + +/* Refer to the scheme above */ +int skb_fpm_endroom(struct sk_buff *skb) +{ + return SKB_SHARED_INFO_SIZE_REDUCED; +} + +static void fpm_recycle(void *nbuff_p, unsigned long context, uint32_t flags) +{ + struct sk_buff *skb = nbuff_p; + uint32_t fpm_num = skb->fpm_num; + + drv_fpm_free_buffer(FPM_BUFFER_SIZE, fpm_num); +} + +static void empty_skb_init(struct sk_buff *skb, void *buf, uint32_t size) +{ + struct skb_shared_info *shinfo; + + memset(skb, 0, offsetof(struct sk_buff, tail)); + skb->truesize = SKB_TRUESIZE(size); + atomic_set(&skb->users, 1); + skb->head = buf; + skb->data = buf; + skb_reset_tail_pointer(skb); + skb->end = skb->tail + size; + skb->mac_header = (typeof(skb->mac_header))~0U; + skb->transport_header = (typeof(skb->transport_header))~0U; + + /* make sure we initialize shinfo sequentially */ + shinfo = skb_shinfo(skb); + memset(shinfo, 0, offsetof(struct skb_shared_info, dataref)); + atomic_set(&shinfo->dataref, 1); +} + +void *drv_fpm_buffer_get_address(uint32_t fpm_bn); + +struct sk_buff *skb_fpm_alloc(void) +{ + struct sk_buff *skb; + uint32_t buff_num, ret; + void *buff_ptr; + + if ((ret = drv_fpm_alloc_buffer(FPM_BUFFER_SIZE, &buff_num)) != BDMF_ERR_OK) + return NULL; + + buff_ptr = drv_fpm_buffer_get_address(buff_num); + + if (!(skb = skb_header_alloc())) + return NULL; + + empty_skb_init(skb, buff_ptr + FPM_DATA_PREHEADROOM, MAX_DATALEN); + + skb->recycle_hook = fpm_recycle; + skb->recycle_flags = (SKB_DATA_RECYCLE | SKB_RECYCLE_FPM_DATA); + skb->fpm_num = buff_num; + + return skb; +} + diff -urN linux-4.1.52-orig/include/linux/gbpm.h linux-4.1.52/include/linux/gbpm.h --- linux-4.1.52-orig/include/linux/gbpm.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/gbpm.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,353 @@ +#if defined(CONFIG_BCM_KF_NBUFF) +#ifndef __GBPM_H_INCLUDED__ +#define __GBPM_H_INCLUDED__ + +/* + * +<:copyright-BRCM:2007:DUAL/GPL:standard + + Copyright (c) 2007 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + +/* + ******************************************************************************* + * File Name : gbpm.h + * + ******************************************************************************* + */ +#define GBPM_VERSION "v0.1" +#define GBPM_VER_STR GBPM_VERSION +#define GBPM_MODNAME "Broadcom GBPM " + +#define GBPM_ERROR (-1) +#define GBPM_SUCCESS 0 + +#define GBPM_RXCHNL_MAX 4 +#define GBPM_RXCHNL_DISABLED 0 +#define GBPM_RXCHNL_ENABLED 1 + +#define CONFIG_GBPM_API_HAS_GET_TOTAL_BUFS 1 +#define CONFIG_GBPM_API_HAS_GET_AVAIL_BUFS 1 + +#if (defined(CONFIG_BCM_FAP) || defined(CONFIG_BCM_FAP_MODULE)) +#define GBPM_FAP_SUPPORT +#endif + +#if defined(CONFIG_BCM_XTMCFG) || defined(CONFIG_BCM_XTMCFG_MODULE) +#define GBPM_XTM_SUPPORT +#endif + +typedef enum { + GBPM_PORT_ETH, + GBPM_PORT_XTM, + GBPM_PORT_FWD, + GBPM_PORT_WLAN, + GBPM_PORT_USB, + GBPM_PORT_MAX +} gbpm_port_t; + +#if defined(CONFIG_BCM_BPM_BUF_TRACKING) +typedef enum { + GBPM_REF_BUFF, + GBPM_REF_FKB, + GBPM_REF_SKB +} gbpm_reftype_t; + +typedef enum { + GBPM_DRV_BPM, + GBPM_DRV_ETH, + GBPM_DRV_XTM, + GBPM_DRV_KERN, + GBPM_DRV_BDMF, + GBPM_DRV_ARCHER, + GBPM_DRV_MAX +} gbpm_driver_t; + +typedef enum { + GBPM_VAL_UNMARKED, + GBPM_VAL_ALLOC, + GBPM_VAL_CLONE, + GBPM_VAL_RECYCLE, + GBPM_VAL_FREE, + GBPM_VAL_RX, + GBPM_VAL_TX, + GBPM_VAL_ENTER, + GBPM_VAL_EXIT, + GBPM_VAL_INFO, + GBPM_VAL_INIT, + GBPM_VAL_COPY_SRC, + GBPM_VAL_COPY_DST, + GBPM_VAL_XLATE, + GBPM_VAL_MAX +} gbpm_value_t; + +typedef struct +{ + size_t addr; + union { + uint16_t word; + struct { + uint16_t driver:4; + uint16_t info:4; + uint16_t reftype:2; + uint16_t value:6; + }; + }; +} gbpm_mark_t; + +typedef struct +{ + atomic_t ref_cnt; + atomic_t idle_cnt; + uint32_t write; + gbpm_mark_t * mbuf_p; +} gbpm_trail_t; + +typedef void (* gbpm_mark_buf_hook_t) ( void *, void *, int, int, int, int ); +typedef void (* gbpm_add_ref_hook_t) ( void *, int ); +#endif /* bpm tracking */ + +/* + *----------------------------------------------------------------------------- + * GBPM callbacks are managed in a single global instantiation of gbpm_t gbpm_g + * GBPM Hooks may be viewed as "BPM" callbacks and "User" callbacks. + * - GBPM_BIND() lists all BPM callbacks + * - GBPM_USER() lists all USER callbacks (bind per user driver module). + *----------------------------------------------------------------------------- + */ + +/* GBPM_DECL may be undef/define, for GBPM_BIND and GBPM_USER template usage */ +#define GBPM_BIND() \ + /* --- BPM BUF POOL --- */ \ + GBPM_DECL(alloc_mult_buf) \ + GBPM_DECL(alloc_mult_buf_ex) \ + GBPM_DECL(free_mult_buf) \ + GBPM_DECL(alloc_buf) \ + GBPM_DECL(free_buf) \ + /* --- BPM SKB POOL --- */ \ + GBPM_DECL(total_skb) \ + GBPM_DECL(avail_skb) \ + GBPM_DECL(attach_skb) \ + GBPM_DECL(alloc_skb) \ + GBPM_DECL(alloc_buf_skb_attach) \ + GBPM_DECL(alloc_mult_skb) \ + GBPM_DECL(free_skb) \ + GBPM_DECL(recycle_skb) \ + /* --- BPM pNBuff --- */ \ + GBPM_DECL(recycle_pNBuff) \ + /* --- BPM Get Accessors --- */ \ + GBPM_DECL(get_dyn_buf_lvl) \ + GBPM_DECL(get_total_bufs) \ + GBPM_DECL(get_avail_bufs) \ + GBPM_DECL(get_max_dyn_bufs) \ + /* --- BPM Runtime --- */ \ + GBPM_DECL(resv_rx_buf) \ + GBPM_DECL(unresv_rx_buf) + + + /* --- BPM Users --- */ +#define GBPM_ENET() \ + GBPM_DECL(enet_status) +#if defined(GBPM_FAP_SUPPORT) +#define GBPM_FAP() \ + GBPM_DECL(fap_status) \ + GBPM_DECL(fap_thresh) \ + GBPM_DECL(enet_thresh) \ + GBPM_DECL(fap_enet_thresh) \ + GBPM_DECL(fap_upd_buf_lvl) +#else +#define GBPM_FAP() +#endif /* GBPM_FAP_SUPPORT */ +#if defined(GBPM_XTM_SUPPORT) +#define GBPM_XTM() \ + GBPM_DECL(xtm_status) \ + GBPM_DECL(xtm_thresh) +#else +#define GBPM_XTM() +#endif /* GBPM_XTM_SUPPORT */ + +#define GBPM_USER() \ + GBPM_ENET() \ + GBPM_FAP() \ + GBPM_XTM() + +/* + * typedefs for callbacks managed by GBPM. + */ + +/* --- BPM BUF POOL --- */ +typedef int (* gbpm_alloc_mult_buf_hook_t)(uint32_t, void **); +typedef void (* gbpm_free_mult_buf_hook_t)( uint32_t, void **); +typedef int (* gbpm_alloc_mult_buf_ex_hook_t)( uint32_t num, void **buf_p, uint32_t prio ); +typedef void * (* gbpm_alloc_buf_hook_t)(void); +typedef void (* gbpm_free_buf_hook_t)(void *); +typedef void (* gbpm_recycle_pNBuff_hook_t)(void *, unsigned long, uint32_t); + +/* --- BPM SKB POOL --- */ +typedef uint32_t (* gbpm_total_skb_hook_t)(void); +typedef uint32_t (* gbpm_avail_skb_hook_t)(void); +typedef void (* gbpm_attach_skb_hook_t)(void *, void *, uint32_t); +typedef void * (* gbpm_alloc_skb_hook_t)(void); +typedef void * (* gbpm_alloc_buf_skb_attach_hook_t)(uint32_t); +typedef void * (* gbpm_alloc_mult_skb_hook_t)(uint32_t); +typedef void (* gbpm_free_skb_hook_t)(void *); +typedef void (* gbpm_recycle_skb_hook_t)(void *, unsigned long, uint32_t); + +/* --- BPM Get Accessors --- */ +typedef int (* gbpm_get_dyn_buf_lvl_hook_t)(void); +typedef uint32_t (* gbpm_get_total_bufs_hook_t)(void); +typedef uint32_t (* gbpm_get_avail_bufs_hook_t)(void); +typedef uint32_t (* gbpm_get_max_dyn_bufs_hook_t)(void); + +/* --- BPM Set Accessors --- */ +typedef void (* gbpm_upd_buf_lvl_hook_t)(int); + +/* --- BPM Runtime --- */ +typedef int (* gbpm_resv_rx_buf_hook_t)(gbpm_port_t, uint32_t, uint32_t, uint32_t); +typedef int (* gbpm_unresv_rx_buf_hook_t)(gbpm_port_t, uint32_t); + + +/* --- BPM User --- */ +typedef void (* gbpm_evt_hook_t)(void); +typedef void (* gbpm_thresh_hook_t)(void); +typedef void (* gbpm_status_hook_t)(void); + + +/* --- BPM User instantiations --- */ +typedef gbpm_status_hook_t gbpm_enet_status_hook_t; +typedef gbpm_status_hook_t gbpm_fap_status_hook_t; +typedef gbpm_thresh_hook_t gbpm_fap_thresh_hook_t; +typedef gbpm_thresh_hook_t gbpm_enet_thresh_hook_t; +typedef gbpm_thresh_hook_t gbpm_fap_enet_thresh_hook_t; +typedef gbpm_upd_buf_lvl_hook_t gbpm_fap_upd_buf_lvl_hook_t; +/* gbpm_fap_evt_hook_g is instantiated in dev.c and not part of gbpm_g */ +typedef gbpm_evt_hook_t gbpm_fap_evt_hook_t; + +typedef gbpm_status_hook_t gbpm_xtm_status_hook_t; +typedef gbpm_thresh_hook_t gbpm_xtm_thresh_hook_t; + + +/* Typedef of the Global BPM hook manager */ +#undef GBPM_DECL +#define GBPM_DECL(HOOKNAME) gbpm_ ## HOOKNAME ## _hook_t HOOKNAME; + +typedef struct gbpm +{ + GBPM_BIND() /* List of BPM "BIND" hooks */ + GBPM_USER() /* List of DRV "USER" hooks */ + uint32_t debug; +} gbpm_t; + +extern gbpm_t gbpm_g; /* exported global */ + + +/* BPM registering callbacks into GBPM */ +#undef GBPM_DECL +#define GBPM_DECL(HOOKNAME) gbpm_ ## HOOKNAME ## _hook_t HOOKNAME, + +void gbpm_bind( GBPM_BIND() uint32_t debug ); +void gbpm_unbind(void); + +void gbpm_queue_work(void); + +/* + * Wrappers for GBPM callbacks + */ + +/* --- BPM BUF POOL --- */ +static inline int gbpm_alloc_mult_buf(uint32_t num, void **buf_p) + { return gbpm_g.alloc_mult_buf(num, buf_p); } +static inline int gbpm_alloc_mult_buf_ex( uint32_t num, void **buf_p, uint32_t prio ) + { return gbpm_g.alloc_mult_buf_ex(num, buf_p, prio); } +static inline void gbpm_free_mult_buf(uint32_t num, void **buf_p) + { gbpm_g.free_mult_buf(num, buf_p); } +static inline void * gbpm_alloc_buf(void) + { return gbpm_g.alloc_buf(); } +static inline void gbpm_free_buf(void * buf_p) + { return gbpm_g.free_buf(buf_p); } + +/* --- BPM SKB --- */ +static inline uint32_t gbpm_total_skb(void) + { return gbpm_g.total_skb(); } +static inline uint32_t gbpm_avail_skb(void) + { return gbpm_g.avail_skb(); } +static inline void gbpm_attach_skb(void *skbp, void *data, uint32_t datalen) + { gbpm_g.attach_skb(skbp, data, datalen); } +static inline void * gbpm_alloc_skb(void) + { return gbpm_g.alloc_skb(); } +static inline void * gbpm_alloc_buf_skb_attach(uint32_t datalen) + { return gbpm_g.alloc_buf_skb_attach(datalen); } +static inline void * gbpm_alloc_mult_skb(uint32_t num) + { return gbpm_g.alloc_mult_skb(num); } +static inline void gbpm_free_skb(void *skbp) + { gbpm_g.free_skb(skbp); } +static inline void gbpm_recycle_skb(void *skbp, unsigned long context, + uint32_t recycle_action) + { gbpm_g.recycle_skb(skbp, context, recycle_action); } + +/* --- BPM pNBuff --- */ +static inline void gbpm_recycle_pNBuff(void * pNBuff, unsigned long context, + uint32_t recycle_action) + { gbpm_g.recycle_pNBuff(pNBuff, context, recycle_action); } + + +/* --- BPM Get Accessors --- */ +static inline int gbpm_get_dyn_buf_lvl(void) + { return gbpm_g.get_dyn_buf_lvl(); } +static inline uint32_t gbpm_get_total_bufs(void) + { return gbpm_g.get_total_bufs(); } +static inline uint32_t gbpm_get_avail_bufs(void) + { return gbpm_g.get_avail_bufs(); } +static inline uint32_t gbpm_get_max_dyn_bufs(void) + { return gbpm_g.get_max_dyn_bufs(); } + +/* --- BPM Runtime --- */ +static inline int gbpm_resv_rx_buf(gbpm_port_t port, uint32_t chnl, + uint32_t num_rx_buf, uint32_t bulk_alloc_cnt) + { return gbpm_g.resv_rx_buf(port, chnl, num_rx_buf, bulk_alloc_cnt); } +static inline int gbpm_unresv_rx_buf(gbpm_port_t port, uint32_t chnl) + { return gbpm_g.unresv_rx_buf( port, chnl ); } + +#if defined(CONFIG_BCM_BPM_BUF_TRACKING) +void gbpm_mark_buf( void * buf_p, void * addr, int reftype, int driver, int value, int info); +void gbpm_add_ref( void * buf_p, int i ); + +#define GBPM_TRACK_BUF(buf, drv, value, info) do { gbpm_mark_buf( (void *)(buf), (void *)0, GBPM_REF_BUFF, (drv), (value), (info) ); } while(0) +#define GBPM_TRACK_SKB(skb, drv, value, info) do { gbpm_mark_buf( (void *)((skb)->data), (void *)(skb), GBPM_REF_SKB, (drv), (value), (info) ); } while(0) +#define GBPM_TRACK_FKB(fkb, drv, value, info) do { gbpm_mark_buf( (void *)((fkb)->data), (void *)(fkb), GBPM_REF_FKB, (drv), (value), (info) ); } while(0) +#define GBPM_INC_REF(buf) do { gbpm_add_ref( (buf), 1); } while (0) +#define GBPM_DEC_REF(buf) do { gbpm_add_ref( (buf), -1); } while (0) +#else +#define GBPM_TRACK_BUF(buf, drv, value, info) do{}while(0) +#define GBPM_TRACK_SKB(skb, drv, value, info) do{}while(0) +#define GBPM_TRACK_FKB(fkb, drv, value, info) do{}while(0) +#define GBPM_INC_REF(buf) do{}while(0) +#define GBPM_DEC_REF(buf) do{}while(0) +#endif + +#endif /* defined(__GBPM_H_INCLUDED__) */ + +#endif /* defined(CONFIG_BCM_KF_NBUFF) */ diff -urN linux-4.1.52-orig/include/linux/gfp.h linux-4.1.52/include/linux/gfp.h --- linux-4.1.52-orig/include/linux/gfp.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/gfp.h 2020-02-07 01:47:54.000000000 +0800 @@ -11,8 +11,30 @@ /* Plain integer GFP bitmasks. Do not use this directly. */ #define ___GFP_DMA 0x01u +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_ZONE_ACP) +/* due to the way kernel only supports up to 4 ZONEs and some tricks + * defined in this header file, in order to introduce a new GFP_ACP + * flag in to GFP, there are some if-defs implemented to adjust + * GFP values. GFP_ACP will use one of the GFP_HIGHMEM or GFP_DMA32 + * value if they are not used. If all 3 are enabled, then compilation + * will fail. */ +#ifndef CONFIG_ZONE_DMA32 +/* if DMA32 is not defined, then GFP_ACP will share value with GFP_DMA32 */ #define ___GFP_HIGHMEM 0x02u #define ___GFP_DMA32 0x04u +#define ___GFP_ACP 0x04u +#elif !defined(CONFIG_HIGHMEM) +/* if HIGHMEM is not defined, then GFP_ACP will share value with GFP_HIGHMEM */ +#define ___GFP_HIGHMEM 0x02u +#define ___GFP_ACP 0x02u +#define ___GFP_DMA32 0x04u +#else +#error gfp.h -- cannot have all ACP, DMA32, highmem enabled +#endif +#else +#define ___GFP_HIGHMEM 0x02u +#define ___GFP_DMA32 0x04u +#endif #define ___GFP_MOVABLE 0x08u #define ___GFP_WAIT 0x10u #define ___GFP_HIGH 0x20u @@ -50,7 +72,12 @@ #define __GFP_HIGHMEM ((__force gfp_t)___GFP_HIGHMEM) #define __GFP_DMA32 ((__force gfp_t)___GFP_DMA32) #define __GFP_MOVABLE ((__force gfp_t)___GFP_MOVABLE) /* Page is movable */ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_ZONE_ACP) +#define __GFP_ACP ((__force gfp_t)___GFP_ACP) +#define GFP_ZONEMASK (__GFP_DMA|__GFP_HIGHMEM|__GFP_ACP|__GFP_DMA32|__GFP_MOVABLE) +#else #define GFP_ZONEMASK (__GFP_DMA|__GFP_HIGHMEM|__GFP_DMA32|__GFP_MOVABLE) +#endif /* * Action modifiers - doesn't change the zoning * @@ -136,7 +163,17 @@ #define GFP_CONSTRAINT_MASK (__GFP_HARDWALL|__GFP_THISNODE) /* Do not use these with a slab allocator */ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_ZONE_ACP) +#ifndef CONFIG_ZONE_DMA32 +#define GFP_SLAB_BUG_MASK (__GFP_HIGHMEM|~__GFP_BITS_MASK) +#elif !defined(CONFIG_HIGHMEM) +#define GFP_SLAB_BUG_MASK (__GFP_DMA32|~__GFP_BITS_MASK) +#else +#error gfp.h -- cannot have all ACP, DMA32, highmem enabled +#endif +#else #define GFP_SLAB_BUG_MASK (__GFP_DMA32|__GFP_HIGHMEM|~__GFP_BITS_MASK) +#endif /* Flag - indicates that the buffer will be suitable for DMA. Ignored on some platforms, used as appropriate on others */ @@ -146,6 +183,9 @@ /* 4GB DMA on some platforms */ #define GFP_DMA32 __GFP_DMA32 +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_ZONE_ACP) +#define GFP_ACP __GFP_ACP +#endif /* Convert GFP flags to their corresponding migrate type */ static inline int gfpflags_to_migratetype(const gfp_t gfp_flags) { @@ -177,6 +217,17 @@ #define OPT_ZONE_DMA32 ZONE_NORMAL #endif +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_ZONE_ACP) +#ifndef CONFIG_ZONE_DMA32 +#undef OPT_ZONE_DMA32 +#define OPT_ZONE_DMA32 ZONE_ACP +#elif !defined(CONFIG_HIGHMEM) +#undef OPT_ZONE_HIGHMEM +#define OPT_ZONE_HIGHMEM ZONE_ACP +#else +#error gfp.h -- cannot have all ACP, DMA32, highmem enabled +#endif +#endif /* * GFP_ZONE_TABLE is a word size bitstring that is used for looking up the * zone to use given the lowest 4 bits of gfp_t. Entries are ZONE_SHIFT long diff -urN linux-4.1.52-orig/include/linux/gpio/driver.h linux-4.1.52/include/linux/gpio/driver.h --- linux-4.1.52-orig/include/linux/gpio/driver.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/gpio/driver.h 2020-02-07 01:47:54.000000000 +0800 @@ -74,6 +74,9 @@ struct gpio_chip { const char *label; struct device *dev; +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + struct device *parent; +#endif struct module *owner; struct list_head list; @@ -111,6 +114,22 @@ bool irq_not_threaded; bool exported; +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +#if IS_ENABLED(CONFIG_GPIO_GENERIC) + unsigned long (*read_reg)(void __iomem *reg); + void (*write_reg)(void __iomem *reg, unsigned long data); + unsigned long (*pin2mask)(struct gpio_chip *gc, unsigned int pin); + void __iomem *reg_dat; + void __iomem *reg_set; + void __iomem *reg_clr; + void __iomem *reg_dir; + int bgpio_bits; + spinlock_t bgpio_lock; + unsigned long bgpio_data; + unsigned long bgpio_dir; +#endif +#endif + #ifdef CONFIG_GPIOLIB_IRQCHIP /* * With CONFIG_GPIOLIB_IRQCHIP we get an irqchip inside the gpiolib @@ -159,6 +178,28 @@ struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +struct bgpio_pdata { + const char *label; + int base; + int ngpio; +}; +#if IS_ENABLED(CONFIG_GPIO_GENERIC) + +int bgpio_init(struct gpio_chip *gc, struct device *dev, + unsigned long sz, void __iomem *dat, void __iomem *set, + void __iomem *clr, void __iomem *dirout, void __iomem *dirin, + unsigned long flags); + +#define BGPIOF_BIG_ENDIAN BIT(0) +#define BGPIOF_UNREADABLE_REG_SET BIT(1) /* reg_set is unreadable */ +#define BGPIOF_UNREADABLE_REG_DIR BIT(2) /* reg_dir is unreadable */ +#define BGPIOF_BIG_ENDIAN_BYTE_ORDER BIT(3) +#define BGPIOF_READ_OUTPUT_REG_SET BIT(4) /* reg_set stores output value */ +#define BGPIOF_NO_OUTPUT BIT(5) /* only input */ + +#endif +#endif #ifdef CONFIG_GPIOLIB_IRQCHIP void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip, diff -urN linux-4.1.52-orig/include/linux/if_bridge.h linux-4.1.52/include/linux/if_bridge.h --- linux-4.1.52-orig/include/linux/if_bridge.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/if_bridge.h 2020-02-07 01:47:54.000000000 +0800 @@ -46,11 +46,42 @@ #define BR_LEARNING_SYNC BIT(9) #define BR_PROXYARP_WIFI BIT(10) +#if defined(CONFIG_BCM_KF_BRIDGE_PORT_ISOLATION) || defined(CONFIG_BCM_KF_BRIDGE_STP) +enum { + BREVT_IF_CHANGED, + BREVT_STP_STATE_CHANGED +}; +#endif + +#if defined(CONFIG_BCM_KF_BRIDGE_PORT_ISOLATION) +extern struct net_device *bridge_get_next_port(char *brName, unsigned int *portNum); +extern int register_bridge_notifier(struct notifier_block *nb); +extern int unregister_bridge_notifier(struct notifier_block *nb); +extern void bridge_get_br_list(char *brList, const unsigned int listSize); +#endif + +#if defined(CONFIG_BCM_KF_BRIDGE_STP) +struct stpPortInfo { + char portName[IFNAMSIZ]; + unsigned char stpState; +}; +extern int register_bridge_stp_notifier(struct notifier_block *nb); +extern int unregister_bridge_stp_notifier(struct notifier_block *nb); +extern void call_br_stp_notifiers(unsigned long evt, struct net_device *dev, struct stpPortInfo *ctx); +#endif + extern void brioctl_set(int (*ioctl_hook)(struct net *, unsigned int, void __user *)); typedef int br_should_route_hook_t(struct sk_buff *skb); extern br_should_route_hook_t __rcu *br_should_route_hook; +#if (defined(CONFIG_BCM_MCAST) || defined(CONFIG_BCM_MCAST_MODULE)) && defined(CONFIG_BCM_KF_MCAST) +typedef int (*br_bcm_mcast_receive_hook)(int ifindex, struct sk_buff *skb, int is_routed); +typedef int (*br_bcm_mcast_should_deliver_hook)(int ifindex, const struct sk_buff *skb, struct net_device *dst_dev, bool dst_mrouter); +int br_bcm_mcast_flood_forward(struct net_device *dev, struct sk_buff *skb); +int br_bcm_mcast_bind(br_bcm_mcast_receive_hook bcm_rx_hook, br_bcm_mcast_should_deliver_hook bcm_should_deliver_hook); +#endif + #if IS_ENABLED(CONFIG_BRIDGE) && IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING) int br_multicast_list_adjacent(struct net_device *dev, struct list_head *br_ip_list); diff -urN linux-4.1.52-orig/include/linux/if_mhi.h linux-4.1.52/include/linux/if_mhi.h --- linux-4.1.52-orig/include/linux/if_mhi.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/if_mhi.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,53 @@ +#ifdef CONFIG_BCM_KF_MHI +/* +<:copyright-BRCM:2012:DUAL/GPL:standard + + Copyright (c) 2012 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +/* + * File: if_mhi.h + * + * 'Modem-Host Interface' kernel definitions + */ + +#ifndef LINUX_IF_MHI_H +#define LINUX_IF_MHI_H + +/* Packet sizes */ + +#define MHI_MIN_MTU 260 +#define MHI_MAX_MTU 65540 + +#define MHI_MTU MHI_MAX_MTU + +/* Packet socket options */ +#define MHI_DROP_COUNT 1 + +/* Ioctl definitions */ + + +#endif /* LINUX_IF_MHI_H */ +#endif /* CONFIG_BCM_KF_MHI */ diff -urN linux-4.1.52-orig/include/linux/if_vlan.h linux-4.1.52/include/linux/if_vlan.h --- linux-4.1.52-orig/include/linux/if_vlan.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/if_vlan.h 2020-02-07 01:47:54.000000000 +0800 @@ -159,6 +159,10 @@ #ifdef CONFIG_NET_POLL_CONTROLLER struct netpoll *netpoll; #endif + +#if defined(CONFIG_BCM_KF_VLAN) && (defined(CONFIG_BCM_VLAN) || defined(CONFIG_BCM_VLAN_MODULE)) + int nfmark_to_priority; +#endif unsigned int nest_level; }; @@ -334,6 +338,11 @@ { int err; +#if defined(CONFIG_BCM_KF_NBUFF) +#if defined(CONFIG_BCM_KF_VLAN) && (defined(CONFIG_BCM_VLAN) || defined(CONFIG_BCM_VLAN_MODULE)) + vlan_tci |= skb->cfi_save; +#endif +#endif err = __vlan_insert_tag(skb, vlan_proto, vlan_tci); if (err) { dev_kfree_skb_any(skb); diff -urN linux-4.1.52-orig/include/linux/igmp.h linux-4.1.52/include/linux/igmp.h --- linux-4.1.52-orig/include/linux/igmp.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/igmp.h 2020-02-07 01:47:54.000000000 +0800 @@ -80,6 +80,9 @@ unsigned int sfmode; struct ip_sf_list *sources; struct ip_sf_list *tomb; +#if defined(CONFIG_BCM_KF_MCAST_GR_SUPPRESSION) + unsigned int osfmode; +#endif unsigned long sfcount[2]; union { struct ip_mc_list *next; diff -urN linux-4.1.52-orig/include/linux/ipv6.h linux-4.1.52/include/linux/ipv6.h --- linux-4.1.52-orig/include/linux/ipv6.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/ipv6.h 2020-02-07 01:47:54.000000000 +0800 @@ -310,12 +310,14 @@ return NULL; } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static inline struct inet6_request_sock * inet6_rsk(const struct request_sock *rsk) { return NULL; } +#endif static inline struct raw6_sock *raw6_sk(const struct sock *sk) { return NULL; diff -urN linux-4.1.52-orig/include/linux/iqos.h linux-4.1.52/include/linux/iqos.h --- linux-4.1.52-orig/include/linux/iqos.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/iqos.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,270 @@ +#if defined(CONFIG_BCM_KF_NBUFF) +#ifndef __IQOS_H_INCLUDED__ +#define __IQOS_H_INCLUDED__ + +/* +<:copyright-BRCM:2009:DUAL/GPL:standard + + Copyright (c) 2009 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + + +/* + ******************************************************************************* + * File Name : ingqos.h + * + ******************************************************************************* + */ +#define IQOS_VERSION "v1.0" +#define IQOS_VER_STR IQOS_VERSION +#define IQOS_MODNAME "Broadcom IQoS " + +#include + +typedef enum { + IQOS_PARAM_TYPE_KEYMASK, + IQOS_PARAM_TYPE_KEY, + IQOS_PARAM_TYPE_MAX +} iqos_param_type_t; + +typedef enum { + IQOS_FIELD_INGRESS_DEVICE, + IQOS_FIELD_SRC_MAC, + IQOS_FIELD_DST_MAC, + IQOS_FIELD_ETHER_TYPE, + IQOS_FIELD_OUTER_VID, + IQOS_FIELD_OUTER_PBIT, + IQOS_FIELD_INNER_VID, + IQOS_FIELD_INNER_PBIT, + IQOS_FIELD_L2_PROTO, + IQOS_FIELD_L3_PROTO, + IQOS_FIELD_IP_PROTO, + IQOS_FIELD_SRC_IP, + IQOS_FIELD_DST_IP, + IQOS_FIELD_DSCP, + IQOS_FIELD_IPV6_FLOW_LABEL, + IQOS_FIELD_SRC_PORT, + IQOS_FIELD_DST_PORT, + IQOS_FIELD_OFFSET_0, + IQOS_FIELD_OFFSET_START = IQOS_FIELD_OFFSET_0, + IQOS_FIELD_OFFSET_0_TYPE, + IQOS_FIELD_OFFSET_0_START, + IQOS_FIELD_OFFSET_0_SIZE, + IQOS_FIELD_OFFSET_0_MASK, + IQOS_FIELD_OFFSET_1, + IQOS_FIELD_OFFSET_1_TYPE, + IQOS_FIELD_OFFSET_1_START, + IQOS_FIELD_OFFSET_1_SIZE, + IQOS_FIELD_OFFSET_1_MASK, + IQOS_FIELD_OFFSET_END = IQOS_FIELD_OFFSET_1_MASK, + IQOS_FIELD_MAX +} iqos_field_t; + +typedef enum { + IQOS_ACTION_NOP, + IQOS_ACTION_PRIO, + IQOS_ACTION_DROP, + IQOS_ACTION_DST_Q, + IQOS_ACTION_TRAP, + IQOS_ACTION_MAX +} iqos_action_t; + +typedef enum { + IQOS_OFFSET_TYPE_L2, + IQOS_OFFSET_TYPE_L3, + IQOS_OFFSET_TYPE_L4, + IQOS_OFFSET_TYPE_MAX +} iqos_offset_type_t; + +typedef struct { + uint32_t type; + uint32_t start; + uint32_t size; + uint32_t mask; +} iqos_offset_data_t; + +#define IQOS_PACKET_CACHE_MAX_SIZE 128 +typedef struct { + uint32_t ingress_device; + uint8_t src_mac[ETH_HLEN]; + uint8_t dst_mac[ETH_HLEN]; + uint16_t eth_type; + uint16_t outer_vid; + uint8_t outer_pbit; + uint16_t inner_vid; + uint8_t inner_pbit; + uint16_t l2_proto; + uint16_t l3_proto; + uint8_t ip_proto; + uint8_t is_ipv6; + uint32_t src_ip[4]; + uint32_t dst_ip[4]; + uint8_t dscp; + uint32_t flow_label; + uint16_t l4_src_port; + uint16_t l4_dst_port; + uint16_t l2_offset; + uint16_t l3_offset; + uint16_t l4_offset; + /* used for storing part of packet buffer for offset check */ + uint8_t packet_cache[IQOS_PACKET_CACHE_MAX_SIZE]; +} iqos_data_t; + +typedef struct { + uint32_t param_type; + uint8_t prio; + uint8_t type; + uint32_t field_mask; + uint32_t action; + uint32_t action_value; + iqos_data_t data; + iqos_offset_data_t offset0; + iqos_offset_data_t offset1; +} iqos_param_t; + +typedef enum { + IQOS_ENT_DYN, + IQOS_ENT_STAT, + IQOS_ENT_MAX +} iqos_ent_t; + +typedef enum { + IQOS_PRIO_LOW, + IQOS_PRIO_HIGH, + IQOS_PRIO_MAX +} iqos_prio_t; + +typedef enum { + IQOS_CONG_STATUS_LO, + IQOS_CONG_STATUS_HI, + IQOS_CONG_STATUS_MAX +} iqos_cong_status_t; + +typedef enum { + IQOS_STATUS_DISABLE, + IQOS_STATUS_ENABLE, + IQOS_STATUS_MAX +} iqos_status_t; + +typedef struct { + uint8_t ipProto; + uint16_t destPort; + iqos_ent_t ent; + iqos_prio_t prio; +} iqos_config_t; + +typedef int (*iqos_common_hook_t)(iqos_param_t *param); +typedef void (*iqos_void_hook_t)(void); +typedef int (*iqos_int_hook_t)(uint32_t val); + +/* the original APIs that are backward supported by the new driver/module */ +int iqos_add_L4port(uint8_t ipProto, uint16_t destPort, iqos_ent_t ent, + iqos_prio_t prio); +int iqos_rem_L4port(uint8_t ipProto, uint16_t destPort, iqos_ent_t ent); +int iqos_prio_L4port(uint8_t ipProto, uint16_t destPort); + +/* the new APIs */ + +/* APIs for setting up keymask: + * WARNING!! one will have to perform iqos_flush() to delete all the dynamic + * entry before making any change of the keymasks. + * Deleting a keymask that has key refer to it will fail and return error */ +int iqos_keymask_param_start(iqos_param_t *param); +int iqos_keymask_param_field_set(iqos_param_t *param, uint32_t field, uint32_t *val_ptr); +int iqos_keymask_commit_and_add(iqos_param_t *param, uint8_t prio); +int iqos_keymask_commit_and_delete(iqos_param_t *param); + +/* APIs for setting up key, + * example can be found in iqos_[add/rem/prio]_L4port */ +int iqos_key_param_start(iqos_param_t *param); +int iqos_key_param_field_set(iqos_param_t *param, uint32_t field, + uint32_t *val_ptr, uint32_t val_size); +int iqos_key_param_action_set(iqos_param_t *param, uint32_t action, + uint32_t value); +int iqos_key_commit_and_add(iqos_param_t *param, uint8_t type); +int iqos_key_commit_and_delete(iqos_param_t *param, uint8_t type); +int iqos_key_commit_and_get(iqos_param_t *param); + +/* API to flush all the dynamic entries, and + * delete keymask if no key refers to it */ +void iqos_flush(void); + +/* API to set the status for IQOS to enabled(1) or disabled(0) */ +int iqos_set_status(uint32_t status); + +void iqos_bind(iqos_common_hook_t iqos_add_keymask, + iqos_common_hook_t iqos_rem_keymask, + iqos_common_hook_t iqos_add_key, + iqos_common_hook_t iqos_rem_key, + iqos_common_hook_t iqos_get_key, + iqos_int_hook_t iqos_set_status, + iqos_void_hook_t iqos_flush); + + +#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT) +#define IQOS_LOCK_IRQSAVE() spin_lock_irqsave( &iqos_cong_lock_g, flags ) +#define IQOS_UNLOCK_IRQRESTORE() spin_unlock_irqrestore( &iqos_cong_lock_g, flags ) +#define IQOS_LOCK_BH() spin_lock_bh( &iqos_lock_g ) +#define IQOS_UNLOCK_BH() spin_unlock_bh( &iqos_lock_g ) +#else +#define IQOS_LOCK_IRQSAVE() local_irq_save(flags) +#define IQOS_UNLOCK_IRQRESTORE() local_irq_restore(flags) +#define IQOS_LOCK_BH() NULL_STMT +#define IQOS_UNLOCK_BH() NULL_STMT +#endif + +#if (defined(CONFIG_BCM_INGQOS) || defined(CONFIG_BCM_INGQOS_MODULE)) +#define IQOS_RXCHNL_MAX 4 +#define IQOS_RXCHNL_DISABLED 0 +#define IQOS_RXCHNL_ENABLED 1 +#define IQOS_MAX_RX_RING_SIZE 4096 + +typedef enum { + IQOS_IF_ENET, + IQOS_IF_ENET_RXCHNL0 = IQOS_IF_ENET, + IQOS_IF_ENET_RXCHNL1, + IQOS_IF_ENET_RXCHNL2, + IQOS_IF_ENET_RXCHNL3, + IQOS_IF_XTM, + IQOS_IF_XTM_RXCHNL0 = IQOS_IF_XTM, + IQOS_IF_XTM_RXCHNL1, + IQOS_IF_XTM_RXCHNL2, + IQOS_IF_XTM_RXCHNL3, + IQOS_IF_FWD, + IQOS_IF_FWD_RXCHNL0 = IQOS_IF_FWD, + IQOS_IF_FWD_RXCHNL1, + IQOS_IF_WL, + IQOS_IF_USB, + IQOS_IF_MAX, +} iqos_if_t; + +iqos_cong_status_t iqos_get_sys_cong_status(void); +iqos_cong_status_t iqos_get_cong_status(iqos_if_t iface, uint32_t chnl); +uint32_t iqos_set_cong_status(iqos_if_t iface, uint32_t chnl, + iqos_cong_status_t status); +#endif +#endif /* defined(__IQOS_H_INCLUDED__) */ +#endif diff -urN linux-4.1.52-orig/include/linux/kallsyms.h linux-4.1.52/include/linux/kallsyms.h --- linux-4.1.52-orig/include/linux/kallsyms.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/kallsyms.h 2020-02-07 01:47:54.000000000 +0800 @@ -122,7 +122,21 @@ static inline void print_ip_sym(unsigned long ip) { +#if defined(CONFIG_BCM_KF_EXTRA_DEBUG) +#if defined(CONFIG_ARM) + if (((ip & 0xF0000000) == 0xc0000000)) +#elif defined (CONFIG_MIPS) + if (((ip & 0xF0000000) == 0x80000000)) +#else + if ((ip & 0xfffffff000000000) == 0xffffffc000000000 || (ip & 0xfffffff000000000) == 0xffffffb000000000) +#endif printk("[<%p>] %pS\n", (void *) ip, (void *) ip); + else + printk("[<%p>] (suspected corrupt symbol)\n", (void *) ip); +#else + + printk("[<%p>] %pS\n", (void *) ip, (void *) ip); +#endif } #endif /*_LINUX_KALLSYMS_H*/ diff -urN linux-4.1.52-orig/include/linux/kernel.h linux-4.1.52/include/linux/kernel.h --- linux-4.1.52-orig/include/linux/kernel.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/kernel.h 2020-02-07 01:47:54.000000000 +0800 @@ -53,6 +53,14 @@ #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr)) +#if defined(CONFIG_BCM_KF_OPTEE_414_BACKPORTS) +#define u64_to_user_ptr(x) ( \ +{ \ + typecheck(u64, x); \ + (void __user *)(uintptr_t)x; \ +} \ +) +#endif /* CONFIG_BCM_KF_OPTEE_414_BACKPORTS */ /* * This looks more complex than it should be. But we need to * get the type for the ~ right in round_down (it needs to be diff -urN linux-4.1.52-orig/include/linux/mhi.h linux-4.1.52/include/linux/mhi.h --- linux-4.1.52-orig/include/linux/mhi.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/mhi.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,72 @@ +#ifdef CONFIG_BCM_KF_MHI +/* +<:copyright-BRCM:2012:DUAL/GPL:standard + + Copyright (c) 2012 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +/* + * file mhi.h + * + * Modem-Host Interface (MHI) kernel interface + */ + +#ifndef LINUX_MHI_H +#define LINUX_MHI_H + +#include +#include +#include +#include + + +struct mhi_sock { + struct sock sk; + int sk_l3proto; + int sk_ifindex; +}; + +struct sockaddr_mhi { + sa_family_t sa_family; + int sa_ifindex; + __u8 sa_zero[ + sizeof(struct sockaddr) + - sizeof(sa_family_t) + - sizeof(int)]; +}; + + +static inline struct mhi_sock *mhi_sk(struct sock *sk) +{ + return (struct mhi_sock *)sk; +} + +static inline struct sockaddr_mhi *sa_mhi(struct sockaddr *sa) +{ + return (struct sockaddr_mhi *)sa; +} + +#endif +#endif /* CONFIG_BCM_KF_MHI */ diff -urN linux-4.1.52-orig/include/linux/mhi_l2mux.h linux-4.1.52/include/linux/mhi_l2mux.h --- linux-4.1.52-orig/include/linux/mhi_l2mux.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/mhi_l2mux.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,215 @@ +#ifdef CONFIG_BCM_KF_MHI +/* +<:copyright-BRCM:2012:DUAL/GPL:standard + + Copyright (c) 2012 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +/* + * File: mhi_l2mux.h + * + * MHI L2MUX kernel definitions + */ + +#ifndef LINUX_L2MUX_H +#define LINUX_L2MUX_H + +#include +#include + +#ifdef __KERNEL__ +#include +#define ACTIVATE_L2MUX_STAT + +#ifdef ACTIVATE_L2MUX_STAT +#include +#include +#endif /* ACTIVATE_L2MUX_STAT */ +#endif /*__KERNEL__*/ + +/* Official L3 protocol IDs */ +#define MHI_L3_PHONET 0x00 +#define MHI_L3_FILE 0x01 +#define MHI_L3_AUDIO 0x02 +#define MHI_L3_SECURITY 0x03 +#define MHI_L3_TEST 0x04 +#define MHI_L3_TEST_PRIO 0x05 +#define MHI_L3_XFILE 0x06 +#define MHI_L3_MHDP_DL 0x07 +#define MHI_L3_MHDP_UL 0x08 +#define MHI_L3_AUX_HOST 0x09 +#define MHI_L3_LOG 0x0A +#define MHI_L3_CELLULAR_AUDIO 0x0B +#define MHI_L3_IMS 0x0D +#define MHI_L3_OEM_CP 0x0E +#define MHI_L3_MHDP_DL_PS2 0x17 +#define MHI_L3_MHDP_UL_PS2 0x18 +#define MHI_L3_CTRL_TMODEM 0xC0 +#define MHI_L3_THERMAL 0xC1 +#define MHI_L3_MHDP_UDP_FILTER 0xFC +#define MHI_L3_HIGH_PRIO_TEST 0xFD +#define MHI_L3_MED_PRIO_TEST 0xFE +#define MHI_L3_LOW_PRIO_TEST 0xFF + +/* 256 possible protocols */ +#define MHI_L3_NPROTO 256 + +/* Special value for ANY */ +#define MHI_L3_ANY 0xFFFF + +#ifdef __KERNEL__ +typedef int (l2mux_skb_fn)(struct sk_buff *skb, struct net_device *dev); +typedef int (l2mux_audio_fn)(unsigned char *buffer, size_t size, uint8_t phonet_dev_id); + +struct l2muxhdr { + __u8 l3_len[3]; + __u8 l3_prot; +} __packed; + +#ifdef ACTIVATE_L2MUX_STAT + +enum l2mux_direction { + UPLINK_DIR = 0, + DOWNLINK_DIR, +}; + +enum l2mux_trace_state { + ON = 0, + OFF, + KERNEL, +}; + + +struct l2muxstat { + unsigned l3pid; + unsigned l3len; + enum l2mux_direction dir; + struct timeval time_val; + struct list_head list; + unsigned int stat_counter; +}; + +struct l2mux_stat_info { + struct proc_dir_entry *proc_entry; + struct l2muxstat l2muxstat_tab; + int l2mux_stat_id; + int previous_stat_counter; + unsigned int l2mux_total_stat_counter; + enum l2mux_trace_state l2mux_traces_state; + int l2mux_traces_activation_done; + struct net_device *dev; + struct work_struct l2mux_stat_work; +}; + +#endif /* ACTIVATE_L2MUX_STAT */ + + +#define L2MUX_HDR_SIZE (sizeof(struct l2muxhdr)) + + +static inline struct l2muxhdr *l2mux_hdr(struct sk_buff *skb) +{ + return (struct l2muxhdr *)skb_mac_header(skb); +} + +static inline void l2mux_set_proto(struct l2muxhdr *hdr, int proto) +{ + hdr->l3_prot = proto; +} + +static inline int l2mux_get_proto(struct l2muxhdr *hdr) +{ + return hdr->l3_prot; +} + +static inline void l2mux_set_length(struct l2muxhdr *hdr, unsigned len) +{ + hdr->l3_len[0] = (len) & 0xFF; + hdr->l3_len[1] = (len >> 8) & 0xFF; + hdr->l3_len[2] = (len >> 16) & 0xFF; +} + +static inline unsigned l2mux_get_length(struct l2muxhdr *hdr) +{ + return (((unsigned)hdr->l3_len[2]) << 16) | + (((unsigned)hdr->l3_len[1]) << 8) | + ((unsigned)hdr->l3_len[0]); +} + +extern int l2mux_netif_rx_register(int l3, l2mux_skb_fn *rx_fn); +extern int l2mux_netif_rx_unregister(int l3); + +extern int l2mux_netif_tx_register(int pt, l2mux_skb_fn *rx_fn); +extern int l2mux_netif_tx_unregister(int pt); + +extern int l2mux_skb_rx(struct sk_buff *skb, struct net_device *dev); +extern int l2mux_skb_tx(struct sk_buff *skb, struct net_device *dev); + +enum l2mux_audio_dev_id { + L2MUX_AUDIO_DEV_ID0, + L2MUX_AUDIO_DEV_ID1, + L2MUX_AUDIO_DEV_ID2, + L2MUX_AUDIO_DEV_ID3, + L2MUX_AUDIO_DEV_MAX +}; +#define L2MUX_AUDIO_DEV_TYPE_RX (0x1 << 24) +#define L2MUX_AUDIO_DEV_TYPE_TX (0x2 << 24) + +/* both register functions will return a positive integer value when + * registration complete successfully, please use the handle for + * unregistration. + * We assume that there should be only 1 voice code on the host side, but + * there might be more than 1 modem */ +extern int l2mux_audio_rx_register(l2mux_audio_fn *fn); +extern int l2mux_audio_rx_unregister(int handle); + +extern int l2mux_audio_tx_register(uint8_t phonet_dev_id, l2mux_audio_fn *fn); +extern int l2mux_audio_tx_unregister(int handle); + +/* + * Input: buffer: pointer to L2muxhdr + audio payload, since payload length + * info is in l2muxhdr, so we don't need another argument + * pn_dev_id: the phonet ID this device is binded to. + * Note: the buffer should never be freed by anyone, since this is a static + * buffer allocated by the modem driver. */ +extern int l2mux_audio_rx(unsigned char *buffer, uint8_t pn_dev_id); + +/* + * Input: buffer: pointer to audio payload WITHOUT l2muxhdr. The buffer should + * reserve at least 4 bytes in the headroom, such that L2mux + * does not need to allocate a new memory for inserting l2muxhdr. + * size: the size of the payload + * pn_dev_id: The phonet ID that indicates the device which this audio + * data is destiend to + * Note: The buffer should never be freed by anyone. It should be a static + * buffer allocated by the voice code. + */ +extern int l2mux_audio_tx(unsigned char *buffer, size_t size, + uint8_t pn_dev_id); + +#endif /*__KERNEL__*/ + +#endif /* LINUX_L2MUX_H */ +#endif /* CONFIG_BCM_KF_MHI */ diff -urN linux-4.1.52-orig/include/linux/mm.h linux-4.1.52/include/linux/mm.h --- linux-4.1.52-orig/include/linux/mm.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/mm.h 2020-02-07 01:47:54.000000000 +0800 @@ -1916,7 +1916,11 @@ void task_dirty_inc(struct task_struct *tsk); /* readahead.c */ +#if (defined(CONFIG_BCM_KF_USB_STORAGE) && defined(CONFIG_MIPS_BCM963XX)) +#define VM_MAX_READAHEAD 512 /* kbytes */ +#else #define VM_MAX_READAHEAD 128 /* kbytes */ +#endif #define VM_MIN_READAHEAD 16 /* kbytes (includes current page) */ int force_page_cache_readahead(struct address_space *mapping, struct file *filp, diff -urN linux-4.1.52-orig/include/linux/mmzone.h linux-4.1.52/include/linux/mmzone.h --- linux-4.1.52-orig/include/linux/mmzone.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/mmzone.h 2020-02-07 01:47:54.000000000 +0800 @@ -301,6 +301,12 @@ */ ZONE_DMA32, #endif +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_ZONE_ACP) + /* + * a specific memory zone allocated for ACP purpose in BCM63xx platform + */ + ZONE_ACP, +#endif /* * Normal addressable memory is in ZONE_NORMAL. DMA operations can be * performed on pages in ZONE_NORMAL if the DMA devices support @@ -880,6 +886,12 @@ return 0; #endif } +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_ZONE_ACP) +static inline int is_acp(struct zone *zone) +{ + return zone == zone->zone_pgdat->node_zones + ZONE_ACP; +} +#endif /* These two functions are used to setup the per zone pages min values */ struct ctl_table; diff -urN linux-4.1.52-orig/include/linux/mroute6.h linux-4.1.52/include/linux/mroute6.h --- linux-4.1.52-orig/include/linux/mroute6.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/mroute6.h 2020-02-07 01:47:54.000000000 +0800 @@ -114,8 +114,13 @@ #define MFC_ASSERT_THRESH (3*HZ) /* Maximal freq. of asserts */ struct rtmsg; +#if defined(CONFIG_BCM_KF_MROUTE) +int ip6mr_get_route(struct net *net, struct sk_buff *skb, + struct rtmsg *rtm, int nowait, int ifIndex); +#else extern int ip6mr_get_route(struct net *net, struct sk_buff *skb, struct rtmsg *rtm, int nowait); +#endif #ifdef CONFIG_IPV6_MROUTE extern struct sock *mroute6_socket(struct net *net, struct sk_buff *skb); diff -urN linux-4.1.52-orig/include/linux/mroute.h linux-4.1.52/include/linux/mroute.h --- linux-4.1.52-orig/include/linux/mroute.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/mroute.h 2020-02-07 01:47:54.000000000 +0800 @@ -101,7 +101,14 @@ #endif struct rtmsg; +#if defined(CONFIG_BCM_KF_MROUTE) +int ipmr_get_route(struct net *net, struct sk_buff *skb, + __be32 saddr, __be32 daddr, + struct rtmsg *rtm, int nowait, int ifIndex); +#else extern int ipmr_get_route(struct net *net, struct sk_buff *skb, __be32 saddr, __be32 daddr, struct rtmsg *rtm, int nowait); #endif + +#endif \ No newline at end of file diff -urN linux-4.1.52-orig/include/linux/msi.h linux-4.1.52/include/linux/msi.h --- linux-4.1.52-orig/include/linux/msi.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/msi.h 2020-02-07 01:47:54.000000000 +0800 @@ -114,6 +114,10 @@ int (*setup_irq)(struct msi_controller *chip, struct pci_dev *dev, struct msi_desc *desc); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + int (*setup_irqs)(struct msi_controller *chip, struct pci_dev *dev, + int nvec, int type); +#endif void (*teardown_irq)(struct msi_controller *chip, unsigned int irq); }; diff -urN linux-4.1.52-orig/include/linux/mtd/bbm.h linux-4.1.52/include/linux/mtd/bbm.h --- linux-4.1.52-orig/include/linux/mtd/bbm.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/mtd/bbm.h 2020-02-07 01:47:54.000000000 +0800 @@ -54,6 +54,11 @@ * that the pattern and the version count are always located in the oob area * of the first block. */ + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +#define BBT_NULL_PAGE (-1LL) +#endif + struct nand_bbt_descr { int options; int pages[NAND_MAX_CHIPS]; @@ -91,6 +96,12 @@ * with NAND_BBT_CREATE. */ #define NAND_BBT_CREATE_EMPTY 0x00000400 +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +/* Search good / bad pattern through all pages of a block */ +#define NAND_BBT_SCANALLPAGES 0x00000800 +/* Scan block empty during good / bad block scan */ +#define NAND_BBT_SCANEMPTY 0x00001000 +#endif /* Write bbt if neccecary */ #define NAND_BBT_WRITE 0x00002000 /* Read and write back block contents when writing bbt */ diff -urN linux-4.1.52-orig/include/linux/mtd/bchp_nand_21_22.h linux-4.1.52/include/linux/mtd/bchp_nand_21_22.h --- linux-4.1.52-orig/include/linux/mtd/bchp_nand_21_22.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/mtd/bchp_nand_21_22.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,884 @@ +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +/* +* + <:copyright-BRCM:2015:DUAL/GPL:standard + + Copyright (c) 2015 Broadcom + All Rights Reserved + + Unless you and Broadcom execute a separate written software license + agreement governing use of this software, this software is licensed + to you under the terms of the GNU General Public License version 2 + (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, + with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + + Not withstanding the above, under no circumstances may you combine + this software in any way with any other Broadcom software provided + under a license other than the GPL, without Broadcom's express prior + written consent. + + :> + */ + +#ifndef BCHP_NAND_21_22_H__ +#define BCHP_NAND_21_22_H__ + +#define BRCMNAND_CTL_BASE ((uintptr_t)(NAND_REG_BASE & 0x0fffffff)) +#define BRCMNAND_CACHE_BASE ((uintptr_t)(NAND_CACHE_BASE & 0x0fffffff)) + +#define BCHP_NAND_REG_START BCHP_NAND_REVISION +#define BCHP_NAND_REG_END BCHP_NAND_BLK_WR_PROTECT + +/*************************************************************************** + *NAND - Nand Flash Control Registers + ***************************************************************************/ +#define BCHP_NAND_REVISION ((uintptr_t)(BRCMNAND_CTL_BASE + 0x00)) /* NAND Revision */ +#define BCHP_NAND_CMD_START ((uintptr_t)(BRCMNAND_CTL_BASE + 0x04)) /* Nand Flash Command Start */ +#define BCHP_NAND_CMD_EXT_ADDRESS ((uintptr_t)(BRCMNAND_CTL_BASE + 0x08)) /* Nand Flash Command Extended Address */ +#define BCHP_NAND_CMD_ADDRESS ((uintptr_t)(BRCMNAND_CTL_BASE + 0x0c)) /* Nand Flash Command Address */ +#define BCHP_NAND_CMD_END_ADDRESS ((uintptr_t)(BRCMNAND_CTL_BASE + 0x10)) /* Nand Flash Command End Address */ +#define BCHP_NAND_CS_NAND_SELECT ((uintptr_t)(BRCMNAND_CTL_BASE + 0x14)) /* Nand Flash EBI CS Select */ +#define BCHP_NAND_CS_NAND_XOR ((uintptr_t)(BRCMNAND_CTL_BASE + 0x18)) /* Nand Flash EBI CS Address XOR with 1FC0 Control */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_0 ((uintptr_t)(BRCMNAND_CTL_BASE + 0x20)) /* Nand Flash Spare Area Read Bytes 0-3 */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_4 ((uintptr_t)(BRCMNAND_CTL_BASE + 0x24)) /* Nand Flash Spare Area Read Bytes 4-7 */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_8 ((uintptr_t)(BRCMNAND_CTL_BASE + 0x28)) /* Nand Flash Spare Area Read Bytes 8-11 */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_C ((uintptr_t)(BRCMNAND_CTL_BASE + 0x2c)) /* Nand Flash Spare Area Read Bytes 12-15 */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_0 ((uintptr_t)(BRCMNAND_CTL_BASE + 0x30)) /* Nand Flash Spare Area Write Bytes 0-3 */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_4 ((uintptr_t)(BRCMNAND_CTL_BASE + 0x34)) /* Nand Flash Spare Area Write Bytes 4-7 */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_8 ((uintptr_t)(BRCMNAND_CTL_BASE + 0x38)) /* Nand Flash Spare Area Write Bytes 8-11 */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_C ((uintptr_t)(BRCMNAND_CTL_BASE + 0x3c)) /* Nand Flash Spare Area Write Bytes 12-15 */ +#define BCHP_NAND_ACC_CONTROL ((uintptr_t)(BRCMNAND_CTL_BASE + 0x40)) /* Nand Flash Access Control */ +#define BCHP_NAND_CONFIG ((uintptr_t)(BRCMNAND_CTL_BASE + 0x44)) /* Nand Flash Config */ +#define BCHP_NAND_TIMING_1 ((uintptr_t)(BRCMNAND_CTL_BASE + 0x48)) /* Nand Flash Timing Parameters 1 */ +#define BCHP_NAND_TIMING_2 ((uintptr_t)(BRCMNAND_CTL_BASE + 0x4c)) /* Nand Flash Timing Parameters 2 */ +#define BCHP_NAND_SEMAPHORE ((uintptr_t)(BRCMNAND_CTL_BASE + 0x50)) /* Semaphore */ +#define BCHP_NAND_FLASH_DEVICE_ID ((uintptr_t)(BRCMNAND_CTL_BASE + 0x54)) /* Nand Flash Device ID */ +#define BCHP_NAND_BLOCK_LOCK_STATUS ((uintptr_t)(BRCMNAND_CTL_BASE + 0x58)) /* Nand Flash Block Lock Status */ +#define BCHP_NAND_INTFC_STATUS ((uintptr_t)(BRCMNAND_CTL_BASE + 0x5c)) /* Nand Flash Interface Status */ +#define BCHP_NAND_ECC_CORR_EXT_ADDR ((uintptr_t)(BRCMNAND_CTL_BASE + 0x60)) /* ECC Correctable Error Extended Address */ +#define BCHP_NAND_ECC_CORR_ADDR ((uintptr_t)(BRCMNAND_CTL_BASE + 0x64)) /* ECC Correctable Error Address */ +#define BCHP_NAND_ECC_UNC_EXT_ADDR ((uintptr_t)(BRCMNAND_CTL_BASE + 0x68)) /* ECC Uncorrectable Error Extended Address */ +#define BCHP_NAND_ECC_UNC_ADDR ((uintptr_t)(BRCMNAND_CTL_BASE + 0x6c)) /* ECC Uncorrectable Error Address */ +#define BCHP_NAND_FLASH_READ_EXT_ADDR ((uintptr_t)(BRCMNAND_CTL_BASE + 0x70)) /* Flash Read Data Extended Address */ +#define BCHP_NAND_FLASH_READ_ADDR ((uintptr_t)(BRCMNAND_CTL_BASE + 0x74)) /* Flash Read Data Address */ +#define BCHP_NAND_PROGRAM_PAGE_EXT_ADDR ((uintptr_t)(BRCMNAND_CTL_BASE + 0x78)) /* Page Program Extended Address */ +#define BCHP_NAND_PROGRAM_PAGE_ADDR ((uintptr_t)(BRCMNAND_CTL_BASE + 0x7c)) /* Page Program Address */ +#define BCHP_NAND_COPY_BACK_EXT_ADDR ((uintptr_t)(BRCMNAND_CTL_BASE + 0x80)) /* Copy Back Extended Address */ +#define BCHP_NAND_COPY_BACK_ADDR ((uintptr_t)(BRCMNAND_CTL_BASE + 0x84)) /* Copy Back Address */ +#define BCHP_NAND_BLOCK_ERASE_EXT_ADDR ((uintptr_t)(BRCMNAND_CTL_BASE + 0x88)) /* Block Erase Extended Address */ +#define BCHP_NAND_BLOCK_ERASE_ADDR ((uintptr_t)(BRCMNAND_CTL_BASE + 0x8c)) /* Block Erase Address */ +#define BCHP_NAND_INV_READ_EXT_ADDR ((uintptr_t)(BRCMNAND_CTL_BASE + 0x90)) /* Flash Invalid Data Extended Address */ +#define BCHP_NAND_INV_READ_ADDR ((uintptr_t)(BRCMNAND_CTL_BASE + 0x94)) /* Flash Invalid Data Address */ +#define BCHP_NAND_BLK_WR_PROTECT ((uintptr_t)(BRCMNAND_CTL_BASE + 0x98)) /* Block Write Protect Enable and Size for EBI_CS0b */ + +/*************************************************************************** + *REVISION - NAND Revision + ***************************************************************************/ +/* NAND :: REVISION :: reserved0 [31:16] */ +#define BCHP_NAND_REVISION_reserved0_MASK 0xffff0000 +#define BCHP_NAND_REVISION_reserved0_SHIFT 16 + +/* NAND :: REVISION :: MAJOR [15:08] */ +#define BCHP_NAND_REVISION_MAJOR_MASK 0x0000ff00 +#define BCHP_NAND_REVISION_MAJOR_SHIFT 8 + +/* NAND :: REVISION :: MINOR [07:00] */ +#define BCHP_NAND_REVISION_MINOR_MASK 0x000000ff +#define BCHP_NAND_REVISION_MINOR_SHIFT 0 + +/*************************************************************************** + *CMD_START - Nand Flash Command Start + ***************************************************************************/ +/* NAND :: CMD_START :: reserved0 [31:28] */ +#define BCHP_NAND_CMD_START_reserved0_MASK 0xf0000000 +#define BCHP_NAND_CMD_START_reserved0_SHIFT 28 + +/* NAND :: CMD_START :: OPCODE [27:24] */ +#define BCHP_NAND_CMD_START_OPCODE_MASK 0x0f000000 +#define BCHP_NAND_CMD_START_OPCODE_SHIFT 24 +#define BCHP_NAND_CMD_START_OPCODE_NULL 0 +#define BCHP_NAND_CMD_START_OPCODE_PAGE_READ 1 +#define BCHP_NAND_CMD_START_OPCODE_SPARE_AREA_READ 2 +#define BCHP_NAND_CMD_START_OPCODE_STATUS_READ 3 +#define BCHP_NAND_CMD_START_OPCODE_PROGRAM_PAGE 4 +#define BCHP_NAND_CMD_START_OPCODE_PROGRAM_SPARE_AREA 5 +#define BCHP_NAND_CMD_START_OPCODE_COPY_BACK 6 +#define BCHP_NAND_CMD_START_OPCODE_DEVICE_ID_READ 7 +#define BCHP_NAND_CMD_START_OPCODE_BLOCK_ERASE 8 +#define BCHP_NAND_CMD_START_OPCODE_FLASH_RESET 9 +#define BCHP_NAND_CMD_START_OPCODE_BLOCKS_LOCK 10 +#define BCHP_NAND_CMD_START_OPCODE_BLOCKS_LOCK_DOWN 11 +#define BCHP_NAND_CMD_START_OPCODE_BLOCKS_UNLOCK 12 +#define BCHP_NAND_CMD_START_OPCODE_READ_BLOCKS_LOCK_STATUS 13 + +/* NAND :: CMD_START :: reserved1 [23:00] */ +#define BCHP_NAND_CMD_START_reserved1_MASK 0x00ffffff +#define BCHP_NAND_CMD_START_reserved1_SHIFT 0 + +/*************************************************************************** + *CMD_EXT_ADDRESS - Nand Flash Command Extended Address + ***************************************************************************/ +/* NAND :: CMD_EXT_ADDRESS :: reserved0 [31:19] */ +#define BCHP_NAND_CMD_EXT_ADDRESS_reserved0_MASK 0xfff80000 +#define BCHP_NAND_CMD_EXT_ADDRESS_reserved0_SHIFT 19 + +/* NAND :: CMD_EXT_ADDRESS :: CS_SEL [18:16] */ +#define BCHP_NAND_CMD_EXT_ADDRESS_CS_SEL_MASK 0x00070000 +#define BCHP_NAND_CMD_EXT_ADDRESS_CS_SEL_SHIFT 16 + +/* NAND :: CMD_EXT_ADDRESS :: EXT_ADDRESS [15:00] */ +#define BCHP_NAND_CMD_EXT_ADDRESS_EXT_ADDRESS_MASK 0x0000ffff +#define BCHP_NAND_CMD_EXT_ADDRESS_EXT_ADDRESS_SHIFT 0 + +/*************************************************************************** + *CMD_ADDRESS - Nand Flash Command Address + ***************************************************************************/ +/* NAND :: CMD_ADDRESS :: ADDRESS [31:00] */ +#define BCHP_NAND_CMD_ADDRESS_ADDRESS_MASK 0xffffffff +#define BCHP_NAND_CMD_ADDRESS_ADDRESS_SHIFT 0 + +/*************************************************************************** + *CMD_END_ADDRESS - Nand Flash Command End Address + ***************************************************************************/ +/* NAND :: CMD_END_ADDRESS :: ADDRESS [31:00] */ +#define BCHP_NAND_CMD_END_ADDRESS_ADDRESS_MASK 0xffffffff +#define BCHP_NAND_CMD_END_ADDRESS_ADDRESS_SHIFT 0 + +/*************************************************************************** + *CS_NAND_SELECT - Nand Flash EBI CS Select + ***************************************************************************/ +/* NAND :: CS_NAND_SELECT :: CS_LOCK [31:31] */ +#define BCHP_NAND_CS_NAND_SELECT_CS_LOCK_MASK 0x80000000 +#define BCHP_NAND_CS_NAND_SELECT_CS_LOCK_SHIFT 31 + +/* NAND :: CS_NAND_SELECT :: AUTO_DEVICE_ID_CONFIG [30:30] */ +#define BCHP_NAND_CS_NAND_SELECT_AUTO_DEVICE_ID_CONFIG_MASK 0x40000000 +#define BCHP_NAND_CS_NAND_SELECT_AUTO_DEVICE_ID_CONFIG_SHIFT 30 + +/* NAND :: CS_NAND_SELECT :: reserved0 [29:29] */ +#define BCHP_NAND_CS_NAND_SELECT_reserved0_MASK 0x20000000 +#define BCHP_NAND_CS_NAND_SELECT_reserved0_SHIFT 29 + +/* NAND :: CS_NAND_SELECT :: WR_PROTECT_BLK0 [28:28] */ +#define BCHP_NAND_CS_NAND_SELECT_WR_PROTECT_BLK0_MASK 0x10000000 +#define BCHP_NAND_CS_NAND_SELECT_WR_PROTECT_BLK0_SHIFT 28 + +/* NAND :: CS_NAND_SELECT :: reserved1 [27:16] */ +#define BCHP_NAND_CS_NAND_SELECT_reserved1_MASK 0x0fff0000 +#define BCHP_NAND_CS_NAND_SELECT_reserved1_SHIFT 16 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_7_USES_NAND [15:15] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_7_USES_NAND_MASK 0x00008000 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_7_USES_NAND_SHIFT 15 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_6_USES_NAND [14:14] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_6_USES_NAND_MASK 0x00004000 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_6_USES_NAND_SHIFT 14 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_5_USES_NAND [13:13] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_5_USES_NAND_MASK 0x00002000 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_5_USES_NAND_SHIFT 13 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_4_USES_NAND [12:12] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_4_USES_NAND_MASK 0x00001000 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_4_USES_NAND_SHIFT 12 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_3_USES_NAND [11:11] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_3_USES_NAND_MASK 0x00000800 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_3_USES_NAND_SHIFT 11 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_2_USES_NAND [10:10] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_2_USES_NAND_MASK 0x00000400 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_2_USES_NAND_SHIFT 10 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_1_USES_NAND [09:09] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_1_USES_NAND_MASK 0x00000200 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_1_USES_NAND_SHIFT 9 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_0_USES_NAND [08:08] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_0_USES_NAND_MASK 0x00000100 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_0_USES_NAND_SHIFT 8 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_7_SEL [07:07] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_7_SEL_MASK 0x00000080 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_7_SEL_SHIFT 7 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_6_SEL [06:06] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_6_SEL_MASK 0x00000040 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_6_SEL_SHIFT 6 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_5_SEL [05:05] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_5_SEL_MASK 0x00000020 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_5_SEL_SHIFT 5 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_4_SEL [04:04] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_4_SEL_MASK 0x00000010 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_4_SEL_SHIFT 4 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_3_SEL [03:03] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_3_SEL_MASK 0x00000008 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_3_SEL_SHIFT 3 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_2_SEL [02:02] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_2_SEL_MASK 0x00000004 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_2_SEL_SHIFT 2 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_1_SEL [01:01] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_1_SEL_MASK 0x00000002 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_1_SEL_SHIFT 1 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_0_SEL [00:00] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_0_SEL_MASK 0x00000001 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_0_SEL_SHIFT 0 + +/*************************************************************************** + *CS_NAND_XOR - Nand Flash EBI CS Address XOR with 1FC0 Control + ***************************************************************************/ +/* NAND :: CS_NAND_XOR :: reserved0 [31:08] */ +#define BCHP_NAND_CS_NAND_XOR_reserved0_MASK 0xffffff00 +#define BCHP_NAND_CS_NAND_XOR_reserved0_SHIFT 8 + +/* NAND :: CS_NAND_XOR :: EBI_CS_7_ADDR_1FC0_XOR [07:07] */ +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_7_ADDR_1FC0_XOR_MASK 0x00000080 +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_7_ADDR_1FC0_XOR_SHIFT 7 + +/* NAND :: CS_NAND_XOR :: EBI_CS_6_ADDR_1FC0_XOR [06:06] */ +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_6_ADDR_1FC0_XOR_MASK 0x00000040 +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_6_ADDR_1FC0_XOR_SHIFT 6 + +/* NAND :: CS_NAND_XOR :: EBI_CS_5_ADDR_1FC0_XOR [05:05] */ +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_5_ADDR_1FC0_XOR_MASK 0x00000020 +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_5_ADDR_1FC0_XOR_SHIFT 5 + +/* NAND :: CS_NAND_XOR :: EBI_CS_4_ADDR_1FC0_XOR [04:04] */ +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_4_ADDR_1FC0_XOR_MASK 0x00000010 +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_4_ADDR_1FC0_XOR_SHIFT 4 + +/* NAND :: CS_NAND_XOR :: EBI_CS_3_ADDR_1FC0_XOR [03:03] */ +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_3_ADDR_1FC0_XOR_MASK 0x00000008 +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_3_ADDR_1FC0_XOR_SHIFT 3 + +/* NAND :: CS_NAND_XOR :: EBI_CS_2_ADDR_1FC0_XOR [02:02] */ +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_2_ADDR_1FC0_XOR_MASK 0x00000004 +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_2_ADDR_1FC0_XOR_SHIFT 2 + +/* NAND :: CS_NAND_XOR :: EBI_CS_1_ADDR_1FC0_XOR [01:01] */ +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_1_ADDR_1FC0_XOR_MASK 0x00000002 +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_1_ADDR_1FC0_XOR_SHIFT 1 + +/* NAND :: CS_NAND_XOR :: EBI_CS_0_ADDR_1FC0_XOR [00:00] */ +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_0_ADDR_1FC0_XOR_MASK 0x00000001 +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_0_ADDR_1FC0_XOR_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_READ_OFS_0 - Nand Flash Spare Area Read Bytes 0-3 + ***************************************************************************/ +/* NAND :: SPARE_AREA_READ_OFS_0 :: BYTE_OFS_0 [31:24] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_0_BYTE_OFS_0_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_0_BYTE_OFS_0_SHIFT 24 + +/* NAND :: SPARE_AREA_READ_OFS_0 :: BYTE_OFS_1 [23:16] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_0_BYTE_OFS_1_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_0_BYTE_OFS_1_SHIFT 16 + +/* NAND :: SPARE_AREA_READ_OFS_0 :: BYTE_OFS_2 [15:08] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_0_BYTE_OFS_2_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_READ_OFS_0_BYTE_OFS_2_SHIFT 8 + +/* NAND :: SPARE_AREA_READ_OFS_0 :: BYTE_OFS_3 [07:00] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_0_BYTE_OFS_3_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_READ_OFS_0_BYTE_OFS_3_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_READ_OFS_4 - Nand Flash Spare Area Read Bytes 4-7 + ***************************************************************************/ +/* NAND :: SPARE_AREA_READ_OFS_4 :: BYTE_OFS_4 [31:24] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_4_BYTE_OFS_4_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_4_BYTE_OFS_4_SHIFT 24 + +/* NAND :: SPARE_AREA_READ_OFS_4 :: BYTE_OFS_5 [23:16] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_4_BYTE_OFS_5_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_4_BYTE_OFS_5_SHIFT 16 + +/* NAND :: SPARE_AREA_READ_OFS_4 :: BYTE_OFS_6 [15:08] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_4_BYTE_OFS_6_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_READ_OFS_4_BYTE_OFS_6_SHIFT 8 + +/* NAND :: SPARE_AREA_READ_OFS_4 :: BYTE_OFS_7 [07:00] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_4_BYTE_OFS_7_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_READ_OFS_4_BYTE_OFS_7_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_READ_OFS_8 - Nand Flash Spare Area Read Bytes 8-11 + ***************************************************************************/ +/* NAND :: SPARE_AREA_READ_OFS_8 :: BYTE_OFS_8 [31:24] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_8_BYTE_OFS_8_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_8_BYTE_OFS_8_SHIFT 24 + +/* NAND :: SPARE_AREA_READ_OFS_8 :: BYTE_OFS_9 [23:16] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_8_BYTE_OFS_9_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_8_BYTE_OFS_9_SHIFT 16 + +/* NAND :: SPARE_AREA_READ_OFS_8 :: BYTE_OFS_10 [15:08] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_8_BYTE_OFS_10_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_READ_OFS_8_BYTE_OFS_10_SHIFT 8 + +/* NAND :: SPARE_AREA_READ_OFS_8 :: BYTE_OFS_11 [07:00] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_8_BYTE_OFS_11_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_READ_OFS_8_BYTE_OFS_11_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_READ_OFS_C - Nand Flash Spare Area Read Bytes 12-15 + ***************************************************************************/ +/* NAND :: SPARE_AREA_READ_OFS_C :: BYTE_OFS_12 [31:24] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_C_BYTE_OFS_12_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_C_BYTE_OFS_12_SHIFT 24 + +/* NAND :: SPARE_AREA_READ_OFS_C :: BYTE_OFS_13 [23:16] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_C_BYTE_OFS_13_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_C_BYTE_OFS_13_SHIFT 16 + +/* NAND :: SPARE_AREA_READ_OFS_C :: BYTE_OFS_14 [15:08] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_C_BYTE_OFS_14_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_READ_OFS_C_BYTE_OFS_14_SHIFT 8 + +/* NAND :: SPARE_AREA_READ_OFS_C :: BYTE_OFS_15 [07:00] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_C_BYTE_OFS_15_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_READ_OFS_C_BYTE_OFS_15_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_WRITE_OFS_0 - Nand Flash Spare Area Write Bytes 0-3 + ***************************************************************************/ +/* NAND :: SPARE_AREA_WRITE_OFS_0 :: BYTE_OFS_0 [31:24] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_0_BYTE_OFS_0_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_0_BYTE_OFS_0_SHIFT 24 + +/* NAND :: SPARE_AREA_WRITE_OFS_0 :: BYTE_OFS_1 [23:16] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_0_BYTE_OFS_1_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_0_BYTE_OFS_1_SHIFT 16 + +/* NAND :: SPARE_AREA_WRITE_OFS_0 :: BYTE_OFS_2 [15:08] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_0_BYTE_OFS_2_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_0_BYTE_OFS_2_SHIFT 8 + +/* NAND :: SPARE_AREA_WRITE_OFS_0 :: BYTE_OFS_3 [07:00] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_0_BYTE_OFS_3_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_0_BYTE_OFS_3_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_WRITE_OFS_4 - Nand Flash Spare Area Write Bytes 4-7 + ***************************************************************************/ +/* NAND :: SPARE_AREA_WRITE_OFS_4 :: BYTE_OFS_4 [31:24] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_4_BYTE_OFS_4_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_4_BYTE_OFS_4_SHIFT 24 + +/* NAND :: SPARE_AREA_WRITE_OFS_4 :: BYTE_OFS_5 [23:16] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_4_BYTE_OFS_5_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_4_BYTE_OFS_5_SHIFT 16 + +/* NAND :: SPARE_AREA_WRITE_OFS_4 :: BYTE_OFS_6 [15:08] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_4_BYTE_OFS_6_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_4_BYTE_OFS_6_SHIFT 8 + +/* NAND :: SPARE_AREA_WRITE_OFS_4 :: BYTE_OFS_7 [07:00] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_4_BYTE_OFS_7_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_4_BYTE_OFS_7_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_WRITE_OFS_8 - Nand Flash Spare Area Write Bytes 8-11 + ***************************************************************************/ +/* NAND :: SPARE_AREA_WRITE_OFS_8 :: BYTE_OFS_8 [31:24] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_8_BYTE_OFS_8_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_8_BYTE_OFS_8_SHIFT 24 + +/* NAND :: SPARE_AREA_WRITE_OFS_8 :: BYTE_OFS_9 [23:16] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_8_BYTE_OFS_9_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_8_BYTE_OFS_9_SHIFT 16 + +/* NAND :: SPARE_AREA_WRITE_OFS_8 :: BYTE_OFS_10 [15:08] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_8_BYTE_OFS_10_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_8_BYTE_OFS_10_SHIFT 8 + +/* NAND :: SPARE_AREA_WRITE_OFS_8 :: BYTE_OFS_11 [07:00] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_8_BYTE_OFS_11_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_8_BYTE_OFS_11_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_WRITE_OFS_C - Nand Flash Spare Area Write Bytes 12-15 + ***************************************************************************/ +/* NAND :: SPARE_AREA_WRITE_OFS_C :: BYTE_OFS_12 [31:24] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_C_BYTE_OFS_12_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_C_BYTE_OFS_12_SHIFT 24 + +/* NAND :: SPARE_AREA_WRITE_OFS_C :: BYTE_OFS_13 [23:16] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_C_BYTE_OFS_13_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_C_BYTE_OFS_13_SHIFT 16 + +/* NAND :: SPARE_AREA_WRITE_OFS_C :: BYTE_OFS_14 [15:08] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_C_BYTE_OFS_14_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_C_BYTE_OFS_14_SHIFT 8 + +/* NAND :: SPARE_AREA_WRITE_OFS_C :: BYTE_OFS_15 [07:00] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_C_BYTE_OFS_15_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_C_BYTE_OFS_15_SHIFT 0 + +/*************************************************************************** + *ACC_CONTROL - Nand Flash Access Control + ***************************************************************************/ +/* NAND :: ACC_CONTROL :: RD_ECC_EN [31:31] */ +#define BCHP_NAND_ACC_CONTROL_RD_ECC_EN_MASK 0x80000000 +#define BCHP_NAND_ACC_CONTROL_RD_ECC_EN_SHIFT 31 + +/* NAND :: ACC_CONTROL :: WR_ECC_EN [30:30] */ +#define BCHP_NAND_ACC_CONTROL_WR_ECC_EN_MASK 0x40000000 +#define BCHP_NAND_ACC_CONTROL_WR_ECC_EN_SHIFT 30 + +/* NAND :: ACC_CONTROL :: RD_ECC_BLK0_EN [29:29] */ +#define BCHP_NAND_ACC_CONTROL_RD_ECC_BLK0_EN_MASK 0x20000000 +#define BCHP_NAND_ACC_CONTROL_RD_ECC_BLK0_EN_SHIFT 29 + +/* NAND :: ACC_CONTROL :: FAST_PGM_RDIN [28:28] */ +#define BCHP_NAND_ACC_CONTROL_FAST_PGM_RDIN_MASK 0x10000000 +#define BCHP_NAND_ACC_CONTROL_FAST_PGM_RDIN_SHIFT 28 + +/* NAND :: ACC_CONTROL :: RD_ERASED_ECC_EN [27:27] */ +#define BCHP_NAND_ACC_CONTROL_RD_ERASED_ECC_EN_MASK 0x08000000 +#define BCHP_NAND_ACC_CONTROL_RD_ERASED_ECC_EN_SHIFT 27 + +/* NAND :: ACC_CONTROL :: reserved0 [26:26] */ +#define BCHP_NAND_ACC_CONTROL_reserved0_MASK 0x04000000 +#define BCHP_NAND_ACC_CONTROL_reserved0_SHIFT 26 + +/* NAND :: ACC_CONTROL :: WR_PREEMPT_EN [25:25] */ +#define BCHP_NAND_ACC_CONTROL_WR_PREEMPT_EN_MASK 0x02000000 +#define BCHP_NAND_ACC_CONTROL_WR_PREEMPT_EN_SHIFT 25 + +/* NAND :: ACC_CONTROL :: PAGE_HIT_EN [24:24] */ +#define BCHP_NAND_ACC_CONTROL_PAGE_HIT_EN_MASK 0x01000000 +#define BCHP_NAND_ACC_CONTROL_PAGE_HIT_EN_SHIFT 24 + +/* NAND :: ACC_CONTROL :: reserved1 [23:00] */ +#define BCHP_NAND_ACC_CONTROL_reserved1_MASK 0x00ffffff +#define BCHP_NAND_ACC_CONTROL_reserved1_SHIFT 0 + +/*************************************************************************** + *CONFIG - Nand Flash Config + ***************************************************************************/ +/* NAND :: CONFIG :: CONFIG_LOCK [31:31] */ +#define BCHP_NAND_CONFIG_CONFIG_LOCK_MASK 0x80000000 +#define BCHP_NAND_CONFIG_CONFIG_LOCK_SHIFT 31 + +/* NAND :: CONFIG :: BLOCK_SIZE [30:28] */ +#if CONFIG_MTD_BRCMNAND_VERSION == CONFIG_MTD_BRCMNAND_VERS_2_2 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_MASK 0x70000000 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_SHIFT 28 +#elif CONFIG_MTD_BRCMNAND_VERSION == CONFIG_MTD_BRCMNAND_VERS_2_1 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_MASK 0x30000000 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_SHIFT 28 +#endif +#define BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_512KB 3 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_128KB 1 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_16KB 0 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_8KB 2 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_256KB 4 + +/* NAND :: CONFIG :: DEVICE_SIZE [27:24] */ +#define BCHP_NAND_CONFIG_DEVICE_SIZE_MASK 0x0f000000 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_SHIFT 24 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_4MB 0 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_8MB 1 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_16MB 2 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_32MB 3 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_64MB 4 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_128MB 5 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_256MB 6 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_512MB 7 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_1GB 8 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_2GB 9 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_4GB 10 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_8GB 11 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_16GB 12 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_32GB 13 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_64GB 14 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_128GB 15 + +/* NAND :: CONFIG :: DEVICE_WIDTH [23:23] */ +#define BCHP_NAND_CONFIG_DEVICE_WIDTH_MASK 0x00800000 +#define BCHP_NAND_CONFIG_DEVICE_WIDTH_SHIFT 23 +#define BCHP_NAND_CONFIG_DEVICE_WIDTH_DVC_WIDTH_8 0 +#define BCHP_NAND_CONFIG_DEVICE_WIDTH_DVC_WIDTH_16 1 + +/* NAND :: CONFIG :: reserved0 [22:22] */ +#define BCHP_NAND_CONFIG_reserved0_MASK 0x00400000 +#define BCHP_NAND_CONFIG_reserved0_SHIFT 22 + +/* NAND :: CONFIG :: PAGE_SIZE [21:20] */ +#if CONFIG_MTD_BRCMNAND_VERSION == CONFIG_MTD_BRCMNAND_VERS_2_2 +#define BCHP_NAND_CONFIG_PAGE_SIZE_MASK 0x00300000 +#define BCHP_NAND_CONFIG_PAGE_SIZE_SHIFT 20 +#elif CONFIG_MTD_BRCMNAND_VERSION == CONFIG_MTD_BRCMNAND_VERS_2_1 +#define BCHP_NAND_CONFIG_PAGE_SIZE_MASK 0x40000000 +#define BCHP_NAND_CONFIG_PAGE_SIZE_SHIFT 30 +#endif +#define BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_512 0 +#define BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_2KB 1 +#define BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_4KB 2 + +/* NAND :: CONFIG :: reserved1 [19:19] */ +#define BCHP_NAND_CONFIG_reserved1_MASK 0x00080000 +#define BCHP_NAND_CONFIG_reserved1_SHIFT 19 + +/* NAND :: CONFIG :: FUL_ADR_BYTES [18:16] */ +#define BCHP_NAND_CONFIG_FUL_ADR_BYTES_MASK 0x00070000 +#define BCHP_NAND_CONFIG_FUL_ADR_BYTES_SHIFT 16 + +/* NAND :: CONFIG :: reserved2 [15:15] */ +#define BCHP_NAND_CONFIG_reserved2_MASK 0x00008000 +#define BCHP_NAND_CONFIG_reserved2_SHIFT 15 + +/* NAND :: CONFIG :: COL_ADR_BYTES [14:12] */ +#define BCHP_NAND_CONFIG_COL_ADR_BYTES_MASK 0x00007000 +#define BCHP_NAND_CONFIG_COL_ADR_BYTES_SHIFT 12 + +/* NAND :: CONFIG :: reserved3 [11:11] */ +#define BCHP_NAND_CONFIG_reserved3_MASK 0x00000800 +#define BCHP_NAND_CONFIG_reserved3_SHIFT 11 + +/* NAND :: CONFIG :: BLK_ADR_BYTES [10:08] */ +#define BCHP_NAND_CONFIG_BLK_ADR_BYTES_MASK 0x00000700 +#define BCHP_NAND_CONFIG_BLK_ADR_BYTES_SHIFT 8 + +/* NAND :: CONFIG :: reserved4 [07:00] */ +#define BCHP_NAND_CONFIG_reserved4_MASK 0x000000ff +#define BCHP_NAND_CONFIG_reserved4_SHIFT 0 + +/*************************************************************************** + *TIMING_1 - Nand Flash Timing Parameters 1 + ***************************************************************************/ +/* NAND :: TIMING_1 :: tWP [31:28] */ +#define BCHP_NAND_TIMING_1_tWP_MASK 0xf0000000 +#define BCHP_NAND_TIMING_1_tWP_SHIFT 28 + +/* NAND :: TIMING_1 :: tWH [27:24] */ +#define BCHP_NAND_TIMING_1_tWH_MASK 0x0f000000 +#define BCHP_NAND_TIMING_1_tWH_SHIFT 24 + +/* NAND :: TIMING_1 :: tRP [23:20] */ +#define BCHP_NAND_TIMING_1_tRP_MASK 0x00f00000 +#define BCHP_NAND_TIMING_1_tRP_SHIFT 20 + +/* NAND :: TIMING_1 :: tREH [19:16] */ +#define BCHP_NAND_TIMING_1_tREH_MASK 0x000f0000 +#define BCHP_NAND_TIMING_1_tREH_SHIFT 16 + +/* NAND :: TIMING_1 :: tCS [15:12] */ +#define BCHP_NAND_TIMING_1_tCS_MASK 0x0000f000 +#define BCHP_NAND_TIMING_1_tCS_SHIFT 12 + +/* NAND :: TIMING_1 :: tCLH [11:08] */ +#define BCHP_NAND_TIMING_1_tCLH_MASK 0x00000f00 +#define BCHP_NAND_TIMING_1_tCLH_SHIFT 8 + +/* NAND :: TIMING_1 :: tALH [07:04] */ +#define BCHP_NAND_TIMING_1_tALH_MASK 0x000000f0 +#define BCHP_NAND_TIMING_1_tALH_SHIFT 4 + +/* NAND :: TIMING_1 :: tADL [03:00] */ +#define BCHP_NAND_TIMING_1_tADL_MASK 0x0000000f +#define BCHP_NAND_TIMING_1_tADL_SHIFT 0 + +/*************************************************************************** + *TIMING_2 - Nand Flash Timing Parameters 2 + ***************************************************************************/ +/* NAND :: TIMING_2 :: reserved0 [31:12] */ +#define BCHP_NAND_TIMING_2_reserved0_MASK 0xfffff000 +#define BCHP_NAND_TIMING_2_reserved0_SHIFT 12 + +/* NAND :: TIMING_2 :: tWB [11:08] */ +#define BCHP_NAND_TIMING_2_tWB_MASK 0x00000f00 +#define BCHP_NAND_TIMING_2_tWB_SHIFT 8 + +/* NAND :: TIMING_2 :: tWHR [07:04] */ +#define BCHP_NAND_TIMING_2_tWHR_MASK 0x000000f0 +#define BCHP_NAND_TIMING_2_tWHR_SHIFT 4 + +/* NAND :: TIMING_2 :: tREAD [03:00] */ +#define BCHP_NAND_TIMING_2_tREAD_MASK 0x0000000f +#define BCHP_NAND_TIMING_2_tREAD_SHIFT 0 + +/*************************************************************************** + *SEMAPHORE - Semaphore + ***************************************************************************/ +/* NAND :: SEMAPHORE :: reserved0 [31:08] */ +#define BCHP_NAND_SEMAPHORE_reserved0_MASK 0xffffff00 +#define BCHP_NAND_SEMAPHORE_reserved0_SHIFT 8 + +/* NAND :: SEMAPHORE :: semaphore_ctrl [07:00] */ +#define BCHP_NAND_SEMAPHORE_semaphore_ctrl_MASK 0x000000ff +#define BCHP_NAND_SEMAPHORE_semaphore_ctrl_SHIFT 0 + +/*************************************************************************** + *FLASH_DEVICE_ID - Nand Flash Device ID + ***************************************************************************/ +/* NAND :: FLASH_DEVICE_ID :: BYTE_0 [31:24] */ +#define BCHP_NAND_FLASH_DEVICE_ID_BYTE_0_MASK 0xff000000 +#define BCHP_NAND_FLASH_DEVICE_ID_BYTE_0_SHIFT 24 + +/* NAND :: FLASH_DEVICE_ID :: BYTE_1 [23:16] */ +#define BCHP_NAND_FLASH_DEVICE_ID_BYTE_1_MASK 0x00ff0000 +#define BCHP_NAND_FLASH_DEVICE_ID_BYTE_1_SHIFT 16 + +/* NAND :: FLASH_DEVICE_ID :: BYTE_2 [15:08] */ +#define BCHP_NAND_FLASH_DEVICE_ID_BYTE_2_MASK 0x0000ff00 +#define BCHP_NAND_FLASH_DEVICE_ID_BYTE_2_SHIFT 8 + +/* NAND :: FLASH_DEVICE_ID :: BYTE_3 [07:00] */ +#define BCHP_NAND_FLASH_DEVICE_ID_BYTE_3_MASK 0x000000ff +#define BCHP_NAND_FLASH_DEVICE_ID_BYTE_3_SHIFT 0 + +/*************************************************************************** + *BLOCK_LOCK_STATUS - Nand Flash Block Lock Status + ***************************************************************************/ +/* NAND :: BLOCK_LOCK_STATUS :: reserved0 [31:08] */ +#define BCHP_NAND_BLOCK_LOCK_STATUS_reserved0_MASK 0xffffff00 +#define BCHP_NAND_BLOCK_LOCK_STATUS_reserved0_SHIFT 8 + +/* NAND :: BLOCK_LOCK_STATUS :: STATUS [07:00] */ +#define BCHP_NAND_BLOCK_LOCK_STATUS_STATUS_MASK 0x000000ff +#define BCHP_NAND_BLOCK_LOCK_STATUS_STATUS_SHIFT 0 + +/*************************************************************************** + *INTFC_STATUS - Nand Flash Interface Status + ***************************************************************************/ +/* NAND :: INTFC_STATUS :: CTLR_READY [31:31] */ +#define BCHP_NAND_INTFC_STATUS_CTLR_READY_MASK 0x80000000 +#define BCHP_NAND_INTFC_STATUS_CTLR_READY_SHIFT 31 + +/* NAND :: INTFC_STATUS :: FLASH_READY [30:30] */ +#define BCHP_NAND_INTFC_STATUS_FLASH_READY_MASK 0x40000000 +#define BCHP_NAND_INTFC_STATUS_FLASH_READY_SHIFT 30 + +/* NAND :: INTFC_STATUS :: CACHE_VALID [29:29] */ +#define BCHP_NAND_INTFC_STATUS_CACHE_VALID_MASK 0x20000000 +#define BCHP_NAND_INTFC_STATUS_CACHE_VALID_SHIFT 29 + +/* NAND :: INTFC_STATUS :: SPARE_AREA_VALID [28:28] */ +#define BCHP_NAND_INTFC_STATUS_SPARE_AREA_VALID_MASK 0x10000000 +#define BCHP_NAND_INTFC_STATUS_SPARE_AREA_VALID_SHIFT 28 + +/* NAND :: INTFC_STATUS :: reserved0 [27:08] */ +#define BCHP_NAND_INTFC_STATUS_reserved0_MASK 0x0fffff00 +#define BCHP_NAND_INTFC_STATUS_reserved0_SHIFT 8 + +/* NAND :: INTFC_STATUS :: FLASH_STATUS [07:00] */ +#define BCHP_NAND_INTFC_STATUS_FLASH_STATUS_MASK 0x000000ff +#define BCHP_NAND_INTFC_STATUS_FLASH_STATUS_SHIFT 0 + +/*************************************************************************** + *ECC_CORR_EXT_ADDR - ECC Correctable Error Extended Address + ***************************************************************************/ +/* NAND :: ECC_CORR_EXT_ADDR :: reserved0 [31:19] */ +#define BCHP_NAND_ECC_CORR_EXT_ADDR_reserved0_MASK 0xfff80000 +#define BCHP_NAND_ECC_CORR_EXT_ADDR_reserved0_SHIFT 19 + +/* NAND :: ECC_CORR_EXT_ADDR :: CS_SEL [18:16] */ +#define BCHP_NAND_ECC_CORR_EXT_ADDR_CS_SEL_MASK 0x00070000 +#define BCHP_NAND_ECC_CORR_EXT_ADDR_CS_SEL_SHIFT 16 + +/* NAND :: ECC_CORR_EXT_ADDR :: EXT_ADDRESS [15:00] */ +#define BCHP_NAND_ECC_CORR_EXT_ADDR_EXT_ADDRESS_MASK 0x0000ffff +#define BCHP_NAND_ECC_CORR_EXT_ADDR_EXT_ADDRESS_SHIFT 0 + +/*************************************************************************** + *ECC_CORR_ADDR - ECC Correctable Error Address + ***************************************************************************/ +/* NAND :: ECC_CORR_ADDR :: ADDRESS [31:00] */ +#define BCHP_NAND_ECC_CORR_ADDR_ADDRESS_MASK 0xffffffff +#define BCHP_NAND_ECC_CORR_ADDR_ADDRESS_SHIFT 0 + +/*************************************************************************** + *ECC_UNC_EXT_ADDR - ECC Uncorrectable Error Extended Address + ***************************************************************************/ +/* NAND :: ECC_UNC_EXT_ADDR :: reserved0 [31:19] */ +#define BCHP_NAND_ECC_UNC_EXT_ADDR_reserved0_MASK 0xfff80000 +#define BCHP_NAND_ECC_UNC_EXT_ADDR_reserved0_SHIFT 19 + +/* NAND :: ECC_UNC_EXT_ADDR :: CS_SEL [18:16] */ +#define BCHP_NAND_ECC_UNC_EXT_ADDR_CS_SEL_MASK 0x00070000 +#define BCHP_NAND_ECC_UNC_EXT_ADDR_CS_SEL_SHIFT 16 + +/* NAND :: ECC_UNC_EXT_ADDR :: EXT_ADDRESS [15:00] */ +#define BCHP_NAND_ECC_UNC_EXT_ADDR_EXT_ADDRESS_MASK 0x0000ffff +#define BCHP_NAND_ECC_UNC_EXT_ADDR_EXT_ADDRESS_SHIFT 0 + +/*************************************************************************** + *ECC_UNC_ADDR - ECC Uncorrectable Error Address + ***************************************************************************/ +/* NAND :: ECC_UNC_ADDR :: ADDRESS [31:00] */ +#define BCHP_NAND_ECC_UNC_ADDR_ADDRESS_MASK 0xffffffff +#define BCHP_NAND_ECC_UNC_ADDR_ADDRESS_SHIFT 0 + +/*************************************************************************** + *FLASH_READ_EXT_ADDR - Flash Read Data Extended Address + ***************************************************************************/ +/* NAND :: FLASH_READ_EXT_ADDR :: reserved0 [31:19] */ +#define BCHP_NAND_FLASH_READ_EXT_ADDR_reserved0_MASK 0xfff80000 +#define BCHP_NAND_FLASH_READ_EXT_ADDR_reserved0_SHIFT 19 + +/* NAND :: FLASH_READ_EXT_ADDR :: CS_SEL [18:16] */ +#define BCHP_NAND_FLASH_READ_EXT_ADDR_CS_SEL_MASK 0x00070000 +#define BCHP_NAND_FLASH_READ_EXT_ADDR_CS_SEL_SHIFT 16 + +/* NAND :: FLASH_READ_EXT_ADDR :: EXT_ADDRESS [15:00] */ +#define BCHP_NAND_FLASH_READ_EXT_ADDR_EXT_ADDRESS_MASK 0x0000ffff +#define BCHP_NAND_FLASH_READ_EXT_ADDR_EXT_ADDRESS_SHIFT 0 + +/*************************************************************************** + *FLASH_READ_ADDR - Flash Read Data Address + ***************************************************************************/ +/* NAND :: FLASH_READ_ADDR :: ADDRESS [31:00] */ +#define BCHP_NAND_FLASH_READ_ADDR_ADDRESS_MASK 0xffffffff +#define BCHP_NAND_FLASH_READ_ADDR_ADDRESS_SHIFT 0 + +/*************************************************************************** + *PROGRAM_PAGE_EXT_ADDR - Page Program Extended Address + ***************************************************************************/ +/* NAND :: PROGRAM_PAGE_EXT_ADDR :: reserved0 [31:19] */ +#define BCHP_NAND_PROGRAM_PAGE_EXT_ADDR_reserved0_MASK 0xfff80000 +#define BCHP_NAND_PROGRAM_PAGE_EXT_ADDR_reserved0_SHIFT 19 + +/* NAND :: PROGRAM_PAGE_EXT_ADDR :: CS_SEL [18:16] */ +#define BCHP_NAND_PROGRAM_PAGE_EXT_ADDR_CS_SEL_MASK 0x00070000 +#define BCHP_NAND_PROGRAM_PAGE_EXT_ADDR_CS_SEL_SHIFT 16 + +/* NAND :: PROGRAM_PAGE_EXT_ADDR :: EXT_ADDRESS [15:00] */ +#define BCHP_NAND_PROGRAM_PAGE_EXT_ADDR_EXT_ADDRESS_MASK 0x0000ffff +#define BCHP_NAND_PROGRAM_PAGE_EXT_ADDR_EXT_ADDRESS_SHIFT 0 + +/*************************************************************************** + *PROGRAM_PAGE_ADDR - Page Program Address + ***************************************************************************/ +/* NAND :: PROGRAM_PAGE_ADDR :: ADDRESS [31:00] */ +#define BCHP_NAND_PROGRAM_PAGE_ADDR_ADDRESS_MASK 0xffffffff +#define BCHP_NAND_PROGRAM_PAGE_ADDR_ADDRESS_SHIFT 0 + +/*************************************************************************** + *COPY_BACK_EXT_ADDR - Copy Back Extended Address + ***************************************************************************/ +/* NAND :: COPY_BACK_EXT_ADDR :: reserved0 [31:19] */ +#define BCHP_NAND_COPY_BACK_EXT_ADDR_reserved0_MASK 0xfff80000 +#define BCHP_NAND_COPY_BACK_EXT_ADDR_reserved0_SHIFT 19 + +/* NAND :: COPY_BACK_EXT_ADDR :: CS_SEL [18:16] */ +#define BCHP_NAND_COPY_BACK_EXT_ADDR_CS_SEL_MASK 0x00070000 +#define BCHP_NAND_COPY_BACK_EXT_ADDR_CS_SEL_SHIFT 16 + +/* NAND :: COPY_BACK_EXT_ADDR :: EXT_ADDRESS [15:00] */ +#define BCHP_NAND_COPY_BACK_EXT_ADDR_EXT_ADDRESS_MASK 0x0000ffff +#define BCHP_NAND_COPY_BACK_EXT_ADDR_EXT_ADDRESS_SHIFT 0 + +/*************************************************************************** + *COPY_BACK_ADDR - Copy Back Address + ***************************************************************************/ +/* NAND :: COPY_BACK_ADDR :: ADDRESS [31:00] */ +#define BCHP_NAND_COPY_BACK_ADDR_ADDRESS_MASK 0xffffffff +#define BCHP_NAND_COPY_BACK_ADDR_ADDRESS_SHIFT 0 + +/*************************************************************************** + *BLOCK_ERASE_EXT_ADDR - Block Erase Extended Address + ***************************************************************************/ +/* NAND :: BLOCK_ERASE_EXT_ADDR :: reserved0 [31:19] */ +#define BCHP_NAND_BLOCK_ERASE_EXT_ADDR_reserved0_MASK 0xfff80000 +#define BCHP_NAND_BLOCK_ERASE_EXT_ADDR_reserved0_SHIFT 19 + +/* NAND :: BLOCK_ERASE_EXT_ADDR :: CS_SEL [18:16] */ +#define BCHP_NAND_BLOCK_ERASE_EXT_ADDR_CS_SEL_MASK 0x00070000 +#define BCHP_NAND_BLOCK_ERASE_EXT_ADDR_CS_SEL_SHIFT 16 + +/* NAND :: BLOCK_ERASE_EXT_ADDR :: EXT_ADDRESS [15:00] */ +#define BCHP_NAND_BLOCK_ERASE_EXT_ADDR_EXT_ADDRESS_MASK 0x0000ffff +#define BCHP_NAND_BLOCK_ERASE_EXT_ADDR_EXT_ADDRESS_SHIFT 0 + +/*************************************************************************** + *BLOCK_ERASE_ADDR - Block Erase Address + ***************************************************************************/ +/* NAND :: BLOCK_ERASE_ADDR :: ADDRESS [31:00] */ +#define BCHP_NAND_BLOCK_ERASE_ADDR_ADDRESS_MASK 0xffffffff +#define BCHP_NAND_BLOCK_ERASE_ADDR_ADDRESS_SHIFT 0 + +/*************************************************************************** + *INV_READ_EXT_ADDR - Flash Invalid Data Extended Address + ***************************************************************************/ +/* NAND :: INV_READ_EXT_ADDR :: reserved0 [31:19] */ +#define BCHP_NAND_INV_READ_EXT_ADDR_reserved0_MASK 0xfff80000 +#define BCHP_NAND_INV_READ_EXT_ADDR_reserved0_SHIFT 19 + +/* NAND :: INV_READ_EXT_ADDR :: CS_SEL [18:16] */ +#define BCHP_NAND_INV_READ_EXT_ADDR_CS_SEL_MASK 0x00070000 +#define BCHP_NAND_INV_READ_EXT_ADDR_CS_SEL_SHIFT 16 + +/* NAND :: INV_READ_EXT_ADDR :: EXT_ADDRESS [15:00] */ +#define BCHP_NAND_INV_READ_EXT_ADDR_EXT_ADDRESS_MASK 0x0000ffff +#define BCHP_NAND_INV_READ_EXT_ADDR_EXT_ADDRESS_SHIFT 0 + +/*************************************************************************** + *INV_READ_ADDR - Flash Invalid Data Address + ***************************************************************************/ +/* NAND :: INV_READ_ADDR :: ADDRESS [31:00] */ +#define BCHP_NAND_INV_READ_ADDR_ADDRESS_MASK 0xffffffff +#define BCHP_NAND_INV_READ_ADDR_ADDRESS_SHIFT 0 + +/*************************************************************************** + *BLK_WR_PROTECT - Block Write Protect Enable and Size for EBI_CS0b + ***************************************************************************/ +/* NAND :: BLK_WR_PROTECT :: BLK_END_ADDR [31:00] */ +#define BCHP_NAND_BLK_WR_PROTECT_BLK_END_ADDR_MASK 0xffffffff +#define BCHP_NAND_BLK_WR_PROTECT_BLK_END_ADDR_SHIFT 0 + +/*************************************************************************** + *FLASH_CACHE%i - Flash Cache Buffer Read Access + ***************************************************************************/ +#define BCHP_NAND_FLASH_CACHEi_ARRAY_BASE BRCMNAND_CACHE_BASE +#define BCHP_NAND_FLASH_CACHEi_ARRAY_START 0 +#define BCHP_NAND_FLASH_CACHEi_ARRAY_END 127 +#define BCHP_NAND_FLASH_CACHEi_ARRAY_ELEMENT_SIZE 32 + +/*************************************************************************** + *FLASH_CACHE%i - Flash Cache Buffer Read Access + ***************************************************************************/ +/* NAND :: FLASH_CACHEi :: WORD [31:00] */ +#define BCHP_NAND_FLASH_CACHEi_WORD_MASK 0xffffffff +#define BCHP_NAND_FLASH_CACHEi_WORD_SHIFT 0 + +/*************************************************************************** + *Definitions not supported by this version NAND controller but defined + *in order to compile + ***************************************************************************/ +#define BCHP_NAND_FLASH_DEVICE_ID_EXT (BCHP_NAND_REG_END + 4) + +#define BCHP_NAND_ACC_CONTROL_CS1_PARTIAL_PAGE_EN_MASK 0 +#define BCHP_NAND_ACC_CONTROL_CS1_PARTIAL_PAGE_EN_SHIFT 0 +#define BCHP_NAND_ACC_CONTROL_PARTIAL_PAGE_EN_MASK 0 +#define BCHP_NAND_ACC_CONTROL_PARTIAL_PAGE_EN_SHIFT 0 +#define BCHP_NAND_ACC_CONTROL_ECC_LEVEL_0_MASK 0 +#define BCHP_NAND_ACC_CONTROL_ECC_LEVEL_0_SHIFT 0 +#define BCHP_NAND_ACC_CONTROL_ECC_LEVEL_MASK 0 +#define BCHP_NAND_ACC_CONTROL_ECC_LEVEL_SHIFT 0 +#define BCHP_NAND_ACC_CONTROL_SPARE_AREA_SIZE_0_MASK 0 +#define BCHP_NAND_ACC_CONTROL_SPARE_AREA_SIZE_0_SHIFT 0 +#define BCHP_NAND_ACC_CONTROL_SPARE_AREA_SIZE_MASK 0 +#define BCHP_NAND_ACC_CONTROL_SPARE_AREA_SIZE_SHIFT 0 + +#endif /* #ifndef BCHP_NAND_21_22_H__ */ + +/* End of File */ +#endif diff -urN linux-4.1.52-orig/include/linux/mtd/bchp_nand_40.h linux-4.1.52/include/linux/mtd/bchp_nand_40.h --- linux-4.1.52-orig/include/linux/mtd/bchp_nand_40.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/mtd/bchp_nand_40.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,1316 @@ +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +/* +* + <:copyright-BRCM:2015:DUAL/GPL:standard + + Copyright (c) 2015 Broadcom + All Rights Reserved + + Unless you and Broadcom execute a separate written software license + agreement governing use of this software, this software is licensed + to you under the terms of the GNU General Public License version 2 + (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, + with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + + Not withstanding the above, under no circumstances may you combine + this software in any way with any other Broadcom software provided + under a license other than the GPL, without Broadcom's express prior + written consent. + + :> + */ + +#ifndef BCHP_NAND_40_H__ +#define BCHP_NAND_40_H__ + +#include + +#define BRCMNAND_CTL_BASE ((uintptr_t)(NAND_REG_BASE & 0x0fffffff)) +#define BRCMNAND_CACHE_BASE ((uintptr_t)(NAND_CACHE_BASE & 0x0fffffff)) +#define BRCMNAND_FLD_ADDR(FLD) \ + (uintptr_t)(BRCMNAND_CTL_BASE + (offsetof(NandCtrlRegs,FLD))) + +#define BCHP_NAND_REG_START ((uintptr_t)BRCMNAND_CTL_BASE) +#define BCHP_NAND_REG_END ((uintptr_t)(BCHP_NAND_REG_START + \ + sizeof(NandCtrlRegs))) + +/*************************************************************************** + *NAND - Nand Flash Control Registers + ***************************************************************************/ +#define BCHP_NAND_REVISION BRCMNAND_FLD_ADDR(NandRevision) /* NAND Revision */ +#define BCHP_NAND_CMD_START BRCMNAND_FLD_ADDR(NandCmdStart) /* Nand Flash Command Start */ +#define BCHP_NAND_CMD_EXT_ADDRESS BRCMNAND_FLD_ADDR(NandCmdExtAddr) /* Nand Flash Command Extended Address */ +#define BCHP_NAND_CMD_ADDRESS BRCMNAND_FLD_ADDR(NandCmdAddr) /* Nand Flash Command Address */ +#define BCHP_NAND_CMD_END_ADDRESS BRCMNAND_FLD_ADDR(NandCmdEndAddr) /* Nand Flash Command End Address */ +#define BCHP_NAND_CS_NAND_SELECT BRCMNAND_FLD_ADDR(NandNandBootConfig) /* Nand Flash EBI CS Select */ +#define BCHP_NAND_CS_NAND_XOR BRCMNAND_FLD_ADDR(NandCsNandXor) /* Nand Flash EBI CS Address XOR with 1FC0 Control */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_0 BRCMNAND_FLD_ADDR(NandSpareAreaReadOfs0) /* Nand Flash Spare Area Read Bytes 0-3 */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_4 BRCMNAND_FLD_ADDR(NandSpareAreaReadOfs4) /* Nand Flash Spare Area Read Bytes 4-7 */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_8 BRCMNAND_FLD_ADDR(NandSpareAreaReadOfs8) /* Nand Flash Spare Area Read Bytes 8-11 */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_C BRCMNAND_FLD_ADDR(NandSpareAreaReadOfsC) /* Nand Flash Spare Area Read Bytes 12-15 */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_0 BRCMNAND_FLD_ADDR(NandSpareAreaWriteOfs0) /* Nand Flash Spare Area Write Bytes 0-3 */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_4 BRCMNAND_FLD_ADDR(NandSpareAreaWriteOfs4) /* Nand Flash Spare Area Write Bytes 4-7 */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_8 BRCMNAND_FLD_ADDR(NandSpareAreaWriteOfs8) /* Nand Flash Spare Area Write Bytes 8-11 */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_C BRCMNAND_FLD_ADDR(NandSpareAreaWriteOfsC) /* Nand Flash Spare Area Write Bytes 12-15 */ +#define BCHP_NAND_ACC_CONTROL BRCMNAND_FLD_ADDR(NandAccControl) /* Nand Flash Access Control */ +#define BCHP_NAND_CONFIG BRCMNAND_FLD_ADDR(NandConfig) /* Nand Flash Config */ +#define BCHP_NAND_TIMING_1 BRCMNAND_FLD_ADDR(NandTiming1) /* Nand Flash Timing Parameters 1 */ +#define BCHP_NAND_TIMING_2 BRCMNAND_FLD_ADDR(NandTiming2) /* Nand Flash Timing Parameters 2 */ +#define BCHP_NAND_SEMAPHORE BRCMNAND_FLD_ADDR(NandSemaphore) /* Semaphore */ +#define BCHP_NAND_FLASH_DEVICE_ID BRCMNAND_FLD_ADDR(NandFlashDeviceId) /* Nand Flash Device ID */ +#define BCHP_NAND_FLASH_DEVICE_ID_EXT BRCMNAND_FLD_ADDR(NandFlashDeviceIdExt) /* Nand Flash Extended Device ID */ +#define BCHP_NAND_BLOCK_LOCK_STATUS BRCMNAND_FLD_ADDR(NandBlockLockStatus) /* Nand Flash Block Lock Status */ +#define BCHP_NAND_INTFC_STATUS BRCMNAND_FLD_ADDR(NandIntfcStatus) /* Nand Flash Interface Status */ +#define BCHP_NAND_ECC_CORR_EXT_ADDR BRCMNAND_FLD_ADDR(NandEccCorrExtAddr) /* ECC Correctable Error Extended Address */ +#define BCHP_NAND_ECC_CORR_ADDR BRCMNAND_FLD_ADDR(NandEccCorrAddr) /* ECC Correctable Error Address */ +#define BCHP_NAND_ECC_UNC_EXT_ADDR BRCMNAND_FLD_ADDR(NandEccUncExtAddr) /* ECC Uncorrectable Error Extended Address */ +#define BCHP_NAND_ECC_UNC_ADDR BRCMNAND_FLD_ADDR(NandEccUncAddr) /* ECC Uncorrectable Error Address */ +#define BCHP_NAND_READ_ERROR_COUNT BRCMNAND_FLD_ADDR(NandReadErrorCount) /* Read Error Count */ +#define BCHP_NAND_CORR_STAT_THRESHOLD BRCMNAND_FLD_ADDR(NandCorrStatThreshold) /* Correctable Error Reporting Threshold */ +#define BCHP_NAND_ONFI_STATUS BRCMNAND_FLD_ADDR(NandOnfiStatus) /* ONFI Status */ +#define BCHP_NAND_ONFI_DEBUG_DATA BRCMNAND_FLD_ADDR(NandOnfiDebugData) /* ONFI Debug Data */ +#define BCHP_NAND_FLASH_READ_EXT_ADDR BRCMNAND_FLD_ADDR(NandFlashReadExtAddr) /* Flash Read Data Extended Address */ +#define BCHP_NAND_FLASH_READ_ADDR BRCMNAND_FLD_ADDR(NandFlashReadAddr) /* Flash Read Data Address */ +#define BCHP_NAND_PROGRAM_PAGE_EXT_ADDR BRCMNAND_FLD_ADDR(NandProgramPageExtAddr) /* Page Program Extended Address */ +#define BCHP_NAND_PROGRAM_PAGE_ADDR BRCMNAND_FLD_ADDR(NandProgramPageAddr) /* Page Program Address */ +#define BCHP_NAND_COPY_BACK_EXT_ADDR BRCMNAND_FLD_ADDR(NandCopyBackExtAddr) /* Copy Back Extended Address */ +#define BCHP_NAND_COPY_BACK_ADDR BRCMNAND_FLD_ADDR(NandCopyBackAddr) /* Copy Back Address */ +#define BCHP_NAND_BLOCK_ERASE_EXT_ADDR BRCMNAND_FLD_ADDR(NandBlockEraseExtAddr) /* Block Erase Extended Address */ +#define BCHP_NAND_BLOCK_ERASE_ADDR BRCMNAND_FLD_ADDR(NandBlockEraseAddr) /* Block Erase Address */ +#define BCHP_NAND_INV_READ_EXT_ADDR BRCMNAND_FLD_ADDR(NandInvReadExtAddr) /* Flash Invalid Data Extended Address */ +#define BCHP_NAND_INV_READ_ADDR BRCMNAND_FLD_ADDR(NandInvReadAddr) /* Flash Invalid Data Address */ +#define BCHP_NAND_BLK_WR_PROTECT BRCMNAND_FLD_ADDR(NandBlkWrProtect) /* Block Write Protect Enable and Size for EBI_CS0b */ +#define BCHP_NAND_ACC_CONTROL_CS1 BRCMNAND_FLD_ADDR(NandAccControlCs1) /* Nand Flash Access Control */ +#define BCHP_NAND_CONFIG_CS1 BRCMNAND_FLD_ADDR(NandConfigCs1) /* Nand Flash Config */ +#define BCHP_NAND_TIMING_1_CS1 BRCMNAND_FLD_ADDR(NandTiming1Cs1) /* Nand Flash Timing Parameters 1 */ +#define BCHP_NAND_TIMING_2_CS1 BRCMNAND_FLD_ADDR(NandTiming2Cs1) /* Nand Flash Timing Parameters 2 */ +#define BCHP_NAND_ACC_CONTROL_CS2 BRCMNAND_FLD_ADDR(NandAccControlCs2) /* Nand Flash Access Control */ +#define BCHP_NAND_CONFIG_CS2 BRCMNAND_FLD_ADDR(NandConfigCs2) /* Nand Flash Config */ +#define BCHP_NAND_TIMING_1_CS2 BRCMNAND_FLD_ADDR(NandTiming1Cs2) /* Nand Flash Timing Parameters 1 */ +#define BCHP_NAND_TIMING_2_CS2 BRCMNAND_FLD_ADDR(NandTiming2Cs2) /* Nand Flash Timing Parameters 2 */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_10 BRCMNAND_FLD_ADDR(NandSpareAreaReadOfs10) /* Nand Flash Spare Area Read Bytes 16-19 */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_14 BRCMNAND_FLD_ADDR(NandSpareAreaReadOfs14) /* Nand Flash Spare Area Read Bytes 20-23 */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_18 BRCMNAND_FLD_ADDR(NandSpareAreaReadOfs18) /* Nand Flash Spare Area Read Bytes 24-27 */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_1C BRCMNAND_FLD_ADDR(NandSpareAreaReadOfs1C) /* Nand Flash Spare Area Read Bytes 28-31 */ +#define BCHP_NAND_LL_OP BRCMNAND_FLD_ADDR(NandLlOpNand) /* Nand Flash Low Level Operation */ +#define BCHP_NAND_LL_RDDATA BRCMNAND_FLD_ADDR(NandLlRdData) /* Nand Flash Low Level Read Data */ + +/*************************************************************************** + *REVISION - NAND Revision + ***************************************************************************/ +/* NAND :: REVISION :: 8KB_PAGE_SUPPORT [31:31] */ +#define BCHP_NAND_REVISION_8KB_PAGE_SUPPORT_MASK 0x80000000 +#define BCHP_NAND_REVISION_8KB_PAGE_SUPPORT_SHIFT 31 + +/* NAND :: REVISION :: reserved0 [30:16] */ +#define BCHP_NAND_REVISION_reserved0_MASK 0x7fff0000 +#define BCHP_NAND_REVISION_reserved0_SHIFT 16 + +/* NAND :: REVISION :: MAJOR [15:08] */ +#define BCHP_NAND_REVISION_MAJOR_MASK 0x0000ff00 +#define BCHP_NAND_REVISION_MAJOR_SHIFT 8 + +/* NAND :: REVISION :: MINOR [07:00] */ +#define BCHP_NAND_REVISION_MINOR_MASK 0x000000ff +#define BCHP_NAND_REVISION_MINOR_SHIFT 0 + +/*************************************************************************** + *CMD_START - Nand Flash Command Start + ***************************************************************************/ +/* NAND :: CMD_START :: reserved0 [31:28] */ +#define BCHP_NAND_CMD_START_reserved0_MASK 0xe0000000 +#define BCHP_NAND_CMD_START_reserved0_SHIFT 28 + +/* NAND :: CMD_START :: OPCODE [27:24] */ +#define BCHP_NAND_CMD_START_OPCODE_MASK 0x1f000000 +#define BCHP_NAND_CMD_START_OPCODE_SHIFT 24 +#define BCHP_NAND_CMD_START_OPCODE_NULL 0 +#define BCHP_NAND_CMD_START_OPCODE_PAGE_READ 1 +#define BCHP_NAND_CMD_START_OPCODE_SPARE_AREA_READ 2 +#define BCHP_NAND_CMD_START_OPCODE_STATUS_READ 3 +#define BCHP_NAND_CMD_START_OPCODE_PROGRAM_PAGE 4 +#define BCHP_NAND_CMD_START_OPCODE_PROGRAM_SPARE_AREA 5 +#define BCHP_NAND_CMD_START_OPCODE_COPY_BACK 6 +#define BCHP_NAND_CMD_START_OPCODE_DEVICE_ID_READ 7 +#define BCHP_NAND_CMD_START_OPCODE_BLOCK_ERASE 8 +#define BCHP_NAND_CMD_START_OPCODE_FLASH_RESET 9 +#define BCHP_NAND_CMD_START_OPCODE_BLOCKS_LOCK 10 +#define BCHP_NAND_CMD_START_OPCODE_BLOCKS_LOCK_DOWN 11 +#define BCHP_NAND_CMD_START_OPCODE_BLOCKS_UNLOCK 12 +#define BCHP_NAND_CMD_START_OPCODE_READ_BLOCKS_LOCK_STATUS 13 +#define BCHP_NAND_CMD_START_OPCODE_PARAMETER_READ 14 +#define BCHP_NAND_CMD_START_OPCODE_PARAMETER_CHANGE_COL 15 +#define BCHP_NAND_CMD_START_OPCODE_LOW_LEVEL_OP 16 + +/* NAND :: CMD_START :: reserved1 [23:00] */ +#define BCHP_NAND_CMD_START_reserved1_MASK 0x00ffffff +#define BCHP_NAND_CMD_START_reserved1_SHIFT 0 + +/*************************************************************************** + *CMD_EXT_ADDRESS - Nand Flash Command Extended Address + ***************************************************************************/ +/* NAND :: CMD_EXT_ADDRESS :: reserved0 [31:19] */ +#define BCHP_NAND_CMD_EXT_ADDRESS_reserved0_MASK 0xfff80000 +#define BCHP_NAND_CMD_EXT_ADDRESS_reserved0_SHIFT 19 + +/* NAND :: CMD_EXT_ADDRESS :: CS_SEL [18:16] */ +#define BCHP_NAND_CMD_EXT_ADDRESS_CS_SEL_MASK 0x00070000 +#define BCHP_NAND_CMD_EXT_ADDRESS_CS_SEL_SHIFT 16 + +/* NAND :: CMD_EXT_ADDRESS :: EXT_ADDRESS [15:00] */ +#define BCHP_NAND_CMD_EXT_ADDRESS_EXT_ADDRESS_MASK 0x0000ffff +#define BCHP_NAND_CMD_EXT_ADDRESS_EXT_ADDRESS_SHIFT 0 + +/*************************************************************************** + *CMD_ADDRESS - Nand Flash Command Address + ***************************************************************************/ +/* NAND :: CMD_ADDRESS :: ADDRESS [31:00] */ +#define BCHP_NAND_CMD_ADDRESS_ADDRESS_MASK 0xffffffff +#define BCHP_NAND_CMD_ADDRESS_ADDRESS_SHIFT 0 + +/*************************************************************************** + *CMD_END_ADDRESS - Nand Flash Command End Address + ***************************************************************************/ +/* NAND :: CMD_END_ADDRESS :: ADDRESS [31:00] */ +#define BCHP_NAND_CMD_END_ADDRESS_ADDRESS_MASK 0xffffffff +#define BCHP_NAND_CMD_END_ADDRESS_ADDRESS_SHIFT 0 + +/*************************************************************************** + *CS_NAND_SELECT - Nand Flash EBI CS Select + ***************************************************************************/ +/* NAND :: CS_NAND_SELECT :: CS_LOCK [31:31] */ +#define BCHP_NAND_CS_NAND_SELECT_CS_LOCK_MASK 0x80000000 +#define BCHP_NAND_CS_NAND_SELECT_CS_LOCK_SHIFT 31 + +/* NAND :: CS_NAND_SELECT :: AUTO_DEVICE_ID_CONFIG [30:30] */ +#define BCHP_NAND_CS_NAND_SELECT_AUTO_DEVICE_ID_CONFIG_MASK 0x40000000 +#define BCHP_NAND_CS_NAND_SELECT_AUTO_DEVICE_ID_CONFIG_SHIFT 30 + +/* NAND :: CS_NAND_SELECT :: reserved0 [29:29] */ +#define BCHP_NAND_CS_NAND_SELECT_reserved0_MASK 0x20000000 +#define BCHP_NAND_CS_NAND_SELECT_reserved0_SHIFT 29 + +/* NAND :: CS_NAND_SELECT :: WR_PROTECT_BLK0 [28:28] */ +#define BCHP_NAND_CS_NAND_SELECT_WR_PROTECT_BLK0_MASK 0x10000000 +#define BCHP_NAND_CS_NAND_SELECT_WR_PROTECT_BLK0_SHIFT 28 + +/* NAND :: CS_NAND_SELECT :: reserved1 [27:16] */ +#define BCHP_NAND_CS_NAND_SELECT_reserved1_MASK 0x0fff0000 +#define BCHP_NAND_CS_NAND_SELECT_reserved1_SHIFT 16 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_7_USES_NAND [15:15] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_7_USES_NAND_MASK 0x00008000 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_7_USES_NAND_SHIFT 15 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_6_USES_NAND [14:14] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_6_USES_NAND_MASK 0x00004000 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_6_USES_NAND_SHIFT 14 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_5_USES_NAND [13:13] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_5_USES_NAND_MASK 0x00002000 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_5_USES_NAND_SHIFT 13 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_4_USES_NAND [12:12] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_4_USES_NAND_MASK 0x00001000 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_4_USES_NAND_SHIFT 12 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_3_USES_NAND [11:11] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_3_USES_NAND_MASK 0x00000800 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_3_USES_NAND_SHIFT 11 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_2_USES_NAND [10:10] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_2_USES_NAND_MASK 0x00000400 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_2_USES_NAND_SHIFT 10 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_1_USES_NAND [09:09] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_1_USES_NAND_MASK 0x00000200 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_1_USES_NAND_SHIFT 9 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_0_USES_NAND [08:08] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_0_USES_NAND_MASK 0x00000100 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_0_USES_NAND_SHIFT 8 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_7_SEL [07:07] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_7_SEL_MASK 0x00000080 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_7_SEL_SHIFT 7 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_6_SEL [06:06] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_6_SEL_MASK 0x00000040 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_6_SEL_SHIFT 6 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_5_SEL [05:05] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_5_SEL_MASK 0x00000020 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_5_SEL_SHIFT 5 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_4_SEL [04:04] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_4_SEL_MASK 0x00000010 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_4_SEL_SHIFT 4 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_3_SEL [03:03] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_3_SEL_MASK 0x00000008 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_3_SEL_SHIFT 3 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_2_SEL [02:02] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_2_SEL_MASK 0x00000004 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_2_SEL_SHIFT 2 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_1_SEL [01:01] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_1_SEL_MASK 0x00000002 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_1_SEL_SHIFT 1 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_0_SEL [00:00] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_0_SEL_MASK 0x00000001 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_0_SEL_SHIFT 0 + +/*************************************************************************** + *CS_NAND_XOR - Nand Flash EBI CS Address XOR with 1FC0 Control + ***************************************************************************/ +/* NAND :: CS_NAND_XOR :: ONLY_BLOCK_0_1FC0_XOR [31:31] */ +#define BCHP_NAND_CS_NAND_XOR_ONLY_BLOCK_0_1FC0_XOR_MASK 0x80000000 +#define BCHP_NAND_CS_NAND_XOR_ONLY_BLOCK_0_1FC0_XOR_SHIFT 31 + +/* NAND :: CS_NAND_XOR :: reserved0 [30:08] */ +#define BCHP_NAND_CS_NAND_XOR_reserved0_MASK 0x7fffff00 +#define BCHP_NAND_CS_NAND_XOR_reserved0_SHIFT 8 + +/* NAND :: CS_NAND_XOR :: EBI_CS_7_ADDR_1FC0_XOR [07:07] */ +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_7_ADDR_1FC0_XOR_MASK 0x00000080 +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_7_ADDR_1FC0_XOR_SHIFT 7 + +/* NAND :: CS_NAND_XOR :: EBI_CS_6_ADDR_1FC0_XOR [06:06] */ +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_6_ADDR_1FC0_XOR_MASK 0x00000040 +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_6_ADDR_1FC0_XOR_SHIFT 6 + +/* NAND :: CS_NAND_XOR :: EBI_CS_5_ADDR_1FC0_XOR [05:05] */ +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_5_ADDR_1FC0_XOR_MASK 0x00000020 +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_5_ADDR_1FC0_XOR_SHIFT 5 + +/* NAND :: CS_NAND_XOR :: EBI_CS_4_ADDR_1FC0_XOR [04:04] */ +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_4_ADDR_1FC0_XOR_MASK 0x00000010 +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_4_ADDR_1FC0_XOR_SHIFT 4 + +/* NAND :: CS_NAND_XOR :: EBI_CS_3_ADDR_1FC0_XOR [03:03] */ +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_3_ADDR_1FC0_XOR_MASK 0x00000008 +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_3_ADDR_1FC0_XOR_SHIFT 3 + +/* NAND :: CS_NAND_XOR :: EBI_CS_2_ADDR_1FC0_XOR [02:02] */ +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_2_ADDR_1FC0_XOR_MASK 0x00000004 +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_2_ADDR_1FC0_XOR_SHIFT 2 + +/* NAND :: CS_NAND_XOR :: EBI_CS_1_ADDR_1FC0_XOR [01:01] */ +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_1_ADDR_1FC0_XOR_MASK 0x00000002 +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_1_ADDR_1FC0_XOR_SHIFT 1 + +/* NAND :: CS_NAND_XOR :: EBI_CS_0_ADDR_1FC0_XOR [00:00] */ +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_0_ADDR_1FC0_XOR_MASK 0x00000001 +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_0_ADDR_1FC0_XOR_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_READ_OFS_0 - Nand Flash Spare Area Read Bytes 0-3 + ***************************************************************************/ +/* NAND :: SPARE_AREA_READ_OFS_0 :: BYTE_OFS_0 [31:24] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_0_BYTE_OFS_0_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_0_BYTE_OFS_0_SHIFT 24 + +/* NAND :: SPARE_AREA_READ_OFS_0 :: BYTE_OFS_1 [23:16] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_0_BYTE_OFS_1_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_0_BYTE_OFS_1_SHIFT 16 + +/* NAND :: SPARE_AREA_READ_OFS_0 :: BYTE_OFS_2 [15:08] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_0_BYTE_OFS_2_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_READ_OFS_0_BYTE_OFS_2_SHIFT 8 + +/* NAND :: SPARE_AREA_READ_OFS_0 :: BYTE_OFS_3 [07:00] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_0_BYTE_OFS_3_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_READ_OFS_0_BYTE_OFS_3_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_READ_OFS_4 - Nand Flash Spare Area Read Bytes 4-7 + ***************************************************************************/ +/* NAND :: SPARE_AREA_READ_OFS_4 :: BYTE_OFS_4 [31:24] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_4_BYTE_OFS_4_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_4_BYTE_OFS_4_SHIFT 24 + +/* NAND :: SPARE_AREA_READ_OFS_4 :: BYTE_OFS_5 [23:16] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_4_BYTE_OFS_5_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_4_BYTE_OFS_5_SHIFT 16 + +/* NAND :: SPARE_AREA_READ_OFS_4 :: BYTE_OFS_6 [15:08] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_4_BYTE_OFS_6_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_READ_OFS_4_BYTE_OFS_6_SHIFT 8 + +/* NAND :: SPARE_AREA_READ_OFS_4 :: BYTE_OFS_7 [07:00] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_4_BYTE_OFS_7_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_READ_OFS_4_BYTE_OFS_7_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_READ_OFS_8 - Nand Flash Spare Area Read Bytes 8-11 + ***************************************************************************/ +/* NAND :: SPARE_AREA_READ_OFS_8 :: BYTE_OFS_8 [31:24] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_8_BYTE_OFS_8_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_8_BYTE_OFS_8_SHIFT 24 + +/* NAND :: SPARE_AREA_READ_OFS_8 :: BYTE_OFS_9 [23:16] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_8_BYTE_OFS_9_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_8_BYTE_OFS_9_SHIFT 16 + +/* NAND :: SPARE_AREA_READ_OFS_8 :: BYTE_OFS_10 [15:08] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_8_BYTE_OFS_10_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_READ_OFS_8_BYTE_OFS_10_SHIFT 8 + +/* NAND :: SPARE_AREA_READ_OFS_8 :: BYTE_OFS_11 [07:00] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_8_BYTE_OFS_11_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_READ_OFS_8_BYTE_OFS_11_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_READ_OFS_C - Nand Flash Spare Area Read Bytes 12-15 + ***************************************************************************/ +/* NAND :: SPARE_AREA_READ_OFS_C :: BYTE_OFS_12 [31:24] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_C_BYTE_OFS_12_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_C_BYTE_OFS_12_SHIFT 24 + +/* NAND :: SPARE_AREA_READ_OFS_C :: BYTE_OFS_13 [23:16] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_C_BYTE_OFS_13_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_C_BYTE_OFS_13_SHIFT 16 + +/* NAND :: SPARE_AREA_READ_OFS_C :: BYTE_OFS_14 [15:08] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_C_BYTE_OFS_14_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_READ_OFS_C_BYTE_OFS_14_SHIFT 8 + +/* NAND :: SPARE_AREA_READ_OFS_C :: BYTE_OFS_15 [07:00] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_C_BYTE_OFS_15_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_READ_OFS_C_BYTE_OFS_15_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_WRITE_OFS_0 - Nand Flash Spare Area Write Bytes 0-3 + ***************************************************************************/ +/* NAND :: SPARE_AREA_WRITE_OFS_0 :: BYTE_OFS_0 [31:24] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_0_BYTE_OFS_0_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_0_BYTE_OFS_0_SHIFT 24 + +/* NAND :: SPARE_AREA_WRITE_OFS_0 :: BYTE_OFS_1 [23:16] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_0_BYTE_OFS_1_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_0_BYTE_OFS_1_SHIFT 16 + +/* NAND :: SPARE_AREA_WRITE_OFS_0 :: BYTE_OFS_2 [15:08] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_0_BYTE_OFS_2_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_0_BYTE_OFS_2_SHIFT 8 + +/* NAND :: SPARE_AREA_WRITE_OFS_0 :: BYTE_OFS_3 [07:00] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_0_BYTE_OFS_3_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_0_BYTE_OFS_3_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_WRITE_OFS_4 - Nand Flash Spare Area Write Bytes 4-7 + ***************************************************************************/ +/* NAND :: SPARE_AREA_WRITE_OFS_4 :: BYTE_OFS_4 [31:24] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_4_BYTE_OFS_4_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_4_BYTE_OFS_4_SHIFT 24 + +/* NAND :: SPARE_AREA_WRITE_OFS_4 :: BYTE_OFS_5 [23:16] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_4_BYTE_OFS_5_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_4_BYTE_OFS_5_SHIFT 16 + +/* NAND :: SPARE_AREA_WRITE_OFS_4 :: BYTE_OFS_6 [15:08] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_4_BYTE_OFS_6_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_4_BYTE_OFS_6_SHIFT 8 + +/* NAND :: SPARE_AREA_WRITE_OFS_4 :: BYTE_OFS_7 [07:00] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_4_BYTE_OFS_7_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_4_BYTE_OFS_7_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_WRITE_OFS_8 - Nand Flash Spare Area Write Bytes 8-11 + ***************************************************************************/ +/* NAND :: SPARE_AREA_WRITE_OFS_8 :: BYTE_OFS_8 [31:24] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_8_BYTE_OFS_8_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_8_BYTE_OFS_8_SHIFT 24 + +/* NAND :: SPARE_AREA_WRITE_OFS_8 :: BYTE_OFS_9 [23:16] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_8_BYTE_OFS_9_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_8_BYTE_OFS_9_SHIFT 16 + +/* NAND :: SPARE_AREA_WRITE_OFS_8 :: BYTE_OFS_10 [15:08] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_8_BYTE_OFS_10_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_8_BYTE_OFS_10_SHIFT 8 + +/* NAND :: SPARE_AREA_WRITE_OFS_8 :: BYTE_OFS_11 [07:00] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_8_BYTE_OFS_11_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_8_BYTE_OFS_11_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_WRITE_OFS_C - Nand Flash Spare Area Write Bytes 12-15 + ***************************************************************************/ +/* NAND :: SPARE_AREA_WRITE_OFS_C :: BYTE_OFS_12 [31:24] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_C_BYTE_OFS_12_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_C_BYTE_OFS_12_SHIFT 24 + +/* NAND :: SPARE_AREA_WRITE_OFS_C :: BYTE_OFS_13 [23:16] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_C_BYTE_OFS_13_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_C_BYTE_OFS_13_SHIFT 16 + +/* NAND :: SPARE_AREA_WRITE_OFS_C :: BYTE_OFS_14 [15:08] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_C_BYTE_OFS_14_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_C_BYTE_OFS_14_SHIFT 8 + +/* NAND :: SPARE_AREA_WRITE_OFS_C :: BYTE_OFS_15 [07:00] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_C_BYTE_OFS_15_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_C_BYTE_OFS_15_SHIFT 0 + +/*************************************************************************** + *ACC_CONTROL - Nand Flash Access Control + ***************************************************************************/ +/* NAND :: ACC_CONTROL :: RD_ECC_EN [31:31] */ +#define BCHP_NAND_ACC_CONTROL_RD_ECC_EN_MASK 0x80000000 +#define BCHP_NAND_ACC_CONTROL_RD_ECC_EN_SHIFT 31 + +/* NAND :: ACC_CONTROL :: WR_ECC_EN [30:30] */ +#define BCHP_NAND_ACC_CONTROL_WR_ECC_EN_MASK 0x40000000 +#define BCHP_NAND_ACC_CONTROL_WR_ECC_EN_SHIFT 30 + +/* NAND :: ACC_CONTROL :: RD_ECC_BLK0_EN [29:29] */ +#define BCHP_NAND_ACC_CONTROL_RD_ECC_BLK0_EN_MASK 0x20000000 +#define BCHP_NAND_ACC_CONTROL_RD_ECC_BLK0_EN_SHIFT 29 + +/* NAND :: ACC_CONTROL :: FAST_PGM_RDIN [28:28] */ +#define BCHP_NAND_ACC_CONTROL_FAST_PGM_RDIN_MASK 0x10000000 +#define BCHP_NAND_ACC_CONTROL_FAST_PGM_RDIN_SHIFT 28 + +/* NAND :: ACC_CONTROL :: RD_ERASED_ECC_EN [27:27] */ +#define BCHP_NAND_ACC_CONTROL_RD_ERASED_ECC_EN_MASK 0x08000000 +#define BCHP_NAND_ACC_CONTROL_RD_ERASED_ECC_EN_SHIFT 27 + +/* NAND :: ACC_CONTROL :: PARTIAL_PAGE_EN [26:26] */ +#define BCHP_NAND_ACC_CONTROL_PARTIAL_PAGE_EN_MASK 0x04000000 +#define BCHP_NAND_ACC_CONTROL_PARTIAL_PAGE_EN_SHIFT 26 + +/* NAND :: ACC_CONTROL :: WR_PREEMPT_EN [25:25] */ +#define BCHP_NAND_ACC_CONTROL_WR_PREEMPT_EN_MASK 0x02000000 +#define BCHP_NAND_ACC_CONTROL_WR_PREEMPT_EN_SHIFT 25 + +/* NAND :: ACC_CONTROL :: PAGE_HIT_EN [24:24] */ +#define BCHP_NAND_ACC_CONTROL_PAGE_HIT_EN_MASK 0x01000000 +#define BCHP_NAND_ACC_CONTROL_PAGE_HIT_EN_SHIFT 24 + +/* NAND :: ACC_CONTROL :: ECC_LEVEL_0 [23:20] */ +#define BCHP_NAND_ACC_CONTROL_ECC_LEVEL_0_MASK 0x00f00000 +#define BCHP_NAND_ACC_CONTROL_ECC_LEVEL_0_SHIFT 20 + +/* NAND :: ACC_CONTROL :: ECC_LEVEL [19:16] */ +#define BCHP_NAND_ACC_CONTROL_ECC_LEVEL_MASK 0x000f0000 +#define BCHP_NAND_ACC_CONTROL_ECC_LEVEL_SHIFT 16 + +/* NAND :: ACC_CONTROL :: reserved0 [15:14] */ +#define BCHP_NAND_ACC_CONTROL_reserved0_MASK 0x0000c000 +#define BCHP_NAND_ACC_CONTROL_reserved0_SHIFT 14 + +/* NAND :: ACC_CONTROL :: SPARE_AREA_SIZE_0 [13:08] */ +#define BCHP_NAND_ACC_CONTROL_SPARE_AREA_SIZE_0_MASK 0x00003f00 +#define BCHP_NAND_ACC_CONTROL_SPARE_AREA_SIZE_0_SHIFT 8 + +/* NAND :: ACC_CONTROL :: reserved1 [07:06] */ +#define BCHP_NAND_ACC_CONTROL_reserved1_MASK 0x000000c0 +#define BCHP_NAND_ACC_CONTROL_reserved1_SHIFT 6 + +/* NAND :: ACC_CONTROL :: SPARE_AREA_SIZE [05:00] */ +#define BCHP_NAND_ACC_CONTROL_SPARE_AREA_SIZE_MASK 0x0000003f +#define BCHP_NAND_ACC_CONTROL_SPARE_AREA_SIZE_SHIFT 0 + +/*************************************************************************** + *CONFIG - Nand Flash Config + ***************************************************************************/ +/* NAND :: CONFIG :: CONFIG_LOCK [31:31] */ +#define BCHP_NAND_CONFIG_CONFIG_LOCK_MASK 0x80000000 +#define BCHP_NAND_CONFIG_CONFIG_LOCK_SHIFT 31 + +/* NAND :: CONFIG :: BLOCK_SIZE [30:28] */ +#define BCHP_NAND_CONFIG_BLOCK_SIZE_MASK 0x70000000 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_SHIFT 28 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_2048KB 6 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_1024KB 5 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_512KB 3 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_128KB 1 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_16KB 0 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_8KB 2 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_256KB 4 + +/* NAND :: CONFIG :: DEVICE_SIZE [27:24] */ +#define BCHP_NAND_CONFIG_DEVICE_SIZE_MASK 0x0f000000 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_SHIFT 24 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_4MB 0 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_8MB 1 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_16MB 2 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_32MB 3 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_64MB 4 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_128MB 5 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_256MB 6 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_512MB 7 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_1GB 8 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_2GB 9 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_4GB 10 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_8GB 11 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_16GB 12 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_32GB 13 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_64GB 14 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_128GB 15 + +/* NAND :: CONFIG :: DEVICE_WIDTH [23:23] */ +#define BCHP_NAND_CONFIG_DEVICE_WIDTH_MASK 0x00800000 +#define BCHP_NAND_CONFIG_DEVICE_WIDTH_SHIFT 23 +#define BCHP_NAND_CONFIG_DEVICE_WIDTH_DVC_WIDTH_8 0 +#define BCHP_NAND_CONFIG_DEVICE_WIDTH_DVC_WIDTH_16 1 + +/* NAND :: CONFIG :: reserved0 [22:22] */ +#define BCHP_NAND_CONFIG_reserved0_MASK 0x00400000 +#define BCHP_NAND_CONFIG_reserved0_SHIFT 22 + +/* NAND :: CONFIG :: PAGE_SIZE [21:20] */ +#define BCHP_NAND_CONFIG_PAGE_SIZE_MASK 0x00300000 +#define BCHP_NAND_CONFIG_PAGE_SIZE_SHIFT 20 +#define BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_512 0 +#define BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_2KB 1 +#define BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_4KB 2 +#define BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_8KB 3 + +/* NAND :: CONFIG :: reserved1 [19:19] */ +#define BCHP_NAND_CONFIG_reserved1_MASK 0x00080000 +#define BCHP_NAND_CONFIG_reserved1_SHIFT 19 + +/* NAND :: CONFIG :: FUL_ADR_BYTES [18:16] */ +#define BCHP_NAND_CONFIG_FUL_ADR_BYTES_MASK 0x00070000 +#define BCHP_NAND_CONFIG_FUL_ADR_BYTES_SHIFT 16 + +/* NAND :: CONFIG :: reserved2 [15:15] */ +#define BCHP_NAND_CONFIG_reserved2_MASK 0x00008000 +#define BCHP_NAND_CONFIG_reserved2_SHIFT 15 + +/* NAND :: CONFIG :: COL_ADR_BYTES [14:12] */ +#define BCHP_NAND_CONFIG_COL_ADR_BYTES_MASK 0x00007000 +#define BCHP_NAND_CONFIG_COL_ADR_BYTES_SHIFT 12 + +/* NAND :: CONFIG :: reserved3 [11:11] */ +#define BCHP_NAND_CONFIG_reserved3_MASK 0x00000800 +#define BCHP_NAND_CONFIG_reserved3_SHIFT 11 + +/* NAND :: CONFIG :: BLK_ADR_BYTES [10:08] */ +#define BCHP_NAND_CONFIG_BLK_ADR_BYTES_MASK 0x00000700 +#define BCHP_NAND_CONFIG_BLK_ADR_BYTES_SHIFT 8 + +/* NAND :: CONFIG :: reserved4 [07:00] */ +#define BCHP_NAND_CONFIG_reserved4_MASK 0x000000ff +#define BCHP_NAND_CONFIG_reserved4_SHIFT 0 + +/*************************************************************************** + *TIMING_1 - Nand Flash Timing Parameters 1 + ***************************************************************************/ +/* NAND :: TIMING_1 :: tWP [31:28] */ +#define BCHP_NAND_TIMING_1_tWP_MASK 0xf0000000 +#define BCHP_NAND_TIMING_1_tWP_SHIFT 28 + +/* NAND :: TIMING_1 :: tWH [27:24] */ +#define BCHP_NAND_TIMING_1_tWH_MASK 0x0f000000 +#define BCHP_NAND_TIMING_1_tWH_SHIFT 24 + +/* NAND :: TIMING_1 :: tRP [23:20] */ +#define BCHP_NAND_TIMING_1_tRP_MASK 0x00f00000 +#define BCHP_NAND_TIMING_1_tRP_SHIFT 20 + +/* NAND :: TIMING_1 :: tREH [19:16] */ +#define BCHP_NAND_TIMING_1_tREH_MASK 0x000f0000 +#define BCHP_NAND_TIMING_1_tREH_SHIFT 16 + +/* NAND :: TIMING_1 :: tCS [15:12] */ +#define BCHP_NAND_TIMING_1_tCS_MASK 0x0000f000 +#define BCHP_NAND_TIMING_1_tCS_SHIFT 12 + +/* NAND :: TIMING_1 :: tCLH [11:08] */ +#define BCHP_NAND_TIMING_1_tCLH_MASK 0x00000f00 +#define BCHP_NAND_TIMING_1_tCLH_SHIFT 8 + +/* NAND :: TIMING_1 :: tALH [07:04] */ +#define BCHP_NAND_TIMING_1_tALH_MASK 0x000000f0 +#define BCHP_NAND_TIMING_1_tALH_SHIFT 4 + +/* NAND :: TIMING_1 :: tADL [03:00] */ +#define BCHP_NAND_TIMING_1_tADL_MASK 0x0000000f +#define BCHP_NAND_TIMING_1_tADL_SHIFT 0 + +/*************************************************************************** + *TIMING_2 - Nand Flash Timing Parameters 2 + ***************************************************************************/ +/* NAND :: TIMING_2 :: CLK_SELECT [31:31] */ +#define BCHP_NAND_TIMING_2_CLK_SELECT_MASK 0x80000000 +#define BCHP_NAND_TIMING_2_CLK_SELECT_SHIFT 31 +#define BCHP_NAND_TIMING_2_CLK_SELECT_CLK_108 0 +#define BCHP_NAND_TIMING_2_CLK_SELECT_CLK_216 1 + +/* NAND :: TIMING_2 :: reserved0 [30:13] */ +#define BCHP_NAND_TIMING_2_reserved0_MASK 0x7fffe000 +#define BCHP_NAND_TIMING_2_reserved0_SHIFT 13 + +/* NAND :: TIMING_2 :: tWB [12:09] */ +#define BCHP_NAND_TIMING_2_tWB_MASK 0x00001e00 +#define BCHP_NAND_TIMING_2_tWB_SHIFT 9 + +/* NAND :: TIMING_2 :: tWHR [08:04] */ +#define BCHP_NAND_TIMING_2_tWHR_MASK 0x000001f0 +#define BCHP_NAND_TIMING_2_tWHR_SHIFT 4 + +/* NAND :: TIMING_2 :: tREAD [03:00] */ +#define BCHP_NAND_TIMING_2_tREAD_MASK 0x0000000f +#define BCHP_NAND_TIMING_2_tREAD_SHIFT 0 + +/*************************************************************************** + *SEMAPHORE - Semaphore + ***************************************************************************/ +/* NAND :: SEMAPHORE :: reserved0 [31:08] */ +#define BCHP_NAND_SEMAPHORE_reserved0_MASK 0xffffff00 +#define BCHP_NAND_SEMAPHORE_reserved0_SHIFT 8 + +/* NAND :: SEMAPHORE :: semaphore_ctrl [07:00] */ +#define BCHP_NAND_SEMAPHORE_semaphore_ctrl_MASK 0x000000ff +#define BCHP_NAND_SEMAPHORE_semaphore_ctrl_SHIFT 0 + +/*************************************************************************** + *FLASH_DEVICE_ID - Nand Flash Device ID + ***************************************************************************/ +/* NAND :: FLASH_DEVICE_ID :: BYTE_0 [31:24] */ +#define BCHP_NAND_FLASH_DEVICE_ID_BYTE_0_MASK 0xff000000 +#define BCHP_NAND_FLASH_DEVICE_ID_BYTE_0_SHIFT 24 + +/* NAND :: FLASH_DEVICE_ID :: BYTE_1 [23:16] */ +#define BCHP_NAND_FLASH_DEVICE_ID_BYTE_1_MASK 0x00ff0000 +#define BCHP_NAND_FLASH_DEVICE_ID_BYTE_1_SHIFT 16 + +/* NAND :: FLASH_DEVICE_ID :: BYTE_2 [15:08] */ +#define BCHP_NAND_FLASH_DEVICE_ID_BYTE_2_MASK 0x0000ff00 +#define BCHP_NAND_FLASH_DEVICE_ID_BYTE_2_SHIFT 8 + +/* NAND :: FLASH_DEVICE_ID :: BYTE_3 [07:00] */ +#define BCHP_NAND_FLASH_DEVICE_ID_BYTE_3_MASK 0x000000ff +#define BCHP_NAND_FLASH_DEVICE_ID_BYTE_3_SHIFT 0 + +/*************************************************************************** + *FLASH_DEVICE_ID_EXT - Nand Flash Extended Device ID + ***************************************************************************/ +/* NAND :: FLASH_DEVICE_ID_EXT :: BYTE_4 [31:24] */ +#define BCHP_NAND_FLASH_DEVICE_ID_EXT_BYTE_4_MASK 0xff000000 +#define BCHP_NAND_FLASH_DEVICE_ID_EXT_BYTE_4_SHIFT 24 + +/* NAND :: FLASH_DEVICE_ID_EXT :: BYTE_5 [23:16] */ +#define BCHP_NAND_FLASH_DEVICE_ID_EXT_BYTE_5_MASK 0x00ff0000 +#define BCHP_NAND_FLASH_DEVICE_ID_EXT_BYTE_5_SHIFT 16 + +/* NAND :: FLASH_DEVICE_ID_EXT :: BYTE_6 [15:08] */ +#define BCHP_NAND_FLASH_DEVICE_ID_EXT_BYTE_6_MASK 0x0000ff00 +#define BCHP_NAND_FLASH_DEVICE_ID_EXT_BYTE_6_SHIFT 8 + +/* NAND :: FLASH_DEVICE_ID_EXT :: BYTE_7 [07:00] */ +#define BCHP_NAND_FLASH_DEVICE_ID_EXT_BYTE_7_MASK 0x000000ff +#define BCHP_NAND_FLASH_DEVICE_ID_EXT_BYTE_7_SHIFT 0 + +/*************************************************************************** + *BLOCK_LOCK_STATUS - Nand Flash Block Lock Status + ***************************************************************************/ +/* NAND :: BLOCK_LOCK_STATUS :: reserved0 [31:08] */ +#define BCHP_NAND_BLOCK_LOCK_STATUS_reserved0_MASK 0xffffff00 +#define BCHP_NAND_BLOCK_LOCK_STATUS_reserved0_SHIFT 8 + +/* NAND :: BLOCK_LOCK_STATUS :: STATUS [07:00] */ +#define BCHP_NAND_BLOCK_LOCK_STATUS_STATUS_MASK 0x000000ff +#define BCHP_NAND_BLOCK_LOCK_STATUS_STATUS_SHIFT 0 + +/*************************************************************************** + *INTFC_STATUS - Nand Flash Interface Status + ***************************************************************************/ +/* NAND :: INTFC_STATUS :: CTLR_READY [31:31] */ +#define BCHP_NAND_INTFC_STATUS_CTLR_READY_MASK 0x80000000 +#define BCHP_NAND_INTFC_STATUS_CTLR_READY_SHIFT 31 + +/* NAND :: INTFC_STATUS :: FLASH_READY [30:30] */ +#define BCHP_NAND_INTFC_STATUS_FLASH_READY_MASK 0x40000000 +#define BCHP_NAND_INTFC_STATUS_FLASH_READY_SHIFT 30 + +/* NAND :: INTFC_STATUS :: CACHE_VALID [29:29] */ +#define BCHP_NAND_INTFC_STATUS_CACHE_VALID_MASK 0x20000000 +#define BCHP_NAND_INTFC_STATUS_CACHE_VALID_SHIFT 29 + +/* NAND :: INTFC_STATUS :: SPARE_AREA_VALID [28:28] */ +#define BCHP_NAND_INTFC_STATUS_SPARE_AREA_VALID_MASK 0x10000000 +#define BCHP_NAND_INTFC_STATUS_SPARE_AREA_VALID_SHIFT 28 + +/* NAND :: INTFC_STATUS :: ERASED [27:27] */ +#define BCHP_NAND_INTFC_STATUS_ERASED_MASK 0x08000000 +#define BCHP_NAND_INTFC_STATUS_ERASED_SHIFT 27 + +/* NAND :: INTFC_STATUS :: reserved0 [26:08] */ +#define BCHP_NAND_INTFC_STATUS_reserved0_MASK 0x07ffff00 +#define BCHP_NAND_INTFC_STATUS_reserved0_SHIFT 8 + +/* NAND :: INTFC_STATUS :: FLASH_STATUS [07:00] */ +#define BCHP_NAND_INTFC_STATUS_FLASH_STATUS_MASK 0x000000ff +#define BCHP_NAND_INTFC_STATUS_FLASH_STATUS_SHIFT 0 + +/*************************************************************************** + *ECC_CORR_EXT_ADDR - ECC Correctable Error Extended Address + ***************************************************************************/ +/* NAND :: ECC_CORR_EXT_ADDR :: reserved0 [31:19] */ +#define BCHP_NAND_ECC_CORR_EXT_ADDR_reserved0_MASK 0xfff80000 +#define BCHP_NAND_ECC_CORR_EXT_ADDR_reserved0_SHIFT 19 + +/* NAND :: ECC_CORR_EXT_ADDR :: CS_SEL [18:16] */ +#define BCHP_NAND_ECC_CORR_EXT_ADDR_CS_SEL_MASK 0x00070000 +#define BCHP_NAND_ECC_CORR_EXT_ADDR_CS_SEL_SHIFT 16 + +/* NAND :: ECC_CORR_EXT_ADDR :: EXT_ADDRESS [15:00] */ +#define BCHP_NAND_ECC_CORR_EXT_ADDR_EXT_ADDRESS_MASK 0x0000ffff +#define BCHP_NAND_ECC_CORR_EXT_ADDR_EXT_ADDRESS_SHIFT 0 + +/*************************************************************************** + *ECC_CORR_ADDR - ECC Correctable Error Address + ***************************************************************************/ +/* NAND :: ECC_CORR_ADDR :: ADDRESS [31:00] */ +#define BCHP_NAND_ECC_CORR_ADDR_ADDRESS_MASK 0xffffffff +#define BCHP_NAND_ECC_CORR_ADDR_ADDRESS_SHIFT 0 + +/*************************************************************************** + *ECC_UNC_EXT_ADDR - ECC Uncorrectable Error Extended Address + ***************************************************************************/ +/* NAND :: ECC_UNC_EXT_ADDR :: reserved0 [31:19] */ +#define BCHP_NAND_ECC_UNC_EXT_ADDR_reserved0_MASK 0xfff80000 +#define BCHP_NAND_ECC_UNC_EXT_ADDR_reserved0_SHIFT 19 + +/* NAND :: ECC_UNC_EXT_ADDR :: CS_SEL [18:16] */ +#define BCHP_NAND_ECC_UNC_EXT_ADDR_CS_SEL_MASK 0x00070000 +#define BCHP_NAND_ECC_UNC_EXT_ADDR_CS_SEL_SHIFT 16 + +/* NAND :: ECC_UNC_EXT_ADDR :: EXT_ADDRESS [15:00] */ +#define BCHP_NAND_ECC_UNC_EXT_ADDR_EXT_ADDRESS_MASK 0x0000ffff +#define BCHP_NAND_ECC_UNC_EXT_ADDR_EXT_ADDRESS_SHIFT 0 + +/*************************************************************************** + *ECC_UNC_ADDR - ECC Uncorrectable Error Address + ***************************************************************************/ +/* NAND :: ECC_UNC_ADDR :: ADDRESS [31:00] */ +#define BCHP_NAND_ECC_UNC_ADDR_ADDRESS_MASK 0xffffffff +#define BCHP_NAND_ECC_UNC_ADDR_ADDRESS_SHIFT 0 + +/*************************************************************************** + *READ_ERROR_COUNT - Read Error Count + ***************************************************************************/ +/* NAND :: READ_ERROR_COUNT :: READ_ERROR_COUNT [31:00] */ +#define BCHP_NAND_READ_ERROR_COUNT_READ_ERROR_COUNT_MASK 0xffffffff +#define BCHP_NAND_READ_ERROR_COUNT_READ_ERROR_COUNT_SHIFT 0 + +/*************************************************************************** + *CORR_STAT_THRESHOLD - Correctable Error Reporting Threshold + ***************************************************************************/ +/* NAND :: CORR_STAT_THRESHOLD :: reserved0 [31:04] */ +#define BCHP_NAND_CORR_STAT_THRESHOLD_reserved0_MASK 0xfffffff0 +#define BCHP_NAND_CORR_STAT_THRESHOLD_reserved0_SHIFT 4 + +/* NAND :: CORR_STAT_THRESHOLD :: CORR_STAT_THRESHOLD [03:00] */ +#define BCHP_NAND_CORR_STAT_THRESHOLD_CORR_STAT_THRESHOLD_MASK 0x0000000f +#define BCHP_NAND_CORR_STAT_THRESHOLD_CORR_STAT_THRESHOLD_SHIFT 0 + +/*************************************************************************** + *ONFI_STATUS - ONFI Status + ***************************************************************************/ +/* NAND :: ONFI_STATUS :: ONFI_DEBUG_SEL [31:28] */ +#define BCHP_NAND_ONFI_STATUS_ONFI_DEBUG_SEL_MASK 0xf0000000 +#define BCHP_NAND_ONFI_STATUS_ONFI_DEBUG_SEL_SHIFT 28 + +/* NAND :: ONFI_STATUS :: reserved0 [27:06] */ +#define BCHP_NAND_ONFI_STATUS_reserved0_MASK 0x0fffffc0 +#define BCHP_NAND_ONFI_STATUS_reserved0_SHIFT 6 + +/* NAND :: ONFI_STATUS :: ONFI_BAD_IDENT_PG2 [05:05] */ +#define BCHP_NAND_ONFI_STATUS_ONFI_BAD_IDENT_PG2_MASK 0x00000020 +#define BCHP_NAND_ONFI_STATUS_ONFI_BAD_IDENT_PG2_SHIFT 5 + +/* NAND :: ONFI_STATUS :: ONFI_BAD_IDENT_PG1 [04:04] */ +#define BCHP_NAND_ONFI_STATUS_ONFI_BAD_IDENT_PG1_MASK 0x00000010 +#define BCHP_NAND_ONFI_STATUS_ONFI_BAD_IDENT_PG1_SHIFT 4 + +/* NAND :: ONFI_STATUS :: ONFI_BAD_IDENT_PG0 [03:03] */ +#define BCHP_NAND_ONFI_STATUS_ONFI_BAD_IDENT_PG0_MASK 0x00000008 +#define BCHP_NAND_ONFI_STATUS_ONFI_BAD_IDENT_PG0_SHIFT 3 + +/* NAND :: ONFI_STATUS :: ONFI_CRC_ERROR_PG2 [02:02] */ +#define BCHP_NAND_ONFI_STATUS_ONFI_CRC_ERROR_PG2_MASK 0x00000004 +#define BCHP_NAND_ONFI_STATUS_ONFI_CRC_ERROR_PG2_SHIFT 2 + +/* NAND :: ONFI_STATUS :: ONFI_CRC_ERROR_PG1 [01:01] */ +#define BCHP_NAND_ONFI_STATUS_ONFI_CRC_ERROR_PG1_MASK 0x00000002 +#define BCHP_NAND_ONFI_STATUS_ONFI_CRC_ERROR_PG1_SHIFT 1 + +/* NAND :: ONFI_STATUS :: ONFI_CRC_ERROR_PG0 [00:00] */ +#define BCHP_NAND_ONFI_STATUS_ONFI_CRC_ERROR_PG0_MASK 0x00000001 +#define BCHP_NAND_ONFI_STATUS_ONFI_CRC_ERROR_PG0_SHIFT 0 + +/*************************************************************************** + *ONFI_DEBUG_DATA - ONFI Debug Data + ***************************************************************************/ +/* NAND :: ONFI_DEBUG_DATA :: ONFI_DEBUG_DATA [31:00] */ +#define BCHP_NAND_ONFI_DEBUG_DATA_ONFI_DEBUG_DATA_MASK 0xffffffff +#define BCHP_NAND_ONFI_DEBUG_DATA_ONFI_DEBUG_DATA_SHIFT 0 + +/*************************************************************************** + *FLASH_READ_EXT_ADDR - Flash Read Data Extended Address + ***************************************************************************/ +/* NAND :: FLASH_READ_EXT_ADDR :: reserved0 [31:19] */ +#define BCHP_NAND_FLASH_READ_EXT_ADDR_reserved0_MASK 0xfff80000 +#define BCHP_NAND_FLASH_READ_EXT_ADDR_reserved0_SHIFT 19 + +/* NAND :: FLASH_READ_EXT_ADDR :: CS_SEL [18:16] */ +#define BCHP_NAND_FLASH_READ_EXT_ADDR_CS_SEL_MASK 0x00070000 +#define BCHP_NAND_FLASH_READ_EXT_ADDR_CS_SEL_SHIFT 16 + +/* NAND :: FLASH_READ_EXT_ADDR :: EXT_ADDRESS [15:00] */ +#define BCHP_NAND_FLASH_READ_EXT_ADDR_EXT_ADDRESS_MASK 0x0000ffff +#define BCHP_NAND_FLASH_READ_EXT_ADDR_EXT_ADDRESS_SHIFT 0 + +/*************************************************************************** + *FLASH_READ_ADDR - Flash Read Data Address + ***************************************************************************/ +/* NAND :: FLASH_READ_ADDR :: ADDRESS [31:00] */ +#define BCHP_NAND_FLASH_READ_ADDR_ADDRESS_MASK 0xffffffff +#define BCHP_NAND_FLASH_READ_ADDR_ADDRESS_SHIFT 0 + +/*************************************************************************** + *PROGRAM_PAGE_EXT_ADDR - Page Program Extended Address + ***************************************************************************/ +/* NAND :: PROGRAM_PAGE_EXT_ADDR :: reserved0 [31:19] */ +#define BCHP_NAND_PROGRAM_PAGE_EXT_ADDR_reserved0_MASK 0xfff80000 +#define BCHP_NAND_PROGRAM_PAGE_EXT_ADDR_reserved0_SHIFT 19 + +/* NAND :: PROGRAM_PAGE_EXT_ADDR :: CS_SEL [18:16] */ +#define BCHP_NAND_PROGRAM_PAGE_EXT_ADDR_CS_SEL_MASK 0x00070000 +#define BCHP_NAND_PROGRAM_PAGE_EXT_ADDR_CS_SEL_SHIFT 16 + +/* NAND :: PROGRAM_PAGE_EXT_ADDR :: EXT_ADDRESS [15:00] */ +#define BCHP_NAND_PROGRAM_PAGE_EXT_ADDR_EXT_ADDRESS_MASK 0x0000ffff +#define BCHP_NAND_PROGRAM_PAGE_EXT_ADDR_EXT_ADDRESS_SHIFT 0 + +/*************************************************************************** + *PROGRAM_PAGE_ADDR - Page Program Address + ***************************************************************************/ +/* NAND :: PROGRAM_PAGE_ADDR :: ADDRESS [31:00] */ +#define BCHP_NAND_PROGRAM_PAGE_ADDR_ADDRESS_MASK 0xffffffff +#define BCHP_NAND_PROGRAM_PAGE_ADDR_ADDRESS_SHIFT 0 + +/*************************************************************************** + *COPY_BACK_EXT_ADDR - Copy Back Extended Address + ***************************************************************************/ +/* NAND :: COPY_BACK_EXT_ADDR :: reserved0 [31:19] */ +#define BCHP_NAND_COPY_BACK_EXT_ADDR_reserved0_MASK 0xfff80000 +#define BCHP_NAND_COPY_BACK_EXT_ADDR_reserved0_SHIFT 19 + +/* NAND :: COPY_BACK_EXT_ADDR :: CS_SEL [18:16] */ +#define BCHP_NAND_COPY_BACK_EXT_ADDR_CS_SEL_MASK 0x00070000 +#define BCHP_NAND_COPY_BACK_EXT_ADDR_CS_SEL_SHIFT 16 + +/* NAND :: COPY_BACK_EXT_ADDR :: EXT_ADDRESS [15:00] */ +#define BCHP_NAND_COPY_BACK_EXT_ADDR_EXT_ADDRESS_MASK 0x0000ffff +#define BCHP_NAND_COPY_BACK_EXT_ADDR_EXT_ADDRESS_SHIFT 0 + +/*************************************************************************** + *COPY_BACK_ADDR - Copy Back Address + ***************************************************************************/ +/* NAND :: COPY_BACK_ADDR :: ADDRESS [31:00] */ +#define BCHP_NAND_COPY_BACK_ADDR_ADDRESS_MASK 0xffffffff +#define BCHP_NAND_COPY_BACK_ADDR_ADDRESS_SHIFT 0 + +/*************************************************************************** + *BLOCK_ERASE_EXT_ADDR - Block Erase Extended Address + ***************************************************************************/ +/* NAND :: BLOCK_ERASE_EXT_ADDR :: reserved0 [31:19] */ +#define BCHP_NAND_BLOCK_ERASE_EXT_ADDR_reserved0_MASK 0xfff80000 +#define BCHP_NAND_BLOCK_ERASE_EXT_ADDR_reserved0_SHIFT 19 + +/* NAND :: BLOCK_ERASE_EXT_ADDR :: CS_SEL [18:16] */ +#define BCHP_NAND_BLOCK_ERASE_EXT_ADDR_CS_SEL_MASK 0x00070000 +#define BCHP_NAND_BLOCK_ERASE_EXT_ADDR_CS_SEL_SHIFT 16 + +/* NAND :: BLOCK_ERASE_EXT_ADDR :: EXT_ADDRESS [15:00] */ +#define BCHP_NAND_BLOCK_ERASE_EXT_ADDR_EXT_ADDRESS_MASK 0x0000ffff +#define BCHP_NAND_BLOCK_ERASE_EXT_ADDR_EXT_ADDRESS_SHIFT 0 + +/*************************************************************************** + *BLOCK_ERASE_ADDR - Block Erase Address + ***************************************************************************/ +/* NAND :: BLOCK_ERASE_ADDR :: ADDRESS [31:00] */ +#define BCHP_NAND_BLOCK_ERASE_ADDR_ADDRESS_MASK 0xffffffff +#define BCHP_NAND_BLOCK_ERASE_ADDR_ADDRESS_SHIFT 0 + +/*************************************************************************** + *INV_READ_EXT_ADDR - Flash Invalid Data Extended Address + ***************************************************************************/ +/* NAND :: INV_READ_EXT_ADDR :: reserved0 [31:19] */ +#define BCHP_NAND_INV_READ_EXT_ADDR_reserved0_MASK 0xfff80000 +#define BCHP_NAND_INV_READ_EXT_ADDR_reserved0_SHIFT 19 + +/* NAND :: INV_READ_EXT_ADDR :: CS_SEL [18:16] */ +#define BCHP_NAND_INV_READ_EXT_ADDR_CS_SEL_MASK 0x00070000 +#define BCHP_NAND_INV_READ_EXT_ADDR_CS_SEL_SHIFT 16 + +/* NAND :: INV_READ_EXT_ADDR :: EXT_ADDRESS [15:00] */ +#define BCHP_NAND_INV_READ_EXT_ADDR_EXT_ADDRESS_MASK 0x0000ffff +#define BCHP_NAND_INV_READ_EXT_ADDR_EXT_ADDRESS_SHIFT 0 + +/*************************************************************************** + *INV_READ_ADDR - Flash Invalid Data Address + ***************************************************************************/ +/* NAND :: INV_READ_ADDR :: ADDRESS [31:00] */ +#define BCHP_NAND_INV_READ_ADDR_ADDRESS_MASK 0xffffffff +#define BCHP_NAND_INV_READ_ADDR_ADDRESS_SHIFT 0 + +/*************************************************************************** + *BLK_WR_PROTECT - Block Write Protect Enable and Size for EBI_CS0b + ***************************************************************************/ +/* NAND :: BLK_WR_PROTECT :: BLK_END_ADDR [31:00] */ +#define BCHP_NAND_BLK_WR_PROTECT_BLK_END_ADDR_MASK 0xffffffff +#define BCHP_NAND_BLK_WR_PROTECT_BLK_END_ADDR_SHIFT 0 + +/*************************************************************************** + *ACC_CONTROL_CS1 - Nand Flash Access Control + ***************************************************************************/ +/* NAND :: ACC_CONTROL_CS1 :: RD_ECC_EN [31:31] */ +#define BCHP_NAND_ACC_CONTROL_CS1_RD_ECC_EN_MASK 0x80000000 +#define BCHP_NAND_ACC_CONTROL_CS1_RD_ECC_EN_SHIFT 31 + +/* NAND :: ACC_CONTROL_CS1 :: WR_ECC_EN [30:30] */ +#define BCHP_NAND_ACC_CONTROL_CS1_WR_ECC_EN_MASK 0x40000000 +#define BCHP_NAND_ACC_CONTROL_CS1_WR_ECC_EN_SHIFT 30 + +/* NAND :: ACC_CONTROL_CS1 :: reserved0 [29:29] */ +#define BCHP_NAND_ACC_CONTROL_CS1_reserved0_MASK 0x20000000 +#define BCHP_NAND_ACC_CONTROL_CS1_reserved0_SHIFT 29 + +/* NAND :: ACC_CONTROL_CS1 :: FAST_PGM_RDIN [28:28] */ +#define BCHP_NAND_ACC_CONTROL_CS1_FAST_PGM_RDIN_MASK 0x10000000 +#define BCHP_NAND_ACC_CONTROL_CS1_FAST_PGM_RDIN_SHIFT 28 + +/* NAND :: ACC_CONTROL_CS1 :: RD_ERASED_ECC_EN [27:27] */ +#define BCHP_NAND_ACC_CONTROL_CS1_RD_ERASED_ECC_EN_MASK 0x08000000 +#define BCHP_NAND_ACC_CONTROL_CS1_RD_ERASED_ECC_EN_SHIFT 27 + +/* NAND :: ACC_CONTROL_CS1 :: PARTIAL_PAGE_EN [26:26] */ +#define BCHP_NAND_ACC_CONTROL_CS1_PARTIAL_PAGE_EN_MASK 0x04000000 +#define BCHP_NAND_ACC_CONTROL_CS1_PARTIAL_PAGE_EN_SHIFT 26 + +/* NAND :: ACC_CONTROL_CS1 :: WR_PREEMPT_EN [25:25] */ +#define BCHP_NAND_ACC_CONTROL_CS1_WR_PREEMPT_EN_MASK 0x02000000 +#define BCHP_NAND_ACC_CONTROL_CS1_WR_PREEMPT_EN_SHIFT 25 + +/* NAND :: ACC_CONTROL_CS1 :: PAGE_HIT_EN [24:24] */ +#define BCHP_NAND_ACC_CONTROL_CS1_PAGE_HIT_EN_MASK 0x01000000 +#define BCHP_NAND_ACC_CONTROL_CS1_PAGE_HIT_EN_SHIFT 24 + +/* NAND :: ACC_CONTROL_CS1 :: reserved1 [23:20] */ +#define BCHP_NAND_ACC_CONTROL_CS1_reserved1_MASK 0x00f00000 +#define BCHP_NAND_ACC_CONTROL_CS1_reserved1_SHIFT 20 + +/* NAND :: ACC_CONTROL_CS1 :: ECC_LEVEL [19:16] */ +#define BCHP_NAND_ACC_CONTROL_CS1_ECC_LEVEL_MASK 0x000f0000 +#define BCHP_NAND_ACC_CONTROL_CS1_ECC_LEVEL_SHIFT 16 + +/* NAND :: ACC_CONTROL_CS1 :: reserved2 [15:06] */ +#define BCHP_NAND_ACC_CONTROL_CS1_reserved2_MASK 0x0000ffc0 +#define BCHP_NAND_ACC_CONTROL_CS1_reserved2_SHIFT 6 + +/* NAND :: ACC_CONTROL_CS1 :: SPARE_AREA_SIZE [05:00] */ +#define BCHP_NAND_ACC_CONTROL_CS1_SPARE_AREA_SIZE_MASK 0x0000003f +#define BCHP_NAND_ACC_CONTROL_CS1_SPARE_AREA_SIZE_SHIFT 0 + +/*************************************************************************** + *CONFIG_CS1 - Nand Flash Config + ***************************************************************************/ +/* NAND :: CONFIG_CS1 :: CONFIG_LOCK [31:31] */ +#define BCHP_NAND_CONFIG_CS1_CONFIG_LOCK_MASK 0x80000000 +#define BCHP_NAND_CONFIG_CS1_CONFIG_LOCK_SHIFT 31 + +/* NAND :: CONFIG_CS1 :: BLOCK_SIZE [30:28] */ +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_MASK 0x70000000 +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_SHIFT 28 +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_BK_SIZE_2048KB 6 +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_BK_SIZE_1024KB 5 +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_BK_SIZE_512KB 3 +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_BK_SIZE_128KB 1 +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_BK_SIZE_16KB 0 +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_BK_SIZE_8KB 2 +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_BK_SIZE_256KB 4 + +/* NAND :: CONFIG_CS1 :: DEVICE_SIZE [27:24] */ +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_MASK 0x0f000000 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_SHIFT 24 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_4MB 0 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_8MB 1 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_16MB 2 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_32MB 3 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_64MB 4 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_128MB 5 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_256MB 6 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_512MB 7 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_1GB 8 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_2GB 9 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_4GB 10 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_8GB 11 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_16GB 12 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_32GB 13 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_64GB 14 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_128GB 15 + +/* NAND :: CONFIG_CS1 :: DEVICE_WIDTH [23:23] */ +#define BCHP_NAND_CONFIG_CS1_DEVICE_WIDTH_MASK 0x00800000 +#define BCHP_NAND_CONFIG_CS1_DEVICE_WIDTH_SHIFT 23 +#define BCHP_NAND_CONFIG_CS1_DEVICE_WIDTH_DVC_WIDTH_8 0 +#define BCHP_NAND_CONFIG_CS1_DEVICE_WIDTH_DVC_WIDTH_16 1 + +/* NAND :: CONFIG_CS1 :: reserved0 [22:22] */ +#define BCHP_NAND_CONFIG_CS1_reserved0_MASK 0x00400000 +#define BCHP_NAND_CONFIG_CS1_reserved0_SHIFT 22 + +/* NAND :: CONFIG_CS1 :: PAGE_SIZE [21:20] */ +#define BCHP_NAND_CONFIG_CS1_PAGE_SIZE_MASK 0x00300000 +#define BCHP_NAND_CONFIG_CS1_PAGE_SIZE_SHIFT 20 +#define BCHP_NAND_CONFIG_CS1_PAGE_SIZE_PG_SIZE_512 0 +#define BCHP_NAND_CONFIG_CS1_PAGE_SIZE_PG_SIZE_2KB 1 +#define BCHP_NAND_CONFIG_CS1_PAGE_SIZE_PG_SIZE_4KB 2 +#define BCHP_NAND_CONFIG_CS1_PAGE_SIZE_PG_SIZE_8KB 3 + +/* NAND :: CONFIG_CS1 :: reserved1 [19:19] */ +#define BCHP_NAND_CONFIG_CS1_reserved1_MASK 0x00080000 +#define BCHP_NAND_CONFIG_CS1_reserved1_SHIFT 19 + +/* NAND :: CONFIG_CS1 :: FUL_ADR_BYTES [18:16] */ +#define BCHP_NAND_CONFIG_CS1_FUL_ADR_BYTES_MASK 0x00070000 +#define BCHP_NAND_CONFIG_CS1_FUL_ADR_BYTES_SHIFT 16 + +/* NAND :: CONFIG_CS1 :: reserved2 [15:15] */ +#define BCHP_NAND_CONFIG_CS1_reserved2_MASK 0x00008000 +#define BCHP_NAND_CONFIG_CS1_reserved2_SHIFT 15 + +/* NAND :: CONFIG_CS1 :: COL_ADR_BYTES [14:12] */ +#define BCHP_NAND_CONFIG_CS1_COL_ADR_BYTES_MASK 0x00007000 +#define BCHP_NAND_CONFIG_CS1_COL_ADR_BYTES_SHIFT 12 + +/* NAND :: CONFIG_CS1 :: reserved3 [11:11] */ +#define BCHP_NAND_CONFIG_CS1_reserved3_MASK 0x00000800 +#define BCHP_NAND_CONFIG_CS1_reserved3_SHIFT 11 + +/* NAND :: CONFIG_CS1 :: BLK_ADR_BYTES [10:08] */ +#define BCHP_NAND_CONFIG_CS1_BLK_ADR_BYTES_MASK 0x00000700 +#define BCHP_NAND_CONFIG_CS1_BLK_ADR_BYTES_SHIFT 8 + +/* NAND :: CONFIG_CS1 :: reserved4 [07:00] */ +#define BCHP_NAND_CONFIG_CS1_reserved4_MASK 0x000000ff +#define BCHP_NAND_CONFIG_CS1_reserved4_SHIFT 0 + +/*************************************************************************** + *TIMING_1_CS1 - Nand Flash Timing Parameters 1 + ***************************************************************************/ +/* NAND :: TIMING_1_CS1 :: tWP [31:28] */ +#define BCHP_NAND_TIMING_1_CS1_tWP_MASK 0xf0000000 +#define BCHP_NAND_TIMING_1_CS1_tWP_SHIFT 28 + +/* NAND :: TIMING_1_CS1 :: tWH [27:24] */ +#define BCHP_NAND_TIMING_1_CS1_tWH_MASK 0x0f000000 +#define BCHP_NAND_TIMING_1_CS1_tWH_SHIFT 24 + +/* NAND :: TIMING_1_CS1 :: tRP [23:20] */ +#define BCHP_NAND_TIMING_1_CS1_tRP_MASK 0x00f00000 +#define BCHP_NAND_TIMING_1_CS1_tRP_SHIFT 20 + +/* NAND :: TIMING_1_CS1 :: tREH [19:16] */ +#define BCHP_NAND_TIMING_1_CS1_tREH_MASK 0x000f0000 +#define BCHP_NAND_TIMING_1_CS1_tREH_SHIFT 16 + +/* NAND :: TIMING_1_CS1 :: tCS [15:12] */ +#define BCHP_NAND_TIMING_1_CS1_tCS_MASK 0x0000f000 +#define BCHP_NAND_TIMING_1_CS1_tCS_SHIFT 12 + +/* NAND :: TIMING_1_CS1 :: tCLH [11:08] */ +#define BCHP_NAND_TIMING_1_CS1_tCLH_MASK 0x00000f00 +#define BCHP_NAND_TIMING_1_CS1_tCLH_SHIFT 8 + +/* NAND :: TIMING_1_CS1 :: tALH [07:04] */ +#define BCHP_NAND_TIMING_1_CS1_tALH_MASK 0x000000f0 +#define BCHP_NAND_TIMING_1_CS1_tALH_SHIFT 4 + +/* NAND :: TIMING_1_CS1 :: tADL [03:00] */ +#define BCHP_NAND_TIMING_1_CS1_tADL_MASK 0x0000000f +#define BCHP_NAND_TIMING_1_CS1_tADL_SHIFT 0 + +/*************************************************************************** + *TIMING_2_CS1 - Nand Flash Timing Parameters 2 + ***************************************************************************/ +/* NAND :: TIMING_2_CS1 :: CLK_SELECT [31:31] */ +#define BCHP_NAND_TIMING_2_CS1_CLK_SELECT_MASK 0x80000000 +#define BCHP_NAND_TIMING_2_CS1_CLK_SELECT_SHIFT 31 +#define BCHP_NAND_TIMING_2_CS1_CLK_SELECT_CLK_108 0 +#define BCHP_NAND_TIMING_2_CS1_CLK_SELECT_CLK_216 1 + +/* NAND :: TIMING_2_CS1 :: reserved0 [30:13] */ +#define BCHP_NAND_TIMING_2_CS1_reserved0_MASK 0x7fffe000 +#define BCHP_NAND_TIMING_2_CS1_reserved0_SHIFT 13 + +/* NAND :: TIMING_2_CS1 :: tWB [12:09] */ +#define BCHP_NAND_TIMING_2_CS1_tWB_MASK 0x00001e00 +#define BCHP_NAND_TIMING_2_CS1_tWB_SHIFT 9 + +/* NAND :: TIMING_2_CS1 :: tWHR [08:04] */ +#define BCHP_NAND_TIMING_2_CS1_tWHR_MASK 0x000001f0 +#define BCHP_NAND_TIMING_2_CS1_tWHR_SHIFT 4 + +/* NAND :: TIMING_2_CS1 :: tREAD [03:00] */ +#define BCHP_NAND_TIMING_2_CS1_tREAD_MASK 0x0000000f +#define BCHP_NAND_TIMING_2_CS1_tREAD_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_READ_OFS_10 - Nand Flash Spare Area Read Bytes 16-19 + ***************************************************************************/ +/* NAND :: SPARE_AREA_READ_OFS_10 :: BYTE_OFS_16 [31:24] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_10_BYTE_OFS_16_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_10_BYTE_OFS_16_SHIFT 24 + +/* NAND :: SPARE_AREA_READ_OFS_10 :: BYTE_OFS_17 [23:16] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_10_BYTE_OFS_17_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_10_BYTE_OFS_17_SHIFT 16 + +/* NAND :: SPARE_AREA_READ_OFS_10 :: BYTE_OFS_18 [15:08] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_10_BYTE_OFS_18_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_READ_OFS_10_BYTE_OFS_18_SHIFT 8 + +/* NAND :: SPARE_AREA_READ_OFS_10 :: BYTE_OFS_19 [07:00] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_10_BYTE_OFS_19_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_READ_OFS_10_BYTE_OFS_19_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_READ_OFS_14 - Nand Flash Spare Area Read Bytes 20-23 + ***************************************************************************/ +/* NAND :: SPARE_AREA_READ_OFS_14 :: BYTE_OFS_20 [31:24] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_14_BYTE_OFS_20_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_14_BYTE_OFS_20_SHIFT 24 + +/* NAND :: SPARE_AREA_READ_OFS_14 :: BYTE_OFS_21 [23:16] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_14_BYTE_OFS_21_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_14_BYTE_OFS_21_SHIFT 16 + +/* NAND :: SPARE_AREA_READ_OFS_14 :: BYTE_OFS_22 [15:08] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_14_BYTE_OFS_22_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_READ_OFS_14_BYTE_OFS_22_SHIFT 8 + +/* NAND :: SPARE_AREA_READ_OFS_14 :: BYTE_OFS_23 [07:00] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_14_BYTE_OFS_23_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_READ_OFS_14_BYTE_OFS_23_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_READ_OFS_18 - Nand Flash Spare Area Read Bytes 24-27 + ***************************************************************************/ +/* NAND :: SPARE_AREA_READ_OFS_18 :: BYTE_OFS_24 [31:24] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_18_BYTE_OFS_24_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_18_BYTE_OFS_24_SHIFT 24 + +/* NAND :: SPARE_AREA_READ_OFS_18 :: BYTE_OFS_25 [23:16] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_18_BYTE_OFS_25_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_18_BYTE_OFS_25_SHIFT 16 + +/* NAND :: SPARE_AREA_READ_OFS_18 :: BYTE_OFS_26 [15:08] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_18_BYTE_OFS_26_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_READ_OFS_18_BYTE_OFS_26_SHIFT 8 + +/* NAND :: SPARE_AREA_READ_OFS_18 :: BYTE_OFS_27 [07:00] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_18_BYTE_OFS_27_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_READ_OFS_18_BYTE_OFS_27_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_READ_OFS_1C - Nand Flash Spare Area Read Bytes 28-31 + ***************************************************************************/ +/* NAND :: SPARE_AREA_READ_OFS_1C :: BYTE_OFS_28 [31:24] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_1C_BYTE_OFS_28_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_1C_BYTE_OFS_28_SHIFT 24 + +/* NAND :: SPARE_AREA_READ_OFS_1C :: BYTE_OFS_29 [23:16] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_1C_BYTE_OFS_29_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_1C_BYTE_OFS_29_SHIFT 16 + +/* NAND :: SPARE_AREA_READ_OFS_1C :: BYTE_OFS_30 [15:08] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_1C_BYTE_OFS_30_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_READ_OFS_1C_BYTE_OFS_30_SHIFT 8 + +/* NAND :: SPARE_AREA_READ_OFS_1C :: BYTE_OFS_31 [07:00] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_1C_BYTE_OFS_31_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_READ_OFS_1C_BYTE_OFS_31_SHIFT 0 + +/*************************************************************************** + *LL_OP - Nand Flash Low Level Operation + ***************************************************************************/ +/* NAND :: LL_OP :: RETURN_IDLE [31:31] */ +#define BCHP_NAND_LL_OP_RETURN_IDLE_MASK 0x80000000 +#define BCHP_NAND_LL_OP_RETURN_IDLE_SHIFT 31 + +/* NAND :: LL_OP :: reserved0 [30:20] */ +#define BCHP_NAND_LL_OP_reserved0_MASK 0x7ff00000 +#define BCHP_NAND_LL_OP_reserved0_SHIFT 20 + +/* NAND :: LL_OP :: CLE [19:19] */ +#define BCHP_NAND_LL_OP_CLE_MASK 0x00080000 +#define BCHP_NAND_LL_OP_CLE_SHIFT 19 + +/* NAND :: LL_OP :: ALE [18:18] */ +#define BCHP_NAND_LL_OP_ALE_MASK 0x00040000 +#define BCHP_NAND_LL_OP_ALE_SHIFT 18 + +/* NAND :: LL_OP :: WE [17:17] */ +#define BCHP_NAND_LL_OP_WE_MASK 0x00020000 +#define BCHP_NAND_LL_OP_WE_SHIFT 17 + +/* NAND :: LL_OP :: RE [16:16] */ +#define BCHP_NAND_LL_OP_RE_MASK 0x00010000 +#define BCHP_NAND_LL_OP_RE_SHIFT 16 + +/* NAND :: LL_OP :: DATA [15:00] */ +#define BCHP_NAND_LL_OP_DATA_MASK 0x0000ffff +#define BCHP_NAND_LL_OP_DATA_SHIFT 0 + +/*************************************************************************** + *LL_RDDATA - Nand Flash Low Level Read Data + ***************************************************************************/ +/* NAND :: LL_RDDATA :: reserved0 [31:16] */ +#define BCHP_NAND_LL_RDDATA_reserved0_MASK 0xffff0000 +#define BCHP_NAND_LL_RDDATA_reserved0_SHIFT 16 + +/* NAND :: LL_RDDATA :: DATA [15:00] */ +#define BCHP_NAND_LL_RDDATA_DATA_MASK 0x0000ffff +#define BCHP_NAND_LL_RDDATA_DATA_SHIFT 0 + +/*************************************************************************** + *FLASH_CACHE%i - Flash Cache Buffer Read Access + ***************************************************************************/ +#define BCHP_NAND_FLASH_CACHEi_ARRAY_BASE BRCMNAND_CACHE_BASE +#define BCHP_NAND_FLASH_CACHEi_ARRAY_START 0 +#define BCHP_NAND_FLASH_CACHEi_ARRAY_END 127 +#define BCHP_NAND_FLASH_CACHEi_ARRAY_ELEMENT_SIZE 32 + +/*************************************************************************** + *FLASH_CACHE%i - Flash Cache Buffer Read Access + ***************************************************************************/ +/* NAND :: FLASH_CACHEi :: WORD [31:00] */ +#define BCHP_NAND_FLASH_CACHEi_WORD_MASK 0xffffffff +#define BCHP_NAND_FLASH_CACHEi_WORD_SHIFT 0 + + +#endif /* #ifndef BCHP_NAND_40_H__ */ + +/* End of File */ +#endif diff -urN linux-4.1.52-orig/include/linux/mtd/bchp_nand_7x.h linux-4.1.52/include/linux/mtd/bchp_nand_7x.h --- linux-4.1.52-orig/include/linux/mtd/bchp_nand_7x.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/mtd/bchp_nand_7x.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,1443 @@ +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +/* +<:copyright-BRCM:2014:DUAL/GPL:standard + + Copyright (c) 2014 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + +#ifndef BCHP_NAND_7x_H__ +#define BCHP_NAND_7x_H__ + +#include +#if defined (CONFIG_ARM) || defined (CONFIG_ARM64) +#define BRCMNAND_CTL_BASE ((uintptr_t)(NAND_REG_BASE)) +#define BRCMNAND_INTR_BASE ((uintptr_t)(NAND_INTR_BASE)) +#define BRCMNAND_CACHE_BASE ((uintptr_t)(NAND_CACHE_BASE)) +#else +#define BRCMNAND_CTL_BASE ((uintptr_t)(NAND_REG_BASE & 0x0fffffff)) +#define BRCMNAND_INTR_BASE ((uintptr_t)(NAND_INTR_BASE & 0x0fffffff)) +#define BRCMNAND_CACHE_BASE ((uintptr_t)(NAND_CACHE_BASE & 0x0fffffff)) +#endif +#define BRCMNAND_FLD_ADDR(FLD) \ + ((uintptr_t)(BRCMNAND_CTL_BASE + (offsetof(NandCtrlRegs,FLD)))) + +#define BCHP_NAND_REG_START ((uintptr_t)BRCMNAND_CTL_BASE) +#define BCHP_NAND_REG_END ((uintptr_t)(BCHP_NAND_REG_START + \ + sizeof(NandCtrlRegs))) + +/*************************************************************************** + *NAND - Nand Flash Control Registers + ***************************************************************************/ +#define BCHP_NAND_REVISION BRCMNAND_FLD_ADDR(NandRevision) /* NAND Revision */ +#define BCHP_NAND_CMD_START BRCMNAND_FLD_ADDR(NandCmdStart) /* Nand Flash Command Start */ +#define BCHP_NAND_CMD_EXT_ADDRESS BRCMNAND_FLD_ADDR(NandCmdExtAddr) /* Nand Flash Command Extended Address */ +#define BCHP_NAND_CMD_ADDRESS BRCMNAND_FLD_ADDR(NandCmdAddr) /* Nand Flash Command Address */ +#define BCHP_NAND_CMD_END_ADDRESS BRCMNAND_FLD_ADDR(NandCmdEndAddr) /* Nand Flash Command End Address */ +#define BCHP_NAND_CS_NAND_SELECT BRCMNAND_FLD_ADDR(NandNandBootConfig) /* Nand Flash EBI CS Select */ +#define BCHP_NAND_CS_NAND_XOR BRCMNAND_FLD_ADDR(NandCsNandXor) /* Nand Flash EBI CS Address XOR with 1FC0 Control */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_0 BRCMNAND_FLD_ADDR(NandSpareAreaReadOfs0) /* Nand Flash Spare Area Read Bytes 0-3 */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_4 BRCMNAND_FLD_ADDR(NandSpareAreaReadOfs4) /* Nand Flash Spare Area Read Bytes 4-7 */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_8 BRCMNAND_FLD_ADDR(NandSpareAreaReadOfs8) /* Nand Flash Spare Area Read Bytes 8-11 */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_C BRCMNAND_FLD_ADDR(NandSpareAreaReadOfsC) /* Nand Flash Spare Area Read Bytes 12-15 */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_0 BRCMNAND_FLD_ADDR(NandSpareAreaWriteOfs0) /* Nand Flash Spare Area Write Bytes 0-3 */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_4 BRCMNAND_FLD_ADDR(NandSpareAreaWriteOfs4) /* Nand Flash Spare Area Write Bytes 4-7 */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_8 BRCMNAND_FLD_ADDR(NandSpareAreaWriteOfs8) /* Nand Flash Spare Area Write Bytes 8-11 */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_C BRCMNAND_FLD_ADDR(NandSpareAreaWriteOfsC) /* Nand Flash Spare Area Write Bytes 12-15 */ +#define BCHP_NAND_ACC_CONTROL BRCMNAND_FLD_ADDR(NandAccControl) /* Nand Flash Access Control */ +#if CONFIG_MTD_BRCMNAND_VERSION > CONFIG_MTD_BRCMNAND_VERS_7_0 +#define BCHP_NAND_CONFIG_EXT BRCMNAND_FLD_ADDR(NandConfigExt) /* Nand Flash Config Ext */ +#endif +#define BCHP_NAND_CONFIG BRCMNAND_FLD_ADDR(NandConfig) /* Nand Flash Config */ +#define BCHP_NAND_TIMING_1 BRCMNAND_FLD_ADDR(NandTiming1) /* Nand Flash Timing Parameters 1 */ +#define BCHP_NAND_TIMING_2 BRCMNAND_FLD_ADDR(NandTiming2) /* Nand Flash Timing Parameters 2 */ +#define BCHP_NAND_SEMAPHORE BRCMNAND_FLD_ADDR(NandSemaphore) /* Semaphore */ +#define BCHP_NAND_FLASH_DEVICE_ID BRCMNAND_FLD_ADDR(NandFlashDeviceId) /* Nand Flash Device ID */ +#define BCHP_NAND_FLASH_DEVICE_ID_EXT BRCMNAND_FLD_ADDR(NandFlashDeviceIdExt) /* Nand Flash Extended Device ID */ +#define BCHP_NAND_BLOCK_LOCK_STATUS BRCMNAND_FLD_ADDR(NandBlockLockStatus) /* Nand Flash Block Lock Status */ +#define BCHP_NAND_INTFC_STATUS BRCMNAND_FLD_ADDR(NandIntfcStatus) /* Nand Flash Interface Status */ +#define BCHP_NAND_ECC_CORR_EXT_ADDR BRCMNAND_FLD_ADDR(NandEccCorrExtAddr) /* ECC Correctable Error Extended Address */ +#define BCHP_NAND_ECC_CORR_ADDR BRCMNAND_FLD_ADDR(NandEccCorrAddr) /* ECC Correctable Error Address */ +#define BCHP_NAND_ECC_UNC_EXT_ADDR BRCMNAND_FLD_ADDR(NandEccUncExtAddr) /* ECC Uncorrectable Error Extended Address */ +#define BCHP_NAND_ECC_UNC_ADDR BRCMNAND_FLD_ADDR(NandEccUncAddr) /* ECC Uncorrectable Error Address */ +#define BCHP_NAND_READ_ERROR_COUNT BRCMNAND_FLD_ADDR(NandReadErrorCount) /* Read Error Count */ +#define BCHP_NAND_CORR_STAT_THRESHOLD BRCMNAND_FLD_ADDR(NandCorrStatThreshold) /* Correctable Error Reporting Threshold */ +#define BCHP_NAND_ONFI_STATUS BRCMNAND_FLD_ADDR(NandOnfiStatus) /* ONFI Status */ +#define BCHP_NAND_ONFI_DEBUG_DATA BRCMNAND_FLD_ADDR(NandOnfiDebugData) /* ONFI Debug Data */ +#define BCHP_NAND_FLASH_READ_EXT_ADDR BRCMNAND_FLD_ADDR(NandFlashReadExtAddr) /* Flash Read Data Extended Address */ +#define BCHP_NAND_FLASH_READ_ADDR BRCMNAND_FLD_ADDR(NandFlashReadAddr) /* Flash Read Data Address */ +#define BCHP_NAND_PROGRAM_PAGE_EXT_ADDR BRCMNAND_FLD_ADDR(NandProgramPageExtAddr) /* Page Program Extended Address */ +#define BCHP_NAND_PROGRAM_PAGE_ADDR BRCMNAND_FLD_ADDR(NandProgramPageAddr) /* Page Program Address */ +#define BCHP_NAND_COPY_BACK_EXT_ADDR BRCMNAND_FLD_ADDR(NandCopyBackExtAddr) /* Copy Back Extended Address */ +#define BCHP_NAND_COPY_BACK_ADDR BRCMNAND_FLD_ADDR(NandCopyBackAddr) /* Copy Back Address */ +#define BCHP_NAND_BLOCK_ERASE_EXT_ADDR BRCMNAND_FLD_ADDR(NandBlockEraseExtAddr) /* Block Erase Extended Address */ +#define BCHP_NAND_BLOCK_ERASE_ADDR BRCMNAND_FLD_ADDR(NandBlockEraseAddr) /* Block Erase Address */ +#define BCHP_NAND_INV_READ_EXT_ADDR BRCMNAND_FLD_ADDR(NandInvReadExtAddr) /* Flash Invalid Data Extended Address */ +#define BCHP_NAND_INV_READ_ADDR BRCMNAND_FLD_ADDR(NandInvReadAddr) /* Flash Invalid Data Address */ +#define BCHP_NAND_BLK_WR_PROTECT BRCMNAND_FLD_ADDR(NandBlkWrProtect) /* Block Write Protect Enable and Size for EBI_CS0b */ +#define BCHP_NAND_ACC_CONTROL_CS1 BRCMNAND_FLD_ADDR(NandAccControlCs1) /* Nand Flash Access Control */ +#if CONFIG_MTD_BRCMNAND_VERSION > CONFIG_MTD_BRCMNAND_VERS_7_0 +#define BCHP_NAND_CONFIG_EXT_CS1 BRCMNAND_FLD_ADDR(NandConfigExtCs1) /* Nand Flash Config Ext */ +#endif +#define BCHP_NAND_CONFIG_CS1 BRCMNAND_FLD_ADDR(NandConfigCs1) /* Nand Flash Config */ +#define BCHP_NAND_TIMING_1_CS1 BRCMNAND_FLD_ADDR(NandTiming1Cs1) /* Nand Flash Timing Parameters 1 */ +#define BCHP_NAND_TIMING_2_CS1 BRCMNAND_FLD_ADDR(NandTiming2Cs1) /* Nand Flash Timing Parameters 2 */ +#define BCHP_NAND_ACC_CONTROL_CS2 BRCMNAND_FLD_ADDR(NandAccControlCs2) /* Nand Flash Access Control */ +#if CONFIG_MTD_BRCMNAND_VERSION > CONFIG_MTD_BRCMNAND_VERS_7_0 +#define BCHP_NAND_CONFIG_EXT_CS2 BRCMNAND_FLD_ADDR(NandConfigExtCs2) /* Nand Flash Config Ext */ +#endif +#define BCHP_NAND_CONFIG_CS2 BRCMNAND_FLD_ADDR(NandConfigCs2) /* Nand Flash Config */ +#define BCHP_NAND_TIMING_1_CS2 BRCMNAND_FLD_ADDR(NandTiming1Cs2) /* Nand Flash Timing Parameters 1 */ +#define BCHP_NAND_TIMING_2_CS2 BRCMNAND_FLD_ADDR(NandTiming2Cs2) /* Nand Flash Timing Parameters 2 */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_10 BRCMNAND_FLD_ADDR(NandSpareAreaReadOfs10) /* Nand Flash Spare Area Read Bytes 16-19 */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_14 BRCMNAND_FLD_ADDR(NandSpareAreaReadOfs14) /* Nand Flash Spare Area Read Bytes 20-23 */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_18 BRCMNAND_FLD_ADDR(NandSpareAreaReadOfs18) /* Nand Flash Spare Area Read Bytes 24-27 */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_1C BRCMNAND_FLD_ADDR(NandSpareAreaReadOfs1C) /* Nand Flash Spare Area Read Bytes 28-31 */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_20 BRCMNAND_FLD_ADDR(NandSpareAreaReadOfs20) /* Nand Flash Spare Area Read Bytes 32-35 */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_24 BRCMNAND_FLD_ADDR(NandSpareAreaReadOfs24) /* Nand Flash Spare Area Read Bytes 36-39 */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_28 BRCMNAND_FLD_ADDR(NandSpareAreaReadOfs28) /* Nand Flash Spare Area Read Bytes 40-43 */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_2C BRCMNAND_FLD_ADDR(NandSpareAreaReadOfs2C) /* Nand Flash Spare Area Read Bytes 44-47 */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_30 BRCMNAND_FLD_ADDR(NandSpareAreaReadOfs30) /* Nand Flash Spare Area Read Bytes 48-51*/ +#define BCHP_NAND_SPARE_AREA_READ_OFS_34 BRCMNAND_FLD_ADDR(NandSpareAreaReadOfs34) /* Nand Flash Spare Area Read Bytes 52-55 */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_38 BRCMNAND_FLD_ADDR(NandSpareAreaReadOfs38) /* Nand Flash Spare Area Read Bytes 56-59 */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_3C BRCMNAND_FLD_ADDR(NandSpareAreaReadOfs3C) /* Nand Flash Spare Area Read Bytes 60-63 */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_10 BRCMNAND_FLD_ADDR(NandSpareAreaWriteOfs10) /* Nand Flash Spare Area Write Bytes 16-19 */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_14 BRCMNAND_FLD_ADDR(NandSpareAreaWriteOfs14) /* Nand Flash Spare Area Write Bytes 20-23 */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_18 BRCMNAND_FLD_ADDR(NandSpareAreaWriteOfs18) /* Nand Flash Spare Area Write Bytes 24-27 */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_1C BRCMNAND_FLD_ADDR(NandSpareAreaWriteOfs1C) /* Nand Flash Spare Area Write Bytes 28-31 */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_20 BRCMNAND_FLD_ADDR(NandSpareAreaWriteOfs20) /* Nand Flash Spare Area Write Bytes 32-35 */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_24 BRCMNAND_FLD_ADDR(NandSpareAreaWriteOfs24) /* Nand Flash Spare Area Write Bytes 36-39 */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_28 BRCMNAND_FLD_ADDR(NandSpareAreaWriteOfs28) /* Nand Flash Spare Area Write Bytes 40-43 */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_2C BRCMNAND_FLD_ADDR(NandSpareAreaWriteOfs2C) /* Nand Flash Spare Area Write Bytes 44-47 */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_30 BRCMNAND_FLD_ADDR(NandSpareAreaWriteOfs30) /* Nand Flash Spare Area Write Bytes 48-51 */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_34 BRCMNAND_FLD_ADDR(NandSpareAreaWriteOfs34) /* Nand Flash Spare Area Write Bytes 52-55 */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_38 BRCMNAND_FLD_ADDR(NandSpareAreaWriteOfs38) /* Nand Flash Spare Area Write Bytes 56-59 */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_3C BRCMNAND_FLD_ADDR(NandSpareAreaWriteOfs3C) /* Nand Flash Spare Area Write Bytes 60-63 */ +#define BCHP_NAND_LL_OP BRCMNAND_FLD_ADDR(NandLlOpNand) /* Nand Flash Low Level Operation */ +#define BCHP_NAND_LL_RDDATA BRCMNAND_FLD_ADDR(NandLlRdData) /* Nand Flash Low Level Read Data */ + +/*************************************************************************** + *REVISION - NAND Revision + ***************************************************************************/ +/* NAND :: REVISION :: 8KB_PAGE_SUPPORT [31:31] */ +#define BCHP_NAND_REVISION_8KB_PAGE_SUPPORT_MASK 0x80000000 +#define BCHP_NAND_REVISION_8KB_PAGE_SUPPORT_SHIFT 31 + +/* NAND :: REVISION :: reserved0 [30:16] */ +#define BCHP_NAND_REVISION_reserved0_MASK 0x7fff0000 +#define BCHP_NAND_REVISION_reserved0_SHIFT 16 + +/* NAND :: REVISION :: MAJOR [15:08] */ +#define BCHP_NAND_REVISION_MAJOR_MASK 0x0000ff00 +#define BCHP_NAND_REVISION_MAJOR_SHIFT 8 + +/* NAND :: REVISION :: MINOR [07:00] */ +#define BCHP_NAND_REVISION_MINOR_MASK 0x000000ff +#define BCHP_NAND_REVISION_MINOR_SHIFT 0 + +/*************************************************************************** + *CMD_START - Nand Flash Command Start + ***************************************************************************/ +/* NAND :: CMD_START :: reserved0 [31:05] */ +#define BCHP_NAND_CMD_START_reserved_MASK 0xffffffe0 +#define BCHP_NAND_CMD_START_reserved_SHIFT 5 + +/* NAND :: CMD_START :: OPCODE [04:00] */ +#define BCHP_NAND_CMD_START_OPCODE_MASK 0x1f +#define BCHP_NAND_CMD_START_OPCODE_SHIFT 0 +#define BCHP_NAND_CMD_START_OPCODE_NULL 0 +#define BCHP_NAND_CMD_START_OPCODE_PAGE_READ 1 +#define BCHP_NAND_CMD_START_OPCODE_SPARE_AREA_READ 2 +#define BCHP_NAND_CMD_START_OPCODE_STATUS_READ 3 +#define BCHP_NAND_CMD_START_OPCODE_PROGRAM_PAGE 4 +#define BCHP_NAND_CMD_START_OPCODE_PROGRAM_SPARE_AREA 5 +#define BCHP_NAND_CMD_START_OPCODE_COPY_BACK 6 +#define BCHP_NAND_CMD_START_OPCODE_DEVICE_ID_READ 7 +#define BCHP_NAND_CMD_START_OPCODE_BLOCK_ERASE 8 +#define BCHP_NAND_CMD_START_OPCODE_FLASH_RESET 9 +#define BCHP_NAND_CMD_START_OPCODE_BLOCKS_LOCK 10 +#define BCHP_NAND_CMD_START_OPCODE_BLOCKS_LOCK_DOWN 11 +#define BCHP_NAND_CMD_START_OPCODE_BLOCKS_UNLOCK 12 +#define BCHP_NAND_CMD_START_OPCODE_READ_BLOCKS_LOCK_STATUS 13 +#define BCHP_NAND_CMD_START_OPCODE_PARAMETER_READ 14 +#define BCHP_NAND_CMD_START_OPCODE_PARAMETER_CHANGE_COL 15 +#define BCHP_NAND_CMD_START_OPCODE_LOW_LEVEL_OP 16 + +/*************************************************************************** + *CMD_EXT_ADDRESS - Nand Flash Command Extended Address + ***************************************************************************/ +/* NAND :: CMD_EXT_ADDRESS :: reserved0 [31:19] */ +#define BCHP_NAND_CMD_EXT_ADDRESS_reserved0_MASK 0xfff80000 +#define BCHP_NAND_CMD_EXT_ADDRESS_reserved0_SHIFT 19 + +/* NAND :: CMD_EXT_ADDRESS :: CS_SEL [18:16] */ +#define BCHP_NAND_CMD_EXT_ADDRESS_CS_SEL_MASK 0x00070000 +#define BCHP_NAND_CMD_EXT_ADDRESS_CS_SEL_SHIFT 16 + +/* NAND :: CMD_EXT_ADDRESS :: EXT_ADDRESS [15:00] */ +#define BCHP_NAND_CMD_EXT_ADDRESS_EXT_ADDRESS_MASK 0x0000ffff +#define BCHP_NAND_CMD_EXT_ADDRESS_EXT_ADDRESS_SHIFT 0 + +/*************************************************************************** + *CMD_ADDRESS - Nand Flash Command Address + ***************************************************************************/ +/* NAND :: CMD_ADDRESS :: ADDRESS [31:00] */ +#define BCHP_NAND_CMD_ADDRESS_ADDRESS_MASK 0xffffffff +#define BCHP_NAND_CMD_ADDRESS_ADDRESS_SHIFT 0 + +/*************************************************************************** + *CMD_END_ADDRESS - Nand Flash Command End Address + ***************************************************************************/ +/* NAND :: CMD_END_ADDRESS :: ADDRESS [31:00] */ +#define BCHP_NAND_CMD_END_ADDRESS_ADDRESS_MASK 0xffffffff +#define BCHP_NAND_CMD_END_ADDRESS_ADDRESS_SHIFT 0 + +/*************************************************************************** + *CS_NAND_SELECT - Nand Flash EBI CS Select + ***************************************************************************/ +/* NAND :: CS_NAND_SELECT :: CS_LOCK [31:31] */ +#define BCHP_NAND_CS_NAND_SELECT_CS_LOCK_MASK 0x80000000 +#define BCHP_NAND_CS_NAND_SELECT_CS_LOCK_SHIFT 31 + +/* NAND :: CS_NAND_SELECT :: AUTO_DEVICE_ID_CONFIG [30:30] */ +#define BCHP_NAND_CS_NAND_SELECT_AUTO_DEVICE_ID_CONFIG_MASK 0x40000000 +#define BCHP_NAND_CS_NAND_SELECT_AUTO_DEVICE_ID_CONFIG_SHIFT 30 + +/* NAND :: CS_NAND_SELECT :: reserved0 [29:29] */ +#define BCHP_NAND_CS_NAND_SELECT_NAND_WP_MASK 0x20000000 +#define BCHP_NAND_CS_NAND_SELECT_NAND_WP_SHIFT 29 + +/* NAND :: CS_NAND_SELECT :: WR_PROTECT_BLK0 [28:28] */ +#define BCHP_NAND_CS_NAND_SELECT_WR_PROTECT_BLK0_MASK 0x10000000 +#define BCHP_NAND_CS_NAND_SELECT_WR_PROTECT_BLK0_SHIFT 28 + +/* NAND :: CS_NAND_SELECT :: reserved1 [27:16] */ +#define BCHP_NAND_CS_NAND_SELECT_reserved1_MASK 0x0fff0000 +#define BCHP_NAND_CS_NAND_SELECT_reserved1_SHIFT 16 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_7_USES_NAND [15:15] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_7_USES_NAND_MASK 0x00008000 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_7_USES_NAND_SHIFT 15 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_6_USES_NAND [14:14] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_6_USES_NAND_MASK 0x00004000 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_6_USES_NAND_SHIFT 14 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_5_USES_NAND [13:13] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_5_USES_NAND_MASK 0x00002000 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_5_USES_NAND_SHIFT 13 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_4_USES_NAND [12:12] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_4_USES_NAND_MASK 0x00001000 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_4_USES_NAND_SHIFT 12 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_3_USES_NAND [11:11] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_3_USES_NAND_MASK 0x00000800 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_3_USES_NAND_SHIFT 11 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_2_USES_NAND [10:10] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_2_USES_NAND_MASK 0x00000400 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_2_USES_NAND_SHIFT 10 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_1_USES_NAND [09:09] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_1_USES_NAND_MASK 0x00000200 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_1_USES_NAND_SHIFT 9 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_0_USES_NAND [08:08] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_0_USES_NAND_MASK 0x00000100 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_0_USES_NAND_SHIFT 8 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_7_SEL [07:07] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_7_SEL_MASK 0x00000080 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_7_SEL_SHIFT 7 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_6_SEL [06:06] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_6_SEL_MASK 0x00000040 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_6_SEL_SHIFT 6 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_5_SEL [05:05] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_5_SEL_MASK 0x00000020 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_5_SEL_SHIFT 5 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_4_SEL [04:04] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_4_SEL_MASK 0x00000010 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_4_SEL_SHIFT 4 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_3_SEL [03:03] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_3_SEL_MASK 0x00000008 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_3_SEL_SHIFT 3 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_2_SEL [02:02] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_2_SEL_MASK 0x00000004 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_2_SEL_SHIFT 2 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_1_SEL [01:01] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_1_SEL_MASK 0x00000002 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_1_SEL_SHIFT 1 + +/* NAND :: CS_NAND_SELECT :: EBI_CS_0_SEL [00:00] */ +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_0_SEL_MASK 0x00000001 +#define BCHP_NAND_CS_NAND_SELECT_EBI_CS_0_SEL_SHIFT 0 + +/*************************************************************************** + *CS_NAND_XOR - Nand Flash EBI CS Address XOR with 1FC0 Control + ***************************************************************************/ +/* NAND :: CS_NAND_XOR :: ONLY_BLOCK_0_1FC0_XOR [31:31] */ +#define BCHP_NAND_CS_NAND_XOR_ONLY_BLOCK_0_1FC0_XOR_MASK 0x80000000 +#define BCHP_NAND_CS_NAND_XOR_ONLY_BLOCK_0_1FC0_XOR_SHIFT 31 + +/* NAND :: CS_NAND_XOR :: reserved0 [30:08] */ +#define BCHP_NAND_CS_NAND_XOR_reserved0_MASK 0x7fffff00 +#define BCHP_NAND_CS_NAND_XOR_reserved0_SHIFT 8 + +/* NAND :: CS_NAND_XOR :: EBI_CS_7_ADDR_1FC0_XOR [07:07] */ +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_7_ADDR_1FC0_XOR_MASK 0x00000080 +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_7_ADDR_1FC0_XOR_SHIFT 7 + +/* NAND :: CS_NAND_XOR :: EBI_CS_6_ADDR_1FC0_XOR [06:06] */ +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_6_ADDR_1FC0_XOR_MASK 0x00000040 +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_6_ADDR_1FC0_XOR_SHIFT 6 + +/* NAND :: CS_NAND_XOR :: EBI_CS_5_ADDR_1FC0_XOR [05:05] */ +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_5_ADDR_1FC0_XOR_MASK 0x00000020 +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_5_ADDR_1FC0_XOR_SHIFT 5 + +/* NAND :: CS_NAND_XOR :: EBI_CS_4_ADDR_1FC0_XOR [04:04] */ +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_4_ADDR_1FC0_XOR_MASK 0x00000010 +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_4_ADDR_1FC0_XOR_SHIFT 4 + +/* NAND :: CS_NAND_XOR :: EBI_CS_3_ADDR_1FC0_XOR [03:03] */ +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_3_ADDR_1FC0_XOR_MASK 0x00000008 +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_3_ADDR_1FC0_XOR_SHIFT 3 + +/* NAND :: CS_NAND_XOR :: EBI_CS_2_ADDR_1FC0_XOR [02:02] */ +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_2_ADDR_1FC0_XOR_MASK 0x00000004 +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_2_ADDR_1FC0_XOR_SHIFT 2 + +/* NAND :: CS_NAND_XOR :: EBI_CS_1_ADDR_1FC0_XOR [01:01] */ +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_1_ADDR_1FC0_XOR_MASK 0x00000002 +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_1_ADDR_1FC0_XOR_SHIFT 1 + +/* NAND :: CS_NAND_XOR :: EBI_CS_0_ADDR_1FC0_XOR [00:00] */ +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_0_ADDR_1FC0_XOR_MASK 0x00000001 +#define BCHP_NAND_CS_NAND_XOR_EBI_CS_0_ADDR_1FC0_XOR_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_READ_OFS_0 - Nand Flash Spare Area Read Bytes 0-3 + ***************************************************************************/ +/* NAND :: SPARE_AREA_READ_OFS_0 :: BYTE_OFS_0 [31:24] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_0_BYTE_OFS_0_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_0_BYTE_OFS_0_SHIFT 24 + +/* NAND :: SPARE_AREA_READ_OFS_0 :: BYTE_OFS_1 [23:16] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_0_BYTE_OFS_1_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_0_BYTE_OFS_1_SHIFT 16 + +/* NAND :: SPARE_AREA_READ_OFS_0 :: BYTE_OFS_2 [15:08] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_0_BYTE_OFS_2_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_READ_OFS_0_BYTE_OFS_2_SHIFT 8 + +/* NAND :: SPARE_AREA_READ_OFS_0 :: BYTE_OFS_3 [07:00] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_0_BYTE_OFS_3_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_READ_OFS_0_BYTE_OFS_3_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_READ_OFS_4 - Nand Flash Spare Area Read Bytes 4-7 + ***************************************************************************/ +/* NAND :: SPARE_AREA_READ_OFS_4 :: BYTE_OFS_4 [31:24] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_4_BYTE_OFS_4_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_4_BYTE_OFS_4_SHIFT 24 + +/* NAND :: SPARE_AREA_READ_OFS_4 :: BYTE_OFS_5 [23:16] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_4_BYTE_OFS_5_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_4_BYTE_OFS_5_SHIFT 16 + +/* NAND :: SPARE_AREA_READ_OFS_4 :: BYTE_OFS_6 [15:08] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_4_BYTE_OFS_6_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_READ_OFS_4_BYTE_OFS_6_SHIFT 8 + +/* NAND :: SPARE_AREA_READ_OFS_4 :: BYTE_OFS_7 [07:00] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_4_BYTE_OFS_7_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_READ_OFS_4_BYTE_OFS_7_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_READ_OFS_8 - Nand Flash Spare Area Read Bytes 8-11 + ***************************************************************************/ +/* NAND :: SPARE_AREA_READ_OFS_8 :: BYTE_OFS_8 [31:24] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_8_BYTE_OFS_8_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_8_BYTE_OFS_8_SHIFT 24 + +/* NAND :: SPARE_AREA_READ_OFS_8 :: BYTE_OFS_9 [23:16] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_8_BYTE_OFS_9_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_8_BYTE_OFS_9_SHIFT 16 + +/* NAND :: SPARE_AREA_READ_OFS_8 :: BYTE_OFS_10 [15:08] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_8_BYTE_OFS_10_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_READ_OFS_8_BYTE_OFS_10_SHIFT 8 + +/* NAND :: SPARE_AREA_READ_OFS_8 :: BYTE_OFS_11 [07:00] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_8_BYTE_OFS_11_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_READ_OFS_8_BYTE_OFS_11_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_READ_OFS_C - Nand Flash Spare Area Read Bytes 12-15 + ***************************************************************************/ +/* NAND :: SPARE_AREA_READ_OFS_C :: BYTE_OFS_12 [31:24] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_C_BYTE_OFS_12_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_C_BYTE_OFS_12_SHIFT 24 + +/* NAND :: SPARE_AREA_READ_OFS_C :: BYTE_OFS_13 [23:16] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_C_BYTE_OFS_13_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_C_BYTE_OFS_13_SHIFT 16 + +/* NAND :: SPARE_AREA_READ_OFS_C :: BYTE_OFS_14 [15:08] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_C_BYTE_OFS_14_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_READ_OFS_C_BYTE_OFS_14_SHIFT 8 + +/* NAND :: SPARE_AREA_READ_OFS_C :: BYTE_OFS_15 [07:00] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_C_BYTE_OFS_15_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_READ_OFS_C_BYTE_OFS_15_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_WRITE_OFS_0 - Nand Flash Spare Area Write Bytes 0-3 + ***************************************************************************/ +/* NAND :: SPARE_AREA_WRITE_OFS_0 :: BYTE_OFS_0 [31:24] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_0_BYTE_OFS_0_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_0_BYTE_OFS_0_SHIFT 24 + +/* NAND :: SPARE_AREA_WRITE_OFS_0 :: BYTE_OFS_1 [23:16] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_0_BYTE_OFS_1_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_0_BYTE_OFS_1_SHIFT 16 + +/* NAND :: SPARE_AREA_WRITE_OFS_0 :: BYTE_OFS_2 [15:08] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_0_BYTE_OFS_2_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_0_BYTE_OFS_2_SHIFT 8 + +/* NAND :: SPARE_AREA_WRITE_OFS_0 :: BYTE_OFS_3 [07:00] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_0_BYTE_OFS_3_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_0_BYTE_OFS_3_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_WRITE_OFS_4 - Nand Flash Spare Area Write Bytes 4-7 + ***************************************************************************/ +/* NAND :: SPARE_AREA_WRITE_OFS_4 :: BYTE_OFS_4 [31:24] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_4_BYTE_OFS_4_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_4_BYTE_OFS_4_SHIFT 24 + +/* NAND :: SPARE_AREA_WRITE_OFS_4 :: BYTE_OFS_5 [23:16] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_4_BYTE_OFS_5_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_4_BYTE_OFS_5_SHIFT 16 + +/* NAND :: SPARE_AREA_WRITE_OFS_4 :: BYTE_OFS_6 [15:08] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_4_BYTE_OFS_6_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_4_BYTE_OFS_6_SHIFT 8 + +/* NAND :: SPARE_AREA_WRITE_OFS_4 :: BYTE_OFS_7 [07:00] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_4_BYTE_OFS_7_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_4_BYTE_OFS_7_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_WRITE_OFS_8 - Nand Flash Spare Area Write Bytes 8-11 + ***************************************************************************/ +/* NAND :: SPARE_AREA_WRITE_OFS_8 :: BYTE_OFS_8 [31:24] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_8_BYTE_OFS_8_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_8_BYTE_OFS_8_SHIFT 24 + +/* NAND :: SPARE_AREA_WRITE_OFS_8 :: BYTE_OFS_9 [23:16] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_8_BYTE_OFS_9_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_8_BYTE_OFS_9_SHIFT 16 + +/* NAND :: SPARE_AREA_WRITE_OFS_8 :: BYTE_OFS_10 [15:08] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_8_BYTE_OFS_10_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_8_BYTE_OFS_10_SHIFT 8 + +/* NAND :: SPARE_AREA_WRITE_OFS_8 :: BYTE_OFS_11 [07:00] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_8_BYTE_OFS_11_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_8_BYTE_OFS_11_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_WRITE_OFS_C - Nand Flash Spare Area Write Bytes 12-15 + ***************************************************************************/ +/* NAND :: SPARE_AREA_WRITE_OFS_C :: BYTE_OFS_12 [31:24] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_C_BYTE_OFS_12_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_C_BYTE_OFS_12_SHIFT 24 + +/* NAND :: SPARE_AREA_WRITE_OFS_C :: BYTE_OFS_13 [23:16] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_C_BYTE_OFS_13_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_C_BYTE_OFS_13_SHIFT 16 + +/* NAND :: SPARE_AREA_WRITE_OFS_C :: BYTE_OFS_14 [15:08] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_C_BYTE_OFS_14_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_C_BYTE_OFS_14_SHIFT 8 + +/* NAND :: SPARE_AREA_WRITE_OFS_C :: BYTE_OFS_15 [07:00] */ +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_C_BYTE_OFS_15_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_WRITE_OFS_C_BYTE_OFS_15_SHIFT 0 + +/*************************************************************************** + *ACC_CONTROL - Nand Flash Access Control + ***************************************************************************/ +/* NAND :: ACC_CONTROL :: RD_ECC_EN [31:31] */ +#define BCHP_NAND_ACC_CONTROL_RD_ECC_EN_MASK 0x80000000 +#define BCHP_NAND_ACC_CONTROL_RD_ECC_EN_SHIFT 31 + +/* NAND :: ACC_CONTROL :: WR_ECC_EN [30:30] */ +#define BCHP_NAND_ACC_CONTROL_WR_ECC_EN_MASK 0x40000000 +#define BCHP_NAND_ACC_CONTROL_WR_ECC_EN_SHIFT 30 + +/* NAND :: ACC_CONTROL :: RD_ECC_BLK0_EN [29:29] */ +#define BCHP_NAND_ACC_CONTROL_CE_CARE_MASK 0x20000000 +#define BCHP_NAND_ACC_CONTROL_CE_CARE_SHIFT 29 + +/* NAND :: ACC_CONTROL :: reserve1 [28:28] */ +#define BCHP_NAND_ACC_CONTROL_RESERVED1_MASK 0x10000000 +#define BCHP_NAND_ACC_CONTROL_RESERVED1_SHIFT 28 + +/* NAND :: ACC_CONTROL :: RD_ERASED_ECC_EN [27:27] */ +#define BCHP_NAND_ACC_CONTROL_RD_ERASED_ECC_EN_MASK 0x08000000 +#define BCHP_NAND_ACC_CONTROL_RD_ERASED_ECC_EN_SHIFT 27 + +/* NAND :: ACC_CONTROL :: PARTIAL_PAGE_EN [26:26] */ +#define BCHP_NAND_ACC_CONTROL_PARTIAL_PAGE_EN_MASK 0x04000000 +#define BCHP_NAND_ACC_CONTROL_PARTIAL_PAGE_EN_SHIFT 26 + +/* NAND :: ACC_CONTROL :: WR_PREEMPT_EN [25:25] */ +#define BCHP_NAND_ACC_CONTROL_WR_PREEMPT_EN_MASK 0x02000000 +#define BCHP_NAND_ACC_CONTROL_WR_PREEMPT_EN_SHIFT 25 + +/* NAND :: ACC_CONTROL :: PAGE_HIT_EN [24:24] */ +#define BCHP_NAND_ACC_CONTROL_PAGE_HIT_EN_MASK 0x01000000 +#define BCHP_NAND_ACC_CONTROL_PAGE_HIT_EN_SHIFT 24 + +/* NAND :: ACC_CONTROL :: PREFETCH_EN [23:23] */ +#define BCHP_NAND_ACC_CONTROL_PREFETCH_EN_MASK 0x00800000 +#define BCHP_NAND_ACC_CONTROL_PREFETCH_EN_SHIFT 23 + +/* NAND :: ACC_CONTROL :: CACHE_MODE_EN [22:22] */ +#define BCHP_NAND_ACC_CONTROL_CACHE_MODE_EN_MASK 0x00400000 +#define BCHP_NAND_ACC_CONTROL_CACHE_MODE_EN_SHIFT 22 + +/* NAND :: ACC_CONTROL :: reserve2 [21:21] */ +#define BCHP_NAND_ACC_CONTROL_RESERVED2_MASK 0x00200000 +#define BCHP_NAND_ACC_CONTROL_RESERVED2_SHIFT 21 + +/* NAND :: ACC_CONTROL :: ECC_LEVEL [20:16] */ +#define BCHP_NAND_ACC_CONTROL_ECC_LEVEL_MASK 0x001f0000 +#define BCHP_NAND_ACC_CONTROL_ECC_LEVEL_SHIFT 16 + +/* NAND :: ACC_CONTROL :: reserved3 [15:8] */ +#define BCHP_NAND_ACC_CONTROL_RESERVED3_MASK 0x0000ff00 +#define BCHP_NAND_ACC_CONTROL_RESERVED3_SHIFT 8 + +/* NAND :: ACC_CONTROL :: SECTOR_SIZE_1K [07:07] */ +#define BCHP_NAND_ACC_CONTROL_SECTOR_SIZE_1K_MASK 0x00000080 +#define BCHP_NAND_ACC_CONTROL_SECTOR_SIZE_1K_SHIFT 7 + +/* NAND :: ACC_CONTROL :: SPARE_AREA_SIZE [06:00] */ +#define BCHP_NAND_ACC_CONTROL_SPARE_AREA_SIZE_MASK 0x0000007f +#define BCHP_NAND_ACC_CONTROL_SPARE_AREA_SIZE_SHIFT 0 + +#if CONFIG_MTD_BRCMNAND_VERSION > CONFIG_MTD_BRCMNAND_VERS_7_0 +/*************************************************************************** + *CONFIG EXT - Nand Flash Config Ext + ***************************************************************************/ +/* NAND :: CONFIG_EXT :: BLOCK_SIZE [11:4] */ +#define BCHP_NAND_CONFIG_BLOCK_SIZE_MASK 0x00000ff0 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_SHIFT 4 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_8192KB 10 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_4096KB 9 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_2048KB 8 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_1024KB 7 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_512KB 6 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_256KB 5 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_128KB 4 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_64KB 3 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_32KB 2 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_16KB 1 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_8KB 0 + +/* NAND :: CONFIG_EXT :: PAGE_SIZE [11:4] */ +#define BCHP_NAND_CONFIG_PAGE_SIZE_MASK 0x0000000f +#define BCHP_NAND_CONFIG_PAGE_SIZE_SHIFT 0 +#define BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_512 0 +#define BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_1KB 1 +#define BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_2KB 2 +#define BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_4KB 3 +#define BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_8KB 4 +#define BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_16KB 5 + +#endif + +/*************************************************************************** + *CONFIG - Nand Flash Config + ***************************************************************************/ +/* NAND :: CONFIG :: CONFIG_LOCK [31:31] */ +#define BCHP_NAND_CONFIG_CONFIG_LOCK_MASK 0x80000000 +#define BCHP_NAND_CONFIG_CONFIG_LOCK_SHIFT 31 + +#if CONFIG_MTD_BRCMNAND_VERSION == CONFIG_MTD_BRCMNAND_VERS_7_0 +/* NAND :: CONFIG :: BLOCK_SIZE [30:28] */ +#define BCHP_NAND_CONFIG_BLOCK_SIZE_MASK 0x70000000 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_SHIFT 28 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_2048KB 6 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_1024KB 5 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_512KB 4 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_256KB 3 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_128KB 2 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_16KB 1 +#define BCHP_NAND_CONFIG_BLOCK_SIZE_BK_SIZE_8KB 0 +#endif + +/* NAND :: CONFIG :: DEVICE_SIZE [27:24] */ +#define BCHP_NAND_CONFIG_DEVICE_SIZE_MASK 0x0f000000 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_SHIFT 24 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_4MB 0 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_8MB 1 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_16MB 2 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_32MB 3 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_64MB 4 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_128MB 5 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_256MB 6 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_512MB 7 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_1GB 8 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_2GB 9 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_4GB 10 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_8GB 11 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_16GB 12 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_32GB 13 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_64GB 14 +#define BCHP_NAND_CONFIG_DEVICE_SIZE_DVC_SIZE_128GB 15 + +/* NAND :: CONFIG :: DEVICE_WIDTH [23:23] */ +#define BCHP_NAND_CONFIG_DEVICE_WIDTH_MASK 0x00800000 +#define BCHP_NAND_CONFIG_DEVICE_WIDTH_SHIFT 23 +#define BCHP_NAND_CONFIG_DEVICE_WIDTH_DVC_WIDTH_8 0 +#define BCHP_NAND_CONFIG_DEVICE_WIDTH_DVC_WIDTH_16 1 + +/* NAND :: CONFIG :: reserved0 [22:22] */ +#define BCHP_NAND_CONFIG_reserved0_MASK 0x00400000 +#define BCHP_NAND_CONFIG_reserved0_SHIFT 22 + +#if CONFIG_MTD_BRCMNAND_VERSION == CONFIG_MTD_BRCMNAND_VERS_7_0 +/* NAND :: CONFIG :: PAGE_SIZE [21:20] */ +#define BCHP_NAND_CONFIG_PAGE_SIZE_MASK 0x00300000 +#define BCHP_NAND_CONFIG_PAGE_SIZE_SHIFT 20 +#define BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_512 0 +#define BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_2KB 1 +#define BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_4KB 2 +#define BCHP_NAND_CONFIG_PAGE_SIZE_PG_SIZE_8KB 3 +#endif + +/* NAND :: CONFIG :: reserved1 [19:19] */ +#define BCHP_NAND_CONFIG_reserved1_MASK 0x00080000 +#define BCHP_NAND_CONFIG_reserved1_SHIFT 19 + +/* NAND :: CONFIG :: FUL_ADR_BYTES [18:16] */ +#define BCHP_NAND_CONFIG_FUL_ADR_BYTES_MASK 0x00070000 +#define BCHP_NAND_CONFIG_FUL_ADR_BYTES_SHIFT 16 + +/* NAND :: CONFIG :: reserved2 [15:15] */ +#define BCHP_NAND_CONFIG_reserved2_MASK 0x00008000 +#define BCHP_NAND_CONFIG_reserved2_SHIFT 15 + +/* NAND :: CONFIG :: COL_ADR_BYTES [14:12] */ +#define BCHP_NAND_CONFIG_COL_ADR_BYTES_MASK 0x00007000 +#define BCHP_NAND_CONFIG_COL_ADR_BYTES_SHIFT 12 + +/* NAND :: CONFIG :: reserved3 [11:11] */ +#define BCHP_NAND_CONFIG_reserved3_MASK 0x00000800 +#define BCHP_NAND_CONFIG_reserved3_SHIFT 11 + +/* NAND :: CONFIG :: BLK_ADR_BYTES [10:08] */ +#define BCHP_NAND_CONFIG_BLK_ADR_BYTES_MASK 0x00000700 +#define BCHP_NAND_CONFIG_BLK_ADR_BYTES_SHIFT 8 + +/* NAND :: CONFIG :: reserved4 [07:00] */ +#define BCHP_NAND_CONFIG_reserved4_MASK 0x000000ff +#define BCHP_NAND_CONFIG_reserved4_SHIFT 0 + +/*************************************************************************** + *TIMING_1 - Nand Flash Timing Parameters 1 + ***************************************************************************/ +/* NAND :: TIMING_1 :: tWP [31:28] */ +#define BCHP_NAND_TIMING_1_tWP_MASK 0xf0000000 +#define BCHP_NAND_TIMING_1_tWP_SHIFT 28 + +/* NAND :: TIMING_1 :: tWH [27:24] */ +#define BCHP_NAND_TIMING_1_tWH_MASK 0x0f000000 +#define BCHP_NAND_TIMING_1_tWH_SHIFT 24 + +/* NAND :: TIMING_1 :: tRP [23:20] */ +#define BCHP_NAND_TIMING_1_tRP_MASK 0x00f00000 +#define BCHP_NAND_TIMING_1_tRP_SHIFT 20 + +/* NAND :: TIMING_1 :: tREH [19:16] */ +#define BCHP_NAND_TIMING_1_tREH_MASK 0x000f0000 +#define BCHP_NAND_TIMING_1_tREH_SHIFT 16 + +/* NAND :: TIMING_1 :: tCS [15:12] */ +#define BCHP_NAND_TIMING_1_tCS_MASK 0x0000f000 +#define BCHP_NAND_TIMING_1_tCS_SHIFT 12 + +/* NAND :: TIMING_1 :: tCLH [11:08] */ +#define BCHP_NAND_TIMING_1_tCLH_MASK 0x00000f00 +#define BCHP_NAND_TIMING_1_tCLH_SHIFT 8 + +/* NAND :: TIMING_1 :: tALH [07:04] */ +#define BCHP_NAND_TIMING_1_tALH_MASK 0x000000f0 +#define BCHP_NAND_TIMING_1_tALH_SHIFT 4 + +/* NAND :: TIMING_1 :: tADL [03:00] */ +#define BCHP_NAND_TIMING_1_tADL_MASK 0x0000000f +#define BCHP_NAND_TIMING_1_tADL_SHIFT 0 + +/*************************************************************************** + *TIMING_2 - Nand Flash Timing Parameters 2 + ***************************************************************************/ +/* NAND :: TIMING_2 :: CLK_SELECT [31:31] */ +#define BCHP_NAND_TIMING_2_CLK_SELECT_MASK 0x80000000 +#define BCHP_NAND_TIMING_2_CLK_SELECT_SHIFT 31 +#define BCHP_NAND_TIMING_2_CLK_SELECT_CLK_108 0 +#define BCHP_NAND_TIMING_2_CLK_SELECT_CLK_216 1 + +/* NAND :: TIMING_2 :: reserved0 [30:13] */ +#define BCHP_NAND_TIMING_2_reserved0_MASK 0x7fffe000 +#define BCHP_NAND_TIMING_2_reserved0_SHIFT 13 + +/* NAND :: TIMING_2 :: tWB [12:09] */ +#define BCHP_NAND_TIMING_2_tWB_MASK 0x00001e00 +#define BCHP_NAND_TIMING_2_tWB_SHIFT 9 + +/* NAND :: TIMING_2 :: tWHR [08:04] */ +#define BCHP_NAND_TIMING_2_tWHR_MASK 0x000001f0 +#define BCHP_NAND_TIMING_2_tWHR_SHIFT 4 + +/* NAND :: TIMING_2 :: tREAD [03:00] */ +#define BCHP_NAND_TIMING_2_tREAD_MASK 0x0000000f +#define BCHP_NAND_TIMING_2_tREAD_SHIFT 0 + +/*************************************************************************** + *SEMAPHORE - Semaphore + ***************************************************************************/ +/* NAND :: SEMAPHORE :: reserved0 [31:08] */ +#define BCHP_NAND_SEMAPHORE_reserved0_MASK 0xffffff00 +#define BCHP_NAND_SEMAPHORE_reserved0_SHIFT 8 + +/* NAND :: SEMAPHORE :: semaphore_ctrl [07:00] */ +#define BCHP_NAND_SEMAPHORE_semaphore_ctrl_MASK 0x000000ff +#define BCHP_NAND_SEMAPHORE_semaphore_ctrl_SHIFT 0 + +/*************************************************************************** + *FLASH_DEVICE_ID - Nand Flash Device ID + ***************************************************************************/ +/* NAND :: FLASH_DEVICE_ID :: BYTE_0 [31:24] */ +#define BCHP_NAND_FLASH_DEVICE_ID_BYTE_0_MASK 0xff000000 +#define BCHP_NAND_FLASH_DEVICE_ID_BYTE_0_SHIFT 24 + +/* NAND :: FLASH_DEVICE_ID :: BYTE_1 [23:16] */ +#define BCHP_NAND_FLASH_DEVICE_ID_BYTE_1_MASK 0x00ff0000 +#define BCHP_NAND_FLASH_DEVICE_ID_BYTE_1_SHIFT 16 + +/* NAND :: FLASH_DEVICE_ID :: BYTE_2 [15:08] */ +#define BCHP_NAND_FLASH_DEVICE_ID_BYTE_2_MASK 0x0000ff00 +#define BCHP_NAND_FLASH_DEVICE_ID_BYTE_2_SHIFT 8 + +/* NAND :: FLASH_DEVICE_ID :: BYTE_3 [07:00] */ +#define BCHP_NAND_FLASH_DEVICE_ID_BYTE_3_MASK 0x000000ff +#define BCHP_NAND_FLASH_DEVICE_ID_BYTE_3_SHIFT 0 + +/*************************************************************************** + *FLASH_DEVICE_ID_EXT - Nand Flash Extended Device ID + ***************************************************************************/ +/* NAND :: FLASH_DEVICE_ID_EXT :: BYTE_4 [31:24] */ +#define BCHP_NAND_FLASH_DEVICE_ID_EXT_BYTE_4_MASK 0xff000000 +#define BCHP_NAND_FLASH_DEVICE_ID_EXT_BYTE_4_SHIFT 24 + +/* NAND :: FLASH_DEVICE_ID_EXT :: BYTE_5 [23:16] */ +#define BCHP_NAND_FLASH_DEVICE_ID_EXT_BYTE_5_MASK 0x00ff0000 +#define BCHP_NAND_FLASH_DEVICE_ID_EXT_BYTE_5_SHIFT 16 + +/* NAND :: FLASH_DEVICE_ID_EXT :: BYTE_6 [15:08] */ +#define BCHP_NAND_FLASH_DEVICE_ID_EXT_BYTE_6_MASK 0x0000ff00 +#define BCHP_NAND_FLASH_DEVICE_ID_EXT_BYTE_6_SHIFT 8 + +/* NAND :: FLASH_DEVICE_ID_EXT :: BYTE_7 [07:00] */ +#define BCHP_NAND_FLASH_DEVICE_ID_EXT_BYTE_7_MASK 0x000000ff +#define BCHP_NAND_FLASH_DEVICE_ID_EXT_BYTE_7_SHIFT 0 + +/*************************************************************************** + *BLOCK_LOCK_STATUS - Nand Flash Block Lock Status + ***************************************************************************/ +/* NAND :: BLOCK_LOCK_STATUS :: reserved0 [31:08] */ +#define BCHP_NAND_BLOCK_LOCK_STATUS_reserved0_MASK 0xffffff00 +#define BCHP_NAND_BLOCK_LOCK_STATUS_reserved0_SHIFT 8 + +/* NAND :: BLOCK_LOCK_STATUS :: STATUS [07:00] */ +#define BCHP_NAND_BLOCK_LOCK_STATUS_STATUS_MASK 0x000000ff +#define BCHP_NAND_BLOCK_LOCK_STATUS_STATUS_SHIFT 0 + +/*************************************************************************** + *INTFC_STATUS - Nand Flash Interface Status + ***************************************************************************/ +/* NAND :: INTFC_STATUS :: CTLR_READY [31:31] */ +#define BCHP_NAND_INTFC_STATUS_CTLR_READY_MASK 0x80000000 +#define BCHP_NAND_INTFC_STATUS_CTLR_READY_SHIFT 31 + +/* NAND :: INTFC_STATUS :: FLASH_READY [30:30] */ +#define BCHP_NAND_INTFC_STATUS_FLASH_READY_MASK 0x40000000 +#define BCHP_NAND_INTFC_STATUS_FLASH_READY_SHIFT 30 + +/* NAND :: INTFC_STATUS :: CACHE_VALID [29:29] */ +#define BCHP_NAND_INTFC_STATUS_CACHE_VALID_MASK 0x20000000 +#define BCHP_NAND_INTFC_STATUS_CACHE_VALID_SHIFT 29 + +/* NAND :: INTFC_STATUS :: SPARE_AREA_VALID [28:28] */ +#define BCHP_NAND_INTFC_STATUS_SPARE_AREA_VALID_MASK 0x10000000 +#define BCHP_NAND_INTFC_STATUS_SPARE_AREA_VALID_SHIFT 28 + +/* NAND :: INTFC_STATUS :: ERASED [27:27] */ +#define BCHP_NAND_INTFC_STATUS_ERASED_MASK 0x08000000 +#define BCHP_NAND_INTFC_STATUS_ERASED_SHIFT 27 + +/* NAND :: INTFC_STATUS :: PLANE_READY [26:26] */ +#define BCHP_NAND_INTFC_STATUS_PLANE_READY_MASK 0x04000000 +#define BCHP_NAND_INTFC_STATUS_PLANE_READY_SHIFT 26 + +/* NAND :: INTFC_STATUS :: reserved0 [25:08] */ +#define BCHP_NAND_INTFC_STATUS_reserved0_MASK 0x03ffff00 +#define BCHP_NAND_INTFC_STATUS_reserved0_SHIFT 8 + +/* NAND :: INTFC_STATUS :: FLASH_STATUS [07:00] */ +#define BCHP_NAND_INTFC_STATUS_FLASH_STATUS_MASK 0x000000ff +#define BCHP_NAND_INTFC_STATUS_FLASH_STATUS_SHIFT 0 + +/*************************************************************************** + *ECC_CORR_EXT_ADDR - ECC Correctable Error Extended Address + ***************************************************************************/ +/* NAND :: ECC_CORR_EXT_ADDR :: reserved0 [31:19] */ +#define BCHP_NAND_ECC_CORR_EXT_ADDR_reserved0_MASK 0xfff80000 +#define BCHP_NAND_ECC_CORR_EXT_ADDR_reserved0_SHIFT 19 + +/* NAND :: ECC_CORR_EXT_ADDR :: CS_SEL [18:16] */ +#define BCHP_NAND_ECC_CORR_EXT_ADDR_CS_SEL_MASK 0x00070000 +#define BCHP_NAND_ECC_CORR_EXT_ADDR_CS_SEL_SHIFT 16 + +/* NAND :: ECC_CORR_EXT_ADDR :: EXT_ADDRESS [15:00] */ +#define BCHP_NAND_ECC_CORR_EXT_ADDR_EXT_ADDRESS_MASK 0x0000ffff +#define BCHP_NAND_ECC_CORR_EXT_ADDR_EXT_ADDRESS_SHIFT 0 + +/*************************************************************************** + *ECC_CORR_ADDR - ECC Correctable Error Address + ***************************************************************************/ +/* NAND :: ECC_CORR_ADDR :: ADDRESS [31:00] */ +#define BCHP_NAND_ECC_CORR_ADDR_ADDRESS_MASK 0xffffffff +#define BCHP_NAND_ECC_CORR_ADDR_ADDRESS_SHIFT 0 + +/*************************************************************************** + *ECC_UNC_EXT_ADDR - ECC Uncorrectable Error Extended Address + ***************************************************************************/ +/* NAND :: ECC_UNC_EXT_ADDR :: reserved0 [31:19] */ +#define BCHP_NAND_ECC_UNC_EXT_ADDR_reserved0_MASK 0xfff80000 +#define BCHP_NAND_ECC_UNC_EXT_ADDR_reserved0_SHIFT 19 + +/* NAND :: ECC_UNC_EXT_ADDR :: CS_SEL [18:16] */ +#define BCHP_NAND_ECC_UNC_EXT_ADDR_CS_SEL_MASK 0x00070000 +#define BCHP_NAND_ECC_UNC_EXT_ADDR_CS_SEL_SHIFT 16 + +/* NAND :: ECC_UNC_EXT_ADDR :: EXT_ADDRESS [15:00] */ +#define BCHP_NAND_ECC_UNC_EXT_ADDR_EXT_ADDRESS_MASK 0x0000ffff +#define BCHP_NAND_ECC_UNC_EXT_ADDR_EXT_ADDRESS_SHIFT 0 + +/*************************************************************************** + *ECC_UNC_ADDR - ECC Uncorrectable Error Address + ***************************************************************************/ +/* NAND :: ECC_UNC_ADDR :: ADDRESS [31:00] */ +#define BCHP_NAND_ECC_UNC_ADDR_ADDRESS_MASK 0xffffffff +#define BCHP_NAND_ECC_UNC_ADDR_ADDRESS_SHIFT 0 + +/*************************************************************************** + *READ_ERROR_COUNT - Read Error Count + ***************************************************************************/ +/* NAND :: READ_ERROR_COUNT :: READ_ERROR_COUNT [31:00] */ +#define BCHP_NAND_READ_ERROR_COUNT_READ_ERROR_COUNT_MASK 0xffffffff +#define BCHP_NAND_READ_ERROR_COUNT_READ_ERROR_COUNT_SHIFT 0 + +/*************************************************************************** + *CORR_STAT_THRESHOLD - Correctable Error Reporting Threshold + ***************************************************************************/ +/* NAND :: CORR_STAT_THRESHOLD :: reserved0 [31:30] */ +#define BCHP_NAND_CORR_STAT_THRESHOLD_reserved0_MASK 0xc0000000 +#define BCHP_NAND_CORR_STAT_THRESHOLD_reserved0_SHIFT 30 + +/* NAND :: CORR_STAT_THRESHOLD :: CORR_STAT_THRESHOLD [05:00] */ +#define BCHP_NAND_CORR_STAT_THRESHOLD_CORR_STAT_THRESHOLD_MASK 0x0000003f +#define BCHP_NAND_CORR_STAT_THRESHOLD_CORR_STAT_THRESHOLD_SHIFT 0 + +/*************************************************************************** + *ONFI_STATUS - ONFI Status + ***************************************************************************/ +/* NAND :: ONFI_STATUS :: ONFI_DEBUG_SEL [31:28] */ +#define BCHP_NAND_ONFI_STATUS_ONFI_DEBUG_SEL_MASK 0xf0000000 +#define BCHP_NAND_ONFI_STATUS_ONFI_DEBUG_SEL_SHIFT 28 + +/* NAND :: ONFI_STATUS :: reserved0 [27:06] */ +#define BCHP_NAND_ONFI_STATUS_reserved0_MASK 0x0fffffc0 +#define BCHP_NAND_ONFI_STATUS_reserved0_SHIFT 6 + +/* NAND :: ONFI_STATUS :: ONFI_BAD_IDENT_PG2 [05:05] */ +#define BCHP_NAND_ONFI_STATUS_ONFI_BAD_IDENT_PG2_MASK 0x00000020 +#define BCHP_NAND_ONFI_STATUS_ONFI_BAD_IDENT_PG2_SHIFT 5 + +/* NAND :: ONFI_STATUS :: ONFI_BAD_IDENT_PG1 [04:04] */ +#define BCHP_NAND_ONFI_STATUS_ONFI_BAD_IDENT_PG1_MASK 0x00000010 +#define BCHP_NAND_ONFI_STATUS_ONFI_BAD_IDENT_PG1_SHIFT 4 + +/* NAND :: ONFI_STATUS :: ONFI_BAD_IDENT_PG0 [03:03] */ +#define BCHP_NAND_ONFI_STATUS_ONFI_BAD_IDENT_PG0_MASK 0x00000008 +#define BCHP_NAND_ONFI_STATUS_ONFI_BAD_IDENT_PG0_SHIFT 3 + +/* NAND :: ONFI_STATUS :: ONFI_CRC_ERROR_PG2 [02:02] */ +#define BCHP_NAND_ONFI_STATUS_ONFI_CRC_ERROR_PG2_MASK 0x00000004 +#define BCHP_NAND_ONFI_STATUS_ONFI_CRC_ERROR_PG2_SHIFT 2 + +/* NAND :: ONFI_STATUS :: ONFI_CRC_ERROR_PG1 [01:01] */ +#define BCHP_NAND_ONFI_STATUS_ONFI_CRC_ERROR_PG1_MASK 0x00000002 +#define BCHP_NAND_ONFI_STATUS_ONFI_CRC_ERROR_PG1_SHIFT 1 + +/* NAND :: ONFI_STATUS :: ONFI_CRC_ERROR_PG0 [00:00] */ +#define BCHP_NAND_ONFI_STATUS_ONFI_CRC_ERROR_PG0_MASK 0x00000001 +#define BCHP_NAND_ONFI_STATUS_ONFI_CRC_ERROR_PG0_SHIFT 0 + +/*************************************************************************** + *ONFI_DEBUG_DATA - ONFI Debug Data + ***************************************************************************/ +/* NAND :: ONFI_DEBUG_DATA :: ONFI_DEBUG_DATA [31:00] */ +#define BCHP_NAND_ONFI_DEBUG_DATA_ONFI_DEBUG_DATA_MASK 0xffffffff +#define BCHP_NAND_ONFI_DEBUG_DATA_ONFI_DEBUG_DATA_SHIFT 0 + +/*************************************************************************** + *FLASH_READ_EXT_ADDR - Flash Read Data Extended Address + ***************************************************************************/ +/* NAND :: FLASH_READ_EXT_ADDR :: reserved0 [31:19] */ +#define BCHP_NAND_FLASH_READ_EXT_ADDR_reserved0_MASK 0xfff80000 +#define BCHP_NAND_FLASH_READ_EXT_ADDR_reserved0_SHIFT 19 + +/* NAND :: FLASH_READ_EXT_ADDR :: CS_SEL [18:16] */ +#define BCHP_NAND_FLASH_READ_EXT_ADDR_CS_SEL_MASK 0x00070000 +#define BCHP_NAND_FLASH_READ_EXT_ADDR_CS_SEL_SHIFT 16 + +/* NAND :: FLASH_READ_EXT_ADDR :: EXT_ADDRESS [15:00] */ +#define BCHP_NAND_FLASH_READ_EXT_ADDR_EXT_ADDRESS_MASK 0x0000ffff +#define BCHP_NAND_FLASH_READ_EXT_ADDR_EXT_ADDRESS_SHIFT 0 + +/*************************************************************************** + *FLASH_READ_ADDR - Flash Read Data Address + ***************************************************************************/ +/* NAND :: FLASH_READ_ADDR :: ADDRESS [31:00] */ +#define BCHP_NAND_FLASH_READ_ADDR_ADDRESS_MASK 0xffffffff +#define BCHP_NAND_FLASH_READ_ADDR_ADDRESS_SHIFT 0 + +/*************************************************************************** + *PROGRAM_PAGE_EXT_ADDR - Page Program Extended Address + ***************************************************************************/ +/* NAND :: PROGRAM_PAGE_EXT_ADDR :: reserved0 [31:19] */ +#define BCHP_NAND_PROGRAM_PAGE_EXT_ADDR_reserved0_MASK 0xfff80000 +#define BCHP_NAND_PROGRAM_PAGE_EXT_ADDR_reserved0_SHIFT 19 + +/* NAND :: PROGRAM_PAGE_EXT_ADDR :: CS_SEL [18:16] */ +#define BCHP_NAND_PROGRAM_PAGE_EXT_ADDR_CS_SEL_MASK 0x00070000 +#define BCHP_NAND_PROGRAM_PAGE_EXT_ADDR_CS_SEL_SHIFT 16 + +/* NAND :: PROGRAM_PAGE_EXT_ADDR :: EXT_ADDRESS [15:00] */ +#define BCHP_NAND_PROGRAM_PAGE_EXT_ADDR_EXT_ADDRESS_MASK 0x0000ffff +#define BCHP_NAND_PROGRAM_PAGE_EXT_ADDR_EXT_ADDRESS_SHIFT 0 + +/*************************************************************************** + *PROGRAM_PAGE_ADDR - Page Program Address + ***************************************************************************/ +/* NAND :: PROGRAM_PAGE_ADDR :: ADDRESS [31:00] */ +#define BCHP_NAND_PROGRAM_PAGE_ADDR_ADDRESS_MASK 0xffffffff +#define BCHP_NAND_PROGRAM_PAGE_ADDR_ADDRESS_SHIFT 0 + +/*************************************************************************** + *COPY_BACK_EXT_ADDR - Copy Back Extended Address + ***************************************************************************/ +/* NAND :: COPY_BACK_EXT_ADDR :: reserved0 [31:19] */ +#define BCHP_NAND_COPY_BACK_EXT_ADDR_reserved0_MASK 0xfff80000 +#define BCHP_NAND_COPY_BACK_EXT_ADDR_reserved0_SHIFT 19 + +/* NAND :: COPY_BACK_EXT_ADDR :: CS_SEL [18:16] */ +#define BCHP_NAND_COPY_BACK_EXT_ADDR_CS_SEL_MASK 0x00070000 +#define BCHP_NAND_COPY_BACK_EXT_ADDR_CS_SEL_SHIFT 16 + +/* NAND :: COPY_BACK_EXT_ADDR :: EXT_ADDRESS [15:00] */ +#define BCHP_NAND_COPY_BACK_EXT_ADDR_EXT_ADDRESS_MASK 0x0000ffff +#define BCHP_NAND_COPY_BACK_EXT_ADDR_EXT_ADDRESS_SHIFT 0 + +/*************************************************************************** + *COPY_BACK_ADDR - Copy Back Address + ***************************************************************************/ +/* NAND :: COPY_BACK_ADDR :: ADDRESS [31:00] */ +#define BCHP_NAND_COPY_BACK_ADDR_ADDRESS_MASK 0xffffffff +#define BCHP_NAND_COPY_BACK_ADDR_ADDRESS_SHIFT 0 + +/*************************************************************************** + *BLOCK_ERASE_EXT_ADDR - Block Erase Extended Address + ***************************************************************************/ +/* NAND :: BLOCK_ERASE_EXT_ADDR :: reserved0 [31:19] */ +#define BCHP_NAND_BLOCK_ERASE_EXT_ADDR_reserved0_MASK 0xfff80000 +#define BCHP_NAND_BLOCK_ERASE_EXT_ADDR_reserved0_SHIFT 19 + +/* NAND :: BLOCK_ERASE_EXT_ADDR :: CS_SEL [18:16] */ +#define BCHP_NAND_BLOCK_ERASE_EXT_ADDR_CS_SEL_MASK 0x00070000 +#define BCHP_NAND_BLOCK_ERASE_EXT_ADDR_CS_SEL_SHIFT 16 + +/* NAND :: BLOCK_ERASE_EXT_ADDR :: EXT_ADDRESS [15:00] */ +#define BCHP_NAND_BLOCK_ERASE_EXT_ADDR_EXT_ADDRESS_MASK 0x0000ffff +#define BCHP_NAND_BLOCK_ERASE_EXT_ADDR_EXT_ADDRESS_SHIFT 0 + +/*************************************************************************** + *BLOCK_ERASE_ADDR - Block Erase Address + ***************************************************************************/ +/* NAND :: BLOCK_ERASE_ADDR :: ADDRESS [31:00] */ +#define BCHP_NAND_BLOCK_ERASE_ADDR_ADDRESS_MASK 0xffffffff +#define BCHP_NAND_BLOCK_ERASE_ADDR_ADDRESS_SHIFT 0 + +/*************************************************************************** + *INV_READ_EXT_ADDR - Flash Invalid Data Extended Address + ***************************************************************************/ +/* NAND :: INV_READ_EXT_ADDR :: reserved0 [31:19] */ +#define BCHP_NAND_INV_READ_EXT_ADDR_reserved0_MASK 0xfff80000 +#define BCHP_NAND_INV_READ_EXT_ADDR_reserved0_SHIFT 19 + +/* NAND :: INV_READ_EXT_ADDR :: CS_SEL [18:16] */ +#define BCHP_NAND_INV_READ_EXT_ADDR_CS_SEL_MASK 0x00070000 +#define BCHP_NAND_INV_READ_EXT_ADDR_CS_SEL_SHIFT 16 + +/* NAND :: INV_READ_EXT_ADDR :: EXT_ADDRESS [15:00] */ +#define BCHP_NAND_INV_READ_EXT_ADDR_EXT_ADDRESS_MASK 0x0000ffff +#define BCHP_NAND_INV_READ_EXT_ADDR_EXT_ADDRESS_SHIFT 0 + +/*************************************************************************** + *INV_READ_ADDR - Flash Invalid Data Address + ***************************************************************************/ +/* NAND :: INV_READ_ADDR :: ADDRESS [31:00] */ +#define BCHP_NAND_INV_READ_ADDR_ADDRESS_MASK 0xffffffff +#define BCHP_NAND_INV_READ_ADDR_ADDRESS_SHIFT 0 + +/*************************************************************************** + *BLK_WR_PROTECT - Block Write Protect Enable and Size for EBI_CS0b + ***************************************************************************/ +/* NAND :: BLK_WR_PROTECT :: BLK_END_ADDR [31:00] */ +#define BCHP_NAND_BLK_WR_PROTECT_BLK_END_ADDR_MASK 0xffffffff +#define BCHP_NAND_BLK_WR_PROTECT_BLK_END_ADDR_SHIFT 0 + +/*************************************************************************** + *ACC_CONTROL_CS1 - Nand Flash Access Control + ***************************************************************************/ +/* NAND :: ACC_CONTROL_CS1 :: RD_ECC_EN [31:31] */ +#define BCHP_NAND_ACC_CONTROL_CS1_RD_ECC_EN_MASK 0x80000000 +#define BCHP_NAND_ACC_CONTROL_CS1_RD_ECC_EN_SHIFT 31 + +/* NAND :: ACC_CONTROL_CS1 :: WR_ECC_EN [30:30] */ +#define BCHP_NAND_ACC_CONTROL_CS1_WR_ECC_EN_MASK 0x40000000 +#define BCHP_NAND_ACC_CONTROL_CS1_WR_ECC_EN_SHIFT 30 + +/* NAND :: ACC_CONTROL :: RD_ECC_BLK0_EN [29:29] */ +#define BCHP_NAND_ACC_CONTROL_CS1_CE_CARE_MASK 0x20000000 +#define BCHP_NAND_ACC_CONTROL_CS1_CE_CARE_SHIFT 29 + +/* NAND :: ACC_CONTROL :: reserve1 [28:28] */ +#define BCHP_NAND_ACC_CONTROL_CS1_RESERVED1_MASK 0x10000000 +#define BCHP_NAND_ACC_CONTROL_CS1_RESERVED1_SHIFT 28 + +/* NAND :: ACC_CONTROL_CS1 :: RD_ERASED_ECC_EN [27:27] */ +#define BCHP_NAND_ACC_CONTROL_CS1_RD_ERASED_ECC_EN_MASK 0x08000000 +#define BCHP_NAND_ACC_CONTROL_CS1_RD_ERASED_ECC_EN_SHIFT 27 + +/* NAND :: ACC_CONTROL_CS1 :: PARTIAL_PAGE_EN [26:26] */ +#define BCHP_NAND_ACC_CONTROL_CS1_PARTIAL_PAGE_EN_MASK 0x04000000 +#define BCHP_NAND_ACC_CONTROL_CS1_PARTIAL_PAGE_EN_SHIFT 26 + +/* NAND :: ACC_CONTROL_CS1 :: WR_PREEMPT_EN [25:25] */ +#define BCHP_NAND_ACC_CONTROL_CS1_WR_PREEMPT_EN_MASK 0x02000000 +#define BCHP_NAND_ACC_CONTROL_CS1_WR_PREEMPT_EN_SHIFT 25 + +/* NAND :: ACC_CONTROL_CS1 :: PAGE_HIT_EN [24:24] */ +#define BCHP_NAND_ACC_CONTROL_CS1_PAGE_HIT_EN_MASK 0x01000000 +#define BCHP_NAND_ACC_CONTROL_CS1_PAGE_HIT_EN_SHIFT 24 + +/* NAND :: ACC_CONTROL :: PREFETCH_EN [23:23] */ +#define BCHP_NAND_ACC_CONTROL_CS1_PREFETCH_EN_MASK 0x00800000 +#define BCHP_NAND_ACC_CONTROL_CS1_PREFETCH_EN_SHIFT 23 + +/* NAND :: ACC_CONTROL :: CACHE_MODE_EN [22:22] */ +#define BCHP_NAND_ACC_CONTROL_CS1_CACHE_MODE_EN_MASK 0x00400000 +#define BCHP_NAND_ACC_CONTROL_CS1_CACHE_MODE_EN_SHIFT 22 + +/* NAND :: ACC_CONTROL :: reserve2 [21:21] */ +#define BCHP_NAND_ACC_CONTROL_CS1_RESERVED2_MASK 0x00200000 +#define BCHP_NAND_ACC_CONTROL_CS1_RESERVED2_SHIFT 21 + +/* NAND :: ACC_CONTROL :: ECC_LEVEL [20:16] */ +#define BCHP_NAND_ACC_CONTROL_CS1_ECC_LEVEL_MASK 0x001f0000 +#define BCHP_NAND_ACC_CONTROL_CS1_ECC_LEVEL_SHIFT 16 + +/* NAND :: ACC_CONTROL :: reserved3 [15:8] */ +#define BCHP_NAND_ACC_CONTROL_CS1_RESERVED3_MASK 0x0000ff00 +#define BCHP_NAND_ACC_CONTROL_CS1_RESERVED3_SHIFT 8 + +/* NAND :: ACC_CONTROL :: SECTOR_SIZE_1K [07:07] */ +#define BCHP_NAND_ACC_CONTROL_CS1_SECTOR_SIZE_1K_MASK 0x00000080 +#define BCHP_NAND_ACC_CONTROL_CS1_SECTOR_SIZE_1K_SHIFT 7 + +/* NAND :: ACC_CONTROL_CS1 :: SPARE_AREA_SIZE [06:00] */ +#define BCHP_NAND_ACC_CONTROL_CS1_SPARE_AREA_SIZE_MASK 0x0000007f +#define BCHP_NAND_ACC_CONTROL_CS1_SPARE_AREA_SIZE_SHIFT 0 + +#if CONFIG_MTD_BRCMNAND_VERSION > CONFIG_MTD_BRCMNAND_VERS_7_0 +/*************************************************************************** + *CONFIG_CS1 EXT - Nand Flash Config Ext + ***************************************************************************/ +/* NAND :: CONFIG_CS1_EXT :: BLOCK_SIZE [11:4] */ +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_MASK 0x00000ff0 +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_SHIFT 4 +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_BK_SIZE_8192KB 10 +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_BK_SIZE_4096KB 9 +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_BK_SIZE_2048KB 8 +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_BK_SIZE_1024KB 7 +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_BK_SIZE_512KB 6 +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_BK_SIZE_256KB 5 +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_BK_SIZE_128KB 4 +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_BK_SIZE_64KB 3 +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_BK_SIZE_32KB 2 +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_BK_SIZE_16KB 1 +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_BK_SIZE_8KB 0 + +/* NAND :: CONFIG_CS1_EXT :: PAGE_SIZE [11:4] */ +#define BCHP_NAND_CONFIG_CS1_PAGE_SIZE_MASK 0x0000000f +#define BCHP_NAND_CONFIG_CS1_PAGE_SIZE_SHIFT 0 +#define BCHP_NAND_CONFIG_CS1_PAGE_SIZE_PG_SIZE_512 0 +#define BCHP_NAND_CONFIG_CS1_PAGE_SIZE_PG_SIZE_1KB 1 +#define BCHP_NAND_CONFIG_CS1_PAGE_SIZE_PG_SIZE_2KB 2 +#define BCHP_NAND_CONFIG_CS1_PAGE_SIZE_PG_SIZE_4KB 3 +#define BCHP_NAND_CONFIG_CS1_PAGE_SIZE_PG_SIZE_8KB 4 +#define BCHP_NAND_CONFIG_CS1_PAGE_SIZE_PG_SIZE_16KB 5 + +#endif + +/*************************************************************************** + *CONFIG_CS1 - Nand Flash Config + ***************************************************************************/ +/* NAND :: CONFIG_CS1 :: CONFIG_LOCK [31:31] */ +#define BCHP_NAND_CONFIG_CS1_CONFIG_LOCK_MASK 0x80000000 +#define BCHP_NAND_CONFIG_CS1_CONFIG_LOCK_SHIFT 31 + +#if CONFIG_MTD_BRCMNAND_VERSION == CONFIG_MTD_BRCMNAND_VERS_7_0 +/* NAND :: CONFIG_CS1 :: BLOCK_SIZE [30:28] */ +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_MASK 0x70000000 +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_SHIFT 28 +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_BK_SIZE_2048KB 6 +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_BK_SIZE_1024KB 5 +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_BK_SIZE_512KB 4 +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_BK_SIZE_256KB 3 +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_BK_SIZE_128KB 2 +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_BK_SIZE_16KB 1 +#define BCHP_NAND_CONFIG_CS1_BLOCK_SIZE_BK_SIZE_8KB 0 +#endif + +/* NAND :: CONFIG_CS1 :: DEVICE_SIZE [27:24] */ +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_MASK 0x0f000000 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_SHIFT 24 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_4MB 0 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_8MB 1 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_16MB 2 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_32MB 3 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_64MB 4 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_128MB 5 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_256MB 6 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_512MB 7 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_1GB 8 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_2GB 9 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_4GB 10 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_8GB 11 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_16GB 12 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_32GB 13 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_64GB 14 +#define BCHP_NAND_CONFIG_CS1_DEVICE_SIZE_DVC_SIZE_128GB 15 + +/* NAND :: CONFIG_CS1 :: DEVICE_WIDTH [23:23] */ +#define BCHP_NAND_CONFIG_CS1_DEVICE_WIDTH_MASK 0x00800000 +#define BCHP_NAND_CONFIG_CS1_DEVICE_WIDTH_SHIFT 23 +#define BCHP_NAND_CONFIG_CS1_DEVICE_WIDTH_DVC_WIDTH_8 0 +#define BCHP_NAND_CONFIG_CS1_DEVICE_WIDTH_DVC_WIDTH_16 1 + +/* NAND :: CONFIG_CS1 :: reserved0 [22:22] */ +#define BCHP_NAND_CONFIG_CS1_reserved0_MASK 0x00400000 +#define BCHP_NAND_CONFIG_CS1_reserved0_SHIFT 22 + +#if CONFIG_MTD_BRCMNAND_VERSION == CONFIG_MTD_BRCMNAND_VERS_7_0 +/* NAND :: CONFIG_CS1 :: PAGE_SIZE [21:20] */ +#define BCHP_NAND_CONFIG_CS1_PAGE_SIZE_MASK 0x00300000 +#define BCHP_NAND_CONFIG_CS1_PAGE_SIZE_SHIFT 20 +#define BCHP_NAND_CONFIG_CS1_PAGE_SIZE_PG_SIZE_512 0 +#define BCHP_NAND_CONFIG_CS1_PAGE_SIZE_PG_SIZE_2KB 1 +#define BCHP_NAND_CONFIG_CS1_PAGE_SIZE_PG_SIZE_4KB 2 +#define BCHP_NAND_CONFIG_CS1_PAGE_SIZE_PG_SIZE_8KB 3 +#endif + +/* NAND :: CONFIG_CS1 :: reserved1 [19:19] */ +#define BCHP_NAND_CONFIG_CS1_reserved1_MASK 0x00080000 +#define BCHP_NAND_CONFIG_CS1_reserved1_SHIFT 19 + +/* NAND :: CONFIG_CS1 :: FUL_ADR_BYTES [18:16] */ +#define BCHP_NAND_CONFIG_CS1_FUL_ADR_BYTES_MASK 0x00070000 +#define BCHP_NAND_CONFIG_CS1_FUL_ADR_BYTES_SHIFT 16 + +/* NAND :: CONFIG_CS1 :: reserved2 [15:15] */ +#define BCHP_NAND_CONFIG_CS1_reserved2_MASK 0x00008000 +#define BCHP_NAND_CONFIG_CS1_reserved2_SHIFT 15 + +/* NAND :: CONFIG_CS1 :: COL_ADR_BYTES [14:12] */ +#define BCHP_NAND_CONFIG_CS1_COL_ADR_BYTES_MASK 0x00007000 +#define BCHP_NAND_CONFIG_CS1_COL_ADR_BYTES_SHIFT 12 + +/* NAND :: CONFIG_CS1 :: reserved3 [11:11] */ +#define BCHP_NAND_CONFIG_CS1_reserved3_MASK 0x00000800 +#define BCHP_NAND_CONFIG_CS1_reserved3_SHIFT 11 + +/* NAND :: CONFIG_CS1 :: BLK_ADR_BYTES [10:08] */ +#define BCHP_NAND_CONFIG_CS1_BLK_ADR_BYTES_MASK 0x00000700 +#define BCHP_NAND_CONFIG_CS1_BLK_ADR_BYTES_SHIFT 8 + +/* NAND :: CONFIG_CS1 :: reserved4 [07:00] */ +#define BCHP_NAND_CONFIG_CS1_reserved4_MASK 0x000000ff +#define BCHP_NAND_CONFIG_CS1_reserved4_SHIFT 0 + +/*************************************************************************** + *TIMING_1_CS1 - Nand Flash Timing Parameters 1 + ***************************************************************************/ +/* NAND :: TIMING_1_CS1 :: tWP [31:28] */ +#define BCHP_NAND_TIMING_1_CS1_tWP_MASK 0xf0000000 +#define BCHP_NAND_TIMING_1_CS1_tWP_SHIFT 28 + +/* NAND :: TIMING_1_CS1 :: tWH [27:24] */ +#define BCHP_NAND_TIMING_1_CS1_tWH_MASK 0x0f000000 +#define BCHP_NAND_TIMING_1_CS1_tWH_SHIFT 24 + +/* NAND :: TIMING_1_CS1 :: tRP [23:20] */ +#define BCHP_NAND_TIMING_1_CS1_tRP_MASK 0x00f00000 +#define BCHP_NAND_TIMING_1_CS1_tRP_SHIFT 20 + +/* NAND :: TIMING_1_CS1 :: tREH [19:16] */ +#define BCHP_NAND_TIMING_1_CS1_tREH_MASK 0x000f0000 +#define BCHP_NAND_TIMING_1_CS1_tREH_SHIFT 16 + +/* NAND :: TIMING_1_CS1 :: tCS [15:12] */ +#define BCHP_NAND_TIMING_1_CS1_tCS_MASK 0x0000f000 +#define BCHP_NAND_TIMING_1_CS1_tCS_SHIFT 12 + +/* NAND :: TIMING_1_CS1 :: tCLH [11:08] */ +#define BCHP_NAND_TIMING_1_CS1_tCLH_MASK 0x00000f00 +#define BCHP_NAND_TIMING_1_CS1_tCLH_SHIFT 8 + +/* NAND :: TIMING_1_CS1 :: tALH [07:04] */ +#define BCHP_NAND_TIMING_1_CS1_tALH_MASK 0x000000f0 +#define BCHP_NAND_TIMING_1_CS1_tALH_SHIFT 4 + +/* NAND :: TIMING_1_CS1 :: tADL [03:00] */ +#define BCHP_NAND_TIMING_1_CS1_tADL_MASK 0x0000000f +#define BCHP_NAND_TIMING_1_CS1_tADL_SHIFT 0 + +/*************************************************************************** + *TIMING_2_CS1 - Nand Flash Timing Parameters 2 + ***************************************************************************/ +/* NAND :: TIMING_2_CS1 :: CLK_SELECT [31:31] */ +#define BCHP_NAND_TIMING_2_CS1_CLK_SELECT_MASK 0x80000000 +#define BCHP_NAND_TIMING_2_CS1_CLK_SELECT_SHIFT 31 +#define BCHP_NAND_TIMING_2_CS1_CLK_SELECT_CLK_108 0 +#define BCHP_NAND_TIMING_2_CS1_CLK_SELECT_CLK_216 1 + +/* NAND :: TIMING_2_CS1 :: reserved0 [30:13] */ +#define BCHP_NAND_TIMING_2_CS1_reserved0_MASK 0x7fffe000 +#define BCHP_NAND_TIMING_2_CS1_reserved0_SHIFT 13 + +/* NAND :: TIMING_2_CS1 :: tWB [12:09] */ +#define BCHP_NAND_TIMING_2_CS1_tWB_MASK 0x00001e00 +#define BCHP_NAND_TIMING_2_CS1_tWB_SHIFT 9 + +/* NAND :: TIMING_2_CS1 :: tWHR [08:04] */ +#define BCHP_NAND_TIMING_2_CS1_tWHR_MASK 0x000001f0 +#define BCHP_NAND_TIMING_2_CS1_tWHR_SHIFT 4 + +/* NAND :: TIMING_2_CS1 :: tREAD [03:00] */ +#define BCHP_NAND_TIMING_2_CS1_tREAD_MASK 0x0000000f +#define BCHP_NAND_TIMING_2_CS1_tREAD_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_READ_OFS_10 - Nand Flash Spare Area Read Bytes 16-19 + ***************************************************************************/ +/* NAND :: SPARE_AREA_READ_OFS_10 :: BYTE_OFS_16 [31:24] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_10_BYTE_OFS_16_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_10_BYTE_OFS_16_SHIFT 24 + +/* NAND :: SPARE_AREA_READ_OFS_10 :: BYTE_OFS_17 [23:16] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_10_BYTE_OFS_17_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_10_BYTE_OFS_17_SHIFT 16 + +/* NAND :: SPARE_AREA_READ_OFS_10 :: BYTE_OFS_18 [15:08] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_10_BYTE_OFS_18_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_READ_OFS_10_BYTE_OFS_18_SHIFT 8 + +/* NAND :: SPARE_AREA_READ_OFS_10 :: BYTE_OFS_19 [07:00] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_10_BYTE_OFS_19_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_READ_OFS_10_BYTE_OFS_19_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_READ_OFS_14 - Nand Flash Spare Area Read Bytes 20-23 + ***************************************************************************/ +/* NAND :: SPARE_AREA_READ_OFS_14 :: BYTE_OFS_20 [31:24] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_14_BYTE_OFS_20_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_14_BYTE_OFS_20_SHIFT 24 + +/* NAND :: SPARE_AREA_READ_OFS_14 :: BYTE_OFS_21 [23:16] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_14_BYTE_OFS_21_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_14_BYTE_OFS_21_SHIFT 16 + +/* NAND :: SPARE_AREA_READ_OFS_14 :: BYTE_OFS_22 [15:08] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_14_BYTE_OFS_22_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_READ_OFS_14_BYTE_OFS_22_SHIFT 8 + +/* NAND :: SPARE_AREA_READ_OFS_14 :: BYTE_OFS_23 [07:00] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_14_BYTE_OFS_23_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_READ_OFS_14_BYTE_OFS_23_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_READ_OFS_18 - Nand Flash Spare Area Read Bytes 24-27 + ***************************************************************************/ +/* NAND :: SPARE_AREA_READ_OFS_18 :: BYTE_OFS_24 [31:24] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_18_BYTE_OFS_24_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_18_BYTE_OFS_24_SHIFT 24 + +/* NAND :: SPARE_AREA_READ_OFS_18 :: BYTE_OFS_25 [23:16] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_18_BYTE_OFS_25_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_18_BYTE_OFS_25_SHIFT 16 + +/* NAND :: SPARE_AREA_READ_OFS_18 :: BYTE_OFS_26 [15:08] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_18_BYTE_OFS_26_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_READ_OFS_18_BYTE_OFS_26_SHIFT 8 + +/* NAND :: SPARE_AREA_READ_OFS_18 :: BYTE_OFS_27 [07:00] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_18_BYTE_OFS_27_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_READ_OFS_18_BYTE_OFS_27_SHIFT 0 + +/*************************************************************************** + *SPARE_AREA_READ_OFS_1C - Nand Flash Spare Area Read Bytes 28-31 + ***************************************************************************/ +/* NAND :: SPARE_AREA_READ_OFS_1C :: BYTE_OFS_28 [31:24] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_1C_BYTE_OFS_28_MASK 0xff000000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_1C_BYTE_OFS_28_SHIFT 24 + +/* NAND :: SPARE_AREA_READ_OFS_1C :: BYTE_OFS_29 [23:16] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_1C_BYTE_OFS_29_MASK 0x00ff0000 +#define BCHP_NAND_SPARE_AREA_READ_OFS_1C_BYTE_OFS_29_SHIFT 16 + +/* NAND :: SPARE_AREA_READ_OFS_1C :: BYTE_OFS_30 [15:08] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_1C_BYTE_OFS_30_MASK 0x0000ff00 +#define BCHP_NAND_SPARE_AREA_READ_OFS_1C_BYTE_OFS_30_SHIFT 8 + +/* NAND :: SPARE_AREA_READ_OFS_1C :: BYTE_OFS_31 [07:00] */ +#define BCHP_NAND_SPARE_AREA_READ_OFS_1C_BYTE_OFS_31_MASK 0x000000ff +#define BCHP_NAND_SPARE_AREA_READ_OFS_1C_BYTE_OFS_31_SHIFT 0 + +/*************************************************************************** + *LL_OP - Nand Flash Low Level Operation + ***************************************************************************/ +/* NAND :: LL_OP :: RETURN_IDLE [31:31] */ +#define BCHP_NAND_LL_OP_RETURN_IDLE_MASK 0x80000000 +#define BCHP_NAND_LL_OP_RETURN_IDLE_SHIFT 31 + +/* NAND :: LL_OP :: reserved0 [30:20] */ +#define BCHP_NAND_LL_OP_reserved0_MASK 0x7ff00000 +#define BCHP_NAND_LL_OP_reserved0_SHIFT 20 + +/* NAND :: LL_OP :: CLE [19:19] */ +#define BCHP_NAND_LL_OP_CLE_MASK 0x00080000 +#define BCHP_NAND_LL_OP_CLE_SHIFT 19 + +/* NAND :: LL_OP :: ALE [18:18] */ +#define BCHP_NAND_LL_OP_ALE_MASK 0x00040000 +#define BCHP_NAND_LL_OP_ALE_SHIFT 18 + +/* NAND :: LL_OP :: WE [17:17] */ +#define BCHP_NAND_LL_OP_WE_MASK 0x00020000 +#define BCHP_NAND_LL_OP_WE_SHIFT 17 + +/* NAND :: LL_OP :: RE [16:16] */ +#define BCHP_NAND_LL_OP_RE_MASK 0x00010000 +#define BCHP_NAND_LL_OP_RE_SHIFT 16 + +/* NAND :: LL_OP :: DATA [15:00] */ +#define BCHP_NAND_LL_OP_DATA_MASK 0x0000ffff +#define BCHP_NAND_LL_OP_DATA_SHIFT 0 + +/*************************************************************************** + *LL_RDDATA - Nand Flash Low Level Read Data + ***************************************************************************/ +/* NAND :: LL_RDDATA :: reserved0 [31:16] */ +#define BCHP_NAND_LL_RDDATA_reserved0_MASK 0xffff0000 +#define BCHP_NAND_LL_RDDATA_reserved0_SHIFT 16 + +/* NAND :: LL_RDDATA :: DATA [15:00] */ +#define BCHP_NAND_LL_RDDATA_DATA_MASK 0x0000ffff +#define BCHP_NAND_LL_RDDATA_DATA_SHIFT 0 + +/*************************************************************************** + *FLASH_CACHE%i - Flash Cache Buffer Read Access + ***************************************************************************/ +#define BCHP_NAND_FLASH_CACHEi_ARRAY_BASE BRCMNAND_CACHE_BASE +#define BCHP_NAND_FLASH_CACHEi_ARRAY_START 0 +#define BCHP_NAND_FLASH_CACHEi_ARRAY_END 127 +#define BCHP_NAND_FLASH_CACHEi_ARRAY_ELEMENT_SIZE 32 + +/*************************************************************************** + *FLASH_CACHE%i - Flash Cache Buffer Read Access + ***************************************************************************/ +/* NAND :: FLASH_CACHEi :: WORD [31:00] */ +#define BCHP_NAND_FLASH_CACHEi_WORD_MASK 0xffffffff +#define BCHP_NAND_FLASH_CACHEi_WORD_SHIFT 0 + +/*************************************************************************** + *NAND FLASH INTR STATUS register + ***************************************************************************/ + +#define BCHP_HIF_INTR2_CPU_STATUS (BRCMNAND_INTR_BASE) +#define BCHP_HIF_INTR2_CPU_STATUS_NAND_UNC_INTR_MASK (0x1<<6) +#define BCHP_HIF_INTR2_CPU_STATUS_NAND_CORR_INTR_MASK (0x1<<7) + +#endif /* #ifndef BCHP_NAND_7x_H__ */ + +/* End of File */ +#endif diff -urN linux-4.1.52-orig/include/linux/mtd/brcmnand.h linux-4.1.52/include/linux/mtd/brcmnand.h --- linux-4.1.52-orig/include/linux/mtd/brcmnand.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/mtd/brcmnand.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,928 @@ +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +/* + <:copyright-BRCM:2012:GPL/GPL:standard + + Copyright (c) 2012 Broadcom + All Rights Reserved + + 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 (the "GPL"). + + 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. + + + A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by + writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + :> + + * drivers/mtd/brcmnand/brcmnand.h + * + * + * + * Data structures for Broadcom NAND controller + * + * when who what + * 20060729 tht Original coding + */ + + +#ifndef _BRCM_NAND_H_ +#define _BRCM_NAND_H_ + +#include +#include +#include +#include +#include + +#if 0 +/* + * Conversion between Kernel Kconfig and Controller version number + * Legacy codes 2.6.18 + */ + +#define CONFIG_MTD_BRCMNAND_VERS_0_0 0 +#define CONFIG_MTD_BRCMNAND_VERS_0_1 1 +#define CONFIG_MTD_BRCMNAND_VERS_1_0 2 + +/* The followings revs are not implemented for 2.6.12 */ +#define CONFIG_MTD_BRCMNAND_VERS_2_0 3 +#define CONFIG_MTD_BRCMNAND_VERS_2_1 4 +#define CONFIG_MTD_BRCMNAND_VERS_2_2 5 + +/* Supporting MLC NAND */ +#define CONFIG_MTD_BRCMNAND_VERS_3_0 6 +#define CONFIG_MTD_BRCMNAND_VERS_3_1_0 7 /* RDB reads as 3.0 */ +#define CONFIG_MTD_BRCMNAND_VERS_3_1_1 8 /* RDB reads as 3.0 */ +#define CONFIG_MTD_BRCMNAND_VERS_3_2 9 +#define CONFIG_MTD_BRCMNAND_VERS_3_3 10 +#define CONFIG_MTD_BRCMNAND_VERS_3_4 11 +#endif + +/* + * New way of using verison numbers + */ +#define BRCMNAND_VERSION(major, minor,int_minor) ((major<<16) | (minor<<8) | int_minor) + +/* + * BRCMNAND_INT_MINOR: Internal version number, not reflected on the silicon + */ +#if defined( CONFIG_BCM7601 ) || defined( CONFIG_BCM7400A0 ) +#define BRCMNAND_INT_MINOR 1 +#else +#define BRCMNAND_INT_MINOR 0 +#endif +#define CONFIG_MTD_BRCMNAND_VERSION \ + BRCMNAND_VERSION(CONFIG_BRCMNAND_MAJOR_VERS, CONFIG_BRCMNAND_MINOR_VERS, BRCMNAND_INT_MINOR) + + +#define CONFIG_MTD_BRCMNAND_VERS_0_0 BRCMNAND_VERSION(0,0,1) /* (0,0,0) is DONT-CARE */ +#define CONFIG_MTD_BRCMNAND_VERS_0_1 BRCMNAND_VERSION(0,1,0) +#define CONFIG_MTD_BRCMNAND_VERS_1_0 BRCMNAND_VERSION(1,0,0) + +/* The followings revs are not implemented for 2.6.12 */ +#define CONFIG_MTD_BRCMNAND_VERS_2_0 BRCMNAND_VERSION(2,0,0) +#define CONFIG_MTD_BRCMNAND_VERS_2_1 BRCMNAND_VERSION(2,1,0) +#define CONFIG_MTD_BRCMNAND_VERS_2_2 BRCMNAND_VERSION(2,2,0) + +/* Supporting MLC NAND */ +#define CONFIG_MTD_BRCMNAND_VERS_3_0 BRCMNAND_VERSION(3,0,0) +#define CONFIG_MTD_BRCMNAND_VERS_3_1_0 BRCMNAND_VERSION(3,1,0) /* RDB reads as 3.0 */ +#define CONFIG_MTD_BRCMNAND_VERS_3_1_1 BRCMNAND_VERSION(3,1,1) /* RDB reads as 3.0 */ +#define CONFIG_MTD_BRCMNAND_VERS_3_2 BRCMNAND_VERSION(3,2,0) +#define CONFIG_MTD_BRCMNAND_VERS_3_3 BRCMNAND_VERSION(3,3,0) +#define CONFIG_MTD_BRCMNAND_VERS_3_4 BRCMNAND_VERSION(3,4,0) + +/* Supporting ONFI */ +#define CONFIG_MTD_BRCMNAND_VERS_4_0 BRCMNAND_VERSION(4,0,0) + +/* Supporting 1KB ECC subpage */ +#define CONFIG_MTD_BRCMNAND_VERS_5_0 BRCMNAND_VERSION(5,0,0) + +/* Add 40-bit ECC support. Remove ECC_LEVEL_0 and SPARE_AREA_SIZE_0 fields. + Expand ECC_LEVEL and SPARE_AREA_SIZE field */ +#define CONFIG_MTD_BRCMNAND_VERS_6_0 BRCMNAND_VERSION(6,0,0) + +/* Remove FAST_PGM_RDIN bit. Always sets true internally when PARTIAL_PAGE_EN=1.*/ +#define CONFIG_MTD_BRCMNAND_VERS_7_0 BRCMNAND_VERSION(7,0,0) +#define CONFIG_MTD_BRCMNAND_VERS_7_1 BRCMNAND_VERSION(7,1,0) + +#ifdef CONFIG_MTD_BRCMNAND_EDU +#define CONFIG_MTD_BRCMNAND_USE_ISR 1 +#endif + +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_1_0 +#define MAX_NAND_CS 8 // Upper limit, actual limit varies depending on platfrom + +#else +#define MAX_NAND_CS 1 +#endif + + +//ST NAND flashes +#ifndef FLASHTYPE_ST + #define FLASHTYPE_ST 0x20 +#endif +#define ST_NAND128W3A 0x73 +#define ST_NAND256R3A 0x35 +#define ST_NAND256W3A 0x75 +#define ST_NAND256R4A 0x45 +#define ST_NAND256W4A 0x55 +#define ST_NAND512R3A 0x36 //Used on Bcm97400 +#define ST_NAND512W3A 0x76 +#define ST_NAND512R4A 0x46 +#define ST_NAND512W4A 0x56 +#define ST_NAND01GR3A 0x39 +#define ST_NAND01GW3A 0x79 +#define ST_NAND01GR4A 0x49 +#define ST_NAND01GW4A 0x59 +#define ST_NAND01GR3B 0xA1 +#define ST_NAND01GW3B 0xF1 +#define ST_NAND01GR4B 0xB1 +#define ST_NAND01GW4B 0xC1 +#define ST_NAND02GR3B 0xAA +#define ST_NAND02GW3B 0xDA +#define ST_NAND02GR4B 0xBA +#define ST_NAND02GW4B 0xCA +#define ST_NAND04GR3B 0xAC +#define ST_NAND04GW3B 0xDC +#define ST_NAND04GR4B 0xBC +#define ST_NAND04GW4B 0xCC +#define ST_NAND08GR3B 0xA3 +#define ST_NAND08GW3B 0xD3 +#define ST_NAND08GR4B 0xB3 +#define ST_NAND08GW4B 0xC3 + +//Samsung NAND flash +#define FLASHTYPE_SAMSUNG 0xEC +#define SAMSUNG_K9F1G08R0A 0xA1 +#define SAMSUNG_K9F1G08U0A 0xF1 +#define SAMSUNG_K9F1G08U0E 0xF1 +#define SAMSUNG_K9F2G08U1A 0xF1 +#define SAMSUNG_K9F2G08U0A 0xDA +#define SAMSUNG_K9K8G08U0A 0xD3 +#define SAMSUNG_K9F8G08U0M 0xD3 + + +//K9F5608(R/U/D)0D +#define SAMSUNG_K9F5608R0D 0x35 +#define SAMSUNG_K9F5608U0D 0x75 +#define SAMSUNG_K9F5608D0D 0x75 +//K9F1208(R/B/U)0B +#define SAMSUNG_K9F1208R0B 0x36 +#define SAMSUNG_K9F1208B0B 0x76 +#define SAMSUNG_K9F1208U0B 0x76 + +/*--------- Chip ID decoding for Samsung MLC NAND flashes -----------------------*/ +#define SAMSUNG_K9LBG08U0M 0xD7 /* 55h, B6h, 78h */ +#define SAMSUNG_K9LBG08U0D 0xD7 /* D5h, 29h, 41h */ +#define SAMSUNG_K9LBG08U0E 0xD7 /* C5h, 72h, 54h, 42h */ + +#define SAMSUNG_K9GAG08U0D 0xD5 /* 94h, 29h, 34h */ +#define SAMSUNG_K9GAG08U0E 0xD5 /* 84h, 72h, 50h, 42h */ + +#define SAMSUNG_3RDID_INT_CHIPNO_MASK NAND_CI_CHIPNR_MSK + +#define SAMSUNG_3RDID_CELLTYPE_MASK NAND_CI_CELLTYPE_MSK +#define SAMSUNG_3RDID_CELLTYPE_SLC 0x00 +#define SAMSUNG_3RDID_CELLTYPE_4LV 0x04 +#define SAMSUNG_3RDID_CELLTYPE_8LV 0x08 +#define SAMSUNG_3RDID_CELLTYPE_16LV 0x0C + +// Low level MLC test as compared to the high level test in mtd-abi.h +#define NAND_IS_MLC(chip) ((chip)->cellinfo & NAND_CI_CELLTYPE_MSK) + +#define SAMSUNG_3RDID_NOP_MASK 0x30 +#define SAMSUNG_3RDID_NOP_1 0x00 +#define SAMSUNG_3RDID_NOP_2 0x10 +#define SAMSUNG_3RDID_NOP_4 0x20 +#define SAMSUNG_3RDID_NOP_8 0x30 + +#define SAMSUNG_3RDID_INTERLEAVE 0x40 + +#define SAMSUNG_3RDID_CACHE_PROG 0x80 + +#define SAMSUNG_4THID_PAGESIZE_MASK 0x03 +#define SAMSUNG_4THID_PAGESIZE_1KB 0x00 +#define SAMSUNG_4THID_PAGESIZE_2KB 0x01 +#define SAMSUNG_4THID_PAGESIZE_4KB 0x02 +#define SAMSUNG_4THID_PAGESIZE_8KB 0x03 + +#define SAMSUNG_4THID_OOBSIZE_MASK 0x04 +#define SAMSUNG_4THID_OOBSIZE_8B 0x00 +#define SAMSUNG_4THID_OOBSIZE_16B 0x04 + +#define SAMSUNG_4THID_BLKSIZE_MASK 0x30 +#define SAMSUNG_4THID_BLKSIZE_64KB 0x00 +#define SAMSUNG_4THID_BLKSIZE_128KB 0x10 +#define SAMSUNG_4THID_BLKSIZE_256KB 0x20 +#define SAMSUNG_4THID_BLKSIZE_512KB 0x30 + + + +#define SAMSUNG2_4THID_PAGESIZE_MASK 0x03 +#define SAMSUNG2_4THID_PAGESIZE_2KB 0x00 +#define SAMSUNG2_4THID_PAGESIZE_4KB 0x01 +#define SAMSUNG2_4THID_PAGESIZE_8KB 0x02 +#define SAMSUNG2_4THID_PAGESIZE_RSV 0x03 + +#define SAMSUNG2_4THID_BLKSIZE_MASK 0xB0 +#define SAMSUNG2_4THID_BLKSIZE_128KB 0x00 +#define SAMSUNG2_4THID_BLKSIZE_256KB 0x10 +#define SAMSUNG2_4THID_BLKSIZE_512KB 0x20 +#define SAMSUNG2_4THID_BLKSIZE_1MB 0x30 +#define SAMSUNG2_4THID_BLKSIZE_RSVD1 0x80 +#define SAMSUNG2_4THID_BLKSIZE_RSVD2 0x90 +#define SAMSUNG2_4THID_BLKSIZE_RSVD3 0xA0 +#define SAMSUNG2_4THID_BLKSIZE_RSVD4 0xB0 + +#define SAMSUNG2_4THID_OOBSIZE_MASK 0x4c +#define SAMSUNG2_4THID_OOBSIZE_PERPAGE_128 0x04 +#define SAMSUNG2_4THID_OOBSIZE_PERPAGE_218 0x08 /* 27.4 per 512B */ +#define SAMSUNG2_4THID_OOBSIZE_PERPAGE_400 0x0C /* 16 per 512B */ +#define SAMSUNG2_4THID_OOBSIZE_PERPAGE_436 0x40 /* 27.5 per 512B */ + +#define SAMSUNG_5THID_NRPLANE_MASK 0x0C +#define SAMSUNG_5THID_NRPLANE_1 0x00 +#define SAMSUNG_5THID_NRPLANE_2 0x04 +#define SAMSUNG_5THID_NRPLANE_4 0x08 +#define SAMSUNG_5THID_NRPLANE_8 0x0C + +#define SAMSUNG_5THID_PLANESZ_MASK 0x70 +#define SAMSUNG_5THID_PLANESZ_64Mb 0x00 +#define SAMSUNG_5THID_PLANESZ_128Mb 0x10 +#define SAMSUNG_5THID_PLANESZ_256Mb 0x20 +#define SAMSUNG_5THID_PLANESZ_512Mb 0x30 +#define SAMSUNG_5THID_PLANESZ_1Gb 0x40 +#define SAMSUNG_5THID_PLANESZ_2Gb 0x50 +#define SAMSUNG_5THID_PLANESZ_4Gb 0x60 +#define SAMSUNG_5THID_PLANESZ_8Gb 0x70 + +#define SAMSUNG2_5THID_ECCLVL_MASK 0x70 +#define SAMSUNG2_5THID_ECCLVL_1BIT 0x00 +#define SAMSUNG2_5THID_ECCLVL_2BIT 0x10 +#define SAMSUNG2_5THID_ECCLVL_4BIT 0x20 +#define SAMSUNG2_5THID_ECCLVL_8BIT 0x30 +#define SAMSUNG2_5THID_ECCLVL_16BIT 0x40 +#define SAMSUNG2_5THID_ECCLVL_24BIT_1KB 0x50 + + + + +/*--------- END Samsung MLC NAND flashes -----------------------*/ + +//Hynix NAND flashes +#define FLASHTYPE_HYNIX 0xAD +//Hynix HY27(U/S)S(08/16)561A +#define HYNIX_HY27US08561A 0x75 +#define HYNIX_HY27US16561A 0x55 +#define HYNIX_HY27SS08561A 0x35 +#define HYNIX_HY27SS16561A 0x45 +//Hynix HY27(U/S)S(08/16)121A +#define HYNIX_HY27US08121A 0x76 +#define HYNIX_HY27US16121A 0x56 +#define HYNIX_HY27SS08121A 0x36 +#define HYNIX_HY27SS16121A 0x46 +//Hynix HY27(U/S)F(08/16)1G2M +#define HYNIX_HY27UF081G2M 0xF1 +#define HYNIX_HY27UF161G2M 0xC1 +#define HYNIX_HY27SF081G2M 0xA1 +#define HYNIX_HY27SF161G2M 0xAD + +/* This is the new version of HYNIX_HY27UF081G2M . The 2M version is EOL */ +#define HYNIX_HY27UF081G2A 0xF1 + +#define HYNIX_HY27UF082G2A 0xDA + +// #define HYNIX_HY27UF084G2M 0xDC /* replaced by the next one */ +#define HYNIX_HY27U4G8F2D 0xDC + +/* Hynix MLC flashes, same infos as Samsung, except the 5th Byte */ +#define HYNIX_HY27UT088G2A 0xD3 + +/* Hynix MLC flashes, same infos as Samsung, except the 5th Byte */ +#define HYNIX_HY27UAG8T2M 0xD5 /* 14H, B6H, 44H: 3rd,4th,5th ID bytes */ + +/* Number of Planes, same as Samsung */ + +/* Plane Size Type 2 */ +#define HYNIX_5THID_PLANESZ_MASK 0x70 +#define HYNIX_5THID_PLANESZ_512Mb 0x00 +#define HYNIX_5THID_PLANESZ_1Gb 0x10 +#define HYNIX_5THID_PLANESZ_2Gb 0x20 +#define HYNIX_5THID_PLANESZ_4Gb 0x30 +#define HYNIX_5THID_PLANESZ_8Gb 0x40 +#define HYNIX_5THID_PLANESZ_RSVD1 0x50 +#define HYNIX_5THID_PLANESZ_RSVD2 0x60 +#define HYNIX_5THID_PLANESZ_RSVD3 0x70 + +/* Legacy Hynix on H27U4G8F2D */ +/* Plane Size */ +#define HYNIX_5THID_LEG_PLANESZ_MASK 0x70 +#define HYNIX_5THID_LEG_PLANESZ_64Mb 0x00 +#define HYNIX_5THID_LEG_PLANESZ_128Mb 0x10 +#define HYNIX_5THID_LEG_PLANESZ_256Mb 0x20 +#define HYNIX_5THID_LEG_PLANESZ_512Mb 0x30 +#define HYNIX_5THID_LEG_PLANESZ_1Gb 0x40 +#define HYNIX_5THID_LEG_PLANESZ_2Gb 0x50 +#define HYNIX_5THID_LEG_PLANESZ_4Gb 0x60 +#define HYNIX_5THID_LEG_PLANESZ_8Gb 0x70 + + +/*--------- END Hynix MLC NAND flashes -----------------------*/ + +//Micron flashes +#define FLASHTYPE_MICRON 0x2C +//MT29F2G(08/16)AAB +#define MICRON_MT29F2G08AAB 0xDA +#define MICRON_MT29F2G16AAB 0xCA + +#define MICRON_MT29F1G08ABA 0xF1 +#define MICRON_MT29F2G08ABA 0xDA +#define MICRON_MT29F4G08ABA 0xDC + +#define MICRON_MT29F8G08ABA 0x38 +#define MICRON_MT29F16G08ABA 0x48 /* SLC, 2Ch, 48h, 00h, 26h, 89h */ + +#define MICRON_MT29F16G08CBA 0x48 /* MLC, 2Ch, 48h, 04h, 46h, 85h + have same dev ID as the SLC part, bytes 3,4,5 are different however */ + +/* + * Micron M60A & M68A ID encoding are similar to Samsung Type 1. + */ + +#define MICRON_3RDID_INT_CHIPNO_MASK NAND_CI_CHIPNR_MSK + +#define MICRON_3RDID_CELLTYPE_MASK NAND_CI_CELLTYPE_MSK +#define MICRON_3RDID_CELLTYPE_SLC 0x00 +#define MICRON_3RDID_CELLTYPE_4LV 0x04 +//#define MICRON_3RDID_CELLTYPE_8LV 0x08 +//#define MICRON_3RDID_CELLTYPE_16LV 0x0C + + +/* Nbr of simultaneously programmed pages */ +#define MICRON_3RDID_SIMPG_MASK 0x30 +#define MICRON_3RDID_SIMPG_1 0x00 +#define MICRON_3RDID_SIMPG_2 0x10 +//#define MICRON_3RDID_SIM_4 0x20 +//#define MICRON_3RDID_SIM_8 0x30 + +#define MICRON_3RDID_INTERLEAVE 0x40 + +#define MICRON_3RDID_CACHE_PROG 0x80 + +#define MICRON_4THID_PAGESIZE_MASK 0x03 +#define MICRON_4THID_PAGESIZE_1KB 0x00 +#define MICRON_4THID_PAGESIZE_2KB 0x01 +#define MICRON_4THID_PAGESIZE_4KB 0x02 +#define MICRON_4THID_PAGESIZE_8KB 0x03 + +#define MICRON_4THID_OOBSIZE_MASK 0x04 +#define MICRON_4THID_OOBSIZE_8B 0x00 +#define MICRON_4THID_OOBSIZE_16B 0x04 + +#define MICRON_4THID_BLKSIZE_MASK 0x30 +#define MICRON_4THID_BLKSIZE_64KB 0x00 +#define MICRON_4THID_BLKSIZE_128KB 0x10 +#define MICRON_4THID_BLKSIZE_256KB 0x20 +#define MICRON_4THID_BLKSIZE_512KB 0x30 + +/* Required ECC level */ +#define MICRON_5THID_ECCLVL_MASK 0x03 +#define MICRON_5THID_ECCLVL_4BITS 0x02 + +#define MICRON_5THID_NRPLANE_MASK 0x0C +#define MICRON_5THID_NRPLANE_1 0x00 +#define MICRON_5THID_NRPLANE_2 0x04 +#define MICRON_5THID_NRPLANE_4 0x08 +//#define SAMSUNG_5THID_NRPLANE_8 0x0C + +#define MICRON_5THID_PLANESZ_MASK 0x70 +#define MICRON_5THID_PLANESZ_64Mb 0x00 +#define MICRON_5THID_PLANESZ_128Mb 0x10 +#define MICRON_5THID_PLANESZ_256Mb 0x20 +#define MICRON_5THID_PLANESZ_512Mb 0x30 +#define MICRON_5THID_PLANESZ_1Gb 0x40 +#define MICRON_5THID_PLANESZ_2Gb 0x50 +#define MICRON_5THID_PLANESZ_4Gb 0x60 +#define MICRON_5THID_PLANESZ_8Gb 0x70 + +#define MICRON_5THID_INT_ECC_MASK 0x80 +#define MICRON_5THID_INT_ECC_ENA 0x80 + + +/* + * Micron M61A ID encoding will be phased out in favor of ONFI + */ + #define MICRON_M61A_2NDID_VOLTAGE_MASK 0x0F + #define MICRON_M61A_2NDID_3_3V 0x08 + +/* Not strictly followed, must rely on 5th ID byte for density */ +#define MICRON_M61A_2NDID_DENSITY_MASK 0xF0 +#define MICRON_M61A_2NDID_2Gb 0x10 +#define MICRON_M61A_2NDID_4Gb 0x20 +#define MICRON_M61A_2NDID_8Gb 0x30 +#define MICRON_M61A_2NDID_16Gb 0x40 + +/* M61A_3RDID_SLC is same as standard Samsung Type 1 */ +/* M61A_4THID_PAGESIZE same as standard Samsung Type 1 */ + +#define MICRON_M61A_4THID_OOBSIZE_MASK 0x0C +#define MICRON_M61A_4THID_OOBSIZE_28B 0x04 /* 224 per 4KB page */ + +/* Pages per block ==> Block Size */ +#define MICRON_M61A_4THID_PGPBLK_MASK 0x70 +#define MICRON_M61A_4THID_128PG_PERBLK 0x20 /* 128 pages per block =512KB blkSize*/ + +#define MICRON_M61A_4THID_MULTI_LUN_MASK 0x80 +#define MICRON_M61A_4THID_MLUN_SUPPORTED 0x80 /* 128 pages per block */ + + +#define MICRON_M61A_5THID_PLN_PER_LUN_MASK 0x03 +#define MICRON_M61A_5THID_2PLN 0x01 /* 2 planes per LUN */ + +#define MICRON_M61A_5THID_BLK_PER_LUN_MASK 0x1C +#define MICRON_M61A_5THID_2048BLKS 0x04 /* 2048 blks per LUN */ + +//Spansion flashes +#ifndef FLASHTYPE_SPANSION + #define FLASHTYPE_SPANSION 0x01 +#endif +/* Large Page */ +#define SPANSION_S30ML01GP_08 0xF1 //x8 mode +#define SPANSION_S30ML01GP_16 0xC1 //x16 mode +#define SPANSION_S30ML02GP_08 0xDA //x8 mode +#define SPANSION_S30ML02GP_16 0xCA //x16 mode +#define SPANSION_S30ML04GP_08 0xDC //x8 mode +#define SPANSION_S30ML04GP_16 0xCC //x16 mode + +/* Small Page */ +#define SPANSION_S30ML512P_08 0x76 //64MB x8 mode +#define SPANSION_S30ML512P_16 0x56 //64MB x16 mode +#define SPANSION_S30ML256P_08 0x75 //32MB x8 mode +#define SPANSION_S30ML256P_16 0x55 //32MB x16 mode +#define SPANSION_S30ML128P_08 0x73 //x8 mode +#define SPANSION_S30ML128P_16 0x53 //x16 mode + + +/* -------- Toshiba NAND E2PROM -----------------*/ +#define FLASHTYPE_TOSHIBA 0x98 + +#define TOSHIBA_TC58NVG0S3ETA00 0xD1 +#define TOSHIBA_TC58NVG1S3ETAI5 0xDA +#define TOSHIBA_TC58NVG3S0ETA00 0xD3 + +/*---------------------------------------------------------------------------------------*/ + +// Low level MLC test as compared to the high level test in mtd-abi.h +#define NAND_IS_MLC(chip) ((chip)->cellinfo & NAND_CI_CELLTYPE_MSK) + + +//Command Opcode +#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_7_0 +#define OP_PAGE_READ 0x01 +#define OP_SPARE_AREA_READ 0x02 +#define OP_STATUS_READ 0x03 +#define OP_PROGRAM_PAGE 0x04 +#define OP_PROGRAM_SPARE_AREA 0x05 +#define OP_COPY_BACK 0x06 +#define OP_DEVICE_ID_READ 0x07 +#define OP_BLOCK_ERASE 0x08 +#define OP_FLASH_RESET 0x09 +#define OP_BLOCKS_LOCK 0x0A +#define OP_BLOCKS_LOCK_DOWN 0x0B +#define OP_BLOCKS_UNLOCK 0x0C +#define OP_READ_BLOCKS_LOCK_STATUS 0x0D +#define OP_PARAMETER_READ 0x0E +#define OP_PARAMETER_CHANGE_COL 0x0F +#define OP_LOW_LEVEL_OP 0x10 +#else +#define OP_PAGE_READ 0x01000000 +#define OP_SPARE_AREA_READ 0x02000000 +#define OP_STATUS_READ 0x03000000 +#define OP_PROGRAM_PAGE 0x04000000 +#define OP_PROGRAM_SPARE_AREA 0x05000000 +#define OP_COPY_BACK 0x06000000 +#define OP_DEVICE_ID_READ 0x07000000 +#define OP_BLOCK_ERASE 0x08000000 +#define OP_FLASH_RESET 0x09000000 +#define OP_BLOCKS_LOCK 0x0A000000 +#define OP_BLOCKS_LOCK_DOWN 0x0B000000 +#define OP_BLOCKS_UNLOCK 0x0C000000 +#define OP_READ_BLOCKS_LOCK_STATUS 0x0D000000 +#define OP_PARAMETER_READ 0x0E000000 +#define OP_PARAMETER_CHANGE_COL 0x0F000000 +#define OP_LOW_LEVEL_OP 0x10000000 +#endif + +//NAND flash controller +#define NFC_FLASHCACHE_SIZE 512 + +#if CONFIG_MTD_BRCMNAND_VERSION <= CONFIG_MTD_BRCMNAND_VERS_3_2 +#define BCHP_NAND_LAST_REG BCHP_NAND_BLK_WR_PROTECT + +#elif CONFIG_MTD_BRCMNAND_VERSION <= CONFIG_MTD_BRCMNAND_VERS_3_3 + #ifdef BCHP_NAND_TIMING_2_CS3 +#define BCHP_NAND_LAST_REG BCHP_NAND_TIMING_2_CS3 + #else +#define BCHP_NAND_LAST_REG BCHP_NAND_TIMING_2_CS2 + #endif +#elif CONFIG_MTD_BRCMNAND_VERSION <= CONFIG_MTD_BRCMNAND_VERS_5_0 +#define BCHP_NAND_LAST_REG BCHP_NAND_SPARE_AREA_READ_OFS_1C +#else +#define BCHP_NAND_LAST_REG BCHP_NAND_SPARE_AREA_WRITE_OFS_1C +#endif + +#define BRCMNAND_CTRL_REGS (BCHP_NAND_REVISION) +#define BRCMNAND_CTRL_REGS_END (BCHP_NAND_LAST_REG) + + +/** + * brcmnand_state_t - chip states + * Enumeration for BrcmNAND flash chip state + */ +typedef enum { + BRCMNAND_FL_READY = FL_READY, + BRCMNAND_FL_READING = FL_READING, + BRCMNAND_FL_WRITING = FL_WRITING, + BRCMNAND_FL_ERASING = FL_ERASING, + BRCMNAND_FL_SYNCING = FL_SYNCING, + BRCMNAND_FL_CACHEDPRG = FL_CACHEDPRG, + BRCMNAND_FL_UNLOCKING = FL_UNLOCKING, + BRCMNAND_FL_LOCKING = FL_LOCKING, + BRCMNAND_FL_RESETING = FL_RESETING, + BRCMNAND_FL_OTPING = FL_OTPING, + BRCMNAND_FL_PM_SUSPENDED = FL_PM_SUSPENDED, + BRCMNAND_FL_EXCLUSIVE = FL_UNKNOWN+10, // Exclusive access to NOR flash, prevent all NAND accesses. + BRCMNAND_FL_XIP, // Exclusive access to XIP part of the flash +} brcmnand_state_t; + +//#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_3_0 +/* + * ECC levels, corresponding to BCHP_NAND_ACC_CONTROL_ECC_LEVEL + */ +typedef enum { + BRCMNAND_ECC_DISABLE = 0u, + BRCMNAND_ECC_BCH_1 = 1u, + BRCMNAND_ECC_BCH_2 = 2u, + BRCMNAND_ECC_BCH_3 = 3u, + BRCMNAND_ECC_BCH_4 = 4u, + BRCMNAND_ECC_BCH_5 = 5u, + BRCMNAND_ECC_BCH_6 = 6u, + BRCMNAND_ECC_BCH_7 = 7u, + BRCMNAND_ECC_BCH_8 = 8u, + BRCMNAND_ECC_BCH_9 = 9u, + BRCMNAND_ECC_BCH_10 = 10u, + BRCMNAND_ECC_BCH_11 = 11u, + BRCMNAND_ECC_BCH_12 = 12u, + BRCMNAND_ECC_RESVD_1 = 13u, + BRCMNAND_ECC_RESVD_2 = 14u, + BRCMNAND_ECC_HAMMING = 15u, +} brcmnand_ecc_level_t; + +/* + * Number of required ECC bytes per 512B slice + */ +#if CONFIG_MTD_BRCMNAND_VERSION < CONFIG_MTD_BRCMNAND_VERS_7_0 +static const unsigned int brcmnand_eccbytes[16] = { + [BRCMNAND_ECC_DISABLE] = 0, + [BRCMNAND_ECC_BCH_1] = 2, + [BRCMNAND_ECC_BCH_2] = 4, + [BRCMNAND_ECC_BCH_3] = 5, + [BRCMNAND_ECC_BCH_4] = 7, + [BRCMNAND_ECC_BCH_5] = 9, + [BRCMNAND_ECC_BCH_6] = 10, + [BRCMNAND_ECC_BCH_7] = 12, + [BRCMNAND_ECC_BCH_8] = 13, + [BRCMNAND_ECC_BCH_9] = 15, + [BRCMNAND_ECC_BCH_10] = 17, + [BRCMNAND_ECC_BCH_11] = 18, + [BRCMNAND_ECC_BCH_12] = 20, + [BRCMNAND_ECC_RESVD_1] = 0, + [BRCMNAND_ECC_RESVD_2] = 0, + [BRCMNAND_ECC_HAMMING] = 3, +}; +#else +static const unsigned int brcmnand_eccbytes[16] = { + [BRCMNAND_ECC_DISABLE] = 0, + [BRCMNAND_ECC_BCH_1] = 2, + [BRCMNAND_ECC_BCH_2] = 4, + [BRCMNAND_ECC_BCH_3] = 6, + [BRCMNAND_ECC_BCH_4] = 7, + [BRCMNAND_ECC_BCH_5] = 9, + [BRCMNAND_ECC_BCH_6] = 11, + [BRCMNAND_ECC_BCH_7] = 13, + [BRCMNAND_ECC_BCH_8] = 14, + [BRCMNAND_ECC_BCH_9] = 16, + [BRCMNAND_ECC_BCH_10] = 18, + [BRCMNAND_ECC_BCH_11] = 20, + [BRCMNAND_ECC_BCH_12] = 21, + [BRCMNAND_ECC_RESVD_1] = 0, + [BRCMNAND_ECC_RESVD_2] = 0, + [BRCMNAND_ECC_HAMMING] = 3, +}; + +#endif + +//#endif + +/* For backaward compatiblity. BRCM NAND driver still rely on the old nand_buffers structure + which is changed to use dynamic buf alloction in 3.16 kernel. So copy the old nand_buffer + definition here */ + +/* + * This constant declares the max. oobsize / page, which + * is supported now. If you add a chip with bigger oobsize/page + * adjust this accordingly. + */ +#define NAND_MAX_OOBSIZE 576 +#define NAND_MAX_PAGESIZE 8192 + +/** + * struct nand_buffers - buffer structure for read/write + * @ecccalc: buffer for calculated ECC + * @ecccode: buffer for ECC read from flash + * @databuf: buffer for data - dynamically sized + * + * Do not change the order of buffers. databuf and oobrbuf must be in + * consecutive order. + */ +struct brcmnand_buffers { + uint8_t ecccalc[NAND_MAX_OOBSIZE]; + uint8_t ecccode[NAND_MAX_OOBSIZE]; + uint8_t databuf[NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE]; +}; + +/** + * struct brcmnand_chip - BrcmNAND Private Flash Chip Data + * @param base [BOARDSPECIFIC] address to access Broadcom NAND controller + * @param chipsize [INTERN] the size of one chip for multichip arrays + * @param device_id [INTERN] device ID + * @param verstion_id [INTERN] version ID + * @param options [BOARDSPECIFIC] various chip options. They can partly be set to inform brcmnand_scan about + * @param erase_shift [INTERN] number of address bits in a block + * @param page_shift [INTERN] number of address bits in a page + * @param ppb_shift [INTERN] number of address bits in a pages per block + * @param page_mask [INTERN] a page per block mask + * @cellinfo: [INTERN] MLC/multichip data from chip ident + * @param readw [REPLACEABLE] hardware specific function for read short + * @param writew [REPLACEABLE] hardware specific function for write short + * @param command [REPLACEABLE] hardware specific function for writing commands to the chip + * @param wait [REPLACEABLE] hardware specific function for wait on ready + * @param read_bufferram [REPLACEABLE] hardware specific function for BufferRAM Area + * @param write_bufferram [REPLACEABLE] hardware specific function for BufferRAM Area + * @param read_word [REPLACEABLE] hardware specific function for read register of BrcmNAND + * @param write_word [REPLACEABLE] hardware specific function for write register of BrcmNAND + * @param scan_bbt [REPLACEALBE] hardware specific function for scaning Bad block Table + * @param chip_lock [INTERN] spinlock used to protect access to this structure and the chip + * @param wq [INTERN] wait queue to sleep on if a BrcmNAND operation is in progress + * @param state [INTERN] the current state of the BrcmNAND device + * @param autooob [REPLACEABLE] the default (auto)placement scheme + * @param bbm [REPLACEABLE] pointer to Bad Block Management + * @param priv [OPTIONAL] pointer to private chip date + */ + +/* + * Global members, shared by all ChipSelect, one per controller + */ +struct brcmnand_ctrl { + spinlock_t chip_lock; + //atomic_t semCount; // Used to lock out NAND access for NOR, TBD + wait_queue_head_t wq; + brcmnand_state_t state; + + struct brcmnand_buffers* buffers; // THT 2.6.18-5.3: Changed to pointer to accomodate EDU +#define BRCMNAND_OOBBUF(pbuf) (&((pbuf)->databuf[NAND_MAX_PAGESIZE])) + + unsigned int numchips; // Always 1 in v0.0 and 0.1, up to 8 in v1.0+ + int CS[MAX_NAND_CS]; // Value of CS selected one per chip, in ascending order of chip Select (enforced).. + // Say, user uses CS0, CS2, and CS5 for NAND, then the first 3 entries + // have the values 0, 2 and 5, and numchips=3. +}; + +struct brcmnand_chip { + + + /* Shared by all Chip select */ + struct brcmnand_ctrl* ctrl; + + /* + * Private members, + * + */ + //unsigned long regs; /* Register page */ + unsigned char __iomem *vbase; /* Virtual address of start of flash */ + unsigned long pbase; // Physical address of vbase + unsigned long device_id; + + //THT: In BrcmNAND, the NAND controller keeps track of the 512B Cache + // so there is no need to manage the buffer ram. + //unsigned int bufferram_index; + //struct brcmnand_bufferram bufferram; + + int (*command)(struct mtd_info *mtd, int cmd, loff_t address, size_t len); + int (*wait)(struct mtd_info *mtd, int state, uint32_t* pStatus, int timeout ); + + unsigned short (*read_word)(void __iomem *addr); + void (*write_word)(unsigned short value, void __iomem *addr); + + // THT: Sync Burst Read, not supported. + //void (*mmcontrol)(struct mtd_info *mtd, int sync_read); + + // Private methods exported from BBT + int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip); + int (*block_markbad)(struct mtd_info *mtd, loff_t ofs); + int (*scan_bbt)(struct mtd_info *mtd); + int (*erase_bbt)(struct mtd_info *mtd, struct erase_info *instr, int allowbbt, int doNotUseBBT); + + uint32_t (*ctrl_read) (uintptr_t command); + void (*ctrl_write) (uintptr_t command, uint32_t val); + uint32_t (*ctrl_writeAddr)(struct brcmnand_chip* chip, loff_t addr, int cmdEndAddr); + + /* + * THT: Private methods exported to BBT, equivalent to the methods defined in struct ecc_nand_ctl + * The caller is responsible to hold locks before calling these routines + * Input and output buffers __must__ be aligned on a DW boundary (enforced inside the driver). + * EDU may require that the buffer be aligned on a 512B boundary. + */ + int (*read_page)(struct mtd_info *mtd, + uint8_t *outp_buf, uint8_t* outp_oob, uint64_t page); + int (*write_page)(struct mtd_info *mtd, + const uint8_t *inp_buf, const uint8_t* inp_oob, uint64_t page); + int (*read_page_oob)(struct mtd_info *mtd, uint8_t* outp_oob, uint64_t page); + int (*write_page_oob)(struct mtd_info *mtd, const uint8_t* inp_oob, uint64_t page, int isMarkBadBlock); + + int (*write_is_complete)(struct mtd_info *mtd, int* outp_needBBT); + + /* + * THT: Same as the mtd calls with same name, except that locks are + * expected to be already held by caller. Mostly used by BBT codes + */ + int (*read_oob) (struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops); + int (*write_oob) (struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops); + + uint64_t chipSize; + + int directAccess; // For v1,0+, use directAccess or EBI address + int xor_disable; // Value of !NAND_CS_NAND_XOR:00 + int csi; /* index into the CS array. chip->CS[chip->csi] yield the value of HW ChipSelect */ + + unsigned int chip_shift; // How many bits shold be shifted. + uint64_t mtdSize; // Total size of NAND flash, 64 bit integer for V1.0. This supercedes mtd->size which is + // currently defined as a uint32_t. + + /* THT Added */ + unsigned int busWidth, pageSize, blockSize; /* Actually page size from chip, as reported by the controller */ + + unsigned int erase_shift; + unsigned int page_shift; + int phys_erase_shift; + int bbt_erase_shift; + //unsigned int ppb_shift; /* Pages per block shift */ + unsigned int page_mask; + //int subpagesize; + uint8_t cellinfo; + uint8_t nop; + + //u_char* data_buf; // Replaced by buffers + //u_char* oob_buf; + int oobdirty; + uint8_t* data_poi; + uint8_t* oob_poi; + unsigned int options; + int badblockpos; + + //unsigned long chipsize; + int pagemask; + int64_t pagebuf; /* Cached page number. This can be a 36 bit signed integer. + * -1LL denotes NULL/invalidated page cache. */ + int oobavail; // Number of free bytes per page + int disableECC; /* Turn on for 100% valid chips that don't need ECC + * might need in future for Spansion flash */ + + struct nand_ecclayout *ecclayout; + + + int reqEccLevel; /* Required ECC level, from chipID string (Samsung Type 2, Micron) + * or from datasheet otherwise */ + + // THT Used in lieu of struct nand_ecc_ctrl ecc; + brcmnand_ecc_level_t ecclevel; // Actual ECC scheme used, must be >= reqEccLevel + int ecctotal; // total number of ECC bytes per page, 3 for Small pages, 12 for large pages. + int eccsize; // Size of the ECC block, always 512 for Brcm Nand Controller + int eccbytes; // How many bytes are used for ECC per eccsize (3 for Hamming) + int eccsteps; // How many ECC block per page (4 for 2K page, 1 for 512B page, 8 for 4K page etc... + int eccOobSize; // # of oob byte per ECC step, mostly 16, 27 for BCH-8 + + int eccSectorSize; // Sector size, not necessarily 512B for new flashes + + + //struct nand_hw_control hwcontrol; + + struct mtd_oob_ops ops; + + + uint8_t *bbt; + uint32_t bbtSize; + int (*isbad_bbt)(struct mtd_info *mtd, loff_t ofs, int allowbbt); + struct nand_bbt_descr *bbt_td; + struct nand_bbt_descr *bbt_md; + struct nand_bbt_descr *badblock_pattern; +#ifdef CONFIG_MTD_BRCMNAND_CORRECTABLE_ERR_HANDLING + struct brcmnand_cet_descr *cet; /* CET descriptor */ +#endif + + void *priv; +}; + +#ifdef CONFIG_MTD_BRCMNAND_CORRECTABLE_ERR_HANDLING + +#define BRCMNAND_CET_DISABLED 0x01 /* CET is disabled due to a serious error */ +#define BRCMNAND_CET_LAZY 0x02 /* Reload CET when needed */ +#define BRCMNAND_CET_LOADED 0x04 /* CET is in memory */ +/* + * struct brcmnand_cet_descr - Correctable Error Table (CET) descriptor + * @offs Offset in OOB where the CET signature begins + * @len Length (in bytes) of the CET signature + * @startblk Block address starting where CET begins + * @sign Growth of CET (top->down or down->top) + * Inverse direct of BBT's sign + * @flags Status of CET disabled/lazy/loaded + * @cerr_count Total correctable errors encountered so far + * @numblks Number of blocks that CET spans + * @maxblks Maximum blocks that CET can have 2*numblks + * @brcmnand_cet_memtable Pointer to in-memory CET + * @pattern Identifier used to recognize CET + * @cet_flush Kernel work queue to handle flush of in-mem + * CET to the flash + */ +struct brcmnand_cet_descr { + uint8_t offs; + uint8_t len; + int startblk; + char sign; /* 1 => bottom->top -1 => top->bottom - inverse of BBT */ + char flags; + uint32_t cerr_count; + int numblks; + int maxblks; + struct brcmnand_cet_memtable *memtbl; + char *pattern; + struct mtd_info *mtd; + struct delayed_work cet_flush; +}; + +/* + * Copy of the CET in memory for faster access and easy rewrites + * @isdirty dirty = true => flush data to flash + * @blk the physical block# (flash) that this bitvec belongs to + * @bitvec pointer to one block (blk#) of data + */ +struct brcmnand_cet_memtable { + char isdirty; + int blk; + char *bitvec; +}; +#endif + + +/* + * Options bits + */ +#define BRCMNAND_CONT_LOCK (0x0001) + + +//extern void brcmnand_prepare_reboot(void); + +/* + * @ mtd The MTD interface handle from opening "/dev/mtd" or "/dev/mtdblock" + * @ buff Buffer to hold the data read from the NOR flash, must be able to hold len bytes, and aligned on + * word boundary. + * @ offset Offset of the data from CS0 (on NOR flash), must be on word boundary. + * @ len Number of bytes to be read, must be even number. + * + * returns 0 on success, negative error codes on failure. + * + * The caller thread may block until access to the NOR flash can be granted. + * Further accesses to the NAND flash (from other threads) will be blocked until this routine returns. + * The routine performs the required swapping of CS0/CS1 under the hood. + */ +extern int brcmnand_readNorFlash(struct mtd_info *mtd, void* buff, unsigned int offset, int len); + +#if (CONFIG_BRCMNAND_MAJOR_VERS == 7) +#include "bchp_nand_7x.h" +#elif (CONFIG_BRCMNAND_MAJOR_VERS == 4) +#include "bchp_nand_40.h" +#elif (CONFIG_BRCMNAND_MAJOR_VERS == 2) +#include "bchp_nand_21_22.h" +#endif + +#endif +#endif diff -urN linux-4.1.52-orig/include/linux/mtd/mtd64.h linux-4.1.52/include/linux/mtd/mtd64.h --- linux-4.1.52-orig/include/linux/mtd/mtd64.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/mtd/mtd64.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,330 @@ +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +/* + * drivers/mtd/mtd64.h + * + <:copyright-BRCM:2012:DUAL/GPL:standard + + Copyright (c) 2012 Broadcom + All Rights Reserved + + Unless you and Broadcom execute a separate written software license + agreement governing use of this software, this software is licensed + to you under the terms of the GNU General Public License version 2 + (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, + with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + + Not withstanding the above, under no circumstances may you combine + this software in any way with any other Broadcom software provided + under a license other than the GPL, without Broadcom's express prior + written consent. + + :> + * + * Data structures for MTD 64 bit ops (Borrowed heavily from broadcom NAND + * controller brcmnand_priv.h) + * + * when who what + * 20080805 sidc Original coding + */ + +#ifndef _MTD64_H_ +#define _MTD64_H_ + +#include + +/* + * 64 bit arithmetics + */ +#include +#include + +#define LONGLONG_TO_BITS (sizeof(uint64_t)*BITS_PER_UNIT) + +/* + * Create a 64 bit number out of a 32 bit + */ +static inline int64_t mtd64_constructor(long hi, unsigned long low) +{ + DIunion ull; + + ull.s.high = hi; + ull.s.low = low; + + return ull.ll; +} + + +/* + * Allow inline printing of 64 bit integer values + */ +static inline char *mtd64_sprintf(char* msg, int64_t offset) +{ + DIunion llw; + + llw.ll = offset; + sprintf(msg, "%08x:%08x", llw.s.high, llw.s.low); + return msg; +} + +static inline int mtd64_is_positive(int64_t ll) +{ + DIunion u; + + u.ll = ll; + return ((int) u.s.high > 0 || (((int) u.s.high) == 0 && ((unsigned int) u.s.low) > 0)); +} + +static inline int mtd64_is_positiveorzero(int64_t ll) +{ + DIunion u; + + u.ll = ll; + return ((int) u.s.high >= 0 || (((int) u.s.high) == 0 && ((unsigned int) u.s.low) >= 0)); +} + +/* + * Returns low DWord + */ +static inline uint32_t mtd64_ll_low(int64_t ll) +{ + DIunion ull; + + ull.ll = ll; + return (uint32_t) ull.s.low; +} + +/* + * Returns high DWord + */ +static inline int32_t mtd64_ll_high(int64_t ll) +{ + DIunion ull; + + ull.ll = ll; + return (int32_t) ull.s.high; +} + +static inline int mtd64_ll_ffs(uint64_t ll) +{ + DIunion ull; + int res; + + ull.ll = ll; + res = ffs(ull.s.low); + if (res) + return res; + res = ffs(ull.s.high); + return (32 + res); +} + +#if 0 +/* + * Returns (ll >> shift) + */ +static inline uint64_t mtd64_rshft(uint64_t ll, int shift) +{ + DIunion src, res; + + src.ll = ll; + bitmap_shift_right((unsigned long*) &res, (unsigned long*) &src, shift, LONGLONG_TO_BITS); + return res.ll; +} +#define mtd64_rshft32(ll,s) mtd64_rshft(ll, s) + +/* + * Returns (ul << shift) with ul a 32-bit unsigned integer. Returned value is a 64bit integer + */ +static inline uint64_t mtd64_lshft32(uint64_t ll, int shift) +{ + DIunion src, res; + + src.ll = ll; + bitmap_shift_left((unsigned long*) &res, (unsigned long*) &src, shift, LONGLONG_TO_BITS); + return res.ll; +} + +/* + * returns (left + right) + */ +static inline int64_t mtd64_add(int64_t left, int64_t right) +{ + DIunion l, r, sum; + + l.ll = left; + r.ll = right; + + add_ssaaaa(sum.s.high, sum.s.low, l.s.high, l.s.low, r.s.high, r.s.low); + return sum.ll; +} + +/* + * returns (left + right), with right being a 32-bit integer + */ +static inline int64_t mtd64_add32(int64_t left, int right) +{ + DIunion l, r, sum; + + l.ll = left; + r.s.high = 0; + r.s.low = right; + + add_ssaaaa(sum.s.high, sum.s.low, l.s.high, l.s.low, r.s.high, r.s.low); + return sum.ll; +} + +/* + * returns (left - right) + */ +static inline int64_t mtd64_sub(int64_t left, int64_t right) +{ + DIunion l, r, diff; + + l.ll = left; + r.ll = right; + + sub_ddmmss(diff.s.high, diff.s.low, l.s.high, l.s.low, r.s.high, r.s.low); + return diff.ll; +} + +/* + * returns (left - right) + */ +static inline int64_t mtd64_sub32(int64_t left, int right) +{ + DIunion l, r, diff; + + l.ll = left; + r.s.low = right; + r.s.high = 0; + + sub_ddmmss(diff.s.high, diff.s.low, l.s.high, l.s.low, r.s.high, r.s.low); + return diff.ll; +} + +static inline int mtd64_notequals(int64_t left, int64_t right) +{ + DIunion l, r; + + l.ll = left; + r.ll = right; + + if (l.s.high == r.s.high && l.s.low == r.s.low) + return 0; + return 1; +} + +static inline int mtd64_equals(int64_t left, int64_t right) +{ + DIunion l, r; + + l.ll = left; + r.ll = right; + + if (l.s.high == r.s.high && l.s.low == r.s.low) + return 1; + return 0; +} + +static inline int mtd64_is_greater(int64_t left, int64_t right) +{ + return mtd64_is_positive(mtd64_sub(left, right)); +} + +static inline int mtd64_is_gteq(int64_t left, int64_t right) +{ + return mtd64_is_positiveorzero(mtd64_sub(left, right)); +} + +static inline int mtd64_is_less(int64_t left, int64_t right) +{ + return mtd64_is_positive(mtd64_sub(right, left)); +} + +static inline int mtd64_is_lteq(int64_t left, int64_t right) +{ + return mtd64_is_positiveorzero(mtd64_sub(right, left)); +} + +/* + * Returns (left & right) + */ +static inline uint64_t mtd64_and(uint64_t left, uint64_t right) +{ + uint64_t res; + bitmap_and((unsigned long*) &res, (unsigned long*) &left, (unsigned long*) &right, LONGLONG_TO_BITS); + return res; +} + +static inline uint64_t mtd64_or(uint64_t left, uint64_t right) +{ + uint64_t res; + bitmap_or((unsigned long *) &res, (unsigned long *) &left, (unsigned long *) &right, LONGLONG_TO_BITS); + return res; +} + +/* + * Multiply 2 32-bit integer, result is 64bit + */ +static inline uint64_t mtd64_mul(unsigned int left, unsigned int right) +{ + DIunion llw; + + umul_ppmm(llw.s.high, llw.s.low, left, right); + return llw.ll; +} + +/* + * res Result + * high:low u64 bit + * base Divisor + * rem Remainder + */ +#define do_mtd_div64_32(res, high, low, base, rem) ({ \ + unsigned long __quot, __mod; \ + unsigned long __cf, __tmp, __tmp2, __i; \ + \ + __asm__(".set push\n\t" \ + ".set noat\n\t" \ + ".set noreorder\n\t" \ + "move %2, $0\n\t" \ + "move %3, $0\n\t" \ + "b 1f\n\t" \ + " li %4, 0x21\n" \ + "0:\n\t" \ + "sll $1, %0, 0x1\n\t" \ + "srl %3, %0, 0x1f\n\t" \ + "or %0, $1, %5\n\t" \ + "sll %1, %1, 0x1\n\t" \ + "sll %2, %2, 0x1\n" \ + "1:\n\t" \ + "bnez %3, 2f\n\t" \ + " sltu %5, %0, %z6\n\t" \ + "bnez %5, 3f\n" \ + "2:\n\t" \ + " addiu %4, %4, -1\n\t" \ + "subu %0, %0, %z6\n\t" \ + "addiu %2, %2, 1\n" \ + "3:\n\t" \ + "bnez %4, 0b\n\t" \ + " srl %5, %1, 0x1f\n\t" \ + ".set pop" \ + : "=&r" (__mod), "=&r" (__tmp), "=&r" (__quot), "=&r" (__cf), \ + "=&r" (__i), "=&r" (__tmp2) \ + : "Jr" (base), "0" (high), "1" (low)); \ + \ + (res) = __quot; \ + (rem) = __mod; \ + __quot; }) + +#endif +#endif +#endif // CONFIG_BCM_KF_MTD_BCMNAND diff -urN linux-4.1.52-orig/include/linux/mtd/nand.h linux-4.1.52/include/linux/mtd/nand.h --- linux-4.1.52-orig/include/linux/mtd/nand.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/mtd/nand.h 2020-02-07 01:47:54.000000000 +0800 @@ -26,6 +26,9 @@ struct mtd_info; struct nand_flash_dev; +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +struct device_node; +#endif /* Scan and identify a NAND device */ extern int nand_scan(struct mtd_info *mtd, int max_chips); /* @@ -188,6 +191,14 @@ */ #define NAND_USE_BOUNCE_BUFFER 0x00100000 +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +/* For Hynix MLC flashes, the BI are written to last and (last-2) pages. */ +#define NAND_SCAN_BI_3RD_PAGE 0x10000000 + +/* NOP=1 NAND SLC device */ +#define NAND_PAGE_NOP1 0x20000000 +#endif + /* Options set by nand scan */ /* Nand scan has allocated controller struct */ #define NAND_CONTROLLER_ALLOC 0x80000000 @@ -643,6 +654,9 @@ struct nand_chip { void __iomem *IO_ADDR_R; void __iomem *IO_ADDR_W; +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + struct device_node *dn; +#endif uint8_t (*read_byte)(struct mtd_info *mtd); u16 (*read_word)(struct mtd_info *mtd); @@ -690,6 +704,13 @@ uint16_t ecc_strength_ds; uint16_t ecc_step_ds; int onfi_timing_mode_default; +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + /* before ONFI auto timing adjustment in kernel, add Broadcom specific timing + * parameters for better performnace. + */ + uint32_t timing_1; + uint32_t timing_2; +#endif int badblockpos; int badblockbits; @@ -738,6 +759,7 @@ #define NAND_MFR_INTEL 0x89 #define NAND_MFR_ATO 0x9b + /* The maximum expected count of bytes in the NAND ID sequence */ #define NAND_MAX_ID_LEN 8 @@ -818,6 +840,13 @@ uint16_t step_ds; } ecc; int onfi_timing_mode_default; +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + /* before ONFI auto timing adjustment in kernel, add Broadcom specific timing + * parameters for better performnace. + */ + uint32_t timing_1; + uint32_t timing_2; +#endif }; /** diff -urN linux-4.1.52-orig/include/linux/nbuff.h linux-4.1.52/include/linux/nbuff.h --- linux-4.1.52-orig/include/linux/nbuff.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/nbuff.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,1857 @@ +#if defined(CONFIG_BCM_KF_NBUFF) + +#ifndef __NBUFF_H_INCLUDED__ +#define __NBUFF_H_INCLUDED__ + + +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + +/* + ******************************************************************************* + * + * File Name : nbuff.h + * Description: Definition of a network buffer to support various forms of + * network buffer, to include Linux socket buff (SKB), lightweight + * fast kernel buff (FKB), BRCM Free Pool buffer (FPB), and traffic + * generator support buffer (TGB) + * + * nbuff.h may also be used to provide an interface to common APIs + * available on other OS (in particular BSD style mbuf). + * + * Common APIs provided: pushing, pulling, reading, writing, cloning, freeing + * + * Implementation Note: + * + * One may view NBuff as a base class from which other buff types are derived. + * Examples of derived network buffer types are sk_buff, fkbuff, fpbuff, tgbuff + * + * A pointer to a buffer is converted to a pointer to a special (derived) + * network buffer type by encoding the type into the least significant 2 bits + * of a word aligned buffer pointer. pBuf points to the real network + * buffer and pNBuff refers to pBuf ANDed with the Network Buffer Type. + * C++ this pointer to a virtual class (vtable based virtual function thunks). + * + * Thunk functions to redirect the calls to the appropriate buffer type, e.g. + * SKB or FKB uses the Network Buffer Pointer type information. + * + * This file also implements the Fast Kernel Buffer API. The fast kernel buffer + * carries a minimal context of the received buffer and associated buffer + * recycling information. + * + ******************************************************************************* */ + +#include +#include +#include /* include ISO C99 inttypes.h */ +#include /* include corresponding BSD style mbuf */ +#include +#include /*TODO rename this file as bcm_net.h as it's not specific to blog */ +#include +#include + +#define NBUFF_VERSION "v1.0" + +/* Engineering Constants for Fast Kernel Buffer Global Pool (used for clones) */ +#define SUPPORT_FKB_EXTEND +#if defined(CONFIG_BCM_KF_WL) +#define FKBC_POOL_SIZE_ENGG (2080) /*1280 more to be allocated for wireless*/ +#else +#define FKBC_POOL_SIZE_ENGG 800 +#endif +#define FKBC_EXTEND_SIZE_ENGG 32 /* Number of FkBuf_t per extension*/ +#define FKBC_EXTEND_MAX_ENGG 16 /* Maximum extensions allowed */ + +#define FKBM_POOL_SIZE_ENGG 128 +#define FKBM_EXTEND_SIZE_ENGG 2 +#define FKBM_EXTEND_MAX_ENGG 200 /* Assuming one unshare */ + +/* + * Network device drivers ported to NBUFF must ensure that the headroom is at + * least 186 bytes in size. Remove this dependancy (TBD). + */ +// #define CC_FKB_HEADROOM_AUDIT + +/* Conditional compile of FKB functional APIs as inlined or non-inlined */ +#define CC_CONFIG_FKB_FN_INLINE +#ifdef CC_CONFIG_FKB_FN_INLINE +#define FKB_FN(fn_name, fn_signature, body) \ +static inline fn_signature { body; } /* APIs inlined in header file */ +#else +#ifdef FKB_IMPLEMENTATION_FILE +#define FKB_FN(fn_name, fn_signature, body) \ +fn_signature { body; } \ +EXPORT_SYMBOL(fn_name); /* APIs declared in implementation */ +#else +#define FKB_FN(fn_name, fn_signature, body) \ +extern fn_signature; +#endif /* !defined(FKB_IMPLEMENTATION_FILE) */ +#endif /* !defined(FKB_FN) */ + +/* LAB ONLY: Design development */ +// #define CC_CONFIG_FKB_STATS +// #define CC_CONFIG_FKB_COLOR +// #define CC_CONFIG_FKB_DEBUG +// #define CC_CONFIG_FKB_AUDIT +// #define CC_CONFIG_FKB_STACK + +// #include /* smp_processor_id() CC_CONFIG_FKB_AUDIT */ + +#if defined(CC_CONFIG_FKB_STATS) +#define FKB_STATS(stats_code) do { stats_code } while(0) +#else +#define FKB_STATS(stats_code) NULL_STMT +#endif + +#if defined(CC_CONFIG_FKB_STACK) +extern void dump_stack(void); +#define DUMP_STACK() dump_stack() +#else +#define DUMP_STACK() NULL_STMT +#endif + +#if defined(CC_CONFIG_FKB_AUDIT) +#define FKB_AUDIT(audit_code) do { audit_code } while(0) +#else +#define FKB_AUDIT(audit_code) NULL_STMT +#endif + +extern int nbuff_dbg; +#if defined(CC_CONFIG_FKB_DEBUG) +#define fkb_dbg(lvl, fmt, arg...) \ + if (nbuff_dbg >= lvl) printk( "FKB %s :" fmt "[<%p>]\n", \ + __FUNCTION__, ##arg, __builtin_return_address(0) ) +#define FKB_DBG(debug_code) do { debug_code } while(0) +#else +#define fkb_dbg(lvl, fmt, arg...) do {} while(0) +#define FKB_DBG(debug_code) NULL_STMT +#endif + +#define CC_NBUFF_FLUSH_OPTIMIZATION + +/* CACHE OPERATIONS */ +#define FKB_CACHE_FLUSH 0 +#define FKB_CACHE_INV 1 + +/* OS Specific Section Begin */ +#if defined(__KERNEL__) /* Linux MIPS Cache Specific */ +/* + *------------------------------------------------------------------------------ + * common cache operations: + * + * - addr is rounded down to the cache line + * - end is rounded up to cache line. + * + * - if ((addr == end) and (addr was cache aligned before rounding)) + * no operation is performed. + * else + * flush data cache line UPTO but NOT INCLUDING rounded up end. + * + * Note: + * if before rounding, (addr == end) AND addr was not cache aligned, + * we would flush at least one line. + * + * Uses: L1_CACHE_BYTES + *------------------------------------------------------------------------------ + */ +#include +#ifdef CONFIG_MIPS +#include +#endif /* CONFIG_MIPS */ + +extern void cache_flush_data_len(void *addr, int len); + +/* + * Macros to round down and up, an address to a cachealigned address + */ +#define ADDR_ALIGN_DN(addr, align) ( (addr) & ~((align) - 1) ) +#define ADDR_ALIGN_UP(addr, align) ( ((addr) + (align) - 1) & ~((align) - 1) ) + +#ifdef CONFIG_MIPS +/* + *------------------------------------------------------------------------------ + * Function : cache_flush_region + * Description: + * Writeback flush, then invalidate a region demarcated by addr to end. + * Cache line following rounded up end is not flushed. + *------------------------------------------------------------------------------ + */ +static inline void cache_flush_region(void *addr, void *end) +{ + uintptr_t a = ADDR_ALIGN_DN( (uintptr_t)addr, L1_CACHE_BYTES ); + uintptr_t e = ADDR_ALIGN_UP( (uintptr_t)end, L1_CACHE_BYTES ); + while ( a < e ) + { + flush_dcache_line(a); /* Hit_Writeback_Inv_D */ + a += L1_CACHE_BYTES; /* next cache line base */ + } +} + +/* + *------------------------------------------------------------------------------ + * Function : cache_flush_len + * Description: + * Writeback flush, then invalidate a region given an address and a length. + * The demarcation end is computed by applying length to address before + * rounding down address. End is rounded up. + * Cache line following rounded up end is not flushed. + *------------------------------------------------------------------------------ + */ +static inline void cache_flush_len(void *addr, int len) +{ + uintptr_t a = ADDR_ALIGN_DN( (uintptr_t)addr, L1_CACHE_BYTES ); + uintptr_t e = ADDR_ALIGN_UP( ((uintptr_t)addr + len), + L1_CACHE_BYTES ); + while ( a < e ) + { + flush_dcache_line(a); /* Hit_Writeback_Inv_D */ + a += L1_CACHE_BYTES; /* next cache line base */ + } +} + +static inline void fpm_cache_flush_len(void *addr, int len) +{ + /* No global coherency support for MIPS, always perform flush */ + cache_flush_len(addr, len); +} + +/* + *------------------------------------------------------------------------------ + * Function : cache_invalidate_region + * Description: + * invalidate a region demarcated by addr to end. + * Cache line following rounded up end is not invalidateed. + *------------------------------------------------------------------------------ + */ +static inline void cache_invalidate_region(void *addr, void *end) +{ + uintptr_t a = ADDR_ALIGN_DN( (uintptr_t)addr, L1_CACHE_BYTES ); + uintptr_t e = ADDR_ALIGN_UP( (uintptr_t)end, L1_CACHE_BYTES ); + while ( a < e ) + { + invalidate_dcache_line(a); /* Hit_Invalidate_D */ + a += L1_CACHE_BYTES; /* next cache line base */ + } +} + +/* + *------------------------------------------------------------------------------ + * Function : cache_invalidate_len + * Description: + * invalidate a region given an address and a length. + * The demarcation end is computed by applying length to address before + * rounding down address. End is rounded up. + * Cache line following rounded up end is not invalidateed. + *------------------------------------------------------------------------------ + */ +static inline void cache_invalidate_len(void *addr, int len) +{ + uintptr_t a = ADDR_ALIGN_DN( (uintptr_t)addr, L1_CACHE_BYTES ); + uintptr_t e = ADDR_ALIGN_UP( ((uintptr_t)addr + len), + L1_CACHE_BYTES ); + while ( a < e ) + { + invalidate_dcache_line(a); /* Hit_Invalidate_D */ + a += L1_CACHE_BYTES; /* next cache line base */ + } +} + +/* + *------------------------------------------------------------------------------ + * Function : _is_kptr_ + * Description: Test whether a variable can be a pointer to a kernel space. + * This form of variable overloading may only be used for denoting + * pointers to kernel space or as a variable where the most + * significant nibble is unused. + * In 32bit Linux kernel, a pointer to a KSEG0, KSEG1, KSEG2 will + * have 0x8, 0xA or 0xC in the most significant nibble. + *------------------------------------------------------------------------------ + */ +static inline uint32_t _is_kptr_(const void * vptr) +{ + return ( (uintptr_t)vptr > 0x0FFFFFFF ); +} + +#define cache_invalidate_region_outer_first(a, b) cache_invalidate_region(a, b) +#define cache_invalidate_len_outer_first(a, b) cache_invalidate_len(a, b) + +#elif defined(CONFIG_ARM) + + +#include +#if defined(CONFIG_ARM_L1_CACHE_SHIFT) +#define L1_CACHE_LINE_SIZE (0x1 << CONFIG_ARM_L1_CACHE_SHIFT) +#else +#warning There is no L1 cache line size defined! +#endif + +#if defined(CONFIG_OUTER_CACHE) + +#if defined(CONFIG_CACHE_L2X0) +#define L2_CACHE_LINE_SIZE 32 +#endif + +#if defined(L2_CACHE_LINE_SIZE) && (L1_CACHE_LINE_SIZE != L2_CACHE_LINE_SIZE) +#warning L1 Cache line size is different from L2 cache line size! +#endif + +#define CONFIG_OPTIMIZED_CACHE_FLUSH 1 +#endif + +static inline void cache_flush_len(void *addr, int len); +static inline void _cache_flush_len(void *addr, int len); + +#ifdef CONFIG_BCM_GLB_COHERENCY +#define cache_invalidate_len_outer_first(virt_addr, len) +#define cache_invalidate_region_outer_first(virt_addr, end) +#define cache_invalidate_len(virt_addr, len) +#define cache_invalidate_region(virt_addr, end) +#define cache_flush_region(addr, end) +#else + +/* the following functions are optimized that it does NOT support + * HIGHMEM in 32-bit system, please make sure buffer allocated + * are in memory zone 'Normal' or before */ +static inline void cache_invalidate_len_outer_first(void *virt_addr, int len) +{ + uintptr_t start_vaddr = (uintptr_t)virt_addr; + uintptr_t end_vaddr = start_vaddr + len; +#if defined(CONFIG_OUTER_CACHE) + uintptr_t start_paddr = virt_to_phys(virt_addr); + uintptr_t end_paddr = start_paddr + len; +#endif + +#if defined(CONFIG_OUTER_CACHE) + outer_spin_lock_irqsave(); +#endif + /* 1st, flush & invalidate if start addr and / or end addr are not + * cache line aligned */ + if (start_vaddr & (L1_CACHE_LINE_SIZE - 1)) { + start_vaddr &= ~(L1_CACHE_LINE_SIZE - 1); + __cpuc_flush_line(start_vaddr); +#if defined(CONFIG_OUTER_CACHE) + dsb(); +#endif + start_vaddr += L1_CACHE_LINE_SIZE; + } + +#if defined(CONFIG_OUTER_CACHE) + if (start_paddr & (L2_CACHE_LINE_SIZE - 1)) { + start_paddr &= ~(L2_CACHE_LINE_SIZE - 1); + outer_flush_line_no_lock(start_paddr); + outer_sync_no_lock(); + start_paddr += L2_CACHE_LINE_SIZE; + } +#endif + + if (end_vaddr & (L1_CACHE_LINE_SIZE - 1)) { + end_vaddr &= ~(L1_CACHE_LINE_SIZE - 1); + __cpuc_flush_line(end_vaddr); +#if defined(CONFIG_OUTER_CACHE) + dsb(); +#endif + } + +#if defined(CONFIG_OUTER_CACHE) + if (end_paddr & (L2_CACHE_LINE_SIZE - 1)) { + end_paddr &= ~(L2_CACHE_LINE_SIZE - 1); + outer_flush_line_no_lock(end_paddr); + outer_sync_no_lock(); + } +#endif + +#if defined(CONFIG_OUTER_CACHE) + /* now do the real invalidation jobs */ + while (start_paddr < end_paddr) { + outer_inv_line_no_lock(start_paddr); + start_paddr += L2_CACHE_LINE_SIZE; + } + outer_sync_no_lock(); +#endif + + /* now do the real invalidation jobs */ + while (start_vaddr < end_vaddr) { + __cpuc_inv_line(start_vaddr); + start_vaddr += L1_CACHE_LINE_SIZE; + } + + dsb(); +#if defined(CONFIG_OUTER_CACHE) + outer_spin_unlock_irqrestore(); +#endif + + if ((len >= PAGE_SIZE) && (((uintptr_t)virt_addr & ~PAGE_MASK) == 0)) + set_bit(PG_dcache_clean, &phys_to_page(virt_to_phys(virt_addr))->flags); +} + +static inline void cache_invalidate_region_outer_first(void *virt_addr, void *end) +{ + cache_invalidate_len_outer_first(virt_addr, + (uintptr_t)end - (uintptr_t)virt_addr); +} + +static inline void cache_invalidate_len(void *virt_addr, int len) +{ + uintptr_t start_vaddr = (uintptr_t)virt_addr; + uintptr_t end_vaddr = start_vaddr + len; +#if defined(CONFIG_OUTER_CACHE) + uintptr_t start_paddr = virt_to_phys(virt_addr); + uintptr_t end_paddr = start_paddr + len; +#endif + +#if defined(CONFIG_OUTER_CACHE) + outer_spin_lock_irqsave(); +#endif + /* 1st, flush & invalidate if start addr and / or end addr are not + * cache line aligned */ + if (start_vaddr & (L1_CACHE_LINE_SIZE - 1)) { + start_vaddr &= ~(L1_CACHE_LINE_SIZE - 1); + __cpuc_flush_line(start_vaddr); +#if defined(CONFIG_OUTER_CACHE) + dsb(); +#endif + start_vaddr += L1_CACHE_LINE_SIZE; + } + +#if defined(CONFIG_OUTER_CACHE) + if (start_paddr & (L2_CACHE_LINE_SIZE - 1)) { + start_paddr &= ~(L2_CACHE_LINE_SIZE - 1); + outer_flush_line_no_lock(start_paddr); + start_paddr += L2_CACHE_LINE_SIZE; + } +#endif + + if (end_vaddr & (L1_CACHE_LINE_SIZE - 1)) { + end_vaddr &= ~(L1_CACHE_LINE_SIZE - 1); + __cpuc_flush_line(end_vaddr); +#if defined(CONFIG_OUTER_CACHE) + dsb(); +#endif + } + +#if defined(CONFIG_OUTER_CACHE) + if (end_paddr & (L2_CACHE_LINE_SIZE - 1)) { + end_paddr &= ~(L2_CACHE_LINE_SIZE - 1); + outer_flush_line_no_lock(end_paddr); + } +#endif + + /* now do the real invalidation jobs */ + while (start_vaddr < end_vaddr) { + __cpuc_inv_line(start_vaddr); +#if defined(CONFIG_OUTER_CACHE) + dsb(); + outer_inv_line_no_lock(start_paddr); + start_paddr += L2_CACHE_LINE_SIZE; +#endif + start_vaddr += L1_CACHE_LINE_SIZE; + } +#if defined(CONFIG_OUTER_CACHE) + outer_sync_no_lock(); + outer_spin_unlock_irqrestore(); +#else + dsb(); +#endif + + if ((len >= PAGE_SIZE) && (((uintptr_t)virt_addr & ~PAGE_MASK) == 0)) + set_bit(PG_dcache_clean, &phys_to_page(virt_to_phys(virt_addr))->flags); +} + +static inline void cache_invalidate_region(void *virt_addr, void *end) +{ + cache_invalidate_len(virt_addr, + (uintptr_t)end - (uintptr_t)virt_addr); +} + +static inline void cache_flush_region(void *addr, void *end) +{ + cache_flush_len(addr, (uintptr_t)end - (uintptr_t)addr); +} + +#endif /* CONFIG_BCM_GLB_COHERENCY */ + +static inline void cache_flush_len(void *addr, int len) +{ +#ifndef CONFIG_BCM_GLB_COHERENCY + _cache_flush_len(addr, len); +#endif +} +static inline void fpm_cache_flush_len(void *addr, int len) +{ +#if !defined(CONFIG_BCM_GLB_COHERENCY) || defined(CONFIG_BCM_FPM_COHERENCY_EXCLUDE) + _cache_flush_len(addr, len); +#endif +} + +static inline void _cache_flush_len(void *addr, int len) +{ + uintptr_t start_vaddr = (uintptr_t)addr & ~(L1_CACHE_LINE_SIZE - 1); + uintptr_t end_vaddr = (uintptr_t)addr + len; +#if defined(CONFIG_OUTER_CACHE) + uintptr_t start_paddr = (uintptr_t)virt_to_phys((void *)start_vaddr); +#endif + +#if defined(CONFIG_OUTER_CACHE) + outer_spin_lock_irqsave(); +#endif +#if defined(CONFIG_OPTIMIZED_CACHE_FLUSH) + /* this function has been optimized in a non-recommended way, if any + * type of packet error occurs, please try undefine + * CONFIG_OPTIMIZED_CACHE_FLUSH to use the recommended algorithm + * provided by ARM cache document. + * Usually, when we have multiple levels of cache, in a cache_flush + * case, we do L1_clean -> L2_clean -> L2_invalidate -> L1_clean + * -> L1_invalidate, we can optimize this sequence to L1_clean -> + * L2_flush -> L1_flush. This is our original approach. However, + * this will introduce 3 loops of cache operation. + * This optimized method will do L1_flush -> L2_flush. This will only + * introduce 2 loops of cache operation, but it also puts us into + * danger that L2 cache might update L1 cache on the cache line + * that should have been invalidated. */ + + while (start_vaddr < end_vaddr) { + __cpuc_flush_line(start_vaddr); + start_vaddr += L1_CACHE_LINE_SIZE; +#if defined(CONFIG_OUTER_CACHE) + dsb(); + outer_flush_line_no_lock(start_paddr); + start_paddr += L2_CACHE_LINE_SIZE; +#endif + } +#if defined(CONFIG_OUTER_CACHE) + outer_sync_no_lock(); +#else + wmb(); +#endif +#else /* the non-optimized cache_flush */ + while (start_vaddr < end_vaddr) { +#if defined(CONFIG_OUTER_CACHE) + __cpuc_clean_line(start_vaddr); + dsb(); + outer_flush_line_no_lock(start_paddr); + start_paddr += L2_CACHE_LINE_SIZE; + outer_sync_no_lock(); +#endif + __cpuc_flush_line(start_vaddr); + start_vaddr += L1_CACHE_LINE_SIZE; + } + wmb(); +#endif +#if defined(CONFIG_OUTER_CACHE) + outer_spin_unlock_irqrestore(); +#endif +} + + +static inline uint32_t _is_kptr_(const void * vptr) +{ + return ( (uintptr_t)vptr > 0x0FFFFFFF ); +} + +#elif defined(CONFIG_ARM64) + +#define nbuff_flush_dcache_area(addr, len) \ + __asm__ __volatile__ ( \ + "mov x0, %0 \n" \ + "mov x1, %1 \n" \ + "mrs x3, ctr_el0 \n" \ + "ubfm x3, x3, #16, #19 \n" \ + "mov x2, #4 \n" \ + "lsl x2, x2, x3 \n" \ + "add x1, x0, x1 \n" \ + "sub x3, x2, #1 \n" \ + "bic x0, x0, x3 \n" \ + "1: dc civac, x0 \n" \ + "add x0, x0, x2 \n" \ + "cmp x0, x1 \n" \ + "b.lo 1b \n" \ + "dsb sy \n" \ + : : "r" ((uintptr_t)addr), "r" ((uintptr_t)len) \ + : "x0", "x1", "x2", "x3", "cc") + +#define nbuff_inval_dcache_range(start, end) \ + __asm__ __volatile__ ( \ + "mov x0, %0 \n" \ + "mov x1, %1 \n" \ + "mrs x3, ctr_el0 \n" \ + "ubfm x3, x3, #16, #19 \n" \ + "mov x2, #4 \n" \ + "lsl x2, x2, x3 \n" \ + "sub x3, x2, #1 \n" \ + "tst x1, x3 \n" \ + "bic x1, x1, x3 \n" \ + "b.eq 1f \n" \ + "dc civac, x1 \n" \ + "1: tst x0, x3 \n" \ + "bic x0, x0, x3 \n" \ + "b.eq 2f \n" \ + "dc civac, x0 \n" \ + "b 3f \n" \ + "2: dc ivac, x0 \n" \ + "3: add x0, x0, x2 \n" \ + "cmp x0, x1 \n" \ + "b.lo 2b \n" \ + "dsb sy \n" \ + : : "r" ((uintptr_t)start), "r" ((uintptr_t)end)\ + : "x0", "x1", "x2", "x3", "cc") + +static inline void cache_flush_region(void *addr, void *end) +{ +#ifndef CONFIG_BCM_GLB_COHERENCY + nbuff_flush_dcache_area(addr, (uintptr_t)end - (uintptr_t)addr); +#endif +} + +static inline void cache_flush_len(void *addr, int len) +{ +#ifndef CONFIG_BCM_GLB_COHERENCY + nbuff_flush_dcache_area(addr, len); +#endif +} + +static inline void fpm_cache_flush_len(void *addr, int len) +{ +#if !defined(CONFIG_BCM_GLB_COHERENCY) || defined(CONFIG_BCM_FPM_COHERENCY_EXCLUDE) + nbuff_flush_dcache_area(addr, len); +#endif +} + +static inline void cache_invalidate_region(void *addr, void *end) +{ +#ifndef CONFIG_BCM_GLB_COHERENCY + nbuff_inval_dcache_range(addr, end); +#endif +} + +static inline void cache_invalidate_len(void *addr, int len) +{ +#ifndef CONFIG_BCM_GLB_COHERENCY + nbuff_inval_dcache_range(addr, (void*)((uintptr_t)addr+len)); +#endif +} + +#define cache_invalidate_region_outer_first(a, b) cache_invalidate_region(a, b) +#define cache_invalidate_len_outer_first(a, b) cache_invalidate_len(a, b) + +static inline uint32_t _is_kptr_(const void * vptr) +{ + return ( (uintptr_t)vptr > 0xFFFFFF8000000000 ); +} +#endif + +#endif /* defined(__KERNEL__) Linux MIPS Cache Specific */ +/* OS Specific Section End */ + + +/* + * For BSD style mbuf with FKB : + * generate nbuff.h by replacing "SKBUFF" to "BCMMBUF", and, + * use custom arg1 and arg2 instead of mark and priority, respectively. + */ + +#ifdef TRACE_COMPILE +#pragma message "got here 4" +#endif + +struct sk_buff; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +struct blog_t; +#endif +struct net_device; +typedef int (*HardStartXmitFuncP) (struct sk_buff *skb, + struct net_device *dev); + +struct fkbuff; +typedef struct fkbuff FkBuff_t; + +#define FKB_NULL ((FkBuff_t *)NULL) + +#include + +/* + *------------------------------------------------------------------------------ + * + * Pointer conversion between pBuf and pNBuff encoded buffer pointers + * uint8_t * pBuf; + * pNBuff_t pNBuff; + * ... + * // overlays FKBUFF_PTR into pointer to build a virtual pNBuff_t + * pNBuff = PBUF_2_PNBUFF(pBuf,FKBUFF_PTR); + * ... + * // extracts a real uint8_t * from a virtual pNBuff_t + * pBuf = PNBUFF_2_PBUF(pNBuff); + * + *------------------------------------------------------------------------------ + */ +#define PBUF_2_PNBUFF(pBuf,realType) \ + ( (pNBuff_t) ((uintptr_t)(pBuf) | (uintptr_t)(realType)) ) +#define PNBUFF_2_PBUF(pNBuff) \ + ( (uint8_t*) ((uintptr_t)(pNBuff) & (uintptr_t)NBUFF_PTR_MASK) ) + +#if (MUST_BE_ZERO != 0) +#error "Design assumption SKBUFF_PTR == 0" +#endif +#define PNBUFF_2_SKBUFF(pNBuff) ((struct sk_buff *)(pNBuff)) + +#define SKBUFF_2_PNBUFF(skb) ((pNBuff_t)(skb)) /* see MUST_BE_ZERO */ +#define FKBUFF_2_PNBUFF(fkb) PBUF_2_PNBUFF(fkb,FKBUFF_PTR) + +/* + *------------------------------------------------------------------------------ + * + * Cast from/to virtual "pNBuff_t" to/from real typed pointers + * + * pNBuff_t pNBuff2Skb, pNBuff2Fkb; // "void *" with NBuffPtrType_t + * struct sk_buff * skb_p; + * struct fkbuff * fkb_p; + * ... + * pNBuff2Skb = CAST_REAL_TO_VIRT_PNBUFF(skb_p,SKBUFF_PTR); + * pNBuff2Fkb = CAST_REAL_TO_VIRT_PNBUFF(fkb_p,FKBUFF_PTR); + * ... + * skb_p = CAST_VIRT_TO_REAL_PNBUFF(pNBuff2Skb, struct sk_buff *); + * fkb_p = CAST_VIRT_TO_REAL_PNBUFF(pNBuff2Fkb, struct fkbuff *); + * or, + * fkb_p = PNBUFF_2_FKBUFF(pNBuff2Fkb); + *------------------------------------------------------------------------------ + */ + +#define CAST_REAL_TO_VIRT_PNBUFF(pRealNBuff,realType) \ + ( (pNBuff_t) (PBUF_2_PNBUFF((pRealNBuff),(realType))) ) + +#define CAST_VIRT_TO_REAL_PNBUFF(pVirtNBuff,realType) \ + ( (realType) PNBUFF_2_PBUF(pVirtNBuff) ) + +#define PNBUFF_2_FKBUFF(pNBuff) CAST_VIRT_TO_REAL_PNBUFF((pNBuff), struct fkbuff*) + +/* + *------------------------------------------------------------------------------ + * FKB: Fast Kernel Buffers placed directly into Rx DMA Buffer + * May be used ONLY for common APIs such as those available in BSD-Style mbuf + *------------------------------------------------------------------------------ + */ + +struct fkbuff +{ + /* List pointer must be the first field */ + union { + FkBuff_t * list; /* SLL of free FKBs for cloning */ + FkBuff_t * master_p; /* Clone FKB to point to master FKB */ + atomic_long_t users; /* (private) # of references to FKB */ + }; + union { /* Use _is_kptr_ to determine if ptr */ + union { + void *ptr; + struct blog_t *blog_p; /* Pointer to a blog */ + uint8_t *dirty_p; /* Pointer to packet payload dirty incache*/ + uint32_t flags; /* Access all flags */ + }; + /* + * First nibble denotes a pointer or flag usage. + * Lowest two significant bits denote the type of pinter + * Remaining 22 bits may be used as flags + */ + struct { + uint32_t ptr_type : 8;/* Identifies whether pointer */ + uint32_t unused :21;/* Future use for flags */ + uint32_t in_skb : 1;/* flag: FKB passed inside a SKB */ + uint32_t other_ptr: 1;/* future use, to override another pointer*/ + uint32_t dptr_tag : 1;/* Pointer type is a dirty pointer */ + }; + }; + uint8_t * data; /* Pointer to packet data */ + + union { + /* here the bits 31-24 are valid only for native fkbs's + * these bits bits will be cleared when using fkbInSkb + * Note that it is critical to have the Little Endian/Big endian + * declaration since FKB will use length as bit field and SKB will use + * length as a word Need to maintain the same bit positions across MIPS + * and ARM. + */ + struct{ + BE_DECL( + uint32_t rx_csum_verified:1; + uint32_t spdtst:1; + uint32_t reserved:6; + uint32_t len:24; /* Packet length */ + ) + LE_DECL( + uint32_t len:24; + uint32_t reserved:6; + uint32_t spdtst:1; + uint32_t rx_csum_verified:1; + ) + }; + uint32_t len_word; + }; + + union { + /* only the lower 32 bit in mark is used in 64 bit system, + * but we delcare it as unsigned long for the ease for fcache + * to handle it in different architecture, since it is part + * of union with a dst_entry pointer */ + unsigned long mark; /* Custom arg1, e.g. tag or mark field */ + void *queue; /* Single link list queue of FKB | SKB */ + void *dst_entry; /* rtcache entry for locally termiated pkts */ + uint32_t fc_ctxt; /* hybrid flow cache context */ + }; + union { + uint32_t priority; /* Custom arg2, packet priority, tx info */ + wlFlowInf_t wl; /* WLAN Flow Info */ + uint32_t flowid; /* used for locally terminated pkts */ + }; + + RecycleFuncP recycle_hook; /* Nbuff recycle handler */ + union { + /* recycle hook for Clone FKB is used in DHD pointing to extra info + * BE CAREFULL when using this recyle_context for free etc.... + */ + void *dhd_pkttag_info_p; + unsigned long recycle_context; /* Rx network device/channel or pool */ + uint32_t fpm_num; + }; + +} ____cacheline_aligned; /* 2 cache lines wide */ + +#define FKB_CLEAR_LEN_WORD_FLAGS(len_word) (len_word &= 0x00FFFFFF) + + +/* + *------------------------------------------------------------------------------ + * An fkbuff may be referred to as a: + * master - a pre-allocated rxBuffer, inplaced ahead of the headroom. + * cloned - allocated from a free pool of fkbuff and points to a master. + * + * in_skb - when a FKB is passed as a member of a SKB structure. + *------------------------------------------------------------------------------ + */ +#define FKB_IN_SKB (1 << 2) /* Bit#2 is in_skb */ + +/* Return flags with the in_skb tag set */ +static inline uint32_t _set_in_skb_tag_(uint32_t flags) +{ + return (flags | FKB_IN_SKB); +} + +/* Fetch the in_skb tag in flags */ +static inline uint32_t _get_in_skb_tag_(void *ptr, uint32_t flags) +{ + if (_is_kptr_(ptr)) + return 0; + return (flags & FKB_IN_SKB); +} + +/* Determine whether the in_skb tag is set in flags */ +static inline uint32_t _is_in_skb_tag_(void *ptr, uint32_t flags) +{ + return ( _get_in_skb_tag_(ptr, flags) ? 1 : 0 ); +} + +#define CHK_IQ_PRIO (1 << 3) /* Bit#3 is check IQ Prio */ + +/* Return flags with the in_skb_tag and chk_iq_prio set */ +static inline uint32_t _set_in_skb_n_chk_iq_prio_tag_(uint32_t flags) +{ + return (flags | FKB_IN_SKB | CHK_IQ_PRIO); +} + +/* Return flags with the chk_iq_prio set */ +static inline uint32_t _set_chk_iq_prio_tag_(uint32_t flags) +{ + return (flags | CHK_IQ_PRIO); +} + +/* Fetch the chk_iq_prio tag in flags */ +static inline uint32_t _get_chk_iq_prio_tag_(uint32_t flags) +{ + return (flags & CHK_IQ_PRIO); +} + +/* Determine whether the chk_iq_prio tag is set in flags */ +static inline uint32_t _is_chk_iq_prio_tag_(uint32_t flags) +{ + return ( _get_chk_iq_prio_tag_(flags) ? 1 : 0 ); +} + + +/* + *------------------------------------------------------------------------------ + * APIs to convert between a real kernel pointer and a dirty pointer. + *------------------------------------------------------------------------------ + */ + +#define FKB_DPTR_TAG (1 << 0) /* Bit#0 is dptr_tag */ + +/* Test whether a pointer is a dirty pointer type */ +static inline uint32_t is_dptr_tag_(uint8_t * ptr) +{ + return ( ( (uint32_t) ((uintptr_t)ptr & FKB_DPTR_TAG) ) ? 1 : 0); +} + +/* Encode a real kernel pointer to a dirty pointer type */ +static inline uint8_t * _to_dptr_from_kptr_(uint8_t * kernel_ptr) +{ + if((uintptr_t)(kernel_ptr) & FKB_DPTR_TAG) + kernel_ptr++; + /* Tag a kernel pointer's dirty_ptr bit, to denote a FKB dirty pointer */ + return ( (uint8_t*) ((uintptr_t)(kernel_ptr) | FKB_DPTR_TAG) ); +} + +/* Decode a dirty pointer type into a real kernel pointer */ +static inline uint8_t * _to_kptr_from_dptr_(uint8_t * dirty_ptr) +{ + FKB_AUDIT( + if ( dirty_ptr && !is_dptr_tag_(dirty_ptr) ) + printk("FKB ASSERT %s !is_dptr_tag_(0x%08x)\n", + __FUNCTION__, (uintptr_t)dirty_ptr); ); + + /* Fetch kernel pointer from encoded FKB dirty_ptr, + by clearing dirty_ptr bit */ + return ( (uint8_t*) ((uintptr_t)(dirty_ptr) & (~FKB_DPTR_TAG)) ); +} + +#define FKB_OPTR_TAG (1<<1) /* Bit#1 other_ptr tag */ + +#define FKB_BLOG_TAG_MASK (FKB_DPTR_TAG | FKB_OPTR_TAG) + +/* Verify whether a FKB pointer is pointing to a Blog */ +#define _IS_BPTR_(fkb_ptr) \ + ( _is_kptr_(fkb_ptr) && ! ((uintptr_t)(fkb_ptr) & FKB_BLOG_TAG_MASK) ) + + +/* + *------------------------------------------------------------------------------ + * + * Types of preallocated FKB pools + * + * - A Master FKB object contains memory for the rx buffer, with a FkBuff_t + * placed at the head of the buffer. A Master FKB object may serve to + * replenish a network devices receive ring, when packet buffers are not + * promptly recycled. A Master FKB may also be used for packet replication + * where in one of the transmitted packet replicas may need a unique + * modification distinct from other replicas. In such a case, the FKB must + * be first "unshared" by a deep packet buffer copy into a Master Fkb. + * A Free Pool of Master FKB objects is maintained. Master FKB may be + * alocated and recycled from this Master FKB Pool. + * The Master FKB Pool may also be used for replinishing a network device + * driver's rx buffer ring. + * + * - A Cloned FKB object does not contain memory for the rx buffer. + * Used by fkb_clone, to create multiple references to a packet buffer. + * Multiple references to a packet buffer may be used for packet replication. + * A FKB allocated from the FKB Cloned Pool will have master_p pointing to + * a Master FKB and the recycle_hook member set to NULL. + * + *------------------------------------------------------------------------------ + */ +typedef enum { + FkbMasterPool_e = 0, + FkbClonedPool_e = 1, + FkbMaxPools_e +} FkbObject_t; + +/* + * Function : _get_master_users_ + * Description: Given a pointer to a Master FKB, fetch the users count + * Caution : Does not check whether the FKB is a Master or not! + */ +static inline uint32_t _get_master_users_(FkBuff_t * fkbM_p) +{ + uint32_t users; + users = atomic_read(&fkbM_p->users); + + FKB_AUDIT( + if ( users == 0 ) + printk("FKB ASSERT cpu<%u> %s(0x%08x) users == 0, recycle<%pS>\n", + smp_processor_id(), __FUNCTION__, + (int)fkbM_p, fkbM_p->recycle_hook); ); + return users; +} + +/* + * Function : _is_fkb_cloned_pool_ + * Description: Test whether an "allocated" FKB is from the FKB Cloned Pool. + */ +static inline uint32_t _is_fkb_cloned_pool_(FkBuff_t * fkb_p) +{ + if ( _is_kptr_(fkb_p->master_p) + && (fkb_p->recycle_hook == (RecycleFuncP)NULL) ) + { + FKB_AUDIT( + /* ASSERT if the FKB is actually linked in a FKB pool */ + if ( _is_kptr_(fkb_p->master_p->list) ) + { + printk("FKB ASSERT cpu<%u> %s :" + " _is_kptr_((0x%08x)->0x%08x->0x%08x)" + " master<0x%08x>.recycle<%pS>\n", + smp_processor_id(), __FUNCTION__, (int)fkb_p, + (int)fkb_p->master_p, (int)fkb_p->master_p->list, + (int)fkb_p->master_p, + fkb_p->master_p->recycle_hook); + } + /* ASSERT that Master FKB users count is greater than 0 */ + if ( _get_master_users_(fkb_p->master_p) == 0 ) + { + printk("FKB ASSERT cpu<%u> %s :" + " _get_master_users_(0x%08x->0x%08x) == 0\n", + smp_processor_id(), __FUNCTION__, + (int)fkb_p, (int)fkb_p->master_p); + return 0; + } ); + + return 1; /* Allocated FKB is from the FKB Cloned Pool */ + } + else + return 0; +} + +/* + * Function : _get_fkb_users_ + * Description: Given a pointer to a FKB (Master or Cloned), fetch users count + */ +static inline uint32_t _get_fkb_users_(FkBuff_t * fkb_p) +{ + if ( _is_kptr_(fkb_p->master_p) ) /* Cloned FKB */ + { + FKB_AUDIT( + if ( !_is_fkb_cloned_pool_(fkb_p) ) /* double check Cloned FKB */ + { + printk("FKB ASSERT cpu<%u> %s :" + " !_is_fkb_cloned_pool_(0x%08x)" + " master<0x%08x>.recycle<%pS>\n", + smp_processor_id(), __FUNCTION__, + (int)fkb_p, (int)fkb_p->master_p, + fkb_p->master_p->recycle_hook); + return 0; + } ); + + return _get_master_users_(fkb_p->master_p); + } + else /* Master FKB */ + return _get_master_users_(fkb_p); +} + +/* + * Function : _get_fkb_master_ptr_ + * Description: Fetch the pointer to the Master FKB. + */ +static inline FkBuff_t * _get_fkb_master_ptr_(FkBuff_t * fkb_p) +{ + if ( _is_kptr_(fkb_p->master_p) ) /* Cloned FKB */ + { + FKB_AUDIT( + if ( !_is_fkb_cloned_pool_(fkb_p) ) /* double check Cloned FKB */ + { + printk("FKB ASSERT cpu<%u> %s " + " !_is_fkb_cloned_pool_(0x%08x)" + " master<0x%08x>.recycle<%pS>\n", + smp_processor_id(), __FUNCTION__, + (int)fkb_p, (int)fkb_p->master_p, + fkb_p->master_p->recycle_hook); + return FKB_NULL; + } ); + + return fkb_p->master_p; + } + else /* Master FKB */ + { + FKB_AUDIT( + if ( _get_master_users_(fkb_p) == 0 ) /* assert Master FKB users */ + { + printk("FKB ASSERT cpu<%u> %s " + " _get_master_users_(0x%08x) == 0\n", + smp_processor_id(), __FUNCTION__, (int)fkb_p); + return FKB_NULL; + } ); + + return fkb_p; + } +} + +/* + *------------------------------------------------------------------------------ + * Placement of a FKB object in the Rx DMA buffer: + * + * RX DMA Buffer: |----- FKB ----|--- reserve headroom ---|---...... + * ^ ^ ^ + * pFkb pHead pData + * pBuf + *------------------------------------------------------------------------------ + */ +#define PFKBUFF_PHEAD_OFFSET sizeof(FkBuff_t) +#define PFKBUFF_TO_PHEAD(pFkb) ((uint8_t*)((FkBuff_t*)(pFkb) + 1)) +#define PHEAD_TO_PFKBUFF(pHead) \ + (FkBuff_t *)((uint8_t*)(pHead)-PFKBUFF_PHEAD_OFFSET) + +#define PDATA_TO_PFKBUFF(pData,headroom) \ + (FkBuff_t *)((uint8_t*)(pData)-(headroom)-PFKBUFF_PHEAD_OFFSET) +#define PFKBUFF_TO_PDATA(pFkb,headroom) \ + (uint8_t*)((uint8_t*)(pFkb) + PFKBUFF_PHEAD_OFFSET + (headroom)) + + +#define NBUFF_ALIGN_MASK_8 0x07 +pNBuff_t nbuff_align_data(pNBuff_t pNBuff, uint8_t **data_pp, + uint32_t len, unsigned long alignMask); + +/* + *------------------------------------------------------------------------------ + * FKB Functional Interfaces + *------------------------------------------------------------------------------ + */ + +/* + * Function : fkb_in_skb_test + * Description: Verifies that the layout of SKB member fields corresponding to + * a FKB have the same layout. This allows a FKB to be passed via + * a SKB. + */ + +extern int fkb_in_skb_test( int fkb_in_skb_offset, + int list_offset, int blog_p_offset, + int data_offset, int len_offset, int mark_offset, + int priority_offset, int recycle_hook_offset, + int recycle_context_offset ); + +/* + * Global FKB Subsystem Constructor + * fkb_construct() validates that the layout of fkbuff members in sk_buff + * is the same. An sk_buff contains an fkbuff and permits a quick translation + * to and from a fkbuff. It also preallocates the pools of FKBs. + */ +extern int fkb_construct(int fkb_in_skb_offset); + +/* + * Function : fkb_stats + * Description: Report FKB Pool statistics, see CC_CONFIG_FKB_STATS + */ +extern void fkb_stats(void); + +/* + * Function : fkb_alloc + * Description: Allocate a Cloned/Master FKB object from preallocated pool + */ +extern FkBuff_t * fkb_alloc( FkbObject_t object ); + +/* + * Function : fkb_free + * Description: Free a FKB object to its respective preallocated pool. + */ +extern void fkb_free(FkBuff_t * fkb_p); + +/* + * Function : fkb_unshare + * Description: If a FKB is pointing to a buffer with multiple references + * to this buffer, then create a copy of the buffer and return a FKB with a + * single reference to this buffer. + */ +extern FkBuff_t * fkb_unshare(FkBuff_t * fkb_p); + +/* + * Function : fkbM_borrow + * Description: Allocate a Master FKB object from the pre-allocated pool. + */ +extern FkBuff_t * fkbM_borrow(void); + +/* + * Function : fkbM_return + * Description: Return a Master FKB object to a pre-allocated pool. + */ +extern void fkbM_return(FkBuff_t * fkbM_p); + +/* + * Function : fkb_set_ref + * Description: Set reference count to an FKB. + */ +static inline void _fkb_set_ref(FkBuff_t * fkb_p, const int count) +{ + atomic_long_set(&fkb_p->users, count); +} +FKB_FN( fkb_set_ref, + void fkb_set_ref(FkBuff_t * fkb_p, const int count), + _fkb_set_ref(fkb_p, count) ) + +/* + * Function : fkb_inc_ref + * Description: Increment reference count to an FKB. + */ +static inline void _fkb_inc_ref(FkBuff_t * fkb_p) +{ + atomic_long_inc(&fkb_p->users); +} +FKB_FN( fkb_inc_ref, + void fkb_inc_ref(FkBuff_t * fkb_p), + _fkb_inc_ref(fkb_p) ) + +/* + * Function : fkb_dec_ref + * Description: Decrement reference count to an FKB. + */ +static inline void _fkb_dec_ref(FkBuff_t * fkb_p) +{ + atomic_long_dec(&fkb_p->users); + /* For debug, may want to assert that users does not become negative */ +} +FKB_FN( fkb_dec_ref, + void fkb_dec_ref(FkBuff_t * fkb_p), + _fkb_dec_ref(fkb_p) ) + + +/* + * Function : fkb_preinit + * Description: A network device driver may use this function to place a + * FKB object into rx buffers, when they are created. FKB objects preceeds + * the reserved headroom. + */ +static inline void fkb_preinit(uint8_t * pBuf, RecycleFuncP recycle_hook, + unsigned long recycle_context) +{ + FkBuff_t *fkb_p = (FkBuff_t *)pBuf; + fkb_p->recycle_hook = recycle_hook; /* never modified */ + fkb_p->recycle_context = recycle_context; /* never modified */ + + fkb_p->ptr = NULL; /* resets dirty_p, blog_p */ + fkb_p->data = NULL; + fkb_p->len_word = 0; + fkb_p->mark = 0; + fkb_p->priority = 0; + fkb_set_ref(fkb_p, 0); +} + +/* + * Function : fkb_init + * Description: Initialize the FKB context for a received packet. Invoked by a + * network device on extract the packet from a buffer descriptor and associating + * a FKB context to the received packet. + */ +static inline FkBuff_t * _fkb_init(uint8_t * pBuf, uint32_t headroom, + uint8_t * pData, uint32_t len) +{ + FkBuff_t * fkb_p = PDATA_TO_PFKBUFF(pBuf, headroom); + fkb_dbg( 1, "fkb_p<%p> pBuf<%p> headroom<%u> pData<%p> len<%d>", + fkb_p, pBuf, (int)headroom, pData, len ); + +#if defined(CC_FKB_HEADROOM_AUDIT) + if ( headroom < BCM_PKT_HEADROOM ) + printk("NBUFF: Insufficient headroom <%u>, need <%u> %-10s\n", + headroom, BCM_PKT_HEADROOM, __FUNCTION__); +#endif + + fkb_p->data = pData; + fkb_p->len_word = 0;/*clear flags */ + fkb_p->len = len; + fkb_p->ptr = (void*)NULL; /* resets dirty_p, blog_p */ + fkb_p->mark = 0; + fkb_p->priority = 0; + + fkb_set_ref( fkb_p, 1 ); + + return fkb_p; +} +FKB_FN( fkb_init, + FkBuff_t * fkb_init(uint8_t * pBuf, uint32_t headroom, + uint8_t * pData, uint32_t len), + return _fkb_init(pBuf, headroom, pData, len) ) + +/* + * Function : fkb_qinit + * Description: Same as fkb_init, with the exception that a recycle queue + * context is associated with the FKB, each time the packet is receieved. + */ +static inline FkBuff_t * _fkb_qinit(uint8_t * pBuf, uint32_t headroom, + uint8_t * pData, uint32_t len, unsigned long qcontext) +{ + FkBuff_t * fkb_p = PDATA_TO_PFKBUFF(pBuf, headroom); + fkb_dbg(1, "fkb_p<%p> qcontext<%lx>", fkb_p, qcontext ); + fkb_p->recycle_context = qcontext; + + return _fkb_init(pBuf, headroom, pData, len); +} +FKB_FN( fkb_qinit, + FkBuff_t * fkb_qinit(uint8_t * pBuf, uint32_t headroom, + uint8_t * pData, uint32_t len, unsigned long qcontext), + return _fkb_qinit(pBuf, headroom, pData, len, qcontext) ) + +/* + * Function : fkb_release + * Description: Release any associated blog and set ref count to 0. A fkb + * may be released multiple times (not decrement reference count). + */ +static inline void _fkb_release(FkBuff_t * fkb_p) +{ + fkb_dbg(1, "fkb_p<%p> fkb_p->blog_p<%p>", fkb_p, fkb_p->blog_p ); +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + if ( _IS_BPTR_( fkb_p->blog_p ) ) + blog_put(fkb_p->blog_p); +#endif + fkb_p->ptr = (void*)NULL; /* reset dirty_p, blog_p */ + + fkb_set_ref( fkb_p, 0 ); /* fkb_release may be invoked multiple times */ +} +FKB_FN( fkb_release, + void fkb_release(FkBuff_t * fkb_p), + _fkb_release(fkb_p) ) + +/* + * Function : fkb_headroom + * Description: Determine available headroom for the packet in the buffer. + */ +static inline int _fkb_headroom(const FkBuff_t *fkb_p) +{ + return (int)( (uintptr_t)(fkb_p->data) - (uintptr_t)(fkb_p+1) ); +} +FKB_FN( fkb_headroom, + int fkb_headroom(const FkBuff_t *fkb_p), + return _fkb_headroom(fkb_p) ) + +/* + * Function : fkb_init_headroom + * Description: The available headroom the packet in the buffer at fkb_init time. + */ +static inline int _fkb_init_headroom(void) +{ + return BCM_PKT_HEADROOM; +} +FKB_FN( fkb_init_headroom, + int fkb_init_headroom(void), + return _fkb_init_headroom() ) + + +/* + * Function : fkb_push + * Description: Prepare space for data at head of the packet buffer. + */ +static inline uint8_t * _fkb_push(FkBuff_t * fkb_p, uint32_t len) +{ + fkb_p->len += len; + fkb_p->data -= len; + return fkb_p->data; +} +FKB_FN( fkb_push, + uint8_t * fkb_push(FkBuff_t * fkb_p, uint32_t len), + return _fkb_push(fkb_p, len) ) + +/* + * Function : fkb_pull + * Description: Delete data from the head of packet buffer. + */ +static inline uint8_t * _fkb_pull(FkBuff_t * fkb_p, uint32_t len) +{ + fkb_p->len -= len; + fkb_p->data += len; + return fkb_p->data; +} +FKB_FN( fkb_pull, + uint8_t * fkb_pull(FkBuff_t * fkb_p, uint32_t len), + return _fkb_pull(fkb_p, len) ) + +/* + * Function : fkb_put + * Description: Prepare space for data at tail of the packet buffer. + */ +static inline uint8_t * _fkb_put(FkBuff_t * fkb_p, uint32_t len) +{ + uint8_t * tail_p = fkb_p->data + fkb_p->len; + fkb_p->len += len; + return tail_p; +} +FKB_FN( fkb_put, + uint8_t * fkb_put(FkBuff_t * fkb_p, uint32_t len), + return _fkb_put(fkb_p, len) ) + +/* + * Function : fkb_pad + * Description: Pad the packet by requested number of bytes. + */ +static inline uint32_t _fkb_pad(FkBuff_t * fkb_p, uint32_t padding) +{ + memset((uint8_t *)(fkb_p->data + fkb_p->len), 0, padding); + fkb_p->len += padding; + return fkb_p->len; +} +FKB_FN( fkb_pad, + uint32_t fkb_pad(FkBuff_t * fkb_p, uint32_t padding), + return _fkb_pad(fkb_p, padding) ) + +/* + * Function : fkb_len + * Description: Determine the length of the packet. + */ +static inline uint32_t _fkb_len(FkBuff_t * fkb_p) +{ + return fkb_p->len; +} +FKB_FN( fkb_len, + uint32_t fkb_len(FkBuff_t * fkb_p), + return _fkb_len(fkb_p) ) + +/* + * Function : fkb_data + * Description: Fetch the start of the packet. + */ +static inline uint8_t * _fkb_data(FkBuff_t * fkb_p) +{ + return fkb_p->data; +} +FKB_FN( fkb_data, + uint8_t * fkb_data(FkBuff_t * fkb_p), + return _fkb_data(fkb_p) ) + +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +/* + * Function : fkb_blog + * Description: Fetch the associated blog. + */ +static inline struct blog_t * _fkb_blog(FkBuff_t * fkb_p) +{ + return fkb_p->blog_p; +} +FKB_FN( fkb_blog, + struct blog_t * fkb_blog(FkBuff_t * fkb_p), + return _fkb_blog(fkb_p) ) +#endif + +/* + * Function : fkb_clone + * Description: Allocate a FKB from the Cloned Pool and make it reference the + * same packet. + */ +static inline FkBuff_t * _fkb_clone(FkBuff_t * fkbM_p) +{ + FkBuff_t * fkbC_p; + + FKB_AUDIT( + if ( smp_processor_id() ) + printk("FKB ASSERT %s not supported on CP 1\n", __FUNCTION__); ); + + /* Fetch a pointer to the Master FKB */ + fkbM_p = _get_fkb_master_ptr_( fkbM_p ); + + fkbC_p = fkb_alloc( FkbClonedPool_e ); /* Allocate FKB from Cloned pool */ + + if ( unlikely(fkbC_p != FKB_NULL) ) + { + fkb_inc_ref( fkbM_p ); + fkbC_p->master_p = fkbM_p; + fkbC_p->ptr = fkbM_p->ptr; + + fkbC_p->data = fkbM_p->data; + fkbC_p->len_word = fkbM_p->len_word; + fkbC_p->mark = fkbM_p->mark; + fkbC_p->priority = fkbM_p->priority; + } + + fkb_dbg(1, "fkbC_p<%p> ---> fkbM_p<%p>", fkbC_p, fkbM_p ); + + return fkbC_p; /* May be null */ +} +FKB_FN( fkb_clone, + FkBuff_t * fkb_clone(FkBuff_t * fkbM_p), + return _fkb_clone(fkbM_p) ) + +extern void fkb_flush(FkBuff_t * fkb_p, uint8_t * data_p, int len, int cache_op); + +/* + *------------------------------------------------------------------------------ + * Virtual accessors to common members of network kernel buffer + *------------------------------------------------------------------------------ + */ + +/* __BUILD_NBUFF_SET_ACCESSOR: generates function nbuff_set_MEMBER() */ +#define __BUILD_NBUFF_SET_ACCESSOR( TYPE, MEMBER ) \ +static inline void nbuff_set_##MEMBER(pNBuff_t pNBuff, TYPE MEMBER) \ +{ \ + void * pBuf = PNBUFF_2_PBUF(pNBuff); \ + if ( IS_SKBUFF_PTR(pNBuff) ) \ + ((struct sk_buff *)pBuf)->MEMBER = MEMBER; \ + /* else if IS_FPBUFF_PTR, else if IS_TGBUFF_PTR */ \ + else \ + ((FkBuff_t *)pBuf)->MEMBER = MEMBER; \ +} + +/* __BUILD_NBUFF_GET_ACCESSOR: generates function nbuff_get_MEMBER() */ +#define __BUILD_NBUFF_GET_ACCESSOR( TYPE, MEMBER ) \ +static inline TYPE nbuff_get_##MEMBER(pNBuff_t pNBuff) \ +{ \ + void * pBuf = PNBUFF_2_PBUF(pNBuff); \ + if ( IS_SKBUFF_PTR(pNBuff) ) \ + return (TYPE)(((struct sk_buff *)pBuf)->MEMBER); \ + /* else if IS_FPBUFF_PTR, else if IS_TGBUFF_PTR */ \ + else \ + return (TYPE)(((FkBuff_t *)pBuf)->MEMBER); \ +} + +/* + * Common set/get accessor of base network buffer fields: + * nbuff_set_data(), nbuff_set_len(), nbuff_set_mark(), nbuff_set_priority() + * nbuff_get_data(), nbuff_get_len(), nbuff_get_mark(), nbuff_get_priority() + */ +__BUILD_NBUFF_SET_ACCESSOR(uint8_t *, data) +__BUILD_NBUFF_SET_ACCESSOR(uint32_t, len) +__BUILD_NBUFF_SET_ACCESSOR(uint32_t, mark) /* Custom network buffer arg1 */ +__BUILD_NBUFF_SET_ACCESSOR(void *, queue) /* Custom network buffer arg1 */ +__BUILD_NBUFF_SET_ACCESSOR(uint32_t, priority) /* Custom network buffer arg2 */ + +__BUILD_NBUFF_GET_ACCESSOR(uint8_t *, data) +__BUILD_NBUFF_GET_ACCESSOR(uint32_t, len) +__BUILD_NBUFF_GET_ACCESSOR(uint32_t, mark) /* Custom network buffer arg1 */ +__BUILD_NBUFF_GET_ACCESSOR(void *, queue) /* Custom network buffer arg1 */ +__BUILD_NBUFF_GET_ACCESSOR(uint32_t, priority) /* Custom network buffer arg2 */ + +/* + * Function : nbuff_get_context + * Description: Extracts the data and len fields from a pNBuff_t. + */ +static inline void * nbuff_get_context(pNBuff_t pNBuff, + uint8_t ** data_p, uint32_t *len_p) +{ + void * pBuf = PNBUFF_2_PBUF(pNBuff); + if ( pBuf == (void*) NULL ) + return pBuf; + if ( IS_SKBUFF_PTR(pNBuff) ) + { + *data_p = ((struct sk_buff *)pBuf)->data; + *len_p = ((struct sk_buff *)pBuf)->len; + } + else + { + *data_p = ((FkBuff_t *)pBuf)->data; + *len_p = ((FkBuff_t *)pBuf)->len; + } + fkb_dbg(1, "pNBuff<%p> pBuf<%p> data_p<%p>", + pNBuff, pBuf, *data_p ); + return pBuf; +} + +/* + * Function : nbuff_get_params + * Description: Extracts the data, len, mark and priority field from a network + * buffer. + */ +static inline void * nbuff_get_params(pNBuff_t pNBuff, + uint8_t ** data_p, uint32_t *len_p, + uint32_t * mark_p, uint32_t *priority_p) +{ + void * pBuf = PNBUFF_2_PBUF(pNBuff); + if ( pBuf == (void*) NULL ) + return pBuf; + if ( IS_SKBUFF_PTR(pNBuff) ) + { + *data_p = ((struct sk_buff *)pBuf)->data; + *len_p = ((struct sk_buff *)pBuf)->len; + *mark_p = ((struct sk_buff *)pBuf)->mark; + *priority_p = ((struct sk_buff *)pBuf)->priority; + } + else + { + *data_p = ((FkBuff_t *)pBuf)->data; + *len_p = ((FkBuff_t *)pBuf)->len; + *mark_p = ((FkBuff_t *)pBuf)->mark; + *priority_p = ((FkBuff_t *)pBuf)->priority; + } + fkb_dbg(1, "pNBuff<%p> pBuf<%p> data_p<%p>", + pNBuff, pBuf, *data_p ); + return pBuf; +} + +/* adds recycle flags/context to nbuff_get_params used in impl4 enet */ +/* + * Function : nbuff_get_params_ext + * Description: Extracts the data, len, mark, priority and + * recycle flags/context field from a network buffer. + */ +static inline void * nbuff_get_params_ext(pNBuff_t pNBuff, uint8_t **data_p, + uint32_t *len_p, uint32_t *mark_p, + uint32_t *priority_p, + uint32_t *rflags_p) +{ + void * pBuf = PNBUFF_2_PBUF(pNBuff); + if ( pBuf == (void*) NULL ) + return pBuf; + if ( IS_SKBUFF_PTR(pNBuff) ) + { + *data_p = ((struct sk_buff *)pBuf)->data; + *len_p = ((struct sk_buff *)pBuf)->len; + *mark_p = ((struct sk_buff *)pBuf)->mark; + *priority_p = ((struct sk_buff *)pBuf)->priority; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + *rflags_p = ((struct sk_buff *)pBuf)->recycle_flags; +#endif + } + else + { + *data_p = ((FkBuff_t *)pBuf)->data; + *len_p = ((FkBuff_t *)pBuf)->len; + *mark_p = ((FkBuff_t *)pBuf)->mark; + *priority_p = ((FkBuff_t *)pBuf)->priority; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + *rflags_p = ((FkBuff_t *)pBuf)->recycle_context; +#endif + } + fkb_dbg(1, "pNBuff<%p> pBuf<%p> data_p<%p>", + pNBuff, pBuf, *data_p ); + return pBuf; +} + +/* + *------------------------------------------------------------------------------ + * Virtual common functional apis of a network kernel buffer + *------------------------------------------------------------------------------ + */ + +/* + * Function : nbuff_push + * Description: Make space at the start of a network buffer. + * CAUTION : In the case of a FKB, no check for headroom is done. + */ +static inline uint8_t * nbuff_push(pNBuff_t pNBuff, uint32_t len) +{ + uint8_t * data; + void * pBuf = PNBUFF_2_PBUF(pNBuff); + if ( IS_SKBUFF_PTR(pNBuff) ) + data = skb_push(((struct sk_buff *)pBuf), len); + /* else if IS_FPBUFF_PTR, else if IS_TGBUFF_PTR */ + else + data = fkb_push((FkBuff_t*)pBuf, len); + fkb_dbg(1, "pNBuff<%p> pBuf<%p> data<%p> len<%u>", + pNBuff, pBuf, data, len ); + return data; +} + +/* + * Function : nbuff_pull + * Description: Delete data from start of a network buffer. + */ +static inline uint8_t * nbuff_pull(pNBuff_t pNBuff, uint32_t len) +{ + uint8_t * data; + void * pBuf = PNBUFF_2_PBUF(pNBuff); + if ( IS_SKBUFF_PTR(pNBuff) ) + data = skb_pull(((struct sk_buff *)pBuf), len); + /* else if IS_FPBUFF_PTR, else if IS_TGBUFF_PTR */ + else + data = fkb_pull((FkBuff_t *)pBuf, len); + fkb_dbg(1, "pNBuff<%p> pBuf<%p> data<%p> len<%u>", + pNBuff, pBuf, data, len ); + return data; +} + +/* + * Function : nbuff_put + * Description: Make space at the tail of a network buffer. + * CAUTION: In the case of a FKB, no check for tailroom is done. + */ +static inline uint8_t * nbuff_put(pNBuff_t pNBuff, uint32_t len) +{ + uint8_t * tail; + void * pBuf = PNBUFF_2_PBUF(pNBuff); + if ( IS_SKBUFF_PTR(pNBuff) ) + tail = skb_put(((struct sk_buff *)pBuf), len); + /* else if IS_FPBUFF_PTR, else if IS_TGBUFF_PTR */ + else + tail = fkb_put((FkBuff_t *)pBuf, len); + fkb_dbg(1, "pNBuff<%p> pBuf<%p> tail<%p> len<%u>", + pNBuff, pBuf, tail, len ); + return tail; +} + +extern void dev_kfree_skb_thread(struct sk_buff *skb); +extern void nbuff_free_ex(pNBuff_t pNBuff, int in_thread); +extern void nbuff_free(pNBuff_t pNBuff); + +/* + * Function : nbuff_unshare + * Description: If there are more than one references to the data buffer + * associated with the network buffer, create a deep copy of the data buffer + * and return a network buffer context to it. The returned network buffer + * may be then used to modify the data packet without impacting the original + * network buffer and its data buffer. + * + * If the data packet had a single network buffer referencing it, then the + * original network buffer is returned. + */ +static inline pNBuff_t nbuff_unshare(pNBuff_t pNBuff) +{ + void * pBuf = PNBUFF_2_PBUF(pNBuff); + fkb_dbg(1, "pNBuff<%p> pBuf<%p>", pNBuff, pBuf); + if ( IS_SKBUFF_PTR(pNBuff) ) + { + struct sk_buff *skb_p; + skb_p = skb_unshare( (struct sk_buff *)pBuf, GFP_ATOMIC); + pNBuff = SKBUFF_2_PNBUFF(skb_p); + } + else + { + FkBuff_t * fkb_p; + fkb_p = fkb_unshare( (FkBuff_t *)pBuf ); + pNBuff = FKBUFF_2_PNBUFF(fkb_p); + } + + fkb_dbg(2, "<<"); + return pNBuff; +} + +/* + * Function : nbuff_invalidate_headroom + * Description: invalidate datacache lines of memory prefixing "data" pointer. + * Invalidation does not include the dcache line "data" is in. This dcache line + * must be flushed, not invalidated. + */ +static inline void nbuff_invalidate_headroom(pNBuff_t pNBuff, uint8_t * data) +{ + + /* Invalidate functions used here will round up end pointer to cache line + * boundry. That's the reason for L1_CACHE_BYTES substruction. + */ + int32_t inv_len = 0; + fkb_dbg(1, "pNBuff<0x%08x> data<0x%08x>", (int)pNBuff, (int)data); + + if ( IS_SKBUFF_PTR(pNBuff) ) + { + inv_len = skb_avail_headroom( PNBUFF_2_SKBUFF(pNBuff) ) - L1_CACHE_BYTES; + cache_invalidate_region(PNBUFF_2_SKBUFF(pNBuff)->head, data - L1_CACHE_BYTES); + } + else + { + FkBuff_t * fkb_p = (FkBuff_t *)PNBUFF_2_PBUF(pNBuff); + + if ( _is_fkb_cloned_pool_(fkb_p) ) + fkb_p = fkb_p->master_p; + + inv_len = data - PFKBUFF_TO_PHEAD(fkb_p) - L1_CACHE_BYTES; + fkb_flush(fkb_p, PFKBUFF_TO_PHEAD(fkb_p), inv_len, FKB_CACHE_INV); + } + fkb_dbg(1, " len<%d>", inv_len); + fkb_dbg(2, "<<"); +} + +extern void nbuff_flush(pNBuff_t pNBuff, uint8_t * data, int len); +extern void nbuff_flushfree(pNBuff_t pNBuff); + +/* + * Function : nbuff_xlate + * Description: Convert a FKB to a SKB. The SKB is data filled with the + * data, len, mark, priority, and recycle hook and context. + * + * Other SKB fields for SKB API manipulation are also initialized. + * SKB fields for network stack manipulation are NOT initialized. + * + * This function is typically used only in a network device drivers' hard + * start xmit function handler. A hard start xmit function handler may receive + * a network buffer of a FKB type and may not wish to rework the implementation + * to use nbuff APIs. In such an event, a nbuff may be translated to a skbuff. + */ +struct sk_buff * fkb_xlate(FkBuff_t * fkb_p); +static inline struct sk_buff * nbuff_xlate( pNBuff_t pNBuff ) +{ + void * pBuf = PNBUFF_2_PBUF(pNBuff); + fkb_dbg(1, "pNBuff<%p> pBuf<%p>", pNBuff, pBuf); + + if ( IS_SKBUFF_PTR(pNBuff) ) + return (struct sk_buff *)pBuf; + /* else if IS_FPBUFF_PTR, else if IS_TGBUFF_PTR */ + else + return fkb_xlate( (FkBuff_t *)pBuf ); +} + + +/* Miscellaneous helper routines */ +static inline void u16cpy( void * dst_p, const void * src_p, uint32_t bytes ) +{ + uint16_t * dst16_p = (uint16_t*)dst_p; + uint16_t * src16_p = (uint16_t*)src_p; + do { // assuming: (bytes % sizeof(uint16_t) == 0 !!! + *dst16_p++ = *src16_p++; + } while ( bytes -= sizeof(uint16_t) ); +} + +static inline void u16datacpy( void * dst_p, const void * src_p, uint32_t bytes ) +{ + uint16_t * dst16_p = (uint16_t*)dst_p; + uint16_t * src16_p = (uint16_t*)src_p; + do { // assuming: (bytes % sizeof(uint16_t) == 0 !!! + *dst16_p++ = htons (*src16_p++); + } while ( bytes -= sizeof(uint16_t) ); +} + +static inline int u16cmp( void * dst_p, const void * src_p, + uint32_t bytes ) +{ + uint16_t * dst16_p = (uint16_t*)dst_p; + uint16_t * src16_p = (uint16_t*)src_p; + do { // assuming: (bytes % sizeof(uint16_t) == 0 !!! + if ( *dst16_p++ != *src16_p++ ) + return -1; + } while ( bytes -= sizeof(uint16_t) ); + + return 0; +} + +static inline int nbuff_pad(pNBuff_t pNBuff, int padLen) +{ + if ( IS_SKBUFF_PTR(pNBuff) ) + { + skb_pad((struct sk_buff *)pNBuff, padLen); + } + else + { + fkb_pad(PNBUFF_2_FKBUFF(pNBuff), padLen); + } + return 0; +} + +#ifdef DUMP_DATA +/* dumpHexData dump out the hex base binary data */ +static inline void dumpHexData1(uint8_t *pHead, uint32_t len) +{ + uint32_t i; + uint8_t *c = pHead; + for (i = 0; i < len; ++i) { + if (i % 16 == 0) + printk("\n"); + printk("0x%02X, ", *c++); + } + printk("\n"); +} + +static inline void dump_pkt(const char * fname, uint8_t * pBuf, uint32_t len) +{ + //int dump_len = ( len < 64) ? len : 64; + int dump_len = len ; + printk("%s: data<0x%lu len<%u>", fname, (uintptr_t)pBuf, len); + dumpHexData1(pBuf, dump_len); + cache_flush_len((void*)pBuf, dump_len); +} +#define DUMP_PKT(pBuf,len) dump_pkt(__FUNCTION__, (pBuf), (len)) +#else /* !defined(DUMP_DATA) */ +#define DUMP_PKT(pBuf,len) do {} while(0) +#endif + +#endif /* defined(__NBUFF_H_INCLUDED__) */ + +#endif diff -urN linux-4.1.52-orig/include/linux/nbuff_types.h linux-4.1.52/include/linux/nbuff_types.h --- linux-4.1.52-orig/include/linux/nbuff_types.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/nbuff_types.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,70 @@ +#if defined(CONFIG_BCM_KF_NBUFF) +#ifndef __NBUFF_TYPES_H_INCLUDED__ +#define __NBUFF_TYPES_H_INCLUDED__ + +/* +<:copyright-BRCM:2013:DUAL/GPL:standard + + Copyright (c) 2013 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + +/* + ******************************************************************************* + * + * File Name : nbuff_types.h + * Description: Simple nbuff type defines. + * + ******************************************************************************* */ + +#define MUST_BE_ZERO 0 + +/* virtual network buffer pointer to SKB|FPB|TGB|FKB */ +typedef void * pNBuff_t; +#define PNBUFF_NULL ((pNBuff_t)NULL) + +typedef enum NBuffPtrType +{ + SKBUFF_PTR = MUST_BE_ZERO, /* Default Linux networking socket buffer */ + FPBUFF_PTR, /* Experimental BRCM IuDMA freepool buffer*/ + TGBUFF_PTR, /* LAB Traffic generated network buffer */ + FKBUFF_PTR, /* Lightweight fast kernel network buffer */ + /* Do not add new ptr types */ +} NBuffPtrType_t; + + /* 2lsbits in pointer encode NbuffType_t */ +#define NBUFF_TYPE_MASK 0x3ul +#define NBUFF_PTR_MASK (~NBUFF_TYPE_MASK) +#define NBUFF_PTR_TYPE(pNBuff) ((uintptr_t)(pNBuff) & NBUFF_TYPE_MASK) + + +#define IS_SKBUFF_PTR(pNBuff) ( NBUFF_PTR_TYPE(pNBuff) == SKBUFF_PTR ) +#define IS_FPBUFF_PTR(pNBuff) ( NBUFF_PTR_TYPE(pNBuff) == FPBUFF_PTR ) +#define IS_TGBUFF_PTR(pNBuff) ( NBUFF_PTR_TYPE(pNBuff) == TGBUFF_PTR ) +#define IS_FKBUFF_PTR(pNBuff) ( NBUFF_PTR_TYPE(pNBuff) == FKBUFF_PTR ) + + +#endif /* defined(__NBUFF_TYPES_H_INCLUDED__) */ +#endif diff -urN linux-4.1.52-orig/include/linux/netdev_features.h linux-4.1.52/include/linux/netdev_features.h --- linux-4.1.52-orig/include/linux/netdev_features.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/netdev_features.h 2020-02-07 01:47:54.000000000 +0800 @@ -68,6 +68,10 @@ NETIF_F_BUSY_POLL_BIT, /* Busy poll */ NETIF_F_HW_SWITCH_OFFLOAD_BIT, /* HW switch offload */ +#if defined(CONFIG_BCM_KF_EXTSTATS) + NETIF_F_EXTSTATS_BIT, /* Support extended statistics */ +#endif + /* * Add your fresh new feature above and remember to update * netdev_features_strings[] in net/core/ethtool.c and maybe @@ -127,6 +131,10 @@ #define NETIF_F_BUSY_POLL __NETIF_F(BUSY_POLL) #define NETIF_F_HW_SWITCH_OFFLOAD __NETIF_F(HW_SWITCH_OFFLOAD) +#if defined(CONFIG_BCM_KF_EXTSTATS) +#define NETIF_F_EXTSTATS __NETIF_F(EXTSTATS) +#endif + /* Features valid for ethtool to change */ /* = all defined minus driver/device-class-related */ #define NETIF_F_NEVER_CHANGE (NETIF_F_VLAN_CHALLENGED | \ diff -urN linux-4.1.52-orig/include/linux/netdevice.h linux-4.1.52/include/linux/netdevice.h --- linux-4.1.52-orig/include/linux/netdevice.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/netdevice.h 2020-02-07 01:47:54.000000000 +0800 @@ -48,6 +48,10 @@ #include #include +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#include +#include +#endif #include #include #include @@ -178,6 +182,16 @@ unsigned long tx_window_errors; unsigned long rx_compressed; unsigned long tx_compressed; +#if defined(CONFIG_BCM_KF_EXTSTATS) + unsigned long tx_multicast_packets; /* multicast packets transmitted */ + unsigned long rx_multicast_bytes; /* multicast bytes recieved */ + unsigned long tx_multicast_bytes; /* multicast bytes transmitted */ + unsigned long rx_broadcast_packets; /* broadcast packets recieved */ + unsigned long tx_broadcast_packets; /* broadcast packets transmitted */ + /* NOTE: Unicast packets are not counted but are instead calculated as needed + using total - (broadcast + multicast) */ + unsigned long rx_unknown_packets; /* unknown protocol packets recieved */ +#endif }; @@ -1050,6 +1064,10 @@ int (*ndo_stop)(struct net_device *dev); netdev_tx_t (*ndo_start_xmit) (struct sk_buff *skb, struct net_device *dev); +#if defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_QOS_CPU) + int (*ndo_dpi_enqueue)(struct sk_buff *skb, + struct net_device *dev); +#endif u16 (*ndo_select_queue)(struct net_device *dev, struct sk_buff *skb, void *accel_priv, @@ -1210,6 +1228,34 @@ int (*ndo_get_iflink)(const struct net_device *dev); }; +#if defined(CONFIG_BCM_KF_NETDEV_PATH) +#define NETDEV_PATH_HW_SUBPORTS_MAX CONFIG_BCM_MAX_GEM_PORTS +struct netdev_path +{ + /* this pointer is used to create lists of interfaces that belong + to the same interface path in Linux. It points to the next + interface towards the physical interface (the root interface) */ + struct net_device *next_dev; + /* this reference counter indicates the number of interfaces + referencing this interface */ + int refcount; + /* indicates the RX hardware port number associated to the + interface */ + unsigned int hw_port; + /* indicates the TX hardware port number associated to the + interface */ + unsigned int hw_tx_port; + /* hardware port type, must be set to one of the types defined in + BlogPhy_t */ + unsigned int hw_port_type; + /* some device drivers support virtual subports within a hardware + port. hw_subport_mcast is used to map a multicast hw subport + to a hw port. */ + unsigned int hw_subport_mcast_idx; +}; +#endif + + /** * enum net_device_priv_flags - &struct net_device priv_flags * @@ -1271,6 +1317,28 @@ IFF_XMIT_DST_RELEASE_PERM = 1<<22, IFF_IPVLAN_MASTER = 1<<23, IFF_IPVLAN_SLAVE = 1<<24, +#if defined(CONFIG_BCM_KF_WL) + IFF_BCM_WLANDEV = 1<<25, /* Broadcom WLAN Interface */ +#endif +#if defined(CONFIG_BCM_KF_WANDEV) + IFF_WANDEV = 1<<26, /* avoid WAN bridge traffic leaking */ +#endif +#if defined(CONFIG_BCM_KF_VLAN) + IFF_BCM_VLAN = 1<<27, /* Broadcom VLAN Interface */ +#endif +#if defined(CONFIG_BCM_KF_PPP) + IFF_PPP =1<<28, /* PPP Interface */ +#endif +#if defined(CONFIG_BCM_KF_RUNNER) + IFF_RNR =1<<29, /* Interface operated by Runner. */ +#endif +#if defined(CONFIG_BCM_KF_ENET_SWITCH) + IFF_HW_SWITCH =1<<30, + IFF_EXT_SWITCH =1<<31, /* Indicates the interface is an external switch interface */ + +#endif /* CONFIG_BCM_KF_ENET_SWITCH */ + + }; #define IFF_802_1Q_VLAN IFF_802_1Q_VLAN @@ -1555,6 +1623,36 @@ struct net_device_stats stats; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + BlogStats_t blog_stats; /* Cummulative stats of accelerated flows */ + unsigned int blog_stats_flags; /* Blog stats collection property for the device */ + +#define BLOG_DEV_STAT_FLAG_INCLUDE_SW_UC (1<<0) /* Include SW accelerated Unicast stats */ +#define BLOG_DEV_STAT_FLAG_INCLUDE_HW_UC (1<<1) /* Include HW accelerated Unicast stats */ +#define BLOG_DEV_STAT_FLAG_INCLUDE_SW_MC (1<<2) /* Include SW accelerated Multicast stats */ +#define BLOG_DEV_STAT_FLAG_INCLUDE_HW_MC (1<<3) /* Include HW accelerated Multicast stats */ +#define BLOG_DEV_STAT_FLAG_INCLUDE_SW (BLOG_DEV_STAT_FLAG_INCLUDE_SW_UC|BLOG_DEV_STAT_FLAG_INCLUDE_SW_MC) +#define BLOG_DEV_STAT_FLAG_INCLUDE_HW (BLOG_DEV_STAT_FLAG_INCLUDE_HW_UC|BLOG_DEV_STAT_FLAG_INCLUDE_HW_MC) +#define BLOG_DEV_STAT_FLAG_INCLUDE_ALL (BLOG_DEV_STAT_FLAG_INCLUDE_SW|BLOG_DEV_STAT_FLAG_INCLUDE_HW) + + /* Clear the stats information */ + void (*clr_stats)(struct net_device *dev_p); /* This should not be tied to BLOG anymore ?*/ + + /* CAUTION : put_stats and get_stats_pointer MUST only be used by WLAN */ + /* Update the bstats */ + void (*put_stats)(struct net_device *dev_p, BlogStats_t * bStats_p); + /* Get stats pointer by type */ + void* (*get_stats_pointer)(struct net_device *dev_p, char type); + + /* runner multicast acceleration hook,to be enclosed in different MACRO??? */ + wlan_client_get_info_t wlan_client_get_info; +#endif + + +#if defined(CONFIG_BCM_KF_NETDEV_PATH) + struct netdev_path path; +#endif + atomic_long_t rx_dropped; atomic_long_t tx_dropped; @@ -2914,6 +3012,21 @@ } #endif +#if (defined(CONFIG_BCM_KF_FAP_GSO_LOOPBACK) && defined(CONFIG_BCM_FAP_GSO_LOOPBACK)) +typedef enum { +BCM_GSO_LOOPBACK_NONE=0, /*null device for error protection*/ +BCM_GSO_LOOPBACK_WL0, /* wlan interface 0 */ +BCM_GSO_LOOPBACK_WL1, /* wlan interface 1 */ +BCM_GSO_LOOPBACK_WL2, /* wlan interface 2 */ +BCM_GSO_LOOPBACK_MAXDEVS +} gso_loopback_devids; + +extern int (*bcm_gso_loopback_hw_offload)(struct sk_buff *skb, unsigned int txDevId); +extern inline unsigned int bcm_is_gso_loopback_dev(void *dev); +extern unsigned int bcm_gso_loopback_devptr2devid(void *dev); +extern struct net_device * bcm_gso_loopback_devid2devptr(unsigned int devId); +#endif + #ifdef CONFIG_SYSFS static inline unsigned int get_netdev_rx_queue_index( struct netdev_rx_queue *queue) @@ -2935,6 +3048,15 @@ }; void __dev_kfree_skb_irq(struct sk_buff *skb, enum skb_free_reason reason); + +#if defined(CONFIG_BCM_KF_SKB_DEFINES) && defined(CONFIG_SMP) +/* put the skb on a queue, and wake up the skbfreeTask to free it later, + * to save some cyles now + */ +extern void dev_kfree_skb_thread(struct sk_buff *skb); +#endif + + void __dev_kfree_skb_any(struct sk_buff *skb, enum skb_free_reason reason); /* @@ -3830,6 +3952,65 @@ extern struct pernet_operations __net_initdata loopback_net_ops; +#if defined(CONFIG_BCM_KF_NETDEV_PATH) + +/* Returns TRUE when _dev is a member of a path, otherwise FALSE */ +#define netdev_path_is_linked(_dev) ( (_dev)->path.next_dev != NULL ) + +/* Returns TRUE when _dev is the leaf in a path, otherwise FALSE */ +#define netdev_path_is_leaf(_dev) ( (_dev)->path.refcount == 0 ) + +/* Returns TRUE when _dev is the root of a path, otherwise FALSE. The root + device is the physical device */ +#define netdev_path_is_root(_dev) ( (_dev)->path.next_dev == NULL ) + +/* Returns a pointer to the next device in a path, towards the root + (physical) device */ +#define netdev_path_next_dev(_dev) ( (_dev)->path.next_dev ) + +#define netdev_path_set_hw_port(_dev, _hw_port, _hw_port_type) \ + do { \ + (_dev)->path.hw_port = (_hw_port); \ + (_dev)->path.hw_tx_port = (_hw_port); \ + (_dev)->path.hw_port_type = (_hw_port_type); \ + } while(0) + +#define netdev_path_set_hw_port_only(_dev, _hw_port) \ + do { \ + (_dev)->path.hw_port = (_hw_port); \ + } while(0) + +#define netdev_path_set_hw_tx_port_only(_dev, _hw_port) \ + do { \ + (_dev)->path.hw_tx_port = (_hw_port); \ + } while(0) + +#define netdev_path_get_hw_port(_dev) ( (_dev)->path.hw_port ) +#define netdev_path_get_hw_tx_port(_dev) ( (_dev)->path.hw_tx_port ) + +#define netdev_path_get_hw_port_type(_dev) ( (_dev)->path.hw_port_type ) + +#define netdev_path_get_hw_subport_mcast_idx(_dev) ( (_dev)->path.hw_subport_mcast_idx ) + +static inline struct net_device *netdev_path_get_root(struct net_device *dev) +{ + for (; !netdev_path_is_root(dev); dev = netdev_path_next_dev(dev)); + return dev; +} + +int netdev_path_add(struct net_device *new_dev, struct net_device *next_dev); + +int netdev_path_remove(struct net_device *dev); + +void netdev_path_dump(struct net_device *dev); + +int netdev_path_set_hw_subport_mcast_idx(struct net_device *dev, + unsigned int subport_idx); + +#endif /* CONFIG_BCM_KF_NETDEV_PATH */ + + + /* Logging, debugging and troubleshooting/diagnostic helpers. */ /* netdev_printk helpers, similar to dev_printk */ diff -urN linux-4.1.52-orig/include/linux/netfilter/nf_conntrack_h323.h linux-4.1.52/include/linux/netfilter/nf_conntrack_h323.h --- linux-4.1.52-orig/include/linux/netfilter/nf_conntrack_h323.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/netfilter/nf_conntrack_h323.h 2020-02-07 01:47:54.000000000 +0800 @@ -12,6 +12,23 @@ /* This structure exists only once per master */ struct nf_ct_h323_master { + +#if defined(CONFIG_BCM_KF_NETFILTER) + enum{ + /* tpkt header and payload are wrapped in one packet */ + DIVTYPE_NORMAL = 0x00, + /* tpkt header is in the first packet and payload is the + * next one + */ + DIVTYPE_TPKTHDR = 0x01, + /* tpkt packet (size maybe is more than several kbytes) is + * seperated into several parts by the tcp protocol. This + * dividing method is different from the second one. + */ + DIVTYPE_Q931 = 0x02, + }div_type[IP_CT_DIR_MAX]; +#endif + /* Original and NATed Q.931 or H.245 signal ports */ __be16 sig_port[IP_CT_DIR_MAX]; @@ -29,6 +46,11 @@ struct nf_conn; +#if defined(CONFIG_BCM_KF_NETFILTER) +extern int have_direct_route(union nf_inet_addr *src, union nf_inet_addr *dst, + int family); +#endif + int get_h225_addr(struct nf_conn *ct, unsigned char *data, TransportAddress *taddr, union nf_inet_addr *addr, __be16 *port); @@ -36,6 +58,10 @@ struct nf_conntrack_expect *this); void nf_conntrack_q931_expect(struct nf_conn *new, struct nf_conntrack_expect *this); +#if defined(CONFIG_BCM_KF_NETFILTER) +extern int (*set_addr_bf_hook)(struct sk_buff **pskb, + unsigned char **data, int datalen, int dataoff); +#endif extern int (*set_h245_addr_hook) (struct sk_buff *skb, unsigned int protoff, unsigned char **data, int dataoff, H245_TransportAddress *taddr, diff -urN linux-4.1.52-orig/include/linux/netfilter/nf_conntrack_ipsec.h linux-4.1.52/include/linux/netfilter/nf_conntrack_ipsec.h --- linux-4.1.52-orig/include/linux/netfilter/nf_conntrack_ipsec.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/netfilter/nf_conntrack_ipsec.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,45 @@ +#if defined(CONFIG_BCM_KF_PROTO_IPSEC) +/* IPSEC constants and structs */ +#ifndef _NF_CONNTRACK_IPSEC_H +#define _NF_CONNTRACK_IPSEC_H + +#include + +/* conntrack private data */ +struct nf_ct_ipsec_master +{ + __be32 initcookie; /* initcookie of ISAKMP */ + __be32 lan_ip; /* LAN IP */ +}; + +struct nf_nat_ipsec +{ + __be32 lan_ip; /* LAN IP */ +}; + +#ifdef __KERNEL__ + +#define IPSEC_PORT 500 +#define MAX_VPN_CONNECTION 8 + +struct isakmp_pkt_hdr +{ + __be32 initcookie; +}; + + +/* crap needed for nf_conntrack_compat.h */ +struct nf_conn; +struct nf_conntrack_expect; + +extern int +(*nf_nat_ipsec_hook_outbound)(struct sk_buff *skb, + struct nf_conn *ct, enum ip_conntrack_info ctinfo); + +extern int +(*nf_nat_ipsec_hook_inbound)(struct sk_buff *skb, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, __be32 lan_ip); + +#endif /* __KERNEL__ */ +#endif /* _NF_CONNTRACK_IPSEC_H */ +#endif diff -urN linux-4.1.52-orig/include/linux/netfilter/nf_conntrack_proto_esp.h linux-4.1.52/include/linux/netfilter/nf_conntrack_proto_esp.h --- linux-4.1.52-orig/include/linux/netfilter/nf_conntrack_proto_esp.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/netfilter/nf_conntrack_proto_esp.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,22 @@ +#if defined(CONFIG_BCM_KF_PROTO_ESP) +#ifndef _CONNTRACK_PROTO_ESP_H +#define _CONNTRACK_PROTO_ESP_H +#include + +/* ESP PROTOCOL HEADER */ + +struct esphdr { + __u32 spi; +}; + +struct nf_ct_esp { + unsigned int stream_timeout; + unsigned int timeout; +}; + +#ifdef __KERNEL__ +#include + +#endif /* __KERNEL__ */ +#endif /* _CONNTRACK_PROTO_ESP_H */ +#endif diff -urN linux-4.1.52-orig/include/linux/netfilter/nf_conntrack_rtsp.h linux-4.1.52/include/linux/netfilter/nf_conntrack_rtsp.h --- linux-4.1.52-orig/include/linux/netfilter/nf_conntrack_rtsp.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/netfilter/nf_conntrack_rtsp.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,94 @@ +#if defined(CONFIG_BCM_KF_NETFILTER) + +/* +* <:copyright-BRCM:2012:DUAL/GPL:standard +* +* Copyright (c) 2012 Broadcom +* All Rights Reserved +* +* Unless you and Broadcom execute a separate written software license +* agreement governing use of this software, this software is licensed +* to you under the terms of the GNU General Public License version 2 +* (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +* with the following added to such license: +* +* As a special exception, the copyright holders of this software give +* you permission to link this software with independent modules, and +* to copy and distribute the resulting executable under terms of your +* choice, provided that you also meet, for each linked independent +* module, the terms and conditions of the license of that module. +* An independent module is a module which is not derived from this +* software. The special exception does not apply to any modifications +* of the software. +* +* Not withstanding the above, under no circumstances may you combine +* this software in any way with any other Broadcom software provided +* under a license other than the GPL, without Broadcom's express prior +* written consent. +* +:> +*/ + +#ifndef _NF_CONNTRACK_RTSP_H +#define _NF_CONNTRACK_RTSP_H + +#ifdef __KERNEL__ + +/* This structure exists only once per master */ +struct nf_ct_rtsp_master { + /* The client has sent PAUSE message and not replied */ + int paused; +}; + +/* Single data channel */ +extern int (*nat_rtsp_channel_hook) (struct sk_buff *skb, + unsigned int protoff, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int matchoff, + unsigned int matchlen, + struct nf_conntrack_expect *exp, + int *delta); + +/* A pair of data channels (RTP/RTCP) */ +extern int (*nat_rtsp_channel2_hook) (struct sk_buff *skb, + unsigned int protoff, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int matchoff, + unsigned int matchlen, + struct nf_conntrack_expect *rtp_exp, + struct nf_conntrack_expect *rtcp_exp, + char dash, int *delta); + +/* Modify parameters like client_port in Transport for single data channel */ +extern int (*nat_rtsp_modify_port_hook) (struct sk_buff *skb, + unsigned int protoff, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int matchoff, + unsigned int matchlen, + __be16 rtpport, int *delta); + +/* Modify parameters like client_port in Transport for multiple data channels*/ +extern int (*nat_rtsp_modify_port2_hook) (struct sk_buff *skb, + unsigned int protoff, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int matchoff, + unsigned int matchlen, + __be16 rtpport, __be16 rtcpport, + char dash, int *delta); + +/* Modify parameters like destination in Transport */ +extern int (*nat_rtsp_modify_addr_hook) (struct sk_buff *skb, + unsigned int protoff, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + int matchoff, int matchlen, + int *delta); +#endif /* __KERNEL__ */ + +#endif /* _NF_CONNTRACK_RTSP_H */ + +#endif diff -urN linux-4.1.52-orig/include/linux/netfilter/nf_conntrack_sip.h linux-4.1.52/include/linux/netfilter/nf_conntrack_sip.h --- linux-4.1.52-orig/include/linux/netfilter/nf_conntrack_sip.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/netfilter/nf_conntrack_sip.h 2020-02-07 01:47:54.000000000 +0800 @@ -9,6 +9,7 @@ #define SIP_PORT 5060 #define SIP_TIMEOUT 3600 + struct nf_ct_sip_master { unsigned int register_cseq; unsigned int invite_cseq; @@ -194,6 +195,5 @@ enum sdp_header_types type, enum sdp_header_types term, unsigned int *matchoff, unsigned int *matchlen); - #endif /* __KERNEL__ */ #endif /* __NF_CONNTRACK_SIP_H__ */ diff -urN linux-4.1.52-orig/include/linux/netfilter/xt_layer7.h linux-4.1.52/include/linux/netfilter/xt_layer7.h --- linux-4.1.52-orig/include/linux/netfilter/xt_layer7.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/netfilter/xt_layer7.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,21 @@ +#if defined(CONFIG_BCM_KF_XT_MATCH_LAYER7) +#ifndef _XT_LAYER7_H +#define _XT_LAYER7_H + +#if 0 +#define MAX_PATTERN_LEN 8192 +#define MAX_PROTOCOL_LEN 256 +#else +#define MAX_PATTERN_LEN 256 +#define MAX_PROTOCOL_LEN 64 +#endif + +struct xt_layer7_info { + char protocol[MAX_PROTOCOL_LEN]; + char pattern[MAX_PATTERN_LEN]; + u_int8_t invert; + u_int8_t pkt; +}; + +#endif /* _XT_LAYER7_H */ +#endif diff -urN linux-4.1.52-orig/include/linux/netlink.h linux-4.1.52/include/linux/netlink.h --- linux-4.1.52-orig/include/linux/netlink.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/netlink.h 2020-02-07 01:47:54.000000000 +0800 @@ -108,11 +108,15 @@ * use enormous buffer sizes on recvmsg() calls just to avoid * MSG_TRUNC when PAGE_SIZE is very large. */ +#if defined(CONFIG_BCM_KF_512MB_DDR) && defined(CONFIG_BCM_512MB_DDR) +#define NLMSG_GOODSIZE SKB_WITH_OVERHEAD(4096UL) +#else #if PAGE_SIZE < 8192UL #define NLMSG_GOODSIZE SKB_WITH_OVERHEAD(PAGE_SIZE) #else #define NLMSG_GOODSIZE SKB_WITH_OVERHEAD(8192UL) #endif +#endif #define NLMSG_DEFAULT_SIZE (NLMSG_GOODSIZE - NLMSG_HDRLEN) diff -urN linux-4.1.52-orig/include/linux/pci.h linux-4.1.52/include/linux/pci.h --- linux-4.1.52-orig/include/linux/pci.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/pci.h 2020-02-07 01:47:54.000000000 +0800 @@ -1326,6 +1326,9 @@ #ifdef CONFIG_PCI_DOMAINS extern int pci_domains_supported; int pci_get_new_domain_nr(void); +#ifdef CONFIG_BCM_KF_PCI_RESET_DOMAIN_NR +void pci_reset_domain_nr(void); +#endif /* CONFIG_BCM_KF_PCI_RESET_DOMAIN_NR */ #else enum { pci_domains_supported = 0 }; static inline int pci_domain_nr(struct pci_bus *bus) { return 0; } diff -urN linux-4.1.52-orig/include/linux/pfn.h linux-4.1.52/include/linux/pfn.h --- linux-4.1.52-orig/include/linux/pfn.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/pfn.h 2020-02-07 01:47:54.000000000 +0800 @@ -9,5 +9,11 @@ #define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) #define PFN_DOWN(x) ((x) >> PAGE_SHIFT) #define PFN_PHYS(x) ((phys_addr_t)(x) << PAGE_SHIFT) +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +/* this definition is missing in 4.1.y, needs to be brought + from master branch +*/ +#define PHYS_PFN(x) ((unsigned long)((x) >> PAGE_SHIFT)) +#endif #endif diff -urN linux-4.1.52-orig/include/linux/sched.h linux-4.1.52/include/linux/sched.h --- linux-4.1.52-orig/include/linux/sched.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/sched.h 2020-02-07 01:47:54.000000000 +0800 @@ -64,6 +64,7 @@ #define SCHED_ATTR_SIZE_VER0 48 /* sizeof first published struct */ + /* * Extended scheduling parameters data structure. * @@ -1342,6 +1343,10 @@ unsigned int policy; int nr_cpus_allowed; cpumask_t cpus_allowed; +#if defined(CONFIG_BCM_KF_CPU_AFFINITY_HINT) && \ + defined(CONFIG_BCM_PROC_CPU_AFFINITY_HINT) + cpumask_t cpus_hint; +#endif #ifdef CONFIG_PREEMPT_RCU int rcu_read_lock_nesting; diff -urN linux-4.1.52-orig/include/linux/skbuff.h linux-4.1.52/include/linux/skbuff.h --- linux-4.1.52-orig/include/linux/skbuff.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/skbuff.h 2020-02-07 01:47:54.000000000 +0800 @@ -30,6 +30,26 @@ #include #include #include + +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#include +#endif + +#if defined(CONFIG_BCM_KF_BPM_BUF_TRACKING) +#if defined(CONFIG_BCM_BPM_BUF_TRACKING) +#include +#define KERN_GBPM_TRACK_BUF(buf, value, info) GBPM_TRACK_BUF(buf, GBPM_DRV_KERN, value, info) +#define KERN_GBPM_TRACK_SKB(skb, value, info) GBPM_TRACK_SKB(skb, GBPM_DRV_KERN, value, info) +#define KERN_GBPM_TRACK_FKB(fkb, value, info) GBPM_TRACK_FKB(fkb, GBPM_DRV_KERN, value, info) +#else +#define KERN_GBPM_TRACK_BUF(buf, value, info) do{}while(0) +#define KERN_GBPM_TRACK_SKB(skb, value, info) do{}while(0) +#define KERN_GBPM_TRACK_FKB(fkb, value, info) do{}while(0) +#define GBPM_INC_REF(buf) do{}while(0) +#define GBPM_DEC_REF(buf) do{}while(0) +#endif +#endif + #include #include #include @@ -157,12 +177,95 @@ struct iov_iter; struct napi_struct; +#if defined(CONFIG_BCM_KF_MAP) +#define MAP_FORWARD_NONE 0 +#define MAP_FORWARD_MODE1 1 +#define MAP_FORWARD_MODE2 2 +#define MAP_FORWARD_MODE3 3 /* MAP-E Pre-Fragmentation */ +#endif + +#if defined(CONFIG_BCM_KF_NBUFF) +/* This is required even if blog is not defined, so it falls + under the nbuff catagory +*/ +struct blog_t; /* defined(CONFIG_BLOG) */ + +#ifndef NULL_STMT +#define NULL_STMT do { /* NULL BODY */ } while (0) +#endif + +typedef void (*RecycleFuncP)(void *nbuff_p, unsigned long context, uint32_t flags); +#define SKB_DATA_RECYCLE (1 << 0) +#define SKB_DATA_NO_RECYCLE (~SKB_DATA_RECYCLE) /* to mask out */ + +#define SKB_RECYCLE (1 << 1) +#define SKB_NO_RECYCLE (~SKB_RECYCLE) /* to mask out */ + +#define SKB_RECYCLE_NOFREE (1 << 2) /* DO NOT USE */ +#define SKB_RECYCLE_FPM_DATA (1 << 3) /* Data buffer from Runner FPM pool */ +#define SKB_RNR_FLOOD (1 << 4) /* Data buffer flooded by Runner to flooding-capable ports */ + +/* Indicates whether a sk_buf or a data buffer is in BPM pristine state */ +#define SKB_BPM_PRISTINE (1 << 5) + +/* UDP Speed Test flags */ +#define SKB_RNR_UDPSPDT_BASIC (1 << 6) +#define SKB_RNR_UDPSPDT_IPERF3 (1 << 7) + +#define SKB_RNR_FLAGS (SKB_RNR_FLOOD | SKB_RNR_UDPSPDT_BASIC | SKB_RNR_UDPSPDT_IPERF3) + +#define SKB_BPM_TAINTED(skb) \ +({ \ + ((struct sk_buff *)skb)->recycle_flags &= ~SKB_BPM_PRISTINE; \ + (skb_shinfo(skb))->dirty_p = NULL; \ +}) + + +#define SKB_DATA_PRISTINE(skb) \ +({ \ + (skb_shinfo(skb))->dirty_p = ((struct sk_buff *)skb)->head; \ +}) + +struct fkbuff; + +extern void skb_frag_xmit4(struct sk_buff *origskb, struct net_device *txdev, + uint32_t is_pppoe, uint32_t minMtu, void *ip_p); +extern void skb_frag_xmit6(struct sk_buff *origskb, struct net_device *txdev, + uint32_t is_pppoe, uint32_t minMtu, void *ip_p); +extern struct sk_buff * skb_xlate(struct fkbuff *fkb_p); +extern struct sk_buff * skb_xlate_dp(struct fkbuff *fkb_p, uint8_t *dirty_p); +extern int skb_avail_headroom(const struct sk_buff *skb); +extern void skb_bpm_tainted(struct sk_buff *skb); + +#if defined(CONFIG_BCM_KF_VLAN) && (defined(CONFIG_BCM_VLAN) || defined(CONFIG_BCM_VLAN_MODULE)) +#define SKB_VLAN_MAX_TAGS 4 +#endif + +#define CONFIG_SKBSHINFO_HAS_DIRTYP 1 +#endif // CONFIG_BCM_KF_NBUFF + #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) struct nf_conntrack { atomic_t use; }; #endif +#if defined(CONFIG_BCM_KF_WL) +struct nf_bridge_info { + atomic_t use; + enum { + BRNF_PROTO_UNCHANGED, + BRNF_PROTO_8021Q, + BRNF_PROTO_PPPOE + } orig_proto; + bool pkt_otherhost; + unsigned int mask; + struct net_device *physindev; + struct net_device *physoutdev; + char neigh_header[8]; + __be32 ipv4_daddr; +}; +#else #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) struct nf_bridge_info { atomic_t use; @@ -179,6 +282,7 @@ __be32 ipv4_daddr; }; #endif +#endif struct sk_buff_head { /* These two members must be first. */ @@ -314,6 +418,11 @@ * the end of the header data, ie. at skb->end. */ struct skb_shared_info { +#if defined(CONFIG_BCM_KF_NBUFF) + /* to preserve compat with binary only modules, do not change the + * position of this field relative to the start of the structure. */ + __u8 *dirty_p; +#endif /* defined(CONFIG_BCM_KF_NBUFF) */ unsigned char nr_frags; __u8 tx_flags; unsigned short gso_size; @@ -338,6 +447,19 @@ skb_frag_t frags[MAX_SKB_FRAGS]; }; +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) +typedef struct bl_buffer_info { + unsigned char *buffer; /* address of the buffer from bpm */ + unsigned char *packet; /* address of the data */ + unsigned int buffer_len; /* size of the buffer */ + unsigned int packet_len; /* size of the data packet */ + unsigned int buffer_number; /* the buffer location in the bpm */ + unsigned int port; /* the port */ +} bl_skbuff_info; +#endif /* CONFIG_BCM_RUNNER */ +#endif /* CONFIG_BCM_KF_RUNNER */ + /* We divide dataref into two halves. The higher 16 bits hold references * to the payload part of skb->data. The lower 16 bits hold references to * the entire skb->data. A clone of a headerless skb holds the length of @@ -448,8 +570,67 @@ return delta_us; } +#if defined(CONFIG_BCM_KF_NBUFF) +typedef union wlFlowInf +{ + uint32_t u32; + union { + union { + struct { + /* Start - Shared fields between ucast and mcast */ + uint32_t is_ucast:1; + /* wl_prio is 4 bits for nic and 3 bits for dhd. Plan is + to make NIC as 3 bits after more analysis */ + uint32_t wl_prio:4; + /* End - Shared fields between ucast and mcast */ + uint32_t nic_reserved1:11; + uint32_t wl_chainidx:16; + }; + struct { + uint32_t overlayed_field:16; + uint32_t ssid_dst:16; /* For bridged traffic we don't have chainidx (0xFE) */ + }; + } nic; + + struct { + /* Start - Shared fields between ucast and mcast */ + uint32_t is_ucast:1; + uint32_t wl_prio:4; + /* End - Shared fields between ucast and mcast */ + /* Start - Shared fields between dhd ucast and dhd mcast */ + uint32_t flowring_idx:10; + /* End - Shared fields between dhd ucast and dhd mcast */ + uint32_t dhd_reserved:13; + uint32_t ssid:4; + } dhd; + } ucast; + struct { + /* Start - Shared fields between ucast and mcast */ + /* for multicast, WFD does not need to populate this flowring_idx, it is used internally by dhd driver */ + uint32_t is_ucast:1; + uint32_t wl_prio:4; + /* End - Shared fields between ucast and mcast */ + /* Start - Shared fields between dhd ucast and dhd mcast */ + uint32_t flowring_idx:10; + /* End - Shared fields between dhd ucast and dhd mcast */ + uint32_t mcast_reserved:1; + uint32_t ssid_vector:16; + } mcast; + + struct { + /* Start - Shared fields b/w ucast, mcast & pktfwd */ + uint32_t is_ucast : 1; /* Start - Shared fields b/w ucast, mcast */ + uint32_t wl_prio : 4; /* packet priority */ + /* End - Shared fields between ucast, mcast & pktfwd */ + uint32_t pktfwd_reserved : 7; + uint32_t ssid : 4; + uint32_t pktfwd_key : 16; /* pktfwd_key_t : 2b domain, 2b incarn, 12b index */ + } pktfwd; + +} wlFlowInf_t; +#endif -/** +/** * struct sk_buff - socket buffer * @next: Next buffer in list * @prev: Previous buffer in list @@ -497,7 +678,7 @@ * @wifi_acked_valid: wifi_acked was set * @wifi_acked: whether frame was acked on wifi or not * @no_fcs: Request NIC to treat last 4 bytes as Ethernet FCS - * @napi_id: id of the NAPI struct this skb came from + * @napi_id: id of the NAPI struct this skb came from * @secmark: security marking * @mark: Generic packet mark * @vlan_proto: vlan encapsulation protocol @@ -533,28 +714,162 @@ }; struct sock *sk; struct net_device *dev; +#if defined(CONFIG_BCM_KF_NBUFF) + void *tunl; + + union { + /* 3 bytes unused */ + unsigned int recycle_and_rnr_flags; + unsigned int recycle_flags; + }; + + /* + * Several skb fields have been regrouped together for better data locality + * cache performance, 16byte cache line proximity. + * In 32 bit architecture, we have 32 bytes of data before this comment. + * In 64 bit architecture, we have 52 bytes of data at this point. + */ + + /*--- members common to fkbuff: begin here ---*/ + struct { + union { + /* see fkb_in_skb_test() */ + void *fkbInSkb; + struct sk_buff_head *list; + }; + + /* defined(CONFIG_BLOG), use blog_ptr() */ + struct blog_t *blog_p; + unsigned char *data; + + /* The len in fkb is only 24 bits other 8 bits are used as internal flags + * when fkbInSkb is used the max len can be only 24 bits, the bits 31-24 + * are cleared + * currently we don't have a case where len can be >24 bits. + */ + union { + unsigned int len; + /* used for fkb_in_skb test */ + __u32 len_word; + }; + + union { + __u32 mark; + __u32 dropcount; + void *queue; + /* have to declare the following variation of fkb_mark + * for the ease of handling 64 bit vs 32 bit in fcache + */ + unsigned long fkb_mark; + __u32 fc_ctxt; /* hybrid flow cache context */ + }; + union { + __u32 priority; + wlFlowInf_t wl; + }; + + /* Recycle preallocated skb or data */ + RecycleFuncP recycle_hook; + + union { + unsigned long recycle_context; + struct sk_buff *next_free; + __u32 fpm_num; + }; +#ifdef CONFIG_64BIT + } ____cacheline_aligned; + /* + * purposedly making the above fkbuff data structure cacheline aligned + * in 64 bit architecture. + * This can ensure the offset to the content is fixed into same cacheline. + * Main reason we only declare as cacheline_aligned for 64 bit is that + * we have manually calculated to ensure that this structure is 32 byte + * aligned in 32 bit architecture. If we add ____cacheline_aligned + * also for 32 bit architecture, it will waste 64 byte memory if that + * architecture is with 64 byte cache line size (i.e., 63148). + */ +#else + }; +#endif + /*--- members common to fkbuff: end here ---*/ + + struct nf_conntrack *nfct; /* CONFIG_NETFILTER */ + struct sk_buff *nfct_reasm; /* CONFIG_NF_CONNTRACK MODULE*/ + +/* + * ------------------------------- CAUTION!!! --------------------------------- + * Do NOT add a new field or modify any existing field before this line + * to the beginning of the struct sk_buff. Doing so will cause struct sk_buff + * to be incompatible with the compiled binaries and may cause the binary to + * crash. + * --------------------------------------------------------------------------- + */ +#endif /* * This is the control buffer. It is free to use for every * layer. Please put your private variables there. If you * want to keep them across layers you have to do a skb_clone() * first. This is owned by whoever has the skb queued ATM. */ +#if defined(CONFIG_BCM_KF_NBUFF) + char cb[64] ____cacheline_aligned; +#else char cb[48] __aligned(8); +#endif +#if defined(CONFIG_BCM_KF_WL) + union { + __u32 wl_cb[6]; + struct { + /* pktc_cb should hold space for void* and unsigned int */ + unsigned char pktc_cb[16]; +#if defined(CONFIG_CPU_BIG_ENDIAN) + __u16 dma_index; /* used by HND router for NIC Bulk Tx */ + __u16 pktc_flags; /* wl_flags */ + __u16 wl_flowid; /* cfp flowid */ + __u16 wl_rsvd; +#else /* !CONFIG_CPU_BIG_ENDIAN */ + __u16 pktc_flags; + __u16 dma_index; + __u16 wl_rsvd; + __u16 wl_flowid; +#endif /* !CONFIG_CPU_BIG_ENDIAN */ + }; + } __aligned(8); +#endif /* CONFIG_BCM_KF_WL */ unsigned long _skb_refdst; void (*destructor)(struct sk_buff *skb); +#if defined(CONFIG_BCM_KF_BIN_CONFIG_INDEP) + /* CONFIG_XFRM is toggled by BRCM_KERNEL_CRYPTO and + causes binary incompatible issue w/ the remaining offset shifted. + Enabled this field constantly. + */ + struct sec_path *sp; +#else #ifdef CONFIG_XFRM struct sec_path *sp; #endif +#endif /* CONFIG_BCM_KF_BIN_CONFIG_INDEP */ +#if defined(CONFIG_BCM_KF_NBUFF) +#else /* CONFIG_BCM_KF_NBUFF */ #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) struct nf_conntrack *nfct; #endif +#endif +#if defined(CONFIG_BCM_KF_WL) + struct nf_bridge_info *nf_bridge; +#else #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) struct nf_bridge_info *nf_bridge; #endif +#endif +#if defined(CONFIG_BCM_KF_NBUFF) + unsigned int data_len; +#else unsigned int len, data_len; +#endif __u16 mac_len, hdr_len; @@ -616,6 +931,12 @@ __u8 ipvs_property:1; __u8 inner_protocol_type:1; __u8 remcsum_offload:1; +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + __u8 map_forward:2; + __u8 map_mf:1; + __u32 map_offset; + __u32 map_id; +#endif /* 3 or 5 bit hole */ #ifdef CONFIG_NET_SCHED @@ -632,7 +953,10 @@ __u16 csum_offset; }; }; +#ifdef CONFIG_BCM_KF_NBUFF +#else __u32 priority; +#endif int skb_iif; __u32 hash; __be16 vlan_proto; @@ -646,11 +970,14 @@ #ifdef CONFIG_NETWORK_SECMARK __u32 secmark; #endif +#if defined(CONFIG_BCM_KF_NBUFF) + __u32 reserved_tailroom; +#else union { __u32 mark; __u32 reserved_tailroom; }; - +#endif /* CONFIG_BCM_KF_NBUFF */ union { __be16 inner_protocol; __u8 inner_ipproto; @@ -668,12 +995,80 @@ /* private: */ __u32 headers_end[0]; /* public: */ +#if defined(CONFIG_BCM_KF_NBUFF) + unsigned char *clone_wr_head; /* indicates drivers(ex:enet)about writable headroom in aggregated skb*/ + unsigned char *clone_fc_head; /* indicates fcache about writable headroom in aggregated skb */ + + union { + unsigned int vtag_word; + struct { unsigned short vtag, vtag_save; }; + }; + union { /* CONFIG_NET_SCHED CONFIG_NET_CLS_ACT*/ + unsigned int tc_word; + }; +#endif /* CONFIG_BCM_KF_NBUFF */ + +#if defined(CONFIG_BCM_KF_NBUFF) +#if defined(CONFIG_BCM_KF_VLAN) && (defined(CONFIG_BCM_VLAN) || defined(CONFIG_BCM_VLAN_MODULE)) + __u16 vlan_count; + __u16 vlan_tpid; + __u32 cfi_save; + __u32 vlan_header[SKB_VLAN_MAX_TAGS]; + struct net_device *rxdev; +#endif // CONFIG_BCM_KF_VLAN + union { + struct { + __u32 reserved:31; + __u32 restore_rx_vlan:1; /* Restore Rx VLAN at xmit. Used in ONT mode */ + }; + __u32 bcm_flags_word; + }bcm_flags; +#if defined(CONFIG_BLOG_FEATURE) + union { + __u32 u32[BLOG_MAX_PARAM_NUM]; + __u16 u16[BLOG_MAX_PARAM_NUM * 2]; + __u8 u8[BLOG_MAX_PARAM_NUM * 4]; + } ipt_log; + __u32 ipt_check; +#define IPT_MATCH_LENGTH (1 << 1) +#define IPT_MATCH_TCP (1 << 2) +#define IPT_MATCH_UDP (1 << 3) +#define IPT_MATCH_TOS (1 << 4) +#define IPT_MATCH_DSCP (1 << 5) +#define IPT_TARGET_CLASSIFY (1 << 6) +#define IPT_TARGET_CONNMARK (1 << 7) +#define IPT_TARGET_CONNSECMARK (1 << 8) +#define IPT_TARGET_DSCP (1 << 9) +#define IPT_TARGET_HL (1 << 10) +#define IPT_TARGET_LED (1 << 11) +#define IPT_TARGET_MARK (1 << 12) +#define IPT_TARGET_NFLOG (1 << 13) +#define IPT_TARGET_NFQUEUE (1 << 14) +#define IPT_TARGET_NOTRACK (1 << 15) +#define IPT_TARGET_RATEEST (1 << 16) +#define IPT_TARGET_SECMARK (1 << 17) +#define IPT_TARGET_SKIPLOG (1 << 18) +#define IPT_TARGET_TCPMSS (1 << 19) +#define IPT_TARGET_TCPOPTSTRIP (1 << 20) +#define IPT_TARGET_TOS (1 << 21) +#define IPT_TARGET_TPROXY (1 << 22) +#define IPT_TARGET_TRACE (1 << 23) +#define IPT_TARGET_TTL (1 << 24) +#define IPT_TARGET_CHECK (1 << 25) +#endif + /* Physical device where this pkt is received */ + struct net_device *in_dev; +#endif /* These elements must be at the end, see alloc_skb() for details. */ sk_buff_data_t tail; sk_buff_data_t end; +#if defined(CONFIG_BCM_KF_NBUFF) + unsigned char *head; +#else unsigned char *head, *data; +#endif unsigned int truesize; atomic_t users; }; @@ -710,7 +1105,7 @@ */ static inline struct dst_entry *skb_dst(const struct sk_buff *skb) { - /* If refdst was not refcounted, check we still are in a + /* If refdst was not refcounted, check we still are in a * rcu_read_lock section */ WARN_ON((skb->_skb_refdst & SKB_DST_NOREF) && @@ -762,6 +1157,43 @@ return (struct rtable *)skb_dst(skb); } +#if defined(CONFIG_BCM_KF_WL) +/** + * Zero out the skb's control buffers + * @skb: buffer + * + * Fast zero ARMv7(ldmia,stmia using 256B global all zeros) ARMv8(stp,zero) + */ +static inline void skb_cb_zero(struct sk_buff * skb) +{ + unsigned long long *cb; /* 8B assigns - ARMv8 stp + zero reg */ + + cb = (unsigned long long *) (&skb->cb[0]); /* 64 bytes */ + + *(cb + 0) = 0ULL; *(cb + 1) = 0ULL; /* 16 Bytes = 16 Bytes */ + *(cb + 2) = 0ULL; *(cb + 3) = 0ULL; /* + 16 Bytes = 32 Bytes */ + *(cb + 4) = 0ULL; *(cb + 5) = 0ULL; /* + 16 Bytes = 48 Bytes */ +#if defined(CONFIG_BCM_KF_NBUFF) + *(cb + 6) = 0ULL; *(cb + 7) = 0ULL; /* + 16 Bytes = 64 Bytes */ +#endif + +#if defined(CONFIG_BCM_KF_WL) + cb = (unsigned long long *)(&skb->wl_cb[0]); /* 24 bytes */ + + *(cb + 0) = 0ULL; *(cb + 1) = 0ULL; /* 16 Bytes = 16 Bytes */ + *(cb + 2) = 0ULL; /* + 8 Bytes = 24 Bytes */ +#endif +} +#endif /* CONFIG_BCM_KF_WL */ + +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) +extern void bl_kfree_skb_structure(struct sk_buff *skb); +extern void bl_kfree_skb_structure_irq(struct sk_buff *skb); +#endif /* CONFIG_BCM_RUNNER */ +#endif /* CONFIG_BCM_KF_RUNNER */ + + void kfree_skb(struct sk_buff *skb); void kfree_skb_list(struct sk_buff *segs); void skb_tx_error(struct sk_buff *skb); @@ -991,6 +1423,36 @@ return &skb_shinfo(skb)->hwtstamps; } +#if defined(CONFIG_BCM_KF_NBUFF) +/* Returns size of struct sk_buff */ +extern size_t skb_size(void); +extern size_t skb_aligned_size(void); +extern int skb_layout_test(int head_offset, int tail_offset, int end_offset); + +/** + * skb_headerinit - initialize a socket buffer header + * @headroom: reserved headroom size + * @datalen: data buffer size, data buffer is allocated by caller + * @skb: skb allocated by caller + * @data: data buffer allocated by caller + * @recycle_hook: callback function to free data buffer and skb + * @recycle_context: context value passed to recycle_hook, param1 + * @blog_p: pass a blog to a skb for logging + * + * Initializes the socket buffer and assigns the data buffer to it. + * Both the sk_buff and the pointed data buffer are pre-allocated. + * + */ +void skb_headerinit(unsigned int headroom, unsigned int datalen, + struct sk_buff *skb, unsigned char *data, + RecycleFuncP recycle_hook, unsigned long recycle_context, + struct blog_t * blog_p); + +extern void skb_header_free(struct sk_buff *skb); + +#endif /* CONFIG_BCM_KF_NBUFF */ + + /** * skb_queue_empty - check if a queue is empty * @list: queue head @@ -1141,6 +1603,25 @@ atomic_add(1 << SKB_DATAREF_SHIFT, &skb_shinfo(skb)->dataref); } +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_ACCELERATION)) +/** + * skb_clone_headers_set - set the clone_fc_head and clone_wr_head in + * an aggregated skb(ex: used in USBNET RX packet aggregation) + * @skb: buffer to operate on + * @len: lenghth of writable clone headroom + * + * when this pointer is set you can still modify the cloned packet and also + * expand the packet till clone_wr_head. This is used in cases on packet aggregation. + */ +static inline void skb_clone_headers_set(struct sk_buff *skb, unsigned int len) +{ + skb->clone_fc_head = skb->data - len; + if (skb_cloned(skb)) + skb->clone_wr_head = skb->data - len; + else + skb->clone_wr_head = NULL; +} +#endif /** * __skb_header_release - release reference to header * @skb: buffer to operate on @@ -1741,6 +2222,30 @@ return skb->data - skb->head; } +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_ACCELERATION)) +/** + * skb_writable_headroom - bytes preceding skb->data that are writable(even on some + * cloned skb's); + * @skb: buffer to check + * + * Return the number of bytes of writable free space preceding the skb->data of an &sk_buff. + * note:skb->cloned_wr_head is used to indicate the padding between 2 packets when multiple packets + * are present in buffer pointed by skb->head(ex: used in USBNET RX packet aggregation) + * + */ +static inline unsigned int skb_writable_headroom(const struct sk_buff *skb) +{ + if (skb_cloned(skb)) { + if (skb->clone_wr_head) + return skb->data - skb->clone_wr_head; + else if (skb->clone_fc_head) + return 0; + } + + return skb_headroom(skb); +} +#endif + /** * skb_tailroom - bytes at buffer end * @skb: buffer to check diff -urN linux-4.1.52-orig/include/linux/slab.h linux-4.1.52/include/linux/slab.h --- linux-4.1.52-orig/include/linux/slab.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/slab.h 2020-02-07 01:47:54.000000000 +0800 @@ -25,6 +25,9 @@ #define SLAB_POISON 0x00000800UL /* DEBUG: Poison objects */ #define SLAB_HWCACHE_ALIGN 0x00002000UL /* Align objs on cache lines */ #define SLAB_CACHE_DMA 0x00004000UL /* Use GFP_DMA memory */ +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_ZONE_ACP) +#define SLAB_CACHE_ACP 0x00008000UL /* Use GFP_ACP memory */ +#endif #define SLAB_STORE_USER 0x00010000UL /* DEBUG: Store the last owner for bug hunting */ #define SLAB_PANIC 0x00040000UL /* Panic if kmem_cache_create() fails */ /* @@ -247,7 +250,6 @@ { if (!size) return 0; - if (size <= KMALLOC_MIN_SIZE) return KMALLOC_SHIFT_LOW; diff -urN linux-4.1.52-orig/include/linux/socket.h linux-4.1.52/include/linux/socket.h --- linux-4.1.52-orig/include/linux/socket.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/socket.h 2020-02-07 01:47:54.000000000 +0800 @@ -200,7 +200,13 @@ #define AF_ALG 38 /* Algorithm sockets */ #define AF_NFC 39 /* NFC sockets */ #define AF_VSOCK 40 /* vSockets */ +#ifdef CONFIG_BCM_KF_MHI +#define AF_MHI 41 /* MHI sockets */ +#define AF_RAW 42 /* RAW sockets */ +#define AF_MAX 43 /* For now.. */ +#else #define AF_MAX 41 /* For now.. */ +#endif /* Protocol families, same as address families. */ #define PF_UNSPEC AF_UNSPEC @@ -246,6 +252,10 @@ #define PF_ALG AF_ALG #define PF_NFC AF_NFC #define PF_VSOCK AF_VSOCK +#ifdef CONFIG_BCM_KF_MHI +#define PF_RAW AF_RAW +#define PF_MHI AF_MHI +#endif #define PF_MAX AF_MAX /* Maximum queue length specifiable by listen. */ diff -urN linux-4.1.52-orig/include/linux/spi/spi.h linux-4.1.52/include/linux/spi/spi.h --- linux-4.1.52-orig/include/linux/spi/spi.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/spi/spi.h 2020-02-07 01:47:54.000000000 +0800 @@ -625,6 +625,37 @@ u16 delay_usecs; u32 speed_hz; +#if defined(CONFIG_BCM_KF_SPI) + /* added for controllers that support an ignore count for read + operations. This is useful if the read requires command bytes + and you want to ignore the read data on the bus during the + transmission of those bytes. Note that only prepend_cnt bytes + of data will be written from tx_buf. + */ + u8 prepend_cnt; + + /* added for multibit support + @multi_bit_en - enable multibit operation for this transfer + @multi_bit_start_offset - start offset for multibit data + */ + u8 multi_bit_en; + u8 multi_bit_start_offset; + + /* added for controllers that do not support large transfers + the controller will break up the transfer into smaller + transfers to avoid additional data copies + Note that hdr_len should not be included in len + @hdr_len - length of header + @unit_size - data for each transfer will be divided into multiples of + unit_size + @adr_len - length of address field (max 4 bytes) + @adr_offset - offset of first addr byte in header + */ + u8 hdr_len; + u8 unit_size; + u8 addr_len; + u8 addr_offset; +#endif struct list_head transfer_list; }; diff -urN linux-4.1.52-orig/include/linux/sysrq.h linux-4.1.52/include/linux/sysrq.h --- linux-4.1.52-orig/include/linux/sysrq.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/sysrq.h 2020-02-07 01:47:54.000000000 +0800 @@ -47,6 +47,9 @@ int register_sysrq_key(int key, struct sysrq_key_op *op); int unregister_sysrq_key(int key, struct sysrq_key_op *op); struct sysrq_key_op *__sysrq_get_key_op(int key); +#if defined(CONFIG_BCM_KF_CHAR_SYSRQ) +bool sysrq_on(void); +#endif int sysrq_toggle_support(int enable_mask); diff -urN linux-4.1.52-orig/include/linux/tcp.h linux-4.1.52/include/linux/tcp.h --- linux-4.1.52-orig/include/linux/tcp.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/tcp.h 2020-02-07 01:47:54.000000000 +0800 @@ -57,7 +57,11 @@ /* TCP Fast Open */ #define TCP_FASTOPEN_COOKIE_MIN 4 /* Min Fast Open Cookie size in bytes */ #define TCP_FASTOPEN_COOKIE_MAX 16 /* Max Fast Open Cookie size in bytes */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) #define TCP_FASTOPEN_COOKIE_SIZE 8 /* the size employed by this impl. */ +#else +#define TCP_FASTOPEN_COOKIE_SIZE 4 /* the size employed by this impl. */ +#endif /* TCP Fast Open Cookie as stored in memory */ struct tcp_fastopen_cookie { @@ -77,6 +81,58 @@ u32 end_seq; }; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +struct tcp_out_options { + u16 options; /* bit field of OPTION_* */ + u8 ws; /* window scale, 0 to disable */ + u8 num_sack_blocks;/* number of SACK blocks to include */ + u8 hash_size; /* bytes in hash_location */ + u16 mss; /* 0 to disable */ + __u8 *hash_location; /* temporary pointer, overloaded */ + __u32 tsval, tsecr; /* need to include OPTION_TS */ + struct tcp_fastopen_cookie *fastopen_cookie; /* Fast open cookie */ +#ifdef CONFIG_MPTCP + u16 mptcp_options; /* bit field of MPTCP related OPTION_* */ + u8 dss_csum:1, /* dss-checksum required? */ + add_addr_v4:1, + add_addr_v6:1, + mptcp_ver:4; + + union { + struct { + __u64 sender_key; /* sender's key for mptcp */ + __u64 receiver_key; /* receiver's key for mptcp */ + } mp_capable; + + struct { + __u64 sender_truncated_mac; + __u32 sender_nonce; + /* random number of the sender */ + __u32 token; /* token for mptcp */ + u8 low_prio:1; + } mp_join_syns; + }; + + struct { + __u64 trunc_mac; + struct in_addr addr; + u16 port; + u8 addr_id; + } add_addr4; + + struct { + __u64 trunc_mac; + struct in6_addr addr; + u16 port; + u8 addr_id; + } add_addr6; + + u16 remove_addrs; /* list of address id */ + u8 addr_id; /* address id (mp_join or add_address) */ +#endif /* CONFIG_MPTCP */ +}; + +#endif /*These are used to set the sack_ok field in struct tcp_options_received */ #define TCP_SACK_SEEN (1 << 0) /*1 = peer is SACK capable, */ #define TCP_FACK_ENABLED (1 << 1) /*1 = FACK is enabled locally*/ @@ -100,6 +156,11 @@ u16 mss_clamp; /* Maximal mss, negotiated at connection setup */ }; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +struct mptcp_cb; +struct mptcp_tcp_sock; + +#endif static inline void tcp_clear_options(struct tcp_options_received *rx_opt) { rx_opt->tstamp_ok = rx_opt->sack_ok = 0; @@ -133,6 +194,10 @@ return (struct tcp_request_sock *)req; } +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +struct tcp_md5sig_key; + +#endif struct tcp_sock { /* inet_connection_sock has to be the first member of tcp_sock */ struct inet_connection_sock inet_conn; @@ -173,7 +238,9 @@ u32 tsoffset; /* timestamp offset */ +#if !defined(CONFIG_BCM_KF_TCP_NO_TSQ) struct list_head tsq_node; /* anchor in tsq_tasklet.head list */ +#endif unsigned long tsq_flags; /* Data for direct copy to user */ @@ -333,6 +400,42 @@ * socket. Used to retransmit SYNACKs etc. */ struct request_sock *fastopen_rsk; +#if defined(CONFIG_BCM_KF_SPEEDYGET) && defined(CONFIG_BCM_SPEEDYGET) + u8 tcp_nocopy; +#endif +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + + /* MPTCP/TCP-specific callbacks */ + const struct tcp_sock_ops *ops; + + struct mptcp_cb *mpcb; + struct sock *meta_sk; + /* We keep these flags even if CONFIG_MPTCP is not checked, because + * it allows checking MPTCP capability just by checking the mpc flag, + * rather than adding ifdefs everywhere. + */ + u16 mpc:1, /* Other end is multipath capable */ + inside_tk_table:1, /* Is the tcp_sock inside the token-table? */ + send_mp_fclose:1, + request_mptcp:1, /* Did we send out an MP_CAPABLE? + * (this speeds up mptcp_doit() in tcp_recvmsg) + */ + pf:1, /* Potentially Failed state: when this flag is set, we + * stop using the subflow + */ + mp_killed:1, /* Killed with a tcp_done in mptcp? */ + was_meta_sk:1, /* This was a meta sk (in case of reuse) */ + is_master_sk:1, + close_it:1, /* Must close socket in mptcp_data_ready? */ + closing:1, + mptcp_ver:4; + struct mptcp_tcp_sock *mptcp; +#ifdef CONFIG_MPTCP + struct hlist_nulls_node tk_table; + u32 mptcp_loc_token; + u64 mptcp_loc_key; +#endif /* CONFIG_MPTCP */ +#endif }; enum tsq_flags { @@ -344,6 +447,10 @@ TCP_MTU_REDUCED_DEFERRED, /* tcp_v{4|6}_err() could not call * tcp_v{4|6}_mtu_reduced() */ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + MPTCP_PATH_MANAGER, /* MPTCP deferred creation of new subflows */ + MPTCP_SUB_DEFERRED, /* A subflow got deferred - process them */ +#endif }; static inline struct tcp_sock *tcp_sk(const struct sock *sk) @@ -366,6 +473,9 @@ #ifdef CONFIG_TCP_MD5SIG struct tcp_md5sig_key *tw_md5_key; #endif +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + struct mptcp_tw *mptcp_tw; +#endif }; static inline struct tcp_timewait_sock *tcp_twsk(const struct sock *sk) @@ -400,4 +510,10 @@ return 0; } +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +/*CVE-2019-11477*/ +int tcp_skb_shift(struct sk_buff *to, struct sk_buff *from, int pcount, + int shiftlen); +#endif //#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + #endif /* _LINUX_TCP_H */ diff -urN linux-4.1.52-orig/include/linux/tee_drv.h linux-4.1.52/include/linux/tee_drv.h --- linux-4.1.52-orig/include/linux/tee_drv.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/tee_drv.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,280 @@ +#if defined(CONFIG_BCM_KF_OPTEE_414_BACKPORTS) +/* + * Copyright (c) 2015-2016, Linaro Limited + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#ifndef __TEE_DRV_H +#define __TEE_DRV_H + +#include +#include +#include +#include + +/* + * The file describes the API provided by the generic TEE driver to the + * specific TEE driver. + */ + +#define TEE_SHM_MAPPED 0x1 /* Memory mapped by the kernel */ +#define TEE_SHM_DMA_BUF 0x2 /* Memory with dma-buf handle */ + +struct device; +struct tee_device; +struct tee_shm; +struct tee_shm_pool; + +/** + * struct tee_context - driver specific context on file pointer data + * @teedev: pointer to this drivers struct tee_device + * @list_shm: List of shared memory object owned by this context + * @data: driver specific context data, managed by the driver + */ +struct tee_context { + struct tee_device *teedev; + struct list_head list_shm; + void *data; +}; + +struct tee_param_memref { + size_t shm_offs; + size_t size; + struct tee_shm *shm; +}; + +struct tee_param_value { + u64 a; + u64 b; + u64 c; +}; + +struct tee_param { + u64 attr; + union { + struct tee_param_memref memref; + struct tee_param_value value; + } u; +}; + +/** + * struct tee_driver_ops - driver operations vtable + * @get_version: returns version of driver + * @open: called when the device file is opened + * @release: release this open file + * @open_session: open a new session + * @close_session: close a session + * @invoke_func: invoke a trusted function + * @cancel_req: request cancel of an ongoing invoke or open + * @supp_revc: called for supplicant to get a command + * @supp_send: called for supplicant to send a response + */ +struct tee_driver_ops { + void (*get_version)(struct tee_device *teedev, + struct tee_ioctl_version_data *vers); + int (*open)(struct tee_context *ctx); + void (*release)(struct tee_context *ctx); + int (*open_session)(struct tee_context *ctx, + struct tee_ioctl_open_session_arg *arg, + struct tee_param *param); + int (*close_session)(struct tee_context *ctx, u32 session); + int (*invoke_func)(struct tee_context *ctx, + struct tee_ioctl_invoke_arg *arg, + struct tee_param *param); + int (*cancel_req)(struct tee_context *ctx, u32 cancel_id, u32 session); + int (*supp_recv)(struct tee_context *ctx, u32 *func, u32 *num_params, + struct tee_param *param); + int (*supp_send)(struct tee_context *ctx, u32 ret, u32 num_params, + struct tee_param *param); +}; + +/** + * struct tee_desc - Describes the TEE driver to the subsystem + * @name: name of driver + * @ops: driver operations vtable + * @owner: module providing the driver + * @flags: Extra properties of driver, defined by TEE_DESC_* below + */ +#define TEE_DESC_PRIVILEGED 0x1 +struct tee_desc { + const char *name; + const struct tee_driver_ops *ops; + struct module *owner; + u32 flags; +}; + +/** + * tee_device_alloc() - Allocate a new struct tee_device instance + * @teedesc: Descriptor for this driver + * @dev: Parent device for this device + * @pool: Shared memory pool, NULL if not used + * @driver_data: Private driver data for this device + * + * Allocates a new struct tee_device instance. The device is + * removed by tee_device_unregister(). + * + * @returns a pointer to a 'struct tee_device' or an ERR_PTR on failure + */ +struct tee_device *tee_device_alloc(const struct tee_desc *teedesc, + struct device *dev, + struct tee_shm_pool *pool, + void *driver_data); + +/** + * tee_device_register() - Registers a TEE device + * @teedev: Device to register + * + * tee_device_unregister() need to be called to remove the @teedev if + * this function fails. + * + * @returns < 0 on failure + */ +int tee_device_register(struct tee_device *teedev); + +/** + * tee_device_unregister() - Removes a TEE device + * @teedev: Device to unregister + * + * This function should be called to remove the @teedev even if + * tee_device_register() hasn't been called yet. Does nothing if + * @teedev is NULL. + */ +void tee_device_unregister(struct tee_device *teedev); + +/** + * struct tee_shm_pool_mem_info - holds information needed to create a shared + * memory pool + * @vaddr: Virtual address of start of pool + * @paddr: Physical address of start of pool + * @size: Size in bytes of the pool + */ +struct tee_shm_pool_mem_info { + unsigned long vaddr; + phys_addr_t paddr; + size_t size; +}; + +/** + * tee_shm_pool_alloc_res_mem() - Create a shared memory pool from reserved + * memory range + * @priv_info: Information for driver private shared memory pool + * @dmabuf_info: Information for dma-buf shared memory pool + * + * Start and end of pools will must be page aligned. + * + * Allocation with the flag TEE_SHM_DMA_BUF set will use the range supplied + * in @dmabuf, others will use the range provided by @priv. + * + * @returns pointer to a 'struct tee_shm_pool' or an ERR_PTR on failure. + */ +struct tee_shm_pool * +tee_shm_pool_alloc_res_mem(struct tee_shm_pool_mem_info *priv_info, + struct tee_shm_pool_mem_info *dmabuf_info); + +/** + * tee_shm_pool_free() - Free a shared memory pool + * @pool: The shared memory pool to free + * + * The must be no remaining shared memory allocated from this pool when + * this function is called. + */ +void tee_shm_pool_free(struct tee_shm_pool *pool); + +/** + * tee_get_drvdata() - Return driver_data pointer + * @returns the driver_data pointer supplied to tee_register(). + */ +void *tee_get_drvdata(struct tee_device *teedev); + +/** + * tee_shm_alloc() - Allocate shared memory + * @ctx: Context that allocates the shared memory + * @size: Requested size of shared memory + * @flags: Flags setting properties for the requested shared memory. + * + * Memory allocated as global shared memory is automatically freed when the + * TEE file pointer is closed. The @flags field uses the bits defined by + * TEE_SHM_* above. TEE_SHM_MAPPED must currently always be set. If + * TEE_SHM_DMA_BUF global shared memory will be allocated and associated + * with a dma-buf handle, else driver private memory. + * + * @returns a pointer to 'struct tee_shm' + */ +struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags); + +/** + * tee_shm_free() - Free shared memory + * @shm: Handle to shared memory to free + */ +void tee_shm_free(struct tee_shm *shm); + +/** + * tee_shm_put() - Decrease reference count on a shared memory handle + * @shm: Shared memory handle + */ +void tee_shm_put(struct tee_shm *shm); + +/** + * tee_shm_va2pa() - Get physical address of a virtual address + * @shm: Shared memory handle + * @va: Virtual address to tranlsate + * @pa: Returned physical address + * @returns 0 on success and < 0 on failure + */ +int tee_shm_va2pa(struct tee_shm *shm, void *va, phys_addr_t *pa); + +/** + * tee_shm_pa2va() - Get virtual address of a physical address + * @shm: Shared memory handle + * @pa: Physical address to tranlsate + * @va: Returned virtual address + * @returns 0 on success and < 0 on failure + */ +int tee_shm_pa2va(struct tee_shm *shm, phys_addr_t pa, void **va); + +/** + * tee_shm_get_va() - Get virtual address of a shared memory plus an offset + * @shm: Shared memory handle + * @offs: Offset from start of this shared memory + * @returns virtual address of the shared memory + offs if offs is within + * the bounds of this shared memory, else an ERR_PTR + */ +void *tee_shm_get_va(struct tee_shm *shm, size_t offs); + +/** + * tee_shm_get_pa() - Get physical address of a shared memory plus an offset + * @shm: Shared memory handle + * @offs: Offset from start of this shared memory + * @pa: Physical address to return + * @returns 0 if offs is within the bounds of this shared memory, else an + * error code. + */ +int tee_shm_get_pa(struct tee_shm *shm, size_t offs, phys_addr_t *pa); + +/** + * tee_shm_get_id() - Get id of a shared memory object + * @shm: Shared memory handle + * @returns id + */ +int tee_shm_get_id(struct tee_shm *shm); + +/** + * tee_shm_get_from_id() - Find shared memory object and increase reference + * count + * @ctx: Context owning the shared memory + * @id: Id of shared memory object + * @returns a pointer to 'struct tee_shm' on success or an ERR_PTR on failure + */ +struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id); + +#endif /*__TEE_DRV_H*/ +#endif /* CONFIG_BCM_KF_OPTEE_414_BACKPORTS */ diff -urN linux-4.1.52-orig/include/linux/types.h linux-4.1.52/include/linux/types.h --- linux-4.1.52-orig/include/linux/types.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/types.h 2020-02-07 01:47:54.000000000 +0800 @@ -215,5 +215,14 @@ /* clocksource cycle base type */ typedef u64 cycle_t; + +#if defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) +#if defined(CONFIG_MIPS_BCM963XX) +#define LINUX_NET_PACKED __attribute__((packed)) +#else +#define LINUX_NET_PACKED +#endif /* CONFIG_MIPS_BCM963XX */ +#endif /* CONFIG_BCM_KF_UNALIGNED_EXCEPTION */ + #endif /* __ASSEMBLY__ */ #endif /* _LINUX_TYPES_H */ diff -urN linux-4.1.52-orig/include/linux/usb/usbnet.h linux-4.1.52/include/linux/usb/usbnet.h --- linux-4.1.52-orig/include/linux/usb/usbnet.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/usb/usbnet.h 2020-02-07 01:47:54.000000000 +0800 @@ -62,7 +62,15 @@ unsigned interrupt_count; struct mutex interrupt_mutex; struct usb_anchor deferred; +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_THREAD)) + struct task_struct *usbnet_thread; + int usbnet_thread_resched; + wait_queue_head_t thread_wq; + int pending_rx_skb_thresh; + int pending_rx_skb_count; +#else struct tasklet_struct bh; +#endif struct work_struct kevent; unsigned long flags; diff -urN linux-4.1.52-orig/include/linux/vlanctl_bind.h linux-4.1.52/include/linux/vlanctl_bind.h --- linux-4.1.52-orig/include/linux/vlanctl_bind.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/linux/vlanctl_bind.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,129 @@ +#if defined(CONFIG_BCM_KF_VLANCTL_BIND) +/* +* Copyright (c) 2003-2014 Broadcom Corporation +* All Rights Reserved +* +<:label-BRCM:2014:DUAL/GPL:standard + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + + +#ifndef _VLANCTL_BIND_ +#define _VLANCTL_BIND_ + +typedef enum { +#if defined(CONFIG_BCM_KF_FAP) + VLANCTL_BIND_CLIENT_FAP, +#endif +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) + VLANCTL_BIND_CLIENT_RUNNER, +#endif /* CONFIG_BCM_RUNNER */ +#endif /* CONFIG_BCM_KF_RUNNER */ + VLANCTL_BIND_CLIENT_MAX +} vlanctl_bind_client_t; + + +/* + * vlanctl_bind defines three(!) hooks: + * NotifHook: When blog_notify is invoked, the bound hook is invoked. Based on + * event type the bound Blog client may perform a custom action. + * SC Hook: If this hook is defined, blog_activate() will pass a blog with + * necessary information for statical configuration. + * SD Hook: If this hook is defined, blog_deactivate() will pass a pointer + * to a network object with BlogActivateKey information. The + * respective flow entry will be deleted. + */ +typedef union { + struct { + uint8_t unused : 5; + uint8_t SN_HOOK : 1; + uint8_t SC_HOOK : 1; + uint8_t SD_HOOK : 1; + } bmap; + uint8_t hook_info; +} vlanctl_bind_t; + +typedef struct { + struct net_device *vlan_dev; + unsigned int vid; + int enable; +} vlanctl_vlan_t; + +typedef struct { + uint8_t mac[6]; + int enable; +} vlanctl_route_mac_t; + +typedef struct { + struct net_device *aggregate_vlan_dev; + struct net_device *deaggregate_vlan_dev; +} vlanctl_vlan_aggregate_t; + +typedef enum { + VLANCTL_BIND_NOTIFY_TPID, /* set interface tpid */ + VLANCTL_BIND_NOTIFY_VLAN, /* set vlan object */ + VLANCTL_BIND_NOTIFY_ROUTE_MAC, /* route mac create and delete */ + VLANCTL_BIND_NOTIFY_VLAN_AGGREGATE, /* set vlan aggregation */ + VLANCTL_BIND_DROP_PRECEDENCE_SET, /* rdpa_mw_drop_precedence_set */ +} vlanctl_bind_Notify_t; + +#if defined(CONFIG_BLOG) + +typedef uint32_t (* vlanctl_bind_ScHook_t)(Blog_t * blog_p, BlogTraffic_t traffic); + +typedef Blog_t * (* vlanctl_bind_SdHook_t)(uint32_t key, BlogTraffic_t traffic); + +typedef void (* vlanctl_bind_SnHook_t)(vlanctl_bind_Notify_t event, void *ptr); + +void vlanctl_bind_config(vlanctl_bind_ScHook_t vlanctl_bind_sc, + vlanctl_bind_SdHook_t vlanctl_bind_sd, + vlanctl_bind_SnHook_t vlanctl_bind_sn, + vlanctl_bind_client_t client, + vlanctl_bind_t bind); + + +int vlanctl_bind_activate(vlanctl_bind_client_t client); + +int vlanctl_notify(vlanctl_bind_Notify_t event, void *ptr, vlanctl_bind_client_t client); + +/* + *------------------------------------------------------------------------------ + * vlanctl_activate(): static configuration function of blog application + * pass a filled blog to the hook for configuration + *------------------------------------------------------------------------------ + */ +extern uint32_t vlanctl_activate( Blog_t * blog_p, vlanctl_bind_client_t client ); + +/* + *------------------------------------------------------------------------------ + * vlanctl_deactivate(): static deconfiguration function of blog application + *------------------------------------------------------------------------------ + */ +extern Blog_t * vlanctl_deactivate( uint32_t key, vlanctl_bind_client_t client ); + +#endif /* CONFIG_BLOG */ + +#endif /* ! _VLANCTL_BIND_ */ +#endif diff -urN linux-4.1.52-orig/include/linux/vm_event_item.h linux-4.1.52/include/linux/vm_event_item.h --- linux-4.1.52-orig/include/linux/vm_event_item.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/vm_event_item.h 2020-02-07 01:47:54.000000000 +0800 @@ -12,6 +12,9 @@ #else #define DMA32_ZONE(xx) #endif +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_ZONE_ACP) +#define ACP_ZONE(xx) xx##_ACP, +#endif #ifdef CONFIG_HIGHMEM #define HIGHMEM_ZONE(xx) , xx##_HIGH @@ -19,7 +22,11 @@ #define HIGHMEM_ZONE(xx) #endif +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_ZONE_ACP) +#define FOR_ALL_ZONES(xx) DMA_ZONE(xx) DMA32_ZONE(xx) ACP_ZONE(xx) xx##_NORMAL HIGHMEM_ZONE(xx) , xx##_MOVABLE +#else #define FOR_ALL_ZONES(xx) DMA_ZONE(xx) DMA32_ZONE(xx) xx##_NORMAL HIGHMEM_ZONE(xx) , xx##_MOVABLE +#endif enum vm_event_item { PGPGIN, PGPGOUT, PSWPIN, PSWPOUT, FOR_ALL_ZONES(PGALLOC), diff -urN linux-4.1.52-orig/include/linux/vmstat.h linux-4.1.52/include/linux/vmstat.h --- linux-4.1.52-orig/include/linux/vmstat.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/vmstat.h 2020-02-07 01:47:54.000000000 +0800 @@ -178,6 +178,9 @@ #ifdef CONFIG_ZONE_DMA32 zone_page_state(&zones[ZONE_DMA32], item) + #endif +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_ZONE_ACP) + zone_page_state(&zones[ZONE_ACP], item) + +#endif #ifdef CONFIG_HIGHMEM zone_page_state(&zones[ZONE_HIGHMEM], item) + #endif diff -urN linux-4.1.52-orig/include/linux/watchdog.h linux-4.1.52/include/linux/watchdog.h --- linux-4.1.52-orig/include/linux/watchdog.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/linux/watchdog.h 2020-02-07 01:47:54.000000000 +0800 @@ -136,6 +136,9 @@ unsigned int timeout_parm, struct device *dev); extern int watchdog_register_device(struct watchdog_device *); extern void watchdog_unregister_device(struct watchdog_device *); +#if defined(CONFIG_BCM_KF_WDT) +extern void watchdog_force_disable( void ); +#endif #ifdef CONFIG_HARDLOCKUP_DETECTOR void watchdog_nmi_disable_all(void); diff -urN linux-4.1.52-orig/include/mtd/brcmnand_oob.h linux-4.1.52/include/mtd/brcmnand_oob.h --- linux-4.1.52-orig/include/mtd/brcmnand_oob.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/mtd/brcmnand_oob.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,839 @@ +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +/* + * include/mtd/brcmnand_oob.h + * +<:copyright-BRCM:2002:GPL/GPL:standard + + Copyright (c) 2002 Broadcom + All Rights Reserved + +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 (the "GPL"). + +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. + + +A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by +writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + +:> + */ + +#ifndef __BRCMNAND_OOB_H +#define __BRCMNAND_OOB_H + +#include +#include + +#ifndef CONFIG_BRCMNAND_MTD_EXTENSION +#define UNDERSIZED_ECCPOS_API 1 +#endif + + +/* + * Assuming proper include that precede this has the typedefs for struct nand_oobinfo + */ + +/** + * brcmnand_oob oob info for 2K page + */ + +/** + * brcmnand_oob oob info for 512 page + */ +static struct nand_ecclayout brcmnand_oob_16 = { + .eccbytes = 3, + .eccpos = { + 6,7,8 + }, + .oobfree = { {.offset=0, .length=5}, + {.offset=9,.length=7}, /* Byte 5 (6th byte) used for BI */ + {.offset=0, .length=0} /* End marker */ + } + /* THT Bytes offset 4&5 are used by BBT. Actually only byte 5 is used, but in order to accomodate + * for 16 bit bus width, byte 4 is also not used. If we only use byte-width chip, (We did) + * then we can also use byte 4 as free bytes. + */ +}; + +static struct nand_ecclayout brcmnand_oob_64 = { + .eccbytes = 12, + .eccpos = { + 6,7,8, + 22,23,24, + 38,39,40, + 54,55,56 + }, + .oobfree = { /* 0-1 used for BBT and/or manufacturer bad block marker, + * first slice loses 2 bytes for BBT */ + {.offset=2, .length=4}, + {.offset=9,.length=13}, /* First slice {9,7} 2nd slice {16,6}are combined */ + /* ST uses 6th byte (offset=5) as Bad Block Indicator, + * in addition to the 1st byte, and will be adjusted at run time */ + {.offset=25, .length=13}, /* 2nd slice */ + {.offset=41, .length=13}, /* 3rd slice */ + {.offset=57, .length=7}, /* 4th slice */ + {.offset=0, .length=0} /* End marker */ + } +}; + + +/* + * 4K page SLC with Hamming ECC + */ +static struct nand_ecclayout brcmnand_oob_128 = { + .eccbytes = 24, + .eccpos = { + 6,7,8, + 22,23,24, + 38,39,40, + 54,55,56, + 70,71,72, + 86,87,88, + 102,103,104, + 118,119,120 + }, + .oobfree = { /* 0-1 used for BBT and/or manufacturer bad block marker, + * first slice loses 2 bytes for BBT */ + {.offset=2, .length=4}, + {.offset=9,.length=13}, + {.offset=25, .length=13}, /* 2nd slice */ + {.offset=41, .length=13}, /* 3rd slice */ + {.offset=57, .length=13}, /* 4th slice */ + {.offset=73, .length=13}, /* 5th slice */ + {.offset=89, .length=13}, /* 6th slice */ + {.offset=105, .length=13}, /* 7th slice */ +#if ! defined(UNDERSIZED_ECCPOS_API) + {.offset=121, .length=7}, /* 8th slice */ + {.offset=0, .length=0} /* End marker */ +#endif + } +}; + + +/* Small page with BCH-4 */ +static struct nand_ecclayout brcmnand_oob_bch4_512 = { + .eccbytes = 7, + .eccpos = { + 9,10,11,12,13,14,15 + }, + .oobfree = { {.offset=0, .length=5}, + {.offset=6,.length=3}, /* Byte 5 (6th byte) used for BI */ + {.offset=0, .length=0} /* End marker */ + } +}; + +/* + * 2K page SLC/MLC with BCH-4 ECC, uses 7 ECC bytes per 512B ECC step + */ +static struct nand_ecclayout brcmnand_oob_bch4_2k = { + .eccbytes = 7*4, /* 7*4 = 28 bytes */ + .eccpos = { + 9,10,11,12,13,14,15, + 25,26,27,28,29,30,31, + 41,42,43,44,45,46,47, + 57,58,59,60,61,62,63 + }, + .oobfree = { /* 0 used for BBT and/or manufacturer bad block marker, + * first slice loses 1 byte for BBT */ + {.offset=1, .length=8}, /* 1st slice loses byte 0 */ + {.offset=16,.length=9}, /* 2nd slice */ + {.offset=32, .length=9}, /* 3rd slice */ + {.offset=48, .length=9}, /* 4th slice */ + {.offset=0, .length=0} /* End marker */ + } +}; + + +/* + * 4K page MLC with BCH-4 ECC, uses 7 ECC bytes per 512B ECC step + */ +static struct nand_ecclayout brcmnand_oob_bch4_4k = { + .eccbytes = 7*8, /* 7*8 = 56 bytes */ + .eccpos = { + 9,10,11,12,13,14,15, + 25,26,27,28,29,30,31, + 41,42,43,44,45,46,47, + 57,58,59,60,61,62,63, + 73,74,75,76,77,78,79, + 89,90,91,92,93,94,95, + 105,106,107,108,109,110,111, + 121,122,123,124,125,126,127 + }, + .oobfree = { /* 0 used for BBT and/or manufacturer bad block marker, + * first slice loses 1 byte for BBT */ + {.offset=1, .length=8}, /* 1st slice loses byte 0 */ + {.offset=16,.length=9}, /* 2nd slice */ + {.offset=32, .length=9}, /* 3rd slice */ + {.offset=48, .length=9}, /* 4th slice */ + {.offset=64, .length=9}, /* 5th slice */ + {.offset=80, .length=9}, /* 6th slice */ + {.offset=96, .length=9}, /* 7th slice */ + {.offset=112, .length=9}, /* 8th slice */ + //{.offset=0, .length=0} /* End marker */ + } +}; + +/* + * 4K page MLC with BCH-4 ECC, uses 7 ECC bytes per 512B ECC step + */ +static struct nand_ecclayout brcmnand_oob_bch4_8k = { + .eccbytes = 7*16, /* 7*16 = 112 bytes */ + .eccpos = { + 9,10,11,12,13,14,15, + 25,26,27,28,29,30,31, + 41,42,43,44,45,46,47, + 57,58,59,60,61,62,63, + 73,74,75,76,77,78,79, + 89,90,91,92,93,94,95, + 105,106,107,108,109,110,111, + 121,122,123,124,125,126,127, +#if ! defined(UNDERSIZED_ECCPOS_API) + 137,138,139,140,141,142,142, + 153,154,155,156,157,158,159, + 169,170,171,172,173,174,175, + 185,186,187,188,189,190,191, + 201,202,203,204,205,206,207, + 217,208,209,210,211,212,218, + 233,204,205,206,207,208,209, + 249,250,251,252,253,254,255 +#endif + }, + .oobfree = { /* 0 used for BBT and/or manufacturer bad block marker, + * first slice loses 1 byte for BBT */ + {.offset=1, .length=8}, /* 1st slice loses byte 0 */ + {.offset=16,.length=9}, /* 2nd slice */ + {.offset=32, .length=9}, /* 3rd slice */ + {.offset=48, .length=9}, /* 4th slice */ + {.offset=64, .length=9}, /* 5th slice */ + {.offset=80, .length=9}, /* 6th slice */ + {.offset=96, .length=9}, /* 7th slice */ + {.offset=112, .length=9}, /* 8th slice */ +#if ! defined(UNDERSIZED_ECCPOS_API) + {.offset=128, .length=9}, /* 9th slice */ + {.offset=144, .length=9}, /* 10th slice */ + {.offset=160, .length=9}, /* 11th slice */ + {.offset=176, .length=9}, /* 12th slice */ + {.offset=192, .length=9}, /* 13th slice */ + {.offset=208, .length=9}, /* 14th slice */ + {.offset=240, .length=9}, /* 15th slice */ +#endif + //{.offset=0, .length=0} /* End marker */ + } +}; + +/* For NAND controller REV 7.0 or later, it use new ECC algorithm that requires more ECC + bytes. ECC_Bytes_Reqd (per 512 data Bytes) = roundup(ECC_LEVEL * M/8) where + M is the BCH finite field order. For early chip, M is 13. For 63138, M is 14. + It does not affect the Hamming and BCH4. But for BCH8 and BCH12, 63138 use + one more byte. On 63138, for BCH8 2K page size, there is not enough spare area + for cleanmarker if spare area is 16 bytes. So only the NAND part with 27 bytes + spare area is supported */ + +#if CONFIG_MTD_BRCMNAND_VERSION < CONFIG_MTD_BRCMNAND_VERS_7_0 +/* + * 2K page SLC with BCH-8 ECC, uses 13 ECC bytes per 512B ECC step, and only have 16B OOB + * Rely on the fact that the UBI/UBIFS layer does not store anything in the OOB + */ +static struct nand_ecclayout brcmnand_oob_bch8_16_2k = { + .eccbytes = 13*4, /* 13*4 = 52 bytes */ + .eccpos = { + 3,4,5,6,7,8,9,10,11,12,13,14,15, + 19,20,21,22,23,24,25,26,27,28,29,30,31, + 35,36,37,38,39,40,41,42,43,44,45,46,47, + 51,52,53,54,55,56,57,58,59,60,61,62,63, + }, + .oobfree = { /* 0 used for BBT and/or manufacturer bad block marker, + * first slice loses 1 byte for BBT */ + {.offset=1, .length=2}, /* 1st slice loses byte 0 */ + {.offset=16,.length=3}, /* 2nd slice */ + {.offset=32, .length=3}, /* 3rd slice */ + {.offset=48, .length=3}, /* 4th slice */ + //{.offset=0, .length=0} /* End marker */ + } +}; + +/* + * 2K page SLC with BCH-8 ECC, uses 13 ECC bytes per 512B ECC step, 27B+ OOB size + */ +static struct nand_ecclayout brcmnand_oob_bch8_27_2k = { + .eccbytes = 13*4, /* 13*4 = 52 bytes */ + .eccpos = { + 14,15,16,17,18,19,20,21,22,23,24,25,26, + 41,42,43,44,45,46,47,48,49,50,51,52,53, + 68,69,70,71,72,73,74,75,76,77,78,79,80, + 95,96,97,98,99,100,101,102,103,104,105,106,107, + }, + .oobfree = { /* 0 used for BBT and/or manufacturer bad block marker, + * first slice loses 1 byte for BBT */ + {.offset=1, .length=13}, /* 1st slice loses byte 0 */ + {.offset=27,.length=14}, /* 2nd slice */ + {.offset=54, .length=14}, /* 3rd slice */ + {.offset=81, .length=14}, /* 4th slice */ + {.offset=0, .length=0} /* End marker */ + } +}; + +/* + * 4K page SLC/MLC with BCH-8 ECC, uses 13 ECC bytes per 512B ECC step, and only have 16B OOB + * Rely on the fact that the UBI/UBIFS layer does not store anything in the OOB + */ +static struct nand_ecclayout brcmnand_oob_bch8_16_4k = { + .eccbytes = 13*8, /* 13*8 = 104 bytes */ + .eccpos = { + 3,4,5,6,7,8,9,10,11,12,13,14,15, + 19,20,21,22,23,24,25,26,27,28,29,30,31, + 35,36,37,38,39,40,41,42,43,44,45,46,47, + 51,52,53,54,55,56,57,58,59,60,61,62,63, +#if ! defined(UNDERSIZED_ECCPOS_API) + 67,68,69,70,71,72,73,74,75,76,77,78,79, + 83,84,85,86,87,88,89,90,91,92,93,94,95, + 99,100,101,102,103,104,105,106,107,108,109,110,111, + 115,116,117,118,119,120,121,122,123,124,125,126,127, +#endif + }, + .oobfree = { /* 0 used for BBT and/or manufacturer bad block marker, + * first slice loses 1 byte for BBT */ + {.offset=1, .length=2}, /* 1st slice loses byte 0 */ + {.offset=16,.length=3}, /* 2nd slice */ + {.offset=32, .length=3}, /* 3rd slice */ + {.offset=48, .length=3}, /* 4th slice */ + {.offset=64, .length=3}, /* 5th slice */ + {.offset=80, .length=3}, /* 6th slice */ + {.offset=96, .length=3}, /* 7th slice */ + {.offset=112, .length=3}, /* 8th slice */ + //{.offset=0, .length=0} /* End marker */ + } +}; + + +/* + * 4K page MLC with BCH-8 ECC, uses 13 ECC bytes per 512B ECC step, and requires OOB size of 27B+ + */ +static struct nand_ecclayout brcmnand_oob_bch8_27_4k = { + .eccbytes = 13*8, /* 13*8 = 104 bytes */ + .eccpos = { + 14,15,16,17,18,19,20,21,22,23,24,25,26, + 41,42,43,44,45,46,47,48,49,50,51,52,53, + 68,69,70,71,72,73,74,75,76,77,78,79,80, + 95,96,97,98,99,100,101,102,103,104,105,106,107, +#if ! defined(UNDERSIZED_ECCPOS_API) + 122,123,124,125,126,127,128,129,130,131,132,133,134, + 149,150,151,152,153,154,155,156,157,158,159,160,161, + 176,177,178,179,180,181,182,183,184,185,186,187,188, + 203,204,205,206,207,208,209,210,211,212,213,214,215 +#endif + }, + .oobfree = { /* 0 used for BBT and/or manufacturer bad block marker, + * first slice loses 1 byte for BBT */ + {.offset=1, .length=13}, /* 1st slice loses byte 0 */ + {.offset=27,.length=14}, /* 2nd slice */ + {.offset=54, .length=14}, /* 3rd slice */ + {.offset=81, .length=14}, /* 4th slice */ + {.offset=108, .length=14}, /* 5th slice */ + {.offset=135, .length=14}, /* 6th slice */ + {.offset=162, .length=14}, /* 7th slice */ + {.offset=189, .length=14}, /* 8th slice */ + //{.offset=0, .length=0} /* End marker */ + } +}; + +/* + * 4K page SLC/MLC with BCH-8 ECC, uses 13 ECC bytes per 512B ECC step, and only have 16B OOB + * Rely on the fact that the UBI/UBIFS layer does not store anything in the OOB + */ +static struct nand_ecclayout brcmnand_oob_bch8_16_8k = { + .eccbytes = 13*16, /* 13*8 = 208 bytes */ + .eccpos = { + 3,4,5,6,7,8,9,10,11,12,13,14,15, + 19,20,21,22,23,24,25,26,27,28,29,30,31, + 35,36,37,38,39,40,41,42,43,44,45,46,47, + 51,52,53,54,55,56,57,58,59,60,61,62,63, +#if ! defined(UNDERSIZED_ECCPOS_API) + 67,68,69,70,71,72,73,74,75,76,77,78,79, + 83,84,85,86,87,88,89,90,91,92,93,94,95, + 99,100,101,102,103,104,105,106,107,108,109,110,111, + 115,116,117,118,119,120,121,122,123,124,125,126,127, + + 131,132,133,134,135,136,137,138,139,140,141,142,143, + 147,148,149,150,151,152,153,154,155,156,157,158,159, + 163,164,165,166,167,168,169,170,171,172,173,174,175, + 179,180,181,182,183,184,185,186,187,188,189,190,191, + 195,196,197,198,199,200,201,202,203,204,205,206,207, + 211,212,213,214,215,216,217,218,219,220,221,222,223, + 227,228,229,230,231,232,233,234,235,236,237,238,239, + 243,244,245,246,247,248,249,250,251,252,253,254,255 +#endif + }, + .oobfree = { /* 0 used for BBT and/or manufacturer bad block marker, + * first slice loses 1 byte for BBT */ + {.offset=1, .length=2}, /* 1st slice loses byte 0 */ + {.offset=16,.length=3}, /* 2nd slice */ + {.offset=32, .length=3}, /* 3rd slice */ + {.offset=48, .length=3}, /* 4th slice */ + {.offset=64, .length=3}, /* 5th slice */ + {.offset=80, .length=3}, /* 6th slice */ + {.offset=96, .length=3}, /* 7th slice */ + {.offset=112, .length=3}, /* 8th slice */ +#if ! defined(UNDERSIZED_ECCPOS_API) + {.offset=128, .length=3}, /* 9th slice */ + {.offset=144, .length=3}, /* 10th slice */ + {.offset=128, .length=3}, /* 11th slice */ + {.offset=144, .length=3}, /* 12th slice */ + {.offset=160, .length=3}, /* 13th slice */ + {.offset=176, .length=3}, /* 14th slice */ + {.offset=192, .length=3}, /* 15th slice */ + {.offset=208, .length=3}, /* 16th slice */ +#endif + //{.offset=0, .length=0} /* End marker */ + } +}; + + +/* + * 4K page MLC with BCH-8 ECC, uses 13 ECC bytes per 512B ECC step, and requires OOB size of 27B+ + */ +static struct nand_ecclayout brcmnand_oob_bch8_27_8k = { + .eccbytes = 13*16, /* 13*16 = 208 bytes */ + .eccpos = { + 14,15,16,17,18,19,20,21,22,23,24,25,26, + 41,42,43,44,45,46,47,48,49,50,51,52,53, + 68,69,70,71,72,73,74,75,76,77,78,79,80, + 95,96,97,98,99,100,101,102,103,104,105,106,107, +#if ! defined(UNDERSIZED_ECCPOS_API) + 122,123,124,125,126,127,128,129,130,131,132,133,134, + 149,150,151,152,153,154,155,156,157,158,159,160,161, + 176,177,178,179,180,181,182,183,184,185,186,187,188, + 203,204,205,206,207,208,209,210,211,212,213,214,215, + + 230,231,232,233,234,235,236,237,238,239,240,241,242, + 257,258,259,260,261,262,263,264,265,266,267,268,269, + 284,285,286,287,288,289,290,291,292,293,294,295,296, + 311,312,313,314,315,316,317,318,319,320,321,322,323, + 338,339,340,341,342,343,344,345,346,347,348,349,350, + 365,366,367,368,369,370,371,372,373,374,375,376,377, + 392,393,394,395,396,397,398,399,400,401,402,403,404, + 419,420,421,422,423,424,425,426,427,428,429,430,431 +#endif + }, + .oobfree = { /* 0 used for BBT and/or manufacturer bad block marker, + * first slice loses 1 byte for BBT */ + {.offset=1, .length=13}, /* 1st slice loses byte 0 */ + {.offset=27,.length=14}, /* 2nd slice */ + {.offset=54, .length=14}, /* 3rd slice */ + {.offset=81, .length=14}, /* 4th slice */ + {.offset=108, .length=14}, /* 5th slice */ + {.offset=135, .length=14}, /* 6th slice */ + {.offset=162, .length=14}, /* 7th slice */ + {.offset=189, .length=14}, /* 8th slice */ +#if ! defined(UNDERSIZED_ECCPOS_API) + {.offset=216, .length=14}, /* 9th slice */ + {.offset=243, .length=14}, /* 10th slice */ + {.offset=270, .length=14}, /* 11th slice */ + {.offset=297, .length=14}, /* 12th slice */ + {.offset=324, .length=14}, /* 13th slice */ + {.offset=351, .length=14}, /* 14th slice */ + {.offset=378, .length=14}, /* 15th slice */ + {.offset=405, .length=14}, /* 16th slice */ +#endif + } +}; + +/* + * 2K page with BCH-12 ECC, uses 20 ECC bytes per 512B ECC step, and requires OOB size of 27B+ + */ +static struct nand_ecclayout brcmnand_oob_bch12_27_2k = { + .eccbytes = 20*4, /* 20*8 = 160 bytes */ + .eccpos = { + 7, 8, 9, 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26, + 34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53, + 61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80, + 88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107 + }, + .oobfree = { /* 0 used for BBT and/or manufacturer bad block marker, + * first slice loses 1 byte for BBT */ + {.offset=1, .length=6}, /* 1st slice loses byte 0 */ + {.offset=27,.length=7}, /* 2nd slice */ + {.offset=54, .length=7}, /* 3rd slice */ + {.offset=81, .length=7}, /* 4th slice */ + {.offset=0, .length=0} /* End marker */ + } +}; + +/* + * 4K page MLC with BCH-12 ECC, uses 20 ECC bytes per 512B ECC step, and requires OOB size of 27B+ + */ +static struct nand_ecclayout brcmnand_oob_bch12_27_4k = { + .eccbytes = 20*8, /* 20*8 = 160 bytes */ + .eccpos = { + 7, 8, 9, 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26, + 34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53, + 61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80, + 88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107, +#if ! defined(UNDERSIZED_ECCPOS_API) + 115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134, + 142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161, + 169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188, + 196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215 +#endif + }, + .oobfree = { /* 0 used for BBT and/or manufacturer bad block marker, + * first slice loses 1 byte for BBT */ + {.offset=1, .length=6}, /* 1st slice loses byte 0 */ + {.offset=27,.length=7}, /* 2nd slice */ + {.offset=54, .length=7}, /* 3rd slice */ + {.offset=81, .length=7}, /* 4th slice */ + {.offset=108, .length=7}, /* 5th slice */ + {.offset=135, .length=7}, /* 6th slice */ + {.offset=162, .length=7}, /* 7th slice */ + {.offset=189, .length=7}, /* 8th slice */ + //{.offset=0, .length=0} /* End marker */ + } +}; + + +/* + * 4K page MLC with BCH-12 ECC, uses 20 ECC bytes per 512B ECC step, and requires OOB size of 27B+ + */ +static struct nand_ecclayout brcmnand_oob_bch12_27_8k = { + .eccbytes = 20*16, /* 20*8 = 320 bytes */ + .eccpos = { + 7, 8, 9, 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26, + 34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53, + 61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80, + 88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107, +#if ! defined(UNDERSIZED_ECCPOS_API) + 115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134, + 142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161, + 169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188, + 196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215, + + 223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242, + 250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269, + 277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296, + 304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323, + 331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350, + 358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377, + 385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404, + 412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431 +#endif + }, + .oobfree = { /* 0 used for BBT and/or manufacturer bad block marker, + * first slice loses 1 byte for BBT */ + {.offset=1, .length=6}, /* 1st slice loses byte 0 */ + {.offset=27,.length=7}, /* 2nd slice */ + {.offset=54, .length=7}, /* 3rd slice */ + {.offset=81, .length=7}, /* 4th slice */ + {.offset=108, .length=7}, /* 5th slice */ + {.offset=135, .length=7}, /* 6th slice */ + {.offset=162, .length=7}, /* 7th slice */ + {.offset=189, .length=7}, /* 8th slice */ +#if ! defined(UNDERSIZED_ECCPOS_API) + {.offset=216, .length=7}, /* 5th slice */ + {.offset=243, .length=7}, /* 6th slice */ + {.offset=270, .length=7}, /* 7th slice */ + {.offset=297, .length=7}, /* 8th slice */ + {.offset=324, .length=7}, /* 5th slice */ + {.offset=351, .length=7}, /* 6th slice */ + {.offset=378, .length=7}, /* 7th slice */ + {.offset=405, .length=7}, /* 8th slice */ +#endif + } +}; +#else //CONFIG_MTD_BRCMNAND_VERSION = CONFIG_MTD_BRCMNAND_VERS_7_0 +/* + * 2K page SLC with BCH-8 ECC, uses 14 ECC bytes per 512B ECC step, 27B+ OOB size + */ +static struct nand_ecclayout brcmnand_oob_bch8_27_2k = { + .eccbytes = 14*4, /* 14*4 = 56 bytes */ + .eccpos = { + 13,14,15,16,17,18,19,20,21,22,23,24,25,26, + 40,41,42,43,44,45,46,47,48,49,50,51,52,53, + 67,68,69,70,71,72,73,74,75,76,77,78,79,80, + 94,95,96,97,98,99,100,101,102,103,104,105,106,107, + }, + .oobfree = { /* 0 used for BBT and/or manufacturer bad block marker, + * first slice loses 1 byte for BBT */ + {.offset=1, .length=12}, /* 1st slice loses byte 0 */ + {.offset=27,.length=13}, /* 2nd slice */ + {.offset=54, .length=13}, /* 3rd slice */ + {.offset=81, .length=13}, /* 4th slice */ + {.offset=0, .length=0} /* End marker */ + } +}; + +/* + * 4K page SLC/MLC with BCH-8 ECC, uses 14 ECC bytes per 512B ECC step, and only have 16B OOB + * Rely on the fact that the UBI/UBIFS layer does not store anything in the OOB + */ +static struct nand_ecclayout brcmnand_oob_bch8_16_4k = { + .eccbytes = 14*8, /* 14*8 = 112 bytes */ + .eccpos = { + 2,3,4,5,6,7,8,9,10,11,12,13,14,15, + 18,19,20,21,22,23,24,25,26,27,28,29,30,31, + 34,35,36,37,38,39,40,41,42,43,44,45,46,47, + 50,51,52,53,54,55,56,57,58,59,60,61,62,63, +#if ! defined(UNDERSIZED_ECCPOS_API) + 66,67,68,69,70,71,72,73,74,75,76,77,78,79, + 82,83,84,85,86,87,88,89,90,91,92,93,94,95, + 98,99,100,101,102,103,104,105,106,107,108,109,110,111, + 114,115,116,117,118,119,120,121,122,123,124,125,126,127, +#endif + }, + .oobfree = { /* 0 used for BBT and/or manufacturer bad block marker, + * first slice loses 1 byte for BBT */ + {.offset=1, .length=1}, /* 1st slice loses byte 0 */ + {.offset=16,.length=2}, /* 2nd slice */ + {.offset=32, .length=2}, /* 3rd slice */ + {.offset=48, .length=2}, /* 4th slice */ + {.offset=64, .length=2}, /* 5th slice */ + {.offset=80, .length=2}, /* 6th slice */ + {.offset=96, .length=2}, /* 7th slice */ + {.offset=112, .length=2}, /* 8th slice */ + {.offset=0, .length=0} /* End marker */ + } +}; + + +/* + * 4K page MLC with BCH-8 ECC, uses 14 ECC bytes per 512B ECC step, and requires OOB size of 27B+ + */ +static struct nand_ecclayout brcmnand_oob_bch8_27_4k = { + .eccbytes = 14*8, /* 14*8 = 112 bytes */ + .eccpos = { + 13,14,15,16,17,18,19,20,21,22,23,24,25,26, + 40,41,42,43,44,45,46,47,48,49,50,51,52,53, + 67,68,69,70,71,72,73,74,75,76,77,78,79,80, + 94,95,96,97,98,99,100,101,102,103,104,105,106,107, +#if ! defined(UNDERSIZED_ECCPOS_API) + 121,122,123,124,125,126,127,128,129,130,131,132,133,134, + 148,149,150,151,152,153,154,155,156,157,158,159,160,161, + 175,176,177,178,179,180,181,182,183,184,185,186,187,188, + 202,203,204,205,206,207,208,209,210,211,212,213,214,215 +#endif + }, + .oobfree = { /* 0 used for BBT and/or manufacturer bad block marker, + * first slice loses 1 byte for BBT */ + {.offset=1, .length=12}, /* 1st slice loses byte 0 */ + {.offset=27,.length=13}, /* 2nd slice */ + {.offset=54, .length=13}, /* 3rd slice */ + {.offset=81, .length=13}, /* 4th slice */ + {.offset=108, .length=13}, /* 5th slice */ + {.offset=135, .length=13}, /* 6th slice */ + {.offset=162, .length=13}, /* 7th slice */ + {.offset=189, .length=13}, /* 8th slice */ + {.offset=0, .length=0} /* End marker */ + } +}; + +/* + * 4K page SLC/MLC with BCH-8 ECC, uses 14 ECC bytes per 512B ECC step, and only have 16B OOB + * Rely on the fact that the UBI/UBIFS layer does not store anything in the OOB + */ +static struct nand_ecclayout brcmnand_oob_bch8_16_8k = { + .eccbytes = 14*16, /* 14*16 = 224 bytes */ + .eccpos = { + 2,3,4,5,6,7,8,9,10,11,12,13,14,15, + 18,19,20,21,22,23,24,25,26,27,28,29,30,31, + 34,35,36,37,38,39,40,41,42,43,44,45,46,47, + 50,51,52,53,54,55,56,57,58,59,60,61,62,63, +#if ! defined(UNDERSIZED_ECCPOS_API) + 66,67,68,69,70,71,72,73,74,75,76,77,78,79, + 82,83,84,85,86,87,88,89,90,91,92,93,94,95, + 98,99,100,101,102,103,104,105,106,107,108,109,110,111, + 114,115,116,117,118,119,120,121,122,123,124,125,126,127, + + 130,131,132,133,134,135,136,137,138,139,140,141,142,143, + 146,147,148,149,150,151,152,153,154,155,156,157,158,159, + 162,163,164,165,166,167,168,169,170,171,172,173,174,175, + 178,179,180,181,182,183,184,185,186,187,188,189,190,191, + 194,195,196,197,198,199,200,201,202,203,204,205,206,207, + 210,211,212,213,214,215,216,217,218,219,220,221,222,223, + 226,227,228,229,230,231,232,233,234,235,236,237,238,239, + 242,243,244,245,246,247,248,249,250,251,252,253,254,255 +#endif + }, + .oobfree = { /* 0 used for BBT and/or manufacturer bad block marker, + * first slice loses 1 byte for BBT */ + {.offset=1, .length=1}, /* 1st slice loses byte 0 */ + {.offset=16,.length=2}, /* 2nd slice */ + {.offset=32, .length=2}, /* 3rd slice */ + {.offset=48, .length=2}, /* 4th slice */ + {.offset=64, .length=2}, /* 5th slice */ + {.offset=80, .length=2}, /* 6th slice */ + {.offset=96, .length=2}, /* 7th slice */ + {.offset=112, .length=2}, /* 8th slice */ +#if ! defined(UNDERSIZED_ECCPOS_API) + {.offset=128, .length=2}, /* 9th slice */ + {.offset=144, .length=2}, /* 10th slice */ + {.offset=128, .length=2}, /* 11th slice */ + {.offset=144, .length=2}, /* 12th slice */ + {.offset=160, .length=2}, /* 13th slice */ + {.offset=176, .length=2}, /* 14th slice */ + {.offset=192, .length=2}, /* 15th slice */ + {.offset=208, .length=2}, /* 16th slice */ +#endif + {.offset=0, .length=0} /* End marker */ + } +}; + + +/* + * 4K page MLC with BCH-8 ECC, uses 14 ECC bytes per 512B ECC step, and requires OOB size of 27B+ + */ +static struct nand_ecclayout brcmnand_oob_bch8_27_8k = { + .eccbytes = 14*16, /* 14*16 = 224 bytes */ + .eccpos = { + 13,14,15,16,17,18,19,20,21,22,23,24,25,26, + 40,41,42,43,44,45,46,47,48,49,50,51,52,53, + 67,68,69,70,71,72,73,74,75,76,77,78,79,80, + 94,95,96,97,98,99,100,101,102,103,104,105,106,107, +#if ! defined(UNDERSIZED_ECCPOS_API) + 121,122,123,124,125,126,127,128,129,130,131,132,133,134, + 148,149,150,151,152,153,154,155,156,157,158,159,160,161, + 175,176,177,178,179,180,181,182,183,184,185,186,187,188, + 202,203,204,205,206,207,208,209,210,211,212,213,214,215, + + 229,230,231,232,233,234,235,236,237,238,239,240,241,242, + 256,257,258,259,260,261,262,263,264,265,266,267,268,269, + 283,284,285,286,287,288,289,290,291,292,293,294,295,296, + 310,311,312,313,314,315,316,317,318,319,320,321,322,323, + 337,338,339,340,341,342,343,344,345,346,347,348,349,350, + 364,365,366,367,368,369,370,371,372,373,374,375,376,377, + 391,392,393,394,395,396,397,398,399,400,401,402,403,404, + 418,419,420,421,422,423,424,425,426,427,428,429,430,431 +#endif + }, + .oobfree = { /* 0 used for BBT and/or manufacturer bad block marker, + * first slice loses 1 byte for BBT */ + {.offset=1, .length=12}, /* 1st slice loses byte 0 */ + {.offset=27,.length=13}, /* 2nd slice */ + {.offset=54, .length=13}, /* 3rd slice */ + {.offset=81, .length=13}, /* 4th slice */ + {.offset=108, .length=13}, /* 5th slice */ + {.offset=135, .length=13}, /* 6th slice */ + {.offset=162, .length=13}, /* 7th slice */ + {.offset=189, .length=13}, /* 8th slice */ +#if ! defined(UNDERSIZED_ECCPOS_API) + {.offset=216, .length=13}, /* 9th slice */ + {.offset=243, .length=13}, /* 10th slice */ + {.offset=270, .length=13}, /* 11th slice */ + {.offset=297, .length=13}, /* 12th slice */ + {.offset=324, .length=13}, /* 13th slice */ + {.offset=351, .length=13}, /* 14th slice */ + {.offset=378, .length=13}, /* 15th slice */ + {.offset=405, .length=13}, /* 16th slice */ +#endif + } +}; + +/* + * 2K page with BCH-12 ECC, uses 21 ECC bytes per 512B ECC step, and requires OOB size of 27B+ + */ +static struct nand_ecclayout brcmnand_oob_bch12_27_2k = { + .eccbytes = 21*4, /* 21*4 = 84 bytes */ + .eccpos = { + 6, 7, 8, 9, 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26, + 33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53, + 60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80, + 87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107 + }, + .oobfree = { /* 0 used for BBT and/or manufacturer bad block marker, + * first slice loses 1 byte for BBT */ + {.offset=1, .length=5}, /* 1st slice loses byte 0 */ + {.offset=27,.length=6}, /* 2nd slice */ + {.offset=54, .length=6}, /* 3rd slice */ + {.offset=81, .length=6}, /* 4th slice */ + {.offset=0, .length=0} /* End marker */ + } +}; + +/* + * 4K page MLC with BCH-12 ECC, uses 21 ECC bytes per 512B ECC step, and requires OOB size of 27B+ + */ +static struct nand_ecclayout brcmnand_oob_bch12_27_4k = { + .eccbytes = 21*8, /* 21*8 = 168 bytes */ + .eccpos = { + 6, 7, 8, 9, 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26, + 33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53, + 60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80, + 87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107, +#if ! defined(UNDERSIZED_ECCPOS_API) + 114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134, + 141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161, + 168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188, + 195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215 +#endif + }, + .oobfree = { /* 0 used for BBT and/or manufacturer bad block marker, + * first slice loses 1 byte for BBT */ + {.offset=1, .length=5}, /* 1st slice loses byte 0 */ + {.offset=27,.length=6}, /* 2nd slice */ + {.offset=54, .length=6}, /* 3rd slice */ + {.offset=81, .length=6}, /* 4th slice */ + {.offset=108, .length=6}, /* 5th slice */ + {.offset=135, .length=6}, /* 6th slice */ + {.offset=162, .length=6}, /* 7th slice */ + {.offset=189, .length=6}, /* 8th slice */ + {.offset=0, .length=0} /* End marker */ + } +}; + + +/* + * 4K page MLC with BCH-12 ECC, uses 21 ECC bytes per 512B ECC step, and requires OOB size of 27B+ + */ +static struct nand_ecclayout brcmnand_oob_bch12_27_8k = { + .eccbytes = 21*16, /* 21*16 = 336 bytes */ + .eccpos = { + 6, 7, 8, 9, 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26, + 33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53, + 60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80, + 87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107, +#if ! defined(UNDERSIZED_ECCPOS_API) + 114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134, + 141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161, + 168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188, + 195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215, + 222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242, + 249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269, + 276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296, + 303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323, + 330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350, + 357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377, + 384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404, + 411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431 +#endif + }, + .oobfree = { /* 0 used for BBT and/or manufacturer bad block marker, + * first slice loses 1 byte for BBT */ + {.offset=1, .length=5}, /* 1st slice loses byte 0 */ + {.offset=27,.length=6}, /* 2nd slice */ + {.offset=54, .length=6}, /* 3rd slice */ + {.offset=81, .length=6}, /* 4th slice */ + {.offset=108, .length=6}, /* 5th slice */ + {.offset=135, .length=6}, /* 6th slice */ + {.offset=162, .length=6}, /* 7th slice */ + {.offset=189, .length=6}, /* 8th slice */ +#if ! defined(UNDERSIZED_ECCPOS_API) + {.offset=216, .length=6}, /* 5th slice */ + {.offset=243, .length=6}, /* 6th slice */ + {.offset=270, .length=6}, /* 7th slice */ + {.offset=297, .length=6}, /* 8th slice */ + {.offset=324, .length=6}, /* 5th slice */ + {.offset=351, .length=6}, /* 6th slice */ + {.offset=378, .length=6}, /* 7th slice */ + {.offset=405, .length=6}, /* 8th slice */ +#endif + } +}; +#endif + +#endif + +#endif diff -urN linux-4.1.52-orig/include/net/af_mhi.h linux-4.1.52/include/net/af_mhi.h --- linux-4.1.52-orig/include/net/af_mhi.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/net/af_mhi.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,53 @@ +#ifdef CONFIG_BCM_KF_MHI +/* +<:copyright-BRCM:2011:DUAL/GPL:standard + + Copyright (c) 2011 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +/* + * File: net/af_mhi.h + * + * MHI Protocol Family kernel definitions + */ + +#ifndef __LINUX_NET_AFMHI_H +#define __LINUX_NET_AFMHI_H + +#include +#include + +#include + + +extern int mhi_register_protocol(int protocol); +extern int mhi_unregister_protocol(int protocol); +extern int mhi_protocol_registered(int protocol); + +extern int mhi_skb_send(struct sk_buff *skb, struct net_device *dev, u8 proto); + + +#endif /* __LINUX_NET_AFMHI_H */ +#endif /* CONFIG_BCM_KF_MHI */ diff -urN linux-4.1.52-orig/include/net/bl_ops.h linux-4.1.52/include/net/bl_ops.h --- linux-4.1.52-orig/include/net/bl_ops.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/net/bl_ops.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,50 @@ +#if defined(CONFIG_BCM_KF_RUNNER) +#ifndef BL_OPS_H +#define BL_OPS_H + +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) +#if defined(CONFIG_BCM_RUNNER_RG) || defined(CONFIG_BCM_RUNNER_RG_MODULE) + +#include +#include + +struct bl_ops_t { + void (*net_sched_sch_tbf_tbf_change)(void *_q, void *_sch, void *_qopt, int max_size); + void (*net_sched_sch_tbf_tbf_destroy)(void *_q, void *_sch); + void (*net_sched_sch_prio_prio_classify)(struct sk_buff *skb, u32 band); + void (*net_netfilter_nf_conntrack_ftp)(struct nf_conn *ct, int ctinfo, struct nf_conntrack_expect *exp, + int ftptype); + void (*net_ipv4_netfilter_nf_nat_ftp)(struct nf_conn *ct, u_int16_t port, char buffer[], int ctinfo); + void (*net_ipv4_netfilter_nf_nat_sip)(struct nf_conn *ct, u_int16_t port, int dir); + void (*net_ipv4_netfilter_nf_nat_rtsp)(int num, int ctinfo, struct nf_conn *ct, u_int16_t loport, + u_int16_t hiport); + void (*net_ipv4_netfilter_ip_tables_check_match)(void *_m, void *_par, const void *_ip); + void (*net_ipv4_netfilter_ip_tables___do_replace)(void *_oldinfo, void *_newinfo); + void (*net_ipv4_netfilter_ip_tables_do_replace)(void *_oldinfo); + void (*net_netfilter_xt_PORTTRIG_trigger_refresh)(void *_trig); + void (*net_netfilter_xt_PORTTRIG_trigger_delete)(void *_trig); + void (*net_netfilter_xt_PORTTRIG_trigger_new)(struct nf_conn *ct, __be32 srcip, __be32 dstip, __be16 port_start, __be16 port_end, __be16 protocol); + void (*net_netfilter_nf_conntrack_core_destroy_conntrack)(struct nf_conn *ct); + int (*net_netfilter_nf_conntrack_core_death_by_timeout)(struct nf_conn *ct); + void (*net_netfilter_nf_conntrack_core_nf_conntrack_confirm)(struct nf_conn *ct, struct sk_buff *skb); + void (*net_netfilter_nf_conntrack_core_nf_conntrack_in)(struct nf_conn *ct, struct sk_buff *skb); + void (*net_netfilter_nf_conntrack_core_nf_conntrack_alloc)(struct nf_conn *ct); + void (*net_netfilter_nf_conntrack_core_nf_conntrack_free)(struct nf_conn *ct); +}; + +#define BL_OPS(op) { if (bl_ops) bl_ops->op; } +#define BL_OPS_CR(op) { if (bl_ops && (bl_ops->op)) return; } + +extern struct bl_ops_t *bl_ops; + +#else /* CONFIG_BCM_RDPA_BRIDGE || CONFIG_BCM_RDPA_BRIDGE_MODULE */ + +#define BL_OPS(op) +#define BL_OPS_CR(op) + +#endif /* CONFIG_BCM_RUNNER_RG || CONFIG_BCM_RUNNER_RG_MODULE */ +#endif /* CONFIG_BCM_RUNNER */ + +#endif /* BL_OPS_H */ +#endif /* CONFIG_BCM_KF_RUNNER */ + diff -urN linux-4.1.52-orig/include/net/bond_3ad.h linux-4.1.52/include/net/bond_3ad.h --- linux-4.1.52-orig/include/net/bond_3ad.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/net/bond_3ad.h 2020-02-07 01:47:54.000000000 +0800 @@ -37,6 +37,11 @@ #define AD_LACP_SLOW 0 #define AD_LACP_FAST 1 +#if defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) +#define BOND_ASYNC_LINKSPEED_OFF 0 +#define BOND_ASYNC_LINKSPEED_ON 1 +#endif /* defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) */ + typedef struct mac_addr { u8 mac_addr_value[ETH_ALEN]; } __packed mac_addr_t; diff -urN linux-4.1.52-orig/include/net/bonding.h linux-4.1.52/include/net/bonding.h --- linux-4.1.52-orig/include/net/bonding.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/net/bonding.h 2020-02-07 01:47:54.000000000 +0800 @@ -136,6 +136,9 @@ int packets_per_slave; int tlb_dynamic_lb; struct reciprocal_value reciprocal_packets_per_slave; +#if defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) + int async_linkspeed; +#endif /* defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) */ }; struct bond_parm_tbl { diff -urN linux-4.1.52-orig/include/net/bond_options.h linux-4.1.52/include/net/bond_options.h --- linux-4.1.52-orig/include/net/bond_options.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/net/bond_options.h 2020-02-07 01:47:54.000000000 +0800 @@ -63,6 +63,9 @@ BOND_OPT_LP_INTERVAL, BOND_OPT_SLAVES, BOND_OPT_TLB_DYNAMIC_LB, +#if defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) + BOND_OPT_ASYNC_LINKSPEED, +#endif /* defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) */ BOND_OPT_LAST }; diff -urN linux-4.1.52-orig/include/net/cfg80211.h linux-4.1.52/include/net/cfg80211.h --- linux-4.1.52-orig/include/net/cfg80211.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/net/cfg80211.h 2020-02-07 01:47:54.000000000 +0800 @@ -75,12 +75,18 @@ * @IEEE80211_BAND_2GHZ: 2.4GHz ISM band * @IEEE80211_BAND_5GHZ: around 5GHz band (4.9-5.7) * @IEEE80211_BAND_60GHZ: around 60 GHz band (58.32 - 64.80 GHz) +#ifdef CONFIG_BCM_KF_NL80211_6G_BAND_SUPPORT + * @IEEE80211_BAND_6GHZ: around 6 GHz band (5.9 - 7.1 GHz) +#endif * @IEEE80211_NUM_BANDS: number of defined bands */ enum ieee80211_band { IEEE80211_BAND_2GHZ = NL80211_BAND_2GHZ, IEEE80211_BAND_5GHZ = NL80211_BAND_5GHZ, IEEE80211_BAND_60GHZ = NL80211_BAND_60GHZ, +#ifdef CONFIG_BCM_KF_NL80211_6G_BAND_SUPPORT + IEEE80211_BAND_6GHZ = NL80211_BAND_6GHZ, +#endif /* CONFIG_BCM_KF_NLG80211_6G_BAND_SUPPORT */ /* keep last */ IEEE80211_NUM_BANDS @@ -2179,6 +2185,34 @@ struct cfg80211_dscp_exception dscp_exception[IEEE80211_QOS_MAP_MAX_EX]; struct cfg80211_dscp_range up[8]; }; +#ifdef CONFIG_BCM_KF_CFG80211_BACKPORT +/** + * struct cfg80211_external_auth_params - Trigger External authentication. + * + * Commonly used across the external auth request and event interfaces. + * + * @action: action type / trigger for external authentication. Only significant + * for the authentication request event interface (driver to user space). + * @bssid: BSSID of the peer with which the authentication has + * to happen. Used by both the authentication request event and + * authentication response command interface. + * @ssid: SSID of the AP. Used by both the authentication request event and + * authentication response command interface. + * @key_mgmt_suite: AKM suite of the respective authentication. Used by the + * authentication request event interface. + * @status: status code, %WLAN_STATUS_SUCCESS for successful authentication, + * use %WLAN_STATUS_UNSPECIFIED_FAILURE if user space cannot give you + * the real status code for failures. Used only for the authentication + * response command interface (user space to driver). + */ +struct cfg80211_external_auth_params { + enum nl80211_external_auth_action action; + u8 bssid[ETH_ALEN] __aligned(2); + struct cfg80211_ssid ssid; + unsigned int key_mgmt_suite; + u16 status; +}; +#endif /** * struct cfg80211_ops - backend description for wireless configuration @@ -2712,6 +2746,14 @@ void (*tdls_cancel_channel_switch)(struct wiphy *wiphy, struct net_device *dev, const u8 *addr); +#ifdef CONFIG_BCM_KF_CFG80211_BACKPORT +/* + * @external_auth: indicates result of offloaded authentication processing from + * user space + */ + int (*external_auth)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_external_auth_params *params); +#endif }; /* @@ -5210,7 +5252,18 @@ /* ethtool helper */ void cfg80211_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info); - +#ifdef CONFIG_BCM_KF_CFG80211_BACKPORT +/** + * cfg80211_external_auth_request - userspace request for authentication + * @netdev: network device + * @params: External authentication parameters + * @gfp: allocation flags + * Returns: 0 on success, < 0 on error + */ +int cfg80211_external_auth_request(struct net_device *netdev, + struct cfg80211_external_auth_params *params, + gfp_t gfp); +#endif /* Logging, debugging and troubleshooting/diagnostic helpers. */ /* wiphy_printk helpers, similar to dev_printk */ diff -urN linux-4.1.52-orig/include/net/icmp.h linux-4.1.52/include/net/icmp.h --- linux-4.1.52-orig/include/net/icmp.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/net/icmp.h 2020-02-07 01:47:54.000000000 +0800 @@ -45,4 +45,7 @@ int icmp_init(void); void icmp_out_count(struct net *net, unsigned char type); +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) +void send_icmp_frag(struct sk_buff *skb_in, int type, int code, __be32 info); +#endif #endif /* _ICMP_H */ diff -urN linux-4.1.52-orig/include/net/if_inet6.h linux-4.1.52/include/net/if_inet6.h --- linux-4.1.52-orig/include/net/if_inet6.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/net/if_inet6.h 2020-02-07 01:47:54.000000000 +0800 @@ -120,6 +120,9 @@ struct ip6_sf_list *mca_sources; struct ip6_sf_list *mca_tomb; unsigned int mca_sfmode; +#if defined(CONFIG_BCM_KF_MCAST_GR_SUPPRESSION) + unsigned int mca_osfmode; +#endif unsigned char mca_crcount; unsigned long mca_sfcount[2]; struct timer_list mca_timer; diff -urN linux-4.1.52-orig/include/net/inet6_connection_sock.h linux-4.1.52/include/net/inet6_connection_sock.h --- linux-4.1.52-orig/include/net/inet6_connection_sock.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/net/inet6_connection_sock.h 2020-02-07 01:47:54.000000000 +0800 @@ -27,6 +27,10 @@ struct dst_entry *inet6_csk_route_req(struct sock *sk, struct flowi6 *fl6, const struct request_sock *req); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +u32 inet6_synq_hash(const struct in6_addr *raddr, const __be16 rport, + const u32 rnd, const u32 synq_hsize); +#endif struct request_sock *inet6_csk_search_req(struct sock *sk, const __be16 rport, diff -urN linux-4.1.52-orig/include/net/inet_common.h linux-4.1.52/include/net/inet_common.h --- linux-4.1.52-orig/include/net/inet_common.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/net/inet_common.h 2020-02-07 01:47:54.000000000 +0800 @@ -1,6 +1,10 @@ #ifndef _INET_COMMON_H #define _INET_COMMON_H +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#include + +#endif extern const struct proto_ops inet_stream_ops; extern const struct proto_ops inet_dgram_ops; @@ -13,6 +17,10 @@ struct sockaddr; struct socket; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +int inet_create(struct net *net, struct socket *sock, int protocol, int kern); +int inet6_create(struct net *net, struct socket *sock, int protocol, int kern); +#endif int inet_release(struct socket *sock); int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags); diff -urN linux-4.1.52-orig/include/net/inet_connection_sock.h linux-4.1.52/include/net/inet_connection_sock.h --- linux-4.1.52-orig/include/net/inet_connection_sock.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/net/inet_connection_sock.h 2020-02-07 01:47:54.000000000 +0800 @@ -30,6 +30,9 @@ struct inet_bind_bucket; struct tcp_congestion_ops; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +struct tcp_options_received; +#endif /* * Pointers to address related TCP functions @@ -258,6 +261,11 @@ struct sock *inet_csk_accept(struct sock *sk, int flags, int *err); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +u32 inet_synq_hash(const __be32 raddr, const __be16 rport, const u32 rnd, + const u32 synq_hsize); + +#endif struct request_sock *inet_csk_search_req(struct sock *sk, const __be16 rport, const __be32 raddr, diff -urN linux-4.1.52-orig/include/net/inet_sock.h linux-4.1.52/include/net/inet_sock.h --- linux-4.1.52-orig/include/net/inet_sock.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/net/inet_sock.h 2020-02-07 01:47:54.000000000 +0800 @@ -91,7 +91,13 @@ wscale_ok : 1, ecn_ok : 1, acked : 1, +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) no_srccheck: 1; +#else + no_srccheck: 1, + mptcp_rqsk : 1, + saw_mpc : 1; +#endif kmemcheck_bitfield_end(flags); u32 ir_mark; union { diff -urN linux-4.1.52-orig/include/net/ip.h linux-4.1.52/include/net/ip.h --- linux-4.1.52-orig/include/net/ip.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/net/ip.h 2020-02-07 01:47:54.000000000 +0800 @@ -481,6 +481,9 @@ IP_DEFRAG_VS_FWD, IP_DEFRAG_AF_PACKET, IP_DEFRAG_MACVLAN, +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + IP_DEFRAG_MAP, +#endif }; int ip_defrag(struct sk_buff *skb, u32 user); diff -urN linux-4.1.52-orig/include/net/ipv6.h linux-4.1.52/include/net/ipv6.h --- linux-4.1.52-orig/include/net/ipv6.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/net/ipv6.h 2020-02-07 01:47:54.000000000 +0800 @@ -310,11 +310,22 @@ static inline bool ipv6_accept_ra(struct inet6_dev *idev) { +#if defined(CONFIG_BCM_KF_IP) + /* WAN interface needs to act like a host. */ + if (((idev->cnf.forwarding) && + (!(idev->dev->priv_flags & IFF_WANDEV) || + ((idev->dev->priv_flags & IFF_WANDEV) && + netdev_path_is_root(idev->dev)))) + && (idev->cnf.accept_ra < 2)) + return 0; + return idev->cnf.accept_ra; +#else /* If forwarding is enabled, RA are not accepted unless the special * hybrid mode (accept_ra=2) is enabled. */ return idev->cnf.forwarding ? idev->cnf.accept_ra == 2 : idev->cnf.accept_ra; +#endif } #if IS_ENABLED(CONFIG_IPV6) diff -urN linux-4.1.52-orig/include/net/mhi/dgram.h linux-4.1.52/include/net/mhi/dgram.h --- linux-4.1.52-orig/include/net/mhi/dgram.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/net/mhi/dgram.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,55 @@ +#ifdef CONFIG_BCM_KF_MHI +/* +<:copyright-BRCM:2012:DUAL/GPL:standard + + Copyright (c) 2012 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +/* + * File: mhi/dgram.h + * + * MHI DGRAM socket definitions + */ + +#ifndef MHI_DGRAM_H +#define MHI_DGRAM_H + +#include +#include + +#include + + +extern int mhi_dgram_sock_create( + struct net *net, + struct socket *sock, + int proto, + int kern); + +extern int mhi_dgram_proto_init(void); +extern void mhi_dgram_proto_exit(void); + +#endif /* MHI_DGRAM_H */ +#endif /* CONFIG_BCM_KF_MHI */ diff -urN linux-4.1.52-orig/include/net/mhi/mhdp.h linux-4.1.52/include/net/mhi/mhdp.h --- linux-4.1.52-orig/include/net/mhi/mhdp.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/net/mhi/mhdp.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,61 @@ +#ifdef CONFIG_BCM_KF_MHI +/* +<:copyright-BRCM:2012:DUAL/GPL:standard + + Copyright (c) 2012 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +/* + * File: mhdp.h + * + * Modem-Host Interface (MHI) - MHDP kernel interface + */ + + +#ifndef __NET_MHI_MHDP_H +#define __NET_MHI_MHDP_H + +struct mhdp_tunnel_parm { + char name[IFNAMSIZ]; + char master[IFNAMSIZ]; + int pdn_id; + int sim_id; +}; + +struct mhdp_udp_filter { + + unsigned short port_id; + unsigned char active; +}; + +#define SIOCADDPDNID (SIOCDEVPRIVATE + 1) +#define SIOCDELPDNID (SIOCDEVPRIVATE + 2) +#define SIOCRESETMHDP (SIOCDEVPRIVATE + 3) +#define SIOSETUDPFILTER (SIOCDEVPRIVATE + 4) + +struct net_device *mhdp_get_netdev_by_pdn_id(struct net_device *dev, int pdn_id); + +#endif /* __NET_MHI_MHDP_H */ +#endif /* CONFIG_BCM_KF_MHI */ diff -urN linux-4.1.52-orig/include/net/mhi/raw.h linux-4.1.52/include/net/mhi/raw.h --- linux-4.1.52-orig/include/net/mhi/raw.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/net/mhi/raw.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,55 @@ +#ifdef CONFIG_BCM_KF_MHI +/* +<:copyright-BRCM:2012:DUAL/GPL:standard + + Copyright (c) 2012 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +/* + * File: mhi/raw.h + * + * MHI RAW socket definitions + */ + +#ifndef MHI_RAW_H +#define MHI_RAW_H + +#include +#include + +#include + + +extern int mhi_raw_sock_create( + struct net *net, + struct socket *sock, + int proto, + int kern); + +extern int mhi_raw_proto_init(void); +extern void mhi_raw_proto_exit(void); + +#endif /* MHI_RAW_H */ +#endif /* CONFIG_BCM_KF_MHI */ diff -urN linux-4.1.52-orig/include/net/mhi/sched.h linux-4.1.52/include/net/mhi/sched.h --- linux-4.1.52-orig/include/net/mhi/sched.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/net/mhi/sched.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,53 @@ +#ifdef CONFIG_BCM_KF_MHI +/* +<:copyright-BRCM:2012:DUAL/GPL:standard + + Copyright (c) 2012 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +/* + * File: mhi/sched.h + * + * Modem-Host Interface Scheduling + */ + +#ifndef MHI_SCHED_H +#define MHI_SCHED_H + +#define MHI_NOTIFY_QUEUE_LOW 19 +#define MHI_NOTIFY_QUEUE_HIGH 20 + +extern int +mhi_register_queue_notifier(struct Qdisc *sch, + struct notifier_block *nb, + unsigned long cl); + +extern int +mhi_unregister_queue_notifier(struct Qdisc *sch, + struct notifier_block *nb, + unsigned long cl); + +#endif /* MHI_SCHED_H */ +#endif /* CONFIG_BCM_KF_MHI */ diff -urN linux-4.1.52-orig/include/net/mhi/sock.h linux-4.1.52/include/net/mhi/sock.h --- linux-4.1.52-orig/include/net/mhi/sock.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/net/mhi/sock.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,57 @@ +#ifdef CONFIG_BCM_KF_MHI +/* +<:copyright-BRCM:2012:DUAL/GPL:standard + + Copyright (c) 2012 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +/* + * File: mhi/sock.h + * + * MHI socket definitions + */ + +#ifndef MHI_SOCK_H +#define MHI_SOCK_H + +#include +#include + +#include + + +extern const struct proto_ops mhi_socket_ops; + +extern int mhi_sock_rcv_unicast(struct sk_buff *skb, u8 l3prot, u32 l3len); +extern int mhi_sock_rcv_multicast(struct sk_buff *skb, u8 l3prot, u32 l3len); + +extern void mhi_sock_hash(struct sock *sk); +extern void mhi_sock_unhash(struct sock *sk); + +extern int mhi_sock_init(void); +extern void mhi_sock_exit(void); + +#endif /* MHI_SOCK_H */ +#endif /* CONFIG_BCM_KF_MHI */ diff -urN linux-4.1.52-orig/include/net/mptcp.h linux-4.1.52/include/net/mptcp.h --- linux-4.1.52-orig/include/net/mptcp.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/net/mptcp.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,1518 @@ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +/* + * MPTCP implementation + * + * Initial Design & Implementation: + * Sébastien Barré + * + * Current Maintainer & Author: + * Christoph Paasch + * + * Additional authors: + * Jaakko Korkeaniemi + * Gregory Detal + * Fabien Duchêne + * Andreas Seelinger + * Lavkesh Lahngir + * Andreas Ripke + * Vlad Dogaru + * Octavian Purdila + * John Ronan + * Catalin Nicutar + * Brandon Heller + * + * + * 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. + */ + +#ifndef _MPTCP_H +#define _MPTCP_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if defined(__LITTLE_ENDIAN_BITFIELD) + #define ntohll(x) be64_to_cpu(x) + #define htonll(x) cpu_to_be64(x) +#elif defined(__BIG_ENDIAN_BITFIELD) + #define ntohll(x) (x) + #define htonll(x) (x) +#endif + +struct mptcp_loc4 { + u8 loc4_id; + u8 low_prio:1; + int if_idx; + struct in_addr addr; +}; + +struct mptcp_rem4 { + u8 rem4_id; + __be16 port; + struct in_addr addr; +}; + +struct mptcp_loc6 { + u8 loc6_id; + u8 low_prio:1; + int if_idx; + struct in6_addr addr; +}; + +struct mptcp_rem6 { + u8 rem6_id; + __be16 port; + struct in6_addr addr; +}; + +struct mptcp_request_sock { + struct tcp_request_sock req; + /* hlist-nulls entry to the hash-table. Depending on whether this is a + * a new MPTCP connection or an additional subflow, the request-socket + * is either in the mptcp_reqsk_tk_htb or mptcp_reqsk_htb. + */ + struct hlist_nulls_node hash_entry; + + union { + struct { + /* Only on initial subflows */ + u64 mptcp_loc_key; + u64 mptcp_rem_key; + u32 mptcp_loc_token; + }; + + struct { + /* Only on additional subflows */ + struct mptcp_cb *mptcp_mpcb; + u32 mptcp_rem_nonce; + u32 mptcp_loc_nonce; + u64 mptcp_hash_tmac; + }; + }; + + u8 loc_id; + u8 rem_id; /* Address-id in the MP_JOIN */ + u8 dss_csum:1, + is_sub:1, /* Is this a new subflow? */ + low_prio:1, /* Interface set to low-prio? */ + rcv_low_prio:1, + mptcp_ver:4; +}; + +struct mptcp_options_received { + u16 saw_mpc:1, + dss_csum:1, + drop_me:1, + + is_mp_join:1, + join_ack:1, + + saw_low_prio:2, /* 0x1 - low-prio set for this subflow + * 0x2 - low-prio set for another subflow + */ + low_prio:1, + + saw_add_addr:2, /* Saw at least one add_addr option: + * 0x1: IPv4 - 0x2: IPv6 + */ + more_add_addr:1, /* Saw one more add-addr. */ + + saw_rem_addr:1, /* Saw at least one rem_addr option */ + more_rem_addr:1, /* Saw one more rem-addr. */ + + mp_fail:1, + mp_fclose:1; + u8 rem_id; /* Address-id in the MP_JOIN */ + u8 prio_addr_id; /* Address-id in the MP_PRIO */ + + const unsigned char *add_addr_ptr; /* Pointer to add-address option */ + const unsigned char *rem_addr_ptr; /* Pointer to rem-address option */ + + u32 data_ack; + u32 data_seq; + u16 data_len; + + u8 mptcp_ver; /* MPTCP version */ + + /* Key inside the option (from mp_capable or fast_close) */ + u64 mptcp_sender_key; + u64 mptcp_receiver_key; + + u32 mptcp_rem_token; /* Remote token */ + + u32 mptcp_recv_nonce; + u64 mptcp_recv_tmac; + u8 mptcp_recv_mac[20]; +}; + +struct mptcp_tcp_sock { + struct tcp_sock *next; /* Next subflow socket */ + struct hlist_node cb_list; + struct mptcp_options_received rx_opt; + + /* Those three fields record the current mapping */ + u64 map_data_seq; + u32 map_subseq; + u16 map_data_len; + u16 slave_sk:1, + fully_established:1, + establish_increased:1, + second_packet:1, + attached:1, + send_mp_fail:1, + include_mpc:1, + mapping_present:1, + map_data_fin:1, + low_prio:1, /* use this socket as backup */ + rcv_low_prio:1, /* Peer sent low-prio option to us */ + send_mp_prio:1, /* Trigger to send mp_prio on this socket */ + pre_established:1; /* State between sending 3rd ACK and + * receiving the fourth ack of new subflows. + */ + + /* isn: needed to translate abs to relative subflow seqnums */ + u32 snt_isn; + u32 rcv_isn; + u8 path_index; + u8 loc_id; + u8 rem_id; + +#define MPTCP_SCHED_SIZE 16 + u8 mptcp_sched[MPTCP_SCHED_SIZE] __aligned(8); + + struct sk_buff *shortcut_ofoqueue; /* Shortcut to the current modified + * skb in the ofo-queue. + */ + + int init_rcv_wnd; + u32 infinite_cutoff_seq; + struct delayed_work work; + u32 mptcp_loc_nonce; + struct tcp_sock *tp; /* Where is my daddy? */ + u32 last_end_data_seq; + + /* MP_JOIN subflow: timer for retransmitting the 3rd ack */ + struct timer_list mptcp_ack_timer; + + /* HMAC of the third ack */ + char sender_mac[20]; +}; + +struct mptcp_tw { + struct list_head list; + u64 loc_key; + u64 rcv_nxt; + struct mptcp_cb __rcu *mpcb; + u8 meta_tw:1, + in_list:1; +}; + +#define MPTCP_PM_NAME_MAX 16 +struct mptcp_pm_ops { + struct list_head list; + + /* Signal the creation of a new MPTCP-session. */ + void (*new_session)(const struct sock *meta_sk); + void (*release_sock)(struct sock *meta_sk); + void (*fully_established)(struct sock *meta_sk); + void (*new_remote_address)(struct sock *meta_sk); + int (*get_local_id)(sa_family_t family, union inet_addr *addr, + struct net *net, bool *low_prio); + void (*addr_signal)(struct sock *sk, unsigned *size, + struct tcp_out_options *opts, struct sk_buff *skb); + void (*add_raddr)(struct mptcp_cb *mpcb, const union inet_addr *addr, + sa_family_t family, __be16 port, u8 id); + void (*rem_raddr)(struct mptcp_cb *mpcb, u8 rem_id); + void (*init_subsocket_v4)(struct sock *sk, struct in_addr addr); + void (*init_subsocket_v6)(struct sock *sk, struct in6_addr addr); + + char name[MPTCP_PM_NAME_MAX]; + struct module *owner; +}; + +#define MPTCP_SCHED_NAME_MAX 16 +struct mptcp_sched_ops { + struct list_head list; + + struct sock * (*get_subflow)(struct sock *meta_sk, + struct sk_buff *skb, + bool zero_wnd_test); + struct sk_buff * (*next_segment)(struct sock *meta_sk, + int *reinject, + struct sock **subsk, + unsigned int *limit); + void (*init)(struct sock *sk); + void (*release)(struct sock *sk); + + char name[MPTCP_SCHED_NAME_MAX]; + struct module *owner; +}; + +struct mptcp_cb { + /* list of sockets in this multipath connection */ + struct tcp_sock *connection_list; + /* list of sockets that need a call to release_cb */ + struct hlist_head callback_list; + + /* High-order bits of 64-bit sequence numbers */ + u32 snd_high_order[2]; + u32 rcv_high_order[2]; + + u16 send_infinite_mapping:1, + in_time_wait:1, + list_rcvd:1, /* XXX TO REMOVE */ + addr_signal:1, /* Path-manager wants us to call addr_signal */ + dss_csum:1, + server_side:1, + infinite_mapping_rcv:1, + infinite_mapping_snd:1, + dfin_combined:1, /* Was the DFIN combined with subflow-fin? */ + passive_close:1, + snd_hiseq_index:1, /* Index in snd_high_order of snd_nxt */ + rcv_hiseq_index:1; /* Index in rcv_high_order of rcv_nxt */ + + /* socket count in this connection */ + u8 cnt_subflows; + u8 cnt_established; + +#define MPTCP_SCHED_DATA_SIZE 8 + u8 mptcp_sched[MPTCP_SCHED_DATA_SIZE] __aligned(8); + struct mptcp_sched_ops *sched_ops; + + struct sk_buff_head reinject_queue; + /* First cache-line boundary is here minus 8 bytes. But from the + * reinject-queue only the next and prev pointers are regularly + * accessed. Thus, the whole data-path is on a single cache-line. + */ + + u64 csum_cutoff_seq; + u64 infinite_rcv_seq; + + /***** Start of fields, used for connection closure */ + spinlock_t tw_lock; + unsigned char mptw_state; + u8 dfin_path_index; + + struct list_head tw_list; + + /***** Start of fields, used for subflow establishment and closure */ + atomic_t mpcb_refcnt; + + /* Mutex needed, because otherwise mptcp_close will complain that the + * socket is owned by the user. + * E.g., mptcp_sub_close_wq is taking the meta-lock. + */ + struct mutex mpcb_mutex; + + /***** Start of fields, used for subflow establishment */ + struct sock *meta_sk; + + /* Master socket, also part of the connection_list, this + * socket is the one that the application sees. + */ + struct sock *master_sk; + + __u64 mptcp_loc_key; + __u64 mptcp_rem_key; + __u32 mptcp_loc_token; + __u32 mptcp_rem_token; + +#define MPTCP_PM_SIZE 608 + u8 mptcp_pm[MPTCP_PM_SIZE] __aligned(8); + struct mptcp_pm_ops *pm_ops; + + u32 path_index_bits; + /* Next pi to pick up in case a new path becomes available */ + u8 next_path_index; + + __u8 mptcp_ver; + + /* Original snd/rcvbuf of the initial subflow. + * Used for the new subflows on the server-side to allow correct + * autotuning + */ + int orig_sk_rcvbuf; + int orig_sk_sndbuf; + u32 orig_window_clamp; +}; + +#define MPTCP_VERSION_0 0 +#define MPTCP_VERSION_1 1 + +#define MPTCP_SUB_CAPABLE 0 +#define MPTCP_SUB_LEN_CAPABLE_SYN 12 +#define MPTCP_SUB_LEN_CAPABLE_SYN_ALIGN 12 +#define MPTCP_SUB_LEN_CAPABLE_ACK 20 +#define MPTCP_SUB_LEN_CAPABLE_ACK_ALIGN 20 + +#define MPTCP_SUB_JOIN 1 +#define MPTCP_SUB_LEN_JOIN_SYN 12 +#define MPTCP_SUB_LEN_JOIN_SYN_ALIGN 12 +#define MPTCP_SUB_LEN_JOIN_SYNACK 16 +#define MPTCP_SUB_LEN_JOIN_SYNACK_ALIGN 16 +#define MPTCP_SUB_LEN_JOIN_ACK 24 +#define MPTCP_SUB_LEN_JOIN_ACK_ALIGN 24 + +#define MPTCP_SUB_DSS 2 +#define MPTCP_SUB_LEN_DSS 4 +#define MPTCP_SUB_LEN_DSS_ALIGN 4 + +/* Lengths for seq and ack are the ones without the generic MPTCP-option header, + * as they are part of the DSS-option. + * To get the total length, just add the different options together. + */ +#define MPTCP_SUB_LEN_SEQ 10 +#define MPTCP_SUB_LEN_SEQ_CSUM 12 +#define MPTCP_SUB_LEN_SEQ_ALIGN 12 + +#define MPTCP_SUB_LEN_SEQ_64 14 +#define MPTCP_SUB_LEN_SEQ_CSUM_64 16 +#define MPTCP_SUB_LEN_SEQ_64_ALIGN 16 + +#define MPTCP_SUB_LEN_ACK 4 +#define MPTCP_SUB_LEN_ACK_ALIGN 4 + +#define MPTCP_SUB_LEN_ACK_64 8 +#define MPTCP_SUB_LEN_ACK_64_ALIGN 8 + +/* This is the "default" option-length we will send out most often. + * MPTCP DSS-header + * 32-bit data sequence number + * 32-bit data ack + * + * It is necessary to calculate the effective MSS we will be using when + * sending data. + */ +#define MPTCP_SUB_LEN_DSM_ALIGN (MPTCP_SUB_LEN_DSS_ALIGN + \ + MPTCP_SUB_LEN_SEQ_ALIGN + \ + MPTCP_SUB_LEN_ACK_ALIGN) + +#define MPTCP_SUB_ADD_ADDR 3 +#define MPTCP_SUB_LEN_ADD_ADDR4 8 +#define MPTCP_SUB_LEN_ADD_ADDR4_VER1 16 +#define MPTCP_SUB_LEN_ADD_ADDR6 20 +#define MPTCP_SUB_LEN_ADD_ADDR6_VER1 28 +#define MPTCP_SUB_LEN_ADD_ADDR4_ALIGN 8 +#define MPTCP_SUB_LEN_ADD_ADDR4_ALIGN_VER1 16 +#define MPTCP_SUB_LEN_ADD_ADDR6_ALIGN 20 +#define MPTCP_SUB_LEN_ADD_ADDR6_ALIGN_VER1 28 + +#define MPTCP_SUB_REMOVE_ADDR 4 +#define MPTCP_SUB_LEN_REMOVE_ADDR 4 + +#define MPTCP_SUB_PRIO 5 +#define MPTCP_SUB_LEN_PRIO 3 +#define MPTCP_SUB_LEN_PRIO_ADDR 4 +#define MPTCP_SUB_LEN_PRIO_ALIGN 4 + +#define MPTCP_SUB_FAIL 6 +#define MPTCP_SUB_LEN_FAIL 12 +#define MPTCP_SUB_LEN_FAIL_ALIGN 12 + +#define MPTCP_SUB_FCLOSE 7 +#define MPTCP_SUB_LEN_FCLOSE 12 +#define MPTCP_SUB_LEN_FCLOSE_ALIGN 12 + + +#define OPTION_MPTCP (1 << 5) + +#ifdef CONFIG_MPTCP + +/* Used for checking if the mptcp initialization has been successful */ +extern bool mptcp_init_failed; + +/* MPTCP options */ +#define OPTION_TYPE_SYN (1 << 0) +#define OPTION_TYPE_SYNACK (1 << 1) +#define OPTION_TYPE_ACK (1 << 2) +#define OPTION_MP_CAPABLE (1 << 3) +#define OPTION_DATA_ACK (1 << 4) +#define OPTION_ADD_ADDR (1 << 5) +#define OPTION_MP_JOIN (1 << 6) +#define OPTION_MP_FAIL (1 << 7) +#define OPTION_MP_FCLOSE (1 << 8) +#define OPTION_REMOVE_ADDR (1 << 9) +#define OPTION_MP_PRIO (1 << 10) + +/* MPTCP flags: both TX and RX */ +#define MPTCPHDR_SEQ 0x01 /* DSS.M option is present */ +#define MPTCPHDR_FIN 0x02 /* DSS.F option is present */ +#define MPTCPHDR_SEQ64_INDEX 0x04 /* index of seq in mpcb->snd_high_order */ +/* MPTCP flags: RX only */ +#define MPTCPHDR_ACK 0x08 +#define MPTCPHDR_SEQ64_SET 0x10 /* Did we received a 64-bit seq number? */ +#define MPTCPHDR_SEQ64_OFO 0x20 /* Is it not in our circular array? */ +#define MPTCPHDR_DSS_CSUM 0x40 +#define MPTCPHDR_JOIN 0x80 +/* MPTCP flags: TX only */ +#define MPTCPHDR_INF 0x08 +#define MPTCP_REINJECT 0x10 /* Did we reinject this segment? */ + +struct mptcp_option { + __u8 kind; + __u8 len; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8 ver:4, + sub:4; +#elif defined(__BIG_ENDIAN_BITFIELD) + __u8 sub:4, + ver:4; +#else +#error "Adjust your defines" +#endif +}; + +struct mp_capable { + __u8 kind; + __u8 len; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8 ver:4, + sub:4; + __u8 h:1, + rsv:5, + b:1, + a:1; +#elif defined(__BIG_ENDIAN_BITFIELD) + __u8 sub:4, + ver:4; + __u8 a:1, + b:1, + rsv:5, + h:1; +#else +#error "Adjust your defines" +#endif + __u64 sender_key; + __u64 receiver_key; +} __attribute__((__packed__)); + +struct mp_join { + __u8 kind; + __u8 len; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8 b:1, + rsv:3, + sub:4; +#elif defined(__BIG_ENDIAN_BITFIELD) + __u8 sub:4, + rsv:3, + b:1; +#else +#error "Adjust your defines" +#endif + __u8 addr_id; + union { + struct { + u32 token; + u32 nonce; + } syn; + struct { + __u64 mac; + u32 nonce; + } synack; + struct { + __u8 mac[20]; + } ack; + } u; +} __attribute__((__packed__)); + +struct mp_dss { + __u8 kind; + __u8 len; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u16 rsv1:4, + sub:4, + A:1, + a:1, + M:1, + m:1, + F:1, + rsv2:3; +#elif defined(__BIG_ENDIAN_BITFIELD) + __u16 sub:4, + rsv1:4, + rsv2:3, + F:1, + m:1, + M:1, + a:1, + A:1; +#else +#error "Adjust your defines" +#endif +}; + +struct mp_add_addr { + __u8 kind; + __u8 len; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8 ipver:4, + sub:4; +#elif defined(__BIG_ENDIAN_BITFIELD) + __u8 sub:4, + ipver:4; +#else +#error "Adjust your defines" +#endif + __u8 addr_id; + union { + struct { + struct in_addr addr; + __be16 port; + __u8 mac[8]; + } v4; + struct { + struct in6_addr addr; + __be16 port; + __u8 mac[8]; + } v6; + } u; +} __attribute__((__packed__)); + +struct mp_remove_addr { + __u8 kind; + __u8 len; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8 rsv:4, + sub:4; +#elif defined(__BIG_ENDIAN_BITFIELD) + __u8 sub:4, + rsv:4; +#else +#error "Adjust your defines" +#endif + /* list of addr_id */ + __u8 addrs_id; +}; + +struct mp_fail { + __u8 kind; + __u8 len; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u16 rsv1:4, + sub:4, + rsv2:8; +#elif defined(__BIG_ENDIAN_BITFIELD) + __u16 sub:4, + rsv1:4, + rsv2:8; +#else +#error "Adjust your defines" +#endif + __be64 data_seq; +} __attribute__((__packed__)); + +struct mp_fclose { + __u8 kind; + __u8 len; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u16 rsv1:4, + sub:4, + rsv2:8; +#elif defined(__BIG_ENDIAN_BITFIELD) + __u16 sub:4, + rsv1:4, + rsv2:8; +#else +#error "Adjust your defines" +#endif + __u64 key; +} __attribute__((__packed__)); + +struct mp_prio { + __u8 kind; + __u8 len; +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8 b:1, + rsv:3, + sub:4; +#elif defined(__BIG_ENDIAN_BITFIELD) + __u8 sub:4, + rsv:3, + b:1; +#else +#error "Adjust your defines" +#endif + __u8 addr_id; +} __attribute__((__packed__)); + +static inline int mptcp_sub_len_dss(const struct mp_dss *m, const int csum) +{ + return 4 + m->A * (4 + m->a * 4) + m->M * (10 + m->m * 4 + csum * 2); +} + +#define MPTCP_SYSCTL 1 + +extern int sysctl_mptcp_enabled; +extern int sysctl_mptcp_version; +extern int sysctl_mptcp_checksum; +extern int sysctl_mptcp_debug; +extern int sysctl_mptcp_syn_retries; + +extern struct workqueue_struct *mptcp_wq; + +#define mptcp_debug(fmt, args...) \ + do { \ + if (unlikely(sysctl_mptcp_debug)) \ + pr_err(__FILE__ ": " fmt, ##args); \ + } while (0) + +/* Iterates over all subflows */ +#define mptcp_for_each_tp(mpcb, tp) \ + for ((tp) = (mpcb)->connection_list; (tp); (tp) = (tp)->mptcp->next) + +#define mptcp_for_each_sk(mpcb, sk) \ + for ((sk) = (struct sock *)(mpcb)->connection_list; \ + sk; \ + sk = (struct sock *)tcp_sk(sk)->mptcp->next) + +#define mptcp_for_each_sk_safe(__mpcb, __sk, __temp) \ + for (__sk = (struct sock *)(__mpcb)->connection_list, \ + __temp = __sk ? (struct sock *)tcp_sk(__sk)->mptcp->next : NULL; \ + __sk; \ + __sk = __temp, \ + __temp = __sk ? (struct sock *)tcp_sk(__sk)->mptcp->next : NULL) + +/* Iterates over all bit set to 1 in a bitset */ +#define mptcp_for_each_bit_set(b, i) \ + for (i = ffs(b) - 1; i >= 0; i = ffs(b >> (i + 1) << (i + 1)) - 1) + +#define mptcp_for_each_bit_unset(b, i) \ + mptcp_for_each_bit_set(~b, i) + +#define MPTCP_INC_STATS(net, field) SNMP_INC_STATS((net)->mptcp.mptcp_statistics, field) +#define MPTCP_INC_STATS_BH(net, field) SNMP_INC_STATS_BH((net)->mptcp.mptcp_statistics, field) + +enum +{ + MPTCP_MIB_NUM = 0, + MPTCP_MIB_MPCAPABLEPASSIVE, /* Received SYN with MP_CAPABLE */ + MPTCP_MIB_MPCAPABLEACTIVE, /* Sent SYN with MP_CAPABLE */ + MPTCP_MIB_MPCAPABLEACTIVEACK, /* Received SYN/ACK with MP_CAPABLE */ + MPTCP_MIB_MPCAPABLEPASSIVEACK, /* Received third ACK with MP_CAPABLE */ + MPTCP_MIB_MPCAPABLEPASSIVEFALLBACK,/* Server-side fallback during 3-way handshake */ + MPTCP_MIB_MPCAPABLEACTIVEFALLBACK, /* Client-side fallback during 3-way handshake */ + MPTCP_MIB_MPCAPABLERETRANSFALLBACK,/* Client-side stopped sending MP_CAPABLE after too many SYN-retransmissions */ + MPTCP_MIB_CSUMENABLED, /* Created MPTCP-connection with DSS-checksum enabled */ + MPTCP_MIB_RETRANSSEGS, /* Segments retransmitted at the MPTCP-level */ + MPTCP_MIB_MPFAILRX, /* Received an MP_FAIL */ + MPTCP_MIB_CSUMFAIL, /* Received segment with invalid checksum */ + MPTCP_MIB_FASTCLOSERX, /* Recevied a FAST_CLOSE */ + MPTCP_MIB_FASTCLOSETX, /* Sent a FAST_CLOSE */ + MPTCP_MIB_FBACKSUB, /* Fallback upon ack without data-ack on new subflow */ + MPTCP_MIB_FBACKINIT, /* Fallback upon ack without data-ack on initial subflow */ + MPTCP_MIB_FBDATASUB, /* Fallback upon data without DSS at the beginning on new subflow */ + MPTCP_MIB_FBDATAINIT, /* Fallback upon data without DSS at the beginning on initial subflow */ + MPTCP_MIB_REMADDRSUB, /* Remove subflow due to REMOVE_ADDR */ + MPTCP_MIB_JOINNOTOKEN, /* Received MP_JOIN but the token was not found */ + MPTCP_MIB_JOINFALLBACK, /* Received MP_JOIN on session that has fallen back to reg. TCP */ + MPTCP_MIB_JOINSYNTX, /* Sent a SYN + MP_JOIN */ + MPTCP_MIB_JOINSYNRX, /* Received a SYN + MP_JOIN */ + MPTCP_MIB_JOINSYNACKRX, /* Received a SYN/ACK + MP_JOIN */ + MPTCP_MIB_JOINSYNACKMAC, /* HMAC was wrong on SYN/ACK + MP_JOIN */ + MPTCP_MIB_JOINACKRX, /* Received an ACK + MP_JOIN */ + MPTCP_MIB_JOINACKMAC, /* HMAC was wrong on ACK + MP_JOIN */ + MPTCP_MIB_JOINACKFAIL, /* Third ACK on new subflow did not contain an MP_JOIN */ + MPTCP_MIB_JOINACKRTO, /* Retransmission timer for third ACK + MP_JOIN timed out */ + MPTCP_MIB_JOINACKRXMIT, /* Retransmitted an ACK + MP_JOIN */ + MPTCP_MIB_NODSSWINDOW, /* Received too many packets without a DSS-option */ + MPTCP_MIB_DSSNOMATCH, /* Received a new mapping that did not match the previous one */ + MPTCP_MIB_INFINITEMAPRX, /* Received an infinite mapping */ + MPTCP_MIB_DSSTCPMISMATCH, /* DSS-mapping did not map with TCP's sequence numbers */ + MPTCP_MIB_DSSTRIMHEAD, /* Trimmed segment at the head (coalescing middlebox) */ + MPTCP_MIB_DSSSPLITTAIL, /* Trimmed segment at the tail (coalescing middlebox) */ + MPTCP_MIB_PURGEOLD, /* Removed old skb from the rcv-queue due to missing DSS-mapping */ + MPTCP_MIB_ADDADDRRX, /* Received an ADD_ADDR */ + MPTCP_MIB_ADDADDRTX, /* Sent an ADD_ADDR */ + MPTCP_MIB_REMADDRRX, /* Received a REMOVE_ADDR */ + MPTCP_MIB_REMADDRTX, /* Sent a REMOVE_ADDR */ + __MPTCP_MIB_MAX +}; + +#define MPTCP_MIB_MAX __MPTCP_MIB_MAX +struct mptcp_mib { + unsigned long mibs[MPTCP_MIB_MAX]; +}; + +extern struct lock_class_key meta_key; +extern struct lock_class_key meta_slock_key; +extern u32 mptcp_secret[MD5_MESSAGE_BYTES / 4]; + +/* This is needed to ensure that two subsequent key/nonce-generation result in + * different keys/nonces if the IPs and ports are the same. + */ +extern u32 mptcp_seed; + +#define MPTCP_HASH_SIZE 1024 + +extern struct hlist_nulls_head tk_hashtable[MPTCP_HASH_SIZE]; + +/* This second hashtable is needed to retrieve request socks + * created as a result of a join request. While the SYN contains + * the token, the final ack does not, so we need a separate hashtable + * to retrieve the mpcb. + */ +extern struct hlist_nulls_head mptcp_reqsk_htb[MPTCP_HASH_SIZE]; +extern spinlock_t mptcp_reqsk_hlock; /* hashtable protection */ + +/* Lock, protecting the two hash-tables that hold the token. Namely, + * mptcp_reqsk_tk_htb and tk_hashtable + */ +extern spinlock_t mptcp_tk_hashlock; /* hashtable protection */ + +/* Request-sockets can be hashed in the tk_htb for collision-detection or in + * the regular htb for join-connections. We need to define different NULLS + * values so that we can correctly detect a request-socket that has been + * recycled. See also c25eb3bfb9729. + */ +#define MPTCP_REQSK_NULLS_BASE (1U << 29) + + +void mptcp_data_ready(struct sock *sk); +void mptcp_write_space(struct sock *sk); + +void mptcp_add_meta_ofo_queue(const struct sock *meta_sk, struct sk_buff *skb, + struct sock *sk); +void mptcp_ofo_queue(struct sock *meta_sk); +void mptcp_purge_ofo_queue(struct tcp_sock *meta_tp); +void mptcp_cleanup_rbuf(struct sock *meta_sk, int copied); +int mptcp_add_sock(struct sock *meta_sk, struct sock *sk, u8 loc_id, u8 rem_id, + gfp_t flags); +void mptcp_del_sock(struct sock *sk); +void mptcp_update_metasocket(struct sock *sock, const struct sock *meta_sk); +void mptcp_reinject_data(struct sock *orig_sk, int clone_it); +void mptcp_update_sndbuf(const struct tcp_sock *tp); +void mptcp_send_fin(struct sock *meta_sk); +void mptcp_send_active_reset(struct sock *meta_sk, gfp_t priority); +bool mptcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, + int push_one, gfp_t gfp); +void tcp_parse_mptcp_options(const struct sk_buff *skb, + struct mptcp_options_received *mopt); +void mptcp_parse_options(const uint8_t *ptr, int opsize, + struct mptcp_options_received *mopt, + const struct sk_buff *skb, + struct tcp_sock *tp); +void mptcp_syn_options(const struct sock *sk, struct tcp_out_options *opts, + unsigned *remaining); +void mptcp_synack_options(struct request_sock *req, + struct tcp_out_options *opts, + unsigned *remaining); +void mptcp_established_options(struct sock *sk, struct sk_buff *skb, + struct tcp_out_options *opts, unsigned *size); +void mptcp_options_write(__be32 *ptr, struct tcp_sock *tp, + const struct tcp_out_options *opts, + struct sk_buff *skb); +void mptcp_close(struct sock *meta_sk, long timeout); +int mptcp_doit(struct sock *sk); +int mptcp_create_master_sk(struct sock *meta_sk, __u64 remote_key, + __u8 mptcp_ver, u32 window); +int mptcp_check_req_fastopen(struct sock *child, struct request_sock *req); +int mptcp_check_req_master(struct sock *sk, struct sock *child, + struct request_sock *req, int drop); +struct sock *mptcp_check_req_child(struct sock *sk, struct sock *child, + struct request_sock *req, + const struct mptcp_options_received *mopt); +u32 __mptcp_select_window(struct sock *sk); +void mptcp_select_initial_window(int __space, __u32 mss, __u32 *rcv_wnd, + __u32 *window_clamp, int wscale_ok, + __u8 *rcv_wscale, __u32 init_rcv_wnd, + const struct sock *sk); +unsigned int mptcp_current_mss(struct sock *meta_sk); +int mptcp_select_size(const struct sock *meta_sk, bool sg); +void mptcp_key_sha1(u64 key, u32 *token, u64 *idsn); +void mptcp_hmac_sha1(u8 *key_1, u8 *key_2, u32 *hash_out, int arg_num, ...); +void mptcp_clean_rtx_infinite(const struct sk_buff *skb, struct sock *sk); +void mptcp_fin(struct sock *meta_sk); +void mptcp_meta_retransmit_timer(struct sock *meta_sk); +void mptcp_sub_retransmit_timer(struct sock *sk); +int mptcp_write_wakeup(struct sock *meta_sk); +void mptcp_sub_close_wq(struct work_struct *work); +void mptcp_sub_close(struct sock *sk, unsigned long delay); +struct sock *mptcp_select_ack_sock(const struct sock *meta_sk); +void mptcp_fallback_meta_sk(struct sock *meta_sk); +int mptcp_backlog_rcv(struct sock *meta_sk, struct sk_buff *skb); +void mptcp_ack_handler(unsigned long); +int mptcp_check_rtt(const struct tcp_sock *tp, int time); +int mptcp_check_snd_buf(const struct tcp_sock *tp); +int mptcp_handle_options(struct sock *sk, const struct tcphdr *th, + const struct sk_buff *skb); +void __init mptcp_init(void); +void mptcp_destroy_sock(struct sock *sk); +int mptcp_rcv_synsent_state_process(struct sock *sk, struct sock **skptr, + const struct sk_buff *skb, + const struct mptcp_options_received *mopt); +unsigned int mptcp_xmit_size_goal(const struct sock *meta_sk, u32 mss_now, + int large_allowed); +int mptcp_init_tw_sock(struct sock *sk, struct tcp_timewait_sock *tw); +void mptcp_twsk_destructor(struct tcp_timewait_sock *tw); +void mptcp_time_wait(struct sock *sk, int state, int timeo); +void mptcp_disconnect(struct sock *sk); +bool mptcp_should_expand_sndbuf(const struct sock *sk); +int mptcp_retransmit_skb(struct sock *meta_sk, struct sk_buff *skb); +void mptcp_tsq_flags(struct sock *sk); +void mptcp_tsq_sub_deferred(struct sock *meta_sk); +struct mp_join *mptcp_find_join(const struct sk_buff *skb); +void mptcp_hash_remove_bh(struct tcp_sock *meta_tp); +void mptcp_hash_remove(struct tcp_sock *meta_tp); +struct sock *mptcp_hash_find(const struct net *net, const u32 token); +int mptcp_lookup_join(struct sk_buff *skb, struct inet_timewait_sock *tw); +int mptcp_do_join_short(struct sk_buff *skb, + const struct mptcp_options_received *mopt, + struct net *net); +void mptcp_reqsk_destructor(struct request_sock *req); +int mptcp_check_req(struct sk_buff *skb, struct net *net); +void mptcp_connect_init(struct sock *sk); +void mptcp_sub_force_close(struct sock *sk); +int mptcp_sub_len_remove_addr_align(u16 bitfield); +void mptcp_remove_shortcuts(const struct mptcp_cb *mpcb, + const struct sk_buff *skb); +void mptcp_init_buffer_space(struct sock *sk); +void mptcp_join_reqsk_init(struct mptcp_cb *mpcb, const struct request_sock *req, + struct sk_buff *skb); +void mptcp_reqsk_init(struct request_sock *req, struct sock *sk, + const struct sk_buff *skb, bool want_cookie); +int mptcp_conn_request(struct sock *sk, struct sk_buff *skb); +void mptcp_enable_sock(struct sock *sk); +void mptcp_disable_sock(struct sock *sk); +void mptcp_enable_static_key(void); +void mptcp_disable_static_key(void); +void mptcp_cookies_reqsk_init(struct request_sock *req, + struct mptcp_options_received *mopt, + struct sk_buff *skb); +void mptcp_sock_destruct(struct sock *sk); + +/* MPTCP-path-manager registration/initialization functions */ +int mptcp_register_path_manager(struct mptcp_pm_ops *pm); +void mptcp_unregister_path_manager(struct mptcp_pm_ops *pm); +void mptcp_init_path_manager(struct mptcp_cb *mpcb); +void mptcp_cleanup_path_manager(struct mptcp_cb *mpcb); +void mptcp_fallback_default(struct mptcp_cb *mpcb); +void mptcp_get_default_path_manager(char *name); +int mptcp_set_default_path_manager(const char *name); +extern struct mptcp_pm_ops mptcp_pm_default; + +/* MPTCP-scheduler registration/initialization functions */ +int mptcp_register_scheduler(struct mptcp_sched_ops *sched); +void mptcp_unregister_scheduler(struct mptcp_sched_ops *sched); +void mptcp_init_scheduler(struct mptcp_cb *mpcb); +void mptcp_cleanup_scheduler(struct mptcp_cb *mpcb); +void mptcp_get_default_scheduler(char *name); +int mptcp_set_default_scheduler(const char *name); +bool mptcp_is_available(struct sock *sk, const struct sk_buff *skb, + bool zero_wnd_test); +bool mptcp_is_def_unavailable(struct sock *sk); +bool subflow_is_active(const struct tcp_sock *tp); +bool subflow_is_backup(const struct tcp_sock *tp); +struct sock *get_available_subflow(struct sock *meta_sk, struct sk_buff *skb, + bool zero_wnd_test); +extern struct mptcp_sched_ops mptcp_sched_default; + +/* Initializes function-pointers and MPTCP-flags */ +static inline void mptcp_init_tcp_sock(struct sock *sk) +{ + if (!mptcp_init_failed && sysctl_mptcp_enabled == MPTCP_SYSCTL) + mptcp_enable_sock(sk); +} + +static inline int mptcp_pi_to_flag(int pi) +{ + return 1 << (pi - 1); +} + +static inline +struct mptcp_request_sock *mptcp_rsk(const struct request_sock *req) +{ + return (struct mptcp_request_sock *)req; +} + +static inline +struct request_sock *rev_mptcp_rsk(const struct mptcp_request_sock *req) +{ + return (struct request_sock *)req; +} + +static inline bool mptcp_can_sendpage(struct sock *sk) +{ + struct sock *sk_it; + + if (tcp_sk(sk)->mpcb->dss_csum) + return false; + + mptcp_for_each_sk(tcp_sk(sk)->mpcb, sk_it) { + if (!(sk_it->sk_route_caps & NETIF_F_SG) || + !(sk_it->sk_route_caps & NETIF_F_ALL_CSUM)) + return false; + } + + return true; +} + +static inline void mptcp_push_pending_frames(struct sock *meta_sk) +{ + /* We check packets out and send-head here. TCP only checks the + * send-head. But, MPTCP also checks packets_out, as this is an + * indication that we might want to do opportunistic reinjection. + */ + if (tcp_sk(meta_sk)->packets_out || tcp_send_head(meta_sk)) { + struct tcp_sock *tp = tcp_sk(meta_sk); + + /* We don't care about the MSS, because it will be set in + * mptcp_write_xmit. + */ + __tcp_push_pending_frames(meta_sk, 0, tp->nonagle); + } +} + +static inline void mptcp_send_reset(struct sock *sk) +{ + if (tcp_need_reset(sk->sk_state)) + tcp_sk(sk)->ops->send_active_reset(sk, GFP_ATOMIC); + mptcp_sub_force_close(sk); +} + +static inline void mptcp_sub_force_close_all(struct mptcp_cb *mpcb, + struct sock *except) +{ + struct sock *sk_it, *tmp; + + mptcp_for_each_sk_safe(mpcb, sk_it, tmp) { + if (sk_it != except) + mptcp_send_reset(sk_it); + } +} + +static inline bool mptcp_is_data_seq(const struct sk_buff *skb) +{ + return TCP_SKB_CB(skb)->mptcp_flags & MPTCPHDR_SEQ; +} + +static inline bool mptcp_is_data_fin(const struct sk_buff *skb) +{ + return TCP_SKB_CB(skb)->mptcp_flags & MPTCPHDR_FIN; +} + +/* Is it a data-fin while in infinite mapping mode? + * In infinite mode, a subflow-fin is in fact a data-fin. + */ +static inline bool mptcp_is_data_fin2(const struct sk_buff *skb, + const struct tcp_sock *tp) +{ + return mptcp_is_data_fin(skb) || + (tp->mpcb->infinite_mapping_rcv && + (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN)); +} + +static inline u8 mptcp_get_64_bit(u64 data_seq, struct mptcp_cb *mpcb) +{ + u64 data_seq_high = (u32)(data_seq >> 32); + + if (mpcb->rcv_high_order[0] == data_seq_high) + return 0; + else if (mpcb->rcv_high_order[1] == data_seq_high) + return MPTCPHDR_SEQ64_INDEX; + else + return MPTCPHDR_SEQ64_OFO; +} + +/* Sets the data_seq and returns pointer to the in-skb field of the data_seq. + * If the packet has a 64-bit dseq, the pointer points to the last 32 bits. + */ +static inline __u32 *mptcp_skb_set_data_seq(const struct sk_buff *skb, + u32 *data_seq, + struct mptcp_cb *mpcb) +{ + __u32 *ptr = (__u32 *)(skb_transport_header(skb) + TCP_SKB_CB(skb)->dss_off); + + if (TCP_SKB_CB(skb)->mptcp_flags & MPTCPHDR_SEQ64_SET) { + u64 data_seq64 = get_unaligned_be64(ptr); + + if (mpcb) + TCP_SKB_CB(skb)->mptcp_flags |= mptcp_get_64_bit(data_seq64, mpcb); + + *data_seq = (u32)data_seq64; + ptr++; + } else { + *data_seq = get_unaligned_be32(ptr); + } + + return ptr; +} + +static inline struct sock *mptcp_meta_sk(const struct sock *sk) +{ + return tcp_sk(sk)->meta_sk; +} + +static inline struct tcp_sock *mptcp_meta_tp(const struct tcp_sock *tp) +{ + return tcp_sk(tp->meta_sk); +} + +static inline int is_meta_tp(const struct tcp_sock *tp) +{ + return tp->mpcb && mptcp_meta_tp(tp) == tp; +} + +static inline int is_meta_sk(const struct sock *sk) +{ + return sk->sk_type == SOCK_STREAM && sk->sk_protocol == IPPROTO_TCP && + mptcp(tcp_sk(sk)) && mptcp_meta_sk(sk) == sk; +} + +static inline int is_master_tp(const struct tcp_sock *tp) +{ + return !mptcp(tp) || (!tp->mptcp->slave_sk && !is_meta_tp(tp)); +} + +static inline void mptcp_hash_request_remove(struct request_sock *req) +{ + int in_softirq = 0; + + if (hlist_nulls_unhashed(&mptcp_rsk(req)->hash_entry)) + return; + + if (in_softirq()) { + spin_lock(&mptcp_reqsk_hlock); + in_softirq = 1; + } else { + spin_lock_bh(&mptcp_reqsk_hlock); + } + + hlist_nulls_del_init_rcu(&mptcp_rsk(req)->hash_entry); + + if (in_softirq) + spin_unlock(&mptcp_reqsk_hlock); + else + spin_unlock_bh(&mptcp_reqsk_hlock); +} + +static inline void mptcp_init_mp_opt(struct mptcp_options_received *mopt) +{ + mopt->saw_mpc = 0; + mopt->dss_csum = 0; + mopt->drop_me = 0; + + mopt->is_mp_join = 0; + mopt->join_ack = 0; + + mopt->saw_low_prio = 0; + mopt->low_prio = 0; + + mopt->saw_add_addr = 0; + mopt->more_add_addr = 0; + + mopt->saw_rem_addr = 0; + mopt->more_rem_addr = 0; + + mopt->mp_fail = 0; + mopt->mp_fclose = 0; +} + +static inline void mptcp_reset_mopt(struct tcp_sock *tp) +{ + struct mptcp_options_received *mopt = &tp->mptcp->rx_opt; + + mopt->saw_low_prio = 0; + mopt->saw_add_addr = 0; + mopt->more_add_addr = 0; + mopt->saw_rem_addr = 0; + mopt->more_rem_addr = 0; + mopt->join_ack = 0; + mopt->mp_fail = 0; + mopt->mp_fclose = 0; +} + +static inline __be32 mptcp_get_highorder_sndbits(const struct sk_buff *skb, + const struct mptcp_cb *mpcb) +{ + return htonl(mpcb->snd_high_order[(TCP_SKB_CB(skb)->mptcp_flags & + MPTCPHDR_SEQ64_INDEX) ? 1 : 0]); +} + +static inline u64 mptcp_get_data_seq_64(const struct mptcp_cb *mpcb, int index, + u32 data_seq_32) +{ + return ((u64)mpcb->rcv_high_order[index] << 32) | data_seq_32; +} + +static inline u64 mptcp_get_rcv_nxt_64(const struct tcp_sock *meta_tp) +{ + struct mptcp_cb *mpcb = meta_tp->mpcb; + return mptcp_get_data_seq_64(mpcb, mpcb->rcv_hiseq_index, + meta_tp->rcv_nxt); +} + +static inline void mptcp_check_sndseq_wrap(struct tcp_sock *meta_tp, int inc) +{ + if (unlikely(meta_tp->snd_nxt > meta_tp->snd_nxt + inc)) { + struct mptcp_cb *mpcb = meta_tp->mpcb; + mpcb->snd_hiseq_index = mpcb->snd_hiseq_index ? 0 : 1; + mpcb->snd_high_order[mpcb->snd_hiseq_index] += 2; + } +} + +static inline void mptcp_check_rcvseq_wrap(struct tcp_sock *meta_tp, + u32 old_rcv_nxt) +{ + if (unlikely(old_rcv_nxt > meta_tp->rcv_nxt)) { + struct mptcp_cb *mpcb = meta_tp->mpcb; + mpcb->rcv_high_order[mpcb->rcv_hiseq_index] += 2; + mpcb->rcv_hiseq_index = mpcb->rcv_hiseq_index ? 0 : 1; + } +} + +static inline int mptcp_sk_can_send(const struct sock *sk) +{ + return tcp_passive_fastopen(sk) || + ((1 << sk->sk_state) & (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT) && + !tcp_sk(sk)->mptcp->pre_established); +} + +static inline int mptcp_sk_can_recv(const struct sock *sk) +{ + return (1 << sk->sk_state) & (TCPF_ESTABLISHED | TCPF_FIN_WAIT1 | TCPF_FIN_WAIT2); +} + +static inline int mptcp_sk_can_send_ack(const struct sock *sk) +{ + return !((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV | + TCPF_CLOSE | TCPF_LISTEN)) && + !tcp_sk(sk)->mptcp->pre_established; +} + +/* Only support GSO if all subflows supports it */ +static inline bool mptcp_sk_can_gso(const struct sock *meta_sk) +{ + struct sock *sk; + + if (tcp_sk(meta_sk)->mpcb->dss_csum) + return false; + + mptcp_for_each_sk(tcp_sk(meta_sk)->mpcb, sk) { + if (!mptcp_sk_can_send(sk)) + continue; + if (!sk_can_gso(sk)) + return false; + } + return true; +} + +static inline bool mptcp_can_sg(const struct sock *meta_sk) +{ + struct sock *sk; + + if (tcp_sk(meta_sk)->mpcb->dss_csum) + return false; + + mptcp_for_each_sk(tcp_sk(meta_sk)->mpcb, sk) { + if (!mptcp_sk_can_send(sk)) + continue; + if (!(sk->sk_route_caps & NETIF_F_SG)) + return false; + } + return true; +} + +static inline void mptcp_set_rto(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct sock *sk_it; + struct inet_connection_sock *micsk = inet_csk(mptcp_meta_sk(sk)); + __u32 max_rto = 0; + + /* We are in recovery-phase on the MPTCP-level. Do not update the + * RTO, because this would kill exponential backoff. + */ + if (micsk->icsk_retransmits) + return; + + mptcp_for_each_sk(tp->mpcb, sk_it) { + if (mptcp_sk_can_send(sk_it) && + inet_csk(sk_it)->icsk_rto > max_rto) + max_rto = inet_csk(sk_it)->icsk_rto; + } + if (max_rto) { + micsk->icsk_rto = max_rto << 1; + + /* A successfull rto-measurement - reset backoff counter */ + micsk->icsk_backoff = 0; + } +} + +static inline void mptcp_sub_close_passive(struct sock *sk) +{ + struct sock *meta_sk = mptcp_meta_sk(sk); + struct tcp_sock *tp = tcp_sk(sk), *meta_tp = tcp_sk(meta_sk); + + /* Only close, if the app did a send-shutdown (passive close), and we + * received the data-ack of the data-fin. + */ + if (tp->mpcb->passive_close && meta_tp->snd_una == meta_tp->write_seq) + mptcp_sub_close(sk, 0); +} + +static inline bool mptcp_fallback_infinite(struct sock *sk, int flag) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct mptcp_cb *mpcb = tp->mpcb; + + /* If data has been acknowleged on the meta-level, fully_established + * will have been set before and thus we will not fall back to infinite + * mapping. + */ + if (likely(tp->mptcp->fully_established)) + return false; + + if (!(flag & MPTCP_FLAG_DATA_ACKED)) + return false; + + /* Don't fallback twice ;) */ + if (mpcb->infinite_mapping_snd) + return false; + + pr_err("%s %#x will fallback - pi %d, src %pI4 dst %pI4 from %pS\n", + __func__, mpcb->mptcp_loc_token, tp->mptcp->path_index, + &inet_sk(sk)->inet_saddr, &inet_sk(sk)->inet_daddr, + __builtin_return_address(0)); + if (!is_master_tp(tp)) { + MPTCP_INC_STATS_BH(sock_net(sk), MPTCP_MIB_FBACKSUB); + return true; + } + + mpcb->infinite_mapping_snd = 1; + mpcb->infinite_mapping_rcv = 1; + mpcb->infinite_rcv_seq = mptcp_get_rcv_nxt_64(mptcp_meta_tp(tp)); + tp->mptcp->fully_established = 1; + + mptcp_sub_force_close_all(mpcb, sk); + + MPTCP_INC_STATS_BH(sock_net(sk), MPTCP_MIB_FBACKINIT); + + return false; +} + +/* Find the first index whose bit in the bit-field == 0 */ +static inline u8 mptcp_set_new_pathindex(struct mptcp_cb *mpcb) +{ + u8 base = mpcb->next_path_index; + int i; + + /* Start at 1, because 0 is reserved for the meta-sk */ + mptcp_for_each_bit_unset(mpcb->path_index_bits >> base, i) { + if (i + base < 1) + continue; + if (i + base >= sizeof(mpcb->path_index_bits) * 8) + break; + i += base; + mpcb->path_index_bits |= (1 << i); + mpcb->next_path_index = i + 1; + return i; + } + mptcp_for_each_bit_unset(mpcb->path_index_bits, i) { + if (i >= sizeof(mpcb->path_index_bits) * 8) + break; + if (i < 1) + continue; + mpcb->path_index_bits |= (1 << i); + mpcb->next_path_index = i + 1; + return i; + } + + return 0; +} + +static inline bool mptcp_v6_is_v4_mapped(const struct sock *sk) +{ + return sk->sk_family == AF_INET6 && + ipv6_addr_type(&inet6_sk(sk)->saddr) == IPV6_ADDR_MAPPED; +} + +/* TCP and MPTCP mpc flag-depending functions */ +u16 mptcp_select_window(struct sock *sk); +void mptcp_init_buffer_space(struct sock *sk); +void mptcp_tcp_set_rto(struct sock *sk); + +/* TCP and MPTCP flag-depending functions */ +bool mptcp_prune_ofo_queue(struct sock *sk); + +#else /* CONFIG_MPTCP */ +#define mptcp_debug(fmt, args...) \ + do { \ + } while (0) + +/* Without MPTCP, we just do one iteration + * over the only socket available. This assumes that + * the sk/tp arg is the socket in that case. + */ +#define mptcp_for_each_sk(mpcb, sk) +#define mptcp_for_each_sk_safe(__mpcb, __sk, __temp) + +#define MPTCP_INC_STATS(net, field) \ + do { \ + } while(0) + +static inline bool mptcp_is_data_fin(const struct sk_buff *skb) +{ + return false; +} +static inline bool mptcp_is_data_seq(const struct sk_buff *skb) +{ + return false; +} +static inline struct sock *mptcp_meta_sk(const struct sock *sk) +{ + return NULL; +} +static inline struct tcp_sock *mptcp_meta_tp(const struct tcp_sock *tp) +{ + return NULL; +} +static inline int is_meta_sk(const struct sock *sk) +{ + return 0; +} +static inline int is_master_tp(const struct tcp_sock *tp) +{ + return 0; +} +static inline void mptcp_purge_ofo_queue(struct tcp_sock *meta_tp) {} +static inline void mptcp_del_sock(const struct sock *sk) {} +static inline void mptcp_update_metasocket(struct sock *sock, const struct sock *meta_sk) {} +static inline void mptcp_reinject_data(struct sock *orig_sk, int clone_it) {} +static inline void mptcp_update_sndbuf(const struct tcp_sock *tp) {} +static inline void mptcp_clean_rtx_infinite(const struct sk_buff *skb, + const struct sock *sk) {} +static inline void mptcp_sub_close(struct sock *sk, unsigned long delay) {} +static inline void mptcp_set_rto(const struct sock *sk) {} +static inline void mptcp_send_fin(const struct sock *meta_sk) {} +static inline void mptcp_parse_options(const uint8_t *ptr, const int opsize, + struct mptcp_options_received *mopt, + const struct sk_buff *skb, + const struct tcp_sock *tp) {} +static inline void mptcp_syn_options(const struct sock *sk, + struct tcp_out_options *opts, + unsigned *remaining) {} +static inline void mptcp_synack_options(struct request_sock *req, + struct tcp_out_options *opts, + unsigned *remaining) {} + +static inline void mptcp_established_options(struct sock *sk, + struct sk_buff *skb, + struct tcp_out_options *opts, + unsigned *size) {} +static inline void mptcp_options_write(__be32 *ptr, struct tcp_sock *tp, + const struct tcp_out_options *opts, + struct sk_buff *skb) {} +static inline void mptcp_close(struct sock *meta_sk, long timeout) {} +static inline int mptcp_doit(struct sock *sk) +{ + return 0; +} +static inline int mptcp_check_req_fastopen(struct sock *child, + struct request_sock *req) +{ + return 1; +} +static inline int mptcp_check_req_master(const struct sock *sk, + const struct sock *child, + struct request_sock *req, + int drop) +{ + return 1; +} +static inline struct sock *mptcp_check_req_child(struct sock *sk, + struct sock *child, + struct request_sock *req, + const struct mptcp_options_received *mopt) +{ + return NULL; +} +static inline unsigned int mptcp_current_mss(struct sock *meta_sk) +{ + return 0; +} +static inline void mptcp_sub_close_passive(struct sock *sk) {} +static inline bool mptcp_fallback_infinite(const struct sock *sk, int flag) +{ + return false; +} +static inline void mptcp_init_mp_opt(const struct mptcp_options_received *mopt) {} +static inline int mptcp_check_rtt(const struct tcp_sock *tp, int time) +{ + return 0; +} +static inline int mptcp_check_snd_buf(const struct tcp_sock *tp) +{ + return 0; +} +static inline void mptcp_send_reset(const struct sock *sk) {} +static inline int mptcp_handle_options(struct sock *sk, + const struct tcphdr *th, + struct sk_buff *skb) +{ + return 0; +} +static inline void mptcp_reset_mopt(struct tcp_sock *tp) {} +static inline void __init mptcp_init(void) {} +static inline bool mptcp_sk_can_gso(const struct sock *sk) +{ + return false; +} +static inline bool mptcp_can_sg(const struct sock *meta_sk) +{ + return false; +} +static inline unsigned int mptcp_xmit_size_goal(const struct sock *meta_sk, + u32 mss_now, int large_allowed) +{ + return 0; +} +static inline void mptcp_destroy_sock(struct sock *sk) {} +static inline int mptcp_rcv_synsent_state_process(struct sock *sk, + struct sock **skptr, + struct sk_buff *skb, + const struct mptcp_options_received *mopt) +{ + return 0; +} +static inline bool mptcp_can_sendpage(struct sock *sk) +{ + return false; +} +static inline int mptcp_init_tw_sock(struct sock *sk, + struct tcp_timewait_sock *tw) +{ + return 0; +} +static inline void mptcp_twsk_destructor(struct tcp_timewait_sock *tw) {} +static inline void mptcp_disconnect(struct sock *sk) {} +static inline void mptcp_tsq_flags(struct sock *sk) {} +static inline void mptcp_tsq_sub_deferred(struct sock *meta_sk) {} +static inline void mptcp_hash_remove_bh(struct tcp_sock *meta_tp) {} +static inline void mptcp_hash_remove(struct tcp_sock *meta_tp) {} +static inline void mptcp_remove_shortcuts(const struct mptcp_cb *mpcb, + const struct sk_buff *skb) {} +static inline void mptcp_init_tcp_sock(struct sock *sk) {} +static inline void mptcp_disable_static_key(void) {} +static inline void mptcp_cookies_reqsk_init(struct request_sock *req, + struct mptcp_options_received *mopt, + struct sk_buff *skb) {} +#endif /* CONFIG_MPTCP */ + +#endif /* _MPTCP_H */ +#endif diff -urN linux-4.1.52-orig/include/net/mptcp_v4.h linux-4.1.52/include/net/mptcp_v4.h --- linux-4.1.52-orig/include/net/mptcp_v4.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/net/mptcp_v4.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,70 @@ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +/* + * MPTCP implementation + * + * Initial Design & Implementation: + * Sébastien Barré + * + * Current Maintainer & Author: + * Christoph Paasch + * + * Additional authors: + * Jaakko Korkeaniemi + * Gregory Detal + * Fabien Duchêne + * Andreas Seelinger + * Lavkesh Lahngir + * Andreas Ripke + * Vlad Dogaru + * Octavian Purdila + * John Ronan + * Catalin Nicutar + * Brandon Heller + * + * + * 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. + */ + +#ifndef MPTCP_V4_H_ +#define MPTCP_V4_H_ + + +#include +#include +#include +#include +#include + +extern struct request_sock_ops mptcp_request_sock_ops; +extern const struct inet_connection_sock_af_ops mptcp_v4_specific; +extern struct tcp_request_sock_ops mptcp_request_sock_ipv4_ops; +extern struct tcp_request_sock_ops mptcp_join_request_sock_ipv4_ops; + +#ifdef CONFIG_MPTCP + +int mptcp_v4_do_rcv(struct sock *meta_sk, struct sk_buff *skb); +struct sock *mptcp_v4_search_req(const __be16 rport, const __be32 raddr, + const __be32 laddr, const struct net *net); +int mptcp_init4_subsockets(struct sock *meta_sk, const struct mptcp_loc4 *loc, + struct mptcp_rem4 *rem); +int mptcp_pm_v4_init(void); +void mptcp_pm_v4_undo(void); +u32 mptcp_v4_get_nonce(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport); +u64 mptcp_v4_get_key(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport, + u32 seed); + +#else + +static inline int mptcp_v4_do_rcv(const struct sock *meta_sk, + const struct sk_buff *skb) +{ + return 0; +} + +#endif /* CONFIG_MPTCP */ + +#endif /* MPTCP_V4_H_ */ +#endif diff -urN linux-4.1.52-orig/include/net/mptcp_v6.h linux-4.1.52/include/net/mptcp_v6.h --- linux-4.1.52-orig/include/net/mptcp_v6.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/net/mptcp_v6.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,71 @@ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +/* + * MPTCP implementation + * + * Initial Design & Implementation: + * Sébastien Barré + * + * Current Maintainer & Author: + * Jaakko Korkeaniemi + * + * Additional authors: + * Jaakko Korkeaniemi + * Gregory Detal + * Fabien Duchêne + * Andreas Seelinger + * Lavkesh Lahngir + * Andreas Ripke + * Vlad Dogaru + * Octavian Purdila + * John Ronan + * Catalin Nicutar + * Brandon Heller + * + * + * 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. + */ + +#ifndef _MPTCP_V6_H +#define _MPTCP_V6_H + +#include +#include + +#include + + +#ifdef CONFIG_MPTCP +extern const struct inet_connection_sock_af_ops mptcp_v6_mapped; +extern const struct inet_connection_sock_af_ops mptcp_v6_specific; +extern struct request_sock_ops mptcp6_request_sock_ops; +extern struct tcp_request_sock_ops mptcp_request_sock_ipv6_ops; +extern struct tcp_request_sock_ops mptcp_join_request_sock_ipv6_ops; + +int mptcp_v6_do_rcv(struct sock *meta_sk, struct sk_buff *skb); +struct sock *mptcp_v6_search_req(const __be16 rport, const struct in6_addr *raddr, + const struct in6_addr *laddr, const struct net *net); +int mptcp_init6_subsockets(struct sock *meta_sk, const struct mptcp_loc6 *loc, + struct mptcp_rem6 *rem); +int mptcp_pm_v6_init(void); +void mptcp_pm_v6_undo(void); +__u32 mptcp_v6_get_nonce(const __be32 *saddr, const __be32 *daddr, + __be16 sport, __be16 dport); +u64 mptcp_v6_get_key(const __be32 *saddr, const __be32 *daddr, + __be16 sport, __be16 dport, u32 seed); + +#else /* CONFIG_MPTCP */ + +#define mptcp_v6_mapped ipv6_mapped + +static inline int mptcp_v6_do_rcv(struct sock *meta_sk, struct sk_buff *skb) +{ + return 0; +} + +#endif /* CONFIG_MPTCP */ + +#endif /* _MPTCP_V6_H */ +#endif diff -urN linux-4.1.52-orig/include/net/netevent.h linux-4.1.52/include/net/netevent.h --- linux-4.1.52-orig/include/net/netevent.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/net/netevent.h 2020-02-07 01:47:54.000000000 +0800 @@ -24,6 +24,9 @@ enum netevent_notif_type { NETEVENT_NEIGH_UPDATE = 1, /* arg is struct neighbour ptr */ NETEVENT_REDIRECT, /* arg is struct netevent_redirect ptr */ +#if defined(CONFIG_BCM_KF_BLOG) + NETEVENT_ARP_BINDING_CHANGE, /* arg is structure neighbour pointer */ +#endif }; int register_netevent_notifier(struct notifier_block *nb); diff -urN linux-4.1.52-orig/include/net/netfilter/nf_conntrack_acct.h linux-4.1.52/include/net/netfilter/nf_conntrack_acct.h --- linux-4.1.52-orig/include/net/netfilter/nf_conntrack_acct.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/net/netfilter/nf_conntrack_acct.h 2020-02-07 01:47:54.000000000 +0800 @@ -13,10 +13,19 @@ #include #include #include +#if defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_MODULE) +#include +#endif + struct nf_conn_counter { atomic64_t packets; atomic64_t bytes; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + unsigned long cum_fast_pkts; + unsigned long long cum_fast_bytes; + unsigned long ts; +#endif }; struct nf_conn_acct { @@ -48,6 +57,11 @@ unsigned int seq_print_acct(struct seq_file *s, const struct nf_conn *ct, int dir); +#if defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_MODULE) +void conntrack_dpi_evict_conn(struct nf_conn *ct, int dir); +int conntrack_dpi_seq_print_stats(struct seq_file *s, struct nf_conn *ct, + int dir); +#endif /* Check if connection tracking accounting is enabled */ static inline bool nf_ct_acct_enabled(struct net *net) diff -urN linux-4.1.52-orig/include/net/netfilter/nf_conntrack_expect.h linux-4.1.52/include/net/netfilter/nf_conntrack_expect.h --- linux-4.1.52-orig/include/net/netfilter/nf_conntrack_expect.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/net/netfilter/nf_conntrack_expect.h 2020-02-07 01:47:54.000000000 +0800 @@ -42,6 +42,12 @@ /* Expectation class */ unsigned int class; +#if defined(CONFIG_BCM_KF_NETFILTER) + unsigned derived_timeout; /* 0 means no derived_timeout, 0xFFFFFFFF + * means never timeout until master ct is + * disconnected, others means timeout secs */ +#endif + #ifdef CONFIG_NF_NAT_NEEDED union nf_inet_addr saved_addr; /* This is the original per-proto part, used to map the @@ -69,6 +75,10 @@ #define NF_CT_EXPECT_CLASS_DEFAULT 0 +#if defined(CONFIG_BCM_KF_NETFILTER) +#define NF_CT_EXPECT_DERIVED_TIMEOUT 0x80 +#endif + int nf_conntrack_expect_pernet_init(struct net *net); void nf_conntrack_expect_pernet_fini(struct net *net); diff -urN linux-4.1.52-orig/include/net/netfilter/nf_conntrack.h linux-4.1.52/include/net/netfilter/nf_conntrack.h --- linux-4.1.52-orig/include/net/netfilter/nf_conntrack.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/net/netfilter/nf_conntrack.h 2020-02-07 01:47:54.000000000 +0800 @@ -22,6 +22,11 @@ #include #include #include +#if defined(CONFIG_BCM_KF_PROTO_IPSEC) && \ + (defined(CONFIG_NF_CONNTRACK_IPSEC) || defined(CONFIG_NF_CONNTRACK_IPSEC_MODULE)) +#include +#include +#endif #include #include @@ -33,12 +38,20 @@ struct ip_ct_sctp sctp; struct ip_ct_tcp tcp; struct nf_ct_gre gre; +#if defined(CONFIG_BCM_KF_PROTO_ESP) && \ + (defined(CONFIG_NF_CT_PROTO_ESP) || defined(CONFIG_NF_CT_PROTO_ESP_MODULE)) + struct nf_ct_esp esp; +#endif }; union nf_conntrack_expect_proto { /* insert expect proto private data here */ }; +#if defined(CONFIG_BCM_KF_NETFILTER) +#define NF_ALG_BUFFER_SIZE 2000 +#endif + #include #include #include @@ -71,6 +84,10 @@ #include #include +#if defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_MODULE) +#include +#endif + struct nf_conn { /* Usage count in here is 1 for hash table/destruct timer, 1 per skb, * plus 1 for any connection(s) we are `master' for @@ -85,13 +102,54 @@ spinlock_t lock; u16 cpu; +#if defined(CONFIG_BCM_KF_BLOG) +#if defined(CONFIG_BLOG) + unsigned int blog_key[2]; /* Associating 2=IP_CT_DIR_MAX blogged flows */ + unsigned long extra_jiffies;/* connection timeout value */ +#endif + uint8_t iq_prio; /* Ingress QoS Prio */ + uint8_t unused0; + uint16_t unused1; +#endif + +#if defined(CONFIG_BCM_KF_NETFILTER) + uint32_t hw_accel_flows; /* Number of HW accelerated flows, need 32 bit for tunnel connection */ + uint32_t sw_accel_flows; /* Number of SW accelerated flows, need 32 bit for tunnel connection */ + struct list_head safe_list; /* regardless drop of connections */ + struct list_head *curr_safe_list; /* current safe_list for this connection*/ + struct list_head derived_connections; /* Used by master connection */ + struct list_head derived_list; /* Used by child connection */ + unsigned derived_timeout; /* 0 means no derived_timeout, 0xFFFFFFFF + * means never timeout until master ct is + * disconnected, others means timeout secs */ + + /* Have we seen traffic both ways yet? (bitset) */ // bcm version + unsigned long status; // moved position for bcm + +#if defined(CONFIG_NF_DYNDSCP) || defined(CONFIG_NF_DYNDSCP_MODULE) + struct nf_tos_inheritance { + u_int16_t status; + u_int8_t dscp[2]; /* IP_CT_DIR_MAX */ + }dyndscp; +#endif + /*---------- Add any custom fields below this line ----------*/ + + /* If we were expected by an expectation, this will be it */ + struct nf_conn *master; // moved position for bcm +#endif /* CONFIG_BCM_KF_NETFILTER */ + +#if defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_MODULE) + struct dpi_info dpi; +#endif /* XXX should I move this to the tail ? - Y.K */ /* These are my tuples; original and reply */ struct nf_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX]; +#if !defined(CONFIG_BCM_KF_NETFILTER) /* Have we seen traffic both ways yet? (bitset) */ unsigned long status; +#endif /* Timer function; drops refcnt when it goes off. */ struct timer_list timeout; @@ -100,8 +158,10 @@ /* all members below initialized via memset */ u8 __nfct_init_offset[0]; +#if !defined(CONFIG_BCM_KF_NETFILTER) /* If we were expected by an expectation, this will be it */ struct nf_conn *master; +#endif #if defined(CONFIG_NF_CONNTRACK_MARK) u_int32_t mark; @@ -116,6 +176,28 @@ /* Storage reserved for other modules, must be the last member */ union nf_conntrack_proto proto; + +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) + void *bl_ctx; +#endif /* CONFIG_BCM_RDPA || CONFIG_BCM_RDPA_MODULE */ +#endif /* CONFIG_BCM_KF_RUNNER */ + +#if defined(CONFIG_BCM_KF_XT_MATCH_LAYER7) && \ + (defined(CONFIG_NETFILTER_XT_MATCH_LAYER7) || defined(CONFIG_NETFILTER_XT_MATCH_LAYER7_MODULE)) + struct { + /* + * e.g. "http". NULL before decision. "unknown" after decision + * if no match. + */ + char *app_proto; + /* + * application layer data so far. NULL after match decision. + */ + char *app_data; + unsigned int app_data_len; + } layer7; +#endif }; static inline struct nf_conn * @@ -251,10 +333,19 @@ int (*iter)(struct nf_conn *i, void *data), void *data, u32 portid, int report); void nf_conntrack_free(struct nf_conn *ct); +#if defined(CONFIG_BCM_KF_NETFILTER) +struct nf_conn * +nf_conntrack_alloc(struct net *net, u16 zone, + struct sk_buff *skb, + const struct nf_conntrack_tuple *orig, + const struct nf_conntrack_tuple *repl, + gfp_t gfp); +#else struct nf_conn *nf_conntrack_alloc(struct net *net, u16 zone, const struct nf_conntrack_tuple *orig, const struct nf_conntrack_tuple *repl, gfp_t gfp); +#endif static inline int nf_ct_is_template(const struct nf_conn *ct) { diff -urN linux-4.1.52-orig/include/net/netfilter/nf_conntrack_tuple.h linux-4.1.52/include/net/netfilter/nf_conntrack_tuple.h --- linux-4.1.52-orig/include/net/netfilter/nf_conntrack_tuple.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/net/netfilter/nf_conntrack_tuple.h 2020-02-07 01:47:54.000000000 +0800 @@ -61,6 +61,12 @@ struct { __be16 key; } gre; +#if defined(CONFIG_BCM_KF_PROTO_ESP) && \ + (defined(CONFIG_NF_CT_PROTO_ESP) || defined(CONFIG_NF_CT_PROTO_ESP_MODULE)) + struct { + __be16 spi; + } esp; +#endif } u; /* The protocol. */ diff -urN linux-4.1.52-orig/include/net/net_namespace.h linux-4.1.52/include/net/net_namespace.h --- linux-4.1.52-orig/include/net/net_namespace.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/net/net_namespace.h 2020-02-07 01:47:54.000000000 +0800 @@ -16,6 +16,9 @@ #include #include #include +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#include +#endif #include #include #include @@ -92,6 +95,11 @@ #if IS_ENABLED(CONFIG_IPV6) struct netns_ipv6 ipv6; #endif +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#if IS_ENABLED(CONFIG_MPTCP) + struct netns_mptcp mptcp; +#endif +#endif #if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN) struct netns_ieee802154_lowpan ieee802154_lowpan; #endif diff -urN linux-4.1.52-orig/include/net/netns/ipv4.h linux-4.1.52/include/net/netns/ipv4.h --- linux-4.1.52-orig/include/net/netns/ipv4.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/net/netns/ipv4.h 2020-02-07 01:47:54.000000000 +0800 @@ -85,6 +85,10 @@ int sysctl_tcp_fwmark_accept; int sysctl_tcp_mtu_probing; int sysctl_tcp_base_mss; +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +/*CVE-2019-11479*/ + int sysctl_tcp_min_snd_mss; +#endif int sysctl_tcp_probe_threshold; u32 sysctl_tcp_probe_interval; diff -urN linux-4.1.52-orig/include/net/netns/mptcp.h linux-4.1.52/include/net/netns/mptcp.h --- linux-4.1.52-orig/include/net/netns/mptcp.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/net/netns/mptcp.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,54 @@ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +/* + * MPTCP implementation - MPTCP namespace + * + * Initial Design & Implementation: + * Sébastien Barré + * + * Current Maintainer: + * Christoph Paasch + * + * Additional authors: + * Jaakko Korkeaniemi + * Gregory Detal + * Fabien Duchêne + * Andreas Seelinger + * Lavkesh Lahngir + * Andreas Ripke + * Vlad Dogaru + * Octavian Purdila + * John Ronan + * Catalin Nicutar + * Brandon Heller + * + * + * 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. + */ + +#ifndef __NETNS_MPTCP_H__ +#define __NETNS_MPTCP_H__ + +#include + +enum { + MPTCP_PM_FULLMESH = 0, + MPTCP_PM_MAX +}; + +struct mptcp_mib; + +struct netns_mptcp { + DEFINE_SNMP_STAT(struct mptcp_mib, mptcp_statistics); + +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *proc_net_mptcp; +#endif + + void *path_managers[MPTCP_PM_MAX]; +}; + +#endif /* __NETNS_MPTCP_H__ */ +#endif diff -urN linux-4.1.52-orig/include/net/phonet/phonet.h linux-4.1.52/include/net/phonet/phonet.h --- linux-4.1.52-orig/include/net/phonet/phonet.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/net/phonet/phonet.h 2020-02-07 01:47:54.000000000 +0800 @@ -38,6 +38,10 @@ u16 sobject; u16 dobject; u8 resource; +#ifdef CONFIG_BCM_KF_PHONET + u8 resource_type; + u8 resource_subtype; +#endif }; static inline struct pn_sock *pn_sk(struct sock *sk) @@ -48,6 +52,11 @@ extern const struct proto_ops phonet_dgram_ops; void pn_sock_init(void); +#ifdef CONFIG_BCM_KF_PHONET +struct sock *pn_find_sock_by_sa_and_skb(struct net *net, + const struct sockaddr_pn *spn, + struct sk_buff *skb); +#endif struct sock *pn_find_sock_by_sa(struct net *net, const struct sockaddr_pn *sa); void pn_deliver_sock_broadcast(struct net *net, struct sk_buff *skb); void phonet_get_local_port_range(int *min, int *max); @@ -116,4 +125,36 @@ int isi_register(void); void isi_unregister(void); +#ifdef CONFIG_BCM_KF_PHONET +#ifdef CONFIG_PHONET_DEBUG +#define ACTIVATE_PHONET_DEBUG +#else +#undef ACTIVATE_PHONET_DEBUG +#endif + +#ifdef ACTIVATE_PHONET_DEBUG +typedef enum { + OFF = 0, + ON, + DATA, +} phonet_debug_state ; +extern phonet_debug_state phonet_dbg_state; + +# define PN_PRINTK(...) if (OFF != phonet_dbg_state) \ + pr_debug("PHONET: " __VA_ARGS__) +# define PN_DATA_PRINTK(...) if (DATA == phonet_dbg_state) \ + pr_debug(__VA_ARGS__) +# define PEP_PRINTK(...) if (OFF != phonet_dbg_state) \ + pr_debug("PEP: " __VA_ARGS__) +# define PEP_DATA_PRINTK(...) if (DATA == phonet_dbg_state) \ + pr_debug(__VA_ARGS__) +#else +# define PN_PRINTK(...) +# define PN_DATA_PRINTK(...) +# define PEP_PRINTK(...) +# define PEP_DATA_PRINTK(...) + +#endif +#endif /* CONFIG_BCM_KF_PHONET */ + #endif diff -urN linux-4.1.52-orig/include/net/request_sock.h linux-4.1.52/include/net/request_sock.h --- linux-4.1.52-orig/include/net/request_sock.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/net/request_sock.h 2020-02-07 01:47:54.000000000 +0800 @@ -191,7 +191,11 @@ }; int reqsk_queue_alloc(struct request_sock_queue *queue, +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) unsigned int nr_table_entries); +#else + unsigned int nr_table_entries, gfp_t flags); +#endif void __reqsk_queue_destroy(struct request_sock_queue *queue); void reqsk_queue_destroy(struct request_sock_queue *queue); diff -urN linux-4.1.52-orig/include/net/sock.h linux-4.1.52/include/net/sock.h --- linux-4.1.52-orig/include/net/sock.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/net/sock.h 2020-02-07 01:47:54.000000000 +0800 @@ -721,6 +721,9 @@ */ SOCK_FILTER_LOCKED, /* Filter cannot be changed anymore */ SOCK_SELECT_ERR_QUEUE, /* Wake select on error queue */ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + SOCK_MPTCP, /* MPTCP set on this socket */ +#endif }; #define SK_FLAGS_TIMESTAMP ((1UL << SOCK_TIMESTAMP) | (1UL << SOCK_TIMESTAMPING_RX_SOFTWARE)) @@ -915,6 +918,18 @@ int sk_wait_data(struct sock *sk, long *timeo); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +/* START - needed for MPTCP */ +struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority, int family); +void sock_lock_init(struct sock *sk); + +extern struct lock_class_key af_callback_keys[AF_MAX]; +extern char *const af_family_clock_key_strings[AF_MAX+1]; + +#define SK_FLAGS_TIMESTAMP ((1UL << SOCK_TIMESTAMP) | (1UL << SOCK_TIMESTAMPING_RX_SOFTWARE)) +/* END - needed for MPTCP */ + +#endif struct request_sock_ops; struct timewait_sock_ops; struct inet_hashinfo; diff -urN linux-4.1.52-orig/include/net/tcp.h linux-4.1.52/include/net/tcp.h --- linux-4.1.52-orig/include/net/tcp.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/net/tcp.h 2020-02-07 01:47:54.000000000 +0800 @@ -54,6 +54,11 @@ #define MAX_TCP_HEADER (128 + MAX_HEADER) #define MAX_TCP_OPTION_SPACE 40 +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +/*CVE-2019-11477*/ +#define TCP_MIN_SND_MSS 48 +#define TCP_MIN_GSO_SIZE (TCP_MIN_SND_MSS - MAX_TCP_OPTION_SPACE) +#endif //#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) /* * Never offer a window over 32767 without using window scaling. Some @@ -179,6 +184,9 @@ #define TCPOPT_SACK 5 /* SACK Block */ #define TCPOPT_TIMESTAMP 8 /* Better RTT estimations/PAWS */ #define TCPOPT_MD5SIG 19 /* MD5 Signature (RFC2385) */ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#define TCPOPT_MPTCP 30 +#endif #define TCPOPT_FASTOPEN 34 /* Fast open (RFC7413) */ #define TCPOPT_EXP 254 /* Experimental */ /* Magic number to be after the option value for sharing TCP @@ -233,6 +241,29 @@ #define TFO_SERVER_WO_SOCKOPT1 0x400 #define TFO_SERVER_WO_SOCKOPT2 0x800 +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +/* Flags from tcp_input.c for tcp_ack */ +#define FLAG_DATA 0x01 /* Incoming frame contained data. */ +#define FLAG_WIN_UPDATE 0x02 /* Incoming ACK was a window update. */ +#define FLAG_DATA_ACKED 0x04 /* This ACK acknowledged new data. */ +#define FLAG_RETRANS_DATA_ACKED 0x08 /* "" "" some of which was retransmitted. */ +#define FLAG_SYN_ACKED 0x10 /* This ACK acknowledged SYN. */ +#define FLAG_DATA_SACKED 0x20 /* New SACK. */ +#define FLAG_ECE 0x40 /* ECE in this ACK */ +#define FLAG_SLOWPATH 0x100 /* Do not skip RFC checks for window update.*/ +#define FLAG_ORIG_SACK_ACKED 0x200 /* Never retransmitted data are (s)acked */ +#define FLAG_SND_UNA_ADVANCED 0x400 /* Snd_una was changed (!= FLAG_DATA_ACKED) */ +#define FLAG_DSACKING_ACK 0x800 /* SACK blocks contained D-SACK info */ +#define FLAG_SACK_RENEGING 0x2000 /* snd_una advanced to a sacked seq */ +#define FLAG_UPDATE_TS_RECENT 0x4000 /* tcp_replace_ts_recent() */ +#define MPTCP_FLAG_DATA_ACKED 0x8000 + +#define FLAG_ACKED (FLAG_DATA_ACKED|FLAG_SYN_ACKED) +#define FLAG_NOT_DUP (FLAG_DATA|FLAG_WIN_UPDATE|FLAG_ACKED) +#define FLAG_CA_ALERT (FLAG_DATA_SACKED|FLAG_ECE) +#define FLAG_FORWARD_PROGRESS (FLAG_ACKED|FLAG_DATA_SACKED) + +#endif extern struct inet_timewait_death_row tcp_death_row; /* sysctl variables for tcp */ @@ -347,6 +378,104 @@ #define TCP_ADD_STATS_USER(net, field, val) SNMP_ADD_STATS_USER((net)->mib.tcp_statistics, field, val) #define TCP_ADD_STATS(net, field, val) SNMP_ADD_STATS((net)->mib.tcp_statistics, field, val) +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +/**** START - Exports needed for MPTCP ****/ +extern const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops; +extern const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops; + +struct mptcp_options_received; + +void tcp_cleanup_rbuf(struct sock *sk, int copied); +void tcp_cwnd_validate(struct sock *sk, bool is_cwnd_limited); +void tcp_enter_quickack_mode(struct sock *sk); +int tcp_close_state(struct sock *sk); +void tcp_minshall_update(struct tcp_sock *tp, unsigned int mss_now, + const struct sk_buff *skb); +int tcp_xmit_probe_skb(struct sock *sk, int urgent); +void tcp_event_new_data_sent(struct sock *sk, const struct sk_buff *skb); +int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, + gfp_t gfp_mask); +unsigned int tcp_mss_split_point(const struct sock *sk, + const struct sk_buff *skb, + unsigned int mss_now, + unsigned int max_segs, + int nonagle); +bool tcp_nagle_test(const struct tcp_sock *tp, const struct sk_buff *skb, + unsigned int cur_mss, int nonagle); +bool tcp_snd_wnd_test(const struct tcp_sock *tp, const struct sk_buff *skb, + unsigned int cur_mss); +unsigned int tcp_cwnd_test(const struct tcp_sock *tp, const struct sk_buff *skb); +int tcp_init_tso_segs(const struct sock *sk, struct sk_buff *skb, + unsigned int mss_now); +int __pskb_trim_head(struct sk_buff *skb, int len); +void tcp_queue_skb(struct sock *sk, struct sk_buff *skb); +void tcp_init_nondata_skb(struct sk_buff *skb, u32 seq, u8 flags); +void tcp_reset(struct sock *sk); +bool tcp_may_update_window(const struct tcp_sock *tp, const u32 ack, + const u32 ack_seq, const u32 nwin); +bool tcp_urg_mode(const struct tcp_sock *tp); +void tcp_ack_probe(struct sock *sk); +void tcp_rearm_rto(struct sock *sk); +int tcp_write_timeout(struct sock *sk); +bool retransmits_timed_out(struct sock *sk, unsigned int boundary, + unsigned int timeout, bool syn_set); +void tcp_write_err(struct sock *sk); +void tcp_adjust_pcount(struct sock *sk, const struct sk_buff *skb, int decr); +void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb, + unsigned int mss_now); + +void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, + struct request_sock *req); +int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, + struct flowi *fl, + struct request_sock *req, + u16 queue_mapping, + struct tcp_fastopen_cookie *foc); +void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb); +struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb); +void tcp_v4_reqsk_destructor(struct request_sock *req); + +void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, + struct request_sock *req); +int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst, + struct flowi *fl, struct request_sock *req, + u16 queue_mapping, struct tcp_fastopen_cookie *foc); +void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb); +int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb); +int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len); +void tcp_v6_destroy_sock(struct sock *sk); +void inet6_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb); +void tcp_v6_hash(struct sock *sk); +struct sock *tcp_v6_hnd_req(struct sock *sk,struct sk_buff *skb); +struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, + struct request_sock *req, + struct dst_entry *dst); +void tcp_v6_reqsk_destructor(struct request_sock *req); + +unsigned int tcp_xmit_size_goal(struct sock *sk, u32 mss_now, + int large_allowed); +u32 tcp_tso_acked(struct sock *sk, struct sk_buff *skb); + +void skb_clone_fraglist(struct sk_buff *skb); +void copy_skb_header(struct sk_buff *new, const struct sk_buff *old); + +void inet_twsk_free(struct inet_timewait_sock *tw); +int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb); +/* These states need RST on ABORT according to RFC793 */ +static inline bool tcp_need_reset(int state) +{ + return (1 << state) & + (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT | TCPF_FIN_WAIT1 | + TCPF_FIN_WAIT2 | TCPF_SYN_RECV); +} + +int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int hdrlen, + bool *fragstolen); +bool tcp_try_coalesce(struct sock *sk, struct sk_buff *to, + struct sk_buff *from, bool *fragstolen); +/**** END - Exports needed for MPTCP ****/ + +#endif void tcp_tasklet_init(void); void tcp_v4_err(struct sk_buff *skb, u32); @@ -440,7 +569,13 @@ int flags, int *addr_len); void tcp_parse_options(const struct sk_buff *skb, struct tcp_options_received *opt_rx, +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) int estab, struct tcp_fastopen_cookie *foc); +#else + struct mptcp_options_received *mopt_rx, + int estab, struct tcp_fastopen_cookie *foc, + struct tcp_sock *tp); +#endif const u8 *tcp_parse_md5sig_option(const struct tcphdr *th); /* @@ -449,6 +584,9 @@ void tcp_v4_send_check(struct sock *sk, struct sk_buff *skb); void tcp_v4_mtu_reduced(struct sock *sk); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +void tcp_v6_mtu_reduced(struct sock *sk); +#endif void tcp_req_err(struct sock *sk, u32 seq); int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb); struct sock *tcp_create_openreq_child(struct sock *sk, @@ -495,8 +633,13 @@ u32 __cookie_v4_init_sequence(const struct iphdr *iph, const struct tcphdr *th, u16 *mssp); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) __u32 cookie_v4_init_sequence(struct sock *sk, const struct sk_buff *skb, __u16 *mss); +#else +__u32 cookie_v4_init_sequence(struct request_sock *req, struct sock *sk, + const struct sk_buff *skb, __u16 *mss); +#endif __u32 cookie_init_timestamp(struct request_sock *req); bool cookie_timestamp_decode(struct tcp_options_received *opt); bool cookie_ecn_ok(const struct tcp_options_received *opt, @@ -509,8 +652,13 @@ u32 __cookie_v6_init_sequence(const struct ipv6hdr *iph, const struct tcphdr *th, u16 *mssp); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) __u32 cookie_v6_init_sequence(struct sock *sk, const struct sk_buff *skb, __u16 *mss); +#else +__u32 cookie_v6_init_sequence(struct request_sock *req, struct sock *sk, + const struct sk_buff *skb, __u16 *mss); +#endif #endif /* tcp_output.c */ @@ -537,10 +685,22 @@ void tcp_send_loss_probe(struct sock *sk); bool tcp_schedule_loss_probe(struct sock *sk); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +u16 tcp_select_window(struct sock *sk); +int select_size(const struct sock *sk, bool sg); +bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, + int push_one, gfp_t gfp); + +#endif /* tcp_input.c */ void tcp_resume_early_retransmit(struct sock *sk); void tcp_rearm_rto(struct sock *sk); void tcp_reset(struct sock *sk); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +void tcp_set_rto(struct sock *sk); +bool tcp_should_expand_sndbuf(const struct sock *sk); +bool tcp_prune_ofo_queue(struct sock *sk); +#endif /* tcp_timer.c */ void tcp_init_xmit_timers(struct sock *); @@ -711,6 +871,14 @@ __u32 tcp_tw_isn; __u32 tcp_gso_segs; }; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + +#ifdef CONFIG_MPTCP + __u8 mptcp_flags; /* flags for the MPTCP layer */ + __u8 dss_off; /* Number of 4-byte words until + * seq-number */ +#endif +#endif __u8 tcp_flags; /* TCP header flags. (tcp[13]) */ __u8 sacked; /* State flags for SACK/FACK. */ @@ -726,12 +894,31 @@ __u8 ip_dsfield; /* IPv4 tos or IPv6 dsfield */ /* 1 byte hole */ __u32 ack_seq; /* Sequence number ACK'd */ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + union { + union { + struct inet_skb_parm h4; +#if IS_ENABLED(CONFIG_IPV6) + struct inet6_skb_parm h6; +#endif + } header; /* For incoming frames */ +#ifdef CONFIG_MPTCP + union { /* For MPTCP outgoing frames */ + __u32 path_mask; /* paths that tried to send this skb */ + __u32 dss[6]; /* DSS options */ + }; + }; +#endif + +#else union { struct inet_skb_parm h4; #if IS_ENABLED(CONFIG_IPV6) struct inet6_skb_parm h6; #endif } header; /* For incoming frames */ + +#endif }; #define TCP_SKB_CB(__skb) ((struct tcp_skb_cb *)&((__skb)->cb[0])) @@ -1119,7 +1306,12 @@ /* Determine a window scaling and initial window to offer. */ void tcp_select_initial_window(int __space, __u32 mss, __u32 *rcv_wnd, __u32 *window_clamp, int wscale_ok, +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) __u8 *rcv_wscale, __u32 init_rcv_wnd); +#else + __u8 *rcv_wscale, __u32 init_rcv_wnd, + const struct sock *sk); +#endif static inline int tcp_win_from_space(int space) { @@ -1130,6 +1322,21 @@ space - (space>>tcp_adv_win_scale); } +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#ifdef CONFIG_MPTCP +extern struct static_key mptcp_static_key; +static inline bool mptcp(const struct tcp_sock *tp) +{ + return static_key_false(&mptcp_static_key) && tp->mpc; +} +#else +static inline bool mptcp(const struct tcp_sock *tp) +{ + return 0; +} +#endif + +#endif /* Note: caller must be prepared to deal with negative returns */ static inline int tcp_space(const struct sock *sk) { @@ -1633,6 +1840,33 @@ #endif }; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +/* TCP/MPTCP-specific functions */ +struct tcp_sock_ops { + u32 (*__select_window)(struct sock *sk); + u16 (*select_window)(struct sock *sk); + void (*select_initial_window)(int __space, __u32 mss, __u32 *rcv_wnd, + __u32 *window_clamp, int wscale_ok, + __u8 *rcv_wscale, __u32 init_rcv_wnd, + const struct sock *sk); + int (*select_size)(const struct sock *sk, bool sg); + void (*init_buffer_space)(struct sock *sk); + void (*set_rto)(struct sock *sk); + bool (*should_expand_sndbuf)(const struct sock *sk); + void (*send_fin)(struct sock *sk); + bool (*write_xmit)(struct sock *sk, unsigned int mss_now, int nonagle, + int push_one, gfp_t gfp); + void (*send_active_reset)(struct sock *sk, gfp_t priority); + int (*write_wakeup)(struct sock *sk); + bool (*prune_ofo_queue)(struct sock *sk); + void (*retransmit_timer)(struct sock *sk); + void (*time_wait)(struct sock *sk, int state, int timeo); + void (*cleanup_rbuf)(struct sock *sk, int copied); + void (*cwnd_validate)(struct sock *sk, bool is_cwnd_limited); +}; +extern const struct tcp_sock_ops tcp_specific; + +#endif struct tcp_request_sock_ops { u16 mss_clamp; #ifdef CONFIG_TCP_MD5SIG @@ -1643,11 +1877,21 @@ const struct sock *sk, const struct sk_buff *skb); #endif +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) void (*init_req)(struct request_sock *req, struct sock *sk, struct sk_buff *skb); +#else + int (*init_req)(struct request_sock *req, struct sock *sk_listener, + struct sk_buff *skb, bool want_cookie); +#endif #ifdef CONFIG_SYN_COOKIES +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) __u32 (*cookie_init_seq)(struct sock *sk, const struct sk_buff *skb, __u16 *mss); +#else + __u32 (*cookie_init_seq)(struct request_sock *req, struct sock *sk, + const struct sk_buff *skb, __u16 *mss); +#endif #endif struct dst_entry *(*route_req)(struct sock *sk, struct flowi *fl, const struct request_sock *req, @@ -1661,14 +1905,28 @@ }; #ifdef CONFIG_SYN_COOKIES +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static inline __u32 cookie_init_sequence(const struct tcp_request_sock_ops *ops, +#else +static inline __u32 cookie_init_sequence(struct request_sock *req, + const struct tcp_request_sock_ops *ops, +#endif struct sock *sk, struct sk_buff *skb, __u16 *mss) { +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) return ops->cookie_init_seq(sk, skb, mss); +#else + return ops->cookie_init_seq(req, sk, skb, mss); +#endif } #else +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static inline __u32 cookie_init_sequence(const struct tcp_request_sock_ops *ops, +#else +static inline __u32 cookie_init_sequence(struct request_sock *req, + const struct tcp_request_sock_ops *ops, +#endif struct sock *sk, struct sk_buff *skb, __u16 *mss) { diff -urN linux-4.1.52-orig/include/net/transp_v6.h linux-4.1.52/include/net/transp_v6.h --- linux-4.1.52-orig/include/net/transp_v6.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/net/transp_v6.h 2020-02-07 01:47:54.000000000 +0800 @@ -51,6 +51,10 @@ /* address family specific functions */ extern const struct inet_connection_sock_af_ops ipv4_specific; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +extern const struct inet_connection_sock_af_ops ipv6_mapped; +extern const struct inet_connection_sock_af_ops ipv6_specific; +#endif void inet6_destroy_sock(struct sock *sk); diff -urN linux-4.1.52-orig/include/trace/events/cpufreq_interactive.h linux-4.1.52/include/trace/events/cpufreq_interactive.h --- linux-4.1.52-orig/include/trace/events/cpufreq_interactive.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/trace/events/cpufreq_interactive.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,114 @@ +#if defined CONFIG_BCM_KF_INTERACTIVE && defined CONFIG_CPU_FREQ_GOV_INTERACTIVE +#undef TRACE_SYSTEM +#define TRACE_SYSTEM cpufreq_interactive + +#if !defined(_TRACE_CPUFREQ_INTERACTIVE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_CPUFREQ_INTERACTIVE_H + +#include + +DECLARE_EVENT_CLASS(set, + TP_PROTO(u32 cpu_id, unsigned long targfreq, + unsigned long actualfreq), + TP_ARGS(cpu_id, targfreq, actualfreq), + + TP_STRUCT__entry( + __field( u32, cpu_id ) + __field(unsigned long, targfreq ) + __field(unsigned long, actualfreq ) + ), + + TP_fast_assign( + __entry->cpu_id = (u32) cpu_id; + __entry->targfreq = targfreq; + __entry->actualfreq = actualfreq; + ), + + TP_printk("cpu=%u targ=%lu actual=%lu", + __entry->cpu_id, __entry->targfreq, + __entry->actualfreq) +); + +DEFINE_EVENT(set, cpufreq_interactive_setspeed, + TP_PROTO(u32 cpu_id, unsigned long targfreq, + unsigned long actualfreq), + TP_ARGS(cpu_id, targfreq, actualfreq) +); + +DECLARE_EVENT_CLASS(loadeval, + TP_PROTO(unsigned long cpu_id, unsigned long load, + unsigned long curtarg, unsigned long curactual, + unsigned long newtarg), + TP_ARGS(cpu_id, load, curtarg, curactual, newtarg), + + TP_STRUCT__entry( + __field(unsigned long, cpu_id ) + __field(unsigned long, load ) + __field(unsigned long, curtarg ) + __field(unsigned long, curactual ) + __field(unsigned long, newtarg ) + ), + + TP_fast_assign( + __entry->cpu_id = cpu_id; + __entry->load = load; + __entry->curtarg = curtarg; + __entry->curactual = curactual; + __entry->newtarg = newtarg; + ), + + TP_printk("cpu=%lu load=%lu cur=%lu actual=%lu targ=%lu", + __entry->cpu_id, __entry->load, __entry->curtarg, + __entry->curactual, __entry->newtarg) +); + +DEFINE_EVENT(loadeval, cpufreq_interactive_target, + TP_PROTO(unsigned long cpu_id, unsigned long load, + unsigned long curtarg, unsigned long curactual, + unsigned long newtarg), + TP_ARGS(cpu_id, load, curtarg, curactual, newtarg) +); + +DEFINE_EVENT(loadeval, cpufreq_interactive_already, + TP_PROTO(unsigned long cpu_id, unsigned long load, + unsigned long curtarg, unsigned long curactual, + unsigned long newtarg), + TP_ARGS(cpu_id, load, curtarg, curactual, newtarg) +); + +DEFINE_EVENT(loadeval, cpufreq_interactive_notyet, + TP_PROTO(unsigned long cpu_id, unsigned long load, + unsigned long curtarg, unsigned long curactual, + unsigned long newtarg), + TP_ARGS(cpu_id, load, curtarg, curactual, newtarg) +); + +TRACE_EVENT(cpufreq_interactive_boost, + TP_PROTO(const char *s), + TP_ARGS(s), + TP_STRUCT__entry( + __string(s, s) + ), + TP_fast_assign( + __assign_str(s, s); + ), + TP_printk("%s", __get_str(s)) +); + +TRACE_EVENT(cpufreq_interactive_unboost, + TP_PROTO(const char *s), + TP_ARGS(s), + TP_STRUCT__entry( + __string(s, s) + ), + TP_fast_assign( + __assign_str(s, s); + ), + TP_printk("%s", __get_str(s)) +); + +#endif /* _TRACE_CPUFREQ_INTERACTIVE_H */ + +/* This part must be outside protection */ +#include +#endif diff -urN linux-4.1.52-orig/include/uapi/linux/aio_abi.h linux-4.1.52/include/uapi/linux/aio_abi.h --- linux-4.1.52-orig/include/uapi/linux/aio_abi.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/aio_abi.h 2020-02-07 01:47:54.000000000 +0800 @@ -62,6 +62,15 @@ __s64 res2; /* secondary result */ }; +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(__LITTLE_ENDIAN) +#define PADDED(x,y) x, y +#elif defined(__BIG_ENDIAN) +#define PADDED(x,y) y, x +#else +#error edit for your odd byteorder. +#endif +#else #if defined(__BYTE_ORDER) ? __BYTE_ORDER == __LITTLE_ENDIAN : defined(__LITTLE_ENDIAN) #define PADDED(x,y) x, y #elif defined(__BYTE_ORDER) ? __BYTE_ORDER == __BIG_ENDIAN : defined(__BIG_ENDIAN) @@ -69,6 +78,7 @@ #else #error edit for your odd byteorder. #endif +#endif /* * we always use a 64bit off_t when communicating diff -urN linux-4.1.52-orig/include/uapi/linux/atmdev.h linux-4.1.52/include/uapi/linux/atmdev.h --- linux-4.1.52-orig/include/uapi/linux/atmdev.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/atmdev.h 2020-02-07 01:47:54.000000000 +0800 @@ -6,12 +6,16 @@ #ifndef _UAPILINUX_ATMDEV_H #define _UAPILINUX_ATMDEV_H +#if defined(CONFIG_BCM_KF_DOUBLE_INCLUSION) || !defined(CONFIG_BCM_IN_KERNEL) +#ifndef LINUX_ATMDEV_H +#define LINUX_ATMDEV_H +#endif +#endif #include #include #include - #define ESI_LEN 6 #define ATM_OC3_PCR (155520000/270*260/8/53) @@ -107,6 +111,11 @@ #define ATM_DROPPARTY _IOW('a', ATMIOC_SPECIAL+5,int) /* drop party from p2mp call */ +#if defined(CONFIG_BCM_KF_ATM_BACKEND) || !defined(CONFIG_BCM_IN_KERNEL) +#define ATM_EXTBACKENDIF _IOW('a',ATMIOC_SPECIAL+6,atm_backend_t) +#define ATM_SETEXTFILT _IOW('a',ATMIOC_SPECIAL+7,atm_backend_t) +#endif + /* * These are backend handkers that can be set via the ATM_SETBACKEND call * above. In the future we may support dynamic loading of these - for now, @@ -116,6 +125,14 @@ #define ATM_BACKEND_PPP 1 /* PPPoATM - RFC2364 */ #define ATM_BACKEND_BR2684 2 /* Bridged RFC1483/2684 */ +#if defined(CONFIG_BCM_KF_ATM_BACKEND) || !defined(CONFIG_BCM_IN_KERNEL) +#define ATM_BACKEND_RT2684 3 /* Routed RFC1483/2684 */ +#define ATM_BACKEND_BR2684_BCM 4 /* Bridged RFC1483/2684 uses Broadcom ATMAPI*/ +#define ATM_BACKEND_PPP_BCM 5 /* PPPoA uses Broadcom bcmxtmrt driver */ +#define ATM_BACKEND_PPP_BCM_DISCONN 6 /* PPPoA LCP disconnect */ +#define ATM_BACKEND_PPP_BCM_CLOSE_DEV 7 /* PPPoA close device */ +#endif + /* for ATM_GETTYPE */ #define ATM_ITFTYP_LEN 8 /* maximum length of interface type name */ diff -urN linux-4.1.52-orig/include/uapi/linux/bcm_colors.h linux-4.1.52/include/uapi/linux/bcm_colors.h --- linux-4.1.52-orig/include/uapi/linux/bcm_colors.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/bcm_colors.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,98 @@ +#if defined(CONFIG_BCM_KF_LOG) || defined(CONFIG_BCM_KF_BLOG) || \ +defined(CONFIG_BCM_KF_BUZZZ) || !defined(CONFIG_BCM_IN_KERNEL) +/* + * <:copyright-BRCM:2016:DUAL/GPL:standard + * + * Copyright (c) 2016 Broadcom + * All Rights Reserved + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed + * to you under the terms of the GNU General Public License version 2 + * (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, + * with the following added to such license: + * + * As a special exception, the copyright holders of this software give + * you permission to link this software with independent modules, and + * to copy and distribute the resulting executable under terms of your + * choice, provided that you also meet, for each linked independent + * module, the terms and conditions of the license of that module. + * An independent module is a module which is not derived from this + * software. The special exception does not apply to any modifications + * of the software. + * + * Not withstanding the above, under no circumstances may you combine + * this software in any way with any other Broadcom software provided + * under a license other than the GPL, without Broadcom's express prior + * written consent. + * + * :> + */ + +/*----------------------------------------------------------------------* + * NOTE: FOR USERSPACE USERS: + *----------------------------------------------------------------------* + *ALL USERSPACE DEVELOPERS MUST INCLUDE THIS FILE IN THEIR APPLICATIONS. + * + * EXAMPLE: + * #include + *----------------------------------------------------------------------*/ + +/* + *-------------------------------------------------------------------------- + * Color encodings for console printing: + * + * This feature is controlled from top level make menuconfig, under + * Debug Selection>Enable Colorized Prints + * + * You may select a color specific to your subsystem by: + * #define CLRsys CLRg + * + * Usage: PRINT(CLRr "format" CLRNL); + *-------------------------------------------------------------------------- + */ + +#ifndef __UAPI_BCM_COLORS_H__ +#define __UAPI_BCM_COLORS_H__ + +#ifdef CONFIG_BCM_COLORIZE_PRINTS +#define BCMCOLOR(clr_code) clr_code +#else +#define BCMCOLOR(clr_code) +#endif + +/* White background */ +#define CLRr BCMCOLOR("\e[0;31m") /* red */ +#define CLRg BCMCOLOR("\e[0;32m") /* green */ +#define CLRy BCMCOLOR("\e[0;33m") /* yellow */ +#define CLRb BCMCOLOR("\e[0;34m") /* blue */ +#define CLRm BCMCOLOR("\e[0;35m") /* magenta */ +#define CLRc BCMCOLOR("\e[0;36m") /* cyan */ + +/* blacK "inverted" background */ +#define CLRrk BCMCOLOR("\e[0;31;40m") /* red on blacK */ +#define CLRgk BCMCOLOR("\e[0;32;40m") /* green on blacK */ +#define CLRyk BCMCOLOR("\e[0;33;40m") /* yellow on blacK */ +#define CLRmk BCMCOLOR("\e[0;35;40m") /* magenta on blacK */ +#define CLRck BCMCOLOR("\e[0;36;40m") /* cyan on blacK */ +#define CLRwk BCMCOLOR("\e[0;37;40m") /* whilte on blacK */ + +/* Colored background */ +#define CLRcb BCMCOLOR("\e[0;36;44m") /* cyan on blue */ +#define CLRyr BCMCOLOR("\e[0;33;41m") /* yellow on red */ +#define CLRym BCMCOLOR("\e[0;33;45m") /* yellow on magen */ + +/* Generic foreground colors */ +#define CLRhigh CLRm /* Highlight color */ +#define CLRbold CLRcb /* Bold color */ +#define CLRbold2 CLRym /* Bold2 color */ +#define CLRerr CLRyr /* Error color */ +#define CLRnorm BCMCOLOR("\e[0m") /* Normal color */ +#define CLRnl CLRnorm "\n" /* Normal + newline */ + +/* Each subsystem may define CLRsys */ + +#endif /* __BCM_COLORS_H__ */ + +#endif /* CONFIG_BCM_KF_LOG || CONFIG_BCM_KF_BLOG || CONFIG_BCM_KF_BUZZZ || + * !CONFIG_BCM_IN_KERNEL */ diff -urN linux-4.1.52-orig/include/uapi/linux/byteorder/big_endian.h linux-4.1.52/include/uapi/linux/byteorder/big_endian.h --- linux-4.1.52-orig/include/uapi/linux/byteorder/big_endian.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/byteorder/big_endian.h 2020-02-07 01:47:54.000000000 +0800 @@ -40,6 +40,7 @@ #define __cpu_to_be16(x) ((__force __be16)(__u16)(x)) #define __be16_to_cpu(x) ((__force __u16)(__be16)(x)) + static inline __le64 __cpu_to_le64p(const __u64 *p) { return (__force __le64)__swab64p(p); @@ -88,6 +89,8 @@ { return (__force __u16)*p; } + + #define __cpu_to_le64s(x) __swab64s((x)) #define __le64_to_cpus(x) __swab64s((x)) #define __cpu_to_le32s(x) __swab32s((x)) diff -urN linux-4.1.52-orig/include/uapi/linux/compiler.h linux-4.1.52/include/uapi/linux/compiler.h --- linux-4.1.52-orig/include/uapi/linux/compiler.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/compiler.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,31 @@ +#if !defined(CONFIG_BCM_IN_KERNEL) +/* +<:copyright-gpl + Copyright 2014 Broadcom Corp. All Rights Reserved. + + This program is free software; you can distribute 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 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., + 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. +:> + */ + +/* For userspace app to include compiler.h */ +#ifndef __UAPI_LINUX_COMPILER_H +#define __UAPI_LINUX_COMPILER_H + +#ifndef __user +#define __user +#endif + +#endif + +#endif diff -urN linux-4.1.52-orig/include/uapi/linux/ethtool.h linux-4.1.52/include/uapi/linux/ethtool.h --- linux-4.1.52-orig/include/uapi/linux/ethtool.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/ethtool.h 2020-02-07 01:47:54.000000000 +0800 @@ -1269,6 +1269,9 @@ #define SPEED_100 100 #define SPEED_1000 1000 #define SPEED_2500 2500 +#if defined(CONFIG_BCM_KF_ETHTOOL) +#define SPEED_5000 5000 +#endif #define SPEED_10000 10000 #define SPEED_20000 20000 #define SPEED_40000 40000 diff -urN linux-4.1.52-orig/include/uapi/linux/icmp.h linux-4.1.52/include/uapi/linux/icmp.h --- linux-4.1.52-orig/include/uapi/linux/icmp.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/icmp.h 2020-02-07 01:47:54.000000000 +0800 @@ -80,7 +80,11 @@ __be16 mtu; } frag; } un; +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) +} LINUX_NET_PACKED; +#else }; +#endif /* diff -urN linux-4.1.52-orig/include/uapi/linux/if_arp.h linux-4.1.52/include/uapi/linux/if_arp.h --- linux-4.1.52-orig/include/uapi/linux/if_arp.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/if_arp.h 2020-02-07 01:47:54.000000000 +0800 @@ -23,6 +23,12 @@ #ifndef _UAPI_LINUX_IF_ARP_H #define _UAPI_LINUX_IF_ARP_H +#if defined(CONFIG_BCM_KF_DOUBLE_INCLUSION) || !defined(CONFIG_BCM_IN_KERNEL) +#ifndef _LINUX_IF_ARP_H +#define _LINUX_IF_ARP_H +#endif +#endif + #include /* ARP protocol HARDWARE identifiers. */ @@ -40,6 +46,10 @@ #define ARPHRD_METRICOM 23 /* Metricom STRIP (new IANA id) */ #define ARPHRD_IEEE1394 24 /* IEEE 1394 IPv4 - RFC 2734 */ #define ARPHRD_EUI64 27 /* EUI-64 */ +#if defined(CONFIG_BCM_KF_IP) || !defined(CONFIG_BCM_IN_KERNEL) +#define ARPHRD_CPCS 28 /* CPCS */ +#define ARPHRD_DSL 29 /* ADSL */ +#endif #define ARPHRD_INFINIBAND 32 /* InfiniBand */ /* Dummy types for non ARP hardware */ @@ -95,6 +105,9 @@ #define ARPHRD_IP6GRE 823 /* GRE over IPv6 */ #define ARPHRD_NETLINK 824 /* Netlink header */ #define ARPHRD_6LOWPAN 825 /* IPv6 over LoWPAN */ +#ifdef CONFIG_BCM_KF_MHI +#define ARPHRD_MHI 1823 /* Modem-Host IF */ +#endif #define ARPHRD_VOID 0xFFFF /* Void type, nothing is known */ #define ARPHRD_NONE 0xFFFE /* zero header length */ diff -urN linux-4.1.52-orig/include/uapi/linux/if_bridge.h linux-4.1.52/include/uapi/linux/if_bridge.h --- linux-4.1.52-orig/include/uapi/linux/if_bridge.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/if_bridge.h 2020-02-07 01:47:54.000000000 +0800 @@ -13,6 +13,12 @@ #ifndef _UAPI_LINUX_IF_BRIDGE_H #define _UAPI_LINUX_IF_BRIDGE_H +#if !defined(CONFIG_BCM_IN_KERNEL) +#ifndef _LINUX_IF_BRIDGE_H +#define _LINUX_IF_BRIDGE_H +#endif +#endif + #include #include #include @@ -96,7 +102,11 @@ __u32 ageing_timer_value; __u8 port_hi; __u8 pad0; +#if defined(CONFIG_BCM_KF_VLAN_AGGREGATION) && defined(CONFIG_BCM_VLAN_AGGREGATION) + __u16 vid; +#else __u16 unused; +#endif }; /* Bridge Flags */ diff -urN linux-4.1.52-orig/include/uapi/linux/if_ether.h linux-4.1.52/include/uapi/linux/if_ether.h --- linux-4.1.52-orig/include/uapi/linux/if_ether.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/if_ether.h 2020-02-07 01:47:54.000000000 +0800 @@ -99,6 +99,10 @@ #define ETH_P_802_3_MIN 0x0600 /* If the value in the ethernet type is less than this value * then the frame is Ethernet II. Else it is 802.3 */ +#if defined(CONFIG_BCM_KF_VLAN) +#define ETH_P_8021AG 0x8902 /* 802.1ag Connectivity Fault Mgmt */ +#define ETH_P_8023AH 0x8809 /* 802.3ah Ethernet OAM */ +#endif /* * Non DIX types. Won't clash for 1500 types. @@ -129,6 +133,11 @@ #define ETH_P_IEEE802154 0x00F6 /* IEEE802.15.4 frame */ #define ETH_P_CAIF 0x00F7 /* ST-Ericsson CAIF protocol */ #define ETH_P_XDSA 0x00F8 /* Multiplexed DSA protocol */ +#ifdef CONFIG_BCM_KF_MHI +#define ETH_P_MHI 0x00F9 /* Renesas MHI protocol */ +#define ETH_P_RAW 0x00FA /* RAW access to frames */ +#define ETH_P_MHDP 0x00FB /* MHDP data frames */ +#endif /* * This is an Ethernet frame header. diff -urN linux-4.1.52-orig/include/uapi/linux/if.h linux-4.1.52/include/uapi/linux/if.h --- linux-4.1.52-orig/include/uapi/linux/if.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/if.h 2020-02-07 01:47:54.000000000 +0800 @@ -21,7 +21,11 @@ #include /* for "__kernel_caddr_t" et al */ #include /* for "struct sockaddr" et al */ +#if !defined(CONFIG_BCM_IN_KERNEL) +//userspace already has __user defined.... +#else #include /* for "__user" et al */ +#endif #define IFNAMSIZ 16 #define IFALIASZ 256 @@ -109,6 +113,11 @@ #define IFF_DORMANT IFF_DORMANT #define IFF_ECHO IFF_ECHO +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#define IFF_NOMULTIPATH 0x80000 /* Disable for MPTCP */ +#define IFF_MPBACKUP 0x100000 /* Use as backup path for MPTCP */ + +#endif #define IFF_VOLATILE (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_ECHO|\ IFF_MASTER|IFF_SLAVE|IFF_RUNNING|IFF_LOWER_UP|IFF_DORMANT) diff -urN linux-4.1.52-orig/include/uapi/linux/if_link.h linux-4.1.52/include/uapi/linux/if_link.h --- linux-4.1.52-orig/include/uapi/linux/if_link.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/if_link.h 2020-02-07 01:47:54.000000000 +0800 @@ -15,6 +15,17 @@ __u32 rx_dropped; /* no space in linux buffers */ __u32 tx_dropped; /* no space available in linux */ __u32 multicast; /* multicast packets received */ +#if defined(CONFIG_BCM_KF_EXTSTATS) + __u32 tx_multicast_packets; /* multicast packets transmitted */ + __u32 rx_multicast_bytes; /* multicast bytes recieved */ + __u32 tx_multicast_bytes; /* multicast bytes transmitted */ + __u32 rx_broadcast_packets; /* broadcast packets recieved */ + __u32 tx_broadcast_packets; /* broadcast packets transmitted */ + /* NOTE: Unicast packets are not counted but are instead calculated as needed + using total - (broadcast + multicast) */ + __u32 rx_unknown_packets; /* unknown protocol packets recieved */ +#endif + __u32 collisions; /* detailed rx_errors: */ @@ -48,6 +59,7 @@ __u64 rx_dropped; /* no space in linux buffers */ __u64 tx_dropped; /* no space available in linux */ __u64 multicast; /* multicast packets received */ + __u64 collisions; /* detailed rx_errors: */ @@ -68,6 +80,16 @@ /* for cslip etc */ __u64 rx_compressed; __u64 tx_compressed; +#if defined(CONFIG_BCM_KF_EXTSTATS) + __u64 tx_multicast_packets; /* multicast packets transmitted */ + __u64 rx_multicast_bytes; /* multicast bytes recieved */ + __u64 tx_multicast_bytes; /* multicast bytes transmitted */ + __u64 rx_broadcast_packets; /* broadcast packets recieved */ + __u64 tx_broadcast_packets; /* broadcast packets transmitted */ + /* NOTE: Unicast packets are not counted but are instead calculated as needed + using total - (broadcast + multicast) */ + __u64 rx_unknown_packets; /* unknown protocol packets recieved */ +#endif }; /* The struct should be in sync with struct ifmap */ diff -urN linux-4.1.52-orig/include/uapi/linux/if_vlan.h linux-4.1.52/include/uapi/linux/if_vlan.h --- linux-4.1.52-orig/include/uapi/linux/if_vlan.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/if_vlan.h 2020-02-07 01:47:54.000000000 +0800 @@ -28,6 +28,10 @@ SET_VLAN_FLAG_CMD, GET_VLAN_REALDEV_NAME_CMD, /* If this works, you know it's a VLAN device, btw */ GET_VLAN_VID_CMD /* Get the VID of this VLAN (specified by name) */ +#if defined(CONFIG_BCM_KF_VLAN) && (defined(CONFIG_BCM_VLAN) || defined(CONFIG_BCM_VLAN_MODULE)) + , + SET_VLAN_NFMARK_TO_PRIORITY_CMD +#endif }; enum vlan_flags { @@ -56,6 +60,9 @@ unsigned int name_type; unsigned int bind_type; unsigned int flag; /* Matches vlan_dev_priv flags */ +#if defined(CONFIG_BCM_KF_VLAN) && (defined(CONFIG_BCM_VLAN) || defined(CONFIG_BCM_VLAN_MODULE)) + int nfmark_to_priority; +#endif } u; short vlan_qos; diff -urN linux-4.1.52-orig/include/uapi/linux/igmp.h linux-4.1.52/include/uapi/linux/igmp.h --- linux-4.1.52-orig/include/uapi/linux/igmp.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/igmp.h 2020-02-07 01:47:54.000000000 +0800 @@ -32,7 +32,11 @@ __u8 code; /* For newer IGMP */ __sum16 csum; __be32 group; +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) +} LINUX_NET_PACKED; +#else }; +#endif /* V3 group record types [grec_type] */ #define IGMPV3_MODE_IS_INCLUDE 1 @@ -48,7 +52,11 @@ __be16 grec_nsrcs; __be32 grec_mca; __be32 grec_src[0]; +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) +} LINUX_NET_PACKED; +#else }; +#endif struct igmpv3_report { __u8 type; @@ -57,7 +65,11 @@ __be16 resv2; __be16 ngrec; struct igmpv3_grec grec[0]; +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) +} LINUX_NET_PACKED; +#else }; +#endif struct igmpv3_query { __u8 type; @@ -78,7 +90,11 @@ __u8 qqic; __be16 nsrcs; __be32 srcs[0]; +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) +} LINUX_NET_PACKED; +#else }; +#endif #define IGMP_HOST_MEMBERSHIP_QUERY 0x11 /* From RFC1112 */ #define IGMP_HOST_MEMBERSHIP_REPORT 0x12 /* Ditto */ diff -urN linux-4.1.52-orig/include/uapi/linux/ip.h linux-4.1.52/include/uapi/linux/ip.h --- linux-4.1.52-orig/include/uapi/linux/ip.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/ip.h 2020-02-07 01:47:54.000000000 +0800 @@ -102,8 +102,11 @@ __be32 saddr; __be32 daddr; /*The options start here. */ +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) +} LINUX_NET_PACKED; +#else }; - +#endif struct ip_auth_hdr { __u8 nexthdr; diff -urN linux-4.1.52-orig/include/uapi/linux/jffs2.h linux-4.1.52/include/uapi/linux/jffs2.h --- linux-4.1.52-orig/include/uapi/linux/jffs2.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/jffs2.h 2020-02-07 01:47:54.000000000 +0800 @@ -26,6 +26,11 @@ #define KSAMTIB_CIGAM_2SFFJ 0x8519 /* For detecting wrong-endian fs */ #define JFFS2_EMPTY_BITMASK 0xffff #define JFFS2_DIRTY_BITMASK 0x0000 +#if defined(CONFIG_BCM_KF_JFFS) || !defined(CONFIG_BCM_IN_KERNEL) +#define JFFS2_EBH_COMPAT_FSET 0x00 +#define JFFS2_EBH_INCOMPAT_FSET 0x00 +#define JFFS2_EBH_ROCOMPAT_FSET 0x00 +#endif /* Summary node MAGIC marker */ #define JFFS2_SUM_MAGIC 0x02851885 @@ -68,6 +73,10 @@ #define JFFS2_NODETYPE_XATTR (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 8) #define JFFS2_NODETYPE_XREF (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 9) +#if defined(CONFIG_BCM_KF_JFFS) || !defined(CONFIG_BCM_IN_KERNEL) +#define JFFS2_NODETYPE_ERASEBLOCK_HEADER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 5) +#endif + /* XATTR Related */ #define JFFS2_XPREFIX_USER 1 /* for "user." */ #define JFFS2_XPREFIX_SECURITY 2 /* for "security." */ @@ -203,6 +212,22 @@ jint32_t node_crc; /* node crc */ jint32_t sum[0]; /* inode summary info */ }; +#if defined(CONFIG_BCM_KF_JFFS) || !defined(CONFIG_BCM_IN_KERNEL) +struct jffs2_raw_ebh +{ + jint16_t magic; + jint16_t nodetype; /* == JFFS2_NODETYPE_ERASEBLOCK_HEADER */ + jint32_t totlen; + jint32_t hdr_crc; + jint32_t node_crc; + uint8_t reserved; /* reserved for future use and alignment */ + uint8_t compat_fset; + uint8_t incompat_fset; + uint8_t rocompat_fset; + jint32_t erase_count; /* the erase count of this erase block */ + jint32_t data[0]; +} __attribute__((packed)); +#endif union jffs2_node_union { @@ -211,6 +236,9 @@ struct jffs2_raw_xattr x; struct jffs2_raw_xref r; struct jffs2_raw_summary s; +#if defined(CONFIG_BCM_KF_JFFS) || !defined(CONFIG_BCM_IN_KERNEL) + struct jffs2_raw_ebh eh; +#endif struct jffs2_unknown_node u; }; diff -urN linux-4.1.52-orig/include/uapi/linux/netfilter/nf_conntrack_common.h linux-4.1.52/include/uapi/linux/netfilter/nf_conntrack_common.h --- linux-4.1.52-orig/include/uapi/linux/netfilter/nf_conntrack_common.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/netfilter/nf_conntrack_common.h 2020-02-07 01:47:54.000000000 +0800 @@ -91,6 +91,15 @@ /* Conntrack got a helper explicitly attached via CT target. */ IPS_HELPER_BIT = 13, IPS_HELPER = (1 << IPS_HELPER_BIT), +#if defined(CONFIG_BCM_KF_NETFILTER) + IPS_IQOS_BIT = 30, + IPS_IQOS = (1 << IPS_IQOS_BIT), +#endif +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + /* Conntrack eligible for Blogging */ + IPS_BLOG_BIT = 31, + IPS_BLOG = (1 << IPS_BLOG_BIT) +#endif }; /* Connection tracking event types */ @@ -107,6 +116,9 @@ IPCT_NATSEQADJ = IPCT_SEQADJ, IPCT_SECMARK, /* new security mark has been set */ IPCT_LABEL, /* new connlabel has been set */ +#if defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_MODULE) + IPCT_DPI, /* dpi classification for ct is complete */ +#endif }; enum ip_conntrack_expect_events { diff -urN linux-4.1.52-orig/include/uapi/linux/netfilter/nf_conntrack_pt.h linux-4.1.52/include/uapi/linux/netfilter/nf_conntrack_pt.h --- linux-4.1.52-orig/include/uapi/linux/netfilter/nf_conntrack_pt.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/netfilter/nf_conntrack_pt.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,20 @@ +#if defined(CONFIG_BCM_KF_NETFILTER) || !defined(CONFIG_BCM_IN_KERNEL) +#ifndef _NF_CONNTRACK_PT_H +#define _NF_CONNTRACK_PT_H +/* PT tracking. */ +#define PT_MAX_ENTRIES 100 +#define PT_MAX_PORTS 1000 +#define PT_MAX_EXPECTED 1000 +#define PT_TIMEOUT 180 + +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) +#define PT_PROTO_TCP 1 +#define PT_PROTO_UDP 2 +#define PT_PROTO_ALL (PT_PROTO_TCP|PT_PROTO_UDP) +#define PT_PROTO_ALL_IN 0 +#endif /* CONFIG_BCM_RUNNER */ +#endif /* CONFIG_BCM_KF_RUNNER */ + +#endif /* _NF_CONNTRACK_PT_H */ +#endif diff -urN linux-4.1.52-orig/include/uapi/linux/netfilter/nf_conntrack_tuple_common.h linux-4.1.52/include/uapi/linux/netfilter/nf_conntrack_tuple_common.h --- linux-4.1.52-orig/include/uapi/linux/netfilter/nf_conntrack_tuple_common.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/netfilter/nf_conntrack_tuple_common.h 2020-02-07 01:47:54.000000000 +0800 @@ -32,6 +32,12 @@ struct { __be16 key; /* GRE key is 32bit, PPtP only uses 16bit */ } gre; +#if defined(CONFIG_BCM_KF_PROTO_ESP) && \ + (defined(CONFIG_NF_CT_PROTO_ESP) || defined(CONFIG_NF_CT_PROTO_ESP_MODULE)) + struct { + __be16 spi; + } esp; +#endif }; #define CTINFO2DIR(ctinfo) ((ctinfo) >= IP_CT_IS_REPLY ? IP_CT_DIR_REPLY : IP_CT_DIR_ORIGINAL) diff -urN linux-4.1.52-orig/include/uapi/linux/netfilter/nfnetlink_conntrack.h linux-4.1.52/include/uapi/linux/netfilter/nfnetlink_conntrack.h --- linux-4.1.52-orig/include/uapi/linux/netfilter/nfnetlink_conntrack.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/netfilter/nfnetlink_conntrack.h 2020-02-07 01:47:54.000000000 +0800 @@ -53,6 +53,9 @@ CTA_MARK_MASK, CTA_LABELS, CTA_LABELS_MASK, +#if defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_MODULE) + CTA_DPI, +#endif __CTA_MAX }; #define CTA_MAX (__CTA_MAX - 1) @@ -260,4 +263,16 @@ }; #define CTA_STATS_EXP_MAX (__CTA_STATS_EXP_MAX - 1) +#if defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_MODULE) +enum ctattr_dpi { + CTA_DPI_UNSPEC, + CTA_DPI_APP_ID, + CTA_DPI_MAC, + CTA_DPI_STATUS, + CTA_DPI_URL, + __CTA_DPI_MAX, +}; +#define CTA_DPI_MAX (__CTA_DPI_MAX - 1) +#endif + #endif /* _IPCONNTRACK_NETLINK_H */ diff -urN linux-4.1.52-orig/include/uapi/linux/netfilter/x_tables.h linux-4.1.52/include/uapi/linux/netfilter/x_tables.h --- linux-4.1.52-orig/include/uapi/linux/netfilter/x_tables.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/netfilter/x_tables.h 2020-02-07 01:47:54.000000000 +0800 @@ -124,6 +124,25 @@ #define XT_INV_PROTO 0x40 /* Invert the sense of PROTO. */ #ifndef __KERNEL__ +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG_FEATURE) +#define XT_MATCH_ITERATE(type, e, fn, args...) \ +({ \ + unsigned int __i; \ + int __ret = 0, __rval = 0; \ + struct xt_entry_match *__m; \ + \ + for (__i = sizeof(type); \ + __i < (e)->target_offset; \ + __i += __m->u.match_size) { \ + __m = (void *)e + __i; \ + \ + __ret = fn(__m , ## args); \ + if (__ret != 0) \ + __rval = __ret; \ + } \ + __rval; \ +}) +#else /* fn returns 0 to continue iteration */ #define XT_MATCH_ITERATE(type, e, fn, args...) \ ({ \ @@ -142,6 +161,7 @@ } \ __ret; \ }) +#endif /* fn returns 0 to continue iteration */ #define XT_ENTRY_ITERATE_CONTINUE(type, entries, size, n, fn, args...) \ diff -urN linux-4.1.52-orig/include/uapi/linux/netfilter/xt_blog.h linux-4.1.52/include/uapi/linux/netfilter/xt_blog.h --- linux-4.1.52-orig/include/uapi/linux/netfilter/xt_blog.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/netfilter/xt_blog.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,13 @@ +#if defined(CONFIG_BCM_KF_NETFILTER) && defined(CONFIG_BCM_KF_BLOG) +#ifndef _XT_BLOG_H +#define _XT_BLOG_H + +#include + +struct xt_blog { + __u8 tcp_pure_ack; + __u8 invert; +}; + +#endif /*_XT_BLOG_H*/ +#endif diff -urN linux-4.1.52-orig/include/uapi/linux/netfilter/xt_flowlabel.h linux-4.1.52/include/uapi/linux/netfilter/xt_flowlabel.h --- linux-4.1.52-orig/include/uapi/linux/netfilter/xt_flowlabel.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/netfilter/xt_flowlabel.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,56 @@ +#if defined(CONFIG_BCM_KF_NETFILTER) +/* +* Copyright (c) 2003-2019 Broadcom +* All Rights Reserved +* +<:label-BRCM:2019:DUAL/GPL:standard + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + + +/* IP tables module for matching the value of the IPv6 flowlabel field + * + * BRCM, Feb, 1. 2019. + */ + + +#ifndef _XT_FLOWLABEL_H +#define _XT_FLOWLABEL_H + +#include + +#define XT_FLOWLABEL_MAX cpu_to_be32(0x000FFFFF) + + +/* match info */ +struct xt_flowlabel_info { + __be32 flowlabel; + __u8 invert; +}; + + +#endif /* _XT_FLOWLABEL_H */ + +#endif // defined(CONFIG_BCM_KF_NETFILTER) + diff -urN linux-4.1.52-orig/include/uapi/linux/netfilter/xt_mac_extend.h linux-4.1.52/include/uapi/linux/netfilter/xt_mac_extend.h --- linux-4.1.52-orig/include/uapi/linux/netfilter/xt_mac_extend.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/netfilter/xt_mac_extend.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,15 @@ +#ifndef _XT_MAC_EXTEND_H +#define _XT_MAC_EXTEND_H + +/* extend from xt_mac.h for MAC address extend match operations, + * i.e, MAC/mask. + * BRCM, Jan, 31. 2019. + */ + + +struct xt_mac_info_extend { + unsigned char srcaddr[ETH_ALEN]; + unsigned char msk[ETH_ALEN]; + int invert; +}; +#endif /*_XT_MAC_EXTEND_H*/ diff -urN linux-4.1.52-orig/include/uapi/linux/netfilter/xt_tcpudp.h linux-4.1.52/include/uapi/linux/netfilter/xt_tcpudp.h --- linux-4.1.52-orig/include/uapi/linux/netfilter/xt_tcpudp.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/netfilter/xt_tcpudp.h 2020-02-07 01:47:54.000000000 +0800 @@ -11,6 +11,9 @@ __u8 flg_mask; /* TCP flags mask byte */ __u8 flg_cmp; /* TCP flags compare byte */ __u8 invflags; /* Inverse flags */ +#if defined(CONFIG_BCM_KF_NETFILTER) + __u8 pure_ack; /* Pure ACK packet */ +#endif }; /* Values for "inv" field in struct ipt_tcp. */ diff -urN linux-4.1.52-orig/include/uapi/linux/netfilter_bridge/ebt_blog.h linux-4.1.52/include/uapi/linux/netfilter_bridge/ebt_blog.h --- linux-4.1.52-orig/include/uapi/linux/netfilter_bridge/ebt_blog.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/netfilter_bridge/ebt_blog.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,14 @@ +#if defined(CONFIG_BCM_KF_NETFILTER) && defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#ifndef __LINUX_BRIDGE_EBT_BLOG_H +#define __LINUX_BRIDGE_EBT_BLOG_H + +#include + +struct ebt_blog_info +{ + __u8 tcp_pure_ack; + __u8 invert; +}; + +#endif +#endif diff -urN linux-4.1.52-orig/include/uapi/linux/netfilter_bridge/ebt_ftos_t.h linux-4.1.52/include/uapi/linux/netfilter_bridge/ebt_ftos_t.h --- linux-4.1.52-orig/include/uapi/linux/netfilter_bridge/ebt_ftos_t.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/netfilter_bridge/ebt_ftos_t.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,24 @@ +#if defined(CONFIG_BCM_KF_NETFILTER) +#ifndef __LINUX_BRIDGE_EBT_FTOS_T_H +#define __LINUX_BRIDGE_EBT_FTOS_T_H + +struct ebt_ftos_t_info +{ + int ftos_set; + unsigned char ftos; + // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN + int target; +}; +#define EBT_FTOS_TARGET "ftos" + +#define FTOS_TARGET 0x01 +#define FTOS_SETFTOS 0x02 +#define FTOS_WMMFTOS 0x04 +#define FTOS_8021QFTOS 0x08 + +#define DSCP_MASK_SHIFT 5 +#define PRIO_LOC_NFMARK 16 +#define PRIO_LOC_NFMASK 7 + +#endif +#endif diff -urN linux-4.1.52-orig/include/uapi/linux/netfilter_bridge/ebt_ip6_extend.h linux-4.1.52/include/uapi/linux/netfilter_bridge/ebt_ip6_extend.h --- linux-4.1.52-orig/include/uapi/linux/netfilter_bridge/ebt_ip6_extend.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/netfilter_bridge/ebt_ip6_extend.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,32 @@ +/* + * ebt_ip6 + * + * Authors: + * Kuo-Lang Tseng + * Manohar Castelino + * + * Jan 11, 2008 + * + * Extend by Broadcom at Jan 31, 2019 + */ +#ifndef __LINUX_BRIDGE_EBT_IP6_EXTEND_H +#define __LINUX_BRIDGE_EBT_IP6_EXTEND_H + +#include + +#define EBT_IP6_TCLASS_EXTEND 0x01 +#define EBT_IP6_FLOWLABEL_EXTEND 0x02 + +#define EBT_IP6_MASK_EXTEND (EBT_IP6_TCLASS_EXTEND | EBT_IP6_FLOWLABEL_EXTEND) +#define EBT_IP6_MATCH_EXTEND "ip6-extend" + +/* the same values are used for the invflags */ +struct ebt_ip6_extend_info { + __u8 flow_lbl[3]; + __u8 tclass[2]; + __u8 tclassmsk; + __u8 bitmask; + __u8 invflags; +}; + +#endif diff -urN linux-4.1.52-orig/include/uapi/linux/netfilter_bridge/ebt_ip_extend.h linux-4.1.52/include/uapi/linux/netfilter_bridge/ebt_ip_extend.h --- linux-4.1.52-orig/include/uapi/linux/netfilter_bridge/ebt_ip_extend.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/netfilter_bridge/ebt_ip_extend.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,35 @@ +/* + * ebt_ip + * + * Authors: + * Bart De Schuymer + * + * April, 2002 + * + * Changes: + * added ip-sport and ip-dport + * Innominate Security Technologies AG + * September, 2002 + * + * Extend by Broadcom at Jan 31, 2019 + */ +#ifndef __LINUX_BRIDGE_EBT_IP_EXTEND_H +#define __LINUX_BRIDGE_EBT_IP_EXTEND_H + +#include + + +#define EBT_IP_TOS_EXTEND 0x01 + +#define EBT_IP_MASK_EXTEND (EBT_IP_TOS_EXTEND) +#define EBT_IP_MATCH_EXTEND "ip-extend" + +/* the same values are used for the invflags */ +struct ebt_ip_extend_info { + __u8 tos[2]; + __u8 tosmask; + __u8 bitmask; + __u8 invflags; +}; + +#endif diff -urN linux-4.1.52-orig/include/uapi/linux/netfilter_bridge/ebt_ip.h linux-4.1.52/include/uapi/linux/netfilter_bridge/ebt_ip.h --- linux-4.1.52-orig/include/uapi/linux/netfilter_bridge/ebt_ip.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/netfilter_bridge/ebt_ip.h 2020-02-07 01:47:54.000000000 +0800 @@ -23,8 +23,14 @@ #define EBT_IP_PROTO 0x08 #define EBT_IP_SPORT 0x10 #define EBT_IP_DPORT 0x20 +#if defined(CONFIG_BCM_KF_NETFILTER) || !defined(CONFIG_BCM_IN_KERNEL) +#define EBT_IP_DSCP 0x40 +#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO |\ + EBT_IP_SPORT | EBT_IP_DPORT | EBT_IP_DSCP ) +#else #define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO |\ EBT_IP_SPORT | EBT_IP_DPORT ) +#endif #define EBT_IP_MATCH "ip" /* the same values are used for the invflags */ @@ -34,6 +40,9 @@ __be32 smsk; __be32 dmsk; __u8 tos; +#if defined(CONFIG_BCM_KF_NETFILTER) || !defined(CONFIG_BCM_IN_KERNEL) + __u8 dscp; +#endif __u8 protocol; __u8 bitmask; __u8 invflags; diff -urN linux-4.1.52-orig/include/uapi/linux/netfilter_bridge/ebt_mark_t.h linux-4.1.52/include/uapi/linux/netfilter_bridge/ebt_mark_t.h --- linux-4.1.52-orig/include/uapi/linux/netfilter_bridge/ebt_mark_t.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/netfilter_bridge/ebt_mark_t.h 2020-02-07 01:47:54.000000000 +0800 @@ -12,6 +12,10 @@ #define MARK_OR_VALUE (0xffffffe0) #define MARK_AND_VALUE (0xffffffd0) #define MARK_XOR_VALUE (0xffffffc0) +#if defined(CONFIG_BCM_KF_NETFILTER) +#define VTAG_SET_VALUE (0xffffffb0) +#endif + struct ebt_mark_t_info { unsigned long mark; diff -urN linux-4.1.52-orig/include/uapi/linux/netfilter_bridge/ebt_qos_map.h linux-4.1.52/include/uapi/linux/netfilter_bridge/ebt_qos_map.h --- linux-4.1.52-orig/include/uapi/linux/netfilter_bridge/ebt_qos_map.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/netfilter_bridge/ebt_qos_map.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,11 @@ +#if defined(CONFIG_BCM_KF_NETFILTER) +#ifndef __LINUX_QOS_MAP_H +#define __LINUX_QOS_MAP_H + +struct ebt_qos_map_info +{ + int dscp2pbit; + int dscp2q; +}; +#endif +#endif diff -urN linux-4.1.52-orig/include/uapi/linux/netfilter_bridge/ebt_reject.h linux-4.1.52/include/uapi/linux/netfilter_bridge/ebt_reject.h --- linux-4.1.52-orig/include/uapi/linux/netfilter_bridge/ebt_reject.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/netfilter_bridge/ebt_reject.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,14 @@ +#if defined(CONFIG_BCM_KF_NETFILTER) +#ifndef __LINUX_BRIDGE_EBT_REJECT_H +#define __LINUX_BRIDGE_EBT_REJECT_H + +enum ebt_reject_with { + EBT_ICMP6_POLICY_FAIL +}; + +struct ebt_reject_info { + int with; /* reject type */ +}; + +#endif +#endif diff -urN linux-4.1.52-orig/include/uapi/linux/netfilter_bridge/ebt_skbvlan_m.h linux-4.1.52/include/uapi/linux/netfilter_bridge/ebt_skbvlan_m.h --- linux-4.1.52-orig/include/uapi/linux/netfilter_bridge/ebt_skbvlan_m.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/netfilter_bridge/ebt_skbvlan_m.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,45 @@ +#ifndef __LINUX_BRIDGE_EBT_SKBVLAN_H +#define __LINUX_BRIDGE_EBT_SKBVLAN_H + +#include + +#define EBT_SKBVLAN_ID 0x0001 +#define EBT_SKBVLAN_PRIO 0x0002 +#define EBT_SKBVLAN_ENCAP 0x0004 +#define EBT_SKBVLAN_VLAN_TAG_0 0x0008 +#define EBT_SKBVLAN_VLAN_TAG_1 0x0010 +#define EBT_SKBVLAN_VLAN_TAG_2 0x0020 +#define EBT_SKBVLAN_VLAN_TAG_3 0x0040 +#define EBT_SKBVLAN_VLAN_TPID_0 0x0080 +#define EBT_SKBVLAN_VLAN_TPID_1 0x0100 +#define EBT_SKBVLAN_MASK (EBT_SKBVLAN_ID | EBT_SKBVLAN_PRIO | EBT_SKBVLAN_ENCAP | \ + EBT_SKBVLAN_VLAN_TAG_0 | EBT_SKBVLAN_VLAN_TAG_1 | EBT_SKBVLAN_VLAN_TAG_2 | \ + EBT_SKBVLAN_VLAN_TAG_3 |EBT_SKBVLAN_VLAN_TPID_0 | EBT_SKBVLAN_VLAN_TPID_1) + + + +#define EBT_SKBVLAN_MATCH "skbvlan" + +struct ebt_skbvlan_m_info { + __u16 id; /* VLAN ID {1-4095} */ + __u8 prio; /* VLAN User Priority {0-7} */ + __be16 encap; /* VLAN Encapsulated frame code {0-65535} */ + __u16 bitmask; /* Args bitmask bit 1=1 - ID arg, + bit 2=1 User-Priority arg, bit 3=1 encap*/ + __u16 invflags; /* Inverse bitmask bit 1=1 - inversed ID arg, + bit 2=1 - inversed Pirority arg */ + __be32 vlantag0[2]; + __be32 vlanmask0; + __be32 vlantag1[2]; + __be32 vlanmask1; + __be32 vlantag2[2]; + __be32 vlanmask2; + __be32 vlantag3[2]; + __be32 vlanmask3; + __be16 vlantpid0; + __be16 vlantpid1; +}; + + +#endif /* __LINUX_BRIDGE_EBT_SKBVLAN_H */ + diff -urN linux-4.1.52-orig/include/uapi/linux/netfilter_bridge/ebt_time.h linux-4.1.52/include/uapi/linux/netfilter_bridge/ebt_time.h --- linux-4.1.52-orig/include/uapi/linux/netfilter_bridge/ebt_time.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/netfilter_bridge/ebt_time.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,16 @@ +#if defined(CONFIG_BCM_KF_NETFILTER) +#ifndef __LINUX_BRIDGE_EBT_TIME_H +#define __LINUX_BRIDGE_EBT_TIME_H + + +struct ebt_time_info { + u_int8_t days_match; /* 1 bit per day. -SMTWTFS */ + u_int16_t time_start; /* 0 < time_start < 23*60+59 = 1439 */ + u_int16_t time_stop; /* 0:0 < time_stat < 23:59 */ + u_int8_t kerneltime; /* ignore skb time (and use kerneltime) or not. */ +}; + +#define EBT_TIME_MATCH "time" + +#endif /* __LINUX_BRIDGE_EBT_TIME_H */ +#endif diff -urN linux-4.1.52-orig/include/uapi/linux/netfilter_bridge/ebt_u32.h linux-4.1.52/include/uapi/linux/netfilter_bridge/ebt_u32.h --- linux-4.1.52-orig/include/uapi/linux/netfilter_bridge/ebt_u32.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/netfilter_bridge/ebt_u32.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,55 @@ +/* + * ebt_u32 + * + * Authors: + * extend by Broadcom at Jan 24, 2019 + * + * + */ + +#ifndef __LINUX_BRIDGE_EBT_U32_H +#define __LINUX_BRIDGE_EBT_U32_H + +#include + +enum ebt_u32_ops { + EBT_U32_AND, + EBT_U32_LEFTSH, + EBT_U32_RIGHTSH, + EBT_U32_AT, +}; + +struct ebt_u32_location_element { + __u32 number; + __u8 nextop; +}; + +struct ebt_u32_value_element { + __u32 min; + __u32 max; +}; + +/* + * Any way to allow for an arbitrary number of elements? + * For now, I settle with a limit of 10 each. + */ +#define EBT_U32_MAXSIZE 10 + +#define EBT_U32_MATCH "u32" + + +struct ebt_u32_test { + struct ebt_u32_location_element location[EBT_U32_MAXSIZE+1]; + struct ebt_u32_value_element value[EBT_U32_MAXSIZE+1]; + __u8 nnums; + __u8 nvalues; +}; + +struct ebt_u32_info { + struct ebt_u32_test tests[EBT_U32_MAXSIZE+1]; + __u8 ntests; + __u8 invert; +}; + +#endif + diff -urN linux-4.1.52-orig/include/uapi/linux/netfilter_bridge/ebt_wmm_mark_t.h linux-4.1.52/include/uapi/linux/netfilter_bridge/ebt_wmm_mark_t.h --- linux-4.1.52-orig/include/uapi/linux/netfilter_bridge/ebt_wmm_mark_t.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/netfilter_bridge/ebt_wmm_mark_t.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,29 @@ +#if defined(CONFIG_BCM_KF_NETFILTER) +#ifndef __LINUX_BRIDGE_EBT_MARK_T_H +#define __LINUX_BRIDGE_EBT_MARK_T_H + +#define WMM_MARK_DSCP 1 +#define WMM_MARK_8021D 2 + +#define WMM_MARK_DSCP_STR "dscp" +#define WMM_MARK_8021D_STR "vlan" + +#define PRIO_LOC_NFMARK 16 +#define PRIO_LOC_NFMASK 7 + +#define WMM_DSCP_MASK_SHIFT 5 +#define WMM_MARK_VALUE_NONE -1 + + +struct ebt_wmm_mark_t_info +{ + int mark; + int markpos; + int markset; + /* EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN */ + int target; +}; +#define EBT_WMM_MARK_TARGET "wmm-mark" + +#endif +#endif diff -urN linux-4.1.52-orig/include/uapi/linux/netlink.h linux-4.1.52/include/uapi/linux/netlink.h --- linux-4.1.52-orig/include/uapi/linux/netlink.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/netlink.h 2020-02-07 01:47:54.000000000 +0800 @@ -1,6 +1,12 @@ #ifndef _UAPI__LINUX_NETLINK_H #define _UAPI__LINUX_NETLINK_H +#if !defined(CONFIG_BCM_IN_KERNEL) +/* user app includes this file directly.To avoid double inclusion of this file from the toolchain + version, define the same header check definition as the one from toolchain */ +#define __LINUX_NETLINK_H +#endif + #include #include /* for __kernel_sa_family_t */ #include @@ -30,6 +36,25 @@ #define NETLINK_INET_DIAG NETLINK_SOCK_DIAG +#if defined(CONFIG_BCM_KF_NETFILTER) || !defined(CONFIG_BCM_IN_KERNEL) +#define NETLINK_BRCM_MONITOR 25 /*send events to userspace monitor task(broadcom specific)*/ +#define NETLINK_BRCM_EPON 26 +#endif + +#if defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_MODULE) +#define NETLINK_DPI 27 /* dpicore driver */ +#define NETLINK_DPI_QOS 28 /* DPI QoS */ +#endif + +#if ((defined(CONFIG_BCM_MCAST) || defined(CONFIG_BCM_MCAST_MODULE)) && defined(CONFIG_BCM_KF_MCAST)) || !defined(CONFIG_BCM_IN_KERNEL) +#define NETLINK_BCM_MCAST 30 /* for multicast */ +#endif + +#if defined(CONFIG_BCM_KF_WL) || !defined(CONFIG_BCM_IN_KERNEL) +#define NETLINK_IGSC 29 /* for multicast igs sdb listing */ +#define NETLINK_WLCSM 31 /* for brcm wireless cfg[nvram]/statics/management extention */ +#endif + #define MAX_LINKS 32 struct sockaddr_nl { diff -urN linux-4.1.52-orig/include/uapi/linux/nl80211.h linux-4.1.52/include/uapi/linux/nl80211.h --- linux-4.1.52-orig/include/uapi/linux/nl80211.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/nl80211.h 2020-02-07 01:47:54.000000000 +0800 @@ -997,6 +997,29 @@ NL80211_CMD_WIPHY_REG_CHANGE, +#ifdef CONFIG_BCM_KF_CFG80211_BACKPORT + NL80211_CMD_ABORT_SCAN, + + NL80211_CMD_START_NAN, + NL80211_CMD_STOP_NAN, + NL80211_CMD_ADD_NAN_FUNCTION, + NL80211_CMD_DEL_NAN_FUNCTION, + NL80211_CMD_CHANGE_NAN_CONFIG, + NL80211_CMD_NAN_MATCH, + + NL80211_CMD_SET_MULTICAST_TO_UNICAST, + + NL80211_CMD_UPDATE_CONNECT_PARAMS, + + NL80211_CMD_SET_PMK, + NL80211_CMD_DEL_PMK, + + NL80211_CMD_PORT_AUTHORIZED, + + NL80211_CMD_RELOAD_REGDB, + + NL80211_CMD_EXTERNAL_AUTH, +#endif /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -2129,7 +2152,67 @@ NL80211_ATTR_SCHED_SCAN_DELAY, NL80211_ATTR_REG_INDOOR, +#ifdef CONFIG_BCM_KF_CFG80211_BACKPORT + NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS, + NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL, + NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS, + NL80211_ATTR_SCHED_SCAN_PLANS, + + NL80211_ATTR_PBSS, + + NL80211_ATTR_BSS_SELECT, + + NL80211_ATTR_STA_SUPPORT_P2P_PS, + + NL80211_ATTR_PAD, + + NL80211_ATTR_IFTYPE_EXT_CAPA, + + NL80211_ATTR_MU_MIMO_GROUP_DATA, + NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR, + + NL80211_ATTR_SCAN_START_TIME_TSF, + NL80211_ATTR_SCAN_START_TIME_TSF_BSSID, + NL80211_ATTR_MEASUREMENT_DURATION, + NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY, + NL80211_ATTR_MESH_PEER_AID, + + NL80211_ATTR_NAN_MASTER_PREF, + NL80211_ATTR_BANDS, + NL80211_ATTR_NAN_FUNC, + NL80211_ATTR_NAN_MATCH, + + NL80211_ATTR_FILS_KEK, + NL80211_ATTR_FILS_NONCES, + + NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED, + + NL80211_ATTR_BSSID, + + NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI, + NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST, + + NL80211_ATTR_TIMEOUT_REASON, + + NL80211_ATTR_FILS_ERP_USERNAME, + NL80211_ATTR_FILS_ERP_REALM, + NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM, + NL80211_ATTR_FILS_ERP_RRK, + NL80211_ATTR_FILS_CACHE_ID, + + NL80211_ATTR_PMK, + + NL80211_ATTR_SCHED_SCAN_MULTI, + NL80211_ATTR_SCHED_SCAN_MAX_REQS, + + NL80211_ATTR_WANT_1X_4WAY_HS, + NL80211_ATTR_PMKR0_NAME, + NL80211_ATTR_PORT_AUTHORIZED, + + NL80211_ATTR_EXTERNAL_AUTH_ACTION, + NL80211_ATTR_EXTERNAL_AUTH_SUPPORT, +#endif /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -3564,11 +3647,17 @@ * @NL80211_BAND_2GHZ: 2.4 GHz ISM band * @NL80211_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz) * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 64.80 GHz) +#ifdef CONFIG_BCM_KF_CFG80211_BACKPORT + * @NL80211_BAND_6GHZ: around 6 GHz band (5.9 - 7.1 GHz) +#endif */ enum nl80211_band { NL80211_BAND_2GHZ, NL80211_BAND_5GHZ, NL80211_BAND_60GHZ, +#ifdef CONFIG_BCM_KF_NL80211_6G_BAND_SUPPORT + NL80211_BAND_6GHZ, +#endif /* CONFIG_BCM_KF_NL80211_6G_BAND_SUPPORT */ }; /** @@ -4360,6 +4449,42 @@ */ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_VHT_IBSS, +#ifdef CONFIG_BCM_KF_CFG80211_BACKPORT + NL80211_EXT_FEATURE_RRM, + NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER, + NL80211_EXT_FEATURE_SCAN_START_TIME, + NL80211_EXT_FEATURE_BSS_PARENT_TSF, + NL80211_EXT_FEATURE_SET_SCAN_DWELL, + NL80211_EXT_FEATURE_BEACON_RATE_LEGACY, + NL80211_EXT_FEATURE_BEACON_RATE_HT, + NL80211_EXT_FEATURE_BEACON_RATE_VHT, + NL80211_EXT_FEATURE_FILS_STA, + NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA, + NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED, + NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI, + NL80211_EXT_FEATURE_CQM_RSSI_LIST, + NL80211_EXT_FEATURE_FILS_SK_OFFLOAD, + NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK, + NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X, + NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME, + NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP, + NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE, + NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION, + NL80211_EXT_FEATURE_MFP_OPTIONAL, + NL80211_EXT_FEATURE_LOW_SPAN_SCAN, + NL80211_EXT_FEATURE_LOW_POWER_SCAN, + NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN, + NL80211_EXT_FEATURE_DFS_OFFLOAD, + NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211, + NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT, + /* we renamed this - stay compatible */ + NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT = NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT, + NL80211_EXT_FEATURE_TXQS, + NL80211_EXT_FEATURE_SCAN_RANDOM_SN, + NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT, + NL80211_EXT_FEATURE_CAN_REPLACE_PTK0, + NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER, +#endif /* CONFIG_BCM_KF_CFG80211_BACKPORT */ /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, @@ -4584,5 +4709,17 @@ NL80211_TDLS_PEER_VHT = 1<<1, NL80211_TDLS_PEER_WMM = 1<<2, }; +#ifdef CONFIG_BCM_KF_CFG80211_BACKPORT +/** + * nl80211_external_auth_action - Action to perform with external + * authentication request. Used by NL80211_ATTR_EXTERNAL_AUTH_ACTION. + * @NL80211_EXTERNAL_AUTH_START: Start the authentication. + * @NL80211_EXTERNAL_AUTH_ABORT: Abort the ongoing authentication. + */ +enum nl80211_external_auth_action { + NL80211_EXTERNAL_AUTH_START, + NL80211_EXTERNAL_AUTH_ABORT, +}; +#endif #endif /* __LINUX_NL80211_H */ diff -urN linux-4.1.52-orig/include/uapi/linux/phonet.h linux-4.1.52/include/uapi/linux/phonet.h --- linux-4.1.52-orig/include/uapi/linux/phonet.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/phonet.h 2020-02-07 01:47:54.000000000 +0800 @@ -26,6 +26,15 @@ #include #include +#ifdef CONFIG_BCM_KF_PHONET +/* Phonet media types */ +#define PN_MEDIA_ROUTING 0x00 +#define PN_MEDIA_USB 0x1B +#define PN_MEDIA_DEFAULT 0x25 +#define PN_MEDIA_MODEM_HOST_IF 0x26 +#define PN_MEDIA_AUX_HOST_HOST_IF 0x27 +#endif /* CONFIG_BCM_KF_PHONET */ + /* Automatic protocol selection */ #define PN_PROTO_TRANSPORT 0 /* Phonet datagram socket */ @@ -44,12 +53,21 @@ #define PNADDR_BROADCAST 0xFC #define PNPORT_RESOURCE_ROUTING 0 +#ifdef CONFIG_BCM_KF_PHONET +/* define object for multicast */ +#define PNOBJECT_MULTICAST 0x20 +#endif /* CONFIG_BCM_KF_PHONET */ + /* Values for PNPIPE_ENCAP option */ #define PNPIPE_ENCAP_NONE 0 #define PNPIPE_ENCAP_IP 1 /* ioctls */ #define SIOCPNGETOBJECT (SIOCPROTOPRIVATE + 0) +#ifdef CONFIG_BCM_KF_PHONET +#define SIOCCONFIGTYPE (SIOCPROTOPRIVATE + 1) +#define SIOCCONFIGSUBTYPE (SIOCPROTOPRIVATE + 2) +#endif #define SIOCPNENABLEPIPE (SIOCPROTOPRIVATE + 13) #define SIOCPNADDRESOURCE (SIOCPROTOPRIVATE + 14) #define SIOCPNDELRESOURCE (SIOCPROTOPRIVATE + 15) diff -urN linux-4.1.52-orig/include/uapi/linux/ppp-ioctl.h linux-4.1.52/include/uapi/linux/ppp-ioctl.h --- linux-4.1.52-orig/include/uapi/linux/ppp-ioctl.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/ppp-ioctl.h 2020-02-07 01:47:54.000000000 +0800 @@ -78,6 +78,11 @@ __aligned_u64 rx_errors; }; +#if defined(CONFIG_BCM_KF_PPP) +/* PPP device name type */ +typedef char ppp_real_dev_name[IFNAMSIZ]; +#endif + /* * Ioctl definitions. */ @@ -112,6 +117,9 @@ #define PPPIOCATTCHAN _IOW('t', 56, int) /* attach to ppp channel */ #define PPPIOCGCHAN _IOR('t', 55, int) /* get ppp channel number */ #define PPPIOCGL2TPSTATS _IOR('t', 54, struct pppol2tp_ioc_stats) +#if defined(CONFIG_BCM_KF_PPP) +#define PPPIOCSREALDEV _IOW('t', 53, ppp_real_dev_name) /* set real device name */ +#endif #define SIOCGPPPSTATS (SIOCDEVPRIVATE + 0) #define SIOCGPPPVER (SIOCDEVPRIVATE + 1) /* NEVER change this!! */ diff -urN linux-4.1.52-orig/include/uapi/linux/psci.h linux-4.1.52/include/uapi/linux/psci.h --- linux-4.1.52-orig/include/uapi/linux/psci.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/psci.h 2020-02-07 01:47:54.000000000 +0800 @@ -39,6 +39,9 @@ #define PSCI_0_2_FN_MIGRATE_INFO_UP_CPU PSCI_0_2_FN(7) #define PSCI_0_2_FN_SYSTEM_OFF PSCI_0_2_FN(8) #define PSCI_0_2_FN_SYSTEM_RESET PSCI_0_2_FN(9) +#if defined(CONFIG_BCM_KF_OPTEE) && defined (CONFIG_BCM_KERNEL_CODE_PROTECTION) +#define PSCI_0_2_FN_MEM_INFO PSCI_0_2_FN(18) +#endif #define PSCI_0_2_FN64_CPU_SUSPEND PSCI_0_2_FN64(1) #define PSCI_0_2_FN64_CPU_ON PSCI_0_2_FN64(3) diff -urN linux-4.1.52-orig/include/uapi/linux/snmp.h linux-4.1.52/include/uapi/linux/snmp.h --- linux-4.1.52-orig/include/uapi/linux/snmp.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/snmp.h 2020-02-07 01:47:54.000000000 +0800 @@ -276,6 +276,10 @@ LINUX_MIB_TCPACKSKIPPEDFINWAIT2, /* TCPACKSkippedFinWait2 */ LINUX_MIB_TCPACKSKIPPEDTIMEWAIT, /* TCPACKSkippedTimeWait */ LINUX_MIB_TCPACKSKIPPEDCHALLENGE, /* TCPACKSkippedChallenge */ +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +/*CVE-2019-11478*/ + LINUX_MIB_TCPWQUEUETOOBIG, /* TCPWqueueTooBig */ +#endif __LINUX_MIB_MAX }; diff -urN linux-4.1.52-orig/include/uapi/linux/sockios.h linux-4.1.52/include/uapi/linux/sockios.h --- linux-4.1.52-orig/include/uapi/linux/sockios.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/sockios.h 2020-02-07 01:47:54.000000000 +0800 @@ -128,6 +128,18 @@ #define SIOCSHWTSTAMP 0x89b0 /* set and get config */ #define SIOCGHWTSTAMP 0x89b1 /* get config */ +#if !defined(CONFIG_BCM_IN_KERNEL) || defined(CONFIG_BCM_KF_MISC_IOCTLS) || defined(CONFIG_BCM_KF_WANDEV) +/***********************BRCM global ioctl calls*****************************/ +#define SIOC_BRCM_GLOBAL_BASE 0x89c0 +#endif +#if !defined(CONFIG_BCM_IN_KERNEL) || defined(CONFIG_BCM_KF_MISC_IOCTLS) +#define SIOCGIFTRANSSTART (SIOC_BRCM_GLOBAL_BASE+0) /* Used by SNMP */ +#define SIOCCIFSTATS (SIOC_BRCM_GLOBAL_BASE+1) /* Clear stats of a device */ +#endif +#if !defined(CONFIG_BCM_IN_KERNEL) || defined(CONFIG_BCM_KF_WANDEV) +#define SIOCDEVISWANDEV (SIOC_BRCM_GLOBAL_BASE+2) +#endif + /* Device private ioctl calls */ /* diff -urN linux-4.1.52-orig/include/uapi/linux/sysctl.h linux-4.1.52/include/uapi/linux/sysctl.h --- linux-4.1.52-orig/include/uapi/linux/sysctl.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/sysctl.h 2020-02-07 01:47:54.000000000 +0800 @@ -154,6 +154,9 @@ KERN_NMI_WATCHDOG=75, /* int: enable/disable nmi watchdog */ KERN_PANIC_ON_NMI=76, /* int: whether we will panic on an unrecovered */ KERN_PANIC_ON_WARN=77, /* int: call panic() in WARN() functions */ +#if defined(CONFIG_BCM_KF_PRINTK_INT_ENABLED) && defined(CONFIG_BCM_PRINTK_INT_ENABLED) + KERN_PRINTK_WITH_INTERRUPTS_ENABLED=78, /* int: print with interrupts enabled */ +#endif }; diff -urN linux-4.1.52-orig/include/uapi/linux/tcp.h linux-4.1.52/include/uapi/linux/tcp.h --- linux-4.1.52-orig/include/uapi/linux/tcp.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/tcp.h 2020-02-07 01:47:54.000000000 +0800 @@ -54,7 +54,11 @@ __be16 window; __sum16 check; __be16 urg_ptr; +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) +} LINUX_NET_PACKED; +#else }; +#endif /* * The union cast uses a gcc extension to avoid aliasing problems @@ -64,7 +68,11 @@ union tcp_word_hdr { struct tcphdr hdr; __be32 words[5]; -}; +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) +} LINUX_NET_PACKED; +#else +}; +#endif #define tcp_flag_word(tp) ( ((union tcp_word_hdr *)(tp))->words [3]) @@ -113,6 +121,13 @@ #define TCP_TIMESTAMP 24 #define TCP_NOTSENT_LOWAT 25 /* limit number of unsent bytes in write queue */ #define TCP_CC_INFO 26 /* Get Congestion Control (optional) info */ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#define MPTCP_ENABLED 42 +#endif + +#if defined(CONFIG_BCM_KF_SPEEDYGET) && defined(CONFIG_BCM_SPEEDYGET) +#define TCP_NOCOPY 27 /* Don't copy buffer to user space */ +#endif struct tcp_repair_opt { __u32 opt_code; diff -urN linux-4.1.52-orig/include/uapi/linux/tee.h linux-4.1.52/include/uapi/linux/tee.h --- linux-4.1.52-orig/include/uapi/linux/tee.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/tee.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,349 @@ +#if defined(CONFIG_BCM_KF_OPTEE_414_BACKPORTS) +/* + * Copyright (c) 2015-2016, Linaro Limited + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __TEE_H +#define __TEE_H + +#include +#include + +/* + * This file describes the API provided by a TEE driver to user space. + * + * Each TEE driver defines a TEE specific protocol which is used for the + * data passed back and forth using TEE_IOC_CMD. + */ + +/* Helpers to make the ioctl defines */ +#define TEE_IOC_MAGIC 0xa4 +#define TEE_IOC_BASE 0 + +/* Flags relating to shared memory */ +#define TEE_IOCTL_SHM_MAPPED 0x1 /* memory mapped in normal world */ +#define TEE_IOCTL_SHM_DMA_BUF 0x2 /* dma-buf handle on shared memory */ + +#define TEE_MAX_ARG_SIZE 1024 + +#define TEE_GEN_CAP_GP (1 << 0)/* GlobalPlatform compliant TEE */ +#define TEE_GEN_CAP_PRIVILEGED (1 << 1)/* Privileged device (for supplicant) */ + +/* + * TEE Implementation ID + */ +#define TEE_IMPL_ID_OPTEE 1 + +/* + * OP-TEE specific capabilities + */ +#define TEE_OPTEE_CAP_TZ (1 << 0) + +/** + * struct tee_ioctl_version_data - TEE version + * @impl_id: [out] TEE implementation id + * @impl_caps: [out] Implementation specific capabilities + * @gen_caps: [out] Generic capabilities, defined by TEE_GEN_CAPS_* above + * + * Identifies the TEE implementation, @impl_id is one of TEE_IMPL_ID_* above. + * @impl_caps is implementation specific, for example TEE_OPTEE_CAP_* + * is valid when @impl_id == TEE_IMPL_ID_OPTEE. + */ +struct tee_ioctl_version_data { + __u32 impl_id; + __u32 impl_caps; + __u32 gen_caps; +}; + +/** + * TEE_IOC_VERSION - query version of TEE + * + * Takes a tee_ioctl_version_data struct and returns with the TEE version + * data filled in. + */ +#define TEE_IOC_VERSION _IOR(TEE_IOC_MAGIC, TEE_IOC_BASE + 0, \ + struct tee_ioctl_version_data) + +/** + * struct tee_ioctl_shm_alloc_data - Shared memory allocate argument + * @size: [in/out] Size of shared memory to allocate + * @flags: [in/out] Flags to/from allocation. + * @id: [out] Identifier of the shared memory + * + * The flags field should currently be zero as input. Updated by the call + * with actual flags as defined by TEE_IOCTL_SHM_* above. + * This structure is used as argument for TEE_IOC_SHM_ALLOC below. + */ +struct tee_ioctl_shm_alloc_data { + __u64 size; + __u32 flags; + __s32 id; +}; + +/** + * TEE_IOC_SHM_ALLOC - allocate shared memory + * + * Allocates shared memory between the user space process and secure OS. + * + * Returns a file descriptor on success or < 0 on failure + * + * The returned file descriptor is used to map the shared memory into user + * space. The shared memory is freed when the descriptor is closed and the + * memory is unmapped. + */ +#define TEE_IOC_SHM_ALLOC _IOWR(TEE_IOC_MAGIC, TEE_IOC_BASE + 1, \ + struct tee_ioctl_shm_alloc_data) + +/** + * struct tee_ioctl_buf_data - Variable sized buffer + * @buf_ptr: [in] A __user pointer to a buffer + * @buf_len: [in] Length of the buffer above + * + * Used as argument for TEE_IOC_OPEN_SESSION, TEE_IOC_INVOKE, + * TEE_IOC_SUPPL_RECV, and TEE_IOC_SUPPL_SEND below. + */ +struct tee_ioctl_buf_data { + __u64 buf_ptr; + __u64 buf_len; +}; + +/* + * Attributes for struct tee_ioctl_param, selects field in the union + */ +#define TEE_IOCTL_PARAM_ATTR_TYPE_NONE 0 /* parameter not used */ + +/* + * These defines value parameters (struct tee_ioctl_param_value) + */ +#define TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT 1 +#define TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT 2 +#define TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT 3 /* input and output */ + +/* + * These defines shared memory reference parameters (struct + * tee_ioctl_param_memref) + */ +#define TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT 5 +#define TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT 6 +#define TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT 7 /* input and output */ + +/* + * Mask for the type part of the attribute, leaves room for more types + */ +#define TEE_IOCTL_PARAM_ATTR_TYPE_MASK 0xff + +/* + * Matches TEEC_LOGIN_* in GP TEE Client API + * Are only defined for GP compliant TEEs + */ +#define TEE_IOCTL_LOGIN_PUBLIC 0 +#define TEE_IOCTL_LOGIN_USER 1 +#define TEE_IOCTL_LOGIN_GROUP 2 +#define TEE_IOCTL_LOGIN_APPLICATION 4 +#define TEE_IOCTL_LOGIN_USER_APPLICATION 5 +#define TEE_IOCTL_LOGIN_GROUP_APPLICATION 6 + +/** + * struct tee_ioctl_param - parameter + * @attr: attributes + * @a: if a memref, offset into the shared memory object, else a value parameter + * @b: if a memref, size of the buffer, else a value parameter + * @c: if a memref, shared memory identifier, else a value parameter + * + * @attr & TEE_PARAM_ATTR_TYPE_MASK indicates if memref or value is used in + * the union. TEE_PARAM_ATTR_TYPE_VALUE_* indicates value and + * TEE_PARAM_ATTR_TYPE_MEMREF_* indicates memref. TEE_PARAM_ATTR_TYPE_NONE + * indicates that none of the members are used. + * + * Shared memory is allocated with TEE_IOC_SHM_ALLOC which returns an + * identifier representing the shared memory object. A memref can reference + * a part of a shared memory by specifying an offset (@a) and size (@b) of + * the object. To supply the entire shared memory object set the offset + * (@a) to 0 and size (@b) to the previously returned size of the object. + */ +struct tee_ioctl_param { + __u64 attr; + __u64 a; + __u64 b; + __u64 c; +}; + +#define TEE_IOCTL_UUID_LEN 16 + +/** + * struct tee_ioctl_open_session_arg - Open session argument + * @uuid: [in] UUID of the Trusted Application + * @clnt_uuid: [in] UUID of client + * @clnt_login: [in] Login class of client, TEE_IOCTL_LOGIN_* above + * @cancel_id: [in] Cancellation id, a unique value to identify this request + * @session: [out] Session id + * @ret: [out] return value + * @ret_origin [out] origin of the return value + * @num_params [in] number of parameters following this struct + */ +struct tee_ioctl_open_session_arg { + __u8 uuid[TEE_IOCTL_UUID_LEN]; + __u8 clnt_uuid[TEE_IOCTL_UUID_LEN]; + __u32 clnt_login; + __u32 cancel_id; + __u32 session; + __u32 ret; + __u32 ret_origin; + __u32 num_params; + /* num_params tells the actual number of element in params */ + struct tee_ioctl_param params[]; +}; + +/** + * TEE_IOC_OPEN_SESSION - opens a session to a Trusted Application + * + * Takes a struct tee_ioctl_buf_data which contains a struct + * tee_ioctl_open_session_arg followed by any array of struct + * tee_ioctl_param + */ +#define TEE_IOC_OPEN_SESSION _IOR(TEE_IOC_MAGIC, TEE_IOC_BASE + 2, \ + struct tee_ioctl_buf_data) + +/** + * struct tee_ioctl_invoke_func_arg - Invokes a function in a Trusted + * Application + * @func: [in] Trusted Application function, specific to the TA + * @session: [in] Session id + * @cancel_id: [in] Cancellation id, a unique value to identify this request + * @ret: [out] return value + * @ret_origin [out] origin of the return value + * @num_params [in] number of parameters following this struct + */ +struct tee_ioctl_invoke_arg { + __u32 func; + __u32 session; + __u32 cancel_id; + __u32 ret; + __u32 ret_origin; + __u32 num_params; + /* num_params tells the actual number of element in params */ + struct tee_ioctl_param params[]; +}; + +/** + * TEE_IOC_INVOKE - Invokes a function in a Trusted Application + * + * Takes a struct tee_ioctl_buf_data which contains a struct + * tee_invoke_func_arg followed by any array of struct tee_param + */ +#define TEE_IOC_INVOKE _IOR(TEE_IOC_MAGIC, TEE_IOC_BASE + 3, \ + struct tee_ioctl_buf_data) + +/** + * struct tee_ioctl_cancel_arg - Cancels an open session or invoke ioctl + * @cancel_id: [in] Cancellation id, a unique value to identify this request + * @session: [in] Session id, if the session is opened, else set to 0 + */ +struct tee_ioctl_cancel_arg { + __u32 cancel_id; + __u32 session; +}; + +/** + * TEE_IOC_CANCEL - Cancels an open session or invoke + */ +#define TEE_IOC_CANCEL _IOR(TEE_IOC_MAGIC, TEE_IOC_BASE + 4, \ + struct tee_ioctl_cancel_arg) + +/** + * struct tee_ioctl_close_session_arg - Closes an open session + * @session: [in] Session id + */ +struct tee_ioctl_close_session_arg { + __u32 session; +}; + +/** + * TEE_IOC_CLOSE_SESSION - Closes a session + */ +#define TEE_IOC_CLOSE_SESSION _IOR(TEE_IOC_MAGIC, TEE_IOC_BASE + 5, \ + struct tee_ioctl_close_session_arg) + +/** + * struct tee_iocl_supp_recv_arg - Receive a request for a supplicant function + * @func: [in] supplicant function + * @num_params [in/out] number of parameters following this struct + * + * @num_params is the number of params that tee-supplicant has room to + * receive when input, @num_params is the number of actual params + * tee-supplicant receives when output. + */ +struct tee_iocl_supp_recv_arg { + __u32 func; + __u32 num_params; + /* num_params tells the actual number of element in params */ + struct tee_ioctl_param params[]; +}; + +/** + * TEE_IOC_SUPPL_RECV - Receive a request for a supplicant function + * + * Takes a struct tee_ioctl_buf_data which contains a struct + * tee_iocl_supp_recv_arg followed by any array of struct tee_param + */ +#define TEE_IOC_SUPPL_RECV _IOR(TEE_IOC_MAGIC, TEE_IOC_BASE + 6, \ + struct tee_ioctl_buf_data) + +/** + * struct tee_iocl_supp_send_arg - Send a response to a received request + * @ret: [out] return value + * @num_params [in] number of parameters following this struct + */ +struct tee_iocl_supp_send_arg { + __u32 ret; + __u32 num_params; + /* num_params tells the actual number of element in params */ + struct tee_ioctl_param params[]; +}; + +/** + * TEE_IOC_SUPPL_SEND - Receive a request for a supplicant function + * + * Takes a struct tee_ioctl_buf_data which contains a struct + * tee_iocl_supp_send_arg followed by any array of struct tee_param + */ +#define TEE_IOC_SUPPL_SEND _IOR(TEE_IOC_MAGIC, TEE_IOC_BASE + 7, \ + struct tee_ioctl_buf_data) + +/* + * Five syscalls are used when communicating with the TEE driver. + * open(): opens the device associated with the driver + * ioctl(): as described above operating on the file descriptor from open() + * close(): two cases + * - closes the device file descriptor + * - closes a file descriptor connected to allocated shared memory + * mmap(): maps shared memory into user space using information from struct + * tee_ioctl_shm_alloc_data + * munmap(): unmaps previously shared memory + */ + +#endif /*__TEE_H*/ +#endif /* CONFIG_BCM_KF_OPTEE_414_BACKPORTS */ diff -urN linux-4.1.52-orig/include/uapi/linux/tty.h linux-4.1.52/include/uapi/linux/tty.h --- linux-4.1.52-orig/include/uapi/linux/tty.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/linux/tty.h 2020-02-07 01:47:54.000000000 +0800 @@ -34,5 +34,10 @@ #define N_TI_WL 22 /* for TI's WL BT, FM, GPS combo chips */ #define N_TRACESINK 23 /* Trace data routing for MIPI P1149.7 */ #define N_TRACEROUTER 24 /* Trace data routing for MIPI P1149.7 */ +#ifdef CONFIG_BCM_KF_PHONET +#define N_BRCM_HCI 25 /* Broadcom Bluetooth HCI */ +#define N_PHONET 26 /* PHONET over USB/ACM */ +#define N_LDTMODEM 27 /* Line discipline for Thin Modem support */ +#endif #endif /* _UAPI_LINUX_TTY_H */ diff -urN linux-4.1.52-orig/include/uapi/mtd/mtd-abi.h linux-4.1.52/include/uapi/mtd/mtd-abi.h --- linux-4.1.52-orig/include/uapi/mtd/mtd-abi.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/include/uapi/mtd/mtd-abi.h 2020-02-07 01:47:54.000000000 +0800 @@ -103,6 +103,9 @@ #define MTD_BIT_WRITEABLE 0x800 /* Single bits can be flipped */ #define MTD_NO_ERASE 0x1000 /* No erase necessary */ #define MTD_POWERUP_LOCK 0x2000 /* Always locked after reset */ +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +#define MTD_NAND_NOP1 0x10000000 /* SLC NAND that only supports single page write (NOP=1) */ +#endif /* Some common devices / combinations of capabilities */ #define MTD_CAP_ROM 0 diff -urN linux-4.1.52-orig/init/main.c linux-4.1.52/init/main.c --- linux-4.1.52-orig/init/main.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/init/main.c 2020-02-07 01:47:54.000000000 +0800 @@ -465,7 +465,7 @@ { } -# if THREAD_SIZE >= PAGE_SIZE +#if THREAD_SIZE >= PAGE_SIZE void __init __weak thread_info_cache_init(void) { } @@ -673,7 +673,6 @@ } ftrace_init(); - /* Do the rest non-__init'ed, we're now alive */ rest_init(); } diff -urN linux-4.1.52-orig/Kconfig linux-4.1.52/Kconfig --- linux-4.1.52-orig/Kconfig 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/Kconfig 2020-02-07 01:47:53.000000000 +0800 @@ -9,3 +9,16 @@ option env="SRCARCH" source "arch/$SRCARCH/Kconfig" + +#IGNORE_BCM_KF_EXCEPTION + +menu "Broadcom Specific Configurations" + +source "Kconfig.bcm_kf" +source "Kconfig.bcm" + +endmenu + +config BCM_IN_KERNEL + bool + default y diff -urN linux-4.1.52-orig/Kconfig.bcm linux-4.1.52/Kconfig.bcm --- linux-4.1.52-orig/Kconfig.bcm 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/Kconfig.bcm 2020-02-07 01:56:25.000000000 +0800 @@ -0,0 +1,1865 @@ +if (BCM_KF_MISC_MAKEFILE) + +config BRCM_IKOS + bool "IKOS" + +config BCM_CHIP_NUMBER + int "numerical value of chipid" + +config BRCM_CHIP_REV + hex "numerical value of chipid and revision" + +config BCM_KERNEL_CUSTOM + bool "Build kernel with Broadcom custom changes" + default y + help + This should always be selected for Broadcom + Internal builds + +config BCM_CA7_MPCORE + bool "ARM Cortex A7 MPCORE architecture support" + select CPU_V7 + select ARM_GIC + select ARM_L1_CACHE_SHIFT_6 + select HAVE_CLK + select CLKDEV_LOOKUP + select HAVE_ARM_ARCH_TIMER + select SPARSE_IRQ + help + Support for ARM A7 MPCORE subsystem + +config BCM_CA7_SMP + bool "Enable SMP on ARM Cortex A7 MPCORE" + select HAVE_SMP + select SMP + select SMP_ON_UP + select HOTPLUG_CPU + select HAVE_ARM_SCU + select BCM_CA7_MPCORE + help + Support for ARM A7 SMP + +choice + prompt "Broadcom Commengine board type" + default BCM963268 + depends on (BCM_KF_MIPS_BCM963XX || BCM_KF_ARM_BCM963XX) + help + Select different Broadcom ADSL board + +config BCM947189 + bool "947189" + depends on ARM + select EMBEDDED + select CPU_V7 + +config BCM96846 + bool "96846" + depends on ARM + depends on BCM_KF_ARM_BCM963XX + select MACH_BCM96846 + select ARCH_REQUIRE_GPIOLIB + +config BCM96878 + bool "96878" + depends on ARM + depends on BCM_KF_ARM_BCM963XX + select ARM_AMBA + select ARCH_MULTIPLATFORM + select EMBEDDED + select COMMON_CLK + select BCM_CA7_SMP + +config BCM947622 + bool "947622" + depends on ARM + depends on BCM_KF_ARM_BCM963XX + select ARM_AMBA + select ARCH_MULTIPLATFORM + select EMBEDDED + select COMMON_CLK + select BCM_CA7_SMP + +config BCM963178 + bool "963178" + depends on ARM + depends on BCM_KF_ARM_BCM963XX + select ARM_AMBA + select ARCH_MULTIPLATFORM + select EMBEDDED + select COMMON_CLK + select BCM_CA7_SMP + + +config BCM963148 + bool "63148" + depends on BCM_KF_ARM_BCM963XX + depends on ARM + select EMBEDDED + select MACH_BCM963148 + +config BCM963138 + bool "63138" + depends on BCM_KF_ARM_BCM963XX + depends on ARM + select EMBEDDED + select MACH_BCM963138 + +config BCM963158 + bool "63158" + depends on BCM_KF_ARM64_BCM963XX + depends on ARM64 + +config BCM960333 + bool "960333" + depends on BCM_KF_MIPS_BCM963XX + depends on MIPS_BCM963XX + select DMA_NONCOHERENT + +config BCM963268 + bool "963268 VDSL board" + depends on BCM_KF_MIPS_BCM963XX + depends on MIPS_BCM963XX + select DMA_NONCOHERENT + select SYS_SUPPORTS_SMP + select NR_CPUS_DEFAULT_2 + +config BCM96838 + bool "96838 GPON/EPON board" + depends on BCM_KF_MIPS_BCM963XX + depends on MIPS_BCM963XX + select DMA_NONCOHERENT + select SYS_SUPPORTS_SMP + select NR_CPUS_DEFAULT_2 + +config BCM963381 + bool "963381 VDSL board" + depends on BCM_KF_MIPS_BCM963XX + depends on MIPS_BCM963XX + select DMA_NONCOHERENT + select SYS_SUPPORTS_SMP + select NR_CPUS_DEFAULT_2 + +config BCM96848 + bool "96848 GPON/EPON board" + depends on BCM_KF_MIPS_BCM963XX + depends on MIPS_BCM963XX + select DMA_NONCOHERENT + select SYS_SUPPORTS_SMP + select NR_CPUS_DEFAULT_2 + +config BCM96858 + bool "6858 GPON/EPON" + depends on BCM_KF_ARM64_BCM963XX + depends on ARM64 + +config BCM94908 + bool "4908 Wireless Router board" + depends on BCM_KF_ARM64_BCM963XX + depends on ARM64 + +config BCM96856 + bool "6856 GPON/EPON" + depends on BCM_KF_ARM64_BCM963XX + depends on ARM64 + +endchoice + +config MACH_BCM963148 + bool "BCM963148 board" + select EMBEDDED + select PLAT_B15_CORE + select PLAT_BCM63148 + default y + +config MACH_BCM963138 + bool "BCM963138 board" + select EMBEDDED + select PLAT_CA9_MPCORE + select PLAT_BCM63138 + default y + +config MACH_BCM96846 + bool "BCM96846 board" + select EMBEDDED + select PLAT_CA7_MPCORE + select PLAT_BCM6846 + default y + +config BOARD_ZRELADDR + hex + default 0x00018000 + depends on BCM_KF_ARM_BCM963XX + depends on ARM + help + Must be consistent with the definition of "textaddr-y" in + arch/arm/Makefile; + BOARD_ZRELADDR == virt_to_phys(TEXTADDR) + +config ARCH_BCM947XXX + bool "BCM947189 board" if ARCH_MULTI_V7 + select ARM_AMBA + select ARM_GIC + select HAVE_ARM_ARCH_TIMER + default n + +config BOARD_PARAMS_PHYS + hex + depends on BCM_KF_ARM_BCM963XX + depends on ARM + default 0x00000000 + help + Board_PARAMS_PHYS must be within 4MB of ZRELADDR + +config DEBUG_UART_ADDR + hex + default 0xfffe8600 if (BCM963138) || (BCM963148) + default 0xff800640 if (BCM96846) + default 0xff812000 if (BCM947622) || (BCM963178) || (BCM96878) + depends on BCM_KF_ARM_BCM963XX + depends on ARM + help + Physical address of the UART used in early kernel debugging. + +config BCM63148_SIM + bool "63148 SIM" + default n + depends on BCM963148 + help + BCM63148 for simulation + +config BCM63138_SIM + bool "63138 SIM" + default n + depends on BCM963138 + help + BCM63138 for simulation + +config BCM6846_SIM + bool "6846 SIM" + default n + depends on BCM96846 + help + BCM6846 for simulation + +config BCM6878_SIM + bool "6878 SIM" + default n + depends on BCM96878 + help + BCM6878 for simulation + +config BCM47622_SIM + bool "47622 SIM" + default n + depends on BCM947622 + help + BCM47622 for simulation + +config BCM63178_SIM + bool "63178 SIM" + default n + depends on BCM963178 + help + BCM63178 for simulation + +config BCM6858_SIM + bool "6858 SIM" + default n + depends on BCM96858 + help + BCM6858 for simulation + +config BCM6856_SIM + bool "6856 SIM" + default n + depends on BCM96856 + help + BCM6856 for simulation + +config BCM_SCHED_RT_PERIOD + int "Period over which RT task cpu usage is measured (in us)" + range 0 1000000 + +config BCM_SCHED_RT_RUNTIME + int "Portion of the period that RT tasks are allowed to run (in us)" + range 0 1000000 + +config BCM_SCHED_RT_SHARE + bool "Allow RT threads to take time from other cores" + +config BCM_PROC_CPU_AFFINITY_HINT + bool "Allow process to recover its cpu affinity mask when feasible" + +config BCM_BOARD + bool "Support for Broadcom Board" + select CRC32 + +config BCM_CHIPINFO + tristate "Support for Broadcom chipinfo" + + +config BCM_PLATFORM + tristate "Support for Broadcom platform" + +config BCM_OTP + tristate "Secure boot support for Broadcom otp" + +config BCM_SOTP + tristate "Secure boot support for Broadcom Secure otp" + +config BCM_SERIAL + tristate "Support for Serial Port" + select SERIAL_CORE + +config BCM_SERIAL_CONSOLE + bool "Console on BCM63XX serial port" + depends on BCM_SERIAL=y + select SERIAL_CORE_CONSOLE + +config BCM_EXT_TIMER + bool "Support for external timer" + default n + +config BCM_WATCHDOG_TIMER + bool "Support for watchdog timer" + default n + +config BCM_FAILSAFE_BOOT + depends on WATCHDOG + bool "Support for failsafe boot" + default n + +config BCM_TIMER + bool "Support for Broadcom Timer" + +config L2TP + tristate "Support for L2TP" + +config ACCEL_PPTP + tristate "Support for ACCEL_PPTP" + +config BCM_PKTFLOW + tristate "Support for Broadcom Packet Flow Cache" + +config BCM_PKTFLOW_MAX_FLOWS + int "Max number of Flow-cache flows Entries" + depends on BCM_PKTFLOW + +config BCM_PKTFLOW_MAX_FDB + int "Max number of Flow-cache FDB Entries" + depends on BCM_PKTFLOW + +config BCM_PKTFLOW_MAX_HOST_DEV + int "Max number of Flow-cache Host Device Entries" + depends on BCM_PKTFLOW + +config BCM_PKTFLOW_MAX_HOST_MAC + int "Max number of Flow-cache Host MAC Entries" + depends on BCM_PKTFLOW + +config BCM_FHW + tristate "Support for HW Accelerator" + +config BCM_PKTCMF + tristate "Support for Packet CMF" + +config BCM_PKTRUNNER + tristate "Support for Packet runner" + +config BCM_PKTRUNNER_GSO + bool "Runner GSO Support " + +config BCM_SW_GSO + bool "Software GSO Support " + depends on BCM_KF_SW_GSO && BCM_KF_NBUFF + +config BCM_CSO + bool "CHECKSUM OFFLOAD Support " + +config BCM_CAPWAP + bool "CAPWAP Support " + +config BCM_CMDLIST + tristate "Support for Command List Driver" + +config BCM_CMDLIST_SIM + bool "Command List Simulation" + +config BCM_ARCHER + tristate "Support for Archer Driver" + +config BCM_ARCHER_WLAN + bool "Archer WLAN Interface" + +config BCM_ARCHER_GSO + bool "Archer Generic Segmentation Offload (GSO)" + +config BCM_ARCHER_SIM + bool "Archer Simulation" + +config BCM_LTE + tristate "BCM LTE WAN support" + depends on BCM963138 || BCM963381 + +config BCM_LTE_IMPL + int "Implementation index for Broadcom LTE" + depends on BCM_LTE + +config BCM_LTE_PCI + tristate "LTE on PCI" + depends on (BCM_LTE && BCM_PCI) + +config BCM_LTE_PCI_MODEM_BOOT + bool "Modem Boot device node" + depends on BCM_LTE_PCI + +config BCM_LTE_USB + tristate "LTE on USB" + depends on (BCM_LTE && BCM_USB) + +config BCM_UBUSCAP + tristate "Support for Broadcom UBUS capture driver" + +config BCM_UBUSCAP_IMPL + int "UBUS capture char driver" + depends on BCM_UBUSCAP + +config BCM_MEMCCAP + tristate "Support for Broadcom MEMC capture driver" + +config BCM_MEMCCAP_IMPL + int "MEMC capture char driver" + depends on BCM_MEMCCAP + +config BCM_INGQOS + tristate "Support for Ingress QoS" + +config BCM_BPM + tristate "Support for Buffer Pool Manager" + +config BCM_TM + tristate "Support for Traffic Manager" + +config BCM_LICENSE + tristate "Broadcom License" + default n + select GLOB + help + This option enables the Broadcom Licensing module + +config BCM_SPDSVC + tristate "Speed Service Support" + default n + help + This option enables the Broadcom Speed Service feature. + +config BCM_SPDTEST + tristate "TCP/UDP Speed Test Support" + depends on BCM_LICENSE + default n + help + This option enables the Broadcom TCP/UDP Speed Test feature. + +config BCM_BPM_BUF_MEM_PRCNT + int "Buffer Memory as % of Total Memory" + range 1 100 + default 15 + depends on BCM_BPM + +config BCM_BPM_BUF_TRACKING + bool "Buffer Tracking Enabled" + depends on BCM_BPM + +config BCM_FEED_RING_DYNAMIC + bool "Enable Feed Ring dynamic allocations" + +config BCM_FEED_RING_SIZE + int "Feed Ring Size" + range 1024 16384 + default 2048 + depends on BCM_FEED_RING_DYNAMIC + +config BCM_FEED_RING_ALLOC_START + int "Feed Allocation Start" + range 256 16384 + default 1536 + depends on BCM_FEED_RING_DYNAMIC + +config BCM_FEED_RING_ALLOC_BATCH + int "Feed Allocation Batch" + range 16 256 + default 32 + depends on BCM_FEED_RING_DYNAMIC + +config BCM_FEED_RING_REFILL_EVERY + int "Feed Check Refill Every" + range 128 16384 + default 1024 + depends on BCM_FEED_RING_DYNAMIC + +config BCM_FEED_RING_MAX_ALLOCATIONS + int "Feed Max Allocations" + range 2048 16384 + default 16384 + depends on BCM_FEED_RING_DYNAMIC + +config BCM_FAP + tristate "Support for Forward Assist Processor (FAP)" + depends on MIPS + depends on BCM_KF_FAP + +config BCM_FAP_GSO + bool "Support GSO in FAP" + depends on MIPS + depends on BCM_KF_FAP && BCM_FAP + +config BCM_FAP_GSO_LOOPBACK + bool "FAP GSO LOOPBACK Support " + depends on MIPS + depends on BCM_KF_FAP && BCM_FAP_GSO + +config BCM_FAP_LAYER2 + bool "FAP Layer 2 Bridge" + depends on MIPS + depends on BCM_KF_FAP && BCM_FAP + +config BCM_FAP_IPV6 + bool "Support for IPV6 in FAP" + depends on BCM_FAP && BCM_PKTFLOW + depends on BCM_KF_FAP + depends on MIPS + +config BCM_PKTDMA + tristate "Support for Packet DMA" + depends on (MIPS || BCM963138 || BCM_ENET_4908_GMAC || BCM947189) + +config BCM_IUDMA + bool "Support for Iudma" + depends on MIPS + +config BCM_RDPA_BRIDGE + tristate "Support for Runner bridge" + depends on BCM96838 || BCM96848 || BCM96858 || BCM96846 || BCM96856 || BCM96878 + depends on BCM_RDPA + +config BCM_RDPA_MCAST + bool "Support for Runner Multicast" + depends on BCM963138 || BCM963148 || BCM94908 || BCM963158 + depends on BCM_RDPA + +config BCM_ENET + tristate "Support for Ethernet" + +config BCM_ENET_SYSPORT + tristate "Support for Ethernet System Port" + +config BCM_DEF_NR_RX_DMA_CHANNELS + int "Number of RX DMA Channels" + range 1 4 + +config BCM_DEF_NR_TX_DMA_CHANNELS + int "Number of TX DMA Channels" + range 1 4 + +config BCM_PKTDMA_RX_SPLITTING + bool "PktDma Iudma Rx Splitting" + depends on BCM_PKTDMA && BCM_FAP + +config BCM_PKTDMA_TX_SPLITTING + bool "PktDma Iudma Tx Splitting" + depends on BCM_PKTDMA && BCM_FAP + +config BCM_GMAC + bool "Support for GMAC" + depends on (MIPS || BCM_ENET_4908_GMAC) + +config EPON_SDK + tristate "Support for EPON" + +config EPON_SDK_VOICE_OAM + tristate "Support for EPON Voice OAM" + depends on EPON_SDK + +config GPON_SFU + bool "Support for GPON" + depends on BCM_GPON + +config GPON_HGU + bool "Support for GPON" + depends on BCM_GPON + +config EPON_SFU + bool "Support for EPON" + depends on EPON_SDK + +config EPON_SBU + bool "Support for EPON" + depends on EPON_SDK + +config EPON_HGU + bool "Support for EPON" + depends on EPON_SDK + +config EPON_UNI_UNI_ENABLED + tristate "Support for EPON SFU/SBU UNI to UNI Forwarding" + depends on EPON_SDK + +config EPON_DS_DLF_FORWARD + tristate "Support for EPON Downstream DLF Forward" + depends on EPON_SDK + +config BCM_GPON + tristate "Support for GPON" + depends on BCM96838 || BCM96848 || BCM96858 || BCM963158 || BCM96846 || BCM96856 || BCM96878 + +config BCM_WLCLED + bool "Support for WLAN LED" + +config BCM_NGPON + tristate "Support for XGPON1/XGS/NGPON2 stack" + depends on BCM96858 || BCM96856 || BCM963158 + +config BCM_OPTICALDET + tristate "Support for optical device detection" + depends on BCM_I2C_CHIP + default m + +config BCM_OPTICALDET_IMPL + int "Implementation index for BRCM optical device detection" + depends on BCM_OPTICALDET + default 1 + +config BCM_WANTYPEDET + tristate "Support for WAN type auto-detection" + depends on BCM_I2C_CHIP && BCM_GPON + default m + +config BCM_WANTYPEDET_IMPL + int "Implementation index for BRCM WAN type auto-detection" + depends on BCM_WANTYPEDET + default 1 + +config BCM_PON_WAN_TYPE_AUTO_DETECT + bool "Enable WAN type auto-detection on BCM96858, BCM96856, BCM96846, BCM96878" + depends on BCM_WANTYPEDET && (BCM96858 || BCM96856 || BCM96846 || BCM96878) + default y + +config BCM_BUILD_NGPON_AMD2 + bool "989.3AMD2 / 9807.1AMD1 support" + depends on (BCM96858 || BCM96856 || BCM963158) + default y + +config BCM_SFP + tristate "Support for SFP" + +config BCM_SFP_IMPL + int "Implementation index for external SFP module" + depends on BCM_SFP + +config BCM_I2C_BUS_IMPL + int "Implementation index for I2C Bus module" + depends on BCM_I2C_BUS + +config BCM_I2C_CHIP_IMPL + int "Implementation index for I2C Bus module" + depends on BCM_I2C_CHIP + +config BCM_I2S_IMPL + int "Implementation index for I2S module" + depends on BCM_I2S + +config BCM_LASER + tristate "Support for LASER" + +config BCM_MAX_GEM_PORTS + int "Number of Gem Ports" + range 32 256 if BCM96838 || BCM96848 || BCM96858 || BCM963158 || BCM96846 || BCM96878 + default 32 + +config BCM_MoCA + tristate "Support for MoCA" + +config BCM_6802_MoCA + bool "Support for 6802 MoCA" + depends on BCM_MoCA + help + Select 'M' to include support for Broadcom MoCA Solution. + No Support For Static Build Model. + +config BCM_MoCA_AVS + bool "Support for AVS on BCM MoCA chip" + depends on BCM_6802_MoCA + default y + +config BCM_HS_UART + tristate "Support for High Speed UART" + +config BCM_TSTAMP + bool + +config BCM_LOG + tristate "Support for BCM LOG" + +config BCM_COLORIZE_PRINTS + bool "Color code various prints" + +config BCM_ASSERTS + bool "Compile in assert code" + +config BCM_FATAL_ASSERTS + bool "Generate a fatal error when assert fails" + +config BCM_I2C_BUS + tristate "Support for I2C Bus Drivers" + +config BCM_I2C_GPIO + bool "Support for I2C GPIO Driver" + default n + +config BCM_I2C_CHIP + tristate "Support for I2C Chip Drivers" + +config BCM_I2S + tristate "Support for I2S Drivers" + +config BCM_VLAN + tristate "Support for BCM VLAN" + +config BCM_EPON + tristate "Support for EPON LUE" + depends on BCM96838 || BCM96848 || BCM96858 || BCM96846 || BCM96856 || BCM96878 || BCM963158 + +config BCM_EPON_STACK + tristate "Support for EPON STACK" + help + Include it as m to enable EPON stack + +config BCM_USB + tristate "Support for USB" + depends on MIPS + +config BCM_USBNET_ACCELERATION + bool "Support for USBNET/LTE ACCELERATION in FCACHE" + depends on BCM_PKTFLOW + +config BCM_USBNET_THREAD + bool "Run USBNET in kernel thread" + +config BCM_SATA_TEST + tristate "Support for SATA compliance tests" + +config BCM_M2M_DMA + bool "Support for M2M DMA" + depends on BCM_KF_M2M_DMA + depends on BCM963138 + +config BCM_SPEEDYGET + bool "Support for wget speed test" + +config BCM_REASON_TO_SKB_MARK + bool "Support for trap reason to skb mark" + default y + depends on BCM96858 || BCM96846 || BCM96856 || BCM96878 + +config BCM_DW_CXC_DATA_MODE + bool "Support for CXC data mode" + default y + depends on BCM96858 || BCM96846 || BCM96856 || BCM96878 || BCM96838 || BCM96848 + +config BCM_ISDN + tristate "Support for ISDN" + depends on MIPS + +config BCM_WLAN + tristate "Support for Wireless" + +config BCM_WAPI + bool "Support for Wireless WAPI" + depends on BCM_WLAN + +config BCM_CPEROUTER + bool "Enable BCA CPEROUTER" + default n + depends on BCM_WLAN + +config BCM_WLAN_DPDCTL + bool "Power down with dummy interface for Wireless LAN" + default n + select DUMMY + depends on BCM_CPEROUTER + +config BCM_WLAN_WLVISUALIZATION + bool "Enable wlan Visualization" + default n + depends on BCM_WLAN + +config BCM_WLAN_WBD + bool "Enable wlan wifi blanket" + default n + depends on BCM_WLAN + +config BCM_WLALTBLD + string "Wireless Alternate Build" + depends on BCM_WLAN + +config BCM_WLAN_IMPL + int "Wireless Implementation Selection" + depends on BCM_WLAN + +config BCM_PCI + bool "Support for PCI" + select PCI + select PCI_DOMAINS + select PCI_QUIRKS + +# NOTE: the select PCI_DOMAINS line will cause problems with old kernel, but removing it +# will cause PCI_DOMAINS to be removed from the .config file for some unknown reason... + +config BCM_PCIE_HCD + tristate "PCIe host controller driver from bcmdriver" + depends on BCM_PCI + default y if PCI_MSI + default m + +config BCM_PCIE_HCD_IMPL + int "PCIe host controller driver implementation selection" + depends on BCM_PCIE_HCD + default 1 + +config BCM_PCIE_VCD + bool "Support for PCIe Virtual Core Device for internal WiFi devices" + depends on BCM_PCIE_HCD && BCM_WLAN + depends on BCM963178 || BCM947622 || BCM96878 + default y + +config BCM_PCIE_PMC_BRD_STUBS + bool "Support for PCIe PMC and Board API Stubs for Virtual Core Device bring up" + depends on BCM_PCIE_VCD + default n + +config BCM_PCIE_PMC_BRD_STUBS + bool "Stubs for external PMC, board, shared utils during vcd bringup" + depends on BCM_PCIE_VCD + depends on BRCM_IKOS + default y + +config BCM_PCIE_GEPD + tristate "PCIe Generic End Point driver from bcmdriver" + default n + +config BCM_PCIE_GEPD_IMPL + int "PCIe Generic End Point driver implementation selection" + depends on BCM_PCIE_GEPD + default 1 + +config BCM_NVRAM + tristate "Kernel NVRAM driver" + default m + +config BCM_NVRAM_IMPL + int "Kernel NVRAM driver implementation selection" + depends on BCM_NVRAM + default 1 + +config BCM_WLAN_USBAP + bool "Support for Wireless USBAP" + +config BCM_XTMCFG + tristate "Support for XTM" + +config BCM_XTMRT + tristate "Support for XTM" + +config BCM_ADSL + tristate "Support for ADSL" + +config BCM_DSL_GINP_RTX + tristate "Support for DSL_GINP_RTX" + +config BCM_EXT_BONDING + tristate "Support for EXT_BONDING" + depends on MIPS + +config BCM_DSL_GFAST + tristate "Support for DSL_GFAST" + depends on BCM963138 || BCM963158 + +config BCM_DSL_GFASTCOMBO + tristate "Support for DSL_GFASTCOMBO" + depends on (BCM963138 || BCM963158) && BCM_DSL_GFAST + +config BCM_DSL_CO + tristate "Support for DSL_CO" + depends on BCM963158 + +config BCM_FTTDP + bool "Support for FTTDP DPU" + depends on BCM96838 || BCM96858 + +config BCM_FTTDP_G9991 + bool "Support for FTTDP DPU with G.999.1" + depends on BCM_FTTDP + +config BCM_55153_DPU + bool "Support for 55153 DPU" + depends on BCM_DSL_CO && BCM963158 + default n + +config BCM_VOICE_SUPPORT + bool "Support for voice" + +config BCM_SLICSLAC + tristate "Support for VoIP SLIC/SLAC" + depends on BCM_VOICE_SUPPORT + +config BCM_DSPHAL + tristate "Support for VoIP DSP" + depends on BCM_VOICE_SUPPORT + +config BCM_DSP_ALSA_BRIDGE + bool "Support for DSP-ALSA bridge sound device" + depends on BCM_DSPHAL + +config BCM_DECT_SUPPORT + bool "Support for DECT" + +config BCM_DECT + tristate "Support for DECT" + depends on BCM_DECT_SUPPORT + +config BCM_BCMPROF + tristate "Support for profiling" + depends on MIPS + +config BCM_PWRMNGT + tristate "Support for Power Management" + depends on m + +config BCM_ETH_PWRSAVE + bool "Support for Ethernet Auto Power Down and Sleep" + +config BCM_ENERGY_EFFICIENT_ETHERNET + bool "Support for Energy Efficient Ethernet" + +config BCM_ETH_DEEP_GREEN_MODE + bool "Support for Ethernet Deep Green Mode" + +config BCM_ETH_HWAPD_PWRSAVE + bool "Support for Ethernet HW Auto Power Down for external PHYs" + +config BCM_HOSTMIPS_PWRSAVE + bool "Support for PWRMNGT MIPS clock divider" + depends on MIPS + +config BCM_HOSTMIPS_PWRSAVE_TIMERS + bool "Hostmips Power Save Timers" + depends on MIPS + +config BCM_CPUIDLE_CLK_DIVIDER + bool "Support for CPU Clock Divider in CPUIDLE" + +config BCM_DDR_SELF_REFRESH_PWRSAVE + bool "Support for DRAM Self Refresh mode" + +config BCM_AVS_PWRSAVE + bool "Support for Automatic Voltage Scaling" + +config BCM_1V2REG_AUTO_SHUTDOWN + bool "Support for Automatically Shutting down Internal 1.2V Regulator" + depends on MIPS + +config BCM_1V2REG_ALWAYS_SHUTDOWN + bool "Support for Overriding Automatically Shutting down Internal 1.2V Regulator" + depends on MIPS + +config BCM_1V2REG_NEVER_SHUTDOWN + bool "Support for Overriding Automatically Shutting down Internal 1.2V Regulator" + depends on MIPS + +config BCM_CPLD1 + bool "Support for CPLD standby timer" + depends on MIPS + +config BCM_PROCFS + tristate "Support for PROCFS" + +config BCM_TRNG + tristate "BCM HW Random Number Generator support" +# ---help--- +# This driver provides kernel-side support for the Random Number +# Generator hardware found on bcm. +# +# To compile this driver as a module, choose M here: the +# module will be called intel-rng. +# +# If unsure, say Y. + +config BCM_ARL + tristate "Support for ARL Table Management" + depends on MIPS + +config BCM_TMS + tristate "TMS support (802.3ah, 802.1ag, Y.1731)" + +config BCM_PMC + bool "PMC" + depends on BCM96838 || BCM963138 || BCM963148 || BCM963381 || BCM96848 || BCM94908 || BCM96858 || BCM963158 || BCM96846 || BCM96856 || BCM947622 || BCM963178 || BCM96878 + +config BCM_PLC_BOOT + tristate "PLC boot support" + +config BCM_IEEE1905 + tristate "IEEE1905 support" + +config BCM_BMU + bool "Battery Management Unit" + +config BCM_DPI + tristate "Support for Deep Packet Inspection" + +config BCM_DPI_TEST + bool "DPI Classification Testing" + depends on BCM_DPI + +config BCM_DPI_QOS + bool "DPI QoS" + depends on BCM_DPI + depends on BCM_RDPA || BCM_DPI_QOS_CPU || BCM_ARCHER + +config BCM_DPI_WLAN_QOS + bool "DPI WLAN QoS" + depends on BCM_DPI_QOS + +config BCM_DPI_QOS_CPU + bool "DPI Software Path" + depends on BCM_DPI + +config BCM_MAP + tristate "Support for MAP-T MAP-E" + +config BCM_BOARD_IMPL + int "Implementation index for Board" + depends on BCM_BOARD + +config BCM_CHIPINFO_IMPL + int "Implementation index for Chipinfo module" + depends on BCM_CHIPINFO + + +config BCM_PLATFORM_IMPL + int "Implementation index for BCM platform" + depends on BCM_PLATFORM + +config BCM_OTP_IMPL + int "Implementation index for otp module" + depends on BCM_OTP + +config BCM_SOTP_IMPL + int "Implementation index for secure otp module" + depends on BCM_SOTP + +config BCM_SERIAL_IMPL + int "Implementation index for Serial" + depends on BCM_SERIAL + +config BCM_EXT_TIMER_IMPL + int "Implementation index for external timer" + depends on BCM_EXT_TIMER + +config BCM_TRNG_IMPL + int "Implementation index for TRNG " + depends on BCM_TRNG + +config BCM_PKTFLOW_IMPL + int "Implementation index for Broadcom Flow Cache" + depends on BCM_PKTFLOW + +config BCM_PKTCMF_IMPL + int "Implementation index for Packet CMF" + depends on BCM_PKTCMF + +config BCM_PKTRUNNER_IMPL + int "Implementation index for Broadcom packet runner" + depends on BCM_PKTRUNNER + +config BCM_CMDLIST_IMPL + int "Implementation index for Broadcom Command List Driver" + depends on BCM_CMDLIST + +config BCM_ARCHER_IMPL + int "Implementation index for Broadcom Archer Driver" + depends on BCM_ARCHER + +config BCM_INGQOS_IMPL + int "Implementation index for Ingress QoS" + depends on BCM_INGQOS + +config BCM_BPM_IMPL + int "Implementation index for BPM" + depends on BCM_BPM + +config BCM_TM_IMPL + int "Implementation index for TM" + depends on BCM_TM + +config BCM_LICENSE_IMPL + int "Implementation index for LICENSE" + depends on BCM_LICENSE + +config BCM_SPDSVC_IMPL + int "Implementation index for SPDSVC" + depends on BCM_SPDSVC + +config BCM_SPDTEST_IMPL + int "Implementation index for TCP/UDP Speed Test" + depends on BCM_SPDTEST + +config BCM_FAP_IMPL + int "Implementation index for FAP" + depends on BCM_FAP + depends on MIPS + +config BCM_PKTDMA_IMPL + int "Implementation index for Packet DMA" + depends on BCM_PKTDMA + +config BCM_RDPA_BRIDGE_IMPL + int "Implementation index for Runner bridge" + depends on BCM_RDPA_BRIDGE + depends on BCM96838 || BCM96848 || BCM96858 || BCM96846 || BCM96856 || BCM96878 + +config BCM_ENET_IMPL + int "Implementation index for Ethernet" + depends on BCM_ENET + +config BCM_ENET_SYSPORT_IMPL + int "Implementation index for Ethernet System Port" + depends on BCM_ENET_SYSPORT + +config BCM_GPON_IMPL + int "Implementation index for GPON" + depends on BCM_GPON + +config BCM_LASER_IMPL + int "Implementation index for LASER" + depends on BCM_LASER + +config BCM_MoCA_IMPL + int "Implementation index for MoCA" + depends on BCM_MoCA + +config BCM_HS_UART_IMPL + int "Implementation index for High Speed UART" + depends on BCM_HS_UART + +config BCM_LOG_IMPL + int "Implementation index for BCM LOG" + depends on BCM_LOG + +config BCM_I2C_BUS_IMPL + int "Implementation index for I2C Bus" + depends on BCM_I2C_BUS + +config BCM_I2C_CHIP_IMPL + int "Implementation index for I2C Chip" + depends on BCM_I2C_CHIP + +config BCM_VLAN_IMPL + int "Implementation index for BCM VLAN" + depends on BCM_VLAN + +config BCM_EPON_IMPL + int "Implementation index for BCM EPON" + depends on BCM_EPON + +config BCM_USB_IMPL + int "Implementation index for USB" + depends on BCM_USB + +config BCM_ISDN_IMPL + int "Implementation index for ISDN" + depends on BCM_ISDN + +config BCM_XTMCFG_IMPL + int "Implementation index for XTMCFG" + depends on BCM_XTMCFG + +config BCM_XTMRT_IMPL + int "Implementation index for XTMRT" + depends on BCM_XTMRT + +config BCM_ADSL_IMPL + int "Implementation index for ADSL" + depends on BCM_ADSL + +config BCM_EXT_BONDING_IMPL + int "Implementation index for EXT_BONDING" + depends on BCM_EXT_BONDING + +config BCM_DECT_IMPL + int "Implementation index for DECT" + depends on BCM_DECT + +config BCM_SLICSLAC_IMPL + int "Implementation index for VoIP SLIC/SLAC" + depends on BCM_SLICSLAC + +config BCM_DSPHAL_IMPL + int "Implementation index for VoIP DSP HAL" + depends on BCM_DSPHAL + +config BCM_BCMPROF_IMPL + int "Implementation index for PROFILING" + depends on BCM_BCMPROF + +config BCM_PROCFS_IMPL + int "Implementation index for PROCFS" + depends on BCM_PROCFS + +config BCM_PWRMNGT_IMPL + int "Implementation index for PWRMNGT" + depends on BCM_PWRMNGT + +config BCM_ARL_IMPL + int "Implementation index for ARL" + depends on BCM_ARL + +config BCM_PMC_IMPL + int "Implementation index for PMC" + depends on BCM_PMC + +config BCM_TMS_IMPL + int "Implementation index for TMS" + depends on BCM_TMS + +config BCM_DPI_IMPL + int "Implementation index for Deep Packet Inspection" + depends on BCM_DPI + +config BCM_MAP_IMPL + int "Implementation index for MAP-T MAP-E" + depends on BCM_MAP + +config BCM_PORTS_ON_INT_EXT_SW + bool "Ports on both Internal and External Switch" + default n + help + This option enables the feature where ports can be on Internal switch and External switch. + +config BCM_VLAN_ROUTED_WAN_USES_ROOT_DEV_MAC + bool "Assign Same MAC address to Routed WAN Interface as root" + default n + help + This option enables the feature where Routed VLANCTL WAN Interfaces are assigned the same MAC as root device. + +config ROOT_FLASHFS + string "flash partition" + help + This is the root file system partition on flash memory + +config BRCM_KTOOLS + bool "BRCM_KTOOLS" + default n + help + Enables the build of BRCM MIPS Kernel Tools Support for PMON, BOUNCE + +config BCM_EXT_SWITCH + bool "External Switch present" + default n + help + This option enables the external switch support. + +config BCM_EXT_SWITCH_TYPE + int "External Switch Type/Number" + default 0 + +config BCM_JUMBO_FRAME + bool "Jumbo Frame Support (for Enet/Xtm/GPON only)" + default n + +config BCM_MAX_MTU_SIZE + int "Support for Maximum Payload Size" + depends on BCM_JUMBO_FRAME + range 64 10000 + default 2044 if (BCM963138 || BCM963148 || BCM94908 || BCM963158 || BCM947622 || BCM963178) + default 2048 if (BCM963268 || BCM963381) + default 1998 if (BCM96838 || BCM96848 || BCM96858 || BCM96846 || BCM96856 || BCM96878) + +config BCM_USER_DEFINED_DEFAULT_MTU + bool "User Defined Default Linux Device Interface MTU" + depends on BCM_JUMBO_FRAME + default n + +config BCM_IGNORE_BRIDGE_MTU + bool "Ignore Bridge MTU while forwarding in accelerated path" + depends on BCM_JUMBO_FRAME + default n + +config BCM_USER_DEFINED_DEFAULT_MTU_SIZE + int "User Defined Default Linux Device Interface MTU size" + depends on BCM_USER_DEFINED_DEFAULT_MTU + +choice + prompt "Restrict memory used (testing)" + depends on BCM_KF_MIPS_BCM963XX && MIPS_BCM963XX + +config BRCM_MEMORY_RESTRICTION_OFF + bool "Use all available" + +config BRCM_MEMORY_RESTRICTION_16M + bool "Use 16M" + +config BRCM_MEMORY_RESTRICTION_32M + bool "Use 32M" + +config BRCM_MEMORY_RESTRICTION_64M + bool "Use 64M" + +endchoice + +config AUXFS_JFFS2 + bool "JFFS2" + default n + help + Say Y here to enable support for an auxillary file system on flash. + +config BRCM_OLT_FPGA_RESTORE + bool "BRCM_OLT_FPGA_RESTORE" + default n + help + Enables /proc/brcm/olt_fpga_restore + +config PCI_DOMAINS + bool + default y + +config BCM_DCACHE_SHARED + bool "Share Dcache between TPs" + +config BCM_CPU_ARCH_NAME + string "Name of CPU Architecture" + +config BCM_CPU_ARCH_ABI_NAME + string "Name of CPU ABI Architecture" + +config BCM_PRINTK_INT_ENABLED + bool "printk with interrupt enabled" + +config BCM_BDMF + tristate 'Support for Broadcom Device Management Framework' + help + Say 'M' to include support for BDMF. + Static compile not supported. + +config BCM_PON + bool + depends on ( BCM96838 || BCM96848 || BCM96858 || BCM96846 || BCM96856 || BCM96878) && !BRCM_IKOS + default y + +config BCM_PON_XRDP + bool + depends on ( BCM96858 || BCM96846 || BCM96856 || BCM96878) && !BRCM_IKOS + default y + +config BCM_DSL_XRDP + bool + depends on BCM963158 + default y + +config BCM_PON_RDP + bool + depends on BCM96838 || BCM96848 + default y + +config BCM_DSL_RDP + bool + depends on BCM963138 || BCM963148 || BCM94908 + default y + +config BCM_XRDP + bool + depends on ( BCM96858 || BCM963158 || BCM96846 || BCM96856 || BCM96878) && !BRCM_IKOS + default y + +config BCM_RDP + bool + depends on BCM96838 || BCM96848 || BCM963138 || BCM963148 || BCM94908 + default y + +config BCM_RDPA + tristate 'Support for Runner Data Path API Driver' + depends on BCM_BDMF && (BCM_RDP || BCM_XRDP) + help + Say 'M' to include support for RDPA. + Static compile not supported. + +config BCM_SIM_CARD + tristate 'Support for Sim Card Driver' + help + Say 'M' to include support for Sim Card. + Static compile not supported. + +config BCM_PMD + tristate 'Support for PMD' + depends on BCM96838 || BCM96848 || BCM96858 || BCM963158 || BCM96846 || BCM96856 || BCM947622 || BCM963178 || BCM96878 + help + Say 'M' to include support for PMD. + Static compile not supported. + +config BCM_RDPA_GPL + tristate 'Support for Runner Data Path API GPL Driver' + depends on BCM_RDPA + +config BCM_RDPA_USR + tristate 'Support for Runner Data Path API USR Driver' + depends on BCM_RDPA + +config BCM_RDPA_GPL_EXT + tristate 'Support for Runner Data Path API GPL EXT Driver' + depends on BCM_RDPA + +config BCM_GPON_STACK + tristate 'Support for GPON Stack Driver' + depends on BCM96838 || BCM96848 || BCM96858 || BCM963158 || BCM96846 || BCM96878 + help + Say 'M' to include support for GPON Stack driver. + Static compile not supported. + + +config BCM_RDPA_MW + tristate 'Support for Runner Data Path API MW Driver' + depends on BCM_RDPA + +config BCM_RDPA_DRV + tristate 'Support for Runner Command Driver' + depends on BCM_RDPA + +config BCM_FPM + tristate "Support for Broadcom Frame Pool Manager" + depends on BCM_RDP && BCM94908 + help + Say 'M' to include support for FPM. + Static compile not supported. + +config BCM_RUNNER_MAX_FLOWS + int "Number of runner accelerated flows" + depends on BCM_RDPA + range 1024 131072 + +config BCM_FPM_POOL_NUM + int "Number of FPM Pools" + depends on BCM_FPM + default 1 + +config BCM_FPM_DT + bool "Support device tree for FPM" + depends on BCM_FPM + default n + +config BCM_WIFI_FORWARDING_DRV + tristate 'Support for Wifi forwarding driver' + depends on BCM_WLAN + help + Say 'M' to include support for Wifi forwarding driver. + Static compile not supported. + +config BCM_WFD_RATE_LIMITER + bool 'Support rate limiter for Wifi forwarding driver' + depends on BCM_WIFI_FORWARDING_DRV + default n + +config BCM_WLAN_16BIT_STATION_CHAIN_IDX_SUPPORT + bool "16bit Station Chain Index support" + depends on BCM_WLAN + default n + +config BCM_CPEPKTC + bool "Build CPE-specific Packet Chaining" + depends on BCM_WLAN_16BIT_STATION_CHAIN_IDX_SUPPORT + default n + +config BCM_PKTFWD + bool "Build Packet Forwarder Handoff" + depends on BCM_WLAN_16BIT_STATION_CHAIN_IDX_SUPPORT + default n + +config BCM_PKTFWD_FLCTL + bool "Build Credit based Host Flow Control" + depends on BCM_PKTFWD + default n + +config BCM_WLAN_REMOVE_INTERNAL_DEBUG + bool "Build Image without WLAN Internal/Bcmdbg" + depends on BCM_WLAN + default n + +config BCM_NETXL + tristate 'Support for NetXL forwarding driver' + help + Say 'M' to include support for NetXL forwarding driver. + Static compile not supported. + +config BCM_NETXL_IMPL + int "Implementation index for NetXL Forwarding Driver" + depends on BCM_NETXL + +config BCM_DHD_RUNNER + depends on BCM_WLAN + bool 'Support for DHD acceleration using Runner' + help + Say 'M' to include support DHD acceleration using Runner + Static compile not supported. + +config BCM_PMD_IMPL + int "Implementation index for PMD" + depends on BCM_PMD + +config BCM_SIM_CARD_IMPL + int "Implementation index for Sim Card" + depends on BCM_SIM_CARD + +config BCM_RDPA_GPL_EXT_IMPL + int "Implementation index for RDPA GPL EXT" + depends on BCM_RDPA_GPL_EXT + +config BCM_WIFI_FORWARDING_DRV_IMPL + int "Implementation index for Wifi Forwarding Driver" + depends on BCM_WIFI_FORWARDING_DRV + +config BCM_RDPA_MW_IMPL + int "Implementation index for RDPA MW" + depends on BCM_RDPA_MW + +config BCM_RDPA_DRV_IMPL + int "Implementation index for RDPA DRV" + depends on BCM_RDPA_DRV + +config BCM_FPM_IMPL + int "Implementation index for FPM" + depends on BCM_FPM + +config BCM_SATA_TEST_IMPL + int "Implementation index for SATA test module" + depends on BCM_SATA_TEST + + +config BCM_EPON_STACK_IMPL + int "Implementation index for EPON STACK" + depends on BCM_EPON_STACK + +config EPON_CLOCK_TRANSPORT + bool "Epon clock transport support" + depends on BCM_EPON_STACK + +config EPON_10G_SUPPORT + bool "EPON Rate 10G support" + depends on BCM_EPON_STACK + +# Time Synchronization: Feature +config BCM_TIME_SYNC + tristate 'Support for Time Synchronization' + depends on BCM96838 || BCM96848 || BCM96858 || BCM96846 || BCM96856 || BCM963158 + help + Say 'M' to include support for Time Synchronization driver. + Static compile not supported. + +# Time Synchronization: Implementation +config BCM_TIME_SYNC_IMPL + int "Implementation index for Time Synchronization" + depends on BCM_TIME_SYNC + +# GPON ToDD +config BCM_GPON_TODD + bool "Support GPON ToDD" + depends on BCM_TIME_SYNC + +# PTP 1588 +config BCM_PTP_1588 + bool "Support PTP 1588" + depends on BCM_TIME_SYNC + +# Sync Clock: 8KHz +config BCM_TIME_SYNC_8KHZ + bool "Support Sync Clock: 8KHz" + depends on BCM_TIME_SYNC + +# Sync Signal: 1PPS +config BCM_TIME_SYNC_1PPS + bool "Support Sync Signal: 1PPS" + depends on BCM_TIME_SYNC + +# Sync Signal: PON Unstable +config BCM_TIME_SYNC_PON_UNSTABLE + bool "Support Sync Signal: PON Unstable" + depends on BCM_TIME_SYNC + +config BCM_BRIDGE_MAC_FDB_LIMIT + bool "Support MAC limit in kernel" + +config BCM_ZONE_ACP + bool "ACP Support" + depends on BCM963138 + +config BCM_ACP_MEM_SIZE + int "Reserved Memory Size for ACP Purpose, unit in MB" + default 16 + depends on BCM_ZONE_ACP + +config MTD_BCM_SPI_NAND + tristate "Broadcom 63xx SPI NAND MTD support" + depends on BCM_KF_MTD_BCMNAND + help + Broadcom 963xx SPI NAND MTD support + +config BCM_ENET_4908_GMAC + bool "4908 Ethernet traffic using GMAC (No RDP)" + depends on (BCM963268 || BCM94908) + +config BCM_ETHWAN + bool "Ethernet WAN Support enabled" + default false + +config BCM_ENET_MULTI_IMP_SUPPORT + bool "Multiple IMP Ports towards Runner" + depends on (BCM94908 || BCM963158) + +config BCM_WAN_2_WAN_FWD_ENABLED + bool "Enable Packet Forwarding between two WAN Interfaces" + default false + depends on BCM_KF_WANDEV + +config BCM_ETHTOOL + bool "Enable ethtool command" + default true + +config BCM_SWMDK + bool "Enable swmdk" + default false + +config BCM_KERNEL_BONDING + bool "Enable Kernel Bonding Driver" + default false + +config BCM_SWITCH_SCHED_SP + bool "Set switch scheduling SP mode" + default false + +config BCM_SWITCH_SCHED_WRR + bool "Set switch schedule WRR mode" + default false + +config BCM_RUNNER_FLOODING + bool "Enable Runner Flooding to LAN ports" + default true + depends on BCM_RDPA_BRIDGE + depends on BCM_XRDP + +config BCM_OCF + tristate "Linux OCF module" + +config BCM_OCF_IMPL + int "Linux OCF implementation" + depends on BCM_OCF + +config BCM_BLUETOOTH_USB + tristate "Enable BRCM Bluetooth USB Driver" + default false + +config BCM_BLUETOOTH_USB_IMPL + int "Implementation index for BRCM BLUETOOTH USB dongle" + depends on BCM_BLUETOOTH_USB + +config BCM_NFC_I2C + tristate "Enable BRCM NFC I2C Driver" + default n + +config BCM_NFC_I2C_IMPL + int "Implementation index for BRCM NFC I2C" + depends on BCM_NFC_I2C + +config BRCM_MINIGW + bool "Minimal DDR GW" + +config ARCH_SPARSEMEM_ENABLE + bool "Enable Ssparsemem" + depends on ARM64 && BRCM_IKOS + +config BCM_512MB_DDR + bool "Support for 512MB DDR" + select SYS_SUPPORTS_HIGHMEM + select HIGHMEM + +config BCM_CFE_XARGS + tristate "Support for Broadcom CFE Extra Arguments" + +config BCM_ROOTFS_SQUASHFS_HASH + tristate "Support for Broadcom ROOTFS SQUASHFS hash check" + +config BCM_CFE_XARGS_EARLY + bool "CFE (Bootloader) extended parameters support - early processing" + default y if (BCM_CFE_XARGS) + help + If selected CFE extended parameters will be processed before major services are ready (such as slab). + In this case memory for parameter buffer is statically defined at compile time. + Useful if there is an intention to tweak functionality for debug or other purposes. + +config BCM_CFE_XARGS_EARLY_SIZE + hex + depends on BCM_CFE_XARGS_EARLY + default 0x00000400 if (BCM_CFE_XARGS_EARLY) + help + Size of static buffer if memory subsystime is unavailable. + +config BCM_HNDROUTER + bool "4908 Wireless Router board" + depends on BCM_KF_HNDROUTER + +config BCM_HND_EAP + bool "4908 Enterprise AP" + default n + +config BCM_ARM_CPUIDLE + bool "Cpuidle driver for BCM ARM and ARM64 chips" + depends on CPU_IDLE + +config BCM_DHD_RUNNER_GSO + bool "GSO support for DHD on Runner" + +config BCM_THERMAL + tristate "Support for Broadcom Thermal Protection" + +config BCM_CPUOFF + tristate "Support for auto idle cpu power-down" + +config BCM_INTF_BRG_ENABLED + bool "Interface-based bridginge" + default n + +config BCM_MPTCP + bool "MultiPath TCP support" + default n + +config BCM_COHERENT_OUTER_SHARED + bool "Coherent Memory as outer shareable" + depends on BCM96858 || BCM96846 || BCM947622 || BCM963178 + +config BCM_OVERLAYFS_BACKPORTS + bool "backporting of overlay fs fixes." + default n + depends on OVERLAY_FS + +config BCM_JFFS2_OVERLAY + bool "implementing RENAME_WHITEOUT and RENAME_EXCHANGE in jffs2." + default n + depends on JFFS2_FS + depends on OVERLAY_FS + +config BCM_UBIFS_OVERLAY_BACKPORTS + bool "implementing RENAME_WHITEOUT and RENAME_EXCHANGE in ubifs." + default n + depends on UBIFS_FS + depends on OVERLAY_FS + +config BP_PHYS_INTF + bool "Board Parameter Physical Interface Configuration" + default n + depends on BCM963158 || BCM963178 || BCM947622 + +config BCM_GLB_COHERENCY + bool "Global Hardware Cache Coherency" + default n + depends on BCM963158 || BCM96846 || BCM96858 || BCM96856 || BCM963178 || BCM947622 + +config BCM_CMA_RSVMEM + bool "Linux CMA based memory reservation" + depends on ARM || ARM64 + default n + +config BCM_FPM_COHERENCY_EXCLUDE + bool "Keep FPM pool memory area non-coherent" + default n + depends on (BCM_GLB_COHERENCY && BCM96858) + +config BCM_UBUS_DECODE_REMAP + bool "Remap UBUS decode window" + default n + depends on BCM963158 || BCM96846 || BCM96858 || BCM96856 || BCM963178 || BCM947622 + +config BCM_UBUS4_DCM + bool "UBUS4 Dynamic Clock Management" + default n + depends on BCM963158 || BCM96858 || BCM96846 || BCM96856 || BCM963178 || BCM947622 + +config BCM_RNR_CPU_RX_DYN_METER + bool "Runner CPU Rx dynamic meter management" + default n + depends on BCM_RDPA + +config BCM_FCACHE_CLASSIFICATION_BYPASS + bool "Enable Sw Flow bypass" + depends on BCM96858 || BCM96846 || BCM96856 || BCM963158 || BCM963138 || BCM963148 || BCM94908 || BCM96878 + + +config BCM_XRDP_AUTOGATE + bool "XRDP Dynamic Clock Management/Gating" + default n + depends on BCM963158 || BCM96846 || BCM96856 || BCM96858 || BCM96878 + +config BCM_WLCSM_DEBUG + bool "enable WLCSM Ddebug" + default n + depends on BCM_WLAN + +config BCM_AIRIQ + bool "AirIQ support" + default n + +config BCM_HOSTAPD + bool "HOSTAPD support" + default n + +config BCM_SPECTRE_PATCH_ENABLE + bool "Enable Spectre Patch" + default n + +config BCM_NETFILTER_REGARDLESS_DROP_ORDER + int "Netfilter Regardless Connection drop order" + default 1 + +config BCM_EAPFWD + tristate "EAP Forwarder" + default n + depends on BCM_PKTFWD + +config BCM_EAPFWD_IMPL + int "EAP Forwarder" + depends on BCM_EAPFWD + +config BCM_DISABLE_NOR_RAW_PARTITION + bool "Disable NOR flash raw partition" + depends on BCM963178 || BCM947622 + default n + +config BCM_IMAGE_OFFSET_ZERO + bool "Set image offset to 0" + depends on BCM963178 || BCM947622 + default n + +config BCM_OVS + bool "OVS Broadcom Support" + default n + +config BCM_KERNEL_CODE_PROTECTION + bool "Enable Kernel Code Protection" + +config BCM_OVS_MCAST + bool "OVS Broadcom Multicast Support" + depends on BCM_OVS + default n + +menu "Autodetected Drivers" +source "../../bcmdrivers/Kconfig.autogen" +endmenu + +endif + diff -urN linux-4.1.52-orig/Kconfig.bcm_kf linux-4.1.52/Kconfig.bcm_kf --- linux-4.1.52-orig/Kconfig.bcm_kf 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/Kconfig.bcm_kf 2020-02-07 02:14:59.000000000 +0800 @@ -0,0 +1,585 @@ +# +# Automatically Generated Kconfig File. DO NOT MODIFY +# + + +config BCM_KF_MISC_MAKEFILE + bool + default y + +config BCM_KF_MIPS_BCM963XX + bool + default y + +config BCM_KF_ARM_BCM963XX + bool + default y + +config BCM_KF_ARM64_BCM963XX + bool + default y + +config BCM_KF_LINKER_WORKAROUND + bool + default y + +config BCM_KF_KERN_WARNING + bool + default y + +config BCM_KF_DOUBLE_INCLUSION + bool + default y + +config BCM_KF_FIXADDR_TOP + bool + default y + +config BCM_KF_NBUFF + bool + default y + +config BCM_KF_VLAN + bool + default y + +config BCM_KF_NETDEV_PATH + bool + default y + +config BCM_KF_SKB_DEFINES + bool + default y + +config BCM_KF_BLOG + bool + default y + +config BCM_KF_VLANCTL_BIND + bool + default y + +config BCM_KF_WANDEV + bool + default y + +config BCM_KF_FAP + bool + default y + +config BCM_KF_CSUM_UNALIGNED + bool + default y + +config BCM_KF_THREAD_SIZE_FIX + bool + default y + +config BCM_KF_TSTAMP + bool + default y + +config BCM_KF_UNALIGNED_EXCEPTION + bool + default y + +config BCM_KF_MIPS_IOPORT_BASE + bool + default y + +config BCM_KF_SHOW_RAW_BACKTRACE + bool + default y + +config BCM_KF_DCACHE_SHARED + bool + default y + +config BCM_KF_CPU_DATA_CPUID + bool + default y + +config BCM_KF_PCI_FIXUP + bool + default y + +config BCM_KF_PCI_RESET_DOMAIN_NR + bool + default y + +config BCM_KF_SYSRQ_AUX_CHAR + bool + default y + +config BCM_KF_CHAR_SYSRQ + bool + default y + +config BCM_KF_MTD_BCMNAND + bool + default y + +config BCM_KF_MTD_BCM963XX + bool + default y + +config BCM_KF_MTD_OOB_AUTO + bool + default y + +config BCM_KF_MTD_OOPS + bool + default y + +config BCM_KF_PPP + bool + default y + +config BCM_KF_PROC_BCM + bool + default y + +config BCM_KF_LOG + bool + default y + +config BCM_KF_TRACE_CUSTOM + bool + default y + +config BCM_KF_CGROUP + bool + default y + +config BCM_KF_HARDIRQ_CYCLES + bool + default n + +config BCM_KF_SHOW_HEAP_STACK + bool + default y + +config BCM_KF_BYPASS_SMP_WARNING + bool + default y + +config BCM_KF_OOM_REBOOT + bool + default y + +config BCM_KF_VMSCAN_OPT + bool + default y + +config BCM_KF_IGMP + bool + default y + +config BCM_KF_IGMP_RATE_LIMIT + bool + default y + +config BCM_KF_MLD + bool + default y + +config BCM_KF_MROUTE + bool + default y + +config BCM_KF_IGMP_RIP_ROUTER + bool + default y + +config BCM_KF_MCAST_GR_SUPPRESSION + bool + default y + +config BCM_KF_MCAST + bool + default y + +config BCM_KF_MISC_IOCTLS + bool + default y + +config BCM_KF_MISALIGN_MQS + bool + default n + +config BCM_KF_NETFILTER + bool + default y + +config BCM_KF_ENET_SWITCH + bool + default y + +config BCM_KF_FAIL_CONFIG_ON_EOF + bool + default y + +config BCM_KF_RUNNER + bool + default y + +config BCM_KF_WL + bool + default y + +config BCM_KF_LINKWATCH_WQ + bool + default y + +config BCM_KF_IP + bool + default y + +config BCM_KF_SPU + bool + default y + +config BCM_KF_80211 + bool + default y + +config BCM_KF_BUZZZ + bool + default y + +config BCM_KF_ATM_BACKEND + bool + default y + +config BCM_KF_EXTRA_DEBUG + bool + default y + +config BCM_KF_SUPPRESS_DEBUG + bool + default y + +config BCM_KF_SPI + bool + default y + +config BCM_KF_POWER_SAVE + bool + default y + +config BCM_KF_RCU_CONSTANT_BUG + bool + default y + +config BCM_KF_JFFS + bool + default y + +config BCM_KF_PROC_DEFAULT + bool + default y + +config BCM_KF_EXTSTATS + bool + default y + +config BCM_KF_USB_STORAGE + bool + default y + +config BCM_KF_USBNET + bool + default y + +config BCM_KF_FAP_GSO_LOOPBACK + bool + default y + +config BCM_KF_SW_GSO + bool + default y + +config BCM_KF_PROTO_IPSEC + bool + default y + +config BCM_KF_PROTO_ESP + bool + default y + +config BCM_KF_PRINTK_INT_ENABLED + bool + default y + +config BCM_KF_LIMITED_IFINDEX + bool + default y + +config BCM_KF_BRIDGE_PORT_ISOLATION + bool + default y + +config BCM_KF_BRIDGE_STP + bool + default y + +config BCM_KF_BRIDGE_STATIC_FDB_MOVE + bool + default y + +config BCM_KF_BRIDGE_MAC_FDB_LIMIT + bool + default y + +config BCM_KF_IEEE1905 + bool + default y + +config BCM_KF_MCAST_RP_FILTER + bool + default y + +config BCM_KF_XT_MATCH_LAYER7 + bool + default y + +config BCM_KF_XT_TARGET_DC + bool + default y + +config BCM_KF_VLAN_AGGREGATION + bool + default y + +config BCM_KF_ETHTOOL + bool + default y + +config BCM_KF_DEBUGGING_DISABLED_FIX + bool + default y + +config BCM_KF_IPV6RD_SECURITY + bool + default y + +config BCM_KF_DPI + bool + default y + +config BCM_KF_ARM_ERRATA_798181 + bool + default y + +config BCM_KF_MISC_3_4_CVE_PORTS + bool + default y + +config BCM_KF_MISC_BACKPORTS + bool + default y + +config BCM_KF_SCHED_RT + bool + default y + +config BCM_KF_SCHED_RT_SHARE + bool + default y + +config BCM_KF_ONDEMAND + bool + default y + +config BCM_KF_INTERACTIVE + bool + default y + +config BCM_KF_EMMC + bool + default y + +config BCM_KF_USB_HOSTS + bool + default y + +config BCM_KF_ACCEL_PPTP + bool + default y + +config BCM_KF_RTPRIO_DEF + bool + default y + +config BCM_KF_LEDS + bool + default y + +config BCM_KF_BRIDGE_COUNTERS + bool + default y + +config BCM_KF_IRQ_AFFINITY + bool + default y + +config BCM_KF_ANDROID + bool + default y + +config BCM_KF_MHI + bool + default y + +config BCM_KF_PHONET + bool + default y + +config BCM_KF_M2M_DMA + bool + default y + +config BCM_KF_RECVFILE + bool + default y + +config BCM_KF_FILENAME_CHECK + bool + default y + +config BCM_KF_BLUETOOTH_VENDOR_ID + bool + default y + +config BCM_KF_SPEEDYGET + bool + default y + +config BCM_KF_512MB_DDR + bool + default y + +config BCM_KF_TCP_NO_TSQ + bool + default y + +config BCM_KF_TMS + bool + default y + +config BCM_KF_UBI + bool + default y + +config BCM_KF_HNDROUTER + bool + default y + +config BCM_KF_WDT + bool + default y + +config BCM_KF_INTF_BRG + bool + default y + +config BCM_KF_MPTCP + bool + default y + +config BCM_KF_KBONDING + bool + default y + +config BCM_KF_MAP + bool + default y + +config BCM_KF_OPTEE + bool + default y + +config BCM_KF_OPTEE_414_BACKPORTS + bool + default y + +config BCM_KF_SPECTRE_PATCH + bool + default y + +config BCM_KF_OVERLAYFS_BACKPORTS + bool + default y + +config BCM_KF_GLB_COHERENCY + bool + default y + +config BCM_KF_JFFS2_OVERLAY + bool + default y + +config BCM_KF_UBIFS_OVERLAY_BACKPORTS + bool + default y + +config BCM_KF_NETFILTER_SIP + bool + default y + +config BCM_KF_RUNNER_FLOODING + bool + default y + +config BCM_KF_LOCAL_SWITCHING_DISABLE + bool + default y + +config BCM_KF_CPU_AFFINITY_HINT + bool + default y + +config BCM_KF_BPM_BUF_TRACKING + bool + default y + +config BCM_KF_BCM63XX_CPUFREQ + bool + default y + +config BCM_KF_MIPS_4350 + bool + default y + +config BCM_KF_BIN_CONFIG_INDEP + bool + default y + +config BCM_KF_CFG80211_BACKPORT + bool + default y + +config BCM_KF_OVS + bool + default y + +config BCM_KF_GPIO + bool + default y + +config BCM_KF_HWRNG + bool + default y + +config BCM_KF_VETH_IPV6_DISABLE + bool + default y + +config BCM_KF_WL_HOSTAPD + bool + default y + +config BCM_KF_NL80211_6G_BAND_SUPPORT + bool + default y + diff -urN linux-4.1.52-orig/kernel/bcm_log.c linux-4.1.52/kernel/bcm_log.c --- linux-4.1.52-orig/kernel/bcm_log.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/kernel/bcm_log.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,977 @@ +#if defined(CONFIG_BCM_KF_LOG) +/* +* <:copyright-BRCM:2010:DUAL/GPL:standard +* +* Copyright (c) 2010 Broadcom +* All Rights Reserved +* +* Unless you and Broadcom execute a separate written software license +* agreement governing use of this software, this software is licensed +* to you under the terms of the GNU General Public License version 2 +* (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +* with the following added to such license: +* +* As a special exception, the copyright holders of this software give +* you permission to link this software with independent modules, and +* to copy and distribute the resulting executable under terms of your +* choice, provided that you also meet, for each linked independent +* module, the terms and conditions of the license of that module. +* An independent module is a module which is not derived from this +* software. The special exception does not apply to any modifications +* of the software. +* +* Not withstanding the above, under no circumstances may you combine +* this software in any way with any other Broadcom software provided +* under a license other than the GPL, without Broadcom's express prior +* written consent. +* +:> +*/ + +#include /*copy_from_user*/ +#include + +#include +#include +#include +#include + + + +#define VERSION "0.1" +#define VER_STR "v" VERSION + +#define PROC_ENTRY_NAME "bcmlog" + +#if defined(BCM_DATADUMP_SUPPORTED) +#define MAX_NUM_DATADUMP_IDS 20 +#define MAX_NUM_QIDS 10 +#define PRINTBUF_SIZE 0x10000 +#endif + +#ifdef CONFIG_BCM_LOG + +#define BCM_LOG_CHECK_LOG_ID(_logId) \ + BCM_ASSERT((_logId) >= 0 && (_logId) < BCM_LOG_ID_MAX); + +#define BCM_LOG_CHECK_LOG_LEVEL(_logLevel) \ + BCM_ASSERT((_logLevel) >= 0 && (_logLevel) < BCM_LOG_LEVEL_MAX); + +#define BCM_LOG_CHECK_DD_LEVEL(_ddLevel) \ + BCM_ASSERT((_ddLevel) >= 0 && (_ddLevel) < BCM_LOG_DD_MAX); + +static bcmLogLevel_t globalLogLevel = BCM_LOG_LEVEL_DEBUG; + +static bcmLogModuleInfo_t modInfo[] = BCM_LOG_MODULE_INFO; + +#if defined(BCM_DATADUMP_SUPPORTED) +static bcmLogDataDumpLevel_t globalDataDumpLevel = BCM_LOG_DD_IMPORTANT; +static Bcm_DataDumpPrintFunc *printFuns[MAX_NUM_DATADUMP_IDS*MAX_NUM_QIDS]; +static char buf[PRINTBUF_SIZE]; +static const char* qids[MAX_NUM_QIDS]; +#endif + +static bcmFun_t* funTable[BCM_FUN_ID_MAX]; +static bcmLogSpiCallbacks_t spiFns = { .reserveSlave = NULL, + .syncTrans = NULL, + .kerSysSlaveWrite = NULL, + .kerSysSlaveRead = NULL, + .bpGet6829PortInfo = NULL}; +static int spiDev = 0; + +/** + ** Local Functions + **/ + +static char char2num(char in) { + char out; + + if ((in >= '0') && (in <= '9')) + out = (in - '0'); + else if ((in >= 'a') && (in <= 'f')) + out = (in - 'a') + 10; + else if ((in >= 'A') && (in <= 'F')) + out = (in - 'A') + 10; + else + out = 0; + + return out; +} + +static int ishex(char *str) { + return str && (str[0]=='0') && (str[1]=='x'); +} + +static uint32_t str2val(char *str) { + int i; + int value; + int base = ishex(str) ? 16 : 10; + + if (str == NULL) return(0); + + for (i=0,value=0; str[i]; i++) { + value = (value*base) + char2num(str[i]); + } + + return(value); +} + +#define UNIT_SIZE_BYTES 1 +#define UNIT_SIZE_HALFWORDS 2 +#define UNIT_SIZE_WORDS 4 +#define UNIT_SIZE_DWORDS 8 + +static void setMem(void *start, uint32_t val, uint32_t len, uint32_t unitSize) { + int i; + uint8_t* curPtr = start; + + BCM_ASSERT((unitSize == UNIT_SIZE_BYTES) || + (unitSize == UNIT_SIZE_HALFWORDS) || + (unitSize == UNIT_SIZE_WORDS) || + (unitSize == UNIT_SIZE_DWORDS)); + BCM_ASSERT(((uintptr_t)start&~(unitSize-1)) == (uintptr_t)start); + + for (i = 0; i < len; ++i) { + switch (unitSize) { + case UNIT_SIZE_BYTES: + { + *curPtr = (uint8_t)val; + break; + } + case UNIT_SIZE_HALFWORDS: + { + uint16_t *cur16Ptr = (uint16_t*)curPtr; + *cur16Ptr = (uint16_t)val; + break; + } + case UNIT_SIZE_WORDS: + { + uint32_t *cur32Ptr = (uint32_t*)curPtr; + *cur32Ptr = (uint32_t)val; + break; + } +#if defined (CONFIG_64BIT) + case UNIT_SIZE_DWORDS: + { + uint64_t *cur64Ptr = (uint64_t*)curPtr; + *cur64Ptr = (uint64_t)val; + break; + } +#endif + default: + break; + } + + curPtr += unitSize; + } +} + +static void dumpHexData(void *start, uint32_t len, uint32_t unitSize, int bSpiRead) +{ + int i; + unsigned int temp; + /*Force natural alignment*/ + uint8_t* curPtr; + + BCM_ASSERT((unitSize == UNIT_SIZE_BYTES) || + (unitSize == UNIT_SIZE_HALFWORDS) || + (unitSize == UNIT_SIZE_WORDS) || + (unitSize == UNIT_SIZE_DWORDS)); + + curPtr = (uint8_t*)((uintptr_t)start&(~(unitSize-1))); + + for (i = 0; i < len; ++i) { + if (i % (4/unitSize) == 0) + bcm_printk(" "); + if (i % (16/unitSize) == 0) + bcm_printk("\n0x%p : ", curPtr); + + switch (unitSize) { + case UNIT_SIZE_BYTES: + { + if ( bSpiRead ) + { + spiFns.kerSysSlaveRead(spiDev, (uintptr_t)curPtr, &temp, unitSize); + bcm_printk("%02X ", (unsigned char)temp); + } + else + + { + bcm_printk("%02X ", *curPtr); + } + break; + } + case UNIT_SIZE_HALFWORDS: + { + uint16_t *cur16Ptr = (uint16_t*)curPtr; + if ( bSpiRead ) + { + spiFns.kerSysSlaveRead(spiDev, (uintptr_t)curPtr, &temp, unitSize); + bcm_printk("%04X ", (unsigned short)temp); + } + else + { + bcm_printk("%04X ", *cur16Ptr); + } + break; + } + case UNIT_SIZE_WORDS: + { + uint32_t *cur32Ptr = (uint32_t*)curPtr; + if ( bSpiRead ) + { + spiFns.kerSysSlaveRead(spiDev, (uintptr_t)curPtr, &temp, unitSize); + bcm_printk("%08X ", (unsigned int)temp); + } + else + { + bcm_printk("%08X ", *cur32Ptr); + } + break; + } + default: + break; + } + + curPtr += unitSize; + } + + bcm_printk("\n"); +} + +static bcmLogModuleInfo_t *getModInfoByName(char *name) { + int logId; + + for(logId=0; logId LOG_WR_KBUF_SIZE-1) || (copy_from_user(kbuf, buf, cnt) != 0)) + return -EFAULT; + + kbuf[cnt]=0; + + argc = sscanf(kbuf, "%c %s %s %s %s %s", &cmd, arg[0], arg[1], arg[2], arg[3], arg[4]); + + for (i=0; i= 0 && logLevel < BCM_LOG_LEVEL_MAX) + bcmLog_setGlobalLogLevel(logLevel); + else + BCM_LOG_ERROR(BCM_LOG_ID_LOG, "Invalid Parameter '%s'\n", arg[0]); + break; + } ) + + BCM_LOGCODE( + case 'r': + { + bcm_printk ("Global Log Level : %d\n", bcmLog_getGlobalLogLevel()); + break; + } ) + + BCM_LOGCODE( + case 'i': + { + if (argc == 1) { + int logId; + for(logId=0; logIdname); + bcm_printk("Id : %d, Log Level : %d\n", pModInfo->logId, bcmLog_getLogLevel(pModInfo->logId)); + } + } + else if((argc==2) && ((pModInfo=getModInfoByName(arg[0])) != NULL)) { + bcm_printk("Name : %s\n", pModInfo->name); + bcm_printk("Id : %d, Log Level : %d\n", pModInfo->logId, bcmLog_getLogLevel(pModInfo->logId)); + } else { + BCM_LOG_ERROR(BCM_LOG_ID_LOG, "Invalid Parameter '%s'\n", arg[0]); + } + break; + } ) + + BCM_LOGCODE( + case 'l': + { + bcmLogLevel_t logLevel = str2val(arg[1]); + if(argc == 3 && ((pModInfo=getModInfoByName(arg[0])) != NULL)) { + if(logLevel >= 0 && logLevel < BCM_LOG_LEVEL_MAX) { + bcmLog_setLogLevel( pModInfo->logId, logLevel); + break; + } + } + + BCM_LOG_ERROR(BCM_LOG_ID_LOG, "Invalid Parameters '%s' '%s'\n", arg[0], arg[1]); + + break; + } ) + + BCM_DATADUMPCODE( + case 'd': + { + bcmLogDataDumpLevel_t ddLevel = str2val(arg[1]); + if(argc == 3 && ((pModInfo=getModInfoByName(arg[0])) != NULL)) { + if(ddLevel >= 0 && ddLevel < BCM_LOG_DD_MAX) { + pModInfo->ddLevel = ddLevel; + break; + } + } + + BCM_LOG_ERROR(BCM_LOG_ID_LOG, "Invalid Parameters '%s' '%s'\n", arg[0], arg[1]); + + break; + } ) + + BCM_DATADUMPCODE( + case 'e': + { + if (argc == 1) { + int logId; + for(logId=0; logIdname); + bcm_printk("Id : %d, DataDump Level : %d\n", pModInfo->logId, pModInfo->ddLevel); + } + } + else if((argc==2) && ((pModInfo=getModInfoByName(arg[0])) != NULL)) { + bcm_printk("Name : %s\n", pModInfo->name); + bcm_printk("Id : %d, DataDump Level : %d\n", pModInfo->logId, pModInfo->ddLevel); + } else { + BCM_LOG_ERROR(BCM_LOG_ID_LOG, "Invalid Parameter '%s'\n", arg[0]); + } + break; + } ) + BCM_DATADUMPCODE( + case 'h': + { + bcmLogDataDumpLevel_t ddLevel = str2val(arg[0]); + if(argc == 2 && ddLevel >= 0 && ddLevel < BCM_LOG_DD_MAX) + globalDataDumpLevel = ddLevel; + else + BCM_LOG_ERROR(BCM_LOG_ID_LOG, "Invalid Parameter '%s'\n", arg[0]); + break; + } ) + BCM_LOGCODE( + case 's': + { + bcm_printk ("Global Datadump Level : %d\n", globalDataDumpLevel); + break; + } ) + case 'm': + { + uintptr_t addr = 0; + uint32_t len = 1; + uint32_t unitSize = UNIT_SIZE_BYTES; + int cmdValid = 1; + + if ((argc < 3) || (argc > 4)) { + cmdValid = 0; + } + else { + if (!ishex(arg[0])) { + cmdValid = 0; + BCM_LOG_ERROR(BCM_LOG_ID_LOG, "Incorrect address: %s Must be in hex., starting with 0x\n", arg[0]); + } + else { + addr = str2val(arg[0]); + } + + if (argc >= 3) + len = str2val(arg[1]); + + if (argc == 4) { + switch (arg[2][0]) { + case 'b': + unitSize = UNIT_SIZE_BYTES; + break; + case 'h': + unitSize = UNIT_SIZE_HALFWORDS; + break; + case 'w': + unitSize = UNIT_SIZE_WORDS; + break; + default: + BCM_LOG_ERROR(BCM_LOG_ID_LOG, "Incorrect unit size '%s', must be 'b', 'h' or 'w'\n", arg[2]); + cmdValid = 0; + } + } + } + + if (cmdValid) { + dumpHexData((void *)addr, len, unitSize, 0); + } else { + BCM_LOG_ERROR(BCM_LOG_ID_LOG, "Invalid Command: %s", kbuf); + } + break; + } + + case 'w': + { + uintptr_t addr = 0; + uint32_t val = 0; + uint32_t len = 1; + uint32_t unitSize = UNIT_SIZE_BYTES; + int cmdValid = 1; + + if ((argc < 3) || (argc > 5)) { + cmdValid = 0; + } + else { + if (!ishex(arg[0])) { + cmdValid = 0; + BCM_LOG_ERROR(BCM_LOG_ID_LOG, "Incorrect address: %s Must be in hex., starting with 0x\n", arg[0]); + } + else { + addr = str2val(arg[0]); + } + + val = str2val(arg[1]); + + if (argc >= 4) { + len = str2val(arg[2]); + } + + if (argc == 5) { + switch (arg[3][0]) { + case 'b': + unitSize = UNIT_SIZE_BYTES; + break; + case 'h': + unitSize = UNIT_SIZE_HALFWORDS; + break; + case 'w': + unitSize = UNIT_SIZE_WORDS; + break; + default: + BCM_LOG_ERROR(BCM_LOG_ID_LOG, "Incorrect unit size '%s', must be 'b', 'h' or 'w'\n", arg[3]); + cmdValid = 0; + } + } + } + + if ((addr&~(unitSize-1)) != addr) { + BCM_LOG_ERROR(BCM_LOG_ID_LOG, "Incorrect address alignment: 0x%lx\n", addr); + cmdValid = 0; + } + + if (cmdValid) { + setMem((void *)addr, val, len, unitSize); + bcm_printk("Done.\n"); + } else { + BCM_LOG_ERROR(BCM_LOG_ID_LOG, "Invalid Command: %s", kbuf); + } + break; + } + case 'p': + { + // Generic SPI commands + // Should be usable with any SPI device + // Leg(0)/HS(1), CS (0-3), CLK Speed, Write Data (hex), length + uint32_t busnum = 0; + uint32_t chipsel = 0; + uint32_t clkspeed = 0; + uint32_t writeint = 0; + uint32_t length = 0; + unsigned char txbuf[32]; + unsigned char rxbuf[32]; + int cmdValid = 1; + + if (argc != 6) { + cmdValid = 0; + } + else if (spiFns.syncTrans == NULL) { + BCM_LOG_ERROR(BCM_LOG_ID_LOG, "Attempt to use spi before registered\n"); + cmdValid = 0; + } + else { + if (!ishex(arg[3])) { + cmdValid = 0; + BCM_LOG_ERROR(BCM_LOG_ID_LOG, "Incorrect write data: %s Must be in hex., starting with 0x\n", arg[3]); + } + else { + // Pad the write buffer with 0s to ensure it is a complete 4 bytes word + for (i=0;i<10;++i) { + if(arg[3][i] == 0) { + arg[3][i] = 0x30; + } + } + arg[3][10] = 0; + + if ((length = str2val(arg[4])) > 32) { + cmdValid = 0; + BCM_LOG_ERROR(BCM_LOG_ID_LOG, "Incorrect length: Must be <= 32\n"); + } + else { + busnum = str2val(arg[0]); + chipsel = str2val(arg[1]); + clkspeed = str2val(arg[2]); + writeint = str2val(arg[3]); + memset(txbuf,0,sizeof(txbuf)); + memset(rxbuf,0,sizeof(txbuf)); + txbuf[0] = (writeint >> 24) & 0x000000FF; + txbuf[1] = (writeint >> 16) & 0x000000FF; + txbuf[2] = (writeint >> 8) & 0x000000FF; + txbuf[3] = (writeint >> 0) & 0x000000FF; + if (0 != spiFns.reserveSlave(busnum, chipsel, clkspeed)) + { + bcm_printk ("Spi device already reserved, clkspeed parameter ignored\n"); + } + spiFns.syncTrans(txbuf, rxbuf, 0, length, busnum, chipsel); + bcm_printk ("Transmitted:\n"); + dumpHexData((void *)txbuf, length, UNIT_SIZE_BYTES, 0); + bcm_printk ("Received:\n"); + dumpHexData((void *)rxbuf, length, UNIT_SIZE_BYTES, 0); + } + } + } + + if (0 == cmdValid) { + BCM_LOG_ERROR(BCM_LOG_ID_LOG, "Invalid Command: %s", kbuf); + } + break; + } +#if defined(CONFIG_BCM963268) + // SPI command to read/write to an external BRCM chip configured as spi slave device (eg. 6829 for BHR) + case 'u': + { + unsigned int addr = 0; + unsigned int val = 0; + unsigned int rwCount = 0; + int unitSize = 0; + int loopCount; + int cmdValid = 1; + if (spiFns.kerSysSlaveRead == NULL) + { + BCM_LOG_ERROR(BCM_LOG_ID_LOG, "SPI slave not registered"); + cmdValid = 0; + } + if ((0 == cmdValid) || ((argc != 5) && (argc != 6))) + { + cmdValid = 0; + } + else + { + if( ((spiDev = str2val(arg[0])) < 0) || !ishex(arg[1]) ) + { + cmdValid = 0; + BCM_LOG_ERROR(BCM_LOG_ID_LOG, "Incorrect address: %s Must be in hex., starting with 0x or device number %s\n", arg[1], arg[0]); + } + else + { + char trSize; + + addr = str2val(arg[1]); + trSize = arg[2][0]; + rwCount = str2val(arg[3]); + if ( 6 == argc ) + val = str2val(arg[4]); + + switch (trSize) + { + case 'b': + unitSize = 1; + break; + case 'h': + unitSize = 2; + break; + case 'w': + unitSize = 4; + break; + default: + BCM_LOG_ERROR(BCM_LOG_ID_LOG, "Incorrect unit size '%s', must be 'b', 'h' or 'w'\n", arg[2]); + cmdValid = 0; + unitSize = 0; + break; + } + + if ( 1 == cmdValid ) + { + if ( 5 == argc ) + { + /* read operation */ + dumpHexData((void *)addr, rwCount, unitSize, 1); + } + else + { + /* write operation */ + for ( loopCount = 0; loopCount < rwCount; loopCount++ ) + { + spiFns.kerSysSlaveWrite(spiDev, addr, val, unitSize); + addr += unitSize; + } + } + } + } + } + + if (0 == cmdValid) + { + BCM_LOG_ERROR(BCM_LOG_ID_LOG, "Invalid Command: %s", kbuf); + } + break; + } +#endif + default: + { + bcm_printk("Usage:\n"); + + BCM_LOGCODE( + bcm_printk("g : Set global log level\n"); + bcm_printk("r : Get global log level\n"); + bcm_printk("l : Set the log level of a module\n"); + bcm_printk("i [] : Get module information\n"); + ) + + BCM_DATADUMPCODE( + bcm_printk("h : Set global datadump level\n"); + bcm_printk("s : Get global datadump level\n"); + bcm_printk("d : Set data dump detail level\n"); + bcm_printk("e [] : Get data dump detail level\n"); + ) + + bcm_printk("m [ []]: Dump a memory region\n"); + bcm_printk("w [ []]: Write to a memory region\n"); + break; + } + } + + return cnt; +} + +static struct file_operations log_proc_fops = { + .read = log_proc_read, + .write = log_proc_write, +}; + + + +/** + ** Helper Functions + **/ + +bcmLogModuleInfo_t *bcmLog_logIsEnabled(bcmLogId_t logId, bcmLogLevel_t logLevel) { + BCM_LOG_CHECK_LOG_ID(logId); + BCM_LOG_CHECK_LOG_LEVEL(logLevel); + + if(globalLogLevel >= logLevel && + modInfo[logId].logLevel >= logLevel) + return &modInfo[logId]; + + return NULL; +} + +#if defined(BCM_DATADUMP_SUPPORTED) +bcmLogModuleInfo_t *bcmLog_ddIsEnabled(bcmLogId_t logId, bcmLogDataDumpLevel_t ddLevel) { + BCM_LOG_CHECK_LOG_ID(logId); + BCM_LOG_CHECK_DD_LEVEL(ddLevel); + + if(globalDataDumpLevel >= ddLevel && + modInfo[logId].ddLevel >= ddLevel) + return &modInfo[logId]; + + return NULL; +} +#endif + +/** + ** Public API + **/ + +void bcmLog_setGlobalLogLevel(bcmLogLevel_t logLevel) { + + bcmLogId_t logId; + bcmLogLevel_t oldGlobalLevel; + + BCM_LOG_CHECK_LOG_LEVEL(logLevel); + + oldGlobalLevel = globalLogLevel; + globalLogLevel = logLevel; + + for (logId = 0; logId < BCM_LOG_ID_MAX; logId++) + { + if (modInfo[logId].lcCallback) + { + bcmLogLevel_t oldLevel; + bcmLogLevel_t newLevel; + + oldLevel = min(modInfo[logId].logLevel, oldGlobalLevel); + newLevel = min(modInfo[logId].logLevel, globalLogLevel); + if (oldLevel != newLevel) + { + modInfo[logId].lcCallback(logId, newLevel, modInfo[logId].lcCallbackCtx); + } + } + } + + BCM_LOG_INFO(BCM_LOG_ID_LOG, "Global log level was set to %d", globalLogLevel); +} + +bcmLogLevel_t bcmLog_getGlobalLogLevel(void) { + return globalLogLevel; +} + +void bcmLog_setLogLevel(bcmLogId_t logId, bcmLogLevel_t logLevel) { + + bcmLogLevel_t oldLocalLevel; + + BCM_LOG_CHECK_LOG_ID(logId); + BCM_LOG_CHECK_LOG_LEVEL(logLevel); + + oldLocalLevel = modInfo[logId].logLevel; + modInfo[logId].logLevel = logLevel; + + if (modInfo[logId].lcCallback) + { + bcmLogLevel_t newLevel; + bcmLogLevel_t oldLevel; + + oldLevel = min(oldLocalLevel, globalLogLevel); + newLevel = min(modInfo[logId].logLevel, globalLogLevel); + + if (oldLevel != newLevel) + { + modInfo[logId].lcCallback(logId, newLevel, modInfo[logId].lcCallbackCtx); + } + } + + BCM_LOG_INFO(BCM_LOG_ID_LOG, "Log level of %s was set to %d", + modInfo[logId].name, modInfo[logId].logLevel); +} + + +void bcmLog_registerLevelChangeCallback(bcmLogId_t logId, bcmLogLevelChangeCallback_t callback, void *ctx) { + BCM_LOG_CHECK_LOG_ID(logId); + + modInfo[logId].lcCallback = callback; + modInfo[logId].lcCallbackCtx = ctx; +} + + +bcmLogLevel_t bcmLog_getLogLevel(bcmLogId_t logId) { + BCM_LOG_CHECK_LOG_ID(logId); + return modInfo[logId].logLevel; +} + +char *bcmLog_getModName(bcmLogId_t logId) { + BCM_LOG_CHECK_LOG_ID(logId); + return modInfo[logId].name; +} + +void bcmFun_reg(bcmFunId_t funId, bcmFun_t *f) { + BCM_ASSERT(f); + BCM_ASSERT(funId < BCM_FUN_ID_MAX); + + funTable[funId] = f; +} + +void bcmFun_dereg(bcmFunId_t funId) { + BCM_ASSERT(funId < BCM_FUN_ID_MAX); + + funTable[funId] = 0; +} + +bcmFun_t* bcmFun_get(bcmFunId_t funId) { + BCM_ASSERT(funId < BCM_FUN_ID_MAX); + + return funTable[funId]; +} + +#if defined(BCM_DATADUMP_SUPPORTED) +/*Dummy implementation*/ +void bcm_dataDumpRegPrinter(uint32_t qId, uint32_t dataDumpId, Bcm_DataDumpPrintFunc *printFun) { + BCM_ASSERT(qId < MAX_NUM_QIDS); + BCM_ASSERT(dataDumpId < MAX_NUM_DATADUMP_IDS); + printFuns[qId*MAX_NUM_DATADUMP_IDS + dataDumpId] = printFun; +} + +/*Dummy implementation*/ +void bcm_dataDump(uint32_t qID, uint32_t dataDumpID, const char* dataDumpName, void *ptr, uint32_t numBytes) { + Bcm_DataDumpPrintFunc* printFun; + BCM_ASSERT( qID < MAX_NUM_QIDS); + BCM_ASSERT( dataDumpID < MAX_NUM_DATADUMP_IDS); + bcm_printk("---DataDump Start---\n"); + if (qids[qID] == 0) { + BCM_LOG_ERROR(BCM_LOG_ID_LOG, "DataDump qID %d not registered.\n", qID); + } + else { + printFun = printFuns[qID*MAX_NUM_DATADUMP_IDS + dataDumpID]; + bcm_printk("qID: %s, DataDump ID: %s, numBytes: %d\n", qids[qID], dataDumpName, numBytes); + if (printFun) { + buf[0]=0; + (*printFun)(dataDumpID, ptr, numBytes, buf, PRINTBUF_SIZE); + bcm_printk(buf); + } + else { + uint32_t *data = ptr; + uint8_t *dataBytes; + int i=0; + + while (i+16<=numBytes) { + bcm_printk("%4.4x: 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x\n", i, data[i/4], data[i/4+1], data[i/4+2], data[i/4+3]); + i+=16; + } + + if (i+4<=numBytes) { + bcm_printk("%4.4x: ", i); + while (i+4<=numBytes) { + bcm_printk("0x%8.8x ", data[i/4]); + i+=4; + } + } + + if (i< numBytes) { + if (i % 16 == 0) { + bcm_printk("%4.4x: ", i); + } + + dataBytes = (uint8_t*)&data[i/4]; + bcm_printk("0x"); + while (i #include +#if defined(CONFIG_BCM_KF_BUZZZ) && defined(CONFIG_BUZZZ_KEVT) +#include +#endif #include "internals.h" @@ -29,6 +32,9 @@ */ void handle_bad_irq(unsigned int irq, struct irq_desc *desc) { +#if defined(CONFIG_BCM_KF_BUZZZ) && defined(CONFIG_BUZZZ_KEVT) + BUZZZ_KNL3(IRQ_BAD, irq, 0); +#endif print_irq_desc(irq, desc); kstat_incr_irqs_this_cpu(irq, desc); ack_bad_irq(irq); @@ -130,17 +136,94 @@ wake_up_process(action->thread); } +#if defined(CONFIG_BCM_KF_HARDIRQ_CYCLES) +/* see the description in arch/mips/bcm963xx/Kconfig */ +struct kernel_stat_shadow { + struct cpu_usage_stat last_cpustat; /* cpustat when we started accumulating */ + unsigned int start_cnt; /**< c0 count when starting hardirq */ + unsigned int accumulated_cnt; /**< cycles accumulated so far */ + unsigned int intrs; /**< debug only, how many intrs accumulate whole tick */ + /* we could even expand this structure to keep track of cycle counts on a + * per interrupt basis and find out which interrupt is using too many + * cycles. Surprisingly, the timer interrupt seems to take about 10-15us. + */ +}; + +DEFINE_PER_CPU(struct kernel_stat_shadow, kstat_shadow); +static unsigned int cycles_per_tick; +extern unsigned int mips_hpt_frequency; + +static void start_hardirq_count(void) +{ + struct kernel_stat_shadow *ks_shadow = &per_cpu(kstat_shadow, smp_processor_id()); + ks_shadow->start_cnt = read_c0_count(); +} + +static void stop_hardirq_count(void) +{ + unsigned int end_cnt = read_c0_count(); + struct kernel_stat_shadow *ks_shadow; + ks_shadow = &per_cpu(kstat_shadow, smp_processor_id()); + ks_shadow->intrs++; + if (end_cnt > ks_shadow->start_cnt) + ks_shadow->accumulated_cnt += end_cnt - ks_shadow->start_cnt; + else + //counter rolled over + ks_shadow->accumulated_cnt += (UINT_MAX - ks_shadow->start_cnt) + end_cnt; + + if (cycles_per_tick == 0) { + cycles_per_tick = mips_hpt_frequency/HZ; + } + + // See if we have accumulated a whole tick + if (ks_shadow->accumulated_cnt >= cycles_per_tick) { + struct cpu_usage_stat *cpustat = &kstat_this_cpu.cpustat; + cputime64_t user_delta = cpustat->user - ks_shadow->last_cpustat.user; + cputime64_t system_delta = cpustat->system - ks_shadow->last_cpustat.system; + cputime64_t softirq_delta = cpustat->softirq - ks_shadow->last_cpustat.softirq; + cputime64_t idle_delta = cpustat->idle - ks_shadow->last_cpustat.idle; + +// printk("TICK on %d in %d intrs!\n", smp_processor_id(), ks_shadow->intrs); + cpustat->irq++; + // subtract 1 tick from the field that has incremented the most + if (user_delta > system_delta && user_delta > softirq_delta && user_delta > idle_delta) + cpustat->user--; + else if (system_delta > user_delta && system_delta > softirq_delta && system_delta > idle_delta) + cpustat->system--; + else if (softirq_delta > user_delta && softirq_delta > system_delta && softirq_delta > idle_delta) + cpustat->softirq--; + else + cpustat->idle--; + + ks_shadow->accumulated_cnt -= cycles_per_tick; + ks_shadow->intrs = 0; + ks_shadow->last_cpustat = *cpustat; + } +} +#endif + + irqreturn_t handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action) { irqreturn_t retval = IRQ_NONE; unsigned int flags = 0, irq = desc->irq_data.irq; +#if defined(CONFIG_BCM_KF_HARDIRQ_CYCLES) + start_hardirq_count(); +#endif + do { irqreturn_t res; trace_irq_handler_entry(irq, action); +#if defined(CONFIG_BCM_KF_BUZZZ) && defined(CONFIG_BUZZZ_KEVT) + BUZZZ_KNL3(IRQ_ENT, irq, action->handler); +#endif res = action->handler(irq, action->dev_id); +#if defined(CONFIG_BCM_KF_BUZZZ) && defined(CONFIG_BUZZZ_KEVT) + BUZZZ_KNL3(IRQ_EXT, irq, action->handler); +#endif trace_irq_handler_exit(irq, action, res); if (WARN_ONCE(!irqs_disabled(),"irq %u handler %pF enabled interrupts\n", @@ -177,6 +260,11 @@ if (!noirqdebug) note_interrupt(irq, desc, retval); + +#if defined(CONFIG_BCM_KF_HARDIRQ_CYCLES) + stop_hardirq_count(); +#endif + return retval; } diff -urN linux-4.1.52-orig/kernel/irq/irqdesc.c linux-4.1.52/kernel/irq/irqdesc.c --- linux-4.1.52-orig/kernel/irq/irqdesc.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/kernel/irq/irqdesc.c 2020-02-07 01:47:54.000000000 +0800 @@ -24,11 +24,36 @@ static struct lock_class_key irq_desc_lock_class; #if defined(CONFIG_SMP) +#if defined(CONFIG_BCM_KF_IRQ_AFFINITY) +static int __init irq_affinity_setup(char *str) +{ + zalloc_cpumask_var(&irq_default_affinity, GFP_NOWAIT); + cpulist_parse(str, irq_default_affinity); + /* + * Set at least the boot cpu. We don't want to end up with + * bugreports caused by random comandline masks + */ + cpumask_set_cpu(smp_processor_id(), irq_default_affinity); + return 1; +} +__setup("irqaffinity=", irq_affinity_setup); + +static void __init init_irq_default_affinity(void) +{ +#ifdef CONFIG_CPUMASK_OFFSTACK + if (!irq_default_affinity) + zalloc_cpumask_var(&irq_default_affinity, GFP_NOWAIT); +#endif + if (cpumask_empty(irq_default_affinity)) + cpumask_setall(irq_default_affinity); +} +#else static void __init init_irq_default_affinity(void) { alloc_cpumask_var(&irq_default_affinity, GFP_NOWAIT); cpumask_setall(irq_default_affinity); } +#endif #else static void __init init_irq_default_affinity(void) { diff -urN linux-4.1.52-orig/kernel/irq/manage.c linux-4.1.52/kernel/irq/manage.c --- linux-4.1.52-orig/kernel/irq/manage.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/kernel/irq/manage.c 2020-02-07 01:47:54.000000000 +0800 @@ -1095,10 +1095,17 @@ * set the trigger type must match. Also all must * agree on ONESHOT. */ +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +#else unsigned int oldtype = irqd_get_trigger_type(&desc->irq_data); +#endif if (!((old->flags & new->flags) & IRQF_SHARED) || +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK) || +#else (oldtype != (new->flags & IRQF_TRIGGER_MASK)) || +#endif ((old->flags ^ new->flags) & IRQF_ONESHOT)) goto mismatch; diff -urN linux-4.1.52-orig/kernel/irq/resend.c linux-4.1.52/kernel/irq/resend.c --- linux-4.1.52-orig/kernel/irq/resend.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/kernel/irq/resend.c 2020-02-07 01:47:54.000000000 +0800 @@ -17,6 +17,9 @@ #include #include #include +#if defined(CONFIG_BCM_KF_BUZZZ) && defined(CONFIG_BUZZZ_KEVT) +#include +#endif #include "internals.h" @@ -35,6 +38,9 @@ while (!bitmap_empty(irqs_resend, nr_irqs)) { irq = find_first_bit(irqs_resend, nr_irqs); +#if defined(CONFIG_BCM_KF_BUZZZ) && defined(CONFIG_BUZZZ_KEVT) + BUZZZ_KNL3(IRQ_RESEND, irq, 0); +#endif clear_bit(irq, irqs_resend); desc = irq_to_desc(irq); local_irq_disable(); @@ -55,6 +61,10 @@ */ void check_irq_resend(struct irq_desc *desc, unsigned int irq) { +#if defined(CONFIG_BCM_KF_BUZZZ) && defined(CONFIG_BUZZZ_KEVT) + BUZZZ_KNL3(IRQ_CHECK, irq, 0); +#endif + /* * We do not resend level type interrupts. Level type * interrupts are resent by hardware when they are still diff -urN linux-4.1.52-orig/kernel/irq/spurious.c linux-4.1.52/kernel/irq/spurious.c --- linux-4.1.52-orig/kernel/irq/spurious.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/kernel/irq/spurious.c 2020-02-07 01:47:54.000000000 +0800 @@ -13,6 +13,9 @@ #include #include #include +#if defined(CONFIG_BCM_KF_BUZZZ) && defined(CONFIG_BUZZZ_KEVT) +#include +#endif #include "internals.h" @@ -37,6 +40,9 @@ */ bool irq_wait_for_poll(struct irq_desc *desc) { +#if defined(CONFIG_BCM_KF_BUZZZ) && defined(CONFIG_BUZZZ_KEVT) + BUZZZ_KNL3(IRQ_WAIT_POLL, desc->irq_data.irq, 0); +#endif if (WARN_ONCE(irq_poll_cpu == smp_processor_id(), "irq poll in progress on cpu %d for irq %d\n", smp_processor_id(), desc->irq_data.irq)) @@ -65,6 +71,9 @@ irqreturn_t ret = IRQ_NONE; struct irqaction *action; +#if defined(CONFIG_BCM_KF_BUZZZ) && defined(CONFIG_BUZZZ_KEVT) + BUZZZ_KNL3(IRQ_MISROUTED, irq, 0); +#endif raw_spin_lock(&desc->lock); /* diff -urN linux-4.1.52-orig/kernel/irq_work.c linux-4.1.52/kernel/irq_work.c --- linux-4.1.52-orig/kernel/irq_work.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/kernel/irq_work.c 2020-02-07 01:47:54.000000000 +0800 @@ -17,9 +17,11 @@ #include #include #include +#if defined(CONFIG_BCM_KF_BUZZZ) && defined(CONFIG_BUZZZ_KEVT) +#include +#endif #include - static DEFINE_PER_CPU(struct llist_head, raised_list); static DEFINE_PER_CPU(struct llist_head, lazy_list); @@ -75,6 +77,10 @@ if (!irq_work_claim(work)) return false; +#if defined(CONFIG_BCM_KF_BUZZZ) && defined(CONFIG_BUZZZ_KEVT) + BUZZZ_KNL3(IRQ_WORK, cpu, work->func); +#endif + if (llist_add(&work->llnode, &per_cpu(raised_list, cpu))) arch_send_call_function_single_ipi(cpu); diff -urN linux-4.1.52-orig/kernel/Makefile linux-4.1.52/kernel/Makefile --- linux-4.1.52-orig/kernel/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/kernel/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -2,6 +2,7 @@ # Makefile for the linux kernel. # + obj-y = fork.o exec_domain.o panic.o \ cpu.o exit.o softirq.o resource.o \ sysctl.o sysctl_binary.o capability.o ptrace.o user.o \ @@ -90,6 +91,10 @@ obj-$(CONFIG_CPU_PM) += cpu_pm.o obj-$(CONFIG_BPF) += bpf/ +ifdef BCM_KF # defined(CONFIG_BCM_KF_LOG) +obj-y += bcm_log.o +endif # BCM_KF + obj-$(CONFIG_PERF_EVENTS) += events/ obj-$(CONFIG_USER_RETURN_NOTIFIER) += user-return-notifier.o diff -urN linux-4.1.52-orig/kernel/module.c linux-4.1.52/kernel/module.c --- linux-4.1.52-orig/kernel/module.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/kernel/module.c 2020-02-07 01:47:54.000000000 +0800 @@ -3791,6 +3791,7 @@ /* Used by oprofile and other similar tools. */ seq_printf(m, " 0x%pK", mod->module_core); + /* Taints info */ if (mod->taints) seq_printf(m, " %s", module_flags(mod, buf)); @@ -3951,6 +3952,15 @@ if (mod->state == MODULE_STATE_UNFORMED) continue; pr_cont(" %s%s", mod->name, module_flags(mod, buf)); +#if defined(CONFIG_BCM_KF_EXTRA_DEBUG) + { + printk(" init_addr(%p - %p), core_addr(%p - %p)\n", + mod->module_init, + mod->module_init+mod->init_text_size, + mod->module_core, + mod->module_core+mod->core_text_size); + } +#endif } preempt_enable(); if (last_unloaded_module[0]) diff -urN linux-4.1.52-orig/kernel/printk/printk.c linux-4.1.52/kernel/printk/printk.c --- linux-4.1.52-orig/kernel/printk/printk.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/kernel/printk/printk.c 2020-02-07 01:47:54.000000000 +0800 @@ -1474,6 +1474,11 @@ { unsigned int cpu = smp_processor_id(); +#if defined(CONFIG_BCM_KF_PRINTK_INT_ENABLED) && defined(CONFIG_BCM_PRINTK_INT_ENABLED) + if(oops_in_progress || early_boot_irqs_disabled || + preempt_count() > 1 || irqs_disabled()) + return 0; +#endif if (!console_trylock()) return 0; /* @@ -2143,11 +2148,16 @@ goto out; len = cont_print_text(text, size); +#if defined(CONFIG_BCM_KF_PRINTK_INT_ENABLED) && defined(CONFIG_BCM_PRINTK_INT_ENABLED) + raw_spin_unlock_irqrestore(&logbuf_lock, flags); + call_console_drivers(cont.level, text, len); +#else raw_spin_unlock(&logbuf_lock); stop_critical_timings(); call_console_drivers(cont.level, text, len); start_critical_timings(); local_irq_restore(flags); +#endif return; out: raw_spin_unlock_irqrestore(&logbuf_lock, flags); @@ -2246,13 +2256,17 @@ console_idx = log_next(console_idx); console_seq++; console_prev = msg->flags; +#if defined(CONFIG_BCM_KF_PRINTK_INT_ENABLED) && defined(CONFIG_BCM_PRINTK_INT_ENABLED) + raw_spin_unlock_irqrestore(&logbuf_lock, flags); + call_console_drivers(level, text, len); +#else raw_spin_unlock(&logbuf_lock); stop_critical_timings(); /* don't trace print latency */ call_console_drivers(level, text, len); start_critical_timings(); local_irq_restore(flags); - +#endif if (do_cond_resched) cond_resched(); } diff -urN linux-4.1.52-orig/kernel/sched/core.c linux-4.1.52/kernel/sched/core.c --- linux-4.1.52-orig/kernel/sched/core.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/kernel/sched/core.c 2020-02-07 01:47:54.000000000 +0800 @@ -90,6 +90,10 @@ #define CREATE_TRACE_POINTS #include +#if defined(CONFIG_BCM_KF_BUZZZ) && defined(CONFIG_BUZZZ_KEVT) +#include +#endif + void start_bandwidth_timer(struct hrtimer *period_timer, ktime_t period) { unsigned long delta; @@ -296,7 +300,11 @@ * period over which we measure -rt task cpu usage in us. * default: 1s */ +#if defined(CONFIG_BCM_KF_SCHED_RT) && defined(CONFIG_BCM_SCHED_RT_PERIOD) +unsigned int sysctl_sched_rt_period = CONFIG_BCM_SCHED_RT_PERIOD; +#else unsigned int sysctl_sched_rt_period = 1000000; +#endif __read_mostly int scheduler_running; @@ -304,7 +312,12 @@ * part of the period that we allow rt tasks to run in us. * default: 0.95s */ +#if defined(CONFIG_BCM_KF_SCHED_RT) && defined(CONFIG_BCM_SCHED_RT_RUNTIME) +/* RT task takes 100% of time */ +int sysctl_sched_rt_runtime = CONFIG_BCM_SCHED_RT_RUNTIME; +#else int sysctl_sched_rt_runtime = 950000; +#endif /* cpus with isolated domains */ cpumask_var_t cpu_isolated_map; @@ -344,6 +357,9 @@ struct rq *rq = container_of(timer, struct rq, hrtick_timer); WARN_ON_ONCE(cpu_of(rq) != smp_processor_id()); +#if defined(CONFIG_BCM_KF_BUZZZ) && defined(CONFIG_BUZZZ_KEVT) + BUZZZ_KNL3(SCHED_HRTICK, 0, 0); +#endif raw_spin_lock(&rq->lock); update_rq_clock(rq); @@ -1308,6 +1324,11 @@ const struct cpumask *nodemask = NULL; enum { cpuset, possible, fail } state = cpuset; int dest_cpu; +#if defined(CONFIG_BCM_KF_CPU_AFFINITY_HINT) && \ + defined(CONFIG_BCM_PROC_CPU_AFFINITY_HINT) + struct cpumask cpus_valid; + int v; +#endif /* * If the node that the cpu is on has been offlined, cpu_to_node() @@ -1317,6 +1338,36 @@ if (nid != -1) { nodemask = cpumask_of_node(nid); +#if defined(CONFIG_BCM_KF_CPU_AFFINITY_HINT) && \ + defined(CONFIG_BCM_PROC_CPU_AFFINITY_HINT) + /* + * As cpu_active_mask is always a subset of cpu_online_mask, + * any CPU on this node which is also active and allowed could + * be selected as the dest_cpu. We prefer such CPU which is + * in addition hinted, if it exists. + * + * valid = (node & active & allowed); + * if (valid) { + * if (valid & hint) { + * recover allowed as hint; + * return dest_cpu = any_of(valid & hint); + * } + * return dest_cpu = any_of(valid); + * } + * goto try_other_nodes; + */ + v = cpumask_and(&cpus_valid, nodemask, cpu_active_mask) && + cpumask_and(&cpus_valid, &cpus_valid, tsk_cpus_allowed(p)); + if (v) { + dest_cpu = cpumask_any_and(&cpus_valid, &p->cpus_hint); + if (dest_cpu < nr_cpu_ids) { + do_set_cpus_allowed(p, &p->cpus_hint); + return dest_cpu; + } + + return cpumask_any(&cpus_valid); + } +#else /* Look for allowed, online CPU in same node. */ for_each_cpu(dest_cpu, nodemask) { if (!cpu_online(dest_cpu)) @@ -1326,9 +1377,41 @@ if (cpumask_test_cpu(dest_cpu, tsk_cpus_allowed(p))) return dest_cpu; } +#endif } for (;;) { +#if defined(CONFIG_BCM_KF_CPU_AFFINITY_HINT) && \ + defined(CONFIG_BCM_PROC_CPU_AFFINITY_HINT) + if (p->flags & PF_KTHREAD) { + /* + * Kernel threads can be scheduled to run on online + * CPUs, even if the CPU is not active. + * + * valid = (allowed & online); + */ + v = cpumask_and(&cpus_valid, tsk_cpus_allowed(p), + cpu_online_mask); + } else { + /* + * User-space threads must be scheduled to active CPUs. + * + * valid = (allowed & active); + */ + v = cpumask_and(&cpus_valid, tsk_cpus_allowed(p), + cpu_active_mask); + } + dest_cpu = v ? cpumask_any_and(&cpus_valid, &p->cpus_hint) + : nr_cpu_ids; + if (dest_cpu < nr_cpu_ids) { + /* recover allowed as hint; */ + do_set_cpus_allowed(p, &p->cpus_hint); + goto out; /* return dest_cpu = any_of(valid & hint); */ + } + dest_cpu = v ? cpumask_any(&cpus_valid) : nr_cpu_ids; + if (dest_cpu < nr_cpu_ids) + goto out; /* return dest_cpu = any_of(valid); */ +#else /* Any allowed, online CPU? */ for_each_cpu(dest_cpu, tsk_cpus_allowed(p)) { if (!cpu_online(dest_cpu)) @@ -1337,6 +1420,7 @@ continue; goto out; } +#endif switch (state) { case cpuset: @@ -1372,6 +1456,43 @@ return dest_cpu; } +#if defined(CONFIG_BCM_KF_CPU_AFFINITY_HINT) && \ + defined(CONFIG_BCM_PROC_CPU_AFFINITY_HINT) +/* + * Called by select_task_rq() when the selected cpu is allowed and online to + * decide whether select_fallback_rq() should be called to replace the + * selected allowed online cpu with some active/online allowed hinted CPU. + * + * Return 0 (i.e., no need to try to replace the selected allowed online cpu) + * if: + * 1. no active/online hinted CPU, or + * 2. the selected cpu is one of the active/online hinted CPUs, or + * 3. all the active/online hinted CPUs are not allowed now. + * + * Condition 3 exists because do_set_cpus_allowed() is also called by + * cpuset_cpus_allowed_fallback() in kernel/cpuset.c and __kthread_bind() in + * kernel/kthread.c, except for cases in this kernel/sched/core.c. It may be + * called at more places in the future kernels too. However, only for the case + * of set_cpus_allowed_ptr(), we copy cpus_allowed into cpus_hint after + * calling do_set_cpus_allowed(). + */ +static int try_valid_cpu_hint(int cpu, struct task_struct *p) +{ + struct cpumask cpus_valid; + int v; + + if (p->flags & PF_KTHREAD) + v = cpumask_and(&cpus_valid, &p->cpus_hint, + cpu_online_mask); + else + v = cpumask_and(&cpus_valid, &p->cpus_hint, + cpu_active_mask); + if (!v || cpumask_test_cpu(cpu, &cpus_valid)) + return 0; + + return cpumask_intersects(&cpus_valid, tsk_cpus_allowed(p)); +} +#endif /* * The caller (fork, wakeup) owns p->pi_lock, ->cpus_allowed is stable. */ @@ -1380,6 +1501,11 @@ { if (p->nr_cpus_allowed > 1) cpu = p->sched_class->select_task_rq(p, cpu, sd_flags, wake_flags); +#if defined(CONFIG_BCM_KF_CPU_AFFINITY_HINT) && \ + defined(CONFIG_BCM_PROC_CPU_AFFINITY_HINT) + else /* This improvement is back-ported from recent kernel. */ + cpu = cpumask_any(tsk_cpus_allowed(p)); +#endif /* * In order not to call set_task_cpu() on a blocking task we need @@ -1392,7 +1518,12 @@ * not worry about this generic constraint ] */ if (unlikely(!cpumask_test_cpu(cpu, tsk_cpus_allowed(p)) || +#if defined(CONFIG_BCM_KF_CPU_AFFINITY_HINT) && \ + defined(CONFIG_BCM_PROC_CPU_AFFINITY_HINT) + !cpu_online(cpu) || try_valid_cpu_hint(cpu, p))) +#else !cpu_online(cpu))) +#endif cpu = select_fallback_rq(task_cpu(p), p); return cpu; @@ -2520,6 +2651,9 @@ struct rq *rq = cpu_rq(cpu); struct task_struct *curr = rq->curr; +#if defined(CONFIG_BCM_KF_BUZZZ) && defined(CONFIG_BUZZZ_KEVT) + BUZZZ_KNL3(SCHED_TICK, jiffies, 0); +#endif sched_clock_tick(); raw_spin_lock(&rq->lock); @@ -2823,7 +2957,10 @@ rq->nr_switches++; rq->curr = next; ++*switch_count; - +#if defined(CONFIG_BCM_KF_BUZZZ) && defined(CONFIG_BUZZZ_KEVT) + BUZZZ_KNL3(TASK_OUT, prev->pid, prev); + BUZZZ_KNL3(TASK_IN, next->pid, next); +#endif rq = context_switch(rq, prev, next); /* unlocks the rq */ cpu = cpu_of(rq); } else @@ -4827,6 +4964,10 @@ } do_set_cpus_allowed(p, new_mask); +#if defined(CONFIG_BCM_KF_CPU_AFFINITY_HINT) && \ + defined(CONFIG_BCM_PROC_CPU_AFFINITY_HINT) + cpumask_copy(&p->cpus_hint, new_mask); +#endif /* Can the task run on the task's current CPU? If so, we're done */ if (cpumask_test_cpu(task_cpu(p), new_mask)) diff -urN linux-4.1.52-orig/kernel/sched/features.h linux-4.1.52/kernel/sched/features.h --- linux-4.1.52-orig/kernel/sched/features.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/kernel/sched/features.h 2020-02-07 01:47:54.000000000 +0800 @@ -70,7 +70,11 @@ #endif SCHED_FEAT(FORCE_SD_OVERLAP, false) +#if defined(CONFIG_BCM_KF_SCHED_RT_SHARE) && !defined(CONFIG_BCM_SCHED_RT_SHARE) +SCHED_FEAT(RT_RUNTIME_SHARE, false) +#else SCHED_FEAT(RT_RUNTIME_SHARE, true) +#endif SCHED_FEAT(LB_MIN, false) /* diff -urN linux-4.1.52-orig/kernel/sched/idle.c linux-4.1.52/kernel/sched/idle.c --- linux-4.1.52-orig/kernel/sched/idle.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/kernel/sched/idle.c 2020-02-07 01:47:54.000000000 +0800 @@ -217,6 +217,9 @@ */ __current_set_polling(); +#if defined CONFIG_BCM_KF_INTERACTIVE && defined CONFIG_CPU_FREQ_GOV_INTERACTIVE + idle_notifier_call_chain(IDLE_START); +#endif tick_nohz_idle_enter(); while (!need_resched()) { @@ -261,6 +264,9 @@ */ preempt_set_need_resched(); tick_nohz_idle_exit(); +#if defined CONFIG_BCM_KF_INTERACTIVE && defined CONFIG_CPU_FREQ_GOV_INTERACTIVE + idle_notifier_call_chain(IDLE_END); +#endif __current_clr_polling(); /* diff -urN linux-4.1.52-orig/kernel/smp.c linux-4.1.52/kernel/smp.c --- linux-4.1.52-orig/kernel/smp.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/kernel/smp.c 2020-02-07 01:47:54.000000000 +0800 @@ -288,8 +288,26 @@ * send smp call function interrupt to this cpu and as such deadlocks * can't happen. */ +#if defined(CONFIG_BCM_KF_BYPASS_SMP_WARNING) + /* + * There is a tiny chance that some thread has locked the per-cpu + * csd_data locked but has not called generic_exec_single yet, + * then we come in on an interrupt and also try to lock it, but it + * is already locked. Hence the warning about deadlock. The original + * sysrq code played by the rules and deferred the calling of this + * function to a workqueue, which can sleep and allow for the original + * lock holder to complete. But we want to force stack dump in the + * other cpu from interrupt context instead of from workqueue because + * the bottom half/scheduling on this CPU may be disabled due to + * buggy software. So pass in a magic cookie in the info variable to + * bypass the warning. + */ + WARN_ON_ONCE(cpu_online(this_cpu) && irqs_disabled() + && !oops_in_progress && (info != (void *)0xeeee)); +#else WARN_ON_ONCE(cpu_online(this_cpu) && irqs_disabled() && !oops_in_progress); +#endif csd = &csd_stack; if (!wait) { diff -urN linux-4.1.52-orig/kernel/softirq.c linux-4.1.52/kernel/softirq.c --- linux-4.1.52-orig/kernel/softirq.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/kernel/softirq.c 2020-02-07 01:47:54.000000000 +0800 @@ -29,6 +29,9 @@ #define CREATE_TRACE_POINTS #include +#if defined(CONFIG_BCM_KF_BUZZZ) && defined(CONFIG_BUZZZ_KEVT) +#include +#endif /* - No shared variables, all the data are CPU local. @@ -77,6 +80,19 @@ wake_up_process(tsk); } +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM947189) +/* + * If ksoftirqd is scheduled, we do not want to process pending softirqs + * right now. Let ksoftirqd handle this at its own rate, to get fairness. + */ +static bool ksoftirqd_running(void) +{ + struct task_struct *tsk = __this_cpu_read(ksoftirqd); + + return tsk && (tsk->state == TASK_RUNNING); +} +#endif + /* * preempt_count and SOFTIRQ_OFFSET usage: * - preempt_count is changed by SOFTIRQ_OFFSET on entering or leaving @@ -270,7 +286,13 @@ kstat_incr_softirqs_this_cpu(vec_nr); trace_softirq_entry(vec_nr); +#if defined(CONFIG_BCM_KF_BUZZZ) && defined(CONFIG_BUZZZ_KEVT) + BUZZZ_KNL3(SIRQ_ENT, 0, h->action); +#endif h->action(h); +#if defined(CONFIG_BCM_KF_BUZZZ) && defined(CONFIG_BUZZZ_KEVT) + BUZZZ_KNL3(SIRQ_EXT, 0, h->action); +#endif trace_softirq_exit(vec_nr); if (unlikely(prev_count != preempt_count())) { pr_err("huh, entered softirq %u %s %p with preempt_count %08x, exited with %08x?\n", @@ -312,8 +334,11 @@ local_irq_save(flags); pending = local_softirq_pending(); - +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM947189) + if (pending && !ksoftirqd_running()) +#else if (pending) +#endif do_softirq_own_stack(); local_irq_restore(flags); @@ -340,6 +365,10 @@ static inline void invoke_softirq(void) { +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM947189) + if (ksoftirqd_running()) + return; +#endif if (!force_irqthreads) { #ifdef CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK /* diff -urN linux-4.1.52-orig/kernel/trace/trace_clock.c linux-4.1.52/kernel/trace/trace_clock.c --- linux-4.1.52-orig/kernel/trace/trace_clock.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/kernel/trace/trace_clock.c 2020-02-07 01:47:54.000000000 +0800 @@ -78,6 +78,11 @@ * Used by plugins that need globally coherent timestamps. */ +#if defined(CONFIG_BCM_KF_TRACE_CUSTOM) +#include +static u64 bcm_tstamp_rollover_base[NR_CPUS]; +static u32 bcm_tstamp_last[NR_CPUS]; +#else /* keep prev_time and lock in the same cacheline. */ static struct { u64 prev_time; @@ -86,9 +91,37 @@ { .lock = (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED, }; +#endif + u64 notrace trace_clock_global(void) { +#if defined(CONFIG_BCM_KF_TRACE_CUSTOM) + u64 ns; + u32 tstamp = bcm_tstamp_read(); + int cpuid = smp_processor_id(); + + if (tstamp < bcm_tstamp_last[cpuid]) { + // 32 bit counter has wrapped, add to our 64bit base + bcm_tstamp_rollover_base[cpuid] += bcm_tstamp2ns(0xffffffff); + } + bcm_tstamp_last[cpuid] = tstamp; + + /* + * The base value is updated independently on each CPU, but we want + * to report a consistent base from any CPU, so take the larger base. + * The trace buffers seem to require increasing timestamps (no rollover), + * so unfortunately I have to add all this extra code. + */ +#if NR_CPUS > 1 + ns = (bcm_tstamp_rollover_base[0] > bcm_tstamp_rollover_base[1]) ? + bcm_tstamp_rollover_base[0] : bcm_tstamp_rollover_base[1]; +#else + ns = bcm_tstamp_rollover_base[0]; +#endif + ns += bcm_tstamp2ns(tstamp); + return ns; +#else /* CONFIG_BCM_KF_TRACE_CUSTOM */ unsigned long flags; int this_cpu; u64 now; @@ -122,6 +155,7 @@ local_irq_restore(flags); return now; +#endif /* else CONFIG_BCM_KF_TRACE_CUSTOM */ } static atomic64_t trace_counter; diff -urN linux-4.1.52-orig/kernel/workqueue.c linux-4.1.52/kernel/workqueue.c --- linux-4.1.52-orig/kernel/workqueue.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/kernel/workqueue.c 2020-02-07 01:47:54.000000000 +0800 @@ -48,6 +48,9 @@ #include #include #include +#if defined(CONFIG_BCM_KF_BUZZZ) && defined(CONFIG_BUZZZ_KEVT) +#include +#endif #include "workqueue_internal.h" @@ -1460,6 +1463,9 @@ local_irq_save(flags); if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work))) { +#if defined(CONFIG_BCM_KF_BUZZZ) && defined(CONFIG_BUZZZ_KEVT) + BUZZZ_KNL3(WORKQ_SCHED, cpu, work->func); +#endif __queue_work(cpu, wq, work); ret = true; } @@ -2068,7 +2074,13 @@ lock_map_acquire_read(&pwq->wq->lockdep_map); lock_map_acquire(&lockdep_map); trace_workqueue_execute_start(work); +#if defined(CONFIG_BCM_KF_BUZZZ) && defined(CONFIG_BUZZZ_KEVT) + BUZZZ_KNL3(WORKQ_ENT, 0, work->func); +#endif worker->current_func(work); +#if defined(CONFIG_BCM_KF_BUZZZ) && defined(CONFIG_BUZZZ_KEVT) + BUZZZ_KNL3(WORKQ_EXT, 0, work->func); +#endif /* * While we must be careful to not use "work" after this, the trace * point will only record its address. diff -urN linux-4.1.52-orig/lib/pci_iomap.c linux-4.1.52/lib/pci_iomap.c --- linux-4.1.52-orig/lib/pci_iomap.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/lib/pci_iomap.c 2020-02-07 01:47:54.000000000 +0800 @@ -39,7 +39,13 @@ start += offset; if (maxlen && len > maxlen) len = maxlen; +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) + /* IO Resource not supported */ + if (!IS_ENABLED(CONFIG_NO_GENERIC_PCI_IOPORT_MAP) && + (flags & IORESOURCE_IO)) +#else if (flags & IORESOURCE_IO) +#endif return __pci_ioport_map(dev, start, len); if (flags & IORESOURCE_MEM) { if (flags & IORESOURCE_CACHEABLE) diff -urN linux-4.1.52-orig/lib/swiotlb.c linux-4.1.52/lib/swiotlb.c --- linux-4.1.52-orig/lib/swiotlb.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/lib/swiotlb.c 2020-02-07 01:47:54.000000000 +0800 @@ -29,6 +29,9 @@ #include #include #include +#if defined CONFIG_BCM_KF_ARM64_BCM963XX && defined(CONFIG_BCM94908) && defined(CONFIG_BCM_HND_EAP) +#include +#endif #include #include @@ -215,6 +218,23 @@ unsigned char *vstart; unsigned long bytes; +#if defined CONFIG_BCM_KF_ARM64_BCM963XX +#if defined(CONFIG_BCM94908) && defined(CONFIG_BCM_HND_EAP) + /* + * ZONE_DMA is restricted to 1GB due to a runner HW limitation. We + * still don't need swiotlb in this case though so check against the + * 4GB boundary here rather than zone_dma. + */ + if (!swiotlb_force && + (max_pfn <= ((min((memblock_start_of_DRAM() & GENMASK_ULL(63, 32)) + + (1ULL << 32), + memblock_end_of_DRAM())) >> PAGE_SHIFT))) + return; +#elif defined CONFIG_ARM64 + if(!swiotlb_force && (max_pfn <= (arm64_dma_phys_limit >> PAGE_SHIFT))) + return; +#endif +#endif if (!io_tlb_nslabs) { io_tlb_nslabs = (default_size >> IO_TLB_SHIFT); io_tlb_nslabs = ALIGN(io_tlb_nslabs, IO_TLB_SEGSIZE); @@ -990,6 +1010,23 @@ int swiotlb_dma_supported(struct device *hwdev, u64 mask) { +#if defined CONFIG_BCM_KF_ARM64_BCM963XX +#if defined(CONFIG_BCM94908) && defined(CONFIG_BCM_HND_EAP) + /* + * ZONE_DMA is restricted to 1GB due to a runner HW limitation. We + * still don't need swiotlb in this case though so check against the + * 4GB boundary here rather than zone_dma. + */ + if (!swiotlb_force && + (max_pfn <= ((min((memblock_start_of_DRAM() & GENMASK_ULL(63, 32)) + + (1ULL << 32), + memblock_end_of_DRAM())) >> PAGE_SHIFT))) + return 1; +#elif defined CONFIG_ARM64 + if(!swiotlb_force && (max_pfn <= (arm64_dma_phys_limit >> PAGE_SHIFT))) + return 1; +#endif +#endif return phys_to_dma(hwdev, io_tlb_end - 1) <= mask; } EXPORT_SYMBOL(swiotlb_dma_supported); diff -urN linux-4.1.52-orig/.mailmap linux-4.1.52/.mailmap --- linux-4.1.52-orig/.mailmap 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/.mailmap 1970-01-01 08:00:00.000000000 +0800 @@ -1,130 +0,0 @@ -# -# This list is used by git-shortlog to fix a few botched name translations -# in the git archive, either because the author's full name was messed up -# and/or not always written the same way, making contributions from the -# same person appearing not to be so or badly displayed. -# -# repo-abbrev: /pub/scm/linux/kernel/git/ -# - -Aaron Durbin -Adam Oldham -Adam Radford -Adrian Bunk -Alan Cox -Alan Cox -Aleksey Gorelov -Al Viro -Al Viro -Andreas Herrmann -Andrew Morton -Andrew Vasquez -Andy Adamson -Archit Taneja -Arnaud Patard -Arnd Bergmann -Axel Dyks -Axel Lin -Ben Gardner -Ben M Cahill -Björn Steinbrink -Brian Avery -Brian King -Christoph Hellwig -Corey Minyard -Damian Hobson-Garcia -David Brownell -David Woodhouse -Dmitry Eremin-Solenikov -Domen Puncer -Douglas Gilbert -Ed L. Cashin -Evgeniy Polyakov -Felipe W Damasio -Felix Kuhling -Felix Moeller -Filipe Lautert -Franck Bui-Huu -Frank Zago -Greg Kroah-Hartman -Greg Kroah-Hartman -Greg Kroah-Hartman -Henk Vergonet -Henrik Kretzschmar -Henrik Rydberg -Herbert Xu -Jacob Shin -James Bottomley -James Bottomley -James E Wilson -James Ketrenos -Jean Tourrilhes -Jeff Garzik -Jens Axboe -Jens Osterkamp -John Stultz - - - - - -Juha Yrjola -Juha Yrjola -Juha Yrjola -Kay Sievers -Kenneth W Chen -Konstantin Khlebnikov -Koushik -Kuninori Morimoto -Leonid I Ananiev -Linas Vepstas -Mark Brown -Matthieu CASTET -Mauro Carvalho Chehab -Mayuresh Janorkar -Michael Buesch -Michel Dänzer -Mitesh shah -Morten Welinder -Morten Welinder -Morten Welinder -Morten Welinder -Mythri P K -Nguyen Anh Quynh -Paolo 'Blaisorblade' Giarrusso -Patrick Mochel -Peter A Jonsson -Peter Oruba -Peter Oruba -Praveen BP -Rajesh Shah -Ralf Baechle -Ralf Wildenhues -Rémi Denis-Courmont -Ricardo Ribalda Delgado -Rudolf Marek -Rui Saraiva -Sachin P Sant -Sam Ravnborg -Santosh Shilimkar -Santosh Shilimkar -Sascha Hauer -S.ÇaÄŸlar Onur -Shiraz Hashim -Simon Kelley -Stéphane Witzmann -Stephen Hemminger -Sumit Semwal -Tejun Heo -Thomas Graf -Tony Luck -Tsuneo Yoshioka -Uwe Kleine-König -Uwe Kleine-König -Uwe Kleine-König -Valdis Kletnieks -Viresh Kumar -Takashi YOSHII -Yusuke Goda -Gustavo Padovan -Gustavo Padovan diff -urN linux-4.1.52-orig/Makefile linux-4.1.52/Makefile --- linux-4.1.52-orig/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/Makefile 2020-02-07 01:47:53.000000000 +0800 @@ -15,6 +15,7 @@ # o Look for make include files relative to root of kernel src MAKEFLAGS += -rR --include-dir=$(CURDIR) +BRCMDRIVERS_DIR_RELATIVE = ../../bcmdrivers # Avoid funny character set dependencies unexport LC_ALL LC_COLLATE=C @@ -298,7 +299,18 @@ HOSTCC = gcc HOSTCXX = g++ +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) +BCM_FATAL_CC_WARNING_FLAGS := -Werror -Wfatal-errors -Wno-date-time -Wno-declaration-after-statement -Wno-switch-bool + +# BCM_KBUILD_CFLAGS is used when building the Linux kernel (not bcmdrivers) +BCM_KBUILD_CFLAGS := -g $(BCM_FATAL_CC_WARNING_FLAGS) + +# lauterbach setting +#HOSTCFLAGS = -Wall -Wmissing-prototypes -Wstrict-prototypes -fomit-frame-pointer + +else # BCM_KF # CONFIG_BCM_KF_MISC_MAKEFILE HOSTCFLAGS = -Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu89 +endif # BCM_KF # CONFIG_BCM_KF_MISC_MAKEFILE HOSTCXXFLAGS = -O2 ifeq ($(shell $(HOSTCC) -v 2>&1 | grep -c "clang version"), 1) @@ -434,9 +446,13 @@ # Files to ignore in find ... statements +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) + # exclude *prebuilt* directories from being cleaned as they contain + # fw objects in HND profile release builds. export RCS_FIND_IGNORE := \( -name SCCS -o -name BitKeeper -o -name .svn -o \ - -name CVS -o -name .pc -o -name .hg -o -name .git \) \ + -name CVS -o -name .pc -o -name .hg -o -name .git -o -name '*prebuilt*' \) \ -prune -o +endif # BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) export RCS_TAR_IGNORE := --exclude SCCS --exclude BitKeeper --exclude .svn \ --exclude CVS --exclude .pc --exclude .hg --exclude .git @@ -562,9 +578,50 @@ core-y := usr/ endif # KBUILD_EXTMOD +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) +# Read in config +-include include/config/auto.conf +export HPATH := $(TOPDIR)/include +export CONFIG_SYSTEM := linux +ifeq ($(strip $(CONFIG_BRCM_IKOS)),) +brcmdrivers-y := $(INC_BRCMBOARDPARMS_PATH)/$(BRCM_BOARD)/ $(BRCMDRIVERS_DIR_RELATIVE)/ $(INC_UTILS_PATH)/ $(INC_FLASH_PATH)/ +brcmdrivers-y += $(INC_SPI_PATH)/ +# Other chip specific compilations +brcmdrivers-y += $(SHARED_DIR)/opensource/drv/ +ifneq "$(wildcard $(PROJECT_DIR)/target )" "" +brcmdrivers-y += $(RDP_PROJECT_DIR_RELATIVE)/target/bdmf/ $(RDP_PROJECT_DIR_RELATIVE)/target/rdpa/ $(RDP_PROJECT_DIR_RELATIVE)/target/rdpa_gpl/ $(RDP_PROJECT_DIR_RELATIVE)/target/rdpa_user/ +endif +brcmdrivers-y += $(SHARED_DIR)/opensource/drivers/ +else +brcmdrivers-y := $(BRCMDRIVERS_DIR_RELATIVE)/ $(INC_UTILS_PATH)/ +endif + +ifneq ($(CONFIG_BCM_PMC),) +brcmdrivers-$(CONFIG_BCM_PMC) += $(SHARED_DIR)/opensource/pmc/impl$(CONFIG_BCM_PMC_IMPL)/ +endif + +ifneq ($(CONFIG_BUZZZ),) +brcmdrivers-$(CONFIG_BUZZZ) += $(BRCMDRIVERS_DIR)/broadcom/char/buzzz/ +endif + +ifeq ($(KBUILD_VERBOSE),1) +$(info * bcmdrivers-y = $(brcmdrivers-y)) +$(info * bcmdrivers-m = $(brcmdrivers-m)) +$(info * INC_BRCMBOARDPARMS_PATH = $(INC_BRCMBOARDPARMS_PATH)) +$(info * BRCM_BOARD = $(BRCM_BOARD)) +$(info * BRCMDRIVERS_DIR = $(BRCMDRIVERS_DIR)) +$(info * INC_SPI_PATH = $(INC_SPI_PATH)) +$(info * INC_FLASH_PATH = $(INC_FLASH_PATH)) +endif +BRCMDRIVERS := $(brcmdrivers-y) +endif # BCM_KF # CONFIG_BCM_KF_MISC_MAKEFILE + ifeq ($(dot-config),1) +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) +else # BCM_KF # Read in config -include include/config/auto.conf +endif # BCM_KF # CONFIG_BCM_KF_MISC_MAKEFILE ifeq ($(KBUILD_EXTMOD),) # Read in dependencies to all Kconfig* files, make sure to run @@ -579,7 +636,11 @@ # if auto.conf.cmd is missing then we are probably in a cleaned tree so # we execute the config step to be sure to catch updated Kconfig files include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) + $(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig ARCH=$(ARCH) +else # BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) $(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig +endif # BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) else # external modules needs include/generated/autoconf.h and include/config/auto.conf # but do not care if they are up-to-date. Use auto.conf to trigger the test @@ -605,7 +666,12 @@ # command line. # This allow a user to issue only 'make' to build a kernel including modules # Defaults to vmlinux, but the arch makefile usually adds further targets + +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) +all: bcm_vmlinux +else # BCM_KF # CONFIG_BCM_KF_MISC_MAKEFILE all: vmlinux +endif # BCM_KF # CONFIG_BCM_KF_MISC_MAKEFILE include arch/$(SRCARCH)/Makefile @@ -620,8 +686,107 @@ ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE KBUILD_CFLAGS += -Os $(call cc-disable-warning,maybe-uninitialized,) else +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) +KERNEL_DEBUG ?= 0 +ifneq ($(strip $(BRCM_KERNEL_DEBUG)$(KERNEL_DEBUG)),0) +ifeq ($(KBUILD_VERBOSE),1) +$(info "Compiling gdb symbols into kernel (-g)") +endif +KBUILD_CFLAGS += -g +endif +ifneq ($(strip $(KERNEL_DEBUG)), 0) +ifeq ($(KBUILD_VERBOSE),1) +$(info "Setting optimization to debug levels (-O1)") +endif +KBUILD_CFLAGS += -O1 +KBUILD_AFLAGS += -gdwarf-4 +KBUILD_CFLAGS += -Wno-uninitialized + +# Kernel does not compile with -O0. Set optimizatin to -O1 and disable all other optimizations +# except for the ones which result in errors when compiling kernel +KBUILD_CFLAGS += -fno-branch-count-reg +KBUILD_CFLAGS += -fno-combine-stack-adjustments +KBUILD_CFLAGS += -fno-common +KBUILD_CFLAGS += -fno-compare-elim +KBUILD_CFLAGS += -fno-cprop-registers +KBUILD_CFLAGS += -fno-dce +KBUILD_CFLAGS += -fno-defer-pop +KBUILD_CFLAGS += -fno-delayed-branch +KBUILD_CFLAGS += -fno-delete-null-pointer-checks +KBUILD_CFLAGS += -fno-dse +KBUILD_CFLAGS += -fno-early-inlining +#KBUILD_CFLAGS += -fno-forward-propagate +KBUILD_CFLAGS += -fno-gcse-lm +KBUILD_CFLAGS += -fno-guess-branch-probability +KBUILD_CFLAGS += -fno-if-conversion +KBUILD_CFLAGS += -fno-if-conversion2 +KBUILD_CFLAGS += -fno-inline-functions-called-once +KBUILD_CFLAGS += -fno-ipa-profile +KBUILD_CFLAGS += -fno-ipa-pure-const +KBUILD_CFLAGS += -fno-ipa-reference +KBUILD_CFLAGS += -fno-ivopts +KBUILD_CFLAGS += -fno-jump-tables +KBUILD_CFLAGS += -fno-math-errno +KBUILD_CFLAGS += -fno-merge-constants +KBUILD_CFLAGS += -fno-move-loop-invariants +KBUILD_CFLAGS += -fno-omit-frame-pointer +KBUILD_CFLAGS += -fno-peephole +KBUILD_CFLAGS += -fno-prefetch-loop-arrays +KBUILD_CFLAGS += -fno-rename-registers +#KBUILD_CFLAGS += -fno-rtti +KBUILD_CFLAGS += -fno-sched-critical-path-heuristic +KBUILD_CFLAGS += -fno-sched-dep-count-heuristic +KBUILD_CFLAGS += -fno-sched-group-heuristic +KBUILD_CFLAGS += -fno-sched-interblock +KBUILD_CFLAGS += -fno-sched-last-insn-heuristic +KBUILD_CFLAGS += -fno-sched-rank-heuristic +KBUILD_CFLAGS += -fno-sched-spec +KBUILD_CFLAGS += -fno-sched-spec-insn-heuristic +KBUILD_CFLAGS += -fno-sched-stalled-insns-dep +KBUILD_CFLAGS += -fno-short-enums +KBUILD_CFLAGS += -fno-signed-zeros +KBUILD_CFLAGS += -fno-split-ivs-in-unroller +KBUILD_CFLAGS += -fno-split-wide-types +#KBUILD_CFLAGS += -fno-no-threadsafe-statics +KBUILD_CFLAGS += -fno-toplevel-reorder +KBUILD_CFLAGS += -fno-trapping-math +KBUILD_CFLAGS += -fno-tree-bit-ccp +#KBUILD_CFLAGS += -fno-tree-ccp +KBUILD_CFLAGS += -fno-tree-ch +KBUILD_CFLAGS += -fno-tree-copy-prop +KBUILD_CFLAGS += -fno-tree-copyrename +KBUILD_CFLAGS += -fno-tree-cselim +KBUILD_CFLAGS += -fno-tree-dce +KBUILD_CFLAGS += -fno-tree-dominator-opts +KBUILD_CFLAGS += -fno-tree-dse +KBUILD_CFLAGS += -fno-tree-forwprop +KBUILD_CFLAGS += -fno-tree-fre +KBUILD_CFLAGS += -fno-tree-loop-if-convert +KBUILD_CFLAGS += -fno-tree-loop-im +KBUILD_CFLAGS += -fno-tree-loop-ivcanon +KBUILD_CFLAGS += -fno-tree-loop-optimize +KBUILD_CFLAGS += -fno-tree-phiprop +KBUILD_CFLAGS += -fno-tree-pta +KBUILD_CFLAGS += -fno-tree-reassoc +KBUILD_CFLAGS += -fno-tree-scev-cprop +KBUILD_CFLAGS += -fno-tree-sink +KBUILD_CFLAGS += -fno-tree-slp-vectorize +KBUILD_CFLAGS += -fno-tree-sra +KBUILD_CFLAGS += -fno-tree-ter +KBUILD_CFLAGS += -fno-tree-vect-loop-version +KBUILD_CFLAGS += -fno-unit-at-a-time +KBUILD_CFLAGS += -fno-var-tracking +KBUILD_CFLAGS += -fno-var-tracking-assignments +KBUILD_CFLAGS += -fno-web + +CONFIG_FRAME_WARN = 0 +else KBUILD_CFLAGS += -O2 endif +else # BCM_KF # CONFIG_BCM_KF_MISC_MAKEFILE +KBUILD_CFLAGS += -O2 +endif # BCM_KF # CONFIG_BCM_KF_MISC_MAKEFILE +endif # Tell gcc to never replace conditional load with a non-conditional one KBUILD_CFLAGS += $(call cc-option,--param=allow-store-data-races=0) @@ -783,7 +948,6 @@ # Make sure -fstack-check isn't enabled (like gentoo apparently did) KBUILD_CFLAGS += $(call cc-option,-fno-stack-check,) - # conserve stack if available KBUILD_CFLAGS += $(call cc-option,-fconserve-stack) @@ -808,12 +972,42 @@ KBUILD_AFLAGS += $(ARCH_AFLAGS) $(KAFLAGS) KBUILD_CFLAGS += $(ARCH_CFLAGS) $(KCFLAGS) +ifneq ($(strip $(BCA_HNDROUTER)),) +KBUILD_CFLAGS += -DBCA_HNDROUTER -DBCA_CPE_BSP_SHARED +endif +ifneq ($(strip $(BCA_CPEROUTER)),) +KBUILD_CFLAGS += -DBCA_CPEROUTER +endif + +ifdef BCM_KF # defined(CONFIG_BCM_KF_BUZZZ) +ifneq ($(CONFIG_BUZZZ_FUNC),) +# To support -O0 (no inlining) compilation, several compile time check need to +# be disabled within the kernel build as well as driver builds. +# e.g. BUILD_BUG(), __compiletime_assert() are no-ops. These noops may lead to +# unused variables, labels, functions, etc. +KBUILD_CFLAGS += $(call cc-disable-warning,return-type) +KBUILD_CFLAGS += $(call cc-disable-warning,unused-value) +KBUILD_CFLAGS += $(call cc-disable-warning,unused-label) +KBUILD_CFLAGS += $(call cc-disable-warning,unused-function) +KBUILD_CFLAGS += $(call cc-disable-warning,unused-variable) +ifneq ($(CONFIG_ARM64),) +# Compile time check of frame size limits of 2K, were applicable for 8K stacks. +# Post linux 3.16, stack size has been increased to 16K (arch/arm64) +KBUILD_CFLAGS += $(call cc-option,-Wframe-larger-than=4096) +endif # CONFIG_ARM64 +endif # CONFIG_BUZZZ_FUNC +endif # BCM_KF # CONFIG_BCM_KF_BUZZZ + # Use --build-id when available. LDFLAGS_BUILD_ID = $(patsubst -Wl$(comma)%,%,\ $(call cc-ldoption, -Wl$(comma)--build-id,)) KBUILD_LDFLAGS_MODULE += $(LDFLAGS_BUILD_ID) LDFLAGS_vmlinux += $(LDFLAGS_BUILD_ID) +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) +LDFLAGS_vmlinux += -z max-page-size=0x8000 -Map vmlinux.map +endif # BCM_KF # CONFIG_BCM_KF_MISC_MAKEFILE + ifeq ($(CONFIG_STRIP_ASM_SYMS),y) LDFLAGS_vmlinux += $(call ld-option, -X,) endif @@ -843,6 +1037,7 @@ # makefile but the argument can be passed to make if needed. # + MODLIB = $(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE) export MODLIB @@ -905,16 +1100,39 @@ ifeq ($(KBUILD_EXTMOD),) core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/ +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) +vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \ + $(core-y) $(core-m) $(drivers-y) $(drivers-m) \ + $(brcmdrivers-y) $(brcmdrivers-m) \ + $(net-y) $(net-m) $(libs-y) $(libs-m))) + +vmlinux-dirs-1 := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \ + $(core-y) $(core-m) $(drivers-y) $(drivers-m))) + +brcmdriver-dirs := $(patsubst %/,%,$(filter %/, \ + $(brcmdrivers-y) $(brcmdrivers-m))) + +vmlinux-dirs-2 := $(patsubst %/,%,$(filter %/, \ + $(net-y) $(net-m) $(libs-y) $(libs-m))) + +vmlinux-alldirs := $(sort $(vmlinux-dirs) $(patsubst %/,%,$(filter %/, \ + $(init-) $(core-) $(drivers-) $(net-) $(libs-)))) \ + $(brcmdrivers-n) $(brcmdrivers-) +else # BCM_KF # CONFIG_BCM_KF_MISC_MAKEFILE vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \ $(core-y) $(core-m) $(drivers-y) $(drivers-m) \ $(net-y) $(net-m) $(libs-y) $(libs-m))) vmlinux-alldirs := $(sort $(vmlinux-dirs) $(patsubst %/,%,$(filter %/, \ $(init-) $(core-) $(drivers-) $(net-) $(libs-)))) +endif # BCM_KF # CONFIG_BCM_KF_MISC_MAKEFILE init-y := $(patsubst %/, %/built-in.o, $(init-y)) core-y := $(patsubst %/, %/built-in.o, $(core-y)) drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y)) +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) +brcmdrivers-y := $(patsubst %/, %/built-in.o, $(brcmdrivers-y)) +endif # BCM_KF # CONFIG_BCM_KF_MISC_MAKEFILE net-y := $(patsubst %/, %/built-in.o, $(net-y)) libs-y1 := $(patsubst %/, %/lib.a, $(libs-y)) libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y)) @@ -922,7 +1140,18 @@ # Externally visible symbols (used by link-vmlinux.sh) export KBUILD_VMLINUX_INIT := $(head-y) $(init-y) +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) +export KBUILD_VMLINUX_MAIN := $(core-y) $(libs-y) $(drivers-y) $(brcmdrivers-y) $(net-y) +ifeq ($(KBUILD_VERBOSE),1) +$(info kernel makefile environment variables size) +$(shell echo "" | xargs --show-limits) +$(info *******************************) +$(info * vmlinux-main: $(KBUILD_VMLINUX_MAIN)) +$(info * brcmdrivers-y: $(brcmdrivers-y)) +endif +else # BCM_KF #CONFIG_BCM_KF_MISC_MAKEFILE export KBUILD_VMLINUX_MAIN := $(core-y) $(libs-y) $(drivers-y) $(net-y) +endif # BCM_KF #CONFIG_BCM_KF_MISC_MAKEFILE export KBUILD_LDS := arch/$(SRCARCH)/kernel/vmlinux.lds export LDFLAGS_vmlinux # used by scripts/pacmage/Makefile @@ -934,6 +1163,20 @@ cmd_link-vmlinux = $(CONFIG_SHELL) $< $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux) quiet_cmd_link-vmlinux = LINK $@ +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) +# vmlinux image - including updated kernel symbols + +.PHONY: bcm_vmlinux + +bcm_vmlinux: vmlinux | prepare_bcm_driver + +# Ensure that prepare_bcm_driver is run before vmlinux starts. prepare_bcm_driver +# creates all of the bcmdriver symlinks. Note that vmlinux performs actions on +# its normal prerequisites, so this must be added as order-only. +vmlinux : | prepare_bcm_driver + +endif # BCM_KF #CONFIG_BCM_KF_MISC_MAKEFILE + # Include targets which we want to # execute if the rest of the kernel build went well. vmlinux: scripts/link-vmlinux.sh $(vmlinux-deps) FORCE @@ -953,7 +1196,22 @@ # The actual objects are generated when descending, # make sure no implicit rule kicks in +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) +ifeq ($(KBUILD_VERBOSE),1) +$(info * vmlinux-init: $(KBUILD_VMLINUX_INIT)) +$(info * vmlinux-main: $(KBUILD_VMLINUX_MAIN)) +$(info * vmlinux-lds: $(KBUILD_LDS)) +$(info *) +$(info * vmlinux-dirs-1: $(vmlinux-dirs-1)) +$(info * brcmdriver-dirs: $(brcmdriver-dirs)) +$(info * vmlinux-dirs-2: $(vmlinux-dirs-2)) +$(info *) +$(info $(sort $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN)) $(KBUILD_LDS): $(vmlinux-dirs-1) $(brcmdriver-dirs) $(vmlinux-dirs-2)); +endif +$(sort $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN)) $(KBUILD_LDS): $(vmlinux-dirs-1) $(brcmdriver-dirs) $(vmlinux-dirs-2); +else # BCM_KF #CONFIG_BCM_KF_MISC_MAKEFILE $(sort $(vmlinux-deps)): $(vmlinux-dirs) ; +endif # BCM_KF #CONFIG_BCM_KF_MISC_MAKEFILE # Handle descending into subdirectories listed in $(vmlinux-dirs) # Preset locale variables to speed up the build process. Limit locale @@ -961,9 +1219,34 @@ # make menuconfig etc. # Error messages still appears in the original language +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) +PHONY += $(vmlinux-dirs-1) +$(vmlinux-dirs-1): prepare scripts + $(Q)$(MAKE) BCM_KBUILD_CMDLINE_FLAGS='$(BCM_KBUILD_CFLAGS)' $(build)=$@ + +PHONY += $(brcmdriver-dirs) + +ifeq ($(wildcard $(CURDIR)/rdp_flags.txt),) + INC_RDP_FLAGS:='' +else + INC_RDP_FLAGS:='@$(CURDIR)/rdp_flags.txt' +endif + +$(brcmdriver-dirs): $(vmlinux-dirs-1) | prepare_bcm_driver +ifdef NO_BRCMDRIVER_PARALLEL + $(Q)INC_RDP_FLAGS=${INC_RDP_FLAGS} $(MAKE) -j1 $(build)=$@ +else + $(Q)INC_RDP_FLAGS=${INC_RDP_FLAGS} $(MAKE) $(build)=$@ +endif + +PHONY += $(vmlinux-dirs-2) +$(vmlinux-dirs-2): $(brcmdriver-dirs) + $(Q)$(MAKE) BCM_KBUILD_CMDLINE_FLAGS='$(BCM_KBUILD_CFLAGS)' $(build)=$@ +else # BCM_KF #CONFIG_BCM_KF_MISC_MAKEFILE PHONY += $(vmlinux-dirs) $(vmlinux-dirs): prepare scripts $(Q)$(MAKE) $(build)=$@ +endif # BCM_KF #CONFIG_BCM_KF_MISC_MAKEFIL define filechk_kernel.release echo "$(KERNELVERSION)$$($(CONFIG_SHELL) $(srctree)/scripts/setlocalversion $(srctree))" @@ -983,6 +1266,18 @@ # Listed in dependency order PHONY += prepare archprepare prepare0 prepare1 prepare2 prepare3 +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) +PHONY += prepare_bcm_driver +$(brcmdrivers-y): | prepare_bcm_driver + +prepare_bcm_driver: + $(Q)$(MAKE) -C $(BRCMDRIVERS_DIR) symlinks + +version_info: + $(Q)$(MAKE) -C $(BRCMDRIVERS_DIR) version_info + +endif # BCM_KF #CONFIG_BCM_KF_MISC_MAKEFILE + # prepare3 is used to check if we are building in a separate output directory, # and if so do: # 1) Check that make has not been executed in the kernel src $(srctree) @@ -1009,7 +1304,11 @@ $(Q)$(MAKE) $(build)=. # All the preparing.. +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) +prepare: prepare0 | prepare_bcm_driver +else # BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) prepare: prepare0 +endif # BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) # Generate some files # --------------------------------------------------------------------------- @@ -1093,6 +1392,12 @@ $(Q)$(MAKE) $(hdr-inst)=include/uapi HDRCHECK=1 $(Q)$(MAKE) $(hdr-inst)=arch/$(hdr-arch)/include/uapi/asm $(hdr-dst) HDRCHECK=1 +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) +nvram_3k: all + @mv $(CURDIR)/vmlinux $(CURDIR)/vmlinux_secureboot + @mv $(CURDIR)/vmlinux.restore $(CURDIR)/vmlinux +endif # BCM_KF #CONFIG_BCM_KF_MISC_MAKEFILE + # --------------------------------------------------------------------------- # Kernel selftest @@ -1116,18 +1421,35 @@ # using awk while concatenating to the final file. PHONY += modules + +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) + +$(vmlinux-dirs-2) $(if $(KBUILD_BUILTIN),vmlinux) modules.builtin : | prepare_bcm_driver + +modules: $(vmlinux-dirs-2) $(if $(KBUILD_BUILTIN),vmlinux) modules.builtin + $(Q)$(AWK) '!x[$$0]++' $(vmlinux-dirs:%=%/modules.order) > $(objtree)/modules.order + @$(kecho) ' Building modules, stage 2.'; + $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost + $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.fwinst obj=firmware __fw_modbuild +else # BCM_KF #CONFIG_BCM_KF_MISC_MAKEFILE modules: $(vmlinux-dirs) $(if $(KBUILD_BUILTIN),vmlinux) modules.builtin $(Q)$(AWK) '!x[$$0]++' $(vmlinux-dirs:%=$(objtree)/%/modules.order) > $(objtree)/modules.order @$(kecho) ' Building modules, stage 2.'; $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.fwinst obj=firmware __fw_modbuild +endif # BCM_KF #CONFIG_BCM_KF_MISC_MAKEFILE modules.builtin: $(vmlinux-dirs:%=%/modules.builtin) $(Q)$(AWK) '!x[$$0]++' $^ > $(objtree)/modules.builtin +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) +%/modules.builtin: include/config/auto.conf | prepare_bcm_driver + $(Q)$(MAKE) $(modbuiltin)=$* +else # BCM_KF #CONFIG_BCM_KF_MISC_MAKEFILE %/modules.builtin: include/config/auto.conf $(Q)$(MAKE) $(modbuiltin)=$* +endif # BCM_KF #CONFIG_BCM_KF_MISC_MAKEFILE # Target to prepare building external modules PHONY += modules_prepare @@ -1142,7 +1464,6 @@ @rm -rf $(MODLIB)/kernel @rm -f $(MODLIB)/source @mkdir -p $(MODLIB)/kernel - @ln -s `cd $(srctree) && /bin/pwd` $(MODLIB)/source @if [ ! $(objtree) -ef $(MODLIB)/build ]; then \ rm -f $(MODLIB)/build ; \ ln -s $(CURDIR) $(MODLIB)/build ; \ @@ -1203,7 +1524,7 @@ # clean: rm-dirs := $(CLEAN_DIRS) clean: rm-files := $(CLEAN_FILES) -clean-dirs := $(addprefix _clean_, . $(vmlinux-alldirs) Documentation samples) +clean-dirs := $(addprefix _clean_, . $(wildcard $(vmlinux-alldirs)) Documentation samples) PHONY += $(clean-dirs) clean archclean vmlinuxclean $(clean-dirs): @@ -1211,6 +1532,9 @@ vmlinuxclean: $(Q)$(CONFIG_SHELL) $(srctree)/scripts/link-vmlinux.sh clean +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) + rm -f vmlinux.map +endif # BCM_KF #CONFIG_BCM_KF_MISC_MAKEFILE clean: archclean vmlinuxclean @@ -1410,6 +1734,14 @@ modules_install: _emodinst_ _emodinst_post install-dir := $(if $(INSTALL_MOD_DIR),$(INSTALL_MOD_DIR),extra) +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) +ifeq ($(KBUILD_VERBOSE),1) +$(info "2 install-dir is $(install-dir)") +$(info "2 INSTALL_MOD_DIR is $(INSTALL_MOD_DIR)") +$(info "2 MODLIB is $(MODLIB)") +endif +endif # BCM_KF # CONFIG_BCM_KF_MISC_MAKEFILE + PHONY += _emodinst_ _emodinst_: $(Q)mkdir -p $(MODLIB)/$(install-dir) @@ -1422,7 +1754,11 @@ clean-dirs := $(addprefix _clean_,$(KBUILD_EXTMOD)) PHONY += $(clean-dirs) clean +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) +$(clean-dirs): | prepare_bcm_driver +else # BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) $(clean-dirs): +endif # BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) $(Q)$(MAKE) $(clean)=$(patsubst _clean_%,%,$@) clean: rm-dirs := $(MODVERDIR) @@ -1454,6 +1790,17 @@ -o -name '*.symtypes' -o -name 'modules.order' \ -o -name modules.builtin -o -name '.tmp_*.o.*' \ -o -name '*.gcno' \) -type f -print | xargs rm -f +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) + @echo Cleaning bcmdrivers + @find . $(BRCMDRIVERS) $(RCS_FIND_IGNORE) \ + \( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \ + -o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \ + -o -name '*.symtypes' -o -name 'modules.order' \ + -o -name modules.builtin -o -name '.tmp_*.o.*' \ + -o -name '*.gcno' \) -type f -print | xargs rm -f + @echo Cleaning bcmlinks + $(Q)$(MAKE) -C $(BRCMDRIVERS_DIR) cleanlinks +endif # BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) # Generate tags for editors # --------------------------------------------------------------------------- diff -urN linux-4.1.52-orig/mm/backing-dev.c linux-4.1.52/mm/backing-dev.c --- linux-4.1.52-orig/mm/backing-dev.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/mm/backing-dev.c 2020-02-07 01:47:54.000000000 +0800 @@ -421,6 +421,37 @@ } EXPORT_SYMBOL(bdi_init); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +void bdi_unregister(struct backing_dev_info *bdi) +{ + bdi_wb_shutdown(bdi); + bdi_set_min_ratio(bdi, 0); + + WARN_ON(!list_empty(&bdi->work_list)); + WARN_ON(delayed_work_pending(&bdi->wb.dwork)); + + if (bdi->dev) { + bdi_debug_unregister(bdi); + device_unregister(bdi->dev); + bdi->dev = NULL; + } +} +void bdi_exit(struct backing_dev_info *bdi) +{ + int i; + + WARN_ON_ONCE(bdi->dev); + + for (i = 0; i < NR_BDI_STAT_ITEMS; i++) + percpu_counter_destroy(&bdi->bdi_stat[i]); + fprop_local_destroy_percpu(&bdi->completions); +} +void bdi_destroy(struct backing_dev_info *bdi) +{ + bdi_unregister(bdi); + bdi_exit(bdi); +} +#else void bdi_destroy(struct backing_dev_info *bdi) { int i; @@ -441,6 +472,7 @@ percpu_counter_destroy(&bdi->bdi_stat[i]); fprop_local_destroy_percpu(&bdi->completions); } +#endif EXPORT_SYMBOL(bdi_destroy); /* diff -urN linux-4.1.52-orig/mm/Makefile linux-4.1.52/mm/Makefile --- linux-4.1.52-orig/mm/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/mm/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -10,6 +10,7 @@ mlock.o mmap.o mprotect.o mremap.o msync.o rmap.o \ vmalloc.o pagewalk.o pgtable-generic.o + ifdef CONFIG_CROSS_MEMORY_ATTACH mmu-$(CONFIG_MMU) += process_vm_access.o endif diff -urN linux-4.1.52-orig/mm/mremap.c linux-4.1.52/mm/mremap.c --- linux-4.1.52-orig/mm/mremap.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/mm/mremap.c 2020-02-07 01:47:54.000000000 +0800 @@ -95,6 +95,10 @@ struct mm_struct *mm = vma->vm_mm; pte_t *old_pte, *new_pte, pte; spinlock_t *old_ptl, *new_ptl; +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + bool force_flush = false; + unsigned long len = old_end - old_addr; +#endif //if defined(CONFIG_BCM_KF_MISC_BACKPORTS) /* * When need_rmap_locks is true, we take the i_mmap_rwsem and anon_vma @@ -141,12 +145,31 @@ if (pte_none(*old_pte)) continue; pte = ptep_get_and_clear(mm, old_addr, old_pte); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + /* + * If we are remapping a valid PTE, make sure + * to flush TLB before we drop the PTL for the PTE. + * + * NOTE! Both old and new PTL matter: the old one + * for racing with page_mkclean(), the new one to + * make sure the physical page stays valid until + * the TLB entry for the old mapping has been + * flushed. + */ + if (pte_present(pte)) + force_flush = true; +#endif //#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + pte = move_pte(pte, new_vma->vm_page_prot, old_addr, new_addr); pte = move_soft_dirty_pte(pte); set_pte_at(mm, new_addr, new_pte, pte); } arch_leave_lazy_mmu_mode(); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + if (force_flush) + flush_tlb_range(vma, old_end - len, old_end); +#endif //#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) if (new_ptl != old_ptl) spin_unlock(new_ptl); pte_unmap(new_pte - 1); @@ -166,7 +189,9 @@ { unsigned long extent, next, old_end; pmd_t *old_pmd, *new_pmd; +#if !defined(CONFIG_BCM_KF_MISC_BACKPORTS) bool need_flush = false; +#endif //#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) unsigned long mmun_start; /* For mmu_notifiers */ unsigned long mmun_end; /* For mmu_notifiers */ @@ -205,7 +230,9 @@ anon_vma_unlock_write(vma->anon_vma); } if (err > 0) { +#if !defined(CONFIG_BCM_KF_MISC_BACKPORTS) need_flush = true; +#endif continue; } else if (!err) { split_huge_page_pmd(vma, old_addr, old_pmd); @@ -222,10 +249,14 @@ extent = LATENCY_LIMIT; move_ptes(vma, old_pmd, old_addr, old_addr + extent, new_vma, new_pmd, new_addr, need_rmap_locks); +#if !defined(CONFIG_BCM_KF_MISC_BACKPORTS) need_flush = true; +#endif } +#if !defined(CONFIG_BCM_KF_MISC_BACKPORTS) if (likely(need_flush)) flush_tlb_range(vma, old_end-len, old_addr); +#endif mmu_notifier_invalidate_range_end(vma->vm_mm, mmun_start, mmun_end); diff -urN linux-4.1.52-orig/mm/oom_kill.c linux-4.1.52/mm/oom_kill.c --- linux-4.1.52-orig/mm/oom_kill.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/mm/oom_kill.c 2020-02-07 01:47:54.000000000 +0800 @@ -194,6 +194,8 @@ /* * Determine the type of allocation constraint. */ +#if !defined(CONFIG_BCM_KF_OOM_REBOOT) + #ifdef CONFIG_NUMA static enum oom_constraint constrained_alloc(struct zonelist *zonelist, gfp_t gfp_mask, nodemask_t *nodemask, @@ -253,6 +255,7 @@ return CONSTRAINT_NONE; } #endif +#endif /* CONFIG_BCM_KF_OOM_REBOOT */ enum oom_scan_t oom_scan_process_thread(struct task_struct *task, unsigned long totalpages, const nodemask_t *nodemask, @@ -285,6 +288,7 @@ return OOM_SCAN_OK; } +#if !defined(CONFIG_BCM_KF_OOM_REBOOT) /* * Simple selection loop. We chose the process with the highest * number of 'points'. Returns -1 on scan abort. @@ -335,6 +339,7 @@ return chosen; } +#endif /** * dump_tasks - dump current memory state of all system tasks * @memcg: current's memory controller, if constrained @@ -407,6 +412,7 @@ bool oom_killer_disabled __read_mostly; static DECLARE_RWSEM(oom_sem); + /** * mark_tsk_oom_victim - marks the given task as OOM victim. * @tsk: task to mark @@ -493,6 +499,8 @@ up_write(&oom_sem); } + + #define K(x) ((x) << (PAGE_SHIFT-10)) /* * Must be called while holding a reference to p, which will be released upon @@ -707,6 +715,13 @@ static void __out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, int order, nodemask_t *nodemask, bool force_kill) { +#if defined(CONFIG_BCM_KF_OOM_REBOOT) +#define OOM_REBOOT_DELAY (5) +#define OOM_REBOOT_INTERVAL (HZ*120) + static int oom_count=0; + static unsigned long oom_timestamp=0; + unsigned long freed = 0; +#else const nodemask_t *mpol_mask; struct task_struct *p; unsigned long totalpages; @@ -714,6 +729,7 @@ unsigned int uninitialized_var(points); enum oom_constraint constraint = CONSTRAINT_NONE; int killed = 0; +#endif blocking_notifier_call_chain(&oom_notify_list, 0, &freed); if (freed > 0) @@ -734,6 +750,40 @@ return; } +#if defined(CONFIG_BCM_KF_OOM_REBOOT) + + /* For our embedded system, most of the processes are considered essential. */ + /* Randomly killing a process is no better than a reboot so we won't kill process here*/ + + printk(KERN_WARNING "\n\n%s triggered out of memory codition (oom killer not called): " + "gfp_mask=0x%x, order=%d, oom_score_adj=%d\n\n", + current->comm, gfp_mask, order, current->signal->oom_score_adj); + dump_stack(); + show_mem(0); + printk("\n"); + /* + * The process that triggered the oom is not necessarily the one that + * caused it. dump_tasks shows all tasks and their memory usage. + */ + read_lock(&tasklist_lock); + dump_tasks(NULL, nodemask); + read_unlock(&tasklist_lock); + + /* Reboot if OOM, but don't do it immediately - just in case this can be too sensitive */ + if ((jiffies - oom_timestamp) > OOM_REBOOT_INTERVAL) { + oom_timestamp = jiffies; + oom_count = 0; + } + else { + oom_count++; + if (oom_count >= OOM_REBOOT_DELAY) { + panic("Reboot due to persistent out of memory codition.."); + } + } + schedule_timeout_interruptible(HZ*5); + +#else + /* * Check if there were limitations on the allocation (only relevant for * NUMA) that may require different handling. @@ -771,6 +821,7 @@ */ if (killed) schedule_timeout_killable(1); +#endif } /** diff -urN linux-4.1.52-orig/mm/page_alloc.c linux-4.1.52/mm/page_alloc.c --- linux-4.1.52-orig/mm/page_alloc.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/mm/page_alloc.c 2020-02-07 01:47:54.000000000 +0800 @@ -185,6 +185,9 @@ #ifdef CONFIG_ZONE_DMA32 256, #endif +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_ZONE_ACP) + 1, +#endif #ifdef CONFIG_HIGHMEM 32, #endif @@ -200,6 +203,9 @@ #ifdef CONFIG_ZONE_DMA32 "DMA32", #endif +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_ZONE_ACP) + "ACP", +#endif "Normal", #ifdef CONFIG_HIGHMEM "HighMem", diff -urN linux-4.1.52-orig/mm/quicklist.c linux-4.1.52/mm/quicklist.c --- linux-4.1.52-orig/mm/quicklist.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/mm/quicklist.c 2020-02-07 01:47:54.000000000 +0800 @@ -37,6 +37,9 @@ #ifdef CONFIG_ZONE_DMA32 zone_page_state(&zones[ZONE_DMA32], NR_FREE_PAGES) + #endif +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_ZONE_ACP) + zone_page_state(&zones[ZONE_ACP], NR_FREE_PAGES) + +#endif zone_page_state(&zones[ZONE_NORMAL], NR_FREE_PAGES); max = node_free_pages / FRACTION_OF_NODE_MEM; diff -urN linux-4.1.52-orig/mm/slub.c linux-4.1.52/mm/slub.c --- linux-4.1.52-orig/mm/slub.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/mm/slub.c 2020-02-07 01:47:54.000000000 +0800 @@ -4571,6 +4571,13 @@ } SLAB_ATTR_RO(cache_dma); #endif +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_ZONE_ACP) +static ssize_t cache_acp_show(struct kmem_cache *s, char *buf) +{ + return sprintf(buf, "%d\n", !!(s->flags & SLAB_CACHE_ACP)); +} +SLAB_ATTR_RO(cache_acp); +#endif static ssize_t destroy_by_rcu_show(struct kmem_cache *s, char *buf) { @@ -4914,6 +4921,9 @@ #ifdef CONFIG_ZONE_DMA &cache_dma_attr.attr, #endif +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_ZONE_ACP) + &cache_acp_attr.attr, +#endif #ifdef CONFIG_NUMA &remote_node_defrag_ratio_attr.attr, #endif diff -urN linux-4.1.52-orig/mm/vmscan.c linux-4.1.52/mm/vmscan.c --- linux-4.1.52-orig/mm/vmscan.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/mm/vmscan.c 2020-02-07 01:47:54.000000000 +0800 @@ -3369,6 +3369,11 @@ }; const struct cpumask *cpumask = cpumask_of_node(pgdat->node_id); +#if defined(CONFIG_BCM_KF_VMSCAN_OPT) + /* Lower the priority to prevent lock up when running low on memory */ + set_user_nice(current, 10); +#endif + lockdep_set_current_reclaim_state(GFP_KERNEL); if (!cpumask_empty(cpumask)) @@ -3463,6 +3468,17 @@ if (!cpuset_zone_allowed(zone, GFP_KERNEL | __GFP_HARDWALL)) return; + +#if defined(CONFIG_BCM_KF_VMSCAN_OPT) + /* Cap the order at 128k blocks to relax fragmentation standard, so kswapd won't be + * running constantly without much progress in our small swapless system. Big blocks + * should be allocated with vmalloc or kmalloc at boot time for our system + */ + if (order > 5) { + order = 5; + } +#endif + pgdat = zone->zone_pgdat; if (pgdat->kswapd_max_order < order) { pgdat->kswapd_max_order = order; @@ -3610,7 +3626,15 @@ * of a node considered for each zone_reclaim. 4 scans 1/16th of * a zone. */ + +#if defined(CONFIG_BCM_KF_VMSCAN_OPT) +/* Start from a higher priority (lower value) for more optimized memory + * scanning, see DEF_PRIORITY + */ +#define ZONE_RECLAIM_PRIORITY 2 +#else #define ZONE_RECLAIM_PRIORITY 4 +#endif /* * Percentage of pages in a zone that must be unmapped for zone_reclaim to diff -urN linux-4.1.52-orig/mm/vmstat.c linux-4.1.52/mm/vmstat.c --- linux-4.1.52-orig/mm/vmstat.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/mm/vmstat.c 2020-02-07 01:47:54.000000000 +0800 @@ -685,6 +685,9 @@ #else #define TEXT_FOR_DMA32(xx) #endif +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_ZONE_ACP) +#define TEXT_FOR_ACP(xx) xx "_acp", +#endif #ifdef CONFIG_HIGHMEM #define TEXT_FOR_HIGHMEM(xx) xx "_high", @@ -692,8 +695,15 @@ #define TEXT_FOR_HIGHMEM(xx) #endif +#if defined(CONFIG_BCM_KF_ARM_BCM963XX) && defined(CONFIG_BCM_ZONE_ACP) +#define TEXTS_FOR_ZONES(xx) TEXT_FOR_DMA(xx) TEXT_FOR_DMA32(xx) TEXT_FOR_ACP(xx) \ + xx "_normal", TEXT_FOR_HIGHMEM(xx) \ + xx "_movable", + +#else #define TEXTS_FOR_ZONES(xx) TEXT_FOR_DMA(xx) TEXT_FOR_DMA32(xx) xx "_normal", \ TEXT_FOR_HIGHMEM(xx) xx "_movable", +#endif const char * const vmstat_text[] = { /* enum zone_stat_item countes */ diff -urN linux-4.1.52-orig/net/8021q/vlan.c linux-4.1.52/net/8021q/vlan.c --- linux-4.1.52-orig/net/8021q/vlan.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/8021q/vlan.c 2020-02-07 01:47:54.000000000 +0800 @@ -40,12 +40,20 @@ #include "vlan.h" #include "vlanproc.h" +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#include +#endif + #define DRV_VERSION "1.8" /* Global VLAN variables */ int vlan_net_id __read_mostly; +#if defined(CONFIG_BCM_KF_VLAN) && (defined(CONFIG_BCM_VLAN) || defined(CONFIG_BCM_VLAN_MODULE)) +int vlan_dev_set_nfmark_to_priority(char *, int); +#endif + const char vlan_fullname[] = "802.1Q VLAN Support"; const char vlan_version[] = DRV_VERSION; @@ -117,6 +125,7 @@ dev_put(real_dev); } + int vlan_check_real_dev(struct net_device *real_dev, __be16 protocol, u16 vlan_id) { @@ -251,6 +260,11 @@ if (new_dev == NULL) return -ENOBUFS; +#if defined(CONFIG_BCM_KF_VLAN) && (defined(CONFIG_BCM_VLAN) || defined(CONFIG_BCM_VLAN_MODULE)) + /* If real device is a hardware switch port, the vlan device must also be */ + new_dev->priv_flags |= real_dev->priv_flags; +#endif + dev_net_set(new_dev, net); /* need 4 bytes for extra VLAN header info, * hope the underlying device can handle it. @@ -554,6 +568,13 @@ args.vlan_qos); break; +#if defined(CONFIG_BCM_KF_VLAN) && (defined(CONFIG_BCM_VLAN) || defined(CONFIG_BCM_VLAN_MODULE)) + case SET_VLAN_NFMARK_TO_PRIORITY_CMD: + err = vlan_dev_set_nfmark_to_priority(args.device1, + args.u.nfmark_to_priority); + break; +#endif + case SET_VLAN_FLAG_CMD: err = -EPERM; if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) diff -urN linux-4.1.52-orig/net/8021q/vlan_core.c linux-4.1.52/net/8021q/vlan_core.c --- linux-4.1.52-orig/net/8021q/vlan_core.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/8021q/vlan_core.c 2020-02-07 01:47:54.000000000 +0800 @@ -21,6 +21,15 @@ if (unlikely(!skb)) return false; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + if (blog_ptr(skb)) + { + blog_lock(); + blog_link( IF_DEVICE, blog_ptr(skb), (void*)vlan_dev, DIR_RX, skb->len ); + blog_unlock(); + } +#endif + skb->dev = vlan_dev; if (unlikely(skb->pkt_type == PACKET_OTHERHOST)) { /* Our lower layer thinks this is not local, let's make sure. diff -urN linux-4.1.52-orig/net/8021q/vlan_dev.c linux-4.1.52/net/8021q/vlan_dev.c --- linux-4.1.52-orig/net/8021q/vlan_dev.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/8021q/vlan_dev.c 2020-02-07 01:47:54.000000000 +0800 @@ -37,6 +37,7 @@ #include #include + /* * Create the VLAN header for an arbitrary protocol layer * @@ -124,6 +125,14 @@ skb->dev = vlan->real_dev; len = skb->len; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + if (blog_ptr(skb)) + { + blog_lock(); + blog_link( IF_DEVICE, blog_ptr(skb), (void*)dev, DIR_TX, skb->len ); + blog_unlock(); + } +#endif if (unlikely(netpoll_tx_running(dev))) return vlan_netpoll_send_skb(vlan, skb); @@ -352,6 +361,36 @@ return 0; } +#if defined(CONFIG_BCM_KF_VLAN) && (defined(CONFIG_BCM_VLAN) || defined(CONFIG_BCM_VLAN_MODULE)) +int vlan_dev_set_nfmark_to_priority(char *dev_name, int nfmark_to_priority) +{ + struct net_device *dev = dev_get_by_name(&init_net, dev_name); + + if (dev) { + if (dev->priv_flags & IFF_802_1Q_VLAN) { + if (nfmark_to_priority>=-1 && nfmark_to_priority <=29) { + vlan_dev_priv(dev)->nfmark_to_priority = nfmark_to_priority; + dev_put(dev); + return 0; + } + else { + printk("invalid nfmark_to_priority\n"); + } + } + else { + printk(KERN_ERR + "%s: %s is not a vlan device, priv_flags: %hX.\n", + __FUNCTION__, dev->name, dev->priv_flags); + } + } + else { + printk(KERN_ERR "%s: Could not find device: %s\n", __FUNCTION__, dev_name); + } + dev_put(dev); + return -EINVAL; +} +#endif + static int vlan_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { struct net_device *real_dev = vlan_dev_priv(dev)->real_dev; @@ -817,5 +856,9 @@ dev->destructor = vlan_dev_free; dev->ethtool_ops = &vlan_ethtool_ops; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + dev->blog_stats_flags |= BLOG_DEV_STAT_FLAG_INCLUDE_ALL; +#endif eth_zero_addr(dev->broadcast); + } diff -urN linux-4.1.52-orig/net/bridge/br_bcm_mcast.c linux-4.1.52/net/bridge/br_bcm_mcast.c --- linux-4.1.52-orig/net/bridge/br_bcm_mcast.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/bridge/br_bcm_mcast.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,64 @@ +#if (defined(CONFIG_BCM_MCAST) || defined(CONFIG_BCM_MCAST_MODULE)) && defined(CONFIG_BCM_KF_MCAST) +/* +* Copyright (c) 2015 Broadcom Corporation +* All Rights Reserved +* +<:label-BRCM:2015:DUAL/GPL:standard + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +#include "br_private.h" +#include + +br_bcm_mcast_receive_hook br_bcm_mcast_receive = NULL; +br_bcm_mcast_should_deliver_hook br_bcm_mcast_should_deliver = NULL; + +int br_bcm_mcast_bind(br_bcm_mcast_receive_hook bcm_rx_hook, br_bcm_mcast_should_deliver_hook bcm_should_deliver_hook) +{ + br_bcm_mcast_receive = bcm_rx_hook; + br_bcm_mcast_should_deliver = bcm_should_deliver_hook; + return 0; +} +EXPORT_SYMBOL(br_bcm_mcast_bind); + +/* must be called with rcu_read_lock */ +int br_bcm_mcast_flood_forward(struct net_device *dev, struct sk_buff *skb) +{ + if ( IFF_EBRIDGE & dev->priv_flags ) + { + struct net_bridge *br = netdev_priv(dev); + if ( NULL == br ) + { + return -EINVAL; + } + br_flood_forward(br, skb, NULL, 0); + } + else + { + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL(br_bcm_mcast_flood_forward); + +#endif /* CONFIG_BCM_MCAST && CONFIG_BCM_KF_MCAST */ diff -urN linux-4.1.52-orig/net/bridge/br_device.c linux-4.1.52/net/bridge/br_device.c --- linux-4.1.52-orig/net/bridge/br_device.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/bridge/br_device.c 2020-02-07 01:47:54.000000000 +0800 @@ -22,6 +22,18 @@ #include #include "br_private.h" +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#include +#endif +#if defined(CONFIG_BCM_KF_WL) +#if defined(PKTC) +#include +#include +extern unsigned long (*wl_pktc_req_hook)(int req_id, unsigned long param0, unsigned long param1, unsigned long param2); +#endif /* PKTC */ +#include +#endif + #define COMMON_FEATURES (NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA | \ NETIF_F_GSO_MASK | NETIF_F_HW_CSUM) @@ -48,6 +60,12 @@ return NETDEV_TX_OK; } +#if defined(CONFIG_BCM_KF_BLOG) + blog_lock(); + blog_link(IF_DEVICE, blog_ptr(skb), (void*)dev, DIR_TX, skb->len); + blog_unlock(); +#endif + u64_stats_update_begin(&brstats->syncp); brstats->tx_packets++; brstats->tx_bytes += skb->len; @@ -80,7 +98,50 @@ else br_flood_deliver(br, skb, false); } else if ((dst = __br_fdb_get(br, dest, vid)) != NULL) +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + { + blog_lock(); + blog_link(BRIDGEFDB, blog_ptr(skb), (void*)dst, BLOG_PARAM1_DSTFDB, 0); + blog_unlock(); +#if defined(CONFIG_BCM_KF_WL) +#if defined(PKTC) + if (wl_pktc_req_hook && (dst->dst != NULL) && + (BLOG_GET_PHYTYPE(dst->dst->dev->path.hw_port_type) == BLOG_WLANPHY) && + wl_pktc_req_hook(PKTC_TBL_GET_TX_MODE, 0, 0, 0)) + { + struct net_device *root_dst_dev_p = dst->dst->dev; + unsigned long chainIdx; + + /* Get the root destination device */ + while (!netdev_path_is_root(root_dst_dev_p)) { + root_dst_dev_p = netdev_path_next_dev(root_dst_dev_p); + } + chainIdx = wl_pktc_req_hook(PKTC_TBL_UPDATE, (unsigned long)&(dst->addr.addr[0]), (unsigned long)root_dst_dev_p, 0); + if (chainIdx != PKTC_INVALID_CHAIN_IDX) + { + // Update chainIdx in blog + if (skb->blog_p != NULL) + { + skb->blog_p->wfd.nic_ucast.is_tx_hw_acc_en = 1; + skb->blog_p->wfd.nic_ucast.is_wfd = 1; + skb->blog_p->wfd.nic_ucast.is_chain = 1; + skb->blog_p->wfd.nic_ucast.wfd_idx = ((chainIdx & PKTC_WFD_IDX_BITMASK) >> PKTC_WFD_IDX_BITPOS); + skb->blog_p->wfd.nic_ucast.chain_idx = chainIdx; + //printk("%s: Added ChainEntryIdx 0x%x Dev %s blogSrcAddr 0x%x blogDstAddr 0x%x DstMac %x:%x:%x:%x:%x:%x " + // "wfd_q %d wl_metadata %d wl 0x%x\n", __FUNCTION__, + // chainIdx, dst->dst->dev->name, skb->blog_p->rx.tuple.saddr, skb->blog_p->rx.tuple.daddr, + // dst->addr.addr[0], dst->addr.addr[1], dst->addr.addr[2], dst->addr.addr[3], dst->addr.addr[4], + // dst->addr.addr[5], skb->blog_p->wfd_queue, skb->blog_p->wl_metadata, skb->blog_p->wl); + } + } + } +#endif +#endif + br_deliver(dst->dst, skb); + } +#else br_deliver(dst->dst, skb); +#endif else br_flood_deliver(br, skb, true); @@ -170,7 +231,6 @@ stats->tx_packets = sum.tx_packets; stats->rx_bytes = sum.rx_bytes; stats->rx_packets = sum.rx_packets; - return stats; } @@ -368,6 +428,10 @@ eth_hw_addr_random(dev); ether_setup(dev); +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + dev->blog_stats_flags |= BLOG_DEV_STAT_FLAG_INCLUDE_ALL; +#endif + dev->netdev_ops = &br_netdev_ops; dev->destructor = br_dev_free; dev->ethtool_ops = &br_ethtool_ops; @@ -400,8 +464,20 @@ br->bridge_hello_time = br->hello_time = 2 * HZ; br->bridge_forward_delay = br->forward_delay = 15 * HZ; br->ageing_time = 300 * HZ; +#if defined(CONFIG_BCM_KF_BRIDGE_COUNTERS) + br->mac_entry_discard_counter = 0; +#endif br_netfilter_rtable_init(br); br_stp_timer_init(br); br_multicast_init(br); + +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) + br->num_fdb_entries = 0; +#endif + +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) && defined(CONFIG_BCM_BRIDGE_MAC_FDB_LIMIT) + br->max_br_fdb_entries = BR_MAX_FDB_ENTRIES; + br->used_br_fdb_entries = 0; +#endif } diff -urN linux-4.1.52-orig/net/bridge/br_fdb.c linux-4.1.52/net/bridge/br_fdb.c --- linux-4.1.52-orig/net/bridge/br_fdb.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/bridge/br_fdb.c 2020-02-07 01:47:54.000000000 +0800 @@ -25,6 +25,30 @@ #include #include #include "br_private.h" +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#include +#endif +#if defined(CONFIG_BCM_KF_LOG) +#include +#endif + +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) +#if defined(CONFIG_BCM_RDPA_BRIDGE) || defined(CONFIG_BCM_RDPA_BRIDGE_MODULE) +#include "br_fp.h" +#include "br_fp_hooks.h" +#endif /* CONFIG_BCM_RDPA_BRIDGE || CONFIG_BCM_RDPA_BRIDGE_MODULE */ +#endif /* CONFIG_BCM_RDPA || CONFIG_BCM_RDPA_MODULE */ +#endif /* CONFIG_BCM_KF_RUNNER */ + +#if defined(CONFIG_BCM_KF_WL) +#include +int (*fdb_check_expired_wl_hook)(unsigned char *addr, + struct net_device * net_device) = NULL; +int (*fdb_check_expired_dhd_hook)(unsigned char *addr, + struct net_device * net_device) = NULL; +#endif + static struct kmem_cache *br_fdb_cache __read_mostly; static struct net_bridge_fdb_entry *fdb_find(struct hlist_head *head, @@ -61,12 +85,26 @@ */ static inline unsigned long hold_time(const struct net_bridge *br) { +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + /* Seems one timer constant in bridge code can serve several different purposes. As we use forward_delay=0, + if the code left unchanged, every entry in fdb will expire immidately after a topology change and every packet + will flood the local ports for a period of bridge_max_age. This will result in low throughput after boot up. + So we decoulpe this timer from forward_delay. */ + return br->topology_change ? (15*HZ) : br->ageing_time; +#else return br->topology_change ? br->forward_delay : br->ageing_time; +#endif } static inline int has_expired(const struct net_bridge *br, const struct net_bridge_fdb_entry *fdb) { +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_lock(); + if (fdb->fdb_key != BLOG_FDB_KEY_INVALID) + blog_query(QUERY_BRIDGEFDB, (void*)fdb, fdb->fdb_key, 0, 0); + blog_unlock(); +#endif return !fdb->is_static && time_before_eq(fdb->updated + hold_time(br), jiffies); } @@ -75,6 +113,9 @@ { /* use 1 byte of OUI and 3 bytes of NIC */ u32 key = get_unaligned((u32 *)(mac + 2)); +#if defined(CONFIG_BCM_KF_VLAN_AGGREGATION) && defined(CONFIG_BCM_VLAN_AGGREGATION) + vid = 0; +#endif return jhash_2words(key, vid, fdb_salt) & (BR_HASH_SIZE - 1); } @@ -130,13 +171,123 @@ } } +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) && defined(CONFIG_BCM_BRIDGE_MAC_FDB_LIMIT) +static int fdb_limit_port_max_check(struct net_bridge_port *port) +{ + if (port->max_port_fdb_entries != 0) { + /* Check per port max limit */ + if ((port->num_port_fdb_entries+1) > port->max_port_fdb_entries) + return -1; + } + return 0; +} + +/* +return 0 if the new learned mac will be one of the reserved mac +return -1 if reserved mac num is not set, or the mac will occupy the non-reserved place*/ +static int fdb_limit_port_min_check(struct net_bridge_port *port) +{ + if (port->min_port_fdb_entries == 0) + return -1; + /* Check per port min limit */ + if ((port->num_port_fdb_entries+1) > port->min_port_fdb_entries) + return -1; + + return 0; +} + +static int fdb_limit_bridge_check(struct net_bridge *br) +{ + if (br->max_br_fdb_entries != 0) { + /* Check per br limit */ + if ((br->used_br_fdb_entries+1) > br->max_br_fdb_entries) + return -1; + } + + return 0; +} + +static int fdb_limit_check(struct net_bridge *br, struct net_bridge_port *port) +{ + /*if excceeds port max, return fail*/ + if(fdb_limit_port_max_check(port)) + return -1; + + /*else if still in port reserved range, return success*/ + if(0 == fdb_limit_port_min_check(port)) + return 0; + + /*else depend on bridge max check + br->used_br_fdb_entries need to be checked only when port reserved range has been excceeded*/ + return fdb_limit_bridge_check(br); +} + +static int fdb_limit_mac_move_check(struct net_bridge *br, struct net_bridge_port *from, struct net_bridge_port *to) +{ + /*if excceed port max, return fail*/ + if(fdb_limit_port_max_check(to)) + return -1; + + /*else if the mac has already excceeded the from port's reserved places + which means the bridge still has place for the mac*/ + if (from->num_port_fdb_entries > from->min_port_fdb_entries) + return 0; + + /*else if still in to port reserved range, return success*/ + if(0 == fdb_limit_port_min_check(to)) + return 0; + + /*else depend on bridge max check + br->used_br_fdb_entries need to be checked only when port reserved range has been excceeded*/ + return fdb_limit_bridge_check(br); +} + +static void fdb_limit_update(struct net_bridge *br, struct net_bridge_port *port, int isAdd) +{ + if (isAdd) { + port->num_port_fdb_entries++; + if (port->num_port_fdb_entries > port->min_port_fdb_entries) + br->used_br_fdb_entries++; + } + else { + BUG_ON(!port->num_port_fdb_entries); + port->num_port_fdb_entries--; + if (port->num_port_fdb_entries >= port->min_port_fdb_entries) + br->used_br_fdb_entries--; + } +} +#endif + static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f) { if (f->is_static) fdb_del_hw_addr(br, f->addr.addr); +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) + br->num_fdb_entries--; +#endif + +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) && defined(CONFIG_BCM_BRIDGE_MAC_FDB_LIMIT) + if (f->is_local == 0) { + fdb_limit_update(br, f->dst, 0); + } +#endif + +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) +#if defined(CONFIG_BCM_RDPA_BRIDGE) || defined(CONFIG_BCM_RDPA_BRIDGE_MODULE) + if (!f->is_local) /* Do not remove local MAC to the Runner */ + br_fp_hook(BR_FP_FDB_REMOVE, f, NULL); +#endif /* CONFIG_BCM_RDPA_BRIDGE || CONFIG_BCM_RDPA_BRIDGE_MODULE */ +#endif /* CONFIG_BCM_RDPA || CONFIG_BCM_RDPA_MODULE */ +#endif /* CONFIG_BCM_KF_RUNNER */ hlist_del_rcu(&f->hlist); fdb_notify(br, f, RTM_DELNEIGH); +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_lock(); + blog_notify_async(DESTROY_BRIDGEFDB, (void*)f, f->fdb_key, 0, NULL, NULL); + blog_unlock(); +#endif /* CONFIG_BCM_KF_BLOG && CONFIG_BLOG */ call_rcu(&f->rcu, fdb_rcu_free); } @@ -282,9 +433,41 @@ unsigned long this_timer; if (f->is_static) continue; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_lock(); + if (f->fdb_key != BLOG_FDB_KEY_INVALID) + blog_query(QUERY_BRIDGEFDB, (void*)f, f->fdb_key, 0, 0); + blog_unlock(); +#endif this_timer = f->updated + delay; if (time_before_eq(this_timer, jiffies)) +#if defined(CONFIG_BCM_KF_RUNNER) || defined(CONFIG_BCM_KF_WL) + { + int flag = 0; + struct net_device * dev_p; + dev_p = ((f->dst == NULL) || (f->dst->dev == NULL)) ? + NULL : f->dst->dev; + +#if (defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE)) && (defined(CONFIG_BCM_RDPA_BRIDGE) || defined(CONFIG_BCM_RDPA_BRIDGE_MODULE)) + br_fp_hook(BR_FP_FDB_CHECK_AGE, f, &flag); +#endif /* CONFIG_BCM_RDPA && CONFIG_BCM_RDPA_BRIDGE && CONFIG_BCM_RDPA_BRIDGE_MODULE */ + if (flag +#if defined(CONFIG_BCM_KF_WL) + || (fdb_check_expired_wl_hook && + (fdb_check_expired_wl_hook(f->addr.addr, dev_p) == 0)) + || (fdb_check_expired_dhd_hook && + (fdb_check_expired_dhd_hook(f->addr.addr, dev_p) == 0)) +#endif + ) + { + f->updated = jiffies; + } + else + fdb_delete(br, f); + } +#else fdb_delete(br, f); +#endif else if (time_before(this_timer, next_timer)) next_timer = this_timer; } @@ -305,7 +488,22 @@ struct hlist_node *n; hlist_for_each_entry_safe(f, n, &br->hash[i], hlist) { if (!f->is_static) +#if defined(CONFIG_BCM_KF_RUNNER) && (defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE)) && (defined(CONFIG_BCM_RDPA_BRIDGE) || defined(CONFIG_BCM_RDPA_BRIDGE_MODULE)) + { + int flag = 0; + + br_fp_hook(BR_FP_FDB_CHECK_AGE, f, &flag); + if (flag) { + f->updated = jiffies; + } + else + { + fdb_delete(br, f); + } + } +#else /* CONFIG_BCM_KF_RUNNER && CONFIG_BCM_RUNNER && (CONFIG_BCM_RDPA_BRIDGE || CONFIG_BCM_RDPA_BRIDGE_MODULE) */ fdb_delete(br, f); +#endif /* CONFIG_BCM_KF_RUNNER && CONFIG_BCM_RUNNER && (CONFIG_BCM_RDPA_BRIDGE || CONFIG_BCM_RDPA_BRIDGE_MODULE) */ } } spin_unlock_bh(&br->hash_lock); @@ -342,7 +540,85 @@ spin_unlock_bh(&br->hash_lock); } +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) && defined(CONFIG_BCM_BRIDGE_MAC_FDB_LIMIT) +/* Set FDB limit + lmtType 0: Bridge limit + 1: Port limit */ +int br_set_fdb_limit(struct net_bridge *br, + struct net_bridge_port *p, + int lmt_type, + int is_min, + int fdb_limit) +{ + int new_used_fdb; + + if((br == NULL) || ((p == NULL) && lmt_type)) + return -EINVAL; + + if(fdb_limit == 0) { + /* Disable limit */ + if(lmt_type == 0) { + br->max_br_fdb_entries = 0; + } + else if(is_min) { + if (p->num_port_fdb_entries < p->min_port_fdb_entries) { + new_used_fdb = br->used_br_fdb_entries - p->min_port_fdb_entries; + new_used_fdb += p->num_port_fdb_entries; + br->used_br_fdb_entries = new_used_fdb; + } + p->min_port_fdb_entries = 0; + } + else { + p->max_port_fdb_entries = 0; + } + } + else { + if(lmt_type == 0) { + if(br->used_br_fdb_entries > fdb_limit) + return -EINVAL; + br->max_br_fdb_entries = fdb_limit; + } + else if(is_min) { + new_used_fdb = max(p->num_port_fdb_entries, p->min_port_fdb_entries); + new_used_fdb = br->used_br_fdb_entries - new_used_fdb; + new_used_fdb += max(p->num_port_fdb_entries, fdb_limit); + if ( (br->max_br_fdb_entries != 0) && + (new_used_fdb > br->max_br_fdb_entries) ) + return -EINVAL; + + p->min_port_fdb_entries = fdb_limit; + br->used_br_fdb_entries = new_used_fdb; + } + else { + if(p->num_port_fdb_entries > fdb_limit) + return -EINVAL; + p->max_port_fdb_entries = fdb_limit; + } + } + return 0; +} +#endif + /* No locking or refcounting, assumes caller has rcu_read_lock */ +#if defined(CONFIG_BCM_KF_VLAN_AGGREGATION) && defined(CONFIG_BCM_VLAN_AGGREGATION) +struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br, + const unsigned char *addr, + __u16 vid __attribute__((unused))) +{ + struct net_bridge_fdb_entry *fdb; + + hlist_for_each_entry_rcu(fdb, + &br->hash[br_mac_hash(addr, vid)], hlist) { + if (ether_addr_equal(fdb->addr.addr, addr)) { + if (unlikely(has_expired(br, fdb))) + break; + return fdb; + } + } + + return NULL; +} +#else struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br, const unsigned char *addr, __u16 vid) @@ -361,6 +637,7 @@ return NULL; } +#endif #if IS_ENABLED(CONFIG_ATM_LANE) /* Interface used by ATM LANE hook to test @@ -427,6 +704,9 @@ fe->is_local = f->is_local; if (!f->is_static) fe->ageing_timer_value = jiffies_delta_to_clock_t(jiffies - f->updated); +#if defined(CONFIG_BCM_KF_VLAN_AGGREGATION) && defined(CONFIG_BCM_VLAN_AGGREGATION) + fe->vid = f->vlan_id; +#endif ++fe; ++num; } @@ -438,6 +718,34 @@ return num; } +#if defined(CONFIG_BCM_KF_VLAN_AGGREGATION) && defined(CONFIG_BCM_VLAN_AGGREGATION) +static struct net_bridge_fdb_entry *fdb_find(struct hlist_head *head, + const unsigned char *addr, + __u16 vid __attribute__((unused))) +{ + struct net_bridge_fdb_entry *fdb; + + hlist_for_each_entry(fdb, head, hlist) { + if (ether_addr_equal(fdb->addr.addr, addr)) + return fdb; + } + return NULL; +} + +static struct net_bridge_fdb_entry *fdb_find_rcu(struct hlist_head *head, + const unsigned char *addr, + __u16 vid __attribute__((unused))) +{ + struct net_bridge_fdb_entry *fdb; + + hlist_for_each_entry_rcu(fdb, head, hlist) { + if (ether_addr_equal(fdb->addr.addr, addr)) + return fdb; + } + return NULL; +} + +#else static struct net_bridge_fdb_entry *fdb_find(struct hlist_head *head, const unsigned char *addr, __u16 vid) @@ -465,14 +773,27 @@ } return NULL; } +#endif /* defined(CONFIG_BCM_KF_VLAN_AGGREGATION) && defined(CONFIG_BCM_VLAN_AGGREGATION) */ +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) +static struct net_bridge_fdb_entry *fdb_create(struct net_bridge *br, + struct hlist_head *head, + struct net_bridge_port *source, + const unsigned char *addr, + __u16 vid) +#else static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head, struct net_bridge_port *source, const unsigned char *addr, __u16 vid) +#endif { struct net_bridge_fdb_entry *fdb; +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) + if (br->num_fdb_entries >= BR_MAX_FDB_ENTRIES) + return NULL; +#endif fdb = kmem_cache_alloc(br_fdb_cache, GFP_ATOMIC); if (fdb) { memcpy(fdb->addr.addr, addr, ETH_ALEN); @@ -483,6 +804,14 @@ fdb->added_by_user = 0; fdb->added_by_external_learn = 0; fdb->updated = fdb->used = jiffies; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + fdb->fdb_key = BLOG_FDB_KEY_INVALID; +#endif + +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) + br->num_fdb_entries++; +#endif + hlist_add_head_rcu(&fdb->hlist, head); } return fdb; @@ -510,7 +839,11 @@ fdb_delete(br, fdb); } +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) + fdb = fdb_create(br, head, source, addr, vid); +#else fdb = fdb_create(head, source, addr, vid); +#endif if (!fdb) return -ENOMEM; @@ -556,13 +889,79 @@ br_warn(br, "received packet on %s with " "own address as source address\n", source->dev->name); +#if defined(CONFIG_BCM_KF_BRIDGE_STATIC_FDB_MOVE) + } else if ( likely (fdb->is_static == 0) ) { + /* don't allow static fdb entries to move */ +#else } else { +#endif +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) +#if defined(CONFIG_BCM_RDPA_BRIDGE) || defined(CONFIG_BCM_RDPA_BRIDGE_MODULE) + struct net_bridge_port *fdb_dst = fdb->dst; +#if defined(CONFIG_BCM_KF_VLAN_AGGREGATION) && defined(CONFIG_BCM_VLAN_AGGREGATION) + unsigned int fdb_vid = fdb->vlan_id; +#endif /* CONFIG_BCM_KF_VLAN_AGGREGATION && CONFIG_BCM_VLAN_AGGREGATION */ +#endif /* CONFIG_BCM_RDPA_BRIDGE || CONFIG_BCM_RDPA_BRIDGE_MODULE */ +#endif /* CONFIG_BCM_RUNNER */ +#endif /* CONFIG_BCM_KF_RUNNER */ + +#if defined(CONFIG_BCM_KF_BRIDGE_STATIC_FDB_MOVE) +/*TODO find the correct KF */ + /* In case of MAC move - let ethernet driver clear switch ARL */ + if (fdb->dst && fdb->dst->port_no != source->port_no) { + bcmFun_t *ethswClearArlFun; + + /* Get the switch clear ARL function pointer */ + ethswClearArlFun = bcmFun_get(BCM_FUN_IN_ENET_CLEAR_ARL_ENTRY); + if ( ethswClearArlFun ) { + ethswClearArlFun((void*)addr); + } +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_lock(); + /* Also flush the associated entries in accelerators */ + blog_notify_async(DESTROY_BRIDGEFDB, (void*)fdb, fdb->fdb_key, + 0, NULL, NULL); + blog_unlock(); +#endif /* CONFIG_BCM_KF_BLOG && CONFIG_BLOG */ + } +#endif /* CONFIG_BCM_KF_BRIDGE_STATIC_FDB_MOVE */ + /* fastpath: update of existing entry */ if (unlikely(source != fdb->dst)) { +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) && defined(CONFIG_BCM_BRIDGE_MAC_FDB_LIMIT) + /* Check that mac can be learned on new port */ + if (fdb_limit_mac_move_check(br, fdb->dst, source) != 0) + { + return; + } + /* Modify both old and new port counter */ + fdb_limit_update(br, fdb->dst, 0); + fdb_limit_update(br, source, 1); +#endif /* CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT && CONFIG_BCM_BRIDGE_MAC_FDB_LIMIT */ fdb->dst = source; fdb_modified = true; } fdb->updated = jiffies; +#if defined(CONFIG_BCM_KF_VLAN_AGGREGATION) && defined(CONFIG_BCM_VLAN_AGGREGATION) + fdb->vlan_id = vid; +#endif /* CONFIG_BCM_KF_VLAN_AGGREGATION && CONFIG_BCM_VLAN_AGGREGATION */ +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) +#if defined(CONFIG_BCM_RDPA_BRIDGE) || defined(CONFIG_BCM_RDPA_BRIDGE_MODULE) +#if defined(CONFIG_BCM_KF_VLAN_AGGREGATION) && defined(CONFIG_BCM_VLAN_AGGREGATION) + /* Do not update FastPath if the the source still == dst and vid is same */ + if (fdb_dst != source || fdb_vid != vid) + br_fp_hook(BR_FP_FDB_MODIFY, fdb, NULL); +#else + /* Do not update FastPath if the the source still == dst */ + if (fdb_dst != source) + br_fp_hook(BR_FP_FDB_MODIFY, fdb, NULL); +#endif /* CONFIG_BCM_KF_VLAN_AGGREGATION && CONFIG_BCM_VLAN_AGGREGATION */ +#endif /* CONFIG_BCM_RDPA_BRIDGE || CONFIG_BCM_RDPA_BRIDGE_MODULE */ +#endif /* CONFIG_BCM_RUNNER */ +#endif /* CONFIG_BCM_KF_RUNNER */ + if (unlikely(added_by_user)) fdb->added_by_user = 1; if (unlikely(fdb_modified)) @@ -571,11 +970,46 @@ } else { spin_lock(&br->hash_lock); if (likely(!fdb_find(head, addr, vid))) { +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) && defined(CONFIG_BCM_BRIDGE_MAC_FDB_LIMIT) + fdb = NULL; + if (fdb_limit_check(br, source) == 0) +#endif +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) + fdb = fdb_create(br, head, source, addr, vid); +#else fdb = fdb_create(head, source, addr, vid); +#endif if (fdb) { if (unlikely(added_by_user)) fdb->added_by_user = 1; fdb_notify(br, fdb, RTM_NEWNEIGH); +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) + /* In case of new MAC - let ethernet driver clear switch ARL */ + if (fdb->dst) { + bcmFun_t *ethswClearArlFun; + /* Get the switch clear ARL function pointer */ + ethswClearArlFun = bcmFun_get(BCM_FUN_IN_ENET_CLEAR_ARL_ENTRY); + if ( ethswClearArlFun ) { + struct net_device *dev = fdb->dst->dev; + + while( !netdev_path_is_root(dev) ) // find root device + dev = netdev_path_next_dev(dev); + if (!(dev->priv_flags & IFF_EXT_SWITCH)) // clear if root device is not on ext_switch + ethswClearArlFun((void*)addr); + } + } +#endif /* CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT */ + +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) && defined(CONFIG_BCM_BRIDGE_MAC_FDB_LIMIT) + fdb_limit_update(br, source, 1); +#endif +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) +#if defined(CONFIG_BCM_RDPA_BRIDGE) || defined(CONFIG_BCM_RDPA_BRIDGE_MODULE) + br_fp_hook(BR_FP_FDB_ADD, fdb, NULL); +#endif /* CONFIG_BCM_RDPA_BRIDGE || CONFIG_BCM_RDPA_BRIDGE_MODULE */ +#endif /* CONFIG_BCM_RUNNER */ +#endif /* CONFIG_BCM_KF_RUNNER */ } } /* else we lose race and someone else inserts @@ -625,6 +1059,12 @@ goto nla_put_failure; ci.ndm_used = jiffies_to_clock_t(now - fdb->used); ci.ndm_confirmed = 0; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_lock(); + if (fdb->fdb_key != BLOG_FDB_KEY_INVALID) + blog_query(QUERY_BRIDGEFDB, (void*)fdb, fdb->fdb_key, 0, 0); + blog_unlock(); +#endif ci.ndm_updated = jiffies_to_clock_t(now - fdb->updated); ci.ndm_refcnt = 0; if (nla_put(skb, NDA_CACHEINFO, sizeof(ci), &ci)) @@ -741,21 +1181,75 @@ if (!(flags & NLM_F_CREATE)) return -ENOENT; +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) && defined(CONFIG_BCM_BRIDGE_MAC_FDB_LIMIT) + fdb = NULL; + if ((state & NUD_PERMANENT) || (fdb_limit_check(br, source) == 0)) +#endif +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) + fdb = fdb_create(br, head, source, addr, vid); +#else fdb = fdb_create(head, source, addr, vid); +#endif if (!fdb) return -ENOMEM; +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) && defined(CONFIG_BCM_BRIDGE_MAC_FDB_LIMIT) + if (!(state & NUD_PERMANENT)) + { + fdb_limit_update(br, source, 1); + } +#endif +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) +#if defined(CONFIG_BCM_RDPA_BRIDGE) || defined(CONFIG_BCM_RDPA_BRIDGE_MODULE) + if (!(state & NUD_PERMANENT)) + { + br_fp_hook(BR_FP_FDB_ADD, fdb, NULL); + } +#endif /* CONFIG_BCM_RDPA_BRIDGE || CONFIG_BCM_RDPA_BRIDGE_MODULE */ +#endif /* CONFIG_BCM_RUNNER */ +#endif /* CONFIG_BCM_KF_RUNNER */ modified = true; } else { if (flags & NLM_F_EXCL) return -EEXIST; +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) +#if defined(CONFIG_BCM_RDPA_BRIDGE) || defined(CONFIG_BCM_RDPA_BRIDGE_MODULE) +#if defined(CONFIG_BCM_KF_VLAN_AGGREGATION) && defined(CONFIG_BCM_VLAN_AGGREGATION) + /* Do not update FastPath if the the source still == dst and vid is same */ + if (fdb->dst != source || fdb->vlan_id != vid) + br_fp_hook(BR_FP_FDB_MODIFY, fdb, NULL); +#else + /* Do not update FastPath if the the source still == dst */ + if (fdb->dst != source) + br_fp_hook(BR_FP_FDB_MODIFY, fdb, NULL); +#endif /* CONFIG_BCM_KF_VLAN_AGGREGATION && CONFIG_BCM_VLAN_AGGREGATION */ +#endif /* CONFIG_BCM_RDPA_BRIDGE || CONFIG_BCM_RDPA_BRIDGE_MODULE */ +#endif /* CONFIG_BCM_RUNNER */ +#endif /* CONFIG_BCM_KF_RUNNER */ if (fdb->dst != source) { +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) && defined(CONFIG_BCM_BRIDGE_MAC_FDB_LIMIT) + if ( !fdb->is_local ) + { + /* Check that mac can be learned on new port */ + if (fdb_limit_mac_move_check(br, fdb->dst, source) != 0) + { + return -EEXIST; + } + /* Modify both of old and new port counter */ + fdb_limit_update(br, fdb->dst, 0); + fdb_limit_update(br, source, 1); + } +#endif /* CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT && CONFIG_BCM_BRIDGE_MAC_FDB_LIMIT */ fdb->dst = source; modified = true; } } - +#if defined(CONFIG_BCM_KF_VLAN_AGGREGATION) && defined(CONFIG_BCM_VLAN_AGGREGATION) + fdb->vlan_id = vid; +#endif /* CONFIG_BCM_KF_VLAN_AGGREGATION && CONFIG_BCM_VLAN_AGGREGATION */ if (fdb_to_nud(fdb) != state) { if (state & NUD_PERMANENT) { fdb->is_local = 1; @@ -848,7 +1342,11 @@ /* VID was specified, so use it. */ err = __br_fdb_add(ndm, p, addr, nlh_flags, vid); } else { +#if defined(CONFIG_BCM_KF_VLAN_AGGREGATION) && defined(CONFIG_BCM_VLAN_AGGREGATION) + err = __br_fdb_add(ndm, p, addr, nlh_flags, VLAN_N_VID); +#else err = __br_fdb_add(ndm, p, addr, nlh_flags, 0); +#endif if (err || !pv) goto out; @@ -1004,7 +1502,11 @@ head = &br->hash[br_mac_hash(addr, vid)]; fdb = fdb_find(head, addr, vid); if (!fdb) { +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) + fdb = fdb_create(br, head, p, addr, vid); +#else fdb = fdb_create(head, p, addr, vid); +#endif if (!fdb) { err = -ENOMEM; goto err_unlock; @@ -1048,3 +1550,38 @@ return err; } + +#if defined(CONFIG_BCM_KF_WL) +EXPORT_SYMBOL(fdb_check_expired_wl_hook); +EXPORT_SYMBOL(fdb_check_expired_dhd_hook); +#endif + +#if defined(CONFIG_BCM_KF_VLAN_AGGREGATION) && defined(CONFIG_BCM_VLAN_AGGREGATION) +int br_fdb_get_vid(const unsigned char *addr) +{ + struct net_bridge *br = NULL; + struct net_bridge_fdb_entry *fdb; + struct net_device *br_dev; + int addr_hash = br_mac_hash(addr, 0); + int vid = -1; + + rcu_read_lock(); + + for_each_netdev(&init_net, br_dev){ + if (br_dev->priv_flags & IFF_EBRIDGE) { + br = netdev_priv(br_dev); + hlist_for_each_entry_rcu(fdb, &br->hash[addr_hash], hlist) { + if (ether_addr_equal(fdb->addr.addr, addr)) { + if (unlikely(!has_expired(br, fdb))) + vid = (int)fdb->vlan_id; + break; + } + } + } + } + + rcu_read_unlock(); + return vid; +} +EXPORT_SYMBOL(br_fdb_get_vid); +#endif //defined(CONFIG_BCM_KF_VLAN_AGGREGATION) && defined(CONFIG_BCM_VLAN_AGGREGATION) diff -urN linux-4.1.52-orig/net/bridge/br_forward.c linux-4.1.52/net/bridge/br_forward.c --- linux-4.1.52-orig/net/bridge/br_forward.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/bridge/br_forward.c 2020-02-07 01:47:54.000000000 +0800 @@ -20,24 +20,154 @@ #include #include #include "br_private.h" +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#include +#endif static int deliver_clone(const struct net_bridge_port *prev, struct sk_buff *skb, void (*__packet_hook)(const struct net_bridge_port *p, struct sk_buff *skb)); +#if defined(CONFIG_BCM_KF_WL) +static __inline__ int shouldBypassStp (const struct sk_buff *skb, int state) { + if (skb->pkt_type == PACKET_BROADCAST || skb->pkt_type == PACKET_MULTICAST) + return 0; + if (state == BR_STATE_DISABLED) + return 0; + return ( (skb->protocol == htons(0x888e) /* ETHER_TYPE_802_1X */) || + (skb->protocol == htons(0x88c7) /* ETHER_TYPE_802_1X_PREAUTH */) || + (skb->protocol == htons(0x886c) /* ETHER_TYPE_BRCM */ ) ); +} +#endif + /* Don't forward packets to originating port or forwarding disabled */ static inline int should_deliver(const struct net_bridge_port *p, const struct sk_buff *skb) { +#if defined(CONFIG_BCM_KF_WANDEV) +#if !defined(CONFIG_BCM_WAN_2_WAN_FWD_ENABLED) + /* + * Do not forward any packets received from one WAN interface + * to another in multiple PVC case + */ + if( (skb->dev->priv_flags & p->dev->priv_flags) & IFF_WANDEV ) + { + return 0; + } +#endif + + /* Runner can flood both US/LS and DS traffic */ +#if !defined(CONFIG_BCM_KF_RUNNER_FLOODING) && !defined(CONFIG_BCM_RUNNER_FLOODING) + if ((skb->dev->priv_flags & IFF_WANDEV) == 0 && + (p->dev->priv_flags & IFF_WANDEV) == 0) +#endif + { + struct net_device *sdev = skb->dev; + struct net_device *ddev = p->dev; + +#if defined(CONFIG_BCM_KF_NETDEV_PATH) + /* From LAN to LAN */ + /* Do not forward any packets to virtual interfaces on the same + * real interface of the originating virtual interface. + */ + while (!netdev_path_is_root(sdev)) + { + sdev = netdev_path_next_dev(sdev); + } + + while (!netdev_path_is_root(ddev)) + { + ddev = netdev_path_next_dev(ddev); + } +#endif + +#if defined(CONFIG_BCM_KF_LOCAL_SWITCHING_DISABLE) + /* Drop LAN-LAN traffic; do not drop local packets */ + if (p->br->local_switching_disable && !(sdev->priv_flags & IFF_EBRIDGE) && !(sdev->priv_flags & ddev->priv_flags & IFF_WANDEV)) + { + return 0; + } +#endif + + if (sdev == ddev) + { + return 0; + } + +#if defined(CONFIG_BCM_KF_RUNNER_FLOODING) && defined(CONFIG_BCM_RUNNER_FLOODING) + if ((skb->recycle_and_rnr_flags & SKB_RNR_FLOOD) && (ddev->priv_flags & IFF_HW_SWITCH)) + { + /* Flooded by Runner */ + return 0; + } + +#else + + if (skb->pkt_type == PACKET_BROADCAST) + { +#if defined(CONFIG_BCM_KF_ENET_SWITCH) + if (sdev->priv_flags & IFF_HW_SWITCH & ddev->priv_flags) + { + /* both source and destination are IFF_HW_SWITCH + if they are also on the same switch, reject the packet */ + if (!((sdev->priv_flags & IFF_EXT_SWITCH) ^ (ddev->priv_flags & IFF_EXT_SWITCH))) + { + return 0; + } + } +#endif /* CONFIG_BCM_KF_ENET_SWITCH */ + } + +#endif /* CONFIG_BCM_KF_RUNNER_FLOODING */ + } +#endif /* CONFIG_BCM_KF_WANDEV */ + +#if (defined(CONFIG_BCM_MCAST) || defined(CONFIG_BCM_MCAST_MODULE)) && defined(CONFIG_BCM_KF_MCAST) + if ( br_bcm_mcast_should_deliver != NULL ) + { + if ( 0 == br_bcm_mcast_should_deliver(p->br->dev->ifindex, skb, p->dev, +#if defined(CONFIG_BRIDGE_IGMP_SNOOPING) + p->multicast_router == 2 || (p->multicast_router == 1 && timer_pending(&p->multicast_router_timer))) ) +#else + false) ) +#endif + { + return 0; + } + } +#endif + +#if defined(CONFIG_BCM_KF_WL) + return (((p->flags & BR_HAIRPIN_MODE) || skb->dev != p->dev) && + br_allowed_egress(p->br, nbp_get_vlan_info(p), skb) && + ((p->state == BR_STATE_FORWARDING) || shouldBypassStp(skb, p->state))); +#else return ((p->flags & BR_HAIRPIN_MODE) || skb->dev != p->dev) && br_allowed_egress(p->br, nbp_get_vlan_info(p), skb) && p->state == BR_STATE_FORWARDING; +#endif +} + + +#ifdef CONFIG_BCM_KF_MISC_BACKPORTS +static inline unsigned packet_length(const struct sk_buff *skb) +{ + return skb->len - (skb->protocol == htons(ETH_P_8021Q) ? VLAN_HLEN : 0); } +#endif int br_dev_queue_push_xmit(struct sock *sk, struct sk_buff *skb) { +#ifdef CONFIG_BCM_KF_MISC_BACKPORTS + /* is_skb_forwardable() is assuming skb->len already has ETH_LEN and + * that is not correct here and this can cause the packets >MTU + * to be forwarded, adding proper checks + */ + if((packet_length(skb) > skb->dev->mtu ) && !skb_is_gso(skb)) { +#else if (!is_skb_forwardable(skb->dev, skb)) { +#endif kfree_skb(skb); } else { skb_push(skb, ETH_HLEN); @@ -68,7 +198,15 @@ skb->dev = to->dev; if (unlikely(netpoll_tx_running(to->br->dev))) { +#ifdef CONFIG_BCM_KF_MISC_BACKPORTS + /* is_skb_forwardable() is assuming skb->len already has ETH_LEN and + * that is not correct here, this can cause the packets >MTU + * to be forwarded, adding proper checks + */ + if (packet_length(skb) > skb->dev->mtu && !skb_is_gso(skb)) +#else if (!is_skb_forwardable(skb->dev, skb)) +#endif kfree_skb(skb); else { skb_push(skb, ETH_HLEN); @@ -137,13 +275,19 @@ struct sk_buff *skb)) { struct net_device *dev = BR_INPUT_SKB_CB(skb)->brdev; - +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + struct sk_buff *skb2 = skb; +#endif skb = skb_clone(skb, GFP_ATOMIC); if (!skb) { dev->stats.tx_dropped++; return -ENOMEM; } +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_clone(skb2, blog_ptr(skb)); +#endif + __packet_hook(prev, skb); return 0; } @@ -180,6 +324,20 @@ struct net_bridge_port *p; struct net_bridge_port *prev; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + Blog_t * blog_p = blog_ptr(skb); + + if (blog_p && !blog_p->rx.multicast) + { +#if defined(CONFIG_BCM_KF_INTF_BRG) && defined (CONFIG_BCM_INTF_BRG_ENABLED) + /* Keep blog for interface-based bridging. */ + if (!is_interface_br(br, skb)) +#endif /* CONFIG_BCM_KF_INTF_BRG && CONFIG_BCM_INTF_BRG_ENABLED */ + { + blog_skip(skb, blog_skip_reason_br_flood); + } + } +#endif prev = NULL; list_for_each_entry_rcu(p, &br->port_list, list) { diff -urN linux-4.1.52-orig/net/bridge/br_fp.c linux-4.1.52/net/bridge/br_fp.c --- linux-4.1.52-orig/net/bridge/br_fp.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/bridge/br_fp.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,36 @@ +#if defined(CONFIG_BCM_KF_RUNNER) +/* + * + * 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 + * + * 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 "br_private.h" +#include "br_fp.h" + +struct br_fp_data *fp_hooks; + +void br_fp_set_callbacks(struct br_fp_data *fpdata) +{ + fp_hooks = fpdata; +} + +void br_fp_clear_callbacks(void) +{ + fp_hooks = NULL; +} + +EXPORT_SYMBOL(br_fp_set_callbacks); +EXPORT_SYMBOL(br_fp_clear_callbacks); + +#endif diff -urN linux-4.1.52-orig/net/bridge/br_fp.h linux-4.1.52/net/bridge/br_fp.h --- linux-4.1.52-orig/net/bridge/br_fp.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/bridge/br_fp.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,44 @@ +#if defined(CONFIG_BCM_KF_RUNNER) +/* + * + * 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; + * + * 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. + */ + +#ifndef BR_FP_H +#define BR_FP_H + +#include +#include + +#define BR_FP_FDB_ADD 1 +#define BR_FP_FDB_REMOVE 2 +#define BR_FP_FDB_MODIFY 3 +#define BR_FP_FDB_CHECK_AGE 4 +#define BR_FP_PORT_ADD 5 +#define BR_FP_PORT_REMOVE 6 +#define BR_FP_LOCAL_SWITCHING_DISABLE 7 +#define BR_FP_BRIDGE_TYPE 8 + +struct br_fp_data +{ + int (*rdpa_hook)(int cmd, void *in, void *out); + void *rdpa_priv; +}; + +/* interface routine */ +void br_fp_set_callbacks(struct br_fp_data *fpdata); +void br_fp_clear_callbacks(void); + +#endif /* BR_FP_H */ +#endif diff -urN linux-4.1.52-orig/net/bridge/br_fp_hooks.h linux-4.1.52/net/bridge/br_fp_hooks.h --- linux-4.1.52-orig/net/bridge/br_fp_hooks.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/bridge/br_fp_hooks.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,46 @@ +#if defined(CONFIG_BCM_KF_RUNNER) +/* + * + * 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; + * + * 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. + */ +#ifndef BR_FP_HOOKS_H +#define BR_FP_HOOKS_H + +#include +#include "br_private.h" + +extern struct br_fp_data *fp_hooks; + +#undef BR_FP_DEBUG_SET +#ifdef BR_FP_DEBUG_SET +#define BR_FP_DEBUG_LEVEL 4 +#define BR_FP_START_DEBUG(n) do { if (nrdpa_hook(cmd, in, out); +} + +#endif /* BR_FP_HOOKS_H */ +#endif diff -urN linux-4.1.52-orig/net/bridge/br_if.c linux-4.1.52/net/bridge/br_if.c --- linux-4.1.52-orig/net/bridge/br_if.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/bridge/br_if.c 2020-02-07 01:47:54.000000000 +0800 @@ -27,6 +27,11 @@ #include "br_private.h" +#if defined(CONFIG_BCM_KF_RUNNER) && (defined(CONFIG_BCM_RDPA_BRIDGE) || defined(CONFIG_BCM_RDPA_BRIDGE_MODULE)) +#include "br_fp.h" +#include "br_fp_hooks.h" +#endif + /* * Determine initial path cost based on speed. * using recommendations from 802.1d standard @@ -245,9 +250,12 @@ spin_unlock_bh(&br->lock); br_ifinfo_notify(RTM_DELLINK, p); - list_del_rcu(&p->list); +#if defined(CONFIG_BCM_KF_INTF_BRG) && defined(CONFIG_BCM_INTF_BRG_ENABLED) + br_port_num_dec(br, p); +#endif /* CONFIG_BCM_KF_INTF_BRG && CONFIG_BCM_INTF_BRG_ENABLED */ + nbp_vlan_flush(p); br_fdb_delete_by_port(br, p, 1); nbp_update_port_count(br); @@ -333,9 +341,19 @@ p->flags = BR_LEARNING | BR_FLOOD; br_init_port(p); br_set_state(p, BR_STATE_DISABLED); + +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) && defined(CONFIG_BCM_BRIDGE_MAC_FDB_LIMIT) + p->min_port_fdb_entries = 0; + p->max_port_fdb_entries = 0; + p->num_port_fdb_entries = 0; +#endif + br_stp_port_timer_init(p); br_multicast_add_port(p); +#if defined(CONFIG_BCM_KF_BRIDGE_STP) + br_stp_notify_state_port(p); +#endif return p; } @@ -493,6 +511,12 @@ if (err) goto err5; +#if defined(CONFIG_BCM_KF_RUNNER) && (defined(CONFIG_BCM_RDPA_BRIDGE) || defined(CONFIG_BCM_RDPA_BRIDGE_MODULE)) + err = br_fp_hook(BR_FP_PORT_ADD, br, dev); + if (err) + goto err6; +#endif + dev_disable_lro(dev); list_add_rcu(&p->list, &br->port_list); @@ -501,6 +525,10 @@ netdev_update_features(br->dev); +#if defined(CONFIG_BCM_KF_INTF_BRG) && defined(CONFIG_BCM_INTF_BRG_ENABLED) + br_port_num_inc(br, p); +#endif /* CONFIG_BCM_KF_INTF_BRG && CONFIG_BCM_INTF_BRG_ENABLED */ + if (br->dev->needed_headroom < dev->needed_headroom) br->dev->needed_headroom = dev->needed_headroom; @@ -529,6 +557,11 @@ return 0; +#if defined(CONFIG_BCM_KF_RUNNER) && (defined(CONFIG_BCM_RDPA_BRIDGE) || defined(CONFIG_BCM_RDPA_BRIDGE_MODULE)) +err6: + netdev_upper_dev_unlink(dev, br->dev); +#endif + err5: dev->priv_flags &= ~IFF_BRIDGE_PORT; netdev_rx_handler_unregister(dev); @@ -557,6 +590,15 @@ if (!p || p->br != br) return -EINVAL; +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) && defined(CONFIG_BCM_BRIDGE_MAC_FDB_LIMIT) + /* Disable min limit per port in advance */ + (void)br_set_fdb_limit(br, p, 1, 1, 0); +#endif + +#if defined(CONFIG_BCM_KF_RUNNER) && (defined(CONFIG_BCM_RDPA_BRIDGE) || defined(CONFIG_BCM_RDPA_BRIDGE_MODULE)) + br_fp_hook(BR_FP_PORT_REMOVE, br, dev); +#endif + /* Since more than one interface can be attached to a bridge, * there still maybe an alternate path for netconsole to use; * therefore there is no reason for a NETDEV_RELEASE event. diff -urN linux-4.1.52-orig/net/bridge/br_input.c linux-4.1.52/net/bridge/br_input.c --- linux-4.1.52-orig/net/bridge/br_input.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/bridge/br_input.c 2020-02-07 01:47:54.000000000 +0800 @@ -21,6 +21,20 @@ #include #include #include "br_private.h" +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#include +#endif +#if defined(CONFIG_BCM_KF_WL) +#if defined(PKTC) +#include +#include +unsigned long (*wl_pktc_req_hook)(int req_id, unsigned long param0, unsigned long param1, unsigned long param2) = NULL; +EXPORT_SYMBOL(wl_pktc_req_hook); +unsigned long (*dhd_pktc_req_hook)(int req_id, unsigned long param0, unsigned long param1, unsigned long param2) = NULL; +EXPORT_SYMBOL(dhd_pktc_req_hook); +#endif /* PKTC */ +#include +#endif /* Hook for brouter */ br_should_route_hook_t __rcu *br_should_route_hook __read_mostly; @@ -33,6 +47,12 @@ struct pcpu_sw_netstats *brstats = this_cpu_ptr(br->stats); struct net_port_vlans *pv; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_lock(); + blog_link(IF_DEVICE, blog_ptr(skb), (void*)br->dev, DIR_RX, skb->len); + blog_unlock(); +#endif + u64_stats_update_begin(&brstats->syncp); brstats->rx_packets++; brstats->rx_bytes += skb->len; @@ -119,6 +139,11 @@ } } +#if defined(CONFIG_BCM_KF_WL) +// ETHER_TYPE_BRCM 0x886c, ETHER_TYPE_802_1X 0x888e, ETHER_TYPE_802_1X_PREAUTH 0x88c7 +#define WL_AUTH_PROTOCOLS(proto) ((proto)==htons(0x886c)||(proto)==htons(0x888e)||(proto)==htons(0x88c7)) +#endif + /* note: already called with rcu_read_lock */ int br_handle_frame_finish(struct sock *sk, struct sk_buff *skb) { @@ -131,12 +156,32 @@ bool unicast = true; u16 vid = 0; +#if defined(CONFIG_BCM_KF_WL) + if (!p || (p->state == BR_STATE_DISABLED && !WL_AUTH_PROTOCOLS(skb->protocol))) +#else if (!p || p->state == BR_STATE_DISABLED) +#endif goto drop; if (!br_allowed_ingress(p->br, nbp_get_vlan_info(p), skb, &vid)) goto out; +#if defined(CONFIG_BCM_KF_VLAN_AGGREGATION) && defined(CONFIG_BCM_VLAN_AGGREGATION) +#if defined(CONFIG_BCM_KF_VLAN) && (defined(CONFIG_BCM_VLAN) || defined(CONFIG_BCM_VLAN_MODULE)) + if (skb->vlan_count) + vid = (skb->vlan_header[0] >> 16) & VLAN_VID_MASK; + else +#endif /* CONFIG_BCM_VLAN) */ + /* + * dev.c/__netif_receive_skb(): if proto == ETH_P_8021Q + * call vlan_untag() to remove tag and save vid in skb->vlan_tci + */ + if (skb_vlan_tag_present(skb)) + vid = skb->vlan_tci & VLAN_VID_MASK; + else if ( vlan_eth_hdr(skb)->h_vlan_proto == htons(ETH_P_8021Q) ) + vid = ntohs(vlan_eth_hdr(skb)->h_vlan_TCI) & VLAN_VID_MASK; +#endif + /* insert into forwarding database after filtering to avoid spoofing */ br = p->br; if (p->flags & BR_LEARNING) @@ -147,6 +192,9 @@ goto drop; if (p->state == BR_STATE_LEARNING) +#if defined(CONFIG_BCM_KF_WL) + if (!WL_AUTH_PROTOCOLS(skb->protocol)) +#endif goto drop; BR_INPUT_SKB_CB(skb)->brdev = br->dev; @@ -162,6 +210,24 @@ if (IS_ENABLED(CONFIG_INET) && skb->protocol == htons(ETH_P_ARP)) br_do_proxy_arp(skb, br, vid, p); +#if (defined(CONFIG_BCM_MCAST) || defined(CONFIG_BCM_MCAST_MODULE)) && defined(CONFIG_BCM_KF_MCAST) + if ( br_bcm_mcast_receive != NULL ) + { + int rv = br_bcm_mcast_receive(br->dev->ifindex, skb, 0); + if ( rv < 0 ) + { + /* there was an error with the packet */ + goto drop; + } + else if ( rv > 0 ) + { + /* the packet was consumed */ + goto out; + } + /* continue */ + } +#endif + if (is_broadcast_ether_addr(dest)) { skb2 = skb; unicast = false; @@ -181,13 +247,161 @@ unicast = false; br->dev->stats.multicast++; +#if !(defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG)) } else if ((dst = __br_fdb_get(br, dest, vid)) && dst->is_local) { skb2 = skb; /* Do not forward the packet since it's local. */ skb = NULL; } +#else + } else { + struct net_bridge_fdb_entry *src; + + dst = __br_fdb_get(br, dest, vid); + src = __br_fdb_get(br, eth_hdr(skb)->h_source, vid); + blog_lock(); + if (src) + blog_link(BRIDGEFDB, blog_ptr(skb), (void*)src, BLOG_PARAM1_SRCFDB, 0); + + if (dst) + blog_link(BRIDGEFDB, blog_ptr(skb), (void*)dst, BLOG_PARAM1_DSTFDB, 0); + + blog_unlock(); + +#if defined(PKTC) + /* wlan pktc */ + if ((dst != NULL) && (dst->dst != NULL) && (!dst->is_local)) { +#if defined(CONFIG_BCM_KF_WL) + u8 from_wl_to_switch=0, from_switch_to_wl=0, from_wlan_to_wlan=0; + BlogPhy_t srcPhyType, dstPhyType; + uint32_t chainIdx; + uint16_t dst_dev_has_vlan = 0; + uint32_t pktc_tx_enabled = wl_pktc_req_hook ? + wl_pktc_req_hook(PKTC_TBL_GET_TX_MODE, 0, 0, 0) : 0; + + src = __br_fdb_get(br, eth_hdr(skb)->h_source, vid); + if (unlikely(src == NULL) || unlikely(src->dst == NULL)) + goto next; + + srcPhyType = BLOG_GET_PHYTYPE(src->dst->dev->path.hw_port_type); + dstPhyType = BLOG_GET_PHYTYPE(dst->dst->dev->path.hw_port_type); + + if ((srcPhyType == BLOG_WLANPHY) && + (dstPhyType == BLOG_ENETPHY)) { + from_wl_to_switch = 1; + } else if ((srcPhyType == BLOG_ENETPHY || srcPhyType == BLOG_XTMPHY || srcPhyType == BLOG_EPONPHY || srcPhyType == BLOG_GPONPHY || + ((src->dst->dev->rtnl_link_ops != NULL) && (strstr(src->dst->dev->rtnl_link_ops->kind, "gre") != NULL))) && + (dstPhyType == BLOG_WLANPHY) && pktc_tx_enabled) + { + from_switch_to_wl = 1; + } + +#if defined (CONFIG_BCM_DHD_RUNNER) && defined (CONFIG_BCM_WIFI_FORWARDING_DRV_MODULE) || defined(CONFIG_BCM947189) || defined(CONFIG_BCM_ARCHER_WLAN) + else if((srcPhyType == BLOG_WLANPHY) && (dstPhyType == BLOG_WLANPHY)) + from_wlan_to_wlan = 1; +#endif +#if defined(CONFIG_BCM_KF_WANDEV) + + if ((dst->dst->dev->priv_flags & IFF_BCM_VLAN)) + { + int len = strlen(dst->dst->dev->name); + dst_dev_has_vlan = 1; + + /* WLAN Rx: Check dev is default LAN side VLAN (for ex:eth1.0 or eth2.0) + that doesn't add/remove VLAN tag. So still create PKTC for it. + WLAN Tx: Allow to create PKTC as VLAN tag modifications are performed by the accelerator(fcache/fap/runner). + */ + + if ((!(dst->dst->dev->priv_flags & IFF_WANDEV)) && (len > 3)) + { + if (( from_wl_to_switch && (dst->dst->dev->name[len-2] == '.') && (dst->dst->dev->name[len-1] == '0') ) + || from_switch_to_wl ) + { + dst_dev_has_vlan = 0; + } + } + } + if ((from_wl_to_switch || from_switch_to_wl || from_wlan_to_wlan) && + !(dst->dst->dev->priv_flags & IFF_WANDEV) && + !(dst_dev_has_vlan)) { + + /* Also check for non-WAN cases. + * For the Rx direction, VLAN cases are allowed as long + * as the packets are untagged. + * + * Tagged packets are not forwarded through the chaining + * path by WLAN driver. Tagged packets go through the + * flowcache path. + * see wlc_sendup_chain() function for reference. + * + * For the Tx direction, there are no VLAN interfaces + * created on wl device when LAN_VLAN flag is enabled + * in the build. + * + * Get the root dest device and make sure that we + * are always transmitting to a root device */ + + struct net_device *root_dst_dev_p = dst->dst->dev; + /* Get the root destination device */ + while (!netdev_path_is_root(root_dst_dev_p)) { + root_dst_dev_p = netdev_path_next_dev(root_dst_dev_p); + } + + /* Update chaining table for DHD on the wl to switch direction only */ + if ( ( from_wl_to_switch +#if defined(CONFIG_BCM947189) + || from_wlan_to_wlan +#endif + ) + && (dhd_pktc_req_hook != NULL)) + { + dhd_pktc_req_hook(PKTC_TBL_UPDATE, + (unsigned long)&(dst->addr.addr[0]), + (unsigned long)root_dst_dev_p, 0); + } + + /* Update chaining table for WL (NIC driver) */ + chainIdx = wl_pktc_req_hook ? + wl_pktc_req_hook(PKTC_TBL_UPDATE, + (unsigned long)&(dst->addr.addr[0]), + (unsigned long)root_dst_dev_p, 0) : PKTC_INVALID_CHAIN_IDX; + if (chainIdx != PKTC_INVALID_CHAIN_IDX) { + /* Update chainIdx in blog + * chainEntry->tx_dev will always be NOT + * NULL as we just added that above */ + if (skb->blog_p != NULL) + { + if (from_switch_to_wl || from_wlan_to_wlan) + { + skb->blog_p->wfd.nic_ucast.is_tx_hw_acc_en = 1; + + /* in case of flow from WLAN to WLAN the flow will + * be open in Runner only if is_rx_hw_acc_en in DHD + */ + if (from_wlan_to_wlan && (0 == skb->blog_p->rnr.is_rx_hw_acc_en)) + skb->blog_p->wfd.nic_ucast.is_tx_hw_acc_en = 0; + + skb->blog_p->wfd.nic_ucast.is_chain = 1; + skb->blog_p->wfd.nic_ucast.wfd_idx = ((chainIdx & PKTC_WFD_IDX_BITMASK) >> PKTC_WFD_IDX_BITPOS); + skb->blog_p->wfd.nic_ucast.chain_idx = chainIdx; + } + } + } + } +#endif /* CONFIG_BCM_KF_WANDEV */ +#endif + } +next: +#endif /* PKTC */ + if ((dst != NULL) && dst->is_local) { + skb2 = skb; + /* Do not forward the packet since it's local. */ + skb = NULL; + } + } +#endif if (skb) { if (dst) { dst->used = jiffies; @@ -242,6 +456,13 @@ p = br_port_get_rcu(skb->dev); +#if defined(CONFIG_BCM_KF_WANDEV) + if (!p) + { + kfree_skb(skb); + return RX_HANDLER_CONSUMED; + } +#endif if (unlikely(is_link_local_ether_addr(dest))) { u16 fwd_mask = p->br->group_fwd_mask_required; @@ -288,8 +509,39 @@ } forward: +#if defined(CONFIG_BCM_KF_IEEE1905) && defined(CONFIG_BCM_IEEE1905) + /* allow broute to forward packets to the stack in any STP state */ + rhook = rcu_dereference(br_should_route_hook); + if (rhook) { + if ((*rhook)(skb)) { + *pskb = skb; + if ((skb->protocol == htons(0x893a)) || + (skb->protocol == htons(0x8912)) || + (skb->protocol == htons(0x88e1))) + br_handle_local_finish(NULL, skb); + + return RX_HANDLER_PASS; + } else if (skb->protocol == htons(0x893a) && + (skb->pkt_type == PACKET_MULTICAST)) + /* do not bridge multicast 1905 packets when 1905 is compiled */ + goto drop; + + dest = eth_hdr(skb)->h_dest; + } +#endif + +#if defined(CONFIG_BCM_KF_WL) + if ((p->state != BR_STATE_FORWARDING) && WL_AUTH_PROTOCOLS(skb->protocol)) { + /* force to forward brcm_type event packet */ + NF_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, NULL, skb, skb->dev, NULL, + br_handle_frame_finish); + return RX_HANDLER_CONSUMED; + } +#endif + switch (p->state) { case BR_STATE_FORWARDING: +#if !defined(CONFIG_BCM_KF_IEEE1905) || !defined(CONFIG_BCM_IEEE1905) rhook = rcu_dereference(br_should_route_hook); if (rhook) { if ((*rhook)(skb)) { @@ -298,6 +550,7 @@ } dest = eth_hdr(skb)->h_dest; } +#endif /* fall through */ case BR_STATE_LEARNING: if (ether_addr_equal(p->br->dev->dev_addr, dest)) diff -urN linux-4.1.52-orig/net/bridge/br_ioctl.c linux-4.1.52/net/bridge/br_ioctl.c --- linux-4.1.52-orig/net/bridge/br_ioctl.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/bridge/br_ioctl.c 2020-02-07 01:47:54.000000000 +0800 @@ -102,6 +102,11 @@ else ret = br_del_if(br, dev); +#if defined(CONFIG_BCM_KF_BRIDGE_PORT_ISOLATION) + rcu_read_lock(); + br_dev_notify_if_change(br->dev, dev, isadd); + rcu_read_unlock(); +#endif return ret; } @@ -343,6 +348,7 @@ return br_del_bridge(net, buf); } + } return -EOPNOTSUPP; diff -urN linux-4.1.52-orig/net/bridge/br_notifier.c linux-4.1.52/net/bridge/br_notifier.c --- linux-4.1.52-orig/net/bridge/br_notifier.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/bridge/br_notifier.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,187 @@ +#if defined(CONFIG_BCM_KF_BRIDGE_PORT_ISOLATION) || defined(CONFIG_BCM_KF_BRIDGE_STP) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "br_private.h" + +#if defined(CONFIG_BCM_KF_BRIDGE_PORT_ISOLATION) +static RAW_NOTIFIER_HEAD(bridge_event_chain); + +void br_dev_notify_if_change(struct net_device *br_dev, struct net_device *dev, int isadd) +{ + struct bridge_notifier_info info; + + info.br_dev = br_dev; + info.dev = dev; + info.isadd = isadd; + raw_notifier_call_chain(&bridge_event_chain, BREVT_IF_CHANGED, &info); +} + +/* NOTE -- IMPORTANT : Caller MUST take the RCU_READ_LOCK */ +void bridge_get_br_list(char *brList, const unsigned int listSize) +{ + struct net_device *dev = NULL; + unsigned int arrayIdx=0, brNameLen; + + /* Must enable Kernel debugging features and CONFIG_DEBUG_LOCK_ALLOC to make following statement take effect */ + BUG_ON(!rcu_read_lock_held()); + + for_each_netdev_rcu(&init_net, dev) { + if(dev->priv_flags & IFF_EBRIDGE) + { + if (arrayIdx > 0 && arrayIdx+1 <= listSize) + { + /* Bridge names separated by comma */ + brList[arrayIdx++] = ','; + } + + brNameLen = strlen(dev->name); + if (arrayIdx+brNameLen+1 > listSize) + { + printk("bridge_get_br_list() : insufficient size; skipping <%s> <%d>\n", + dev->name,brNameLen); + brList[arrayIdx-1] = '\0'; /* Remove the trailing "," if present */ + break; + } + strcpy(&brList[arrayIdx],dev->name); + arrayIdx += brNameLen; /* Intentionally not accounting for NULL towards the end */ + } + } + brList[arrayIdx] = '\0'; /* Force Null terminated string */ + +} +/* NOTE -- IMPORTANT : Caller MUST take the RCU_READ_LOCK */ +struct net_device *bridge_get_next_port(char *brName, unsigned int *brPort) +{ + struct net_bridge_port *cp; + struct net_bridge_port *np; + struct net_bridge *br; + struct net_device *dev; + struct net_device *prtDev; + + /* Must enable Kernel debugging features and CONFIG_DEBUG_LOCK_ALLOC to make following statement take effect */ + BUG_ON(!rcu_read_lock_held()); + + dev = dev_get_by_name(&init_net, brName); + if(!dev) + return NULL; + + br = netdev_priv(dev); + if (list_empty(&br->port_list)) + { + dev_put(dev); + return NULL; + } + + if (*brPort == 0xFFFFFFFF) + { + np = list_first_entry(&br->port_list, struct net_bridge_port, list); + *brPort = np->port_no; + prtDev = np->dev; + } + else + { + cp = br_get_port(br, *brPort); + if ( cp ) + { + if (list_is_last(&cp->list, &br->port_list)) + { + prtDev = NULL; + } + else + { + np = list_first_entry(&cp->list, struct net_bridge_port, list); + *brPort = np->port_no; + prtDev = np->dev; + } + } + else + { + prtDev = NULL; + } + } + + dev_put(dev); + return prtDev; +} +EXPORT_SYMBOL(bridge_get_next_port); +EXPORT_SYMBOL(bridge_get_br_list); + + +int register_bridge_notifier(struct notifier_block *nb) +{ + return raw_notifier_chain_register(&bridge_event_chain, nb); +} +EXPORT_SYMBOL(register_bridge_notifier); + +int unregister_bridge_notifier(struct notifier_block *nb) +{ + return raw_notifier_chain_unregister(&bridge_event_chain, nb); +} +EXPORT_SYMBOL(unregister_bridge_notifier); +#endif + +#if defined(CONFIG_BCM_KF_BRIDGE_STP) +static RAW_NOTIFIER_HEAD(bridge_stp_event_chain); + +void br_stp_notify_state_port(const struct net_bridge_port *p) +{ + struct stpPortInfo portInfo; + + memcpy(&portInfo.portName[0], p->dev->name, IFNAMSIZ); + portInfo.stpState = ( BR_NO_STP == p->br->stp_enabled ) ? 0xFF/*STP off*/ : p->state; + raw_notifier_call_chain(&bridge_stp_event_chain, BREVT_STP_STATE_CHANGED, &portInfo); +} + +void br_stp_notify_state_bridge(const struct net_bridge *br) +{ + struct net_bridge_port *p; + struct stpPortInfo portInfo; + + rcu_read_lock(); + list_for_each_entry_rcu(p, &br->port_list, list) { + if ( BR_NO_STP == br->stp_enabled ) + { + portInfo.stpState = 0xFF; /* disable */ + } + else + { + portInfo.stpState = p->state; + } + memcpy(&portInfo.portName[0], p->dev->name, IFNAMSIZ); + raw_notifier_call_chain(&bridge_stp_event_chain, BREVT_STP_STATE_CHANGED, &portInfo); + } + rcu_read_unlock(); + +} + +void call_br_stp_notifiers(unsigned long evt, struct net_device *dev, struct stpPortInfo *ctx) +{ + struct stpPortInfo portInfo; + memcpy(&portInfo.portName[0], dev->name, IFNAMSIZ); + portInfo.stpState = ctx->stpState; + + raw_notifier_call_chain(&bridge_stp_event_chain, evt, &portInfo); +} +EXPORT_SYMBOL(call_br_stp_notifiers); + +int register_bridge_stp_notifier(struct notifier_block *nb) +{ + return raw_notifier_chain_register(&bridge_stp_event_chain, nb); +} +EXPORT_SYMBOL(register_bridge_stp_notifier); + +int unregister_bridge_stp_notifier(struct notifier_block *nb) +{ + return raw_notifier_chain_unregister(&bridge_stp_event_chain, nb); +} +EXPORT_SYMBOL(unregister_bridge_stp_notifier); +#endif + +#endif diff -urN linux-4.1.52-orig/net/bridge/br_private.h linux-4.1.52/net/bridge/br_private.h --- linux-4.1.52-orig/net/bridge/br_private.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/bridge/br_private.h 2020-02-07 01:47:54.000000000 +0800 @@ -20,9 +20,23 @@ #include #include +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) +#if defined(CONFIG_BCM_RDPA_BRIDGE) || defined(CONFIG_BCM_RDPA_BRIDGE_MODULE) +#include "br_fp.h" +#endif /* CONFIG_BCM_RDPA_BRIDGE || CONFIG_BCM_RDPA_BRIDGE_MODULE */ +#endif /* CONFIG_BCM_RUNNER */ +#endif /* CONFIG_BCM_KF_RUNNER */ + + #define BR_HASH_BITS 8 #define BR_HASH_SIZE (1 << BR_HASH_BITS) +#if (defined(CONFIG_BCM_MCAST) || defined(CONFIG_BCM_MCAST_MODULE)) && defined(CONFIG_BCM_KF_MCAST) +extern br_bcm_mcast_receive_hook br_bcm_mcast_receive; +extern br_bcm_mcast_should_deliver_hook br_bcm_mcast_should_deliver; +#endif + #define BR_HOLD_TIME (1*HZ) #define BR_PORT_BITS 10 @@ -31,6 +45,10 @@ #define BR_VERSION "2.3" +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) +#define BR_MAX_FDB_ENTRIES 4096 +#endif + /* Control of forwarding link local multicast */ #define BR_GROUPFWD_DEFAULT 0 /* Don't allow forwarding control protocols like STP and LLDP */ @@ -102,9 +120,11 @@ is_static:1, added_by_user:1, added_by_external_learn:1; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + unsigned int fdb_key; +#endif __u16 vlan_id; }; - struct net_bridge_port_group { struct net_bridge_port *port; struct net_bridge_port_group __rcu *next; @@ -156,7 +176,6 @@ u32 path_cost; u32 designated_cost; unsigned long designated_age; - struct timer_list forward_delay_timer; struct timer_list hold_timer; struct timer_list message_age_timer; @@ -186,6 +205,12 @@ #ifdef CONFIG_BRIDGE_VLAN_FILTERING struct net_port_vlans __rcu *vlan_info; #endif + +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) && defined(CONFIG_BCM_BRIDGE_MAC_FDB_LIMIT) + int num_port_fdb_entries; + int max_port_fdb_entries; + int min_port_fdb_entries; +#endif }; #define br_auto_port(p) ((p)->flags & BR_AUTO_MASK) @@ -211,6 +236,9 @@ struct net_device *dev; struct pcpu_sw_netstats __percpu *stats; +#if defined(CONFIG_BCM_KF_BRIDGE_COUNTERS) + u32 mac_entry_discard_counter; +#endif spinlock_t hash_lock; struct hlist_head hash[BR_HASH_SIZE]; #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) @@ -219,6 +247,16 @@ bool nf_call_ip6tables; bool nf_call_arptables; #endif + +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) + int num_fdb_entries; +#endif + +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) && defined(CONFIG_BCM_BRIDGE_MAC_FDB_LIMIT) + int max_br_fdb_entries; + int used_br_fdb_entries; +#endif + u16 group_fwd_mask; u16 group_fwd_mask_required; @@ -294,6 +332,21 @@ u16 default_pvid; struct net_port_vlans __rcu *vlan_info; #endif + +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) +#if defined(CONFIG_BCM_RDPA_BRIDGE) || defined(CONFIG_BCM_RDPA_BRIDGE_MODULE) + struct br_fp_data fp_hooks; +#endif /* CONFIG_BCM_RDPA_BRIDGE || CONFIG_BCM_RDPA_BRIDGE_MODULE */ +#endif /* CONFIG_BCM_RUNNER */ +#endif /* CONFIG_BCM_KF_RUNNER */ +#if defined(CONFIG_BCM_KF_INTF_BRG) && defined(CONFIG_BCM_INTF_BRG_ENABLED) + u16 lan_port_num; + u16 wan_port_num; +#endif /* CONFIG_BCM_KF_INTF_BRG && CONFIG_BCM_INTF_BRG_ENABLED */ +#if defined(CONFIG_BCM_KF_LOCAL_SWITCHING_DISABLE) + int local_switching_disable; +#endif }; struct br_input_skb_cb { @@ -408,6 +461,14 @@ int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p, const unsigned char *addr, u16 vid); +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) && defined(CONFIG_BCM_BRIDGE_MAC_FDB_LIMIT) +int br_set_fdb_limit(struct net_bridge *br, + struct net_bridge_port *p, + int lmt_type, + int is_min, + int fdb_limit); +#endif + /* br_forward.c */ void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb); int br_dev_queue_push_xmit(struct sock *sk, struct sk_buff *skb); @@ -419,6 +480,55 @@ struct sk_buff *skb2, bool unicast); /* br_if.c */ +#if defined(CONFIG_BCM_KF_INTF_BRG) && defined(CONFIG_BCM_INTF_BRG_ENABLED) +static inline void br_port_num_inc(struct net_bridge *br, struct net_bridge_port *p) +{ +#if defined(CONFIG_BCM_KF_WANDEV) + if (p->dev->priv_flags & IFF_WANDEV) + { + br->wan_port_num++; + } + else +#endif + { + br->lan_port_num++; + } +} + +static inline void br_port_num_dec(struct net_bridge *br, struct net_bridge_port *p) +{ +#if defined(CONFIG_BCM_KF_WANDEV) + if (p->dev->priv_flags & IFF_WANDEV) + { + br->wan_port_num--; + } + else +#endif + { + br->lan_port_num--; + } +} + +static inline bool is_interface_br(struct net_bridge *br, struct sk_buff *skb) +{ + struct net_bridge_port *pin; + + if (!skb->dev) + { + return 0; + } + + pin = br_port_get_rcu(skb->dev); + if ((!pin) || (!pin->dev)) + { + return 0; + } + + return ((br->lan_port_num == 1) && (br->wan_port_num == 1)); +} + +#endif /* CONFIG_BCM_KF_INTF_BRG && CONFIG_BCM_INTF_BRG_ENABLED */ + void br_port_carrier_check(struct net_bridge_port *p); int br_add_bridge(struct net *net, const char *name); int br_del_bridge(struct net *net, const char *name); @@ -848,4 +958,18 @@ static inline void br_sysfs_delbr(struct net_device *dev) { return; } #endif /* CONFIG_SYSFS */ +#if defined(CONFIG_BCM_KF_BRIDGE_STP) +/* br_notifier.c */ +extern void br_stp_notify_state_port(const struct net_bridge_port *p); +extern void br_stp_notify_state_bridge(const struct net_bridge *br); +#endif +#if defined(CONFIG_BCM_KF_BRIDGE_PORT_ISOLATION) +struct bridge_notifier_info { + struct net_device *br_dev; + struct net_device *dev; + int isadd; +}; +extern void br_dev_notify_if_change(struct net_device *br_dev, struct net_device *dev, int isadd); +#endif + #endif diff -urN linux-4.1.52-orig/net/bridge/br_stp.c linux-4.1.52/net/bridge/br_stp.c --- linux-4.1.52-orig/net/bridge/br_stp.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/bridge/br_stp.c 2020-02-07 01:47:54.000000000 +0800 @@ -35,6 +35,10 @@ br_info(p->br, "port %u(%s) entered %s state\n", (unsigned int) p->port_no, p->dev->name, br_port_state_names[p->state]); + +#if defined(CONFIG_BCM_KF_BRIDGE_STP) + br_stp_notify_state_port(p); +#endif } void br_set_state(struct net_bridge_port *p, unsigned int state) diff -urN linux-4.1.52-orig/net/bridge/br_stp_if.c linux-4.1.52/net/bridge/br_stp_if.c --- linux-4.1.52-orig/net/bridge/br_stp_if.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/bridge/br_stp_if.c 2020-02-07 01:47:54.000000000 +0800 @@ -19,7 +19,6 @@ #include "br_private.h" #include "br_private_stp.h" - /* Port id is composed of priority and port number. * NB: some bits of priority are dropped to * make room for more ports. @@ -152,6 +151,10 @@ } spin_unlock_bh(&br->lock); +#if defined(CONFIG_BCM_KF_BRIDGE_STP) + /* STP enabled, send notification for all ports */ + br_stp_notify_state_bridge(br); +#endif } static void br_stp_stop(struct net_bridge *br) @@ -171,6 +174,10 @@ } br->stp_enabled = BR_NO_STP; +#if defined(CONFIG_BCM_KF_BRIDGE_STP) + /* STP disabled, send notification for all ports */ + br_stp_notify_state_bridge(br); +#endif } void br_stp_set_enabled(struct net_bridge *br, unsigned long val) @@ -232,6 +239,16 @@ if (br->dev->addr_assign_type == NET_ADDR_SET) return false; +#if defined(CONFIG_BCM_KF_BRIDGE_STP) + /* if the current bridge address is being used by + a member device then keep it */ + list_for_each_entry(p, &br->port_list, list) { + if (0 == memcmp(br->bridge_id.addr, p->dev->dev_addr, ETH_ALEN)) { + return false; + } + } +#endif + list_for_each_entry(p, &br->port_list, list) { if (addr == br_mac_zero || memcmp(p->dev->dev_addr, addr, ETH_ALEN) < 0) diff -urN linux-4.1.52-orig/net/bridge/br_sysfs_br.c linux-4.1.52/net/bridge/br_sysfs_br.c --- linux-4.1.52-orig/net/bridge/br_sysfs_br.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/bridge/br_sysfs_br.c 2020-02-07 01:47:54.000000000 +0800 @@ -22,6 +22,13 @@ #include "br_private.h" +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA_BRIDGE) || defined(CONFIG_BCM_RDPA_BRIDGE_MODULE) +#include "br_fp.h" +#include "br_fp_hooks.h" +#endif +#endif + #define to_dev(obj) container_of(obj, struct device, kobj) #define to_bridge(cd) ((struct net_bridge *)netdev_priv(to_net_dev(cd))) @@ -743,6 +750,111 @@ static DEVICE_ATTR_RW(default_pvid); #endif +#if defined(CONFIG_BCM_KF_BRIDGE_COUNTERS) +static ssize_t show_mac_entry_discard_counter(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct net_bridge *br = to_bridge(d); + return sprintf(buf, "%u\n", br->mac_entry_discard_counter); +} + +static int set_mac_entry_discard_counter(struct net_bridge *br, unsigned long val) +{ + br->mac_entry_discard_counter = val; + return 0; +} + +static ssize_t store_mac_entry_discard_counter(struct device *d, + struct device_attribute *attr, + const char *buf, size_t len) +{ + return store_bridge_parm(d, buf, len, set_mac_entry_discard_counter); +} +static DEVICE_ATTR(mac_entry_discard_counter, S_IRUGO | S_IWUSR, show_mac_entry_discard_counter, + store_mac_entry_discard_counter); +#endif + +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) && defined(CONFIG_BCM_BRIDGE_MAC_FDB_LIMIT) +static ssize_t max_fdb_entries_show(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct net_bridge *br = to_bridge(d); + return sprintf(buf, "%d\n", br->max_br_fdb_entries); +} + +static int set_max_fdb_entries(struct net_bridge *br, unsigned long val) +{ + spin_lock_bh(&br->lock); + br_set_fdb_limit(br, NULL, 0, 0, (int)val); + spin_unlock_bh(&br->lock); + return 0; +} + +static ssize_t max_fdb_entries_store(struct device *d, + struct device_attribute *attr, + const char *buf, size_t len) +{ + return store_bridge_parm(d, buf, len, set_max_fdb_entries); +} +static DEVICE_ATTR_RW(max_fdb_entries); + +static ssize_t used_fdb_entries_show(struct device *d, struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", to_bridge(d)->used_br_fdb_entries); +} +static DEVICE_ATTR_RO(used_fdb_entries); +#endif + +#if defined(CONFIG_BCM_KF_LOCAL_SWITCHING_DISABLE) +static ssize_t local_switching_disable_show(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct net_bridge *br = to_bridge(d); + return sprintf(buf, "%d\n", br->local_switching_disable); +} + +int br_local_switching_disable(struct net_bridge *br, unsigned long val) +{ + br->local_switching_disable = val; +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA_BRIDGE) || defined(CONFIG_BCM_RDPA_BRIDGE_MODULE) + return br_fp_hook(BR_FP_LOCAL_SWITCHING_DISABLE, br, NULL); +#endif +#endif + + return -1; +} + +static ssize_t local_switching_disable_store(struct device *d, + struct device_attribute *attr, + const char *buf, size_t len) +{ + return store_bridge_parm(d, buf, len, br_local_switching_disable); +} +static DEVICE_ATTR_RW(local_switching_disable); +#endif + +#if defined(CONFIG_BCM_KF_RUNNER) +int br_bridge_type(struct net_bridge *br, unsigned long val) +{ +#if defined(CONFIG_BCM_RDPA_BRIDGE) || defined(CONFIG_BCM_RDPA_BRIDGE_MODULE) + return br_fp_hook(BR_FP_BRIDGE_TYPE, br, (void *)&val); +#endif + + return -1; +} + +static ssize_t bridge_type_store(struct device *d, + struct device_attribute *attr, + const char *buf, size_t len) +{ + return store_bridge_parm(d, buf, len, br_bridge_type); +} +static DEVICE_ATTR_WO(bridge_type); +#endif + static struct attribute *bridge_attrs[] = { &dev_attr_forward_delay.attr, &dev_attr_hello_time.attr, @@ -762,6 +874,9 @@ &dev_attr_topology_change_timer.attr, &dev_attr_gc_timer.attr, &dev_attr_group_addr.attr, +#if defined(CONFIG_BCM_KF_BRIDGE_COUNTERS) + &dev_attr_mac_entry_discard_counter.attr, +#endif &dev_attr_flush.attr, #ifdef CONFIG_BRIDGE_IGMP_SNOOPING &dev_attr_multicast_router.attr, @@ -789,6 +904,16 @@ &dev_attr_vlan_protocol.attr, &dev_attr_default_pvid.attr, #endif +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) && defined(CONFIG_BCM_BRIDGE_MAC_FDB_LIMIT) + &dev_attr_max_fdb_entries.attr, + &dev_attr_used_fdb_entries.attr, +#endif +#if defined(CONFIG_BCM_KF_LOCAL_SWITCHING_DISABLE) + &dev_attr_local_switching_disable.attr, +#endif +#if defined(CONFIG_BCM_KF_RUNNER) + &dev_attr_bridge_type.attr, +#endif NULL }; diff -urN linux-4.1.52-orig/net/bridge/br_sysfs_if.c linux-4.1.52/net/bridge/br_sysfs_if.c --- linux-4.1.52-orig/net/bridge/br_sysfs_if.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/bridge/br_sysfs_if.c 2020-02-07 01:47:54.000000000 +0800 @@ -190,6 +190,37 @@ BRPORT_ATTR_FLAG(multicast_fast_leave, BR_MULTICAST_FAST_LEAVE); #endif +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) && defined(CONFIG_BCM_BRIDGE_MAC_FDB_LIMIT) +static ssize_t show_used_num_fdb_entries(struct net_bridge_port *p, char *buf) +{ + return sprintf(buf, "%d\n", p->num_port_fdb_entries); +} +static BRPORT_ATTR(used_fdb_entries, S_IRUGO, show_used_num_fdb_entries, NULL); + +static ssize_t show_max_num_fdb_entries(struct net_bridge_port *p, char *buf) +{ + return sprintf(buf, "%d\n", p->max_port_fdb_entries); +} + +int store_max_num_fdb_entries(struct net_bridge_port *p, unsigned long maxentries) +{ + return br_set_fdb_limit(p->br, p, 1, 0, (int)maxentries); +} +static BRPORT_ATTR(max_fdb_entries, S_IRUGO | S_IWUSR, show_max_num_fdb_entries, store_max_num_fdb_entries); + +static ssize_t show_min_num_fdb_entries(struct net_bridge_port *p, char *buf) +{ + return sprintf(buf, "%d\n", p->min_port_fdb_entries); +} + +int store_min_num_fdb_entries(struct net_bridge_port *p, unsigned long minentries) +{ + return br_set_fdb_limit(p->br, p, 1, 1, (int)minentries); +} +static BRPORT_ATTR(min_fdb_entries, S_IRUGO | S_IWUSR, show_min_num_fdb_entries, store_min_num_fdb_entries); +#endif + + static const struct brport_attribute *brport_attrs[] = { &brport_attr_path_cost, &brport_attr_priority, @@ -217,6 +248,11 @@ #endif &brport_attr_proxyarp, &brport_attr_proxyarp_wifi, +#if defined(CONFIG_BCM_KF_BRIDGE_MAC_FDB_LIMIT) && defined(CONFIG_BCM_BRIDGE_MAC_FDB_LIMIT) + &brport_attr_used_fdb_entries, + &brport_attr_max_fdb_entries, + &brport_attr_min_fdb_entries, +#endif NULL }; diff -urN linux-4.1.52-orig/net/bridge/Kconfig linux-4.1.52/net/bridge/Kconfig --- linux-4.1.52-orig/net/bridge/Kconfig 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/bridge/Kconfig 2020-02-07 01:47:54.000000000 +0800 @@ -60,3 +60,9 @@ Say N to exclude this support and reduce the binary size. If unsure, say Y. + +config BCM_VLAN_AGGREGATION + bool "vlan aggregation" + depends on BCM_KF_VLAN_AGGREGATION + ---help--- + If you say Y here, it will enable vlan aggregation diff -urN linux-4.1.52-orig/net/bridge/Makefile linux-4.1.52/net/bridge/Makefile --- linux-4.1.52-orig/net/bridge/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/bridge/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -2,6 +2,22 @@ # Makefile for the IEEE 802.1d ethernet bridging layer. # +ifdef BCM_KF # defined(CONFIG_BCM_KF_WL) +ifneq ($(strip $(CONFIG_BCM_WLAN)),) +EXTRA_CFLAGS += -DPKTC +ifneq ($(strip $(CONFIG_BCM_PKTFWD)),) +EXTRA_CFLAGS += -DBCM_PKTFWD +endif +EXTRA_CFLAGS += -DDSLCPE -DBCMDRIVER +EXTRA_CFLAGS += -I$(INC_BRCMSHARED_PUB_PATH)/$(BRCM_BOARD) +EXTRA_CFLAGS += -I$(BRCMDRIVERS_DIR)/broadcom/net/wl/bcm9$(BRCM_CHIP)/include +EXTRA_CFLAGS += -I$(BRCMDRIVERS_DIR)/broadcom/net/wl/bcm9$(BRCM_CHIP)/main/src/include +EXTRA_CFLAGS += -I$(BRCMDRIVERS_DIR)/broadcom/net/wl/shared/impl1 +endif +EXTRA_CFLAGS += -I$(INC_BRCMDRIVER_PUB_PATH)/$(BRCM_BOARD) +EXTRA_CFLAGS += -I$(INC_BRCMDRIVER_PRIV_PATH)/$(BRCM_BOARD) +endif #BCM_KF + obj-$(CONFIG_BRIDGE) += bridge.o bridge-y := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \ @@ -19,3 +35,19 @@ bridge-$(CONFIG_BRIDGE_VLAN_FILTERING) += br_vlan.o obj-$(CONFIG_NETFILTER) += netfilter/ + +ifdef BCM_KF # defined(CONFIG_BCM_KF_MCAST) +ifneq ($(strip $(CONFIG_BCM_MCAST))$(strip $(CONFIG_BCM_MCAST_MODULE)),) +bridge-y += br_bcm_mcast.o +endif +endif # BCM_KF + +ifdef BCM_KF # defined(CONFIG_BCM_KF_BRIDGE_PORT_ISOLATION) || defined(CONFIG_BCM_KF_BRIDGE_STP) +bridge-y += br_notifier.o +endif # BCM_KF + +ifdef BCM_KF # defined(CONFIG_BCM_KF_RUNNER) +ifdef CONFIG_BCM_RDPA_BRIDGE +bridge-y += br_fp.o +endif # CONFIG_BCM_RDPA_BRIDGE +endif # BCM_KF diff -urN linux-4.1.52-orig/net/bridge/netfilter/ebt_blog.c linux-4.1.52/net/bridge/netfilter/ebt_blog.c --- linux-4.1.52-orig/net/bridge/netfilter/ebt_blog.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/bridge/netfilter/ebt_blog.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,85 @@ +#if defined(CONFIG_BCM_KF_NETFILTER) && defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +/* +* Copyright (c) 2003-2018 Broadcom Corporation +* All Rights Reserved +* +<:label-BRCM:2018:DUAL/GPL:standard + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + +#include +#include + +#include +#include +#include + +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Broadcom"); +MODULE_DESCRIPTION("ebtables blog match"); +MODULE_ALIAS("ebt_blog"); + +static bool +ebt_blog_mt(const struct sk_buff *skb, struct xt_action_param *par) +{ + const struct ebt_blog_info *info = par->matchinfo; + + if (skb->blog_p) { + if (info->tcp_pure_ack) + return skb->blog_p->key.tcp_pure_ack ^ info->invert; + } + + return 0; +} + +static struct xt_match ebt_blog_mt_reg __read_mostly = { + .name = "blog", + .revision = 0, + .family = NFPROTO_BRIDGE, + .match = ebt_blog_mt, + .matchsize = XT_ALIGN(sizeof(struct ebt_blog_info)), + .me = THIS_MODULE, +}; + +static int __init ebt_blog_mt_init(void) +{ + int ret; + ret = xt_register_match(&ebt_blog_mt_reg); + + if (ret == 0) + printk(KERN_INFO "ebt_blog registered\n"); + + return ret; +} + +static void __exit ebt_blog_mt_fini(void) +{ + xt_unregister_match(&ebt_blog_mt_reg); +} + +module_init(ebt_blog_mt_init); +module_exit(ebt_blog_mt_fini); +#endif diff -urN linux-4.1.52-orig/net/bridge/netfilter/ebt_ftos.c linux-4.1.52/net/bridge/netfilter/ebt_ftos.c --- linux-4.1.52-orig/net/bridge/netfilter/ebt_ftos.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/bridge/netfilter/ebt_ftos.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,212 @@ +#if defined(CONFIG_BCM_KF_NETFILTER) +/* +* Copyright (c) 2003-2012 Broadcom Corporation +* All Rights Reserved +* +<:label-BRCM:2012:GPL/GPL:standard + +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 (the "GPL"). + +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. + + +A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by +writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + +:> +*/ + +/* + * ebt_ftos + * + * Authors: + * Song Wang + * + * Feb, 2004 + * + */ + +// The ftos target can be used in any chain +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define PPPOE_HLEN 6 +#define PPP_TYPE_IPV4 0x0021 /* IPv4 in PPP */ +#define PPP_TYPE_IPV6 0x0057 /* IPv6 in PPP */ + +static unsigned int ebt_ftos_tg(struct sk_buff *skb, const struct xt_action_param *par) +{ + //struct ebt_ftos_t_info *ftosinfo = (struct ebt_ftos_t_info *)data; + const struct ebt_ftos_t_info *ftosinfo = par->targinfo; + struct iphdr *iph = NULL; + struct ipv6hdr *ipv6h = NULL; + /* Need to recalculate IP header checksum after altering TOS byte */ + u_int16_t diffs[2]; + + /* if VLAN frame, we need to point to correct network header */ + if (skb->protocol == __constant_htons(ETH_P_IP)) + iph = (struct iphdr *)skb_network_header(skb); + else if ((skb)->protocol == __constant_htons(ETH_P_IPV6)) + ipv6h = (struct ipv6hdr *)skb_network_header(skb); + else if (skb->protocol == __constant_htons(ETH_P_8021Q)) { + if (*(unsigned short *)(skb_network_header(skb) + VLAN_HLEN - 2) == __constant_htons(ETH_P_IP)) + iph = (struct iphdr *)(skb_network_header(skb) + VLAN_HLEN); + else if (*(unsigned short *)(skb_network_header(skb) + VLAN_HLEN - 2) == __constant_htons(ETH_P_IPV6)) + ipv6h = (struct ipv6hdr *)(skb_network_header(skb) + VLAN_HLEN); + } + else if (skb->protocol == __constant_htons(ETH_P_PPP_SES)) { + if (*(unsigned short *)(skb_network_header(skb) + PPPOE_HLEN) == __constant_htons(PPP_TYPE_IPV4)) + + iph = (struct iphdr *)(skb_network_header(skb) + PPPOE_HLEN + 2); + else if (*(unsigned short *)(skb_network_header(skb) + PPPOE_HLEN) == __constant_htons(PPP_TYPE_IPV6)) + ipv6h = (struct ipv6hdr *)(skb_network_header(skb) + PPPOE_HLEN + 2); + } + /* if not IP header, do nothing. */ + if ((iph == NULL) && (ipv6h == NULL)) + return ftosinfo->target; + + if ( iph != NULL ) //IPv4 + { + if ((ftosinfo->ftos_set & FTOS_SETFTOS) && (iph->tos != ftosinfo->ftos)) { + //printk("ebt_target_ftos:FTOS_SETFTOS .....\n"); + diffs[0] = htons(iph->tos) ^ 0xFFFF; + iph->tos = ftosinfo->ftos; + diffs[1] = htons(iph->tos); + iph->check = csum_fold(csum_partial((char *)diffs, + sizeof(diffs), + iph->check^0xFFFF)); +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// member below is removed +// (*pskb)->nfcache |= NFC_ALTERED; + } else if (ftosinfo->ftos_set & FTOS_WMMFTOS) { + //printk("ebt_target_ftos:FTOS_WMMFTOS .....0x%08x\n", (*pskb)->mark & 0xf); + diffs[0] = htons(iph->tos) ^ 0xFFFF; + iph->tos |= ((skb->mark >> PRIO_LOC_NFMARK) & PRIO_LOC_NFMASK) << DSCP_MASK_SHIFT; + diffs[1] = htons(iph->tos); + iph->check = csum_fold(csum_partial((char *)diffs, + sizeof(diffs), + iph->check^0xFFFF)); +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// member below is removed +// (*pskb)->nfcache |= NFC_ALTERED; + } else if ((ftosinfo->ftos_set & FTOS_8021QFTOS) && skb->protocol == __constant_htons(ETH_P_8021Q)) { + + struct vlan_hdr *frame; + unsigned char prio = 0; + unsigned short TCI; + + frame = (struct vlan_hdr *)skb_network_header(skb); + TCI = ntohs(frame->h_vlan_TCI); + prio = (unsigned char)((TCI >> 13) & 0x7); + //printk("ebt_target_ftos:FTOS_8021QFTOS ..... 0x%08x\n", prio); + diffs[0] = htons(iph->tos) ^ 0xFFFF; + iph->tos |= (prio & 0xf) << DSCP_MASK_SHIFT; + diffs[1] = htons(iph->tos); + iph->check = csum_fold(csum_partial((char *)diffs, + sizeof(diffs), + iph->check^0xFFFF)); +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// member below is removed +// (*pskb)->nfcache |= NFC_ALTERED; + } + } + else //IPv6 + { + __u8 tos; + + /* TOS consists of priority field and first 4 bits of flow_lbl */ + tos = ipv6_get_dsfield((struct ipv6hdr *)(ipv6h)); + + if ((ftosinfo->ftos_set & FTOS_SETFTOS) && (tos != ftosinfo->ftos)) + { + //printk("ebt_target_ftos:FTOS_SETFTOS .....\n"); + ipv6_change_dsfield((struct ipv6hdr *)(ipv6h), 0, ftosinfo->ftos); + } + else if (ftosinfo->ftos_set & FTOS_WMMFTOS) + { + //printk("ebt_target_ftos:FTOS_WMMFTOS .....0x%08x\n", + tos |= ((skb->mark >> PRIO_LOC_NFMARK) & PRIO_LOC_NFMASK) << DSCP_MASK_SHIFT; + ipv6_change_dsfield((struct ipv6hdr *)(ipv6h), 0, tos); + } + else if ((ftosinfo->ftos_set & FTOS_8021QFTOS) && + skb->protocol == __constant_htons(ETH_P_8021Q)) + { + struct vlan_hdr *frame; + unsigned char prio = 0; + unsigned short TCI; + + frame = (struct vlan_hdr *)skb_network_header(skb); + TCI = ntohs(frame->h_vlan_TCI); + prio = (unsigned char)((TCI >> 13) & 0x7); + //printk("ebt_target_ftos:FTOS_8021QFTOS ..... 0x%08x\n", prio); + tos |= (prio & 0xf) << DSCP_MASK_SHIFT; + ipv6_change_dsfield((struct ipv6hdr *)(ipv6h), 0, tos); + } + } + + return ftosinfo->target; +} + +static int ebt_ftos_tg_check(const struct xt_tgchk_param *par) +{ + const struct ebt_ftos_t_info *info = par->targinfo; +/* + if (datalen != sizeof(struct ebt_ftos_t_info)) + return -EINVAL; +*/ + if (BASE_CHAIN && info->target == EBT_RETURN) + return -EINVAL; + + //CLEAR_BASE_CHAIN_BIT; + + if (INVALID_TARGET) + return -EINVAL; + + return 0; +} + +static struct xt_target ebt_ftos_tg_reg = { + .name = EBT_FTOS_TARGET, + .revision = 0, + .family = NFPROTO_BRIDGE, + .target = ebt_ftos_tg, + .checkentry = ebt_ftos_tg_check, + .targetsize = XT_ALIGN(sizeof(struct ebt_ftos_t_info)), + .me = THIS_MODULE, +}; + +static int __init ebt_ftos_init(void) +{ + int ret; + ret = xt_register_target(&ebt_ftos_tg_reg); + if(ret == 0) + printk(KERN_INFO "ebt_ftos registered\n"); + + return ret; +} + +static void __exit ebt_ftos_fini(void) +{ + xt_unregister_target(&ebt_ftos_tg_reg); +} + +module_init(ebt_ftos_init); +module_exit(ebt_ftos_fini); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Song Wang, songw@broadcom.com"); +MODULE_DESCRIPTION("Target to overwrite the full TOS byte in IP header"); +#endif diff -urN linux-4.1.52-orig/net/bridge/netfilter/ebt_ip6_extend.c linux-4.1.52/net/bridge/netfilter/ebt_ip6_extend.c --- linux-4.1.52-orig/net/bridge/netfilter/ebt_ip6_extend.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/bridge/netfilter/ebt_ip6_extend.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,88 @@ +/* + * ebt_ip6 + * + * Authors: + * Manohar Castelino + * Kuo-Lang Tseng + * Jan Engelhardt + * + * Summary: + * This is just a modification of the IPv4 code written by + * Bart De Schuymer + * with the changes required to support IPv6 + * + * Jan, 2008 + * + * Extend by Broadcom at Jan 31, 2019 + */ +#include +#include +#include +#include +#include +#include +#include +#include + +static bool +ebt_ip6_mt(const struct sk_buff *skb, struct xt_action_param *par) +{ + const struct ebt_ip6_extend_info *info = par->matchinfo; + const struct ipv6hdr *ih6; + struct ipv6hdr _ip6h; + + ih6 = skb_header_pointer(skb, 0, sizeof(_ip6h), &_ip6h); + if (ih6 == NULL) + return false; + if (info->bitmask & EBT_IP6_TCLASS_EXTEND) { + u8 tclass = ipv6_get_dsfield(ih6); + if (FWINV( ((tclass & info->tclassmsk) < (info->tclass[0] & info->tclassmsk)) || + ((tclass & info->tclassmsk) > (info->tclass[1] & info->tclassmsk)), EBT_IP6_TCLASS_EXTEND)) + return false; + } + if (info->bitmask & EBT_IP6_FLOWLABEL_EXTEND) { + if (FWINV((ih6->flow_lbl[0] & 0xF) != (info->flow_lbl[0] & 0xF) || + ih6->flow_lbl[1] != info->flow_lbl[1] || + ih6->flow_lbl[2] != info->flow_lbl[2], EBT_IP6_FLOWLABEL_EXTEND)) + return false; + } + return true; +} + +static int ebt_ip6_mt_check(const struct xt_mtchk_param *par) +{ + const struct ebt_entry *e = par->entryinfo; + struct ebt_ip6_extend_info *info = par->matchinfo; + + if (e->ethproto != htons(ETH_P_IPV6)) + return -EINVAL; + if (info->bitmask & ~EBT_IP6_MASK_EXTEND || info->invflags & ~EBT_IP6_MASK_EXTEND) + return -EINVAL; + return 0; +} + +static struct xt_match ebt_ip6_mt_reg __read_mostly = { + .name = EBT_IP6_MATCH_EXTEND, + .revision = 0, + .family = NFPROTO_BRIDGE, + .match = ebt_ip6_mt, + .checkentry = ebt_ip6_mt_check, + .matchsize = sizeof(struct ebt_ip6_extend_info), + .me = THIS_MODULE, +}; + +static int __init ebt_ip6_init(void) +{ + return xt_register_match(&ebt_ip6_mt_reg); +} + +static void __exit ebt_ip6_fini(void) +{ + xt_unregister_match(&ebt_ip6_mt_reg); +} + +module_init(ebt_ip6_init); +module_exit(ebt_ip6_fini); +MODULE_DESCRIPTION("Ebtables: IPv6 protocol packet match extend"); +MODULE_AUTHOR("Kuo-Lang Tseng , Broadcom"); +MODULE_LICENSE("GPL"); diff -urN linux-4.1.52-orig/net/bridge/netfilter/ebt_ip.c linux-4.1.52/net/bridge/netfilter/ebt_ip.c --- linux-4.1.52-orig/net/bridge/netfilter/ebt_ip.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/bridge/netfilter/ebt_ip.c 2020-02-07 01:47:54.000000000 +0800 @@ -39,6 +39,11 @@ if (info->bitmask & EBT_IP_TOS && FWINV(info->tos != ih->tos, EBT_IP_TOS)) return false; +#if defined(CONFIG_BCM_KF_NETFILTER) || !defined(CONFIG_BCM_IN_KERNEL) + if (info->bitmask & EBT_IP_DSCP && + FWINV(info->dscp != (ih->tos & 0xFC), EBT_IP_DSCP)) + return false; +#endif if (info->bitmask & EBT_IP_SOURCE && FWINV((ih->saddr & info->smsk) != info->saddr, EBT_IP_SOURCE)) diff -urN linux-4.1.52-orig/net/bridge/netfilter/ebt_ip_extend.c linux-4.1.52/net/bridge/netfilter/ebt_ip_extend.c --- linux-4.1.52-orig/net/bridge/netfilter/ebt_ip_extend.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/bridge/netfilter/ebt_ip_extend.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,76 @@ +/* + * ebt_ip + * + * Authors: + * Bart De Schuymer + * + * April, 2002 + * + * Changes: + * added ip-sport and ip-dport + * Innominate Security Technologies AG + * September, 2002 + * + * Extend by Broadcom at Jan 31, 2019 + */ +#include +#include +#include +#include +#include +#include +#include + +static bool +ebt_ip_mt(const struct sk_buff *skb, struct xt_action_param *par) +{ + const struct ebt_ip_extend_info *info = par->matchinfo; + const struct iphdr *ih; + struct iphdr _iph; + + ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph); + if (ih == NULL) + return false; + if (info->bitmask & EBT_IP_TOS_EXTEND&& + FWINV(((ih->tos & info->tosmask) < (info->tos[0] & info->tosmask)) || + ((ih->tos & info->tosmask) > (info->tos[1] & info->tosmask)), EBT_IP_TOS_EXTEND)) + return false; + return true; +} + +static int ebt_ip_mt_check(const struct xt_mtchk_param *par) +{ + const struct ebt_ip_extend_info *info = par->matchinfo; + const struct ebt_entry *e = par->entryinfo; + + if (e->ethproto != htons(ETH_P_IP)) + return -EINVAL; + if (info->bitmask & ~EBT_IP_MASK_EXTEND|| info->invflags & ~EBT_IP_MASK_EXTEND) + return -EINVAL; + return 0; +} + +static struct xt_match ebt_ip_mt_reg __read_mostly = { + .name = EBT_IP_MATCH_EXTEND, + .revision = 0, + .family = NFPROTO_BRIDGE, + .match = ebt_ip_mt, + .checkentry = ebt_ip_mt_check, + .matchsize = sizeof(struct ebt_ip_extend_info), + .me = THIS_MODULE, +}; + +static int __init ebt_ip_init(void) +{ + return xt_register_match(&ebt_ip_mt_reg); +} + +static void __exit ebt_ip_fini(void) +{ + xt_unregister_match(&ebt_ip_mt_reg); +} + +module_init(ebt_ip_init); +module_exit(ebt_ip_fini); +MODULE_DESCRIPTION("Ebtables: IPv4 protocol packet match extend"); +MODULE_LICENSE("GPL"); diff -urN linux-4.1.52-orig/net/bridge/netfilter/ebt_mark.c linux-4.1.52/net/bridge/netfilter/ebt_mark.c --- linux-4.1.52-orig/net/bridge/netfilter/ebt_mark.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/bridge/netfilter/ebt_mark.c 2020-02-07 01:47:54.000000000 +0800 @@ -30,8 +30,76 @@ skb->mark |= info->mark; else if (action == MARK_AND_VALUE) skb->mark &= info->mark; +#if !defined(CONFIG_BCM_KF_NETFILTER) else skb->mark ^= info->mark; +#else + else if (action == MARK_XOR_VALUE) + skb->mark ^= info->mark; + else + { + skb->vtag = (unsigned short)(info->mark); + + /* if the 8021p priority field (bits 0-3) of skb->vtag is not zero, we need + * to do p-bit marking. + */ + if (skb->vtag & 0xf) + { + unsigned short TCI = 0; + + /* if this is a vlan frame, we want to re-mark its p-bit with the 8021p + * priority in skb->vtag. + * if this is not a vlan frame, we want to add a 8021p tag to it, with + * vid=0 and p-bit=the 8021p priority in skb->vtag. + */ + if ((skb->protocol == __constant_htons(ETH_P_8021Q))) + { + struct vlan_hdr *frame = (struct vlan_hdr *)(skb_network_header(skb)); + + TCI = ntohs(frame->h_vlan_TCI); + + /* Since the 8021p priority value in vtag had been incremented by 1, + * we need to minus 1 from it to get the exact value. + */ + TCI = (TCI & 0x1fff) | (((skb->vtag & 0xf) - 1) << 13); + + frame->h_vlan_TCI = htons(TCI); + } + else + { + if ((skb_mac_header(skb) - skb->head) < VLAN_HLEN) + { + printk("ebt_mark_tg: No headroom for VLAN tag. Marking is not done.\n"); + } + else + { + struct vlan_ethhdr *ethHeader; + + skb->protocol = __constant_htons(ETH_P_8021Q); + skb->mac_header -= VLAN_HLEN; + skb->network_header -= VLAN_HLEN; + skb->data -= VLAN_HLEN; + skb->len += VLAN_HLEN; + + /* Move the mac addresses to the beginning of the new header. */ + memmove(skb_mac_header(skb), skb_mac_header(skb) + VLAN_HLEN, 2 * ETH_ALEN); + + ethHeader = (struct vlan_ethhdr *)(skb_mac_header(skb)); + + ethHeader->h_vlan_proto = __constant_htons(ETH_P_8021Q); + + /* Since the 8021p priority value in vtag had been incremented by 1, + * we need to minus 1 from it to get the exact value. + */ + TCI = (TCI & 0x1fff) | (((skb->vtag & 0xf) - 1) << 13); + + ethHeader->h_vlan_TCI = htons(TCI); + } + } + skb->vtag = 0; + } + } +#endif // CONFIG_BCM_KF_NETFILTER return info->target | ~EBT_VERDICT_BITS; } @@ -48,7 +116,12 @@ return -EINVAL; tmp = info->target & ~EBT_VERDICT_BITS; if (tmp != MARK_SET_VALUE && tmp != MARK_OR_VALUE && +#if defined(CONFIG_BCM_KF_NETFILTER) + tmp != MARK_AND_VALUE && tmp != MARK_XOR_VALUE && + tmp != VTAG_SET_VALUE) +#else tmp != MARK_AND_VALUE && tmp != MARK_XOR_VALUE) +#endif return -EINVAL; return 0; } diff -urN linux-4.1.52-orig/net/bridge/netfilter/ebt_qos_map.c linux-4.1.52/net/bridge/netfilter/ebt_qos_map.c --- linux-4.1.52-orig/net/bridge/netfilter/ebt_qos_map.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/bridge/netfilter/ebt_qos_map.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,100 @@ +#if defined(CONFIG_BCM_KF_NETFILTER) +/* +* Copyright (c) 2003-2018 Broadcom Corporation +* All Rights Reserved +* +<:label-BRCM:2012:GPL/GPL:standard + +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 (the "GPL"). + +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. + + +A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by +writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + +:> +*/ + +/* + * ebt_qos_map + * + */ +#include +#include +#include +#include +#include +#if defined(CONFIG_BCM_KF_IP) && defined(CONFIG_IPV6) +#include +#endif +#include + +static unsigned int ebt_qos_map_tg(struct sk_buff *skb, const struct xt_action_param *par) +{ + const struct ebt_qos_map_info *info = par->targinfo; + +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_lock(); + if (skb->blog_p) { + skb->blog_p->dscp2pbit = info->dscp2pbit ? 1:0; + skb->blog_p->dscp2q = info->dscp2q ? 1:0; + } + blog_unlock(); +#endif + + return EBT_CONTINUE; +} + +static int ebt_qos_map_tg_check(const struct xt_tgchk_param *par) + +{ + const struct ebt_entry *e = par->entryinfo; + +#if defined(CONFIG_BCM_KF_IP) && defined(CONFIG_IPV6) + if ((e->ethproto != __constant_htons(ETH_P_IPV6) && e->ethproto != __constant_htons(ETH_P_IP)) || +#else + if (e->ethproto != __constant_htons(ETH_P_IP) || +#endif + e->invflags & EBT_IPROTO) + return -EINVAL; + + return 0; +} + +static struct xt_target ebt_qos_map_tg_reg = { + .name = "QOSMAP", + .revision = 0, + .family = NFPROTO_BRIDGE, + .target = ebt_qos_map_tg, + .checkentry = ebt_qos_map_tg_check, + .targetsize = XT_ALIGN(sizeof(struct ebt_qos_map_info)), + .me = THIS_MODULE, +}; + +static int __init ebt_qos_map_init(void) +{ + int ret; + ret = xt_register_target(&ebt_qos_map_tg_reg); + + if(ret == 0) + printk(KERN_INFO "ebt_qos_map registered\n"); + + return ret; +} + +static void __exit ebt_qos_map_fini(void) +{ + xt_unregister_target(&ebt_qos_map_tg_reg); +} + +module_init(ebt_qos_map_init); +module_exit(ebt_qos_map_fini); +MODULE_LICENSE("GPL"); +#endif diff -urN linux-4.1.52-orig/net/bridge/netfilter/ebt_reject.c linux-4.1.52/net/bridge/netfilter/ebt_reject.c --- linux-4.1.52-orig/net/bridge/netfilter/ebt_reject.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/bridge/netfilter/ebt_reject.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,193 @@ +#if defined(CONFIG_BCM_KF_NETFILTER) +/* +* Copyright (c) 2003-2019 Broadcom Corporation +* All Rights Reserved +* +<:label-BRCM:2012:GPL/GPL:standard + +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 (the "GPL"). + +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. + + +A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by +writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + +:> +*/ + +/* + * ebt_reject + * + */ +#include +#include +#include +#include +#include +#if defined(CONFIG_BCM_KF_IP) && defined(CONFIG_IPV6) +#include +#include +#include +#endif +#include +#include "../br_private.h" + +#if defined(CONFIG_BCM_KF_IP) && defined(CONFIG_IPV6) +static struct ipv6hdr * ebt_reject_ip6hdr_put(struct sk_buff *nskb, + const struct sk_buff *oldskb, + __u8 protocol, int hoplimit) +{ + struct ipv6hdr *ip6h; + const struct ipv6hdr *oip6h = ipv6_hdr(oldskb); +#define DEFAULT_TOS_VALUE 0x0U + const __u8 tclass = DEFAULT_TOS_VALUE; + + skb_put(nskb, sizeof(struct ipv6hdr)); + skb_reset_network_header(nskb); + ip6h = ipv6_hdr(nskb); + ip6_flow_hdr(ip6h, tclass, 0); + ip6h->hop_limit = hoplimit; + ip6h->nexthdr = protocol; + ip6h->saddr = oip6h->daddr; + ip6h->daddr = oip6h->saddr; + nskb->protocol = htons(ETH_P_IPV6); + + return ip6h; +} + +static void ebt_reject_br_push_etherhdr(struct sk_buff *oldskb, + struct sk_buff *nskb) +{ + struct ethhdr *eth; + + eth = (struct ethhdr *)skb_push(nskb, ETH_HLEN); + skb_reset_mac_header(nskb); + ether_addr_copy(eth->h_source, eth_hdr(oldskb)->h_dest); + ether_addr_copy(eth->h_dest, eth_hdr(oldskb)->h_source); + eth->h_proto = eth_hdr(oldskb)->h_proto; + skb_pull(nskb, ETH_HLEN); +} + +static void ebt_reject_br_send_v6_unreach(struct net *net, + struct sk_buff *oldskb, + const struct net_device *dev, u8 code) +{ + struct sk_buff *nskb; + struct ipv6hdr *nip6h; + struct icmp6hdr *icmp6h; + unsigned int len; + void *payload; + + /* Include "As much of invoking packet as possible without the ICMPv6 + * packet exceeding the minimum IPv6 MTU" in the ICMP payload. + */ + len = min_t(unsigned int, 1220, oldskb->len); + + if (!pskb_may_pull(oldskb, len)) + return; + + nskb = alloc_skb(sizeof(struct ipv6hdr) + sizeof(struct icmp6hdr) + + LL_MAX_HEADER + len, GFP_ATOMIC); + if (!nskb) + return; + + skb_reserve(nskb, LL_MAX_HEADER); + nip6h = ebt_reject_ip6hdr_put(nskb, oldskb, IPPROTO_ICMPV6, + net->ipv6.devconf_all->hop_limit); + + skb_reset_transport_header(nskb); + icmp6h = (struct icmp6hdr *)skb_put(nskb, sizeof(struct icmp6hdr)); + memset(icmp6h, 0, sizeof(*icmp6h)); + icmp6h->icmp6_type = ICMPV6_DEST_UNREACH; + icmp6h->icmp6_code = code; + + payload = skb_put(nskb, len); + memcpy(payload, skb_network_header(oldskb), len); + nip6h->payload_len = htons(nskb->len - sizeof(struct ipv6hdr)); + + icmp6h->icmp6_cksum = + csum_ipv6_magic(&nip6h->saddr, &nip6h->daddr, + nskb->len - sizeof(struct ipv6hdr), + IPPROTO_ICMPV6, + csum_partial(icmp6h, + nskb->len - sizeof(struct ipv6hdr), + 0)); + + ebt_reject_br_push_etherhdr(oldskb, nskb); + nskb->dev = oldskb->dev; + + br_dev_queue_push_xmit(NULL, nskb); +} +#endif + +static unsigned int ebt_reject_tg(struct sk_buff *skb, const struct xt_action_param *par) +{ + const struct ebt_reject_info *reject = par->targinfo; + + pr_debug("%s: medium point\n", __func__); + switch (reject->with) { + case EBT_ICMP6_POLICY_FAIL: +#if defined(CONFIG_BCM_KF_IP) && defined(CONFIG_IPV6) + ebt_reject_br_send_v6_unreach(dev_net(skb->dev), skb, skb->dev, ICMPV6_POLICY_FAIL); + break; +#endif + default: + net_info_ratelimited("case %u not handled yet\n", reject->with); + break; + } + + return EBT_DROP; +} + +static int ebt_reject_tg_check(const struct xt_tgchk_param *par) + +{ +#if defined(CONFIG_BCM_KF_IP) && defined(CONFIG_IPV6) + const struct ebt_entry *e = par->entryinfo; + + if (e->ethproto != __constant_htons(ETH_P_IPV6) ||e->invflags & EBT_IPROTO) + return -EINVAL; + + return 0; +#else + return -EINVAL; +#endif +} + +static struct xt_target ebt_reject_tg_reg = { + .name = "REJECT", + .revision = 0, + .family = NFPROTO_BRIDGE, + .target = ebt_reject_tg, + .checkentry = ebt_reject_tg_check, + .targetsize = XT_ALIGN(sizeof(struct ebt_reject_info)), + .me = THIS_MODULE, +}; + +static int __init ebt_reject_init(void) +{ + int ret; + ret = xt_register_target(&ebt_reject_tg_reg); + + if(ret == 0) + printk(KERN_INFO "ebt_qos_map registered\n"); + + return ret; +} + +static void __exit ebt_reject_fini(void) +{ + xt_unregister_target(&ebt_reject_tg_reg); +} + +module_init(ebt_reject_init); +module_exit(ebt_reject_fini); +MODULE_LICENSE("GPL"); +#endif diff -urN linux-4.1.52-orig/net/bridge/netfilter/ebt_skbvlan_m.c linux-4.1.52/net/bridge/netfilter/ebt_skbvlan_m.c --- linux-4.1.52-orig/net/bridge/netfilter/ebt_skbvlan_m.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/bridge/netfilter/ebt_skbvlan_m.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,247 @@ +#if defined(CONFIG_BCM_KF_NETFILTER) +/* +* Copyright (c) 2003-2012 Broadcom Corporation +* All Rights Reserved +* +<:label-BRCM:2012:GPL/GPL:standard + +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 (the "GPL"). + +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. + + +A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by +writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + +:> +*/ + +/* + * ebt_skbvlan_m + * + */ + +/* This match moudle is 90% the same with ebt_vlan.c but compare to broadcom defined vlan_header fields */ + +#include +#include +#include +#include +#include +#include +#include + +#include "bcm_vlan_defs.h" + +#define MODULE_VERS "0.1" + +#define GET_SKBVLAN_ID(VTAG) (VTAG & VLAN_VID_MASK) +#define GET_SKBVLAN_PRIO(VTAG) ((VTAG & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT) + +#define GET_BITMASK(_BIT_MASK_) info->bitmask & _BIT_MASK_ +#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) {if (!((info->_MATCH_ == _MATCH_)^!!(info->invflags & _MASK_))) return false; } + +#define EBT_TPID_MASK 0xFFFF0000 + +#define EXIT_ON_MISMATCH_VLANTAG(INDEX,INST) \ +{ \ + if (info->vlanmask##INDEX >= EBT_TPID_MASK) \ + { \ + if (getvlantag(skb, info->vlantag##INDEX[0] & EBT_TPID_MASK, INST, &result)) \ + { \ + if (FWINV((result & info->vlanmask##INDEX) < (info->vlantag##INDEX[0] & info->vlanmask##INDEX) || \ + (result & info->vlanmask##INDEX) > (info->vlantag##INDEX[1] & info->vlanmask##INDEX), EBT_SKBVLAN_VLAN_TAG_##INDEX)) \ + return false; \ + } \ + else \ + { \ + return false; \ + } \ + } \ + else \ + { \ + result = (skb->vlan_tpid << 16) | (skb->vlan_header[0] >> 16); \ + if (FWINV((result & info->vlanmask##INDEX) < (info->vlantag##INDEX[0] & info->vlanmask##INDEX) || \ + (result & info->vlanmask##INDEX) > (info->vlantag##INDEX[1] & info->vlanmask##INDEX), EBT_SKBVLAN_VLAN_TAG_##INDEX)) \ + return false; \ + } \ +} + +#define EXIT_ON_NOTFOUND_VLANTPID(INST) \ +{ \ + find = getvlantag(skb, info->vlantpid##INST << 16, 0, &result); \ + if (info->invflags & EBT_SKBVLAN_VLAN_TPID_##INST) \ + find = !find; \ + return find; \ +} + +static bool getvlantag(const struct sk_buff *skb, __u32 expect_tag, __u8 layer, __u32 * vlantag_result) +{ + int i; + __u32 vlantag; + __u8 count = 0; + + for (i = 0; (i < skb->vlan_count) && (i < SKB_VLAN_MAX_TAGS); i++) + { + if (i == 0) + vlantag = (skb->vlan_tpid << 16) | (skb->vlan_header[0] >> 16); + else + vlantag = (skb->vlan_header[i - 1] << 16) | (skb->vlan_header[i] >> 16); + + if ((vlantag & EBT_TPID_MASK) == (expect_tag & EBT_TPID_MASK)) + { + if (count == layer) + { + *vlantag_result = vlantag; + return true; + } + count++; + } + } + + return false; +} + +static bool +ebt_skbvlan_mt(const struct sk_buff *skb, struct xt_action_param *par) +{ +#if defined(CONFIG_BCM_KF_VLAN) && (defined(CONFIG_BCM_VLAN) || defined(CONFIG_BCM_VLAN_MODULE)) + const struct ebt_skbvlan_m_info *info = par->matchinfo; + unsigned short id; + unsigned char prio; + __be16 encap; + __u32 result; + bool find = true; + + if (skb == NULL) + { + return false; + } + + if (GET_BITMASK(EBT_SKBVLAN_VLAN_TPID_0)) + EXIT_ON_NOTFOUND_VLANTPID(0); + + if (GET_BITMASK(EBT_SKBVLAN_VLAN_TPID_1)) + EXIT_ON_NOTFOUND_VLANTPID(1); + + if (skb->vlan_count == 0) + { + return false; + } + + id = GET_SKBVLAN_ID(skb->vlan_header[0] >> 16); + prio = GET_SKBVLAN_PRIO(skb->vlan_header[0] >> 16); + encap = (skb->vlan_header[0]); + + /* Checking VLAN Identifier (VID) */ + if (GET_BITMASK(EBT_SKBVLAN_ID)) + EXIT_ON_MISMATCH(id, EBT_SKBVLAN_ID); + + /* Checking user_priority */ + if (GET_BITMASK(EBT_SKBVLAN_PRIO)) + EXIT_ON_MISMATCH(prio, EBT_SKBVLAN_PRIO); + + /* Checking Encapsulated Proto (Length/Type) field */ + if (GET_BITMASK(EBT_SKBVLAN_ENCAP)) + EXIT_ON_MISMATCH(encap, EBT_SKBVLAN_ENCAP); + + if (GET_BITMASK(EBT_SKBVLAN_VLAN_TAG_0)) + EXIT_ON_MISMATCH_VLANTAG(0, 0); + + if (GET_BITMASK(EBT_SKBVLAN_VLAN_TAG_1)) + EXIT_ON_MISMATCH_VLANTAG(1, 1); + + if (GET_BITMASK(EBT_SKBVLAN_VLAN_TAG_2)) + EXIT_ON_MISMATCH_VLANTAG(2, 0); + + if (GET_BITMASK(EBT_SKBVLAN_VLAN_TAG_3)) + EXIT_ON_MISMATCH_VLANTAG(3, 1); + + return true; +#else + return false; +#endif +} + +static int ebt_skbvlan_mt_check(const struct xt_mtchk_param *par) +{ + const struct ebt_skbvlan_m_info *info = par->matchinfo; + + /* Check for bitmask range + * True if even one bit is out of mask */ + if (info->bitmask & ~EBT_SKBVLAN_MASK) { + pr_debug("bitmask %2X is out of mask (%2X)\n", + info->bitmask, EBT_SKBVLAN_MASK); + return -EINVAL; + } + + /* Check for inversion flags range */ + if (info->invflags & ~EBT_SKBVLAN_MASK) { + pr_debug("inversion flags %2X is out of mask (%2X)\n", + info->invflags, EBT_SKBVLAN_MASK); + return -EINVAL; + } + + if (GET_BITMASK(EBT_SKBVLAN_ID)) { + if (info->id) { /* if id!=0 => check vid range */ + if (info->id > VLAN_N_VID) { + pr_debug("id %d is out of range (1-4096)\n", + info->id); + return -EINVAL; + } + } + } + + if (GET_BITMASK(EBT_SKBVLAN_PRIO)) { + if ((unsigned char) info->prio > 7) { + pr_debug("prio %d is out of range (0-7)\n", + info->prio); + return -EINVAL; + } + } + /* Check for encapsulated proto range - it is possible to be + * any value for u_short range. + * if_ether.h: ETH_ZLEN 60 - Min. octets in frame sans FCS */ + if (GET_BITMASK(EBT_SKBVLAN_ENCAP)) { + if ((unsigned short) ntohs(info->encap) < ETH_ZLEN) { + pr_debug("encap frame length %d is less than " + "minimal\n", ntohs(info->encap)); + return -EINVAL; + } + } + + return 0; +} + +static struct xt_match ebt_skbvlan_mt_reg __read_mostly = { + .name = "skbvlan", + .revision = 0, + .family = NFPROTO_BRIDGE, + .match = ebt_skbvlan_mt, + .checkentry = ebt_skbvlan_mt_check, + .matchsize = sizeof(struct ebt_skbvlan_m_info), + .me = THIS_MODULE, +}; + +static int __init ebt_skbvlan_m_init(void) +{ + pr_debug("ebtables 802.1Q extension module for LANVLAN" MODULE_VERS "\n"); + return xt_register_match(&ebt_skbvlan_mt_reg); +} + +static void __exit ebt_skbvlan_m_fini(void) +{ + xt_unregister_match(&ebt_skbvlan_mt_reg); +} + +module_init(ebt_skbvlan_m_init); +module_exit(ebt_skbvlan_m_fini); +MODULE_DESCRIPTION("Ebtables: Packet skbvlan match"); +MODULE_LICENSE("GPL"); +#endif diff -urN linux-4.1.52-orig/net/bridge/netfilter/ebt_skiplog.c linux-4.1.52/net/bridge/netfilter/ebt_skiplog.c --- linux-4.1.52-orig/net/bridge/netfilter/ebt_skiplog.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/bridge/netfilter/ebt_skiplog.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,74 @@ +#if defined(CONFIG_BCM_KF_NETFILTER) +/* +* Copyright (c) 2003-2012 Broadcom Corporation +* All Rights Reserved +* +<:label-BRCM:2012:DUAL/GPL:standard + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + +/* + * ebt_skiplog + */ +#include +#include +#include +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#include +#endif + +static unsigned int +ebt_skiplog_tg(struct sk_buff *skb, const struct xt_action_param *par) +{ +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_skip(skb, blog_skip_reason_nf_ebt_skiplog); +#endif + + return EBT_CONTINUE; +} + +static struct xt_target ebt_skiplog_tg_reg __read_mostly = { + .name = "SKIPLOG", + .revision = 0, + .family = NFPROTO_BRIDGE, + .target = ebt_skiplog_tg, + .me = THIS_MODULE, +}; + +static int __init ebt_skiplog_init(void) +{ + return xt_register_target(&ebt_skiplog_tg_reg); +} + +static void __exit ebt_skiplog_fini(void) +{ + xt_unregister_target(&ebt_skiplog_tg_reg); +} + +module_init(ebt_skiplog_init); +module_exit(ebt_skiplog_fini); +MODULE_DESCRIPTION("Ebtables: SKIPLOG target"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Broadcom"); +#endif diff -urN linux-4.1.52-orig/net/bridge/netfilter/ebt_time.c linux-4.1.52/net/bridge/netfilter/ebt_time.c --- linux-4.1.52-orig/net/bridge/netfilter/ebt_time.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/bridge/netfilter/ebt_time.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,228 @@ +#if defined(CONFIG_BCM_KF_NETFILTER) + +/* +* Copyright (c) 2003-2012 Broadcom Corporation +* All Rights Reserved +* +<:label-BRCM:2012:GPL/GPL:standard + +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 (the "GPL"). + +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. + + +A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by +writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + +:> +*/ + +/* + Description: EBTables time match extension kernelspace module. + Authors: Song Wang , ported from netfilter/iptables + The following is the original disclaimer. + + This is a module which is used for time matching + It is using some modified code from dietlibc (localtime() function) + that you can find at http://www.fefe.de/dietlibc/ + This file is distributed under the terms of the GNU General Public + License (GPL). Copies of the GPL can be obtained from: ftp://prep.ai.mit.edu/pub/gnu/GPL + 2001-05-04 Fabrice MARIE : initial development. + 2001-21-05 Fabrice MARIE : bug fix in the match code, + thanks to "Zeng Yu" for bug report. + 2001-26-09 Fabrice MARIE : force the match to be in LOCAL_IN or PRE_ROUTING only. + 2001-30-11 Fabrice : added the possibility to use the match in FORWARD/OUTPUT with a little hack, + added Nguyen Dang Phuoc Dong patch to support timezones. +*/ + +#include +#include +#include +#include +#include +#include + +//static unsigned char debug; +//MODULE_PARM(debug, "0-1b"); +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "debug=1 is turn on debug messages"); +MODULE_AUTHOR("Song Wang "); +MODULE_DESCRIPTION("Match timestamp"); +MODULE_LICENSE("GPL"); + +#define DEBUG_MSG(...) if (debug) printk (KERN_DEBUG "ebt_time: " __VA_ARGS__) + +void localtime(const time_t *timepr, struct tm *r); +bool (*match)(const struct sk_buff *skb, struct xt_action_param *); + +static bool ebt_time_mt(const struct sk_buff *skb, struct xt_action_param *par) +{ + //const struct ebt_time_info *info = (struct ebt_time_info *)data; /* match info for rule */ + const struct ebt_time_info *info = par->matchinfo; + struct tm currenttime; /* time human readable */ + u_int8_t days_of_week[7] = {64, 32, 16, 8, 4, 2, 1}; + u_int16_t packet_time; + struct timeval kerneltimeval; + time_t packet_local_time; + + /* if kerneltime=1, we don't read the skb->timestamp but kernel time instead */ + if (info->kerneltime) + { + do_gettimeofday(&kerneltimeval); + packet_local_time = kerneltimeval.tv_sec; + } + else + { + struct timespec ts; + ts = ktime_to_timespec(skb->tstamp); + packet_local_time = ts.tv_sec; + } + + /* Transform the timestamp of the packet, in a human readable form */ + localtime(&packet_local_time, ¤ttime); + DEBUG_MSG("currenttime: Y-%ld M-%d D-%d H-%d M-%d S-%d, Day: W-%d\n", + currenttime.tm_year, currenttime.tm_mon, currenttime.tm_mday, + currenttime.tm_hour, currenttime.tm_min, currenttime.tm_sec, + currenttime.tm_wday); + + /* check if we match this timestamp, we start by the days... */ + if (info->days_match != 0) { + if ((days_of_week[currenttime.tm_wday] & info->days_match) != days_of_week[currenttime.tm_wday]) + { + DEBUG_MSG("the day doesn't match\n"); + return false; /* the day doesn't match */ + } + } + /* ... check the time now */ + packet_time = (currenttime.tm_hour * 60) + currenttime.tm_min; + if ((packet_time < info->time_start) || (packet_time > info->time_stop)) + { + DEBUG_MSG("the time doesn't match\n"); + return false; + } + + /* here we match ! */ + DEBUG_MSG("the time match!!!!!!!!\n"); + return true; +} + +static int ebt_time_mt_check(const struct xt_mtchk_param *par) +{ + //struct ebt_time_info *info = (struct ebt_time_info *)data; /* match info for rule */ + struct ebt_time_info *info = par->matchinfo; + + /* First, check that we are in the correct hook */ + /* PRE_ROUTING, LOCAL_IN or FROWARD */ +#if 0 + if (hookmask + & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | (1 << NF_BR_LOCAL_OUT))) + { + printk("ebt_time: error, only valid for PRE_ROUTING, LOCAL_IN, FORWARD and OUTPUT)\n"); + return -EINVAL; + } +#endif + /* we use the kerneltime if we are in forward or output */ + info->kerneltime = 1; +#if 0 + if (hookmask & ~((1 << NF_BR_FORWARD) | (1 << NF_BR_LOCAL_OUT))) + /* if not, we use the skb time */ + info->kerneltime = 0; +#endif + + /* Check the size */ + //if (datalen != sizeof(struct ebt_time_info)) + // return -EINVAL; + /* Now check the coherence of the data ... */ + if ((info->time_start > 1439) || /* 23*60+59 = 1439*/ + (info->time_stop > 1439)) + { + printk(KERN_WARNING "ebt_time: invalid argument\n"); + return -EINVAL; + } + + return 0; +} + +static struct xt_match ebt_time_mt_reg = { + .name = EBT_TIME_MATCH, + .revision = 0, + .family = NFPROTO_BRIDGE, + .match = ebt_time_mt, + .checkentry = ebt_time_mt_check, + .matchsize = XT_ALIGN(sizeof(struct ebt_time_info)), + .me = THIS_MODULE, +}; + +static int __init ebt_time_init(void) +{ + int ret; + ret = xt_register_match(&ebt_time_mt_reg); + + if(ret == 0) + printk(KERN_INFO "ebt_time registered\n"); + + return ret; +} + +static void __exit ebt_time_fini(void) +{ + xt_unregister_match(&ebt_time_mt_reg); +} + + + +module_init(ebt_time_init); +module_exit(ebt_time_fini); + + +/* The part below is borowed and modified from dietlibc */ + +/* seconds per day */ +#define SPD 24*60*60 + +void localtime(const time_t *timepr, struct tm *r) { + time_t i; + time_t timep; + extern struct timezone sys_tz; + const unsigned int __spm[12] = + { 0, + (31), + (31+28), + (31+28+31), + (31+28+31+30), + (31+28+31+30+31), + (31+28+31+30+31+30), + (31+28+31+30+31+30+31), + (31+28+31+30+31+30+31+31), + (31+28+31+30+31+30+31+31+30), + (31+28+31+30+31+30+31+31+30+31), + (31+28+31+30+31+30+31+31+30+31+30), + }; + register time_t work; + + timep = (*timepr) - (sys_tz.tz_minuteswest * 60); + work=timep%(SPD); + r->tm_sec=work%60; work/=60; + r->tm_min=work%60; r->tm_hour=work/60; + work=timep/(SPD); + r->tm_wday=(4+work)%7; + for (i=1970; ; ++i) { + register time_t k= (!(i%4) && ((i%100) || !(i%400)))?366:365; + if (work>k) + work-=k; + else + break; + } + r->tm_year=i-1900; + for (i=11; i && __spm[i]>work; --i) ; + r->tm_mon=i; + r->tm_mday=work-__spm[i]+1; +} +#endif \ No newline at end of file diff -urN linux-4.1.52-orig/net/bridge/netfilter/ebt_u32.c linux-4.1.52/net/bridge/netfilter/ebt_u32.c --- linux-4.1.52-orig/net/bridge/netfilter/ebt_u32.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/bridge/netfilter/ebt_u32.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,130 @@ +/* + * ebt_u32 - kernel module to match u32 packet content + * + * Original author: Don Cohen + * (C) CC Computer Consultants GmbH, 2007 + * + * extend it by Broadcom at Jan, 2019 + */ + + +#include +#include +#include +#include +#include +static bool u32_match_it(const struct ebt_u32_info *data, + const struct sk_buff *skb) +{ + const struct ebt_u32_test *ct; + unsigned int testind; + unsigned int nnums; + unsigned int nvals; + unsigned int i; + __be32 n; + u_int32_t pos; + u_int32_t val; + u_int32_t at; + unsigned char * header; + u_int32_t length = skb->mac_len + skb->len - skb->data_len; + + /* In iptable: + * Small example: "0 >> 28 == 4 && 8 & 0xFF0000 >> 16 = 6, 17" + * (=IPv4 and (TCP or UDP)). Outer loop runs over the "&&" operands. + * In ebtable, the start & mask = range, start from Mac Header. + */ + for (testind = 0; testind < data->ntests; ++testind) { + ct = &data->tests[testind]; + at = 0; + pos = ct->location[0].number; + + if (length < 4 || pos > length - 4) + return false; + + header = (unsigned char *)skb_mac_header(skb); + memcpy(&n, header + pos, sizeof(n)); + + val = ntohl(n); + nnums = ct->nnums; + + /* Inner loop runs over "&", "<<", ">>" and "@" operands */ + for (i = 1; i < nnums; ++i) { + u_int32_t number = ct->location[i].number; + switch (ct->location[i].nextop) { + case EBT_U32_AND: + val &= number; + break; + case EBT_U32_LEFTSH: + val <<= number; + break; + case EBT_U32_RIGHTSH: + val >>= number; + break; + case EBT_U32_AT: + if (at + val < at) + return false; + at += val; + pos = number; + if (at + 4 < at || length < at + 4 || + pos > length - at - 4) + return false; + + memcpy(&n, header + at + pos, sizeof(n)); + val = ntohl(n); + break; + } + } + + /* Run over the "," and ":" operands */ + nvals = ct->nvalues; + for (i = 0; i < nvals; ++i) + if (ct->value[i].min <= val && val <= ct->value[i].max) + break; + + if (i >= ct->nvalues) + return false; + } + + return true; +} + + +static bool +ebt_u32_mt(const struct sk_buff *skb, struct xt_action_param *par) +{ + const struct ebt_u32_info *data = par->matchinfo; + bool ret; + + ret = u32_match_it(data, skb); + return ret ^ data->invert; +} + +static int ebt_u32_mt_check(const struct xt_mtchk_param *par) +{ + return 0; +} + +static struct xt_match ebt_u32_mt_reg __read_mostly = { + .name = EBT_U32_MATCH, + .revision = 0, + .family = NFPROTO_BRIDGE, + .match = ebt_u32_mt, + .checkentry = ebt_u32_mt_check, + .matchsize = sizeof(struct ebt_u32_info), + .me = THIS_MODULE, +}; + +static int __init ebt_u32_init(void) +{ + return xt_register_match(&ebt_u32_mt_reg); +} + +static void __exit ebt_u32_fini(void) +{ + xt_unregister_match(&ebt_u32_mt_reg); +} + +module_init(ebt_u32_init); +module_exit(ebt_u32_fini); +MODULE_DESCRIPTION("Ebtables: u32 regress match"); +MODULE_LICENSE("GPL"); diff -urN linux-4.1.52-orig/net/bridge/netfilter/ebt_wmm_mark.c linux-4.1.52/net/bridge/netfilter/ebt_wmm_mark.c --- linux-4.1.52-orig/net/bridge/netfilter/ebt_wmm_mark.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/bridge/netfilter/ebt_wmm_mark.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,175 @@ +#if defined(CONFIG_BCM_KF_NETFILTER) +/* +* Copyright (c) 2003-2012 Broadcom Corporation +* All Rights Reserved +* +<:label-BRCM:2012:GPL/GPL:standard + +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 (the "GPL"). + +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. + + +A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by +writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + +:> +*/ + +/* + * ebt_wmm_mark + * + */ +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_BCM_KF_IP) && defined(CONFIG_IPV6) +#include +#endif +#include + +static unsigned int ebt_wmm_mark_tg(struct sk_buff *skb, const struct xt_action_param *par) + +{ + const struct ebt_wmm_mark_t_info *info = par->targinfo; + +// struct iphdr *iph; +// struct vlan_hdr *frame; + unsigned char prio = 0; +// unsigned short TCI; + + if (info->markset != WMM_MARK_VALUE_NONE) { + /* use marset regardless of supported classification method */ + prio = (unsigned char)info->markset; + +#if 0 /* TOS/DSCP priority update will be handled in wlan driver (bcmutils.c, pktsetprio()) */ + if (skb->protocol == __constant_htons(ETH_P_8021Q)) { + + unsigned short pbits = (unsigned short)(info->markset & 0x0000f000); + + if (pbits) { + frame = (struct vlan_hdr *)(skb->network_header); + TCI = ntohs(frame->h_vlan_TCI); + TCI = (TCI & 0x1fff) | (((pbits >> 12) - 1) << 13); + frame->h_vlan_TCI = htons(TCI); + } + } + } else if (info->mark & WMM_MARK_8021D) { + if (skb->protocol == __constant_htons(ETH_P_8021Q)) { + frame = (struct vlan_hdr *)(skb->network_header); + TCI = ntohs(frame->h_vlan_TCI); + prio = (unsigned char)((TCI >> 13) & 0x7); + } else + return EBT_CONTINUE; + + } else if (info->mark & WMM_MARK_DSCP) { + + /* if VLAN frame, we need to point to correct network header */ + if (skb->protocol == __constant_htons(ETH_P_8021Q)) + iph = (struct iphdr *)(skb->network_header + VLAN_HLEN); + /* ip */ +#if defined(CONFIG_BCM_KF_IP) && defined(CONFIG_IPV6) + else if (skb->protocol == __constant_htons(ETH_P_IP)||skb->protocol == __constant_htons(ETH_P_IPV6)) +#else + else if (skb->protocol == __constant_htons(ETH_P_IP)) +#endif + iph = (struct iphdr *)(skb->network_header); + else + /* pass for others */ + return EBT_CONTINUE; + +#if defined(CONFIG_BCM_KF_IP) && defined(CONFIG_IPV6) + if(skb->protocol == __constant_htons(ETH_P_IPV6)) + prio=((struct ipv6hdr *)iph)->priority>>1; + else +#endif + + prio = iph->tos>>WMM_DSCP_MASK_SHIFT ; + +#endif /* if 0 */ + + } + + //printk("markset 0x%08x, mark 0x%x, mark 0x%x \n", info->markset, info->mark, (*pskb)->mark); + if(prio) { + skb->mark &= ~(PRIO_LOC_NFMASK << info->markpos); + skb->mark |= (prio << info->markpos); +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// member below is removed +// (*pskb)->nfcache |= NFC_ALTERED; + //printk("mark 0x%x, mark 0x%x\n",( prio << info->markpos), (*pskb)->mark); + } + + return info->target; +} + +static int ebt_wmm_mark_tg_check(const struct xt_tgchk_param *par) + +{ + const struct ebt_wmm_mark_t_info *info = par->targinfo; + const struct ebt_entry *e = par->entryinfo; + + //if (datalen != EBT_ALIGN(sizeof(struct ebt_wmm_mark_t_info))) + // return -EINVAL; + + //printk("e->ethproto=0x%x, e->invflags=0x%x\n",e->ethproto, e->invflags); + +#if defined(CONFIG_BCM_KF_IP) && defined(CONFIG_IPV6) + if ((e->ethproto != __constant_htons(ETH_P_IPV6) && e->ethproto != __constant_htons(ETH_P_IP) && e->ethproto != __constant_htons(ETH_P_8021Q)) || +#else + if ((e->ethproto != __constant_htons(ETH_P_IP) && e->ethproto != __constant_htons(ETH_P_8021Q)) || +#endif + e->invflags & EBT_IPROTO) + return -EINVAL; + + if (BASE_CHAIN && info->target == EBT_RETURN) + return -EINVAL; + + //CLEAR_BASE_CHAIN_BIT; + + if (INVALID_TARGET) + return -EINVAL; + + return 0; + +} + +static struct xt_target ebt_wmm_mark_tg_reg = { + .name = EBT_WMM_MARK_TARGET, + .revision = 0, + .family = NFPROTO_BRIDGE, + .target = ebt_wmm_mark_tg, + .checkentry = ebt_wmm_mark_tg_check, + .targetsize = XT_ALIGN(sizeof(struct ebt_wmm_mark_t_info)), + .me = THIS_MODULE, +}; + +static int __init ebt_wmm_mark_init(void) +{ + int ret; + ret = xt_register_target(&ebt_wmm_mark_tg_reg); + + if(ret == 0) + printk(KERN_INFO "ebt_wmm_mark registered\n"); + + return ret; +} + +static void __exit ebt_wmm_mark_fini(void) +{ + xt_unregister_target(&ebt_wmm_mark_tg_reg); +} + +module_init(ebt_wmm_mark_init); +module_exit(ebt_wmm_mark_fini); +MODULE_LICENSE("GPL"); +#endif diff -urN linux-4.1.52-orig/net/bridge/netfilter/Kconfig linux-4.1.52/net/bridge/netfilter/Kconfig --- linux-4.1.52-orig/net/bridge/netfilter/Kconfig 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/bridge/netfilter/Kconfig 2020-02-07 01:47:54.000000000 +0800 @@ -99,6 +99,14 @@ filtering. To compile it as a module, choose M here. If unsure, say N. + +config BRIDGE_EBT_U32 + tristate "ebt: custom filter support" + help + This option adds custom match, which allows any field whose length is 32 bits + filtering. + + To compile it as a module, choose M here. If unsure, say N. config BRIDGE_EBT_IP6 tristate "ebt: IP6 filter support" @@ -154,6 +162,40 @@ 802.1Q vlan fields. To compile it as a module, choose M here. If unsure, say N. + +config BRIDGE_EBT_TIME + tristate "ebt: time filter support" + depends on BRIDGE_NF_EBTABLES + depends on BCM_KF_NETFILTER + help + This option adds the system time match, which allows the filtering + of system time when a frame arrives. + + To compile it as a module, choose M here. If unsure, say N. + +config BRIDGE_EBT_SKBVLAN + tristate "ebt: skbvlan filter support" + depends on BRIDGE_NF_EBTABLES + depends on BCM_KF_NETFILTER + help + This option adds the skb vlan header match, which allows the filtering + of preserved skbvlan fileds. + + To compile it as a module, choose M here. If unsure, say N. + +config BRIDGE_EBT_BLOG + tristate "ebt: blog filter support" + depends on BRIDGE_NF_EBTABLES + depends on BCM_KF_NETFILTER + help + This option adds the blog match, which allows the filtering + of blog fields. + + Available fields are: + tcp_pureack - TCP packet with zero size payload and only ACK flag set. + + To compile it as a module, choose M here. If unsure, say N. + # # targets # @@ -199,6 +241,57 @@ source address of frames. To compile it as a module, choose M here. If unsure, say N. + +config BRIDGE_EBT_FTOS_T + tristate "ebt: ftos target support" + depends on BRIDGE_NF_EBTABLES + depends on BCM_KF_NETFILTER + help + This option adds the ftos target, which allows altering the full TOS byte + in IP frames. + + To compile it as a module, choose M here. If unsure, say N. + +config BRIDGE_EBT_SKIPLOG_T + tristate "ebt: skip target support" + depends on BRIDGE_NF_EBTABLES + depends on BCM_KF_NETFILTER + help + This option adds the skiplog target, which can prevent packet from + acceleration. + + To compile it as a module, choose M here. If unsure, say N. + +config BRIDGE_EBT_WMM_MARK + tristate "ebt: Wireless Wi-Fi WMM marking support" + depends on BRIDGE_NF_EBTABLES + depends on BCM_KF_NETFILTER + help + This option adds the wmm-mark target, which allows to + mark Wi-Fi WMM priorities. + + To compile it as a module, choose M here. If unsure, say N. + +config BRIDGE_EBT_QOS_MAP + tristate "ebt: per IP packet OQS map support" + depends on BRIDGE_NF_EBTABLES + depends on BCM_KF_NETFILTER + help + This option adds QOSMAP target, which allows to + support QOS map per IP packets. + + To compile it as a module, choose M here. If unsure, say N. + +config BRIDGE_EBT_REJECT + tristate "ebt: reject packets support" + depends on BRIDGE_NF_EBTABLES + depends on BCM_KF_NETFILTER + help + This option adds reject target, which allows to + support reject packets. + + To compile it as a module, choose M here. If unsure, say N. + # # watchers # diff -urN linux-4.1.52-orig/net/bridge/netfilter/Makefile linux-4.1.52/net/bridge/netfilter/Makefile --- linux-4.1.52-orig/net/bridge/netfilter/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/bridge/netfilter/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -1,6 +1,7 @@ # # Makefile for the netfilter modules for Link Layer filtering on a bridge. # +EXTRA_CFLAGS += -I$(INC_BRCMDRIVER_PUB_PATH)/$(BRCM_BOARD) obj-$(CONFIG_NF_TABLES_BRIDGE) += nf_tables_bridge.o obj-$(CONFIG_NFT_BRIDGE_META) += nft_meta_bridge.o @@ -27,6 +28,14 @@ obj-$(CONFIG_BRIDGE_EBT_PKTTYPE) += ebt_pkttype.o obj-$(CONFIG_BRIDGE_EBT_STP) += ebt_stp.o obj-$(CONFIG_BRIDGE_EBT_VLAN) += ebt_vlan.o +ifdef BCM_KF # defined(CONFIG_BCM_KF_NETFILTER) +obj-$(CONFIG_BRIDGE_EBT_TIME) += ebt_time.o +obj-$(CONFIG_BRIDGE_EBT_SKBVLAN) += ebt_skbvlan_m.o +obj-$(CONFIG_BRIDGE_EBT_BLOG) += ebt_blog.o +obj-$(CONFIG_BRIDGE_EBT_IP) += ebt_ip_extend.o +obj-$(CONFIG_BRIDGE_EBT_IP6) += ebt_ip6_extend.o +obj-$(CONFIG_BRIDGE_EBT_U32) += ebt_u32.o +endif # BCM_KF # targets obj-$(CONFIG_BRIDGE_EBT_ARPREPLY) += ebt_arpreply.o @@ -34,6 +43,13 @@ obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o +ifdef BCM_KF # defined(CONFIG_BCM_KF_NETFILTER) +obj-$(CONFIG_BRIDGE_EBT_FTOS_T) += ebt_ftos.o +obj-$(CONFIG_BRIDGE_EBT_SKIPLOG_T) += ebt_skiplog.o +obj-$(CONFIG_BRIDGE_EBT_WMM_MARK) += ebt_wmm_mark.o +obj-$(CONFIG_BRIDGE_EBT_QOS_MAP) += ebt_qos_map.o +obj-$(CONFIG_BRIDGE_EBT_REJECT) += ebt_reject.o +endif # BCM_KF # CONFIG_BCM_KF_NETFILTER # watchers obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o diff -urN linux-4.1.52-orig/net/core/blog.c linux-4.1.52/net/core/blog.c --- linux-4.1.52-orig/net/core/blog.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/core/blog.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,5104 @@ +#if defined(CONFIG_BCM_KF_BLOG) + +/* +* Copyright (c) 2003-2012 Broadcom Corporation +* All Rights Reserved +* +<:label-BRCM:2012:DUAL/GPL:standard + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + +/* + ******************************************************************************* + * File Name : blog.c + * Description: Implements the tracing of L2 and L3 modifications to a packet + * buffer while it traverses the Linux networking stack. + ******************************************************************************* + */ + +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_BCM_KF_SKB_DEFINES) +#include +#endif +#include +#include +#if defined(CONFIG_XFRM) +#include +#endif + +#if defined(CONFIG_BLOG) + +#include +#include +#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) +#define BLOG_NF_CONNTRACK +#include +#include +#include +#endif /* defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) */ + +#include "../bridge/br_private.h" + +#include + +#include +#include +#if defined(CONFIG_BCM_KF_MAP) || defined(CONFIG_BCM_MAP_MODULE) +#include "../../../bcmdrivers/opensource/char/map/impl1/ivi_map.h" +#endif +#include + +/*--- globals ---*/ + +/* RFC4008 */ + +/* Debug macros */ +int blog_dbg = 0; + +DEFINE_SPINLOCK(blog_lock_tbl_g); +#define BLOG_LOCK_TBL() spin_lock_bh( &blog_lock_tbl_g ) +#define BLOG_UNLOCK_TBL() spin_unlock_bh( &blog_lock_tbl_g ) + +/* Length prioritization table index */ +static uint8_t blog_len_tbl_idx = 0; +/* Length prioritization table + * {tbl idx}{min, max, original mark, target mark} + */ +static uint32_t blog_len_tbl[BLOG_MAX_LEN_TBLSZ][BLOG_LEN_PARAM_NUM]; + +/* DSCP mangle table + * {target dscp} + */ +static uint8_t blog_dscp_tbl[BLOG_MAX_DSCP_TBLSZ]; + +/* TOS mangle table + * {target tos} + */ +static uint8_t blog_tos_tbl[BLOG_MAX_TOS_TBLSZ]; + +/* Temporary storage for passing the values from pre-modify hook to + * post-modify hook. + * {ack priority, length priority, dscp value, tos value} + */ +static uint32_t blog_mangl_params[BLOG_MAX_FEATURES]; + +#if defined(CC_BLOG_SUPPORT_DEBUG) +#define blog_print(fmt, arg...) \ + if ( blog_dbg ) \ + printk( CLRc "BLOG %s :" fmt CLRnl, __FUNCTION__, ##arg ) +#define blog_assertv(cond) \ + if ( !cond ) { \ + printk( CLRerr "BLOG ASSERT %s : " #cond CLRnl, __FUNCTION__ ); \ + return; \ + } +#define blog_assertr(cond, rtn) \ + if ( !cond ) { \ + printk( CLRerr "BLOG ASSERT %s : " #cond CLRnl, __FUNCTION__ ); \ + return rtn; \ + } +#define BLOG_DBG(debug_code) do { debug_code } while(0) +#else +#define blog_print(fmt, arg...) NULL_STMT +#define blog_assertv(cond) NULL_STMT +#define blog_assertr(cond, rtn) NULL_STMT +#define BLOG_DBG(debug_code) NULL_STMT +#endif + +#define blog_error(fmt, arg...) \ + printk( CLRerr "BLOG ERROR %s :" fmt CLRnl, __FUNCTION__, ##arg) + +#undef BLOG_DECL +#define BLOG_DECL(x) #x, /* string declaration */ +#define BLOG_ARY_INIT(x) [x] = BLOG_DECL(x) /* initialization of member of string array */ + +/*--- globals ---*/ + +DEFINE_SPINLOCK(blog_lock_g); /* blogged packet flow */ +EXPORT_SYMBOL(blog_lock_g); +static DEFINE_SPINLOCK(blog_pool_lock_g); /* blog pool only */ +#define BLOG_POOL_LOCK() spin_lock_irqsave(&blog_pool_lock_g, lock_flags) +#define BLOG_POOL_UNLOCK() spin_unlock_irqrestore(&blog_pool_lock_g, lock_flags) + + +static ATOMIC_NOTIFIER_HEAD(blog_flowevent_chain); + + +/*----- Forward declarations -----*/ +static long blog_drv_ioctl(struct file *filep, unsigned int command, + unsigned long arg); + +static int blog_drv_open(struct inode *inode, struct file *filp); + +typedef struct { + struct file_operations fops; +} __attribute__((aligned(16))) blog_drv_t; + + +static blog_drv_t blog_drv_g = { + .fops = { + .unlocked_ioctl = blog_drv_ioctl, +#if defined(CONFIG_COMPAT) + .compat_ioctl = blog_drv_ioctl, +#endif + .open = blog_drv_open + } +}; + + +const char *blog_drv_ioctl_name[] = +{ + BLOG_ARY_INIT(BLOG_IOCTL_GET_STATS) + BLOG_ARY_INIT(BLOG_IOCTL_RESET_STATS) + BLOG_ARY_INIT(BLOG_IOCTL_DUMP_BLOG) + BLOG_ARY_INIT(BLOG_IOCTL_INVALID) +}; + + +/* Accelerator functions binds to this pointer to set the accel mode */ +blog_accel_mode_set_t blog_accel_mode_set_fn = NULL; + +/* + * blog_support_accel_mode_g inherits the default value from CC_BLOG_SUPPORT_ACCEL_MODE + */ +int blog_support_accel_mode_g = CC_BLOG_SUPPORT_ACCEL_MODE; + +/* + * blog_support_get_accel_mode_g returns the current accel mode + * Exported blog_support_get_accel_mode() + */ +int blog_support_get_accel_mode(void) +{ + return blog_support_accel_mode_g; +} + +/* + * Exported blog_support_accel_mode() may be used to set blog_support_accel_mode_g. + */ +void blog_support_accel_mode(int accel_mode) +{ +#if !defined(CONFIG_BRIDGE_NETFILTER) + if (blog_accel_mode_set_fn) + blog_accel_mode_set_fn( accel_mode ); + + blog_support_accel_mode_g = accel_mode; +#endif +} + +/*TCP ACK Multi-Flow */ +blog_tcp_ack_mflows_set_t blog_tcp_ack_mflows_set_fn = NULL; + +#if defined(CONFIG_BCM963268) || defined(CONFIG_BCM963381) || defined(CONFIG_BCM963138) || defined(CONFIG_BCM963148) || defined(CONFIG_BCM94908) || defined(CONFIG_BCM_XRDP) || defined(CONFIG_BCM_ARCHER) || defined(CONFIG_BCM_ARCHER_MODULE) +int blog_support_tcp_ack_mflows_g = 1; +#else +int blog_support_tcp_ack_mflows_g = 0; +#endif + +int blog_support_get_tcp_ack_mflows(void) +{ + return blog_support_tcp_ack_mflows_g; +} + +/* + * Exported blog_support_set_tcp_ack_mflows() may be used to set blog_support_tcp_ack_mflows_g. + */ +void blog_support_set_tcp_ack_mflows(int enable) +{ +#if defined(CONFIG_BCM963268) || defined(CONFIG_BCM963381) || defined(CONFIG_BCM963138) || defined(CONFIG_BCM963148) || defined(CONFIG_BCM94908) || defined(CONFIG_BCM_XRDP) || defined(CONFIG_BCM_ARCHER) || defined(CONFIG_BCM_ARCHER_MODULE) + if (blog_tcp_ack_mflows_set_fn) + blog_tcp_ack_mflows_set_fn( enable ); + + blog_support_tcp_ack_mflows_g = enable; +#else + printk(KERN_ERR "TCP ACK Multi-Flow is not supported \n"); +#endif +} + +/* + * blog_support_mcast_g inherits the default value from CC_BLOG_SUPPORT_MCAST + * Exported blog_support_mcast() may be used to set blog_support_mcast_g. + */ +int blog_support_mcast_g = CC_BLOG_SUPPORT_MCAST; +void blog_support_mcast(int config) { blog_support_mcast_g = config; } + +/* + * blog_support_mcast_learn_g inherits the default value from + * CC_BLOG_SUPPORT_MCAST_LEARN + * Exported blog_support_mcast_learn() may be used to set + * blog_support_mcast_learn_g. + */ +int blog_support_mcast_learn_g = CC_BLOG_SUPPORT_MCAST_LEARN; +void blog_support_mcast_learn(int config) +{ blog_support_mcast_learn_g = config; } + +/* + * blog_support_ipv6_g inherits the value from CC_BLOG_SUPPORT_IPV6 + * Exported blog_support_ipv6() may be used to set blog_support_ipv6_g. + */ +int blog_support_ipv6_g = CC_BLOG_SUPPORT_IPV6; +void blog_support_ipv6(int config) { blog_support_ipv6_g = config; } + +/* + * Exported blog_set_notify_proc_mode() may be used to set blog_notify_proc_mode_g. + */ +int blog_notify_proc_mode_g = BLOG_NOTIFY_PROC_MODE_HYBRID; +void blog_set_notify_proc_mode(int mode) {blog_notify_proc_mode_g = mode;} + +/* + * blog_tunl_tos_g gets the value from BLOG_DEFAULT_TUNL_TOS + * Exported blog_tunl_tos_g() may be used to set blog_tunl_tos_g. + */ + +/* + * blog_support_gre_g inherits the default value from CC_BLOG_SUPPORT_GRE + * Exported blog_support_gre() may be used to set blog_support_gre_g. + */ +int blog_gre_tunnel_accelerated_g = BLOG_GRE_DISABLE; + +int blog_support_gre_g = CC_BLOG_SUPPORT_GRE; +void blog_support_gre(int config) +{ + blog_support_gre_g = config; + + if (blog_fc_enabled() && (blog_support_gre_g == BLOG_GRE_ENABLE)) + blog_gre_tunnel_accelerated_g = BLOG_GRE_ENABLE; + else + blog_gre_tunnel_accelerated_g = BLOG_GRE_DISABLE; +} + +int blog_rcv_chk_gre(struct fkbuff *fkb_p, uint32_t h_proto, uint16_t *gflags_p); +int blog_xmit_chk_gre(struct sk_buff *skb_p, uint32_t h_proto); + +/* + * blog_support_l2tp_g inherits the default value from CC_BLOG_SUPPORT_L2TP + * Exported blog_support_l2tp() may be used to set blog_support_l2tp_g. + */ + +int blog_l2tp_tunnel_accelerated_g = BLOG_L2TP_DISABLE; +int blog_support_l2tp_g = CC_BLOG_SUPPORT_L2TP; +void blog_support_l2tp(int config) +{ + blog_support_l2tp_g = config; + if (blog_fc_enabled()) + { + if( !blog_support_l2tp_g ) + blog_l2tp_tunnel_accelerated_g = BLOG_L2TP_DISABLE; + else if ( blog_support_l2tp_g == BLOG_L2TP_TUNNEL ) + blog_l2tp_tunnel_accelerated_g = BLOG_L2TP_TUNNEL; + else if ( blog_support_l2tp_g == BLOG_L2TP_TUNNEL_WITHCHKSUM ) + blog_l2tp_tunnel_accelerated_g = BLOG_L2TP_TUNNEL_WITHCHKSUM; + } + +} + +/* + * blog_support_4o6_frag_g + * Exported blog_support_4o6_frag() may be used to set blog_support_4o6_frag_g. + */ +int blog_support_4o6_frag_g = BLOG_4O6_FRAG_ENABLE; +void blog_support_4o6_frag(int config) { blog_support_4o6_frag_g = config; } + +/* + * Traffic flow generator, keep conntrack alive during idle traffic periods + * by refreshing the conntrack. + * Netfilter may not be statically loaded. + */ +blog_cttime_upd_t blog_cttime_update_fn = (blog_cttime_upd_t) NULL; +struct sk_buff * nfskb_p = (struct sk_buff *) NULL; + +#if defined(CONFIG_NET_IPGRE) || defined(CONFIG_NET_IPGRE_MODULE) +blog_gre_rcv_check_t blog_gre_rcv_check_fn = NULL; +blog_gre_xmit_upd_t blog_gre_xmit_update_fn = NULL; +blog_gre6_rcv_check_t blog_gre6_rcv_check_fn = NULL; +blog_gre6_xmit_upd_t blog_gre6_xmit_update_fn = NULL; +#endif + +blog_pptp_rcv_check_t blog_pptp_rcv_check_fn = NULL; +blog_pptp_xmit_upd_t blog_pptp_xmit_update_fn = NULL; +blog_pptp_xmit_get_t blog_pptp_xmit_get_fn = NULL; + +blog_dhd_flow_update_t blog_dhd_flow_update_fn = NULL; +blog_l2tp_rcv_check_t blog_l2tp_rcv_check_fn = NULL; + +#if defined(CONFIG_BCM_OVS) +blog_ovs_hooks_t blog_ovs_hooks_g = {}; +#endif + +/*----- Constant string representation of enums for print -----*/ +const char * strBlogAction[BLOG_ACTION_MAX] = +{ + BLOG_ARY_INIT(PKT_DONE) + BLOG_ARY_INIT(PKT_NORM) + BLOG_ARY_INIT(PKT_BLOG) + BLOG_ARY_INIT(PKT_DROP) + BLOG_ARY_INIT(PKT_TCP4_LOCAL) +}; + +const char * strBlogDir[BLOG_DIR_MAX] = +{ + BLOG_ARY_INIT(DIR_RX) + BLOG_ARY_INIT(DIR_TX) +}; + +const char * strBlogNetEntity[BLOG_NET_ENTITY_MAX] = +{ + BLOG_ARY_INIT(FLOWTRACK) + BLOG_ARY_INIT(BRIDGEFDB) + BLOG_ARY_INIT(MCAST_FDB) + BLOG_ARY_INIT(IF_DEVICE) + BLOG_ARY_INIT(IF_DEVICE_MCAST) + BLOG_ARY_INIT(GRE_TUNL) + BLOG_ARY_INIT(TOS_MODE) + BLOG_ARY_INIT(MAP_TUPLE) + BLOG_ARY_INIT(MEGA) +}; + +const char * strBlogNotify[BLOG_NOTIFY_MAX] = +{ + BLOG_ARY_INIT(DESTROY_FLOWTRACK) + BLOG_ARY_INIT(DESTROY_BRIDGEFDB) + BLOG_ARY_INIT(MCAST_CONTROL_EVT) + BLOG_ARY_INIT(MCAST_SYNC_EVT) + BLOG_ARY_INIT(DESTROY_NETDEVICE) + BLOG_ARY_INIT(FETCH_NETIF_STATS) + BLOG_ARY_INIT(CLEAR_NETIF_STATS) + BLOG_ARY_INIT(DYNAMIC_DSCP_EVENT) + BLOG_ARY_INIT(UPDATE_NETDEVICE) + BLOG_ARY_INIT(ARP_BIND_CHG) + BLOG_ARY_INIT(CONFIG_CHANGE) + BLOG_ARY_INIT(UP_NETDEVICE) + BLOG_ARY_INIT(DN_NETDEVICE) + BLOG_ARY_INIT(CHANGE_ADDR) + BLOG_ARY_INIT(SET_DPI_PARAM) + BLOG_ARY_INIT(DESTROY_MAP_TUPLE) + BLOG_ARY_INIT(FLUSH) + BLOG_ARY_INIT(DESTROY_MEGA) + BLOG_ARY_INIT(FETCH_MEGA_STATS) + BLOG_ARY_INIT(CLEAR_MEGA_STATS) +}; + +const char * strBlogQuery[BLOG_QUERY_MAX] = +{ + BLOG_ARY_INIT(QUERY_FLOWTRACK) + BLOG_ARY_INIT(QUERY_BRIDGEFDB) + BLOG_ARY_INIT(QUERY_MAP_TUPLE) + BLOG_ARY_INIT(QUERY_FLOWTRACK_STATS) + BLOG_ARY_INIT(QUERY_GET_HW_ACCEL) +}; + +const char * strBlogRequest[BLOG_REQUEST_MAX] = +{ + BLOG_ARY_INIT(FLOWTRACK_KEY_SET) + BLOG_ARY_INIT(FLOWTRACK_KEY_GET) + BLOG_ARY_INIT(FLOWTRACK_DSCP_GET) + BLOG_ARY_INIT(FLOWTRACK_CONFIRMED) + BLOG_ARY_INIT(FLOWTRACK_ALG_HELPER) + BLOG_ARY_INIT(FLOWTRACK_EXCLUDE) + BLOG_ARY_INIT(FLOWTRACK_TIME_SET) + BLOG_ARY_INIT(FLOWTRACK_PUT_STATS) + BLOG_ARY_INIT(NETIF_PUT_STATS) + BLOG_ARY_INIT(LINK_XMIT_FN) + BLOG_ARY_INIT(LINK_NOCARRIER) + BLOG_ARY_INIT(NETDEV_NAME) + BLOG_ARY_INIT(MCAST_DFLT_MIPS) + BLOG_ARY_INIT(IQPRIO_SKBMARK_SET) + BLOG_ARY_INIT(DPIQ_SKBMARK_SET) + BLOG_ARY_INIT(BRIDGEFDB_KEY_SET) + BLOG_ARY_INIT(BRIDGEFDB_KEY_GET) + BLOG_ARY_INIT(BRIDGEFDB_TIME_SET) + BLOG_ARY_INIT(BRIDGEFDB_IFIDX_GET) + BLOG_ARY_INIT(SYS_TIME_GET) + BLOG_ARY_INIT(GRE_TUNL_XMIT) + BLOG_ARY_INIT(GRE6_TUNL_XMIT) + BLOG_ARY_INIT(SKB_DST_ENTRY_SET) + BLOG_ARY_INIT(SKB_DST_ENTRY_RELEASE) + BLOG_ARY_INIT(NETDEV_ADDR) + BLOG_ARY_INIT(FLOW_EVENT_ACTIVATE) + BLOG_ARY_INIT(FLOW_EVENT_DEACTIVATE) + BLOG_ARY_INIT(CHK_HOST_DEV_MAC) + BLOG_ARY_INIT(MAP_TUPLE_KEY_SET) + BLOG_ARY_INIT(MAP_TUPLE_KEY_GET) + BLOG_ARY_INIT(MEGA_KEY_SET) + BLOG_ARY_INIT(MEGA_KEY_GET) + BLOG_ARY_INIT(MEGA_PUT_STATS) +}; + +const char * strBlogEncap[PROTO_MAX] = +{ + BLOG_ARY_INIT(GRE_ETH) + BLOG_ARY_INIT(BCM_XPHY) + BLOG_ARY_INIT(BCM_SWC) + BLOG_ARY_INIT(ETH_802x) + BLOG_ARY_INIT(VLAN_8021Q) + BLOG_ARY_INIT(PPPoE_2516) + BLOG_ARY_INIT(PPP_1661) + BLOG_ARY_INIT(PLD_IPv4) + BLOG_ARY_INIT(PLD_IPv6) + BLOG_ARY_INIT(PPTP) + BLOG_ARY_INIT(L2TP) + BLOG_ARY_INIT(GRE) + BLOG_ARY_INIT(ESP) + BLOG_ARY_INIT(DEL_IPv4) + BLOG_ARY_INIT(DEL_IPv6) + BLOG_ARY_INIT(PLD_L2) + BLOG_ARY_INIT(HDR0_IPv4) + BLOG_ARY_INIT(HDR0_IPv6) + BLOG_ARY_INIT(GREoESP_type) + BLOG_ARY_INIT(GREoESP_type_resvd) + BLOG_ARY_INIT(GREoESP) +}; + +/* + *------------------------------------------------------------------------------ + * Support for RFC 2684 headers logging. + *------------------------------------------------------------------------------ + */ +const char * strRfc2684[RFC2684_MAX] = +{ + BLOG_ARY_INIT(RFC2684_NONE) /* */ + BLOG_ARY_INIT(LLC_SNAP_ETHERNET) /* AA AA 03 00 80 C2 00 07 00 00 */ + BLOG_ARY_INIT(LLC_SNAP_ROUTE_IP) /* AA AA 03 00 00 00 08 00 */ + BLOG_ARY_INIT(LLC_ENCAPS_PPP) /* FE FE 03 CF */ + BLOG_ARY_INIT(VC_MUX_ETHERNET) /* 00 00 */ + BLOG_ARY_INIT(VC_MUX_IPOA) /* */ + BLOG_ARY_INIT(VC_MUX_PPPOA) /* */ + BLOG_ARY_INIT(PTM) /* */ +}; + +const uint8_t rfc2684HdrLength[RFC2684_MAX] = +{ + 0, /* header was already stripped. : */ + 10, /* LLC_SNAP_ETHERNET : AA AA 03 00 80 C2 00 07 00 00 */ + 8, /* LLC_SNAP_ROUTE_IP : AA AA 03 00 00 00 08 00 */ + 4, /* LLC_ENCAPS_PPP : FE FE 03 CF */ + 2, /* VC_MUX_ETHERNET : 00 00 */ + 0, /* VC_MUX_IPOA : */ + 0, /* VC_MUX_PPPOA : */ + 0, /* PTM : */ +}; + +const uint8_t rfc2684HdrData[RFC2684_MAX][16] = +{ + {}, + { 0xAA, 0xAA, 0x03, 0x00, 0x80, 0xC2, 0x00, 0x07, 0x00, 0x00 }, + { 0xAA, 0xAA, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00 }, + { 0xFE, 0xFE, 0x03, 0xCF }, + { 0x00, 0x00 }, + {}, + {}, + {} +}; + +const char * strBlogPhy[BLOG_MAXPHY] = +{ + BLOG_ARY_INIT(BLOG_NOPHY) + BLOG_ARY_INIT(BLOG_XTMPHY) + BLOG_ARY_INIT(BLOG_ENETPHY) + BLOG_ARY_INIT(BLOG_GPONPHY) + BLOG_ARY_INIT(BLOG_EPONPHY) + BLOG_ARY_INIT(BLOG_USBPHY) + BLOG_ARY_INIT(BLOG_WLANPHY) + BLOG_ARY_INIT(BLOG_MOCAPHY) + BLOG_ARY_INIT(BLOG_EXTRA1PHY) + BLOG_ARY_INIT(BLOG_LTEPHY) + BLOG_ARY_INIT(BLOG_SIDPHY) + BLOG_ARY_INIT(BLOG_TCP4_LOCALPHY) + BLOG_ARY_INIT(BLOG_SPU_DS) + BLOG_ARY_INIT(BLOG_SPU_US) + BLOG_ARY_INIT(BLOG_NETXLPHY) + BLOG_ARY_INIT(BLOG_SPDTST) +}; + +const char * strIpctDir[] = { /* in reference to enum ip_conntrack_dir */ + BLOG_DECL(DIR_ORIG) + BLOG_DECL(DIR_RPLY) + BLOG_DECL(DIR_UNKN) +}; + +#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) +const char * strIpctStatus[] = /* in reference to enum ip_conntrack_status */ +{ + BLOG_ARY_INIT(IPS_EXPECTED_BIT) + BLOG_ARY_INIT(IPS_SEEN_REPLY_BIT) + BLOG_ARY_INIT(IPS_ASSURED_BIT) + BLOG_ARY_INIT(IPS_CONFIRMED_BIT) + BLOG_ARY_INIT(IPS_SRC_NAT_BIT) + BLOG_ARY_INIT(IPS_DST_NAT_BIT) + BLOG_ARY_INIT(IPS_SEQ_ADJUST_BIT) + BLOG_ARY_INIT(IPS_SRC_NAT_DONE_BIT) + BLOG_ARY_INIT(IPS_DST_NAT_DONE_BIT) + BLOG_ARY_INIT(IPS_DYING_BIT) + BLOG_ARY_INIT(IPS_FIXED_TIMEOUT_BIT) + BLOG_ARY_INIT(IPS_TEMPLATE_BIT) + BLOG_ARY_INIT(IPS_UNTRACKED_BIT) + BLOG_ARY_INIT(IPS_HELPER_BIT) +#if defined(CONFIG_BCM_KF_NETFILTER) + BLOG_ARY_INIT(IPS_IQOS_BIT) +#endif + BLOG_ARY_INIT(IPS_BLOG_BIT) +}; +#endif + +const char * strBlogTos[] = +{ + BLOG_ARY_INIT(BLOG_TOS_FIXED) + BLOG_ARY_INIT(BLOG_TOS_INHERIT) +}; + + +/* + *------------------------------------------------------------------------------ + * Default Rx and Tx hooks. + * FIXME: Group these hooks into a structure and change blog_bind to use + * a structure. + *------------------------------------------------------------------------------ + */ +static BlogDevRxHook_t blog_rx_hook_g = (BlogDevRxHook_t)NULL; +static BlogDevTxHook_t blog_tx_hook_g = (BlogDevTxHook_t)NULL; +static BlogNotifyHook_t blog_xx_hook_g = (BlogNotifyHook_t)NULL; +static BlogQueryHook_t blog_qr_hook_g = (BlogQueryHook_t)NULL; +static BlogScHook_t blog_sc_hook_g[BlogClient_MAX] = { (BlogScHook_t)NULL }; +static BlogSdHook_t blog_sd_hook_g[BlogClient_MAX] = { (BlogSdHook_t)NULL }; +static BlogPaHook_t blog_pa_hook_g = (BlogPaHook_t)NULL; + +#if defined(CONFIG_BCM_KF_WL) +void (*wl_pktc_del_hook)(unsigned long addr, + struct net_device * net_device) = NULL; +void (*dhd_pktc_del_hook)(unsigned long addr, + struct net_device * net_device) = NULL; +EXPORT_SYMBOL(wl_pktc_del_hook); +EXPORT_SYMBOL(dhd_pktc_del_hook); +#endif + +const char *str_blog_skip_reason[blog_skip_reason_max] = +{ + BLOG_ARY_INIT(blog_skip_reason_unknown) /* unknown or customer defined */ + BLOG_ARY_INIT(blog_skip_reason_br_flood) + BLOG_ARY_INIT(blog_skip_reason_ct_tcp_state_not_est) + BLOG_ARY_INIT(blog_skip_reason_ct_tcp_state_ignore) + BLOG_ARY_INIT(blog_skip_reason_ct_status_donot_blog) + BLOG_ARY_INIT(blog_skip_reason_nf_xt_skiplog) + BLOG_ARY_INIT(blog_skip_reason_nf_ebt_skiplog) + BLOG_ARY_INIT(blog_skip_reason_scrub_pkt) + BLOG_ARY_INIT(blog_skip_reason_sch_htb) + BLOG_ARY_INIT(blog_skip_reason_sch_dsmark) + BLOG_ARY_INIT(blog_skip_reason_unknown_proto) + BLOG_ARY_INIT(blog_skip_reason_unknown_proto_ah4) + BLOG_ARY_INIT(blog_skip_reason_unknown_proto_ah6) + BLOG_ARY_INIT(blog_skip_reason_unknown_proto_esp6) + BLOG_ARY_INIT(blog_skip_reason_esp4_crypto_algo) + BLOG_ARY_INIT(blog_skip_reason_esp4_spu_disabled) + BLOG_ARY_INIT(blog_skip_reason_spudd_check_failure) + BLOG_ARY_INIT(blog_skip_reason_dpi) + BLOG_ARY_INIT(blog_skip_reason_bond) + BLOG_ARY_INIT(blog_skip_reason_map_tcp) + BLOG_ARY_INIT(blog_skip_reason_blog) + BLOG_ARY_INIT(blog_skip_reason_l2_local_termination) + BLOG_ARY_INIT(blog_skip_reason_mega_multi_output_ports) + BLOG_ARY_INIT(blog_skip_reason_mega_attr_mismatch) + BLOG_ARY_INIT(blog_skip_reason_mega_field_mismatch) +}; + +const char *str_blog_free_reason[blog_free_reason_max] = +{ + BLOG_ARY_INIT(blog_free_reason_unknown) /* unknown or customer defined */ + BLOG_ARY_INIT(blog_free_reason_blog_emit) + BLOG_ARY_INIT(blog_free_reason_blog_iq_prio) + BLOG_ARY_INIT(blog_free_reason_kfree) + BLOG_ARY_INIT(blog_free_reason_ipmr_local) +}; + +blog_ctx_t blog_ctx_g, *blog_ctx_p = &blog_ctx_g; +EXPORT_SYMBOL(blog_ctx_p); + +/* ***** Blog Device Interface statistics related functions **** START *** */ +/* + *------------------------------------------------------------------------------ + * Function Name: blog_fold_bstats_stats64 + * Description : Helper function to fold BlogStats_t into stats64 structure. + * Returns : void + *------------------------------------------------------------------------------ + */ +static void blog_fold_bstats_stats64(struct rtnl_link_stats64 *stats64_p, + const BlogStats_t * const bStats_p) +{ + stats64_p->rx_packets += bStats_p->rx_packets; + stats64_p->tx_packets += bStats_p->tx_packets; + stats64_p->rx_bytes += bStats_p->rx_bytes; + stats64_p->tx_bytes += bStats_p->tx_bytes; + stats64_p->multicast += bStats_p->multicast; +#if defined(CONFIG_BCM_KF_EXTSTATS) + stats64_p->tx_multicast_packets += bStats_p->tx_multicast_packets; + stats64_p->rx_multicast_bytes += bStats_p->rx_multicast_bytes; + stats64_p->tx_multicast_bytes += bStats_p->tx_multicast_bytes; +#endif + return; +} +static inline void blog_exclude_mcast(BlogStats_t * const bStats_p) +{ + /* Rollover ?? */ + bStats_p->rx_packets -= bStats_p->multicast; /* subtract RX multicast pkts */ + bStats_p->rx_bytes -= bStats_p->rx_multicast_bytes; /* subtract RX multicast bytes */ + bStats_p->multicast = 0; /* Zero out RX multicast pkts */ + bStats_p->rx_multicast_bytes = 0; /* Zero out RX multicast bytes */ + + bStats_p->tx_packets -= bStats_p->tx_multicast_packets; /* subtract TX multicast pkts */ + bStats_p->tx_bytes -= bStats_p->tx_multicast_bytes; /* subtract TX multicast bytes */ + bStats_p->tx_multicast_packets = 0; /* Zero out TX multicast pkts */ + bStats_p->tx_multicast_bytes = 0; /* Zero out TX multicast bytes */ +} +static inline void blog_exclude_ucast(BlogStats_t * const bStats_p) +{ + bStats_p->rx_packets = bStats_p->multicast; /* only count RX multicast pkt */ + bStats_p->rx_bytes = bStats_p->rx_multicast_bytes; /* only count RX multicast bytes */ + bStats_p->tx_packets = bStats_p->tx_multicast_packets; /* only count TX multicast pkt */ + bStats_p->tx_bytes = bStats_p->tx_multicast_bytes; /* only count TX multicast bytes */ +} +/* + *------------------------------------------------------------------------------ + * Function Name: blog_get_dev_specific_stats + * Description : Helper function to nullify stats that device is not interested in. + * Returns : void + *------------------------------------------------------------------------------ + */ +static void blog_get_dev_specific_stats(BlogStats_t * const bStats_sw_p, + BlogStats_t * const bStats_hw_p, + unsigned int stats_flags) +{ + if ( !(stats_flags & BLOG_DEV_STAT_FLAG_INCLUDE_SW_MC) ) { + blog_exclude_mcast(bStats_sw_p); + } + /* NOTE : Order of Multicast followed by unicast is important */ + if ( !(stats_flags & BLOG_DEV_STAT_FLAG_INCLUDE_SW_UC) ) { + blog_exclude_ucast(bStats_sw_p); + } + if ( !(stats_flags & BLOG_DEV_STAT_FLAG_INCLUDE_HW_MC) ) { + blog_exclude_mcast(bStats_hw_p); + } + /* NOTE : Order of Multicast followed by unicast is important */ + if ( !(stats_flags & BLOG_DEV_STAT_FLAG_INCLUDE_HW_UC) ) { + blog_exclude_ucast(bStats_hw_p); + } + return; +} +/* + *------------------------------------------------------------------------------ + * Function Name: blog_fold_stats + * Description : Helper function to fold one BlogStats_t into another. + * Returns : void + *------------------------------------------------------------------------------ + */ +static void blog_fold_stats(BlogStats_t * const d, + const BlogStats_t * const s) +{ + /* Assumption - all the variables of BlogStats_t are of same size */ + int i; + int n = sizeof(BlogStats_t) / sizeof(uint64_t); + const uint64_t *const src = (const uint64_t *const)s; + uint64_t *dst = (uint64_t *)d; + + BUILD_BUG_ON(sizeof(uint64_t) != sizeof(d->rx_packets)); + + for (i = 0; i < n; i++) + dst[i] += src[i]; + return; +} +/* + *------------------------------------------------------------------------------ + * Function Name: blog_put_dev_stats + * Description : Function to accumulate stats from flow-cache. + * Returns : void + *------------------------------------------------------------------------------ + */ +static void blog_put_dev_stats(struct net_device *dev_p, + BlogStats_t * const bStats_sw_p, + BlogStats_t * const bStats_hw_p) +{ + if ( dev_p && (dev_p->blog_stats_flags & BLOG_DEV_STAT_FLAG_INCLUDE_ALL) ) { /* */ + blog_get_dev_specific_stats(bStats_sw_p, bStats_hw_p, dev_p->blog_stats_flags); + blog_fold_stats(&dev_p->blog_stats, bStats_sw_p); /* Fold SW stats */ + blog_fold_stats(&dev_p->blog_stats, bStats_hw_p); /* Fold HW stats */ + } + return; +} + +/* + *------------------------------------------------------------------------------ + * Function Name: blog_notify_callback + * Description : callback function used for fetch_netif_stats + *------------------------------------------------------------------------------ + */ +static void blog_notify_callback(void *data) +{ + BLOG_WAKEUP_WORKER_THREAD((wq_info_t *)data, BLOG_WORK_AVAIL); +} + +int blog_preemptible_task(void) +{ + int preempt_offset = 0; + int nested = (preempt_count() & ~PREEMPT_ACTIVE) + rcu_preempt_depth(); + + /* Is it preemptible? */ + if (preemptible() && (nested == preempt_offset)) + return 1; + else + return 0; +} + +/* + *------------------------------------------------------------------------------ + * Function Name: blog_notify_async_wait + * Description : Calls blog_notify_async() and then waits for completion of + * event. No callback function needed from the caller, this + * function uses its own callback function. + * Note : If called from NOT preempt-safe context, this function will + * change blog_notify_async() to blog_notify(), which means the + * event is processed synchronously. + *------------------------------------------------------------------------------ + */ +void blog_notify_async_wait(BlogNotify_t event, void *net_p, + unsigned long param1, unsigned long param2) +{ + int ret_val; + wq_info_t blog_notify_thread; /* blog_notify_async() caller thread */ + blog_notify_thread.work_avail = 0; + init_waitqueue_head(&blog_notify_thread.wqh); + + if (blog_preemptible_task()) + { + blog_lock(); + ret_val = blog_notify_async(event, net_p, param1, param2, + blog_notify_callback, &blog_notify_thread); + blog_unlock(); + if (ret_val) + { + do + { + wait_event_interruptible(blog_notify_thread.wqh, + blog_notify_thread.work_avail); + } while (!(blog_notify_thread.work_avail & BLOG_WORK_AVAIL)); + blog_notify_thread.work_avail &= (~BLOG_WORK_AVAIL); + } + } + else + { + /* event called from NOT Preempt Safe context!!! + * switching to blog_notify() */ + blog_lock(); + blog_notify(event, net_p, param1, param2); + blog_unlock(); + } +} + +/* + *------------------------------------------------------------------------------ + * Function Name: blog_get_dev_running_stats + * Description : Exported Accessor function to fetch running stats from flow-cache. + * Returns : void + *------------------------------------------------------------------------------ + */ +static void _blog_get_dev_running_stats(struct net_device *dev_p, BlogStats_t * const bStats_p) +{ + if ( dev_p && (dev_p->blog_stats_flags & BLOG_DEV_STAT_FLAG_INCLUDE_ALL) ) { /* */ + BlogStats_t sw_bStats = {0}; + BlogStats_t hw_bStats = {0}; + + blog_notify_async_wait(FETCH_NETIF_STATS, (void*)dev_p, + (unsigned long)&sw_bStats, (unsigned long)&hw_bStats); + blog_get_dev_specific_stats(&sw_bStats, &hw_bStats, dev_p->blog_stats_flags); + blog_fold_stats(bStats_p, &sw_bStats); /* Fold SW stats */ + blog_fold_stats(bStats_p, &hw_bStats); /* Fold HW stats */ + } + return; +} +void blog_get_dev_running_stats(void *dev_p, void * const bStats_p) +{ + _blog_get_dev_running_stats(dev_p, bStats_p); +} +EXPORT_SYMBOL(blog_get_dev_running_stats); + +/* Remove once WLAN falls in place */ +void blog_get_dev_running_stats_wlan(void *dev_p, void * const bStats_p) +{ + if ( dev_p ) { /* */ + BlogStats_t sw_bStats = {0}; + BlogStats_t hw_bStats = {0}; + + blog_notify_async_wait(FETCH_NETIF_STATS, (void*)dev_p, + (unsigned long)&sw_bStats, (unsigned long)&hw_bStats); + blog_fold_stats((BlogStats_t*)bStats_p, &sw_bStats); /* Fold SW stats */ + blog_fold_stats((BlogStats_t*)bStats_p, &hw_bStats); /* Fold HW stats */ + } +} +EXPORT_SYMBOL(blog_get_dev_running_stats_wlan); +/* + *------------------------------------------------------------------------------ + * Function Name: blog_add_dev_accelerated_stats + * Description : Exported Accessor function to add acclerated stats into dev stats64. + * Returns : 0 - success + *------------------------------------------------------------------------------ + */ +static void _blog_add_dev_accelerated_stats(struct net_device *dev_p, struct rtnl_link_stats64 *stats64_p) +{ + if ( dev_p && (dev_p->blog_stats_flags & BLOG_DEV_STAT_FLAG_INCLUDE_ALL) ) { /* */ + BlogStats_t bStats = {0}; + blog_get_dev_stats(dev_p, &bStats); + blog_fold_bstats_stats64(stats64_p, &bStats); + } +} +void blog_add_dev_accelerated_stats(void *dev_p, void *stats64_p) +{ + _blog_add_dev_accelerated_stats(dev_p,stats64_p); +} +EXPORT_SYMBOL(blog_add_dev_accelerated_stats); +/* + *------------------------------------------------------------------------------ + * Function Name: blog_get_dev_stats + * Description : Exported Accessor function to get accelerated blog stats. + * Returns : 0 - success + *------------------------------------------------------------------------------ + */ +static void _blog_get_dev_stats(struct net_device *dev_p, BlogStats_t * bStats_p) +{ + if ( dev_p && (dev_p->blog_stats_flags & BLOG_DEV_STAT_FLAG_INCLUDE_ALL) ) { /* */ + blog_get_dev_running_stats(dev_p, bStats_p); + blog_fold_stats(bStats_p, &dev_p->blog_stats); + } +} +void blog_get_dev_stats(void *dev_p, void *bStats_p) +{ + _blog_get_dev_stats(dev_p, bStats_p); +} +EXPORT_SYMBOL(blog_get_dev_stats); + +/* + *------------------------------------------------------------------------------ + * Function Name: blog_clr_dev_stats + * Description : Exported Accessor function to clear accelerated blog stats. + * Returns : 0 - success + *------------------------------------------------------------------------------ + */ +void _blog_clr_dev_stats(struct net_device *dev_p) +{ + blog_notify_async_wait(CLEAR_NETIF_STATS, (void*)dev_p, (unsigned long)NULL, + (unsigned long)NULL); + /* Now clear the accumulated stats; + * As part of clear, flow-cache flushes the current stats in dev->blog_stats */ + memset(&dev_p->blog_stats, 0, sizeof(dev_p->blog_stats)); +} +void blog_clr_dev_stats(void *dev_p) +{ + _blog_clr_dev_stats(dev_p); +} +EXPORT_SYMBOL(blog_clr_dev_stats); + +/* ***** Blog Device Interface statistics related functions **** END *** */ + + + +/* + *------------------------------------------------------------------------------ + * Blog_t Free Pool Management. + * The free pool of Blog_t is self growing (extends upto an engineered + * value). Could have used a kernel slab cache. + *------------------------------------------------------------------------------ + */ + +/* Global pointer to the free pool of Blog_t */ +static Blog_t * blog_list_gp = BLOG_NULL; + +static int blog_extends = 0; /* Extension of Pool on depletion */ +#if defined(CC_BLOG_SUPPORT_DEBUG) +static int blog_cnt_free = 0; /* Number of Blog_t free */ +static int blog_cnt_used = 0; /* Number of in use Blog_t */ +static int blog_cnt_hwm = 0; /* In use high water mark for engineering */ +static int blog_cnt_fails = 0; +#endif + +/* + *------------------------------------------------------------------------------ + * Function : blog_extend + * Description: Create a pool of Blog_t objects. When a pool is exhausted + * this function may be invoked to extend the pool. The pool is + * identified by a global pointer, blog_list_gp. All objects in + * the pool chained together in a single linked list. + * Parameters : + * num : Number of Blog_t objects to be allocated. + * Returns : Number of Blog_t objects allocated in pool. + * + * CAUTION: blog_extend must be called with blog_pool_lock_g acquired. + *------------------------------------------------------------------------------ + */ +uint32_t blog_extend( uint32_t num ) +{ + register int i; + register Blog_t * list_p; + + blog_print( "%u", num ); + + list_p = (Blog_t *) kmalloc( num * sizeof(Blog_t), GFP_ATOMIC); + if ( list_p == BLOG_NULL ) + { + blog_ctx_p->blog_mem_fails++; +#if defined(CC_BLOG_SUPPORT_DEBUG) + blog_cnt_fails++; +#endif + blog_print( "WARNING: Failure to initialize %d Blog_t", num ); + return 0; + } + + /* memset( (void *)list_p, 0, (sizeof(Blog_t) * num ); */ + for ( i = 0; i < num; i++ ) + list_p[i].blog_p = &list_p[i+1]; + + blog_extends++; + blog_ctx_p->blog_extends++; + + blog_ctx_p->blog_total += num; + blog_ctx_p->blog_avail += num; + BLOG_DBG( blog_cnt_free += num; ); + list_p[num-1].blog_p = blog_list_gp; /* chain last Blog_t object */ + blog_list_gp = list_p; /* Head of list */ + + return num; +} + +/* + *------------------------------------------------------------------------------ + * Function : _fast_memset + * Description : sets the memory starting from dst_p to val. + * Note : dst_p should be atleast 32-bit aligned, and len is in bytes + *------------------------------------------------------------------------------ + */ +static inline +void _fast_memset( uint32_t *dst_p, uint32_t val, uint32_t len ) +{ + int num_words = len >> 2; + int num_bytes = len & 3; + uint8_t *byte_p; + int i; + + for( i=0; i < num_words; i++ ) + *dst_p++ = val; + + if (num_bytes) + { + byte_p = (uint8_t *) dst_p; + for( i=0; i < num_bytes; i++ ) + *byte_p++ = val; + } +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_clr + * Description : Clear the data of a Blog_t + * Need not be protected by blog_pool_lock_g + *------------------------------------------------------------------------------ + */ +static inline void blog_clr( Blog_t * blog_p ) +{ + blog_assertv( ((blog_p != BLOG_NULL) && (_IS_BPTR_(blog_p))) ); + BLOG_DBG( memset( (void*)blog_p, 0, sizeof(Blog_t) ); ); + + _fast_memset( (void*)blog_p, 0, sizeof(Blog_t) ); + + /* clear phyHdr, count, bmap, and channel */ + blog_p->minMtu = BLOG_ETH_MTU_LEN; + blog_p->vtag[0] = 0xFFFFFFFF; + blog_p->vtag[1] = 0xFFFFFFFF; + blog_p->wl = 0; /* Clear the WL-METADATA */ + blog_p->dpi_queue = ~0; + blog_p->spdtst = 0; + + blog_print( "blog<%p>", blog_p ); +} + +void blog_ct_get(Blog_t * blog_p) +{ +#if defined(BLOG_NF_CONNTRACK) + int i; + for(i=0; ict_p[i]) + nf_conntrack_get(&((struct nf_conn *)blog_p->ct_p[i])->ct_general); + } + blog_p->nf_ct_skip_ref_dec=0; +#endif +} +EXPORT_SYMBOL(blog_ct_get); + +void blog_ct_put(Blog_t * blog_p) +{ +#if defined(BLOG_NF_CONNTRACK) + if(!blog_p->nf_ct_skip_ref_dec) + { + int i; + + blog_p->nf_ct_skip_ref_dec=1; + /*decrement refcnt of any associated conntrack's */ + for(i=0; ict_p[i]) + { + nf_conntrack_put(&((struct nf_conn *)blog_p->ct_p[i])->ct_general); + blog_p->ct_p[i] = NULL; + } + } + + } +#endif +} +EXPORT_SYMBOL(blog_ct_put); +/* + *------------------------------------------------------------------------------ + * Function : blog_get + * Description : Allocate a Blog_t from the free list + * Returns : Pointer to an Blog_t or NULL, on depletion. + *------------------------------------------------------------------------------ + */ +Blog_t * blog_get( void ) +{ + register Blog_t * blog_p; + unsigned long lock_flags; + + BLOG_POOL_LOCK(); /* DO NOT USE blog_assertr() until BLOG_POOL_UNLOCK() */ + + if ( blog_list_gp == BLOG_NULL ) + { +#ifdef CC_BLOG_SUPPORT_EXTEND + if ( (blog_extends >= BLOG_EXTEND_MAX_ENGG)/* Try extending free pool */ + || (blog_extend( BLOG_EXTEND_SIZE_ENGG ) != BLOG_EXTEND_SIZE_ENGG)) + { + blog_ctx_p->blog_extend_fails++; + blog_print( "WARNING: free list exhausted" ); + } +#else + if ( blog_extend( BLOG_EXTEND_SIZE_ENGG ) == 0 ) + { + blog_print( "WARNING: out of memory" ); + } +#endif + if (blog_list_gp == BLOG_NULL) + { + blog_p = BLOG_NULL; + BLOG_POOL_UNLOCK(); /* May use blog_assertr() now onwards */ + goto blog_get_return; + } + } + + blog_ctx_p->info_stats.blog_get++;; + blog_ctx_p->blog_avail--; + if ( unlikely(blog_ctx_p->info_stats.blog_min_avail > blog_ctx_p->blog_avail) ) + blog_ctx_p->info_stats.blog_min_avail = blog_ctx_p->blog_avail; + + BLOG_DBG( + blog_cnt_free--; + if ( ++blog_cnt_used > blog_cnt_hwm ) + blog_cnt_hwm = blog_cnt_used; + ); + + blog_p = blog_list_gp; + blog_list_gp = blog_list_gp->blog_p; + + BLOG_POOL_UNLOCK(); /* May use blog_assertr() now onwards */ + + blog_clr( blog_p ); /* quickly clear the contents */ + +blog_get_return: + + blog_print( "blog<%p>", blog_p ); + + return blog_p; +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_put + * Description : Release a Blog_t back into the free pool + * Parameters : + * blog_p : Pointer to a non-null Blog_t to be freed. + *------------------------------------------------------------------------------ + */ +void blog_put( Blog_t * blog_p ) +{ + unsigned long lock_flags; + + blog_assertv( ((blog_p != BLOG_NULL) && (_IS_BPTR_(blog_p))) ); + + blog_ct_put(blog_p); + +#if defined(CONFIG_XFRM) + if (TX_ESP(blog_p)) + { + dst_release(blog_p->esptx.dst_p); + secpath_put(blog_p->esptx.secPath_p); + } +#endif + + blog_clr( blog_p ); + + BLOG_POOL_LOCK(); /* DO NOT USE blog_assertv() until BLOG_POOL_UNLOCK() */ + + blog_ctx_p->blog_avail++; + BLOG_DBG( blog_cnt_used--; blog_cnt_free++; ); + blog_p->blog_p = blog_list_gp; /* clear pointer to skb_p */ + blog_list_gp = blog_p; /* link into free pool */ + + BLOG_POOL_UNLOCK();/* May use blog_assertv() now onwards */ + + blog_ctx_p->info_stats.blog_put++;; + blog_print( "blog<%p>", blog_p ); +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_skb + * Description : Allocate and associate a Blog_t with an sk_buff. + * Parameters : + * skb_p : pointer to a non-null sk_buff + * Returns : A Blog_t object or NULL, + *------------------------------------------------------------------------------ + */ +Blog_t * blog_skb( struct sk_buff * skb_p ) +{ + blog_assertr( (skb_p != (struct sk_buff *)NULL), BLOG_NULL ); + blog_assertr( (!_IS_BPTR_(skb_p->blog_p)), BLOG_NULL ); /* avoid leak */ + + skb_p->blog_p = blog_get(); /* Allocate and associate with sk_buff */ + + blog_print( "skb<%p> blog<%p>", skb_p, skb_p->blog_p ); + + /* CAUTION: blog_p does not point back to the skb, do it explicitly */ + return skb_p->blog_p; /* May be null */ +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_fkb + * Description : Allocate and associate a Blog_t with an fkb. + * Parameters : + * fkb_p : pointer to a non-null FkBuff_t + * Returns : A Blog_t object or NULL, + *------------------------------------------------------------------------------ + */ +Blog_t * blog_fkb( struct fkbuff * fkb_p ) +{ + uint32_t in_skb_tag; + blog_assertr( (fkb_p != (FkBuff_t *)NULL), BLOG_NULL ); + blog_assertr( (!_IS_BPTR_(fkb_p->blog_p)), BLOG_NULL ); /* avoid leak */ + + in_skb_tag = _is_in_skb_tag_( fkb_p->ptr, fkb_p->flags ); + + fkb_p->blog_p = blog_get(); /* Allocate and associate with fkb */ + + if ( fkb_p->blog_p != BLOG_NULL ) /* Move in_skb_tag to blog rx info */ + fkb_p->blog_p->rx.fkbInSkb = in_skb_tag; + + blog_print( "fkb<%p> blog<%p> in_skb_tag<%u>", + fkb_p, fkb_p->blog_p, in_skb_tag ); + return fkb_p->blog_p; /* May be null */ +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_snull + * Description : Dis-associate a sk_buff with any Blog_t + * Parameters : + * skb_p : Pointer to a non-null sk_buff + * Returns : Previous Blog_t associated with sk_buff + *------------------------------------------------------------------------------ + */ +inline Blog_t * _blog_snull( struct sk_buff * skb_p ) +{ + register Blog_t * blog_p; + blog_p = skb_p->blog_p; + skb_p->blog_p = BLOG_NULL; + return blog_p; +} + +Blog_t * blog_snull( struct sk_buff * skb_p ) +{ + blog_assertr( (skb_p != (struct sk_buff *)NULL), BLOG_NULL ); + blog_print( "skb<%p> blog<%p>", skb_p, skb_p->blog_p ); + return _blog_snull( skb_p ); +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_fnull + * Description : Dis-associate a fkbuff with any Blog_t + * Parameters : + * fkb_p : Pointer to a non-null fkbuff + * Returns : Previous Blog_t associated with fkbuff + *------------------------------------------------------------------------------ + */ +inline Blog_t * _blog_fnull( struct fkbuff * fkb_p ) +{ + register Blog_t * blog_p; + blog_p = fkb_p->blog_p; + fkb_p->blog_p = BLOG_NULL; + return blog_p; +} + +Blog_t * blog_fnull( struct fkbuff * fkb_p ) +{ + blog_assertr( (fkb_p != (struct fkbuff *)NULL), BLOG_NULL ); + blog_print( "fkb<%p> blogp<%p>", fkb_p,fkb_p->blog_p ); + return _blog_fnull( fkb_p ); +} + +/* + *------------------------------------------------------------------------------ + * Function : _blog_free + * Description : Free any Blog_t associated with a sk_buff + * Parameters : + * skb_p : Pointer to a non-null sk_buff + *------------------------------------------------------------------------------ + */ +inline void _blog_free( struct sk_buff * skb_p ) +{ + register Blog_t * blog_p; + blog_p = _blog_snull( skb_p ); /* Dis-associate Blog_t from skb_p */ + if ( likely(blog_p != BLOG_NULL) ) + blog_put( blog_p ); /* Recycle blog_p into free list */ +} + + +/* + *------------------------------------------------------------------------------ + * Function : blog_free + * Description : Free any Blog_t associated with a sk_buff + * Parameters : + * skb_p : Pointer to a non-null sk_buff + * reason : The reason/location why blog_free was called + *------------------------------------------------------------------------------ + */ +void blog_free( struct sk_buff * skb_p, blog_skip_reason_t reason ) +{ + blog_assertv( (skb_p != (struct sk_buff *)NULL) ); + + if ( likely(skb_p->blog_p != BLOG_NULL) ) + blog_ctx_p->blog_free_stats_table[reason]++; + + BLOG_DBG( + if ( skb_p->blog_p != BLOG_NULL ) + blog_print( "skb<%p> blog<%p> [<%p>]", + skb_p, skb_p->blog_p, + __builtin_return_address(0) ); ); + _blog_free( skb_p ); +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_skip + * Description : Disable further tracing of sk_buff by freeing associated + * Blog_t (if any) + * Parameters : + * skb_p : Pointer to a sk_buff + * reason : The reason/location why blog_skip was called + *------------------------------------------------------------------------------ + */ +void blog_skip( struct sk_buff * skb_p, blog_skip_reason_t reason ) +{ + blog_print( "skb<%p> [<%p>]", + skb_p, __builtin_return_address(0) ); + + if ( likely(skb_p->blog_p != BLOG_NULL) ) + blog_ctx_p->blog_skip_stats_table[reason]++; + + blog_assertv( (skb_p != (struct sk_buff *)NULL) ); + _blog_free( skb_p ); +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_xfer + * Description : Transfer ownership of a Blog_t between two sk_buff(s) + * Parameters : + * skb_p : New owner of Blog_t object + * prev_p : Former owner of Blog_t object + *------------------------------------------------------------------------------ + */ +void blog_xfer( struct sk_buff * skb_p, const struct sk_buff * prev_p ) +{ + Blog_t * blog_p; + struct sk_buff * mod_prev_p; + blog_assertv( (prev_p != (struct sk_buff *)NULL) ); + blog_assertv( (skb_p != (struct sk_buff *)NULL) ); + + mod_prev_p = (struct sk_buff *) prev_p; /* const removal without warning */ + blog_p = _blog_snull( mod_prev_p ); + skb_p->blog_p = blog_p; + + if ( likely(blog_p != BLOG_NULL) ) + { + blog_ctx_p->info_stats.blog_xfer++; + blog_print( "skb<%p> to new<%p> blog<%p> [<%p>]", + prev_p, skb_p, blog_p, + __builtin_return_address(0) ); + blog_assertv( (_IS_BPTR_(blog_p)) ); + blog_p->skb_p = skb_p; + } +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_clone + * Description : Duplicate a Blog_t for another sk_buff + * Parameters : + * skb_p : New owner of cloned Blog_t object + * prev_p : Blog_t object to be cloned + *------------------------------------------------------------------------------ + */ +void blog_clone( struct sk_buff * skb_p, const struct blog_t * prev_p ) +{ + blog_assertv( (skb_p != (struct sk_buff *)NULL) ); + + if ( likely(prev_p != BLOG_NULL) ) + { + Blog_t * blog_p; + int i; + + blog_assertv( (_IS_BPTR_(prev_p)) ); + + skb_p->blog_p = blog_get(); /* Allocate and associate with skb */ + blog_p = skb_p->blog_p; + + blog_print( "orig blog<%p> new skb<%p> blog<%p> [<%p>]", + prev_p, skb_p, blog_p, + __builtin_return_address(0) ); + + if ( likely(blog_p != BLOG_NULL) ) + { + blog_ctx_p->info_stats.blog_clone++;; + blog_p->skb_p = skb_p; +#define CPY(x) blog_p->x = prev_p->x + CPY(key.match); + CPY(hash); + CPY(mark); + CPY(priority); + CPY(rx); + CPY(rx_dev_p); + CPY(vtag[0]); + CPY(vtag[1]); + CPY(vtag_num); + CPY(tupleV6); + CPY(tuple_offset); + CPY(mega_p); + CPY(map_p); + + if(!prev_p->nf_ct_skip_ref_dec) + { + for(i=0; ict_p[i] = prev_p->ct_p[i]; + } + + /*increment refcnt of ct's */ + blog_ct_get(blog_p); + } + + for(i=0; ivirt_dev_p[i] ) + { + blog_p->virt_dev_p[i] = prev_p->virt_dev_p[i]; + blog_p->delta[i] = prev_p->delta[i]; + } + else + break; + } + blog_p->tx.word = 0; + } + } +#undef CPY +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_copy + * Description : Copy a Blog_t object another blog object. + * Parameters : + * new_p : Blog_t object to be filled in + * prev_p : Blog_t object with the data information + *------------------------------------------------------------------------------ + */ +void blog_copy(struct blog_t * new_p, const struct blog_t * prev_p) +{ + blog_assertv( (new_p != BLOG_NULL) ); + blog_print( "new_p<%p> prev_p<%p>", new_p, prev_p ); + + if ( likely(prev_p != BLOG_NULL) ) + { + blog_ctx_p->info_stats.blog_copy++; + memcpy( new_p, prev_p, sizeof(Blog_t) ); + + if(!prev_p->nf_ct_skip_ref_dec) + { + /*increment refcnt of ct's */ + blog_ct_get(new_p); + } + } +} + + +/* + *------------------------------------------------------------------------------ + * Function : blog_iq + * Description : get the iq prio from blog + * Parameters : + * skb_p : Pointer to a sk_buff + *------------------------------------------------------------------------------ + */ +int blog_iq( const struct sk_buff * skb_p ) +{ + int prio = BLOG_IQ_PRIO_LOW; + + blog_print( "skb<%p> [<%p>]", + skb_p, __builtin_return_address(0) ); + + if (skb_p) + { + Blog_t *blog_p = skb_p->blog_p; + + if (blog_p) + prio = blog_p->iq_prio; + } + return prio; +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_fc_enabled + * Description : get the enabled/disabled status of flow cache + * Parameters : + * none : + *------------------------------------------------------------------------------ + */ +int blog_fc_enabled( void ) +{ + if ( likely(blog_rx_hook_g != (BlogDevRxHook_t)NULL) ) + return 1; + else + return 0; +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_gre_tunnel_accelerated + * Description : get the accelerated status of GRE tunnels + * Parameters : + * none : + *------------------------------------------------------------------------------ + */ + +inline int blog_gre_tunnel_accelerated( void ) +{ + return blog_gre_tunnel_accelerated_g; +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_l2tp_tunnel_accelerated + * Description : get the accelerated status of L2TP tunnels + * Parameters : + * none : + *------------------------------------------------------------------------------ + */ + +inline int blog_l2tp_tunnel_accelerated( void ) +{ + return blog_l2tp_tunnel_accelerated_g; +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_link + * Description : Associate a network entity with an skb's blog object + * Parameters : + * entity_type : Network entity type + * blog_p : Pointer to a Blog_t + * net_p : Pointer to a network stack entity + * param1 : optional parameter 1 + * param2 : optional parameter 2 + * PreRequisite : acquire blog_lock_g before calling blog_link() + *------------------------------------------------------------------------------ + */ +void blog_link( BlogNetEntity_t entity_type, Blog_t * blog_p, + void * net_p, uint32_t param1, uint32_t param2 ) +{ + if ( unlikely(blog_p == BLOG_NULL) ) + return; + + blog_assertv( (entity_type < BLOG_NET_ENTITY_MAX) ); + blog_assertv( (net_p != (void *)NULL) ); + blog_assertv( (_IS_BPTR_(blog_p)) ); + + blog_print( "link<%s> skb<%p> blog<%p> net<%p> %u %u [<%p>]", + strBlogNetEntity[entity_type], blog_p->skb_p, blog_p, + net_p, param1, param2, __builtin_return_address(0) ); + + switch ( entity_type ) + { + case FLOWTRACK: + { +#if defined(BLOG_NF_CONNTRACK) + uint32_t idx = BLOG_CT_PLD; + + blog_assertv( ((param1 == BLOG_PARAM1_DIR_ORIG) || + (param1 == BLOG_PARAM1_DIR_REPLY)|| + (param2 == BLOG_PARAM2_IPV4) || + (param2 == BLOG_PARAM2_IPV6) || + (param2 == BLOG_PARAM2_GRE_IPV4)) ); + + if ( unlikely(blog_p->rx.multicast) ) + return; + + switch (param2) + { + case BLOG_PARAM2_IPV4: + if (RX_IPV4_DEL(blog_p) && (blog_p->ct_p[BLOG_CT_DEL] == NULL)) + idx = BLOG_CT_DEL; + else if (RX_IPV4(blog_p) && (blog_p->ct_p[BLOG_CT_PLD] == NULL)) + idx = BLOG_CT_PLD; + else if (RX_IPV4(blog_p) +#if defined(CONFIG_BCM_KF_MAP) || defined(CONFIG_BCM_MAP_MODULE) + || blog_p->map_p +#endif + ) + idx = BLOG_CT_PLD; + else if (blog_p->ct_p[BLOG_CT_DEL] == NULL) + idx = BLOG_CT_DEL; + else + blog_print( "invalid param2 %u", param2 ); + break; + + case BLOG_PARAM2_IPV6: + if (RX_IPV6_DEL(blog_p) && (blog_p->ct_p[BLOG_CT_DEL] == NULL)) + idx = BLOG_CT_DEL; + else if (RX_IPV6(blog_p) && (blog_p->ct_p[BLOG_CT_PLD] == NULL)) + idx = BLOG_CT_PLD; + else if (RX_IPV6(blog_p) +#if defined(CONFIG_BCM_KF_MAP) || defined(CONFIG_BCM_MAP_MODULE) + || blog_p->map_p +#endif + ) + idx = BLOG_CT_PLD; + else if (blog_p->ct_p[BLOG_CT_DEL] == NULL) + idx = BLOG_CT_DEL; + else + blog_print( "invalid param2 %u", param2 ); + break; + + case BLOG_PARAM2_GRE_IPV4: + idx = BLOG_CT_DEL; + break; + + case BLOG_PARAM2_L2TP_IPV4: + if (RX_IPV4_DEL(blog_p) && (blog_p->ct_p[BLOG_CT_DEL] == NULL)) + idx = BLOG_CT_DEL; + else if (RX_IPV4(blog_p)) + { + if(blog_p->ct_p[BLOG_CT_PLD] == NULL) + idx = BLOG_CT_PLD; + + if(blog_p->ct_p[BLOG_CT_PLD] != NULL && blog_p->ct_p[BLOG_CT_DEL] == NULL) + idx = BLOG_CT_DEL; + } + break; + default: + blog_print( "unknown param2 %u", param2 ); + return; + } + + if(blog_p->ct_p[idx]) + { + /*ct already exists decrement it's ref count */ + nf_conntrack_put(&((struct nf_conn *)blog_p->ct_p[idx])->ct_general); + printk(KERN_WARNING "blog_link: overwriting ct_p=%px, new_ct=%px at idx=%d\n", + blog_p->ct_p[idx], net_p, idx); + } + + blog_p->ct_p[idx] = net_p; /* Pointer to conntrack */ + /*increment refcount of ct, till flow is learned */ + nf_conntrack_get(&((struct nf_conn *)net_p)->ct_general); + + /* param2 indicates the ct_p belongs to IPv4 or IPv6 */ + blog_p->ct_ver[idx] = (param2 == BLOG_PARAM2_GRE_IPV4) ? + BLOG_PARAM2_IPV4 : param2; + /* + * Save flow direction + */ + if(idx == BLOG_CT_PLD) + blog_p->nf_dir_pld = param1; + else + blog_p->nf_dir_del = param1; + + + blog_print( "idx<%d> ct_p<%p> ct_ver<%d>\n", + idx, blog_p->ct_p[idx], blog_p->ct_ver[idx] ); + +#endif + break; + } + + case BRIDGEFDB: + { + blog_assertv( ((param1 == BLOG_PARAM1_SRCFDB) || + (param1 == BLOG_PARAM1_DSTFDB)) ); + + /* Note:- In bridging mode, dest bridge FDB pointer could be NULL if + * the dest MAC has not been learned yet */ + if ( unlikely(net_p != (void *) NULL) ) + { + uint8_t *mac_p; + struct net_bridge_fdb_entry * fdb_p + = (struct net_bridge_fdb_entry *)net_p; + + if ( param1 == BLOG_PARAM1_SRCFDB) + mac_p = (uint8_t *) &blog_p->src_mac; + else + mac_p = (uint8_t *) &blog_p->dst_mac; + + /* copy the MAC from Bridge FDB to blog */ + memcpy(mac_p, fdb_p->addr.addr, BLOG_ETH_ADDR_LEN); + } + + blog_p->fdb[param1] = net_p; + break; + } + + case MCAST_FDB: + { + blog_p->mc_fdb = net_p; /* Pointer to mc_fdb */ + break; + } + + case IF_DEVICE: /* link virtual interfaces traversed by flow */ + case IF_DEVICE_MCAST: + { + int i; + + blog_assertv( (param1 < BLOG_DIR_MAX) ); + + for (i=0; ivirt_dev_p[i]))); + + if ( blog_p->virt_dev_p[i] == NULL ) + { + blog_p->virt_dev_p[i] = DEVP_APPEND_DIR(net_p, param1); + if (IF_DEVICE_MCAST == entity_type ) + { + blog_p->delta[i] = -(param2 & 0xFF); + } + else + { + blog_p->delta[i] = (param2 - blog_p->tx.pktlen) & 0xFF; + } + break; + } + } + + blog_assertv( (i != MAX_VIRT_DEV) ); + break; + } + + case GRE_TUNL: + { + if (param1 == DIR_RX) + blog_p->rx_tunl_p = net_p; /* Pointer to tunnel */ + else + blog_p->tx_tunl_p = net_p; /* Pointer to tunnel */ + + break; + } + + case TOS_MODE: + { + if (param1 == DIR_RX) + blog_p->tos_mode_ds = param2; + else + blog_p->tos_mode_us = param2; + + break; + } + +#if defined(CONFIG_BCM_KF_MAP) || defined(CONFIG_BCM_MAP_MODULE) + case MAP_TUPLE: + { + blog_assertv( ((param1 == BLOG_PARAM1_MAP_DIR_US) || + (param1 == BLOG_PARAM1_MAP_DIR_DS)) ); + + blog_print( "b4 multicast check" ); + if ( unlikely(blog_p->rx.multicast) ) + return; + + blog_p->map_p = net_p; /* Pointer to MAPT tuple */ + blog_print( "map_p<%p> net<%p>", blog_p->map_p, net_p ); + + break; + } +#endif + +#if defined(CONFIG_BCM_OVS) + case MEGA: + { + blog_p->mega_p = (uint32_t *) net_p; /* Pointer to megaflow */ + blog_print( "mega_p<%p> net<%p>", blog_p->mega_p, net_p ); + break; + } +#endif + + default: + break; + } + return; +} + +blog_notify_evt_enqueue_hook_t blog_notify_evt_enqueue_hook_g = NULL; + +/* + *------------------------------------------------------------------------------ + * Function : blog_bind_notify_evt_enqueue + * Description : Bind the blog notify event enqueue function to be called + *------------------------------------------------------------------------------ + */ +void blog_bind_notify_evt_enqueue( + blog_notify_evt_enqueue_hook_t blog_notify_evt_enqueue_fn ) +{ + printk( "Bind blog_notify_evt_enqueue_fn[<%p>]\n", + blog_notify_evt_enqueue_fn ); + blog_notify_evt_enqueue_hook_g = blog_notify_evt_enqueue_fn; +} + +/* + *------------------------------------------------------------------------------ + * Function Name: blog_notify_evt_enqueue + * Description : Prepare and enqueue event work + *------------------------------------------------------------------------------ + */ +void blog_notify_evt_enqueue(blog_notify_evt_type_t evt_type, void *net_p, + unsigned long param1, unsigned long param2, + blog_notify_async_cb_fn_t notify_cb_fn, void *notify_cb_data_p) +{ + if (blog_notify_evt_enqueue_hook_g) + blog_notify_evt_enqueue_hook_g(evt_type, net_p, param1, param2, + notify_cb_fn, notify_cb_data_p); +} + +/* + *------------------------------------------------------------------------------ + * Function : _blog_notify_async + * Description : Notify a Blog client (xx_hook) of an event. + * Parameters : + * blog_notify_api: blog_notify() or blog_notify_async() + * event : notification + * net_p : Pointer to a network stack entity + * param1 : optional parameter 1 + * param2 : optional parameter 2 + * PreRequisite : acquire blog_lock_g before calling blog_notify/blog_notify_async() + * return value : Caller MUST check the return value. + * : 1 = caller's notify callback function will be called + * : 0 = caller's notify callback function will NOT be called. + * : Situations where fc_evt task is not running. + *------------------------------------------------------------------------------ + */ +static inline int _blog_notify_async(blog_notify_api_t blog_notify_api, + BlogNotify_t event, void *net_p, + unsigned long param1, unsigned long param2, + blog_notify_async_cb_fn_t notify_cb_fn, void *notify_cb_data_p) +{ + int ret_val = 0; + struct net_bridge_fdb_entry * fdb_p; + + blog_assertr( (event < BLOG_NOTIFY_MAX), 0 ); + blog_assertr( (blog_notify_api < BLOG_NOTIFY_API_MAX), 0 ); + blog_assertr( (net_p != (void *)NULL), 0 ); + + blog_print( "blog_notify_api<%d> notify<%s> net_p<0x%p>" + " param1<%lu:0x%lx> param2<%lu:0x%lx>" + " notify_cb_fn<%p> notify_cb_data_p<%p> [<0x%p>]\n", + blog_notify_api, strBlogNotify[event], + net_p, param1, param1, param2, param2, + notify_cb_fn, notify_cb_data_p, + __builtin_return_address(0) ); + + if (event == DESTROY_BRIDGEFDB) + { + fdb_p = (struct net_bridge_fdb_entry *)net_p; + + if (fdb_p->fdb_key == BLOG_FDB_KEY_INVALID) + goto blog_notify_async_bypass; + } + + if (unlikely(blog_xx_hook_g == (BlogNotifyHook_t)NULL) || + unlikely(blog_notify_evt_enqueue_hook_g == (blog_notify_evt_enqueue_hook_t) NULL)) + { /* flow cache is disabled or fc_evt task is not running */ + if ( likely(blog_notify_evt_enqueue_hook_g)) + { + /* fc_evt task is running. we can enqueue the events */ + if (notify_cb_fn) + blog_notify_evt_enqueue(BLOG_NOTIFY_EVT_NONE, NULL, 0, 0, + notify_cb_fn, notify_cb_data_p); + return 1; /* caller can wait, callback function will be invoked */ + } + else + return 0; /* caller should not wait, callback won't be invoked */ + } + + ret_val = blog_xx_hook_g( blog_notify_api, event, net_p, + param1, param2, notify_cb_fn, notify_cb_data_p ); + +blog_notify_async_bypass: + +#if defined(CONFIG_BCM_KF_WL) + /* first flush the flows from Flow-cache/FAP and then clear the BRC_HOT */ + if (event == DESTROY_BRIDGEFDB) { /* for WLAN PKTC use */ + struct net_device * dev_p; + + fdb_p = (struct net_bridge_fdb_entry *)net_p; + dev_p = ((fdb_p->dst == NULL) || (fdb_p->dst->dev == NULL)) ? + NULL : fdb_p->dst->dev; + + if ( likely(wl_pktc_del_hook != NULL) ) + { + wl_pktc_del_hook((unsigned long)(fdb_p->addr.addr), dev_p); + } + + if ( likely(dhd_pktc_del_hook != NULL) ) + { + dhd_pktc_del_hook((unsigned long)(fdb_p->addr.addr), dev_p); + } + } +#endif /* CONFIG_BCM_KF_WL */ + + return ret_val; +} + + +/* + *------------------------------------------------------------------------------ + * Function : blog_notify() + * Description : blog_notify() synchronous API + * Parameters : + * event : notification + * net_p : Pointer to a network stack entity + * param1 : optional parameter 1 + * param2 : optional parameter 2 + * PreRequisite : acquire blog_lock_g before calling blog_notify/blog_notify_async() + * return value : Caller MUST check the return value. + * : 1 = caller's notify callback function will be called + * : 0 = caller's notify callback function will NOT be called. + * : Situations where fc_evt task is not running. + *------------------------------------------------------------------------------ + */ + +void blog_notify(BlogNotify_t event, void *net_p, unsigned long param1, unsigned long param2) +{ + _blog_notify_async(BLOG_NOTIFY_API_SYNC, event, net_p, + param1, param2, NULL, NULL); +} + + +/* + *------------------------------------------------------------------------------ + * Function : blog_notify_async() + * Description : blog_notify_async() asynchronous API + * Parameters : + * event : notification + * net_p : Pointer to a network stack entity + * param1 : optional parameter 1 + * param2 : optional parameter 2 + * PreRequisite : acquire blog_lock_g before calling blog_notify_async() + * return value : Caller MUST check the return value. + * : 1 = means caller may wait, and caller's notify callback + * function will be called, + * : 0 = caller's notify callback function will NOT be called. + * : Situations where fc_evt task is not running. + *------------------------------------------------------------------------------ + */ +int blog_notify_async(BlogNotify_t event, void *net_p, + unsigned long param1, unsigned long param2, + blog_notify_async_cb_fn_t notify_cb_fn, void *notify_cb_data_p) +{ + return (_blog_notify_async(BLOG_NOTIFY_API_ASYNC, event, net_p, + param1, param2, notify_cb_fn, notify_cb_data_p)); +} + + +int blog_flowevent_register_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&blog_flowevent_chain, nb); +} + +int blog_flowevent_unregister_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&blog_flowevent_chain, nb); +} + +static inline void blog_flowevent_notifier_call_chain(unsigned long event, + BlogFlowEventInfo_t *info) +{ + atomic_notifier_call_chain(&blog_flowevent_chain, + event, info); +} + +int blog_ct_get_stats( const void * ct, uint32_t blog_key, uint32_t dir, + BlogFcStats_t * stats) +{ + int ret = -1; + + /*this is a hack to avoid const warning */ + void **net_pp = (void *)&ct; + + blog_lock(); + + /*invoke blog_query only if key is valid */ + + if((blog_key != BLOG_KEY_FC_INVALID) && + (blog_query(QUERY_FLOWTRACK_STATS, *net_pp, blog_key, dir, + (unsigned long) stats) == 0)) + ret = 0; + + /* store the poll time stamp in case users needed it */ + stats->pollTS_ms = jiffies_to_msecs(jiffies); + blog_unlock(); + + return ret; +} + + +blog_ct_put_stats_t blog_ct_put_stats_fn = NULL; + +static inline void blog_ct_put_stats(void * net_p, uint32_t dir, + BlogFcStats_t * stats) +{ + /* store the poll time stamp in case users needed it */ + stats->pollTS_ms = jiffies_to_msecs(jiffies); + + if(blog_ct_put_stats_fn) + blog_ct_put_stats_fn(net_p, dir, stats); +} + +#if defined(CONFIG_BCM_OVS) +int blog_is_ovs_internal_dev(void *net_p) +{ + int ret = 0; + + if (blog_ovs_hooks_g.is_ovs_internal_dev) + ret = blog_ovs_hooks_g.is_ovs_internal_dev(net_p); + + return ret; +} + +unsigned long blog_mega_get_key(void *net_p) +{ + unsigned long blog_key = BLOG_KEY_FC_INVALID; + + if (blog_ovs_hooks_g.mega_get_key) + blog_key = blog_ovs_hooks_g.mega_get_key(net_p); + + return blog_key; +} + +void blog_mega_set_key(void *net_p, unsigned long blog_key) +{ + if (blog_ovs_hooks_g.mega_set_key) + blog_ovs_hooks_g.mega_set_key(net_p, blog_key); +} + +void blog_mega_put_fast_stats(void *net_p, blog_fast_stats_t *stats) +{ + if (blog_ovs_hooks_g.mega_put_fast_stats) + blog_ovs_hooks_g.mega_put_fast_stats(net_p, stats); +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_bind_ovs + * Description : Bind OVS hooks to blog + *------------------------------------------------------------------------------ + */ +void blog_bind_ovs(blog_ovs_hooks_t *blog_ovs_hooks_p) +{ + memcpy(&blog_ovs_hooks_g, blog_ovs_hooks_p, sizeof(blog_ovs_hooks_t)); +} +EXPORT_SYMBOL(blog_bind_ovs); +#endif + +/* + *------------------------------------------------------------------------------ + * Function : blog_request + * Description : Blog client requests an operation to be performed on a network + * stack entity. + * Parameters : + * request : request type + * net_p : Pointer to a network stack entity + * param1 : optional parameter 1 + * param2 : optional parameter 2 + *------------------------------------------------------------------------------ + */ +extern int blog_rule_delete_action( void *rule_p ); + +unsigned long blog_request( BlogRequest_t request, void * net_p, + unsigned long param1, unsigned long param2 ) +{ + unsigned long ret=0; + + blog_assertr( (request < BLOG_REQUEST_MAX), 0 ); + blog_assertr( (net_p != (void *)NULL), 0 ); + +#if defined(CC_BLOG_SUPPORT_DEBUG) + if ((request != SYS_TIME_GET) ) +#endif + blog_print( "request<%s> net_p<%p>" + " param1<%lu:0x%08x> param2<%lu:0x%08x>", + strBlogRequest[request], net_p, + param1, (int)param1, param2, (int)param2); + + switch ( request ) + { +#if defined(BLOG_NF_CONNTRACK) + case FLOWTRACK_KEY_SET: + blog_assertr( ((param1 == BLOG_PARAM1_DIR_ORIG) || + (param1 == BLOG_PARAM1_DIR_REPLY)), 0 ); + + /* check pld connections for any corruptions*/ + if((param2 != BLOG_KEY_FC_TUNNEL_IPV6) && (param2 != BLOG_KEY_FC_TUNNEL_IPV4)) + { + if(param2 != BLOG_KEY_FC_INVALID) + { + /*check blog_key[param1] should be BLOG_KEY_FC_INVALID */ + if(((struct nf_conn *)net_p)->blog_key[param1] != BLOG_KEY_FC_INVALID ) + { + blog_error("nf_conn: blog_key corruption when adding flow net_p=%p " + "dir=%ld old_key=0x%08x new_key=0x%08lx\n", net_p, param1, + ((struct nf_conn *)net_p)->blog_key[param1], param2); + } + } + else + { + if(((struct nf_conn *)net_p)->blog_key[param1] == BLOG_KEY_FC_INVALID ) + { + blog_error("nf_conn: blog_key corruption when deleting flow for net_p=%p " + "dir=%ld old_key=0x%08x new_key=0x%08lx\n", net_p, param1, + ((struct nf_conn *)net_p)->blog_key[param1], param2); + } + } + } + + ((struct nf_conn *)net_p)->blog_key[param1] = (uint32_t) param2; + return 0; + + case FLOWTRACK_KEY_GET: + blog_assertr( ((param1 == BLOG_PARAM1_DIR_ORIG) || + (param1 == BLOG_PARAM1_DIR_REPLY)), 0 ); + ret = ((struct nf_conn *)net_p)->blog_key[param1]; + break; + +#if defined(CONFIG_NF_DYNDSCP) || defined(CONFIG_NF_DYNDSCP_MODULE) + case FLOWTRACK_DSCP_GET: + blog_assertr( ((param1 == BLOG_PARAM1_DIR_ORIG) || + (param1 == BLOG_PARAM1_DIR_REPLY)), 0 ); + ret = ((struct nf_conn *)net_p)->dyndscp.dscp[param1]; + break; +#endif + + case FLOWTRACK_CONFIRMED: /* E.g. UDP connection confirmed */ + ret = test_bit( IPS_CONFIRMED_BIT, + &((struct nf_conn *)net_p)->status ); + break; + + case FLOWTRACK_ALG_HELPER: + { + struct nf_conn * nfct_p; + struct nf_conn_help * help; + + nfct_p = (struct nf_conn *)net_p; + help = nfct_help(nfct_p); + + if ( (help != (struct nf_conn_help *)NULL ) + && (help->helper != (struct nf_conntrack_helper *)NULL) + && (help->helper->name && strcmp(help->helper->name, "BCM-NAT")) ) + { + blog_print( "HELPER ct<%p> helper<%s>", + net_p, help->helper->name ); + return 1; + } + return 0; + } + + case FLOWTRACK_EXCLUDE: /* caution: modifies net_p */ + clear_bit(IPS_BLOG_BIT, &((struct nf_conn *)net_p)->status); + return 0; + + case FLOWTRACK_TIME_SET: + { + struct nf_conn *ct = (struct nf_conn *)net_p; + BlogCtTime_t *ct_time_p = (BlogCtTime_t *) param1; + + blog_assertr( (ct_time_p != NULL), 0 ); + + if (blog_cttime_update_fn && ct && ct_time_p) + { + (*blog_cttime_update_fn)(ct, ct_time_p); + } + return 0; + } + + case FLOWTRACK_PUT_STATS: + { + if ( likely(net_p) ) + { + Blog_t *blog_p = (Blog_t *)net_p; + BlogFcStats_t *stats_p = (BlogFcStats_t *)(param1); + void *pld_nwe_p = _blog_get_nwe(blog_p, BLOG_NPE_PLD); + void *del_nwe_p = _blog_get_nwe(blog_p, BLOG_NPE_DEL); + + if (pld_nwe_p) + blog_ct_put_stats(pld_nwe_p, blog_p->nf_dir_pld, stats_p); + + if (del_nwe_p) + blog_ct_put_stats(del_nwe_p, blog_p->nf_dir_del, stats_p); + } + return 0; + } +#endif /* defined(BLOG_NF_CONNTRACK) */ + + case BRIDGEFDB_KEY_SET: + blog_assertr( ((param1 == BLOG_PARAM1_SRCFDB) || + (param1 == BLOG_PARAM1_DSTFDB)), 0 ); + ((struct net_bridge_fdb_entry *)net_p)->fdb_key = (uint32_t) param2; + return 0; + + case BRIDGEFDB_KEY_GET: + blog_assertr( ((param1 == BLOG_PARAM1_SRCFDB) || + (param1 == BLOG_PARAM1_DSTFDB)), 0 ); + ret = ((struct net_bridge_fdb_entry *)net_p)->fdb_key; + break; + + case BRIDGEFDB_TIME_SET: + ((struct net_bridge_fdb_entry *)net_p)->updated = param2; + return 0; + + case BRIDGEFDB_IFIDX_GET: + { + struct net_bridge_fdb_entry *fdb_p = (struct net_bridge_fdb_entry *)net_p; + if (fdb_p && fdb_p->dst && fdb_p->dst->dev) + ret = fdb_p->dst->dev->ifindex; + else + ret = 0; + break; + } + + case NETIF_PUT_STATS: + { + struct net_device * dev_p = (struct net_device *)net_p; + BlogStats_t * sw_bstats_p = (BlogStats_t *) param1; + BlogStats_t * hw_bstats_p = (BlogStats_t *) param2; + blog_assertr( (sw_bstats_p != (BlogStats_t *)NULL), 0 ); + blog_assertr( (hw_bstats_p != (BlogStats_t *)NULL), 0 ); + + blog_print("dev_p<%p> rx_pkt<%llu> rx_byte<%llu> tx_pkt<%llu>" + " tx_byte<%llu> multicast<%llu>", dev_p, + sw_bstats_p->rx_packets+hw_bstats_p->rx_packets, + sw_bstats_p->rx_bytes+hw_bstats_p->rx_bytes, + sw_bstats_p->tx_packets+hw_bstats_p->tx_packets, + sw_bstats_p->tx_bytes+hw_bstats_p->tx_bytes, + sw_bstats_p->multicast+hw_bstats_p->multicast); + /* Work-around : put_stats only used by WLAN for backward compatibility */ + if ( dev_p->put_stats ) + { + BlogStats_t bstats; + memcpy(&bstats, sw_bstats_p, sizeof(bstats)); + blog_fold_stats(&bstats, hw_bstats_p); + dev_p->put_stats( dev_p, &bstats ); + } + else + { + blog_put_dev_stats(dev_p, sw_bstats_p, hw_bstats_p); + } + + return 0; + } + + case LINK_XMIT_FN: + { + struct net_device * dev_p = (struct net_device *)net_p; +#if defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_QOS_CPU) + struct net_device * rx_dev_p = (struct net_device *)(param1); + + if(rx_dev_p->netdev_ops->ndo_dpi_enqueue && (rx_dev_p->priv_flags & IFF_WANDEV)) + { + ret = (unsigned long)(rx_dev_p->netdev_ops->ndo_dpi_enqueue); + } + else +#endif + { + ret = (unsigned long)(dev_p->netdev_ops->ndo_start_xmit); + } + break; + } + + case LINK_NOCARRIER: + ret = test_bit( __LINK_STATE_NOCARRIER, + &((struct net_device *)net_p)->state ); + break; + + case NETDEV_NAME: + { + struct net_device * dev_p = (struct net_device *)net_p; + ret = (unsigned long)(dev_p->name); + break; + } + + case NETDEV_ADDR: + { + struct net_device * dev_p = (struct net_device *)net_p; + ret = (unsigned long)(dev_p->dev_addr); + break; + } + + case IQPRIO_SKBMARK_SET: + { + Blog_t *blog_p = (Blog_t *)net_p; + blog_p->mark = SKBMARK_SET_IQPRIO_MARK(blog_p->mark, param1 ); + return 0; + } + + case DPIQ_SKBMARK_SET: + { +#if defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_QOS_CPU) + Blog_t *blog_p = (Blog_t *)net_p; + blog_p->mark = SKBMARK_SET_DPIQ_MARK( blog_p->mark, param1 ); +#endif + return 0; + } + + case MCAST_DFLT_MIPS: + { + blog_rule_delete_action( net_p ); + return 0; + } + + case SYS_TIME_GET: + { + *(unsigned long *)net_p = jiffies; + return 0; + } + +#if defined(CONFIG_NET_IPGRE) || defined(CONFIG_NET_IPGRE_MODULE) + case GRE_TUNL_XMIT: + { + blog_assertr( ((BlogIpv4Hdr_t *)param1 != NULL), 0 ); + + if (blog_gre_xmit_update_fn != NULL) + return blog_gre_xmit_update_fn(net_p, (BlogIpv4Hdr_t *)param1, + (uint32_t) param2); + } + + case GRE6_TUNL_XMIT: + { + blog_assertr( ((BlogIpv6Hdr_t *)param1 != NULL), 0 ); + + if (blog_gre6_xmit_update_fn != NULL) + { + return blog_gre6_xmit_update_fn(net_p, (BlogIpv6Hdr_t *)param1, + (uint32_t) param2); + } + } +#endif + + case SKB_DST_ENTRY_SET: + { + Blog_t *blog_p = (Blog_t *)net_p; + struct sk_buff *skb_p = (struct sk_buff *)param1; + struct dst_entry *dst_p; + + blog_assertr( (skb_p != (void *)NULL), 0 ); + + dst_p = skb_dst(skb_p); + dst_hold(dst_p); + blog_p->dst_entry = (void *)dst_p; + return 0; + } + + case SKB_DST_ENTRY_RELEASE: + { + dst_release((struct dst_entry *)net_p); + return 0; + } + + case FLOW_EVENT_ACTIVATE: + case FLOW_EVENT_DEACTIVATE: + { + if ( likely(net_p) ) + { + Blog_t *blog_p = (Blog_t *)net_p; + struct net_device * rx_dev_p = (struct net_device *)(param1); + BlogFlowEventInfo_t info = {}; + + info.is_downstream = (rx_dev_p->priv_flags & IFF_WANDEV) ? 1 : 0; + info.skb_mark_flow_id = SKBMARK_GET_FLOW_ID(blog_p->mark); + info.ct_pld_p = _blog_get_nwe(blog_p, BLOG_NPE_PLD); + + /*TODO check if any extra checks are neeeded to ensure ct_del_p is + * a valid nf_conn*/ + info.ct_del_p = _blog_get_nwe(blog_p, BLOG_NPE_DEL); + info.flow_event_type = param2; + + blog_flowevent_notifier_call_chain(request, &info); + } + return 0; + } + + case CHK_HOST_DEV_MAC: + { + return blog_is_config_netdev_mac(net_p, param1); + } + +#if defined(CONFIG_BCM_KF_MAP) || defined(CONFIG_BCM_MAP_MODULE) + case MAP_TUPLE_KEY_SET: + { + struct map_tuple *map_p = (struct map_tuple *)net_p; + struct timeval now; + + blog_assertr( ((param1 == BLOG_PARAM1_MAP_DIR_US) || + (param1 == BLOG_PARAM1_MAP_DIR_DS)), 0 ); + + /* check map tuple connections for any corruptions*/ + if (param2 != BLOG_KEY_FC_INVALID) + { + /*check blog_key[param1] should be BLOG_KEY_FC_INVALID */ + if(map_p->blog_key[param1] != BLOG_KEY_FC_INVALID) + { + blog_error("MAP: blog_key corruption when adding flow map_p=%p " + "dir=%ld old_key=0x%08x new_key=0x%08lx\n", map_p, param1, + map_p->blog_key[param1], param2); + } + } + else + { + if (map_p->blog_key[param1] == BLOG_KEY_FC_INVALID) + { + blog_error("MAP: blog_key corruption when deleting flow " + "map_p=%p dir=%ld old_key=0x%08x new_key=0x%08lx\n", + map_p, param1, map_p->blog_key[param1], param2); + } + + do_gettimeofday(&now); + map_p->evict_time.tv_sec = now.tv_sec; + } + + ((struct map_tuple *)net_p)->blog_key[param1] = (uint32_t) param2; + return 0; + } + + case MAP_TUPLE_KEY_GET: + blog_assertr( ((param1 == BLOG_PARAM1_MAP_DIR_US) || + (param1 == BLOG_PARAM1_MAP_DIR_DS)), 0 ); + ret = ((struct map_tuple *)net_p)->blog_key[param1]; + break; +#endif + +#if defined(CONFIG_BCM_OVS) + case MEGA_KEY_SET: + { + blog_mega_set_key(net_p, param2); + return 0; + } + + case MEGA_KEY_GET: + { + ret = blog_mega_get_key(net_p); + break; + } + + case MEGA_PUT_STATS: + { + Blog_t *blog_p = (Blog_t *)net_p; + blog_npe_t *npe_p = BLOG_NPE_NULL; + blog_fast_stats_t *stats_p = (blog_fast_stats_t *)(param1); + + if (IS_BLOG_NPE_MEGA(blog_p)) + { + npe_p = blog_p->npe_p[BLOG_NPE_MEGA]; + + if (likely(npe_p)) + { + if (npe_p->nwe_p) + blog_mega_put_fast_stats(npe_p->nwe_p, stats_p); + } + } + ret = 0; + } +#endif + + default: + return 0; + } + + blog_print("ret<%lu:0x%08x>", ret, (int)ret); + + return ret; +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_query + * Description : Query a Blog client (qr_hook) of an event. + * Parameters : + * query : query + * net_p : Pointer to a network stack entity + * param1 : optional parameter 1 + * param2 : optional parameter 2 + * param3 : optional parameter 3 + * PreRequisite : acquire blog_lock_g before calling blog_query() + *------------------------------------------------------------------------------ + */ +int blog_query( BlogQuery_t query, void * net_p, + uint32_t param1, uint32_t param2, unsigned long param3 ) +{ + blog_assertr( (query < BLOG_QUERY_MAX), -1 ); + blog_assertr( (net_p != (void *)NULL), -1 ); + + if ( unlikely(blog_qr_hook_g == (BlogQueryHook_t)NULL) ) + return -1; + + blog_print( "Query<%s> net_p<%p> param1<%u:0x%08x> " + "param2<%u:0x%08x> param3<%lu:0x%08x> [<%p>] ", + strBlogQuery[query], net_p, param1, (int)param1, + param2, (int)param2, param3, (int)param3, + __builtin_return_address(0) ); + + return blog_qr_hook_g( query, net_p, param1, param2, param3 ); +} + + + +/* + *------------------------------------------------------------------------------ + * Function : blog_filter + * Description : Filter packets that need blogging. + * E.g. To skip logging of control versus data type packet. + * blog_p : Received packet parsed and logged into a blog + * Returns : + * PKT_NORM : If normal stack processing without logging + * PKT_BLOG : If stack processing with logging + *------------------------------------------------------------------------------ + */ +BlogAction_t blog_filter( Blog_t * blog_p ) +{ + blog_assertr( ((blog_p != BLOG_NULL) && (_IS_BPTR_(blog_p))), PKT_NORM ); + blog_assertr( (blog_p->rx.info.hdrs != 0), PKT_NORM ); + + /* + * E.g. IGRS/UPnP using Simple Service Discovery Protocol SSDP over HTTPMU + * HTTP Multicast over UDP 239.255.255.250:1900, + * + * if ( ! RX_IPinIP(blog_p) && RX_IPV4(blog_p) + * && (blog_p->rx.tuple.daddr == htonl(0xEFFFFFFA)) + * && (blog_p->rx.tuple.port.dest == 1900) + * && (blog_p->key.protocol == IPPROTO_UDP) ) + * return PKT_NORM; + * + * E.g. To filter IPv4 Local Network Control Block 224.0.0/24 + * and IPv4 Internetwork Control Block 224.0.1/24 + * + * if ( ! RX_IPinIP(blog_p) && RX_IPV4(blog_p) + * && ( (blog_p->rx.tuple.daddr & htonl(0xFFFFFE00)) + * == htonl(0xE0000000) ) + * return PKT_NORM; + * + */ + return PKT_BLOG; /* continue in stack with logging */ +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_finit, blog_sinit + * Description : This function may be inserted in a physical network device's + * packet receive handler. A receive handler typically extracts + * the packet data from the rx DMA buffer ring, allocates and + * sets up a sk_buff, decodes the l2 headers and passes the + * sk_buff into the network stack via netif_receive_skb/netif_rx. + * + * Prior to constructing a sk_buff, blog_finit() may be invoked + * using a fast kernel buffer to carry the received buffer's + * context , and the receive net_device and l1 info. + * + * This function invokes the bound receive blog hook. + * + * Parameters : + * blog_finit() fkb_p: Pointer to a fast kernel buffer + * blog_sinit() skb_p: Pointer to a Linux kernel skbuff + * dev_p : Pointer to the net_device on which the packet arrived. + * encap : First encapsulation type + * channel : Channel/Port number on which the packet arrived. + * phyHdr : e.g. XTM device RFC2684 header type + * txdev_pp : tx net_devce on whic pkt will be xmitted, used for LOCAL TCP + * + * Returns : + * PKT_DONE : The fkb|skb is consumed and device should not process fkb|skb. + * + * PKT_NORM : Device may invoke netif_receive_skb for normal processing. + * No Blog is associated and fkb reference count = 0. + * [invoking fkb_release() has no effect] + * + * PKT_BLOG : PKT_NORM behaviour + Blogging enabled. + * Must call fkb_release() to free associated Blog + * PKT_TCP4_LOCAL : Locally terminated IPV4 TCP traffic,inject into + * tcp stack directly + * + *------------------------------------------------------------------------------ + */ +inline +BlogAction_t _blog_finit( struct fkbuff * fkb_p, void * dev_p, + uint32_t encap, uint32_t channel, uint32_t phyHdr, + BlogFcArgs_t *fc_args) +{ + BlogHash_t blogHash; + BlogAction_t action = PKT_NORM; +#if defined(CONFIG_NET_IPGRE) || defined(CONFIG_NET_IPGRE_MODULE) || defined(CONFIG_ACCEL_PPTP) + int gre_rcv_version; + uint16_t flags; +#endif + BlogDevRxHook_t rx_hook = blog_rx_hook_g; + + if ( blog_pa_hook_g != (BlogPaHook_t)NULL ) + { + action = blog_pa_hook_g(fkb_p, dev_p, encap, channel, phyHdr); + if( likely(action == PKT_DONE) ) + goto bypass; + } + + blogHash.match = 0U; /* also clears hash, protocol = 0 */ + + if ( unlikely(rx_hook == (BlogDevRxHook_t)NULL) ) + goto bypass; + + blogHash.l1_tuple.channel = (uint8_t)channel; + blogHash.l1_tuple.phyType = BLOG_GET_PHYTYPE(phyHdr); + blogHash.l1_tuple.phyLen = BLOG_GET_PHYLEN(phyHdr); + + blog_assertr( (blogHash.l1_tuple.phyType < BLOG_MAXPHY), PKT_NORM); + blog_print( "fkb<%p:%x> pData<%p> length<%d> dev<%p>" + " chnl<%u> %s PhyHdrLen<%u> key<0x%08x>", + fkb_p, _is_in_skb_tag_(fkb_p->ptr, fkb_p->flags), + fkb_p->data, fkb_p->len, dev_p, + channel, strBlogPhy[blogHash.l1_tuple.phyType], + rfc2684HdrLength[blogHash.l1_tuple.phyLen], + blogHash.match ); +#if defined(CONFIG_NET_IPGRE) || defined(CONFIG_NET_IPGRE_MODULE) || defined(CONFIG_ACCEL_PPTP) + gre_rcv_version = blog_rcv_chk_gre (fkb_p, encap, &flags); +#endif +#if defined(CONFIG_NET_IPGRE) || defined(CONFIG_NET_IPGRE_MODULE) + if (blog_gre_tunnel_accelerated() && gre_rcv_version == PPTP_GRE_VER_0 && flags) + { + int gre_status; + void *tunl_p = NULL; + uint32_t pkt_seqno; + gre_status = blog_gre_rcv( fkb_p, (void *)dev_p, encap, &tunl_p, + &pkt_seqno ); + + switch (gre_status) + { + case BLOG_GRE_RCV_NOT_GRE: + case BLOG_GRE_RCV_NO_SEQNO: + case BLOG_GRE_RCV_IN_SEQ: + break; + + case BLOG_GRE_RCV_NO_TUNNEL: + blog_print( "RX GRE no matching tunnel" ); + break; + + case BLOG_GRE_RCV_FLAGS_MISSMATCH: + blog_print( "RX GRE flags miss-match" ); + action = PKT_DROP; + goto bypass; + + case BLOG_GRE_RCV_CHKSUM_ERR: + blog_print( "RX GRE checksum error" ); + action = PKT_DROP; + goto bypass; + + case BLOG_GRE_RCV_OOS_LT: + blog_print( "RX GRE out-of-seq LT pkt seqno <%u>", pkt_seqno ); + action = PKT_DROP; + goto bypass; + + case BLOG_GRE_RCV_OOS_GT: + blog_print( "RX GRE out-of-seq GT pkt seqno <%u>", pkt_seqno ); + break; + + default: + blog_print( "RX GRE unkown status <%u>", gre_status ); + break; + } + } +#endif + +#if defined(CONFIG_ACCEL_PPTP) + if (blog_gre_tunnel_accelerated() && gre_rcv_version == PPTP_GRE_VER_1) + { + int pptp_status; + uint32_t rcv_pktSeq; + pptp_status = blog_pptp_rcv( fkb_p, encap, &rcv_pktSeq ); + switch (pptp_status) + { + case BLOG_PPTP_ENCRYPTED: + blog_print( "RX PPTP encrypted pkt seqno <%u>", rcv_pktSeq ); + action = PKT_NORM; + fkb_release( fkb_p ); + goto bypass; + + case BLOG_PPTP_RCV_NOT_PPTP: + case BLOG_PPTP_RCV_NO_SEQNO: + case BLOG_PPTP_RCV_IN_SEQ: + break; + + case BLOG_PPTP_RCV_NO_TUNNEL: + blog_print( "RX PPTP no matching tunnel" ); + break; + + case BLOG_PPTP_RCV_FLAGS_MISSMATCH: + blog_print( "RX PPTP flags miss-match" ); + action = PKT_DROP; + goto bypass; + + case BLOG_PPTP_RCV_OOS_LT: + blog_print( "RX PPTP out-of-seq LT pkt seqno <%u>", rcv_pktSeq ); + action = PKT_DROP; + goto bypass; + + case BLOG_PPTP_RCV_OOS_GT: + blog_print( "RX PPTP out-of-seq GT pkt seqno <%u>", rcv_pktSeq ); + break; + + default: + blog_print( "RX PPTP unkown status <%u>", pptp_status ); + break; + } + + } +#endif + + fc_args->h_proto = encap; + fc_args->key_match = blogHash.match; + fc_args->txdev_p = NULL; + + action = rx_hook( fkb_p, (void *)dev_p, fc_args); + + if ( action == PKT_BLOG ) + { + if (blog_ctx_p->blog_dump & BLOG_DUMP_RXBLOG) + blog_dump(fkb_p->blog_p); + + fkb_p->blog_p->rx_dev_p = (void *)dev_p; /* Log device info */ +#if defined(CC_BLOG_SUPPORT_USER_FILTER) + action = blog_filter(fkb_p->blog_p); +#endif + } + + if ( unlikely(action == PKT_NORM) ) + fkb_release( fkb_p ); + +bypass: + return action; +} + +BlogAction_t blog_finit( struct fkbuff * fkb_p, void * dev_p, + uint32_t encap, uint32_t channel, uint32_t phyHdr ) +{ + /*TODO move this allocation to drivers calling this function */ + BlogFcArgs_t fc_args; + return _blog_finit(fkb_p, dev_p, encap, channel, phyHdr, &fc_args); +} + +BlogAction_t blog_finit_args( struct fkbuff * fkb_p, void * dev_p, + uint32_t encap, uint32_t channel, uint32_t phyHdr, BlogFcArgs_t *fc_args ) +{ + return _blog_finit(fkb_p, dev_p, encap, channel, phyHdr, fc_args); +} + +/* + * blog_sinit serves as a wrapper to blog_finit() by overlaying an fkb into a + * skb and invoking blog_finit(). + */ +static inline BlogAction_t _blog_sinit( struct sk_buff * skb_p, void * dev_p, + uint32_t encap, uint32_t channel, uint32_t phyHdr) +{ + struct fkbuff * fkb_p; + BlogAction_t action = PKT_NORM; + + if ( unlikely(blog_rx_hook_g == (BlogDevRxHook_t)NULL) ) + goto bypass; + + blog_assertr( (BLOG_GET_PHYTYPE(phyHdr) < BLOG_MAXPHY), PKT_NORM ); + blog_print( "skb<%p> pData<%p> length<%d> dev<%p>" + " chnl<%u> %s PhyHdrLen<%u>", + skb_p, skb_p->data, skb_p->len, dev_p, + channel, strBlogPhy[BLOG_GET_PHYTYPE(phyHdr)], + rfc2684HdrLength[BLOG_GET_PHYLEN(phyHdr)] ); + + /* CAUTION: Tag that the fkbuff is from sk_buff */ + fkb_p = (FkBuff_t *) &skb_p->fkbInSkb; + fkb_p->flags = _set_in_skb_tag_(0); /* clear and set in_skb tag */ + FKB_CLEAR_LEN_WORD_FLAGS(fkb_p->len_word); /*clears bits 31-24 of skb->len */ + + action = blog_finit( fkb_p, dev_p, encap, channel, phyHdr ); + + if ( action == PKT_BLOG ) + { + blog_assertr( (fkb_p->blog_p != BLOG_NULL), PKT_NORM ); + fkb_p->blog_p->skb_p = skb_p; + } + +bypass: + return action; +} + +BlogAction_t blog_sinit( struct sk_buff * skb_p, void * dev_p, + uint32_t encap, uint32_t channel, uint32_t phyHdr ) +{ + return _blog_sinit(skb_p, dev_p, encap, channel, phyHdr); +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_emit + * Description : This function may be inserted in a physical network device's + * hard_start_xmit function just before the packet data is + * extracted from the sk_buff and enqueued for DMA transfer. + * + * This function invokes the transmit blog hook. + * Parameters : + * nbuff_p : Pointer to a NBuff + * dev_p : Pointer to the net_device on which the packet is transmited. + * encap : First encapsulation type + * channel : Channel/Port number on which the packet is transmited. + * phyHdr : e.g. XTM device RFC2684 header type + * + * Returns : + * PKT_DONE : The skb_p is consumed and device should not process skb_p. + * PKT_NORM : Device may use skb_p and proceed with hard xmit + * Blog object is disassociated and freed. + *------------------------------------------------------------------------------ + */ +BlogAction_t _blog_emit( void * nbuff_p, void * dev_p, + uint32_t encap, uint32_t channel, uint32_t phyHdr ) +{ + BlogHash_t blogHash; + struct sk_buff * skb_p; + Blog_t * blog_p; + BlogAction_t action = PKT_NORM; +#if defined(CONFIG_NET_IPGRE) || defined(CONFIG_NET_IPGRE_MODULE) || defined(CONFIG_ACCEL_PPTP) + int gre_xmit_version; +#endif + // outer inline function has already verified this is a skbuff + skb_p = PNBUFF_2_SKBUFF(nbuff_p); /* same as nbuff_p */ + + blog_p = skb_p->blog_p; + if ( ( blog_p == BLOG_NULL ) || ( dev_p == NULL ) ) + goto bypass; + + blog_assertr( (_IS_BPTR_(blog_p)), PKT_NORM ); + + blogHash.match = 0U; + + if ( likely(blog_tx_hook_g != (BlogDevTxHook_t)NULL) ) + { + + blog_p->tx_dev_p = (void *)dev_p; /* Log device info */ + /* Log TX dev delta (tx.pktlen has the original RX pktlen) */ + blog_p->tx_dev_delta = (skb_p->len - blog_p->tx.pktlen) & 0xFF; + + blogHash.l1_tuple.channel = (uint8_t)channel; + blogHash.l1_tuple.phyType = BLOG_GET_PHYTYPE(phyHdr); + blogHash.l1_tuple.phyLen = BLOG_GET_PHYLEN(phyHdr); + + blog_p->priority = skb_p->priority; /* Log skb info */ + blog_p->mark = skb_p->fkb_mark; + blog_p->minMtu = blog_getTxMtu(blog_p); + blog_assertr( (BLOG_GET_PHYTYPE(phyHdr) < BLOG_MAXPHY), PKT_NORM); + blog_print( "skb<%p> blog<%p> pData<%p> length<%d>" + " dev<%p> chnl<%u> %s PhyHdrLen<%u> key<0x%08x>", + skb_p, blog_p, skb_p->data, skb_p->len, + dev_p, channel, strBlogPhy[BLOG_GET_PHYTYPE(phyHdr)], + rfc2684HdrLength[BLOG_GET_PHYLEN(phyHdr)], + blogHash.match ); + + /* blog lock/unlock is done inside tx_hook */ + action = blog_tx_hook_g( skb_p, (void*)skb_p->dev, + encap, blogHash.match ); + } + blog_free( skb_p, blog_free_reason_blog_emit ); /* Dis-associate w/ skb */ + +bypass: + +#if defined(CONFIG_NET_IPGRE) || defined(CONFIG_NET_IPGRE_MODULE) || defined(CONFIG_ACCEL_PPTP) + gre_xmit_version = blog_xmit_chk_gre(skb_p, encap); +#endif + +#if defined(CONFIG_NET_IPGRE) || defined(CONFIG_NET_IPGRE_MODULE) + if(gre_xmit_version == PPTP_GRE_VER_0) + { + blog_gre_xmit(skb_p, encap); + } +#endif + +#if defined(CONFIG_ACCEL_PPTP) + if(gre_xmit_version == PPTP_GRE_VER_1) + { + blog_pptp_xmit(skb_p, encap); + } +#endif + + return action; +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_activate + * Description : This function is a static configuration function of blog + * application. It invokes blog configuration hook + * Parameters : + * blog_p : pointer to a blog with configuration information + * traffic : type of the traffic + * client : configuration client + * + * Returns : + * ActivateKey : If the configuration is successful, a key is returned. + * Otherwise, NULL is returned + *------------------------------------------------------------------------------ + */ +BlogActivateKey_t *blog_activate( Blog_t * blog_p, BlogTraffic_t traffic, + BlogClient_t client ) +{ + BlogActivateKey_t *key_p = NULL; + + if ( blog_p == BLOG_NULL || + traffic >= BlogTraffic_MAX || + client >= BlogClient_MAX ) + { + blog_assertr( ( blog_p != BLOG_NULL ), NULL ); + goto bypass; + } + + if ( unlikely(blog_sc_hook_g[client] == (BlogScHook_t)NULL) ) + goto bypass; + +#if defined(CC_BLOG_SUPPORT_DEBUG) + blog_print( "blog_p<%p> traffic<%u> client<%u>", blog_p, traffic, client ); + blog_dump( blog_p ); +#endif + + blog_lock(); + key_p = blog_sc_hook_g[client]( blog_p, traffic ); + blog_unlock(); + + /* on sucess blog_p is transferred to flow, decrement refcnt's of ct's */ + if(key_p) + blog_ct_put(blog_p); + +bypass: + return key_p; +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_deactivate + * Description : This function is a deconfiguration function of blog + * application + * Parameters : + * key : blog key information + * traffic : type of traffic + * client : configuration client + * + * Returns : + * blog_p : If the deconfiguration is successful, the associated blog + * pointer is returned to the caller + *------------------------------------------------------------------------------ + */ +Blog_t * blog_deactivate( BlogActivateKey_t key, BlogTraffic_t traffic, + BlogClient_t client ) +{ + Blog_t * blog_p = NULL; + + if ( key.fc.word == BLOG_KEY_FC_INVALID || key.mc.word == BLOG_KEY_MCAST_INVALID || + traffic >= BlogTraffic_MAX || + client >= BlogClient_MAX ) + { + blog_assertr( (key.fc.word != BLOG_KEY_FC_INVALID), blog_p ); + blog_assertr( (key.mc.word != BLOG_KEY_MCAST_INVALID), blog_p ); + goto bypass; + } + + if ( unlikely(blog_sd_hook_g[client] == (BlogSdHook_t)NULL) ) + goto bypass; + + blog_print( "fc<0x%08x> mc<0x%08x> traffic<%u> client<%u>", + key.fc.word, key.mc.word, traffic, client ); + + blog_lock(); + blog_p = blog_sd_hook_g[client]( key, traffic ); + blog_unlock(); + +#if defined(CC_BLOG_SUPPORT_DEBUG) + blog_dump( blog_p ); +#endif + +bypass: + return blog_p; +} + +/* + * blog_iq_prio determines the Ingress QoS priority of the packet + */ +int blog_iq_prio( struct sk_buff * skb_p, void * dev_p, + uint32_t encap, uint32_t channel, uint32_t phyHdr ) +{ + struct fkbuff * fkb_p; + BlogAction_t action = PKT_NORM; + int iq_prio = BLOG_IQ_PRIO_HIGH; + uint32_t dummy; + void *dummy_dev_p = &dummy; + + if ( unlikely(blog_rx_hook_g == (BlogDevRxHook_t)NULL) ) + goto bypass; + + blog_assertr( (BLOG_GET_PHYTYPE(phyHdr) < BLOG_MAXPHY), 1 ); + blog_print( "skb<%p> pData<%p> length<%d> dev<%p>" + " chnl<%u> %s PhyHdrLen<%u>", + skb_p, skb_p->data, skb_p->len, dev_p, + channel, strBlogPhy[BLOG_GET_PHYTYPE(phyHdr)], + rfc2684HdrLength[BLOG_GET_PHYLEN(phyHdr)] ); + + /* CAUTION: Tag that the fkbuff is from sk_buff */ + fkb_p = (FkBuff_t *) &skb_p->fkbInSkb; + + /* set in_skb and chk_iq_prio tag */ + fkb_p->flags = _set_in_skb_n_chk_iq_prio_tag_(0); + action = blog_finit( fkb_p, dummy_dev_p, encap, channel, phyHdr ); + + /* blog_iq_prio should return only PKT_BLOG, PKT_DROP or PKT_NORM*/ + + if ( action == PKT_BLOG ) + { + blog_assertr( (fkb_p->blog_p != BLOG_NULL), iq_prio); + fkb_p->blog_p->skb_p = skb_p; + iq_prio = fkb_p->blog_p->iq_prio; + blog_free( skb_p, blog_free_reason_blog_iq_prio ); + } + else + { + blog_assertr(((action == PKT_NORM) || (action == PKT_DROP)), iq_prio); + } + +bypass: + return iq_prio; +} + +static int blog_notify_netevent(struct notifier_block *nb, unsigned long event, void *_neigh) +{ + struct neighbour *neigh = _neigh; + switch (event) + { + case NETEVENT_ARP_BINDING_CHANGE: + blog_lock(); + blog_notify_async(ARP_BIND_CHG, nb, *(uint32_t *)neigh->primary_key, + (unsigned long)neigh->ha, NULL, NULL); + blog_unlock(); + return 0; + default: + return 1; + } +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_get_hw_accel + * Description: Get current status of Enable/Disable acceleration of flows by HW. + * Return Val : 0(Disable), 1(Enable) + *------------------------------------------------------------------------------ + */ +int blog_get_hw_accel(void) +{ + int hw_accel = 0; + + blog_query(QUERY_GET_HW_ACCEL, (void*)&hw_accel, 0, 0, 0); + return hw_accel; +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_bind + * Description : Override default rx and tx hooks. + * blog_rx : Function pointer to be invoked in blog_finit(), blog_sinit() + * blog_tx : Function pointer to be invoked in blog_emit() + * blog_xx : Function pointer to be invoked in blog_notify() + * info : Mask of the function pointers for configuration + *------------------------------------------------------------------------------ + */ +void blog_bind( BlogDevRxHook_t blog_rx, BlogDevTxHook_t blog_tx, + BlogNotifyHook_t blog_xx, BlogQueryHook_t blog_qr, + BlogBind_t bind) +{ + blog_print( "Bind Rx[<%p>] Tx[<%p>] Notify[<%p>] bind[<%u>]", + blog_rx, blog_tx, blog_xx, + (uint8_t)bind.hook_info ); + + if ( bind.bmap.RX_HOOK ) + blog_rx_hook_g = blog_rx; /* Receive hook */ + if ( bind.bmap.TX_HOOK ) + blog_tx_hook_g = blog_tx; /* Transmit hook */ + if ( bind.bmap.XX_HOOK ) + blog_xx_hook_g = blog_xx; /* Notify hook */ + if ( bind.bmap.QR_HOOK ) + blog_qr_hook_g = blog_qr; /* Query hook */ +} + +static BlogClient_t hw_accelerator_client = BlogClient_MAX; +static BlogClient_t sw_accelerator_client = BlogClient_MAX; + +/* + *------------------------------------------------------------------------------ + * Function : is_hw_accelerator + * Description : + *------------------------------------------------------------------------------ + */ +static int is_hw_accelerator(BlogClient_t client) +{ + switch(client) + { +#if defined(CONFIG_BCM_KF_FAP) + case BlogClient_fap: +#endif +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) + case BlogClient_runner: +#endif /* (CONFIG_BCM_RDPA) || (CONFIG_BCM_RDPA_MODULE) */ +#endif /* CONFIG_BCM_KF_RUNNER */ + return 1; + default: + break; + } + return 0; +} + +/* + *------------------------------------------------------------------------------ + * Function : hw_accelerator_client_get + * Description : + *------------------------------------------------------------------------------ + */ +int hw_accelerator_client_get(void) +{ + return hw_accelerator_client; +} + +/* + *------------------------------------------------------------------------------ + * Function : sw_accelerator_client_get + * Description : + *------------------------------------------------------------------------------ + */ +int sw_accelerator_client_get(void) +{ + return sw_accelerator_client; +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_bind_config + * Description : Override default sc and sd hooks. + * blog_sc : Function pointer to be invoked in blog_activate() + * blog_sd : Function pointer to be invoked in blog_deactivate() + * client : configuration client + * info : Mask of the function pointers for configuration + *------------------------------------------------------------------------------ + */ +void blog_bind_config( BlogScHook_t blog_sc, BlogSdHook_t blog_sd, + BlogClient_t client, BlogBind_t bind) +{ + blog_print( "Bind Sc[<%p>] Sd[<%p>] Client[<%u>] bind[<%u>]", + blog_sc, blog_sd, client, + (uint8_t)bind.hook_info ); + + if ( bind.bmap.SC_HOOK ) + blog_sc_hook_g[client] = blog_sc; /* Static config hook */ + if ( bind.bmap.SD_HOOK ) + blog_sd_hook_g[client] = blog_sd; /* Static deconf hook */ + + if (is_hw_accelerator(client)) + hw_accelerator_client = client; + else + sw_accelerator_client = client; +} + + +/* + *------------------------------------------------------------------------------ + * Function : blog_bind_packet_accelerator + * Description : Bind a packet accelerator + * blog_pa : Function pointer to packet accelerator + * bind : Mask of the function pointers for configuration + *------------------------------------------------------------------------------ + */ +void blog_bind_packet_accelerator( BlogPaHook_t blog_pa, BlogBind_t bind ) +{ + blog_print( "Bind pa[<%p>] bind[<%x>]", blog_pa, bind.hook_info ); + + if ( bind.bmap.PA_HOOK ) + blog_pa_hook_g = blog_pa; /* Static packet accelerator hook */ +} + +/* + *------------------------------------------------------------------------------ + * Function : blog + * Description : Log the L2 or L3+4 tuple information + * Parameters : + * skb_p : Pointer to the sk_buff + * dir : rx or tx path + * encap : Encapsulation type + * len : Length of header + * data_p : Pointer to encapsulation header data. + *------------------------------------------------------------------------------ + */ +void blog( struct sk_buff * skb_p, BlogDir_t dir, BlogEncap_t encap, + size_t len, void * data_p ) +{ + BlogHeader_t * bHdr_p; + Blog_t * blog_p; + + blog_assertv( (skb_p != (struct sk_buff *)NULL ) ); + blog_assertv( (skb_p->blog_p != BLOG_NULL) ); + blog_assertv( (_IS_BPTR_(skb_p->blog_p)) ); + blog_assertv( (data_p != (void *)NULL ) ); + blog_assertv( (len <= BLOG_HDRSZ_MAX) ); + blog_assertv( (encap < PROTO_MAX) ); + + blog_p = skb_p->blog_p; + blog_assertv( (blog_p->skb_p == skb_p) ); + + bHdr_p = &blog_p->rx + dir; + + if ( encap == PLD_IPv4 ) /* Log the IP Tuple */ + { + BlogTuple_t * bTuple_p = &bHdr_p->tuple; + BlogIpv4Hdr_t * ip_p = (BlogIpv4Hdr_t *)data_p; + + /* Discontinue if non IPv4 or with IP options, or fragmented */ + if ( (ip_p->ver != 4) || (ip_p->ihl != 5) + || (ip_p->flagsFrag & htons(BLOG_IP_FRAG_OFFSET|BLOG_IP_FLAG_MF)) ) + goto skip; + + if ( ip_p->proto == BLOG_IPPROTO_TCP ) + { + BlogTcpHdr_t * th_p; + th_p = (BlogTcpHdr_t*)( (uint8_t *)ip_p + BLOG_IPV4_HDR_LEN ); + + /* Discontinue if TCP RST/FIN */ + if ( TCPH_RST(th_p) | TCPH_FIN(th_p) ) + goto skip; + bTuple_p->port.source = th_p->sPort; + bTuple_p->port.dest = th_p->dPort; + } + else if ( ip_p->proto == BLOG_IPPROTO_UDP ) + { + BlogUdpHdr_t * uh_p; + uh_p = (BlogUdpHdr_t *)( (uint8_t *)ip_p + BLOG_UDP_HDR_LEN ); + bTuple_p->port.source = uh_p->sPort; + bTuple_p->port.dest = uh_p->dPort; + } + else + goto skip; /* Discontinue if non TCP or UDP upper layer protocol */ + + bTuple_p->ttl = ip_p->ttl; + bTuple_p->tos = ip_p->tos; + bTuple_p->check = ip_p->chkSum; + bTuple_p->saddr = blog_read32_align16( (uint16_t *)&ip_p->sAddr ); + bTuple_p->daddr = blog_read32_align16( (uint16_t *)&ip_p->dAddr ); + blog_p->key.protocol = ip_p->proto; + } + else if ( encap == PLD_IPv6 ) /* Log the IPv6 Tuple */ + { + printk("FIXME blog encap PLD_IPv6 \n"); + } + else /* L2 encapsulation */ + { + register short int * d; + register const short int * s; + + blog_assertv( (bHdr_p->count < BLOG_ENCAP_MAX) ); + blog_assertv( ((len<=20) && ((len & 0x1)==0)) ); + blog_assertv( ((bHdr_p->length + len) < BLOG_HDRSZ_MAX) ); + + bHdr_p->info.hdrs |= (1U << encap); + bHdr_p->encap[ bHdr_p->count++ ] = encap; + s = (const short int *)data_p; + d = (short int *)&(bHdr_p->l2hdr[bHdr_p->length]); + bHdr_p->length += len; + + switch ( len ) /* common lengths, using half word alignment copy */ + { + case 20: *(d+9)=*(s+9); + *(d+8)=*(s+8); + *(d+7)=*(s+7); + case 14: *(d+6)=*(s+6); + case 12: *(d+5)=*(s+5); + case 10: *(d+4)=*(s+4); + case 8: *(d+3)=*(s+3); + case 6: *(d+2)=*(s+2); + case 4: *(d+1)=*(s+1); + case 2: *(d+0)=*(s+0); + break; + default: + goto skip; + } + } + + return; + +skip: /* Discontinue further logging by dis-associating Blog_t object */ + + blog_skip( skb_p, blog_skip_reason_blog ); + + /* DO NOT ACCESS blog_p !!! */ +} + + +/* + *------------------------------------------------------------------------------ + * Function : blog_nfct_dump + * Description : Dump the nf_conn context + * ct : Pointer to a nf_conn object + * CAUTION : nf_conn is not held !!! + *------------------------------------------------------------------------------ + */ +#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) +void blog_nfct_dump( struct nf_conn * ct, uint32_t dir ) +{ +#if defined(BLOG_NF_CONNTRACK) + struct nf_conn_help *help_p; + struct nf_conn_nat *nat_p; + int bitix; + if ( ct == NULL ) + { + blog_error( "NULL NFCT error" ); + return; + } + +#ifdef CONFIG_NF_NAT_NEEDED + nat_p = nfct_nat(ct); +#else + nat_p = (struct nf_conn_nat *)NULL; +#endif + + help_p = nfct_help(ct); + printk("\tNFCT: ct<0x%p>, master<0x%p>\n" + "\t\tF_NAT<%p> keys[0x%08x 0x%08x] dir<%s>\n" + "\t\thelp<0x%p> helper<%s>\n", + ct, + ct->master, + nat_p, + ct->blog_key[IP_CT_DIR_ORIGINAL], + ct->blog_key[IP_CT_DIR_REPLY], + (dirhelper) ? help_p->helper->name : "NONE" ); + + printk( "\t\tSTATUS[ " ); + for ( bitix = 0; bitix <= IPS_BLOG_BIT; bitix++ ) + if ( ct->status & (1 << bitix) ) + printk( "%s ", strIpctStatus[bitix] ); + printk( "]\n" ); +#endif /* defined(BLOG_NF_CONNTRACK) */ +} +#endif + +/* + *------------------------------------------------------------------------------ + * Function : blog_netdev_dump + * Description : Dump the contents of a net_device object. + * dev_p : Pointer to a net_device object + * + * CAUTION : Net device is not held !!! + * + *------------------------------------------------------------------------------ + */ +static void blog_netdev_dump( struct net_device * dev_p ) +{ + int i; + printk( "\tDEVICE: %s dev<%p> ndo_start_xmit[<%p>]\n" + "\t dev_addr[ ", dev_p->name, + dev_p, dev_p->netdev_ops->ndo_start_xmit ); + for ( i=0; iaddr_len; i++ ) + printk( "%02x ", *((uint8_t *)(dev_p->dev_addr) + i) ); + printk( "]\n" ); +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_tuple_dump + * Description : Dump the contents of a BlogTuple_t object. + * bTuple_p : Pointer to the BlogTuple_t object + *------------------------------------------------------------------------------ + */ +static void blog_tuple_dump( BlogTuple_t * bTuple_p ) +{ + if (bTuple_p ) + { + printk( "\tIPv4:\n" + "\t\tSrc" BLOG_IPV4_ADDR_PORT_FMT + " Dst" BLOG_IPV4_ADDR_PORT_FMT "\n" + "\t\tttl<%3u> tos<%3u> check<0x%04x>\n", + BLOG_IPV4_ADDR(bTuple_p->saddr), ntohs(bTuple_p->port.source), + BLOG_IPV4_ADDR(bTuple_p->daddr), ntohs(bTuple_p->port.dest), + bTuple_p->ttl, bTuple_p->tos, bTuple_p->check ); + } +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_tupleV6_dump + * Description : Dump the contents of a BlogTupleV6_t object. + * bTupleV6_p : Pointer to the BlogTupleV6_t object + *------------------------------------------------------------------------------ + */ +static void blog_tupleV6_dump( BlogTupleV6_t * bTupleV6_p ) +{ + printk( "\tIPv6:\n" + "\t\tSrc" BLOG_IPV6_ADDR_PORT_FMT "\n" + "\t\tDst" BLOG_IPV6_ADDR_PORT_FMT "\n" + "\t\trx_hop_limit<%3u> tx_hop_limit<%3u>\n", + BLOG_IPV6_ADDR(bTupleV6_p->saddr), ntohs(bTupleV6_p->port.source), + BLOG_IPV6_ADDR(bTupleV6_p->daddr), ntohs(bTupleV6_p->port.dest), + bTupleV6_p->rx_hop_limit, bTupleV6_p->tx_hop_limit ); +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_l2_dump + * Description : parse and dump the contents of all L2 headers + * bHdr_p : Pointer to logged header + *------------------------------------------------------------------------------ + */ +void blog_l2_dump( BlogHeader_t * bHdr_p ) +{ + register int i, ix, length, offset = 0; + BlogEncap_t type; + char * value = bHdr_p->l2hdr; + + for ( ix=0; ixcount; ix++ ) + { + type = bHdr_p->encap[ix]; + + switch ( type ) + { + case PPP_1661 : length = BLOG_PPP_HDR_LEN; break; + case PPPoE_2516 : length = BLOG_PPPOE_HDR_LEN; break; + case VLAN_8021Q : length = BLOG_VLAN_HDR_LEN; break; + case ETH_802x : length = BLOG_ETH_HDR_LEN; break; + case BCM_SWC : + if ( *((uint16_t *)(bHdr_p->l2hdr + 12) ) + == htons(BLOG_ETH_P_BRCM4TAG)) + length = BLOG_BRCM4_HDR_LEN; + else + length = BLOG_BRCM6_HDR_LEN; + break; + + case PLD_IPv4 : + case PLD_IPv6 : + case DEL_IPv4 : + case DEL_IPv6 : + case BCM_XPHY : + default : printk( "Unsupported type %d\n", type ); + return; + } + + printk( "\tENCAP %d. %10s +%2d %2d [ ", + ix, strBlogEncap[type], offset, length ); + + for ( i=0; ivirt_dev_p[i]; + + if ( dev_p == (void *)NULL ) continue; + + dev_dir = DEV_DIR(dev_p); + dev_p = DEVP_DETACH_DIR( dev_p ); + printk("<%p: %s: %d: %d> ", dev_p, dev_p ? dev_p->name : " ", dev_dir, dev_p->mtu); + } + + printk("\n"); +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_grerx_dump + * Description : Dump the contents of a BlogTuple_t object. + * blog_p : Pointer to the Blog + *------------------------------------------------------------------------------ + */ +static void blog_grerx_dump( Blog_t *blog_p ) +{ + BlogGre_t *bGreRx_p = &blog_p->grerx; + int i; + char *value; + + printk( " GRE RX: hlen<%u> l2_hlen<%u> ipid<0x%04x:%u> flags<0x%04x> rx_tunl_p<%p>\n", + bGreRx_p->hlen, bGreRx_p->l2_hlen, ntohs(bGreRx_p->ipid), + ntohs(bGreRx_p->ipid), bGreRx_p->gre_flags.u16, blog_p->rx_tunl_p + ); + + if ( blog_p->rx.info.bmap.GRE && blog_p->rx.info.bmap.GRE_ETH && + (bGreRx_p->l2_hlen>0) ) + { + value = bGreRx_p->l2hdr; + printk( "\t[ "); + for ( i=0; il2_hlen; i++ ) + printk( "%02x ", (uint8_t)value[i] ); + printk( "]\n" ); + } +} + + +/* + *------------------------------------------------------------------------------ + * Function : blog_gretx_dump + * Description : Dump the contents of a BlogTuple_t object. + * blog_p : Pointer to the Blog + *------------------------------------------------------------------------------ + */ +static void blog_gretx_dump( Blog_t *blog_p ) +{ + BlogGre_t *bGreTx_p = &blog_p->gretx; + int i; + char *value; + + printk( " GRE TX: hlen<%u> l2_hlen<%u> ipid<0x%04x:%u> flags<0x%04x> tx_tunl_p<%p>\n", + bGreTx_p->hlen, bGreTx_p->l2_hlen, ntohs(bGreTx_p->ipid), + ntohs(bGreTx_p->ipid), bGreTx_p->gre_flags.u16, blog_p->tx_tunl_p ); + + if ( blog_p->tx.info.bmap.GRE && blog_p->tx.info.bmap.GRE_ETH && + (bGreTx_p->l2_hlen>0) ) + { + value = bGreTx_p->l2hdr; + printk( "\t[ "); + for ( i=0; il2_hlen; i++ ) + printk( "%02x ", (uint8_t)value[i] ); + printk( "]\n" ); + } +} + +void blog_lock(void) +{ + BLOG_LOCK_BH(); +} + +void blog_unlock(void) +{ + BLOG_UNLOCK_BH(); +} + +void blog_fdb_dump(Blog_t *blog_p) +{ + struct net_bridge_fdb_entry *src_fdb_p; + struct net_bridge_fdb_entry *dst_fdb_p; + + /* For learnt flows bridge FDB pointer is stored in NPE */ + src_fdb_p = (struct net_bridge_fdb_entry *) + (blog_p->fdb[0] ? blog_p->fdb[0] : NULL); + + dst_fdb_p = (struct net_bridge_fdb_entry *) + (blog_p->fdb[1] ? blog_p->fdb[1] : NULL); + + printk( "\tfdb_src<%p> key[0x%08x] fdb_dst<%p> key[0x%08x]\n", + src_fdb_p, src_fdb_p ? src_fdb_p->fdb_key : 0, + dst_fdb_p, dst_fdb_p ? dst_fdb_p->fdb_key : 0 ); + + printk( "\tMAC src<%02x:%02x:%02x:%02x:%02x:%02x> dst<%02x:%02x:%02x:%02x:%02x:%02x>\n", + blog_p->src_mac.u8[0], blog_p->src_mac.u8[1], blog_p->src_mac.u8[2], + blog_p->src_mac.u8[3], blog_p->src_mac.u8[4], blog_p->src_mac.u8[5], + blog_p->dst_mac.u8[0], blog_p->dst_mac.u8[1], blog_p->dst_mac.u8[2], + blog_p->dst_mac.u8[3], blog_p->dst_mac.u8[4], blog_p->dst_mac.u8[5] ); +} + +void blog_ct_dump(Blog_t *blog_p) +{ +#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) + blog_npe_t *npe_pld_p = (blog_npe_t *)blog_p->npe_p[BLOG_NPE_PLD]; + blog_npe_t *npe_del_p = (blog_npe_t *)blog_p->npe_p[BLOG_NPE_DEL]; + struct nf_conn *nwe_pld_p; + struct nf_conn *nwe_del_p; + + /* For learnt flows NFCT pointer is stored in NPE */ + nwe_pld_p = (struct nf_conn *) (npe_pld_p ? npe_pld_p->nwe_p : NULL); + nwe_del_p = (struct nf_conn *) (npe_del_p ? npe_del_p->nwe_p : NULL); + + if (!nwe_pld_p && !nwe_del_p) + { + nwe_pld_p = blog_p->ct_p[BLOG_CT_PLD]; + nwe_del_p = blog_p->ct_p[BLOG_CT_DEL]; + } + + printk("\tdelCt<%p> key[0x%08x:%d] pldCt<%p> key[0x%08x:%d]\n", + nwe_del_p, nwe_del_p ? + nwe_del_p->blog_key[blog_p->nf_dir_del] : 0, blog_p->nf_dir_del, + nwe_pld_p, nwe_pld_p ? + nwe_pld_p->blog_key[blog_p->nf_dir_del] : 0, blog_p->nf_dir_del); + + if (nwe_pld_p) + blog_nfct_dump(nwe_pld_p, blog_p->nf_dir_pld); + + if (nwe_del_p) + blog_nfct_dump(nwe_del_p, blog_p->nf_dir_del); +#endif +} + +void blog_map_dump(Blog_t *blog_p) +{ +#if defined(CONFIG_BCM_KF_MAP) || defined(CONFIG_BCM_MAP_MODULE) + blog_npe_t *npe_map_p = (blog_npe_t *)blog_p->npe_p[BLOG_NPE_MAP]; + struct map_tuple *map_p = (struct map_tuple *) blog_p->map_p; + struct map_tuple *nwe_map_p = npe_map_p ? npe_map_p->nwe_p : NULL; + + /* For learnt flows MAP pointer is stored in NPE */ + nwe_map_p = nwe_map_p ? nwe_map_p : map_p; + + printk("\tMAP: map<0x%p>, key[US:0x%08x] key[DS:0x%08x] \n", + nwe_map_p, + nwe_map_p ? nwe_map_p->blog_key[BLOG_PARAM1_MAP_DIR_US]: 0, + nwe_map_p ? nwe_map_p->blog_key[BLOG_PARAM1_MAP_DIR_DS]: 0); +#endif +} + +void blog_mega_dump(Blog_t *blog_p) +{ +#if defined(CONFIG_BCM_OVS) + blog_npe_t *npe_mega_p = (blog_npe_t *)blog_p->npe_p[BLOG_NPE_MEGA]; + void *nwe_mega_p = npe_mega_p ? npe_mega_p->nwe_p : NULL; + + /* For learnt flows megaflow blog_key pointer is stored in NPE */ + nwe_mega_p = nwe_mega_p ? nwe_mega_p : blog_p->mega_p; + + printk("\tMEGA: mega_p<0x%p>, key[0x%08x]\n", + nwe_mega_p, nwe_mega_p ? (unsigned int) blog_mega_get_key(nwe_mega_p): 0); +#endif +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_dump + * Description : Dump the contents of a Blog object. + * blog_p : Pointer to the Blog_t object + *------------------------------------------------------------------------------ + */ +void blog_dump( Blog_t * blog_p ) +{ + if ( blog_p == BLOG_NULL ) + return; + + blog_assertv( (_IS_BPTR_(blog_p)) ); + + printk( "BLOG <%p> owner<%p> flags<0x%08x> tos_mode<%u:%u>\n" + "\tL1 channel<%u> phyLen<%u> phy<%u> <%s> match<0x%08x>\n" + "\thash<0x%08x> prot<%u> wl<0x%08x>\n" + "\tprio<0x%08x> mark<0x%08x> minMTU<%u> tuple_offset<%u>\n" + "\teth_type<0x%04x> vtag_num<%u> vtag[0]<0x%8x> vtag[1]<0x%08x>\n", + blog_p, blog_p->skb_p, blog_p->flags, + (int)blog_p->tos_mode_us, (int)blog_p->tos_mode_ds, + blog_p->key.l1_tuple.channel, + rfc2684HdrLength[blog_p->key.l1_tuple.phyLen], + blog_p->key.l1_tuple.phy, + strBlogPhy[blog_p->key.l1_tuple.phyType], blog_p->key.match, + blog_p->hash, blog_p->key.protocol, blog_p->wl, + blog_p->priority, (uint32_t)blog_p->mark, blog_p->minMtu, + blog_p->tuple_offset, + ntohs(blog_p->eth_type), blog_p->vtag_num, + ntohl(blog_p->vtag[0]), ntohl(blog_p->vtag[1])); + + blog_ct_dump(blog_p); + blog_map_dump(blog_p); + blog_mega_dump(blog_p); + blog_fdb_dump(blog_p); + + printk( "\tfeature<0x%08x> offsets[0]<0x%08x> offsets[1]<0x%08x> \n" + "\tprehook<0x%p> posthook<0x%p>\n", + blog_p->feature, blog_p->offsets[0], blog_p->offsets[1], + blog_p->preHook, blog_p->postHook ); + + printk( " RX count<%u> channel<%02u> bmap<0x%08x> phyLen<%u> " + "phyHdr<%u> %s\n" + " wan_qdisc<%u> multicast<%u> fkbInSkb<%u>\n", + blog_p->rx.count, blog_p->rx.info.channel, + blog_p->rx.info.hdrs, + rfc2684HdrLength[blog_p->rx.info.phyHdrLen], + blog_p->rx.info.phyHdr, + strBlogPhy[blog_p->rx.info.phyHdrType], + blog_p->rx.wan_qdisc, + blog_p->rx.multicast, blog_p->rx.fkbInSkb ); + + blog_l2_dump( &blog_p->rx ); + + printk(" Del Tuple:\n" ); + if ( blog_p->rx.info.bmap.DEL_IPv4 ) + blog_tuple_dump( &blog_p->delrx_tuple ); + else if ( blog_p->rx.info.bmap.DEL_IPv6 ) + { + if ( blog_p->rx.info.bmap.GRE ) + { + if ( !blog_p->tx.info.bmap.GRE ) + blog_tupleV6_dump( &blog_p->del_tupleV6 ); + } + else + blog_tupleV6_dump( &blog_p->tupleV6 ); + } + + if ( blog_p->rx.info.bmap.HDR0_IPv4 ) + { + printk(" Hdr0 Tuple:\n" ); + blog_tuple_dump( &blog_p->rx_tuple[0] ); + } + + printk(" Payload Tuple:\n" ); + if ( blog_p->rx.info.bmap.PLD_IPv4 ) + blog_tuple_dump( &blog_p->rx.tuple ); + else if ( blog_p->rx.info.bmap.PLD_IPv6 ) + blog_tupleV6_dump( &blog_p->tupleV6 ); + + if ( blog_p->rx.info.bmap.GRE ) + blog_grerx_dump( blog_p ); + + printk(" TX count<%u> channel<%02u> bmap<0x%08x> phyLen<%u> " + "phyHdr<%u> %s\n", + blog_p->tx.count, blog_p->tx.info.channel, + blog_p->tx.info.hdrs, + rfc2684HdrLength[blog_p->tx.info.phyHdrLen], + blog_p->tx.info.phyHdr, + strBlogPhy[blog_p->tx.info.phyHdrType] ); + if ( blog_p->tx_dev_p ) + blog_netdev_dump( blog_p->tx_dev_p ); + + blog_l2_dump( &blog_p->tx ); + + printk(" Del Tuple:\n" ); + if ( blog_p->tx.info.bmap.DEL_IPv4 ) + blog_tuple_dump( &blog_p->deltx_tuple ); + else if ( blog_p->tx.info.bmap.DEL_IPv6 ) + { + if ( blog_p->tx.info.bmap.GRE ) + blog_tupleV6_dump( &blog_p->del_tupleV6 ); + else + blog_tupleV6_dump( &blog_p->tupleV6 ); + } + + if ( blog_p->tx.info.bmap.HDR0_IPv4 ) + { + printk(" Hdr0 Tuple:\n" ); + blog_tuple_dump( &blog_p->tx_tuple[0] ); + } + + printk(" Payload Tuple:\n" ); + if ( blog_p->tx.info.bmap.PLD_IPv4 ) + blog_tuple_dump( &blog_p->tx.tuple ); + else if ( blog_p->tx.info.bmap.PLD_IPv6 ) + blog_tupleV6_dump( &blog_p->tupleV6 ); + + if ( blog_p->tx.info.bmap.GRE ) + blog_gretx_dump( blog_p ); + + + blog_virdev_dump( blog_p ); + +#if defined(CC_BLOG_SUPPORT_DEBUG) + printk( "\t\textends<%d> free<%d> used<%d> HWM<%d> fails<%d>\n", + blog_extends, blog_cnt_free, blog_cnt_used, blog_cnt_hwm, + blog_cnt_fails ); +#endif +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_getTxMtu + * Description : Gets unadjusted mtu from tx network devices associated with blog. + * blog_p : Pointer to the Blog_t object + *------------------------------------------------------------------------------ + */ +uint16_t blog_getTxMtu(Blog_t * blog_p) +{ + int i; + uint16_t minMtu; + void * dir_dev_p; + struct net_device * dev_p; + + dev_p = (struct net_device *)blog_p->tx_dev_p; + if (dev_p) + minMtu = dev_p->mtu; + else + minMtu = 0xFFFF; + + + for (i = 0; i < MAX_VIRT_DEV; i++) + { + dir_dev_p = blog_p->virt_dev_p[i]; + if ( dir_dev_p == (void *)NULL ) + continue; + if ( IS_RX_DIR(dir_dev_p) ) + continue; + dev_p = (struct net_device *)DEVP_DETACH_DIR(dir_dev_p); +#ifdef CONFIG_BCM_IGNORE_BRIDGE_MTU + /* Exclude Bridge device - bridge always has the least MTU of all attached interfaces - + * irrespective of this specific flow path */ + if (dev_p && !(dev_p->priv_flags&IFF_EBRIDGE) && dev_p->mtu < minMtu) +#else + if (dev_p && dev_p->mtu < minMtu) +#endif + { + minMtu = dev_p->mtu; + } + } + + if (MAPT_UP(blog_p)) + minMtu -= (sizeof(struct ipv6hdr) - sizeof(struct iphdr)) ; + + blog_print( "minMtu <%d>", (int)minMtu ); + + return minMtu; +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_set_len_tbl + * Description : Set the values learnt from iptables rule for length + * prioritization. + * Parameters : + * val[] : Array that stores {minimum length, maximum length, original + * mark, target mark}. + * Returns : + * Zero : Success + * Non-zero : Fail + *------------------------------------------------------------------------------ + */ +int blog_set_len_tbl(uint32_t val[]) +{ + if ( blog_len_tbl_idx >= BLOG_MAX_LEN_TBLSZ ) + { + blog_print("%s: Length priority entries exceed the table size.\n", __func__); + return -1; + } + + BLOG_LOCK_TBL(); + + blog_len_tbl[blog_len_tbl_idx][BLOG_MIN_LEN_INDEX] = val[BLOG_MIN_LEN_INDEX]; + blog_len_tbl[blog_len_tbl_idx][BLOG_MAX_LEN_INDEX] = val[BLOG_MAX_LEN_INDEX]; + blog_len_tbl[blog_len_tbl_idx][BLOG_ORIGINAL_MARK_INDEX] = val[BLOG_ORIGINAL_MARK_INDEX]; + blog_len_tbl[blog_len_tbl_idx][BLOG_TARGET_MARK_INDEX] = val[BLOG_TARGET_MARK_INDEX]; + blog_len_tbl_idx++; + + BLOG_UNLOCK_TBL(); + return 0; +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_clr_len_tbl + * Description : Clear the table for length prioritization. + * Returns : + * Zero : Success + * Non-zero : Fail + *------------------------------------------------------------------------------ + */ +int blog_clr_len_tbl() +{ + int i; + + BLOG_LOCK_TBL(); + + for ( i = 0; i < BLOG_MAX_LEN_TBLSZ; i++ ) + { + blog_len_tbl[i][BLOG_MIN_LEN_INDEX] = BLOG_INVALID_UINT32; + blog_len_tbl[i][BLOG_MAX_LEN_INDEX] = BLOG_INVALID_UINT32; + blog_len_tbl[i][BLOG_ORIGINAL_MARK_INDEX] = BLOG_INVALID_UINT32; + blog_len_tbl[i][BLOG_TARGET_MARK_INDEX] = BLOG_INVALID_UINT32; + } + blog_len_tbl_idx = 0; + + BLOG_UNLOCK_TBL(); + return 0; +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_set_dscp_tbl + * Description : Set the values learnt from iptables rule for DSCP mangle. + * Parameters : + * idx : DSCP match value + * val : DSCP target value + * Returns : + * Zero : Success + * Non-zero : Fail + *------------------------------------------------------------------------------ + */ +int blog_set_dscp_tbl(uint8_t idx, uint8_t val) +{ + BLOG_LOCK_TBL(); + + blog_dscp_tbl[idx] = val; + + BLOG_UNLOCK_TBL(); + return 0; +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_clr_dscp_tbl + * Description : Clear the table for DSCP mangle. + * Returns : + * Zero : Success + * Non-zero : Fail + *------------------------------------------------------------------------------ + */ +int blog_clr_dscp_tbl() +{ + int i; + + BLOG_LOCK_TBL(); + + for ( i = 0; i < BLOG_MAX_DSCP_TBLSZ; i++ ) + blog_dscp_tbl[i] = BLOG_INVALID_UINT8; + + BLOG_UNLOCK_TBL(); + return 0; +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_set_tos_tbl + * Description : Set the values learnt from iptables rule for TOS mangle. + * Parameters : + * idx : TOS match value + * val : TOS target value + * Returns : + * Zero : Success + * Non-zero : Fail + *------------------------------------------------------------------------------ + */ +int blog_set_tos_tbl(uint8_t idx, uint8_t val) +{ + BLOG_LOCK_TBL(); + + blog_tos_tbl[idx] = val; + + BLOG_UNLOCK_TBL(); + return 0; +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_clr_tos_tbl + * Description : Clear the table for TOS mangle. + * Returns : + * Zero : Success + * Non-zero : Fail + *------------------------------------------------------------------------------ + */ +int blog_clr_tos_tbl() +{ + int i; + + BLOG_LOCK_TBL(); + + for ( i = 0; i < BLOG_MAX_TOS_TBLSZ; i++ ) + blog_tos_tbl[i] = BLOG_INVALID_UINT8; + + BLOG_UNLOCK_TBL(); + return 0; +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_pre_mod_hook + * Description : Called by flow cache prior to the modification phase. + * Parameters : + * blog_p : Pointer to the Blog_t object + * nbuff_p : Pointer to a NBuff + * Returns : + * PKT_DONE : Success + * PKT_DROP : Drop the packet + * PKT_NORM : Return to normal network stack + *------------------------------------------------------------------------------ + */ +int blog_pre_mod_hook(Blog_t *blog_p, void *nbuff_p) +{ + FkBuff_t *fkb_p = PNBUFF_2_FKBUFF(nbuff_p); + BlogIpv4Hdr_t *ip_p = (BlogIpv4Hdr_t *)&fkb_p->data[blog_p->ip_offset]; + + if ( blog_p->lenPrior ) + { + int i; + + for ( i = blog_len_tbl_idx; i >= 0; i-- ) + { + if ( (ip_p->len >= blog_len_tbl[i][BLOG_MIN_LEN_INDEX]) && + (ip_p->len <= blog_len_tbl[i][BLOG_MAX_LEN_INDEX]) ) + { + blog_mangl_params[BLOG_LEN_PARAM_INDEX] = blog_len_tbl[i][BLOG_TARGET_MARK_INDEX]; + break; + } + else + blog_mangl_params[BLOG_LEN_PARAM_INDEX] = blog_len_tbl[i][BLOG_ORIGINAL_MARK_INDEX]; + } + } + + if ( blog_p->dscpMangl ) + { + blog_mangl_params[BLOG_DSCP_PARAM_INDEX] = blog_dscp_tbl[ip_p->tos>>XT_DSCP_SHIFT]; + } + + if ( blog_p->tosMangl ) + { + blog_mangl_params[BLOG_TOS_PARAM_INDEX] = blog_tos_tbl[ip_p->tos]; + } + + return PKT_DONE; +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_post_mod_hook + * Description : Called by flow cache after the modification phase. + * Parameters : + * blog_p : Pointer to the Blog_t object + * nbuff_p : Pointer to a NBuff + * Returns : + * Zero : Success + * Non-zero : Fail + *------------------------------------------------------------------------------ + */ +int blog_post_mod_hook(Blog_t *blog_p, void *nbuff_p) +{ + FkBuff_t *fkb_p = PNBUFF_2_FKBUFF(nbuff_p); + + if ( blog_p->lenPrior ) + { + fkb_p->mark = blog_mangl_params[BLOG_LEN_PARAM_INDEX]; + } + + if ( blog_p->dscpMangl ) + { + if ( blog_mangl_params[BLOG_DSCP_PARAM_INDEX] != BLOG_INVALID_UINT8 ) + { + struct iphdr *ip_p = (struct iphdr *)(fkb_p->data + blog_p->ip_offset + + (sizeof(blog_p->tx.l2hdr) - sizeof(blog_p->rx.l2hdr))); + ipv4_change_dsfield(ip_p, (uint8_t)(~XT_DSCP_MASK), + (uint8_t)(blog_mangl_params[BLOG_DSCP_PARAM_INDEX] << XT_DSCP_SHIFT)); + } + } + + if ( blog_p->tosMangl ) + { + if ( blog_mangl_params[BLOG_TOS_PARAM_INDEX] != BLOG_INVALID_UINT8 ) + { + struct iphdr *ip_p = (struct iphdr *)(fkb_p->data + blog_p->ip_offset + + (sizeof(blog_p->tx.l2hdr) - sizeof(blog_p->rx.l2hdr))); + ipv4_change_dsfield(ip_p, 0, (uint8_t)blog_mangl_params[BLOG_TOS_PARAM_INDEX]); + } + } + + return 0; +} + + +static struct notifier_block net_nb = +{ + .notifier_call = blog_notify_netevent, +}; + +#if defined(CONFIG_NET_IPGRE) || defined(CONFIG_NET_IPGRE_MODULE) || defined(CONFIG_ACCEL_PPTP) +/* + * Macro specific to parsing: Used in blog_gre_rcv(). + * - Fetch the next encapsulation + * - set the hdr_p to point to next next header start + */ +#define BLOG_PARSE(tag, length, proto) h_proto = (proto); \ + hdr_p += (length); \ + ix++; \ + blog_print( "BLOG_PARSE %s: length<%d> proto<0x%04x>", \ + #tag, length, ntohs(h_proto) ); + +/* + *------------------------------------------------------------------------------ + * Function : blog_parse_l2hdr + * Description : Given a packet quickly parse the L2 header + * Parameters : + * fkb_p : Pointer to a fast kernel buffer + * h_proto : First encapsulation type + : NULL : if the parsing failed or not an IPv4 or IPv6 Hdr + : ip_p : pointer to first IPv4 or IPv6 Hdr if the + : parsing was successful up to it + * Return values: + * : Pointer to first IPv4 or IPv6 header + *------------------------------------------------------------------------------ + */ +static inline +BlogIpv4Hdr_t * _blog_parse_l2hdr( struct fkbuff *fkb_p, uint32_t h_proto ) +{ + int ix; + char * hdr_p; + BlogIpv4Hdr_t *ip_p; + + BLOG_DBG( + if ((fkb_p!=FKB_NULL) && + ((h_proto==TYPE_ETH)||(h_proto==TYPE_PPP)||(h_proto==TYPE_IP))) + { + blog_assertr(((fkb_p!=FKB_NULL) + && ((h_proto==TYPE_ETH)||(h_proto==TYPE_PPP) + ||(h_proto==TYPE_IP))), NULL ); + } ); + blog_print( "fkb<%p> data<%p> len<%d> h_proto<%u>", + fkb_p, fkb_p->data, (int)fkb_p->len, h_proto ); + + /* PACKET PARSE PHASE */ + + /* initialize locals */ + hdr_p = fkb_p->data; + ix = 0; + ip_p = (BlogIpv4Hdr_t *)NULL; + h_proto = htons(h_proto); + + switch ( h_proto ) /* First Encap */ + { + case htons(TYPE_ETH): /* first encap: XYZoE */ + /* Check whether multicast logging support is enabled */ + if (((BlogEthHdr_t*)hdr_p)->macDa.u8[0] & 0x1) /* mcast or bcast */ + { + blog_print( "ABORT multicast MAC" ); + goto done; + } + /* PS. Multicast over PPPoE would not have multicast MacDA */ + BLOG_PARSE( ETH, (int)BLOG_ETH_HDR_LEN, *((uint16_t*)hdr_p+6) ); + break; + + case htons(TYPE_PPP): /* first encap: PPPoA */ + if ( unlikely(ix != 0) ) + goto done; + BLOG_PARSE( PPP, (int)BLOG_PPP_HDR_LEN, *(uint16_t*)hdr_p ); + break; + + case htons(TYPE_IP): /* first encap: IPoA */ + ip_p = (BlogIpv4Hdr_t *)hdr_p; + goto done; + + default: + break; + } + + if ( unlikely(ix > BLOG_ENCAP_MAX)) goto done; + switch ( h_proto ) /* parse Broadcom Tags */ + { + case htons(BLOG_ETH_P_BRCM6TAG): + BLOG_PARSE( BRCM6, BLOG_BRCM6_HDR_LEN, *((uint16_t*)hdr_p+2) ); + break; + + case htons(BLOG_ETH_P_BRCM4TAG): + BLOG_PARSE( BRCM4, BLOG_BRCM4_HDR_LEN, *((uint16_t*)hdr_p+1) ); + break; + + default: + break; + } + + do /* parse VLAN tags */ + { + if ( unlikely(ix > BLOG_ENCAP_MAX)) goto done; + switch ( h_proto ) + { + case htons(BLOG_ETH_P_8021Q): + case htons(BLOG_ETH_P_8021AD): + BLOG_PARSE( VLAN, BLOG_VLAN_HDR_LEN, *((uint16_t*)hdr_p+1) ); + break; + + default: + goto _blog_parse_l2hdr_eth_type; + } + } while(1); + +_blog_parse_l2hdr_eth_type: + if ( unlikely(ix > BLOG_ENCAP_MAX)) goto done; + switch ( h_proto ) + { + case htons(BLOG_ETH_P_PPP_SES): + BLOG_PARSE( PPPOE, BLOG_PPPOE_HDR_LEN, *((uint16_t*)hdr_p+3) ); + goto _blog_parse_l2_ppp_ip; + + case htons(BLOG_PPP_IPV6): + case htons(BLOG_ETH_P_IPV6): + case htons(BLOG_PPP_IPV4): + case htons(BLOG_ETH_P_IPV4): + ip_p = (BlogIpv4Hdr_t *)hdr_p; + goto done; + + default : + blog_print( "ABORT UNKNOWN Rx h_proto 0x%04x", + (uint16_t) ntohs(h_proto) ); + goto done; + } /* switch ( h_proto ) */ + +_blog_parse_l2_ppp_ip: + { + switch ( h_proto ) + { + case htons(BLOG_PPP_IPV6): + case htons(BLOG_PPP_IPV4): + ip_p = (BlogIpv4Hdr_t *)hdr_p; + goto done; + + default : + blog_print( "ABORT UNKNOWN Rx h_proto 0x%04x", + (uint16_t) ntohs(h_proto) ); + goto done; + } /* switch ( h_proto ) */ + } + +done: + return ip_p; +} + +int blog_rcv_chk_gre(struct fkbuff *fkb_p, uint32_t h_proto, uint16_t *gflags_p) +{ + BlogIpv4Hdr_t* ip_p; + char * hdr_p; + uint16_t *grehdr_p; + BlogGreIeFlagsVer_t gre_flags = {.u16 = 0 }; + + ip_p = _blog_parse_l2hdr( fkb_p, h_proto ); + + if (ip_p != NULL) + { + int ver = (*(uint8_t*)ip_p) >> 4; + uint32_t ip_proto = -BLOG_IPPROTO_GRE; + + blog_print( "Rcv Check GRE or PPTP" ); + + if ( ver == 4 ) + { + if ( unlikely(*(uint8_t*)ip_p != 0x45) ) + { + blog_print( "ABORT IP ver<%d> len<%d>", ip_p->ver, ip_p->ihl ); + return 0; + } + ip_proto = ip_p->proto; + } + else if ( ver == 6 ) + { + /* Support extension headers? */ + ip_proto = ((BlogIpv6Hdr_t*)ip_p)->nextHdr; + } + + if ( ip_proto == BLOG_IPPROTO_GRE ) + { + hdr_p = (char *)ip_p; + hdr_p += (ver == 4)? BLOG_IPV4_HDR_LEN : BLOG_IPV6_HDR_LEN; + grehdr_p = (uint16_t*)hdr_p; + *gflags_p = gre_flags.u16 = ntohs(*(uint16_t*)grehdr_p); + + if ( gre_flags.ver) + { + return PPTP_GRE_VER_1; + } + else + { + return PPTP_GRE_VER_0; + } + } + } + + *gflags_p = 0; + return PPTP_GRE_NONE; +} + +int blog_xmit_chk_gre(struct sk_buff *skb_p, uint32_t h_proto) +{ + if (skb_p && blog_gre_tunnel_accelerated()) + { + BlogIpv4Hdr_t* ip_p; + struct fkbuff * fkb_p; + char * hdr_p; + uint16_t *grehdr_p; + BlogGreIeFlagsVer_t gre_flags = {.u16 = 0 }; + + blog_print( "Xmit Check GRE or PPTP" ); + + fkb_p = (struct fkbuff*) ((uintptr_t)skb_p + BLOG_OFFSETOF(sk_buff,fkbInSkb)); + ip_p = _blog_parse_l2hdr( fkb_p, h_proto ); + + if (ip_p != NULL) + { + int ver = (*(uint8_t*)ip_p) >> 4; + uint32_t ip_proto = ~BLOG_IPPROTO_GRE; + + if ( ver == 4 ) + { + ip_proto = ip_p->proto; + } + else if ( ver == 6 ) + { + /* Support extension headers? */ + ip_proto = ((BlogIpv6Hdr_t*)ip_p)->nextHdr; + } + + if ( ip_proto == BLOG_IPPROTO_GRE ) + { + hdr_p = (char *)ip_p; + hdr_p += (ver == 4)? BLOG_IPV4_HDR_LEN : BLOG_IPV6_HDR_LEN; + grehdr_p = (uint16_t*)hdr_p; + gre_flags.u16 = ntohs(*(uint16_t*)grehdr_p); + if (gre_flags.ver) + { + //printk("Xmit PPTP_GRE_VER_1 !!!\n"); + return PPTP_GRE_VER_1; + } + else + { + //printk("Xmit PPTP_GRE_VER_0 !!!\n"); + return PPTP_GRE_VER_0; + } + } + } + + } + return PPTP_GRE_NONE; +} + + + +#endif + +#if defined(CONFIG_NET_IPGRE) || defined(CONFIG_NET_IPGRE_MODULE) +/* + *------------------------------------------------------------------------------ + * Function : blog_gre_rcv + * Description : Given a packet quickly detect whether it is a GRE packet. + * If yes then do the other processing based on the GRE flags. + * Parameters : + * fkb_p : Pointer to a fast kernel buffer + * dev_p : Pointer to the net_device on which the packet arrived. + * h_proto : First encapsulation type + * tunl_pp : Pointer to pointer to GRE tunnel + * pkt_seqno_p : Pointer to received packet seqno + * Return values: + * BLOG_GRE_RCV_NO_GRE: + * : Either the packet is not GRE or it cannot be + * accelerated. + * BLOG_GRE_RCV_NO_SEQNO: + * : Received packet does not have seqno. + * BLOG_GRE_RCV_IN_SEQ: + * : GRE tunnel is configured with seqno and the received packet + * : seqno is in sync with the tunnel seqno. + * BLOG_GRE_RCV_NO_TUNNEL: + * : Could not find the GRE tunnel matching with packet. + * BLOG_GRE_RCV_FLAGS_MISSMATCH: + * : GRE flags in the received packet does not match the flags + * : in the configured GRE tunnel. + * BLOG_GRE_RCV_CHKSUM_ERR: + * : Received packet has bad GRE checksum. + * BLOG_GRE_RCV_OOS_LT: + * : GRE tunnel is configured with seqno and the received packet + * : seqno is out-of-seq (OOS) and less than the next seqno + * : expected by the tunnel seqno. + * BLOG_GRE_RCV_OOS_GT: + * : GRE tunnel is configured with seqno and the received packet + * : seqno is out-of-seq and greater than the next seqno + * : expected by the tunnel. + * Note : The *tunl_pp pointer makes all the tunnel fields available + * (including seqno). The tunnel seqno and pkt_seqno can + * be used to implement functions to put received packets + * in sequence before giving the packets to flow cache + * (i.e. invoking the blog_rx_hook_g()). + *------------------------------------------------------------------------------ + */ +int blog_gre_rcv( struct fkbuff *fkb_p, void * dev_p, uint32_t h_proto, + void **tunl_pp, uint32_t *pkt_seqno_p) +{ + BlogIpv4Hdr_t* ip_p; + int ret = BLOG_GRE_RCV_NOT_GRE; + + ip_p = _blog_parse_l2hdr( fkb_p, h_proto ); + + if (ip_p != NULL) + { + int ver = (*(uint8_t*)ip_p) >> 4; + + if ( ver == 4 ) + { + blog_print( "BLOG PARSE IPv4:" ); + + /* + * Abort parse + * - If not IPv4 or with options. + * - If this is a unicast and fragmented IP Pkt, let it pass through the + * network stack, as intermediate fragments do not carry a + * full upper layer protocol to determine the port numbers. + */ + if ( unlikely(*(uint8_t*)ip_p != 0x45) ) + { + blog_print( "ABORT IP ver<%d> len<%d>", ip_p->ver, ip_p->ihl ); + goto pkt_not_gre; + } + if ( ip_p->proto == BLOG_IPPROTO_GRE ) + { + blog_print( "BLOG PARSE GRE:" ); + if (blog_gre_rcv_check_fn != NULL) + ret = blog_gre_rcv_check_fn( dev_p, ip_p, + fkb_p->len - ((uintptr_t)ip_p - (uintptr_t)fkb_p->data), + tunl_pp, pkt_seqno_p ); + } + } + else if ( ver == 6 ) + { + blog_print( "BLOG PARSE IPv6:" ); + + /* Support extension headers? */ + if ( ((BlogIpv6Hdr_t*)ip_p)->nextHdr == BLOG_IPPROTO_GRE ) + { + blog_print( "BLOG PARSE GRE:" ); + if (blog_gre6_rcv_check_fn != NULL) + ret = blog_gre6_rcv_check_fn( dev_p, (BlogIpv6Hdr_t *)ip_p, + fkb_p->len - ((uintptr_t)ip_p - (uintptr_t)fkb_p->data), + tunl_pp, pkt_seqno_p ); + } + } + } + +pkt_not_gre: + return ret; +} + +void blog_gre_xmit(struct sk_buff *skb_p, uint32_t h_proto) +{ + if (skb_p && skb_p->tunl && blog_gre_tunnel_accelerated()) + { + BlogIpv4Hdr_t* ip_p; + struct fkbuff * fkb_p; + + /* non-accelerated GRE tunnel US case we need to sync seqno */ + blog_print( "non-XL GRE Tunnel" ); + + fkb_p = (struct fkbuff*) ((uintptr_t)skb_p + + BLOG_OFFSETOF(sk_buff,fkbInSkb)); + ip_p = _blog_parse_l2hdr( fkb_p, h_proto ); + + if (ip_p != NULL) + { + int ver = (*(uint8_t*)ip_p) >> 4; + + blog_print( "tunl<%p> skb<%p> data<%p> len<%u> ip_p<%p> " + "l2_data_len<%u>", + skb_p->tunl, skb_p, skb_p->data, skb_p->len, ip_p, + skb_p->len - (uint32_t)((uintptr_t) ip_p - (uintptr_t) skb_p->data)); + + if ( ver == 4 ) + { + if (blog_gre_xmit_update_fn != NULL) + blog_gre_xmit_update_fn((struct ip_tunnel *)skb_p->tunl, ip_p, + skb_p->len - ((uintptr_t) ip_p - (uintptr_t) skb_p->data)); + } + else if ( ver == 6 ) + { + if (blog_gre6_xmit_update_fn != NULL) + blog_gre6_xmit_update_fn((struct ip6_tnl *)skb_p->tunl, (BlogIpv6Hdr_t *)ip_p, + skb_p->len - ((uintptr_t) ip_p - (uintptr_t) skb_p->data)); + } + } + } +} +#endif + +#if defined(CONFIG_ACCEL_PPTP) + +int blog_pptp_rcv( struct fkbuff *fkb_p, uint32_t h_proto, uint32_t *rcv_pktSeq) +{ + BlogIpv4Hdr_t* ip_p; + char * hdr_p; + uint16_t *grehdr_p; + BlogGreIeFlagsVer_t gre_flags = {.u16 = 0 }; + uint16_t call_id = 0; + uint32_t saddr, rcv_pktAck = 0; + + int ret = BLOG_PPTP_RCV_NOT_PPTP; + + ip_p = _blog_parse_l2hdr( fkb_p, h_proto ); + + if (ip_p != NULL) + { + blog_print( "BLOG PARSE IPv4:" ); + + /* + * Abort parse + * - If not IPv4 or with options. + * - If this is a unicast and fragmented IP Pkt, let it pass through the + * network stack, as intermediate fragments do not carry a + * full upper layer protocol to determine the port numbers. + */ + if ( unlikely(*(uint8_t*)ip_p != 0x45) ) + { + blog_print( "ABORT IP ver<%d> len<%d>", ip_p->ver, ip_p->ihl ); + goto pkt_not_pptp; + } + + if ( ip_p->proto == BLOG_IPPROTO_GRE ) + { + hdr_p = (char *)ip_p; + hdr_p += BLOG_IPV4_HDR_LEN; + grehdr_p = (uint16_t*)hdr_p; + gre_flags.u16 = ntohs(*(uint16_t*)grehdr_p); + + /* the pkt is PPTP with seq number */ + if (gre_flags.seqIe && gre_flags.keyIe && gre_flags.ver) + { + blog_print( "BLOG PARSE PPTP:" ); + call_id = ntohs(*(uint16_t*) (grehdr_p + 3)); + *rcv_pktSeq = ntohl(*(uint32_t*) (grehdr_p + 4)); + saddr = blog_read32_align16( (uint16_t *)&ip_p->sAddr ); + + blog_print( "\nincoming pptp pkt's seq = %d, callid= %d\n", *rcv_pktSeq , call_id); + if(gre_flags.ackIe) /* the pkt is PPTP with ack number */ + { + rcv_pktAck = ntohl(*(uint32_t*) (grehdr_p + 6)); + blog_print( "rcv_pktAck = %d \n", rcv_pktAck ); + } + + if (blog_pptp_rcv_check_fn != NULL) + ret = blog_pptp_rcv_check_fn(call_id, rcv_pktSeq, + rcv_pktAck, saddr ); + + } + } + } + +pkt_not_pptp: + return ret; +} + +void blog_pptp_xmit(struct sk_buff *skb_p, uint32_t h_proto) +{ + if (skb_p && blog_gre_tunnel_accelerated()) + { + BlogIpv4Hdr_t* ip_p; + struct fkbuff * fkb_p; + char * hdr_p; + uint16_t *grehdr_p; + BlogGreIeFlagsVer_t gre_flags = {.u16 = 0 }; + uint16_t call_id = 0; + uint32_t seqNum = 0, ackNum = 0; + uint32_t saddr; + uint32_t daddr; + + /* non-accelerated PPTP tunnel US case we need to sync seqno */ + blog_print( "non-XL PPTP Tunnel" ); + + fkb_p = (struct fkbuff*) ((uintptr_t)skb_p + BLOG_OFFSETOF(sk_buff,fkbInSkb)); + ip_p = _blog_parse_l2hdr( fkb_p, h_proto ); + + if (ip_p != NULL && (*(uint8_t*)ip_p) >> 4 == 4 + && ip_p->proto == BLOG_IPPROTO_GRE ) + { + hdr_p = (char *)ip_p; + hdr_p += BLOG_IPV4_HDR_LEN; + grehdr_p = (uint16_t*)hdr_p; + gre_flags.u16 = ntohs(*(uint16_t*)grehdr_p); + + /* the pkt is PPTP with seq number */ + if (gre_flags.seqIe && gre_flags.keyIe && gre_flags.ver) + { + call_id = ntohs(*(uint16_t*) (grehdr_p + 3)); + seqNum = ntohl(*(uint32_t*) (grehdr_p + 4)); + + saddr = blog_read32_align16( (uint16_t *)&ip_p->sAddr ); + daddr = blog_read32_align16( (uint16_t *)&ip_p->dAddr ); + + blog_print( "call id = %d, seqNum = %d, daddr = %X\n", + call_id, seqNum, daddr ); + if(gre_flags.ackIe) /* the pkt is PPTP with ack number */ + { + ackNum = ntohl(*(uint32_t*) (grehdr_p + 6)); + blog_print( "ackNum = %d \n", ackNum ); + } + + if (blog_pptp_xmit_update_fn != NULL) + blog_pptp_xmit_update_fn(call_id, seqNum, ackNum, daddr); + } + } + } +} +#endif + +/* + *------------------------------------------------------------------------------ + * Function : blog_ptm_us_bonding + * Description : Sets/Clears the PTM US bonding mode for the flow + * Parameters : + * blog_p : Pointer to a blog + * mode : enable=1, disable=0 + * Note : FIXME This is a temporary fix and should be removed shortly. + *------------------------------------------------------------------------------ + */ +void blog_ptm_us_bonding( struct sk_buff *skb_p, int mode ) +{ + blog_assertv( (skb_p != (struct sk_buff *)NULL) ); + + if ((skb_p != NULL) && + ( likely(skb_p->blog_p != BLOG_NULL) )) + { + skb_p->blog_p->ptm_us_bond = mode; + } +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_is_config_netdev_mac + * Description : This function checks whether the device's MAC address should be + * used for addition/deletion to/from host MAC address table. + * On the LAN side, the bridge device MAC address is used instead + * of each LAN device's MAC address. + * There are some device's which are ignored like: point-to-point, + * PPP, etc. + * Important devices are: WAN and LAN bridge. + * Parameters : + * ptr : Pointer to the net device + * incl_vmacs : include macs of virtual devs(ex:containers etc..) + * Return values: + * 1 : Use the device MAC address + * 0 : Ignore the device MAC address + *------------------------------------------------------------------------------ + */ +int blog_is_config_netdev_mac(void *ptr, unsigned long incl_vmacs) +{ + struct net_device *dev_p = (struct net_device *) ptr; +#if defined(CONFIG_BCM_KF_WANDEV) + struct net_device *root_dev_p = netdev_path_get_root(dev_p); +#endif + + if ( (dev_p->priv_flags & IFF_POINTOPOINT) || (dev_p->priv_flags & IFF_ISATAP) ) + return 0; + +#if defined(CONFIG_BCM_KF_PPP) + if (dev_p->priv_flags & IFF_PPP) + return 0; +#endif + if(incl_vmacs){ + return 1; + }else{ + if ( (dev_p->priv_flags & IFF_EBRIDGE) +#if defined(CONFIG_BCM_OVS) + || blog_is_ovs_internal_dev((void *)dev_p) +#endif +#if defined(CONFIG_BCM_KF_WANDEV) + || (root_dev_p->priv_flags & IFF_WANDEV) +#endif + ) + return 1; + else + return 0; + } +} + +void blog_get_stats(void) +{ + blog_skip_reason_t skip_reason; + blog_free_reason_t free_reason; + + blog_ctx_p->info_stats.blog_skip = 0; + for (skip_reason = blog_skip_reason_unknown; + skip_reason < blog_skip_reason_max; skip_reason++) + { + blog_ctx_p->info_stats.blog_skip + += blog_ctx_p->blog_skip_stats_table[skip_reason]; + } + + blog_ctx_p->info_stats.blog_free = 0; + for (free_reason = blog_free_reason_unknown; + free_reason < blog_free_reason_max; free_reason++) + { + blog_ctx_p->info_stats.blog_free + += blog_ctx_p->blog_free_stats_table[free_reason]; + } + + printk("blog_info stats:\n"); + printk("blog_total = %u blog_avail = %u blog_mem_fails = %u\n", + blog_ctx_p->blog_total, blog_ctx_p->blog_avail, + blog_ctx_p->blog_mem_fails); + printk("blog_extends = %u blog_extend_fails = %u\n", + blog_ctx_p->blog_extends, blog_ctx_p->blog_extend_fails); + printk("blog_extend_max_engg = %u blog_extend_size_engg = %u\n", + (unsigned int)BLOG_EXTEND_MAX_ENGG, (unsigned int)BLOG_EXTEND_SIZE_ENGG); + printk("blog_get = %u blog_put = %u\n", + blog_ctx_p->info_stats.blog_get, blog_ctx_p->info_stats.blog_put); + printk("blog_xfer = %u blog_clone = %u blog_copy = %u\n", + blog_ctx_p->info_stats.blog_xfer, blog_ctx_p->info_stats.blog_clone, + blog_ctx_p->info_stats.blog_copy); + printk("blog_skip = %u blog_free = %u\n", + blog_ctx_p->info_stats.blog_skip, blog_ctx_p->info_stats.blog_free); + printk("blog_min_avail = %u\n", blog_ctx_p->info_stats.blog_min_avail); + + printk("\nblog_skip stats:\n"); + for (skip_reason = blog_skip_reason_unknown; + skip_reason < blog_skip_reason_max; skip_reason++) + printk("%s = %u\n", str_blog_skip_reason[skip_reason], + blog_ctx_p->blog_skip_stats_table[skip_reason]); + + printk("\nblog_free stats:\n"); + for (free_reason = blog_free_reason_unknown; + free_reason < blog_free_reason_max; free_reason++) + printk("%s = %u\n", str_blog_free_reason[free_reason], + blog_ctx_p->blog_free_stats_table[free_reason]); +} + +void blog_reset_stats(void) +{ + memset(&blog_ctx_g.info_stats, 0, sizeof(blog_info_stats_t)); + blog_ctx_g.info_stats.blog_min_avail = U32_MAX; + memset(&blog_ctx_g.blog_skip_stats_table, 0, sizeof(uint32_t) * blog_skip_reason_max); + memset(&blog_ctx_g.blog_free_stats_table, 0, sizeof(uint32_t) * blog_free_reason_max); +} + + +/* + *------------------------------------------------------------------------------ + * Function Name: blog_drv_ioctl + * Description : Main entry point to handle user applications IOCTL requests + * Flow Cache Utility. + * Returns : 0 - success or error + *------------------------------------------------------------------------------ + */ +static long blog_drv_ioctl(struct file *filep, unsigned int command, + unsigned long arg) +{ + blog_ioctl_t cmd; + int ret = BLOG_SUCCESS; + + if ( command > BLOG_IOCTL_INVALID ) + cmd = BLOG_IOCTL_INVALID; + else + cmd = (blog_ioctl_t)command; + + blog_print( "cmd<%d> %s arg<%lu>", + command, blog_drv_ioctl_name[cmd], arg ); + + /* protect the fc linked lists by disabling all interrupts */ + BLOG_LOCK_BH(); + + switch ( cmd ) + { + case BLOG_IOCTL_GET_STATS: + { + blog_get_stats(); + break; + } + + case BLOG_IOCTL_RESET_STATS: + { + blog_reset_stats(); + break; + } + + case BLOG_IOCTL_DUMP_BLOG: + { + blog_ctx_p->blog_dump = arg; + break; + } + + default : + { + printk( "Invalid cmd[%u]\n", command ); + ret = BLOG_ERROR; + } + } + + BLOG_UNLOCK_BH(); + + return ret; + +} /* blog_drv_ioctl */ + +/* + *------------------------------------------------------------------------------ + * Function Name: blog_drv_open + * Description : Called when a user application opens this device. + * Returns : 0 - success + *------------------------------------------------------------------------------ + */ +int blog_drv_open(struct inode *inode, struct file *filp) +{ + blog_print( "Access Blog Char Device" ); + return BLOG_SUCCESS; +} /* blog_drv_open */ + + + +/* + *------------------------------------------------------------------------------ + * Function : __init_blog + * Description : Incarnates the blog system during kernel boot sequence, + * in phase subsys_initcall() + *------------------------------------------------------------------------------ + */ +static int __init __init_blog( void ) +{ + /* Clear the feature tables for per-packet modification */ + blog_clr_len_tbl(); + blog_clr_dscp_tbl(); + blog_clr_tos_tbl(); + blog_reset_stats(); + + /* Register a character device for Ioctl handling */ + if ( register_chrdev(BLOG_DRV_MAJOR, BLOG_DRV_NAME, &blog_drv_g.fops) ) + { + printk( CLRerr "BLOG %s Unable to get major number <%d>" CLRnl, + __FUNCTION__, BLOG_DRV_MAJOR); + return BLOG_ERROR; + } + + nfskb_p = alloc_skb( 0, GFP_ATOMIC ); + blog_cttime_update_fn = (blog_cttime_upd_t) NULL; + blog_extend( BLOG_POOL_SIZE_ENGG ); /* Build preallocated pool */ + BLOG_DBG( printk( CLRb "BLOG blog_dbg<%p> = %d\n" + "%d Blogs allocated of size %lu" CLRnl, + &blog_dbg, blog_dbg, + BLOG_POOL_SIZE_ENGG, (unsigned long)sizeof(Blog_t) );); + register_netevent_notifier(&net_nb); + + printk( CLRb "BLOG %s Initialized" CLRnl, BLOG_VERSION ); + return 0; +} + +subsys_initcall(__init_blog); + +EXPORT_SYMBOL(_blog_emit); +EXPORT_SYMBOL(blog_extend); + +EXPORT_SYMBOL(strBlogAction); +EXPORT_SYMBOL(strBlogEncap); + +EXPORT_SYMBOL(strRfc2684); +EXPORT_SYMBOL(rfc2684HdrLength); +EXPORT_SYMBOL(rfc2684HdrData); + +EXPORT_SYMBOL(blog_set_len_tbl); +EXPORT_SYMBOL(blog_clr_len_tbl); +EXPORT_SYMBOL(blog_set_dscp_tbl); +EXPORT_SYMBOL(blog_clr_dscp_tbl); +EXPORT_SYMBOL(blog_set_tos_tbl); +EXPORT_SYMBOL(blog_clr_tos_tbl); +EXPORT_SYMBOL(blog_pre_mod_hook); +EXPORT_SYMBOL(blog_post_mod_hook); + +#else /* !defined(CONFIG_BLOG) */ + +int blog_dbg = 0; + +int blog_support_accel_mode_g = BLOG_ACCEL_MODE_L3; /* = CC_BLOG_SUPPORT_ACCEL_MODE; */ +void blog_support_accel_mode(int accel_mode) {blog_support_accel_mode_g = BLOG_ACCEL_MODE_L3;} +int blog_support_get_accel_mode(void) {return blog_support_accel_mode_g;} + +blog_accel_mode_set_t blog_accel_mode_set_fn = NULL; +blog_tcp_ack_mflows_set_t blog_tcp_ack_mflows_set_fn = NULL; + +int blog_support_mcast_g = BLOG_MCAST_DISABLE; /* = CC_BLOG_SUPPORT_MCAST; */ +void blog_support_mcast(int enable) {blog_support_mcast_g = BLOG_MCAST_DISABLE;} + +/* = CC_BLOG_SUPPORT_MCAST_LEARN; */ +int blog_support_mcast_learn_g = BLOG_MCAST_LEARN_DISABLE; +void blog_support_mcast_learn(int enable) {blog_support_mcast_learn_g = BLOG_MCAST_LEARN_DISABLE;} + +int blog_support_ipv6_g = BLOG_IPV6_DISABLE; /* = CC_BLOG_SUPPORT_IPV6; */ +void blog_support_ipv6(int enable) {blog_support_ipv6_g = BLOG_IPV6_DISABLE;} + +int blog_notify_proc_mode_g = BLOG_NOTIFY_PROC_MODE_NOW; +void blog_set_notify_proc_mode(int mode) {blog_notify_proc_mode_g = mode;} + +blog_cttime_upd_t blog_cttime_update_fn = (blog_cttime_upd_t) NULL; + +int blog_gre_tunnel_accelerated_g = BLOG_GRE_DISABLE; +int blog_gre_tunnel_accelerated(void) { return blog_gre_tunnel_accelerated_g; } + +int blog_support_gre_g = BLOG_GRE_DISABLE; /* = CC_BLOG_SUPPORT_GRE; */ +void blog_support_gre(int enable) {blog_support_gre_g = BLOG_GRE_DISABLE;} + +#if defined(CONFIG_NET_IPGRE) || defined(CONFIG_NET_IPGRE_MODULE) +blog_gre_rcv_check_t blog_gre_rcv_check_fn = NULL; +blog_gre_xmit_upd_t blog_gre_xmit_update_fn = NULL; +blog_gre6_rcv_check_t blog_gre6_rcv_check_fn = NULL; +blog_gre6_xmit_upd_t blog_gre6_xmit_update_fn = NULL; +#endif + +blog_pptp_rcv_check_t blog_pptp_rcv_check_fn = NULL; +blog_pptp_xmit_upd_t blog_pptp_xmit_update_fn = NULL; +blog_pptp_xmit_get_t blog_pptp_xmit_get_fn = NULL; + +blog_l2tp_rcv_check_t blog_l2tp_rcv_check_fn = NULL; + +int blog_l2tp_tunnel_accelerated_g = BLOG_L2TP_DISABLE; +int blog_support_l2tp_g = BLOG_L2TP_DISABLE; /* = CC_BLOG_SUPPORT_l2TP; */ +void blog_support_l2tp(int enable) {blog_support_l2tp_g = BLOG_L2TP_DISABLE;} + +int blog_support_4o6_frag_g = BLOG_4O6_FRAG_DISABLE; +void blog_support_4o6_frag(int enable) {blog_support_4o6_frag_g = BLOG_4O6_FRAG_DISABLE;} + + +/* Stub functions for Blog APIs that may be used by modules */ +Blog_t * blog_get( void ) { return BLOG_NULL; } +void blog_put( Blog_t * blog_p ) { return; } + +Blog_t * blog_skb( struct sk_buff * skb_p) { return BLOG_NULL; } +Blog_t * blog_fkb( struct fkbuff * fkb_p ) { return BLOG_NULL; } + +Blog_t * blog_snull( struct sk_buff * skb_p ) { return BLOG_NULL; } +Blog_t * blog_fnull( struct fkbuff * fkb_p ) { return BLOG_NULL; } + +void blog_free( struct sk_buff * skb_p, blog_skip_reason_t reason ) { return; } + +void blog_skip( struct sk_buff * skb_p, blog_skip_reason_t reason ) { return; } +void blog_xfer( struct sk_buff * skb_p, const struct sk_buff * prev_p ) + { return; } +void blog_clone( struct sk_buff * skb_p, const struct blog_t * prev_p ) + { return; } +void blog_copy(struct blog_t * new_p, const struct blog_t * prev_p) + { return; } + +int blog_iq( const struct sk_buff * skb_p ) { return BLOG_IQ_PRIO_LOW; } +int blog_fc_enabled(void) { return 0; }; + +void blog_link( BlogNetEntity_t entity_type, Blog_t * blog_p, + void * net_p, uint32_t param1, uint32_t param2 ) { return; } + +void blog_notify( BlogNotify_t event, void * net_p, + unsigned long param1, unsigned long param2 ) { return; } + +void blog_notify_async(BlogNotify_t event, void *net_p, + unsigned long param1, unsigned long param2, + blog_notify_async_cb_fn_t notify_cb_fn, void *notify_cb_data_p) { return; } + +void blog_notify_async_wait(BlogNotify_t event, void *net_p, + unsigned long param1, unsigned long param2) { return; } + +unsigned long blog_request( BlogRequest_t event, void * net_p, + unsigned long param1, unsigned long param2 ) { return 0; } + +int blog_query( BlogQuery_t event, void * net_p, + uint32_t param1, uint32_t param2, unsigned long param3 ) { return 0; } + +BlogAction_t blog_filter( Blog_t * blog_p ) + { return PKT_NORM; } + +BlogAction_t blog_sinit( struct sk_buff * skb_p, void * dev_p, + uint32_t encap, uint32_t channel, uint32_t phyHdr ) + { return PKT_NORM; } + +BlogAction_t blog_finit( struct fkbuff * fkb_p, void * dev_p, + uint32_t encap, uint32_t channel, uint32_t phyHdr ) + { return PKT_NORM; } + +BlogAction_t blog_emit( void * nbuff_p, void * dev_p, + uint32_t encap, uint32_t channel, uint32_t phyHdr ) + { return PKT_NORM; } + +int blog_iq_prio( struct sk_buff * skb_p, void * dev_p, + uint32_t encap, uint32_t channel, uint32_t phyHdr ) + { return 1; } + +void blog_bind( BlogDevRxHook_t blog_rx, BlogDevTxHook_t blog_tx, + BlogNotifyHook_t blog_xx, BlogQueryHook_t blog_qr, + BlogBind_t bind) { return; } + +void blog_bind_config( BlogScHook_t blog_sc, BlogSdHook_t blog_sd, + BlogClient_t client, BlogBind_t bind ) { return; } + +int blog_flowevent_register_notifier(struct notifier_block *nb) { return 0;} + +int blog_flowevent_unregister_notifier(struct notifier_block *nb) { return 0;} + +void blog( struct sk_buff * skb_p, BlogDir_t dir, BlogEncap_t encap, + size_t len, void * data_p ) { return; } + +void blog_dump( Blog_t * blog_p ) { return; } + +void blog_lock(void) {return; } + +void blog_unlock(void) {return; } + +uint16_t blog_getTxMtu(Blog_t * blog_p) {return 0;} + +uint32_t blog_activate( Blog_t * blog_p, BlogTraffic_t traffic, + BlogClient_t client ) { return 0; } + +Blog_t * blog_deactivate( BlogActivateKey_t key, BlogTraffic_t traffic, + BlogClient_t client ) { return BLOG_NULL; } + +#if defined(CONFIG_NET_IPGRE) || defined(CONFIG_NET_IPGRE_MODULE) +int blog_gre_rcv( struct fkbuff *fkb_p, void * dev_p, uint32_t h_proto, + void **tunl_pp, uint32_t *pkt_seqno_p ) { return 1; } +void blog_gre_xmit(struct sk_buff *skb_p, uint32_t h_proto) { return; } +#endif + +#if defined(CONFIG_ACCEL_PPTP) +int blog_pptp_rcv( struct fkbuff *fkb_p, uint32_t h_proto, + uint32_t *rcv_pktSeq) { return 1; } +void blog_pptp_xmit( struct sk_buff *skb_p, uint32_t h_proto ) { return; } +#endif + +void blog_ptm_us_bonding( struct sk_buff *skb_p, int mode ) { return; } + +int blog_is_config_netdev_mac(void *ptr, unsigned long incl_vmacs) { return 0; } +inline int blog_preemptible_task(void) { return 0;} + +void blog_bind_packet_accelerator( BlogPaHook_t blog_pa, BlogBind_t bind ) { return; } + +BlogAction_t blog_finit_args( struct fkbuff * fkb_p, void * dev_p, + uint32_t encap, uint32_t channel, uint32_t phyHdr, BlogFcArgs_t *fc_args ) { return 0; } +EXPORT_SYMBOL(blog_emit); + +#endif /* else !defined(CONFIG_BLOG) */ + +EXPORT_SYMBOL(blog_dbg); +EXPORT_SYMBOL(blog_support_accel_mode_g); +EXPORT_SYMBOL(blog_support_accel_mode); +EXPORT_SYMBOL(blog_support_get_accel_mode); +EXPORT_SYMBOL(blog_support_tcp_ack_mflows_g); +EXPORT_SYMBOL(blog_support_set_tcp_ack_mflows); +EXPORT_SYMBOL(blog_support_get_tcp_ack_mflows); +EXPORT_SYMBOL(blog_accel_mode_set_fn); +EXPORT_SYMBOL(blog_tcp_ack_mflows_set_fn); +EXPORT_SYMBOL(blog_ct_get_stats); +EXPORT_SYMBOL(blog_ct_put_stats_fn); +EXPORT_SYMBOL(blog_support_mcast_g); +EXPORT_SYMBOL(blog_support_mcast); +EXPORT_SYMBOL(blog_support_mcast_learn_g); +EXPORT_SYMBOL(blog_support_mcast_learn); +EXPORT_SYMBOL(blog_support_ipv6_g); +EXPORT_SYMBOL(blog_support_ipv6); +EXPORT_SYMBOL(blog_cttime_update_fn); +EXPORT_SYMBOL(blog_gre_tunnel_accelerated_g); +EXPORT_SYMBOL(blog_support_gre_g); +EXPORT_SYMBOL(blog_support_gre); +EXPORT_SYMBOL(blog_support_4o6_frag_g); +EXPORT_SYMBOL(blog_support_4o6_frag); +#if defined(CONFIG_NET_IPGRE) || defined(CONFIG_NET_IPGRE_MODULE) +EXPORT_SYMBOL(blog_gre_rcv_check_fn); +EXPORT_SYMBOL(blog_gre_xmit_update_fn); +EXPORT_SYMBOL(blog_gre6_rcv_check_fn); +EXPORT_SYMBOL(blog_gre6_xmit_update_fn); +EXPORT_SYMBOL(blog_gre_rcv); +EXPORT_SYMBOL(blog_gre_xmit); +#endif + +EXPORT_SYMBOL(blog_pptp_rcv_check_fn); +EXPORT_SYMBOL(blog_pptp_xmit_update_fn); +EXPORT_SYMBOL(blog_pptp_xmit_get_fn); + +#if defined(CONFIG_ACCEL_PPTP) +EXPORT_SYMBOL(blog_pptp_rcv); +EXPORT_SYMBOL(blog_pptp_xmit); +#endif + +EXPORT_SYMBOL(blog_l2tp_tunnel_accelerated_g); +EXPORT_SYMBOL(blog_support_l2tp_g); +EXPORT_SYMBOL(blog_support_l2tp); +EXPORT_SYMBOL(blog_l2tp_rcv_check_fn); + +EXPORT_SYMBOL(blog_get); +EXPORT_SYMBOL(blog_put); +EXPORT_SYMBOL(blog_skb); +EXPORT_SYMBOL(blog_fkb); +EXPORT_SYMBOL(blog_snull); +EXPORT_SYMBOL(blog_fnull); +EXPORT_SYMBOL(blog_free); +EXPORT_SYMBOL(blog_dump); +EXPORT_SYMBOL(blog_skip); +EXPORT_SYMBOL(blog_xfer); +EXPORT_SYMBOL(blog_clone); +EXPORT_SYMBOL(blog_copy); +EXPORT_SYMBOL(blog_iq); +EXPORT_SYMBOL(blog_fc_enabled); +EXPORT_SYMBOL(blog_gre_tunnel_accelerated); +EXPORT_SYMBOL(blog_link); +EXPORT_SYMBOL(blog_notify); +EXPORT_SYMBOL(blog_notify_async); +EXPORT_SYMBOL(blog_notify_async_wait); +EXPORT_SYMBOL(blog_bind_notify_evt_enqueue); +EXPORT_SYMBOL(blog_set_notify_proc_mode); +EXPORT_SYMBOL(blog_notify_proc_mode_g); +EXPORT_SYMBOL(blog_preemptible_task); +EXPORT_SYMBOL(blog_request); +EXPORT_SYMBOL(blog_query); +EXPORT_SYMBOL(blog_filter); +EXPORT_SYMBOL(blog_sinit); +EXPORT_SYMBOL(blog_finit); +EXPORT_SYMBOL(blog_finit_args); +EXPORT_SYMBOL(blog_lock); +EXPORT_SYMBOL(blog_unlock); +EXPORT_SYMBOL(blog_bind); +EXPORT_SYMBOL(blog_bind_config); +EXPORT_SYMBOL(blog_flowevent_register_notifier); +EXPORT_SYMBOL(blog_flowevent_unregister_notifier); +EXPORT_SYMBOL(blog_bind_packet_accelerator); +EXPORT_SYMBOL(blog_iq_prio); +EXPORT_SYMBOL(blog_getTxMtu); +EXPORT_SYMBOL(blog_activate); +EXPORT_SYMBOL(blog_deactivate); +EXPORT_SYMBOL(blog_ptm_us_bonding); +EXPORT_SYMBOL(blog_dhd_flow_update_fn); +EXPORT_SYMBOL(blog_is_config_netdev_mac); +EXPORT_SYMBOL(blog_get_hw_accel); + +EXPORT_SYMBOL(blog); + +#endif // defined(BCM_KF_BLOG) diff -urN linux-4.1.52-orig/net/core/blog_rule.c linux-4.1.52/net/core/blog_rule.c --- linux-4.1.52-orig/net/core/blog_rule.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/core/blog_rule.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,442 @@ +#if defined(CONFIG_BCM_KF_BLOG) +/* +* <:copyright-BRCM:2010:DUAL/GPL:standard +* +* Copyright (c) 2010 Broadcom +* All Rights Reserved +* +* Unless you and Broadcom execute a separate written software license +* agreement governing use of this software, this software is licensed +* to you under the terms of the GNU General Public License version 2 +* (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +* with the following added to such license: +* +* As a special exception, the copyright holders of this software give +* you permission to link this software with independent modules, and +* to copy and distribute the resulting executable under terms of your +* choice, provided that you also meet, for each linked independent +* module, the terms and conditions of the license of that module. +* An independent module is a module which is not derived from this +* software. The special exception does not apply to any modifications +* of the software. +* +* Not withstanding the above, under no circumstances may you combine +* this software in any way with any other Broadcom software provided +* under a license other than the GPL, without Broadcom's express prior +* written consent. +* +:> +*/ + +/* + ******************************************************************************* + * + * File Name : blog_rule.c + * + * Description: Implements packet modification rules that can be associated to + * a Blog. + * + ******************************************************************************* + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + *------------------------------------------------------------------------------ + * Private functions, macros and global variables. + *------------------------------------------------------------------------------ + */ + +#if defined(CC_CONFIG_BLOG_RULE_DEBUG) +#define blog_rule_assertv(cond) \ + if ( !(cond) ) { \ + printk( "BLOG RULE ASSERT %s : " #cond, __FUNCTION__ ); \ + return; \ + } +#define blog_rule_assertr(cond, rtn) \ + if ( !(cond) ) { \ + printk( "BLOG RULE ASSERT %s : " #cond CLRN, __FUNCTION__ ); \ + return rtn; \ + } +#else +#define blog_rule_assertv(cond) +#define blog_rule_assertr(cond, rtn) +#endif + +typedef struct { + struct kmem_cache *kmemCache; +} blogRule_Ctrl_t; + +static blogRule_Ctrl_t blogRuleCtrl; + +/* External hooks */ +blogRuleVlanHook_t blogRuleVlanHook = NULL; +blogRuleVlanNotifyHook_t blogRuleVlanNotifyHook = NULL; +#if (defined(CONFIG_BCM_ARL) || defined(CONFIG_BCM_ARL_MODULE)) +blogArlHook_t bcm_arl_process_hook_g = NULL; +#endif + +#undef BLOG_RULE_DECL +#define BLOG_RULE_DECL(x) #x + +static char *blogRuleCommandName[] = { + BLOG_RULE_DECL(BLOG_RULE_CMD_NOP), + BLOG_RULE_DECL(BLOG_RULE_CMD_SET_MAC_DA), + BLOG_RULE_DECL(BLOG_RULE_CMD_SET_MAC_SA), + BLOG_RULE_DECL(BLOG_RULE_CMD_SET_ETHERTYPE), + BLOG_RULE_DECL(BLOG_RULE_CMD_PUSH_VLAN_HDR), + BLOG_RULE_DECL(BLOG_RULE_CMD_POP_VLAN_HDR), + BLOG_RULE_DECL(BLOG_RULE_CMD_SET_PBITS), + BLOG_RULE_DECL(BLOG_RULE_CMD_SET_DEI), + BLOG_RULE_DECL(BLOG_RULE_CMD_SET_VID), + BLOG_RULE_DECL(BLOG_RULE_CMD_SET_VLAN_PROTO), + BLOG_RULE_DECL(BLOG_RULE_CMD_COPY_PBITS), + BLOG_RULE_DECL(BLOG_RULE_CMD_COPY_DEI), + BLOG_RULE_DECL(BLOG_RULE_CMD_COPY_VID), + BLOG_RULE_DECL(BLOG_RULE_CMD_COPY_VLAN_PROTO), +// BLOG_RULE_DECL(BLOG_RULE_CMD_XLATE_DSCP_TO_PBITS), + BLOG_RULE_DECL(BLOG_RULE_CMD_POP_PPPOE_HDR), + BLOG_RULE_DECL(BLOG_RULE_CMD_SET_DSCP), + BLOG_RULE_DECL(BLOG_RULE_CMD_DECR_TTL), + BLOG_RULE_DECL(BLOG_RULE_CMD_DECR_HOP_LIMIT), + BLOG_RULE_DECL(BLOG_RULE_CMD_DROP), + BLOG_RULE_DECL(BLOG_RULE_CMD_SET_SKB_MARK_PORT), + BLOG_RULE_DECL(BLOG_RULE_CMD_SET_SKB_MARK_QUEUE), + BLOG_RULE_DECL(BLOG_RULE_CMD_OVRD_LEARNING_VID), + BLOG_RULE_DECL(BLOG_RULE_CMD_SET_STA_MAC_ADDRESS), + BLOG_RULE_DECL(BLOG_RULE_CMD_MAX) +}; + +static void __printEthAddr(char *name, char *addr) +{ + int i; + + printk("%s : ", name); + + for(i=0; iblogRule_p; + blogRuleCount = 0; + + while(blogRule_p != NULL) + { + nextBlogRule_p = blogRule_p->next_p; + + blog_rule_free(blogRule_p); + + blogRule_p = nextBlogRule_p; + + blogRuleCount++; + } + + return blogRuleCount; +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_rule_init + * Description : Initializes a Blog Rule with no filters and no modifications. + *------------------------------------------------------------------------------ + */ +void blog_rule_init(blogRule_t *blogRule_p) +{ + blog_rule_assertv(blogRule_p != NULL); + + memset(blogRule_p, 0, sizeof(blogRule_t)); +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_rule_dump + * Description : Prints the contents of a Blog Rule. + *------------------------------------------------------------------------------ + */ +void blog_rule_dump(blogRule_t *blogRule_p) +{ + int i; + blogRuleFilter_t *filter_p; + blogRuleFilterVlan_t *vlanFilter_p; + blogRuleAction_t *action_p; + + blog_rule_assertv(blogRule_p != NULL); + + printk("Blog Rule <%p>, next <%p>\n", + blogRule_p, blogRule_p->next_p); + + filter_p = &blogRule_p->filter; + + if(filter_p->flags) + { + printk("Flags: "); + if(filter_p->flags & BLOG_RULE_FILTER_FLAGS_IS_UNICAST) + { + printk("IS_UNICAST "); + } + if(filter_p->flags & BLOG_RULE_FILTER_FLAGS_IS_MULTICAST) + { + printk("IS_MULTICAST "); + } + if(filter_p->flags & BLOG_RULE_FILTER_FLAGS_IS_BROADCAST) + { + printk("IS_BROADCAST "); + } + printk("\n"); + } + + printk("Ethernet Filters:\n"); + if(blog_rule_filterInUse(filter_p->eth.mask.h_dest)) + { + __printEthAddr("\tDA", filter_p->eth.value.h_dest); + } + if(blog_rule_filterInUse(filter_p->eth.mask.h_source)) + { + __printEthAddr("\tSA", filter_p->eth.value.h_source); + } + if(blog_rule_filterInUse(filter_p->eth.mask.h_proto)) + { + printk("\tEthertype : 0x%04X\n", filter_p->eth.value.h_proto); + } + + printk("PPPoE Header: %s\n", filter_p->hasPppoeHeader ? "Yes" : "No"); + + printk("VLAN Filters:\n"); + printk("\tNumber of Tags : <%d>\n", filter_p->nbrOfVlanTags); + for(i=0; inbrOfVlanTags; ++i) + { + vlanFilter_p = &filter_p->vlan[i]; + + if(vlanFilter_p->mask.h_vlan_TCI & BLOG_RULE_PBITS_MASK) + { + printk("\tPBITS : <%d>, tag <%d>\n", + BLOG_RULE_GET_TCI_PBITS(vlanFilter_p->value.h_vlan_TCI), i); + } + + if(vlanFilter_p->mask.h_vlan_TCI & BLOG_RULE_DEI_MASK) + { + printk("\tDEI : <%d>, tag <%d>\n", + BLOG_RULE_GET_TCI_DEI(vlanFilter_p->value.h_vlan_TCI), i); + } + + if(vlanFilter_p->mask.h_vlan_TCI & BLOG_RULE_VID_MASK) + { + printk("\tVID : <%d>, tag <%d>\n", + BLOG_RULE_GET_TCI_VID(vlanFilter_p->value.h_vlan_TCI), i); + } + + if(vlanFilter_p->mask.h_vlan_encapsulated_proto) + { + printk(" etherType : <%04x>, tag <%d>\n", + vlanFilter_p->value.h_vlan_encapsulated_proto, i); + } + } + + printk("IPv4 Filters:\n"); + if(blog_rule_filterInUse(filter_p->ipv4.mask.tos)) + { + printk("\tTOS : 0x%04X -> DSCP <%d>\n", + filter_p->ipv4.value.tos, + filter_p->ipv4.value.tos >> BLOG_RULE_DSCP_IN_TOS_SHIFT); + } + if(blog_rule_filterInUse(filter_p->ipv4.mask.ip_proto)) + { + printk("\tIP-PROTO : %d \n", filter_p->ipv4.value.ip_proto); + } + + printk("SKB Filters:\n"); + if(blog_rule_filterInUse(filter_p->skb.priority)) + { + printk("\tpriority : %d\n", filter_p->skb.priority - 1); + } + if(blog_rule_filterInUse(filter_p->skb.markFlowId)) + { + printk("\tmark->flowId : %d\n", filter_p->skb.markFlowId); + } + if(blog_rule_filterInUse(filter_p->skb.markPort)) + { + printk("\tmark->port : %d\n", filter_p->skb.markPort - 1); + } + + printk("Actions:\n"); + for(i=0; iactionCount; ++i) + { + action_p = &blogRule_p->action[i]; + + if(action_p->cmd == BLOG_RULE_CMD_SET_MAC_DA || + action_p->cmd == BLOG_RULE_CMD_SET_MAC_SA || + action_p->cmd == BLOG_RULE_CMD_SET_STA_MAC_ADDRESS) + { + printk("\t"); + __printEthAddr(blogRuleCommandName[action_p->cmd], + action_p->macAddr); + } + else + { + printk("\t%s : arg <%d>/<0x%02X>, tag <%d>\n", + blogRuleCommandName[action_p->cmd], + action_p->arg, action_p->arg, action_p->toTag); + } + } +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_rule_add_action + * Description : Adds an action to a Blog Rule. + *------------------------------------------------------------------------------ + */ +int blog_rule_add_action(blogRule_t *blogRule_p, blogRuleAction_t *action_p) +{ + int ret = 0; + + if(blogRule_p->actionCount == BLOG_RULE_ACTION_MAX) + { + printk("ERROR : Maximum number of actions reached for blogRule_p <%p>\n", + blogRule_p); + + ret = -ENOMEM; + goto out; + } + + blogRule_p->action[blogRule_p->actionCount] = *action_p; + + blogRule_p->actionCount++; + +out: + return ret; +} + +/* + *------------------------------------------------------------------------------ + * Function : blog_rule_delete_action + * Description : Set actionCount of all blogRule chain to 0 for deleting action + *------------------------------------------------------------------------------ + */ +int blog_rule_delete_action( void *rule_p ) +{ + blogRule_t *blogrule_p = (blogRule_t *)rule_p; + int ret = 0; + + while ( blogrule_p != NULL ) + { + blogrule_p->actionCount = 0; + blogrule_p = blogrule_p->next_p; + } + + return ret; +} + + +/* + *------------------------------------------------------------------------------ + * Function : __init_blog_rule + * Description : Initializes the Blog Rule subsystem. + *------------------------------------------------------------------------------ + */ +static int __init __init_blog_rule(void) +{ + int ret = 0; + + /* create a slab cache for device descriptors */ + blogRuleCtrl.kmemCache = kmem_cache_create("blog_rule", + sizeof(blogRule_t), + 0, /* align */ + SLAB_HWCACHE_ALIGN, /* flags */ + NULL); /* ctor */ + if(blogRuleCtrl.kmemCache == NULL) + { + printk("ERROR : Unable to create Blog Rule cache\n"); + + ret = -ENOMEM; + goto out; + } + + printk("BLOG Rule %s Initialized\n", BLOG_RULE_VERSION); + +out: + return ret; +} + +/* /\* */ +/* *------------------------------------------------------------------------------ */ +/* * Function : __exit_blog_rule */ +/* * Description : Brings down the Blog Rule subsystem. */ +/* *------------------------------------------------------------------------------ */ +/* *\/ */ +/* void __exit __exit_blog_rule(void) */ +/* { */ +/* kmem_cache_destroy(blogRuleCtrl.kmemCache); */ +/* } */ + +subsys_initcall(__init_blog_rule); + +EXPORT_SYMBOL(blog_rule_alloc); +EXPORT_SYMBOL(blog_rule_free); +EXPORT_SYMBOL(blog_rule_free_list); +EXPORT_SYMBOL(blog_rule_init); +EXPORT_SYMBOL(blog_rule_dump); +EXPORT_SYMBOL(blog_rule_add_action); +EXPORT_SYMBOL(blog_rule_delete_action); +EXPORT_SYMBOL(blogRuleVlanHook); +EXPORT_SYMBOL(blogRuleVlanNotifyHook); +#if (defined(CONFIG_BCM_ARL) || defined(CONFIG_BCM_ARL_MODULE)) +EXPORT_SYMBOL(bcm_arl_process_hook_g); +#endif +#endif /* defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) */ diff -urN linux-4.1.52-orig/net/core/dev.c linux-4.1.52/net/core/dev.c --- linux-4.1.52-orig/net/core/dev.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/core/dev.c 2020-02-07 01:47:54.000000000 +0800 @@ -138,6 +138,20 @@ #include "net-sysfs.h" +#if defined(CONFIG_BCM_KF_SKB_DEFINES) +#include +#include +#include "skb_defines.h" +#endif +#if defined(CONFIG_BCM_KF_LOG) +#include "linux/bcm_log.h" +#endif + +#if defined(CONFIG_BCM_KF_SW_GSO) && defined(CONFIG_BCM_SW_GSO) +#include +#include +#endif + /* Instead of increasing this, you should create a hash table. */ #define MAX_GRO_SKBS 8 @@ -184,6 +198,9 @@ static DEFINE_HASHTABLE(napi_hash, 8); static seqcount_t devnet_rename_seq; +#if defined(CONFIG_BCM_KF_SW_GSO) && defined(CONFIG_BCM_SW_GSO) && !defined(CONFIG_BCM_BPM) && !defined(CONFIG_BCM_BPM_MODULE) +static struct kmem_cache *pktBufferCache; +#endif static inline void dev_base_seq_inc(struct net *net) { @@ -346,6 +363,80 @@ } #endif +#if defined(CONFIG_BCM_KF_NETDEV_PATH) +/* Adds a NON-ROOT device to a path. A Root device is indirectly + added to a path once another device points to it */ +int netdev_path_add(struct net_device *new_dev, struct net_device *next_dev) +{ + /* new device already in a path, fail */ + if (netdev_path_is_linked(new_dev)) + return -EBUSY; + + netdev_path_next_dev(new_dev) = next_dev; + + next_dev->path.refcount++; + + return 0; +} +EXPORT_SYMBOL(netdev_path_add); + +/* Removes a device from a path */ +int netdev_path_remove(struct net_device *dev) +{ + /* device referenced by one or more interfaces, fail */ + if (!netdev_path_is_leaf(dev)) + return -EBUSY; + + /* device is the first in the list */ + /* Nothing to do */ + if (netdev_path_is_root(dev)) + return 0; + + netdev_path_next_dev(dev)->path.refcount--; + + netdev_path_next_dev(dev) = NULL; + + return 0; +} +EXPORT_SYMBOL(netdev_path_remove); + +/* Prints all devices in a path */ +void netdev_path_dump(struct net_device *dev) +{ + printk("netdev path : "); + + while (1) { + printk("%s", dev->name); + + if (netdev_path_is_root(dev)) + break; + + printk(" -> "); + + dev = netdev_path_next_dev(dev); + } + + printk("\n"); +} +EXPORT_SYMBOL(netdev_path_dump); + +int netdev_path_set_hw_subport_mcast_idx(struct net_device *dev, + unsigned int subport_idx) +{ + if (subport_idx >= NETDEV_PATH_HW_SUBPORTS_MAX) { + printk(KERN_ERR "%s : Invalid subport <%u>, max <%u>", + __func__, subport_idx, NETDEV_PATH_HW_SUBPORTS_MAX); + return -1; + } + + dev->path.hw_subport_mcast_idx = subport_idx; + + return 0; +} +EXPORT_SYMBOL(netdev_path_set_hw_subport_mcast_idx); +#endif /* CONFIG_BCM_KF_NETDEV_PATH */ + + /******************************************************************************* Protocol management and registration routines @@ -971,6 +1062,33 @@ } EXPORT_SYMBOL(dev_valid_name); +#if defined(CONFIG_BCM_KF_FAP) +/* + * Features changed due to FAP power up/down + */ +void dev_change_features(unsigned int features, unsigned int op) +{ + struct net *net; + struct net_device *dev; + + write_lock_bh(&dev_base_lock); + + for_each_net(net) { + for_each_netdev(net, dev) { + if (dev->priv_flags & (IFF_HW_SWITCH | IFF_EBRIDGE)) { + if (op) /* FAP power up = add features */ + dev->features |= features; + else /* FAP powerdown = remove features */ + dev->features &= ~features; + } + } + } + + write_unlock_bh(&dev_base_lock); +} +EXPORT_SYMBOL(dev_change_features); +#endif + /** * __dev_alloc_name - allocate a name for a device * @net: network namespace to allocate the device name in @@ -2310,6 +2428,261 @@ } EXPORT_SYMBOL(__dev_kfree_skb_irq); +#if defined(CONFIG_BCM_KF_SKB_DEFINES) +static struct task_struct *skb_free_task = NULL; +static struct sk_buff *skb_completion_queue = NULL; +static unsigned int skb_completion_queue_cnt = 0; +static DEFINE_SPINLOCK(skbfree_lock); + +/* Setting value to WDF budget + some room for SKBs + * freed by other threads */ +#define MAX_SKB_FREE_BUDGET 256 + +/* the min number of skb to wake up free task */ +static unsigned int skb_free_start_budget __read_mostly; + +static int skb_free_thread_func(void *thread_data) +{ + unsigned int budget; + struct sk_buff *skb; + struct sk_buff *free_list = NULL; + unsigned long flags; + /* wake up periodically for every 20ms */ + unsigned timeout_jiffies = msecs_to_jiffies(20); + + while (!kthread_should_stop()) { + budget = MAX_SKB_FREE_BUDGET; + +update_list: + spin_lock_irqsave(&skbfree_lock, flags); + if (free_list == NULL) { + if (skb_completion_queue) { + free_list = skb_completion_queue; + skb_completion_queue = NULL; + skb_completion_queue_cnt = 0; + } + } + spin_unlock_irqrestore(&skbfree_lock, flags); + + while (free_list && budget) { + skb = free_list; + free_list = free_list->next; + __kfree_skb(skb); + budget--; + } + + if (free_list || skb_completion_queue) { + if (budget) + goto update_list; + + /* we still have packets in Q, reschedule the task */ + yield(); + } else { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(timeout_jiffies); + } + } + return 0; +} + +static ssize_t skb_free_thread_proc_rd_func(struct file *filep, char __user *page, size_t count, loff_t *offset) +{ + int len = 0; + + if( *offset != 0) + return 0; + + len += sprintf(page, "skb_completion_queue_cnt %d \n", skb_completion_queue_cnt); + + *offset = len; + + return len; + +} /* skb_free_thread_proc_rd_func */ + +static const struct file_operations skb_free_thread_stats_fops = { + .owner = THIS_MODULE, + .read = skb_free_thread_proc_rd_func, +}; + +static struct proc_dir_entry *skb_free_thread_proc_directory, *skb_free_thread_proc_file_conf; + +static int skb_free_thread_proc_init(void) +{ + skb_free_thread_proc_directory = proc_mkdir("skb_free_thread", NULL) ; + + if (!skb_free_thread_proc_directory) goto fail_dir ; + + if ((skb_free_thread_proc_file_conf = proc_create("skb_free_thread/stats", 0644, NULL, &skb_free_thread_stats_fops)) == NULL) { + goto fail_entry; + } + + return (0) ; + +fail_entry: + printk("%s %s: Failed to create proc entry in skb_free_thread\n", __FILE__, __FUNCTION__); + remove_proc_entry("skb_free_thread" ,NULL); /* remove already registered directory */ + +fail_dir: + printk("%s %s: Failed to create directory skb_free_thread\n", __FILE__, __FUNCTION__) ; + return (-EIO) ; +} /* skb_free_thread_proc_init */ + +#ifndef SZ_32M +#define SZ_32M 0x02000000 +#endif +#ifndef SZ_64M +#define SZ_64M 0x04000000 +#endif +#ifndef SZ_128M +#define SZ_128M 0x08000000 +#endif +#ifndef SZ_256M +#define SZ_256M 0x10000000 +#endif + +struct task_struct *create_skb_free_task(void) +{ + struct task_struct *tsk; + struct sched_param param; + struct sysinfo sinfo; + + si_meminfo(&sinfo); + + if (sinfo.totalram <= (SZ_32M / sinfo.mem_unit)) + skb_free_start_budget = 16; + else if (sinfo.totalram <= (SZ_64M / sinfo.mem_unit)) + skb_free_start_budget = 32; + else if (sinfo.totalram <= (SZ_128M / sinfo.mem_unit)) + skb_free_start_budget = 64; + else if (sinfo.totalram <= (SZ_256M / sinfo.mem_unit)) + skb_free_start_budget = 128; + else + skb_free_start_budget = 256; + + tsk = kthread_create(skb_free_thread_func, NULL, "skb_free_task"); + + if (IS_ERR(tsk)) { + printk("skb_free_task creation failed\n"); + return NULL; + } + + /* Initialize the proc interface for debugging information */ + if (skb_free_thread_proc_init()!=0) + { + printk("\n%s %s: skb_free_thread_proc_init() failed\n", __FILE__, __FUNCTION__) ; + return NULL; + } + + param.sched_priority = BCM_RTPRIO_DATA_CONTROL; + sched_setscheduler(tsk, SCHED_RR, ¶m); + wake_up_process(tsk); + + printk("skb_free_task created successfully with start budget %d\n", skb_free_start_budget); + return tsk; +} + +/* queue the skb so it can be freed in thread context + * note: this thread is not binded to any cpu,and we rely on scheduler to + * run it on cpu with less load + */ +void dev_kfree_skb_thread(struct sk_buff *skb) +{ + unsigned long flags; + + if (atomic_dec_and_test(&skb->users)) { + spin_lock_irqsave(&skbfree_lock, flags); + skb->next = skb_completion_queue; + skb_completion_queue = skb; + skb_completion_queue_cnt++ ; + spin_unlock_irqrestore(&skbfree_lock, flags); + + if ((skb_free_task->state != TASK_RUNNING) && + (skb_completion_queue_cnt >= skb_free_start_budget)) + wake_up_process(skb_free_task); + } +} +EXPORT_SYMBOL(dev_kfree_skb_thread); + +/* bulk queue the skb so it can be freed in thread context + * note: this thread is not binded to any cpu,and we rely on scheduler to + * run it on cpu with less load + */ +void dev_kfree_skb_thread_bulk(struct sk_buff *skb) +{ + unsigned long flags; + struct sk_buff *skbfreelistend; + unsigned int skbcnt = 1; + + /* locate last skb of the supplied skb list */ + skbfreelistend = skb; + while (skbfreelistend->next != NULL) { + skbfreelistend = skbfreelistend->next; + /* +1 for first skb already done during init */ + skbcnt++; + } + + if (atomic_dec_and_test(&skb->users)) { + spin_lock_irqsave(&skbfree_lock, flags); + skbfreelistend->next = skb_completion_queue; + skb_completion_queue = skb; + skb_completion_queue_cnt += skbcnt; + spin_unlock_irqrestore(&skbfree_lock, flags); + + if ((skb_free_task->state != TASK_RUNNING) && + (skb_completion_queue_cnt >= skb_free_start_budget)) + wake_up_process(skb_free_task); + } +} +EXPORT_SYMBOL(dev_kfree_skb_thread_bulk); + +#include +/* NOTE: gbpm_fap_evt_hook_g is not part of gbpm_g */ +gbpm_fap_evt_hook_t gbpm_fap_evt_hook_g = (gbpm_fap_evt_hook_t)NULL; +EXPORT_SYMBOL(gbpm_fap_evt_hook_g); +static int fapdrv_thread_func(void *thread_data) +{ + while (!kthread_should_stop()) { + static int scheduled = 0; + local_bh_disable(); + + if (!scheduled) { + printk("gbpm_do_work scheduled\n"); + scheduled = 1; + } + if (gbpm_fap_evt_hook_g) + gbpm_fap_evt_hook_g(); + + local_bh_enable(); + + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + } + return 0; +} + +struct task_struct *fapDrvTask = NULL; +EXPORT_SYMBOL(fapDrvTask); +struct task_struct *create_fapDrvTask(void) +{ + struct sched_param param; + /* Create FAP driver thread for dqm work handling */ + fapDrvTask = kthread_create(fapdrv_thread_func, NULL, "bcmFapDrv"); + + if (IS_ERR(fapDrvTask)) { + printk("fapDrvTask creation failed\n"); + return 0; + } + + param.sched_priority = BCM_RTPRIO_DATA_CONTROL; + sched_setscheduler(fapDrvTask, SCHED_RR, ¶m); + + wake_up_process(fapDrvTask); + return fapDrvTask; +} + +#endif + void __dev_kfree_skb_any(struct sk_buff *skb, enum skb_free_reason reason) { if (in_irq() || irqs_disabled()) @@ -2353,7 +2726,11 @@ static void skb_warn_bad_offload(const struct sk_buff *skb) { +#if defined(CONFIG_BCM_KF_DEBUGGING_DISABLED_FIX) + static const netdev_features_t null_features __attribute__((unused)) = 0; +#else static const netdev_features_t null_features = 0; +#endif struct net_device *dev = skb->dev; const char *driver = ""; @@ -2665,13 +3042,48 @@ { unsigned int len; int rc; +#if (defined(CONFIG_BCM_KF_FAP_GSO_LOOPBACK) && defined(CONFIG_BCM_FAP_GSO_LOOPBACK)) + unsigned int devId = bcm_is_gso_loopback_dev(dev); +#endif if (!list_empty(&ptype_all) || !list_empty(&dev->ptype_all)) dev_queue_xmit_nit(skb, dev); len = skb->len; trace_net_dev_start_xmit(skb, dev); +#if (defined(CONFIG_BCM_KF_FAP_GSO_LOOPBACK) && defined(CONFIG_BCM_FAP_GSO_LOOPBACK)) + if (devId && bcm_gso_loopback_hw_offload) { + if (skb_shinfo(skb)->nr_frags || + skb_is_gso(skb) || + (skb->ip_summed == CHECKSUM_PARTIAL)) { + skb->xmit_more = more ? 1 : 0; + rc = bcm_gso_loopback_hw_offload(skb, devId); + if (rc == NETDEV_TX_OK) + txq_trans_update(txq); + } else if (!skb->recycle_hook) { + /* To avoid any outof order packets, send all the + * locally generated packets through gso loop back + */ + + /* TODO: we are classifying the traffic as local based + * on recycle hook. But cloned forwarding traffic can + * also have recyle_hook as NULL, so this traffic will + * make an extra trip through FAP unnecessarily. + * But we don't expecet a lot of traffic in this case, + * so this shoud be okay for now. Later add a flag in + * skb and mark the skb as local in local_out hook. + */ + skb->xmit_more = more ? 1 : 0; + rc = bcm_gso_loopback_hw_offload(skb, devId); + if (rc == NETDEV_TX_OK) + txq_trans_update(txq); + } else + rc = netdev_start_xmit(skb, dev, txq, more); + } else + rc = netdev_start_xmit(skb, dev, txq, more); +#else rc = netdev_start_xmit(skb, dev, txq, more); +#endif trace_net_dev_xmit(skb, rc, dev, len); return rc; @@ -3412,6 +3824,17 @@ net_timestamp_check(netdev_tstamp_prequeue, skb); +#if defined(CONFIG_BCM_KF_WANDEV) + /* mark IFFWAN flag in skb based on dev->priv_flags */ + if (skb->dev) { + unsigned int mark = skb->mark; + skb->mark |= SKBMARK_SET_IFFWAN_MARK(mark, ((skb->dev->priv_flags & IFF_WANDEV) ? 1:0)); +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG_FEATURE) + if (skb->blog_p) + skb->blog_p->isWan = (skb->dev->priv_flags & IFF_WANDEV) ? 1 : 0; +#endif /* defined(CONFIG_BLOG_FEATURE) */ + } +#endif /* CONFIG_BCM_KF_WANDEV */ trace_netif_rx(skb); #ifdef CONFIG_RPS if (static_key_false(&rps_needed)) { @@ -3639,6 +4062,18 @@ } EXPORT_SYMBOL_GPL(netdev_rx_handler_register); +#if defined(CONFIG_BCM_KF_VLAN) && (defined(CONFIG_BCM_VLAN) || defined(CONFIG_BCM_VLAN_MODULE)) +int (*bcm_vlan_handle_frame_hook)(struct sk_buff **) = NULL; +#endif +#if (defined(CONFIG_BCM_MCAST) || defined(CONFIG_BCM_MCAST_MODULE)) && defined(CONFIG_BCM_KF_MCAST) +void (*bcm_mcast_def_pri_queue_hook)(struct sk_buff *) = NULL; +EXPORT_SYMBOL(bcm_mcast_def_pri_queue_hook); +#endif +#if defined(CONFIG_BCM_KF_TMS) && defined(CONFIG_BCM_TMS_MODULE) +int (*bcm_1ag_handle_frame_check_hook)(struct sk_buff *) = NULL; +int (*bcm_3ah_handle_frame_check_hook)(void *dev, unsigned short protocol, unsigned char *datPtr) = NULL; +#endif + /** * netdev_rx_handler_unregister - unregister receive handler * @dev: device to unregister a handler from @@ -3687,11 +4122,26 @@ bool deliver_exact = false; int ret = NET_RX_DROP; __be16 type; +#if defined(CONFIG_BCM_KF_VLAN) && (defined(CONFIG_BCM_VLAN) || defined(CONFIG_BCM_VLAN_MODULE)) && defined(CONFIG_BCM_PTP_1588) + struct skb_shared_hwtstamps *timestamp; +#endif net_timestamp_check(!netdev_tstamp_prequeue, skb); trace_netif_receive_skb(skb); +#if defined(CONFIG_BCM_KF_WANDEV) + /* mark IFFWAN flag in skb based on dev->priv_flags */ + if (skb->dev) { + unsigned int mark = skb->mark; + skb->mark |= SKBMARK_SET_IFFWAN_MARK(mark, ((skb->dev->priv_flags & IFF_WANDEV) ? 1:0)); +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG_FEATURE) + if (skb->blog_p) + skb->blog_p->isWan = (skb->dev->priv_flags & IFF_WANDEV) ? 1 : 0; +#endif + } +#endif + orig_dev = skb->dev; skb_reset_network_header(skb); @@ -3701,7 +4151,47 @@ pt_prev = NULL; +#if (defined(CONFIG_BCM_MCAST) || defined(CONFIG_BCM_MCAST_MODULE)) && defined(CONFIG_BCM_KF_MCAST) + if (bcm_mcast_def_pri_queue_hook != NULL) + bcm_mcast_def_pri_queue_hook(skb); + if (!skb->in_dev) { + skb->in_dev = orig_dev; + } +#endif + +#if defined(CONFIG_BCM_KF_VLAN) && (defined(CONFIG_BCM_VLAN) || defined(CONFIG_BCM_VLAN_MODULE)) +#if defined(CONFIG_BCM_KF_TMS) && defined(CONFIG_BCM_TMS_MODULE) + /* Check if 802.1ag service is started. */ + if (bcm_1ag_handle_frame_check_hook) { + /* We want to skip vlanctl for 1ag packet. */ + if (bcm_1ag_handle_frame_check_hook(skb)) + goto skip_vlanctl; + } + /* Check if 802.3ah service is started. */ + if (bcm_3ah_handle_frame_check_hook) { + /* We want to skip vlanctl for 3ah packet, or for any packet + * when 3ah loopback was enabled. */ + if ((bcm_3ah_handle_frame_check_hook(skb->dev, ntohs(skb->protocol), skb->data))) + goto skip_vlanctl; + } +#endif /* #if defined(CONFIG_BCM_TMS_MODULE) */ + +#if (defined(CONFIG_BCM_PTP_1588)) + timestamp = skb_hwtstamps(skb); + if (timestamp->hwtstamp.tv64 == 0) +#endif + +another_round: + if (bcm_vlan_handle_frame_hook && (ret = bcm_vlan_handle_frame_hook(&skb)) != 0) + goto out; + +#if defined(CONFIG_BCM_KF_TMS) && defined(CONFIG_BCM_TMS_MODULE) +skip_vlanctl: +#endif +#else another_round: +#endif + skb->skb_iif = skb->dev->ifindex; __this_cpu_inc(softnet_data.processed); @@ -3761,6 +4251,23 @@ } rx_handler = rcu_dereference(skb->dev->rx_handler); + +#if defined(CONFIG_BCM_KF_PPP) + if (skb->protocol == __constant_htons(ETH_P_PPP_SES) || + skb->protocol == __constant_htons(ETH_P_PPP_DISC)) { + if (!memcmp(skb_mac_header(skb), skb->dev->dev_addr, ETH_ALEN)) +#if defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) && defined(CONFIG_BCM_KF_LOG) + { + bcmFun_t *bcmFun = bcmFun_get(BCM_FUN_ID_BOND_RX_HANDLER); + if (!bcmFun || !bcmFun(rx_handler)) +#endif + goto skip_rx_handler; +#if defined(CONFIG_BCM_KF_KBONDING) && defined(CONFIG_BCM_KERNEL_BONDING) && defined(CONFIG_BCM_KF_LOG) + } +#endif + } +#endif + if (rx_handler) { if (pt_prev) { ret = deliver_skb(skb, pt_prev, orig_dev); @@ -3788,9 +4295,17 @@ * and set skb->priority like in vlan_do_receive() * For the time being, just ignore Priority Code Point */ +#if defined(CONFIG_BCM_KF_TMS) && defined(CONFIG_BCM_TMS_MODULE) + if (skb->protocol != htons(ETH_P_8021AG) && skb->protocol != htons(ETH_P_8023AH) && + ((struct vlan_hdr *)skb->data)->h_vlan_encapsulated_proto != htons(ETH_P_8021AG)) +#endif skb->vlan_tci = 0; } +#if defined(CONFIG_BCM_KF_PPP) +skip_rx_handler: +#endif + type = skb->protocol; /* deliver only exact match when indicated */ @@ -5753,7 +6268,11 @@ dev->flags = (flags & (IFF_DEBUG | IFF_NOTRAILERS | IFF_NOARP | IFF_DYNAMIC | IFF_MULTICAST | IFF_PORTSEL | +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) IFF_AUTOMEDIA)) | +#else + IFF_AUTOMEDIA | IFF_NOMULTIPATH | IFF_MPBACKUP)) | +#endif (dev->flags & (IFF_UP | IFF_VOLATILE | IFF_PROMISC | IFF_ALLMULTI)); @@ -6009,7 +6528,31 @@ static int dev_new_index(struct net *net) { int ifindex = net->ifindex; +#if defined(CONFIG_BCM_KF_LIMITED_IFINDEX) + int loop = 0; +#endif + for (;;) { +#if defined(CONFIG_BCM_KF_LIMITED_IFINDEX) +#define BCM_MAX_IFINDEX 128 + + /* On DSL CPE, xtm interfaces are created/deleted when + * the link goes up/down. On a noisy dsl link that goes + * down and up frequently, the xtm ifindex may get higher + * and higher if we don't reuse the lower ifindex values. + * that were released when the interfaces were deleted. + * Therefore, we limit index value to BCM_MAX_IFINDEX, + * and try reuse ifindex that had been released. + */ + WARN_ONCE((++loop > BCM_MAX_IFINDEX), + "Cannot get new ifindex. All %d index values had " + "been used.\n", BCM_MAX_IFINDEX); + /* try reuse ifindex that had been released. */ + if (ifindex >= BCM_MAX_IFINDEX) + ifindex = 0; + +#undef BCM_MAX_IFINDEX +#endif if (++ifindex <= 0) ifindex = 1; if (!__dev_get_by_index(net, ifindex)) @@ -6350,6 +6893,729 @@ return 0; } +#if defined(CONFIG_BCM_KF_SW_GSO) && defined(CONFIG_BCM_SW_GSO) + +#define BCM_SW_GSO_MAX_HEADERS 16 +#define BCM_SW_GSO_MAX_SEGS 64 +#define BCM_L3_PROTO_IPV4 1 +#define BCM_L3_PROTO_IPV6 2 + +enum { + BCM_SW_GSO_SEG_TYPE_SINGLE=0, + BCM_SW_GSO_SEG_TYPE_FIRST, + BCM_SW_GSO_SEG_TYPE_MIDDLE, + BCM_SW_GSO_SEG_TYPE_LAST, + BCM_SW_GSO_SEG_TYPE_MAX +}; + +struct gso_hdrs{ + void *ethhdr; /*including VLAN's */ + void *ppphdr; + void *ipv4hdr; + void *ipv6hdr; + void *l4hdr; + union { + struct { + uint8_t ethhdrlen;/*including VLAN's */ + uint8_t ppphdrlen; + uint8_t ipv4hdrlen; + uint8_t ipv6hdrlen; + uint8_t l4hdrlen; + uint8_t totlen; + uint8_t l3proto; + uint8_t l4proto; + }; + uint64_t lenproto; + }; + void *txdev; + HardStartXmitFuncP xmit_fn; + uint32_t ipv6_flowlbl; + uint32_t tcpseq; + uint32_t ipv6_fragid; + uint16_t ipv4_id; + uint16_t ip_fragoff; + uint8_t ip_fragneeded; + uint8_t ip_lastfrag; + uint8_t tcp_segtype; +}; + +static inline void bcm_push_hdr(FkBuff_t *fkb, void *src, unsigned int len) +{ + fkb->data -= len; + fkb->len +=len; + memcpy(fkb->data, src, len); +} + +static inline int bcm_add_l2hdr(FkBuff_t *fkb, struct gso_hdrs *hdrs) +{ + if(hdrs->ppphdrlen) + { + bcm_push_hdr(fkb, hdrs->ppphdr, hdrs->ppphdrlen); + /*TODO adjust ppphdr len ip +2 */ + } + bcm_push_hdr(fkb, hdrs->ethhdr, hdrs->ethhdrlen); + return 0; +} + +static inline int bcm_add_l3hdr(FkBuff_t *fkb, struct gso_hdrs *hdrs) +{ + if(hdrs->l3proto == BCM_L3_PROTO_IPV4){ + struct iphdr *ipv4; + bcm_push_hdr(fkb, hdrs->ipv4hdr, hdrs->ipv4hdrlen); + ipv4 = (struct iphdr *)fkb->data; + /* update len */ + ipv4->tot_len = htons(fkb->len); + + if(hdrs->ip_fragneeded){ + /* update offset */ + ipv4->frag_off = htons((hdrs->ip_fragoff >> 3)); + /* update flags */ + if( !hdrs->ip_lastfrag) + ipv4->frag_off |= htons(IP_MF); + } + + /* update IPID */ + + /* update csum */ + ipv4->check = 0; + ipv4->check = ip_fast_csum((unsigned char *)ipv4, ipv4->ihl); + } + else if(hdrs->l3proto == BCM_L3_PROTO_IPV6){ + struct ipv6hdr *ipv6; + + if(hdrs->ip_fragneeded){ + struct frag_hdr fh; + + fh.nexthdr = ((struct ipv6hdr *)hdrs->ipv6hdr)->nexthdr; + fh.reserved = 0; + fh.identification = hdrs->ipv6_fragid; + fh.frag_off = htons(hdrs->ip_fragoff); + bcm_push_hdr(fkb, &fh, sizeof(struct frag_hdr)); + } + + bcm_push_hdr(fkb, hdrs->ipv6hdr, hdrs->ipv6hdrlen); + ipv6 = (struct ipv6hdr *)fkb->data; + + /* update len */ + ipv6->payload_len = htons( fkb->len - hdrs->ipv6hdrlen); + + if(hdrs->ip_fragneeded){ + ipv6->nexthdr =NEXTHDR_FRAGMENT; + } + + /* update IPID */ + /* update flags */ + } + else{ + printk(KERN_ERR "%s:Unsuppoerted L3 protocol %d\n", __func__, hdrs->l3proto); + return -1; + } + return 0; +} + +static inline __sum16 bcm_sw_gso_l4_csum(struct gso_hdrs *hdrs, uint32_t len, uint16_t proto, __wsum base) +{ + if(hdrs->l3proto == BCM_L3_PROTO_IPV4 ){ + struct iphdr *ipv4= hdrs->ipv4hdr; + return csum_tcpudp_magic(ipv4->saddr, ipv4->daddr, len, proto, base); + } else { + struct ipv6hdr *ipv6 = hdrs->ipv6hdr; + return csum_ipv6_magic(&ipv6->saddr, &ipv6->daddr, len, proto, base); + } +} + +static inline void bcm_add_tcphdr(FkBuff_t *fkb, struct gso_hdrs *hdrs) +{ + struct tcphdr *th; + bcm_push_hdr(fkb, hdrs->l4hdr, hdrs->l4hdrlen); + /* update seq */ + th = (struct tcphdr *)fkb->data; + th->seq = htonl(hdrs->tcpseq); + + if(hdrs->tcp_segtype != BCM_SW_GSO_SEG_TYPE_SINGLE){ + + /* clear cwr in all packets except first */ + if(hdrs->tcp_segtype != BCM_SW_GSO_SEG_TYPE_FIRST) + th->cwr = 0; + + /* clear fin, psh in all packets except last */ + if((hdrs->tcp_segtype != BCM_SW_GSO_SEG_TYPE_LAST)) + th->fin = th->psh = 0; + } + + th->check = 0; + th->check = bcm_sw_gso_l4_csum(hdrs, fkb->len, IPPROTO_TCP, csum_partial(fkb->data, fkb->len, 0)); +} + +static void bcm_sw_gso_recycle_func(void *pNBuff, unsigned long context, uint32_t flags) +{ +#if defined(CONFIG_BCM_BPM) || defined(CONFIG_BCM_BPM_MODULE) + gbpm_free_buf(PFKBUFF_TO_PDATA(PNBUFF_2_PBUF(pNBuff), BCM_PKT_HEADROOM)); +#else + kmem_cache_free(pktBufferCache, PNBUFF_2_PBUF(pNBuff)); +#endif +} + +static inline int bcm_sw_alloc_pkt_buffers(int npkts, void **buffer_pool) +{ +#if defined(CONFIG_BCM_BPM) || defined(CONFIG_BCM_BPM_MODULE) + /* allocate buffers from BPM */ + if( gbpm_alloc_mult_buf(npkts, buffer_pool) == GBPM_ERROR ){ + return -1; + } + return 0; +#else + int i; + void *buf; + + for(i=0; i < npkts; i++){ + buf = kmem_cache_alloc(pktBufferCache, GFP_ATOMIC); + if(buf == NULL) + break; + buffer_pool[i] = (void *)PFKBUFF_TO_PDATA((void *)(buf), BCM_PKT_HEADROOM); + } + + /* check if we allocated all the buffers requested */ + if( i && i != npkts){ + printk("%s %s: Not enough memory for packet buffer allocation\n", __FILE__, __FUNCTION__); + npkts =i; + for(i=0; ilen - hdrs->totlen; + offset = hdrs->totlen; + + mss = skb_shinfo(skb)->gso_size; + + if(mss == 0) + { + mss = bytesleft; + npkts =1; + } + else + npkts = DIV_ROUND_UP(bytesleft, mss); + + if(npkts > BCM_SW_GSO_MAX_SEGS){ + printk(KERN_ERR "%s: npkts=%d greater than max segs(%d) \n", + __func__, npkts, BCM_SW_GSO_MAX_SEGS); + return -1; + } + + if( bcm_sw_alloc_pkt_buffers(npkts, buffer_pool) != 0 ) + return -1; + + hdrs->tcp_segtype = BCM_SW_GSO_SEG_TYPE_FIRST; + + do { + + if(cur_pkt > npkts){ + /* we shoud never be here,if we are then most likely there is + * some thing wrong with mss, allocated packets are already xmitted, + * no need to free them here + */ + printk(KERN_ERR "%s:error pktcount =%d > allocated buffers=%d mss=%d\n", + __func__, cur_pkt, npkts, mss); + return -1; + } + /* initialize fkb */ + pbuf = buffer_pool[cur_pkt++]; + + fkb = fkb_init(pbuf, BCM_PKT_HEADROOM, pbuf + hdrs->totlen, 0); + fkb->recycle_hook = bcm_sw_gso_recycle_func; + fkb->recycle_context = 0; + fkb->mark = skb->mark; + fkb->priority = skb->priority; + + payloadlen = min(mss, bytesleft); + + /* copy data from original skb to new packet */ + + /* copy tcp payload */ + skb_copy_bits(skb, offset, fkb->data, payloadlen); + fkb->len += payloadlen; + + offset +=payloadlen; + bytesleft -=payloadlen; + + if(bytesleft == 0){ + if(likely(hdrs->tcp_segtype != BCM_SW_GSO_SEG_TYPE_FIRST)) + hdrs->tcp_segtype = BCM_SW_GSO_SEG_TYPE_LAST; + else + hdrs->tcp_segtype = BCM_SW_GSO_SEG_TYPE_SINGLE; + } + + /* copy tcp hdr & update fields */ + bcm_add_tcphdr(fkb, hdrs); + + hdrs->tcpseq +=payloadlen; + hdrs->tcp_segtype = BCM_SW_GSO_SEG_TYPE_MIDDLE; + + /* copy l3 hdr */ + bcm_add_l3hdr(fkb, hdrs); + + /* copy l2 hdr */ + bcm_add_l2hdr(fkb,hdrs); + + + fkb->dirty_p = _to_dptr_from_kptr_(fkb->data + fkb->len); + /* xmit packet */ + hdrs->xmit_fn(FKBUFF_2_PNBUFF(fkb), hdrs->txdev); + + } while (bytesleft); + + if(cur_pkt < npkts){ + int i; + printk(KERN_ERR "%s:error pktcount =%d < allocated buffers=%d\n", __func__, cur_pkt, npkts); + for(i=cur_pkt; i < npkts; i++) + bcm_sw_gso_recycle_func(PDATA_TO_PFKBUFF(buffer_pool[i], BCM_PKT_HEADROOM), 0, 0 ); + } + + return 0; +} + +static uint32_t bcm_sw_gso_gen_ipv6id(void) +{ + static atomic_t ipv6_fragmentation_id; + uint32_t old, new; + + do { + old = atomic_read(&ipv6_fragmentation_id); + new = old + 1; + if (!new) + new = 1; + } while (atomic_cmpxchg(&ipv6_fragmentation_id, old, new) != old); + return new; +} + +static int bcm_sw_gso_ip_fragment(struct sk_buff *skb, struct gso_hdrs *hdrs) +{ + int npkts; + FkBuff_t *fkb; + void *pbuf; + unsigned short bytesleft; + unsigned short offset; + unsigned short mss; + unsigned short payloadlen; + int cur_pkt=0; + /*TODO do kmalloc if stack size is an issue*/ + void *buffer_pool[BCM_SW_GSO_MAX_SEGS]; + + + /* calculate the number of packets needed to transmit this skb */ + bytesleft = skb->len - (hdrs->totlen - hdrs->l4hdrlen); + offset = hdrs->totlen - hdrs->l4hdrlen; + + mss = skb_shinfo(skb)->gso_size; + + if(mss == 0){ + mss = bytesleft; + npkts =1; + } + else{ + mss = mss & ~7; /* make it multiple of 8 */ + npkts = DIV_ROUND_UP(bytesleft, mss); + } + + if(npkts > BCM_SW_GSO_MAX_SEGS){ + printk(KERN_ERR "%s: npkts=%d greater than max segs(%d) \n", + __func__, npkts, BCM_SW_GSO_MAX_SEGS); + return -1; + } + + if( bcm_sw_alloc_pkt_buffers(npkts, buffer_pool) != 0 ) + return -1; + + hdrs->ip_fragoff = 0; + hdrs->ip_lastfrag = 0; + if(bytesleft - mss){ + /* perform IP fragmentation */ + hdrs->ip_fragneeded = 1; + } + + if(hdrs->l3proto == BCM_L3_PROTO_IPV6){ + hdrs->ipv6_fragid = htonl(bcm_sw_gso_gen_ipv6id()); + } + + /* perform UDPchecksum */ + if( (hdrs->l4proto == IPPROTO_UDP) && (skb->ip_summed == CHECKSUM_PARTIAL)) + { + struct udphdr *uh= hdrs->l4hdr; + uh->check = 0; + int csum_offset = hdrs->l4hdr - hdrs->ethhdr; + + uh->check = bcm_sw_gso_l4_csum(hdrs, skb->len-csum_offset, IPPROTO_UDP, + skb_checksum(skb, csum_offset, skb->len-csum_offset, 0)); + } + + do{ + + if(cur_pkt > npkts){ + /* we shoud never be here,if we are then most likely there is + * some thing wrong with mss, allocated packets are already xmitted, + * no need to free them here + */ + printk(KERN_ERR "%s:error pktcount =%d > allocated buffers=%d mss=%d\n", + __func__, cur_pkt, npkts, mss); + return -1; + } + /* initialize fkb */ + pbuf = buffer_pool[cur_pkt++]; + + fkb = fkb_init(pbuf, BCM_PKT_HEADROOM, pbuf + hdrs->totlen, 0); + fkb->recycle_hook = bcm_sw_gso_recycle_func; + fkb->recycle_context = 0; + fkb->mark = skb->mark; + fkb->priority = skb->priority; + + payloadlen = min(mss, bytesleft); + + /* copy data from original skb to new packet */ + + /* copy tcp payload */ + skb_copy_bits(skb, offset, fkb->data, payloadlen); + fkb->len += payloadlen; + + + offset +=payloadlen; + bytesleft -=payloadlen; + + if(bytesleft == 0) + hdrs->ip_lastfrag = 1; + + /* copy l3 hdr */ + bcm_add_l3hdr(fkb, hdrs); + + hdrs->ip_fragoff +=payloadlen; + + /* copy l2 hdr */ + bcm_add_l2hdr(fkb,hdrs); + + fkb->dirty_p = _to_dptr_from_kptr_(fkb->data + fkb->len); + /* xmit packet */ + hdrs->xmit_fn(FKBUFF_2_PNBUFF(fkb), hdrs->txdev); + + }while (bytesleft); + + if(cur_pkt < npkts){ + int i; + printk(KERN_ERR "%s:error pktcount =%d < allocated buffers=%d\n", __func__, cur_pkt, npkts); + for(i=cur_pkt; i< npkts; i++) + bcm_sw_gso_recycle_func(PDATA_TO_PFKBUFF(buffer_pool[i], BCM_PKT_HEADROOM), 0, 0 ); + } + + return 0; +} + + +static inline int bcm_parse_gso_hdrs(struct sk_buff *skb, struct gso_hdrs *hdrs) +{ + struct iphdr *ipv4; + struct ipv6hdr *ipv6; + struct tcphdr *tcp_hdr; + int header = -1; + void *data = skb->data; + unsigned int skbheadlen = skb_headlen(skb); + /*assumes first header is ETH always */ + uint16_t proto = ETH_P_802_3; + + while(1) + { + header++; + + if(hdrs->totlen > skbheadlen){ + printk(KERN_ERR "%s: headers not present in linear data hdrslen=%u skbheadlen=%d \n", + __func__, hdrs->totlen, skbheadlen); + return -1; + } + + if (header > BCM_SW_GSO_MAX_HEADERS){ + printk(KERN_ERR "%s:Too many headers <%d>\n", __func__, header); + return -1; + } + + switch(proto) + { + case ETH_P_802_3: /* first encap: XYZoE */ + + if(header != 0) + { + return -1; + } + hdrs->ethhdr = data; + + proto = ntohs(((struct ethhdr *)data)->h_proto); + hdrs->ethhdrlen = ETH_HLEN; + hdrs->totlen += ETH_HLEN; + data += ETH_HLEN; + break; + + case ETH_P_8021Q: + case ETH_P_8021AD: + + proto = ntohs(*((uint16_t *)(data+2))); + hdrs->ethhdrlen += 4; + hdrs->totlen += 4; + data += 4; + break; + + case ETH_P_IP: + + ipv4 = data; + + hdrs->l3proto = BCM_L3_PROTO_IPV4; + proto = ipv4->protocol; + + if ((proto == IPPROTO_TCP) || (proto == IPPROTO_UDP)){ + + hdrs->ipv4hdr = data; + hdrs->ipv4_id = ntohs(ipv4->id); + hdrs->ipv4hdrlen = ipv4->ihl<<2; + hdrs->totlen += ipv4->ihl<<2; + data += ipv4->ihl<<2; + + } else { + printk(KERN_ERR "%s Unsupported L4 type=%u \n", __func__, proto); + return -1; + } + break; + + case ETH_P_IPV6: + + ipv6 = data; + + hdrs->l3proto = BCM_L3_PROTO_IPV6; + proto = ipv6->nexthdr; + + if ((proto == IPPROTO_TCP) || (proto == IPPROTO_UDP)){ + + hdrs->ipv6hdr = data; + hdrs->ipv6_flowlbl = ntohl(ip6_flowlabel(ipv6)); + hdrs->ipv6hdrlen = sizeof(struct ipv6hdr); + hdrs->totlen += sizeof(struct ipv6hdr); + data += sizeof(struct ipv6hdr); + + } else { + printk(KERN_ERR "%s Unsupported L4 type=%u \n", __func__, proto); + return -1; + } + break; + + case IPPROTO_TCP : + tcp_hdr = data; + + hdrs->l4hdr = data; + hdrs->l4proto = IPPROTO_TCP; + hdrs->l4hdrlen = tcp_hdr->doff <<2; + hdrs->totlen += tcp_hdr->doff <<2; + hdrs->tcpseq = ntohl(tcp_hdr->seq); + return 0 ; + + case IPPROTO_UDP : + hdrs->l4proto = IPPROTO_UDP; + hdrs->l4hdr = data; + hdrs->l4hdrlen = sizeof(struct udphdr) ; + hdrs->totlen += sizeof(struct udphdr); + return 0 ; + + /*TODO add PPP */ + + default : + printk(KERN_ERR "%s:UNSUPPORTED protocol %u ", __func__, proto); + return -1; + } /* switch (headerType) */ + } + return -1; +} + + +static inline int bcm_sw_gso(struct sk_buff *skb, struct net_device *txdev, HardStartXmitFuncP xmit_fn) +{ + struct gso_hdrs hdrs; + int ret=-1; + + /*check if this skb needs GSO processing*/ + if(skb_is_gso(skb) || skb_shinfo(skb)->nr_frags) + { + + memset(&hdrs,0, sizeof(struct gso_hdrs)); + /* extract l2 & l3 headers and l4 proto */ + if( bcm_parse_gso_hdrs(skb, &hdrs) < 0){ + ret = -1; + goto done; + }; + + hdrs.txdev = txdev; + hdrs.xmit_fn =xmit_fn; + + /*perform GSO and transmit packets*/ + + switch(hdrs.l4proto) { + case IPPROTO_TCP: + ret = bcm_sw_gso_tcp_segment(skb, &hdrs); + break; + + case IPPROTO_UDP: + ret = bcm_sw_gso_ip_fragment(skb, &hdrs); + break; + + default: + printk(KERN_ERR "%s:SW GSO not supported for protocol=%d \n",__func__, hdrs.l4proto); + ret= -1; + goto done; + } + } else if((skb->ip_summed == CHECKSUM_PARTIAL)){ + /*only csum is needed */ + memset(&hdrs,0, sizeof(struct gso_hdrs)); + if( bcm_parse_gso_hdrs(skb, &hdrs) < 0){ + ret = -1; + goto done; + }; + + switch(hdrs.l4proto) { + case IPPROTO_TCP: + { + struct tcphdr *th= hdrs.l4hdr; + th->check = 0; + /*TODO calulate l4 len based on l3 len */ + th->check = bcm_sw_gso_l4_csum(&hdrs, skb->len-(hdrs.l4hdr - hdrs.ethhdr), + IPPROTO_TCP, csum_partial(hdrs.l4hdr, skb->len-(hdrs.l4hdr - hdrs.ethhdr), 0)); + break; + } + + case IPPROTO_UDP: + { + struct udphdr *uh= hdrs.l4hdr; + uh->check = 0; + /*TODO calulate l4 len based on l3 len */ + uh->check = bcm_sw_gso_l4_csum(&hdrs, skb->len-(hdrs.l4hdr - hdrs.ethhdr), + IPPROTO_UDP, csum_partial(hdrs.l4hdr, skb->len-(hdrs.l4hdr - hdrs.ethhdr), 0)); + break; + } + + default: + printk(KERN_ERR "%s:SW GSO not supported for protocol=%d \n",__func__, hdrs.l4proto); + ret= -1; + goto done; + } + + skb->ip_summed = CHECKSUM_NONE; + xmit_fn(SKBUFF_2_PNBUFF(skb), txdev); + return 0; + }else{ + /* transmit skb as is */ + xmit_fn(SKBUFF_2_PNBUFF(skb), txdev); + return 0; + } +done: + /*free the original skb & return */ + dev_kfree_skb_any(skb); + return ret; +} + +int bcm_sw_gso_xmit(struct sk_buff *skb, struct net_device *txdev, HardStartXmitFuncP xmit_fn) +{ + return bcm_sw_gso(skb, txdev, xmit_fn); +} +EXPORT_SYMBOL(bcm_sw_gso_xmit); + +#endif /* defined(CONFIG_BCM_KF_SW_GSO) && defined(CONFIG_BCM_SW_GSO) */ + +#if (defined(CONFIG_BCM_KF_FAP_GSO_LOOPBACK) && defined(CONFIG_BCM_FAP_GSO_LOOPBACK)) +int (*bcm_gso_loopback_hw_offload)(struct sk_buff *skb, unsigned int txDevId) = NULL; +EXPORT_SYMBOL(bcm_gso_loopback_hw_offload); + +struct net_device *bcm_gso_loopback_devs[BCM_GSO_LOOPBACK_MAXDEVS]; + +void bcm_gso_loopback_devs_init(void) +{ + unsigned int i; + for (i = 0; i < BCM_GSO_LOOPBACK_MAXDEVS; i++) { + bcm_gso_loopback_devs[i] = NULL; + } +} + +static inline void bcm_gso_loopback_add_devptr(struct net_device *dev) +{ + if (!strcmp("wl0", dev->name)) + bcm_gso_loopback_devs[BCM_GSO_LOOPBACK_WL0] = dev; + else if (!strcmp("wl1", dev->name)) + bcm_gso_loopback_devs[BCM_GSO_LOOPBACK_WL1] = dev; + else if (!strcmp("wl2", dev->name)) + bcm_gso_loopback_devs[BCM_GSO_LOOPBACK_WL2] = dev; + else /* not a know device */ + return; + + printk("+++++ Added gso loopback support for dev=%s <%p>\n", + dev->name, dev); +} + +static inline void bcm_gso_loopback_del_devptr(struct net_device *dev) +{ + if (!strcmp("wl0", dev->name)) + bcm_gso_loopback_devs[BCM_GSO_LOOPBACK_WL0] = NULL; + else if (!strcmp("wl1", dev->name)) + bcm_gso_loopback_devs[BCM_GSO_LOOPBACK_WL1] = NULL; + else if (!strcmp("wl2", dev->name)) + bcm_gso_loopback_devs[BCM_GSO_LOOPBACK_WL2] = NULL; + else /* not a know device */ + return; + + printk("------ Removed gso loopback support for dev=%s <%p>\n", + dev->name, dev); +} + +inline unsigned int bcm_is_gso_loopback_dev(void *dev) +{ + int i; + + for (i = 1; i < BCM_GSO_LOOPBACK_MAXDEVS; i++) { + if (bcm_gso_loopback_devs[i] == dev) + return i; + } + + return BCM_GSO_LOOPBACK_NONE; +} + +unsigned int bcm_gso_loopback_devptr2devid(void *dev) +{ + return bcm_is_gso_loopback_dev(dev); +} +EXPORT_SYMBOL(bcm_gso_loopback_devptr2devid); + +struct net_device *bcm_gso_loopback_devid2devptr(unsigned int devId) +{ + if (devId < BCM_GSO_LOOPBACK_MAXDEVS) + return bcm_gso_loopback_devs[devId]; + else { + printk(KERN_ERR "%s: invalid devId<%d> max devs=%d\n", + __func__, devId, BCM_GSO_LOOPBACK_MAXDEVS); + return NULL; + } +} +EXPORT_SYMBOL(bcm_gso_loopback_devid2devptr); +#endif + /** * register_netdevice - register a network device * @dev: device to register @@ -6469,6 +7735,10 @@ if (dev->addr_assign_type == NET_ADDR_PERM) memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); +#if (defined(CONFIG_BCM_KF_FAP_GSO_LOOPBACK) && defined(CONFIG_BCM_FAP_GSO_LOOPBACK)) + bcm_gso_loopback_add_devptr(dev); +#endif + /* Notify protocols, that a new device appeared. */ ret = call_netdevice_notifiers(NETDEV_REGISTER, dev); ret = notifier_to_errno(ret); @@ -6758,6 +8028,9 @@ } storage->rx_dropped += (unsigned long)atomic_long_read(&dev->rx_dropped); storage->tx_dropped += (unsigned long)atomic_long_read(&dev->tx_dropped); +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_add_dev_accelerated_stats(dev, storage); +#endif return storage; } EXPORT_SYMBOL(dev_get_stats); @@ -6818,6 +8091,9 @@ struct net_device *dev; size_t alloc_size; struct net_device *p; +#if defined(CONFIG_BCM_KF_MISALIGN_MQS) + static int offset = 0; +#endif BUG_ON(strlen(name) >= sizeof(dev->name)); @@ -6842,13 +8118,32 @@ /* ensure 32-byte alignment of whole construct */ alloc_size += NETDEV_ALIGN - 1; +#if defined(CONFIG_BCM_KF_MISALIGN_MQS) + /* KU_TBD - This causes the ethernet driver to panic on boot in register_netdev + * BUG_ON(dev->reg_state != NETREG_UNINITIALIZED); */ + + /* Add an offset to break possible alignment of dev structs in cache */ + /* Note that "offset" is a static variable so it will retain its value */ + /* on each call of this function */ + alloc_size += offset; +#endif + p = kzalloc(alloc_size, GFP_KERNEL | __GFP_NOWARN | __GFP_REPEAT); if (!p) p = vzalloc(alloc_size); if (!p) return NULL; +#if defined(CONFIG_BCM_KF_MISALIGN_MQS) + dev = PTR_ALIGN(p, NETDEV_ALIGN) + offset; + /* Increment offset in preparation for the next call to this function */ + /* but don't allow it to increment excessively to avoid wasting memory */ + offset += NETDEV_ALIGN; + if (offset >= 512) + offset -= 512; +#else dev = PTR_ALIGN(p, NETDEV_ALIGN); +#endif dev->padded = (char *)dev - (char *)p; dev->pcpu_refcnt = alloc_percpu(int); @@ -7030,6 +8325,9 @@ void unregister_netdev(struct net_device *dev) { rtnl_lock(); +#if (defined(CONFIG_BCM_KF_FAP_GSO_LOOPBACK) && defined(CONFIG_BCM_FAP_GSO_LOOPBACK)) + bcm_gso_loopback_del_devptr(dev); +#endif unregister_netdevice(dev); rtnl_unlock(); } @@ -7273,6 +8571,7 @@ if (net->dev_index_head == NULL) goto err_idx; + return 0; err_idx: @@ -7550,9 +8849,44 @@ hotcpu_notifier(dev_cpu_callback, 0); dst_init(); + +#if defined(CONFIG_BCM_KF_SKB_DEFINES) + skb_free_task = create_skb_free_task(); +#if defined(CONFIG_BCM_FAP) || defined(CONFIG_BCM_FAP_MODULE) + create_fapDrvTask(); +#endif +#endif + +#if (defined(CONFIG_BCM_KF_FAP_GSO_LOOPBACK) && defined(CONFIG_BCM_FAP_GSO_LOOPBACK)) + bcm_gso_loopback_devs_init(); +#endif + +#if (defined(CONFIG_BCM_KF_SW_GSO) && defined(CONFIG_BCM_SW_GSO)) && !defined(CONFIG_BCM_BPM) && !defined(CONFIG_BCM_BPM_MODULE) + /* create a slab cache for GSO buffers */ + pktBufferCache = kmem_cache_create("pktBufferCache", + BCM_PKTBUF_SIZE, + 0, /* align */ + SLAB_HWCACHE_ALIGN, /* flags */ + NULL); /* ctor */ + if(pktBufferCache == NULL) + { + printk("%s %s: Failed to create packet buffer cache\n", __FILE__, __FUNCTION__); + return -ENOMEM; + } +#endif rc = 0; out: return rc; } subsys_initcall(net_dev_init); + + +#if defined(CONFIG_BCM_KF_VLAN) && (defined(CONFIG_BCM_VLAN) || defined(CONFIG_BCM_VLAN_MODULE)) +EXPORT_SYMBOL(bcm_vlan_handle_frame_hook); +#endif + +#if defined(CONFIG_BCM_KF_TMS) && defined(CONFIG_BCM_TMS_MODULE) +EXPORT_SYMBOL(bcm_1ag_handle_frame_check_hook); +EXPORT_SYMBOL(bcm_3ah_handle_frame_check_hook); +#endif diff -urN linux-4.1.52-orig/net/core/dev_ioctl.c linux-4.1.52/net/core/dev_ioctl.c --- linux-4.1.52-orig/net/core/dev_ioctl.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/core/dev_ioctl.c 2020-02-07 01:47:54.000000000 +0800 @@ -39,6 +39,7 @@ return 0; } + static gifconf_func_t *gifconf_list[NPROTO]; /** @@ -173,6 +174,20 @@ ifr->ifr_qlen = dev->tx_queue_len; return 0; +#if defined(CONFIG_BCM_KF_WANDEV) + case SIOCDEVISWANDEV: + if (dev->priv_flags & IFF_WANDEV) + ifr->ifr_flags = 1; + else + ifr->ifr_flags = 0; + return 0; +#endif +#if defined(CONFIG_BCM_KF_MISC_IOCTLS) + case SIOCGIFTRANSSTART: + ifr->ifr_ifru.ifru_ivalue = dev->trans_start; + return 0; +#endif + default: /* dev_ioctl() should ensure this case * is never reached @@ -307,6 +322,38 @@ ifr->ifr_newname[IFNAMSIZ-1] = '\0'; return dev_change_name(dev, ifr->ifr_newname); +#if defined(CONFIG_BCM_KF_MISC_IOCTLS) + case SIOCCIFSTATS: /* Clear stats of a device */ +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + if ( dev->clr_stats ) + { + dev->clr_stats( dev ); + } + else +#endif + { + struct net_device_stats * pStats; + if (dev->netdev_ops == NULL || dev->netdev_ops->ndo_get_stats == NULL) + { + printk("[%s.%d]: dev->netdev_ops->ndo_get_stats is %p (%s)\n", __func__, __LINE__, dev->netdev_ops->ndo_get_stats, dev->name); + return 0; + } + else + { + pStats = dev->netdev_ops->ndo_get_stats(dev); + } + if (pStats) + { + memset(pStats, 0, sizeof(struct net_device_stats)); + } + else + { + printk("ERROR: [%s.%d]: could not reset stats for device %s\n", __func__, __LINE__, dev->name); + } + } + return 0; +#endif + case SIOCSHWTSTAMP: err = net_hwtstamp_validate(ifr); if (err) @@ -439,6 +486,12 @@ case SIOCGIFMAP: case SIOCGIFINDEX: case SIOCGIFTXQLEN: +#if defined(CONFIG_BCM_KF_MISC_IOCTLS) + case SIOCGIFTRANSSTART: +#endif +#if defined(CONFIG_BCM_KF_WANDEV) + case SIOCDEVISWANDEV: +#endif dev_load(net, ifr.ifr_name); rcu_read_lock(); ret = dev_ifsioc_locked(net, &ifr, cmd); @@ -548,6 +601,9 @@ */ default: if (cmd == SIOCWANDEV || +#if defined(CONFIG_BCM_KF_MISC_IOCTLS) + cmd == SIOCCIFSTATS || +#endif cmd == SIOCGHWTSTAMP || (cmd >= SIOCDEVPRIVATE && cmd <= SIOCDEVPRIVATE + 15)) { diff -urN linux-4.1.52-orig/net/core/gbpm.c linux-4.1.52/net/core/gbpm.c --- linux-4.1.52-orig/net/core/gbpm.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/core/gbpm.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,268 @@ +#if defined(CONFIG_BCM_KF_NBUFF) +/* +<:copyright-BRCM:2009:DUAL/GPL:standard + + Copyright (c) 2009 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + + + +/* + ******************************************************************************* + * File Name : gbpm.c + ******************************************************************************* + */ +#if (defined(CONFIG_BCM_BPM) || defined(CONFIG_BCM_BPM_MODULE)) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global Buffer Pool Manager (BPM) */ + +#undef GBPM_DECL +#define GBPM_DECL(HOOKNAME) .HOOKNAME = (gbpm_ ## HOOKNAME ## _hook_t)NULL, +gbpm_t gbpm_g = +{ + GBPM_BIND() /* Static initialization to NULL of BPM "BIND" hooks */ + GBPM_USER() /* Static initialization to NULL of BPM "USER" hooks */ + .debug = 0U +}; /* End of static initialization of gbpm globals */ +EXPORT_SYMBOL(gbpm_g); + + +/* Debug macros */ + +#if defined(CC_GBPM_SUPPORT_DEBUG) +#define gbpm_print(fmt, arg...) \ + if ( gbpm_g.debug ) \ + printk( CLRc "GBPM %s :" fmt CLRnl, __FUNCTION__, ##arg ) +#define gbpm_assertv(cond) \ + if ( !cond ) { \ + printk( CLRerr "GBPM ASSERT %s : " #cond CLRnl, __FUNCTION__ ); \ + return; \ + } +#define gbpm_assertr(cond, rtn) \ + if ( !cond ) { \ + printk( CLRerr "GBPM ASSERT %s : " #cond CLRnl, __FUNCTION__ ); \ + return rtn; \ + } +#define GBPM_DBG(debug_code) do { debug_code } while(0) +#else +#define gbpm_print(fmt, arg...) NULL_STMT +#define gbpm_assertv(cond) NULL_STMT +#define gbpm_assertr(cond, rtn) NULL_STMT +#define GBPM_DBG(debug_code) NULL_STMT +#endif + +#define gbpm_error(fmt, arg...) \ + printk( CLRerr "GBPM ERROR %s :" fmt CLRnl, __FUNCTION__, ##arg) + + +#if defined(GBPM_FAP_SUPPORT) +static void gbpm_do_work(struct work_struct *); +static DECLARE_WORK(gbpm_work, gbpm_do_work); +static struct workqueue_struct *gbpm_workqueue; +extern gbpm_fap_evt_hook_t gbpm_fap_evt_hook_g; /* see net/core/dev.c */ +void gbpm_do_work(struct work_struct *work_unused) /* Do the BPM work */ +{ + /* process BPM pending events */ + if ( likely(gbpm_fap_evt_hook_g != (gbpm_fap_evt_hook_t)NULL) ) + gbpm_fap_evt_hook_g(); +} +EXPORT_SYMBOL(gbpm_do_work); + +void gbpm_queue_work(void) /* Add the BPM work */ +{ + queue_work(gbpm_workqueue, &gbpm_work); +} +EXPORT_SYMBOL(gbpm_queue_work); +#endif /* GBPM_FAP_SUPPORT */ + + +/* + * ---------------------------------------------------------------------------- + * STUBS: Default stubs, until BPM binds its service handlers. + * ---------------------------------------------------------------------------- + */ + +/* --- BPM BUF POOL --- */ +int gbpm_alloc_mult_buf_stub(uint32_t num, void **buf_p) + { return GBPM_ERROR; } +int gbpm_alloc_mult_buf_ex_stub(uint32_t num, void **buf_p, uint32_t prio) + { return GBPM_ERROR; } +void gbpm_free_mult_buf_stub(uint32_t num, void **buf_p) + { return; } +void * gbpm_alloc_buf_stub(void) + { return NULL; } +void gbpm_free_buf_stub(void *buf_p) + { return; } + +/* --- BPM SKB POOL --- */ +uint32_t gbpm_total_skb_stub(void) + { return 0U; } +uint32_t gbpm_avail_skb_stub(void) + { return 0U; } +void gbpm_attach_skb_stub(void *skbp, void *data, uint32_t len) + { return; } +void * gbpm_alloc_skb_stub(void) + { return NULL; } +void * gbpm_alloc_buf_skb_attach_stub(uint32_t len) + { return NULL; } +void * gbpm_alloc_mult_skb_stub(uint32_t num) + { return NULL; } +void gbpm_free_skb_stub(void *skbp) + { return; } +void gbpm_recycle_skb_stub(void *skbp, unsigned long context, + uint32_t recycle_action) + { return; } + +/* --- BPM pNBuff --- */ +void gbpm_recycle_pNBuff_stub(void * pNBuff, unsigned long context, + uint32_t recycle_action) + { return; } + +/* --- BPM Get Accessors --- */ +int gbpm_get_dyn_buf_lvl_stub(void) { return 1; } +uint32_t gbpm_get_total_bufs_stub(void) { return 0; } +uint32_t gbpm_get_avail_bufs_stub(void) { return 0; } +uint32_t gbpm_get_max_dyn_bufs_stub(void) { return 0; } + +/* --- BPM Runtime --- */ +int gbpm_resv_rx_buf_stub(gbpm_port_t port, uint32_t chnl, + uint32_t num_rx_buf, uint32_t bulk_alloc_cnt) + { return GBPM_ERROR; } +int gbpm_unresv_rx_buf_stub(gbpm_port_t port, uint32_t chnl) + { return GBPM_ERROR; } + +#if defined(CONFIG_BCM_BPM_BUF_TRACKING) + +gbpm_mark_buf_hook_t gbpm_mark_buf_hook_g = (gbpm_mark_buf_hook_t)NULL; +gbpm_add_ref_hook_t gbpm_add_ref_hook_g = (gbpm_add_ref_hook_t)NULL; + +EXPORT_SYMBOL(gbpm_mark_buf_hook_g); +EXPORT_SYMBOL(gbpm_add_ref_hook_g); +EXPORT_SYMBOL(gbpm_mark_buf); +EXPORT_SYMBOL(gbpm_add_ref); + +void gbpm_mark_buf( void * buf_p, void * addr, int reftype, int driver, int value, int info ) +{ + if ( gbpm_mark_buf_hook_g != NULL ) + { + return gbpm_mark_buf_hook_g( buf_p, addr, reftype, driver, value, info ); + } +} +void gbpm_add_ref( void * buf_p, int i ) +{ + if ( gbpm_mark_buf_hook_g != NULL ) + { + return gbpm_add_ref_hook_g( buf_p, i ); + } +} +#endif + +/* + *------------------------------------------------------------------------------ + * Function : gbpm_bind + * Description : Override default hooks. + *------------------------------------------------------------------------------ + */ +#undef GBPM_DECL +#define GBPM_DECL(HOOKNAME) gbpm_ ## HOOKNAME ## _hook_t HOOKNAME, + +void gbpm_bind( GBPM_BIND() uint32_t debug ) +{ + uint32_t saved_debug = gbpm_g.debug; + + { + gbpm_g.debug = debug; /* Debug print */ + +#undef GBPM_DECL +#define GBPM_DECL(HOOKNAME) \ + gbpm_print("Hook:%s[<%p>]", #HOOKNAME, HOOKNAME); + GBPM_BIND() + } + + { /* --- Bind BPM hooks --- */ +# undef GBPM_DECL +# define GBPM_DECL(HOOKNAME) gbpm_g.HOOKNAME = HOOKNAME; + GBPM_BIND() + } + + gbpm_g.debug = saved_debug; /* restore gbpm debug level */ +} + + +/* + *------------------------------------------------------------------------------ + * Function : gbpm_unbind + * Description : use default stub hooks. + *------------------------------------------------------------------------------ + */ +void gbpm_unbind( void ) +{ +#undef GBPM_DECL +#define GBPM_DECL(HOOKNAME) gbpm_ ## HOOKNAME ## _stub, + gbpm_bind( GBPM_BIND() gbpm_g.debug ); +} + + +/* + *------------------------------------------------------------------------------ + * Function : __init_gbpm + * Description : Static construction of global buffer pool manager subsystem. + *------------------------------------------------------------------------------ + */ +static int __init __init_gbpm( void ) +{ +#if defined(GBPM_FAP_SUPPORT) + /* Set up BPM workqueue - single threaded/high priority */ + gbpm_workqueue = alloc_workqueue("bpm", WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_HIGHPRI, 1); +#endif + + gbpm_unbind(); + + printk( GBPM_MODNAME GBPM_VER_STR " initialized\n" ); + return 0; +} + +subsys_initcall(__init_gbpm); + +EXPORT_SYMBOL(gbpm_bind); +EXPORT_SYMBOL(gbpm_unbind); +#endif /* (defined(CONFIG_BCM_BPM) || defined(CONFIG_BCM_BPM_MODULE)) */ + +#endif diff -urN linux-4.1.52-orig/net/core/iqos.c linux-4.1.52/net/core/iqos.c --- linux-4.1.52-orig/net/core/iqos.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/core/iqos.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,662 @@ +#if defined(CONFIG_BCM_KF_NBUFF) +/* +<:copyright-BRCM:2009:DUAL/GPL:standard + + Copyright (c) 2009 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + + +/* + ******************************************************************************* + * File Name : iqos.c + ******************************************************************************* + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_BCM_KF_LOG) +#include +#endif + + +/* Debug macros */ +#if defined(CC_IQOS_SUPPORT_DEBUG) +#define iqos_print(fmt, arg...) \ + if (iqos_debug_g) \ + printk(CLRc "IQOS %s :" fmt CLRnl, __func__, ##arg) \ +#define iqos_assertv(cond) \ + if (!cond) { \ + printk(CLRerr "IQOS ASSERT %s : " #cond CLRnl, __func__); \ + return; \ + } +#define iqos_assertr(cond, rtn) \ + if (!cond) { \ + printk(CLRerr "IQOS ASSERT %s : " #cond CLRnl, __func__); \ + return rtn; \ + } +#define IQOS_DBG(debug_code) do { debug_code } while(0) +#else +#ifndef NULL_STMT +#define NULL_STMT do { /* NULL BODY */ } while (0) +#endif +#define iqos_print(fmt, arg...) NULL_STMT +#define iqos_assertv(cond) NULL_STMT +#define iqos_assertr(cond, rtn) NULL_STMT +#define IQOS_DBG(debug_code) NULL_STMT +#endif + +#define iqos_error(fmt, arg...) \ + printk(CLRerr "IQOS ERROR %s :" fmt CLRnl, __func__, ##arg) + +#undef IQOS_DECL +#define IQOS_DECL(x) #x, /* string declaration */ + + +/*--- globals ---*/ +uint32_t iqos_enable_g = 0; /* Enable Ingress QoS feature */ +uint32_t iqos_cpu_cong_g = 0; +uint32_t iqos_debug_g = 0; + +#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT) +DEFINE_SPINLOCK(iqos_lock_g); +EXPORT_SYMBOL(iqos_lock_g); +DEFINE_SPINLOCK(iqos_cong_lock_g); +EXPORT_SYMBOL(iqos_cong_lock_g); +#endif + +/* + *------------------------------------------------------------------------------ + * Default Ingress QoS hooks. + *------------------------------------------------------------------------------ + */ +static iqos_common_hook_t iqos_add_keymask_hook_g = NULL; +static iqos_common_hook_t iqos_rem_keymask_hook_g = NULL; +static iqos_common_hook_t iqos_add_key_hook_g = NULL; +static iqos_common_hook_t iqos_rem_key_hook_g = NULL; +static iqos_common_hook_t iqos_get_key_hook_g = NULL; +static iqos_int_hook_t iqos_set_status_hook_g = NULL; +static iqos_void_hook_t iqos_flush_hook_g = NULL; + +/* + *------------------------------------------------------------------------------ + * Get the Ingress QoS priority for L4 Dest port (layer4 UDP or TCP) + *------------------------------------------------------------------------------ + */ +int iqos_prio_L4port(uint8_t ipProto, uint16_t destPort) +{ + iqos_param_t param; + int rc; + + iqos_key_param_start(¶m); + iqos_key_param_field_set(¶m, IQOS_FIELD_IP_PROTO, (uint32_t *)&ipProto, 1); + iqos_key_param_field_set(¶m, IQOS_FIELD_DST_PORT, (uint32_t *)&destPort, 2); + + rc = iqos_key_commit_and_get(¶m); + if (rc) + return IQOS_PRIO_LOW; + + if (param.action != IQOS_ACTION_PRIO) + return IQOS_PRIO_LOW; + + return param.action_value; +} + + +/* + *------------------------------------------------------------------------------ + * Add the Ingress QoS priority, and type of entry for L4 Dest port. + *------------------------------------------------------------------------------ + */ +int iqos_add_L4port(uint8_t ipProto, uint16_t destPort, iqos_ent_t ent, + iqos_prio_t prio) +{ + iqos_param_t param; + int rc; + +#if defined(CONFIG_BCM_KF_LOG) + BCM_LOG_DEBUG(BCM_LOG_ID_IQ, + "AddPort ent<%d> ipProto<%d> dport<%d> prio<%d> ", + ent, ipProto, destPort, prio); +#endif + + iqos_key_param_start(¶m); + iqos_key_param_field_set(¶m, IQOS_FIELD_IP_PROTO, (uint32_t *)&ipProto, 1); + iqos_key_param_field_set(¶m, IQOS_FIELD_DST_PORT, (uint32_t *)&destPort, 2); + iqos_key_param_action_set(¶m, IQOS_ACTION_PRIO, (uint32_t)prio); + + rc = iqos_key_commit_and_add(¶m, ent); +#if defined(CONFIG_BCM_KF_LOG) + BCM_LOG_DEBUG(BCM_LOG_ID_IQ, "rc<%d>", rc); +#endif + return rc; +} + + +/* + *------------------------------------------------------------------------------ + * Remove the L4 dest port from the Ingress QoS priority table + *------------------------------------------------------------------------------ + */ +int iqos_rem_L4port(uint8_t ipProto, uint16_t destPort, iqos_ent_t ent) +{ + iqos_param_t param; + int rc; + +#if defined(CONFIG_BCM_KF_LOG) + BCM_LOG_DEBUG(BCM_LOG_ID_IQ, "RemPort ent<%d> ipProto<%d> dport<%d> ", + ent, ipProto, destPort); +#endif + + iqos_key_param_start(¶m); + iqos_key_param_field_set(¶m, IQOS_FIELD_IP_PROTO, (uint32_t *)&ipProto, 1); + iqos_key_param_field_set(¶m, IQOS_FIELD_DST_PORT, (uint32_t *)&destPort, 2); + + rc = iqos_key_commit_and_delete(¶m, ent); +#if defined(CONFIG_BCM_KF_LOG) + BCM_LOG_DEBUG(BCM_LOG_ID_IQ, "rc<%d> ", rc); +#endif + return rc; +} + +int iqos_keymask_param_start(iqos_param_t *param) +{ + if (param == NULL) + return -EINVAL; + + memset(param, 0x0, sizeof(iqos_param_t)); + param->param_type = IQOS_PARAM_TYPE_KEYMASK; + + return 0; +} + +int iqos_keymask_param_field_set(iqos_param_t *param, uint32_t field, uint32_t *val_ptr) +{ + uint32_t offset_type; + + if (param == NULL) + return -EINVAL; + + if (param->param_type != IQOS_PARAM_TYPE_KEYMASK) + return -EINVAL; + + if (field >= IQOS_FIELD_MAX) + return -EINVAL; + + param->field_mask |= 1 << field; + + /* for offset type of keymask, it requires more parameter */ + switch (field) { + case IQOS_FIELD_OFFSET_0_TYPE: + offset_type = *(uint32_t *)val_ptr; + if (offset_type >= IQOS_OFFSET_TYPE_MAX) + return -EINVAL; + param->field_mask |= 1 << IQOS_FIELD_OFFSET_0; + param->offset0.type = offset_type; + break; + case IQOS_FIELD_OFFSET_0_START: + param->field_mask |= 1 << IQOS_FIELD_OFFSET_0; + param->offset0.start = *(uint32_t *)val_ptr; + break; + case IQOS_FIELD_OFFSET_0_SIZE: + param->field_mask |= 1 << IQOS_FIELD_OFFSET_0; + param->offset0.size = *(uint32_t *)val_ptr; + break; + case IQOS_FIELD_OFFSET_0_MASK: + param->field_mask |= 1 << IQOS_FIELD_OFFSET_0; + param->offset0.mask = *(uint32_t *)val_ptr; + break; + case IQOS_FIELD_OFFSET_1_TYPE: + offset_type = *(uint32_t *)val_ptr; + if (offset_type >= IQOS_OFFSET_TYPE_MAX) + return -EINVAL; + param->field_mask |= 1 << IQOS_FIELD_OFFSET_1; + param->offset1.type = offset_type; + break; + case IQOS_FIELD_OFFSET_1_START: + param->field_mask |= 1 << IQOS_FIELD_OFFSET_1; + param->offset1.start = *(uint32_t *)val_ptr; + break; + case IQOS_FIELD_OFFSET_1_SIZE: + param->field_mask |= 1 << IQOS_FIELD_OFFSET_1; + param->offset1.size = *(uint32_t *)val_ptr; + break; + case IQOS_FIELD_OFFSET_1_MASK: + param->field_mask |= 1 << IQOS_FIELD_OFFSET_1; + param->offset1.mask = *(uint32_t *)val_ptr; + break; + default: + break; + } + + return 0; +} + +int iqos_keymask_commit_and_add(iqos_param_t *param, uint8_t prio) +{ + if (param == NULL) + return -EINVAL; + + if (param->param_type != IQOS_PARAM_TYPE_KEYMASK) + return -EINVAL; + + if (unlikely(iqos_add_keymask_hook_g == NULL)) + return -EPERM; + + param->prio = prio; + + return iqos_add_keymask_hook_g(param); +} + +int iqos_keymask_commit_and_delete(iqos_param_t *param) +{ + if (param == NULL) + return -EINVAL; + + if (param->param_type != IQOS_PARAM_TYPE_KEYMASK) + return -EINVAL; + + if (unlikely(iqos_rem_keymask_hook_g == NULL)) + return -EPERM; + + return iqos_rem_keymask_hook_g(param); +} + +int iqos_key_param_start(iqos_param_t *param) +{ + if (param == NULL) + return -EINVAL; + + memset(param, 0x0, sizeof(iqos_param_t)); + param->param_type = IQOS_PARAM_TYPE_KEY; + + return 0; +} + +int iqos_key_param_field_set(iqos_param_t *param, uint32_t field, + uint32_t *val_ptr, uint32_t val_size) +{ + if (param == NULL) + return -EINVAL; + + if (param->param_type != IQOS_PARAM_TYPE_KEY) + return -EINVAL; + + if (field >= IQOS_FIELD_MAX) + return -EINVAL; + + param->field_mask |= 1 << field; + + switch (field) { + case IQOS_FIELD_INGRESS_DEVICE: + /* FIXME!! implement this */ + if (val_size != 4) + return -EINVAL; + param->data.ingress_device = *val_ptr; + break; + case IQOS_FIELD_SRC_MAC: + if (val_size != ETH_ALEN) + return -EINVAL; + memcpy(param->data.src_mac, val_ptr, val_size); + break; + case IQOS_FIELD_DST_MAC: + if (val_size != ETH_ALEN) + return -EINVAL; + memcpy(param->data.dst_mac, val_ptr, val_size); + break; + case IQOS_FIELD_ETHER_TYPE: + if (val_size != 2) + return -EINVAL; + param->data.eth_type = *((uint16_t *)val_ptr); + break; + case IQOS_FIELD_OUTER_VID: + if (val_size != 2) + return -EINVAL; + param->data.outer_vid = *((uint16_t *)val_ptr); + break; + case IQOS_FIELD_OUTER_PBIT: + if (val_size != 1) + return -EINVAL; + param->data.outer_pbit = *((uint8_t *)val_ptr); + break; + case IQOS_FIELD_INNER_VID: + if (val_size != 2) + return -EINVAL; + param->data.inner_vid = *((uint16_t *)val_ptr); + break; + case IQOS_FIELD_INNER_PBIT: + if (val_size != 1) + return -EINVAL; + param->data.inner_pbit = *((uint8_t *)val_ptr); + break; + case IQOS_FIELD_L2_PROTO: + if (val_size != 2) + return -EINVAL; + param->data.l2_proto = *((uint16_t *)val_ptr); + break; + case IQOS_FIELD_L3_PROTO: + if (val_size != 2) + return -EINVAL; + param->data.l3_proto = *((uint16_t *)val_ptr); + break; + case IQOS_FIELD_IP_PROTO: + if (val_size != 1) + return -EINVAL; + param->data.ip_proto = *((uint8_t *)val_ptr); + break; + case IQOS_FIELD_SRC_IP: + if (val_size == 4) { /* IPV4 case */ + /* someone has set the IQOS to do IPv6 */ + if (param->data.is_ipv6 == 1) + return -EINVAL; + param->data.src_ip[0] = *val_ptr; + } else if (val_size == 16) { /* IPV6 case */ + memcpy(param->data.src_ip, val_ptr, val_size); + param->data.is_ipv6 = 1; + } else + return -EINVAL; + break; + case IQOS_FIELD_DST_IP: + if (val_size == 4) { /* IPV4 case */ + /* someone has set the IQOS to do IPv6 */ + if (param->data.is_ipv6 == 1) + return -EINVAL; + param->data.dst_ip[0] = *val_ptr; + } else if (val_size == 16) { /* IPV6 case */ + memcpy(param->data.dst_ip, val_ptr, val_size); + param->data.is_ipv6 = 1; + } else + return -EINVAL; + break; + case IQOS_FIELD_DSCP: + if (val_size != 1) + return -EINVAL; + + param->data.dscp = *((uint8_t *)val_ptr); + break; + case IQOS_FIELD_IPV6_FLOW_LABEL: + if (val_size != 4) + return -EINVAL; + + param->data.is_ipv6 = 1; + param->data.flow_label = *((uint32_t *)val_ptr); + break; + case IQOS_FIELD_SRC_PORT: + if (val_size != 2) + return -EINVAL; + param->data.l4_src_port = *((uint16_t *)val_ptr); + break; + case IQOS_FIELD_DST_PORT: + if (val_size != 2) + return -EINVAL; + param->data.l4_dst_port = *((uint16_t *)val_ptr); + break; + case IQOS_FIELD_OFFSET_0_TYPE: + if (val_size != 4) + return -EINVAL; + param->offset0.type = *((uint32_t *)val_ptr); + break; + case IQOS_FIELD_OFFSET_0_START: + if (val_size != 4) + return -EINVAL; + param->offset0.type = *((uint32_t *)val_ptr); + break; + case IQOS_FIELD_OFFSET_0_SIZE: + if (val_size != 4) + return -EINVAL; + param->offset0.size = *((uint32_t *)val_ptr); + break; + case IQOS_FIELD_OFFSET_0_MASK: + if (val_size != 4) + return -EINVAL; + param->offset0.mask = *((uint32_t *)val_ptr); + break; + case IQOS_FIELD_OFFSET_1_TYPE: + if (val_size != 4) + return -EINVAL; + param->offset0.type = *((uint32_t *)val_ptr); + break; + case IQOS_FIELD_OFFSET_1_START: + if (val_size != 4) + return -EINVAL; + param->offset0.type = *((uint32_t *)val_ptr); + break; + case IQOS_FIELD_OFFSET_1_SIZE: + if (val_size != 4) + return -EINVAL; + param->offset0.size = *((uint32_t *)val_ptr); + break; + case IQOS_FIELD_OFFSET_1_MASK: + if (val_size != 4) + return -EINVAL; + param->offset0.mask = *((uint32_t *)val_ptr); + break; + case IQOS_FIELD_OFFSET_0: + /* FIXME!! implement getting some offset-used value */ + case IQOS_FIELD_OFFSET_1: + /* FIXME!! implement getting some offset-used value */ + default: + break; + } + + return 0; +} + +int iqos_key_param_action_set(iqos_param_t *param, uint32_t action, + uint32_t value) +{ + if (param == NULL) + return -EINVAL; + + if (param->param_type != IQOS_PARAM_TYPE_KEY) + return -EINVAL; + + if (action >= IQOS_ACTION_MAX) + return -EINVAL; + + param->action = action; + param->action_value = value; + + return 0; +} + +int iqos_key_commit_and_add(iqos_param_t *param, uint8_t type) +{ + if (param == NULL) + return -EINVAL; + + if (param->param_type != IQOS_PARAM_TYPE_KEY) + return -EINVAL; + + if (unlikely(iqos_add_key_hook_g == NULL)) + return -EPERM; + + if (type >= IQOS_ENT_MAX) + return -EINVAL; + param->type = type; + + return iqos_add_key_hook_g(param); +} + +int iqos_key_commit_and_delete(iqos_param_t *param, uint8_t type) +{ + if (param == NULL) + return -EINVAL; + + if (param->param_type != IQOS_PARAM_TYPE_KEY) + return -EINVAL; + + if (unlikely(iqos_rem_key_hook_g == NULL)) + return -EPERM; + + if (type >= IQOS_ENT_MAX) + return -EINVAL; + param->type = type; + + return iqos_rem_key_hook_g(param); +} + +int iqos_key_commit_and_get(iqos_param_t *param) +{ + if (param == NULL) + return -EINVAL; + + if (param->param_type != IQOS_PARAM_TYPE_KEY) + return -EINVAL; + + if (unlikely(iqos_get_key_hook_g == NULL)) + return -EPERM; + + return iqos_get_key_hook_g(param); +} + +/* flush out all the existing dynamic entry, also delete keymask if they are not used + * by static entries */ +void iqos_flush(void) +{ + if (likely(iqos_flush_hook_g != NULL)) + iqos_flush_hook_g(); +} + +int iqos_set_status(uint32_t status) +{ + if (likely(iqos_set_status_hook_g != NULL)) + return iqos_set_status_hook_g(status); + return -EPERM; +} + +/* + *------------------------------------------------------------------------------ + * Function : iqos_bind + * Description : Override default hooks. + *------------------------------------------------------------------------------ + */ +void iqos_bind(iqos_common_hook_t iqos_add_keymask, + iqos_common_hook_t iqos_rem_keymask, + iqos_common_hook_t iqos_add_key, + iqos_common_hook_t iqos_rem_key, + iqos_common_hook_t iqos_get_key, + iqos_int_hook_t iqos_set_status, + iqos_void_hook_t iqos_flush) +{ + iqos_print("Bind add_keymask[<%p>] rem_keymask[<%p>] add_key[<%p>] " + "rem_key[<%p>] get_key[<%p>] flush[<%p>\n", + iqos_add_keymask, iqos_rem_keymask, iqos_add_key, + iqos_rem_key, iqos_get_key, iqos_flush); + + iqos_add_keymask_hook_g = iqos_add_keymask; + iqos_rem_keymask_hook_g = iqos_rem_keymask; + iqos_add_key_hook_g = iqos_add_key; + iqos_rem_key_hook_g = iqos_rem_key; + iqos_get_key_hook_g = iqos_get_key; + iqos_set_status_hook_g = iqos_set_status; + iqos_flush_hook_g = iqos_flush; +} + +EXPORT_SYMBOL(iqos_cpu_cong_g); +EXPORT_SYMBOL(iqos_enable_g); +EXPORT_SYMBOL(iqos_debug_g); +EXPORT_SYMBOL(iqos_add_L4port); +EXPORT_SYMBOL(iqos_rem_L4port); +EXPORT_SYMBOL(iqos_prio_L4port); +EXPORT_SYMBOL(iqos_bind); +EXPORT_SYMBOL(iqos_flush); +EXPORT_SYMBOL(iqos_set_status); + +EXPORT_SYMBOL(iqos_keymask_param_start); +EXPORT_SYMBOL(iqos_keymask_param_field_set); +EXPORT_SYMBOL(iqos_keymask_commit_and_add); +EXPORT_SYMBOL(iqos_keymask_commit_and_delete); + +EXPORT_SYMBOL(iqos_key_param_start); +EXPORT_SYMBOL(iqos_key_param_field_set); +EXPORT_SYMBOL(iqos_key_param_action_set); +EXPORT_SYMBOL(iqos_key_commit_and_add); +EXPORT_SYMBOL(iqos_key_commit_and_delete); +EXPORT_SYMBOL(iqos_key_commit_and_get); + +/* + *------------------------------------------------------------------------------ + * Function : __init_iqos + * Description : Static construction of ingress QoS subsystem. + *------------------------------------------------------------------------------ + */ +static int __init __init_iqos(void) +{ + printk( IQOS_MODNAME IQOS_VER_STR " initialized\n" ); + return 0; +} + +subsys_initcall(__init_iqos); + +#if (defined(CONFIG_BCM_INGQOS) || defined(CONFIG_BCM_INGQOS_MODULE)) + +/* get the congestion status for system */ +iqos_cong_status_t iqos_get_sys_cong_status(void) +{ + return ((iqos_cpu_cong_g)? IQOS_CONG_STATUS_HI : IQOS_CONG_STATUS_LO); +} + + +/* get the congestion status for an RX channel of an interface */ +iqos_cong_status_t iqos_get_cong_status(iqos_if_t iface, uint32_t chnl) +{ + return ((iqos_cpu_cong_g & (1 << (iface + chnl))) ? + IQOS_CONG_STATUS_HI : IQOS_CONG_STATUS_LO); +} + + +/* set/reset the congestion status for an RX channel of an interface */ +uint32_t iqos_set_cong_status(iqos_if_t iface, uint32_t chnl, + iqos_cong_status_t status) +{ + unsigned long flags; + + IQOS_LOCK_IRQSAVE(); + + if (status == IQOS_CONG_STATUS_HI) + iqos_cpu_cong_g |= (1 << (iface + chnl)); + else + iqos_cpu_cong_g &= ~(1 << (iface + chnl)); + + IQOS_UNLOCK_IRQRESTORE(); + + return iqos_cpu_cong_g; +} + +EXPORT_SYMBOL(iqos_get_cong_status); +EXPORT_SYMBOL(iqos_set_cong_status); + +#endif /* (defined(CONFIG_BCM_INGQOS) || defined(CONFIG_BCM_INGQOS_MODULE)) */ +#endif + diff -urN linux-4.1.52-orig/net/core/link_watch.c linux-4.1.52/net/core/link_watch.c --- linux-4.1.52-orig/net/core/link_watch.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/core/link_watch.c 2020-02-07 01:47:54.000000000 +0800 @@ -23,6 +23,30 @@ #include #include +#if defined(CONFIG_BCM_KF_LINKWATCH_WQ) +#include +/* + * Problem scenario: wlmngr does an ioctl, and the upper layers + * of ioctl code grabs the rtnl lock before calling the wlan ioctl code. + * Wlan ioctl code calls flush_workqueue, but there is a linkwatch_event + * function on the work queue. Linkwatch_event function tries to grab the + * rtnl lock, but cannot, so now the event thread is deadlocked. + * Partial solution: put linkwatch_event on its own workqueue so that + * the wlan events can get flushed without having to run the linkwatch_event. + * Eventually, the wlan events should also get moved off of the common + * event workqueue. + */ +static struct workqueue_struct *lw_wq=NULL; + +int __init init_linkwatch(void) +{ + lw_wq = create_singlethread_workqueue("linkwatch"); + + return 0; +} + +__initcall(init_linkwatch); +#endif enum lw_bits { LW_URGENT = 0, @@ -135,9 +159,17 @@ * override the existing timer. */ if (test_bit(LW_URGENT, &linkwatch_flags)) +#if defined(CONFIG_BCM_KF_LINKWATCH_WQ) + mod_delayed_work(lw_wq, &linkwatch_work, 0); +#else mod_delayed_work(system_wq, &linkwatch_work, 0); +#endif else +#if defined(CONFIG_BCM_KF_LINKWATCH_WQ) + queue_delayed_work(lw_wq, &linkwatch_work, delay); +#else schedule_delayed_work(&linkwatch_work, delay); +#endif } diff -urN linux-4.1.52-orig/net/core/Makefile linux-4.1.52/net/core/Makefile --- linux-4.1.52-orig/net/core/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/core/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -2,6 +2,11 @@ # Makefile for the Linux networking core. # +ifdef BCM_KF # defined(CONFIG_BCM_KF_BLOG) +EXTRA_CFLAGS += -I$(INC_BRCMDRIVER_PUB_PATH)/$(BRCM_BOARD) +EXTRA_CFLAGS += -I$(INC_BRCMSHARED_PUB_PATH)/bcm963xx +endif # BCM_KF + obj-y := sock.o request_sock.o skbuff.o datagram.o stream.o scm.o \ gen_stats.o gen_estimator.o net_namespace.o secure_seq.o flow_dissector.o @@ -23,3 +28,13 @@ obj-$(CONFIG_NET_PTP_CLASSIFY) += ptp_classifier.o obj-$(CONFIG_CGROUP_NET_PRIO) += netprio_cgroup.o obj-$(CONFIG_CGROUP_NET_CLASSID) += netclassid_cgroup.o + +ifdef BCM_KF # defined(CONFIG_BCM_KF_BLOG) +obj-$(CONFIG_BLOG) += blog.o blog_rule.o +obj-$(CONFIG_BCM_KF_VLANCTL_BIND) += vlanctl_bind.o +endif # BCM_KF +ifdef BCM_KF # defined(CONFIG_BCM_KF_NBUFF) +obj-y += nbuff.o iqos.o gbpm.o +endif # BCM_KF + + diff -urN linux-4.1.52-orig/net/core/nbuff.c linux-4.1.52/net/core/nbuff.c --- linux-4.1.52-orig/net/core/nbuff.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/core/nbuff.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,1094 @@ +#if defined(CONFIG_BCM_KF_NBUFF) + +/* + * <:copyright-BRCM:2009:GPL/GPL:standard + * + * Copyright (c) 2009 Broadcom + * All Rights Reserved + * + * 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 (the "GPL"). + * + * 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. + * + * + * A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by + * writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * :> +*/ + +#define FKB_IMPLEMENTATION_FILE + +#include +#include +#include +#include + +#ifdef CC_CONFIG_FKB_COLOR +#define COLOR(clr_code) clr_code +#else +#define COLOR(clr_code) +#endif +#define CLRb COLOR("\e[0;34m") /* blue */ +#define CLRc COLOR("\e[0;36m") /* cyan */ +#define CLRn COLOR("\e[0m") /* normal */ +#define CLRerr COLOR("\e[0;33;41m") /* yellow on red */ +#define CLRN CLRn"\n" /* normal newline */ + +int nbuff_dbg = 0; +#if defined(CC_CONFIG_FKB_DEBUG) +#define fkb_print(fmt, arg...) \ + printk( CLRc "FKB %s :" fmt CLRN, __FUNCTION__, ##arg ) +#define fkb_assertv(cond) \ + if ( !cond ) { \ + printk( CLRerr "FKB ASSERT %s : " #cond CLRN, __FUNCTION__ ); \ + return; \ + } +#define fkb_assertr(cond, rtn) \ + if ( !cond ) { \ + printk( CLRerr "FKB ASSERT %s : " #cond CLRN, __FUNCTION__ ); \ + return rtn; \ + } +#else +#define fkb_print(fmt, arg...) NULL_STMT +#define fkb_assertv(cond) NULL_STMT +#define fkb_assertr(cond, rtn) NULL_STMT +#endif + +/* + *------------------------------------------------------------------------------ + * Test whether an FKB may be translated onto a skb. + *------------------------------------------------------------------------------ + */ +int fkb_in_skb_test(int fkb_offset, int list_offset, int blog_p_offset, + int data_offset, int len_word_offset, int queue_offset, + int priority_offset, int recycle_hook_offset, + int recycle_context_offset) +{ +#undef OFFSETOF +#define OFFSETOF(stype, member) ((size_t)&((struct stype *)0)->member) +#define FKBOFFSETOF(member) (member##_offset) +#define SKBOFFSETOF(member) (((size_t)&((struct sk_buff *)0)->member)-fkb_offset) +#define FKBSIZEOF(member) (sizeof(((struct fkbuff *)0)->member)) +#define SKBSIZEOF(member) (sizeof(((struct sk_buff *)0)->member)) +#define FKBINSKB_TEST(member) ((FKBOFFSETOF(member) == SKBOFFSETOF(member)) \ + && (FKBSIZEOF(member) == SKBSIZEOF(member))) + + if (OFFSETOF(sk_buff, fkbInSkb) != fkb_offset) + return 0; + + if (!FKBINSKB_TEST(list)) + return 0; + + if (!FKBINSKB_TEST(blog_p)) + return 0; + + if (!FKBINSKB_TEST(data)) + return 0; + + if (!FKBINSKB_TEST(len_word)) + return 0; + + if (!FKBINSKB_TEST(queue)) + return 0; + + if (!FKBINSKB_TEST(priority)) + return 0; + + if (!FKBINSKB_TEST(recycle_hook)) + return 0; + + if (!FKBINSKB_TEST(recycle_context)) + return 0; + + /* to ensure that fkbuff is not larger than 2 cache lines */ + if (sizeof(struct fkbuff) > (2 * cache_line_size())) + return 0; + + /* this is to ensure fkb is not placed across different cache line + * in 32 bit architecture (ARM and MIPS), we have 16, 32, and 64 + * byte cache line sizes. In 64 bit architecture, we have 64 byte + * cache line sizes. The following will ensure fkb_offset is at + * 32 byte aligned for 32 bit system, and 64 byte aligned for + * 64 bit system. */ + if ((fkb_offset & (sizeof(unsigned long) * 8 - 1)) != 0) + return 0; + + return 1; +} + +/* + *------------------------------------------------------------------------------ + * Pre-allocated Pool of Cloned and Master FkBuff_t objects. + *------------------------------------------------------------------------------ */ + +typedef struct fkbPool { + FkBuff_t * freelist_p; /* List of free objects */ + uint32_t extends; /* Number of pool extensions performed */ + + /* Pool dimensioning parameters */ + uint32_t pool_size; /* Startup default pool size */ + uint32_t object_size; /* Size of each object in the pool */ + uint32_t extend_size; /* Number of objects per extension */ + uint32_t extend_max; /* Maximum number of extensions permitted */ + char name[8]; + +#if defined(CC_CONFIG_FKB_STATS) + int cnt_free; /* Number of free objects */ + int cnt_used; /* Number of in use objects */ + int cnt_hwm; /* In use high water mark for engineering */ + int cnt_fails; /* Failure due to out of memory */ +#endif +} FkbPool_t; + +/* + *------------------------------------------------------------------------------ + * Global pools for Cloned and Master FKB Objects. + *------------------------------------------------------------------------------ + */ +FkbPool_t fkb_pool_g[ FkbMaxPools_e ] = { + { + .freelist_p = FKB_NULL, + .extends = 0, + .pool_size = FKBM_POOL_SIZE_ENGG, + .object_size = BCM_PKTBUF_SIZE, /* Rx Buffer wth in place FKB */ + .extend_size = FKBM_EXTEND_SIZE_ENGG, + .extend_max = FKBM_EXTEND_MAX_ENGG, + .name = "Master", +#if defined(CC_CONFIG_FKB_STATS) + .cnt_free = 0, .cnt_used = 0, .cnt_hwm = 0, .cnt_fails = 0, +#endif + } + , + { + .freelist_p = FKB_NULL, + .extends = 0, + .pool_size = FKBC_POOL_SIZE_ENGG, + .object_size = sizeof(FkBuff_t), /* Only FKB object */ + .extend_size = FKBC_EXTEND_SIZE_ENGG, + .extend_max = FKBC_EXTEND_MAX_ENGG, + .name = "Cloned", +#if defined(CC_CONFIG_FKB_STATS) + .cnt_free = 0, .cnt_used = 0, .cnt_hwm = 0, .cnt_fails = 0, +#endif + } +}; + + +/* + *------------------------------------------------------------------------------ + * Statistics collection for engineering free pool parameters. + *------------------------------------------------------------------------------ + */ +void fkb_stats(void) +{ + int pool; + FkbPool_t *pool_p; + for (pool = 0; pool < FkbMaxPools_e; pool++ ) + { + pool_p = &fkb_pool_g[pool]; + + printk("FKB %s Pool: extends<%u>\n", pool_p->name, pool_p->extends ); + + FKB_STATS( + printk("\t free<%d> used<%d> HWM<%d> fails<%d>\n", + pool_p->cnt_free, + pool_p->cnt_used, pool_p->cnt_hwm, pool_p->cnt_fails ); ); + } +} + +#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT) +#include "linux/spinlock.h" +static DEFINE_SPINLOCK(fkb_pool_lock_g); /* FkBuff packet flow */ +#define FKB_POOL_LOCK() spin_lock_irqsave(&fkb_pool_lock_g, lock_flags) +#define FKB_POOL_UNLOCK() spin_unlock_irqrestore(&fkb_pool_lock_g, lock_flags) +#else +#define FKB_POOL_LOCK() local_irq_disable() +#define FKB_POOL_UNLOCK() local_irq_enable() +#endif + +#if defined(CC_CONFIG_FKB_AUDIT) +void fkb_audit(const char * function, int line) +{ /* place any audits here */ } +EXPORT_SYMBOL(fkb_audit); +#define FKB_AUDIT_RUN() fkb_audit(__FUNCTION__,__LINE__) +#else +#define FKB_AUDIT_RUN() NULL_STMT +#endif + +/* + *------------------------------------------------------------------------------ + * Function : fkbM_recycle + * Description: Recycling a Master FKB that was allocated from Master FKB Pool. + * Parameters : + * pNBuff : pointer to a network buffer + * context : registered context argument with network buffer. + * flags : unused by fkb recycling. + *------------------------------------------------------------------------------ + */ +void fkbM_recycle(pNBuff_t pNBuff, unsigned long context, unsigned flags) +{ + register FkBuff_t * fkbM_p; + register FkbPool_t * pool_p = (FkbPool_t *)((unsigned long)context); +#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT) + unsigned long lock_flags; +#endif + + fkb_assertv( (pool_p == &fkb_pool_g[FkbMasterPool_e]) ); + + if ( IS_SKBUFF_PTR(pNBuff) ) + { + struct sk_buff * skb_p = (struct sk_buff *)PNBUFF_2_SKBUFF(pNBuff); + fkb_assertv( (flags & SKB_DATA_RECYCLE) ); + fkbM_p = (FkBuff_t *)((uintptr_t)(skb_p->head) - PFKBUFF_PHEAD_OFFSET); + } + /* else if IS_FPBUFF_PTR, else if IS_TGBUFF_PTR */ + else + fkbM_p = PNBUFF_2_FKBUFF(pNBuff); + + fkb_dbg(1, "fkbM_p<%p>", fkbM_p); + + FKB_AUDIT( + if ( fkbM_p->list != NULL ) + printk("FKB ASSERT cpu<%u> %s(%p) list<%p> recycle<%pS>\n", + smp_processor_id(), __FUNCTION__, + fkbM_p, fkbM_p->list, fkbM_p->recycle_hook); + if ( fkbM_p->recycle_hook != (RecycleFuncP)fkbM_recycle ) + printk("FKB ASSERT cpu<%u> %s <%p>.recycle<%pS>\n", + smp_processor_id(), __FUNCTION__, + fkbM_p, fkbM_p->recycle_hook); ); + + FKB_AUDIT_RUN(); + + FKB_POOL_LOCK(); + + FKB_STATS( pool_p->cnt_used--; pool_p->cnt_free++; ); + + fkbM_p->list = pool_p->freelist_p; /* resets users */ + pool_p->freelist_p = fkbM_p; /* link into Master FKB free pool */ + + FKB_POOL_UNLOCK(); + + FKB_AUDIT_RUN(); +} + +/* + *------------------------------------------------------------------------------ + * Function : fkbC_recycle + * Description: Recycling a Cloned FKB back to the Cloned FKB Pool. + * Parameters : + * fkbC_p : Pointer to a Cloned FKB Object. + *------------------------------------------------------------------------------ + */ +void fkbC_recycle(FkBuff_t * fkbC_p) +{ + register FkbPool_t * pool_p; +#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT) + unsigned long lock_flags; +#endif + + pool_p = &fkb_pool_g[ FkbClonedPool_e ]; + + fkb_dbg(2, "fkb_p<%p>", fkbC_p); + + FKB_AUDIT( + if ( fkbC_p->recycle_hook != (RecycleFuncP)NULL ) + printk("FKB ASSERT cpu<%u> %s <0x%08x>.recycle<%pS>\n", + smp_processor_id(), __FUNCTION__, + (int)fkbC_p, fkbC_p->recycle_hook); ); + + FKB_AUDIT_RUN(); + + FKB_POOL_LOCK(); + + FKB_STATS( pool_p->cnt_used--; pool_p->cnt_free++; ); + + fkbC_p->list = pool_p->freelist_p; /* resets master_p */ + pool_p->freelist_p = fkbC_p; /* link into Cloned free pool */ + + FKB_POOL_UNLOCK(); + + FKB_AUDIT_RUN(); +} + +/* + *------------------------------------------------------------------------------ + * Function : fkb_extend + * Description: Create a pool of FKB objects. When a pool is exhausted + * this function may be invoked to extend the pool. + * All objects in the pool are chained in a single linked list. + * Parameters : + * num : Number of FKB objects to be allocated. + * object : Object type to locate pool + * Returns : Number of FKB objects allocated. + *------------------------------------------------------------------------------ + */ +static uint32_t fkb_extend(uint32_t num, FkbObject_t object) +{ + register int i; + register FkBuff_t * list_p, * fkb_p, * fkbNext_p; + register FkbPool_t * pool_p; + + fkb_assertr( (object < FkbMaxPools_e), 0 ); + + pool_p = &fkb_pool_g[object]; /* select free pool */ + + list_p = (FkBuff_t *) kmalloc( num * pool_p->object_size, GFP_ATOMIC); + + fkb_print( "fkb_extend %u FKB %s objects <%p> .. <%p>", + num, pool_p->name, list_p, + (FkBuff_t*)((uintptr_t)list_p + ((num-1) * pool_p->object_size))); + + if ( unlikely(list_p == FKB_NULL) ) /* may fail if in_interrupt or oom */ + { + FKB_STATS( pool_p->cnt_fails++; ); + fkb_print( "WARNING: Failure to initialize %d FKB %s objects", + num, pool_p->name ); + return 0; + } + pool_p->extends++; + + /* memset( (void *)list, 0, ( num * pool_p->object_size ) ); */ + + /* Link all allocated objects together */ + fkb_p = FKB_NULL; + fkbNext_p = list_p; + for ( i = 0; i < num; i++ ) + { + fkb_p = fkbNext_p; + fkbNext_p = (FkBuff_t *)( (uintptr_t)fkb_p + pool_p->object_size ); + + if ( object == FkbClonedPool_e ) + { + fkb_p->recycle_hook = (RecycleFuncP)NULL; + fkb_p->recycle_context = (unsigned long)&fkb_pool_g[FkbClonedPool_e]; + } + else + { + // fkb_set_ref(fkb_p, 0); ... see fkb_p->list + fkb_p->recycle_hook = (RecycleFuncP)fkbM_recycle; + fkb_p->recycle_context = (unsigned long)&fkb_pool_g[FkbMasterPool_e]; + } + + fkb_p->list = fkbNext_p; /* link each FkBuff */ + } + + FKB_STATS( pool_p->cnt_free += num; ); + + /* link allocated list into FKB free pool */ + fkb_p->list = pool_p->freelist_p; /* chain last FKB object */ + pool_p->freelist_p = list_p; /* head of allocated list */ + + return num; +} + +/* + *------------------------------------------------------------------------------ + * Function : fkb_construct + * Description : Incarnates the FKB system pools during kernel/module init + *------------------------------------------------------------------------------ + */ +int fkb_construct(int fkb_in_skb_offset) +{ +#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT) + unsigned long lock_flags; +#endif +#undef FKBOFFSETOF +#define FKBOFFSETOF(member) ((size_t)&((struct fkbuff*)0)->member) + if ( fkb_in_skb_test(fkb_in_skb_offset, + FKBOFFSETOF(list), FKBOFFSETOF(blog_p), FKBOFFSETOF(data), + FKBOFFSETOF(len_word), FKBOFFSETOF(mark), FKBOFFSETOF(priority), + FKBOFFSETOF(recycle_hook), FKBOFFSETOF(recycle_context)) == 0 ) + return -1; + else + FKB_DBG( printk(CLRb "FKB compatible with SKB" CLRN); ); + + FKB_POOL_LOCK(); + + /* Prepare a free pool for Cloned FkBuffs */ + fkb_extend( fkb_pool_g[FkbClonedPool_e].pool_size, FkbClonedPool_e ); + + /* Prepare a free pool for Master FkBuffs */ + fkb_extend( fkb_pool_g[FkbMasterPool_e].pool_size, FkbMasterPool_e ); + + FKB_POOL_UNLOCK(); + + FKB_AUDIT_RUN(); + + FKB_DBG( printk(CLRb "NBUFF nbuff_dbg<%p> = %d\n" + "\t Pool FkBuff %s size<%u> num<%u>\n" + "\t Pool FkBuff %s size<%u> num<%u>" CLRN, + &nbuff_dbg, nbuff_dbg, + fkb_pool_g[FkbClonedPool_e].name, + fkb_pool_g[FkbClonedPool_e].object_size, + fkb_pool_g[FkbClonedPool_e].pool_size, + fkb_pool_g[FkbMasterPool_e].name, + fkb_pool_g[FkbMasterPool_e].object_size, + fkb_pool_g[FkbMasterPool_e].pool_size ); + ); + + printk( CLRb "NBUFF %s Initialized" CLRN, NBUFF_VERSION ); + + return 0; +} + +/* + *------------------------------------------------------------------------------ + * Function : fkb_alloc + * Description : Allocate an FKB from one of the pools + * object : Type of FkbObject, to identify Free Pool + * Returns : Pointer to an FKB, or NULL on pool depletion. + *------------------------------------------------------------------------------ + */ +FkBuff_t * fkb_alloc( FkbObject_t object ) +{ + register FkBuff_t * fkb_p; + register FkbPool_t * pool_p; +#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT) + unsigned long lock_flags; +#endif + + fkb_assertr( (object < FkbMaxPools_e), FKB_NULL ); + + FKB_AUDIT( + if ( smp_processor_id() ) + printk("FKB ASSERT %s not supported on CP 1\n", __FUNCTION__); ); + + FKB_AUDIT_RUN(); + + pool_p = &fkb_pool_g[object]; + + fkb_dbg(2, "%s freelist_p<%p>", pool_p->name, pool_p->freelist_p); + + FKB_POOL_LOCK(); /* DO NOT USE fkb_assertr() until FKB_POOL_UNLOCK() */ + + if ( unlikely(pool_p->freelist_p == FKB_NULL) ) + { +#ifdef SUPPORT_FKB_EXTEND + /* Try extending free pool */ + if ( (pool_p->extends >= pool_p->extend_max) + || (fkb_extend( pool_p->extend_size, object ) != pool_p->extend_size)) + { + fkb_print( "WARNING: FKB Pool %s exhausted", pool_p->name ); + } +#else + if ( fkb_extend( pool_p->extend_size, object ) == 0 ) + { + fkb_print( "WARNING: FKB Pool %s out of memory", pool_p->name ); + } +#endif + if (pool_p->freelist_p == FKB_NULL) + { + fkb_p = FKB_NULL; + goto fkb_alloc_return; + } + } + + FKB_STATS( + pool_p->cnt_free--; + if ( ++pool_p->cnt_used > pool_p->cnt_hwm ) + pool_p->cnt_hwm = pool_p->cnt_used; + ); + + /* Delete an FkBuff from the pool */ + fkb_p = pool_p->freelist_p; + pool_p->freelist_p = pool_p->freelist_p->list; + + // fkb_set_ref(fkb_p, 0); + fkb_p->list = FKB_NULL; /* resets list, master_p to NULL , users to 0 */ + +fkb_alloc_return: + + FKB_POOL_UNLOCK(); /* May use kb_assertr() now onwards */ + + FKB_AUDIT_RUN(); + + fkb_dbg(1, "fkb_p<%p>", fkb_p); + + return fkb_p; +} + +/* + *------------------------------------------------------------------------------ + * Function : fkb_free + * Description : Free an FKB and associated buffer if reference count of the + * buffer is 0. + * All cloned fkb's are freed to the global Cloned free pool. + * Master FKBs will be recycled into the appropriate network + * device driver's rx pool or the global Master FKB pool. + * Parameters : + * fkb_p : Pointer to a FKB to be freed. + *------------------------------------------------------------------------------ + */ +void fkb_free(FkBuff_t * fkb_p) +{ + register FkBuff_t * fkbM_p; + + FKB_AUDIT_RUN(); + + fkb_assertv( (fkb_p!=FKB_NULL) ); + fkb_dbg(1, "fkb_p<%p>", fkb_p); + + /* FKB should never point to a Blog, so no need to free fkb_p->blog_p */ + fkb_assertv( (fkb_p!=FKB_NULL) ); + + /* Implementation Note: list_p, master_p and users union. + If it is a cloned fkb, then fkb_p->master_p is a KPTR. If a double free + is invoked on the same fkb_p, then list_p will also be a KPTR! */ + + if ( _is_fkb_cloned_pool_(fkb_p) ) + { + fkbM_p = fkb_p->master_p; + fkbC_recycle(fkb_p); + } + else + fkbM_p = fkb_p; + + fkb_assertv( (_get_master_users_(fkbM_p) > 0) ); + + /* API atomic_dec_and_test: After decrement, return true if result is 0 */ + if ( likely(atomic_long_dec_and_test(&fkbM_p->users)) ) + { + /* No fkbs are referring to master, so master and buffer recyclable */ + fkbM_p->recycle_hook(FKBUFF_2_PNBUFF(fkbM_p), + fkbM_p->recycle_context, 0); + } + + FKB_AUDIT_RUN(); + + fkb_dbg(2, "fkbM_p<%p>", fkbM_p); +} + +/* + *------------------------------------------------------------------------------ + * Function : fkb_unshare + * Description: Returns a pointer to a Master FKB with a single reference to the + * packet. A Cloned FKB with a single reference will result in the + * Clone's Master FKB being returned, and the Cloned FKB Object is + * recycled. + *------------------------------------------------------------------------------ + */ +FkBuff_t * fkb_unshare(FkBuff_t * fkb_p) +{ + register FkBuff_t * fkbM_p; + uint8_t * dirty_p; + + FKB_AUDIT( + if ( smp_processor_id() ) + printk("FKB ASSERT %s not supported on CP 1\n", __FUNCTION__); ); + + FKB_AUDIT_RUN(); + + if ( unlikely(_is_fkb_cloned_pool_(fkb_p)) ) /* Cloned FKB */ + { + /* If master is also referenced by other FkBuffs */ + if ( _get_master_users_(fkb_p->master_p) > 1 ) + { + /* Real unsharing, by allocating new master FkBuff */ + fkbM_p = fkb_alloc( FkbMasterPool_e ); + if (fkbM_p == FKB_NULL) + { + fkb_dbg(1, "fkb_unshare Cloned FKB fkb_alloc failure"); + return FKB_NULL; + } + fkb_set_ref(fkbM_p, 1); + + /* Setup FkBuff context */ + fkbM_p->data = (uint8_t*)(fkbM_p) + + ((uintptr_t)fkb_p->data - (uintptr_t)fkb_p->master_p); + fkbM_p->len_word = fkb_p->len_word; + fkbM_p->mark = fkb_p->mark; + fkbM_p->priority = fkb_p->priority; + + fkbM_p->dirty_p = _to_dptr_from_kptr_(fkbM_p->data + fkbM_p->len); + + /* Copy from original clone FkBuff */ + memcpy(fkbM_p->data, fkb_p->data, fkb_p->len); + + dirty_p = _to_dptr_from_kptr_(fkb_p->data + fkb_p->len); + if ( fkb_p->master_p->dirty_p < dirty_p ) + fkb_p->master_p->dirty_p = dirty_p; + + fkb_dec_ref(fkb_p->master_p); /* decrement masters user count */ + fkb_dbg(1, "cloned fkb_p with multiple ref master"); + } + else + { + fkb_dbg(1, "cloned fkb_p with single ref master"); + fkbM_p = fkb_p->master_p; + + // Move clone context to master and return master + fkbM_p->data = fkb_p->data; + fkbM_p->len_word = fkb_p->len_word; + fkbM_p->mark = fkb_p->mark; + fkbM_p->priority = fkb_p->priority; + + if ( fkbM_p->dirty_p < fkb_p->dirty_p ) + fkbM_p->dirty_p = fkb_p->dirty_p; + } + + fkb_dbg(2, "fkbM_p<%p> fkbM_data<%p> dirty_p<%p> len<%d>", + fkbM_p, fkbM_p->data, fkbM_p->dirty_p, fkbM_p->len); + fkb_dbg(2, "fkb_p<%p> fkb_data<%p> dirty_p<%p> len<%d>", + fkb_p, fkb_p->data, fkb_p->dirty_p, fkb_p->len); + + fkbC_recycle(fkb_p); /* always recycle original clone fkb */ + + return fkbM_p; /* return new allocate master FkBuff */ + } + else /* Original is a Master */ + { + /* Single reference, no need to unshare */ + if ( _get_master_users_(fkb_p) == 1 ) + { + fkb_dbg(1, "master fkb_p with single ref "); + fkb_dbg(2, "fkb_p<%p> fkb_data<%p> dirty_p<%p> len<%d>", + fkb_p, fkb_p->data, fkb_p->dirty_p, fkb_p->len); + return fkb_p; + } + + /* Allocate a master FkBuff with associated data buffer */ + fkbM_p = fkb_alloc( FkbMasterPool_e ); + if (fkbM_p == FKB_NULL) + { + fkb_dbg(1, "fkb_unshare Master Fkb fkb_alloc failure"); + return FKB_NULL; + } + fkb_set_ref(fkbM_p, 1); + + /* Setup FkBuff context */ + fkbM_p->data = (uint8_t*)(fkbM_p) + + ((uintptr_t)fkb_p->data - (uintptr_t)fkb_p); + fkbM_p->len_word = fkb_p->len_word; + fkbM_p->mark = fkb_p->mark; + fkbM_p->priority = fkb_p->priority; + + fkbM_p->dirty_p = _to_dptr_from_kptr_(fkbM_p->data + fkbM_p->len); + + /* Copy original FkBuff's data into new allocated master FkBuff */ + memcpy(fkbM_p->data, fkb_p->data, fkb_p->len); + + dirty_p = _to_dptr_from_kptr_(fkb_p->data + fkb_p->len); + if ( fkb_p->dirty_p < dirty_p ) + fkb_p->dirty_p = dirty_p; + + /* unshare by decrementing reference count */ + fkb_dec_ref(fkb_p); + + fkb_dbg(1, "master fkb_p with multiple ref"); + fkb_dbg(2, "fkbM_p<%p> fkbM_data<%p> dirty_p<%p> len<%d>", + fkbM_p, fkbM_p->data, fkbM_p->dirty_p, fkbM_p->len); + fkb_dbg(2, "fkb_p<%p> fkb_data<%p> dirty_p<%p> len<%d>", + fkb_p, fkb_p->data, fkb_p->dirty_p, fkb_p->len); + return fkbM_p; /* return new allocate master FkBuff */ + } +} + +/* + *------------------------------------------------------------------------------ + * Function : fkbM_borrow + * Description: Fetch a Master FKB from the Master FKB pool. A Master FKB Object + * Pool can serve as a network device driver's preallocated buffer + * pool overflow. + *------------------------------------------------------------------------------ + */ +FkBuff_t * fkbM_borrow(void) +{ + FkBuff_t * fkbM_p; + + fkbM_p = fkb_alloc( FkbMasterPool_e ); + + fkb_dbg(1, "fkbM_p<%p>", fkbM_p); + return fkbM_p; +} + +/* + *------------------------------------------------------------------------------ + * Function : fkbM_return + * Description: Return a Master FKB to the global Master FKB pool. It is not + * necessary that a returned Master FKB was originially allocated + * from the global Master FKB pool. + *------------------------------------------------------------------------------ + */ +void fkbM_return(FkBuff_t * fkbM_p) +{ + register FkbPool_t * pool_p; +#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT) + unsigned long lock_flags; +#endif + + fkb_assertv( (fkbM_p != FKB_NULL) ); + fkb_dbg(1, "fkbM_p<%p>", fkbM_p); + + FKB_AUDIT_RUN(); + + FKB_POOL_LOCK(); /* DO NOT USE fkb_assertr() until FKB_POOL_UNLOCK() */ + + pool_p = &fkb_pool_g[FkbMasterPool_e]; + + /* Setup FKB Master Pool recycling feature */ + fkbM_p->recycle_hook = (RecycleFuncP)fkbM_recycle; + fkbM_p->recycle_context = (unsigned long)pool_p; + + FKB_STATS( pool_p->cnt_used--; pool_p->cnt_free++; ); + + fkbM_p->list = pool_p->freelist_p; /* resets fkbM_p->users */ + pool_p->freelist_p = fkbM_p; /* link into Master free pool */ + + FKB_POOL_UNLOCK(); /* May use kb_assertr() now onwards */ + + FKB_AUDIT_RUN(); +} + +/* + *------------------------------------------------------------------------------ + * Function : fkb_xlate + * Description: Translates an FKB to an SKB allocated from kernel SKB cache. + * If the FKB is refering to a packet with multiple FKB references + * to it then it will be first unshared, before it is translated + * to a SKB. Unsharing is done by allocating a Master FKB from the + * Master FKB Pool. + *------------------------------------------------------------------------------ + */ +extern struct sk_buff * skb_xlate_dp(FkBuff_t * fkb_p, uint8_t *dirty_p); + +struct sk_buff * fkb_xlate(FkBuff_t * fkb_p) +{ + struct sk_buff * skb_p; + uint8_t *dirty_p; + + FKB_AUDIT( + if ( smp_processor_id() ) + printk("FKB ASSERT %s not supported on CP 1\n", __FUNCTION__); ); + + FKB_AUDIT_RUN(); + + if ( unlikely(fkb_p == FKB_NULL) ) + return (struct sk_buff *)NULL; + + fkb_assertr( (!_IS_BPTR_(fkb_p->ptr)), (struct sk_buff *)NULL ); + + /* Ensure that only a single reference exists to the FKB */ + fkb_p = fkb_unshare(fkb_p); + if ( unlikely(fkb_p == FKB_NULL) ) + goto clone_fail; + + /* carry the dirty_p to the skb */ + dirty_p = (is_dptr_tag_(fkb_p->dirty_p)) ? + _to_kptr_from_dptr_(fkb_p->dirty_p) : NULL; + + /* Now translate the fkb_p to a skb_p */ + skb_p = skb_xlate_dp(fkb_p, dirty_p); + + if ( unlikely(skb_p == (struct sk_buff *)NULL) ) + goto clone_fail; + + /* pNBuff may not be used henceforth */ + return skb_p; + +clone_fail: + return (struct sk_buff *)NULL; +} + + +/* + *------------------------------------------------------------------------------ + * Function : nbuff_align_data + * Description : Aligns NBUFF data to a byte boundary defined by alignMask + * This function can be called ONLY by driver Transmit functions + *------------------------------------------------------------------------------ + */ +pNBuff_t nbuff_align_data(pNBuff_t pNBuff, uint8_t **data_pp, + uint32_t len, unsigned long alignMask) +{ + fkb_dbg(1, "pNBuff<%p>", pNBuff); + + FKB_AUDIT( + if ( smp_processor_id() ) + printk("FKB ASSERT %s not supported on CP 1\n", __FUNCTION__); ); + + FKB_AUDIT_RUN(); + + if ( IS_SKBUFF_PTR(pNBuff) ) + { + struct sk_buff * skb_p = PNBUFF_2_SKBUFF(pNBuff); + uint32_t headroom; + uint8_t *skb_data_p; + + headroom = (uintptr_t)(skb_p->data) & alignMask; + + if(headroom == 0) + { + /* data is already aligned */ + goto out; + } + + if(skb_cow(skb_p, headroom) < 0) + { + kfree_skb(skb_p); + + pNBuff = NULL; + goto out; + } + + skb_data_p = (uint8_t *)((uintptr_t)(skb_p->data) & ~alignMask); + + memcpy(skb_data_p, skb_p->data, len); + + skb_p->data = skb_data_p; + *data_pp = skb_data_p; + } + /* else if IS_FPBUFF_PTR, else if IS_TGBUFF_PTR */ + else + { + FkBuff_t * fkb_p = (FkBuff_t *)PNBUFF_2_PBUF(pNBuff); + FkBuff_t * fkb2_p; + uint32_t headroom; + uint8_t *fkb_data_p; + + headroom = (uintptr_t)(fkb_p->data) & alignMask; + + if(headroom == 0) + { + /* data is already aligned */ + goto out; + } + + if(fkb_headroom(fkb_p) < headroom) + { + fkb_dbg(1, "FKB has no headroom " + "(fkb_p<%p>, fkb_p->data<%p>)", + fkb_p, fkb_p->data); + + goto out; + } + + fkb2_p = fkb_unshare(fkb_p); + if (fkb2_p == FKB_NULL) + { + fkb_free(fkb_p); + pNBuff = NULL; + goto out; + } + pNBuff = FKBUFF_2_PNBUFF(fkb2_p); + + fkb_data_p = (uint8_t *)((uintptr_t)(fkb2_p->data) & ~alignMask); + + memcpy(fkb_data_p, fkb2_p->data, len); + + fkb2_p->data = fkb_data_p; + *data_pp = fkb_data_p; + +#if defined(CC_NBUFF_FLUSH_OPTIMIZATION) + { + uint8_t * tail_p = fkb2_p->data + len; + fkb2_p->dirty_p = _to_dptr_from_kptr_(tail_p); + } +#endif + } + + fkb_dbg(2, "<<"); + +out: + FKB_AUDIT_RUN(); + + return pNBuff; +} + +/* + * Function : nbuff_flush + * Description: Flush (Hit_Writeback_Inv_D) a network buffer's packet data. + */ +void nbuff_flush(pNBuff_t pNBuff, uint8_t * data, int len) +{ + fkb_dbg(1, "pNBuff<%p> data<%p> len<%d>", + pNBuff, data, len); + if ( IS_SKBUFF_PTR(pNBuff) ) + { +#if !defined(CONFIG_BCM_GLB_COHERENCY) +/* Optimized flush for BPM buffers */ +#if (defined(CONFIG_BCM_BPM) || defined(CONFIG_BCM_BPM_MODULE)) + struct sk_buff *skb = (struct sk_buff *)pNBuff; + /* Use the dirty pointer cache flush optimization when the BPM is in + PRISTINE state which means the BPM is allocated by WFD/WLAN */ + if (skb->recycle_flags & SKB_BPM_PRISTINE) { + uint8_t *dirty_p = (uint8_t*)skb_shinfo(skb)->dirty_p; + if ((dirty_p != NULL) && (dirty_p > (uint8_t *)data)) + { + len = (uint8_t *)(dirty_p) - (uint8_t *)data; + len = len > BCM_MAX_PKT_LEN ? BCM_MAX_PKT_LEN : len; + } + } +#endif /* CONFIG_BCM_BPM || CONFIG_BCM_BPM_MODULE */ + if (len > 0) { + cache_flush_len(data, len); + } +#endif /* !CONFIG_BCM_GLB_COHERENCY */ + } + else + { + FkBuff_t * fkb_p = (FkBuff_t *)PNBUFF_2_PBUF(pNBuff); + fkb_flush(fkb_p, data, len, FKB_CACHE_FLUSH); + } + fkb_dbg(2, "<<"); +} + +/* + * Function : nbuff_free + * Description: Free/recycle a network buffer and associated data + * + * Freeing may involve a recyling of the network buffer into its respective + * pool (per network device driver pool, kernel cache or FKB pool). Likewise + * the associated buffer may be recycled if there are no other network buffers + * referencing it. + */ +void nbuff_free_ex(pNBuff_t pNBuff, int in_thread) +{ + void * pBuf = PNBUFF_2_PBUF(pNBuff); + fkb_dbg(1, "pNBuff<%p> pBuf<%p>", pNBuff, pBuf); + if ( IS_SKBUFF_PTR(pNBuff) ) + { + if(!in_thread) + dev_kfree_skb_any((struct sk_buff *)pBuf); + else + dev_kfree_skb_thread((struct sk_buff *)pBuf); + } + /* else if IS_FPBUFF_PTR, else if IS_TGBUFF_PTR */ + else + fkb_free(pBuf); + fkb_dbg(2, "<<"); +} + +void nbuff_free(pNBuff_t pNBuff) +{ +#if defined(CONFIG_BCM_PON) || defined(CONFIG_BCM_HNDROUTER) || defined(CONFIG_BCM947189) + nbuff_free_ex(pNBuff, 0); +#else + nbuff_free_ex(pNBuff, 1); +#endif +} + +/* + * Function : nbuff_flushfree + * Description: Flush (Hit_Writeback_Inv_D) and free/recycle a network buffer. + * If the data buffer was referenced by a single network buffer, then the data + * buffer will also be freed/recycled. + */ +static inline void nbuff_flushfree_ex(pNBuff_t pNBuff, int in_thread) +{ + void * pBuf = PNBUFF_2_PBUF(pNBuff); + fkb_dbg(1, "pNBuff<%p> pBuf<%p>", pNBuff, pBuf); + if ( IS_SKBUFF_PTR(pNBuff) ) + { + cache_flush_len(((struct sk_buff *)pBuf)->data, ((struct sk_buff *)pBuf)->len); + if(!in_thread) + dev_kfree_skb_irq((struct sk_buff *)pBuf); + else + dev_kfree_skb_thread((struct sk_buff *)pBuf); + } + /* else if IS_FPBUFF_PTR, else if IS_TGBUFF_PTR */ + else + { + FkBuff_t * fkb_p = (FkBuff_t *)pBuf; + fkb_flush(fkb_p, fkb_p->data, fkb_p->len, FKB_CACHE_FLUSH); + fkb_free(fkb_p); + } + fkb_dbg(2, "<<"); +} + +void nbuff_flushfree(pNBuff_t pNBuff) +{ +#if defined(CONFIG_BCM_PON) || defined(CONFIG_BCM_HNDROUTER) || defined(CONFIG_BCM947189) + nbuff_flushfree_ex(pNBuff, 0); +#else + nbuff_flushfree_ex(pNBuff, 1); +#endif +} + +/* + * Function : fkb_flush + * Description: Flush a FKB from current data or received packet data upto + * the dirty_p. When Flush Optimization is disabled, the entire length. + */ +void fkb_flush(FkBuff_t * fkb_p, uint8_t * data_p, int len, int cache_op) +{ + uint8_t * fkb_data_p; + + if ( _is_fkb_cloned_pool_(fkb_p) ) + fkb_data_p = PFKBUFF_TO_PDATA(fkb_p->master_p, BCM_PKT_HEADROOM); + else + fkb_data_p = PFKBUFF_TO_PDATA(fkb_p, BCM_PKT_HEADROOM); + + /* headers may have been popped */ + if ( (uintptr_t)data_p < (uintptr_t)fkb_data_p ) + fkb_data_p = data_p; + + { +#if defined(CC_NBUFF_FLUSH_OPTIMIZATION) + uint8_t * dirty_p; /* Flush only L1 dirty cache lines */ + dirty_p = _to_kptr_from_dptr_(fkb_p->dirty_p); /* extract kernel pointer */ + + fkb_dbg(1, "fkb_p<%p> fkb_data<%p> dirty_p<%p> len<%d>", + fkb_p, fkb_data_p, dirty_p, len); + + if (cache_op == FKB_CACHE_FLUSH) + cache_flush_region(fkb_data_p, dirty_p); + else + cache_invalidate_region(fkb_data_p, dirty_p); +#else + uint32_t data_offset; + data_offset = (uintptr_t)data_p - (uintptr_t)fkb_data_p; + + fkb_dbg(1, "fkb_p<%p> fkb_data<%p> data_offset<%d> len<%d>", + fkb_p, fkb_data_p, data_offset, len); + + if (cache_op == FKB_CACHE_FLUSH) + cache_flush_len(fkb_data_p, data_offset + len); + else + cache_invalidate_len(fkb_data_p, data_offset + len); +#endif + } +} + +/* + *------------------------------------------------------------------------------ + * Function Name: cache_flush_data_len + * Description : Flush Cache + *------------------------------------------------------------------------------ + */ +void cache_flush_data_len(void *addr, int len) +{ + cache_flush_len(addr, len); +} + + +EXPORT_SYMBOL(nbuff_dbg); + +EXPORT_SYMBOL(fkb_in_skb_test); +EXPORT_SYMBOL(fkb_construct); +EXPORT_SYMBOL(fkb_stats); + +EXPORT_SYMBOL(fkb_alloc); +EXPORT_SYMBOL(fkb_free); + +EXPORT_SYMBOL(fkb_unshare); + +EXPORT_SYMBOL(fkbM_borrow); +EXPORT_SYMBOL(fkbM_return); + +EXPORT_SYMBOL(fkb_xlate); +EXPORT_SYMBOL(nbuff_align_data); +EXPORT_SYMBOL(nbuff_flush); +EXPORT_SYMBOL(nbuff_free_ex); +EXPORT_SYMBOL(nbuff_free); +EXPORT_SYMBOL(nbuff_flushfree); +EXPORT_SYMBOL(fkb_flush); +EXPORT_SYMBOL(cache_flush_data_len); + +#endif /* CONFIG_BCM_KF_NBUFF */ diff -urN linux-4.1.52-orig/net/core/neighbour.c linux-4.1.52/net/core/neighbour.c --- linux-4.1.52-orig/net/core/neighbour.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/core/neighbour.c 2020-02-07 01:47:54.000000000 +0800 @@ -1180,6 +1180,15 @@ } if (lladdr != neigh->ha) { +#if defined(CONFIG_BCM_KF_BLOG) + /* Do not raise an ARP BINDING Change event when the ARP is + resolved first time. Raise this event only when there is + a real MAC address change.*/ + if (neigh->ha[0] != 0 || neigh->ha[1] != 0 || neigh->ha[2] != 0 || + neigh->ha[3] != 0 || neigh->ha[4] != 0 || neigh->ha[5] != 0) { + call_netevent_notifiers(NETEVENT_ARP_BINDING_CHANGE, neigh); + } +#endif write_seqlock(&neigh->ha_lock); memcpy(&neigh->ha, lladdr, dev->addr_len); write_sequnlock(&neigh->ha_lock); diff -urN linux-4.1.52-orig/net/core/net-procfs.c linux-4.1.52/net/core/net-procfs.c --- linux-4.1.52-orig/net/core/net-procfs.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/core/net-procfs.c 2020-02-07 01:47:54.000000000 +0800 @@ -98,6 +98,66 @@ stats->tx_compressed); } +#if defined(CONFIG_BCM_KF_EXTSTATS) + +static void devextstats_seq_printf_stats(struct seq_file *seq, struct net_device *dev) +{ + struct rtnl_link_stats64 temp; + const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp); + + unsigned long long rx_unicast_packets=0, tx_unicast_packets=0; /* Calculated unicast packets */ + + /* Calculate unicast packet counts as total packets less broadcast and multicast. + Normalize to zero in case an error sum of multicast and broadcast packets is reported */ + if((stats->multicast + stats->rx_broadcast_packets) < stats->rx_packets) + rx_unicast_packets = stats->rx_packets - (stats->multicast + stats->rx_broadcast_packets); + else + rx_unicast_packets = 0; + + if((stats->tx_multicast_packets + stats->tx_broadcast_packets) < stats->tx_packets) + tx_unicast_packets = stats->tx_packets - (stats->tx_multicast_packets + stats->tx_broadcast_packets); + else + tx_unicast_packets = 0; + +/* Print basic statistics, which are identical to baseline with only a few spacing differences */ + seq_printf(seq, "%10s:%8llu %7llu %4llu %4llu %4llu %5llu %5llu %5llu " + "%8llu %7llu %4llu %4llu %4llu %4llu %4llu %5llu ", + dev->name, stats->rx_bytes, stats->rx_packets, + stats->rx_errors, + stats->rx_dropped + stats->rx_missed_errors, + stats->rx_fifo_errors, + stats->rx_length_errors + stats->rx_over_errors + + stats->rx_crc_errors + stats->rx_frame_errors, + stats->rx_compressed, stats->multicast, + stats->tx_bytes, stats->tx_packets, + stats->tx_errors, stats->tx_dropped, + stats->tx_fifo_errors, stats->collisions, + stats->tx_carrier_errors + + stats->tx_aborted_errors + + stats->tx_window_errors + + stats->tx_heartbeat_errors, + stats->tx_compressed); + + /* Are extended stats supported? */ + if (dev->features & NETIF_F_EXTSTATS) + /* Print extended statistics */ + seq_printf(seq, "%6llu %6llu %6llu " /* Multicast */ + "%5llu %5llu %5llu %5llu " /* Unicast and broadcast*/ + "%5llu\n", /* Unknown RX errors */ + stats->tx_multicast_packets, stats->rx_multicast_bytes, stats->tx_multicast_bytes, + rx_unicast_packets, tx_unicast_packets, stats->rx_broadcast_packets, stats->tx_broadcast_packets, + stats->rx_unknown_packets); + else + /* Print placeholder with dashes */ + seq_printf(seq, " - - - " /* Multicast */ + " - - - - " /* Unicast and broadcast*/ + " -\n"); /* Unknown RX errors */ + +} +#endif + + + /* * Called from the PROCfs module. This now uses the new arbitrary sized * /proc/net interface to create /proc/net/dev @@ -115,6 +175,23 @@ return 0; } +#if defined(CONFIG_BCM_KF_EXTSTATS) +/* + * Called from the PROCfs module to create extended statistics file /proc/net/dev_extstats + */ +static int devextstats_seq_show(struct seq_file *seq, void *v) +{ + if (v == SEQ_START_TOKEN) + seq_puts(seq, " Basic Statistics | Extended Statistics\n" + " Interface| Receive | Transmit |multicast |unicast |broadcast |unkn\n" + " | bytes pckts errs drop fifo frame comp multi| bytes pckts errs drop fifo coll carr comp|txpckt rxbyte txbyte| rx tx| rx tx|rxerr\n"); + // 123456:12345678 1234567 1234 1234 1234 12345 12345 12345 12345678 1234567 1234 1234 1234 1234 1234 12345 123456 123456 123456 12345 12345 12345 12345 12345 + else + devextstats_seq_printf_stats(seq, v); + return 0; +} +#endif + static struct softnet_data *softnet_get_online(loff_t *pos) { struct softnet_data *sd = NULL; @@ -173,12 +250,29 @@ .show = dev_seq_show, }; +#if defined(CONFIG_BCM_KF_EXTSTATS) +static const struct seq_operations devextstats_seq_ops = { + .start = dev_seq_start, + .next = dev_seq_next, + .stop = dev_seq_stop, + .show = devextstats_seq_show, +}; +#endif + static int dev_seq_open(struct inode *inode, struct file *file) { return seq_open_net(inode, file, &dev_seq_ops, sizeof(struct seq_net_private)); } +#if defined(CONFIG_BCM_KF_EXTSTATS) +static int devextstats_seq_open(struct inode *inode, struct file *file) +{ + return seq_open_net(inode, file, &devextstats_seq_ops, + sizeof(struct seq_net_private)); +} +#endif + static const struct file_operations dev_seq_fops = { .owner = THIS_MODULE, .open = dev_seq_open, @@ -187,6 +281,17 @@ .release = seq_release_net, }; +#if defined(CONFIG_BCM_KF_EXTSTATS) +static const struct file_operations devextstats_seq_fops = { + .owner = THIS_MODULE, + .open = devextstats_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_net, +}; +#endif + + static const struct seq_operations softnet_seq_ops = { .start = softnet_seq_start, .next = softnet_seq_next, @@ -326,9 +431,17 @@ if (wext_proc_init(net)) goto out_ptype; + +#if defined(CONFIG_BCM_KF_EXTSTATS) && defined(CONFIG_BLOG) + if (!proc_create("dev_extstats", S_IRUGO, net->proc_net, &devextstats_seq_fops)) + goto out_ptype; + +#endif + rc = 0; out: return rc; + out_ptype: remove_proc_entry("ptype", net->proc_net); out_softnet: diff -urN linux-4.1.52-orig/net/core/request_sock.c linux-4.1.52/net/core/request_sock.c --- linux-4.1.52-orig/net/core/request_sock.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/core/request_sock.c 2020-02-07 01:47:54.000000000 +0800 @@ -38,7 +38,12 @@ EXPORT_SYMBOL(sysctl_max_syn_backlog); int reqsk_queue_alloc(struct request_sock_queue *queue, +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) unsigned int nr_table_entries) +#else + unsigned int nr_table_entries, + gfp_t flags) +#endif { size_t lopt_size = sizeof(struct listen_sock); struct listen_sock *lopt = NULL; @@ -49,11 +54,21 @@ lopt_size += nr_table_entries * sizeof(struct request_sock *); if (lopt_size <= (PAGE_SIZE << PAGE_ALLOC_COSTLY_ORDER)) +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) lopt = kzalloc(lopt_size, GFP_KERNEL | +#else + lopt = kzalloc(lopt_size, flags | +#endif __GFP_NOWARN | __GFP_NORETRY); if (!lopt) +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) lopt = vzalloc(lopt_size); +#else + lopt = __vmalloc(lopt_size, + flags | __GFP_HIGHMEM | __GFP_ZERO, + PAGE_KERNEL); +#endif if (!lopt) return -ENOMEM; diff -urN linux-4.1.52-orig/net/core/skbuff.c linux-4.1.52/net/core/skbuff.c --- linux-4.1.52-orig/net/core/skbuff.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/core/skbuff.c 2020-02-07 01:47:54.000000000 +0800 @@ -77,11 +77,183 @@ #include #include + struct kmem_cache *skbuff_head_cache __read_mostly; static struct kmem_cache *skbuff_fclone_cache __read_mostly; int sysctl_max_skb_frags __read_mostly = MAX_SKB_FRAGS; EXPORT_SYMBOL(sysctl_max_skb_frags); +#if defined(CONFIG_BCM_KF_NBUFF) +#include +#include + +/* Returns size of struct sk_buff */ +size_t skb_size(void) +{ + return sizeof(struct sk_buff); +} +EXPORT_SYMBOL(skb_size); + +size_t skb_aligned_size(void) +{ + return ((sizeof(struct sk_buff) + 0x0f) & ~0x0f); +} +EXPORT_SYMBOL(skb_aligned_size); + +int skb_layout_test(int head_offset, int tail_offset, int end_offset) +{ +#define SKBOFFSETOF(member) ((size_t)&((struct sk_buff*)0)->member) + if ((SKBOFFSETOF(head) == head_offset) && + (SKBOFFSETOF(tail) == tail_offset) && + (SKBOFFSETOF(end) == end_offset)) + return 1; + return 0; +} +EXPORT_SYMBOL(skb_layout_test); + +int skb_avail_headroom(const struct sk_buff *skb) +{ +#if defined(CONFIG_BCM_USBNET_ACCELERATION) + if (skb->clone_fc_head) { + /* In this case it's unlikely but possible for + * the value of skb->data - skb->clone_fc_head to be negative + * the caller should check for negative value + */ + return skb->data - skb->clone_fc_head; + } else +#endif + return skb->data - skb->head; +} +EXPORT_SYMBOL(skb_avail_headroom); + +void skb_bpm_tainted(struct sk_buff *skb) +{ + /* recycle_flags &= ~SKB_BPM_PRISTINE, dirty_p = NULL */ + SKB_BPM_TAINTED(skb); +} +EXPORT_SYMBOL(skb_bpm_tainted); + +void skb_data_pristine(struct sk_buff *skb) +{ + SKB_DATA_PRISTINE(skb); /* skb->dirty = skb->head */ +} +EXPORT_SYMBOL(skb_data_pristine); + +static inline void _skb_headerreset(struct sk_buff *skb) +{ + memset(skb, 0, offsetof(struct sk_buff, truesize)); +} +void skb_headerreset(struct sk_buff *skb) +{ + _skb_headerreset(skb); + atomic_set(&skb->users, 1); +} +EXPORT_SYMBOL(skb_headerreset); + +static inline void _skb_shinforeset(struct skb_shared_info *skb_shinfo) +{ + memset(skb_shinfo, 0, offsetof(struct skb_shared_info, dataref)); +} +void skb_shinforeset(struct skb_shared_info *skb_shinfo) +{ + _skb_shinforeset(skb_shinfo); + atomic_set(&(skb_shinfo->dataref), 1); +} +EXPORT_SYMBOL(skb_shinforeset); + +/** + * + * skb_headerinit - initialize a socket buffer header + * @headroom: reserved headroom size + * @datalen: data buffer size, data buffer is allocated by caller + * @skb: skb allocated by caller + * @data: data buffer allocated by caller + * @recycle_hook: callback function to free data buffer and skb + * @recycle_context: context value passed to recycle_hook, param1 + * @blog_p: pass a blog to a skb for logging + * + * Initializes the socket buffer and assigns the data buffer to it. + * Both the sk_buff and the pointed data buffer are pre-allocated. + * + */ +void skb_headerinit(unsigned int headroom, unsigned int datalen, + struct sk_buff *skb, unsigned char *data, + RecycleFuncP recycle_hook, unsigned long recycle_context, + struct blog_t *blog_p) /* defined(CONFIG_BLOG) */ +{ + _skb_headerreset(skb); /* memset to truesize */ + + skb->truesize = datalen + sizeof(struct sk_buff); + atomic_set(&skb->users, 1); + skb->head = data - headroom; + skb->data = data; + skb_set_tail_pointer(skb, datalen); + /* FIXME!! check if this alignment is to ensure cache line aligned? + * make sure skb buf ends at 16 bytes boudary */ + skb->end = skb->tail + (0x10 - (((uintptr_t)skb_tail_pointer(skb)) & 0xf)); + skb->len = datalen; + +#if defined (CONFIG_BCM_KF_BPM_BUF_TRACKING) + GBPM_INC_REF(data); + KERN_GBPM_TRACK_SKB(skb, GBPM_VAL_INIT, 0); +#endif +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + skb->blog_p = blog_p; + if (blog_p) + blog_p->skb_p = skb; + // skb->tunl = NULL; memset in _skb_headerreset +#endif +#if defined(CONFIG_BCM_KF_VLAN) && (defined(CONFIG_BCM_VLAN) || defined(CONFIG_BCM_VLAN_MODULE)) + // skb->vlan_count = 0; memset in _skb_headerreset +#endif +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + // skb->map_id = 0; memset in _skb_headerreset + // skb->map_offset = 0; memset in skb_headerreset +#endif + skb->fc_ctxt = 0; + skb->recycle_hook = recycle_hook; + skb->recycle_context = recycle_context; + skb->recycle_flags = SKB_RECYCLE | SKB_DATA_RECYCLE; + + _skb_shinforeset(skb_shinfo(skb)); + atomic_set(&(skb_shinfo(skb)->dataref), 1); +} +EXPORT_SYMBOL(skb_headerinit); + +struct sk_buff *skb_header_alloc(void) +{ + return kmem_cache_alloc(skbuff_head_cache, GFP_ATOMIC); +} +EXPORT_SYMBOL(skb_header_alloc); + +/* + * ----------------------------------------------------------------------------- + * Function: Free or recycle an sk_buff memory + * + * CAUTION : API ignores any databuf attached to it. + * Caller of the function should handle databuf. + * ----------------------------------------------------------------------------- + */ +void skb_header_free(struct sk_buff *skb) +{ + /* If the skb came from a preallocated pool, pass it to recycler hook */ + if (skb->recycle_hook && (skb->recycle_flags & SKB_RECYCLE)) + { + /* Detach data buffer */ + /* Caller should take care of databuf */ + skb->head = NULL; + skb->data = NULL; + skb->recycle_flags &= ~ SKB_DATA_RECYCLE; + (*skb->recycle_hook)(skb, skb->recycle_context, SKB_RECYCLE); + } + else + { + kmem_cache_free(skbuff_head_cache, skb); + } +} +EXPORT_SYMBOL(skb_header_free); +#endif /* CONFIG_BCM_KF_NBUFF */ + /** * skb_panic - private function for out-of-line support * @skb: buffer @@ -208,6 +380,20 @@ u8 *data; bool pfmemalloc; +#if defined(CONFIG_BCM_KF_ARM64_BCM963XX) && defined(CONFIG_BCM94908) && defined(CONFIG_BCM_HND_EAP) + /* + * runner on 94908 supports only 30 bit addressing, which limits + * its operation to the lower 1 Gig memory space. Otherwise an + * extra packet copy is performed which slows its operation. + * Therefore, for this platform, force the buffer to always be + * dma'able, hence ensuring it always comes from the low 1 Gig + * address space. + */ + if (likely(!(gfp_mask & GFP_HIGHUSER))) + gfp_mask |= GFP_DMA; + +#endif /* defined(CONFIG_BCM_KF_ARM64_BCM963XX) && defined(CONFIG_BCM94908) && defined(CONFIG_BCM_HND_EAP) */ + cache = (flags & SKB_ALLOC_FCLONE) ? skbuff_fclone_cache : skbuff_head_cache; @@ -220,6 +406,29 @@ goto out; prefetchw(skb); +#if defined(CONFIG_BCM_KF_NBUFF) + if (size < ENET_MIN_MTU_SIZE_EXT_SWITCH) { + /* Add enough tailroom so that small packets can be padded + * with 0s to meet the Ethernet minimum pkt size of 60 bytes + + * EXT_SW TAG Total: 64 bytes) + * This will help improve performance with NAS test scenarios + * where the TCP ACK is usually less than 60 bytes + * WARNING: Note that the macro SKB_WITH_OVERHEAD() does not + * take into account this additional tailroom overhead. If + * the original size passed to this function uses the + * SKB_WITH_OVERHEAD macro to calculate the alloc length then + * this function will allocate more than what is expected and + * this can cause length (calculated using SKB_WITH_OVERHEAD) + * based operations to fail. We found few instances of skb + * allocations using the macro SKB_WITH_OVERHEAD (for ex: + * allocations using NLMSG_DEFAULT_SIZE in netlink.h). + * However, all those allocations allocate large skbs + * (page size 4k/8k) and will not enter this additional tailroom + * logic */ + size = ENET_MIN_MTU_SIZE_EXT_SWITCH; + } +#endif + /* We do our best to align skb_shared_info on a separate cache * line. It usually works because kmalloc(X > SMP_CACHE_BYTES) gives * aligned memory blocks, unless SLUB/SLAB debug is enabled. @@ -237,12 +446,21 @@ size = SKB_WITH_OVERHEAD(ksize(data)); prefetchw(data + size); +#if defined(CONFIG_BCM_KF_NBUFF) + /* + * Clearing all fields -- fields that were not cleared before + * were moved to earlier locations in the structure, so just + * zeroing them out (OK, since we overwrite them shortly: + */ + memset(skb, 0, offsetof(struct sk_buff, truesize)); +#else /* * Only clear those fields we need to clear, not those that we will * actually initialise below. Hence, don't put any more fields after * the tail pointer in struct sk_buff! */ memset(skb, 0, offsetof(struct sk_buff, tail)); +#endif /* Account for allocated memory : skb + skb->head */ skb->truesize = SKB_TRUESIZE(size); skb->pfmemalloc = pfmemalloc; @@ -257,6 +475,10 @@ /* make sure we initialize shinfo sequentially */ shinfo = skb_shinfo(skb); memset(shinfo, 0, offsetof(struct skb_shared_info, dataref)); +#if defined(CONFIG_BCM_KF_NBUFF) + shinfo->dirty_p = NULL; +#endif + atomic_set(&shinfo->dataref, 1); kmemcheck_annotate_variable(shinfo->destructor_arg); @@ -603,7 +825,11 @@ skb_drop_list(&skb_shinfo(skb)->frag_list); } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static void skb_clone_fraglist(struct sk_buff *skb) +#else +void skb_clone_fraglist(struct sk_buff *skb) +#endif { struct sk_buff *list; @@ -613,6 +839,15 @@ static void skb_free_head(struct sk_buff *skb) { +#if defined(CONFIG_BCM_KF_NBUFF) + /* If the data buffer came from a pre-allocated pool, recycle it. + * Recycling may only be performed when no references exist to it. */ + if (skb->recycle_hook && (skb->recycle_flags & SKB_DATA_RECYCLE)) { + (*skb->recycle_hook)(skb, skb->recycle_context, SKB_DATA_RECYCLE); + skb->recycle_flags &= SKB_DATA_NO_RECYCLE; /* mask out */ + return; + } +#endif if (skb->head_frag) put_page(virt_to_head_page(skb->head)); else @@ -657,6 +892,17 @@ { struct sk_buff_fclones *fclones; +#if defined(CONFIG_BCM_KF_NBUFF) +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_free(skb, blog_free_reason_kfree); +#endif + + /* If the skb came from a preallocated pool, pass it to recycler hook */ + if (skb->recycle_hook && (skb->recycle_flags & SKB_RECYCLE)) + (*skb->recycle_hook)(skb, skb->recycle_context, SKB_RECYCLE); + else { +#endif /* CONFIG_BCM_KF_NBUFF */ + switch (skb->fclone) { case SKB_FCLONE_UNAVAILABLE: kmem_cache_free(skbuff_head_cache, skb); @@ -681,6 +927,9 @@ return; fastpath: kmem_cache_free(skbuff_fclone_cache, fclones); +#if defined(CONFIG_BCM_KF_NBUFF) + } +#endif /* CONFIG_BCM_KF_NBUFF */ } static void skb_release_head_state(struct sk_buff *skb) @@ -699,6 +948,9 @@ #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) nf_bridge_put(skb->nf_bridge); #endif +#if defined(CONFIG_BCM_KF_NBUFF) + skb->tc_word = 0; +#endif /* CONFIG_BCM_KF_NBUFF */ } /* Free everything but the sk_buff shell. */ @@ -720,6 +972,16 @@ void __kfree_skb(struct sk_buff *skb) { +#if defined (CONFIG_BCM_KF_BPM_BUF_TRACKING) + GBPM_DEC_REF(skb->data); + KERN_GBPM_TRACK_SKB(skb, GBPM_VAL_FREE, 0); +#endif +#if defined(CONFIG_BCM_KF_NBUFF) + if (skb->recycle_hook && (skb->recycle_flags & SKB_RECYCLE_NOFREE)) { + (*skb->recycle_hook)(skb, skb->recycle_context, SKB_RECYCLE_NOFREE); + return; + } +#endif skb_release_all(skb); kfree_skbmem(skb); } @@ -756,6 +1018,486 @@ } EXPORT_SYMBOL(kfree_skb_list); +#if defined(CONFIG_BCM_KF_NBUFF) +/* + * Translate a fkb to a skb, by allocating a skb from the skbuff_head_cache. + * PS. skb->dev is not set during initialization. + * + * Caller verifies whether the fkb is unshared: + * if fkb_p==NULL||IS_FKB_CLONE(fkb_p)||fkb_p->users>1 and return NULL skb. + * + * skb_xlate is deprecated. New code should call skb_xlate_dp directly. + */ +struct sk_buff * skb_xlate(struct fkbuff * fkb_p) +{ + return (skb_xlate_dp(fkb_p, NULL)); +} +EXPORT_SYMBOL(skb_xlate); + +struct sk_buff *skb_xlate_dp(struct fkbuff * fkb_p, uint8_t *dirty_p) +{ + struct sk_buff * skb_p; + unsigned int datalen; + + /* Optimization: use preallocated pool of skb with SKB_POOL_RECYCLE flag */ + skb_p = kmem_cache_alloc(skbuff_head_cache, GFP_ATOMIC); + if (!skb_p) + return skb_p; + skb_p->fclone = SKB_FCLONE_UNAVAILABLE; + + memset(skb_p, 0, offsetof(struct sk_buff, truesize)); + + datalen = SKB_DATA_ALIGN(fkb_p->len + BCM_SKB_TAILROOM); + + skb_p->data = fkb_p->data; + skb_p->head = (unsigned char *)(fkb_p + 1 ); + skb_set_tail_pointer(skb_p, fkb_p->len); + /* FIXME!! check whether this has to do with the cache line size + * make sure skb buf ends at 16 bytes boudary */ + skb_p->end = skb_p->tail + (0x10 - (((uintptr_t)skb_tail_pointer(skb_p)) & 0xf)); + +#if defined (CONFIG_BCM_KF_BPM_BUF_TRACKING) + GBPM_INC_REF(skb_p->data); + KERN_GBPM_TRACK_SKB(skb_p, GBPM_VAL_XLATE, 0); +#endif + +#define F2S(x) skb_p->x = fkb_p->x + F2S(len); + F2S(queue); + F2S(priority); + +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + if (_IS_BPTR_(fkb_p->blog_p)) { /* should not happen */ + F2S(blog_p); + fkb_p->blog_p->skb_p = skb_p; + } +#endif + F2S(recycle_hook); + F2S(recycle_context); + skb_p->recycle_flags = SKB_DATA_RECYCLE; + + /* redundant: fkb_p must not be used henceforth */ + fkb_dec_ref(fkb_p); + + atomic_set(&skb_p->users, 1); + skb_p->truesize = datalen + sizeof(struct sk_buff); + + /* any change to skb_shinfo initialization in __alloc_skb must be ported + * to this block. */ + atomic_set(&(skb_shinfo(skb_p)->dataref), 1); + skb_shinfo(skb_p)->nr_frags = 0; + skb_shinfo(skb_p)->gso_size = 0; + skb_shinfo(skb_p)->gso_segs = 0; + skb_shinfo(skb_p)->gso_type = 0; + skb_shinfo(skb_p)->ip6_frag_id = 0; + skb_shinfo(skb_p)->tx_flags = 0; + skb_shinfo(skb_p)->frag_list = NULL; + memset(&(skb_shinfo(skb_p)->hwtstamps), 0, + sizeof(skb_shinfo(skb_p)->hwtstamps)); +#if defined(CONFIG_BCM_CSO) + if (fkb_p->rx_csum_verified) + skb_p->ip_summed = CHECKSUM_UNNECESSARY; +#endif + /* + * When fkb is xlated to skb, preserve the dirty_p info. + * This allows receiving driver to shorten its cache flush and also + * can shorten the cache flush when the buffer is recycled. Improves + * wlan perf by 10%. + */ + skb_shinfo(skb_p)->dirty_p = dirty_p; + + return skb_p; +#undef F2S +} +EXPORT_SYMBOL(skb_xlate_dp); + +#define NETDEV_XMIT(_dev, _buff) \ + _dev->netdev_ops->ndo_start_xmit(_buff, _dev) + +/* + * This fucntion fragments the skb into multiple skbs and xmits them + * this fucntion is a substitue for ip_fragment when Ip stack is skipped + * for packet acceleartion(fcache,CMF) + * + * Currently only IPv4 is supported + */ +void skb_frag_xmit4(struct sk_buff *origskb, struct net_device *txdev, + uint32_t is_pppoe, uint32_t minMtu, void *ipp) +{ + +#define DEBUG_SKBFRAG(args) + +#define IP_DF 0x4000 /* Flag: "Don't Fragment" */ +#define IP_MF 0x2000 /* Flag: "More Fragments" */ +#define IP_OFFSET 0x1FFF /* "Fragment Offset" part */ + + struct iphdr *iph; + int datapos, offset; + unsigned int max_dlen, hlen, hdrslen, left, len; + uint16_t not_last_frag; + struct sk_buff *fraglisthead; + struct sk_buff *fraglisttail; + struct sk_buff *skb2; + + DEBUG_SKBFRAG(("skb_frag_xmit4:enter origskb=%p,netdev=%p,is_pppoe=%d,\ + minMtu=%d ipp=%p\n",origskb, txdev, is_pppoe, + minMtu, ipp)); + + if (likely(origskb->len <= minMtu)) { + /* xmit packet */ + NETDEV_XMIT(txdev, (void *)CAST_REAL_TO_VIRT_PNBUFF(origskb, + SKBUFF_PTR)); + return; + } + + fraglisthead = NULL; + fraglisttail = NULL; + skb2 = NULL; + + DEBUG_SKBFRAG(("skb_frag_xmit4: checking for DF\n")); + iph = (struct iphdr *)ipp; + /* DROP the packet if DF flag is set */ + if (unlikely((iph->frag_off & htons(IP_DF)) && !(origskb->ignore_df))) { + /*----TODO: update error stats, send icmp error message ?--- */ + kfree_skb(origskb); + return; + } + + hlen = iph->ihl * 4; + + DEBUG_SKBFRAG(("skb_frag_xmit4: calculating hdrs len\n")); + /* calculate space for data,(ip payload) */ + hdrslen = ((uintptr_t)ipp - (uintptr_t)(origskb->data)) + hlen; + + left = origskb->len - hdrslen; /* Size of ip payload */ + datapos = hdrslen;/* Where to start from */ + max_dlen = minMtu - hdrslen; /* ip payload per frame */ + + DEBUG_SKBFRAG(("skb_frag_xmit4: computed hdrslen=%d, left=%d\n", + hdrslen, left)); + + /* frag_offset is represented in 8 byte blocks */ + offset = (ntohs(iph->frag_off) & IP_OFFSET) << 3; + not_last_frag = iph->frag_off & htons(IP_MF); + + /* copy the excess data (>MTU size) from orig fkb to new fkb's */ + fraglisthead = origskb; + + while (left > 0) { + DEBUG_SKBFRAG(("skb_frag_xmit4: making fragments\n")); + len = left; + /* IF: it doesn't fit, use 'max_dlen' - the data space left */ + if (len > max_dlen) + len = max_dlen; + /* IF: we are not sending upto and including the packet end + then align the next start on an eight byte boundary */ + if (len < left) + len &= ~7; + + if (datapos == hdrslen) { + /*reuse the orig skb for 1st fragment */ + skb2 = origskb; + DEBUG_SKBFRAG(("skb_frag_xmit4: reusing skb\n")); + skb2->next = NULL; + fraglisttail = skb2; + skb2->len = hdrslen+len; + skb_set_tail_pointer(skb2, hdrslen+len); + } else { + + DEBUG_SKBFRAG(("skb_frag_xmit4: genrating new skb\n")); + /* Allocate a new skb */ + if ((skb2 = alloc_skb(len+hdrslen, GFP_ATOMIC)) == NULL) { + printk(KERN_INFO "no memory for new fragment!\n"); + goto fail; + } + + /* copy skb metadata */ + skb2->queue = origskb->queue; + skb2->priority = origskb->priority; + skb2->dev = origskb->dev; + + dst_release(skb_dst(skb2)); + skb_dst_set(skb2, dst_clone(skb_dst(origskb))); +#ifdef CONFIG_NET_SCHED + skb2->tc_index = origskb->tc_index; +#endif + + skb_put(skb2, len + hdrslen); + + DEBUG_SKBFRAG(("skb_frag_xmit4: copying headerto new skb\n")); + + /* copy the l2 header &l3 header to new fkb from orig fkb */ + memcpy(skb2->data, origskb->data, hdrslen); + + DEBUG_SKBFRAG(("skb_frag_xmit4: copying data to new skb\n")); + /* + * Copy a block of the IP datagram. + */ + memcpy(skb2->data + hdrslen, origskb->data + datapos, + len); + + skb2->next = NULL; + fraglisttail->next = skb2; + fraglisttail = skb2; + } + + /* Fill in the new header fields. */ + DEBUG_SKBFRAG(("skb_frag_xmit4: adjusting ipheader\n")); + iph = (struct iphdr *)(skb2->data + (hdrslen- hlen)); + iph->frag_off = htons((offset >> 3)); + iph->tot_len = htons(len + hlen); + + /* fix pppoelen */ + if (is_pppoe) + *((uint16_t*)iph - 2) = htons(len + hlen + + sizeof(uint16_t)); + + left -= len; + datapos += len; + offset += len; + + /* If we are fragmenting a fragment that's not the + * last fragment then keep MF on each fragment */ + if (left > 0 || not_last_frag) + iph->frag_off |= htons(IP_MF); + //else + //iph->frag_off &= ~htons(IP_MF);/*make sure MF is cleared */ + + DEBUG_SKBFRAG(("skb_frag_xmit4: computing ipcsum\n")); + /* fix ip checksum */ + iph->check = 0; + /* TODO replace with our own csum_calc */ + iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); + + DEBUG_SKBFRAG(("skb_frag_xmit4: loop done\n")); + } + + /* xmit skb's */ + while (fraglisthead) { + DEBUG_SKBFRAG(("skb_frag_xmit4: sending skb fragment \n")); + skb2 = fraglisthead; + fraglisthead = fraglisthead->next; + skb2->next = NULL; + NETDEV_XMIT(txdev, (void *)CAST_REAL_TO_VIRT_PNBUFF(skb2, + SKBUFF_PTR)); + } + return; + +fail: + DEBUG_SKBFRAG(("skb_frag_xmit4: ENTERED FAIL CASE\n")); + while (fraglisthead) { + skb2 = fraglisthead; + fraglisthead = fraglisthead->next; + kfree_skb(skb2); + } + return; + +} +EXPORT_SYMBOL(skb_frag_xmit4); + +#ifdef CONFIG_IPV6 +static void ipv6_generate_ident(struct frag_hdr *fhdr) +{ + static atomic_t ipv6_fragmentation_id; + int old, new; + + do { + old = atomic_read(&ipv6_fragmentation_id); + new = old + 1; + if (!new) + new = 1; + } while (atomic_cmpxchg(&ipv6_fragmentation_id, old, new) != old); + fhdr->identification = htonl(new); +} +#endif + +/* + * This fucntion fragments the skb into multiple skbs and xmits them + * this fucntion is a substitue for ip6_fragment when IPv6 stack is skipped + * for packet acceleartion + * + * Assumption: there should be no extension header in IPv6 header while + * learning the tunnel traffic + * + * Currently only IPv6 is supported + */ +void skb_frag_xmit6(struct sk_buff *origskb, struct net_device *txdev, + uint32_t is_pppoe, uint32_t minMtu, void *ipp) +{ +#ifdef CONFIG_IPV6 + struct ipv6hdr *iph; + int datapos, offset; + struct frag_hdr *fh; + __be32 frag_id = 0; + u8 nexthdr; + unsigned int max_dlen, hlen, hdrslen, left, len, frag_hdrs_len; + struct sk_buff *fraglisthead; + struct sk_buff *fraglisttail; + struct sk_buff *skb2; + + DEBUG_SKBFRAG(("skb_frag_xmit6:enter origskb=%p,netdev=%p,is_pppoe=%d,\ + minMtu=%d ipp=%p\n",origskb, txdev, is_pppoe, minMtu, ipp)); + + if (likely(origskb->len <= minMtu)) { + /* xmit packet */ + NETDEV_XMIT(txdev, (void *)CAST_REAL_TO_VIRT_PNBUFF(origskb, + SKBUFF_PTR)); + return; + } + + fraglisthead = NULL; + fraglisttail = NULL; + skb2 = NULL; + + iph = (struct ipv6hdr *)ipp; + hlen = sizeof(struct ipv6hdr); + + DEBUG_SKBFRAG(("skb_frag_xmit6: calculating hdrs len\n")); + /* calculate space for data,(ip payload) */ + hdrslen = ((uintptr_t)ipp - (uintptr_t)(origskb->data)) + hlen; + + left = origskb->len - hdrslen; /* Size of remaining ip payload */ + datapos = hdrslen;/* Where to start from */ + /* hdrlens including frag_hdr of packets after fragmented */ + frag_hdrs_len = hdrslen + sizeof(struct frag_hdr); + /* calculate max ip payload len per frame */ + max_dlen = minMtu - frag_hdrs_len; + nexthdr = iph->nexthdr; + + DEBUG_SKBFRAG(("skb_frag_xmit6: computed hdrslen=%d, left=%d, max=%d\n", + hdrslen, left, max_dlen)); + + offset = 0; + /* copy the excess data (>MTU size) from orig fkb to new fkb's */ + fraglisthead = origskb; + + /* len represents length of payload! */ + while (left > 0) { + DEBUG_SKBFRAG(("skb_frag_xmit6: making fragments\n")); + len = left; + /* IF: it doesn't fit, use 'max_dlen' - the data space left */ + if (len > max_dlen) + len = max_dlen; + /* IF: we are not sending upto and including the packet end + then align the next start on an eight byte boundary */ + if (len < left) + len &= ~7; + + /* + * Create new skbs to fragment the packet. Instead of reusing the + * orignal skb, a new skb is allocated to insert frag header + */ + DEBUG_SKBFRAG(("skb_frag_xmit6: genrating new skb\n")); + /* Allocate a new skb */ + if ((skb2 = alloc_skb(len+frag_hdrs_len, GFP_ATOMIC)) == NULL) { + printk(KERN_INFO "no memory for new fragment!\n"); + goto fail; + } + + /* copy skb metadata */ + skb2->queue = origskb->queue; + skb2->priority = origskb->priority; + skb2->dev = origskb->dev; + + dst_release(skb_dst(skb2)); + skb_dst_set(skb2, dst_clone(skb_dst(origskb))); +#ifdef CONFIG_NET_SCHED + skb2->tc_index = origskb->tc_index; +#endif + skb_put(skb2, len + frag_hdrs_len); + + DEBUG_SKBFRAG(("skb_frag_xmit6: copying headerto new skb\n")); + + /* copy the l2 header & l3 header to new fkb from orig fkb */ + memcpy(skb2->data, origskb->data, hdrslen); + + DEBUG_SKBFRAG(("skb_frag_xmit6: copying data to new skb\n")); + /* Copy a block of the IP datagram. */ + memcpy(skb2->data+frag_hdrs_len, origskb->data+datapos, len); + + skb2->next = NULL; + + /* first fragment, setup fraglist */ + if (datapos == hdrslen) { + fraglisthead = skb2; + fraglisttail = skb2; + } else { + fraglisttail->next = skb2; + fraglisttail = skb2; + } + + /* Fill in the new header fields. */ + DEBUG_SKBFRAG(("skb_frag_xmit6: adjusting IPv6 header\n")); + iph = (struct ipv6hdr *)(skb2->data + (hdrslen - hlen)); + iph->payload_len = htons(len + sizeof(struct frag_hdr)); + iph->nexthdr = NEXTHDR_FRAGMENT; + + /* insert fragmentation header */ + fh = (struct frag_hdr *)(iph + 1); + fh->nexthdr = nexthdr; + fh->reserved = 0; + if (!frag_id) { + ipv6_generate_ident(fh); + frag_id = fh->identification; + } else + fh->identification = frag_id; + fh->frag_off = htons(offset); + + /* fix pppoelen */ + if (is_pppoe) + *((uint16_t*)iph - 2) = htons(len + + sizeof(struct frag_hdr) + + sizeof(struct ipv6hdr) + + sizeof(uint16_t)); + left -= len; + datapos += len; + offset += len; + + /* If we are fragmenting a fragment that's not the + * last fragment then keep MF on each fragment */ + if (left > 0) + fh->frag_off |= htons(IP6_MF); + + DEBUG_SKBFRAG(("skb_frag_xmit6: loop done\n")); + } + + /* xmit skb's */ + while (fraglisthead) { + DEBUG_SKBFRAG(("skb_frag_xmit6: sending skb fragment \n")); + skb2 = fraglisthead; + fraglisthead = fraglisthead->next; + skb2->next = NULL; + NETDEV_XMIT(txdev, (void *)CAST_REAL_TO_VIRT_PNBUFF(skb2, + SKBUFF_PTR)); + } + + /* free the orignal skb */ + kfree_skb(origskb); + + return; + +fail: + DEBUG_SKBFRAG(("skb_frag_xmit6: ENTERED FAIL CASE\n")); + while (fraglisthead) { + skb2 = fraglisthead; + fraglisthead = fraglisthead->next; + kfree_skb(skb2); + } + + /* free the orignal skb */ + kfree_skb(origskb); + + return; + +#else /* !CONFIG_IPV6 */ + DEBUG_SKBFRAG(("skb_frag_xmit6: called while IPv6 is disabled in kernel?\n")); + kfree_skb(origskb); + return; +#endif +} +EXPORT_SYMBOL(skb_frag_xmit6); +#endif /* defined(CONFIG_BCM_KF_NBUFF) */ + /** * skb_tx_error - report an sk_buff xmit error * @skb: buffer that triggered an error @@ -806,6 +1548,14 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old) { +#if defined(CONFIG_BCM_KF_VLAN) && (defined(CONFIG_BCM_VLAN) || defined(CONFIG_BCM_VLAN_MODULE)) + int i; +#endif + +#if defined(CONFIG_BCM_KF_WL) + memset(new->pktc_cb, 0, sizeof(new->pktc_cb)); +#endif + new->tstamp = old->tstamp; /* We do not copy old->sk */ new->dev = old->dev; @@ -820,6 +1570,11 @@ * It is not yet because we do not want to have a 16 bit hole */ new->queue_mapping = old->queue_mapping; +#if defined(CONFIG_BCM_KF_NBUFF) + new->queue = old->queue; + new->priority = old->priority; + new->recycle_and_rnr_flags |= old->recycle_and_rnr_flags & SKB_RNR_FLAGS; +#endif memcpy(&new->headers_start, &old->headers_start, offsetof(struct sk_buff, headers_end) - @@ -827,18 +1582,25 @@ CHECK_SKB_FIELD(protocol); CHECK_SKB_FIELD(csum); CHECK_SKB_FIELD(hash); +#if defined(CONFIG_BCM_KF_NBUFF) +#else CHECK_SKB_FIELD(priority); +#endif CHECK_SKB_FIELD(skb_iif); CHECK_SKB_FIELD(vlan_proto); CHECK_SKB_FIELD(vlan_tci); CHECK_SKB_FIELD(transport_header); CHECK_SKB_FIELD(network_header); CHECK_SKB_FIELD(mac_header); + CHECK_SKB_FIELD(inner_protocol); CHECK_SKB_FIELD(inner_transport_header); CHECK_SKB_FIELD(inner_network_header); CHECK_SKB_FIELD(inner_mac_header); +#if defined(CONFIG_BCM_KF_NBUFF) +#else CHECK_SKB_FIELD(mark); +#endif #ifdef CONFIG_NETWORK_SECMARK CHECK_SKB_FIELD(secmark); #endif @@ -848,12 +1610,48 @@ #ifdef CONFIG_XPS CHECK_SKB_FIELD(sender_cpu); #endif +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + new->tunl = old->tunl; +#endif +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_xfer(new, old); /* CONFIG_BLOG: transfers blog ownership */ +#endif + +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_ACCELERATION)) + if (old->clone_fc_head) { + /* here we expect old->data > old->clone_fc_head, if for some reason this is not + * true we still need to set new->clone_fc_head. + * skb_avail_headroom , will check for this error + */ + new->clone_fc_head = new->data - (int)(old->data - old->clone_fc_head); + } +#endif + +#if defined(CONFIG_BCM_KF_NBUFF) + new->vtag_word = old->vtag_word; + new->tc_word = old->tc_word; +#if defined(CONFIG_BCM_KF_VLAN) && (defined(CONFIG_BCM_VLAN) || defined(CONFIG_BCM_VLAN_MODULE)) + new->vlan_count = old->vlan_count; + new->vlan_tpid = old->vlan_tpid; + for (i = 0; i < SKB_VLAN_MAX_TAGS; i++) { + new->vlan_header[i] = old->vlan_header[i]; + } + new->rxdev = old->rxdev; +#endif /* BCM_VLAN */ + new->in_dev = old->in_dev; +#else /* CONFIG_BCM_KF_NBUFF */ + #ifdef CONFIG_NET_SCHED CHECK_SKB_FIELD(tc_index); #ifdef CONFIG_NET_CLS_ACT CHECK_SKB_FIELD(tc_verd); #endif #endif +#endif /* CONFIG_BCM_KF_NBUFF */ + +#if defined(CONFIG_BCM_KF_WL) + new->pktc_flags = old->pktc_flags; +#endif } @@ -863,6 +1661,9 @@ */ static struct sk_buff *__skb_clone(struct sk_buff *n, struct sk_buff *skb) { +#if defined(CONFIG_BCM_KF_VLAN) && (defined(CONFIG_BCM_VLAN) || defined(CONFIG_BCM_VLAN_MODULE)) + int i; +#endif #define C(x) n->x = skb->x n->next = n->prev = NULL; @@ -883,6 +1684,42 @@ C(head_frag); C(data); C(truesize); + +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_ACCELERATION)) + n->clone_wr_head = NULL; + skb->clone_wr_head = NULL; + C(clone_fc_head); +#endif + +#if defined (CONFIG_BCM_KF_BPM_BUF_TRACKING) + GBPM_INC_REF(skb->data); + KERN_GBPM_TRACK_SKB(skb, GBPM_VAL_COPY_SRC, 0); + KERN_GBPM_TRACK_SKB(skb, GBPM_VAL_COPY_DST, 0); +#endif + +#if defined(CONFIG_BCM_KF_NBUFF) + C(recycle_hook); + C(recycle_context); + n->recycle_flags = skb->recycle_flags & SKB_NO_RECYCLE; +#if defined(CONFIG_BCM_KF_VLAN) && (defined(CONFIG_BCM_VLAN) || defined(CONFIG_BCM_VLAN_MODULE)) + C(vlan_count); + C(vlan_tpid); + C(cfi_save); + C(vlan_tci); + C(vlan_proto); + for (i = 0; iusers, 1); atomic_inc(&(skb_shinfo(skb)->dataref)); @@ -904,8 +1741,30 @@ */ struct sk_buff *skb_morph(struct sk_buff *dst, struct sk_buff *src) { +#if defined(CONFIG_BCM_KF_NBUFF) + struct sk_buff *skb; + unsigned int recycle_flags; + unsigned long recycle_context; + RecycleFuncP recycle_hook; + + skb_release_all(dst); + + /* Need to retain the recycle flags, context & hook of dst to free it + * into proper pool. */ + recycle_flags = dst->recycle_flags & SKB_RECYCLE; + recycle_hook = dst->recycle_hook; + recycle_context = dst->recycle_context; + + skb = __skb_clone(dst, src); + + dst->recycle_flags |= recycle_flags; + dst->recycle_hook = recycle_hook; + dst->recycle_context = recycle_context; + return skb; +#else /* CONFIG_BCM_KF_NBUFF */ skb_release_all(dst); return __skb_clone(dst, src); +#endif /* CONFIG_BCM_KF_NBUFF */ } EXPORT_SYMBOL_GPL(skb_morph); @@ -1029,7 +1888,11 @@ skb->inner_mac_header += off; } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old) +#else +void copy_skb_header(struct sk_buff *new, const struct sk_buff *old) +#endif { __copy_skb_header(new, old); @@ -1081,6 +1944,10 @@ BUG(); copy_skb_header(n, skb); + +#if defined (CONFIG_BCM_KF_BPM_BUF_TRACKING) + KERN_GBPM_TRACK_SKB(skb, GBPM_VAL_COPY_SRC, 1); +#endif return n; } EXPORT_SYMBOL(skb_copy); @@ -1144,6 +2011,11 @@ } copy_skb_header(n, skb); + +#if defined (CONFIG_BCM_KF_BPM_BUF_TRACKING) + KERN_GBPM_TRACK_SKB(skb, GBPM_VAL_COPY_SRC, 2); +#endif + out: return n; } @@ -1172,6 +2044,9 @@ u8 *data; int size = nhead + skb_end_offset(skb) + ntail; long off; +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_ACCELERATION)) + int clone_fc_len = 0; +#endif BUG_ON(nhead < 0); @@ -1220,7 +2095,18 @@ skb->head = data; skb->head_frag = 0; +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_ACCELERATION)) + if (skb->clone_fc_head) + clone_fc_len = skb->data - skb->clone_fc_head; +#endif + skb->data += off; + +#if (defined(CONFIG_BCM_KF_USBNET) && defined(CONFIG_BCM_USBNET_ACCELERATION)) + if (skb->clone_fc_head) + skb->clone_fc_head = skb->data - clone_fc_len; +#endif + #ifdef NET_SKBUFF_DATA_USES_OFFSET skb->end = size; off = nhead; @@ -1232,6 +2118,20 @@ skb->cloned = 0; skb->hdr_len = 0; skb->nohdr = 0; + +#if defined(CONFIG_BCM_KF_NBUFF) + /* Clear Data recycle as this buffer was allocated via kmalloc. + * Note that skb_release_data might have already cleared it but it is + * not guaranteed. If the buffer is cloned, then skb_release_data + * does not clear the buffer. The original data buffer will be freed + * when the cloned skb is freed */ + skb->recycle_flags &= SKB_DATA_NO_RECYCLE; + /* The data buffer of this skb is not pre-allocated any more + * even though the skb itself is pre-allocated, + * dirty_p pertains to previous buffer so clear it */ + skb_shinfo(skb)->dirty_p = NULL; +#endif + atomic_set(&skb_shinfo(skb)->dataref, 1); return 0; @@ -1318,6 +2218,10 @@ skb_headers_offset_update(n, newheadroom - oldheadroom); +#if defined (CONFIG_BCM_KF_BPM_BUF_TRACKING) + KERN_GBPM_TRACK_SKB(skb, GBPM_VAL_COPY_SRC, 3); +#endif + return n; } EXPORT_SYMBOL(skb_copy_expand); @@ -4189,6 +5093,22 @@ ipvs_reset(skb); skb_orphan(skb); + +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +/* + * when using containers in some network configurations, the packets + * will be treated as L2 flows while learning. Local aceleration + * is not supported for L2 flows so skip it. + * + * note: we try to add containers MAC address to Host MAC table (using + * netdev_notfier) to treat flows terminating in containers as L3 flows, + * but this check helps to catch any unknown configurations +*/ + if(skb->blog_p && skb->blog_p->l2_mode) + { + blog_skip(skb, blog_skip_reason_scrub_pkt); /* No local accel in l2 mode */ + } +#endif skb->mark = 0; } EXPORT_SYMBOL_GPL(skb_scrub_packet); @@ -4257,6 +5177,11 @@ vhdr = (struct vlan_hdr *)skb->data; vlan_tci = ntohs(vhdr->h_vlan_TCI); +#if defined(CONFIG_BCM_KF_NBUFF) +#if defined(CONFIG_BCM_KF_VLAN) && (defined(CONFIG_BCM_VLAN) || defined(CONFIG_BCM_VLAN_MODULE)) + skb->cfi_save = vlan_tci & VLAN_CFI_MASK; +#endif +#endif __vlan_hwaccel_put_tag(skb, skb->protocol, vlan_tci); skb_pull_rcsum(skb, VLAN_HLEN); diff -urN linux-4.1.52-orig/net/core/sock.c linux-4.1.52/net/core/sock.c --- linux-4.1.52-orig/net/core/sock.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/core/sock.c 2020-02-07 01:47:54.000000000 +0800 @@ -136,6 +136,13 @@ #include +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#ifdef CONFIG_MPTCP +#include +#include +#endif + +#endif #include #include @@ -277,7 +284,11 @@ "slock-AF_IEEE802154", "slock-AF_CAIF" , "slock-AF_ALG" , "slock-AF_NFC" , "slock-AF_VSOCK" ,"slock-AF_MAX" }; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static const char *const af_family_clock_key_strings[AF_MAX+1] = { +#else +char *const af_family_clock_key_strings[AF_MAX+1] = { +#endif "clock-AF_UNSPEC", "clock-AF_UNIX" , "clock-AF_INET" , "clock-AF_AX25" , "clock-AF_IPX" , "clock-AF_APPLETALK", "clock-AF_NETROM", "clock-AF_BRIDGE" , "clock-AF_ATMPVC" , @@ -298,7 +309,11 @@ * sk_callback_lock locking rules are per-address-family, * so split the lock classes by using a per-AF key: */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static struct lock_class_key af_callback_keys[AF_MAX]; +#else +struct lock_class_key af_callback_keys[AF_MAX]; +#endif /* Take into consideration the size of the struct sk_buff overhead in the * determination of these values, since that is non-constant across @@ -1265,8 +1280,30 @@ * * (We also register the sk_lock with the lock validator.) */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static inline void sock_lock_init(struct sock *sk) { +#else +void sock_lock_init(struct sock *sk) +{ +#ifdef CONFIG_MPTCP + /* Reclassify the lock-class for subflows */ + if (sk->sk_type == SOCK_STREAM && sk->sk_protocol == IPPROTO_TCP) + if (mptcp(tcp_sk(sk)) || tcp_sk(sk)->is_master_sk) { + sock_lock_init_class_and_name(sk, "slock-AF_INET-MPTCP", + &meta_slock_key, + "sk_lock-AF_INET-MPTCP", + &meta_key); + + /* We don't yet have the mptcp-point. + * Thus we still need inet_sock_destruct + */ + sk->sk_destruct = inet_sock_destruct; + return; + } +#endif + +#endif sock_lock_init_class_and_name(sk, af_family_slock_key_strings[sk->sk_family], af_family_slock_keys + sk->sk_family, @@ -1313,7 +1350,11 @@ } EXPORT_SYMBOL(sk_prot_clear_portaddr_nulls); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority, +#else +struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority, +#endif int family) { struct sock *sk; @@ -1534,6 +1575,9 @@ newsk->sk_userlocks = sk->sk_userlocks & ~SOCK_BINDPORT_LOCK; sock_reset_flag(newsk, SOCK_DONE); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + sock_reset_flag(newsk, SOCK_MPTCP); +#endif skb_queue_head_init(&newsk->sk_error_queue); filter = rcu_dereference_protected(newsk->sk_filter, 1); diff -urN linux-4.1.52-orig/net/core/vlanctl_bind.c linux-4.1.52/net/core/vlanctl_bind.c --- linux-4.1.52-orig/net/core/vlanctl_bind.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/core/vlanctl_bind.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,174 @@ +#if defined(CONFIG_BCM_KF_VLANCTL_BIND) && defined(CONFIG_BLOG) +/* +* Copyright (c) 2003-2014 Broadcom Corporation +* All Rights Reserved +* +<:label-BRCM:2014:DUAL/GPL:standard + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +#include "bcm_OS_Deps.h" +#include +#include + +#include +#include + +static vlanctl_bind_SnHook_t vlanctl_bind_sn_hook_g[VLANCTL_BIND_CLIENT_MAX] = { (vlanctl_bind_SnHook_t)NULL }; +static vlanctl_bind_ScHook_t vlanctl_bind_sc_hook_g[VLANCTL_BIND_CLIENT_MAX] = { (vlanctl_bind_ScHook_t)NULL }; +static vlanctl_bind_SdHook_t vlanctl_bind_sd_hook_g[VLANCTL_BIND_CLIENT_MAX] = { (vlanctl_bind_SdHook_t)NULL }; + +#if defined(CC_VLANCTL_BIND_SUPPORT_DEBUG) +#define vlanctl_assertr(cond, rtn) \ + if ( !cond ) { \ + printk( CLRerr "VLANCTL_BIND ASSERT %s : " #cond CLRnl, __FUNCTION__ ); \ + return rtn; \ + } +#else +#define vlanctl_assertr(cond, rtn) NULL_STMT +#endif + +/*------------------------------------------------------------------------------ + * Function : vlanctl_bind_config + * Description : Override default config and deconf hook. + * vlanctl_sc : Function pointer to be invoked in blog_activate() + * client : configuration client + *------------------------------------------------------------------------------ + */ +void vlanctl_bind_config(vlanctl_bind_ScHook_t vlanctl_bind_sc, + vlanctl_bind_SdHook_t vlanctl_bind_sd, + vlanctl_bind_SnHook_t vlanctl_bind_sn, + vlanctl_bind_client_t client, + vlanctl_bind_t bind) +{ + BCM_LOG_DEBUG(BCM_LOG_ID_VLAN, "vlanctl Bind Sc[<%p>] Sd[<%p>] Sn[<%p>] Client[<%u>] bind[<%u>]", + vlanctl_bind_sc, vlanctl_bind_sd, vlanctl_bind_sn, client, (uint8_t)bind.hook_info); + + if ( bind.bmap.SC_HOOK ) + vlanctl_bind_sc_hook_g[client] = vlanctl_bind_sc; /* config hook */ + if ( bind.bmap.SD_HOOK ) + vlanctl_bind_sd_hook_g[client] = vlanctl_bind_sd; /* deconf hook */ + if ( bind.bmap.SN_HOOK ) + vlanctl_bind_sn_hook_g[client] = vlanctl_bind_sn; /* notify hook */ +} + + + +/* + *------------------------------------------------------------------------------ + * Function : vlanctl_activate + * Description : This function invokes vlanctl configuration hook + * Parameters : + * blog_p : pointer to a blog with configuration information + * client : configuration client + * + * Returns : + * ActivateKey : If the configuration is successful, a key is returned. + * Otherwise, BLOG_KEY_INVALID is returned + *------------------------------------------------------------------------------ + */ +uint32_t vlanctl_activate( Blog_t * blog_p, vlanctl_bind_client_t client ) +{ + uint32_t key; + + key = BLOG_KEY_INVALID; + + if ( blog_p == BLOG_NULL || client >= VLANCTL_BIND_CLIENT_MAX ) + { + vlanctl_assertr((blog_p != BLOG_NULL), key); + goto bypass; + } + + if (unlikely(vlanctl_bind_sc_hook_g[client] == (vlanctl_bind_ScHook_t)NULL)) + goto bypass; + + + BLOG_LOCK_BH(); + key = vlanctl_bind_sc_hook_g[client](blog_p, BlogTraffic_Layer2_Flow); + BLOG_UNLOCK_BH(); + +bypass: + return key; +} + +/* + *------------------------------------------------------------------------------ + * Function : vlanctl_deactivate + * Description : This function invokes a deconfiguration hook + * Parameters : + * key : blog key information + * client : configuration client + * + * Returns : + * blog_p : If the deconfiguration is successful, the associated blog + * pointer is returned to the caller + *------------------------------------------------------------------------------ + */ +Blog_t * vlanctl_deactivate( uint32_t key, vlanctl_bind_client_t client ) +{ + Blog_t * blog_p = NULL; + + if ( key == BLOG_KEY_INVALID || client >= VLANCTL_BIND_CLIENT_MAX ) + { + vlanctl_assertr( (key != BLOG_KEY_INVALID), blog_p ); + goto bypass; + } + + if ( unlikely(vlanctl_bind_sd_hook_g[client] == (vlanctl_bind_SdHook_t)NULL) ) + goto bypass; + + BLOG_LOCK_BH(); + blog_p = vlanctl_bind_sd_hook_g[client](key, BlogTraffic_Layer2_Flow); + BLOG_UNLOCK_BH(); + +bypass: + return blog_p; +} + + +int vlanctl_notify(vlanctl_bind_Notify_t event, void *ptr, vlanctl_bind_client_t client) +{ + + if (client >= VLANCTL_BIND_CLIENT_MAX) + goto bypass; + + BCM_LOG_DEBUG(BCM_LOG_ID_VLAN, "client<%u>" "event<%u>", client, event); + + if (unlikely(vlanctl_bind_sn_hook_g[client] == (vlanctl_bind_SnHook_t)NULL)) + goto bypass; + + BLOG_LOCK_BH(); + vlanctl_bind_sn_hook_g[client](event, ptr); + BLOG_UNLOCK_BH(); + +bypass: + return 0; +} + + +EXPORT_SYMBOL(vlanctl_bind_config); +EXPORT_SYMBOL(vlanctl_activate); +EXPORT_SYMBOL(vlanctl_deactivate); +EXPORT_SYMBOL(vlanctl_notify); + +#endif /* defined(BCM_KF_VLANCTL_BIND && defined(CONFIG_BLOG) */ diff -urN linux-4.1.52-orig/net/ipv4/af_inet.c linux-4.1.52/net/ipv4/af_inet.c --- linux-4.1.52-orig/net/ipv4/af_inet.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/af_inet.c 2020-02-07 01:47:54.000000000 +0800 @@ -104,6 +104,9 @@ #include #include #include +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#include +#endif #include #include #include @@ -147,6 +150,11 @@ return; } +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (sock_flag(sk, SOCK_MPTCP)) + mptcp_disable_static_key(); + +#endif WARN_ON(atomic_read(&sk->sk_rmem_alloc)); WARN_ON(atomic_read(&sk->sk_wmem_alloc)); WARN_ON(sk->sk_wmem_queued); @@ -248,8 +256,12 @@ * Create an inet socket. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static int inet_create(struct net *net, struct socket *sock, int protocol, int kern) +#else +int inet_create(struct net *net, struct socket *sock, int protocol, int kern) +#endif { struct sock *sk; struct inet_protosw *answer; @@ -675,6 +687,25 @@ lock_sock(sk2); sock_rps_record_flow(sk2); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + + if (sk2->sk_protocol == IPPROTO_TCP && mptcp(tcp_sk(sk2))) { + struct sock *sk_it = sk2; + + mptcp_for_each_sk(tcp_sk(sk2)->mpcb, sk_it) + sock_rps_record_flow(sk_it); + + if (tcp_sk(sk2)->mpcb->master_sk) { + sk_it = tcp_sk(sk2)->mpcb->master_sk; + + write_lock_bh(&sk_it->sk_callback_lock); + sk_it->sk_wq = newsock->wq; + sk_it->sk_socket = newsock; + write_unlock_bh(&sk_it->sk_callback_lock); + } + } + +#endif WARN_ON(!((1 << sk2->sk_state) & (TCPF_ESTABLISHED | TCPF_SYN_RECV | TCPF_CLOSE_WAIT | TCPF_CLOSE))); @@ -1787,6 +1818,11 @@ ip_init(); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + /* We must initialize MPTCP before TCP. */ + mptcp_init(); + +#endif tcp_v4_init(); /* Setup TCP slab cache for open requests. */ diff -urN linux-4.1.52-orig/net/ipv4/ah4.c linux-4.1.52/net/ipv4/ah4.c --- linux-4.1.52-orig/net/ipv4/ah4.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/ah4.c 2020-02-07 01:47:54.000000000 +0800 @@ -13,6 +13,10 @@ #include #include +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#include +#endif + struct ah_skb_cb { struct xfrm_skb_cb xfrm; void *tmp; @@ -160,6 +164,10 @@ int sglists = 0; struct scatterlist *seqhisg; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_skip(skb, blog_skip_reason_unknown_proto_ah4); +#endif + ahp = x->data; ahash = ahp->ahash; @@ -220,7 +228,14 @@ ah->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low); sg_init_table(sg, nfrags + sglists); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + err = +#endif skb_to_sgvec_nomark(skb, sg, 0, skb->len); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + if (unlikely(err) < 0) + goto out_free; +#endif if (x->props.flags & XFRM_STATE_ESN) { /* Attach seqhi sg right after packet payload */ @@ -232,6 +247,22 @@ AH_SKB_CB(skb)->tmp = iph; +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) && !(defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE)) + /* ensure there is enough headroom and tailroom for HW info */ + if((skb_headroom(skb) < 12) || + (skb_tailroom(skb) < 20)) + { + req->alloc_buff_spu = 1; + } + else + { + req->alloc_buff_spu = 0; + } + + /* not used for output */ + req->headerLen = 0; +#endif + err = crypto_ahash_digest(req); if (err) { if (err == -EINPROGRESS) @@ -317,6 +348,10 @@ int sglists = 0; struct scatterlist *seqhisg; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_skip(skb, blog_skip_reason_unknown_proto_ah4); +#endif + if (!pskb_may_pull(skb, sizeof(*ah))) goto out; @@ -391,7 +426,14 @@ skb_push(skb, ihl); sg_init_table(sg, nfrags + sglists); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + err = +#endif skb_to_sgvec_nomark(skb, sg, 0, skb->len); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + if (unlikely(err) < 0) + goto out_free; +#endif if (x->props.flags & XFRM_STATE_ESN) { /* Attach seqhi sg right after packet payload */ @@ -403,6 +445,22 @@ AH_SKB_CB(skb)->tmp = work_iph; +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) && !(defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE)) + /* ensure there is enough headroom and tailroom for HW info */ + if((skb_headroom(skb) < 12) || + (skb_tailroom(skb) < 20)) + { + req->alloc_buff_spu = 1; + } + else + { + req->alloc_buff_spu = 0; + } + + /* offset to icv */ + req->headerLen = &ah->auth_data[0] - skb->data; +#endif + err = crypto_ahash_digest(req); if (err) { if (err == -EINPROGRESS) diff -urN linux-4.1.52-orig/net/ipv4/esp4.c linux-4.1.52/net/ipv4/esp4.c --- linux-4.1.52-orig/net/ipv4/esp4.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/esp4.c 2020-02-07 01:47:54.000000000 +0800 @@ -18,6 +18,11 @@ #include #include + +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#include +#endif + struct esp_skb_cb { struct xfrm_skb_cb xfrm; void *tmp; @@ -135,12 +140,34 @@ int sglists; int seqhilen; __be32 *seqhi; - +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) && (defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE)) + u8 next_hdr; +#endif +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) + struct crypto_tfm *ctfm; + struct crypto_alg *calg; +#endif +#endif /* skb is pure payload to encrypt */ aead = x->data; alen = crypto_aead_authsize(aead); +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) + ctfm = crypto_aead_tfm(aead); + calg = ctfm->__crt_alg; + if ( !(calg->cra_flags & CRYPTO_ALG_BLOG) ) + { + blog_skip(skb, blog_skip_reason_esp4_crypto_algo); + } else { + crypto_tfm_set_flags(ctfm, CRYPTO_TFM_REQ_MAY_BLOG); + } +#else + blog_skip(skb, blog_skip_reason_esp4_spu_disabled); +#endif +#endif tfclen = 0; if (x->tfcpad) { struct xfrm_dst *dst = (struct xfrm_dst *)skb_dst(skb); @@ -194,6 +221,9 @@ } while (0); tail[plen - 2] = plen - 2; tail[plen - 1] = *skb_mac_header(skb); +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) && (defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE)) + next_hdr = *skb_mac_header(skb); +#endif pskb_put(skb, trailer, clen - skb->len + alen); skb_push(skb, -skb_network_offset(skb)); @@ -239,10 +269,19 @@ esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low); sg_init_table(sg, nfrags); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + err = +#endif skb_to_sgvec(skb, sg, esph->enc_data + crypto_aead_ivsize(aead) - skb->data, clen + alen); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + if(unlikely(err) < 0) + goto error; +#endif + + if ((x->props.flags & XFRM_STATE_ESN)) { sg_init_table(asg, 3); sg_set_buf(asg, &esph->spi, sizeof(__be32)); @@ -260,6 +299,25 @@ ((u64)XFRM_SKB_CB(skb)->seq.output.hi << 32)); ESP_SKB_CB(skb)->tmp = tmp; + +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) + req->areq.data_offset = (unsigned char *)esph - skb->data; + req->areq.next_hdr = next_hdr; +#else + /* ensure there is enough headroom and tailroom for HW info */ + if((skb_headroom(skb) < 12) || + (skb_tailroom(skb) < 16)) + { + req->areq.alloc_buff_spu = 1; + } + else + { + req->areq.alloc_buff_spu = 0; + } + req->areq.headerLen = esph->enc_data + crypto_aead_ivsize(aead) - skb->data; +#endif +#endif err = crypto_aead_givencrypt(req); if (err == -EINPROGRESS) goto error; @@ -386,7 +444,27 @@ struct scatterlist *sg; struct scatterlist *asg; int err = -EINVAL; - +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) && !(defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE)) + int macLen; +#endif +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) + struct crypto_tfm *ctfm; + struct crypto_alg *calg; + + ctfm = crypto_aead_tfm(aead); + calg = ctfm->__crt_alg; + + if ( !(calg->cra_flags & CRYPTO_ALG_BLOG) ) + { + blog_skip(skb, blog_skip_reason_esp4_crypto_algo); + } else { + crypto_tfm_set_flags(ctfm, CRYPTO_TFM_REQ_MAY_BLOG); + } +#else + blog_skip(skb, blog_skip_reason_esp4_spu_disabled); +#endif +#endif if (!pskb_may_pull(skb, sizeof(*esph) + crypto_aead_ivsize(aead))) goto out; @@ -429,7 +507,15 @@ iv = esph->enc_data; sg_init_table(sg, nfrags); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + err = +#endif skb_to_sgvec(skb, sg, sizeof(*esph) + crypto_aead_ivsize(aead), elen); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + if (unlikely(err) < 0) + goto out; +#endif + if ((x->props.flags & XFRM_STATE_ESN)) { sg_init_table(asg, 3); @@ -444,6 +530,27 @@ aead_request_set_crypt(req, sg, sg, elen, iv); aead_request_set_assoc(req, asg, assoclen); +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) + req->data_offset = 0; + req->next_hdr = 0; +#else + /* ensure there is enough headroom and tailroom for HW info */ + if ( (skb->data >= skb_mac_header(skb)) && + (skb_headroom(skb) >= ((skb->data - skb_mac_header(skb)) + 12)) && + (skb_tailroom(skb) >= 16)) + { + macLen = skb->data - skb_mac_header(skb); + req->alloc_buff_spu = 0; + } + else + { + macLen = 0; + req->alloc_buff_spu = 1; + } + req->headerLen = sizeof(*esph) + crypto_aead_ivsize(aead) + macLen; +#endif +#endif err = crypto_aead_decrypt(req); if (err == -EINPROGRESS) goto out; diff -urN linux-4.1.52-orig/net/ipv4/fib_frontend.c linux-4.1.52/net/ipv4/fib_frontend.c --- linux-4.1.52-orig/net/ipv4/fib_frontend.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/fib_frontend.c 2020-02-07 01:47:54.000000000 +0800 @@ -379,6 +379,13 @@ { int r = secpath_exists(skb) ? 0 : IN_DEV_RPFILTER(idev); +#if defined(CONFIG_BCM_KF_MCAST_RP_FILTER) + /* ignore rp_filter for multicast traffic */ + if (skb->pkt_type == PACKET_MULTICAST) { + r = 0; + } +#endif + if (!r && !fib_num_tclassid_users(dev_net(dev)) && IN_DEV_ACCEPT_LOCAL(idev) && (dev->ifindex != oif || !IN_DEV_TX_REDIRECTS(idev))) { diff -urN linux-4.1.52-orig/net/ipv4/fib_trie.c linux-4.1.52/net/ipv4/fib_trie.c --- linux-4.1.52-orig/net/ipv4/fib_trie.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/fib_trie.c 2020-02-07 01:47:54.000000000 +0800 @@ -2433,9 +2433,13 @@ static struct key_vector *fib_route_get_idx(struct fib_route_iter *iter, loff_t pos) { +#if !defined(CONFIG_BCM_KF_MISC_BACKPORTS) struct fib_table *tb = iter->main_tb; +#endif struct key_vector *l, **tp = &iter->tnode; +#if !defined(CONFIG_BCM_KF_MISC_BACKPORTS) struct trie *t; +#endif t_key key; /* use cache location of next-to-find key */ @@ -2443,8 +2447,10 @@ pos -= iter->pos; key = iter->key; } else { +#if !defined(CONFIG_BCM_KF_MISC_BACKPORTS) t = (struct trie *)tb->tb_data; iter->tnode = t->kv; +#endif iter->pos = 0; key = 0; } @@ -2485,12 +2491,18 @@ return NULL; iter->main_tb = tb; +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + t = (struct trie *)tb->tb_data; + iter->tnode = t->kv; +#endif if (*pos != 0) return fib_route_get_idx(iter, *pos); +#if !defined(CONFIG_BCM_KF_MISC_BACKPORTS) t = (struct trie *)tb->tb_data; iter->tnode = t->kv; +#endif iter->pos = 0; iter->key = 0; diff -urN linux-4.1.52-orig/net/ipv4/icmp.c linux-4.1.52/net/ipv4/icmp.c --- linux-4.1.52-orig/net/ipv4/icmp.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/icmp.c 2020-02-07 01:47:54.000000000 +0800 @@ -530,6 +530,98 @@ return ERR_PTR(err); } +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) +/* + * This function is only used by MAP-T (or MAP-E) feature. + * If LAN IPv4 packet with DF flag set and translated IPv6 packet + * exceeds mtu, this function is used to send fragment needed ICMP + */ +void send_icmp_frag(struct sk_buff *skb_in, int type, int code, __be32 info) +{ + struct iphdr *iph; + int room; + struct icmp_bxm *icmp_param; + struct rtable *rt; + struct ipcm_cookie ipc; + struct flowi4 fl4; + __be32 saddr; + u8 tos; + u32 mark; + struct net *net; + + net = dev_net(skb_in->dev); + iph = ip_hdr(skb_in); + + if ((u8 *)iph < skb_in->head || + (skb_network_header(skb_in) + sizeof(*iph)) > + skb_tail_pointer(skb_in)) + goto out; + + if (skb_in->pkt_type != PACKET_HOST) + goto out; + + /* + * Only reply to fragment 0. We byte re-order the constant + * mask for efficiency. + */ + if (iph->frag_off & htons(IP_OFFSET)) + goto out; + + icmp_param = kmalloc(sizeof(*icmp_param), GFP_ATOMIC); + if (!icmp_param) + return; + + saddr = 0; + + tos = icmp_pointers[type].error ? ((iph->tos & IPTOS_TOS_MASK) | + IPTOS_PREC_INTERNETCONTROL) : + iph->tos; + mark = skb_in->mark; + + if (ip_options_echo(&icmp_param->replyopts.opt.opt, skb_in)) + goto out_free; + + + /* + * Prepare data for ICMP header. + */ + icmp_param->data.icmph.type = type; + icmp_param->data.icmph.code = code; + icmp_param->data.icmph.un.gateway = info; + icmp_param->data.icmph.checksum = 0; + icmp_param->skb = skb_in; + icmp_param->offset = skb_network_offset(skb_in); + ipc.addr = iph->saddr; + ipc.opt = &icmp_param->replyopts.opt; + ipc.tx_flags = 0; + ipc.ttl = 0; + ipc.tos = -1; + + rt = icmp_route_lookup(net, &fl4, skb_in, iph, saddr, tos, mark, + type, code, icmp_param); + if (IS_ERR(rt)) + goto out_free; + + room = dst_mtu(&rt->dst); + if (room > 576) + room = 576; + room -= sizeof(struct iphdr) + icmp_param->replyopts.opt.opt.optlen; + room -= sizeof(struct icmphdr); + + icmp_param->data_len = skb_in->len - icmp_param->offset; + if (icmp_param->data_len > room) + icmp_param->data_len = room; + icmp_param->head_len = sizeof(struct icmphdr); + + icmp_push_reply(icmp_param, &fl4, &ipc, &rt); + +out_free: + kfree(icmp_param); +out:; +} +EXPORT_SYMBOL(send_icmp_frag); +#endif + /* * Send an ICMP message in response to a situation * diff -urN linux-4.1.52-orig/net/ipv4/igmp.c linux-4.1.52/net/ipv4/igmp.c --- linux-4.1.52-orig/net/ipv4/igmp.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/igmp.c 2020-02-07 01:47:54.000000000 +0800 @@ -661,6 +661,9 @@ /* change recs */ for_each_pmc_rcu(in_dev, pmc) { spin_lock_bh(&pmc->lock); +#if defined(CONFIG_BCM_KF_MCAST_GR_SUPPRESSION) + if ( pmc->osfmode == pmc->sfmode ) { +#endif if (pmc->sfcount[MCAST_EXCLUDE]) { type = IGMPV3_BLOCK_OLD_SOURCES; dtype = IGMPV3_ALLOW_NEW_SOURCES; @@ -670,15 +673,29 @@ } skb = add_grec(skb, pmc, type, 0, 0); skb = add_grec(skb, pmc, dtype, 0, 1); /* deleted sources */ +#if defined(CONFIG_BCM_KF_MCAST_GR_SUPPRESSION) + } +#endif /* filter mode changes */ if (pmc->crcount) { +#if defined(CONFIG_BCM_KF_MCAST_GR_SUPPRESSION) + if ( pmc->osfmode != pmc->sfmode ) { +#endif if (pmc->sfmode == MCAST_EXCLUDE) type = IGMPV3_CHANGE_TO_EXCLUDE; else type = IGMPV3_CHANGE_TO_INCLUDE; skb = add_grec(skb, pmc, type, 0, 0); +#if defined(CONFIG_BCM_KF_MCAST_GR_SUPPRESSION) + } +#endif pmc->crcount--; +#if defined(CONFIG_BCM_KF_MCAST_GR_SUPPRESSION) + if ( pmc->crcount == 0 ) { + pmc->osfmode = pmc->sfmode; + } +#endif } spin_unlock_bh(&pmc->lock); } @@ -1210,6 +1227,12 @@ if (im->multiaddr == IGMP_ALL_HOSTS) return; +#if defined(CONFIG_BCM_KF_IGMP_RIP_ROUTER) + /* do not send leave for RIP */ + if (im->multiaddr == htonl(0xE0000009L)) + return; +#endif + reporter = im->reporter; igmp_stop_timer(im); @@ -1342,6 +1365,9 @@ im->multiaddr = addr; /* initial mode is (EX, empty) */ im->sfmode = MCAST_EXCLUDE; +#if defined(CONFIG_BCM_KF_MCAST_GR_SUPPRESSION) + im->osfmode = MCAST_INCLUDE; +#endif im->sfcount[MCAST_EXCLUDE] = 1; atomic_set(&im->refcnt, 1); spin_lock_init(&im->lock); diff -urN linux-4.1.52-orig/net/ipv4/inet_connection_sock.c linux-4.1.52/net/ipv4/inet_connection_sock.c --- linux-4.1.52-orig/net/ipv4/inet_connection_sock.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/inet_connection_sock.c 2020-02-07 01:47:54.000000000 +0800 @@ -23,6 +23,9 @@ #include #include #include +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#include +#endif #include #ifdef INET_CSK_DEBUG @@ -469,8 +472,13 @@ } EXPORT_SYMBOL_GPL(inet_csk_route_child_sock); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static inline u32 inet_synq_hash(const __be32 raddr, const __be16 rport, const u32 rnd, const u32 synq_hsize) +#else +u32 inet_synq_hash(const __be32 raddr, const __be16 rport, const u32 rnd, + const u32 synq_hsize) +#endif { return jhash_2words((__force u32)raddr, (__force u32)rport, rnd) & (synq_hsize - 1); } @@ -610,7 +618,12 @@ int max_retries, thresh; u8 defer_accept; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (sk_listener->sk_state != TCP_LISTEN || !lopt) { +#else + if ((sk_listener->sk_state != TCP_LISTEN && !is_meta_sk(sk_listener)) || + !lopt) { +#endif reqsk_put(req); return; } @@ -706,7 +719,13 @@ const struct request_sock *req, const gfp_t priority) { +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) struct sock *newsk = sk_clone_lock(sk, priority); +#else + struct sock *newsk; + + newsk = sk_clone_lock(sk, priority); +#endif if (newsk) { struct inet_connection_sock *newicsk = inet_csk(newsk); @@ -789,7 +808,12 @@ { struct inet_sock *inet = inet_sk(sk); struct inet_connection_sock *icsk = inet_csk(sk); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) int rc = reqsk_queue_alloc(&icsk->icsk_accept_queue, nr_table_entries); +#else + int rc = reqsk_queue_alloc(&icsk->icsk_accept_queue, nr_table_entries, + GFP_KERNEL); +#endif if (rc != 0) return rc; @@ -845,9 +869,18 @@ while ((req = acc_req) != NULL) { struct sock *child = req->sk; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + bool mutex_taken = false; +#endif acc_req = req->dl_next; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (is_meta_sk(child)) { + mutex_lock(&tcp_sk(child)->mpcb->mpcb_mutex); + mutex_taken = true; + } +#endif local_bh_disable(); bh_lock_sock(child); WARN_ON(sock_owned_by_user(child)); @@ -875,6 +908,10 @@ bh_unlock_sock(child); local_bh_enable(); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (mutex_taken) + mutex_unlock(&tcp_sk(child)->mpcb->mpcb_mutex); +#endif sock_put(child); sk_acceptq_removed(sk); diff -urN linux-4.1.52-orig/net/ipv4/ip_forward.c linux-4.1.52/net/ipv4/ip_forward.c --- linux-4.1.52-orig/net/ipv4/ip_forward.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/ip_forward.c 2020-02-07 01:47:54.000000000 +0800 @@ -139,6 +139,15 @@ skb->priority = rt_tos2priority(iph->tos); +#if defined(CONFIG_BCM_KF_WANDEV) +#if !defined(CONFIG_BCM_WAN_2_WAN_FWD_ENABLED) + /* Never forward a packet from a WAN intf to the other WAN intf */ + if( (skb->dev) && (rt->dst.dev) && + ((skb->dev->priv_flags & rt->dst.dev->priv_flags) & IFF_WANDEV) ) + goto drop; +#endif +#endif + return NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, NULL, skb, skb->dev, rt->dst.dev, ip_forward_finish); diff -urN linux-4.1.52-orig/net/ipv4/ip_fragment.c linux-4.1.52/net/ipv4/ip_fragment.c --- linux-4.1.52-orig/net/ipv4/ip_fragment.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/ip_fragment.c 2020-02-07 01:47:54.000000000 +0800 @@ -832,8 +832,13 @@ * we will prune down to 3MB, making room for approx 8 big 64K * fragments 8x128k. */ +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + net->ipv4.frags.high_thresh = 256 * 1024; + net->ipv4.frags.low_thresh = 192 * 1024; +#else net->ipv4.frags.high_thresh = 4 * 1024 * 1024; net->ipv4.frags.low_thresh = 3 * 1024 * 1024; +#endif /* * Important NOTE! Fragment queue must be destroyed before MSL expires. * RFC791 is wrong proposing to prolongate timer each fragment arrival diff -urN linux-4.1.52-orig/net/ipv4/ip_gre.c linux-4.1.52/net/ipv4/ip_gre.c --- linux-4.1.52-orig/net/ipv4/ip_gre.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/ip_gre.c 2020-02-07 01:47:54.000000000 +0800 @@ -54,6 +54,11 @@ #include #endif +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#include +#include +#endif + /* Problems & solutions -------------------- @@ -217,6 +222,14 @@ iph->saddr, iph->daddr, tpi->key); if (tunnel) { +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_lock(); + blog_link(IF_DEVICE, blog_ptr(skb), (void*)tunnel->dev, DIR_RX, + skb->len); + blog_link(GRE_TUNL, blog_ptr(skb), (void*)tunnel, DIR_RX, 0); + blog_link(TOS_MODE, blog_ptr(skb), tunnel, DIR_RX, BLOG_TOS_FIXED); + blog_unlock(); +#endif skb_pop_mac_header(skb); ip_tunnel_rcv(tunnel, skb, tpi, log_ecn_error); return PACKET_RCVD; @@ -231,11 +244,24 @@ struct ip_tunnel *tunnel = netdev_priv(dev); struct tnl_ptk_info tpi; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_lock(); + blog_link(IF_DEVICE, blog_ptr(skb), (void*)dev, DIR_TX, skb->len); + blog_link(GRE_TUNL, blog_ptr(skb), (void*)tunnel, DIR_TX, 0); + blog_link(TOS_MODE, blog_ptr(skb), tunnel, DIR_TX, tnl_params->tos); + blog_unlock(); +#endif + tpi.flags = tunnel->parms.o_flags; tpi.proto = proto; tpi.key = tunnel->parms.o_key; if (tunnel->parms.o_flags & TUNNEL_SEQ) +#if (defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG)) + if (!blog_gre_tunnel_accelerated()) + tunnel->o_seqno++; +#else tunnel->o_seqno++; +#endif tpi.seq = htonl(tunnel->o_seqno); /* Push GRE header. */ @@ -243,6 +269,9 @@ skb_set_inner_protocol(skb, tpi.proto); +#if (defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG)) + skb->tunl = tunnel; +#endif ip_tunnel_xmit(skb, dev, tnl_params, tnl_params->protocol); } @@ -469,6 +498,9 @@ dev->netdev_ops = &ipgre_netdev_ops; dev->type = ARPHRD_IPGRE; ip_tunnel_setup(dev, ipgre_net_id); +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + dev->blog_stats_flags |= BLOG_DEV_STAT_FLAG_INCLUDE_ALL; +#endif } static void __gre_tunnel_init(struct net_device *dev) @@ -696,6 +728,9 @@ dev->netdev_ops = &gre_tap_netdev_ops; dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; ip_tunnel_setup(dev, gre_tap_net_id); +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + dev->blog_stats_flags |= BLOG_DEV_STAT_FLAG_INCLUDE_ALL; +#endif } static int ipgre_newlink(struct net *src_net, struct net_device *dev, @@ -867,6 +902,150 @@ .size = sizeof(struct ip_tunnel_net), }; + +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +static inline int __bcm_gre_rcv_check(struct ip_tunnel *tunnel, struct iphdr *iph, + uint16_t len, uint32_t *pkt_seqno) +{ + int ret = BLOG_GRE_RCV_NO_SEQNO; + int grehlen = 4; + int iph_len = iph->ihl<<2; + __be16 *p = (__be16*)((uint8_t *)iph+iph_len); + __be16 flags; + + flags = p[0]; + + if (tunnel->parms.i_flags & TUNNEL_CSUM) { + uint16_t csum; + + grehlen += 4; + csum = *(((__be16 *)p) + 2); + + if (!csum) + goto no_csum; + + csum = ip_compute_csum((void*)(iph+1), len - iph_len); + + if (csum) { + tunnel->dev->stats.rx_crc_errors++; + tunnel->dev->stats.rx_errors++; + ret = BLOG_GRE_RCV_CHKSUM_ERR; + goto rcv_done; + } + } + +no_csum: + if ((tunnel->parms.i_flags & TUNNEL_KEY) && (flags&GRE_KEY)) + grehlen += 4; + + if (tunnel->parms.i_flags & TUNNEL_SEQ) { + uint32_t seqno = *(((__be32 *)p) + (grehlen / 4)); + *pkt_seqno = seqno; + if (tunnel->i_seqno && (s32)(seqno - tunnel->i_seqno) == 0) { + tunnel->i_seqno = seqno + 1; + ret = BLOG_GRE_RCV_IN_SEQ; + } else if (tunnel->i_seqno && (s32)(seqno - tunnel->i_seqno) < 0) { + tunnel->dev->stats.rx_fifo_errors++; + tunnel->dev->stats.rx_errors++; + ret = BLOG_GRE_RCV_OOS_LT; + } else { + tunnel->i_seqno = seqno + 1; + ret = BLOG_GRE_RCV_OOS_GT; + } + } + +rcv_done: + return ret; +} + +int bcm_gre_rcv_check(struct net_device *dev, struct iphdr *iph, + uint16_t len, void **tunl, uint32_t *pkt_seqno) +{ + int ret = BLOG_GRE_RCV_NO_TUNNEL; + + struct net *net = dev_net(dev); + struct ip_tunnel_net *itn; + struct ip_tunnel *tunnel; + struct tnl_ptk_info tpi; + + int iph_len = iph->ihl<<2; + struct gre_base_hdr *greh = (struct gre_base_hdr *)((uint8_t *)iph+iph_len); + __be32 *options =(__be32*)(__be32 *)(greh + 1); + + tpi.flags = gre_flags_to_tnl_flags(greh->flags); + tpi.proto = greh->protocol; + + if (greh->flags & GRE_CSUM) { + options++; + } + + if (greh->flags & GRE_KEY) { + tpi.key = *options; + options++; + } else + tpi.key = 0; + +#if 1 + if (tpi.proto == htons(ETH_P_TEB)) + itn = net_generic(net, gre_tap_net_id); + else +#endif + itn = net_generic(net, ipgre_net_id); + + tunnel = ip_tunnel_lookup(itn, dev->ifindex, tpi.flags, + iph->saddr, iph->daddr, tpi.key); + + if (tunnel) { + rcu_read_lock(); + ret = __bcm_gre_rcv_check(tunnel, iph, len, pkt_seqno); + rcu_read_unlock(); + } + + *tunl = (void *) tunnel; + return ret; +} + +/* Adds the TX seqno, Key and updates the GRE checksum */ +static inline +void __bcm_gre_xmit_update(struct ip_tunnel *tunnel, struct iphdr *iph, + uint16_t len) +{ + /* IPV4 Tunnel doesn't use GRE flags, uses TUNNEL flags */ + if (tunnel->parms.o_flags&(TUNNEL_KEY|TUNNEL_CSUM|TUNNEL_SEQ)) { + int iph_len = iph->ihl<<2; + /* tunnel->encap_hlen is only used for FOU, GUE. Otherwise it's 0 */ + __be32 *ptr = (__be32*)(((u8*)iph) + iph_len + tunnel->hlen - 4); + + if (tunnel->parms.o_flags&TUNNEL_SEQ) { + ++tunnel->o_seqno; + *ptr = htonl(tunnel->o_seqno); + ptr--; + } + + if (tunnel->parms.o_flags&TUNNEL_KEY) { + *ptr = tunnel->parms.o_key; + ptr--; + } + + if (tunnel->parms.o_flags&TUNNEL_CSUM) { + *ptr = 0; + *(__sum16*)ptr = ip_compute_csum((void*)(iph+1), len - iph_len); + } + cache_flush_len(ptr, tunnel->hlen); + } +} + +/* Adds the oseqno and updates the GRE checksum */ +void bcm_gre_xmit_update(struct ip_tunnel *tunnel, struct iphdr *iph, + uint16_t len) +{ + rcu_read_lock(); + __bcm_gre_xmit_update(tunnel, iph, len); + rcu_read_unlock(); +} + +#endif + static int __init ipgre_init(void) { int err; @@ -895,6 +1074,11 @@ if (err < 0) goto tap_ops_failed; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_gre_rcv_check_fn = (blog_gre_rcv_check_t) bcm_gre_rcv_check; + blog_gre_xmit_update_fn = (blog_gre_xmit_upd_t) bcm_gre_xmit_update; +#endif + return 0; tap_ops_failed: diff -urN linux-4.1.52-orig/net/ipv4/ip_input.c linux-4.1.52/net/ipv4/ip_input.c --- linux-4.1.52-orig/net/ipv4/ip_input.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/ip_input.c 2020-02-07 01:47:54.000000000 +0800 @@ -378,6 +378,9 @@ { const struct iphdr *iph; u32 len; +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) + __u8 iph_ihl, iph_version; +#endif /* When the interface is in promisc. mode, drop all the crap * that it receives, do not try to analyse it. @@ -409,8 +412,14 @@ * 3. Checksums correctly. [Speed optimisation for later, skip loopback checksums] * 4. Doesn't have a bogus length */ +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) + iph_ihl = *(__u8 *)iph & 0xf; + iph_version = *(__u8 *)iph >> 4; + if (iph_ihl < 5 || iph_version != 4) +#else if (iph->ihl < 5 || iph->version != 4) +#endif goto inhdr_error; BUILD_BUG_ON(IPSTATS_MIB_ECT1PKTS != IPSTATS_MIB_NOECTPKTS + INET_ECN_ECT_1); @@ -420,12 +429,20 @@ IPSTATS_MIB_NOECTPKTS + (iph->tos & INET_ECN_MASK), max_t(unsigned short, 1, skb_shinfo(skb)->gso_segs)); +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) + if (!pskb_may_pull(skb, iph_ihl*4)) +#else if (!pskb_may_pull(skb, iph->ihl*4)) +#endif goto inhdr_error; iph = ip_hdr(skb); +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) + if (unlikely(ip_fast_csum((u8 *)iph, iph_ihl))) +#else if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl))) +#endif goto csum_error; len = ntohs(iph->tot_len); diff -urN linux-4.1.52-orig/net/ipv4/ipmr.c linux-4.1.52/net/ipv4/ipmr.c --- linux-4.1.52-orig/net/ipv4/ipmr.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/ipmr.c 2020-02-07 01:47:54.000000000 +0800 @@ -826,6 +826,40 @@ return 0; } +#if defined(CONFIG_BCM_KF_MROUTE) +/* look for (S,G,iif) and then (*,G,iif) */ +static struct mfc_cache *ipmr_cache_find_parent(struct mr_table *mrt, + __be32 origin, + __be32 mcastgrp, + vifi_t vifi) +{ + int line = MFC_HASH(mcastgrp, origin); + struct mfc_cache *c; + list_for_each_entry_rcu(c, &mrt->mfc_cache_array[line], list) { + if ((c->mfc_origin == origin) && + (c->mfc_mcastgrp == mcastgrp) && + (c->mfc_parent == vifi)) + { + return c; + } + } + + /* for ASM multicast source does not matter so need to check + for an entry with NULL origin */ + line = MFC_HASH(mcastgrp, 0x0); + list_for_each_entry_rcu(c, &mrt->mfc_cache_array[line], list) { + if ((c->mfc_origin == 0x0) && + (c->mfc_mcastgrp == mcastgrp) && + (c->mfc_parent == vifi)) + { + return c; + } + } + + return NULL; +} +#endif + /* called with rcu_read_lock() */ static struct mfc_cache *ipmr_cache_find(struct mr_table *mrt, __be32 origin, @@ -857,6 +891,7 @@ return NULL; } +#if !defined(CONFIG_BCM_KF_MROUTE) /* Look for a (*,G) entry */ static struct mfc_cache *ipmr_cache_find_any(struct mr_table *mrt, __be32 mcastgrp, int vifi) @@ -883,6 +918,7 @@ skip: return ipmr_cache_find_any_parent(mrt, vifi); } +#endif /* * Allocate a multicast cache entry @@ -1904,8 +1940,15 @@ struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); if (skb2) +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + { + blog_clone(skb, blog_ptr(skb2)); +#endif ipmr_queue_xmit(net, mrt, skb2, cache, psend); +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + } +#endif } psend = ct; } @@ -1997,6 +2040,27 @@ } } +#if defined(CONFIG_BCM_KF_MROUTE) + /* mroute should not apply to IGMP traffic + in addition it does not make sense for TCP protocol to be used + for multicast so just check for UDP */ + cache = NULL; + if( ip_hdr(skb)->protocol == IPPROTO_UDP ) + { + /* Don't forward the multicast stream with the dest port == 0 */ + struct udphdr *uh = udp_hdr(skb); + if(ntohs(uh->dest) == 0) + { + goto dont_forward; + } + + vifi_t vifi = ipmr_find_vif(mrt, skb->dev); + if ( vifi >= 0 ) + { + cache = ipmr_cache_find_parent(mrt, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr, vifi); + } + } +#else /* already under rcu_read_lock() */ cache = ipmr_cache_find(mrt, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr); if (!cache) { @@ -2006,7 +2070,7 @@ cache = ipmr_cache_find_any(mrt, ip_hdr(skb)->daddr, vif); } - +#endif /* * No usable cache entry */ @@ -2039,13 +2103,29 @@ read_unlock(&mrt_lock); if (local) +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + { + /* free blog if present */ + blog_free(skb, blog_free_reason_ipmr_local); +#endif return ip_local_deliver(skb); +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + } +#endif return 0; dont_forward: if (local) +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + { + /* free blog if present */ + blog_free(skb, blog_free_reason_ipmr_local); +#endif return ip_local_deliver(skb); +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + } +#endif kfree_skb(skb); return 0; } @@ -2196,9 +2276,15 @@ return 1; } +#if defined(CONFIG_BCM_KF_MROUTE) +int ipmr_get_route(struct net *net, struct sk_buff *skb, + __be32 saddr, __be32 daddr, + struct rtmsg *rtm, int nowait, int ifIndex) +#else int ipmr_get_route(struct net *net, struct sk_buff *skb, __be32 saddr, __be32 daddr, struct rtmsg *rtm, int nowait) +#endif { struct mfc_cache *cache; struct mr_table *mrt; @@ -2209,6 +2295,33 @@ return -ENOENT; rcu_read_lock(); +#if defined(CONFIG_BCM_KF_MROUTE) + /* mroute should not apply to IGMP traffic + in addition it does not make sense for TCP protocol to be used + for multicast so just check for UDP */ + cache = NULL; + if ((NULL == skb->dev) || (ip_hdr(skb) == NULL) || + (ip_hdr(skb)->protocol == IPPROTO_UDP)) + { + /* Don't forward the multicast stream with the dest port == 0 */ + struct udphdr *uh = udp_hdr(skb); + if(ntohs(uh->dest) == 0) + { + return -EFAULT; + } + + struct net_device *dev = dev_get_by_index(net, ifIndex); + if (dev) + { + vifi_t vifi = ipmr_find_vif(mrt, dev); + if ( vifi >= 0 ) + { + cache = ipmr_cache_find_parent(mrt, saddr, daddr, vifi); + } + dev_put(dev); + } + } +#else cache = ipmr_cache_find(mrt, saddr, daddr); if (!cache && skb->dev) { int vif = ipmr_find_vif(mrt, skb->dev); @@ -2216,6 +2329,7 @@ if (vif >= 0) cache = ipmr_cache_find_any(mrt, daddr, vif); } +#endif if (!cache) { struct sk_buff *skb2; struct iphdr *iph; diff -urN linux-4.1.52-orig/net/ipv4/ip_options.c linux-4.1.52/net/ipv4/ip_options.c --- linux-4.1.52-orig/net/ipv4/ip_options.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/ip_options.c 2020-02-07 01:47:54.000000000 +0800 @@ -237,6 +237,9 @@ opt->ts_needaddr = 0; opt->ts_needtime = 0; } +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) +EXPORT_SYMBOL(ip_options_fragment); +#endif /* helper used by ip_options_compile() to call fib_compute_spec_dst() * at most one time. diff -urN linux-4.1.52-orig/net/ipv4/ip_output.c linux-4.1.52/net/ipv4/ip_output.c --- linux-4.1.52-orig/net/ipv4/ip_output.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/ip_output.c 2020-02-07 01:47:54.000000000 +0800 @@ -79,6 +79,9 @@ #include #include #include +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#include +#endif int sysctl_ip_default_ttl __read_mostly = IPDEFTTL; EXPORT_SYMBOL(sysctl_ip_default_ttl); @@ -312,9 +315,16 @@ ) { struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC); if (newskb) +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + { + blog_clone(skb, blog_ptr(newskb)); +#endif NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING, sk, newskb, NULL, newskb->dev, dev_loopback_xmit); +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + } +#endif } /* Multicasts with ttl 0 must not go beyond the host */ @@ -518,6 +528,17 @@ return -EMSGSIZE; } +#if defined(CONFIG_BCM_KF_IP) + /* + * Do not fragment the packets going to 4in6 tunnel: + * RFC2473 sec 7.2: fragmentation should happen in tunnel + */ + if (strstr(dev->name, "ip6tnl")) + { + return output(sk, skb); + } +#endif + /* * Setup starting values. */ @@ -706,6 +727,9 @@ BUG(); left -= len; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_xfer(skb2, skb); +#endif /* * Fill in the new header fields. */ diff -urN linux-4.1.52-orig/net/ipv4/ip_sockglue.c linux-4.1.52/net/ipv4/ip_sockglue.c --- linux-4.1.52-orig/net/ipv4/ip_sockglue.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/ip_sockglue.c 2020-02-07 01:47:54.000000000 +0800 @@ -43,6 +43,10 @@ #endif #include +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#include + +#endif #include #include @@ -722,6 +726,19 @@ inet->tos = val; sk->sk_priority = rt_tos2priority(val); sk_dst_reset(sk); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + /* Update TOS on mptcp subflow */ + if (is_meta_sk(sk)) { + struct sock *sk_it; + mptcp_for_each_sk(tcp_sk(sk)->mpcb, sk_it) { + if (inet_sk(sk_it)->tos != inet_sk(sk)->tos) { + inet_sk(sk_it)->tos = inet_sk(sk)->tos; + sk_it->sk_priority = sk->sk_priority; + sk_dst_reset(sk_it); + } + } + } +#endif } break; case IP_TTL: diff -urN linux-4.1.52-orig/net/ipv4/ip_tunnel.c linux-4.1.52/net/ipv4/ip_tunnel.c --- linux-4.1.52-orig/net/ipv4/ip_tunnel.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/ip_tunnel.c 2020-02-07 01:47:54.000000000 +0800 @@ -63,6 +63,11 @@ #include #endif +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#include +#include +#endif + static unsigned int ip_tunnel_hash(__be32 key, __be32 remote) { return hash_32((__force u32)key ^ (__force u32)remote, @@ -440,7 +445,18 @@ goto drop; } +#if (defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG)) + /* as bcm_gre_rcv_check increments seqno (for all tunnel packets) when + * GRE acceleration is enabled, we try to avoid double increment here + */ + + /*TODO: fix this check to make sure this logic applies only for GRE + * tunnel. right now the code may break other type of tunnels + */ + if (!blog_gre_tunnel_accelerated() && (tunnel->parms.i_flags&TUNNEL_SEQ)){ +#else if (tunnel->parms.i_flags&TUNNEL_SEQ) { +#endif if (!(tpi->flags&TUNNEL_SEQ) || (tunnel->i_seqno && (s32)(ntohl(tpi->seq) - tunnel->i_seqno) < 0)) { tunnel->dev->stats.rx_fifo_errors++; @@ -478,6 +494,16 @@ } else { skb->dev = tunnel->dev; } +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + if ((skb->protocol != htons(ETH_P_IP)) +#if IS_ENABLED(CONFIG_IPV6) + && (skb->protocol != htons(ETH_P_IPV6)) && + (tpi->proto != htons(ETH_P_TEB) || skb->protocol != htons(ETH_P_8021Q)) +#endif + ) { + blog_skip(skb, blog_skip_reason_unknown_proto); /* No blogging */ + } +#endif gro_cells_receive(&tunnel->gro_cells, skb); return 0; diff -urN linux-4.1.52-orig/net/ipv4/Kconfig linux-4.1.52/net/ipv4/Kconfig --- linux-4.1.52-orig/net/ipv4/Kconfig 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/Kconfig 2020-02-07 01:47:54.000000000 +0800 @@ -615,6 +615,40 @@ For further details see: http://simula.stanford.edu/~alizade/Site/DCTCP_files/dctcp-final.pdf +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +config TCP_CONG_LIA + tristate "MPTCP Linked Increase" + depends on MPTCP + default n + ---help--- + MultiPath TCP Linked Increase Congestion Control + To enable it, just put 'lia' in tcp_congestion_control + +config TCP_CONG_OLIA + tristate "MPTCP Opportunistic Linked Increase" + depends on MPTCP + default n + ---help--- + MultiPath TCP Opportunistic Linked Increase Congestion Control + To enable it, just put 'olia' in tcp_congestion_control + +config TCP_CONG_WVEGAS + tristate "MPTCP WVEGAS CONGESTION CONTROL" + depends on MPTCP + default n + ---help--- + wVegas congestion control for MPTCP + To enable it, just put 'wvegas' in tcp_congestion_control + +config TCP_CONG_BALIA + tristate "MPTCP BALIA CONGESTION CONTROL" + depends on MPTCP + default n + ---help--- + Multipath TCP Balanced Linked Adaptation Congestion Control + To enable it, just put 'balia' in tcp_congestion_control + +#endif choice prompt "Default TCP congestion control" default DEFAULT_CUBIC @@ -646,6 +680,20 @@ config DEFAULT_DCTCP bool "DCTCP" if TCP_CONG_DCTCP=y +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + config DEFAULT_LIA + bool "Lia" if TCP_CONG_LIA=y + + config DEFAULT_OLIA + bool "Olia" if TCP_CONG_OLIA=y + + config DEFAULT_WVEGAS + bool "Wvegas" if TCP_CONG_WVEGAS=y + + config DEFAULT_BALIA + bool "Balia" if TCP_CONG_BALIA=y + +#endif config DEFAULT_RENO bool "Reno" endchoice @@ -666,6 +714,12 @@ default "vegas" if DEFAULT_VEGAS default "westwood" if DEFAULT_WESTWOOD default "veno" if DEFAULT_VENO +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + default "lia" if DEFAULT_LIA + default "olia" if DEFAULT_OLIA + default "wvegas" if DEFAULT_WVEGAS + default "balia" if DEFAULT_BALIA +#endif default "reno" if DEFAULT_RENO default "dctcp" if DEFAULT_DCTCP default "cubic" diff -urN linux-4.1.52-orig/net/ipv4/Makefile linux-4.1.52/net/ipv4/Makefile --- linux-4.1.52-orig/net/ipv4/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -2,6 +2,11 @@ # Makefile for the Linux TCP/IP (INET) layer. # +ifdef BCM_KF # defined(CONFIG_BCM_KF_BLOG) +EXTRA_CFLAGS += -I$(INC_BRCMDRIVER_PUB_PATH)/$(BRCM_BOARD) +EXTRA_CFLAGS += -I$(INC_BRCMSHARED_PUB_PATH)/bcm963xx +endif # BCM_KF + obj-y := route.o inetpeer.o protocol.o \ ip_input.o ip_fragment.o ip_forward.o ip_options.o \ ip_output.o ip_sockglue.o inet_hashtables.o \ diff -urN linux-4.1.52-orig/net/ipv4/netfilter/ip_tables.c linux-4.1.52/net/ipv4/netfilter/ip_tables.c --- linux-4.1.52-orig/net/ipv4/netfilter/ip_tables.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/netfilter/ip_tables.c 2020-02-07 01:47:54.000000000 +0800 @@ -31,6 +31,12 @@ #include #include "../../netfilter/xt_repldata.h" +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) +#include +#endif /* CONFIG_BCM_RUNNER */ +#endif /* CONFIG_BCM_KF_RUNNER */ + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Netfilter Core Team "); MODULE_DESCRIPTION("IPv4 packet filter"); @@ -72,7 +78,13 @@ /* Returns whether matches rule or not. */ /* Performance critical - called for every packet */ static inline bool + +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG_FEATURE) +ip_packet_match(struct sk_buff *skb, + const struct iphdr *ip, +#else ip_packet_match(const struct iphdr *ip, +#endif const char *indev, const char *outdev, const struct ipt_ip *ipinfo, @@ -82,6 +94,13 @@ #define FWINV(bool, invflg) ((bool) ^ !!(ipinfo->invflags & (invflg))) +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG_FEATURE) + if ( ipinfo->proto == IPPROTO_TCP ) + skb->ipt_check |= IPT_MATCH_TCP; + else if ( ipinfo->proto == IPPROTO_UDP ) + skb->ipt_check |= IPT_MATCH_UDP; +#endif + if (FWINV((ip->saddr&ipinfo->smsk.s_addr) != ipinfo->src.s_addr, IPT_INV_SRCIP) || FWINV((ip->daddr&ipinfo->dmsk.s_addr) != ipinfo->dst.s_addr, @@ -347,9 +366,19 @@ const struct xt_entry_match *ematch; IP_NF_ASSERT(e); + +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG_FEATURE) + skb->ipt_check = 0; + if (!ip_packet_match(skb, ip, indev, outdev, + &e->ip, acpar.fragoff)) { +#else if (!ip_packet_match(ip, indev, outdev, &e->ip, acpar.fragoff)) { +#endif + +#if !defined(CONFIG_BCM_KF_BLOG) || !defined(CONFIG_BLOG_FEATURE) no_match: +#endif e = ipt_next_entry(e); continue; } @@ -358,7 +387,11 @@ acpar.match = ematch->u.kernel.match; acpar.matchinfo = ematch->data; if (!acpar.match->match(skb, &acpar)) +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG_FEATURE) + skb->ipt_check |= IPT_TARGET_CHECK; +#else goto no_match; +#endif } ADD_COUNTER(e->counters, skb->len, 1); @@ -417,6 +450,50 @@ verdict = t->u.kernel.target->target(skb, &acpar); /* Target might have changed stuff. */ ip = ip_hdr(skb); +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG_FEATURE) + /* Use default policy of the chain */ + if ( skb->ipt_check & IPT_TARGET_CHECK ) + verdict = t->u.kernel.target->target(skb, &acpar); + + if ( skb->blog_p ) { + if ( (skb->ipt_check & IPT_MATCH_LENGTH) && + (skb->ipt_check & IPT_MATCH_TCP) && + (skb->ipt_check & IPT_TARGET_MARK) && + !(skb->blog_p->isWan) ) { + skb->blog_p->preMod = 1; + skb->blog_p->postMod = 1; + skb->blog_p->preHook = &blog_pre_mod_hook; + skb->blog_p->postHook = &blog_post_mod_hook; + } + if ( (skb->ipt_check & IPT_MATCH_LENGTH) && + (skb->ipt_check & IPT_TARGET_MARK) ) { + blog_set_len_tbl(skb->ipt_log.u32); + skb->blog_p->preMod = 1; + skb->blog_p->postMod = 1; + skb->blog_p->lenPrior = 1; + skb->blog_p->preHook = &blog_pre_mod_hook; + skb->blog_p->postHook = &blog_post_mod_hook; + } + if ( (skb->ipt_check & IPT_MATCH_DSCP) && + (skb->ipt_check & IPT_TARGET_DSCP) ) { + blog_set_dscp_tbl(skb->ipt_log.u8[BLOG_MATCH_DSCP_INDEX], skb->ipt_log.u8[BLOG_TARGET_DSCP_INDEX]); + skb->blog_p->preMod = 1; + skb->blog_p->postMod = 1; + skb->blog_p->dscpMangl = 1; + skb->blog_p->preHook = &blog_pre_mod_hook; + skb->blog_p->postHook = &blog_post_mod_hook; + } + if ( (skb->ipt_check & IPT_MATCH_TOS) && + (skb->ipt_check & IPT_TARGET_TOS) ) { + blog_set_tos_tbl(skb->ipt_log.u8[BLOG_MATCH_TOS_INDEX], skb->ipt_log.u8[BLOG_TARGET_DSCP_INDEX]); + skb->blog_p->preMod = 1; + skb->blog_p->postMod = 1; + skb->blog_p->tosMangl = 1; + skb->blog_p->preHook = &blog_pre_mod_hook; + skb->blog_p->postHook = &blog_post_mod_hook; + } + } +#endif if (verdict == XT_CONTINUE) e = ipt_next_entry(e); else @@ -587,6 +664,12 @@ duprintf("check failed for `%s'.\n", par->match->name); return ret; } + +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) + BL_OPS(net_ipv4_netfilter_ip_tables_check_match(m, par, ip)); +#endif /* CONFIG_BCM_RUNNER */ +#endif /* CONFIG_BCM_KF_RUNNER */ return 0; } @@ -1227,6 +1310,12 @@ (newinfo->number <= oldinfo->initial_entries)) module_put(t->me); +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) + BL_OPS(net_ipv4_netfilter_ip_tables___do_replace(oldinfo, newinfo)); +#endif /* CONFIG_BCM_RUNNER */ +#endif /* CONFIG_BCM_KF_RUNNER */ + /* Get the old counters, and synchronize with replace */ get_counters(oldinfo, counters); @@ -1286,6 +1375,13 @@ goto free_newinfo; } +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) + BL_OPS(net_ipv4_netfilter_ip_tables_do_replace(&tmp)); +#endif /* CONFIG_BCM_RUNNER */ +#endif /* CONFIG_BCM_KF_RUNNER */ + + ret = translate_table(net, newinfo, loc_cpu_entry, &tmp); if (ret != 0) goto free_newinfo; diff -urN linux-4.1.52-orig/net/ipv4/netfilter/ipt_MASQUERADE.c linux-4.1.52/net/ipv4/netfilter/ipt_MASQUERADE.c --- linux-4.1.52-orig/net/ipv4/netfilter/ipt_MASQUERADE.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/netfilter/ipt_MASQUERADE.c 2020-02-07 01:47:54.000000000 +0800 @@ -24,10 +24,12 @@ #include #include + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Netfilter Core Team "); MODULE_DESCRIPTION("Xtables: automatic-address SNAT"); + /* FIXME: Multiple targets. --RR */ static int masquerade_tg_check(const struct xt_tgchk_param *par) { @@ -55,6 +57,11 @@ range.min_proto = mr->range[0].min; range.max_proto = mr->range[0].max; +#if defined(CONFIG_BCM_KF_NETFILTER) + range.min_addr.ip = mr->range[0].min_ip; + range.max_addr.ip = mr->range[0].max_ip; +#endif + return nf_nat_masquerade_ipv4(skb, par->hooknum, &range, par->out); } diff -urN linux-4.1.52-orig/net/ipv4/netfilter/Kconfig linux-4.1.52/net/ipv4/netfilter/Kconfig --- linux-4.1.52-orig/net/ipv4/netfilter/Kconfig 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/netfilter/Kconfig 2020-02-07 01:47:54.000000000 +0800 @@ -298,6 +298,29 @@ endif # IP_NF_NAT +config NF_NAT_PROTO_ESP + tristate + depends on BCM_KF_NETFILTER && NF_NAT_IPV4 && NF_CT_PROTO_ESP + default NF_NAT_IPV4 && NF_CT_PROTO_ESP + + +config NF_NAT_IPSEC + tristate + depends on BCM_KF_NETFILTER && NF_CONNTRACK && NF_NAT_IPV4 + default NF_NAT_IPV4 && NF_CONNTRACK_IPSEC + +config NF_NAT_PT + tristate "Port Triggering support" + depends on NF_NAT_IPV4 && BCM_KF_NETFILTER + help + Port Triggering support + + To compile it as a module, choose M here. If unsure, say Y. + +config NF_NAT_RTSP + tristate + depends on BCM_KF_NETFILTER && IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT_IPV4 + default NF_NAT_IPV4 && NF_CONNTRACK_RTSP # mangle + specific targets config IP_NF_MANGLE tristate "Packet mangling" diff -urN linux-4.1.52-orig/net/ipv4/netfilter/Makefile linux-4.1.52/net/ipv4/netfilter/Makefile --- linux-4.1.52-orig/net/ipv4/netfilter/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/netfilter/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -29,11 +29,22 @@ # NAT helpers (nf_conntrack) obj-$(CONFIG_NF_NAT_H323) += nf_nat_h323.o obj-$(CONFIG_NF_NAT_PPTP) += nf_nat_pptp.o +ifdef BCM_KF # defined(CONFIG_BCM_KF_NETFILTER) +obj-$(CONFIG_NF_NAT_IPSEC) += nf_nat_ipsec.o +endif #BCM_KF # defined(CONFIG_BCM_KF_NETFILTER) obj-$(CONFIG_NF_NAT_SNMP_BASIC) += nf_nat_snmp_basic.o obj-$(CONFIG_NF_NAT_MASQUERADE_IPV4) += nf_nat_masquerade_ipv4.o +ifdef BCM_KF # defined(CONFIG_BCM_KF_NETFILTER) +obj-$(CONFIG_NF_NAT_PT) += nf_nat_pt.o +obj-$(CONFIG_NF_NAT_RTSP) += nf_nat_rtsp.o +endif #BCM_KF # defined(CONFIG_BCM_KF_NETFILTER) + # NAT protocols (nf_nat) obj-$(CONFIG_NF_NAT_PROTO_GRE) += nf_nat_proto_gre.o +ifdef BCM_KF # defined(CONFIG_BCM_KF_NETFILTER) +obj-$(CONFIG_NF_NAT_PROTO_ESP) += nf_nat_proto_esp.o +endif #BCM_KF # defined(CONFIG_BCM_KF_NETFILTER obj-$(CONFIG_NF_TABLES_IPV4) += nf_tables_ipv4.o obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV4) += nft_chain_route_ipv4.o diff -urN linux-4.1.52-orig/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c linux-4.1.52/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c --- linux-4.1.52-orig/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c 2020-02-07 01:47:54.000000000 +0800 @@ -41,8 +41,13 @@ if (ap == NULL) return false; +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) + memcpy(&tuple->src.u3.ip, &ap[0], sizeof(__be32)); + memcpy(&tuple->dst.u3.ip, &ap[1], sizeof(__be32)); +#else tuple->src.u3.ip = ap[0]; tuple->dst.u3.ip = ap[1]; +#endif return true; } diff -urN linux-4.1.52-orig/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c linux-4.1.52/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c --- linux-4.1.52-orig/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c 2020-02-07 01:47:54.000000000 +0800 @@ -114,6 +114,7 @@ } #endif + static int ct_seq_show(struct seq_file *s, void *v) { struct nf_conntrack_tuple_hash *hash = v; @@ -121,6 +122,9 @@ const struct nf_conntrack_l3proto *l3proto; const struct nf_conntrack_l4proto *l4proto; int ret = 0; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BCM_KF_NETFILTER) && defined(CONFIG_BLOG) + long ctExpiryVal = 0; +#endif NF_CT_ASSERT(ct); if (unlikely(!atomic_inc_not_zero(&ct->ct_general.use))) @@ -139,11 +143,37 @@ NF_CT_ASSERT(l4proto); ret = -ENOSPC; + +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BCM_KF_NETFILTER) && defined(CONFIG_BLOG) + if (timer_pending(&ct->timeout)) + { + BlogCtTime_t ct_time; + memset(&ct_time, 0, sizeof(ct_time)); + + blog_lock(); + if (ct->blog_key[BLOG_PARAM1_DIR_ORIG] != BLOG_KEY_FC_INVALID || + ct->blog_key[BLOG_PARAM1_DIR_REPLY] != BLOG_KEY_FC_INVALID) { + blog_query(QUERY_FLOWTRACK, (void*)ct, + ct->blog_key[BLOG_PARAM1_DIR_ORIG], + ct->blog_key[BLOG_PARAM1_DIR_REPLY], (unsigned long) &ct_time); + + ctExpiryVal = (long)(ct->extra_jiffies)/HZ - ct_time.idle; + } else + ctExpiryVal = (long)(ct->timeout.expires - jiffies)/HZ; + + blog_unlock(); + } + seq_printf(s, "%-8s %u %-8s %u %ld ", + l3proto->name, nf_ct_l3num(ct), + l4proto->name, nf_ct_protonum(ct), + ctExpiryVal); +#else seq_printf(s, "%-8s %u %ld ", l4proto->name, nf_ct_protonum(ct), timer_pending(&ct->timeout) ? (long)(ct->timeout.expires - jiffies)/HZ : 0); - +#endif + if (l4proto->print_conntrack) l4proto->print_conntrack(s, ct); diff -urN linux-4.1.52-orig/net/ipv4/netfilter/nf_conntrack_proto_icmp.c linux-4.1.52/net/ipv4/netfilter/nf_conntrack_proto_icmp.c --- linux-4.1.52-orig/net/ipv4/netfilter/nf_conntrack_proto_icmp.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/netfilter/nf_conntrack_proto_icmp.c 2020-02-07 01:47:54.000000000 +0800 @@ -131,6 +131,13 @@ enum ip_conntrack_info *ctinfo, unsigned int hooknum) { +#if defined(CONFIG_BCM_KF_NETFILTER) + struct inside { + struct icmphdr icmp; + struct iphdr ip; + } __attribute__((packed)); + struct inside _in, *pIn; +#endif struct nf_conntrack_tuple innertuple, origtuple; const struct nf_conntrack_l4proto *innerproto; const struct nf_conntrack_tuple_hash *h; @@ -138,6 +145,13 @@ NF_CT_ASSERT(skb->nfct == NULL); +#if defined(CONFIG_BCM_KF_NETFILTER) + /* Not enough header? */ + pIn = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_in), &_in); + if (pIn == NULL) + return -NF_ACCEPT; +#endif + /* Are they talking about one of our connections? */ if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb) + ip_hdrlen(skb) @@ -148,7 +162,13 @@ } /* rcu_read_lock()ed by nf_hook_slow */ +#if defined(CONFIG_BCM_KF_NETFILTER) + innerproto = __nf_ct_l4proto_find(PF_INET, pIn->ip.protocol); + origtuple.src.u3.ip = pIn->ip.saddr; + origtuple.dst.u3.ip = pIn->ip.daddr; +#else innerproto = __nf_ct_l4proto_find(PF_INET, origtuple.dst.protonum); +#endif /* Ordinarily, we'd expect the inverted tupleproto, but it's been preserved inside the ICMP. */ diff -urN linux-4.1.52-orig/net/ipv4/netfilter/nf_nat_ipsec.c linux-4.1.52/net/ipv4/netfilter/nf_nat_ipsec.c --- linux-4.1.52-orig/net/ipv4/netfilter/nf_nat_ipsec.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/ipv4/netfilter/nf_nat_ipsec.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,94 @@ +#if defined(CONFIG_BCM_KF_PROTO_IPSEC) +/* +<:copyright-BRCM:2012:GPL/GPL:standard + + Copyright (c) 2012 Broadcom + All Rights Reserved + +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 (the "GPL"). + +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. + + +A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by +writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + +:> +*/ + +#include +#include +#include + +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Pavan Kumar "); +MODULE_DESCRIPTION("Netfilter connection tracking module for ipsec"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("nf_nat_ipsec"); + +/* outbound packets == from LAN to WAN */ +static int +ipsec_outbound_pkt(struct sk_buff *skb, + struct nf_conn *ct, enum ip_conntrack_info ctinfo) + +{ + struct iphdr *iph = ip_hdr(skb); + struct udphdr *udph = (void *)iph + iph->ihl * 4; + + /* make sure source port is 500 */ + udph->source = htons(IPSEC_PORT); + udph->check = 0; + + return NF_ACCEPT; +} + + +/* inbound packets == from WAN to LAN */ +static int +ipsec_inbound_pkt(struct sk_buff *skb, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, __be32 lan_ip) +{ + struct iphdr *iph = ip_hdr(skb); + struct udphdr *udph = (void *)iph + iph->ihl * 4; + + iph->daddr = lan_ip; + udph->check = 0; + iph->check = 0; + iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); + + return NF_ACCEPT; +} + +static int __init nf_nat_helper_ipsec_init(void) +{ + BUG_ON(nf_nat_ipsec_hook_outbound != NULL); + RCU_INIT_POINTER(nf_nat_ipsec_hook_outbound, ipsec_outbound_pkt); + + BUG_ON(nf_nat_ipsec_hook_inbound != NULL); + RCU_INIT_POINTER(nf_nat_ipsec_hook_inbound, ipsec_inbound_pkt); + + return 0; +} + +static void __exit nf_nat_helper_ipsec_fini(void) +{ + RCU_INIT_POINTER(nf_nat_ipsec_hook_inbound, NULL); + RCU_INIT_POINTER(nf_nat_ipsec_hook_outbound, NULL); + + synchronize_rcu(); +} + +module_init(nf_nat_helper_ipsec_init); +module_exit(nf_nat_helper_ipsec_fini); +#endif diff -urN linux-4.1.52-orig/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c linux-4.1.52/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c --- linux-4.1.52-orig/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c 2020-02-07 01:47:54.000000000 +0800 @@ -22,6 +22,140 @@ #include #include +#if defined(CONFIG_BCM_KF_NETFILTER) +#include +#include +#include +#endif + +#if defined(CONFIG_BCM_KF_NETFILTER) +/****************************************************************************/ +static void bcm_nat_expect(struct nf_conn *ct, + struct nf_conntrack_expect *exp) +{ + struct nf_nat_range range; + + /* This must be a fresh one. */ + BUG_ON(ct->status & IPS_NAT_DONE_MASK); + + /* Change src to where new ct comes from */ + range.flags = NF_NAT_RANGE_MAP_IPS; + range.min_addr = range.max_addr = + ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3; + nf_nat_setup_info(ct, &range, NF_NAT_MANIP_SRC); + + /* For DST manip, map port here to where it's expected. */ + range.flags = (NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED); + range.min_proto = range.max_proto = exp->saved_proto; + range.min_addr = range.max_addr = exp->saved_addr; + nf_nat_setup_info(ct, &range, NF_NAT_MANIP_DST); +} + +/****************************************************************************/ +static int bcm_nat_help(struct sk_buff *skb, unsigned int protoff, + struct nf_conn *ct, enum ip_conntrack_info ctinfo) +{ + int dir = CTINFO2DIR(ctinfo); + struct nf_conn_help *help = nfct_help(ct); + struct nf_conntrack_expect *exp; + + if (dir != IP_CT_DIR_ORIGINAL || + help->expecting[NF_CT_EXPECT_CLASS_DEFAULT]) + return NF_ACCEPT; + + pr_debug("bcm_nat: packet[%d bytes] ", skb->len); + nf_ct_dump_tuple(&ct->tuplehash[dir].tuple); + pr_debug("reply: "); + nf_ct_dump_tuple(&ct->tuplehash[!dir].tuple); + + /* Create expect */ + if ((exp = nf_ct_expect_alloc(ct)) == NULL) + return NF_ACCEPT; + + nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, AF_INET, NULL, + &ct->tuplehash[!dir].tuple.dst.u3, IPPROTO_UDP, + NULL, &ct->tuplehash[!dir].tuple.dst.u.udp.port); + exp->flags = NF_CT_EXPECT_PERMANENT; + exp->saved_addr = ct->tuplehash[dir].tuple.src.u3; + exp->saved_proto.udp.port = ct->tuplehash[dir].tuple.src.u.udp.port; + exp->dir = !dir; + exp->expectfn = bcm_nat_expect; + + /* Setup expect */ + nf_ct_expect_related(exp); + nf_ct_expect_put(exp); + pr_debug("bcm_nat: expect setup\n"); + + return NF_ACCEPT; +} + +/****************************************************************************/ +static struct nf_conntrack_expect_policy bcm_nat_exp_policy __read_mostly = { + .max_expected = 1000, + .timeout = 240, +}; + +/****************************************************************************/ +static struct nf_conntrack_helper nf_conntrack_helper_bcm_nat __read_mostly = { + .name = "BCM-NAT", + .me = THIS_MODULE, + .tuple.src.l3num = AF_INET, + .tuple.dst.protonum = IPPROTO_UDP, + .expect_policy = &bcm_nat_exp_policy, + .expect_class_max = 1, + .help = bcm_nat_help, +}; + +/****************************************************************************/ +static inline int find_exp(__be32 ip, __be16 port, struct nf_conn *ct) +{ + struct nf_conntrack_tuple tuple; + struct nf_conntrack_expect *i = NULL; + + + memset(&tuple, 0, sizeof(tuple)); + tuple.src.l3num = AF_INET; + tuple.dst.protonum = IPPROTO_UDP; + tuple.dst.u3.ip = ip; + tuple.dst.u.udp.port = port; + + rcu_read_lock(); + i = __nf_ct_expect_find(nf_ct_net(ct), nf_ct_zone(ct), &tuple); + rcu_read_unlock(); + + return i != NULL; +} + +/****************************************************************************/ +static inline struct nf_conntrack_expect *find_fullcone_exp(struct nf_conn *ct) +{ + struct nf_conntrack_tuple * tp = + &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; + struct net *net = nf_ct_net(ct); + struct nf_conntrack_expect * exp = NULL; + struct nf_conntrack_expect * i; + unsigned int h; + + rcu_read_lock(); + for (h = 0; h < nf_ct_expect_hsize; h++) { + hlist_for_each_entry_rcu(i, &net->ct.expect_hash[h], hnode) { + if (nf_inet_addr_cmp(&i->saved_addr, &tp->src.u3) && + i->saved_proto.all == tp->src.u.all && + i->tuple.dst.protonum == tp->dst.protonum && + i->tuple.src.u3.ip == 0 && + i->tuple.src.u.udp.port == 0) { + exp = i; + break; + } + } + } + rcu_read_unlock(); + + return exp; +} +#endif /* CONFIG_KF_NETFILTER */ + + unsigned int nf_nat_masquerade_ipv4(struct sk_buff *skb, unsigned int hooknum, const struct nf_nat_range *range, @@ -58,6 +192,76 @@ nat->masq_index = out->ifindex; +#if defined(CONFIG_BCM_KF_NETFILTER) + +/* RFC 4787 - 4.2.2. Port Parity + i.e., an even port will be mapped to an even port, and an odd port will be mapped to an odd port. +*/ +#define CHECK_PORT_PARITY(a, b) ((a%2)==(b%2)) + + if (range->min_addr.ip != 0 /* nat_mode == full cone */ + && (nfct_help(ct) == NULL || nfct_help(ct)->helper == NULL) + && nf_ct_protonum(ct) == IPPROTO_UDP) { + unsigned int ret; + u_int16_t minport; + u_int16_t maxport; + struct nf_conntrack_expect *exp; + + pr_debug("bcm_nat: need full cone NAT\n"); + + /* Choose port */ + spin_lock_bh(&nf_conntrack_expect_lock); + /* Look for existing expectation */ + exp = find_fullcone_exp(ct); + if (exp) { + minport = maxport = exp->tuple.dst.u.udp.port; + pr_debug("bcm_nat: existing mapped port = %hu\n", + ntohs(minport)); + } else { /* no previous expect */ + u_int16_t newport, tmpport, orgport; + + minport = range->min_proto.all == 0? + ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src. + u.udp.port : range->min_proto.all; + maxport = range->max_proto.all == 0? + htons(65535) : range->max_proto.all; + orgport = ntohs(minport); + for (newport = ntohs(minport),tmpport = ntohs(maxport); + newport <= tmpport; newport++) { + if (CHECK_PORT_PARITY(orgport, newport) && !find_exp(newsrc, htons(newport), ct)) { + pr_debug("bcm_nat: new mapped port = " + "%hu\n", newport); + minport = maxport = htons(newport); + break; + } + } + } + spin_unlock_bh(&nf_conntrack_expect_lock); + + + memset(&newrange.min_addr, 0, sizeof(newrange.min_addr)); + memset(&newrange.max_addr, 0, sizeof(newrange.max_addr)); + + newrange.flags = range->flags | NF_NAT_RANGE_MAP_IPS | + NF_NAT_RANGE_PROTO_SPECIFIED; + newrange.max_addr.ip = newrange.min_addr.ip = newsrc; + newrange.min_proto.udp.port = newrange.max_proto.udp.port = minport; + + /* Set ct helper */ + ret = nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_SRC); + if (ret == NF_ACCEPT) { + struct nf_conn_help *help = nfct_help(ct); + if (help == NULL) + help = nf_ct_helper_ext_add(ct, &nf_conntrack_helper_bcm_nat, GFP_ATOMIC); + if (help != NULL) { + help->helper = &nf_conntrack_helper_bcm_nat; + pr_debug("bcm_nat: helper set\n"); + } + } + return ret; + } +#endif /* CONFIG_KF_NETFILTER */ + /* Transfer from original range. */ memset(&newrange.min_addr, 0, sizeof(newrange.min_addr)); memset(&newrange.max_addr, 0, sizeof(newrange.max_addr)); @@ -148,6 +352,9 @@ void nf_nat_masquerade_ipv4_unregister_notifier(void) { +#if defined(CONFIG_BCM_KF_NETFILTER) + nf_conntrack_helper_unregister(&nf_conntrack_helper_bcm_nat); +#endif /* check if the notifier still has clients */ if (atomic_dec_return(&masquerade_notifier_refcount) > 0) return; diff -urN linux-4.1.52-orig/net/ipv4/netfilter/nf_nat_proto_esp.c linux-4.1.52/net/ipv4/netfilter/nf_nat_proto_esp.c --- linux-4.1.52-orig/net/ipv4/netfilter/nf_nat_proto_esp.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/ipv4/netfilter/nf_nat_proto_esp.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,117 @@ +#if defined(CONFIG_BCM_KF_PROTO_ESP) +/* +<:copyright-BRCM:2012:GPL/GPL:standard + + Copyright (c) 2012 Broadcom + All Rights Reserved + +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 (the "GPL"). + +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. + + +A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by +writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + +:> +*/ +/****************************************************************************** +Filename: nf_nat_proto_esp.c +Author: Pavan Kumar +Creation Date: 05/27/04 + +Description: + Implements the ESP ALG connectiontracking. + Migrated to kernel 2.6.21.5 on April 16, 2008 by Dan-Han Tsai. + Migrated to kernel 3.4.11 on Jan 21, 2013 by Kirill Tsym +*****************************************************************************/ +#include +#include +#include + +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Harald Welte "); +MODULE_DESCRIPTION("Netfilter NAT protocol helper module for ESP"); + +/* is spi in given range between min and max */ +static bool +esp_in_range(const struct nf_conntrack_tuple *tuple, + enum nf_nat_manip_type maniptype, + const union nf_conntrack_man_proto *min, + const union nf_conntrack_man_proto *max) +{ + return true; +} + +/* generate unique tuple ... */ +static void +esp_unique_tuple(const struct nf_nat_l3proto *l3proto, + struct nf_conntrack_tuple *tuple, + const struct nf_nat_range *range, + enum nf_nat_manip_type maniptype, + const struct nf_conn *ct) +{ + return; +} + +/* manipulate a ESP packet according to maniptype */ +static bool +esp_manip_pkt(struct sk_buff *skb, + const struct nf_nat_l3proto *l3proto, + unsigned int iphdroff, unsigned int hdroff, + const struct nf_conntrack_tuple *tuple, + enum nf_nat_manip_type maniptype) +{ + struct esphdr *esph; + struct iphdr *iph = (struct iphdr *)(skb->data + iphdroff); + __be32 oldip, newip; + + if (!skb_make_writable(skb, hdroff + sizeof(*esph))) + return false; + + if (maniptype == NF_NAT_MANIP_SRC) + { + /* Get rid of src ip and src pt */ + oldip = iph->saddr; + newip = tuple->src.u3.ip; + } + else + { + /* Get rid of dst ip and dst pt */ + oldip = iph->daddr; + newip = tuple->dst.u3.ip; + } + + return true; +} + +const struct nf_nat_l4proto esp __read_mostly = { + .l4proto = IPPROTO_ESP, + .manip_pkt = esp_manip_pkt, + .in_range = esp_in_range, + .unique_tuple = esp_unique_tuple, +}; + +int __init nf_nat_proto_esp_init(void) +{ + return nf_nat_l4proto_register(NFPROTO_IPV4, &esp); +} + +void __exit nf_nat_proto_esp_fini(void) +{ + nf_nat_l4proto_unregister(NFPROTO_IPV4, &esp); +} + +module_init(nf_nat_proto_esp_init); +module_exit(nf_nat_proto_esp_fini); +#endif diff -urN linux-4.1.52-orig/net/ipv4/netfilter/nf_nat_pt.c linux-4.1.52/net/ipv4/netfilter/nf_nat_pt.c --- linux-4.1.52-orig/net/ipv4/netfilter/nf_nat_pt.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/ipv4/netfilter/nf_nat_pt.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,547 @@ +#if defined(CONFIG_BCM_KF_NETFILTER) +/* +<:copyright-BRCM:2011:DUAL/GPL:standard + + Copyright (c) 2011 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + +/* PT for IP connection tracking. */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) +#include +#endif /* CONFIG_BCM_RUNNER */ +#endif /* CONFIG_BCM_KF_RUNNER */ + +#define PT_PROTO_TCP 1 +#define PT_PROTO_UDP 2 +#define PT_PROTO_ALL (PT_PROTO_TCP|PT_PROTO_UDP) + +/* +// add/del entry +- add trigger port (tcp, 100, 200) open port (udp, 300, 400) on interface eth0.1 +- add trigger port (udp, 6000, 6000) open port (both, 8000, 8001) on interface ppp0 +# echo a 1 100 200 2 300 400 eth0.1 > /proc/net/nat_pt +# echo a 1 6000 6000 3 8000 8001 ppp0 > /proc/net/nat_pt + +// Read entry +# cat /proc/net/nat_pt +idx: trigger port(protocol,start,end) : open port(protocol,start,end) intf + 0: 1 100 200 2 300 400 eth0.1 + 1: 1 6000 6000 3 8000 8001 ppp0 +total_outport_cnt=102, total_inport_cnt=103, max_idx_used=2 + +// data structure + outport={1,100,200,2,6000,6000, ...} + inport={2,300,400,3,8000,8001, ...} + iface={eth0.1,ppp0, ...} +*/ +static unsigned short outport[PT_MAX_ENTRIES*3]; +static unsigned short inport[PT_MAX_ENTRIES*3]; +static char *iface[PT_MAX_ENTRIES]; + +// sum of the outports of all configuration entries <= PT_MAX_PORTS +// sum of the inports of one configuration entry <= PT_MAX_EXPECTED +static int total_inport_cnt; +static int total_outport_cnt; +static int max_idx_used; // max entry index is currently used + +static void trigger_ports(struct nf_conn *ct, int dir, int idx) +{ + __be16 port; + unsigned short iport, iproto; + struct nf_conntrack_expect *exp; + struct nf_conntrack_expect *exp2; + + /* Setup expectations */ + for (iport = inport[idx*3+1]; iport <= inport[idx*3+2]; iport++) { + port = htons(iport); + if ((exp = nf_ct_expect_alloc(ct)) == NULL) { + pr_debug("nf_nat_pt: nf_ct_expect_alloc() error\n"); + return; + } + if (inport[idx*3] == PT_PROTO_TCP) + iproto = IPPROTO_TCP; + else if (inport[idx*3] == PT_PROTO_UDP) + iproto = IPPROTO_UDP; + else { + if ((exp2 = nf_ct_expect_alloc(ct)) == NULL) { + pr_debug("nf_nat_pt: " + "nf_ct_expect_alloc() error\n"); + return; + } + iproto = IPPROTO_TCP; + nf_ct_expect_init(exp2, NF_CT_EXPECT_CLASS_DEFAULT, + AF_INET, NULL, + &ct->tuplehash[!dir].tuple.dst.u3, + iproto, NULL, &port); + exp2->expectfn = nf_nat_follow_master; + exp2->flags = NF_CT_EXPECT_PERMANENT; + exp2->saved_proto.all = port; + exp2->dir = !dir; + if(nf_ct_expect_related(exp2) == 0) { + pr_debug("nf_nat_pt: expect incoming " + "connection to %pI4:%hu %s\n", + &exp2->tuple.dst.u3.ip, iport, + iproto == IPPROTO_TCP? "tcp" : "udp"); + } else { + pr_debug("nf_nat_pt: failed to expect incoming " + "connection to %pI4:%hu %s\n", + &exp2->tuple.dst.u3.ip, iport, + iproto == IPPROTO_TCP? "tcp" : "udp"); + } + nf_ct_expect_put(exp2); + + iproto = IPPROTO_UDP; + } + + nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, + AF_INET, NULL, + &ct->tuplehash[!dir].tuple.dst.u3, + iproto, NULL, &port); + exp->expectfn = nf_nat_follow_master; + exp->flags = NF_CT_EXPECT_PERMANENT; + exp->saved_proto.all = port; + exp->dir = !dir; + if(nf_ct_expect_related(exp) == 0) { + pr_debug("nf_nat_pt: expect incoming connection to " + "%pI4:%hu %s\n", &exp->tuple.dst.u3.ip, iport, + iproto == IPPROTO_TCP? "tcp" : "udp"); + } else { + pr_debug("nf_nat_pt: failed to expect incoming " + "connection to %pI4:%hu %s\n", + &exp->tuple.dst.u3.ip, iport, + iproto == IPPROTO_TCP? "tcp" : "udp"); + } + nf_ct_expect_put(exp); + + } +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) +#if defined(CONFIG_BCM_RUNNER_RG) || defined(CONFIG_BCM_RUNNER_RG_MODULE) + BL_OPS(net_netfilter_xt_PORTTRIG_trigger_new (ct, ntohl(ct->tuplehash[!dir].tuple.src.u3.ip), ntohl(ct->tuplehash[!dir].tuple.dst.u3.ip), + inport[idx*3+1], inport[idx*3+2], inport[idx*3])); +#endif /* CONFIG_BCM_RUNNER_RG || CONFIG_BCM_RUNNER_RG_MODULE */ +#endif /* CONFIG_BCM_RUNNER */ +#endif /* CONFIG_BCM_KF_RUNNER */ +} + +/* FIXME: This should be in userspace. Later. */ +static int help(struct sk_buff *skb, unsigned int protoff, + struct nf_conn *ct, enum ip_conntrack_info ctinfo) +{ + int dir = CTINFO2DIR(ctinfo); + unsigned short oport, oproto; + int i; + + if ((nfct_help(ct))->expecting[NF_CT_EXPECT_CLASS_DEFAULT]) { + /* Already triggered */ + return NF_ACCEPT; + } + + /* We care only NATed outgoing packets */ + if (!(ct->status & IPS_SRC_NAT)) + return NF_ACCEPT; + + /* Get out protocol and port */ + if (nf_ct_protonum(ct) == IPPROTO_TCP) { + /* Don't do anything until TCP connection is established */ + if (ctinfo != IP_CT_ESTABLISHED && + ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) + return NF_ACCEPT; + oproto = PT_PROTO_TCP; + oport = ntohs(ct->tuplehash[dir].tuple.dst.u.tcp.port); + } else if(ct->tuplehash[dir].tuple.dst.protonum == IPPROTO_UDP) { + oproto = PT_PROTO_UDP; + oport = ntohs(ct->tuplehash[dir].tuple.dst.u.udp.port); + } else /* Care only TCP and UDP */ + return NF_ACCEPT; + for (i = 0; i < max_idx_used; i++) { + /* Look for matched port range */ + if (!(oproto & outport[i*3]) || (oport < outport[i*3+1]) || + (oport > outport[i*3+2])) + continue; + + /* If interface specified, they must match */ + if (iface[i] && strcmp(iface[i], skb_dst(skb)->dev->name)) + continue; + trigger_ports(ct, dir, i); + } + + return NF_ACCEPT; +} + +/* +** Becareful with ports that are registered already. +** ftp:21 +** irc:6667 +** tftp:69 +** snmp: 161,162 +** talk: 517,518 +** h323: 1720 +** sip: 5060 +** pptp: 1723 +** http: 80 +*/ +static int check_port(unsigned short port, unsigned short proto) +{ + if(proto & PT_PROTO_TCP) { + if (port == 21 || port == 6667 || port == 1720 || + port == 1723 || port == 80) + return 1; + } + + if(proto & PT_PROTO_UDP) { + if (port == 69 || port == 161 || port == 162 || port == 517 || + port == 518 || port == 5060) + return 1; + } + + return 0; +} + +static struct nf_conntrack_expect_policy pt_exp_policy = { + .max_expected = PT_MAX_EXPECTED, + .timeout = PT_TIMEOUT, +}; + + +static struct nf_conntrack_helper *helper_ptr[PT_MAX_ENTRIES]; // each entry may have up to range*2(tcp/udp) helper structures +static int helper_cnt[PT_MAX_ENTRIES]; +static int nat_pt_helper_register(int idx) +{ + struct nf_conntrack_helper *h; + int port, helper_num=0; + int ret=0; + + if(helper_cnt[idx] > 0) { + printk("nf_nat_pt: entry %d is occupied\n", idx); + return 1; + } + + helper_cnt[idx] = 0; + // calculate how many helper structures need for this entry + for (port = outport[idx*3+1]; port <= outport[idx*3+2]; port++ ) { + /* Don't register known ports */ + if (check_port(port, outport[idx*3])) { + printk("nf_nat_pt: cannot register port %hu " + "(already registered by other module)\n", + port); + continue; + } + if(outport[idx*3] == PT_PROTO_TCP || outport[idx*3] == PT_PROTO_UDP) + helper_num++; + else if (outport[idx*3] == PT_PROTO_ALL) { + helper_num+=2; + } + else { + printk("nf_nat_pt: unkown protocol !\n"); + return 1; + } + } + + if ((h = kzalloc(helper_num*sizeof(*h), GFP_KERNEL)) == NULL) { + printk("nf_nat_pt: OOM\n"); + return -ENOMEM; + } + helper_ptr[idx] = h; + helper_cnt[idx] = helper_num; + + for (port = outport[idx*3+1]; port <= outport[idx*3+2]; port++ ) { + + if (check_port(port, outport[idx*3])) + continue; + + snprintf(h->name, NF_CT_HELPER_NAME_LEN, "pt-%d-%d", port, inport[idx*3+1]); + h->me = THIS_MODULE; + h->expect_policy = &pt_exp_policy; + h->expect_class_max = 1; + if (outport[idx*3] == PT_PROTO_TCP) { + h->tuple.dst.protonum = IPPROTO_TCP; + } else if (outport[idx*3] == PT_PROTO_UDP) { + h->tuple.dst.protonum = IPPROTO_UDP; + } else if (outport[idx*3] == PT_PROTO_ALL) { + h->tuple.dst.protonum = IPPROTO_TCP; + h->tuple.src.u.all = htons(port); + h->tuple.src.l3num = AF_INET; + h->help = help; + + if ((ret = nf_conntrack_helper_register(h)) < 0) { + printk("nf_nat_pt: register helper error\n"); + return ret; + } + total_outport_cnt++; + h++; + + snprintf(h->name, NF_CT_HELPER_NAME_LEN, "pt-%d-%d", port, inport[idx*3+1]); + h->me = THIS_MODULE; + h->expect_policy = &pt_exp_policy; + h->expect_class_max = 1; + h->tuple.dst.protonum = IPPROTO_UDP; + } + h->tuple.src.u.all = htons(port); + h->tuple.src.l3num = AF_INET; + h->help = help; + if ((ret = nf_conntrack_helper_register(h)) < 0) { + pr_debug("nf_nat_pt: register helper error\n"); + return ret; + } + total_outport_cnt++; + h++; + } + return 0; +} + +static int nat_pt_helper_unregister(int idx) +{ + int i, cnt; + struct nf_conntrack_helper *h; + + if(helper_cnt[idx] == 0) { + printk("nf_nat_pt: entry %d is not valid \n", idx); + return 1; + } + + cnt = helper_cnt[idx]; + for (i = 0, h = helper_ptr[idx] ; i < cnt; i++, h++) { + if (hlist_unhashed(&h->hnode)) /* Not registered */ + continue; + nf_conntrack_helper_unregister(h); + total_outport_cnt--; + } + kfree(helper_ptr[idx]); + helper_cnt[idx] = 0; + return 0; +} + +ssize_t nat_pt_read_proc(struct file *file, char __user *buf, size_t size, loff_t *offset) +//static int nat_pt_read_proc(char* page, char ** start, off_t off, int count, int* eof, void * data) +{ + int i; + char *p, *np; + + if(*offset !=0) + { + return 0; + } + + p = np = buf; + np += sprintf(p, "idx: trigger port(protocol,start,end) : open port(protocol,start,end) intf\n"); + p = np; + for (i = 0; i < max_idx_used; i++) { + if( (outport[i*3] & (PT_PROTO_TCP|PT_PROTO_UDP)) != 0) + np += sprintf(p, "%3d: %2d %6d %6d %2d %6d %6d %s\n", + i, outport[i*3], outport[i*3+1], outport[i*3+2], inport[i*3], inport[i*3+1], inport[i*3+2], iface[i]); + p = np; + } + np += sprintf(p, "total_outport_cnt=%d, total_inport_cnt=%d\n", total_outport_cnt, total_inport_cnt); + + return (np - buf); +} + +static ssize_t nat_pt_write_proc(struct file *f, const char *buf, size_t cnt, loff_t *pos) +//static int nat_pt_write_proc(struct file* file, const char* buf, unsigned long cnt, void *data) +{ + char input[64], intf[16], op; + int argc, idx; + int trigger[3], open[3]; + + if (copy_from_user(input, buf, cnt) != 0) + return -EFAULT; + + argc = sscanf(input, "%c %d %d %d %d %d %d %s\n", &op, (int *)&trigger[0], (int *)&trigger[1], (int *)&trigger[2], (int *)&open[0], (int *)&open[1], (int *)&open[2], intf); + if( argc < 7) { + printk("nf_nat_pt: wrong number of argument !\n"); + printk("Format : echo op trigger_protocol trigger_start_port trigger_end_port open_protocol open_start_port open_end_port [interface_name] > /proc/net/nf_nat_pt\n"); + printk(" --op a(add), d(del) --protocol TCP=1, UDP=2, Both=3\n"); + return cnt; + } + + if((op != 'a') && (op != 'd')) { + printk("nf_nat_pt: Unknown op=%c --op a(add) or d(del)\n", op); + return cnt; + } + + if((trigger[0] > PT_PROTO_ALL) || trigger[0] <= 0) { + printk("nf_nat_pt: Unknown protocol : TCP=1, UDP=2, Both=3\n"); + return cnt; + } + + if((trigger[1] > trigger[2]) || (open[1] > open[2]) ) { + printk("nf_nat_pt: Error (end-start) < 0 \n"); + return cnt; + } + + // add entry + if(op == 'a') { + for(idx = 0; idx < max_idx_used; idx++) { + // find available entry index + if(outport[idx*3] == 0) { + break; + } + } + + if(idx >= PT_MAX_ENTRIES) { + printk("nf_nat_pt: index = %d, should be 0~99 !\n", idx); + return cnt; + } + + // sum of the outports of all configuration entries <= PT_MAX_PORTS + if ((total_outport_cnt + (trigger[2] - trigger[1] + 1)) > PT_MAX_PORTS) { + printk("nf_nat_pt: outport range is greater than maximum number total %d. Total inport used = %d.\n", + PT_MAX_PORTS, total_outport_cnt); + return cnt; + } + + // sum of the inports <= PT_MAX_EXPECTED + if(total_inport_cnt + (open[2] - open[1] + 1) > PT_MAX_EXPECTED) + { + printk("nf_nat_pt: inport range is greater than maximum number total %d. Total inport used = %d.\n", + PT_MAX_EXPECTED, total_inport_cnt); + return cnt; + } + total_inport_cnt += (open[2] - open[1] + 1); + + outport[idx*3] = trigger[0]; + outport[idx*3+1] = trigger[1]; + outport[idx*3+2] = trigger[2]; + inport[idx*3] = open[0]; + inport[idx*3+1] = open[1]; + inport[idx*3+2] = open[2]; + + if(argc == 8) { + if ((iface[idx] = kzalloc(sizeof(intf), GFP_KERNEL)) == NULL) { + printk("nf_nat_pt: allocate ifname buf error\n"); + return -ENOMEM; + } + strcpy(iface[idx], intf); + } else { + iface[idx] = NULL; + } + + nat_pt_helper_register(idx); + + if(idx == max_idx_used) + max_idx_used++; + + pr_debug("nf_nat_pt: op=%c idx=%d trigger=%4d %4d %4d open=%4d %4d %4d %s\n", + op, idx, trigger[0], trigger[1], trigger[2], open[0], open[1], open[2], iface[idx]); + + } else if(op =='d') { + // remove matched entry + for(idx=0; idx < max_idx_used; idx++) { + + if((trigger[0] == outport[idx*3]) && (trigger[1] == outport[idx*3+1]) && (trigger[2] == outport[idx*3+2]) && + (open[0] == inport[idx*3]) && (open[1] == inport[idx*3+1]) && (open[2] == inport[idx*3+2])) { + + pr_debug("nf_nat_pt: delete index=%d\n", idx); + total_inport_cnt -= (inport[idx*3+2] - inport[idx*3+1] + 1); + + outport[idx*3] = 0; outport[idx*3+1] = 0; outport[idx*3+2] = 0; + inport[idx*3] = 0; inport[idx*3+1] = 0; inport[idx*3+2] = 0; + kfree(iface[idx]); + iface[idx] = NULL; + + nat_pt_helper_unregister(idx); + break; + } + } + if(idx == max_idx_used) { + printk("nf_nat_pt: delete fail - op=%c trigger=%d %d %d open=%d %d %d is not found\n", + op, trigger[0], trigger[1], trigger[2], open[0], open[1], open[2]); + } + else if(idx == (max_idx_used-1)) { + max_idx_used--; + while(max_idx_used > 0) { + if(outport[(max_idx_used-1)*3] != 0) + break; + max_idx_used--; + } + } + } + return cnt; +} + +static struct file_operations proc_fops = { + .owner = THIS_MODULE, + .read = nat_pt_read_proc, + .write = nat_pt_write_proc, +}; + +/* register the proc file */ +static void nat_pt_init_proc(void) +{ + struct proc_dir_entry *p; + + p = proc_create("nf_nat_pt", 0644, init_net.proc_net, &proc_fops); + if (p == NULL) + printk("nf_nat_pt: create proc - nf_nat_ptfail !\n"); + + return; +} +static void nat_pt_cleanup_proc(void) +{ + remove_proc_entry("nf_nat_pt", init_net.proc_net); +} + + +static void fini(void) +{ + int i; + + for(i=0; i < max_idx_used; i++) { + nat_pt_helper_unregister(i); + } + nat_pt_cleanup_proc(); +} + +static int __init init(void) +{ + /* init proc file */ + nat_pt_init_proc(); + total_inport_cnt = 0; + total_outport_cnt = 0; + return 0; +} + +MODULE_AUTHOR("Eddie Shi "); +MODULE_DESCRIPTION("Netfilter Conntrack helper for PT"); +MODULE_LICENSE("GPL"); + +module_init(init); +module_exit(fini); +#endif diff -urN linux-4.1.52-orig/net/ipv4/netfilter/nf_nat_rtsp.c linux-4.1.52/net/ipv4/netfilter/nf_nat_rtsp.c --- linux-4.1.52-orig/net/ipv4/netfilter/nf_nat_rtsp.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/ipv4/netfilter/nf_nat_rtsp.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,398 @@ +#if defined(CONFIG_BCM_KF_NETFILTER) +/* + * RTSP extension for NAT alteration. + * + * Copyright (c) 2008 Broadcom Corporation. + * + * <:label-BRCM:2011:DUAL/GPL:standard + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed + * to you under the terms of the GNU General Public License version 2 + * (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, + * with the following added to such license: + * + * As a special exception, the copyright holders of this software give + * you permission to link this software with independent modules, and + * to copy and distribute the resulting executable under terms of your + * choice, provided that you also meet, for each linked independent + * module, the terms and conditions of the license of that module. + * An independent module is a module which is not derived from this + * software. The special exception does not apply to any modifications + * of the software. + * + * Not withstanding the above, under no circumstances may you combine + * this software in any way with any other Broadcom software provided + * under a license other than the GPL, without Broadcom's express prior + * written consent. + * + * :> + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static inline int rtsp_mangle_tcp_packet(struct sk_buff *skb, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int protoff, + unsigned int match_offset, + unsigned int match_len, + const char *rep_buffer, + unsigned int rep_len) +{ + return __nf_nat_mangle_tcp_packet(skb, ct, ctinfo, protoff, + match_offset, match_len, + rep_buffer, rep_len, false); +} + +/****************************************************************************/ +static int modify_ports(struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + int matchoff, int matchlen, + u_int16_t rtpport, u_int16_t rtcpport, + char dash, int *delta) +{ + char buf[sizeof("65535-65535")]; + int len; + + if (dash) + len = sprintf(buf, "%hu%c%hu", rtpport, dash, rtcpport); + else + len = sprintf(buf, "%hu", rtpport); + if (!rtsp_mangle_tcp_packet(skb, ct, ctinfo, protoff, matchoff, matchlen, + buf, len)) { + if (net_ratelimit()) + printk("nf_nat_rtsp: rtsp_mangle_tcp_packet error\n"); + return -1; + } + *delta = len - matchlen; + return 0; +} + +/* Setup NAT on this expected conntrack so it follows master but expect the src ip. */ +/* If we fail to get a free NAT slot, we'll get dropped on confirm */ +static void nf_nat_follow_master_nosrc(struct nf_conn *ct, + struct nf_conntrack_expect *exp) +{ + struct nf_nat_range range; + + /* This must be a fresh one. */ + BUG_ON(ct->status & IPS_NAT_DONE_MASK); + + /* For DST manip, map port here to where it's expected. */ + range.flags = (NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED); + range.min_proto = range.max_proto = exp->saved_proto; + range.min_addr = range.max_addr + = ct->master->tuplehash[!exp->dir].tuple.src.u3; + nf_nat_setup_info(ct, &range, NF_NAT_MANIP_DST); +} + +/****************************************************************************/ +/* One data channel */ +static int nat_rtsp_channel (struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int matchoff, unsigned int matchlen, + struct nf_conntrack_expect *rtp_exp, int *delta) +{ + struct nf_conn_help *help = nfct_help(ct); + struct nf_conntrack_expect *exp; + int dir = CTINFO2DIR(ctinfo); + u_int16_t nated_port = 0; + int exp_exist = 0; + + /* Set expectations for NAT */ + rtp_exp->saved_proto.udp.port = rtp_exp->tuple.dst.u.udp.port; + rtp_exp->expectfn = nf_nat_follow_master_nosrc; + rtp_exp->dir = !dir; + + /* Lookup existing expects */ + spin_lock_bh(&nf_conntrack_expect_lock); + hlist_for_each_entry(exp, &help->expectations, lnode) { + if (exp->saved_proto.udp.port == rtp_exp->saved_proto.udp.port){ + /* Expectation already exists */ + rtp_exp->tuple.dst.u.udp.port = + exp->tuple.dst.u.udp.port; + nated_port = ntohs(exp->tuple.dst.u.udp.port); + exp_exist = 1; + break; + } + } + spin_unlock_bh(&nf_conntrack_expect_lock); + + if (exp_exist) { + nf_ct_expect_related(rtp_exp); + goto modify_message; + } + + /* Try to get a port. */ + for (nated_port = ntohs(rtp_exp->tuple.dst.u.udp.port); + nated_port != 0; nated_port++) { + rtp_exp->tuple.dst.u.udp.port = htons(nated_port); + if (nf_ct_expect_related(rtp_exp) == 0) + break; + } + + if (nated_port == 0) { /* No port available */ + if (net_ratelimit()) + printk("nf_nat_rtsp: out of UDP ports\n"); + return 0; + } + +modify_message: + /* Modify message */ + if (modify_ports(skb, protoff, ct, ctinfo, matchoff, matchlen, + nated_port, 0, 0, delta) < 0) { + nf_ct_unexpect_related(rtp_exp); + return -1; + } + + /* Success */ + pr_debug("nf_nat_rtsp: expect RTP "); + nf_ct_dump_tuple(&rtp_exp->tuple); + + return 0; +} + +/****************************************************************************/ +/* A pair of data channels (RTP/RTCP) */ +static int nat_rtsp_channel2 (struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int matchoff, unsigned int matchlen, + struct nf_conntrack_expect *rtp_exp, + struct nf_conntrack_expect *rtcp_exp, + char dash, int *delta) +{ + struct nf_conn_help *help = nfct_help(ct); + struct nf_conntrack_expect *exp; + int dir = CTINFO2DIR(ctinfo); + u_int16_t nated_port = 0; + int exp_exist = 0; + + /* Set expectations for NAT */ + rtp_exp->saved_proto.udp.port = rtp_exp->tuple.dst.u.udp.port; + rtp_exp->expectfn = nf_nat_follow_master_nosrc; + rtp_exp->dir = !dir; + rtcp_exp->saved_proto.udp.port = rtcp_exp->tuple.dst.u.udp.port; + rtcp_exp->expectfn = nf_nat_follow_master_nosrc; + rtcp_exp->dir = !dir; + + /* Lookup existing expects */ + spin_lock_bh(&nf_conntrack_expect_lock); + hlist_for_each_entry(exp, &help->expectations, lnode) { + if (exp->saved_proto.udp.port == rtp_exp->saved_proto.udp.port){ + /* Expectation already exists */ + rtp_exp->tuple.dst.u.udp.port = + exp->tuple.dst.u.udp.port; + rtcp_exp->tuple.dst.u.udp.port = + htons(ntohs(exp->tuple.dst.u.udp.port) + 1); + nated_port = ntohs(exp->tuple.dst.u.udp.port); + exp_exist = 1; + break; + } + } + spin_unlock_bh(&nf_conntrack_expect_lock); + + if (exp_exist) { + nf_ct_expect_related(rtp_exp); + nf_ct_expect_related(rtcp_exp); + goto modify_message; + } + + /* Try to get a pair of ports. */ + for (nated_port = ntohs(rtp_exp->tuple.dst.u.udp.port) & (~1); + nated_port != 0; nated_port += 2) { + rtp_exp->tuple.dst.u.udp.port = htons(nated_port); + if (nf_ct_expect_related(rtp_exp) == 0) { + rtcp_exp->tuple.dst.u.udp.port = + htons(nated_port + 1); + if (nf_ct_expect_related(rtcp_exp) == 0) + break; + nf_ct_unexpect_related(rtp_exp); + } + } + + if (nated_port == 0) { /* No port available */ + if (net_ratelimit()) + printk("nf_nat_rtsp: out of RTP/RTCP ports\n"); + return 0; + } + +modify_message: + /* Modify message */ + if (modify_ports(skb, protoff, ct, ctinfo, matchoff, matchlen, + nated_port, nated_port + 1, dash, delta) < 0) { + nf_ct_unexpect_related(rtp_exp); + nf_ct_unexpect_related(rtcp_exp); + return -1; + } + + /* Success */ + pr_debug("nf_nat_rtsp: expect RTP "); + nf_ct_dump_tuple(&rtp_exp->tuple); + pr_debug("nf_nat_rtsp: expect RTCP "); + nf_ct_dump_tuple(&rtcp_exp->tuple); + + return 0; +} + +/****************************************************************************/ +static __be16 lookup_mapping_port(struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + __be16 port) +{ + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + struct nf_conn_help *help = nfct_help(ct); + struct nf_conntrack_expect *exp; + struct nf_conn *child; + __be16 proto_all; + + spin_lock_bh(&nf_conntrack_expect_lock); + /* Lookup existing expects */ + pr_debug("nf_nat_rtsp: looking up existing expectations...\n"); + hlist_for_each_entry(exp, &help->expectations, lnode) { + if (exp->tuple.dst.u.udp.port == port) { + pr_debug("nf_nat_rtsp: found port %hu mapped from " + "%hu\n", + ntohs(exp->tuple.dst.u.udp.port), + ntohs(exp->saved_proto.all)); + + proto_all = exp->saved_proto.all; + spin_unlock_bh(&nf_conntrack_expect_lock); + return proto_all; + } + } + spin_unlock_bh(&nf_conntrack_expect_lock); + + /* Lookup existing connections */ + pr_debug("nf_nat_rtsp: looking up existing connections...\n"); + list_for_each_entry(child, &ct->derived_connections, derived_list) { + if (child->tuplehash[dir].tuple.dst.u.udp.port == port) { + pr_debug("nf_nat_rtsp: found port %hu mapped from " + "%hu\n", + ntohs(child->tuplehash[dir]. + tuple.dst.u.udp.port), + ntohs(child->tuplehash[!dir]. + tuple.src.u.udp.port)); + return child->tuplehash[!dir].tuple.src.u.udp.port; + } + } + + return htons(0); +} + +/****************************************************************************/ +static int nat_rtsp_modify_port (struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int matchoff, unsigned int matchlen, + __be16 rtpport, int *delta) +{ + __be16 orig_port; + + orig_port = lookup_mapping_port(ct, ctinfo, rtpport); + if (orig_port == htons(0)) { + *delta = 0; + return 0; + } + if (modify_ports(skb, protoff, ct, ctinfo, matchoff, matchlen, + ntohs(orig_port), 0, 0, delta) < 0) + return -1; + pr_debug("nf_nat_rtsp: Modified client_port from %hu to %hu\n", + ntohs(rtpport), ntohs(orig_port)); + return 0; +} + +/****************************************************************************/ +static int nat_rtsp_modify_port2 (struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int matchoff, unsigned int matchlen, + __be16 rtpport, __be16 rtcpport, + char dash, int *delta) +{ + __be16 orig_port; + + orig_port = lookup_mapping_port(ct, ctinfo, rtpport); + if (orig_port == htons(0)) { + *delta = 0; + return 0; + } + if (modify_ports(skb, protoff, ct, ctinfo, matchoff, matchlen, + ntohs(orig_port), ntohs(orig_port)+1, dash, delta) < 0) + return -1; + pr_debug("nf_nat_rtsp: Modified client_port from %hu to %hu\n", + ntohs(rtpport), ntohs(orig_port)); + return 0; +} + +/****************************************************************************/ +static int nat_rtsp_modify_addr(struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + int matchoff, int matchlen, int *delta) +{ + char buf[sizeof("255.255.255.255")]; + int dir = CTINFO2DIR(ctinfo); + int len; + + /* Change the destination address to FW's WAN IP address */ + + len = sprintf(buf, "%pI4", + &ct->tuplehash[!dir].tuple.dst.u3.ip); + if (!rtsp_mangle_tcp_packet(skb, ct, ctinfo, protoff, matchoff, matchlen, + buf, len)) { + if (net_ratelimit()) + printk("nf_nat_rtsp: rtsp_mangle_tcp_packet error\n"); + return -1; + } + *delta = len - matchlen; + return 0; +} + +/****************************************************************************/ +static int __init init(void) +{ + BUG_ON(rcu_dereference(nat_rtsp_channel_hook) != NULL); + BUG_ON(rcu_dereference(nat_rtsp_channel2_hook) != NULL); + BUG_ON(rcu_dereference(nat_rtsp_modify_port_hook) != NULL); + BUG_ON(rcu_dereference(nat_rtsp_modify_port2_hook) != NULL); + BUG_ON(rcu_dereference(nat_rtsp_modify_addr_hook) != NULL); + rcu_assign_pointer(nat_rtsp_channel_hook, nat_rtsp_channel); + rcu_assign_pointer(nat_rtsp_channel2_hook, nat_rtsp_channel2); + rcu_assign_pointer(nat_rtsp_modify_port_hook, nat_rtsp_modify_port); + rcu_assign_pointer(nat_rtsp_modify_port2_hook, nat_rtsp_modify_port2); + rcu_assign_pointer(nat_rtsp_modify_addr_hook, nat_rtsp_modify_addr); + + pr_debug("nf_nat_rtsp: init success\n"); + return 0; +} + +/****************************************************************************/ +static void __exit fini(void) +{ + rcu_assign_pointer(nat_rtsp_channel_hook, NULL); + rcu_assign_pointer(nat_rtsp_channel2_hook, NULL); + rcu_assign_pointer(nat_rtsp_modify_port_hook, NULL); + rcu_assign_pointer(nat_rtsp_modify_port2_hook, NULL); + rcu_assign_pointer(nat_rtsp_modify_addr_hook, NULL); + synchronize_rcu(); +} + +/****************************************************************************/ +module_init(init); +module_exit(fini); + +MODULE_AUTHOR("Broadcom Corporation"); +MODULE_DESCRIPTION("RTSP NAT helper"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("ip_nat_rtsp"); + +#endif // defined(CONFIG_BCM_KF_NETFILTER) diff -urN linux-4.1.52-orig/net/ipv4/proc.c linux-4.1.52/net/ipv4/proc.c --- linux-4.1.52-orig/net/ipv4/proc.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/proc.c 2020-02-07 01:47:54.000000000 +0800 @@ -298,6 +298,10 @@ SNMP_MIB_ITEM("TCPACKSkippedFinWait2", LINUX_MIB_TCPACKSKIPPEDFINWAIT2), SNMP_MIB_ITEM("TCPACKSkippedTimeWait", LINUX_MIB_TCPACKSKIPPEDTIMEWAIT), SNMP_MIB_ITEM("TCPACKSkippedChallenge", LINUX_MIB_TCPACKSKIPPEDCHALLENGE), +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +/*CVE-2019-11478*/ + SNMP_MIB_ITEM("TCPWqueueTooBig", LINUX_MIB_TCPWQUEUETOOBIG), +#endif SNMP_MIB_SENTINEL }; diff -urN linux-4.1.52-orig/net/ipv4/route.c linux-4.1.52/net/ipv4/route.c --- linux-4.1.52-orig/net/ipv4/route.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/route.c 2020-02-07 01:47:54.000000000 +0800 @@ -1471,7 +1471,14 @@ goto e_inval; if (ipv4_is_zeronet(saddr)) { +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + /* Backporting latest kernel fix(1d2f4ebbbeb1ec055dcd3cf3dba833cfd0a84f3a) + to allow IGMP packets with zero source IP */ + if (!ipv4_is_local_multicast(daddr) + && ip_hdr(skb)->protocol != IPPROTO_IGMP) +#else if (!ipv4_is_local_multicast(daddr)) +#endif goto e_inval; } else { err = fib_validate_source(skb, saddr, 0, tos, 0, dev, @@ -2440,9 +2447,15 @@ #ifdef CONFIG_IP_MROUTE if (ipv4_is_multicast(dst) && !ipv4_is_local_multicast(dst) && IPV4_DEVCONF_ALL(net, MC_FORWARDING)) { +#if defined(CONFIG_BCM_KF_MROUTE) + int err = ipmr_get_route(net, skb, + fl4->saddr, fl4->daddr, + r, nowait, rt->rt_iif); +#else int err = ipmr_get_route(net, skb, fl4->saddr, fl4->daddr, r, nowait); +#endif if (err <= 0) { if (!nowait) { if (err == 0) diff -urN linux-4.1.52-orig/net/ipv4/syncookies.c linux-4.1.52/net/ipv4/syncookies.c --- linux-4.1.52-orig/net/ipv4/syncookies.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/syncookies.c 2020-02-07 01:47:54.000000000 +0800 @@ -16,6 +16,10 @@ #include #include #include +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#include +#include +#endif #include #include @@ -192,8 +196,13 @@ } EXPORT_SYMBOL_GPL(__cookie_v4_init_sequence); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) __u32 cookie_v4_init_sequence(struct sock *sk, const struct sk_buff *skb, __u16 *mssp) +#else +__u32 cookie_v4_init_sequence(struct request_sock *req, struct sock *sk, + const struct sk_buff *skb, __u16 *mssp) +#endif { const struct iphdr *iph = ip_hdr(skb); const struct tcphdr *th = tcp_hdr(skb); @@ -225,8 +234,30 @@ { struct inet_connection_sock *icsk = inet_csk(sk); struct sock *child; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#ifdef CONFIG_MPTCP + int ret; +#endif +#endif child = icsk->icsk_af_ops->syn_recv_sock(sk, skb, req, dst); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + +#ifdef CONFIG_MPTCP + if (!child) + goto listen_overflow; + + ret = mptcp_check_req_master(sk, child, req, 0); + if (ret < 0) + return NULL; + + if (!ret) + return tcp_sk(child)->mpcb->master_sk; + +listen_overflow: +#endif + +#endif if (child) { atomic_set(&req->rsk_refcnt, 1); inet_csk_reqsk_queue_add(sk, req, child); @@ -292,6 +323,9 @@ { struct ip_options *opt = &TCP_SKB_CB(skb)->header.h4.opt; struct tcp_options_received tcp_opt; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + struct mptcp_options_received mopt; +#endif struct inet_request_sock *ireq; struct tcp_request_sock *treq; struct tcp_sock *tp = tcp_sk(sk); @@ -320,13 +354,27 @@ /* check for timestamp cookie support */ memset(&tcp_opt, 0, sizeof(tcp_opt)); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_parse_options(skb, &tcp_opt, 0, NULL); +#else + mptcp_init_mp_opt(&mopt); + tcp_parse_options(skb, &tcp_opt, &mopt, 0, NULL, NULL); +#endif if (!cookie_timestamp_decode(&tcp_opt)) goto out; ret = NULL; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) req = inet_reqsk_alloc(&tcp_request_sock_ops, sk); /* for safety */ +#else +#ifdef CONFIG_MPTCP + if (mopt.saw_mpc) + req = inet_reqsk_alloc(&mptcp_request_sock_ops, sk); /* for safety */ + else +#endif + req = inet_reqsk_alloc(&tcp_request_sock_ops, sk); /* for safety */ +#endif if (!req) goto out; @@ -344,12 +392,21 @@ ireq->sack_ok = tcp_opt.sack_ok; ireq->wscale_ok = tcp_opt.wscale_ok; ireq->tstamp_ok = tcp_opt.saw_tstamp; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + ireq->mptcp_rqsk = 0; + ireq->saw_mpc = 0; +#endif req->ts_recent = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsval : 0; treq->snt_synack = tcp_opt.saw_tstamp ? tcp_opt.rcv_tsecr : 0; treq->tfo_listener = false; ireq->ir_iif = sk->sk_bound_dev_if; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (mopt.saw_mpc) + mptcp_cookies_reqsk_init(req, &mopt, skb); + +#endif /* We throwed the options of the initial SYN away, so we hope * the ACK carries the same options again (see RFC1122 4.2.3.8) */ @@ -383,10 +440,17 @@ /* Try to redo what tcp_v4_send_synack did. */ req->window_clamp = tp->window_clamp ? :dst_metric(&rt->dst, RTAX_WINDOW); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_select_initial_window(tcp_full_space(sk), req->mss, &req->rcv_wnd, &req->window_clamp, ireq->wscale_ok, &rcv_wscale, dst_metric(&rt->dst, RTAX_INITRWND)); +#else + tp->ops->select_initial_window(tcp_full_space(sk), req->mss, + &req->rcv_wnd, &req->window_clamp, + ireq->wscale_ok, &rcv_wscale, + dst_metric(&rt->dst, RTAX_INITRWND), sk); +#endif ireq->rcv_wscale = rcv_wscale; ireq->ecn_ok = cookie_ecn_ok(&tcp_opt, sock_net(sk), &rt->dst); diff -urN linux-4.1.52-orig/net/ipv4/sysctl_net_ipv4.c linux-4.1.52/net/ipv4/sysctl_net_ipv4.c --- linux-4.1.52-orig/net/ipv4/sysctl_net_ipv4.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/sysctl_net_ipv4.c 2020-02-07 01:47:54.000000000 +0800 @@ -35,6 +35,13 @@ static int ip_local_port_range_max[] = { 65535, 65535 }; static int tcp_adv_win_scale_min = -31; static int tcp_adv_win_scale_max = 31; + +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +/*CVE-2019-11479*/ +static int tcp_min_snd_mss_min = TCP_MIN_SND_MSS; +static int tcp_min_snd_mss_max = 65535; +#endif + static int ip_ttl_min = 1; static int ip_ttl_max = 255; static int tcp_syn_retries_min = 1; @@ -883,6 +890,18 @@ .mode = 0644, .proc_handler = proc_dointvec, }, +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +/*CVE-2019-11479*/ + { + .procname = "tcp_min_snd_mss", + .data = &init_net.ipv4.sysctl_tcp_min_snd_mss, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &tcp_min_snd_mss_min, + .extra2 = &tcp_min_snd_mss_max, + }, +#endif { .procname = "tcp_probe_threshold", .data = &init_net.ipv4.sysctl_tcp_probe_threshold, diff -urN linux-4.1.52-orig/net/ipv4/tcp.c linux-4.1.52/net/ipv4/tcp.c --- linux-4.1.52-orig/net/ipv4/tcp.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/tcp.c 2020-02-07 01:47:54.000000000 +0800 @@ -272,6 +272,9 @@ #include #include +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#include +#endif #include #include #include @@ -372,6 +375,27 @@ return period; } +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +const struct tcp_sock_ops tcp_specific = { + .__select_window = __tcp_select_window, + .select_window = tcp_select_window, + .select_initial_window = tcp_select_initial_window, + .select_size = select_size, + .init_buffer_space = tcp_init_buffer_space, + .set_rto = tcp_set_rto, + .should_expand_sndbuf = tcp_should_expand_sndbuf, + .send_fin = tcp_send_fin, + .write_xmit = tcp_write_xmit, + .send_active_reset = tcp_send_active_reset, + .write_wakeup = tcp_write_wakeup, + .prune_ofo_queue = tcp_prune_ofo_queue, + .retransmit_timer = tcp_retransmit_timer, + .time_wait = tcp_time_wait, + .cleanup_rbuf = tcp_cleanup_rbuf, + .cwnd_validate = tcp_cwnd_validate, +}; + +#endif /* Address-family independent initialization for a tcp_sock. * * NOTE: A lot of things set to zero explicitly by call to @@ -385,7 +409,9 @@ __skb_queue_head_init(&tp->out_of_order_queue); tcp_init_xmit_timers(sk); tcp_prequeue_init(tp); +#if !defined(CONFIG_BCM_KF_TCP_NO_TSQ) INIT_LIST_HEAD(&tp->tsq_node); +#endif icsk->icsk_rto = TCP_TIMEOUT_INIT; tp->mdev_us = jiffies_to_usecs(TCP_TIMEOUT_INIT); @@ -421,6 +447,13 @@ sk->sk_sndbuf = sysctl_tcp_wmem[1]; sk->sk_rcvbuf = sysctl_tcp_rmem[1]; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + tp->ops = &tcp_specific; + + /* Initialize MPTCP-specific stuff and function-pointers */ + mptcp_init_tcp_sock(sk); + +#endif local_bh_disable(); sock_update_memcg(sk); sk_sockets_allocated_inc(sk); @@ -646,6 +679,23 @@ * autocorking if we only have an ACK in Qdisc/NIC queues, * or if TX completion was delayed after we processed ACK packet. */ +#if defined(CONFIG_BCM_KF_TCP_NO_TSQ) +static void tcp_push(struct sock *sk, int flags, int mss_now, + int nonagle, int size_goal) +{ + if (tcp_send_head(sk)) { + struct tcp_sock *tp = tcp_sk(sk); + + if (!(flags & MSG_MORE) || forced_push(tp)) + tcp_mark_push(tp, tcp_write_queue_tail(sk)); + + tcp_mark_urg(tp, flags); + __tcp_push_pending_frames(sk, mss_now, + (flags & MSG_MORE) ? TCP_NAGLE_CORK : nonagle); + } +} + +#else static bool tcp_should_autocork(struct sock *sk, struct sk_buff *skb, int size_goal) { @@ -689,6 +739,7 @@ __tcp_push_pending_frames(sk, mss_now, nonagle); } +#endif static int tcp_splice_data_recv(read_descriptor_t *rd_desc, struct sk_buff *skb, unsigned int offset, size_t len) @@ -741,6 +792,16 @@ int ret; sock_rps_record_flow(sk); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + +#ifdef CONFIG_MPTCP + if (mptcp(tcp_sk(sk))) { + struct sock *sk_it; + mptcp_for_each_sk(tcp_sk(sk)->mpcb, sk_it) + sock_rps_record_flow(sk_it); + } +#endif +#endif /* * We can't seek on a socket input */ @@ -780,6 +841,15 @@ ret = -EAGAIN; break; } +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +/* CVE-2017-6214*/ + /* if __tcp_splice_read() got nothing while we have + * an skb in receive queue, we do not want to loop. + * This might happen with URG data. + */ + if (!skb_queue_empty(&sk->sk_receive_queue)) + break; +#endif sk_wait_data(sk, &timeo); if (signal_pending(current)) { ret = sock_intr_errno(timeo); @@ -836,8 +906,12 @@ return NULL; } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static unsigned int tcp_xmit_size_goal(struct sock *sk, u32 mss_now, int large_allowed) +#else +unsigned int tcp_xmit_size_goal(struct sock *sk, u32 mss_now, int large_allowed) +#endif { struct tcp_sock *tp = tcp_sk(sk); u32 new_size_goal, size_goal; @@ -865,8 +939,18 @@ { int mss_now; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) mss_now = tcp_current_mss(sk); *size_goal = tcp_xmit_size_goal(sk, mss_now, !(flags & MSG_OOB)); +#else + if (mptcp(tcp_sk(sk))) { + mss_now = mptcp_current_mss(sk); + *size_goal = mptcp_xmit_size_goal(sk, mss_now, !(flags & MSG_OOB)); + } else { + mss_now = tcp_current_mss(sk); + *size_goal = tcp_xmit_size_goal(sk, mss_now, !(flags & MSG_OOB)); + } +#endif return mss_now; } @@ -885,11 +969,38 @@ * is fully established. */ if (((1 << sk->sk_state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) && +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) !tcp_passive_fastopen(sk)) { +#else + !tcp_passive_fastopen(mptcp(tp) && tp->mpcb->master_sk ? + tp->mpcb->master_sk : sk)) { +#endif if ((err = sk_stream_wait_connect(sk, &timeo)) != 0) goto out_err; } +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (mptcp(tp)) { + struct sock *sk_it = sk; + + /* We must check this with socket-lock hold because we iterate + * over the subflows. + */ + if (!mptcp_can_sendpage(sk)) { + ssize_t ret; + + release_sock(sk); + ret = sock_no_sendpage(sk->sk_socket, page, offset, + size, flags); + lock_sock(sk); + return ret; + } + + mptcp_for_each_sk(tp->mpcb, sk_it) + sock_rps_record_flow(sk_it); + } + +#endif clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags); mss_now = tcp_send_mss(sk, &size_goal, flags); @@ -996,8 +1107,14 @@ { ssize_t res; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (!(sk->sk_route_caps & NETIF_F_SG) || !(sk->sk_route_caps & NETIF_F_ALL_CSUM)) +#else + /* If MPTCP is enabled, we check it later after establishment */ + if (!mptcp(tcp_sk(sk)) && (!(sk->sk_route_caps & NETIF_F_SG) || + !(sk->sk_route_caps & NETIF_F_ALL_CSUM))) +#endif return sock_no_sendpage(sk->sk_socket, page, offset, size, flags); @@ -1008,7 +1125,11 @@ } EXPORT_SYMBOL(tcp_sendpage); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static inline int select_size(const struct sock *sk, bool sg) +#else +int select_size(const struct sock *sk, bool sg) +#endif { const struct tcp_sock *tp = tcp_sk(sk); int tmp = tp->mss_cache; @@ -1095,11 +1216,24 @@ * is fully established. */ if (((1 << sk->sk_state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) && +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) !tcp_passive_fastopen(sk)) { +#else + !tcp_passive_fastopen(mptcp(tp) && tp->mpcb->master_sk ? + tp->mpcb->master_sk : sk)) { +#endif if ((err = sk_stream_wait_connect(sk, &timeo)) != 0) goto do_error; } +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (mptcp(tp)) { + struct sock *sk_it = sk; + mptcp_for_each_sk(tp->mpcb, sk_it) + sock_rps_record_flow(sk_it); + } + +#endif if (unlikely(tp->repair)) { if (tp->repair_queue == TCP_RECV_QUEUE) { copied = tcp_send_rcvq(sk, msg, size); @@ -1125,7 +1259,14 @@ if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN)) goto out_err; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) sg = !!(sk->sk_route_caps & NETIF_F_SG); +#else + if (mptcp(tp)) + sg = mptcp_can_sg(sk); + else + sg = !!(sk->sk_route_caps & NETIF_F_SG); +#endif while (msg_data_left(msg)) { int copy = 0; @@ -1147,15 +1288,33 @@ goto wait_for_sndbuf; skb = sk_stream_alloc_skb(sk, +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) select_size(sk, sg), +#else + tp->ops->select_size(sk, sg), +#endif sk->sk_allocation); if (!skb) goto wait_for_memory; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) /* * Check whether we can use HW checksum. */ if (sk->sk_route_caps & NETIF_F_ALL_CSUM) +#else + /* + * Check whether we can use HW checksum. + * + * If dss-csum is enabled, we do not do hw-csum. + * In case of non-mptcp we check the + * device-capabilities. + * In case of mptcp, hw-csum's will be handled + * later in mptcp_write_xmit. + */ + if (((mptcp(tp) && !tp->mpcb->dss_csum) || !mptcp(tp)) && + (mptcp(tp) || sk->sk_route_caps & NETIF_F_ALL_CSUM)) +#endif skb->ip_summed = CHECKSUM_PARTIAL; skb_entail(sk, skb); @@ -1178,9 +1337,19 @@ if (skb_availroom(skb) > 0) { /* We have some space in skb head. Superb! */ copy = min_t(int, copy, skb_availroom(skb)); - err = skb_add_data_nocache(sk, skb, &msg->msg_iter, copy); - if (err) - goto do_fault; +#if defined(CONFIG_BCM_KF_SPEEDYGET) && defined(CONFIG_BCM_SPEEDYGET) + if( unlikely(!tp->tcp_nocopy) ) { +#endif + err = skb_add_data_nocache(sk, skb, &msg->msg_iter, copy); + if (err) + goto do_fault; +#if defined(CONFIG_BCM_KF_SPEEDYGET) && defined(CONFIG_BCM_SPEEDYGET) + } else { + skb->csum=0; + memset(skb_put(skb,copy),0,copy); + iov_iter_advance(&msg->msg_iter, copy); + } +#endif } else { bool merge = true; int i = skb_shinfo(skb)->nr_frags; @@ -1357,7 +1526,11 @@ * calculation of whether or not we must ACK for the sake of * a window update. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static void tcp_cleanup_rbuf(struct sock *sk, int copied) +#else +void tcp_cleanup_rbuf(struct sock *sk, int copied) +#endif { struct tcp_sock *tp = tcp_sk(sk); bool time_to_ack = false; @@ -1400,7 +1573,11 @@ /* Optimize, __tcp_select_window() is not cheap. */ if (2*rcv_window_now <= tp->window_clamp) { +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) __u32 new_window = __tcp_select_window(sk); +#else + __u32 new_window = tp->ops->__select_window(sk); +#endif /* Send ACK now, if this read freed lots of space * in our buffer. Certainly, new_window is new window. @@ -1532,7 +1709,11 @@ /* Clean up data we have read: This will do ACK frames. */ if (copied > 0) { tcp_recv_skb(sk, seq, &offset); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_cleanup_rbuf(sk, copied); +#else + tp->ops->cleanup_rbuf(sk, copied); +#endif } return copied; } @@ -1570,6 +1751,16 @@ lock_sock(sk); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#ifdef CONFIG_MPTCP + if (mptcp(tp)) { + struct sock *sk_it; + mptcp_for_each_sk(tp->mpcb, sk_it) + sock_rps_record_flow(sk_it); + } +#endif + +#endif err = -ENOTCONN; if (sk->sk_state == TCP_LISTEN) goto out; @@ -1686,7 +1877,11 @@ } } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_cleanup_rbuf(sk, copied); +#else + tp->ops->cleanup_rbuf(sk, copied); +#endif if (!sysctl_tcp_low_latency && tp->ucopy.task == user_recv) { /* Install new reader */ @@ -1796,16 +1991,22 @@ } } - if (!(flags & MSG_TRUNC)) { - err = skb_copy_datagram_msg(skb, offset, msg, used); - if (err) { - /* Exception. Bailout! */ - if (!copied) - copied = -EFAULT; - break; - } - } - + if (!(flags & MSG_TRUNC)) { +#if defined(CONFIG_BCM_KF_SPEEDYGET) && defined(CONFIG_BCM_SPEEDYGET) + if(unlikely(!tp->tcp_nocopy)) + { +#endif + err = skb_copy_datagram_msg(skb, offset, msg, used); + if (err) { + /* Exception. Bailout! */ + if (!copied) + copied = -EFAULT; + break; + } +#if defined(CONFIG_BCM_KF_SPEEDYGET) && defined(CONFIG_BCM_SPEEDYGET) + } +#endif + } *seq += used; copied += used; len -= used; @@ -1858,7 +2059,11 @@ */ /* Clean up data we have read: This will do ACK frames. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_cleanup_rbuf(sk, copied); +#else + tp->ops->cleanup_rbuf(sk, copied); +#endif release_sock(sk); return copied; @@ -1936,7 +2141,11 @@ [TCP_NEW_SYN_RECV] = TCP_CLOSE, /* should not happen ! */ }; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static int tcp_close_state(struct sock *sk) +#else +int tcp_close_state(struct sock *sk) +#endif { int next = (int)new_state[sk->sk_state]; int ns = next & TCP_STATE_MASK; @@ -1966,7 +2175,11 @@ TCPF_SYN_RECV | TCPF_CLOSE_WAIT)) { /* Clear out any half completed packets. FIN if needed. */ if (tcp_close_state(sk)) +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_send_fin(sk); +#else + tcp_sk(sk)->ops->send_fin(sk); +#endif } } EXPORT_SYMBOL(tcp_shutdown); @@ -1991,6 +2204,13 @@ int data_was_unread = 0; int state; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (is_meta_sk(sk)) { + mptcp_close(sk, timeout); + return; + } + +#endif lock_sock(sk); sk->sk_shutdown = SHUTDOWN_MASK; @@ -2035,7 +2255,11 @@ /* Unread data was tossed, zap the connection. */ NET_INC_STATS_USER(sock_net(sk), LINUX_MIB_TCPABORTONCLOSE); tcp_set_state(sk, TCP_CLOSE); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_send_active_reset(sk, sk->sk_allocation); +#else + tcp_sk(sk)->ops->send_active_reset(sk, sk->sk_allocation); +#endif } else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) { /* Check zero linger _after_ checking for unread data. */ sk->sk_prot->disconnect(sk, 0); @@ -2115,7 +2339,11 @@ struct tcp_sock *tp = tcp_sk(sk); if (tp->linger2 < 0) { tcp_set_state(sk, TCP_CLOSE); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_send_active_reset(sk, GFP_ATOMIC); +#else + tp->ops->send_active_reset(sk, GFP_ATOMIC); +#endif NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPABORTONLINGER); } else { @@ -2125,7 +2353,12 @@ inet_csk_reset_keepalive_timer(sk, tmo - TCP_TIMEWAIT_LEN); } else { +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_time_wait(sk, TCP_FIN_WAIT2, tmo); +#else + tcp_sk(sk)->ops->time_wait(sk, TCP_FIN_WAIT2, + tmo); +#endif goto out; } } @@ -2134,7 +2367,11 @@ sk_mem_reclaim(sk); if (tcp_check_oom(sk, 0)) { tcp_set_state(sk, TCP_CLOSE); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_send_active_reset(sk, GFP_ATOMIC); +#else + tcp_sk(sk)->ops->send_active_reset(sk, GFP_ATOMIC); +#endif NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPABORTONMEMORY); } else if (!check_net(sock_net(sk))) { @@ -2162,6 +2399,7 @@ } EXPORT_SYMBOL(tcp_close); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) /* These states need RST on ABORT according to RFC793 */ static inline bool tcp_need_reset(int state) @@ -2171,6 +2409,7 @@ TCPF_FIN_WAIT2 | TCPF_SYN_RECV); } +#endif int tcp_disconnect(struct sock *sk, int flags) { struct inet_sock *inet = inet_sk(sk); @@ -2193,7 +2432,11 @@ /* The last check adjusts for discrepancy of Linux wrt. RFC * states */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_send_active_reset(sk, gfp_any()); +#else + tp->ops->send_active_reset(sk, gfp_any()); +#endif sk->sk_err = ECONNRESET; } else if (old_state == TCP_SYN_SENT) sk->sk_err = ECONNRESET; @@ -2208,6 +2451,15 @@ if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK)) inet_reset_saddr(sk); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (is_meta_sk(sk)) { + mptcp_disconnect(sk); + } else { + if (tp->inside_tk_table) + mptcp_hash_remove_bh(tp); + } + +#endif sk->sk_shutdown = 0; sock_reset_flag(sk, SOCK_DONE); tp->srtt_us = 0; @@ -2352,6 +2604,18 @@ lock_sock(sk); switch (optname) { +#if defined(CONFIG_BCM_KF_SPEEDYGET) && defined(CONFIG_BCM_SPEEDYGET) + case TCP_NOCOPY: + { + if(val < 0 || val > 1) + { + err = -EINVAL; + break; + } + tp->tcp_nocopy = val; + break; + } +#endif case TCP_MAXSEG: /* Values greater than interface MTU won't take effect. However * at the point when this call is done we typically don't yet @@ -2512,6 +2776,14 @@ break; case TCP_DEFER_ACCEPT: +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + /* An established MPTCP-connection (mptcp(tp) only returns true + * if the socket is established) should not use DEFER on new + * subflows. + */ + if (mptcp(tp)) + break; +#endif /* Translate value in seconds to number of retransmits */ icsk->icsk_accept_queue.rskq_defer_accept = secs_to_retrans(val, TCP_TIMEOUT_INIT / HZ, @@ -2539,7 +2811,11 @@ (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT) && inet_csk_ack_scheduled(sk)) { icsk->icsk_ack.pending |= ICSK_ACK_PUSHED; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_cleanup_rbuf(sk, 1); +#else + tp->ops->cleanup_rbuf(sk, 1); +#endif if (!(val & 1)) icsk->icsk_ack.pingpong = 1; } @@ -2584,6 +2860,22 @@ tp->notsent_lowat = val; sk->sk_write_space(sk); break; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#ifdef CONFIG_MPTCP + case MPTCP_ENABLED: + if (mptcp_init_failed || !sysctl_mptcp_enabled || + sk->sk_state != TCP_CLOSE) { + err = -EPERM; + break; + } + + if (val) + mptcp_enable_sock(sk); + else + mptcp_disable_sock(sk); + break; +#endif +#endif default: err = -ENOPROTOOPT; break; @@ -2848,6 +3140,13 @@ case TCP_NOTSENT_LOWAT: val = tp->notsent_lowat; break; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#ifdef CONFIG_MPTCP + case MPTCP_ENABLED: + val = sock_flag(sk, SOCK_MPTCP) ? 1 : 0; + break; +#endif +#endif default: return -ENOPROTOOPT; } @@ -3018,6 +3317,9 @@ if (sk->sk_state == TCP_SYN_SENT || sk->sk_state == TCP_SYN_RECV) TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_ATTEMPTFAILS); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + WARN_ON(sk->sk_state == TCP_CLOSE); +#endif tcp_set_state(sk, TCP_CLOSE); tcp_clear_xmit_timers(sk); if (req) @@ -3065,6 +3367,10 @@ int max_rshare, max_wshare, cnt; unsigned int i; +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +/*CVE-2019-11477*/ + BUILD_BUG_ON(TCP_MIN_SND_MSS <= MAX_TCP_OPTION_SPACE); +#endif //#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) sock_skb_cb_check_size(sizeof(struct tcp_skb_cb)); percpu_counter_init(&tcp_sockets_allocated, 0, GFP_KERNEL); @@ -3136,5 +3442,7 @@ tcp_metrics_init(); BUG_ON(tcp_register_congestion_control(&tcp_reno) != 0); +#if !defined(CONFIG_BCM_KF_TCP_NO_TSQ) tcp_tasklet_init(); +#endif } diff -urN linux-4.1.52-orig/net/ipv4/tcp_fastopen.c linux-4.1.52/net/ipv4/tcp_fastopen.c --- linux-4.1.52-orig/net/ipv4/tcp_fastopen.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/tcp_fastopen.c 2020-02-07 01:47:54.000000000 +0800 @@ -7,6 +7,9 @@ #include #include #include +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#include +#endif int sysctl_tcp_fastopen __read_mostly = TFO_CLIENT_ENABLE; @@ -131,7 +134,11 @@ { struct tcp_sock *tp; struct request_sock_queue *queue = &inet_csk(sk)->icsk_accept_queue; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) struct sock *child; +#else + struct sock *child, *meta_sk; +#endif u32 end_seq; req->num_retrans = 0; @@ -171,6 +178,7 @@ /* Add the child socket directly into the accept queue */ inet_csk_reqsk_queue_add(sk, req, child); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) /* Now finish processing the fastopen child socket. */ inet_csk(child)->icsk_af_ops->rebuild_header(child); tcp_init_congestion_control(child); @@ -178,6 +186,7 @@ tcp_init_metrics(child); tcp_init_buffer_space(child); +#endif /* Queue the data carried in the SYN packet. We need to first * bump skb's refcnt because the caller will attempt to free it. * Note that IPv6 might also have used skb_get() trick @@ -214,8 +223,30 @@ } } tcp_rsk(req)->rcv_nxt = tp->rcv_nxt = end_seq; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + + meta_sk = child; + if (!mptcp_check_req_fastopen(meta_sk, req)) { + child = tcp_sk(meta_sk)->mpcb->master_sk; + tp = tcp_sk(child); + } + + /* Now finish processing the fastopen child socket. */ + inet_csk(child)->icsk_af_ops->rebuild_header(child); + tcp_init_congestion_control(child); + tcp_mtup_init(child); + tcp_init_metrics(child); + tp->ops->init_buffer_space(child); + +#endif sk->sk_data_ready(sk); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) bh_unlock_sock(child); +#else + if (mptcp(tcp_sk(child))) + bh_unlock_sock(child); + bh_unlock_sock(meta_sk); +#endif sock_put(child); WARN_ON(!req->sk); return true; diff -urN linux-4.1.52-orig/net/ipv4/tcp_input.c linux-4.1.52/net/ipv4/tcp_input.c --- linux-4.1.52-orig/net/ipv4/tcp_input.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/tcp_input.c 2020-02-07 01:47:54.000000000 +0800 @@ -75,6 +75,11 @@ #include #include #include +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#include +#include +#include +#endif int sysctl_tcp_timestamps __read_mostly = 1; int sysctl_tcp_window_scaling __read_mostly = 1; @@ -102,6 +107,7 @@ int sysctl_tcp_early_retrans __read_mostly = 3; int sysctl_tcp_invalid_ratelimit __read_mostly = HZ/2; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) #define FLAG_DATA 0x01 /* Incoming frame contained data. */ #define FLAG_WIN_UPDATE 0x02 /* Incoming ACK was a window update. */ #define FLAG_DATA_ACKED 0x04 /* This ACK acknowledged new data. */ @@ -121,6 +127,7 @@ #define FLAG_CA_ALERT (FLAG_DATA_SACKED|FLAG_ECE) #define FLAG_FORWARD_PROGRESS (FLAG_ACKED|FLAG_DATA_SACKED) +#endif #define TCP_REMNANT (TCP_FLAG_FIN|TCP_FLAG_URG|TCP_FLAG_SYN|TCP_FLAG_PSH) #define TCP_HP_BITS (~(TCP_RESERVED_BITS|TCP_FLAG_PSH)) @@ -184,7 +191,11 @@ icsk->icsk_ack.quick = min(quickacks, TCP_MAX_QUICKACKS); } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static void tcp_enter_quickack_mode(struct sock *sk) +#else +void tcp_enter_quickack_mode(struct sock *sk) +#endif { struct inet_connection_sock *icsk = inet_csk(sk); tcp_incr_quickack(sk); @@ -296,8 +307,17 @@ per_mss = roundup_pow_of_two(per_mss) + SKB_DATA_ALIGN(sizeof(struct sk_buff)); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) nr_segs = max_t(u32, TCP_INIT_CWND, tp->snd_cwnd); nr_segs = max_t(u32, nr_segs, tp->reordering + 1); +#else + if (mptcp(tp)) { + nr_segs = mptcp_check_snd_buf(tp); + } else { + nr_segs = max_t(u32, TCP_INIT_CWND, tp->snd_cwnd); + nr_segs = max_t(u32, nr_segs, tp->reordering + 1); + } +#endif /* Fast Recovery (RFC 5681 3.2) : * Cubic needs 1.7 factor, rounded to 2 to include @@ -305,8 +325,22 @@ */ sndmem = 2 * nr_segs * per_mss; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (sk->sk_sndbuf < sndmem) +#else + /* MPTCP: after this sndmem is the new contribution of the + * current subflow to the aggregated sndbuf */ + if (sk->sk_sndbuf < sndmem) { + int old_sndbuf = sk->sk_sndbuf; +#endif sk->sk_sndbuf = min(sndmem, sysctl_tcp_wmem[2]); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + /* MPTCP: ok, the subflow sndbuf has grown, reflect + * this in the aggregate buffer.*/ + if (mptcp(tp) && old_sndbuf != sk->sk_sndbuf) + mptcp_update_sndbuf(tp); + } +#endif } /* 2. Tuning advertised window (window_clamp, rcv_ssthresh) @@ -355,10 +389,19 @@ static void tcp_grow_window(struct sock *sk, const struct sk_buff *skb) { struct tcp_sock *tp = tcp_sk(sk); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + struct sock *meta_sk = mptcp(tp) ? mptcp_meta_sk(sk) : sk; + struct tcp_sock *meta_tp = tcp_sk(meta_sk); +#endif /* Check #1 */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (tp->rcv_ssthresh < tp->window_clamp && (int)tp->rcv_ssthresh < tcp_space(sk) && +#else + if (meta_tp->rcv_ssthresh < meta_tp->window_clamp && + (int)meta_tp->rcv_ssthresh < tcp_space(meta_sk) && +#endif !sk_under_memory_pressure(sk)) { int incr; @@ -366,14 +409,27 @@ * will fit to rcvbuf in future. */ if (tcp_win_from_space(skb->truesize) <= skb->len) +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) incr = 2 * tp->advmss; +#else + incr = 2 * meta_tp->advmss; +#endif else +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) incr = __tcp_grow_window(sk, skb); +#else + incr = __tcp_grow_window(meta_sk, skb); +#endif if (incr) { incr = max_t(int, incr, 2 * skb->len); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tp->rcv_ssthresh = min(tp->rcv_ssthresh + incr, tp->window_clamp); +#else + meta_tp->rcv_ssthresh = min(meta_tp->rcv_ssthresh + incr, + meta_tp->window_clamp); +#endif inet_csk(sk)->icsk_ack.quick |= 1; } } @@ -556,7 +612,14 @@ int copied; time = tcp_time_stamp - tp->rcvq_space.time; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (time < (tp->rcv_rtt_est.rtt >> 3) || tp->rcv_rtt_est.rtt == 0) +#else + if (mptcp(tp)) { + if (mptcp_check_rtt(tp, time)) + return; + } else if (time < (tp->rcv_rtt_est.rtt >> 3) || tp->rcv_rtt_est.rtt == 0) +#endif return; /* Number of bytes copied to user in last RTT */ @@ -774,7 +837,11 @@ /* Calculate rto without backoff. This is the second half of Van Jacobson's * routine referred to above. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static void tcp_set_rto(struct sock *sk) +#else +void tcp_set_rto(struct sock *sk) +#endif { const struct tcp_sock *tp = tcp_sk(sk); /* Old crap is replaced with new one. 8) @@ -1309,7 +1376,12 @@ TCP_SKB_CB(skb)->seq += shifted; tcp_skb_pcount_add(prev, pcount); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +/*CVE-2019-11477*/ + WARN_ON_ONCE(tcp_skb_pcount(skb) < pcount); +#else BUG_ON(tcp_skb_pcount(skb) < pcount); +#endif //#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) tcp_skb_pcount_add(skb, -pcount); /* When we're adding to gso_segs == 1, gso_size will be zero, @@ -1375,6 +1447,24 @@ return !skb_headlen(skb) && skb_is_nonlinear(skb); } +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +/*CVE-2019-11477*/ +int tcp_skb_shift(struct sk_buff *to, struct sk_buff *from, + int pcount, int shiftlen) +{ + /* TCP min gso_size is 8 bytes (TCP_MIN_GSO_SIZE) + * Since TCP_SKB_CB(skb)->tcp_gso_segs is 16 bits, we need + * to make sure not storing more than 65535 * 8 bytes per skb, + * even if current MSS is bigger. + */ + if (unlikely(to->len + shiftlen >= 65535 * TCP_MIN_GSO_SIZE)) + return 0; + if (unlikely(tcp_skb_pcount(to) + pcount > 65535)) + return 0; + return skb_shift(to, from, shiftlen); +} +#endif //#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + /* Try collapsing SACK blocks spanning across multiple skbs to a single * skb. */ @@ -1386,11 +1476,23 @@ struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *prev; int mss; +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +/*CVE-2019-11477*/ + int next_pcount; +#endif //#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) int pcount = 0; int len; int in_sack; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (!sk_can_gso(sk)) +#else + /* For MPTCP we cannot shift skb-data and remove one skb from the + * send-queue, because this will make us loose the DSS-option (which + * is stored in TCP_SKB_CB(skb)->dss) of the skb we are removing. + */ + if (!sk_can_gso(sk) || mptcp(tp)) +#endif goto fallback; /* Normally R but no L won't result in plain S */ @@ -1479,8 +1581,12 @@ /* tcp_sacktag_one() won't SACK-tag ranges below snd_una */ if (!after(TCP_SKB_CB(skb)->seq + len, tp->snd_una)) goto fallback; - +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +/*CVE-2019-11477*/ + if (!tcp_skb_shift(prev, skb, pcount, len)) +#else if (!skb_shift(prev, skb, len)) +#endif //#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) goto fallback; if (!tcp_shifted_skb(sk, skb, state, pcount, len, mss, dup_sack)) goto out; @@ -1499,10 +1605,19 @@ goto out; len = skb->len; +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +/*CVE-2019-11477*/ + next_pcount = tcp_skb_pcount(skb); + if (tcp_skb_shift(prev, skb, next_pcount, len)) { + pcount += next_pcount; + tcp_shifted_skb(sk, skb, state, next_pcount, len, mss, 0); + } +#else if (skb_shift(prev, skb, len)) { pcount += tcp_skb_pcount(skb); tcp_shifted_skb(sk, skb, state, tcp_skb_pcount(skb), len, mss, 0); } +#endif //#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) out: state->fack_count += pcount; @@ -2930,7 +3045,11 @@ return false; tcp_rtt_estimator(sk, seq_rtt_us); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_set_rto(sk); +#else + tp->ops->set_rto(sk); +#endif /* RFC6298: only reset backoff on valid RTT measurement. */ inet_csk(sk)->icsk_backoff = 0; @@ -3015,7 +3134,11 @@ } /* If we get here, the whole TSO packet has not been acked. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static u32 tcp_tso_acked(struct sock *sk, struct sk_buff *skb) +#else +u32 tcp_tso_acked(struct sock *sk, struct sk_buff *skb) +#endif { struct tcp_sock *tp = tcp_sk(sk); u32 packets_acked; @@ -3128,6 +3251,10 @@ */ if (likely(!(scb->tcp_flags & TCPHDR_SYN))) { flag |= FLAG_DATA_ACKED; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (mptcp(tp) && mptcp_is_data_seq(skb)) + flag |= MPTCP_FLAG_DATA_ACKED; +#endif } else { flag |= FLAG_SYN_ACKED; tp->retrans_stamp = 0; @@ -3224,7 +3351,11 @@ return flag; } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static void tcp_ack_probe(struct sock *sk) +#else +void tcp_ack_probe(struct sock *sk) +#endif { const struct tcp_sock *tp = tcp_sk(sk); struct inet_connection_sock *icsk = inet_csk(sk); @@ -3272,9 +3403,14 @@ /* Check that window update is acceptable. * The function assumes that snd_una<=ack<=snd_next. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static inline bool tcp_may_update_window(const struct tcp_sock *tp, const u32 ack, const u32 ack_seq, const u32 nwin) +#else +bool tcp_may_update_window(const struct tcp_sock *tp, const u32 ack, + const u32 ack_seq, const u32 nwin) +#endif { return after(ack, tp->snd_una) || after(ack_seq, tp->snd_wl1) || @@ -3472,7 +3608,11 @@ } /* This routine deals with incoming acks, but not outgoing ones. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) +#else +static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag) +#endif { struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); @@ -3576,6 +3716,18 @@ sack_rtt_us); acked -= tp->packets_out; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (mptcp(tp)) { + if (mptcp_fallback_infinite(sk, flag)) { + pr_err("%s resetting flow\n", __func__); + mptcp_send_reset(sk); + goto invalid_ack; + } + + mptcp_clean_rtx_infinite(skb, sk); + } + +#endif /* Advance cwnd if state allows */ if (tcp_may_raise_cwnd(sk, flag)) tcp_cong_avoid(sk, ack, acked); @@ -3656,8 +3808,15 @@ * the fast version below fails. */ void tcp_parse_options(const struct sk_buff *skb, +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) struct tcp_options_received *opt_rx, int estab, struct tcp_fastopen_cookie *foc) +#else + struct tcp_options_received *opt_rx, + struct mptcp_options_received *mopt, + int estab, struct tcp_fastopen_cookie *foc, + struct tcp_sock *tp) +#endif { const unsigned char *ptr; const struct tcphdr *th = tcp_hdr(skb); @@ -3740,6 +3899,12 @@ */ break; #endif +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + case TCPOPT_MPTCP: + mptcp_parse_options(ptr - 2, opsize, mopt, skb, tp); + break; + +#endif case TCPOPT_FASTOPEN: tcp_parse_fastopen_option( opsize - TCPOLEN_FASTOPEN_BASE, @@ -3802,8 +3967,13 @@ if (tcp_parse_aligned_timestamp(tp, th)) return true; } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_parse_options(skb, &tp->rx_opt, 1, NULL); +#else + tcp_parse_options(skb, &tp->rx_opt, + mptcp(tp) ? &tp->mptcp->rx_opt : NULL, 1, NULL, tp); +#endif if (tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr) tp->rx_opt.rcv_tsecr -= tp->tsoffset; @@ -3973,6 +4143,10 @@ dst = __sk_dst_get(sk); if (!dst || !dst_metric(dst, RTAX_QUICKACK)) inet_csk(sk)->icsk_ack.pingpong = 1; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (mptcp(tp)) + mptcp_sub_close_passive(sk); +#endif break; case TCP_CLOSE_WAIT: @@ -3994,9 +4168,22 @@ tcp_set_state(sk, TCP_CLOSING); break; case TCP_FIN_WAIT2: +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (mptcp(tp)) { + /* The socket will get closed by mptcp_data_ready. + * We first have to process all data-sequences. + */ + tp->close_it = 1; + break; + } +#endif /* Received a FIN -- send ACK and enter TIME_WAIT. */ tcp_send_ack(sk); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_time_wait(sk, TCP_TIME_WAIT, 0); +#else + tp->ops->time_wait(sk, TCP_TIME_WAIT, 0); +#endif break; default: /* Only TCP_LISTEN and TCP_CLOSE are left, in these @@ -4018,6 +4205,12 @@ if (!sock_flag(sk, SOCK_DEAD)) { sk->sk_state_change(sk); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + /* Don't wake up MPTCP-subflows */ + if (mptcp(tp)) + return; + +#endif /* Do not send POLL_HUP for half duplex close. */ if (sk->sk_shutdown == SHUTDOWN_MASK || sk->sk_state == TCP_CLOSE) @@ -4208,15 +4401,25 @@ * Better try to coalesce them right now to avoid future collapses. * Returns true if caller should free @from instead of queueing it */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static bool tcp_try_coalesce(struct sock *sk, struct sk_buff *to, struct sk_buff *from, bool *fragstolen) +#else +bool tcp_try_coalesce(struct sock *sk, struct sk_buff *to, struct sk_buff *from, + bool *fragstolen) +#endif { int delta; *fragstolen = false; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (mptcp(tcp_sk(sk)) && !is_meta_sk(sk)) + return false; + +#endif /* Its possible this segment overlaps with prior segment in queue */ if (TCP_SKB_CB(from)->seq != TCP_SKB_CB(to)->end_seq) return false; @@ -4255,7 +4458,15 @@ } __skb_unlink(skb, &tp->out_of_order_queue); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (!after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt)) { +#else + /* In case of MPTCP, the segment may be empty if it's a + * non-data DATA_FIN. (see beginning of tcp_data_queue) + */ + if (!after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt) && + !(mptcp(tp) && TCP_SKB_CB(skb)->end_seq == TCP_SKB_CB(skb)->seq)) { +#endif SOCK_DEBUG(sk, "ofo packet was already received\n"); __kfree_skb(skb); continue; @@ -4276,12 +4487,19 @@ } } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static bool tcp_prune_ofo_queue(struct sock *sk); +#endif static int tcp_prune_queue(struct sock *sk); static int tcp_try_rmem_schedule(struct sock *sk, struct sk_buff *skb, unsigned int size) { +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (mptcp(tcp_sk(sk))) + sk = mptcp_meta_sk(sk); + +#endif if (atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf || !sk_rmem_schedule(sk, skb, size)) { @@ -4289,7 +4507,11 @@ return -1; if (!sk_rmem_schedule(sk, skb, size)) { +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (!tcp_prune_ofo_queue(sk)) +#else + if (!tcp_sk(sk)->ops->prune_ofo_queue(sk)) +#endif return -1; if (!sk_rmem_schedule(sk, skb, size)) @@ -4370,7 +4592,13 @@ /* Do skb overlap to previous one? */ if (skb1 && before(seq, TCP_SKB_CB(skb1)->end_seq)) { +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (!after(end_seq, TCP_SKB_CB(skb1)->end_seq)) { +#else + /* MPTCP allows non-data data-fin to be in the ofo-queue */ + if (!after(end_seq, TCP_SKB_CB(skb1)->end_seq) && + !(mptcp(tp) && end_seq == seq)) { +#endif /* All the bits are present. Drop. */ NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPOFOMERGE); __kfree_skb(skb); @@ -4408,6 +4636,11 @@ end_seq); break; } +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + /* MPTCP allows non-data data-fin to be in the ofo-queue */ + if (mptcp(tp) && TCP_SKB_CB(skb1)->seq == TCP_SKB_CB(skb1)->end_seq) + continue; +#endif __skb_unlink(skb1, &tp->out_of_order_queue); tcp_dsack_extend(sk, TCP_SKB_CB(skb1)->seq, TCP_SKB_CB(skb1)->end_seq); @@ -4425,8 +4658,13 @@ } } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int hdrlen, bool *fragstolen) +#else +int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int hdrlen, + bool *fragstolen) +#endif { int eaten; struct sk_buff *tail = skb_peek_tail(&sk->sk_receive_queue); @@ -4498,7 +4736,14 @@ int eaten = -1; bool fragstolen = false; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (TCP_SKB_CB(skb)->seq == TCP_SKB_CB(skb)->end_seq) +#else + /* If no data is present, but a data_fin is in the options, we still + * have to call mptcp_queue_skb later on. */ + if (TCP_SKB_CB(skb)->seq == TCP_SKB_CB(skb)->end_seq && + !(mptcp(tp) && mptcp_is_data_fin(skb))) +#endif goto drop; skb_dst_drop(skb); @@ -4544,7 +4789,11 @@ eaten = tcp_queue_rcv(sk, skb, 0, &fragstolen); } tcp_rcv_nxt_update(tp, TCP_SKB_CB(skb)->end_seq); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (skb->len) +#else + if (skb->len || mptcp_is_data_fin(skb)) +#endif tcp_event_data_recv(sk, skb); if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) tcp_fin(sk); @@ -4566,7 +4815,15 @@ if (eaten > 0) kfree_skb_partial(skb, fragstolen); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (!sock_flag(sk, SOCK_DEAD)) +#else + if (!sock_flag(sk, SOCK_DEAD) || mptcp(tp)) + /* MPTCP: we always have to call data_ready, because + * we may be about to receive a data-fin, which still + * must get queued. + */ +#endif sk->sk_data_ready(sk); return; } @@ -4618,6 +4875,10 @@ next = skb_queue_next(list, skb); __skb_unlink(skb, list); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (mptcp(tcp_sk(sk))) + mptcp_remove_shortcuts(tcp_sk(sk)->mpcb, skb); +#endif __kfree_skb(skb); NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPRCVCOLLAPSED); @@ -4772,7 +5033,11 @@ * Purge the out-of-order queue. * Return true if queue was pruned. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static bool tcp_prune_ofo_queue(struct sock *sk) +#else +bool tcp_prune_ofo_queue(struct sock *sk) +#endif { struct tcp_sock *tp = tcp_sk(sk); bool res = false; @@ -4828,7 +5093,11 @@ /* Collapsing did not help, destructive actions follow. * This must not ever occur. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_prune_ofo_queue(sk); +#else + tp->ops->prune_ofo_queue(sk); +#endif if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf) return 0; @@ -4844,7 +5113,33 @@ return -1; } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static bool tcp_should_expand_sndbuf(const struct sock *sk) +#else +/* RFC2861, slow part. Adjust cwnd, after it was not full during one rto. + * As additional protections, we do not touch cwnd in retransmission phases, + * and if application hit its sndbuf limit recently. + */ +void tcp_cwnd_application_limited(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + + if (inet_csk(sk)->icsk_ca_state == TCP_CA_Open && + sk->sk_socket && !test_bit(SOCK_NOSPACE, &sk->sk_socket->flags)) { + /* Limited by application or receiver window. */ + u32 init_win = tcp_init_cwnd(tp, __sk_dst_get(sk)); + u32 win_used = max(tp->snd_cwnd_used, init_win); + if (win_used < tp->snd_cwnd) { + tp->snd_ssthresh = tcp_current_ssthresh(sk); + tp->snd_cwnd = (tp->snd_cwnd + win_used) >> 1; + } + tp->snd_cwnd_used = 0; + } + tp->snd_cwnd_stamp = tcp_time_stamp; +} + +bool tcp_should_expand_sndbuf(const struct sock *sk) +#endif { const struct tcp_sock *tp = tcp_sk(sk); @@ -4879,7 +5174,11 @@ { struct tcp_sock *tp = tcp_sk(sk); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (tcp_should_expand_sndbuf(sk)) { +#else + if (tp->ops->should_expand_sndbuf(sk)) { +#endif tcp_sndbuf_expand(sk); tp->snd_cwnd_stamp = tcp_time_stamp; } @@ -4893,8 +5192,14 @@ sock_reset_flag(sk, SOCK_QUEUE_SHRUNK); /* pairs with tcp_poll() */ smp_mb(); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (sk->sk_socket && test_bit(SOCK_NOSPACE, &sk->sk_socket->flags)) +#else + if (mptcp(tcp_sk(sk)) || + (sk->sk_socket && + test_bit(SOCK_NOSPACE, &sk->sk_socket->flags))) +#endif tcp_new_space(sk); } } @@ -4917,7 +5222,11 @@ /* ... and right edge of window advances far enough. * (tcp_recvmsg() will send ACK otherwise). Or... */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) __tcp_select_window(sk) >= tp->rcv_wnd) || +#else + tp->ops->__select_window(sk) >= tp->rcv_wnd) || +#endif /* We ACK each frame or... */ tcp_in_quickack_mode(sk) || /* We have out of order data. */ @@ -5019,6 +5328,12 @@ { struct tcp_sock *tp = tcp_sk(sk); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + /* MPTCP urgent data is not yet supported */ + if (mptcp(tp)) + return; + +#endif /* Check if we get a new urgent pointer - normally not. */ if (th->urg) tcp_check_urg(sk, th); @@ -5154,9 +5469,19 @@ goto discard; } +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + /* If valid: post process the received MPTCP options. */ + if (mptcp(tp) && mptcp_handle_options(sk, th, skb)) + goto discard; + +#endif return true; discard: +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (mptcp(tp)) + mptcp_reset_mopt(tp); +#endif __kfree_skb(skb); return false; } @@ -5208,6 +5533,12 @@ tp->rx_opt.saw_tstamp = 0; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + /* MPTCP: force slowpath. */ + if (mptcp(tp)) + goto slow_path; + +#endif /* pred_flags is 0xS?10 << 16 + snd_wnd * if header_prediction is to be made * 'S' will always be tp->tcp_header_len >> 2 @@ -5404,7 +5735,11 @@ */ tp->lsndtime = tcp_time_stamp; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_init_buffer_space(sk); +#else + tp->ops->init_buffer_space(sk); +#endif if (sock_flag(sk, SOCK_KEEPOPEN)) inet_csk_reset_keepalive_timer(sk, keepalive_time_when(tp)); @@ -5430,7 +5765,11 @@ /* Get original SYNACK MSS value if user MSS sets mss_clamp */ tcp_clear_options(&opt); opt.user_mss = opt.mss_clamp = 0; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_parse_options(synack, &opt, 0, NULL); +#else + tcp_parse_options(synack, &opt, NULL, 0, NULL, NULL); +#endif mss = opt.mss_clamp; } @@ -5454,7 +5793,15 @@ tcp_fastopen_cache_set(sk, mss, cookie, syn_drop, try_exp); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (data) { /* Retransmit unacked data in SYN */ +#else + /* In mptcp case, we do not rely on "retransmit", but instead on + * "transmit", because if fastopen data is not acked, the retransmission + * becomes the first MPTCP data (see mptcp_rcv_synsent_fastopen). + */ + if (data && !mptcp(tp)) { /* Retransmit unacked data in SYN */ +#endif tcp_for_write_queue_from(data, sk) { if (data == tcp_send_head(sk) || __tcp_retransmit_skb(sk, data)) @@ -5478,8 +5825,17 @@ struct tcp_fastopen_cookie foc = { .len = -1 }; int saved_clamp = tp->rx_opt.mss_clamp; bool fastopen_fail; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + struct mptcp_options_received mopt; + mptcp_init_mp_opt(&mopt); +#endif +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_parse_options(skb, &tp->rx_opt, 0, &foc); +#else + tcp_parse_options(skb, &tp->rx_opt, + mptcp(tp) ? &tp->mptcp->rx_opt : &mopt, 0, &foc, tp); +#endif if (tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr) tp->rx_opt.rcv_tsecr -= tp->tsoffset; @@ -5538,6 +5894,32 @@ tcp_init_wl(tp, TCP_SKB_CB(skb)->seq); tcp_ack(sk, skb, FLAG_SLOWPATH); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (tp->request_mptcp || mptcp(tp)) { + int ret; + ret = mptcp_rcv_synsent_state_process(sk, &sk, + skb, &mopt); + + /* May have changed if we support MPTCP */ + tp = tcp_sk(sk); + icsk = inet_csk(sk); + + if (ret == 1) + goto reset_and_undo; + if (ret == 2) + goto discard; + } + + if (mptcp(tp) && !is_master_tp(tp)) { + /* Timer for repeating the ACK until an answer + * arrives. Used only when establishing an additional + * subflow inside of an MPTCP connection. + */ + sk_reset_timer(sk, &tp->mptcp->mptcp_ack_timer, + jiffies + icsk->icsk_rto); + } + +#endif /* Ok.. it's good. Set up sequence numbers and * move to established. */ @@ -5564,6 +5946,13 @@ tp->tcp_header_len = sizeof(struct tcphdr); } +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (mptcp(tp)) { + tp->tcp_header_len += MPTCP_SUB_LEN_DSM_ALIGN; + tp->advmss -= MPTCP_SUB_LEN_DSM_ALIGN; + } + +#endif if (tcp_is_sack(tp) && sysctl_tcp_fack) tcp_enable_fack(tp); @@ -5589,9 +5978,21 @@ } if (fastopen_fail) return -1; + +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (sk->sk_write_pending || +#else + /* With MPTCP we cannot send data on the third ack due to the + * lack of option-space to combine with an MP_CAPABLE. + */ + if (!mptcp(tp) && (sk->sk_write_pending || +#endif icsk->icsk_accept_queue.rskq_defer_accept || +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) icsk->icsk_ack.pingpong) { +#else + icsk->icsk_ack.pingpong)) { +#endif /* Save one ACK. Data will be ready after * several ticks, if write_pending is set. * @@ -5630,6 +6031,9 @@ tcp_paws_reject(&tp->rx_opt, 0)) goto discard_and_undo; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + /* TODO - check this here for MPTCP */ +#endif if (th->syn) { /* We see SYN without ACK. It is attempt of * simultaneous connect with crossed SYNs. @@ -5646,6 +6050,13 @@ tp->tcp_header_len = sizeof(struct tcphdr); } +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (mptcp(tp)) { + tp->tcp_header_len += MPTCP_SUB_LEN_DSM_ALIGN; + tp->advmss -= MPTCP_SUB_LEN_DSM_ALIGN; + } + +#endif tp->rcv_nxt = TCP_SKB_CB(skb)->seq + 1; tp->copied_seq = tp->rcv_nxt; tp->rcv_wup = TCP_SKB_CB(skb)->seq + 1; @@ -5705,6 +6116,9 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, const struct tcphdr *th, unsigned int len) +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + __releases(&sk->sk_lock.slock) +#endif { struct tcp_sock *tp = tcp_sk(sk); struct inet_connection_sock *icsk = inet_csk(sk); @@ -5756,6 +6170,18 @@ case TCP_SYN_SENT: queued = tcp_rcv_synsent_state_process(sk, skb, th, len); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (is_meta_sk(sk)) { + sk = tcp_sk(sk)->mpcb->master_sk; + tp = tcp_sk(sk); + + /* Need to call it here, because it will announce new + * addresses, which can only be done after the third ack + * of the 3-way handshake. + */ + mptcp_update_metasocket(sk, tp->meta_sk); + } +#endif if (queued >= 0) return queued; @@ -5763,6 +6189,10 @@ tcp_urg(sk, skb, th); __kfree_skb(skb); tcp_data_snd_check(sk); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (mptcp(tp) && is_master_tp(tp)) + bh_unlock_sock(sk); +#endif return 0; } @@ -5805,7 +6235,11 @@ tcp_mtup_init(sk); tp->copied_seq = tp->rcv_nxt; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_init_buffer_space(sk); +#else + tp->ops->init_buffer_space(sk); +#endif } smp_mb(); tcp_set_state(sk, TCP_ESTABLISHED); @@ -5825,6 +6259,10 @@ if (tp->rx_opt.tstamp_ok) tp->advmss -= TCPOLEN_TSTAMP_ALIGNED; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (mptcp(tp)) + tp->advmss -= MPTCP_SUB_LEN_DSM_ALIGN; +#endif if (req) { /* Re-arm the timer because data may have been sent out. @@ -5846,6 +6284,14 @@ tcp_initialize_rcv_mss(sk); tcp_fast_path_on(tp); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + /* Send an ACK when establishing a new + * MPTCP subflow, i.e. using an MP_JOIN + * subtype. + */ + if (mptcp(tp) && !is_master_tp(tp)) + tcp_send_ack(sk); +#endif break; case TCP_FIN_WAIT1: { @@ -5897,7 +6343,12 @@ tmo = tcp_fin_time(sk); if (tmo > TCP_TIMEWAIT_LEN) { inet_csk_reset_keepalive_timer(sk, tmo - TCP_TIMEWAIT_LEN); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) } else if (th->fin || sock_owned_by_user(sk)) { +#else + } else if (th->fin || mptcp_is_data_fin(skb) || + sock_owned_by_user(sk)) { +#endif /* Bad case. We could lose such FIN otherwise. * It is not a big problem, but it looks confusing * and not so rare event. We still can lose it now, @@ -5906,7 +6357,11 @@ */ inet_csk_reset_keepalive_timer(sk, tmo); } else { +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_time_wait(sk, TCP_FIN_WAIT2, tmo); +#else + tp->ops->time_wait(sk, TCP_FIN_WAIT2, tmo); +#endif goto discard; } break; @@ -5914,7 +6369,11 @@ case TCP_CLOSING: if (tp->snd_una == tp->write_seq) { +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_time_wait(sk, TCP_TIME_WAIT, 0); +#else + tp->ops->time_wait(sk, TCP_TIME_WAIT, 0); +#endif goto discard; } break; @@ -5926,6 +6385,11 @@ goto discard; } break; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + case TCP_CLOSE: + if (tp->mp_killed) + goto discard; +#endif } /* step 6: check the URG bit */ @@ -5946,7 +6410,15 @@ */ if (sk->sk_shutdown & RCV_SHUTDOWN) { if (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq && +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt)) { +#else + after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt) && + !mptcp(tp)) { + /* In case of mptcp, the reset is handled by + * mptcp_rcv_state_process + */ +#endif NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPABORTONDATA); tcp_reset(sk); return 1; @@ -6039,6 +6511,10 @@ ireq->wscale_ok = rx_opt->wscale_ok; ireq->acked = 0; ireq->ecn_ok = 0; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + ireq->mptcp_rqsk = 0; + ireq->saw_mpc = 0; +#endif ireq->ir_rmt_port = tcp_hdr(skb)->source; ireq->ir_num = ntohs(tcp_hdr(skb)->dest); ireq->ir_mark = inet_request_mark(sk, skb); @@ -6112,11 +6588,24 @@ * limitations, they conserve resources and peer is * evidently real one. */ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + /* MPTCP: new subflows cannot be established in a stateless manner.*/ +#endif + +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if ((sysctl_tcp_syncookies == 2 || +#else + if (((!is_meta_sk(sk) && sysctl_tcp_syncookies == 2) || +#endif inet_csk_reqsk_queue_is_full(sk)) && !isn) { want_cookie = tcp_syn_flood_action(sk, skb, rsk_ops->slab_name); if (!want_cookie) goto drop; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + + if (is_meta_sk(sk)) + goto drop; +#endif } @@ -6139,7 +6628,11 @@ tcp_clear_options(&tmp_opt); tmp_opt.mss_clamp = af_ops->mss_clamp; tmp_opt.user_mss = tp->rx_opt.user_mss; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_parse_options(skb, &tmp_opt, 0, want_cookie ? NULL : &foc); +#else + tcp_parse_options(skb, &tmp_opt, NULL, 0, want_cookie ? NULL : &foc, NULL); +#endif if (want_cookie && !tmp_opt.saw_tstamp) tcp_clear_options(&tmp_opt); @@ -6150,7 +6643,12 @@ /* Note: tcp_v6_init_req() might override ir_iif for link locals */ inet_rsk(req)->ir_iif = sk->sk_bound_dev_if; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) af_ops->init_req(req, sk, skb); +#else + if (af_ops->init_req(req, sk, skb, want_cookie)) + goto drop_and_free; +#endif if (security_inet_conn_request(sk, skb, req)) goto drop_and_free; @@ -6206,7 +6704,11 @@ tcp_ecn_create_request(req, skb, sk, dst); if (want_cookie) { +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) isn = cookie_init_sequence(af_ops, sk, skb, &req->mss); +#else + isn = cookie_init_sequence(req, af_ops, sk, skb, &req->mss); +#endif req->cookie_ts = tmp_opt.tstamp_ok; if (!tmp_opt.tstamp_ok) inet_rsk(req)->ecn_ok = 0; diff -urN linux-4.1.52-orig/net/ipv4/tcp_ipv4.c linux-4.1.52/net/ipv4/tcp_ipv4.c --- linux-4.1.52-orig/net/ipv4/tcp_ipv4.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/tcp_ipv4.c 2020-02-07 01:47:54.000000000 +0800 @@ -67,6 +67,10 @@ #include #include #include +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#include +#include +#endif #include #include #include @@ -85,6 +89,10 @@ #include #include +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#include +#endif + int sysctl_tcp_tw_reuse __read_mostly; int sysctl_tcp_low_latency __read_mostly; EXPORT_SYMBOL(sysctl_tcp_low_latency); @@ -366,7 +374,11 @@ struct inet_sock *inet; const int type = icmp_hdr(icmp_skb)->type; const int code = icmp_hdr(icmp_skb)->code; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) struct sock *sk; +#else + struct sock *sk, *meta_sk; +#endif struct sk_buff *skb; struct request_sock *fastopen; __u32 seq, snd_una; @@ -389,13 +401,27 @@ if (sk->sk_state == TCP_NEW_SYN_RECV) return tcp_req_err(sk, seq); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) bh_lock_sock(sk); +#else + tp = tcp_sk(sk); + if (mptcp(tp)) + meta_sk = mptcp_meta_sk(sk); + else + meta_sk = sk; + + bh_lock_sock(meta_sk); +#endif /* If too many ICMPs get dropped on busy * servers this needs to be solved differently. * We do take care of PMTU discovery (RFC1191) special case : * we can receive locally generated ICMP messages while socket is held. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (sock_owned_by_user(sk)) { +#else + if (sock_owned_by_user(meta_sk)) { +#endif if (!(type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)) NET_INC_STATS_BH(net, LINUX_MIB_LOCKDROPPEDICMPS); } @@ -408,7 +434,9 @@ } icsk = inet_csk(sk); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tp = tcp_sk(sk); +#endif /* XXX (TFO) - tp->snd_una should be ISN (tcp_create_openreq_child() */ fastopen = tp->fastopen_rsk; snd_una = fastopen ? tcp_rsk(fastopen)->snt_isn : tp->snd_una; @@ -442,11 +470,19 @@ goto out; tp->mtu_info = info; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (!sock_owned_by_user(sk)) { +#else + if (!sock_owned_by_user(meta_sk)) { +#endif tcp_v4_mtu_reduced(sk); } else { if (!test_and_set_bit(TCP_MTU_REDUCED_DEFERRED, &tp->tsq_flags)) sock_hold(sk); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (mptcp(tp)) + mptcp_tsq_flags(sk); +#endif } goto out; } @@ -460,7 +496,11 @@ !icsk->icsk_backoff || fastopen) break; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (sock_owned_by_user(sk)) +#else + if (sock_owned_by_user(meta_sk)) +#endif break; icsk->icsk_backoff--; @@ -481,7 +521,11 @@ } else { /* RTO revert clocked out retransmission. * Will retransmit now */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_retransmit_timer(sk); +#else + tcp_sk(sk)->ops->retransmit_timer(sk); +#endif } break; @@ -501,7 +545,11 @@ if (fastopen && !fastopen->sk) break; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (!sock_owned_by_user(sk)) { +#else + if (!sock_owned_by_user(meta_sk)) { +#endif sk->sk_err = err; sk->sk_error_report(sk); @@ -530,7 +578,11 @@ */ inet = inet_sk(sk); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (!sock_owned_by_user(sk) && inet->recverr) { +#else + if (!sock_owned_by_user(meta_sk) && inet->recverr) { +#endif sk->sk_err = err; sk->sk_error_report(sk); } else { /* Only an error on timeout */ @@ -538,7 +590,11 @@ } out: +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) bh_unlock_sock(sk); +#else + bh_unlock_sock(meta_sk); +#endif sock_put(sk); } @@ -580,7 +636,11 @@ * Exception: precedence violation. We do not implement it in any case. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) +#else +void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) +#endif { const struct tcphdr *th = tcp_hdr(skb); struct { @@ -710,10 +770,18 @@ */ static void tcp_v4_send_ack(struct net *net, +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) struct sk_buff *skb, u32 seq, u32 ack, +#else + struct sk_buff *skb, u32 seq, u32 ack, u32 data_ack, +#endif u32 win, u32 tsval, u32 tsecr, int oif, struct tcp_md5sig_key *key, +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) int reply_flags, u8 tos) +#else + int reply_flags, u8 tos, int mptcp) +#endif { const struct tcphdr *th = tcp_hdr(skb); struct { @@ -722,6 +790,12 @@ #ifdef CONFIG_TCP_MD5SIG + (TCPOLEN_MD5SIG_ALIGNED >> 2) #endif +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#ifdef CONFIG_MPTCP + + ((MPTCP_SUB_LEN_DSS >> 2) + + (MPTCP_SUB_LEN_ACK >> 2)) +#endif +#endif ]; } rep; struct ip_reply_arg arg; @@ -765,6 +839,23 @@ ip_hdr(skb)->daddr, &rep.th); } #endif +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#ifdef CONFIG_MPTCP + if (mptcp) { + int offset = (tsecr) ? 3 : 0; + /* Construction of 32-bit data_ack */ + rep.opt[offset++] = htonl((TCPOPT_MPTCP << 24) | + ((MPTCP_SUB_LEN_DSS + MPTCP_SUB_LEN_ACK) << 16) | + (0x20 << 8) | + (0x01)); + rep.opt[offset] = htonl(data_ack); + + arg.iov[0].iov_len += MPTCP_SUB_LEN_DSS + MPTCP_SUB_LEN_ACK; + rep.th.doff = arg.iov[0].iov_len / 4; + } +#endif /* CONFIG_MPTCP */ + +#endif arg.flags = reply_flags; arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr, ip_hdr(skb)->saddr, /* XXX */ @@ -785,23 +876,45 @@ { struct inet_timewait_sock *tw = inet_twsk(sk); struct tcp_timewait_sock *tcptw = tcp_twsk(sk); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + u32 data_ack = 0; + int mptcp = 0; + + if (tcptw->mptcp_tw && tcptw->mptcp_tw->meta_tw) { + data_ack = (u32)tcptw->mptcp_tw->rcv_nxt; + mptcp = 1; + } +#endif tcp_v4_send_ack(sock_net(sk), skb, +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt, +#else + tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt, data_ack, +#endif tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale, tcp_time_stamp + tcptw->tw_ts_offset, tcptw->tw_ts_recent, tw->tw_bound_dev_if, tcp_twsk_md5_key(tcptw), tw->tw_transparent ? IP_REPLY_ARG_NOSRCCHECK : 0, +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tw->tw_tos +#else + tw->tw_tos, mptcp +#endif ); inet_twsk_put(tw); } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, struct request_sock *req) +#else +void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, + struct request_sock *req) +#endif { /* sk->sk_state == TCP_LISTEN -> for regular TCP_SYN_RECV * sk->sk_state == TCP_SYN_RECV -> for Fast Open. @@ -810,14 +923,22 @@ tcp_sk(sk)->snd_nxt; tcp_v4_send_ack(sock_net(sk), skb, seq, +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_rsk(req)->rcv_nxt, req->rcv_wnd, +#else + tcp_rsk(req)->rcv_nxt, 0, req->rcv_wnd, +#endif tcp_time_stamp, req->ts_recent, 0, tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&ip_hdr(skb)->saddr, AF_INET), inet_rsk(req)->no_srccheck ? IP_REPLY_ARG_NOSRCCHECK : 0, +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) ip_hdr(skb)->tos); +#else + ip_hdr(skb)->tos, 0); +#endif } /* @@ -825,11 +946,19 @@ * This still operates on a request_sock only, not on a big * socket. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, struct flowi *fl, struct request_sock *req, u16 queue_mapping, struct tcp_fastopen_cookie *foc) +#else +int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, + struct flowi *fl, + struct request_sock *req, + u16 queue_mapping, + struct tcp_fastopen_cookie *foc) +#endif { const struct inet_request_sock *ireq = inet_rsk(req); struct flowi4 fl4; @@ -858,7 +987,11 @@ /* * IPv4 request_sock destructor. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static void tcp_v4_reqsk_destructor(struct request_sock *req) +#else +void tcp_v4_reqsk_destructor(struct request_sock *req) +#endif { kfree(inet_rsk(req)->opt); } @@ -1176,8 +1309,13 @@ } #endif +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static void tcp_v4_init_req(struct request_sock *req, struct sock *sk_listener, struct sk_buff *skb) +#else +static int tcp_v4_init_req(struct request_sock *req, struct sock *sk_listener, + struct sk_buff *skb, bool want_cookie) +#endif { struct inet_request_sock *ireq = inet_rsk(req); @@ -1185,6 +1323,10 @@ sk_daddr_set(req_to_sk(req), ip_hdr(skb)->saddr); ireq->no_srccheck = inet_sk(sk_listener)->transparent; ireq->opt = tcp_v4_save_options(skb); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + + return 0; +#endif } static struct dst_entry *tcp_v4_route_req(struct sock *sk, struct flowi *fl, @@ -1213,7 +1355,11 @@ .syn_ack_timeout = tcp_syn_ack_timeout, }; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = { +#else +const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = { +#endif .mss_clamp = TCP_MSS_DEFAULT, #ifdef CONFIG_TCP_MD5SIG .req_md5_lookup = tcp_v4_md5_lookup, @@ -1346,7 +1492,11 @@ } EXPORT_SYMBOL(tcp_v4_syn_recv_sock); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb) +#else +struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb) +#endif { const struct tcphdr *th = tcp_hdr(skb); const struct iphdr *iph = ip_hdr(skb); @@ -1366,8 +1516,21 @@ if (nsk) { if (nsk->sk_state != TCP_TIME_WAIT) { +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + /* Don't lock again the meta-sk. It has been locked + * before mptcp_v4_do_rcv. + */ + if (mptcp(tcp_sk(nsk)) && !is_meta_sk(sk)) + bh_lock_sock(mptcp_meta_sk(nsk)); +#endif bh_lock_sock(nsk); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + +#endif return nsk; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + +#endif } inet_twsk_put(inet_twsk(nsk)); return NULL; @@ -1392,6 +1555,11 @@ { struct sock *rsk; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (is_meta_sk(sk)) + return mptcp_v4_do_rcv(sk, skb); + +#endif if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */ struct dst_entry *dst = sk->sk_rx_dst; @@ -1452,6 +1620,113 @@ } EXPORT_SYMBOL(tcp_v4_do_rcv); + +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +static inline struct sk_buff *bcm_find_skb_by_flow_id(uint32_t flowid) +{ + /* TODO add this function later,needed for coalescing */ + return NULL; +} + +static inline void set_skb_fields(FkBuff_t *fkb, struct sk_buff *skb + , struct net_device *dev) +{ + /*TODO check if we can use skb_dst_set_noref as blog holds reference*/ + + if(fkb->dst_entry){ + dst_hold(fkb->dst_entry); + skb_dst_set(skb, fkb->dst_entry); + } + skb->dev = dev; + skb->skb_iif = dev->ifindex; + return; +} + +static inline void position_skb_ptrs_to_transport(struct sk_buff *skb, BlogFcArgs_t *fc_args) +{ + + /*initialize ip & tcp header related fields in skb */ + skb_set_mac_header(skb, 0); + skb_set_network_header(skb, fc_args->tx_l3_offset); + skb_set_transport_header(skb, fc_args->tx_l4_offset); + + /*position data pointer to start of TCP hdr */ + skb_pull(skb,fc_args->tx_l4_offset); + skb->pkt_type = PACKET_HOST; + return; +} + + +/* inject the packet into ipv4_tcp_stack directly from the network driver */ +int bcm_tcp_v4_recv(pNBuff_t pNBuff, BlogFcArgs_t *fc_args) +{ + struct sk_buff *skb; + FkBuff_t *fkb; + + if(IS_FKBUFF_PTR(pNBuff)) + { + fkb = PNBUFF_2_FKBUFF(pNBuff); + /* Translate the fkb to skb */ + /* find the skb for flowid or allocate a new skb */ + skb = bcm_find_skb_by_flow_id(fkb->flowid); + + if(!skb) + { + skb = skb_xlate_dp(fkb, NULL); + + if(!skb) + { + nbuff_free(fkb); + return 0; + } + + } + skb->mark=0; + skb->priority=0; + } + else + { + skb = PNBUFF_2_SKBUFF(pNBuff); + fkb = (FkBuff_t *)&skb->fkbInSkb; + } + + set_skb_fields(fkb, skb, fc_args->txdev_p); + position_skb_ptrs_to_transport(skb, fc_args); + + /* + * bh_disable is needed to prevent deadlock on sock_lock when TCP timers + * are executed + */ + if (skb) { + local_bh_disable(); + tcp_v4_rcv(skb); + local_bh_enable(); + } + + return 0; +} +EXPORT_SYMBOL(bcm_tcp_v4_recv); + +static const struct net_device_ops bcm_tcp4_netdev_ops = { + .ndo_open = NULL, + .ndo_stop = NULL, + .ndo_start_xmit = (HardStartXmitFuncP)bcm_tcp_v4_recv, + .ndo_set_mac_address = NULL, + .ndo_do_ioctl = NULL, + .ndo_tx_timeout = NULL, + .ndo_get_stats = NULL, + .ndo_change_mtu = NULL +}; + +struct net_device bcm_tcp4_netdev = { + .name = "tcp4_netdev", + /* set it to 64K incase we aggregate pkts in HW in future */ + .mtu = 64 * 1024, + .netdev_ops = &bcm_tcp4_netdev_ops +}; + +#endif + void tcp_v4_early_demux(struct sk_buff *skb) { const struct iphdr *iph; @@ -1535,7 +1810,11 @@ } else if (skb_queue_len(&tp->ucopy.prequeue) == 1) { wake_up_interruptible_sync_poll(sk_sleep(sk), POLLIN | POLLRDNORM | POLLRDBAND); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (!inet_csk_ack_scheduled(sk)) +#else + if (!inet_csk_ack_scheduled(sk) && !mptcp(tp)) +#endif inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, (3 * tcp_rto_min(sk)) / 4, TCP_RTO_MAX); @@ -1548,11 +1827,48 @@ * From tcp_input.c */ +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +inline static int ethernet_offset_heuristic(struct sk_buff *skb) +{ + uint16_t offset = BLOG_ETH_HDR_LEN ; + uint8_t *ether_type_loc = + (skb_transport_header(skb)-BLOG_IPV4_HDR_LEN-BLOG_ETH_TYPE_LEN); + + if ( ntohs(*(uint16_t*)ether_type_loc) == BLOG_ETH_P_IPV4 ) + { + offset += BLOG_IPV4_HDR_LEN; + goto parse_success; + } + else + { + ether_type_loc -= BLOG_PPPOE_HDR_LEN; + + if ( ntohs(*(uint16_t*)ether_type_loc) == BLOG_ETH_P_PPP_SES ) + { + offset += (BLOG_PPPOE_HDR_LEN + BLOG_IPV4_HDR_LEN); + goto parse_success; + } + goto parse_fail; + } + goto parse_fail; + +parse_success: + return offset; +parse_fail: + return 0; + +} +#endif + int tcp_v4_rcv(struct sk_buff *skb) { const struct iphdr *iph; const struct tcphdr *th; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) struct sock *sk; +#else + struct sock *sk, *meta_sk = NULL; +#endif int ret; struct net *net = dev_net(skb->dev); @@ -1593,19 +1909,60 @@ TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin + skb->len - th->doff * 4); TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#ifdef CONFIG_MPTCP + TCP_SKB_CB(skb)->mptcp_flags = 0; + TCP_SKB_CB(skb)->dss_off = 0; +#endif +#endif TCP_SKB_CB(skb)->tcp_flags = tcp_flag_byte(th); TCP_SKB_CB(skb)->tcp_tw_isn = 0; TCP_SKB_CB(skb)->ip_dsfield = ipv4_get_dsfield(iph); TCP_SKB_CB(skb)->sacked = 0; sk = __inet_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (!sk) goto no_tcp_socket; +#endif process: +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (sk->sk_state == TCP_TIME_WAIT) +#else + if (sk && sk->sk_state == TCP_TIME_WAIT) +#endif goto do_time_wait; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + /* TODO can we move this deeper into TCP stack */ + + if(skb->blog_p && skb->blog_p->l2_mode) + { + blog_skip(skb,blog_skip_reason_l2_local_termination); + } + else if( (sk && sk->sk_state == TCP_ESTABLISHED) && skb->blog_p && + ( skb->blog_p->rx.info.phyHdrType == BLOG_ENETPHY + || skb->blog_p->rx.info.phyHdrType == BLOG_EPONPHY + || skb->blog_p->rx.info.phyHdrType == BLOG_GPONPHY + || ((skb->blog_p->rx.info.phyHdrType == BLOG_XTMPHY) + && (skb->blog_p->rx.info.bmap.ETH_802x == 1))) ) + { + struct net_device *tmpdev; + int offset = ethernet_offset_heuristic(skb); + skb_push(skb,offset); + tmpdev = skb->dev; + skb->dev = &bcm_tcp4_netdev; + blog_emit(skb, tmpdev, TYPE_ETH, 0, BLOG_TCP4_LOCALPHY); + skb->dev = tmpdev; + skb_pull(skb,offset); + } +#endif + +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (!sk) + goto no_tcp_socket; +#endif if (unlikely(iph->ttl < inet_sk(sk)->min_ttl)) { NET_INC_STATS_BH(net, LINUX_MIB_TCPMINTTLDROP); goto discard_and_relse; @@ -1614,6 +1971,17 @@ if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb)) goto discard_and_relse; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#ifdef CONFIG_MPTCP + /* Is there a pending request sock for this segment ? */ + if (sk->sk_state == TCP_LISTEN && mptcp_check_req(skb, net)) { + if (sk) + sock_put(sk); + return 0; + } +#endif + +#endif #ifdef CONFIG_TCP_MD5SIG /* * We really want to reject the packet as early as possible @@ -1633,18 +2001,47 @@ sk_incoming_cpu_update(sk); skb->dev = NULL; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) bh_lock_sock_nested(sk); +#else + if (mptcp(tcp_sk(sk))) { + meta_sk = mptcp_meta_sk(sk); + + bh_lock_sock_nested(meta_sk); + if (sock_owned_by_user(meta_sk)) + skb->sk = sk; + } else { + meta_sk = sk; + bh_lock_sock_nested(sk); + } + +#endif ret = 0; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (!sock_owned_by_user(sk)) { if (!tcp_prequeue(sk, skb)) +#else + if (!sock_owned_by_user(meta_sk)) { + if (!tcp_prequeue(meta_sk, skb)) +#endif ret = tcp_v4_do_rcv(sk, skb); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) } else if (unlikely(sk_add_backlog(sk, skb, sk->sk_rcvbuf + sk->sk_sndbuf))) { bh_unlock_sock(sk); +#else + } else if (unlikely(sk_add_backlog(meta_sk, skb, + meta_sk->sk_rcvbuf + meta_sk->sk_sndbuf))) { + bh_unlock_sock(meta_sk); +#endif NET_INC_STATS_BH(net, LINUX_MIB_TCPBACKLOGDROP); goto discard_and_relse; } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) bh_unlock_sock(sk); +#else + bh_unlock_sock(meta_sk); +#endif sock_put(sk); @@ -1654,6 +2051,28 @@ if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) goto discard_it; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#ifdef CONFIG_MPTCP + if (!sk && th->syn && !th->ack) { + int ret = mptcp_lookup_join(skb, NULL); + + if (ret < 0) { + tcp_v4_send_reset(NULL, skb); + goto discard_it; + } else if (ret > 0) { + return 0; + } + } + + /* Is there a pending request sock for this segment ? */ + if (!sk && mptcp_check_req(skb, net)) { + if (sk) + sock_put(sk); + return 0; + } +#endif + +#endif if (skb->len < (th->doff << 2) || tcp_checksum_complete(skb)) { csum_error: TCP_INC_STATS_BH(net, TCP_MIB_CSUMERRORS); @@ -1699,6 +2118,20 @@ sk = sk2; goto process; } +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#ifdef CONFIG_MPTCP + if (th->syn && !th->ack) { + int ret = mptcp_lookup_join(skb, inet_twsk(sk)); + + if (ret < 0) { + tcp_v4_send_reset(NULL, skb); + goto discard_it; + } else if (ret > 0) { + return 0; + } + } +#endif +#endif /* Fall through to ACK */ } case TCP_TW_ACK: @@ -1766,7 +2199,16 @@ tcp_init_sock(sk); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) icsk->icsk_af_ops = &ipv4_specific; +#else +#ifdef CONFIG_MPTCP + if (sock_flag(sk, SOCK_MPTCP)) + icsk->icsk_af_ops = &mptcp_v4_specific; + else +#endif + icsk->icsk_af_ops = &ipv4_specific; +#endif #ifdef CONFIG_TCP_MD5SIG tcp_sk(sk)->af_specific = &tcp_sock_ipv4_specific; @@ -1783,6 +2225,13 @@ tcp_cleanup_congestion_control(sk); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (mptcp(tp)) + mptcp_destroy_sock(sk); + if (tp->inside_tk_table) + mptcp_hash_remove(tp); + +#endif /* Cleanup up the write buffer. */ tcp_write_queue_purge(sk); @@ -2342,6 +2791,21 @@ } #endif /* CONFIG_PROC_FS */ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#ifdef CONFIG_MPTCP +static void tcp_v4_clear_sk(struct sock *sk, int size) +{ + struct tcp_sock *tp = tcp_sk(sk); + + /* we do not want to clear tk_table field, because of RCU lookups */ + sk_prot_clear_nulls(sk, offsetof(struct tcp_sock, tk_table)); + + size -= offsetof(struct tcp_sock, tk_table) + sizeof(tp->tk_table); + memset((char *)&tp->tk_table + sizeof(tp->tk_table), 0, size); +} +#endif + +#endif struct proto tcp_prot = { .name = "TCP", .owner = THIS_MODULE, @@ -2388,6 +2852,11 @@ .destroy_cgroup = tcp_destroy_cgroup, .proto_cgroup = tcp_proto_cgroup, #endif +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#ifdef CONFIG_MPTCP + .clear_sk = tcp_v4_clear_sk, +#endif +#endif }; EXPORT_SYMBOL(tcp_prot); @@ -2419,6 +2888,10 @@ } net->ipv4.sysctl_tcp_ecn = 2; net->ipv4.sysctl_tcp_base_mss = TCP_BASE_MSS; +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +/*CVE-2019-11479*/ + net->ipv4.sysctl_tcp_min_snd_mss = TCP_MIN_SND_MSS; +#endif net->ipv4.sysctl_tcp_probe_threshold = TCP_PROBE_THRESHOLD; net->ipv4.sysctl_tcp_probe_interval = TCP_PROBE_INTERVAL; return 0; diff -urN linux-4.1.52-orig/net/ipv4/tcp_minisocks.c linux-4.1.52/net/ipv4/tcp_minisocks.c --- linux-4.1.52-orig/net/ipv4/tcp_minisocks.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/tcp_minisocks.c 2020-02-07 01:47:54.000000000 +0800 @@ -18,11 +18,17 @@ * Jorge Cwik, */ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#include +#endif #include #include #include #include #include +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#include +#endif #include #include #include @@ -103,10 +109,19 @@ struct tcp_options_received tmp_opt; struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw); bool paws_reject = false; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + struct mptcp_options_received mopt; +#endif tmp_opt.saw_tstamp = 0; if (th->doff > (sizeof(*th) >> 2) && tcptw->tw_ts_recent_stamp) { +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_parse_options(skb, &tmp_opt, 0, NULL); +#else + mptcp_init_mp_opt(&mopt); + + tcp_parse_options(skb, &tmp_opt, &mopt, 0, NULL, NULL); +#endif if (tmp_opt.saw_tstamp) { tmp_opt.rcv_tsecr -= tcptw->tw_ts_offset; @@ -114,6 +129,13 @@ tmp_opt.ts_recent_stamp = tcptw->tw_ts_recent_stamp; paws_reject = tcp_paws_reject(&tmp_opt, th->rst); } +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + + if (unlikely(mopt.mp_fclose) && tcptw->mptcp_tw) { + if (mopt.mptcp_sender_key == tcptw->mptcp_tw->loc_key) + goto kill_with_rst; + } +#endif } if (tw->tw_substate == TCP_FIN_WAIT2) { @@ -137,6 +159,18 @@ if (!th->ack || !after(TCP_SKB_CB(skb)->end_seq, tcptw->tw_rcv_nxt) || TCP_SKB_CB(skb)->end_seq == TCP_SKB_CB(skb)->seq) { +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + /* If mptcp_is_data_fin() returns true, we are sure that + * mopt has been initialized - otherwise it would not + * be a DATA_FIN. + */ + if (tcptw->mptcp_tw && tcptw->mptcp_tw->meta_tw && + mptcp_is_data_fin(skb) && + TCP_SKB_CB(skb)->seq == tcptw->tw_rcv_nxt && + mopt.data_seq + 1 == (u32)tcptw->mptcp_tw->rcv_nxt) + return TCP_TW_ACK; + +#endif inet_twsk_put(tw); return TCP_TW_SUCCESS; } @@ -293,6 +327,17 @@ tcptw->tw_ts_offset = tp->tsoffset; tcptw->tw_last_oow_ack_time = 0; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (mptcp(tp)) { + if (mptcp_init_tw_sock(sk, tcptw)) { + inet_twsk_free(tw); + goto exit; + } + } else { + tcptw->mptcp_tw = NULL; + } + +#endif #if IS_ENABLED(CONFIG_IPV6) if (tw->tw_family == PF_INET6) { struct ipv6_pinfo *np = inet6_sk(sk); @@ -348,10 +393,27 @@ NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPTIMEWAITOVERFLOW); } +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +exit: +#endif tcp_update_metrics(sk); tcp_done(sk); } +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +void tcp_twsk_destructor(struct sock *sk) +{ + struct tcp_timewait_sock *twsk = tcp_twsk(sk); + + if (twsk->mptcp_tw) + mptcp_twsk_destructor(twsk); + +#ifdef CONFIG_TCP_MD5SIG + if (twsk->tw_md5_key) + kfree_rcu(twsk->tw_md5_key, rcu); +#endif +} +#else void tcp_twsk_destructor(struct sock *sk) { #ifdef CONFIG_TCP_MD5SIG @@ -361,6 +423,7 @@ kfree_rcu(twsk->tw_md5_key, rcu); #endif } +#endif EXPORT_SYMBOL_GPL(tcp_twsk_destructor); void tcp_openreq_init_rwin(struct request_sock *req, @@ -383,13 +446,23 @@ req->window_clamp = tcp_full_space(sk); /* tcp_full_space because it is guaranteed to be the first packet */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_select_initial_window(tcp_full_space(sk), mss - (ireq->tstamp_ok ? TCPOLEN_TSTAMP_ALIGNED : 0), +#else + tp->ops->select_initial_window(tcp_full_space(sk), + mss - (ireq->tstamp_ok ? TCPOLEN_TSTAMP_ALIGNED : 0) - + (ireq->saw_mpc ? MPTCP_SUB_LEN_DSM_ALIGN : 0), +#endif &req->rcv_wnd, &req->window_clamp, ireq->wscale_ok, &rcv_wscale, +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) dst_metric(dst, RTAX_INITRWND)); +#else + dst_metric(dst, RTAX_INITRWND), sk); +#endif ireq->rcv_wscale = rcv_wscale; } EXPORT_SYMBOL(tcp_openreq_init_rwin); @@ -455,7 +528,9 @@ newtp->snd_nxt = newtp->snd_up = treq->snt_isn + 1; tcp_prequeue_init(newtp); +#if !defined(CONFIG_BCM_KF_TCP_NO_TSQ) INIT_LIST_HEAD(&newtp->tsq_node); +#endif tcp_init_wl(newtp, treq->rcv_isn); @@ -526,6 +601,10 @@ newtp->rx_opt.ts_recent_stamp = 0; newtp->tcp_header_len = sizeof(struct tcphdr); } +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (ireq->saw_mpc) + newtp->tcp_header_len += MPTCP_SUB_LEN_DSM_ALIGN; +#endif newtp->tsoffset = 0; #ifdef CONFIG_TCP_MD5SIG newtp->md5sig_info = NULL; /*XXX*/ @@ -561,16 +640,32 @@ bool fastopen) { struct tcp_options_received tmp_opt; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + struct mptcp_options_received mopt; +#endif struct sock *child; const struct tcphdr *th = tcp_hdr(skb); __be32 flg = tcp_flag_word(th) & (TCP_FLAG_RST|TCP_FLAG_SYN|TCP_FLAG_ACK); bool paws_reject = false; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) BUG_ON(fastopen == (sk->sk_state == TCP_LISTEN)); +#else + BUG_ON(!mptcp(tcp_sk(sk)) && fastopen == (sk->sk_state == TCP_LISTEN)); +#endif tmp_opt.saw_tstamp = 0; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + + mptcp_init_mp_opt(&mopt); + +#endif if (th->doff > (sizeof(struct tcphdr)>>2)) { +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_parse_options(skb, &tmp_opt, 0, NULL); +#else + tcp_parse_options(skb, &tmp_opt, &mopt, 0, NULL, NULL); +#endif if (tmp_opt.saw_tstamp) { tmp_opt.ts_recent = req->ts_recent; @@ -610,6 +705,15 @@ * Reset timer after retransmitting SYNACK, similar to * the idea of fast retransmit in recovery. */ + +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + /* Fall back to TCP if MP_CAPABLE is not set.i */ + + if (inet_rsk(req)->saw_mpc && !mopt.saw_mpc) + inet_rsk(req)->saw_mpc = false; + + +#endif if (!tcp_oow_rate_limited(sock_net(sk), skb, LINUX_MIB_TCPACKSKIPPEDSYNRECV, &tcp_rsk(req)->last_oow_ack_time) && @@ -758,6 +862,20 @@ if (!child) goto listen_overflow; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (!is_meta_sk(sk)) { + int ret = mptcp_check_req_master(sk, child, req, 1); + if (ret < 0) + goto listen_overflow; + + /* MPTCP-supported */ + if (!ret) + return tcp_sk(child)->mpcb->master_sk; + } else { + return mptcp_check_req_child(sk, child, req, &mopt); + } + +#endif inet_csk_reqsk_queue_drop(sk, req); inet_csk_reqsk_queue_add(sk, req, child); /* Warning: caller must not call reqsk_put(req); @@ -808,8 +926,15 @@ { int ret = 0; int state = child->sk_state; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + struct sock *meta_sk = mptcp(tcp_sk(child)) ? mptcp_meta_sk(child) : child; +#endif +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (!sock_owned_by_user(child)) { +#else + if (!sock_owned_by_user(meta_sk)) { +#endif ret = tcp_rcv_state_process(child, skb, tcp_hdr(skb), skb->len); /* Wakeup parent, send SIGIO */ @@ -820,10 +945,22 @@ * in main socket hash table and lock on listening * socket does not protect us more. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) __sk_add_backlog(child, skb); +#else + if (mptcp(tcp_sk(child))) + skb->sk = child; + __sk_add_backlog(meta_sk, skb); +#endif } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) bh_unlock_sock(child); +#else + if (mptcp(tcp_sk(child))) + bh_unlock_sock(child); + bh_unlock_sock(meta_sk); +#endif sock_put(child); return ret; } diff -urN linux-4.1.52-orig/net/ipv4/tcp_output.c linux-4.1.52/net/ipv4/tcp_output.c --- linux-4.1.52-orig/net/ipv4/tcp_output.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/tcp_output.c 2020-02-07 01:47:54.000000000 +0800 @@ -36,6 +36,14 @@ #define pr_fmt(fmt) "TCP: " fmt +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#include +#include +#if IS_ENABLED(CONFIG_IPV6) +#include +#endif +#include +#endif #include #include @@ -65,11 +73,17 @@ unsigned int sysctl_tcp_notsent_lowat __read_mostly = UINT_MAX; EXPORT_SYMBOL(sysctl_tcp_notsent_lowat); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, int push_one, gfp_t gfp); +#endif /* Account for new data that has been sent to the network. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static void tcp_event_new_data_sent(struct sock *sk, const struct sk_buff *skb) +#else +void tcp_event_new_data_sent(struct sock *sk, const struct sk_buff *skb) +#endif { struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); @@ -211,7 +225,11 @@ void tcp_select_initial_window(int __space, __u32 mss, __u32 *rcv_wnd, __u32 *window_clamp, int wscale_ok, __u8 *rcv_wscale, +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) __u32 init_rcv_wnd) +#else + __u32 init_rcv_wnd, const struct sock *sk) +#endif { unsigned int space = (__space < 0 ? 0 : __space); @@ -266,12 +284,25 @@ * value can be stuffed directly into th->window for an outgoing * frame. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static u16 tcp_select_window(struct sock *sk) +#else +u16 tcp_select_window(struct sock *sk) +#endif { struct tcp_sock *tp = tcp_sk(sk); u32 old_win = tp->rcv_wnd; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) u32 cur_win = tcp_receive_window(tp); u32 new_win = __tcp_select_window(sk); +#else + /* The window must never shrink at the meta-level. At the subflow we + * have to allow this. Otherwise we may announce a window too large + * for the current meta-level sk_rcvbuf. + */ + u32 cur_win = tcp_receive_window(mptcp(tp) ? tcp_sk(mptcp_meta_sk(sk)) : tp); + u32 new_win = tp->ops->__select_window(sk); +#endif /* Never shrink the offered window */ if (new_win < cur_win) { @@ -287,6 +318,9 @@ LINUX_MIB_TCPWANTZEROWINDOWADV); new_win = ALIGN(cur_win, 1 << tp->rx_opt.rcv_wscale); } +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + +#endif tp->rcv_wnd = new_win; tp->rcv_wup = tp->rcv_nxt; @@ -391,7 +425,11 @@ /* Constructs common control bits of non-data skb. If SYN/FIN is present, * auto increment end seqno. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static void tcp_init_nondata_skb(struct sk_buff *skb, u32 seq, u8 flags) +#else +void tcp_init_nondata_skb(struct sk_buff *skb, u32 seq, u8 flags) +#endif { struct skb_shared_info *shinfo = skb_shinfo(skb); @@ -411,7 +449,11 @@ TCP_SKB_CB(skb)->end_seq = seq; } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static inline bool tcp_urg_mode(const struct tcp_sock *tp) +#else +bool tcp_urg_mode(const struct tcp_sock *tp) +#endif { return tp->snd_una != tp->snd_up; } @@ -421,6 +463,7 @@ #define OPTION_MD5 (1 << 2) #define OPTION_WSCALE (1 << 3) #define OPTION_FAST_OPEN_COOKIE (1 << 8) +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) struct tcp_out_options { u16 options; /* bit field of OPTION_* */ @@ -432,6 +475,9 @@ __u32 tsval, tsecr; /* need to include OPTION_TS */ struct tcp_fastopen_cookie *fastopen_cookie; /* Fast open cookie */ }; +#else +/* Before adding here - take a look at OPTION_MPTCP in include/net/mptcp.h */ +#endif /* Write previously computed TCP options to the packet. * @@ -447,7 +493,11 @@ * (but it may well be that other scenarios fail similarly). */ static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp, +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) struct tcp_out_options *opts) +#else + struct tcp_out_options *opts, struct sk_buff *skb) +#endif { u16 options = opts->options; /* mungable copy */ @@ -539,6 +589,11 @@ } ptr += (len + 3) >> 2; } +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + + if (unlikely(OPTION_MPTCP & opts->options)) + mptcp_options_write(ptr, tp, opts, skb); +#endif } /* Compute TCP options for SYN packets. This is not the final @@ -590,6 +645,10 @@ if (unlikely(!(OPTION_TS & opts->options))) remaining -= TCPOLEN_SACKPERM_ALIGNED; } +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (tp->request_mptcp || mptcp(tp)) + mptcp_syn_options(sk, opts, &remaining); +#endif if (fastopen && fastopen->cookie.len >= 0) { u32 need = fastopen->cookie.len; @@ -667,6 +726,11 @@ } } +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (ireq->saw_mpc) + mptcp_synack_options(req, opts, &remaining); + +#endif return MAX_TCP_OPTION_SPACE - remaining; } @@ -699,9 +763,14 @@ opts->tsecr = tp->rx_opt.ts_recent; size += TCPOLEN_TSTAMP_ALIGNED; } +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (mptcp(tp)) + mptcp_established_options(sk, skb, opts, &size); +#endif eff_sacks = tp->rx_opt.num_sacks + tp->rx_opt.dsack; if (unlikely(eff_sacks)) { +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) const unsigned int remaining = MAX_TCP_OPTION_SPACE - size; opts->num_sack_blocks = min_t(unsigned int, eff_sacks, @@ -709,12 +778,26 @@ TCPOLEN_SACK_PERBLOCK); size += TCPOLEN_SACK_BASE_ALIGNED + opts->num_sack_blocks * TCPOLEN_SACK_PERBLOCK; +#else + const unsigned remaining = MAX_TCP_OPTION_SPACE - size; + if (remaining < TCPOLEN_SACK_BASE_ALIGNED) + opts->num_sack_blocks = 0; + else + opts->num_sack_blocks = + min_t(unsigned int, eff_sacks, + (remaining - TCPOLEN_SACK_BASE_ALIGNED) / + TCPOLEN_SACK_PERBLOCK); + if (opts->num_sack_blocks) + size += TCPOLEN_SACK_BASE_ALIGNED + + opts->num_sack_blocks * TCPOLEN_SACK_PERBLOCK; +#endif } return size; } +#if !defined(CONFIG_BCM_KF_TCP_NO_TSQ) /* TCP SMALL QUEUES (TSQ) * * TSQ goal is to keep small amount of skbs per tcp flow in tx queues (qdisc+dev) @@ -740,8 +823,13 @@ if ((1 << sk->sk_state) & (TCPF_ESTABLISHED | TCPF_FIN_WAIT1 | TCPF_CLOSING | TCPF_CLOSE_WAIT | TCPF_LAST_ACK)) +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_write_xmit(sk, tcp_current_mss(sk), tcp_sk(sk)->nonagle, 0, GFP_ATOMIC); +#else + tcp_sk(sk)->ops->write_xmit(sk, tcp_current_mss(sk), + tcp_sk(sk)->nonagle, 0, GFP_ATOMIC); +#endif } /* * One tasklet per cpu tries to send more skbs. @@ -756,7 +844,11 @@ unsigned long flags; struct list_head *q, *n; struct tcp_sock *tp; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) struct sock *sk; +#else + struct sock *sk, *meta_sk; +#endif local_irq_save(flags); list_splice_init(&tsq->head, &list); @@ -767,25 +859,77 @@ list_del(&tp->tsq_node); sk = (struct sock *)tp; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) bh_lock_sock(sk); +#else + meta_sk = mptcp(tp) ? mptcp_meta_sk(sk) : sk; + bh_lock_sock(meta_sk); +#endif +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (!sock_owned_by_user(sk)) { +#else + if (!sock_owned_by_user(meta_sk)) { +#endif tcp_tsq_handler(sk); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (mptcp(tp)) + tcp_tsq_handler(meta_sk); +#endif } else { +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (mptcp(tp) && sk->sk_state == TCP_CLOSE) + goto exit; + +#endif /* defer the work to tcp_release_cb() */ set_bit(TCP_TSQ_DEFERRED, &tp->tsq_flags); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + + if (mptcp(tp)) + mptcp_tsq_flags(sk); +#endif } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) bh_unlock_sock(sk); +#else +exit: + bh_unlock_sock(meta_sk); +#endif clear_bit(TSQ_QUEUED, &tp->tsq_flags); sk_free(sk); } } +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) #define TCP_DEFERRED_ALL ((1UL << TCP_TSQ_DEFERRED) | \ (1UL << TCP_WRITE_TIMER_DEFERRED) | \ (1UL << TCP_DELACK_TIMER_DEFERRED) | \ + (1UL << MPTCP_PATH_MANAGER) | \ + (1UL << MPTCP_SUB_DEFERRED) | \ + (1UL << TCP_MTU_REDUCED_DEFERRED)) +#else +#define TCP_DEFERRED_ALL ((1UL << TCP_TSQ_DEFERRED) | \ + (1UL << TCP_WRITE_TIMER_DEFERRED) | \ + (1UL << TCP_DELACK_TIMER_DEFERRED) | \ + (1UL << TCP_MTU_REDUCED_DEFERRED)) + +#endif +#else + +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#define TCP_DEFERRED_ALL ((1UL << TCP_WRITE_TIMER_DEFERRED) | \ + (1UL << TCP_DELACK_TIMER_DEFERRED) | \ + (1UL << MPTCP_PATH_MANAGER) | \ + (1UL << MPTCP_SUB_DEFERRED) | \ (1UL << TCP_MTU_REDUCED_DEFERRED)) +#else +#define TCP_DEFERRED_ALL ((1UL << TCP_WRITE_TIMER_DEFERRED) | \ + (1UL << TCP_DELACK_TIMER_DEFERRED) | \ + (1UL << TCP_MTU_REDUCED_DEFERRED)) +#endif +#endif /** * tcp_release_cb - tcp release_sock() callback * @sk: socket @@ -806,8 +950,10 @@ nflags = flags & ~TCP_DEFERRED_ALL; } while (cmpxchg(&tp->tsq_flags, flags, nflags) != flags); +#if !defined(CONFIG_BCM_KF_TCP_NO_TSQ) if (flags & (1UL << TCP_TSQ_DEFERRED)) tcp_tsq_handler(sk); +#endif /* Here begins the tricky part : * We are called from release_sock() with : @@ -832,9 +978,19 @@ inet_csk(sk)->icsk_af_ops->mtu_reduced(sk); __sock_put(sk); } +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (flags & (1UL << MPTCP_PATH_MANAGER)) { + if (tcp_sk(sk)->mpcb->pm_ops->release_sock) + tcp_sk(sk)->mpcb->pm_ops->release_sock(sk); + __sock_put(sk); + } + if (flags & (1UL << MPTCP_SUB_DEFERRED)) + mptcp_tsq_sub_deferred(sk); +#endif } EXPORT_SYMBOL(tcp_release_cb); +#if !defined(CONFIG_BCM_KF_TCP_NO_TSQ) void __init tcp_tasklet_init(void) { int i; @@ -848,12 +1004,30 @@ (unsigned long)tsq); } } +#endif /* * Write buffer destructor automatically called from kfree_skb. * We can't xmit new skbs from this context, as we might already * hold qdisc lock. */ +#if defined(CONFIG_BCM_KF_TCP_NO_TSQ) +void tcp_wfree(struct sk_buff *skb) +{ + struct sock *sk = skb->sk; + + /* + * Keep a reference on sk_wmem_alloc, this will be released + * after sk_write_space() call + */ + atomic_sub_return(skb->truesize - 1, &sk->sk_wmem_alloc); + + if (!sock_flag(sk, SOCK_USE_WRITE_QUEUE)) { + sk->sk_write_space(sk); + } + sk_free(sk); +} +#else void tcp_wfree(struct sk_buff *skb) { struct sock *sk = skb->sk; @@ -891,6 +1065,7 @@ out: sk_free(sk); } +#endif /* This routine actually transmits TCP packets queued in by * tcp_do_sendmsg(). This is used by both the initial @@ -903,8 +1078,13 @@ * We are working here with either a clone of the original * SKB, or a fresh unique copy made by the retransmit engine. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, gfp_t gfp_mask) +#else +int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, + gfp_t gfp_mask) +#endif { const struct inet_connection_sock *icsk = inet_csk(sk); struct inet_sock *inet; @@ -956,11 +1136,15 @@ skb_push(skb, tcp_header_size); skb_reset_transport_header(skb); +#if defined(CONFIG_BCM_KF_TCP_NO_TSQ) + skb_set_owner_w(skb, sk); +#else skb_orphan(skb); skb->sk = sk; skb->destructor = skb_is_tcp_pure_ack(skb) ? sock_wfree : tcp_wfree; skb_set_hash_from_sk(skb, sk); atomic_add(skb->truesize, &sk->sk_wmem_alloc); +#endif /* Build TCP header and checksum it. */ th = tcp_hdr(skb); @@ -977,7 +1161,11 @@ */ th->window = htons(min(tp->rcv_wnd, 65535U)); } else { +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) th->window = htons(tcp_select_window(sk)); +#else + th->window = htons(tp->ops->select_window(sk)); +#endif } th->check = 0; th->urg_ptr = 0; @@ -993,7 +1181,11 @@ } } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_options_write((__be32 *)(th + 1), tp, &opts); +#else + tcp_options_write((__be32 *)(th + 1), tp, &opts, skb); +#endif if (likely((tcb->tcp_flags & TCPHDR_SYN) == 0)) tcp_ecn_send(sk, skb, tcp_header_size); @@ -1043,7 +1235,11 @@ * NOTE: probe0 timer is not checked, do not forget tcp_push_pending_frames, * otherwise socket can stall. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static void tcp_queue_skb(struct sock *sk, struct sk_buff *skb) +#else +void tcp_queue_skb(struct sock *sk, struct sk_buff *skb) +#endif { struct tcp_sock *tp = tcp_sk(sk); @@ -1056,15 +1252,25 @@ } /* Initialize TSO segments for a packet. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb, unsigned int mss_now) +#else +void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb, + unsigned int mss_now) +#endif { struct skb_shared_info *shinfo = skb_shinfo(skb); /* Make sure we own this skb before messing gso_size/gso_segs */ WARN_ON_ONCE(skb_cloned(skb)); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (skb->len <= mss_now || skb->ip_summed == CHECKSUM_NONE) { +#else + if (skb->len <= mss_now || (is_meta_sk(sk) && !mptcp_sk_can_gso(sk)) || + (!is_meta_sk(sk) && !sk_can_gso(sk)) || skb->ip_summed == CHECKSUM_NONE) { +#endif /* Avoid the costly divide in the normal * non-TSO case. */ @@ -1096,7 +1302,11 @@ /* Pcount in the middle of the write queue got changed, we need to do various * tweaks to fix counters */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static void tcp_adjust_pcount(struct sock *sk, const struct sk_buff *skb, int decr) +#else +void tcp_adjust_pcount(struct sock *sk, const struct sk_buff *skb, int decr) +#endif { struct tcp_sock *tp = tcp_sk(sk); @@ -1158,6 +1368,14 @@ nsize = skb_headlen(skb) - len; if (nsize < 0) nsize = 0; +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +/*CVE-2019-11478*/ +/*CVE-2019-11479*/ + if (unlikely((sk->sk_wmem_queued >> 1) > sk->sk_sndbuf + 0x20000)) { + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPWQUEUETOOBIG); + return -ENOMEM; + } +#endif if (skb_unclone(skb, gfp)) return -ENOMEM; @@ -1231,7 +1449,11 @@ * eventually). The difference is that pulled data not copied, but * immediately discarded. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static int __pskb_trim_head(struct sk_buff *skb, int len) +#else +int __pskb_trim_head(struct sk_buff *skb, int len) +#endif { struct skb_shared_info *shinfo; int i, k, eat; @@ -1325,8 +1547,13 @@ mss_now -= icsk->icsk_ext_hdr_len; /* Then reserve room for full set of TCP options and 8 bytes of data */ +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +/*CVE-2019-11479*/ + mss_now = max(mss_now, sock_net(sk)->ipv4.sysctl_tcp_min_snd_mss); +#else if (mss_now < 48) mss_now = 48; +#endif //#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) return mss_now; } @@ -1454,6 +1681,9 @@ return mss_now; } +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +EXPORT_SYMBOL(tcp_current_mss); +#endif /* RFC2861, slow part. Adjust cwnd, after it was not full during one rto. * As additional protections, we do not touch cwnd in retransmission phases, @@ -1477,7 +1707,11 @@ tp->snd_cwnd_stamp = tcp_time_stamp; } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static void tcp_cwnd_validate(struct sock *sk, bool is_cwnd_limited) +#else +void tcp_cwnd_validate(struct sock *sk, bool is_cwnd_limited) +#endif { struct tcp_sock *tp = tcp_sk(sk); @@ -1521,8 +1755,13 @@ * But we can avoid doing the divide again given we already have * skb_pcount = skb->len / mss_now */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static void tcp_minshall_update(struct tcp_sock *tp, unsigned int mss_now, const struct sk_buff *skb) +#else +void tcp_minshall_update(struct tcp_sock *tp, unsigned int mss_now, + const struct sk_buff *skb) +#endif { if (skb->len < tcp_skb_pcount(skb) * mss_now) tp->snd_sml = TCP_SKB_CB(skb)->end_seq; @@ -1564,11 +1803,19 @@ } /* Returns the portion of skb which can be sent right away */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static unsigned int tcp_mss_split_point(const struct sock *sk, const struct sk_buff *skb, unsigned int mss_now, unsigned int max_segs, int nonagle) +#else +unsigned int tcp_mss_split_point(const struct sock *sk, + const struct sk_buff *skb, + unsigned int mss_now, + unsigned int max_segs, + int nonagle) +#endif { const struct tcp_sock *tp = tcp_sk(sk); u32 partial, needed, window, max_len; @@ -1598,13 +1845,23 @@ /* Can at least one segment of SKB be sent right now, according to the * congestion window rules? If so, return how many segments are allowed. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static inline unsigned int tcp_cwnd_test(const struct tcp_sock *tp, const struct sk_buff *skb) +#else +unsigned int tcp_cwnd_test(const struct tcp_sock *tp, + const struct sk_buff *skb) +#endif { u32 in_flight, cwnd, halfcwnd; /* Don't be strict about the congestion window for the final FIN. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if ((TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) && +#else + if (skb && + (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) && +#endif tcp_skb_pcount(skb) == 1) return 1; @@ -1624,8 +1881,13 @@ * This must be invoked the first time we consider transmitting * SKB onto the wire. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static int tcp_init_tso_segs(const struct sock *sk, struct sk_buff *skb, unsigned int mss_now) +#else +int tcp_init_tso_segs(const struct sock *sk, struct sk_buff *skb, + unsigned int mss_now) +#endif { int tso_segs = tcp_skb_pcount(skb); @@ -1640,8 +1902,13 @@ /* Return true if the Nagle test allows this packet to be * sent now. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static inline bool tcp_nagle_test(const struct tcp_sock *tp, const struct sk_buff *skb, unsigned int cur_mss, int nonagle) +#else +bool tcp_nagle_test(const struct tcp_sock *tp, const struct sk_buff *skb, + unsigned int cur_mss, int nonagle) +#endif { /* Nagle rule does not apply to frames, which sit in the middle of the * write_queue (they have no chances to get new data). @@ -1653,7 +1920,12 @@ return true; /* Don't use the nagle rule for urgent data (or for the final FIN). */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (tcp_urg_mode(tp) || (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN)) +#else + if (tcp_urg_mode(tp) || (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) || + mptcp_is_data_fin(skb)) +#endif return true; if (!tcp_nagle_check(skb->len < cur_mss, tp, nonagle)) @@ -1663,9 +1935,14 @@ } /* Does at least the first segment of SKB fit into the send window? */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static bool tcp_snd_wnd_test(const struct tcp_sock *tp, const struct sk_buff *skb, unsigned int cur_mss) +#else +bool tcp_snd_wnd_test(const struct tcp_sock *tp, const struct sk_buff *skb, + unsigned int cur_mss) +#endif { u32 end_seq = TCP_SKB_CB(skb)->end_seq; @@ -1779,7 +2056,11 @@ struct sk_buff *head; int win_divisor; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) +#else + if ((TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) || mptcp_is_data_fin(skb)) +#endif goto send_now; if (!((1 << icsk->icsk_ca_state) & (TCPF_CA_Open | TCPF_CA_CWR))) @@ -2035,7 +2316,11 @@ * Returns true, if no segments are in flight and we have queued segments, * but cannot send anything now because of SWS or another problem. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, +#else +bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, +#endif int push_one, gfp_t gfp) { struct tcp_sock *tp = tcp_sk(sk); @@ -2048,7 +2333,15 @@ sent_pkts = 0; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (!push_one) { +#else + /* pmtu not yet supported with MPTCP. Should be possible, by early + * exiting the loop inside tcp_mtu_probe, making sure that only one + * single DSS-mapping gets probed. + */ + if (!push_one && !mptcp(tp)) { +#endif /* Do MTU probing. */ result = tcp_mtu_probe(sk); if (!result) { @@ -2108,6 +2401,7 @@ unlikely(tso_fragment(sk, skb, limit, mss_now, gfp))) break; +#if !defined(CONFIG_BCM_KF_TCP_NO_TSQ) /* TCP Small Queues : * Control number of packets in qdisc/devices to two packets / or ~1 ms. * This allows for : @@ -2132,6 +2426,7 @@ break; } +#endif if (unlikely(tcp_transmit_skb(sk, skb, 1, gfp))) break; @@ -2155,7 +2450,12 @@ /* Send one loss probe per tail loss episode. */ if (push_one != 2) tcp_schedule_loss_probe(sk); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_cwnd_validate(sk, is_cwnd_limited); +#else + if (tp->ops->cwnd_validate) + tp->ops->cwnd_validate(sk, is_cwnd_limited); +#endif return false; } return (push_one == 2) || (!tp->packets_out && tcp_send_head(sk)); @@ -2247,7 +2547,12 @@ int err = -1; if (tcp_send_head(sk)) { +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) err = tcp_write_xmit(sk, mss, TCP_NAGLE_OFF, 2, GFP_ATOMIC); +#else + err = tp->ops->write_xmit(sk, mss, TCP_NAGLE_OFF, 2, + GFP_ATOMIC); +#endif goto rearm_timer; } @@ -2307,8 +2612,13 @@ if (unlikely(sk->sk_state == TCP_CLOSE)) return; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (tcp_write_xmit(sk, cur_mss, nonagle, 0, sk_gfp_atomic(sk, GFP_ATOMIC))) +#else + if (tcp_sk(sk)->ops->write_xmit(sk, cur_mss, nonagle, 0, + sk_gfp_atomic(sk, GFP_ATOMIC))) +#endif tcp_check_probe_timer(sk); } @@ -2321,7 +2631,12 @@ BUG_ON(!skb || skb->len < mss_now); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_write_xmit(sk, mss_now, TCP_NAGLE_PUSH, 1, sk->sk_allocation); +#else + tcp_sk(sk)->ops->write_xmit(sk, mss_now, TCP_NAGLE_PUSH, 1, + sk->sk_allocation); +#endif } /* This function returns the amount that we can raise the @@ -2534,6 +2849,12 @@ if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_SYN) return; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + /* Currently not supported for MPTCP - but it should be possible */ + if (mptcp(tp)) + return; + +#endif tcp_for_write_queue_from_safe(skb, tmp, sk) { if (!tcp_can_collapse(sk, skb)) break; @@ -3012,7 +3333,11 @@ /* RFC1323: The window in SYN & SYN/ACK segments is never scaled. */ th->window = htons(min(req->rcv_wnd, 65535U)); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_options_write((__be32 *)(th + 1), tp, &opts); +#else + tcp_options_write((__be32 *)(th + 1), tp, &opts, skb); +#endif th->doff = (tcp_header_size >> 2); TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_OUTSEGS); @@ -3089,6 +3414,7 @@ (tp->window_clamp > tcp_full_space(sk) || tp->window_clamp == 0)) tp->window_clamp = tcp_full_space(sk); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_select_initial_window(tcp_full_space(sk), tp->advmss - (tp->rx_opt.ts_recent_stamp ? tp->tcp_header_len - sizeof(struct tcphdr) : 0), &tp->rcv_wnd, @@ -3096,6 +3422,15 @@ sysctl_tcp_window_scaling, &rcv_wscale, dst_metric(dst, RTAX_INITRWND)); +#else + tp->ops->select_initial_window(tcp_full_space(sk), + tp->advmss - (tp->rx_opt.ts_recent_stamp ? tp->tcp_header_len - sizeof(struct tcphdr) : 0), + &tp->rcv_wnd, + &tp->window_clamp, + sysctl_tcp_window_scaling, + &rcv_wscale, + dst_metric(dst, RTAX_INITRWND), sk); +#endif tp->rx_opt.rcv_wscale = rcv_wscale; tp->rcv_ssthresh = tp->rcv_wnd; @@ -3119,6 +3454,38 @@ inet_csk(sk)->icsk_rto = TCP_TIMEOUT_INIT; inet_csk(sk)->icsk_retransmits = 0; tcp_clear_retrans(tp); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + +#ifdef CONFIG_MPTCP + if (sock_flag(sk, SOCK_MPTCP) && mptcp_doit(sk)) { + if (is_master_tp(tp)) { + tp->request_mptcp = 1; + mptcp_connect_init(sk); + } else if (tp->mptcp) { + struct inet_sock *inet = inet_sk(sk); + + tp->mptcp->snt_isn = tp->write_seq; + tp->mptcp->init_rcv_wnd = tp->rcv_wnd; + + /* Set nonce for new subflows */ + if (sk->sk_family == AF_INET) + tp->mptcp->mptcp_loc_nonce = mptcp_v4_get_nonce( + inet->inet_saddr, + inet->inet_daddr, + inet->inet_sport, + inet->inet_dport); +#if IS_ENABLED(CONFIG_IPV6) + else + tp->mptcp->mptcp_loc_nonce = mptcp_v6_get_nonce( + inet6_sk(sk)->saddr.s6_addr32, + sk->sk_v6_daddr.s6_addr32, + inet->inet_sport, + inet->inet_dport); +#endif + } + } +#endif +#endif } static void tcp_connect_queue_skb(struct sock *sk, struct sk_buff *skb) @@ -3387,7 +3754,11 @@ * one is with SEG.SEQ=SND.UNA to deliver urgent pointer, another is * out-of-date with SND.UNA-1 to probe window. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static int tcp_xmit_probe_skb(struct sock *sk, int urgent) +#else +int tcp_xmit_probe_skb(struct sock *sk, int urgent) +#endif { struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *skb; @@ -3469,7 +3840,11 @@ unsigned long probe_max; int err; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) err = tcp_write_wakeup(sk); +#else + err = tp->ops->write_wakeup(sk); +#endif if (tp->packets_out || !tcp_send_head(sk)) { /* Cancel probe timer, if it is not required. */ diff -urN linux-4.1.52-orig/net/ipv4/tcp_timer.c linux-4.1.52/net/ipv4/tcp_timer.c --- linux-4.1.52-orig/net/ipv4/tcp_timer.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv4/tcp_timer.c 2020-02-07 01:47:54.000000000 +0800 @@ -20,6 +20,9 @@ #include #include +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#include +#endif #include int sysctl_tcp_syn_retries __read_mostly = TCP_SYN_RETRIES; @@ -32,7 +35,11 @@ int sysctl_tcp_orphan_retries __read_mostly; int sysctl_tcp_thin_linear_timeouts __read_mostly; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static void tcp_write_err(struct sock *sk) +#else +void tcp_write_err(struct sock *sk) +#endif { sk->sk_err = sk->sk_err_soft ? : ETIMEDOUT; sk->sk_error_report(sk); @@ -82,7 +89,11 @@ (!tp->snd_wnd && !tp->packets_out)) do_reset = true; if (do_reset) +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_send_active_reset(sk, GFP_ATOMIC); +#else + tp->ops->send_active_reset(sk, GFP_ATOMIC); +#endif tcp_done(sk); NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPABORTONMEMORY); return 1; @@ -132,6 +143,10 @@ mss = tcp_mtu_to_mss(sk, icsk->icsk_mtup.search_low) >> 1; mss = min(net->ipv4.sysctl_tcp_base_mss, mss); mss = max(mss, 68 - tp->tcp_header_len); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +/*CVE-2019-11479*/ + mss = max(mss, net->ipv4.sysctl_tcp_min_snd_mss); +#endif icsk->icsk_mtup.search_low = tcp_mss_to_mtu(sk, mss); tcp_sync_mss(sk, icsk->icsk_pmtu_cookie); } @@ -143,10 +158,15 @@ * retransmissions with an initial RTO of TCP_RTO_MIN or TCP_TIMEOUT_INIT if * syn_set flag is set. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static bool retransmits_timed_out(struct sock *sk, unsigned int boundary, unsigned int timeout, bool syn_set) +#else +bool retransmits_timed_out(struct sock *sk, unsigned int boundary, + unsigned int timeout, bool syn_set) +#endif { unsigned int linear_backoff_thresh, start_ts; unsigned int rto_base = syn_set ? TCP_TIMEOUT_INIT : TCP_RTO_MIN; @@ -171,7 +191,11 @@ } /* A write timeout has occurred. Process the after effects. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static int tcp_write_timeout(struct sock *sk) +#else +int tcp_write_timeout(struct sock *sk) +#endif { struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); @@ -189,6 +213,18 @@ } retry_until = icsk->icsk_syn_retries ? : sysctl_tcp_syn_retries; syn_set = true; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + +#ifdef CONFIG_MPTCP + /* Stop retransmitting MP_CAPABLE options in SYN if timed out. */ + if (tcp_sk(sk)->request_mptcp && + icsk->icsk_retransmits >= sysctl_mptcp_syn_retries) { + tcp_sk(sk)->request_mptcp = 0; + + MPTCP_INC_STATS_BH(sock_net(sk), MPTCP_MIB_MPCAPABLERETRANSFALLBACK); + } +#endif /* CONFIG_MPTCP */ +#endif } else { if (retransmits_timed_out(sk, sysctl_tcp_retries1, 0, 0)) { /* Some middle-boxes may black-hole Fast Open _after_ @@ -282,18 +318,39 @@ static void tcp_delack_timer(unsigned long data) { struct sock *sk = (struct sock *)data; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + struct tcp_sock *tp = tcp_sk(sk); + struct sock *meta_sk = mptcp(tp) ? mptcp_meta_sk(sk) : sk; +#endif +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) bh_lock_sock(sk); if (!sock_owned_by_user(sk)) { +#else + bh_lock_sock(meta_sk); + if (!sock_owned_by_user(meta_sk)) { +#endif tcp_delack_timer_handler(sk); } else { inet_csk(sk)->icsk_ack.blocked = 1; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_DELAYEDACKLOCKED); +#else + NET_INC_STATS_BH(sock_net(meta_sk), LINUX_MIB_DELAYEDACKLOCKED); +#endif /* deleguate our work to tcp_release_cb() */ if (!test_and_set_bit(TCP_DELACK_TIMER_DEFERRED, &tcp_sk(sk)->tsq_flags)) sock_hold(sk); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (mptcp(tp)) + mptcp_tsq_flags(sk); +#endif } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) bh_unlock_sock(sk); +#else + bh_unlock_sock(meta_sk); +#endif sock_put(sk); } @@ -540,7 +597,11 @@ break; case ICSK_TIME_RETRANS: icsk->icsk_pending = 0; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_retransmit_timer(sk); +#else + tcp_sk(sk)->ops->retransmit_timer(sk); +#endif break; case ICSK_TIME_PROBE0: icsk->icsk_pending = 0; @@ -555,16 +616,32 @@ static void tcp_write_timer(unsigned long data) { struct sock *sk = (struct sock *)data; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + struct sock *meta_sk = mptcp(tcp_sk(sk)) ? mptcp_meta_sk(sk) : sk; +#endif +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) bh_lock_sock(sk); if (!sock_owned_by_user(sk)) { +#else + bh_lock_sock(meta_sk); + if (!sock_owned_by_user(meta_sk)) { +#endif tcp_write_timer_handler(sk); } else { /* deleguate our work to tcp_release_cb() */ if (!test_and_set_bit(TCP_WRITE_TIMER_DEFERRED, &tcp_sk(sk)->tsq_flags)) sock_hold(sk); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (mptcp(tcp_sk(sk))) + mptcp_tsq_flags(sk); +#endif } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) bh_unlock_sock(sk); +#else + bh_unlock_sock(meta_sk); +#endif sock_put(sk); } @@ -593,11 +670,19 @@ struct sock *sk = (struct sock *) data; struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + struct sock *meta_sk = mptcp(tp) ? mptcp_meta_sk(sk) : sk; +#endif u32 elapsed; /* Only process if socket is not in use. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) bh_lock_sock(sk); if (sock_owned_by_user(sk)) { +#else + bh_lock_sock(meta_sk); + if (sock_owned_by_user(meta_sk)) { +#endif /* Try again later. */ inet_csk_reset_keepalive_timer (sk, HZ/20); goto out; @@ -608,16 +693,48 @@ goto out; } +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (tp->send_mp_fclose) { + /* MUST do this before tcp_write_timeout, because retrans_stamp + * may have been set to 0 in another part while we are + * retransmitting MP_FASTCLOSE. Then, we would crash, because + * retransmits_timed_out accesses the meta-write-queue. + * + * We make sure that the timestamp is != 0. + */ + if (!tp->retrans_stamp) + tp->retrans_stamp = tcp_time_stamp ? : 1; + + if (tcp_write_timeout(sk)) + goto out; + + tcp_send_ack(sk); + icsk->icsk_retransmits++; + + icsk->icsk_rto = min(icsk->icsk_rto << 1, TCP_RTO_MAX); + elapsed = icsk->icsk_rto; + goto resched; + } + +#endif if (sk->sk_state == TCP_FIN_WAIT2 && sock_flag(sk, SOCK_DEAD)) { if (tp->linger2 >= 0) { const int tmo = tcp_fin_time(sk) - TCP_TIMEWAIT_LEN; if (tmo > 0) { +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_time_wait(sk, TCP_FIN_WAIT2, tmo); +#else + tp->ops->time_wait(sk, TCP_FIN_WAIT2, tmo); +#endif goto out; } } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_send_active_reset(sk, GFP_ATOMIC); +#else + tp->ops->send_active_reset(sk, GFP_ATOMIC); +#endif goto death; } @@ -641,11 +758,19 @@ icsk->icsk_probes_out > 0) || (icsk->icsk_user_timeout == 0 && icsk->icsk_probes_out >= keepalive_probes(tp))) { +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_send_active_reset(sk, GFP_ATOMIC); +#else + tp->ops->send_active_reset(sk, GFP_ATOMIC); +#endif tcp_write_err(sk); goto out; } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (tcp_write_wakeup(sk) <= 0) { +#else + if (tp->ops->write_wakeup(sk) <= 0) { +#endif icsk->icsk_probes_out++; elapsed = keepalive_intvl_when(tp); } else { @@ -669,7 +794,11 @@ tcp_done(sk); out: +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) bh_unlock_sock(sk); +#else + bh_unlock_sock(meta_sk); +#endif sock_put(sk); } diff -urN linux-4.1.52-orig/net/ipv6/addrconf.c linux-4.1.52/net/ipv6/addrconf.c --- linux-4.1.52-orig/net/ipv6/addrconf.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv6/addrconf.c 2020-02-07 01:47:54.000000000 +0800 @@ -173,6 +173,10 @@ static bool ipv6_chk_same_addr(struct net *net, const struct in6_addr *addr, struct net_device *dev); +#if defined(CONFIG_BCM_KF_IP) +static struct inet6_dev * ipv6_find_idev(struct net_device *dev); +#endif + static struct ipv6_devconf ipv6_devconf __read_mostly = { .forwarding = 0, .hop_limit = IPV6_DEFAULT_HOPLIMIT, @@ -207,7 +211,11 @@ .proxy_ndp = 0, .accept_source_route = 0, /* we do not accept RH0 by default. */ .disable_ipv6 = 0, +#if defined(CONFIG_BCM_KF_IP) + .accept_dad = 2, +#else .accept_dad = 1, +#endif .suppress_frag_ndisc = 1, .accept_ra_mtu = 1, .stable_secret = { @@ -249,7 +257,11 @@ .proxy_ndp = 0, .accept_source_route = 0, /* we do not accept RH0 by default. */ .disable_ipv6 = 0, +#if defined(CONFIG_BCM_KF_IP) + .accept_dad = 2, +#else .accept_dad = 1, +#endif .suppress_frag_ndisc = 1, .accept_ra_mtu = 1, .stable_secret = { @@ -353,6 +365,19 @@ ndev->cnf.mtu6 = dev->mtu; ndev->cnf.sysctl = NULL; + +#if defined(CONFIG_BCM_KF_IP) + /* + * At bootup time, there is no interfaces attached to brX. Therefore, DAD of + * brX cannot take any effect and we cannot pass IPv6 ReadyLogo. We here + * increase DAD period of brX to 4 sec which should be long enough for our + * system to attach all interfaces to brX. Thus, DAD of brX can send/receive + * packets through attached interfaces. + */ + if ( !strncmp(dev->name, "br", 2) ) + ndev->cnf.dad_transmits = 4; +#endif + ndev->nd_parms = neigh_parms_alloc(dev, &nd_tbl); if (!ndev->nd_parms) { kfree(ndev); @@ -431,9 +456,18 @@ /* Join all-node multicast group */ ipv6_dev_mc_inc(dev, &in6addr_linklocal_allnodes); +#if defined(CONFIG_BCM_KF_WANDEV) + /* Join all-router multicast group if forwarding is set */ + if (ndev->cnf.forwarding && (dev->flags & IFF_MULTICAST) && + !(dev->priv_flags & IFF_WANDEV)) + { + ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters); + } +#else /* Join all-router multicast group if forwarding is set */ if (ndev->cnf.forwarding && (dev->flags & IFF_MULTICAST)) ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters); +#endif return ndev; @@ -796,6 +830,9 @@ kfree_rcu(ifp, rcu); } +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +EXPORT_SYMBOL(inet6_ifa_finish_destroy); +#endif static void ipv6_link_dev_addr(struct inet6_dev *idev, struct inet6_ifaddr *ifp) @@ -3049,6 +3086,53 @@ } #endif +#if defined(CONFIG_BCM_KF_IP) +static int addrconf_update_lladdr(struct net_device *dev) +{ + struct inet6_dev *idev; + struct inet6_ifaddr *ifladdr = NULL; + struct inet6_ifaddr *ifp; + struct in6_addr addr6; + int err = -EADDRNOTAVAIL; + + ASSERT_RTNL(); + + idev = __in6_dev_get(dev); + if (idev != NULL) + { + read_lock_bh(&idev->lock); + list_for_each_entry(ifp, &idev->addr_list, if_list) { + if (IFA_LINK == ifp->scope) + { + ifladdr = ifp; + in6_ifa_hold(ifp); + break; + } + } + read_unlock_bh(&idev->lock); + + if ( ifladdr ) + { + /* delete the address */ + ipv6_del_addr(ifladdr); + + /* add new LLA */ + memset(&addr6, 0, sizeof(struct in6_addr)); + addr6.s6_addr32[0] = htonl(0xFE800000); + + if (0 == ipv6_generate_eui64(addr6.s6_addr + 8, dev)) + { + addrconf_add_linklocal(idev, &addr6, 0); + err = 0; + } + } + } + + return err; + +} +#endif + #if IS_ENABLED(CONFIG_NET_IPGRE) static void addrconf_gre_config(struct net_device *dev) { @@ -3219,6 +3303,12 @@ } break; +#if defined(CONFIG_BCM_KF_IP) + case NETDEV_CHANGEADDR: + addrconf_update_lladdr(dev); + break; +#endif + case NETDEV_PRE_TYPE_CHANGE: case NETDEV_POST_TYPE_CHANGE: addrconf_type_change(dev, event); @@ -3379,7 +3469,16 @@ if (idev->dead || !(idev->if_flags & IF_READY)) goto out; +#if defined(CONFIG_BCM_KF_WANDEV) + /* WAN interface needs to act as a host. */ + if (idev->cnf.forwarding && + (!(idev->dev->priv_flags & IFF_WANDEV) || + ((idev->dev->priv_flags & IFF_WANDEV) && + netdev_path_is_root(idev->dev)) + )) +#else if (!ipv6_accept_ra(idev)) +#endif goto out; /* Announcement received after solicitation was sent */ @@ -3626,7 +3725,14 @@ read_lock_bh(&ifp->idev->lock); send_mld = ifp->scope == IFA_LINK && ipv6_lonely_lladdr(ifp); send_rs = send_mld && +#if defined(CONFIG_BCM_KF_WANDEV) + /* WAN interface needs to act as a host. */ + ( (ifp->idev->cnf.forwarding == 0) || + ( (ifp->idev->dev->priv_flags & IFF_WANDEV) && + !netdev_path_is_root(ifp->idev->dev) ) ) && +#else ipv6_accept_ra(ifp->idev) && +#endif ifp->idev->cnf.rtr_solicits > 0 && (dev->flags&IFF_LOOPBACK) == 0; read_unlock_bh(&ifp->idev->lock); @@ -5057,14 +5163,24 @@ */ if (!(ifp->rt->rt6i_node)) ip6_ins_rt(ifp->rt); +#if defined(CONFIG_BCM_KF_WANDEV) + if (ifp->idev->cnf.forwarding && + !(ifp->idev->dev->priv_flags & IFF_WANDEV)) +#else if (ifp->idev->cnf.forwarding) +#endif addrconf_join_anycast(ifp); if (!ipv6_addr_any(&ifp->peer_addr)) addrconf_prefix_route(&ifp->peer_addr, 128, ifp->idev->dev, 0, 0); break; case RTM_DELADDR: +#if defined(CONFIG_BCM_KF_WANDEV) + if (ifp->idev->cnf.forwarding && + !(ifp->idev->dev->priv_flags & IFF_WANDEV)) +#else if (ifp->idev->cnf.forwarding) +#endif addrconf_leave_anycast(ifp); addrconf_leave_solict(ifp->idev, &ifp->addr); if (!ipv6_addr_any(&ifp->peer_addr)) { diff -urN linux-4.1.52-orig/net/ipv6/addrconf_core.c linux-4.1.52/net/ipv6/addrconf_core.c --- linux-4.1.52-orig/net/ipv6/addrconf_core.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv6/addrconf_core.c 2020-02-07 01:47:54.000000000 +0800 @@ -37,7 +37,11 @@ { __be32 st; +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) + memcpy(&st, &addr->s6_addr[0], sizeof(__be32)); +#else st = addr->s6_addr32[0]; +#endif /* Consider all addresses with the first three bits different of 000 and 111 as unicasts. diff -urN linux-4.1.52-orig/net/ipv6/af_inet6.c linux-4.1.52/net/ipv6/af_inet6.c --- linux-4.1.52-orig/net/ipv6/af_inet6.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv6/af_inet6.c 2020-02-07 01:47:54.000000000 +0800 @@ -97,8 +97,12 @@ return (struct ipv6_pinfo *)(((u8 *)sk) + offset); } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static int inet6_create(struct net *net, struct socket *sock, int protocol, int kern) +#else +int inet6_create(struct net *net, struct socket *sock, int protocol, int kern) +#endif { struct inet_sock *inet; struct ipv6_pinfo *np; diff -urN linux-4.1.52-orig/net/ipv6/ah6.c linux-4.1.52/net/ipv6/ah6.c --- linux-4.1.52-orig/net/ipv6/ah6.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv6/ah6.c 2020-02-07 01:47:54.000000000 +0800 @@ -40,6 +40,10 @@ #include #include +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#include +#endif + #define IPV6HDR_BASELEN 8 struct tmp_ext { @@ -350,6 +354,10 @@ int sglists = 0; struct scatterlist *seqhisg; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_skip(skb, blog_skip_reason_unknown_proto_ah6); +#endif + ahp = x->data; ahash = ahp->ahash; @@ -423,8 +431,17 @@ ah->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low); sg_init_table(sg, nfrags + sglists); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + err = +#endif skb_to_sgvec_nomark(skb, sg, 0, skb->len); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + if(unlikely(err) < 0) + goto out_free; +#endif + + if (x->props.flags & XFRM_STATE_ESN) { /* Attach seqhi sg right after packet payload */ *seqhi = htonl(XFRM_SKB_CB(skb)->seq.output.hi); @@ -435,6 +452,22 @@ AH_SKB_CB(skb)->tmp = iph_base; +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) && !(defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE)) + /* ensure there is enough headroom and tailroom for HW info */ + if((skb_headroom(skb) < 12) || + (skb_tailroom(skb) < 20)) + { + req->alloc_buff_spu = 1; + } + else + { + req->alloc_buff_spu = 0; + } + + /* not used for output */ + req->headerLen = 0; +#endif + err = crypto_ahash_digest(req); if (err) { if (err == -EINPROGRESS) @@ -535,6 +568,10 @@ int sglists = 0; struct scatterlist *seqhisg; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_skip(skb, blog_skip_reason_unknown_proto_ah6); +#endif + if (!pskb_may_pull(skb, sizeof(struct ip_auth_hdr))) goto out; @@ -601,7 +638,14 @@ ip6h->hop_limit = 0; sg_init_table(sg, nfrags + sglists); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + err = +#endif skb_to_sgvec_nomark(skb, sg, 0, skb->len); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + if(unlikely(err) < 0) + goto out; +#endif if (x->props.flags & XFRM_STATE_ESN) { /* Attach seqhi sg right after packet payload */ @@ -614,6 +658,22 @@ AH_SKB_CB(skb)->tmp = work_iph; +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) && !(defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE)) + /* ensure there is enough headroom and tailroom for HW info */ + if((skb_headroom(skb) < 12) || + (skb_tailroom(skb) < 20)) + { + req->alloc_buff_spu = 1; + } + else + { + req->alloc_buff_spu = 0; + } + + /* offset to icv */ + req->headerLen = &ah->auth_data[0] - skb->data; +#endif + err = crypto_ahash_digest(req); if (err) { if (err == -EINPROGRESS) diff -urN linux-4.1.52-orig/net/ipv6/esp6.c linux-4.1.52/net/ipv6/esp6.c --- linux-4.1.52-orig/net/ipv6/esp6.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv6/esp6.c 2020-02-07 01:47:54.000000000 +0800 @@ -44,6 +44,10 @@ #include #include +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#include +#endif + struct esp_skb_cb { struct xfrm_skb_cb xfrm; void *tmp; @@ -163,6 +167,14 @@ u8 *iv; u8 *tail; __be32 *seqhi; +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) && (defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE)) + u8 next_hdr; +#endif + + +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_skip(skb, blog_skip_reason_unknown_proto_esp6); +#endif /* skb is pure payload to encrypt */ aead = x->data; @@ -221,6 +233,10 @@ } while (0); tail[plen - 2] = plen - 2; tail[plen - 1] = *skb_mac_header(skb); +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) && (defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE)) + next_hdr = *skb_mac_header(skb); +#endif + pskb_put(skb, trailer, clen - skb->len + alen); skb_push(skb, -skb_network_offset(skb)); @@ -231,9 +247,17 @@ esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low); sg_init_table(sg, nfrags); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + err = +#endif skb_to_sgvec(skb, sg, esph->enc_data + crypto_aead_ivsize(aead) - skb->data, clen + alen); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + if(unlikely(err) < 0) + goto error; +#endif + if ((x->props.flags & XFRM_STATE_ESN)) { sg_init_table(asg, 3); @@ -252,6 +276,26 @@ ((u64)XFRM_SKB_CB(skb)->seq.output.hi << 32)); ESP_SKB_CB(skb)->tmp = tmp; + +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) + req->areq.data_offset = (unsigned char *)esph - skb->data; + req->areq.next_hdr = next_hdr; +#else + /* ensure there is enough headroom and tailroom for HW info */ + if((skb_headroom(skb) < 12) || + (skb_tailroom(skb) < 16)) + { + req->areq.alloc_buff_spu = 1; + } + else + { + req->areq.alloc_buff_spu = 0; + } + req->areq.headerLen = esph->enc_data + crypto_aead_ivsize(aead) - skb->data; +#endif +#endif + err = crypto_aead_givencrypt(req); if (err == -EINPROGRESS) goto error; @@ -335,6 +379,17 @@ u8 *iv; struct scatterlist *sg; struct scatterlist *asg; +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) && !(defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE)) + int macLen; +#endif + +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + int err; +#endif + +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_skip(skb, blog_skip_reason_unknown_proto_esp6); +#endif if (!pskb_may_pull(skb, sizeof(*esph) + crypto_aead_ivsize(aead))) { ret = -EINVAL; @@ -383,7 +438,14 @@ iv = esph->enc_data; sg_init_table(sg, nfrags); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + err = +#endif skb_to_sgvec(skb, sg, sizeof(*esph) + crypto_aead_ivsize(aead), elen); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + if(unlikely(err) < 0) + goto out; +#endif if ((x->props.flags & XFRM_STATE_ESN)) { sg_init_table(asg, 3); @@ -398,6 +460,28 @@ aead_request_set_crypt(req, sg, sg, elen, iv); aead_request_set_assoc(req, asg, assoclen); +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) + req->data_offset = 0; + req->next_hdr = 0; +#else + /* ensure there is enough headroom and tailroom for HW info */ + if ( (skb->data >= skb_mac_header(skb)) && + (skb_headroom(skb) >= ((skb->data - skb_mac_header(skb)) + 12)) && + (skb_tailroom(skb) >= 16)) + { + macLen = skb->data - skb_mac_header(skb); + req->alloc_buff_spu = 0; + } + else + { + macLen = 0; + req->alloc_buff_spu = 1; + } + req->headerLen = sizeof(*esph) + crypto_aead_ivsize(aead) + macLen; +#endif +#endif + ret = crypto_aead_decrypt(req); if (ret == -EINPROGRESS) goto out; diff -urN linux-4.1.52-orig/net/ipv6/icmp.c linux-4.1.52/net/ipv6/icmp.c --- linux-4.1.52-orig/net/ipv6/icmp.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv6/icmp.c 2020-02-07 01:47:54.000000000 +0800 @@ -453,6 +453,8 @@ */ if ((addr_type == IPV6_ADDR_ANY) || (addr_type & IPV6_ADDR_MULTICAST)) { net_dbg_ratelimited("icmp6_send: addr_any/mcast source\n"); +#if defined(CONFIG_BCM_KF_FAP) +#endif return; } @@ -676,7 +678,11 @@ { struct net_device *dev = skb->dev; struct inet6_dev *idev = __in6_dev_get(dev); +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) + struct in6_addr saddr, daddr; +#else const struct in6_addr *saddr, *daddr; +#endif struct icmp6hdr *hdr; u8 type; bool success = false; @@ -703,12 +709,22 @@ ICMP6_INC_STATS_BH(dev_net(dev), idev, ICMP6_MIB_INMSGS); +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) + memcpy(&saddr, &ipv6_hdr(skb)->saddr, sizeof(struct in6_addr)); + memcpy(&daddr, &ipv6_hdr(skb)->daddr, sizeof(struct in6_addr)); +#else saddr = &ipv6_hdr(skb)->saddr; daddr = &ipv6_hdr(skb)->daddr; +#endif if (skb_checksum_validate(skb, IPPROTO_ICMPV6, ip6_compute_pseudo)) { +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) + net_dbg_ratelimited("ICMPv6 checksum failed [%pI6c > %pI6c]\n", + &saddr, &daddr); +#else net_dbg_ratelimited("ICMPv6 checksum failed [%pI6c > %pI6c]\n", saddr, daddr); +#endif goto csum_error; } diff -urN linux-4.1.52-orig/net/ipv6/inet6_connection_sock.c linux-4.1.52/net/ipv6/inet6_connection_sock.c --- linux-4.1.52-orig/net/ipv6/inet6_connection_sock.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv6/inet6_connection_sock.c 2020-02-07 01:47:54.000000000 +0800 @@ -97,8 +97,13 @@ /* * request_sock (formerly open request) hash tables. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static u32 inet6_synq_hash(const struct in6_addr *raddr, const __be16 rport, const u32 rnd, const u32 synq_hsize) +#else +u32 inet6_synq_hash(const struct in6_addr *raddr, const __be16 rport, + const u32 rnd, const u32 synq_hsize) +#endif { u32 c; diff -urN linux-4.1.52-orig/net/ipv6/ip6_gre.c linux-4.1.52/net/ipv6/ip6_gre.c --- linux-4.1.52-orig/net/ipv6/ip6_gre.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv6/ip6_gre.c 2020-02-07 01:47:54.000000000 +0800 @@ -56,6 +56,10 @@ #include #include +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#include +#include +#endif static bool log_ecn_error = true; module_param(log_ecn_error, bool, 0644); @@ -493,6 +497,13 @@ &ipv6h->saddr, &ipv6h->daddr, key, gre_proto); if (tunnel) { +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_link(IF_DEVICE, blog_ptr(skb), (void*)tunnel->dev, DIR_RX, + skb->len); + blog_link(GRE_TUNL, blog_ptr(skb), (void*)tunnel, DIR_RX, 0); + blog_link(TOS_MODE, blog_ptr(skb), tunnel, DIR_RX, BLOG_TOS_FIXED); +#endif + struct pcpu_sw_netstats *tstats; if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) @@ -623,6 +634,14 @@ struct sk_buff *new_skb; __be16 protocol; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_link(IF_DEVICE, blog_ptr(skb), (void*)dev, DIR_TX, skb->len); + blog_link(GRE_TUNL, blog_ptr(skb), (void*)tunnel, DIR_TX, 0); + blog_link(TOS_MODE, blog_ptr(skb), tunnel, DIR_TX, + (tunnel->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS) ? + BLOG_TOS_INHERIT : BLOG_TOS_FIXED); +#endif + if (dev->type == ARPHRD_ETHER) IPCB(skb)->flags = 0; @@ -746,7 +765,12 @@ __be32 *ptr = (__be32 *)(((u8 *)ipv6h) + tunnel->hlen - 4); if (tunnel->parms.o_flags&GRE_SEQ) { +#if (defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG)) + if (!blog_gre_tunnel_accelerated()) + ++tunnel->o_seqno; +#else ++tunnel->o_seqno; +#endif *ptr = htonl(tunnel->o_seqno); ptr--; } @@ -763,6 +787,9 @@ skb_set_inner_protocol(skb, protocol); +#if (defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG)) + skb->tunl = tunnel; +#endif ip6tunnel_xmit(NULL, skb, dev); if (ndst) ip6_tnl_dst_store(tunnel, ndst); @@ -1246,6 +1273,9 @@ dev->flags |= IFF_NOARP; dev->addr_len = sizeof(struct in6_addr); netif_keep_dst(dev); +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + dev->blog_stats_flags |= BLOG_DEV_STAT_FLAG_INCLUDE_ALL; +#endif } static int ip6gre_tunnel_init(struct net_device *dev) @@ -1499,6 +1529,9 @@ dev->destructor = ip6gre_dev_free; dev->features |= NETIF_F_NETNS_LOCAL; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + dev->blog_stats_flags |= BLOG_DEV_STAT_FLAG_INCLUDE_ALL; +#endif } static int ip6gre_newlink(struct net *src_net, struct net_device *dev, @@ -1671,6 +1704,133 @@ .get_link_net = ip6_tnl_get_link_net, }; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +static inline int __bcm_gre6_rcv_check(struct ip6_tnl *tunnel, struct ipv6hdr *ipv6h, + uint16_t len, uint32_t *pkt_seqno) +{ + int ret = BLOG_GRE_RCV_NO_SEQNO; + int grehlen = 4; + /* Not supporting extension headers. If this changes, need to calculate. */ + __be16 *p = (__be16*)((uint8_t *)ipv6h+BLOG_IPV6_HDR_LEN); + __be16 flags; + + flags = p[0]; + + if (tunnel->parms.i_flags & GRE_CSUM) { + uint16_t csum; + + grehlen += 4; + csum = *(((__be16 *)p) + 2); + + if (!csum) + goto no_csum; + + csum = ip_compute_csum((void*)(ipv6h+1), len - BLOG_IPV6_HDR_LEN); + + if (csum) { + tunnel->dev->stats.rx_crc_errors++; + tunnel->dev->stats.rx_errors++; + ret = BLOG_GRE_RCV_CHKSUM_ERR; + goto rcv_done; + } + } + +no_csum: + if ((tunnel->parms.i_flags & GRE_KEY) && (flags&GRE_KEY)) + grehlen += 4; + + if (tunnel->parms.i_flags & GRE_SEQ) { + uint32_t seqno = *(((__be32 *)p) + (grehlen / 4)); + *pkt_seqno = seqno; + if (tunnel->i_seqno && (s32)(seqno - tunnel->i_seqno) == 0) { + tunnel->i_seqno = seqno + 1; + ret = BLOG_GRE_RCV_IN_SEQ; + } else if (tunnel->i_seqno && (s32)(seqno - tunnel->i_seqno) < 0) { + tunnel->dev->stats.rx_fifo_errors++; + tunnel->dev->stats.rx_errors++; + ret = BLOG_GRE_RCV_OOS_LT; + } else { + tunnel->i_seqno = seqno + 1; + ret = BLOG_GRE_RCV_OOS_GT; + } + } + +rcv_done: + return ret; +} + +int bcm_gre6_rcv_check(struct net_device *dev, struct ipv6hdr *ipv6h, + uint16_t len, void **tunl, uint32_t *pkt_seqno) +{ + int ret = BLOG_GRE_RCV_NO_TUNNEL; + struct ip6_tnl *tunnel; + + u8 *h = (uint8_t *)ipv6h+BLOG_IPV6_HDR_LEN; + __be16 flags = *(__be16 *)h; + __be16 protocol = *((__be16 *)h + 1); + __be32 *options =(__be32*)(__be32 *)(h + BLOG_GRE_HDR_LEN); + __be32 key = 0; + + if (flags & GRE_CSUM) { + options++; + } + + if (flags & GRE_KEY) { + key = *options; + } + + tunnel = ip6gre_tunnel_lookup(dev, &ipv6h->daddr, &ipv6h->saddr, + key, protocol); + + if (tunnel) { + rcu_read_lock(); + ret = __bcm_gre6_rcv_check(tunnel, ipv6h, len, pkt_seqno); + rcu_read_unlock(); + } + + *tunl = (void *) tunnel; + return ret; +} + +/* Adds the TX seqno, Key and updates the GRE checksum */ +static inline +void __bcm_gre6_xmit_update(struct ip6_tnl *tunnel, struct ipv6hdr *ipv6h, + uint16_t len) +{ + if (tunnel->parms.o_flags&(GRE_KEY|GRE_CSUM|GRE_SEQ)) { + /* ip6 tunnel hlen is precalculated GRE header length (includes encap). */ + __be32 *ptr = (__be32*)(((u8*)ipv6h) + tunnel->hlen - 4); + + if (tunnel->parms.o_flags&GRE_SEQ) { + ++tunnel->o_seqno; + *ptr = htonl(tunnel->o_seqno); + ptr--; + } + + if (tunnel->parms.o_flags&GRE_KEY) { + *ptr = tunnel->parms.o_key; + ptr--; + } + + if (tunnel->parms.o_flags&GRE_CSUM) { + *ptr = 0; + *(__sum16*)ptr = ip_compute_csum((void*)(ipv6h+1), len - BLOG_IPV6_HDR_LEN); + } + cache_flush_len(ptr, tunnel->hlen); + } +} + +/* Adds the oseqno and updates the GRE checksum */ +void bcm_gre6_xmit_update(struct ip6_tnl *tunnel, struct ipv6hdr *ipv6h, + uint16_t len) +{ + rcu_read_lock(); + __bcm_gre6_xmit_update(tunnel, ipv6h, len); + rcu_read_unlock(); +} + +#endif + /* * And now the modules code and kernel interface. */ @@ -1699,6 +1859,11 @@ if (err < 0) goto tap_ops_failed; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_gre6_rcv_check_fn = (blog_gre6_rcv_check_t) bcm_gre6_rcv_check; + blog_gre6_xmit_update_fn = (blog_gre6_xmit_upd_t) bcm_gre6_xmit_update; +#endif + out: return err; diff -urN linux-4.1.52-orig/net/ipv6/ip6_input.c linux-4.1.52/net/ipv6/ip6_input.c --- linux-4.1.52-orig/net/ipv6/ip6_input.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv6/ip6_input.c 2020-02-07 01:47:54.000000000 +0800 @@ -226,7 +226,10 @@ if (ipprot->flags & INET6_PROTO_FINAL) { const struct ipv6hdr *hdr; - +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) + struct in6_addr srcAddr; + struct in6_addr dstAddr; +#endif /* Free reference early: we don't need it any more, and it may hold ip_conntrack module loaded indefinitely. */ @@ -235,11 +238,21 @@ skb_postpull_rcsum(skb, skb_network_header(skb), skb_network_header_len(skb)); hdr = ipv6_hdr(skb); +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) + memcpy(&srcAddr, &hdr->saddr, sizeof(struct in6_addr)); + memcpy(&dstAddr, &hdr->daddr, sizeof(struct in6_addr)); + if (ipv6_addr_is_multicast(&dstAddr) && + !ipv6_chk_mcast_addr(skb->dev, &dstAddr, + &srcAddr) && + !ipv6_is_mld(skb, nexthdr, skb_network_header_len(skb))) + goto discard; +#else if (ipv6_addr_is_multicast(&hdr->daddr) && !ipv6_chk_mcast_addr(skb->dev, &hdr->daddr, &hdr->saddr) && !ipv6_is_mld(skb, nexthdr, skb_network_header_len(skb))) goto discard; +#endif } if (!(ipprot->flags & INET6_PROTO_NOPOLICY) && !xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) @@ -286,20 +299,32 @@ { const struct ipv6hdr *hdr; bool deliver; +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) + struct in6_addr dAddr; +#endif IP6_UPD_PO_STATS_BH(dev_net(skb_dst(skb)->dev), ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_INMCAST, skb->len); hdr = ipv6_hdr(skb); +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) + memcpy(&dAddr, &hdr->daddr, sizeof(struct in6_addr)); + deliver = ipv6_chk_mcast_addr(skb->dev, &dAddr, NULL); +#else deliver = ipv6_chk_mcast_addr(skb->dev, &hdr->daddr, NULL); +#endif #ifdef CONFIG_IPV6_MROUTE /* * IPv6 multicast router mode is now supported ;) */ if (dev_net(skb->dev)->ipv6.devconf_all->mc_forwarding && +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) + !(ipv6_addr_type(&dAddr) & +#else !(ipv6_addr_type(&hdr->daddr) & +#endif (IPV6_ADDR_LOOPBACK|IPV6_ADDR_LINKLOCAL)) && likely(!(IP6CB(skb)->flags & IP6SKB_FORWARDED))) { /* diff -urN linux-4.1.52-orig/net/ipv6/ip6mr.c linux-4.1.52/net/ipv6/ip6mr.c --- linux-4.1.52-orig/net/ipv6/ip6mr.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv6/ip6mr.c 2020-02-07 01:47:54.000000000 +0800 @@ -1014,6 +1014,44 @@ return 0; } +#if defined(CONFIG_BCM_KF_MROUTE) +/* look for (S,G,iif) and then (*,G,iif) */ +static struct mfc6_cache *ip6mr_cache_find_parent(struct mr6_table *mrt, + struct in6_addr *origin, + struct in6_addr *mcastgrp, + mifi_t mifi) +{ + int line = MFC6_HASH(mcastgrp, origin); + struct mfc6_cache *c = NULL; + struct in6_addr nullOrigin; + + list_for_each_entry(c, &mrt->mfc6_cache_array[line], list) { + if (ipv6_addr_equal(&c->mf6c_origin, origin) && + ipv6_addr_equal(&c->mf6c_mcastgrp, mcastgrp) && + (c->mf6c_parent == mifi)) + { + return c; + } + } + + /* for ASM multicast source does not matter so need to check + for an entry with NULL origin as well */ + memset(&nullOrigin, 0, sizeof(struct in6_addr)); + line = MFC6_HASH(mcastgrp, &nullOrigin); + + list_for_each_entry(c, &mrt->mfc6_cache_array[line], list) { + if (ipv6_addr_equal(&c->mf6c_origin, &nullOrigin) && + ipv6_addr_equal(&c->mf6c_mcastgrp, mcastgrp) && + (c->mf6c_parent == mifi)) + { + return c; + } + } + + return NULL; +} +#endif + static struct mfc6_cache *ip6mr_cache_find(struct mr6_table *mrt, const struct in6_addr *origin, const struct in6_addr *mcastgrp) @@ -1045,6 +1083,7 @@ return NULL; } +#if !defined(CONFIG_BCM_KF_MROUTE) /* Look for a (*,G) entry */ static struct mfc6_cache *ip6mr_cache_find_any(struct mr6_table *mrt, struct in6_addr *mcastgrp, @@ -1072,6 +1111,7 @@ skip: return ip6mr_cache_find_any_parent(mrt, mifi); } +#endif /* * Allocate a multicast cache entry @@ -2051,7 +2091,12 @@ * result in receiving multiple packets. */ dev = vif->dev; +#if defined(CONFIG_BCM_KF_MLD) + /* skb->dev is the soruce device. It should not be + set to the destination device */ +#else skb->dev = dev; +#endif vif->pkt_out++; vif->bytes_out += skb->len; @@ -2159,7 +2204,14 @@ if (psend != -1) { struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); if (skb2) +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + { + blog_clone(skb, blog_ptr(skb2)); +#endif ip6mr_forward2(net, mrt, skb2, cache, psend); +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + } +#endif } psend = ct; } @@ -2197,6 +2249,18 @@ } read_lock(&mrt_lock); +#if defined(CONFIG_BCM_KF_MROUTE) + /* mroute6 should not apply to MLD traffic + in addition it does not make sense for TCP protocol to be used + for multicast so just check for UDP */ + cache = NULL; + if( ipv6_hdr(skb)->nexthdr == IPPROTO_UDP ) + { + mifi_t mifi = ip6mr_find_vif(mrt, skb->dev); + cache = ip6mr_cache_find_parent(mrt, &ipv6_hdr(skb)->saddr, + &ipv6_hdr(skb)->daddr, mifi); + } +#else cache = ip6mr_cache_find(mrt, &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr); if (!cache) { @@ -2207,7 +2271,7 @@ &ipv6_hdr(skb)->daddr, vif); } - +#endif /* * No usable cache entry */ @@ -2280,8 +2344,14 @@ return 1; } +#if defined(CONFIG_BCM_KF_MROUTE) +int ip6mr_get_route(struct net *net, + struct sk_buff *skb, struct rtmsg *rtm, int nowait, + int ifIndex) +#else int ip6mr_get_route(struct net *net, struct sk_buff *skb, struct rtmsg *rtm, int nowait) +#endif { int err; struct mr6_table *mrt; @@ -2293,6 +2363,26 @@ return -ENOENT; read_lock(&mrt_lock); +#if defined(CONFIG_BCM_KF_MROUTE) + /* mroute6 should not apply to MLD traffic + in addition it does not make sense for TCP protocol to be used + for multicast so just check for UDP */ + cache = NULL; + if( (skb->dev == NULL) || (ipv6_hdr(skb) == NULL) || + (ipv6_hdr(skb)->nexthdr == IPPROTO_UDP) ) + { + struct net_device *dev = dev_get_by_index(net, ifIndex); + if (dev) { + mifi_t mifi = ip6mr_find_vif(mrt, dev); + if (mifi >= 0) + { + cache = ip6mr_cache_find_parent(mrt, &rt->rt6i_src.addr, + &rt->rt6i_dst.addr, mifi); + } + dev_put(dev); + } + } +#else cache = ip6mr_cache_find(mrt, &rt->rt6i_src.addr, &rt->rt6i_dst.addr); if (!cache && skb->dev) { int vif = ip6mr_find_vif(mrt, skb->dev); @@ -2301,7 +2391,7 @@ cache = ip6mr_cache_find_any(mrt, &rt->rt6i_dst.addr, vif); } - +#endif if (!cache) { struct sk_buff *skb2; struct ipv6hdr *iph; diff -urN linux-4.1.52-orig/net/ipv6/ip6_offload.c linux-4.1.52/net/ipv6/ip6_offload.c --- linux-4.1.52-orig/net/ipv6/ip6_offload.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv6/ip6_offload.c 2020-02-07 01:47:54.000000000 +0800 @@ -261,6 +261,21 @@ return pp; } +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +static struct sk_buff **sit_gro_receive(struct sk_buff **head, + struct sk_buff *skb) +{ + if (NAPI_GRO_CB(skb)->encap_mark) { + NAPI_GRO_CB(skb)->flush = 1; + return NULL; + } + + NAPI_GRO_CB(skb)->encap_mark = 1; + + return ipv6_gro_receive(head, skb); +} +#endif + static int ipv6_gro_complete(struct sk_buff *skb, int nhoff) { const struct net_offload *ops; @@ -295,6 +310,9 @@ static const struct net_offload sit_offload = { .callbacks = { .gso_segment = ipv6_gso_segment, +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + .gro_receive = sit_gro_receive, +#endif }, }; diff -urN linux-4.1.52-orig/net/ipv6/ip6_output.c linux-4.1.52/net/ipv6/ip6_output.c --- linux-4.1.52-orig/net/ipv6/ip6_output.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv6/ip6_output.c 2020-02-07 01:47:54.000000000 +0800 @@ -323,6 +323,35 @@ return dst_output_sk(sk, skb); } +#if defined(CONFIG_BCM_KF_IP) +static inline int isULA(const struct in6_addr *addr) +{ + __be32 st; + + st = addr->s6_addr32[0]; + + /* RFC 4193 */ + if ((st & htonl(0xFE000000)) == htonl(0xFC000000)) + return 1; + else + return 0; +} + +static inline int isSpecialAddr(const struct in6_addr *addr) +{ + __be32 st; + + st = addr->s6_addr32[0]; + + /* RFC 5156 */ + if (((st & htonl(0xFFFFFFFF)) == htonl(0x20010db8)) || + ((st & htonl(0xFFFFFFF0)) == htonl(0x20010010))) + return 1; + else + return 0; +} +#endif + static unsigned int ip6_dst_mtu_forward(const struct dst_entry *dst) { unsigned int mtu; @@ -369,6 +398,9 @@ struct inet6_skb_parm *opt = IP6CB(skb); struct net *net = dev_net(dst->dev); u32 mtu; +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + int needfrag = 0; +#endif if (net->ipv6.devconf_all->forwarding == 0) goto error; @@ -422,6 +454,14 @@ return -ETIMEDOUT; } +#if defined(CONFIG_BCM_KF_IP) + /* No traffic with ULA address should be forwarded at WAN intf */ + if ( isULA(&hdr->daddr) || isULA(&hdr->saddr) ) + if ((skb->dev->priv_flags & IFF_WANDEV) || + (dst->dev->priv_flags & IFF_WANDEV) ) + goto drop; +#endif + /* XXX: idev->cnf.proxy_ndp? */ if (net->ipv6.devconf_all->proxy_ndp && pneigh_lookup(&nd_tbl, net, &hdr->daddr, skb->dev, 0)) { @@ -476,7 +516,19 @@ /* This check is security critical. */ if (addrtype == IPV6_ADDR_ANY || +#if defined(CONFIG_BCM_KF_IP) + /* + * RFC 5156: IPv4 mapped addr and IPv4-compatible addr + * should not appear on the Internet. In addition, + * 2001:db8::/32 and 2001:10::/28 should not appear either. + */ + (addrtype & (IPV6_ADDR_MULTICAST | IPV6_ADDR_LOOPBACK | + IPV6_ADDR_COMPATv4 | IPV6_ADDR_MAPPED | + IPV6_ADDR_SITELOCAL)) || + isSpecialAddr(&hdr->saddr)) +#else addrtype & (IPV6_ADDR_MULTICAST | IPV6_ADDR_LOOPBACK)) +#endif goto error; if (addrtype & IPV6_ADDR_LINKLOCAL) { icmpv6_send(skb, ICMPV6_DEST_UNREACH, @@ -490,6 +542,14 @@ mtu = IPV6_MIN_MTU; if (ip6_pkt_too_big(skb, mtu)) { +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + /* + * MAP_FORWARD_MODE2/MAP_FORWARD_MODE3 is used to fragment translated IPv6 packet + * for MAP-T/MAP-E feature + */ + if (skb->map_forward != MAP_FORWARD_MODE2 && skb->map_forward != MAP_FORWARD_MODE3) + { +#endif /* Again, force OUTPUT device used as source address */ skb->dev = dst->dev; icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); @@ -499,6 +559,15 @@ IPSTATS_MIB_FRAGFAILS); kfree_skb(skb); return -EMSGSIZE; +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + } + else + needfrag = 1; + } + else if ((skb->map_forward == MAP_FORWARD_MODE2) && + (skb->len > (mtu-sizeof(struct frag_hdr)))) { + needfrag = 1; +#endif } if (skb_cow(skb, dst->dev->hard_header_len)) { @@ -513,6 +582,23 @@ hdr->hop_limit--; +#if defined(CONFIG_BCM_KF_WANDEV) +#if !defined(CONFIG_BCM_WAN_2_WAN_FWD_ENABLED) + /* Never forward a packet from a WAN intf to the other WAN intf */ + if( (skb->dev) && (dst->dev) && + ((skb->dev->priv_flags & dst->dev->priv_flags) & IFF_WANDEV) ) + goto drop; +#endif +#endif + +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + if (needfrag) + { + skb->ignore_df = 1; + return ip6_fragment(skb->sk, skb, ip6_forward_finish); + } +#endif + IP6_INC_STATS_BH(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTFORWDATAGRAMS); IP6_ADD_STATS_BH(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTOCTETS, skb->len); return NF_HOOK(NFPROTO_IPV6, NF_INET_FORWARD, NULL, skb, @@ -551,7 +637,14 @@ struct ipv6_pinfo *np = skb->sk && !dev_recursion_level() ? inet6_sk(skb->sk) : NULL; struct ipv6hdr *tmp_hdr; +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + struct iphdr *tmp_hdr2 = NULL; + struct iphdr *iph = NULL; + struct frag_hdr *fh = NULL; + __be16 not_last_frag = 0; +#else struct frag_hdr *fh; +#endif unsigned int mtu, hlen, left, len; int hroom, troom; __be32 frag_id = 0; @@ -588,7 +681,11 @@ if (np->frag_size) mtu = np->frag_size; } +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + mtu -= hlen + (skb->map_forward == MAP_FORWARD_MODE3 ? sizeof(struct iphdr) : sizeof(struct frag_hdr)); +#else mtu -= hlen + sizeof(struct frag_hdr); +#endif if (skb_has_frag_list(skb)) { int first_len = skb_pagelen(skb); @@ -624,7 +721,19 @@ skb_frag_list_init(skb); /* BUILD HEADER */ +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + *prevhdr = skb->map_forward == MAP_FORWARD_MODE3 ? IPPROTO_IPIP : NEXTHDR_FRAGMENT; + if (skb->map_forward == MAP_FORWARD_MODE3) { + tmp_hdr2 = kmemdup(skb_network_header(skb) + hlen, sizeof(struct iphdr), GFP_ATOMIC); + if (!tmp_hdr2) { + IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), + IPSTATS_MIB_FRAGFAILS); + return -ENOMEM; + } + } +#else *prevhdr = NEXTHDR_FRAGMENT; +#endif tmp_hdr = kmemdup(skb_network_header(skb), hlen, GFP_ATOMIC); if (!tmp_hdr) { IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), @@ -633,16 +742,39 @@ } __skb_pull(skb, hlen); +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + if (skb->map_forward == MAP_FORWARD_MODE3) + iph = (struct iphdr *)__skb_push(skb, sizeof(struct iphdr)); + else +#endif fh = (struct frag_hdr *)__skb_push(skb, sizeof(struct frag_hdr)); __skb_push(skb, hlen); skb_reset_network_header(skb); memcpy(skb_network_header(skb), tmp_hdr, hlen); +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + if (skb->map_forward != MAP_FORWARD_MODE3) + { + if (skb->map_id) + fh->identification = skb->map_id; + else +#endif ipv6_select_ident(net, fh, rt); fh->nexthdr = nexthdr; fh->reserved = 0; fh->frag_off = htons(IP6_MF); frag_id = fh->identification; +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + } + else { + memcpy(skb_network_header(skb) + hlen, tmp_hdr2, sizeof(struct iphdr)); + iph->tot_len = htons(first_len - hlen); + iph->frag_off = htons(IP_MF); + if (skb->map_id) + iph->id = skb->map_id; + ip_send_check(iph); + } +#endif first_len = skb_pagelen(skb); skb->data_len = first_len - skb_headlen(skb); @@ -658,18 +790,51 @@ if (frag) { frag->ip_summed = CHECKSUM_NONE; skb_reset_transport_header(frag); +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + if (skb->map_forward == MAP_FORWARD_MODE3) + iph = (struct iphdr *)__skb_push(frag, sizeof(struct iphdr)); + else +#endif fh = (struct frag_hdr *)__skb_push(frag, sizeof(struct frag_hdr)); __skb_push(frag, hlen); skb_reset_network_header(frag); memcpy(skb_network_header(frag), tmp_hdr, hlen); +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + if (skb->map_forward != MAP_FORWARD_MODE3) + { +#endif offset += skb->len - hlen - sizeof(struct frag_hdr); fh->nexthdr = nexthdr; fh->reserved = 0; +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + fh->frag_off = htons(offset+skb->map_offset); +#else fh->frag_off = htons(offset); +#endif if (frag->next) fh->frag_off |= htons(IP6_MF); +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + else if (skb->map_mf) + fh->frag_off |= htons(IP6_MF); +#endif fh->identification = frag_id; +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + } + else { + memcpy(skb_network_header(frag) + hlen, tmp_hdr2, + sizeof(struct iphdr)); + iph->tot_len = htons(frag->len - hlen); + if (offset == 0) + ip_options_fragment(frag); + offset += skb->len - hlen - sizeof(struct iphdr); + iph->frag_off = htons(offset>>3); + if (frag->next) + iph->frag_off |= htons(IP_MF); + /* Ready, complete checksum */ + ip_send_check(iph); + } +#endif ipv6_hdr(frag)->payload_len = htons(frag->len - sizeof(struct ipv6hdr)); @@ -690,6 +855,9 @@ } kfree(tmp_hdr); +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + kfree(tmp_hdr2); +#endif if (err == 0) { IP6_INC_STATS(net, ip6_dst_idev(&rt->dst), @@ -720,14 +888,37 @@ skb_checksum_help(skb)) goto fail; +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + left = skb->len - hlen - (skb->map_forward == MAP_FORWARD_MODE3 ? sizeof(struct iphdr) : 0); + ptr = hlen + (skb->map_forward == MAP_FORWARD_MODE3 ? sizeof(struct iphdr) : 0); +#else left = skb->len - hlen; /* Space per frame */ ptr = hlen; /* Where to start from */ +#endif + +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + if (skb->map_forward == MAP_FORWARD_MODE3) { + tmp_hdr2 = kmemdup(skb_network_header(skb) + hlen, sizeof(struct iphdr), GFP_ATOMIC); + if (!tmp_hdr2) { + IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), + IPSTATS_MIB_FRAGFAILS); + return -ENOMEM; + } + offset = (ntohs(tmp_hdr2->frag_off) & IP_OFFSET) << 3; + not_last_frag = tmp_hdr2->frag_off & htons(IP_MF); + kfree(tmp_hdr2); + } +#endif /* * Fragment the datagram. */ +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + *prevhdr = skb->map_forward == MAP_FORWARD_MODE3 ? IPPROTO_IPIP : NEXTHDR_FRAGMENT; +#else *prevhdr = NEXTHDR_FRAGMENT; +#endif hroom = LL_RESERVED_SPACE(rt->dst.dev); troom = rt->dst.dev->needed_tailroom; @@ -746,8 +937,13 @@ } /* Allocate buffer */ +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + frag = alloc_skb(len + hlen + (skb->map_forward == MAP_FORWARD_MODE3 ? sizeof(struct iphdr) : sizeof(struct frag_hdr)) + + hroom + troom, GFP_ATOMIC); +#else frag = alloc_skb(len + hlen + sizeof(struct frag_hdr) + hroom + troom, GFP_ATOMIC); +#endif if (!frag) { IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_FRAGFAILS); @@ -761,11 +957,27 @@ ip6_copy_metadata(frag, skb); skb_reserve(frag, hroom); +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + skb_put(frag, len + hlen + (skb->map_forward == MAP_FORWARD_MODE3 ? sizeof(struct iphdr) : sizeof(struct frag_hdr))); +#else skb_put(frag, len + hlen + sizeof(struct frag_hdr)); +#endif skb_reset_network_header(frag); +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + if (skb->map_forward != MAP_FORWARD_MODE3) + { +#endif fh = (struct frag_hdr *)(skb_network_header(frag) + hlen); frag->transport_header = (frag->network_header + hlen + sizeof(struct frag_hdr)); +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + } + else { + iph = (struct iphdr *)(skb_network_header(frag) + hlen); + frag->transport_header = (frag->network_header + hlen + + sizeof(struct iphdr)); + } +#endif /* * Charge the memory for the fragment to any owner @@ -777,18 +989,47 @@ /* * Copy the packet header into the new buffer. */ +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + skb_copy_from_linear_data(skb, skb_network_header(frag), hlen + (skb->map_forward == MAP_FORWARD_MODE3 ? sizeof(struct iphdr) : 0)); +#else skb_copy_from_linear_data(skb, skb_network_header(frag), hlen); +#endif +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + if (skb->map_forward != MAP_FORWARD_MODE3) + { +#endif /* * Build fragment header. */ fh->nexthdr = nexthdr; fh->reserved = 0; +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + if (skb->map_id) + fh->identification = skb->map_id; + else + { +#endif if (!frag_id) { ipv6_select_ident(net, fh, rt); frag_id = fh->identification; } else fh->identification = frag_id; +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + } + } + else { + /* + * Build pre-fragment/IPv4 header. + */ + iph->frag_off = htons((offset >> 3)); + if (offset == 0) + ip_options_fragment(skb); + iph->tot_len = htons(len + sizeof(struct iphdr)); + if (skb->map_id) + iph->id = skb->map_id; + } +#endif /* * Copy a block of the IP datagram. @@ -797,9 +1038,24 @@ len)); left -= len; +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + if (skb->map_forward != MAP_FORWARD_MODE3) + { + fh->frag_off = htons(offset+skb->map_offset); +#else fh->frag_off = htons(offset); +#endif if (left > 0) fh->frag_off |= htons(IP6_MF); +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + else if (skb->map_mf) + fh->frag_off |= htons(IP6_MF); + } + else { + if (left > 0 || not_last_frag) + iph->frag_off |= htons(IP_MF); + } +#endif ipv6_hdr(frag)->payload_len = htons(frag->len - sizeof(struct ipv6hdr)); @@ -809,6 +1065,10 @@ /* * Put this fragment into the sending queue. */ +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + if (skb->map_forward == MAP_FORWARD_MODE3) + ip_send_check(iph); +#endif err = output(sk, frag); if (err) goto fail; @@ -827,6 +1087,9 @@ kfree_skb(skb); return err; } +#if defined(CONFIG_BCM_KF_IP) +EXPORT_SYMBOL_GPL(ip6_fragment); +#endif static inline int ip6_rt_check(const struct rt6key *rt_key, const struct in6_addr *fl_addr, diff -urN linux-4.1.52-orig/net/ipv6/ip6_tunnel.c linux-4.1.52/net/ipv6/ip6_tunnel.c --- linux-4.1.52-orig/net/ipv6/ip6_tunnel.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv6/ip6_tunnel.c 2020-02-07 01:47:54.000000000 +0800 @@ -58,6 +58,10 @@ #include #include +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#include +#endif + MODULE_AUTHOR("Ville Nuorvala"); MODULE_DESCRIPTION("IPv6 tunneling device"); MODULE_LICENSE("GPL"); @@ -854,6 +858,15 @@ u64_stats_update_begin(&tstats->syncp); tstats->rx_packets++; tstats->rx_bytes += skb->len; + +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_lock(); + blog_link(TOS_MODE, blog_ptr(skb), NULL, DIR_RX, + (t->parms.flags & IP6_TNL_F_RCV_DSCP_COPY) ? + BLOG_TOS_INHERIT : BLOG_TOS_FIXED); + blog_link(IF_DEVICE, blog_ptr(skb), (void*)t->dev, DIR_RX, skb->len); + blog_unlock(); +#endif u64_stats_update_end(&tstats->syncp); netif_rx(skb); @@ -989,6 +1002,9 @@ unsigned int max_headroom = sizeof(struct ipv6hdr); u8 proto; int err = -1; +#if defined(CONFIG_BCM_KF_IP) + u8 needFrag = 0; +#endif /* NBMA tunnel */ if (ipv6_addr_any(&t->parms.raddr)) { @@ -1051,8 +1067,12 @@ skb_dst(skb)->ops->update_pmtu(skb_dst(skb), NULL, skb, mtu); if (skb->len > mtu) { *pmtu = mtu; +#if defined(CONFIG_BCM_KF_IP) + needFrag = 1; +#else err = -EMSGSIZE; goto tx_err_dst_release; +#endif } skb_scrub_packet(skb, !net_eq(t->net, dev_net(dev))); @@ -1103,7 +1123,17 @@ ipv6h->nexthdr = proto; ipv6h->saddr = fl6->saddr; ipv6h->daddr = fl6->daddr; +#if defined(CONFIG_BCM_KF_IP) + if (needFrag) { + skb->ignore_df = 1; + ip6_fragment(skb->sk, skb, ip6_local_out_sk); + } + else { + ip6tunnel_xmit(skb->sk, skb, dev); + } +#else ip6tunnel_xmit(NULL, skb, dev); +#endif if (ndst) ip6_tnl_dst_store(t, ndst); return 0; @@ -1147,6 +1177,15 @@ if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK) fl6.flowi6_mark = skb->mark; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_lock(); + blog_link(TOS_MODE, blog_ptr(skb), NULL, DIR_TX, + (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS) ? + BLOG_TOS_INHERIT : BLOG_TOS_FIXED); + blog_link(IF_DEVICE, blog_ptr(skb), (void*)dev, DIR_TX, skb->len); + blog_unlock(); +#endif + err = ip6_tnl_xmit2(skb, dev, dsfield, &fl6, encap_limit, &mtu); if (err != 0) { /* XXX: send ICMP error even if DF is not set. */ @@ -1566,6 +1605,9 @@ /* This perm addr will be used as interface identifier by IPv6 */ dev->addr_assign_type = NET_ADDR_RANDOM; eth_random_addr(dev->perm_addr); +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + dev->blog_stats_flags |= BLOG_DEV_STAT_FLAG_INCLUDE_ALL; +#endif } diff -urN linux-4.1.52-orig/net/ipv6/ipv6_sockglue.c linux-4.1.52/net/ipv6/ipv6_sockglue.c --- linux-4.1.52-orig/net/ipv6/ipv6_sockglue.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv6/ipv6_sockglue.c 2020-02-07 01:47:54.000000000 +0800 @@ -48,6 +48,10 @@ #include #include #include +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#include +#include +#endif #include #include #include @@ -215,7 +219,16 @@ sock_prot_inuse_add(net, &tcp_prot, 1); local_bh_enable(); sk->sk_prot = &tcp_prot; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) icsk->icsk_af_ops = &ipv4_specific; +#else +#ifdef CONFIG_MPTCP + if (sock_flag(sk, SOCK_MPTCP)) + icsk->icsk_af_ops = &mptcp_v4_specific; + else +#endif + icsk->icsk_af_ops = &ipv4_specific; +#endif sk->sk_socket->ops = &inet_stream_ops; sk->sk_family = PF_INET; tcp_sync_mss(sk, icsk->icsk_pmtu_cookie); @@ -241,7 +254,16 @@ pktopt = xchg(&np->pktoptions, NULL); kfree_skb(pktopt); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) sk->sk_destruct = inet_sock_destruct; +#else +#ifdef CONFIG_MPTCP + if (is_meta_sk(sk)) + sk->sk_destruct = mptcp_sock_destruct; + else +#endif + sk->sk_destruct = inet_sock_destruct; +#endif /* * ... and add it to the refcnt debug socks count * in the new family. -acme diff -urN linux-4.1.52-orig/net/ipv6/Makefile linux-4.1.52/net/ipv6/Makefile --- linux-4.1.52-orig/net/ipv6/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv6/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -2,6 +2,11 @@ # Makefile for the Linux TCP/IP (INET6) layer. # +ifdef BCM_KF # defined(CONFIG_BCM_KF_BLOG) +EXTRA_CFLAGS += -I$(INC_BRCMDRIVER_PUB_PATH)/$(BRCM_BOARD) +EXTRA_CFLAGS += -I$(INC_BRCMSHARED_PUB_PATH)/bcm963xx +endif # BCM_KF + obj-$(CONFIG_IPV6) += ipv6.o ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \ diff -urN linux-4.1.52-orig/net/ipv6/mcast.c linux-4.1.52/net/ipv6/mcast.c --- linux-4.1.52-orig/net/ipv6/mcast.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv6/mcast.c 2020-02-07 01:47:54.000000000 +0800 @@ -888,6 +888,9 @@ return -ENOMEM; } +#if defined(CONFIG_BCM_KF_MCAST_GR_SUPPRESSION) + mc->mca_osfmode = MCAST_INCLUDE; +#endif mc->next = idev->mc_list; idev->mc_list = mc; @@ -1905,6 +1908,9 @@ /* change recs */ for (pmc = idev->mc_list; pmc; pmc = pmc->next) { spin_lock_bh(&pmc->mca_lock); +#if defined(CONFIG_BCM_KF_MCAST_GR_SUPPRESSION) + if ( pmc->mca_osfmode == pmc->mca_sfmode ) { +#endif if (pmc->mca_sfcount[MCAST_EXCLUDE]) { type = MLD2_BLOCK_OLD_SOURCES; dtype = MLD2_ALLOW_NEW_SOURCES; @@ -1914,15 +1920,29 @@ } skb = add_grec(skb, pmc, type, 0, 0, 0); skb = add_grec(skb, pmc, dtype, 0, 1, 0); /* deleted sources */ +#if defined(CONFIG_BCM_KF_MCAST_GR_SUPPRESSION) + } +#endif /* filter mode changes */ if (pmc->mca_crcount) { +#if defined(CONFIG_BCM_KF_MCAST_GR_SUPPRESSION) + if ( pmc->mca_osfmode != pmc->mca_sfmode ) { +#endif if (pmc->mca_sfmode == MCAST_EXCLUDE) type = MLD2_CHANGE_TO_EXCLUDE; else type = MLD2_CHANGE_TO_INCLUDE; skb = add_grec(skb, pmc, type, 0, 0, 0); +#if defined(CONFIG_BCM_KF_MCAST_GR_SUPPRESSION) + } +#endif pmc->mca_crcount--; +#if defined(CONFIG_BCM_KF_MCAST_GR_SUPPRESSION) + if ( pmc->mca_crcount == 0 ) { + pmc->mca_osfmode = pmc->mca_sfmode; + } +#endif } spin_unlock_bh(&pmc->mca_lock); } diff -urN linux-4.1.52-orig/net/ipv6/ndisc.c linux-4.1.52/net/ipv6/ndisc.c --- linux-4.1.52-orig/net/ipv6/ndisc.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv6/ndisc.c 2020-02-07 01:47:54.000000000 +0800 @@ -982,8 +982,12 @@ return; } +#if defined(CONFIG_BCM_KF_IP) + if (!idev->cnf.forwarding || (idev->dev->priv_flags & IFF_WANDEV)) +#else /* Don't accept RS if we're not in router mode */ if (!idev->cnf.forwarding) +#endif goto out; /* diff -urN linux-4.1.52-orig/net/ipv6/netfilter/nf_conntrack_reasm.c linux-4.1.52/net/ipv6/netfilter/nf_conntrack_reasm.c --- linux-4.1.52-orig/net/ipv6/netfilter/nf_conntrack_reasm.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv6/netfilter/nf_conntrack_reasm.c 2020-02-07 01:47:54.000000000 +0800 @@ -634,6 +634,9 @@ kfree_skb(clone); return skb; } +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) +EXPORT_SYMBOL_GPL(nf_ct_frag6_gather); +#endif void nf_ct_frag6_consume_orig(struct sk_buff *skb) { @@ -646,6 +649,9 @@ s = s2; } } +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) +EXPORT_SYMBOL_GPL(nf_ct_frag6_consume_orig); +#endif static int nf_ct_net_init(struct net *net) { diff -urN linux-4.1.52-orig/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c linux-4.1.52/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c --- linux-4.1.52-orig/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c 2020-02-07 01:47:54.000000000 +0800 @@ -58,6 +58,15 @@ { struct sk_buff *reasm; +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + /* + * MAP_FORWARD_MODE1 indicates Fragment extension header inserted + * for translated MAP-T IPv6 packet in MAP-T driver. + * Netfilter should not defragment in this case. + */ + if (skb->map_forward != MAP_FORWARD_MODE1) + { +#endif #if IS_ENABLED(CONFIG_NF_CONNTRACK) /* Previously seen (loopback)? */ if (skb->nfct && !nf_ct_is_template((struct nf_conn *)skb->nfct)) @@ -78,6 +87,13 @@ NF_HOOK_THRESH(NFPROTO_IPV6, ops->hooknum, state->sk, reasm, state->in, state->out, state->okfn, NF_IP6_PRI_CONNTRACK_DEFRAG + 1); +#if defined(CONFIG_BCM_KF_MAP) && (defined(CONFIG_BCM_MAP) || defined(CONFIG_BCM_MAP_MODULE)) + } + else + NF_HOOK_THRESH(NFPROTO_IPV6, ops->hooknum, state->sk, skb, + state->in, state->out, + state->okfn, NF_IP6_PRI_CONNTRACK_DEFRAG + 1); +#endif return NF_STOLEN; } diff -urN linux-4.1.52-orig/net/ipv6/netfilter.c linux-4.1.52/net/ipv6/netfilter.c --- linux-4.1.52-orig/net/ipv6/netfilter.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv6/netfilter.c 2020-02-07 01:47:54.000000000 +0800 @@ -32,6 +32,16 @@ }; int err; +#if defined(CONFIG_BCM_KF_NETFILTER) + /* keep original output dev if destination is multicast/linklocal */ + if ( ( fl6.flowi6_oif == 0 ) && ((skb_dst(skb)->dev)) + && ( __ipv6_addr_type(&fl6.daddr) & (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL)) + ) + { + fl6.flowi6_oif = (skb_dst(skb)->dev)->ifindex; + } +#endif + dst = ip6_route_output(net, skb->sk, &fl6); err = dst->error; if (err) { @@ -135,11 +145,28 @@ { const struct ipv6hdr *ip6h = ipv6_hdr(skb); __sum16 csum = 0; +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) + struct in6_addr sAddr; + struct in6_addr dAddr; + + memcpy(&sAddr, &ip6h->saddr, sizeof(struct in6_addr)); + memcpy(&dAddr, &ip6h->daddr, sizeof(struct in6_addr)); +#endif switch (skb->ip_summed) { case CHECKSUM_COMPLETE: if (hook != NF_INET_PRE_ROUTING && hook != NF_INET_LOCAL_IN) break; +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) + if (!csum_ipv6_magic(&sAddr, &dAddr, + skb->len - dataoff, protocol, + csum_sub(skb->csum, + skb_checksum(skb, 0, + dataoff, 0)))) { + skb->ip_summed = CHECKSUM_UNNECESSARY; + break; + } +#else if (!csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, skb->len - dataoff, protocol, csum_sub(skb->csum, @@ -148,8 +175,18 @@ skb->ip_summed = CHECKSUM_UNNECESSARY; break; } +#endif /* fall through */ case CHECKSUM_NONE: +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) + skb->csum = ~csum_unfold( + csum_ipv6_magic(&sAddr, &dAddr, + skb->len - dataoff, + protocol, + csum_sub(0, + skb_checksum(skb, 0, + dataoff, 0)))); +#else skb->csum = ~csum_unfold( csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, skb->len - dataoff, @@ -157,6 +194,7 @@ csum_sub(0, skb_checksum(skb, 0, dataoff, 0)))); +#endif csum = __skb_checksum_complete(skb); } return csum; @@ -170,6 +208,13 @@ const struct ipv6hdr *ip6h = ipv6_hdr(skb); __wsum hsum; __sum16 csum = 0; +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) + struct in6_addr sAddr; + struct in6_addr dAddr; + + memcpy(&sAddr, &ip6h->saddr, sizeof(struct in6_addr)); + memcpy(&dAddr, &ip6h->daddr, sizeof(struct in6_addr)); +#endif switch (skb->ip_summed) { case CHECKSUM_COMPLETE: @@ -178,11 +223,19 @@ /* fall through */ case CHECKSUM_NONE: hsum = skb_checksum(skb, 0, dataoff, 0); +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) + skb->csum = ~csum_unfold(csum_ipv6_magic(&sAddr, + &dAddr, + skb->len - dataoff, + protocol, + csum_sub(0, hsum))); +#else skb->csum = ~csum_unfold(csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, skb->len - dataoff, protocol, csum_sub(0, hsum))); +#endif skb->ip_summed = CHECKSUM_NONE; return __skb_checksum_complete_head(skb, dataoff + len); } diff -urN linux-4.1.52-orig/net/ipv6/raw.c linux-4.1.52/net/ipv6/raw.c --- linux-4.1.52-orig/net/ipv6/raw.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv6/raw.c 2020-02-07 01:47:54.000000000 +0800 @@ -157,16 +157,26 @@ */ static bool ipv6_raw_deliver(struct sk_buff *skb, int nexthdr) { +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) + struct in6_addr saddr; + struct in6_addr daddr; +#else const struct in6_addr *saddr; const struct in6_addr *daddr; +#endif struct sock *sk; bool delivered = false; __u8 hash; struct net *net; +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) + memcpy(&saddr, &ipv6_hdr(skb)->saddr, sizeof(struct in6_addr)); + memcpy(&daddr, &ipv6_hdr(skb)->daddr, sizeof(struct in6_addr)); +#else saddr = &ipv6_hdr(skb)->saddr; daddr = saddr + 1; +#endif hash = nexthdr & (RAW_HTABLE_SIZE - 1); read_lock(&raw_v6_hashinfo.lock); @@ -176,8 +186,12 @@ goto out; net = dev_net(skb->dev); +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) + sk = __raw_v6_lookup(net, sk, nexthdr, &daddr, &saddr, inet6_iif(skb)); +#else sk = __raw_v6_lookup(net, sk, nexthdr, daddr, saddr, inet6_iif(skb)); +#endif while (sk) { int filtered; @@ -219,8 +233,13 @@ rawv6_rcv(sk, clone); } } +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) + sk = __raw_v6_lookup(net, sk_next(sk), nexthdr, &daddr, &saddr, + inet6_iif(skb)); +#else sk = __raw_v6_lookup(net, sk_next(sk), nexthdr, daddr, saddr, inet6_iif(skb)); +#endif } out: read_unlock(&raw_v6_hashinfo.lock); @@ -414,6 +433,10 @@ { struct inet_sock *inet = inet_sk(sk); struct raw6_sock *rp = raw6_sk(sk); +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) + struct in6_addr dstAddr; + struct in6_addr srcAddr; +#endif if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) { atomic_inc(&sk->sk_drops); @@ -424,19 +447,38 @@ if (!rp->checksum) skb->ip_summed = CHECKSUM_UNNECESSARY; +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) + memcpy(&srcAddr, &ipv6_hdr(skb)->saddr, sizeof(struct in6_addr)); + memcpy(&dstAddr, &ipv6_hdr(skb)->daddr, sizeof(struct in6_addr)); +#endif + if (skb->ip_summed == CHECKSUM_COMPLETE) { skb_postpull_rcsum(skb, skb_network_header(skb), skb_network_header_len(skb)); +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) + if (!csum_ipv6_magic(&srcAddr, &dstAddr, + skb->len, inet->inet_num, skb->csum)) + skb->ip_summed = CHECKSUM_UNNECESSARY; +#else + if (!csum_ipv6_magic(&ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, skb->len, inet->inet_num, skb->csum)) skb->ip_summed = CHECKSUM_UNNECESSARY; +#endif } if (!skb_csum_unnecessary(skb)) +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) + skb->csum = ~csum_unfold(csum_ipv6_magic(&srcAddr, + &dstAddr, + skb->len, + inet->inet_num, 0)); +#else skb->csum = ~csum_unfold(csum_ipv6_magic(&ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, skb->len, inet->inet_num, 0)); +#endif if (inet->hdrincl) { if (skb_checksum_complete(skb)) { @@ -970,6 +1012,7 @@ switch (optname) { case IPV6_CHECKSUM: +#if !defined(CONFIG_BCM_KF_IP) if (inet_sk(sk)->inet_num == IPPROTO_ICMPV6 && level == IPPROTO_IPV6) { /* @@ -982,6 +1025,7 @@ */ return -EINVAL; } +#endif /* You may get strange result with a positive odd offset; RFC2292bis agrees with me. */ diff -urN linux-4.1.52-orig/net/ipv6/route.c linux-4.1.52/net/ipv6/route.c --- linux-4.1.52-orig/net/ipv6/route.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv6/route.c 2020-02-07 01:47:54.000000000 +0800 @@ -1012,6 +1012,17 @@ const struct ipv6hdr *iph = ipv6_hdr(skb); struct net *net = dev_net(skb->dev); int flags = RT6_LOOKUP_F_HAS_SADDR; +#if defined(CONFIG_MIPS_BCM963XX) && defined(CONFIG_BCM_KF_UNALIGNED_EXCEPTION) + struct flowi6 fl6; + + fl6.flowi6_iif = skb->dev->ifindex; + fl6.flowi6_mark = skb->mark; + fl6.flowi6_proto = iph->nexthdr; + fl6.daddr = iph->daddr; + fl6.saddr = iph->saddr; + memcpy(&fl6.flowlabel, iph, sizeof(__be32)); + fl6.flowlabel &= IPV6_FLOWINFO_MASK; +#else struct flowi6 fl6 = { .flowi6_iif = skb->dev->ifindex, .daddr = iph->daddr, @@ -1020,6 +1031,7 @@ .flowi6_mark = skb->mark, .flowi6_proto = iph->nexthdr, }; +#endif skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags)); } @@ -1868,7 +1880,13 @@ in6_dev = __in6_dev_get(skb->dev); if (!in6_dev) return; +#if defined(CONFIG_BCM_KF_IP) + /* WAN interface needs to act like a host. */ + if (((in6_dev->cnf.forwarding) && !(in6_dev->dev->priv_flags & IFF_WANDEV)) + || (!in6_dev->cnf.accept_redirects)) +#else if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects) +#endif return; /* RFC2461 8.1: @@ -2842,7 +2860,11 @@ if (iif) { #ifdef CONFIG_IPV6_MROUTE if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) { +#if defined(CONFIG_BCM_KF_MROUTE) + int err = ip6mr_get_route(net, skb, rtm, nowait, iif); +#else int err = ip6mr_get_route(net, skb, rtm, nowait); +#endif if (err <= 0) { if (!nowait) { if (err == 0) diff -urN linux-4.1.52-orig/net/ipv6/sit.c linux-4.1.52/net/ipv6/sit.c --- linux-4.1.52-orig/net/ipv6/sit.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv6/sit.c 2020-02-07 01:47:54.000000000 +0800 @@ -56,6 +56,10 @@ #include #include +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#include +#endif + /* This version of net/ipv6/sit.c is cloned of net/ipv4/ip_gre.c @@ -710,6 +714,13 @@ tstats->rx_bytes += skb->len; u64_stats_update_end(&tstats->syncp); +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_lock(); + blog_link(TOS_MODE, blog_ptr(skb), tunnel, DIR_RX, BLOG_TOS_FIXED); + blog_link(IF_DEVICE, blog_ptr(skb), (void*)tunnel->dev, DIR_RX, skb->len); + blog_unlock(); +#endif + netif_rx(skb); return 0; @@ -985,9 +996,36 @@ skb_set_inner_ipproto(skb, IPPROTO_IPV6); - err = iptunnel_xmit(NULL, rt, skb, fl4.saddr, fl4.daddr, - protocol, tos, ttl, df, - !net_eq(tunnel->net, dev_net(dev))); +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_lock(); + blog_link(TOS_MODE, blog_ptr(skb), tunnel, DIR_TX, tunnel->parms.iph.tos); + blog_link(IF_DEVICE, blog_ptr(skb), (void*)dev, DIR_TX, skb->len); + blog_unlock(); +#endif + +#if defined(CONFIG_BCM_KF_IP) + /* + * cd-router #1329: DF flag should not be set + * RFC 3056 sec 4: DF flag should not be set + * RFC 4213 sec 3.2.1: DF flag MUST NOT be set for static MTU cases. + * RFC 4213 sec 3.2.2: For dynamic MTU cases, the algorithm should be: + * if ( (v4MTU-20) < 1280 ) { + * if ( v6Pkt > 1280 ) send ICMPv6 "TooBig" with MTU=1280; + * else encapsulate to v4 packet and DF flag MUST NOT be set + * } + * else { + * if ( v6Pkt > (v4MTU-20) ) send ICMPv6 "TooBig" with MTU=(v4MTU-20); + * else encapsulate to v4 packet and DF flag MUST be set + * } + */ + err = iptunnel_xmit(NULL, rt, skb, fl4.saddr, fl4.daddr, IPPROTO_IPV6, tos, + ttl, 0, !net_eq(tunnel->net, dev_net(dev))); +#else + err = iptunnel_xmit(NULL, rt, skb, fl4.saddr, fl4.daddr, + protocol, tos, ttl, df, + !net_eq(tunnel->net, dev_net(dev))); +#endif + iptunnel_xmit_stats(err, &dev->stats, dev->tstats); return NETDEV_TX_OK; @@ -1373,6 +1411,9 @@ dev->features |= NETIF_F_LLTX; dev->features |= SIT_FEATURES; dev->hw_features |= SIT_FEATURES; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + dev->blog_stats_flags |= BLOG_DEV_STAT_FLAG_INCLUDE_ALL; +#endif } static int ipip6_tunnel_init(struct net_device *dev) diff -urN linux-4.1.52-orig/net/ipv6/syncookies.c linux-4.1.52/net/ipv6/syncookies.c --- linux-4.1.52-orig/net/ipv6/syncookies.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv6/syncookies.c 2020-02-07 01:47:54.000000000 +0800 @@ -19,6 +19,10 @@ #include #include #include +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#include +#include +#endif #include #define COOKIEBITS 24 /* Upper bits store count */ @@ -47,8 +51,30 @@ { struct inet_connection_sock *icsk = inet_csk(sk); struct sock *child; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#ifdef CONFIG_MPTCP + int ret; +#endif +#endif child = icsk->icsk_af_ops->syn_recv_sock(sk, skb, req, dst); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + +#ifdef CONFIG_MPTCP + if (!child) + goto listen_overflow; + + ret = mptcp_check_req_master(sk, child, req, 0); + if (ret < 0) + return NULL; + + if (!ret) + return tcp_sk(child)->mpcb->master_sk; + +listen_overflow: +#endif + +#endif if (child) { atomic_set(&req->rsk_refcnt, 1); inet_csk_reqsk_queue_add(sk, req, child); @@ -131,7 +157,12 @@ } EXPORT_SYMBOL_GPL(__cookie_v6_init_sequence); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) __u32 cookie_v6_init_sequence(struct sock *sk, const struct sk_buff *skb, __u16 *mssp) +#else +__u32 cookie_v6_init_sequence(struct request_sock *req, struct sock *sk, + const struct sk_buff *skb, __u16 *mssp) +#endif { const struct ipv6hdr *iph = ipv6_hdr(skb); const struct tcphdr *th = tcp_hdr(skb); @@ -156,6 +187,9 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) { struct tcp_options_received tcp_opt; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + struct mptcp_options_received mopt; +#endif struct inet_request_sock *ireq; struct tcp_request_sock *treq; struct ipv6_pinfo *np = inet6_sk(sk); @@ -184,20 +218,46 @@ /* check for timestamp cookie support */ memset(&tcp_opt, 0, sizeof(tcp_opt)); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_parse_options(skb, &tcp_opt, 0, NULL); +#else + mptcp_init_mp_opt(&mopt); + tcp_parse_options(skb, &tcp_opt, &mopt, 0, NULL, NULL); +#endif if (!cookie_timestamp_decode(&tcp_opt)) goto out; ret = NULL; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) req = inet_reqsk_alloc(&tcp6_request_sock_ops, sk); +#else +#ifdef CONFIG_MPTCP + if (mopt.saw_mpc) + req = inet_reqsk_alloc(&mptcp6_request_sock_ops, sk); + else +#endif + req = inet_reqsk_alloc(&tcp6_request_sock_ops, sk); +#endif if (!req) goto out; ireq = inet_rsk(req); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + ireq->mptcp_rqsk = 0; + ireq->saw_mpc = 0; +#endif treq = tcp_rsk(req); treq->tfo_listener = false; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + /* Must be done before anything else, as it initializes + * hash_entry of the MPTCP request-sock. + */ + if (mopt.saw_mpc) + mptcp_cookies_reqsk_init(req, &mopt, skb); + +#endif if (security_inet_conn_request(sk, skb, req)) goto out_free; @@ -256,10 +316,17 @@ } req->window_clamp = tp->window_clamp ? :dst_metric(dst, RTAX_WINDOW); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_select_initial_window(tcp_full_space(sk), req->mss, &req->rcv_wnd, &req->window_clamp, ireq->wscale_ok, &rcv_wscale, dst_metric(dst, RTAX_INITRWND)); +#else + tp->ops->select_initial_window(tcp_full_space(sk), req->mss, + &req->rcv_wnd, &req->window_clamp, + ireq->wscale_ok, &rcv_wscale, + dst_metric(dst, RTAX_INITRWND), sk); +#endif ireq->rcv_wscale = rcv_wscale; ireq->ecn_ok = cookie_ecn_ok(&tcp_opt, sock_net(sk), dst); diff -urN linux-4.1.52-orig/net/ipv6/tcp_ipv6.c linux-4.1.52/net/ipv6/tcp_ipv6.c --- linux-4.1.52-orig/net/ipv6/tcp_ipv6.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/ipv6/tcp_ipv6.c 2020-02-07 01:47:54.000000000 +0800 @@ -62,6 +62,10 @@ #include #include #include +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#include +#include +#endif #include #include @@ -70,6 +74,7 @@ #include #include +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb); static void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, struct request_sock *req); @@ -78,6 +83,7 @@ static const struct inet_connection_sock_af_ops ipv6_mapped; static const struct inet_connection_sock_af_ops ipv6_specific; +#endif #ifdef CONFIG_TCP_MD5SIG static const struct tcp_sock_af_ops tcp_sock_ipv6_specific; static const struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific; @@ -89,7 +95,11 @@ } #endif +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static void inet6_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb) +#else +void inet6_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb) +#endif { struct dst_entry *dst = skb_dst(skb); @@ -111,7 +121,11 @@ tcp_hdr(skb)->source); } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, +#else +int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, +#endif int addr_len) { struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr; @@ -209,7 +223,16 @@ sin.sin_port = usin->sin6_port; sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3]; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) icsk->icsk_af_ops = &ipv6_mapped; +#else +#ifdef CONFIG_MPTCP + if (sock_flag(sk, SOCK_MPTCP)) + icsk->icsk_af_ops = &mptcp_v6_mapped; + else +#endif + icsk->icsk_af_ops = &ipv6_mapped; +#endif sk->sk_backlog_rcv = tcp_v4_do_rcv; #ifdef CONFIG_TCP_MD5SIG tp->af_specific = &tcp_sock_ipv6_mapped_specific; @@ -219,7 +242,16 @@ if (err) { icsk->icsk_ext_hdr_len = exthdrlen; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) icsk->icsk_af_ops = &ipv6_specific; +#else +#ifdef CONFIG_MPTCP + if (sock_flag(sk, SOCK_MPTCP)) + icsk->icsk_af_ops = &mptcp_v6_specific; + else +#endif + icsk->icsk_af_ops = &ipv6_specific; +#endif sk->sk_backlog_rcv = tcp_v6_do_rcv; #ifdef CONFIG_TCP_MD5SIG tp->af_specific = &tcp_sock_ipv6_specific; @@ -308,7 +340,11 @@ return err; } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static void tcp_v6_mtu_reduced(struct sock *sk) +#else +void tcp_v6_mtu_reduced(struct sock *sk) +#endif { struct dst_entry *dst; @@ -335,7 +371,11 @@ struct ipv6_pinfo *np; struct tcp_sock *tp; __u32 seq, snd_una; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) struct sock *sk; +#else + struct sock *sk, *meta_sk; +#endif int err; sk = __inet6_lookup_established(net, &tcp_hashinfo, @@ -357,8 +397,19 @@ if (sk->sk_state == TCP_NEW_SYN_RECV) return tcp_req_err(sk, seq); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) bh_lock_sock(sk); if (sock_owned_by_user(sk) && type != ICMPV6_PKT_TOOBIG) +#else + tp = tcp_sk(sk); + if (mptcp(tp)) + meta_sk = mptcp_meta_sk(sk); + else + meta_sk = sk; + + bh_lock_sock(meta_sk); + if (sock_owned_by_user(meta_sk) && type != ICMPV6_PKT_TOOBIG) +#endif NET_INC_STATS_BH(net, LINUX_MIB_LOCKDROPPEDICMPS); if (sk->sk_state == TCP_CLOSE) @@ -369,7 +420,9 @@ goto out; } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tp = tcp_sk(sk); +#endif /* XXX (TFO) - tp->snd_una should be ISN (tcp_create_openreq_child() */ fastopen = tp->fastopen_rsk; snd_una = fastopen ? tcp_rsk(fastopen)->snt_isn : tp->snd_una; @@ -403,11 +456,27 @@ goto out; tp->mtu_info = ntohl(info); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (!sock_owned_by_user(sk)) +#else + if (!sock_owned_by_user(meta_sk)) +#endif tcp_v6_mtu_reduced(sk); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) else if (!test_and_set_bit(TCP_MTU_REDUCED_DEFERRED, +#else + else { + if (!test_and_set_bit(TCP_MTU_REDUCED_DEFERRED, +#endif &tp->tsq_flags)) +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) sock_hold(sk); +#else + sock_hold(sk); + if (mptcp(tp)) + mptcp_tsq_flags(sk); + } +#endif goto out; } @@ -423,7 +492,11 @@ if (fastopen && !fastopen->sk) break; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (!sock_owned_by_user(sk)) { +#else + if (!sock_owned_by_user(meta_sk)) { +#endif sk->sk_err = err; sk->sk_error_report(sk); /* Wake people up to see the error (see connect in sock.c) */ @@ -433,23 +506,39 @@ goto out; } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (!sock_owned_by_user(sk) && np->recverr) { +#else + if (!sock_owned_by_user(meta_sk) && np->recverr) { +#endif sk->sk_err = err; sk->sk_error_report(sk); } else sk->sk_err_soft = err; out: +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) bh_unlock_sock(sk); +#else + bh_unlock_sock(meta_sk); +#endif sock_put(sk); } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst, struct flowi *fl, struct request_sock *req, u16 queue_mapping, struct tcp_fastopen_cookie *foc) +#else +int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst, + struct flowi *fl, + struct request_sock *req, + u16 queue_mapping, + struct tcp_fastopen_cookie *foc) +#endif { struct inet_request_sock *ireq = inet_rsk(req); struct ipv6_pinfo *np = inet6_sk(sk); @@ -477,14 +566,22 @@ np->tclass); rcu_read_unlock(); err = net_xmit_eval(err); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (!tcp_rsk(req)->snt_synack && !err) + tcp_rsk(req)->snt_synack = tcp_time_stamp; +#endif } done: return err; } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static void tcp_v6_reqsk_destructor(struct request_sock *req) +#else +void tcp_v6_reqsk_destructor(struct request_sock *req) +#endif { kfree_skb(inet_rsk(req)->pktopts); } @@ -677,8 +774,13 @@ } #endif +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static void tcp_v6_init_req(struct request_sock *req, struct sock *sk, struct sk_buff *skb) +#else +static int tcp_v6_init_req(struct request_sock *req, struct sock *sk, + struct sk_buff *skb, bool want_cookie) +#endif { struct inet_request_sock *ireq = inet_rsk(req); struct ipv6_pinfo *np = inet6_sk(sk); @@ -699,6 +801,10 @@ atomic_inc(&skb->users); ireq->pktopts = skb; } +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + + return 0; +#endif } static struct dst_entry *tcp_v6_route_req(struct sock *sk, struct flowi *fl, @@ -720,7 +826,11 @@ .syn_ack_timeout = tcp_syn_ack_timeout, }; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = { +#else +const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = { +#endif .mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr), #ifdef CONFIG_TCP_MD5SIG @@ -738,9 +848,17 @@ }; static void tcp_v6_send_response(struct sock *sk, struct sk_buff *skb, u32 seq, +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) u32 ack, u32 win, u32 tsval, u32 tsecr, +#else + u32 ack, u32 data_ack, u32 win, u32 tsval, u32 tsecr, +#endif int oif, struct tcp_md5sig_key *key, int rst, +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) u8 tclass, u32 label) +#else + u8 tclass, u32 label, int mptcp) +#endif { const struct tcphdr *th = tcp_hdr(skb); struct tcphdr *t1; @@ -758,7 +876,12 @@ if (key) tot_len += TCPOLEN_MD5SIG_ALIGNED; #endif - +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#ifdef CONFIG_MPTCP + if (mptcp) + tot_len += MPTCP_SUB_LEN_DSS + MPTCP_SUB_LEN_ACK; +#endif +#endif buff = alloc_skb(MAX_HEADER + sizeof(struct ipv6hdr) + tot_len, GFP_ATOMIC); if (!buff) @@ -796,9 +919,24 @@ tcp_v6_md5_hash_hdr((__u8 *)topt, key, &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, t1); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + topt += 4; +#endif } #endif +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#ifdef CONFIG_MPTCP + if (mptcp) { + /* Construction of 32-bit data_ack */ + *topt++ = htonl((TCPOPT_MPTCP << 24) | + ((MPTCP_SUB_LEN_DSS + MPTCP_SUB_LEN_ACK) << 16) | + (0x20 << 8) | + (0x01)); + *topt++ = htonl(data_ack); + } +#endif +#endif memset(&fl6, 0, sizeof(fl6)); fl6.daddr = ipv6_hdr(skb)->saddr; fl6.saddr = ipv6_hdr(skb)->daddr; @@ -836,7 +974,11 @@ kfree_skb(buff); } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb) +#else +void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb) +#endif { const struct tcphdr *th = tcp_hdr(skb); u32 seq = 0, ack_seq = 0; @@ -896,7 +1038,11 @@ (th->doff << 2); oif = sk ? sk->sk_bound_dev_if : 0; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_v6_send_response(sk, skb, seq, ack_seq, 0, 0, 0, oif, key, 1, 0, 0); +#else + tcp_v6_send_response(sk, skb, seq, ack_seq, 0, 0, 0, 0, oif, key, 1, 0, 0, 0); +#endif #ifdef CONFIG_TCP_MD5SIG release_sk1: @@ -908,44 +1054,91 @@ } static void tcp_v6_send_ack(struct sock *sk, struct sk_buff *skb, u32 seq, +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) u32 ack, u32 win, u32 tsval, u32 tsecr, int oif, +#else + u32 ack, u32 data_ack, u32 win, u32 tsval, u32 tsecr, int oif, +#endif struct tcp_md5sig_key *key, u8 tclass, +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) u32 label) +#else + u32 label, int mptcp) +#endif { +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_v6_send_response(sk, skb, seq, ack, win, tsval, tsecr, oif, key, 0, tclass, label); +#else + tcp_v6_send_response(sk, skb, seq, ack, data_ack, win, tsval, tsecr, oif, + key, 0, tclass, label, mptcp); +#endif } static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb) { struct inet_timewait_sock *tw = inet_twsk(sk); struct tcp_timewait_sock *tcptw = tcp_twsk(sk); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + u32 data_ack = 0; + int mptcp = 0; +#endif +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (tcptw->mptcp_tw && tcptw->mptcp_tw->meta_tw) { + data_ack = (u32)tcptw->mptcp_tw->rcv_nxt; + mptcp = 1; + } +#endif tcp_v6_send_ack(sk, skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt, +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + data_ack, +#endif tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale, tcp_time_stamp + tcptw->tw_ts_offset, tcptw->tw_ts_recent, tw->tw_bound_dev_if, tcp_twsk_md5_key(tcptw), +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tw->tw_tclass, cpu_to_be32(tw->tw_flowlabel)); +#else + tw->tw_tclass, cpu_to_be32(tw->tw_flowlabel), mptcp); +#endif inet_twsk_put(tw); } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, struct request_sock *req) +#else +void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, + struct request_sock *req) +#endif { /* sk->sk_state == TCP_LISTEN -> for regular TCP_SYN_RECV * sk->sk_state == TCP_SYN_RECV -> for Fast Open. */ tcp_v6_send_ack(sk, skb, (sk->sk_state == TCP_LISTEN) ? tcp_rsk(req)->snt_isn + 1 : tcp_sk(sk)->snd_nxt, +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) tcp_rsk(req)->rcv_nxt, req->rcv_wnd, +#else + tcp_rsk(req)->rcv_nxt, 0, req->rcv_wnd, +#endif tcp_time_stamp, req->ts_recent, sk->sk_bound_dev_if, tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->saddr), +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) 0, 0); +#else + 0, 0, 0); +#endif } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static struct sock *tcp_v6_hnd_req(struct sock *sk, struct sk_buff *skb) +#else +struct sock *tcp_v6_hnd_req(struct sock *sk, struct sk_buff *skb) +#endif { const struct tcphdr *th = tcp_hdr(skb); struct request_sock *req; @@ -968,6 +1161,13 @@ if (nsk) { if (nsk->sk_state != TCP_TIME_WAIT) { +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + /* Don't lock again the meta-sk. It has been locked + * before mptcp_v6_do_rcv. + */ + if (mptcp(tcp_sk(nsk)) && !is_meta_sk(sk)) + bh_lock_sock(mptcp_meta_sk(nsk)); +#endif bh_lock_sock(nsk); return nsk; } @@ -982,7 +1182,11 @@ return sk; } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) +#else +int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) +#endif { if (skb->protocol == htons(ETH_P_IP)) return tcp_v4_conn_request(sk, skb); @@ -998,9 +1202,15 @@ return 0; /* don't send reset */ } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, struct request_sock *req, struct dst_entry *dst) +#else +struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, + struct request_sock *req, + struct dst_entry *dst) +#endif { struct inet_request_sock *ireq; struct ipv6_pinfo *newnp, *np = inet6_sk(sk); @@ -1035,7 +1245,19 @@ newnp->saddr = newsk->sk_v6_rcv_saddr; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) inet_csk(newsk)->icsk_af_ops = &ipv6_mapped; +#else +#ifdef CONFIG_MPTCP + /* We must check on the request-socket because the listener + * socket's flag may have been changed halfway through. + */ + if (!inet_rsk(req)->saw_mpc) + inet_csk(newsk)->icsk_af_ops = &mptcp_v6_mapped; + else +#endif + inet_csk(newsk)->icsk_af_ops = &ipv6_mapped; +#endif newsk->sk_backlog_rcv = tcp_v4_do_rcv; #ifdef CONFIG_TCP_MD5SIG newtp->af_specific = &tcp_sock_ipv6_mapped_specific; @@ -1081,6 +1303,16 @@ if (!newsk) goto out_nonewsk; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#ifdef CONFIG_MPTCP + /* If the meta_sk is v6-mapped we can end up here with the wrong af_ops. + * Just make sure that this subflow is v6. + */ + if (is_meta_sk(sk)) + inet_csk(newsk)->icsk_af_ops = &mptcp_v6_specific; +#endif + +#endif /* * No need to charge this sock to the relevant IPv6 refcnt debug socks * count here, tcp_create_openreq_child now does this for us, see the @@ -1205,7 +1437,11 @@ * This is because we cannot sleep with the original spinlock * held. */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) +#else +int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) +#endif { struct ipv6_pinfo *np = inet6_sk(sk); struct tcp_sock *tp; @@ -1222,6 +1458,11 @@ if (skb->protocol == htons(ETH_P_IP)) return tcp_v4_do_rcv(sk, skb); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (is_meta_sk(sk)) + return mptcp_v6_do_rcv(sk, skb); + +#endif if (sk_filter(sk, skb)) goto discard; @@ -1357,6 +1598,12 @@ TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin + skb->len - th->doff*4); TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#ifdef CONFIG_MPTCP + TCP_SKB_CB(skb)->mptcp_flags = 0; + TCP_SKB_CB(skb)->dss_off = 0; +#endif +#endif TCP_SKB_CB(skb)->tcp_flags = tcp_flag_byte(th); TCP_SKB_CB(skb)->tcp_tw_isn = 0; TCP_SKB_CB(skb)->ip_dsfield = ipv6_get_dsfield(hdr); @@ -1376,7 +1623,11 @@ { const struct tcphdr *th; const struct ipv6hdr *hdr; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) struct sock *sk; +#else + struct sock *sk, *meta_sk = NULL; +#endif int ret; struct net *net = dev_net(skb->dev); @@ -1410,9 +1661,18 @@ goto no_tcp_socket; process: +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (sk->sk_state == TCP_TIME_WAIT) +#else + if (sk && sk->sk_state == TCP_TIME_WAIT) +#endif goto do_time_wait; +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + if (!sk) + goto no_tcp_socket; + +#endif if (hdr->hop_limit < inet6_sk(sk)->min_hopcount) { NET_INC_STATS_BH(net, LINUX_MIB_TCPMINTTLDROP); goto discard_and_relse; @@ -1423,6 +1683,17 @@ tcp_v6_fill_cb(skb, hdr, th); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#ifdef CONFIG_MPTCP + /* Is there a pending request sock for this segment ? */ + if (sk->sk_state == TCP_LISTEN && mptcp_check_req(skb, net)) { + if (sk) + sock_put(sk); + return 0; + } +#endif + +#endif #ifdef CONFIG_TCP_MD5SIG if (tcp_v6_inbound_md5_hash(sk, skb)) goto discard_and_relse; @@ -1434,18 +1705,48 @@ sk_incoming_cpu_update(sk); skb->dev = NULL; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) bh_lock_sock_nested(sk); +#else + if (mptcp(tcp_sk(sk))) { + meta_sk = mptcp_meta_sk(sk); + + bh_lock_sock_nested(meta_sk); + if (sock_owned_by_user(meta_sk)) + skb->sk = sk; + } else { + meta_sk = sk; + bh_lock_sock_nested(sk); + } + +#endif ret = 0; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) if (!sock_owned_by_user(sk)) { if (!tcp_prequeue(sk, skb)) +#else + if (!sock_owned_by_user(meta_sk)) { + if (!tcp_prequeue(meta_sk, skb)) +#endif ret = tcp_v6_do_rcv(sk, skb); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) } else if (unlikely(sk_add_backlog(sk, skb, sk->sk_rcvbuf + sk->sk_sndbuf))) { bh_unlock_sock(sk); +#else + } else if (unlikely(sk_add_backlog(meta_sk, skb, + meta_sk->sk_rcvbuf + meta_sk->sk_sndbuf))) { + bh_unlock_sock(meta_sk); +#endif NET_INC_STATS_BH(net, LINUX_MIB_TCPBACKLOGDROP); goto discard_and_relse; } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) bh_unlock_sock(sk); +#else + + bh_unlock_sock(meta_sk); +#endif sock_put(sk); return ret ? -1 : 0; @@ -1456,6 +1757,28 @@ tcp_v6_fill_cb(skb, hdr, th); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#ifdef CONFIG_MPTCP + if (!sk && th->syn && !th->ack) { + int ret = mptcp_lookup_join(skb, NULL); + + if (ret < 0) { + tcp_v6_send_reset(NULL, skb); + goto discard_it; + } else if (ret > 0) { + return 0; + } + } + + /* Is there a pending request sock for this segment ? */ + if (!sk && mptcp_check_req(skb, net)) { + if (sk) + sock_put(sk); + return 0; + } +#endif + +#endif if (skb->len < (th->doff<<2) || tcp_checksum_complete(skb)) { csum_error: TCP_INC_STATS_BH(net, TCP_MIB_CSUMERRORS); @@ -1507,6 +1830,20 @@ tcp_v6_restore_cb(skb); goto process; } +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#ifdef CONFIG_MPTCP + if (th->syn && !th->ack) { + int ret = mptcp_lookup_join(skb, inet_twsk(sk)); + + if (ret < 0) { + tcp_v6_send_reset(NULL, skb); + goto discard_it; + } else if (ret > 0) { + return 0; + } + } +#endif +#endif /* Fall through to ACK */ } case TCP_TW_ACK: @@ -1559,13 +1896,21 @@ } } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static struct timewait_sock_ops tcp6_timewait_sock_ops = { +#else +struct timewait_sock_ops tcp6_timewait_sock_ops = { +#endif .twsk_obj_size = sizeof(struct tcp6_timewait_sock), .twsk_unique = tcp_twsk_unique, .twsk_destructor = tcp_twsk_destructor, }; +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static const struct inet_connection_sock_af_ops ipv6_specific = { +#else +const struct inet_connection_sock_af_ops ipv6_specific = { +#endif .queue_xmit = inet6_csk_xmit, .send_check = tcp_v6_send_check, .rebuild_header = inet6_sk_rebuild_header, @@ -1597,7 +1942,11 @@ /* * TCP over IPv4 via INET6 API */ +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static const struct inet_connection_sock_af_ops ipv6_mapped = { +#else +const struct inet_connection_sock_af_ops ipv6_mapped = { +#endif .queue_xmit = ip_queue_xmit, .send_check = tcp_v4_send_check, .rebuild_header = inet_sk_rebuild_header, @@ -1634,7 +1983,16 @@ tcp_init_sock(sk); +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) icsk->icsk_af_ops = &ipv6_specific; +#else +#ifdef CONFIG_MPTCP + if (sock_flag(sk, SOCK_MPTCP)) + icsk->icsk_af_ops = &mptcp_v6_specific; + else +#endif + icsk->icsk_af_ops = &ipv6_specific; +#endif #ifdef CONFIG_TCP_MD5SIG tcp_sk(sk)->af_specific = &tcp_sock_ipv6_specific; @@ -1643,7 +2001,11 @@ return 0; } +#if !defined(CONFIG_BCM_MPTCP) || !defined(CONFIG_BCM_KF_MPTCP) static void tcp_v6_destroy_sock(struct sock *sk) +#else +void tcp_v6_destroy_sock(struct sock *sk) +#endif { tcp_v4_destroy_sock(sk); inet6_destroy_sock(sk); @@ -1830,12 +2192,34 @@ static void tcp_v6_clear_sk(struct sock *sk, int size) { struct inet_sock *inet = inet_sk(sk); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#ifdef CONFIG_MPTCP + struct tcp_sock *tp = tcp_sk(sk); + /* size_tk_table goes from the end of tk_table to the end of sk */ + int size_tk_table = size - offsetof(struct tcp_sock, tk_table) - + sizeof(tp->tk_table); +#endif +#endif /* we do not want to clear pinet6 field, because of RCU lookups */ sk_prot_clear_nulls(sk, offsetof(struct inet_sock, pinet6)); size -= offsetof(struct inet_sock, pinet6) + sizeof(inet->pinet6); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + +#ifdef CONFIG_MPTCP + /* We zero out only from pinet6 to tk_table */ + size -= size_tk_table + sizeof(tp->tk_table); +#endif +#endif memset(&inet->pinet6 + 1, 0, size); +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) + +#ifdef CONFIG_MPTCP + memset((char *)&tp->tk_table + sizeof(tp->tk_table), 0, size_tk_table); +#endif + +#endif } struct proto tcpv6_prot = { diff -urN linux-4.1.52-orig/net/Kconfig linux-4.1.52/net/Kconfig --- linux-4.1.52-orig/net/Kconfig 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/Kconfig 2020-02-07 01:47:54.000000000 +0800 @@ -76,10 +76,63 @@ Short answer: say Y. +config BLOG + bool "Enable Network Buffer Logging" + depends on BCM_KF_BLOG + ---help--- + Debug logging of protocol header information of a network packet + buffer as it traverses the Linux networking stack. + + Say N unless you wish to debug kernel networking stack. + +config BLOG_IPV6 + bool "Enable Network Buffer Logging of IPv6 packets" + depends on BCM_KF_BLOG + ---help--- + Debug logging of IPv6 protocol header information of a network packet + buffer as it traverses the Linux networking stack. + + Say N unless you wish to debug IPv6 kernel networking stack. + +config BLOG_MCAST + bool "Enable Network Buffer Logging support for Multicast packets" + depends on BCM_KF_BLOG + ---help--- + Debug logging of Multicast packet replication in Linux networking stack. + + Say N unless you wish to debug Multicast in networking stack. + +config BLOG_GRE + bool "Enable GRE support" + depends on BCM_KF_BLOG + ---help--- + Debug logging of GRE protocol header information of a network packet + buffer as it traverses the Linux networking stack. + + Say N unless you wish to debug GRE in networking stack. + +config BLOG_FEATURE + bool "Enable Per Packet Modification support for packet flow" + depends on BCM_KF_BLOG + ---help--- + Enhance the flow cache to be able to modify the packets on the fly. + +config BLOG_L2TP + bool "Enable L2TP support" + depends on BCM_KF_BLOG + ---help--- + Debug logging of L2TP protocol header information of a network packet + buffer as it traverses the Linux networking stack. + + Say N unless you wish to debug l2TP in networking stack. + if INET source "net/ipv4/Kconfig" source "net/ipv6/Kconfig" source "net/netlabel/Kconfig" +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +source "net/mptcp/Kconfig" +#endif endif # if INET @@ -217,6 +270,9 @@ source "net/lapb/Kconfig" source "net/phonet/Kconfig" source "net/6lowpan/Kconfig" +if BCM_KF_MHI +source "net/mhi/Kconfig" +endif source "net/ieee802154/Kconfig" source "net/mac802154/Kconfig" source "net/sched/Kconfig" diff -urN linux-4.1.52-orig/net/key/af_key.c linux-4.1.52/net/key/af_key.c --- linux-4.1.52-orig/net/key/af_key.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/key/af_key.c 2020-02-07 01:47:54.000000000 +0800 @@ -2771,6 +2771,10 @@ int err, err2; err = xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, true); +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) + if (err == 0) + xfrm_garbage_collect(net); +#endif err2 = unicast_flush_resp(sk, hdr); if (err || err2) { if (err == -ESRCH) /* empty table - old silent behavior */ diff -urN linux-4.1.52-orig/net/l2tp/l2tp_core.c linux-4.1.52/net/l2tp/l2tp_core.c --- linux-4.1.52-orig/net/l2tp/l2tp_core.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/l2tp/l2tp_core.c 2020-02-07 01:47:54.000000000 +0800 @@ -66,6 +66,11 @@ #include "l2tp_core.h" +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#include +#include +#endif + #define L2TP_DRV_VERSION "V2.0" /* L2TP header constants */ @@ -278,6 +283,57 @@ } EXPORT_SYMBOL_GPL(l2tp_session_find); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +/* Like l2tp_session_find() but takes a reference on the returned session. + * Optionally calls session->ref() too if do_ref is true. + */ +struct l2tp_session *l2tp_session_get(struct net *net, + struct l2tp_tunnel *tunnel, + u32 session_id, bool do_ref) +{ + struct hlist_head *session_list; + struct l2tp_session *session; + + if (!tunnel) { + struct l2tp_net *pn = l2tp_pernet(net); + + session_list = l2tp_session_id_hash_2(pn, session_id); + + rcu_read_lock_bh(); + hlist_for_each_entry_rcu(session, session_list, global_hlist) { + if (session->session_id == session_id) { + l2tp_session_inc_refcount(session); + if (do_ref && session->ref) + session->ref(session); + rcu_read_unlock_bh(); + + return session; + } + } + rcu_read_unlock_bh(); + + return NULL; + } + + session_list = l2tp_session_id_hash(tunnel, session_id); + read_lock_bh(&tunnel->hlist_lock); + hlist_for_each_entry(session, session_list, hlist) { + if (session->session_id == session_id) { + l2tp_session_inc_refcount(session); + if (do_ref && session->ref) + session->ref(session); + read_unlock_bh(&tunnel->hlist_lock); + + return session; + } + } + read_unlock_bh(&tunnel->hlist_lock); + + return NULL; +} +EXPORT_SYMBOL_GPL(l2tp_session_get); +#endif + struct l2tp_session *l2tp_session_get_nth(struct l2tp_tunnel *tunnel, int nth, bool do_ref) { @@ -1836,6 +1892,30 @@ } EXPORT_SYMBOL_GPL(l2tp_session_create); + +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +int l2tp_rcv_check(struct net_device *dev, uint16_t tunnel_id, uint16_t session_id) +{ + struct net *net = dev_net(dev); + struct l2tp_tunnel *tunnel; + struct l2tp_session *session = NULL; + int ret = BLOG_L2TP_RCV_NO_TUNNEL; + + tunnel = l2tp_tunnel_find(net, tunnel_id); + if (tunnel) + { //printk("*** l2tp tunnel found!!!\n"); + session = l2tp_session_find(net, tunnel, session_id); + } + if (session) + { + //printk("*** l2tp session found!!!\n"); + ret = BLOG_L2TP_RCV_TUNNEL_FOUND; + } +return ret; +} +EXPORT_SYMBOL(l2tp_rcv_check); +#endif + /***************************************************************************** * Init and cleanup *****************************************************************************/ @@ -1883,6 +1963,11 @@ if (rc) goto out; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + printk(KERN_INFO "L2TP core: blog_l2tp_rcv_check \n" ); + blog_l2tp_rcv_check_fn = (blog_l2tp_rcv_check_t) l2tp_rcv_check; +#endif + l2tp_wq = alloc_workqueue("l2tp", WQ_UNBOUND, 0); if (!l2tp_wq) { pr_err("alloc_workqueue failed\n"); diff -urN linux-4.1.52-orig/net/l2tp/l2tp_core.h linux-4.1.52/net/l2tp/l2tp_core.h --- linux-4.1.52-orig/net/l2tp/l2tp_core.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/l2tp/l2tp_core.h 2020-02-07 01:47:54.000000000 +0800 @@ -246,6 +246,9 @@ struct l2tp_session *l2tp_session_find(struct net *net, struct l2tp_tunnel *tunnel, u32 session_id); +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) +extern struct l2tp_session *l2tp_session_get(struct net *net, struct l2tp_tunnel *tunnel, u32 session_id, bool do_ref); //leo_debug +#endif struct l2tp_session *l2tp_session_get_nth(struct l2tp_tunnel *tunnel, int nth, bool do_ref); struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname); diff -urN linux-4.1.52-orig/net/l2tp/l2tp_ppp.c linux-4.1.52/net/l2tp/l2tp_ppp.c --- linux-4.1.52-orig/net/l2tp/l2tp_ppp.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/l2tp/l2tp_ppp.c 2020-02-07 01:47:54.000000000 +0800 @@ -1168,6 +1168,23 @@ err = -EFAULT; break; } +#if defined(CONFIG_BCM_KF_MISC_BACKPORTS) + if (stats.session_id != 0) { + /* resend to session ioctl handler */ + struct l2tp_session *session = + l2tp_session_get(sock_net(sk), tunnel, stats.session_id, true); + + if (session) { + err = pppol2tp_session_ioctl(session, cmd, arg); + if (session->deref) + session->deref(session); + l2tp_session_dec_refcount(session); + } else { + err = -EBADR; + } + break; + } +#else if (stats.session_id != 0) { /* resend to session ioctl handler */ struct l2tp_session *session = @@ -1178,6 +1195,7 @@ err = -EBADR; break; } +#endif #ifdef CONFIG_XFRM stats.using_ipsec = (sk->sk_policy[0] || sk->sk_policy[1]) ? 1 : 0; #endif diff -urN linux-4.1.52-orig/net/l2tp/Makefile linux-4.1.52/net/l2tp/Makefile --- linux-4.1.52-orig/net/l2tp/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/l2tp/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -2,6 +2,11 @@ # Makefile for the L2TP. # +ifdef BCM_KF # defined(CONFIG_BCM_KF_BLOG) +EXTRA_CFLAGS += -I$(INC_BRCMDRIVER_PUB_PATH)/$(BRCM_BOARD) +EXTRA_CFLAGS += -I$(INC_BRCMSHARED_PUB_PATH)/bcm963xx +endif # BCM_KF + obj-$(CONFIG_L2TP) += l2tp_core.o # Build l2tp as modules if L2TP is M diff -urN linux-4.1.52-orig/net/Makefile linux-4.1.52/net/Makefile --- linux-4.1.52-orig/net/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -18,6 +18,9 @@ obj-$(CONFIG_XFRM) += xfrm/ obj-$(CONFIG_UNIX) += unix/ obj-$(CONFIG_NET) += ipv6/ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +obj-$(CONFIG_MPTCP) += mptcp/ +#endif obj-$(CONFIG_PACKET) += packet/ obj-$(CONFIG_NET_KEY) += key/ obj-$(CONFIG_BRIDGE) += bridge/ @@ -38,6 +41,9 @@ obj-$(CONFIG_L2TP) += l2tp/ obj-$(CONFIG_DECNET) += decnet/ obj-$(CONFIG_PHONET) += phonet/ +ifdef BCM_KF # defined(CONFIG_BCM_KF_MHI) +obj-$(CONFIG_MHI) += mhi/ +endif # BCM_KF ifneq ($(CONFIG_VLAN_8021Q),) obj-y += 8021q/ endif diff -urN linux-4.1.52-orig/net/mhi/Kconfig linux-4.1.52/net/mhi/Kconfig --- linux-4.1.52-orig/net/mhi/Kconfig 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/mhi/Kconfig 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,91 @@ +if (BCM_KF_MHI) +# +# MHI protocol family and drivers +# + +config MHI + bool "Modem-Host Interface" + default n + help + The Modem-Host Interface (MHI) is a packet-oriented transport protocol + developed by Renesas Mobile for use with their modems. + + If unsure, say N. + + +if MHI + +config MHI_L2MUX + tristate "L2 MUX Protocol Layer for MHI" + default y + help + L2 MUX is a protocol layer in the MHI stack. It is required + by the MHI L3 components. + + To compile this driver as a module, choose M here: the module + will be called l2mux. If unsure, say Y. + +config MHI_L3MHI + tristate "L3 MHI Protocol Family (AF_MHI)" + select MHI_L2MUX + default y + help + AF_MHI provides datagram access to L2 channels in MHI, + developed by Renesas Mobile for use with their modems. + + To compile this driver as a module, choose M here: the modules + will be called l3mhi and af_mhi. If unsure, say Y. + +config MHI_L3PHONET + tristate "L3 PHONET Protocol bridge (AF_PHONET)" + select MHI_L2MUX + select PHONET + default y + help + L3 PHONET protocol for MHI protocol family, + developed by Renesas Mobile for use with their modems. + + This driver is a bridge between MHI L3 Phonet and Phonet Protocol Family. + + To compile this driver as a module, choose M here: the module + will be called l3phonet. If unsure, say Y. + +config MHI_L3MHDP + tristate "L3 MHDP IP Tunneling Protocol" + select MHI_L2MUX + select NET_SCH_MHI + select INET_TUNNEL + default y + help + Tunneling means encapsulating data of one protocol type within + another protocol and sending it over a channel that understands the + encapsulating protocol. This particular tunneling driver implements + encapsulation of IP within MHDP (Modem Host Data Protocol), which + is used for communication between the APE and the Modem. + + To compile this driver as a module, choose M here: the module + will be called l3mhdp. If unsure, say Y. + +config MHI_DEBUG + bool "MHI Debugging" + default n + help + Generate lots of debugging messages in the MHI stack. + This option is useful when developing MHI. Otherwise it should be off. + + If unsure, say N. + +config MHI_DUMP_FRAMES + bool "Dump MHI frames on L2 layer" + default n + help + Print out every frame passed through L2MUX into kernel log. + This option is useful when developing MHI. Otherwise it should be off. + + If unsure, say N. + +config MHDP_BONDING_SUPPORT + bool "use mhdp as a bonding slave" + default n +endif +endif # BCM_KF_MHI diff -urN linux-4.1.52-orig/net/mhi/l2mux.c linux-4.1.52/net/mhi/l2mux.c --- linux-4.1.52-orig/net/mhi/l2mux.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/mhi/l2mux.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,777 @@ +#ifdef CONFIG_BCM_KF_MHI +/* +<:copyright-BRCM:2011:DUAL/GPL:standard + + Copyright (c) 2011 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +/* + * File: l2mux.c + * + * Modem-Host Interface (MHI) L2MUX layer + */ + +#include +#include +#include +#include +#include +#include + +#ifdef ACTIVATE_L2MUX_STAT +#include +#include +#include +#include +#endif /* ACTIVATE_L2MUX_STAT */ + +#include + +#ifdef CONFIG_MHI_DEBUG +# define DPRINTK(...) pr_debug("MHI/L2MUX: " __VA_ARGS__) +#else +# define DPRINTK(...) +#endif + +#ifdef ACTIVATE_L2MUX_STAT +#define MAX_COOKIE_LENGTH PAGE_SIZE + +/* MAX_COOKIE_LENGTH/sizeof(struct l2muxstat) */ +#define MAX_DEBUG_MESSAGES 5000 + +#define list_l2mux_first_entry_safe(head, type, member) \ + (list_empty(head) ? NULL : \ + list_first_entry(head, type, member)) +static DEFINE_RWLOCK(l2mux_stat_lock); + +static struct l2mux_stat_info l2mux_sinf; + +#endif + +/* Handle ONLY Non DIX types 0x00-0xff */ +#define ETH_NON_DIX_NPROTO 0x0100 + +/* L2MUX master lock */ +static DEFINE_SPINLOCK(l2mux_lock); + +/* L3 ID -> RX function table */ +static l2mux_skb_fn *l2mux_id2rx_tab[MHI_L3_NPROTO] __read_mostly; + +/* Packet Type -> TX function table */ +static l2mux_skb_fn *l2mux_pt2tx_tab[ETH_NON_DIX_NPROTO] __read_mostly; + +/* audio RX/TX fn table */ +static l2mux_audio_fn *l2mux_audio_rx_fn __read_mostly; +static int l2mux_audio_rx_handle __read_mostly; + +static l2mux_audio_fn *l2mux_audio_tx_tab[L2MUX_AUDIO_DEV_MAX] __read_mostly; +static uint8_t l2mux_audio_tx_pn_map[L2MUX_AUDIO_DEV_MAX] __read_mostly; + +#ifdef ACTIVATE_L2MUX_STAT + +static void l2mux_write_stat(unsigned l3pid, unsigned l3len, + enum l2mux_direction dir, struct net_device *dev); +static ssize_t store_l2mux_traces_state(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); +static ssize_t show_l2mux_traces_state(struct device *dev, + struct device_attribute *attr, + char *buf); + +static struct device_attribute l2mux_dev_attrs[] = { + __ATTR(l2mux_trace_status, + S_IRUGO | S_IWUSR, + show_l2mux_traces_state, + store_l2mux_traces_state), + __ATTR_NULL, +}; + +void l2mux_stat_dowork(struct work_struct *work) +{ + int err; + struct l2mux_stat_info *info = + container_of(work, struct l2mux_stat_info, l2mux_stat_work); + + struct net_device *dev = info->dev; + + if (l2mux_sinf.l2mux_traces_activation_done != 1) { + + err = device_create_file(&dev->dev, &l2mux_dev_attrs[0]); + + if (err == 0) + l2mux_sinf.l2mux_traces_activation_done = 1; + else + pr_err("L2MUX cannot create device file\n"); + } +} + +/*call this function to update the l2mux write statistic*/ +static void +l2mux_write_stat(unsigned l3pid, + unsigned l3len, + enum l2mux_direction dir, struct net_device *dev) +{ + + struct l2muxstat *tmp_stat; + struct l2muxstat *old_stat; + + l2mux_sinf.l2mux_total_stat_counter++; + + if ((dev != NULL) && (l2mux_sinf.l2mux_traces_activation_done == 0)) { + l2mux_sinf.dev = dev; + schedule_work(&l2mux_sinf.l2mux_stat_work); + return; + + } else { + + if ((ON == l2mux_sinf.l2mux_traces_state) || + (KERNEL == l2mux_sinf.l2mux_traces_state)) { + + if (write_trylock(&l2mux_stat_lock)) { + + tmp_stat = kmalloc(sizeof(struct l2muxstat), + GFP_ATOMIC); + if (NULL == tmp_stat) { + write_unlock(&l2mux_stat_lock); + return; + } + + tmp_stat->l3pid = l3pid; + tmp_stat->l3len = l3len; + tmp_stat->dir = dir; + do_gettimeofday(&(tmp_stat->time_val)); + tmp_stat->stat_counter = + l2mux_sinf.l2mux_total_stat_counter; + + if (l2mux_sinf.l2mux_stat_id < 0) + l2mux_sinf.l2mux_stat_id = 0; + + l2mux_sinf.l2mux_stat_id++; + + if (l2mux_sinf.l2mux_stat_id >= + MAX_DEBUG_MESSAGES) { + + old_stat = + list_l2mux_first_entry_safe + (&l2mux_sinf.l2muxstat_tab.list, + struct l2muxstat, list); + if (old_stat != NULL) { + list_del(&old_stat->list); + kfree(old_stat); + l2mux_sinf.l2mux_stat_id = + MAX_DEBUG_MESSAGES; + } + } + + list_add_tail(&(tmp_stat->list), + &(l2mux_sinf.l2muxstat_tab.list)); + + write_unlock(&l2mux_stat_lock); + } + } + } + /*in the case lock is taken, information is missed */ +} + +/* start() method */ +static void *l2mux_seq_start(struct seq_file *seq, loff_t *pos) +{ + void *ret = NULL; + + if (l2mux_sinf.l2mux_traces_state == OFF) { + pr_err("L2MUX traces are off. activation -echo on > /sys/class/net/my_modem_net_device/l2mux_trace_status -sizeof(l2muxstat) = %zu\n", + sizeof(struct l2muxstat)); + } else { + if (write_trylock(&l2mux_stat_lock)) { + ret = + list_l2mux_first_entry_safe(&l2mux_sinf. + l2muxstat_tab.list, + struct l2muxstat, list); + write_unlock(&l2mux_stat_lock); + } + } + + return ret; +} + +/* next() method */ +static void *l2mux_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + return list_l2mux_first_entry_safe(&l2mux_sinf.l2muxstat_tab.list, + struct l2muxstat, list); +} + +/* show() method */ +static int l2mux_seq_show(struct seq_file *seq, void *v) +{ + struct l2muxstat *tmp_stat = v; + char temp_string[100]; + + if (write_trylock(&l2mux_stat_lock)) { + + while (l2mux_sinf.previous_stat_counter != + (tmp_stat->stat_counter - 1)) { + + sprintf(temp_string, + "L2MHI_%d : missed : NA : NA : NA : NA\n", + l2mux_sinf.previous_stat_counter + 1); + + /* Interpret the iterator, 'v' */ + seq_puts(seq, temp_string); + + l2mux_sinf.previous_stat_counter++; + } + + l2mux_sinf.previous_stat_counter = tmp_stat->stat_counter; + + sprintf(temp_string, "L2MHI_%d : %d : %d : %x : %d : %d\n", + tmp_stat->stat_counter, tmp_stat->dir, + tmp_stat->l3pid, tmp_stat->l3len, + (unsigned int)tmp_stat->time_val.tv_sec, + (unsigned int)tmp_stat->time_val.tv_usec); + + /* Interpret the iterator, 'v' */ + seq_puts(seq, temp_string); + + if (l2mux_sinf.l2mux_traces_state == KERNEL) + pr_err("%s", temp_string); + + list_del(&tmp_stat->list); + kfree(tmp_stat); + tmp_stat = NULL; + l2mux_sinf.l2mux_stat_id--; + + write_unlock(&l2mux_stat_lock); + } + + return 0; +} + +/* stop() method */ +static void l2mux_seq_stop(struct seq_file *seq, void *v) +{ + /* No cleanup needed */ +} + +/* Define iterator operations */ +static const struct seq_operations l2mux_seq_ops = { + .start = l2mux_seq_start, + .next = l2mux_seq_next, + .stop = l2mux_seq_stop, + .show = l2mux_seq_show, +}; + +static int l2mux_seq_open(struct inode *inode, struct file *file) +{ + /* Register the operators */ + return seq_open(file, &l2mux_seq_ops); +} + +static const struct file_operations l2mux_proc_fops = { + .owner = THIS_MODULE, + .open = l2mux_seq_open, /* User supplied */ + .read = seq_read, /* Built-in helper function */ + .llseek = seq_lseek, /* Built-in helper function */ + .release = seq_release, /* Built-in helper funciton */ +}; + +/*call this function to init the l2mux write statistic*/ +void init_l2mux_stat(void) +{ + l2mux_sinf.proc_entry = + proc_create("l2mux_mhi", 0644, NULL, &l2mux_proc_fops); + + if (l2mux_sinf.proc_entry == NULL) + DPRINTK("cannot create proc file l2mux_mhi\n"); + else { + + l2mux_sinf.l2mux_stat_id = 0; + l2mux_sinf.previous_stat_counter = 0; + l2mux_sinf.l2mux_total_stat_counter = 0; + l2mux_sinf.l2mux_traces_state = OFF; + l2mux_sinf.l2mux_traces_activation_done = 0; + INIT_LIST_HEAD(&l2mux_sinf.l2muxstat_tab.list); + INIT_WORK(&l2mux_sinf.l2mux_stat_work, l2mux_stat_dowork); + } +} + +/*call this function to exit the l2mux write statistic*/ +void exit_l2mux_stat(void) +{ + remove_proc_entry("l2mux_mhi", l2mux_sinf.proc_entry); +} + +/** + * store_l2mux_traces_state - store the l2mux traces status + * @dev: Device to be created + * @attr: attribute of sysfs + * @buf: output stringwait + */ +static ssize_t +store_l2mux_traces_state(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int retval = count; + + if (sysfs_streq(buf, "on")) { + l2mux_sinf.l2mux_traces_state = ON; + pr_err("L2MUX traces activated and available in proc fs\n"); + } else if (sysfs_streq(buf, "off")) { + l2mux_sinf.l2mux_traces_state = OFF; + } else if (sysfs_streq(buf, "kernel")) { + l2mux_sinf.l2mux_traces_state = KERNEL; + } else { + retval = -EINVAL; + } + return retval; +} + +/** + * show_l2mux_traces_state - show l2mux traces state + * @dev: Funnel device + * @attr: attribute of sysfs + * @buf: string written to sysfs file + */ +static ssize_t +show_l2mux_traces_state(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int retval = 0; + char *temp_buf = buf; + + switch (l2mux_sinf.l2mux_traces_state) { + case ON: + return sprintf(temp_buf, "on\n"); + case OFF: + return sprintf(temp_buf, "off\n"); + case KERNEL: + return sprintf(temp_buf, "kernel\n"); + default: + return -ENODEV; + } + + return retval; +} +#endif /* ACTIVATE_L2MUX_STAT */ + +int l2mux_netif_rx_register(int l3, l2mux_skb_fn *fn) +{ + int err = 0; + + DPRINTK("l2mux_netif_rx_register(l3:%d, fn:%p)\n", l3, fn); + + if (l3 < 0 || l3 >= MHI_L3_NPROTO) + return -EINVAL; + + if (!fn) + return -EINVAL; + + spin_lock(&l2mux_lock); + { + if (l2mux_id2rx_tab[l3] == NULL) + l2mux_id2rx_tab[l3] = fn; + else + err = -EBUSY; + } + spin_unlock(&l2mux_lock); + + return err; +} +EXPORT_SYMBOL(l2mux_netif_rx_register); + +int l2mux_netif_rx_unregister(int l3) +{ + int err = 0; + + DPRINTK("l2mux_netif_rx_unregister(l3:%d)\n", l3); + + if (l3 < 0 || l3 >= MHI_L3_NPROTO) + return -EINVAL; + + spin_lock(&l2mux_lock); + { + if (l2mux_id2rx_tab[l3]) + l2mux_id2rx_tab[l3] = NULL; + else + err = -EPROTONOSUPPORT; + } + spin_unlock(&l2mux_lock); + + return err; +} +EXPORT_SYMBOL(l2mux_netif_rx_unregister); + +int l2mux_netif_tx_register(int pt, l2mux_skb_fn *fn) +{ + int err = 0; + + DPRINTK("l2mux_netif_tx_register(pt:%d, fn:%p)\n", pt, fn); + + if (pt <= 0 || pt >= ETH_NON_DIX_NPROTO) + return -EINVAL; + + if (!fn) + return -EINVAL; + + spin_lock(&l2mux_lock); + { + if (l2mux_pt2tx_tab[pt] == NULL) + l2mux_pt2tx_tab[pt] = fn; + else + err = -EBUSY; + } + spin_unlock(&l2mux_lock); + + return err; +} +EXPORT_SYMBOL(l2mux_netif_tx_register); + +int l2mux_netif_tx_unregister(int pt) +{ + int err = 0; + + DPRINTK("l2mux_netif_tx_unregister(pt:%d)\n", pt); + + if (pt <= 0 || pt >= ETH_NON_DIX_NPROTO) + return -EINVAL; + + spin_lock(&l2mux_lock); + { + if (l2mux_pt2tx_tab[pt]) + l2mux_pt2tx_tab[pt] = NULL; + else + err = -EPROTONOSUPPORT; + } + spin_unlock(&l2mux_lock); + + return err; +} +EXPORT_SYMBOL(l2mux_netif_tx_unregister); + +int l2mux_skb_rx(struct sk_buff *skb, struct net_device *dev) +{ + struct l2muxhdr *l2hdr; + unsigned l3pid; + unsigned l3len; + l2mux_skb_fn *rxfn; + + /* Set the device in the skb */ + skb->dev = dev; + + /* Set MAC header here */ + skb_reset_mac_header(skb); + + /* L2MUX header */ + l2hdr = l2mux_hdr(skb); + + /* proto id and length in L2 header */ + l3pid = l2mux_get_proto(l2hdr); + l3len = l2mux_get_length(l2hdr); + +#ifdef ACTIVATE_L2MUX_STAT + l2mux_write_stat(l3pid, l3len, DOWNLINK_DIR, dev); +#endif /* ACTIVATE_L2MUX_STAT */ + +#ifdef CONFIG_MHI_DUMP_FRAMES + { + u8 *ptr = skb->data; + int len = skb_headlen(skb); + int i; + + pr_debug("L2MUX: RX dev:%d skb_len:%d l3_len:%d l3_pid:%d\n", + dev->ifindex, skb->len, l3len, l3pid); + + for (i = 0; i < len; i++) { + if (i % 8 == 0) + pr_debug("L2MUX: RX [%04X] ", i); + pr_debug(" 0x%02X", ptr[i]); + if (i % 8 == 7 || i == len - 1) + pr_debug("\n"); + } + } +#endif + /* check that the advertised length is correct */ + if (l3len != skb->len - L2MUX_HDR_SIZE) { + pr_warn("L2MUX: l2mux_skb_rx: L3_id:%d - skb length mismatch L3:%d (+4) <> SKB:%d", + l3pid, l3len, skb->len); + goto drop; + } + + /* get RX function */ + rxfn = l2mux_id2rx_tab[l3pid]; + + /* Not registered */ + if (!rxfn) + goto drop; + + /* Update RX statistics */ + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; + + /* Call the receiver function */ + return rxfn(skb, dev); + +drop: + dev->stats.rx_dropped++; + kfree_skb(skb); + return NET_RX_DROP; +} +EXPORT_SYMBOL(l2mux_skb_rx); + +int l2mux_skb_tx(struct sk_buff *skb, struct net_device *dev) +{ + l2mux_skb_fn *txfn; + unsigned type; + int err = 0; +#ifdef ACTIVATE_L2MUX_STAT + struct l2muxhdr *l2hdr; + unsigned l3pid; + unsigned l3len; +#endif /* ACTIVATE_L2MUX_STAT */ + + if (unlikely(!skb)) { + pr_err("L2MUX TX skb invalid\n"); + return -EINVAL; + } + + /* Packet type ETH_P_XXX */ + type = ntohs(skb->protocol); + +#ifdef CONFIG_MHI_DUMP_FRAMES + { + u8 *ptr = skb->data; + int len = skb_headlen(skb); + int i; + + pr_debug("L2MUX: TX dev:%d skb_len:%d ETH_P:%d\n", + dev->ifindex, skb->len, type); + + for (i = 0; i < len; i++) { + if (i % 8 == 0) + pr_debug("L2MUX: TX [%04X] ", i); + pr_debug(" 0x%02X", ptr[i]); + if (i % 8 == 7 || i == len - 1) + pr_debug("\n"); + } + } +#endif + /* Only handling non DIX types */ + if (type <= 0 || type >= ETH_NON_DIX_NPROTO) + return -EINVAL; + + /* TX function for this packet type */ + txfn = l2mux_pt2tx_tab[type]; + + if (txfn) + err = txfn(skb, dev); + +#ifdef ACTIVATE_L2MUX_STAT + + if (0 == err) { + /* L2MUX header */ + l2hdr = l2mux_hdr(skb); + /* proto id and length in L2 header */ + l3pid = l2mux_get_proto(l2hdr); + l3len = l2mux_get_length(l2hdr); + + l2mux_write_stat(l3pid, l3len, UPLINK_DIR, dev); + } else { + pr_err("L2MUX TX skb invalid\n"); + } + +#endif /* ACTIVATE_L2MUX_STAT */ + + return err; +} +EXPORT_SYMBOL(l2mux_skb_tx); + +int l2mux_audio_rx_register(l2mux_audio_fn *fn) +{ + int err = -EBUSY, handle; + + handle = ((int)fn & 0x00ffffff) | L2MUX_AUDIO_DEV_TYPE_RX; + spin_lock(&l2mux_lock); + if (l2mux_audio_rx_fn == NULL) { + l2mux_audio_rx_handle = handle; + l2mux_audio_rx_fn = fn; + err = handle; + } + spin_unlock(&l2mux_lock); + return err; +} +EXPORT_SYMBOL(l2mux_audio_rx_register); + +int l2mux_audio_rx_unregister(int handle) +{ + int err = -EPROTONOSUPPORT; + + spin_lock(&l2mux_lock); + if (l2mux_audio_rx_handle == handle) { + l2mux_audio_rx_handle = 0; + l2mux_audio_rx_fn = NULL; + err = 0; + } + spin_unlock(&l2mux_lock); + return err; +} +EXPORT_SYMBOL(l2mux_audio_rx_unregister); + +int l2mux_audio_tx_register(uint8_t phonet_dev_id, l2mux_audio_fn *fn) +{ + int i; + + spin_lock(&l2mux_lock); + for (i = 0; i < L2MUX_AUDIO_DEV_MAX; i++) { + if (l2mux_audio_tx_tab[i] == NULL) { + l2mux_audio_tx_tab[i] = fn; + l2mux_audio_tx_pn_map[i] = phonet_dev_id; + spin_unlock(&l2mux_lock); + return (i | L2MUX_AUDIO_DEV_TYPE_TX); + } + } + spin_unlock(&l2mux_lock); + return -EBUSY; +} +EXPORT_SYMBOL(l2mux_audio_tx_register); + +int l2mux_audio_tx_unregister(int handle) +{ + int err = -EPROTONOSUPPORT; + int internal_dev_id = handle & (~L2MUX_AUDIO_DEV_TYPE_TX); + + if ((internal_dev_id < 0) || (internal_dev_id >= L2MUX_AUDIO_DEV_MAX)) + return err; + + spin_lock(&l2mux_lock); + if (l2mux_audio_tx_tab[internal_dev_id] != NULL) { + l2mux_audio_tx_tab[internal_dev_id] = NULL; + l2mux_audio_tx_pn_map[internal_dev_id] = 0; + err = 0; + } + spin_unlock(&l2mux_lock); + return err; +} +EXPORT_SYMBOL(l2mux_audio_tx_unregister); + +int l2mux_audio_rx(unsigned char *buffer, uint8_t pn_dev_id) +{ + struct l2muxhdr *l2hdr = (struct l2muxhdr *)buffer; + unsigned l3len; + + /* proto id and length in L2 header */ + if (l2mux_get_proto(l2hdr) != MHI_L3_CELLULAR_AUDIO) + return -EINVAL; + + l3len = l2mux_get_length(l2hdr); + + if (l2mux_audio_rx_fn == NULL) + return -EINVAL; + + return l2mux_audio_rx_fn(buffer + L2MUX_HDR_SIZE, l3len, pn_dev_id); +} +EXPORT_SYMBOL(l2mux_audio_rx); + +int l2mux_audio_tx(unsigned char *buffer, size_t size, uint8_t pn_dev_id) +{ + int i; + l2mux_audio_fn *txfn = NULL; + struct l2muxhdr *l2hdr; + + /* TODO for the future!! + * since we don't support multiple devices in RIL yet, we do not + * want to create confusion here, so we simply search for the + * first available txfn registered. (supposedly, there should + * only be one */ +#if 0 + for (i = 0; i < L2MUX_AUDIO_DEV_MAX; i++) { + if (l2mux_audio_tx_pn_map[i] == pn_dev_id) { + txfn = l2mux_audio_tx_tab[i]; + break; + } + } +#else + for (i = 0; i < L2MUX_AUDIO_DEV_MAX; i++) { + if (l2mux_audio_tx_tab[i] != NULL) { + txfn = l2mux_audio_tx_tab[i]; + break; + } + } +#endif + + /* didn't find txfn for the pn_dev_id */ + if (txfn == NULL) + return -EINVAL; + + l2hdr = (struct l2muxhdr *)(buffer - 4); + l2mux_set_proto(l2hdr, MHI_L3_CELLULAR_AUDIO); + l2mux_set_length(l2hdr, size); + + /* we don't care about return value from txfn */ + return txfn((unsigned char *)l2hdr, size + L2MUX_HDR_SIZE, pn_dev_id); +} +EXPORT_SYMBOL(l2mux_audio_tx); + +static int __init l2mux_init(void) +{ + int i; + + DPRINTK("l2mux_init\n"); + + for (i = 0; i < MHI_L3_NPROTO; i++) + l2mux_id2rx_tab[i] = NULL; + + for (i = 0; i < ETH_NON_DIX_NPROTO; i++) + l2mux_pt2tx_tab[i] = NULL; + + l2mux_audio_rx_fn = NULL; + l2mux_audio_rx_handle = 0; + + for (i = 0; i < L2MUX_AUDIO_DEV_MAX; i++) { + l2mux_audio_tx_tab[i] = NULL; + l2mux_audio_tx_pn_map[i] = 0; + } + +#ifdef ACTIVATE_L2MUX_STAT + init_l2mux_stat(); + +#endif /* ACTIVATE_L2MUX_STAT */ + + return 0; +} + +static void __exit l2mux_exit(void) +{ +#ifdef ACTIVATE_L2MUX_STAT + exit_l2mux_stat(); +#endif /* ACTIVATE_L2MUX_STAT */ + DPRINTK("l2mux_exit\n"); +} + +module_init(l2mux_init); +module_exit(l2mux_exit); + +MODULE_DESCRIPTION("L2MUX for MHI Protocol Stack"); +#endif /* CONFIG_BCM_KF_MHI */ diff -urN linux-4.1.52-orig/net/mhi/l3mhdp.c linux-4.1.52/net/mhi/l3mhdp.c --- linux-4.1.52-orig/net/mhi/l3mhdp.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/mhi/l3mhdp.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,1541 @@ +#ifdef CONFIG_BCM_KF_MHI +/* +<:copyright-BRCM:2011:DUAL/GPL:standard + + Copyright (c) 2011 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +/* + * File: l3mhdp.c + * + * MHDP - Modem Host Data Protocol for MHI protocol family. + */ + +#include +#include +#include +#include +#include +#ifdef CONFIG_BLOG +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* wake_lock prevents the system from entering suspend or other + * low power states when active. This seems to be used in an + * Android system, but it is not present in Vanilla Kernel */ +//#define SUPPORT_WAKE_LOCK 1 + +#ifdef SUPPORT_WAKE_LOCK +#include +#endif + +#ifdef CONFIG_MHDP_BONDING_SUPPORT +#define MHDP_BONDING_SUPPORT +#endif + +//#define MHDP_USE_NAPI + +#ifdef MHDP_BONDING_SUPPORT +#include +#endif /*MHDP_BONDING_SUPPORT */ + +#include +#include + +/* MHDP device MTU limits */ +#define MHDP_MTU_MAX 0x2400 +#define MHDP_MTU_MIN 0x44 +#define MAX_MHDP_FRAME_SIZE 16000 + +/* MHDP device names */ +#define MHDP_IFNAME "rmnet%d" +#define MHDP_CTL_IFNAME "rmnetctl" + +/* Print every MHDP SKB content */ +/* #define MHDP_DEBUG_SKB */ + +/* #define CONFIG_MHI_DEBUG */ + +#define UDP_PROT_TYPE 17 + +#define EPRINTK(...) pr_debug("MHI/MHDP: " __VA_ARGS__) + +#ifdef CONFIG_MHI_DEBUG +# define DPRINTK(...) pr_debug("MHI/MHDP: " __VA_ARGS__) +#else +# define DPRINTK(...) +#endif + +#ifdef MHDP_DEBUG_SKB +# define SKBPRINT(a, b) __print_skb_content(a, b) +#else +# define SKBPRINT(a, b) +#endif + +/* IPv6 support */ +#define VER_IPv4 0x04 +#define VER_IPv6 0x06 +#define ETH_IP_TYPE(x) (((0x00|(x>>4)) == VER_IPv4) ? ETH_P_IP : ETH_P_IPV6) + +/*** Type definitions ***/ + +#define MAX_MHDPHDR_SIZE MAX_SKB_FRAGS + +#ifdef MHDP_USE_NAPI +#define NAPI_WEIGHT 64 +#endif /*MHDP_USE_NAPI */ + + +struct mhdp_tunnel { + struct mhdp_tunnel *next; + struct net_device *dev; + struct net_device *master_dev; + struct sk_buff *skb; + int sim_id; + int pdn_id; + int free_pdn; + struct hrtimer tx_timer; + struct tasklet_struct taskl; + struct sk_buff *skb_to_free[MAX_MHDPHDR_SIZE]; + spinlock_t timer_lock; +}; + +struct mhdp_net { + struct mhdp_tunnel *tunnels; + struct net_device *ctl_dev; + struct mhdp_udp_filter udp_filter; + spinlock_t udp_lock; +#ifdef MHDP_USE_NAPI + struct net_device *dev; + struct napi_struct napi; + struct sk_buff_head skb_list; +#endif /*#ifdef MHDP_USE_NAPI */ +#ifdef SUPPORT_WAKE_LOCK + int wake_lock_time; + struct wake_lock wakelock; + spinlock_t wl_lock; +#endif +}; + +struct packet_info { + uint32_t pdn_id; + uint32_t packet_offset; + uint32_t packet_length; +}; + +struct mhdp_hdr { + uint32_t packet_count; + struct packet_info info[MAX_MHDPHDR_SIZE]; +}; + +/*** Prototypes ***/ + +static void mhdp_netdev_setup(struct net_device *dev); + +static void mhdp_submit_queued_skb(struct mhdp_tunnel *tunnel, int force_send); + +static int mhdp_netdev_event(struct notifier_block *this, + unsigned long event, void *ptr); + +static enum hrtimer_restart tx_timer_timeout(struct hrtimer *timer); +static void tx_timer_timeout_tasklet(unsigned long arg); + +#ifdef SUPPORT_WAKE_LOCK +static ssize_t mhdp_write_wakelock_value(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); +static ssize_t mhdp_read_wakelock_value(struct device *dev, + struct device_attribute *attr, + char *buf); +#endif + +#ifdef MHDP_USE_NAPI + +static int mhdp_poll(struct napi_struct *napi, int budget); + +#endif /*MHDP_USE_NAPI */ + +/*** Global Variables ***/ + +static int mhdp_net_id __read_mostly; + +static struct notifier_block mhdp_netdev_notifier = { + .notifier_call = mhdp_netdev_event, +}; + +#ifdef SUPPORT_WAKE_LOCK +static struct device_attribute mhdpwl_dev_attrs[] = { + __ATTR(mhdp_wakelock_time, + S_IRUGO | S_IWUSR, + mhdp_read_wakelock_value, + mhdp_write_wakelock_value), + __ATTR_NULL, +}; +#endif + +/*** Funtions ***/ + +#ifdef MHDP_DEBUG_SKB +static void __print_skb_content(struct sk_buff *skb, const char *tag) +{ + struct page *page; + skb_frag_t *frag; + int len; + int i, j; + u8 *ptr; + + /* Main SKB buffer */ + ptr = (u8 *)skb->data; + len = skb_headlen(skb); + + pr_debug("MHDP: SKB buffer lenght %02u\n", len); + for (i = 0; i < len; i++) { + if (i % 8 == 0) + pr_debug("%s DATA: ", tag); + pr_debug(" 0x%02X", ptr[i]); + if (i % 8 == 7 || i == len - 1) + pr_debug("\n"); + } + + /* SKB fragments */ + for (i = 0; i < (skb_shinfo(skb)->nr_frags); i++) { + frag = &skb_shinfo(skb)->frags[i]; + page = skb_frag_page(frag); + + ptr = page_address(page); + + for (j = 0; j < frag->size; j++) { + if (j % 8 == 0) + pr_debug("%s FRAG[%d]: ", tag, i); + pr_debug(" 0x%02X", ptr[frag->page_offset + j]); + if (j % 8 == 7 || j == frag->size - 1) + pr_debug("\n"); + } + } +} +#endif + +/** + * mhdp_net_dev - Get mhdp_net structure of mhdp tunnel + */ +static inline struct mhdp_net *mhdp_net_dev(struct net_device *dev) +{ + return net_generic(dev_net(dev), mhdp_net_id); +} + +/** + * mhdp_tunnel_init - Initialize MHDP tunnel + */ +static void +mhdp_tunnel_init(struct net_device *dev, + struct mhdp_tunnel_parm *parms, struct net_device *master_dev) +{ + struct mhdp_net *mhdpn = mhdp_net_dev(dev); + struct mhdp_tunnel *tunnel = netdev_priv(dev); + + DPRINTK("mhdp_tunnel_init: dev:%s", dev->name); + + tunnel->next = mhdpn->tunnels; + mhdpn->tunnels = tunnel; +#ifdef SUPPORT_WAKE_LOCK + spin_lock_init(&mhdpn->wl_lock); +#endif + + tunnel->dev = dev; + tunnel->master_dev = master_dev; + tunnel->skb = NULL; + tunnel->sim_id = parms->sim_id; + tunnel->pdn_id = parms->pdn_id; + tunnel->free_pdn = 0; + netdev_path_add(dev, master_dev); + + hrtimer_init(&tunnel->tx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + tunnel->tx_timer.function = &tx_timer_timeout; + tasklet_init(&tunnel->taskl, + tx_timer_timeout_tasklet, (unsigned long)tunnel); + + spin_lock_init(&tunnel->timer_lock); +} + +/** + * mhdp_tunnel_destroy - Destroy MHDP tunnel + */ +static void mhdp_tunnel_destroy(struct net_device *dev) +{ + DPRINTK("mhdp_tunnel_destroy: dev:%s", dev->name); + + netdev_path_remove(dev); + unregister_netdevice(dev); +} + +/** + * mhdp_destroy_tunnels - Initialize all MHDP tunnels + */ +static void mhdp_destroy_tunnels(struct mhdp_net *mhdpn) +{ + struct mhdp_tunnel *tunnel; + + for (tunnel = mhdpn->tunnels; (tunnel); tunnel = tunnel->next) { + mhdp_tunnel_destroy(tunnel->dev); + if (hrtimer_active(&tunnel->tx_timer)) + hrtimer_cancel(&tunnel->tx_timer); + tasklet_kill(&tunnel->taskl); + } + + mhdpn->tunnels = NULL; +} + +/** + * mhdp_locate_tunnel - Retrieve MHDP tunnel thanks to PDN + */ +static inline struct mhdp_tunnel *mhdp_locate_tunnel(struct mhdp_net *mhdpn, + int pdn_id) +{ + struct mhdp_tunnel *tunnel; + + for (tunnel = mhdpn->tunnels; tunnel; tunnel = tunnel->next) + if (tunnel->pdn_id == pdn_id) + return tunnel; + + return NULL; +} + +/** + * mhdp_add_tunnel - Add MHDP tunnel + */ +static struct net_device *mhdp_add_tunnel(struct net *net, + struct mhdp_tunnel_parm *parms) +{ + struct net_device *mhdp_dev, *master_dev; + + DPRINTK("mhdp_add_tunnel: adding a tunnel to %s\n", parms->master); + + master_dev = dev_get_by_name(net, parms->master); + if (!master_dev) + goto err_alloc_dev; + + mhdp_dev = alloc_netdev(sizeof(struct mhdp_tunnel), MHDP_IFNAME, + NET_NAME_UNKNOWN, mhdp_netdev_setup); + if (!mhdp_dev) + goto err_alloc_dev; + + dev_net_set(mhdp_dev, net); + + if (dev_alloc_name(mhdp_dev, MHDP_IFNAME) < 0) + goto err_reg_dev; + + strcpy(parms->name, mhdp_dev->name); + +#if defined(CONFIG_BCM_KF_WANDEV) + mhdp_dev->priv_flags |= IFF_WANDEV; +#endif + + if (register_netdevice(mhdp_dev)) { + pr_err("MHDP: register_netdev failed\n"); + goto err_reg_dev; + } + + dev_hold(mhdp_dev); + + mhdp_tunnel_init(mhdp_dev, parms, master_dev); + + dev_put(master_dev); + + return mhdp_dev; + +err_reg_dev: + netdev_path_remove(mhdp_dev); + free_netdev(mhdp_dev); +err_alloc_dev: + return NULL; +} + +#ifdef SUPPORT_WAKE_LOCK +/** + * mhdp_write_wakelock_value - store the wakelock value in mhdp + * @dev: Device to be created + * @attr: attribute of sysfs + * @buf: output stringwait + */ +static ssize_t +mhdp_write_wakelock_value(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int retval = count; + unsigned long flags; + struct mhdp_net *mhdpn = dev_get_drvdata(dev); + long int time; + + if (kstrtol(buf, 10, &time)) { + EPRINTK("%s cannot access to wake lock time", __func__); + return -EINVAL; + } + spin_lock_irqsave(&mhdpn->wl_lock, flags); + mhdpn->wake_lock_time = (int)time; + spin_unlock_irqrestore(&mhdpn->wl_lock, flags); + + DPRINTK("%s wake_lock_time = %d\n", __func__, mhdpn->wake_lock_time); + + if ((wake_lock_active(&mhdpn->wakelock)) && + (mhdpn->wake_lock_time <= 0)) { + + wake_unlock(&mhdpn->wakelock); + + } else if ((wake_lock_active(&mhdpn->wakelock)) && + (mhdpn->wake_lock_time > 0)) { + + wake_lock_timeout(&mhdpn->wakelock, mhdpn->wake_lock_time * HZ); + } + return retval; +} + +/** + * mhdp_read_wakelock_value - read the wakelock value in mhdp + * @dev: Device to be created + * @attr: attribute of sysfs + * @buf: output stringwait + */ +static ssize_t +mhdp_read_wakelock_value(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct mhdp_net *mhdpn = dev_get_drvdata(dev); + + if (mhdpn) + return sprintf(buf, "%d\n", mhdpn->wake_lock_time); + + return sprintf(buf, "%d\n", 0); +} + +/** + * mmhdp_check_wake_lock - check the wakelock state and restart wake lock if any + * @dev: net Device pointer + */ +void mhdp_check_wake_lock(struct net_device *dev) +{ + unsigned long flags; + struct mhdp_net *mhdpn = mhdp_net_dev(dev); + + spin_lock_irqsave(&mhdpn->wl_lock, flags); + + if (mhdpn->wake_lock_time != 0) { + + spin_unlock_irqrestore(&mhdpn->wl_lock, flags); + + wake_lock_timeout(&mhdpn->wakelock, mhdpn->wake_lock_time * HZ); + } else { + spin_unlock_irqrestore(&mhdpn->wl_lock, flags); + } +} +#endif /* SUPPORT_WAKE_LOCK */ + +static void +mhdp_set_udp_filter(struct mhdp_net *mhdpn, struct mhdp_udp_filter *filter) +{ + unsigned long flags; + spin_lock_irqsave(&mhdpn->udp_lock, flags); + mhdpn->udp_filter.port_id = filter->port_id; + mhdpn->udp_filter.active = 1; + spin_unlock_irqrestore(&mhdpn->udp_lock, flags); +} + +static void mhdp_reset_udp_filter(struct mhdp_net *mhdpn) +{ + unsigned long flags; + spin_lock_irqsave(&mhdpn->udp_lock, flags); + mhdpn->udp_filter.port_id = 0; + mhdpn->udp_filter.active = 0; + spin_unlock_irqrestore(&mhdpn->udp_lock, flags); + +} + +static int mhdp_is_filtered(struct mhdp_net *mhdpn, struct sk_buff *skb) +{ + struct ipv6hdr *ipv6header; + struct iphdr *ipv4header; + struct udphdr *udphdr; + int ret = 0; + __be16 frag_off; + int offset = 0; + u8 next_hdr; + unsigned int size_of_previous_hdr; + struct sk_buff *newskb; + unsigned long flags; + + spin_lock_irqsave(&mhdpn->udp_lock, flags); + + if (mhdpn->udp_filter.active == 0) { + spin_unlock_irqrestore(&mhdpn->udp_lock, flags); + return 0; + } + spin_unlock_irqrestore(&mhdpn->udp_lock, flags); + + /*if udp, check port number */ + if (skb->protocol == htons(ETH_P_IP)) { + + ipv4header = ip_hdr(skb); + + if (ipv4header->protocol == UDP_PROT_TYPE) { + + udphdr = (struct udphdr *)((unsigned int *)ipv4header + + ipv4header->ihl); + + if (htons(udphdr->dest) == mhdpn->udp_filter.port_id) { + size_of_previous_hdr = ipv4header->ihl * + sizeof(unsigned int); + ret = 1; + DPRINTK("MHDP_FIL: IPv4 packet filtered out\n"); + } + } + + } else if (skb->protocol == htons(ETH_P_IPV6)) { + + ipv6header = ipv6_hdr(skb); + next_hdr = ipv6header->nexthdr; + + if ((next_hdr == NEXTHDR_TCP) || (next_hdr == NEXTHDR_ICMP)) + goto no_filter; + else if (next_hdr == UDP_PROT_TYPE) + goto treat_udp; + + if (!ipv6_ext_hdr(next_hdr)) { + DPRINTK("!ipv6_ext_hdr(next_hdr): %d\n", next_hdr); + goto no_filter; + } + + offset = ipv6_skip_exthdr(skb, + sizeof(struct ipv6hdr), + &next_hdr, &frag_off); + + if (offset < 0) { + DPRINTK("MHDP_FILTER offset < 0: %d\n", next_hdr); + goto no_filter; + } + +treat_udp: + if (next_hdr == UDP_PROT_TYPE) { + + udphdr = (struct udphdr *)((unsigned char *)ipv6header + + sizeof(struct ipv6hdr) + + offset); + + DPRINTK("MHDP_FILTER: UDP header found\n"); + + if (htons(udphdr->dest) == mhdpn->udp_filter.port_id) { + ret = 1; + size_of_previous_hdr = + (unsigned int)((unsigned char *)udphdr - + (unsigned char *)ipv6header); + DPRINTK("MHDP_FIL: IPv6 packet filtered out\n"); + } else { + DPRINTK("MHDP_FILTER: wrong port %d != %d\n", + htons(udphdr->dest), + mhdpn->udp_filter.port_id); + } + } + } + + if (ret == 1) { + + newskb = skb_clone(skb, GFP_ATOMIC); + + if (unlikely(!newskb)) { + ret = 0; + goto no_filter; + } + + skb_pull(newskb, (size_of_previous_hdr + sizeof(unsigned int))); + + newskb->len = (unsigned int)htons(udphdr->len) - + sizeof(unsigned int); + newskb->protocol = UDP_PROT_TYPE; + skb_set_tail_pointer(newskb, newskb->len); + + newskb->truesize = newskb->len + sizeof(struct sk_buff); + + mhi_sock_rcv_multicast(newskb, + MHI_L3_MHDP_UDP_FILTER, newskb->len); + + dev_kfree_skb(skb); + } +no_filter: + + return ret; +} + +/** + * mhdp_netdev_ioctl - I/O control on mhdp tunnel + */ +static int mhdp_netdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct net *net = dev_net(dev); + struct mhdp_net *mhdpn = mhdp_net_dev(dev); + struct mhdp_tunnel *tunnel, *pre_dev; + struct mhdp_tunnel_parm __user *u_parms; + struct mhdp_tunnel_parm k_parms; + struct mhdp_udp_filter __user *u_filter; + struct mhdp_udp_filter k_filter; + + int err = 0; + + DPRINTK("mhdp tunnel ioctl %X", cmd); + + switch (cmd) { + + case SIOCADDPDNID: + u_parms = (struct mhdp_tunnel_parm *)ifr->ifr_data; + if (copy_from_user(&k_parms, u_parms, + sizeof(struct mhdp_tunnel_parm))) { + DPRINTK("Error: Failed to copy data from user space"); + return -EFAULT; + } + + DPRINTK("pdn_id:%d sim_id:%d master_device:%s", + k_parms.pdn_id, + k_parms.sim_id, + k_parms.master); + tunnel = mhdp_locate_tunnel(mhdpn, k_parms.pdn_id); + + if (NULL == tunnel) { + if (mhdp_add_tunnel(net, &k_parms)) { + if (copy_to_user(u_parms, &k_parms, + sizeof(struct + mhdp_tunnel_parm))) + err = -EINVAL; + } else { + err = -EINVAL; + } + + } else if (1 == tunnel->free_pdn) { + + tunnel->free_pdn = 0; + + tunnel->sim_id = k_parms.sim_id; + strcpy(k_parms.name, tunnel->dev->name); + + if (copy_to_user(u_parms, &k_parms, + sizeof(struct mhdp_tunnel_parm))) + err = -EINVAL; + } else { + err = -EBUSY; + } + break; + + case SIOCDELPDNID: + u_parms = (struct mhdp_tunnel_parm *)ifr->ifr_data; + + if (copy_from_user(&k_parms, u_parms, + sizeof(struct mhdp_tunnel_parm))) { + DPRINTK("Error: Failed to copy data from user space"); + return -EFAULT; + } + + DPRINTK("pdn_id:%d sim_id:%d", k_parms.pdn_id, k_parms.sim_id); + + for (tunnel = mhdpn->tunnels, pre_dev = NULL; + tunnel; pre_dev = tunnel, tunnel = tunnel->next) { + if (tunnel->pdn_id == k_parms.pdn_id) + tunnel->free_pdn = 1; + } + break; + + case SIOCRESETMHDP: + mhdp_destroy_tunnels(mhdpn); + break; + + case SIOSETUDPFILTER: + + u_filter = (struct mhdp_udp_filter *)ifr->ifr_data; + + if (copy_from_user(&k_filter, u_filter, + sizeof(struct mhdp_udp_filter))) { + DPRINTK("Err: cannot cp filter data from user space\n"); + return -EFAULT; + } + if (k_filter.active == 1) { + DPRINTK("mhdp SIOSETUDPFILTER active on port %d\n", + k_filter.port_id); + mhdp_set_udp_filter(mhdpn, &k_filter); + } else { + DPRINTK("mhdp SIOSETUDPFILTER filter reset\n"); + mhdp_reset_udp_filter(mhdpn); + } + + break; + + default: + err = -EINVAL; + } + + return err; +} + +/** + * mhdp_netdev_change_mtu - Change mhdp tunnel MTU + */ +static int mhdp_netdev_change_mtu(struct net_device *dev, int new_mtu) +{ + if (new_mtu < MHDP_MTU_MIN || new_mtu > MHDP_MTU_MAX) + return -EINVAL; + + dev->mtu = new_mtu; + + return 0; +} + +/** + * mhdp_netdev_uninit - Un initialize mhdp tunnel + */ +static void mhdp_netdev_uninit(struct net_device *dev) +{ + dev_put(dev); +} + +/** + * mhdp_submit_queued_skb - Submit packets to master netdev (IPC) + Packets can be concatenated or not + */ +static void mhdp_submit_queued_skb(struct mhdp_tunnel *tunnel, int force_send) +{ + struct sk_buff *skb = tunnel->skb; + struct l2muxhdr *l2hdr; + struct mhdp_hdr *p_mhdp_hdr; + int i, nb_frags; + + BUG_ON(!tunnel->master_dev); + + if (skb) { + + p_mhdp_hdr = (struct mhdp_hdr *)tunnel->skb->data; + nb_frags = le32_to_cpu(p_mhdp_hdr->packet_count); + + if (hrtimer_active(&tunnel->tx_timer)) + hrtimer_cancel(&tunnel->tx_timer); + + skb->protocol = htons(ETH_P_MHDP); + skb->priority = 1; + + skb->dev = tunnel->master_dev; + + skb_reset_network_header(skb); + + skb_push(skb, L2MUX_HDR_SIZE); + skb_reset_mac_header(skb); + + l2hdr = l2mux_hdr(skb); + l2mux_set_proto(l2hdr, (tunnel->sim_id == 1) ? + MHI_L3_MHDP_UL_PS2 : MHI_L3_MHDP_UL); + l2mux_set_length(l2hdr, skb->len - L2MUX_HDR_SIZE); + + SKBPRINT(skb, "MHDP: TX"); + + tunnel->dev->stats.tx_packets++; + tunnel->dev->stats.tx_bytes += skb->len; + tunnel->skb = NULL; + + dev_queue_xmit(skb); + + for (i = 0; i < nb_frags; i++) { + if (tunnel->skb_to_free[i]) + dev_kfree_skb(tunnel->skb_to_free[i]); + else + EPRINTK("%s error no skb to free\n", __func__); + } + } +} + +/** + * mhdp_netdev_rx - Received packets from master netdev (IPC) + Packets can be concatenated or not + */ +static int mhdp_netdev_rx(struct sk_buff *skb, struct net_device *dev) +{ + skb_frag_t *frag = NULL; + struct page *page = NULL; + struct sk_buff *newskb = NULL; + struct mhdp_hdr *p_mhdp_hdr; + struct mhdp_hdr *p_mhdp_hdr_tmp = NULL; + int offset, length; + int err = 0, i, pdn_id; + int mhdp_header_len; + struct mhdp_tunnel *tunnel = NULL; +#if 0 + int start = 0; +#endif + int has_frag = skb_shinfo(skb)->nr_frags; + uint32_t packet_count; + unsigned char ip_ver; + +#ifdef SUPPORT_WAKE_LOCK + mhdp_check_wake_lock(dev); +#endif + +#if 0 + if (has_frag) { + frag = &skb_shinfo(skb)->frags[0]; + page = skb_frag_page(frag); + } + + if (skb_headlen(skb) > L2MUX_HDR_SIZE) + skb_pull(skb, L2MUX_HDR_SIZE); + else if (has_frag) + frag->page_offset += L2MUX_HDR_SIZE; +#else + skb_pull(skb, L2MUX_HDR_SIZE); +#endif + + packet_count = le32_to_cpu(*((unsigned char *)skb->data)); + + mhdp_header_len = sizeof(packet_count) + + (packet_count * sizeof(struct packet_info)); + +#if 0 + if (mhdp_header_len > skb_headlen(skb)) { + int skbheadlen = skb_headlen(skb); + + DPRINTK("mhdp header length: %d, skb_headerlen: %d", + mhdp_header_len, skbheadlen); + + p_mhdp_hdr = kmalloc(mhdp_header_len, GFP_ATOMIC); + + if (NULL == p_mhdp_hdr) + goto error; + + p_mhdp_hdr_tmp = p_mhdp_hdr; + + if ((skbheadlen == 0) && (has_frag)) { + memcpy((__u8 *) p_mhdp_hdr, page_address(page) + + frag->page_offset, mhdp_header_len); + + } else if (has_frag) { + memcpy((__u8 *) p_mhdp_hdr, skb->data, skbheadlen); + + memcpy((__u8 *) p_mhdp_hdr + skbheadlen, + page_address(page) + + frag->page_offset, mhdp_header_len - skbheadlen); + + start = mhdp_header_len - skbheadlen; + } else { + EPRINTK("not a valid mhdp frame"); + goto error; + } + + DPRINTK("page start: %d", start); + } else { + DPRINTK("skb->data has whole mhdp header"); + p_mhdp_hdr = (struct mhdp_hdr *)(((__u8 *) skb->data)); + } + + DPRINTK("MHDP PACKET COUNT : %d", le32_to_cpu(p_mhdp_hdr->packet_count)); +#else + p_mhdp_hdr = (struct mhdp_hdr *)(((__u8 *) skb->data)); +#endif + + if (le32_to_cpu(p_mhdp_hdr->packet_count) == 1) { + pdn_id = le32_to_cpu(p_mhdp_hdr->info[0].pdn_id); + offset = le32_to_cpu(p_mhdp_hdr->info[0].packet_offset); + length = le32_to_cpu(p_mhdp_hdr->info[0].packet_length); + + skb_pull(skb, mhdp_header_len + offset); + skb_trim(skb, length); + + ip_ver = (u8)*skb->data; + + skb_reset_network_header(skb); + skb->protocol = htons(ETH_IP_TYPE(ip_ver)); + skb->ip_summed = CHECKSUM_NONE; + skb->pkt_type = PACKET_HOST; + +// rcu_read_lock(); +// if (!mhdp_is_filtered(mhdp_net_dev(dev), skb)) { + //skb_tunnel_rx(skb, dev); + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; + + tunnel = mhdp_locate_tunnel(mhdp_net_dev(dev), pdn_id); + if (tunnel) { + struct net_device_stats *stats = + &tunnel->dev->stats; + stats->rx_packets++; + stats->rx_bytes += skb->len; + skb->dev = tunnel->dev; + SKBPRINT(skb, "SKB: RX"); + +#if 0 +{ + /* debug purpose, dump out the packet content */ + int i; + uint32_t *u32_ptr = (uint32_t *)skb->data; + for (i = 0; i < (skb->len >> 2); i++) { + printk("0x%08x: %08x\n", (uint32_t)(skb->data + (i << 2)), u32_ptr[i]); + } +} +#endif + netif_receive_skb(skb); +#if 0 +#ifdef MHDP_USE_NAPI + netif_receive_skb(skb); +#else + netif_rx(skb); +#endif /*#ifdef MHDP_USE_NAPI */ +#endif + } +// } +// rcu_read_unlock(); + kfree(p_mhdp_hdr_tmp); + return 0; + } + + for (i = 0; i < le32_to_cpu(p_mhdp_hdr->packet_count); i++) { + pdn_id = le32_to_cpu(p_mhdp_hdr->info[i].pdn_id); + offset = le32_to_cpu(p_mhdp_hdr->info[i].packet_offset); + length = le32_to_cpu(p_mhdp_hdr->info[i].packet_length); + + DPRINTK(" pkt_info[%d] - PDNID:%d, pkt_off: %d, pkt_len: %d\n", + i, pdn_id, packet_offset, packet_length); + + + if (skb_headlen(skb) > (mhdp_header_len + offset)) { + + newskb = skb_clone(skb, GFP_ATOMIC); + if (unlikely(!newskb)) + goto error; + + skb_pull(newskb, mhdp_header_len + offset); + + skb_trim(newskb, length); + newskb->truesize = SKB_TRUESIZE(length); + + ip_ver = (u8)*newskb->data; + + } else if (has_frag) { + + newskb = netdev_alloc_skb(dev, skb_headlen(skb)); + + if (unlikely(!newskb)) + goto error; + + get_page(page); + skb_add_rx_frag(newskb, + skb_shinfo(newskb)->nr_frags, + page, + frag->page_offset + + ((mhdp_header_len - skb_headlen(skb)) + + offset), length, length); + + ip_ver = *((unsigned char *)page_address(page) + + (frag->page_offset + + ((mhdp_header_len - skb_headlen(skb)) + + offset))); + if ((ip_ver >> 4) != VER_IPv4 && + (ip_ver >> 4) != VER_IPv6) + goto error; + } else { + DPRINTK("Error in the data received"); + goto error; + } + + skb_reset_network_header(newskb); + + /* IPv6 Support - Check the IP version */ + /* and set ETH_P_IP or ETH_P_IPv6 for received packets */ + + newskb->protocol = htons(ETH_IP_TYPE(ip_ver)); + newskb->ip_summed = CHECKSUM_NONE; + newskb->pkt_type = PACKET_HOST; + + rcu_read_lock(); + if (!mhdp_is_filtered(mhdp_net_dev(dev), newskb)) { + + skb_tunnel_rx(newskb, dev, dev_net(dev)); + + tunnel = mhdp_locate_tunnel(mhdp_net_dev(dev), pdn_id); + if (tunnel) { + struct net_device_stats *stats = + &tunnel->dev->stats; + stats->rx_packets++; + stats->rx_bytes += newskb->len; + newskb->dev = tunnel->dev; + SKBPRINT(newskb, "NEWSKB: RX"); + +#ifdef MHDP_USE_NAPI + netif_receive_skb(newskb); +#else + netif_rx(newskb); +#endif /*#ifdef MHDP_USE_NAPI */ + } + } + rcu_read_unlock(); + } + + kfree(p_mhdp_hdr_tmp); + + dev_kfree_skb(skb); + + return err; + +error: + kfree(p_mhdp_hdr_tmp); + + EPRINTK("%s - error detected\n", __func__); + + dev_kfree_skb(skb); + + if (newskb) + dev_kfree_skb(newskb); + + return err; +} + +#ifdef MHDP_USE_NAPI +/* +static int mhdp_poll(struct napi_struct *napi, int budget) +function called through napi to read current ip frame received +*/ +static int mhdp_poll(struct napi_struct *napi, int budget) +{ + struct mhdp_net *mhdpn = container_of(napi, struct mhdp_net, napi); + int err = 0; + struct sk_buff *skb; + + while (!skb_queue_empty(&mhdpn->skb_list)) { + + skb = skb_dequeue(&mhdpn->skb_list); + err = mhdp_netdev_rx(skb, mhdpn->dev); + } + + napi_complete(napi); + + return err; +} + +/*l2mux callback*/ +static int mhdp_netdev_rx_napi(struct sk_buff *skb, struct net_device *dev) +{ + struct mhdp_net *mhdpn = mhdp_net_dev(dev); + + if (mhdpn) { + + mhdpn->dev = dev; + skb_queue_tail(&mhdpn->skb_list, skb); + + napi_schedule(&mhdpn->napi); + + } else { + EPRINTK("mhdp_netdev_rx_napi-MHDP driver init not correct\n"); + } + + return 0; +} + +#endif /*MHDP_USE_NAPI */ + +/** + * tx_timer_timeout - Timer expiration function for TX packet concatenation + => will then call mhdp_submit_queued_skb to pass concatenated packets to IPC + */ +static enum hrtimer_restart tx_timer_timeout(struct hrtimer *timer) +{ + struct mhdp_tunnel *tunnel = container_of(timer, + struct mhdp_tunnel, + tx_timer); + + tasklet_hi_schedule(&tunnel->taskl); + + return HRTIMER_NORESTART; +} + +static void tx_timer_timeout_tasklet(unsigned long arg) +{ + struct mhdp_tunnel *tunnel = (struct mhdp_tunnel *)arg; + + spin_lock_bh(&tunnel->timer_lock); + + mhdp_submit_queued_skb(tunnel, 1); + + spin_unlock_bh(&tunnel->timer_lock); +} + +static int mhdp_netdev_xmit_single(struct sk_buff *skb, struct net_device *dev) +{ + struct mhdp_hdr *p_mhdphdr; + struct mhdp_tunnel *tunnel = netdev_priv(dev); + uint32_t pkt_len = skb->len; + + skb_push(skb, sizeof(uint32_t) + sizeof(struct packet_info)); + memset(skb->data, 0, sizeof(uint32_t) + sizeof(struct packet_info)); + p_mhdphdr = (struct mhdp_hdr *)skb->data; + p_mhdphdr->packet_count = cpu_to_le32(1); + p_mhdphdr->info[0].pdn_id = cpu_to_le32(tunnel->pdn_id); + p_mhdphdr->info[0].packet_length = cpu_to_le32(pkt_len); + spin_lock_bh(&tunnel->timer_lock); + tunnel->skb = skb; + + mhdp_submit_queued_skb(tunnel, 1); + spin_unlock_bh(&tunnel->timer_lock); + return NETDEV_TX_OK; +} + +/* mhdp_netdev_xmit_chain + * if TX packet doezn't fit in max MHDP frame length, send previous + * MHDP frame asap else concatenate TX packet. + * If nb concatenated packets reach max MHDP packets, send current + * MHDP frame asap else start TX timer (if no further packets + * to be transmitted, MHDP frame will be send on timer expiry) */ +static int mhdp_netdev_xmit_chain(struct sk_buff *skb, struct net_device *dev) +{ + struct mhdp_hdr *p_mhdp_hdr; + struct mhdp_tunnel *tunnel = netdev_priv(dev); + struct net_device_stats *stats = &tunnel->dev->stats; + struct page *page = NULL; + int i; + int packet_count, offset, len; + +#ifdef SUPPORT_WAKE_LOCK + mhdp_check_wake_lock(dev); +#endif + + spin_lock_bh(&tunnel->timer_lock); + + SKBPRINT(skb, "SKB: TX"); + +#if 0 + { + int i; + int len = skb->len; + u8 *ptr = skb->data; + + for (i = 0; i < len; i++) { + if (i % 8 == 0) + pr_debug("MHDP mhdp_netdev_xmit:TX [%04X] ", i); + pr_debug(" 0x%02X", ptr[i]); + if (i % 8 == 7 || i == len - 1) + pr_debug("\n"); + } + } +#endif +xmit_again: + + if (tunnel->skb == NULL) { + + tunnel->skb = netdev_alloc_skb(dev, + L2MUX_HDR_SIZE + + sizeof(struct mhdp_hdr)); + + if (!tunnel->skb) { + EPRINTK("mhdp_netdev_xmit error1"); + goto tx_error; + } + + /* Place holder for the mhdp packet count */ + len = skb_headroom(tunnel->skb) - L2MUX_HDR_SIZE; + + skb_push(tunnel->skb, len); + len -= 4; + + memset(tunnel->skb->data, 0, len); + + /* + * Need to replace following logic, with something better like + * __pskb_pull_tail or pskb_may_pull(tunnel->skb, len); + */ + { + tunnel->skb->tail -= len; + tunnel->skb->len -= len; + } + + p_mhdp_hdr = (struct mhdp_hdr *)tunnel->skb->data; + p_mhdp_hdr->packet_count = 0; + + hrtimer_start(&tunnel->tx_timer, + ktime_set(0, NSEC_PER_SEC / 600), + HRTIMER_MODE_REL); + } + + /* This new frame is to big for the current mhdp frame, */ + /* send the frame first */ + if (tunnel->skb->len + skb->len >= MAX_MHDP_FRAME_SIZE) { + + mhdp_submit_queued_skb(tunnel, 1); + + goto xmit_again; + + } else { + + /* + * skb_put cannot be called as the (data_len != 0) + */ + + tunnel->skb->tail += sizeof(struct packet_info); + tunnel->skb->len += sizeof(struct packet_info); + + DPRINTK("new - skb->tail:%lu skb->end:%lu skb->data_len:%lu", + (unsigned long)tunnel->skb->tail, + (unsigned long)tunnel->skb->end, + (unsigned long)tunnel->skb->data_len); + + p_mhdp_hdr = (struct mhdp_hdr *)tunnel->skb->data; + + tunnel->skb_to_free[le32_to_cpu(p_mhdp_hdr->packet_count)] = skb; + + packet_count = le32_to_cpu(p_mhdp_hdr->packet_count); + p_mhdp_hdr->info[packet_count].pdn_id = cpu_to_le32(tunnel->pdn_id); + if (packet_count == 0) { + p_mhdp_hdr->info[packet_count].packet_offset = 0; + } else { + p_mhdp_hdr->info[packet_count].packet_offset = + cpu_to_le32( + le32_to_cpu(p_mhdp_hdr->info[packet_count - 1].packet_offset) + + le32_to_cpu(p_mhdp_hdr->info[packet_count - 1].packet_length)); + } + + p_mhdp_hdr->info[packet_count].packet_length = cpu_to_le32(skb->len); + p_mhdp_hdr->packet_count = cpu_to_le32(le32_to_cpu(p_mhdp_hdr->packet_count) + 1); + + page = virt_to_page(skb->data); + + get_page(page); + + offset = ((unsigned long)skb->data - + (unsigned long)page_address(page)); + + skb_add_rx_frag(tunnel->skb, skb_shinfo(tunnel->skb)->nr_frags, + page, offset, skb_headlen(skb), + skb_headlen(skb)); + + if (skb_shinfo(skb)->nr_frags) { + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + + skb_frag_t *frag = + &skb_shinfo(tunnel->skb)->frags[i]; + + get_page(skb_frag_page(frag)); + + skb_add_rx_frag(tunnel->skb, + skb_shinfo(tunnel->skb)-> + nr_frags, skb_frag_page(frag), + frag->page_offset, frag->size, + frag->size); + } + } + + if (le32_to_cpu(p_mhdp_hdr->packet_count) >= MAX_MHDPHDR_SIZE) + mhdp_submit_queued_skb(tunnel, 1); + } + + spin_unlock_bh(&tunnel->timer_lock); + return NETDEV_TX_OK; + +tx_error: + spin_unlock_bh(&tunnel->timer_lock); + stats->tx_errors++; + dev_kfree_skb(skb); + return NETDEV_TX_OK; +} + +/* mhdp_netdev_xmit - Hard xmit for MHDP tunnel net device. + * If master device supports MHDP chain, it will use mhdp_netdev_xmit_chain. + * otherwise, it will use mhdp_net_xmit_single */ + +/* Also defined in bcm_lte_pcie.h, for net_dev->select_queue => skb->queue_mapping */ +#define BCM_LTE_TX_DATAQ 0 +#define BCM_LTE_TX_CTRLQ 1 +#define BCM_LTE_TX_INVALID 0xffff + +static int mhdp_netdev_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct mhdp_tunnel *tunnel = netdev_priv(dev); + + BUG_ON(!tunnel->master_dev); + +#ifdef CONFIG_BLOG + if (skb_get_queue_mapping(skb) == BCM_LTE_TX_DATAQ) { + struct sk_buff *orig_skb = skb; + skb = nbuff_xlate((pNBuff_t )skb); + if (skb == NULL) { + nbuff_free((pNBuff_t) orig_skb); + printk("drop packet as nbuff_xlate fail, dev_name: %s\n", + dev->name); + return 0; + } + + blog_emit(skb, dev, TYPE_IP, 0, BLOG_LTEPHY); + } +#endif /* CONFIG_BLOG */ + + if (tunnel->master_dev->features & NETIF_F_SG) + return mhdp_netdev_xmit_chain(skb, dev); + else + return mhdp_netdev_xmit_single(skb, dev); +} + +struct net_device *mhdp_get_netdev_by_pdn_id(struct net_device *dev, int pdn_id) +{ + struct mhdp_tunnel *tunnel; + tunnel = mhdp_locate_tunnel(mhdp_net_dev(dev), pdn_id); + if (tunnel == NULL) + return NULL; + else + return tunnel->dev; +} +EXPORT_SYMBOL(mhdp_get_netdev_by_pdn_id); + +/** + * mhdp_netdev_event - Catch MHDP tunnel net dev states + */ +static int +mhdp_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) +{ + struct net_device *event_dev = (struct net_device *)ptr; + + DPRINTK("event_dev: %s, event: %lx\n", + event_dev ? event_dev->name : "None", event); + + switch (event) { + case NETDEV_UNREGISTER: + { + struct mhdp_net *mhdpn = mhdp_net_dev(event_dev); + struct mhdp_tunnel *iter, *prev; + + DPRINTK("event_dev: %s, event: %lx\n", + event_dev ? event_dev->name : "None", event); + + for (iter = mhdpn->tunnels, prev = NULL; + iter; prev = iter, iter = iter->next) { + if (event_dev == iter->master_dev) { + if (!prev) + mhdpn->tunnels = + mhdpn->tunnels->next; + else + prev->next = iter->next; + mhdp_tunnel_destroy(iter->dev); + } + } + } + break; + } + + return NOTIFY_DONE; +} + +#ifdef MHDP_BONDING_SUPPORT + +static void cdma_netdev_uninit(struct net_device *dev) +{ + dev_put(dev); +} + +static void mhdp_ethtool_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *drvinfo) +{ + strncpy(drvinfo->driver, dev->name, 32); +} + +static const struct ethtool_ops mhdp_ethtool_ops = { + .get_drvinfo = mhdp_ethtool_get_drvinfo, + .get_link = ethtool_op_get_link, +}; +#endif /*MHDP_BONDING_SUPPORT */ + +static const struct net_device_ops mhdp_netdev_ops = { + .ndo_uninit = mhdp_netdev_uninit, + .ndo_start_xmit = mhdp_netdev_xmit, + .ndo_do_ioctl = mhdp_netdev_ioctl, + .ndo_change_mtu = mhdp_netdev_change_mtu, +}; + +/** + * mhdp_netdev_setup - Setup MHDP tunnel + */ +static void mhdp_netdev_setup(struct net_device *dev) +{ + dev->netdev_ops = &mhdp_netdev_ops; +#ifdef MHDP_BONDING_SUPPORT + dev->ethtool_ops = &mhdp_ethtool_ops; +#endif /*MHDP_BONDING_SUPPORT */ + + dev->destructor = free_netdev; + +#ifdef MHDP_BONDING_SUPPORT + ether_setup(dev); + dev->flags |= IFF_NOARP; + dev->iflink = 0; + dev->features |= (ETIF_F_NETNS_LOCAL | NETIF_F_SG); +#else + dev->type = ARPHRD_TUNNEL; + dev->hard_header_len = L2MUX_HDR_SIZE + sizeof(struct mhdp_hdr); + dev->mtu = ETH_DATA_LEN; + dev->flags = IFF_NOARP; + dev->group = 0; + dev->addr_len = 4; + dev->features |= (NETIF_F_NETNS_LOCAL); /* temporary removing NETIF_F_SG + * support due to problem with + * skb gets freed before being + * transmitted */ +#endif /* MHDP_BONDING_SUPPORT */ + +} + +/** + * mhdp_init_net - Initalize MHDP net structure + */ +static int __net_init mhdp_init_net(struct net *net) +{ + struct mhdp_net *mhdpn = net_generic(net, mhdp_net_id); + int err; + + mhdpn->tunnels = NULL; + + mhdpn->ctl_dev = alloc_netdev(sizeof(struct mhdp_tunnel), + MHDP_CTL_IFNAME, NET_NAME_UNKNOWN, + mhdp_netdev_setup); + if (!mhdpn->ctl_dev) + return -ENOMEM; + + dev_net_set(mhdpn->ctl_dev, net); + dev_hold(mhdpn->ctl_dev); + + err = register_netdev(mhdpn->ctl_dev); + if (err) { + pr_err(MHDP_CTL_IFNAME " register failed"); + free_netdev(mhdpn->ctl_dev); + return err; + } + spin_lock_init(&mhdpn->udp_lock); + + mhdp_reset_udp_filter(mhdpn); +#ifdef MHDP_USE_NAPI + + netif_napi_add(mhdpn->ctl_dev, &mhdpn->napi, mhdp_poll, NAPI_WEIGHT); + napi_enable(&mhdpn->napi); + skb_queue_head_init(&mhdpn->skb_list); + +#endif /*#ifdef MHDP_USE_NAPI */ + + dev_set_drvdata(&mhdpn->ctl_dev->dev, mhdpn); +#ifdef SUPPORT_WAKE_LOCK + err = device_create_file(&mhdpn->ctl_dev->dev, &mhdpwl_dev_attrs[0]); + + if (err) + pr_err("MHDP cannot create wakelock file"); + + mhdpn->wake_lock_time = 0; + + wake_lock_init(&mhdpn->wakelock, WAKE_LOCK_SUSPEND, "mhdp_wake_lock"); +#endif + + return 0; +} + +/** + * mhdp_exit_net - destroy MHDP net structure + */ +static void __net_exit mhdp_exit_net(struct net *net) +{ + struct mhdp_net *mhdpn = net_generic(net, mhdp_net_id); + + rtnl_lock(); + mhdp_destroy_tunnels(mhdpn); + unregister_netdevice(mhdpn->ctl_dev); +#ifdef SUPPORT_WAKE_LOCK + device_remove_file(&mhdpn->ctl_dev->dev, &mhdpwl_dev_attrs[0]); + wake_lock_destroy(&mhdpn->wakelock); +#endif + + rtnl_unlock(); +} + +static struct pernet_operations mhdp_net_ops = { + .init = mhdp_init_net, + .exit = mhdp_exit_net, + .id = &mhdp_net_id, + .size = sizeof(struct mhdp_net), +}; + +/** + * mhdp_init - Initalize MHDP + */ +static int __init mhdp_init(void) +{ + int err; + +#ifdef MHDP_USE_NAPI + err = l2mux_netif_rx_register(MHI_L3_MHDP_DL, mhdp_netdev_rx_napi); + err = l2mux_netif_rx_register(MHI_L3_MHDP_DL_PS2, mhdp_netdev_rx_napi); +#else + err = l2mux_netif_rx_register(MHI_L3_MHDP_DL, mhdp_netdev_rx); + err = l2mux_netif_rx_register(MHI_L3_MHDP_DL_PS2, mhdp_netdev_rx); + +#endif /*MHDP_USE_NAPI */ + if (err) + goto rollback0; + + err = register_pernet_device(&mhdp_net_ops); + if (err < 0) + goto rollback1; + + err = register_netdevice_notifier(&mhdp_netdev_notifier); + if (err < 0) + goto rollback2; + + return 0; + +rollback2: + unregister_pernet_device(&mhdp_net_ops); +rollback1: + l2mux_netif_rx_unregister(MHI_L3_MHDP_DL_PS2); + l2mux_netif_rx_unregister(MHI_L3_MHDP_DL); +rollback0: + return err; +} + +static void __exit mhdp_exit(void) +{ + l2mux_netif_rx_unregister(MHI_L3_MHDP_DL_PS2); + l2mux_netif_rx_unregister(MHI_L3_MHDP_DL); + unregister_netdevice_notifier(&mhdp_netdev_notifier); + unregister_pernet_device(&mhdp_net_ops); +} + +module_init(mhdp_init); +module_exit(mhdp_exit); + +MODULE_DESCRIPTION("Modem Host Data Protocol for MHI"); +#endif /* CONFIG_BCM_KF_MHI */ diff -urN linux-4.1.52-orig/net/mhi/l3mhi.c linux-4.1.52/net/mhi/l3mhi.c --- linux-4.1.52-orig/net/mhi/l3mhi.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/mhi/l3mhi.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,140 @@ +#ifdef CONFIG_BCM_KF_MHI +/* +<:copyright-BRCM:2011:DUAL/GPL:standard + + Copyright (c) 2011 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +/* + * File: l3mhi.c + * + * L2 channels to AF_MHI binding. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define MAX_CHANNELS 256 + +#ifdef CONFIG_MHI_DEBUG +# define DPRINTK(...) pr_debug("L3MHI: " __VA_ARGS__) +#else +# define DPRINTK(...) +#endif + +/* Module parameters - with defaults */ +static int l2chs[] = { + MHI_L3_FILE, + MHI_L3_XFILE, + MHI_L3_SECURITY, + MHI_L3_TEST, + MHI_L3_TEST_PRIO, + MHI_L3_LOG, + MHI_L3_IMS, + MHI_L3_OEM_CP, + MHI_L3_THERMAL, + MHI_L3_MHDP_UDP_FILTER, + MHI_L3_HIGH_PRIO_TEST, + MHI_L3_MED_PRIO_TEST, + MHI_L3_LOW_PRIO_TEST, +}; + +static int l2cnt = sizeof(l2chs) / sizeof(int); + +/* Functions */ + +static int mhi_netif_rx(struct sk_buff *skb, struct net_device *dev) +{ + skb->protocol = htons(ETH_P_MHI); + + return netif_rx(skb); +} + +/* Module registration */ + +int __init l3mhi_init(void) +{ + int ch, i; + int err; + + pr_info("MHI: %d Channels\n", l2cnt); + for (i = 0; i < l2cnt; i++) { + ch = l2chs[i]; + if (ch >= 0 && ch < MHI_L3_NPROTO) { + err = l2mux_netif_rx_register(ch, mhi_netif_rx); + if (err) + goto error; + + err = mhi_register_protocol(ch); + if (err) + goto error; + } + } + + return 0; + +error: + for (i = 0; i < l2cnt; i++) { + ch = l2chs[i]; + if (ch >= 0 && ch < MHI_L3_NPROTO) { + if (mhi_protocol_registered(ch)) { + l2mux_netif_rx_unregister(ch); + mhi_unregister_protocol(ch); + } + } + } + + return err; +} + +void __exit l3mhi_exit(void) +{ + int ch, i; + + for (i = 0; i < l2cnt; i++) { + ch = l2chs[i]; + if (ch >= 0 && ch < MHI_L3_NPROTO) { + if (mhi_protocol_registered(ch)) { + l2mux_netif_rx_unregister(ch); + mhi_unregister_protocol(ch); + } + } + } +} + +module_init(l3mhi_init); +module_exit(l3mhi_exit); + +module_param_array_named(l2_channels, l2chs, int, &l2cnt, 0444); + +MODULE_DESCRIPTION("L3 MHI Binding"); +#endif /* CONFIG_BCM_KF_MHI */ diff -urN linux-4.1.52-orig/net/mhi/l3phonet.c linux-4.1.52/net/mhi/l3phonet.c --- linux-4.1.52-orig/net/mhi/l3phonet.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/mhi/l3phonet.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,121 @@ +#ifdef CONFIG_BCM_KF_MHI +/* +<:copyright-BRCM:2011:DUAL/GPL:standard + + Copyright (c) 2011 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +/* + * File: l3phonet.c + * + * L2 PHONET channel to AF_PHONET binding. + */ + +#include +#include +#include +#include +#include + +/* Functions */ + +static int mhi_pn_netif_rx(struct sk_buff *skb, struct net_device *dev) +{ + /* Set Protocol Family */ + skb->protocol = htons(ETH_P_PHONET); + + /* Remove L2MUX header and Phonet media byte */ + skb_pull(skb, L2MUX_HDR_SIZE + 1); + + /* Pass upwards to the Procotol Family */ + return netif_rx(skb); +} + +static int mhi_pn_netif_tx(struct sk_buff *skb, struct net_device *dev) +{ + struct l2muxhdr *l2hdr; + int l3len; + u8 *ptr; + + /* Add media byte */ + ptr = skb_push(skb, 1); + + /* Set media byte */ + ptr[0] = dev->dev_addr[0]; + + /* L3 length */ + l3len = skb->len; + + /* Add L2MUX header */ + skb_push(skb, L2MUX_HDR_SIZE); + + /* Mac header starts here */ + skb_reset_mac_header(skb); + + /* L2MUX header pointer */ + l2hdr = l2mux_hdr(skb); + + /* L3 Proto ID */ + l2mux_set_proto(l2hdr, MHI_L3_PHONET); + + /* L3 payload length */ + l2mux_set_length(l2hdr, l3len); + + return 0; +} + +/* Module registration */ + +int __init mhi_pn_init(void) +{ + int err; + + err = l2mux_netif_rx_register(MHI_L3_PHONET, mhi_pn_netif_rx); + if (err) + goto err1; + + err = l2mux_netif_tx_register(ETH_P_PHONET, mhi_pn_netif_tx); + if (err) + goto err2; + + return 0; + +err2: + l2mux_netif_rx_unregister(MHI_L3_PHONET); +err1: + return err; +} + +void __exit mhi_pn_exit(void) +{ + l2mux_netif_rx_unregister(MHI_L3_PHONET); + l2mux_netif_tx_unregister(ETH_P_PHONET); +} + +module_init(mhi_pn_init); +module_exit(mhi_pn_exit); + +MODULE_DESCRIPTION("MHI Phonet protocol family bridge"); +#endif /* CONFIG_BCM_KF_MHI */ diff -urN linux-4.1.52-orig/net/mhi/Makefile linux-4.1.52/net/mhi/Makefile --- linux-4.1.52-orig/net/mhi/Makefile 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/mhi/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,15 @@ +ifdef BCM_KF # defined(CONFIG_BCM_KF_BLOG) +EXTRA_CFLAGS += -I$(INC_BRCMDRIVER_PUB_PATH)/$(BRCM_BOARD) +EXTRA_CFLAGS += -I$(INC_BRCMSHARED_PUB_PATH)/bcm963xx +endif # BCM_KF + +obj-$(CONFIG_MHI_L3MHI) += af_mhi.o + +af_mhi-objs := mhi_proto.o mhi_socket.o mhi_dgram.o mhi_raw.o + +obj-$(CONFIG_MHI_L2MUX) += l2mux.o +obj-$(CONFIG_MHI_L3MHI) += l3mhi.o +obj-$(CONFIG_MHI_L3MHDP) += l3mhdp.o +obj-$(CONFIG_MHI_L3PHONET) += l3phonet.o +subdir-ccflags-y += -Werror + diff -urN linux-4.1.52-orig/net/mhi/mhi_dgram.c linux-4.1.52/net/mhi/mhi_dgram.c --- linux-4.1.52-orig/net/mhi/mhi_dgram.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/mhi/mhi_dgram.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,315 @@ +#ifdef CONFIG_BCM_KF_MHI +/* +<:copyright-BRCM:2011:DUAL/GPL:standard + + Copyright (c) 2011 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +/* + * File: mhi_dgram.c + * + * DGRAM socket implementation for MHI protocol family. + * + * It uses the MHI socket framework in mhi_socket.c + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#ifdef CONFIG_MHI_DEBUG +# define DPRINTK(...) pr_debug("MHI/DGRAM: " __VA_ARGS__) +#else +# define DPRINTK(...) +#endif + +/*** Prototypes ***/ + +static struct proto mhi_dgram_proto; + +static void mhi_dgram_destruct(struct sock *sk); + +/*** Functions ***/ + +int mhi_dgram_sock_create(struct net *net, + struct socket *sock, int proto, int kern) +{ + struct sock *sk; + struct mhi_sock *msk; + + DPRINTK("mhi_dgram_sock_create: proto:%d type:%d\n", proto, sock->type); + + if (sock->type != SOCK_DGRAM) + return -EPROTONOSUPPORT; + + if (proto == MHI_L3_ANY) + return -EPROTONOSUPPORT; + + sk = sk_alloc(net, PF_MHI, GFP_KERNEL, &mhi_dgram_proto); + if (!sk) + return -ENOMEM; + + sock_init_data(sock, sk); + + sock->ops = &mhi_socket_ops; + sock->state = SS_UNCONNECTED; + + sk->sk_protocol = proto; + sk->sk_destruct = mhi_dgram_destruct; + sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv; + + sk->sk_prot->init(sk); + + msk = mhi_sk(sk); + + msk->sk_l3proto = proto; + msk->sk_ifindex = -1; + + return 0; +} + +static int mhi_dgram_init(struct sock *sk) +{ + return 0; +} + +static void mhi_dgram_destruct(struct sock *sk) +{ + skb_queue_purge(&sk->sk_receive_queue); +} + +static void mhi_dgram_close(struct sock *sk, long timeout) +{ + sk_common_release(sk); +} + +static int mhi_dgram_ioctl(struct sock *sk, int cmd, unsigned long arg) +{ + int err; + + DPRINTK("mhi_dgram_ioctl: cmd:%d arg:%lu\n", cmd, arg); + + switch (cmd) { + case SIOCOUTQ: + { + int len; + len = sk_wmem_alloc_get(sk); + err = put_user(len, (int __user *)arg); + } + break; + + case SIOCINQ: + { + struct sk_buff *skb; + int len; + + lock_sock(sk); + { + skb = skb_peek(&sk->sk_receive_queue); + len = skb ? skb->len : 0; + } + release_sock(sk); + + err = put_user(len, (int __user *)arg); + } + break; + + default: + err = -ENOIOCTLCMD; + } + + return err; +} + +static int mhi_dgram_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) +{ + struct mhi_sock *msk = mhi_sk(sk); + struct net_device *dev = NULL; + struct l2muxhdr *l2hdr; + struct sk_buff *skb; + + int err = -EFAULT; + + if (msg->msg_flags & + ~(MSG_DONTWAIT | MSG_EOR | MSG_NOSIGNAL | MSG_CMSG_COMPAT)) { + pr_warn("mhi_dgram_sendmsg: incompatible socket msg_flags: 0x%08X\n", + msg->msg_flags); + err = -EOPNOTSUPP; + goto out; + } + + skb = sock_alloc_send_skb(sk, len + L2MUX_HDR_SIZE, + (msg->msg_flags & MSG_DONTWAIT), &err); + if (!skb) { + pr_err("mhi_dgram_sendmsg: sock_alloc_send_skb failed: %d\n", + err); + goto out; + } + + skb_reserve(skb, L2MUX_HDR_SIZE); + skb_reset_transport_header(skb); + + err = memcpy_from_msg((void *)skb_put(skb, len), msg, len); + if (err < 0) { + pr_err("mhi_dgram_sendmsg: memcpy_from_msg failed: %d\n", + err); + goto drop; + } + + if (msk->sk_ifindex) + dev = dev_get_by_index(sock_net(sk), msk->sk_ifindex); + + if (!dev) { + pr_err("mhi_dgram_sendmsg: no device for ifindex:%d\n", + msk->sk_ifindex); + goto drop; + } + + if (!(dev->flags & IFF_UP)) { + pr_err("mhi_dgram_sendmsg: device %d not IFF_UP\n", + msk->sk_ifindex); + err = -ENETDOWN; + goto drop; + } + + if (len + L2MUX_HDR_SIZE > dev->mtu) { + err = -EMSGSIZE; + goto drop; + } + + skb_reset_network_header(skb); + + skb_push(skb, L2MUX_HDR_SIZE); + skb_reset_mac_header(skb); + + l2hdr = l2mux_hdr(skb); + l2mux_set_proto(l2hdr, sk->sk_protocol); + l2mux_set_length(l2hdr, len); + + err = mhi_skb_send(skb, dev, sk->sk_protocol); + + goto put; + +drop: + kfree(skb); +put: + if (dev) + dev_put(dev); +out: + return err; +} + +static int mhi_dgram_recvmsg(struct sock *sk, + struct msghdr *msg, + size_t len, int noblock, int flags, int *addr_len) +{ + struct sk_buff *skb = NULL; + int cnt, err; + + err = -EOPNOTSUPP; + + if (flags & + ~(MSG_PEEK | MSG_TRUNC | MSG_DONTWAIT | + MSG_NOSIGNAL | MSG_CMSG_COMPAT)) { + pr_warn("mhi_dgram_recvmsg: incompatible socket flags: 0x%08X", + flags); + goto out2; + } + + if (addr_len) + addr_len[0] = 0; + + skb = skb_recv_datagram(sk, flags, noblock, &err); + if (!skb) + goto out2; + + cnt = skb->len - L2MUX_HDR_SIZE; + if (len < cnt) { + msg->msg_flags |= MSG_TRUNC; + cnt = len; + } + + err = skb_copy_datagram_msg(skb, L2MUX_HDR_SIZE, msg, cnt); + if (err) + goto out; + + if (flags & MSG_TRUNC) + err = skb->len - L2MUX_HDR_SIZE; + else + err = cnt; + +out: + skb_free_datagram(sk, skb); +out2: + return err; +} + +static int mhi_dgram_backlog_rcv(struct sock *sk, struct sk_buff *skb) +{ + if (sock_queue_rcv_skb(sk, skb) < 0) { + kfree_skb(skb); + return NET_RX_DROP; + } + + return NET_RX_SUCCESS; +} + +static struct proto mhi_dgram_proto = { + .name = "MHI-DGRAM", + .owner = THIS_MODULE, + .close = mhi_dgram_close, + .ioctl = mhi_dgram_ioctl, + .init = mhi_dgram_init, + .sendmsg = mhi_dgram_sendmsg, + .recvmsg = mhi_dgram_recvmsg, + .backlog_rcv = mhi_dgram_backlog_rcv, + .hash = mhi_sock_hash, + .unhash = mhi_sock_unhash, + .obj_size = sizeof(struct mhi_sock), +}; + +int mhi_dgram_proto_init(void) +{ + DPRINTK("mhi_dgram_proto_init\n"); + + return proto_register(&mhi_dgram_proto, 1); +} + +void mhi_dgram_proto_exit(void) +{ + DPRINTK("mhi_dgram_proto_exit\n"); + + proto_unregister(&mhi_dgram_proto); +} +#endif /* CONFIG_BCM_KF_MHI */ diff -urN linux-4.1.52-orig/net/mhi/mhi_proto.c linux-4.1.52/net/mhi/mhi_proto.c --- linux-4.1.52-orig/net/mhi/mhi_proto.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/mhi/mhi_proto.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,206 @@ +#ifdef CONFIG_BCM_KF_MHI +/* +<:copyright-BRCM:2011:DUAL/GPL:standard + + Copyright (c) 2011 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +/* + * File: mhi_proto.c + * + * Modem-Host Interface (MHI) Protocol Family + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef CONFIG_MHI_DEBUG +# define DPRINTK(...) pr_debug("AF_MHI: " __VA_ARGS__) +#else +# define DPRINTK(...) +#endif + +/* Supported L2 protocols */ +static __u8 mhi_protocols[MHI_L3_NPROTO] __read_mostly = { 0, }; + +/*** Functions ***/ + +int mhi_protocol_registered(int protocol) +{ + if (protocol >= 0 && protocol < MHI_L3_NPROTO) + return mhi_protocols[protocol]; + if (protocol == MHI_L3_ANY) + return 1; + + return 0; +} +EXPORT_SYMBOL(mhi_protocol_registered); + +int mhi_register_protocol(int protocol) +{ + DPRINTK("mhi_register_protocol: %d\n", protocol); + + if (protocol < 0 || protocol >= MHI_L3_NPROTO) + return -EINVAL; + + mhi_protocols[protocol] = 1; + + return 0; +} +EXPORT_SYMBOL(mhi_register_protocol); + +int mhi_unregister_protocol(int protocol) +{ + DPRINTK("mhi_unregister_protocol: %d\n", protocol); + + if (protocol < 0 || protocol >= MHI_L3_NPROTO) + return -EINVAL; + + mhi_protocols[protocol] = 0; + + return 0; +} +EXPORT_SYMBOL(mhi_unregister_protocol); + +int mhi_skb_send(struct sk_buff *skb, struct net_device *dev, u8 proto) +{ + int err = 0; + + DPRINTK("mhi_skb_send: proto:%d skb_len:%d\n", proto, skb->len); + + skb->protocol = htons(ETH_P_MHI); + skb->dev = dev; + + if (skb->pkt_type == PACKET_LOOPBACK) { + skb_orphan(skb); + netif_rx_ni(skb); + } else { + + if ((proto == MHI_L3_XFILE) || (proto == MHI_L3_LOW_PRIO_TEST)) + skb->priority = 1; /* Low prio */ + else if ((proto == MHI_L3_AUDIO) + || (proto == MHI_L3_TEST_PRIO) + || (proto == MHI_L3_HIGH_PRIO_TEST)) + skb->priority = 6; /* high prio */ + else + skb->priority = 0; /* medium prio */ + err = dev_queue_xmit(skb); + } + + return err; +} + +int +mhi_skb_recv(struct sk_buff *skb, + struct net_device *dev, + struct packet_type *type, struct net_device *orig_dev) +{ + struct l2muxhdr *l2hdr; + + u8 l3pid; + u32 l3len; + int err; + + l2hdr = l2mux_hdr(skb); + + l3pid = l2mux_get_proto(l2hdr); + l3len = l2mux_get_length(l2hdr); + + DPRINTK("mhi_skb_recv: skb_len:%d l3pid:%d l3len:%d\n", + skb->len, l3pid, l3len); + + err = mhi_sock_rcv_multicast(skb, l3pid, l3len); + + return err; +} + +static struct packet_type mhi_packet_type __read_mostly = { + .type = cpu_to_be16(ETH_P_MHI), + .func = mhi_skb_recv, +}; + +static int __init mhi_proto_init(void) +{ + int err; + + DPRINTK("mhi_proto_init\n"); + + err = mhi_sock_init(); + if (err) { + pr_alert("MHI socket layer registration failed\n"); + goto err0; + } + + err = mhi_dgram_proto_init(); + if (err) { + pr_alert("MHI DGRAM protocol layer registration failed\n"); + goto err1; + } + + err = mhi_raw_proto_init(); + if (err) { + pr_alert("MHI RAW protocol layer registration failed\n"); + goto err2; + } + + dev_add_pack(&mhi_packet_type); + + return 0; + +err2: + mhi_dgram_proto_exit(); +err1: + mhi_sock_exit(); +err0: + return err; +} + +static void __exit mhi_proto_exit(void) +{ + DPRINTK("mhi_proto_exit\n"); + + dev_remove_pack(&mhi_packet_type); + + mhi_dgram_proto_exit(); + mhi_raw_proto_exit(); + mhi_sock_exit(); +} + +module_init(mhi_proto_init); +module_exit(mhi_proto_exit); + +MODULE_ALIAS_NETPROTO(PF_MHI); + +MODULE_DESCRIPTION("MHI Protocol Family for Linux"); +#endif /* CONFIG_BCM_KF_MHI */ diff -urN linux-4.1.52-orig/net/mhi/mhi_raw.c linux-4.1.52/net/mhi/mhi_raw.c --- linux-4.1.52-orig/net/mhi/mhi_raw.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/mhi/mhi_raw.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,304 @@ +#ifdef CONFIG_BCM_KF_MHI +/* +<:copyright-BRCM:2011:DUAL/GPL:standard + + Copyright (c) 2011 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +/* + * File: mhi_raw.c + * + * RAW socket implementation for MHI protocol family. + * + * It uses the MHI socket framework in mhi_socket.c + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#ifdef CONFIG_MHI_DEBUG +# define DPRINTK(...) pr_debug("MHI/RAW: " __VA_ARGS__) +#else +# define DPRINTK(...) +#endif + +/*** Prototypes ***/ + +static struct proto mhi_raw_proto; + +static void mhi_raw_destruct(struct sock *sk); + +/*** Functions ***/ + +int mhi_raw_sock_create(struct net *net, + struct socket *sock, int proto, int kern) +{ + struct sock *sk; + struct mhi_sock *msk; + + DPRINTK("mhi_raw_sock_create: proto:%d type:%d\n", proto, sock->type); + + if (sock->type != SOCK_RAW) + return -EPROTONOSUPPORT; + + sk = sk_alloc(net, PF_MHI, GFP_KERNEL, &mhi_raw_proto); + if (!sk) + return -ENOMEM; + + sock_init_data(sock, sk); + + sock->ops = &mhi_socket_ops; + sock->state = SS_UNCONNECTED; + + if (proto != MHI_L3_ANY) + sk->sk_protocol = proto; + else + sk->sk_protocol = 0; + + sk->sk_destruct = mhi_raw_destruct; + sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv; + + sk->sk_prot->init(sk); + + msk = mhi_sk(sk); + + msk->sk_l3proto = proto; + msk->sk_ifindex = -1; + + return 0; +} + +static int mhi_raw_init(struct sock *sk) +{ + return 0; +} + +static void mhi_raw_destruct(struct sock *sk) +{ + skb_queue_purge(&sk->sk_receive_queue); +} + +static void mhi_raw_close(struct sock *sk, long timeout) +{ + sk_common_release(sk); +} + +static int mhi_raw_ioctl(struct sock *sk, int cmd, unsigned long arg) +{ + int err; + + DPRINTK("mhi_raw_ioctl: cmd:%d arg:%lu\n", cmd, arg); + + switch (cmd) { + case SIOCOUTQ: + { + int len; + len = sk_wmem_alloc_get(sk); + err = put_user(len, (int __user *)arg); + } + break; + + case SIOCINQ: + { + struct sk_buff *skb; + int len; + + lock_sock(sk); + { + skb = skb_peek(&sk->sk_receive_queue); + len = skb ? skb->len : 0; + } + release_sock(sk); + + err = put_user(len, (int __user *)arg); + } + break; + + default: + err = -ENOIOCTLCMD; + } + + return err; +} + +static int mhi_raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) +{ + struct mhi_sock *msk = mhi_sk(sk); + struct net_device *dev = NULL; + struct sk_buff *skb; + + int err = -EFAULT; + + if (msg->msg_flags & + ~(MSG_DONTWAIT | MSG_EOR | MSG_NOSIGNAL | MSG_CMSG_COMPAT)) { + pr_warn("mhi_raw_sendmsg: incompatible socket msg_flags: 0x%08X\n", + msg->msg_flags); + err = -EOPNOTSUPP; + goto out; + } + + skb = sock_alloc_send_skb(sk, + len, (msg->msg_flags & MSG_DONTWAIT), &err); + if (!skb) { + pr_err("mhi_raw_sendmsg: sock_alloc_send_skb failed: %d\n", + err); + goto out; + } + + err = memcpy_from_msg((void *)skb_put(skb, len), msg, len); + if (err < 0) { + pr_err("mhi_raw_sendmsg: memcpy_from_msg failed: %d\n", err); + goto drop; + } + + if (msk->sk_ifindex) + dev = dev_get_by_index(sock_net(sk), msk->sk_ifindex); + + if (!dev) { + pr_err("mhi_raw_sendmsg: no device for ifindex:%d\n", + msk->sk_ifindex); + goto drop; + } + + if (!(dev->flags & IFF_UP)) { + pr_err("mhi_raw_sendmsg: device %d not IFF_UP\n", + msk->sk_ifindex); + err = -ENETDOWN; + goto drop; + } + + if (len > dev->mtu) { + err = -EMSGSIZE; + goto drop; + } + + skb_reset_network_header(skb); + skb_reset_mac_header(skb); + + err = mhi_skb_send(skb, dev, sk->sk_protocol); + + goto put; + +drop: + kfree(skb); +put: + if (dev) + dev_put(dev); +out: + return err; +} + +static int mhi_raw_recvmsg(struct sock *sk, struct msghdr *msg, + size_t len, int noblock, int flags, int *addr_len) +{ + struct sk_buff *skb = NULL; + int cnt, err; + + err = -EOPNOTSUPP; + + if (flags & + ~(MSG_PEEK | MSG_TRUNC | MSG_DONTWAIT | + MSG_NOSIGNAL | MSG_CMSG_COMPAT)) { + pr_warn("mhi_raw_recvmsg: incompatible socket flags: 0x%08X", + flags); + goto out2; + } + + if (addr_len) + addr_len[0] = 0; + + skb = skb_recv_datagram(sk, flags, noblock, &err); + if (!skb) + goto out2; + + cnt = skb->len; + if (len < cnt) { + msg->msg_flags |= MSG_TRUNC; + cnt = len; + } + + err = skb_copy_datagram_msg(skb, 0, msg, cnt); + if (err) + goto out; + + if (flags & MSG_TRUNC) + err = skb->len; + else + err = cnt; + +out: + skb_free_datagram(sk, skb); +out2: + return err; +} + +static int mhi_raw_backlog_rcv(struct sock *sk, struct sk_buff *skb) +{ + if (sock_queue_rcv_skb(sk, skb) < 0) { + kfree_skb(skb); + return NET_RX_DROP; + } + + return NET_RX_SUCCESS; +} + +static struct proto mhi_raw_proto = { + .name = "MHI-RAW", + .owner = THIS_MODULE, + .close = mhi_raw_close, + .ioctl = mhi_raw_ioctl, + .init = mhi_raw_init, + .sendmsg = mhi_raw_sendmsg, + .recvmsg = mhi_raw_recvmsg, + .backlog_rcv = mhi_raw_backlog_rcv, + .hash = mhi_sock_hash, + .unhash = mhi_sock_unhash, + .obj_size = sizeof(struct mhi_sock), +}; + +int mhi_raw_proto_init(void) +{ + DPRINTK("mhi_raw_proto_init\n"); + + return proto_register(&mhi_raw_proto, 1); +} + +void mhi_raw_proto_exit(void) +{ + DPRINTK("mhi_raw_proto_exit\n"); + + proto_unregister(&mhi_raw_proto); +} +#endif /* CONFIG_BCM_KF_MHI */ diff -urN linux-4.1.52-orig/net/mhi/mhi_socket.c linux-4.1.52/net/mhi/mhi_socket.c --- linux-4.1.52-orig/net/mhi/mhi_socket.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/mhi/mhi_socket.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,330 @@ +#ifdef CONFIG_BCM_KF_MHI +/* +<:copyright-BRCM:2011:DUAL/GPL:standard + + Copyright (c) 2011 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +/* + * File: mhi_socket.c + * + * Socket layer implementation for AF_MHI. + * + * This module implements generic sockets for MHI. + * The protocol is implemented separately, like mhi_dgram.c. + * + * As MHI does not have addressed, the MHI interface is + * defined by sa_ifindex field in sockaddr_mhi. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef CONFIG_MHI_DEBUG +# define DPRINTK(...) pr_debug("MHI/SOCKET: " __VA_ARGS__) +#else +# define DPRINTK(...) +#endif + +/* Master lock for MHI sockets */ +static DEFINE_SPINLOCK(mhi_sock_lock); + +/* List of MHI sockets */ +static struct hlist_head mhi_sock_list; + +static int mhi_sock_create(struct net *net, + struct socket *sock, int proto, int kern) +{ + int err = 0; + + DPRINTK("mhi_sock_create: type:%d proto:%d\n", sock->type, proto); + + if (!capable(CAP_SYS_ADMIN) || !capable(CAP_NET_ADMIN)) { + pr_warn("AF_MHI: socket create failed: PERMISSION DENIED\n"); + return -EPERM; + } + + if (!mhi_protocol_registered(proto)) { + pr_warn("AF_MHI: socket create failed: No support for L2 channel %d\n", + proto); + return -EPROTONOSUPPORT; + } + + if (sock->type == SOCK_DGRAM) + err = mhi_dgram_sock_create(net, sock, proto, kern); + else if (sock->type == SOCK_RAW) + err = mhi_raw_sock_create(net, sock, proto, kern); + else { + pr_warn("AF_MHI: trying to create a socket with unknown type %d\n", + sock->type); + err = -EPROTONOSUPPORT; + } + + if (err) + pr_warn("AF_MHI: socket create failed: %d\n", err); + + return err; +} + +static int mhi_sock_release(struct socket *sock) +{ + if (sock->sk) { + DPRINTK("mhi_sock_release: proto:%d type:%d\n", + sock->sk->sk_protocol, sock->type); + + sock->sk->sk_prot->close(sock->sk, 0); + sock->sk = NULL; + } + + return 0; +} + +static int mhi_sock_bind(struct socket *sock, struct sockaddr *addr, int len) +{ + struct sock *sk = sock->sk; + struct mhi_sock *msk = mhi_sk(sk); + struct sockaddr_mhi *sam = sa_mhi(addr); + + int err = 0; + + DPRINTK("mhi_sock_bind: proto:%d state:%d\n", + sk->sk_protocol, sk->sk_state); + + if (sk->sk_prot->bind) + return sk->sk_prot->bind(sk, addr, len); + + if (len < sizeof(struct sockaddr_mhi)) + return -EINVAL; + + lock_sock(sk); + { + if (sk->sk_state == TCP_CLOSE) { + msk->sk_ifindex = sam->sa_ifindex; + WARN_ON(sk_hashed(sk)); + sk->sk_prot->hash(sk); + } else { + err = -EINVAL; /* attempt to rebind */ + } + } + release_sock(sk); + + return err; +} + +int mhi_sock_rcv_unicast(struct sk_buff *skb, u8 l3proto, u32 l3length) +{ + struct sock *sknode; + struct mhi_sock *msk; + + DPRINTK("mhi_sock_rcv_unicast: proto:%d, len:%d\n", l3proto, l3length); + + spin_lock(&mhi_sock_lock); + { + sk_for_each(sknode, &mhi_sock_list) { + msk = mhi_sk(sknode); + if ((msk->sk_l3proto == MHI_L3_ANY || + msk->sk_l3proto == l3proto) && + (msk->sk_ifindex == skb->dev->ifindex)) { + sock_hold(sknode); + sk_receive_skb(sknode, skb, 0); + skb = NULL; + break; + } + } + } + spin_unlock(&mhi_sock_lock); + + if (skb) + kfree_skb(skb); + + return NET_RX_SUCCESS; +} + +int mhi_sock_rcv_multicast(struct sk_buff *skb, u8 l3proto, u32 l3length) +{ + struct sock *sknode; + struct mhi_sock *msk; + struct sk_buff *clone; + + DPRINTK("mhi_sock_rcv_multicast: proto:%d, len:%d\n", + l3proto, l3length); + + spin_lock(&mhi_sock_lock); + { + sk_for_each(sknode, &mhi_sock_list) { + msk = mhi_sk(sknode); + if ((msk->sk_l3proto == MHI_L3_ANY || + msk->sk_l3proto == l3proto) && + (msk->sk_ifindex == skb->dev->ifindex)) { + clone = skb_clone(skb, GFP_ATOMIC); + if (likely(clone)) { + sock_hold(sknode); + sk_receive_skb(sknode, clone, 0); + } + } + } + } + spin_unlock(&mhi_sock_lock); + + kfree_skb(skb); + + return NET_RX_SUCCESS; +} + +int mhi_sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) +{ + DPRINTK("mhi_sock_sendmsg: len:%u\n", len); + + return sock->sk->sk_prot->sendmsg(sock->sk, msg, len); +} + +int mhi_sock_recvmsg(struct socket *sock, + struct msghdr *msg, size_t len, int flags) +{ + int addrlen = 0; + int err; + + err = sock->sk->sk_prot->recvmsg(sock->sk, msg, len, + flags & MSG_DONTWAIT, + flags & ~MSG_DONTWAIT, &addrlen); + + if (err >= 0) + msg->msg_namelen = addrlen; + + return err; +} + +int mhi_getsockopt(struct socket *sock, int level, int optname, + char __user *optval, int __user *optlen) +{ + struct sock *sk = sock->sk; + int len, val; + void *data; + + if (get_user(len, optlen)) + return -EFAULT; + + if (len < 0) + return -EINVAL; + + switch (optname) { + case MHI_DROP_COUNT: + if (len > sizeof(int)) + len = sizeof(int); + spin_lock_bh(&sk->sk_receive_queue.lock); + val = atomic_read(&sk->sk_drops); + spin_unlock_bh(&sk->sk_receive_queue.lock); + data = &val; + break; + default: + return -ENOPROTOOPT; + } + + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval, data, len)) + return -EFAULT; + return 0; +} + +void mhi_sock_hash(struct sock *sk) +{ + DPRINTK("mhi_sock_hash: proto:%d\n", sk->sk_protocol); + + spin_lock_bh(&mhi_sock_lock); + sk_add_node(sk, &mhi_sock_list); + spin_unlock_bh(&mhi_sock_lock); +} + +void mhi_sock_unhash(struct sock *sk) +{ + DPRINTK("mhi_sock_unhash: proto:%d\n", sk->sk_protocol); + + spin_lock_bh(&mhi_sock_lock); + sk_del_node_init(sk); + spin_unlock_bh(&mhi_sock_lock); +} + +const struct proto_ops mhi_socket_ops = { + .family = AF_MHI, + .owner = THIS_MODULE, + .release = mhi_sock_release, + .bind = mhi_sock_bind, + .connect = sock_no_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = sock_no_getname, + .poll = datagram_poll, + .ioctl = sock_no_ioctl, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = sock_no_setsockopt, + .getsockopt = mhi_getsockopt, +#ifdef CONFIG_COMPAT + .compat_setsockopt = sock_no_setsockopt, + .compat_getsockopt = sock_no_getsockopt, +#endif + .sendmsg = mhi_sock_sendmsg, + .recvmsg = mhi_sock_recvmsg, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, +}; + +static const struct net_proto_family mhi_proto_family = { + .family = PF_MHI, + .create = mhi_sock_create, + .owner = THIS_MODULE, +}; + +int mhi_sock_init(void) +{ + DPRINTK("mhi_sock_init\n"); + + INIT_HLIST_HEAD(&mhi_sock_list); + spin_lock_init(&mhi_sock_lock); + + return sock_register(&mhi_proto_family); +} + +void mhi_sock_exit(void) +{ + DPRINTK("mhi_sock_exit\n"); + + sock_unregister(PF_MHI); +} +#endif /* CONFIG_BCM_KF_MHI */ diff -urN linux-4.1.52-orig/net/mptcp/Kconfig linux-4.1.52/net/mptcp/Kconfig --- linux-4.1.52-orig/net/mptcp/Kconfig 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/mptcp/Kconfig 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,131 @@ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +# +# MPTCP configuration +# +config MPTCP + bool "MPTCP protocol" + depends on (IPV6=y || IPV6=n) + ---help--- + This replaces the normal TCP stack with a Multipath TCP stack, + able to use several paths at once. + +menuconfig MPTCP_PM_ADVANCED + bool "MPTCP: advanced path-manager control" + depends on MPTCP=y + ---help--- + Support for selection of different path-managers. You should choose 'Y' here, + because otherwise you will not actively create new MPTCP-subflows. + +if MPTCP_PM_ADVANCED + +config MPTCP_FULLMESH + tristate "MPTCP Full-Mesh Path-Manager" + depends on MPTCP=y + ---help--- + This path-management module will create a full-mesh among all IP-addresses. + +config MPTCP_NDIFFPORTS + tristate "MPTCP ndiff-ports" + depends on MPTCP=y + ---help--- + This path-management module will create multiple subflows between the same + pair of IP-addresses, modifying the source-port. You can set the number + of subflows via the mptcp_ndiffports-sysctl. + +config MPTCP_BINDER + tristate "MPTCP Binder" + depends on (MPTCP=y) + ---help--- + This path-management module works like ndiffports, and adds the sysctl + option to set the gateway (and/or path to) per each additional subflow + via Loose Source Routing (IPv4 only). + +choice + prompt "Default MPTCP Path-Manager" + default DEFAULT + help + Select the Path-Manager of your choice + + config DEFAULT_FULLMESH + bool "Full mesh" if MPTCP_FULLMESH=y + + config DEFAULT_NDIFFPORTS + bool "ndiff-ports" if MPTCP_NDIFFPORTS=y + + config DEFAULT_BINDER + bool "binder" if MPTCP_BINDER=y + + config DEFAULT_DUMMY + bool "Default" + +endchoice + +endif + +config DEFAULT_MPTCP_PM + string + default "default" if DEFAULT_DUMMY + default "fullmesh" if DEFAULT_FULLMESH + default "ndiffports" if DEFAULT_NDIFFPORTS + default "binder" if DEFAULT_BINDER + default "default" + +menuconfig MPTCP_SCHED_ADVANCED + bool "MPTCP: advanced scheduler control" + depends on MPTCP=y + ---help--- + Support for selection of different schedulers. You should choose 'Y' here, + if you want to choose a different scheduler than the default one. + +if MPTCP_SCHED_ADVANCED + +config MPTCP_ROUNDROBIN + tristate "MPTCP Round-Robin" + depends on (MPTCP=y) + ---help--- + This is a very simple round-robin scheduler. Probably has bad performance + but might be interesting for researchers. + +config MPTCP_REDUNDANT + tristate "MPTCP Redundant" + depends on (MPTCP=y) + ---help--- + This scheduler sends all packets redundantly over all subflows to decreases + latency and jitter on the cost of lower throughput. + +choice + prompt "Default MPTCP Scheduler" + default DEFAULT + help + Select the Scheduler of your choice + + config DEFAULT_SCHEDULER + bool "Default" + ---help--- + This is the default scheduler, sending first on the subflow + with the lowest RTT. + + config DEFAULT_ROUNDROBIN + bool "Round-Robin" if MPTCP_ROUNDROBIN=y + ---help--- + This is the round-rob scheduler, sending in a round-robin + fashion.. + + config DEFAULT_REDUNDANT + bool "Redundant" if MPTCP_REDUNDANT=y + ---help--- + This is the redundant scheduler, sending packets redundantly over + all the subflows. + +endchoice +endif + +config DEFAULT_MPTCP_SCHED + string + depends on (MPTCP=y) + default "default" if DEFAULT_SCHEDULER + default "roundrobin" if DEFAULT_ROUNDROBIN + default "redundant" if DEFAULT_REDUNDANT + default "default" + +#endif diff -urN linux-4.1.52-orig/net/mptcp/Makefile linux-4.1.52/net/mptcp/Makefile --- linux-4.1.52-orig/net/mptcp/Makefile 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/mptcp/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,24 @@ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +# +## Makefile for MultiPath TCP support code. +# +# + +obj-$(CONFIG_MPTCP) += mptcp.o + +mptcp-y := mptcp_ctrl.o mptcp_ipv4.o mptcp_ofo_queue.o mptcp_pm.o \ + mptcp_output.o mptcp_input.o mptcp_sched.o + +obj-$(CONFIG_TCP_CONG_LIA) += mptcp_coupled.o +obj-$(CONFIG_TCP_CONG_OLIA) += mptcp_olia.o +obj-$(CONFIG_TCP_CONG_WVEGAS) += mptcp_wvegas.o +obj-$(CONFIG_TCP_CONG_BALIA) += mptcp_balia.o +obj-$(CONFIG_MPTCP_FULLMESH) += mptcp_fullmesh.o +obj-$(CONFIG_MPTCP_NDIFFPORTS) += mptcp_ndiffports.o +obj-$(CONFIG_MPTCP_BINDER) += mptcp_binder.o +obj-$(CONFIG_MPTCP_ROUNDROBIN) += mptcp_rr.o +obj-$(CONFIG_MPTCP_REDUNDANT) += mptcp_redundant.o + +mptcp-$(subst m,y,$(CONFIG_IPV6)) += mptcp_ipv6.o + +#endif diff -urN linux-4.1.52-orig/net/mptcp/mptcp_balia.c linux-4.1.52/net/mptcp/mptcp_balia.c --- linux-4.1.52-orig/net/mptcp/mptcp_balia.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/mptcp/mptcp_balia.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,269 @@ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +/* + * MPTCP implementation - Balia Congestion Control + * (Balanced Linked Adaptation Algorithm) + * + * Analysis, Design and Implementation: + * Qiuyu Peng + * Anwar Walid + * Jaehyun Hwang + * Steven H. Low + * + * 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 + +/* The variable 'rate' (i.e., x_r) will be scaled + * e.g., from B/s to KB/s, MB/s, or GB/s + * if max_rate > 2^rate_scale_limit + */ + +static int rate_scale_limit = 25; +static int alpha_scale = 10; +static int scale_num = 5; + +struct mptcp_balia { + u64 ai; + u64 md; + bool forced_update; +}; + +static inline int mptcp_balia_sk_can_send(const struct sock *sk) +{ + return mptcp_sk_can_send(sk) && tcp_sk(sk)->srtt_us; +} + +static inline u64 mptcp_get_ai(const struct sock *meta_sk) +{ + return ((struct mptcp_balia *)inet_csk_ca(meta_sk))->ai; +} + +static inline void mptcp_set_ai(const struct sock *meta_sk, u64 ai) +{ + ((struct mptcp_balia *)inet_csk_ca(meta_sk))->ai = ai; +} + +static inline u64 mptcp_get_md(const struct sock *meta_sk) +{ + return ((struct mptcp_balia *)inet_csk_ca(meta_sk))->md; +} + +static inline void mptcp_set_md(const struct sock *meta_sk, u64 md) +{ + ((struct mptcp_balia *)inet_csk_ca(meta_sk))->md = md; +} + +static inline u64 mptcp_balia_scale(u64 val, int scale) +{ + return (u64) val << scale; +} + +static inline bool mptcp_get_forced(const struct sock *meta_sk) +{ + return ((struct mptcp_balia *)inet_csk_ca(meta_sk))->forced_update; +} + +static inline void mptcp_set_forced(const struct sock *meta_sk, bool force) +{ + ((struct mptcp_balia *)inet_csk_ca(meta_sk))->forced_update = force; +} + +static void mptcp_balia_recalc_ai(const struct sock *sk) +{ + const struct tcp_sock *tp = tcp_sk(sk); + const struct mptcp_cb *mpcb = tp->mpcb; + const struct sock *sub_sk; + u64 max_rate = 0, rate = 0, sum_rate = 0; + u64 alpha, ai = tp->snd_cwnd, md = (tp->snd_cwnd >> 1); + int num_scale_down = 0; + + if (!mpcb) + return; + + /* Only one subflow left - fall back to normal reno-behavior */ + if (mpcb->cnt_established <= 1) + goto exit; + + /* Find max_rate first */ + mptcp_for_each_sk(mpcb, sub_sk) { + struct tcp_sock *sub_tp = tcp_sk(sub_sk); + u64 tmp; + + if (!mptcp_balia_sk_can_send(sub_sk)) + continue; + + tmp = div_u64((u64)tp->mss_cache * sub_tp->snd_cwnd + * (USEC_PER_SEC << 3), sub_tp->srtt_us); + sum_rate += tmp; + + if (tp == sub_tp) + rate = tmp; + + if (tmp >= max_rate) + max_rate = tmp; + } + + /* At least, the current subflow should be able to send */ + if (unlikely(!rate)) + goto exit; + + alpha = div64_u64(max_rate, rate); + + /* Scale down max_rate if it is too high (e.g., >2^25) */ + while (max_rate > mptcp_balia_scale(1, rate_scale_limit)) { + max_rate >>= scale_num; + num_scale_down++; + } + + if (num_scale_down) { + sum_rate = 0; + mptcp_for_each_sk(mpcb, sub_sk) { + struct tcp_sock *sub_tp = tcp_sk(sub_sk); + u64 tmp; + + if (!mptcp_balia_sk_can_send(sub_sk)) + continue; + + tmp = div_u64((u64)tp->mss_cache * sub_tp->snd_cwnd + * (USEC_PER_SEC << 3), sub_tp->srtt_us); + tmp >>= (scale_num * num_scale_down); + + sum_rate += tmp; + } + rate >>= (scale_num * num_scale_down); + } + + /* (sum_rate)^2 * 10 * w_r + * ai = ------------------------------------ + * (x_r + max_rate) * (4x_r + max_rate) + */ + sum_rate *= sum_rate; + + ai = div64_u64(sum_rate * 10, rate + max_rate); + ai = div64_u64(ai * tp->snd_cwnd, (rate << 2) + max_rate); + + if (unlikely(!ai)) + ai = tp->snd_cwnd; + + md = ((tp->snd_cwnd >> 1) * min(mptcp_balia_scale(alpha, alpha_scale), + mptcp_balia_scale(3, alpha_scale) >> 1)) + >> alpha_scale; + +exit: + mptcp_set_ai(sk, ai); + mptcp_set_md(sk, md); +} + +static void mptcp_balia_init(struct sock *sk) +{ + if (mptcp(tcp_sk(sk))) { + mptcp_set_forced(sk, 0); + mptcp_set_ai(sk, 0); + mptcp_set_md(sk, 0); + } +} + +static void mptcp_balia_cwnd_event(struct sock *sk, enum tcp_ca_event event) +{ + if (event == CA_EVENT_COMPLETE_CWR || event == CA_EVENT_LOSS) + mptcp_balia_recalc_ai(sk); +} + +static void mptcp_balia_set_state(struct sock *sk, u8 ca_state) +{ + if (!mptcp(tcp_sk(sk))) + return; + + mptcp_set_forced(sk, 1); +} + +static void mptcp_balia_cong_avoid(struct sock *sk, u32 ack, u32 acked) +{ + struct tcp_sock *tp = tcp_sk(sk); + const struct mptcp_cb *mpcb = tp->mpcb; + int snd_cwnd; + + if (!mptcp(tp)) { + tcp_reno_cong_avoid(sk, ack, acked); + return; + } + + if (!tcp_is_cwnd_limited(sk)) + return; + + if (tp->snd_cwnd <= tp->snd_ssthresh) { + /* In "safe" area, increase. */ + tcp_slow_start(tp, acked); + mptcp_balia_recalc_ai(sk); + return; + } + + if (mptcp_get_forced(mptcp_meta_sk(sk))) { + mptcp_balia_recalc_ai(sk); + mptcp_set_forced(sk, 0); + } + + if (mpcb->cnt_established > 1) + snd_cwnd = (int) mptcp_get_ai(sk); + else + snd_cwnd = tp->snd_cwnd; + + if (tp->snd_cwnd_cnt >= snd_cwnd) { + if (tp->snd_cwnd < tp->snd_cwnd_clamp) { + tp->snd_cwnd++; + mptcp_balia_recalc_ai(sk); + } + + tp->snd_cwnd_cnt = 0; + } else { + tp->snd_cwnd_cnt++; + } +} + +static u32 mptcp_balia_ssthresh(struct sock *sk) +{ + const struct tcp_sock *tp = tcp_sk(sk); + const struct mptcp_cb *mpcb = tp->mpcb; + + if (unlikely(!mptcp(tp) || mpcb->cnt_established <= 1)) + return tcp_reno_ssthresh(sk); + else + return max((u32)(tp->snd_cwnd - mptcp_get_md(sk)), 1U); +} + +static struct tcp_congestion_ops mptcp_balia = { + .init = mptcp_balia_init, + .ssthresh = mptcp_balia_ssthresh, + .cong_avoid = mptcp_balia_cong_avoid, + .cwnd_event = mptcp_balia_cwnd_event, + .set_state = mptcp_balia_set_state, + .owner = THIS_MODULE, + .name = "balia", +}; + +static int __init mptcp_balia_register(void) +{ + BUILD_BUG_ON(sizeof(struct mptcp_balia) > ICSK_CA_PRIV_SIZE); + return tcp_register_congestion_control(&mptcp_balia); +} + +static void __exit mptcp_balia_unregister(void) +{ + tcp_unregister_congestion_control(&mptcp_balia); +} + +module_init(mptcp_balia_register); +module_exit(mptcp_balia_unregister); + +MODULE_AUTHOR("Jaehyun Hwang, Anwar Walid, Qiuyu Peng, Steven H. Low"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MPTCP BALIA CONGESTION CONTROL ALGORITHM"); +MODULE_VERSION("0.1"); +#endif diff -urN linux-4.1.52-orig/net/mptcp/mptcp_binder.c linux-4.1.52/net/mptcp/mptcp_binder.c --- linux-4.1.52-orig/net/mptcp/mptcp_binder.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/mptcp/mptcp_binder.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,489 @@ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MPTCP_GW_MAX_LISTS 10 +#define MPTCP_GW_LIST_MAX_LEN 6 +#define MPTCP_GW_SYSCTL_MAX_LEN (15 * MPTCP_GW_LIST_MAX_LEN * \ + MPTCP_GW_MAX_LISTS) + +struct mptcp_gw_list { + struct in_addr list[MPTCP_GW_MAX_LISTS][MPTCP_GW_LIST_MAX_LEN]; + u8 len[MPTCP_GW_MAX_LISTS]; +}; + +struct binder_priv { + /* Worker struct for subflow establishment */ + struct work_struct subflow_work; + + struct mptcp_cb *mpcb; + + /* Prevent multiple sub-sockets concurrently iterating over sockets */ + spinlock_t *flow_lock; +}; + +static struct mptcp_gw_list *mptcp_gws; +static rwlock_t mptcp_gws_lock; + +static int mptcp_binder_ndiffports __read_mostly = 1; + +static char sysctl_mptcp_binder_gateways[MPTCP_GW_SYSCTL_MAX_LEN] __read_mostly; + +static int mptcp_get_avail_list_ipv4(struct sock *sk) +{ + int i, j, list_taken, opt_ret, opt_len; + unsigned char *opt_ptr, *opt_end_ptr, opt[MAX_IPOPTLEN]; + + for (i = 0; i < MPTCP_GW_MAX_LISTS; ++i) { + if (mptcp_gws->len[i] == 0) + goto error; + + mptcp_debug("mptcp_get_avail_list_ipv4: List %i\n", i); + list_taken = 0; + + /* Loop through all sub-sockets in this connection */ + mptcp_for_each_sk(tcp_sk(sk)->mpcb, sk) { + mptcp_debug("mptcp_get_avail_list_ipv4: Next sock\n"); + + /* Reset length and options buffer, then retrieve + * from socket + */ + opt_len = MAX_IPOPTLEN; + memset(opt, 0, MAX_IPOPTLEN); + opt_ret = ip_getsockopt(sk, IPPROTO_IP, + IP_OPTIONS, opt, &opt_len); + if (opt_ret < 0) { + mptcp_debug(KERN_ERR "%s: MPTCP subsocket getsockopt() IP_OPTIONS failed, error %d\n", + __func__, opt_ret); + goto error; + } + + /* If socket has no options, it has no stake in this list */ + if (opt_len <= 0) + continue; + + /* Iterate options buffer */ + for (opt_ptr = &opt[0]; opt_ptr < &opt[opt_len]; opt_ptr++) { + if (*opt_ptr == IPOPT_LSRR) { + mptcp_debug("mptcp_get_avail_list_ipv4: LSRR options found\n"); + goto sock_lsrr; + } + } + continue; + +sock_lsrr: + /* Pointer to the 2nd to last address */ + opt_end_ptr = opt_ptr+(*(opt_ptr+1))-4; + + /* Addresses start 3 bytes after type offset */ + opt_ptr += 3; + j = 0; + + /* Different length lists cannot be the same */ + if ((opt_end_ptr-opt_ptr)/4 != mptcp_gws->len[i]) + continue; + + /* Iterate if we are still inside options list + * and sysctl list + */ + while (opt_ptr < opt_end_ptr && j < mptcp_gws->len[i]) { + /* If there is a different address, this list must + * not be set on this socket + */ + if (memcmp(&mptcp_gws->list[i][j], opt_ptr, 4)) + break; + + /* Jump 4 bytes to next address */ + opt_ptr += 4; + j++; + } + + /* Reached the end without a differing address, lists + * are therefore identical. + */ + if (j == mptcp_gws->len[i]) { + mptcp_debug("mptcp_get_avail_list_ipv4: List already used\n"); + list_taken = 1; + break; + } + } + + /* Free list found if not taken by a socket */ + if (!list_taken) { + mptcp_debug("mptcp_get_avail_list_ipv4: List free\n"); + break; + } + } + + if (i >= MPTCP_GW_MAX_LISTS) + goto error; + + return i; +error: + return -1; +} + +/* The list of addresses is parsed each time a new connection is opened, + * to make sure it's up to date. In case of error, all the lists are + * marked as unavailable and the subflow's fingerprint is set to 0. + */ +static void mptcp_v4_add_lsrr(struct sock *sk, struct in_addr addr) +{ + int i, j, ret; + unsigned char opt[MAX_IPOPTLEN] = {0}; + struct tcp_sock *tp = tcp_sk(sk); + struct binder_priv *fmp = (struct binder_priv *)&tp->mpcb->mptcp_pm[0]; + + /* Read lock: multiple sockets can read LSRR addresses at the same + * time, but writes are done in mutual exclusion. + * Spin lock: must search for free list for one socket at a time, or + * multiple sockets could take the same list. + */ + read_lock(&mptcp_gws_lock); + spin_lock(fmp->flow_lock); + + i = mptcp_get_avail_list_ipv4(sk); + + /* Execution enters here only if a free path is found. + */ + if (i >= 0) { + opt[0] = IPOPT_NOP; + opt[1] = IPOPT_LSRR; + opt[2] = sizeof(mptcp_gws->list[i][0].s_addr) * + (mptcp_gws->len[i] + 1) + 3; + opt[3] = IPOPT_MINOFF; + for (j = 0; j < mptcp_gws->len[i]; ++j) + memcpy(opt + 4 + + (j * sizeof(mptcp_gws->list[i][0].s_addr)), + &mptcp_gws->list[i][j].s_addr, + sizeof(mptcp_gws->list[i][0].s_addr)); + /* Final destination must be part of IP_OPTIONS parameter. */ + memcpy(opt + 4 + (j * sizeof(addr.s_addr)), &addr.s_addr, + sizeof(addr.s_addr)); + + /* setsockopt must be inside the lock, otherwise another + * subflow could fail to see that we have taken a list. + */ + ret = ip_setsockopt(sk, IPPROTO_IP, IP_OPTIONS, opt, + 4 + sizeof(mptcp_gws->list[i][0].s_addr) + * (mptcp_gws->len[i] + 1)); + + if (ret < 0) { + mptcp_debug(KERN_ERR "%s: MPTCP subsock setsockopt() IP_OPTIONS failed, error %d\n", + __func__, ret); + } + } + + spin_unlock(fmp->flow_lock); + read_unlock(&mptcp_gws_lock); + + return; +} + +/* Parses gateways string for a list of paths to different + * gateways, and stores them for use with the Loose Source Routing (LSRR) + * socket option. Each list must have "," separated addresses, and the lists + * themselves must be separated by "-". Returns -1 in case one or more of the + * addresses is not a valid ipv4/6 address. + */ +static int mptcp_parse_gateway_ipv4(char *gateways) +{ + int i, j, k, ret; + char *tmp_string = NULL; + struct in_addr tmp_addr; + + tmp_string = kzalloc(16, GFP_KERNEL); + if (tmp_string == NULL) + return -ENOMEM; + + write_lock(&mptcp_gws_lock); + + memset(mptcp_gws, 0, sizeof(struct mptcp_gw_list)); + + /* A TMP string is used since inet_pton needs a null terminated string + * but we do not want to modify the sysctl for obvious reasons. + * i will iterate over the SYSCTL string, j will iterate over the + * temporary string where each IP is copied into, k will iterate over + * the IPs in each list. + */ + for (i = j = k = 0; + i < MPTCP_GW_SYSCTL_MAX_LEN && k < MPTCP_GW_MAX_LISTS; + ++i) { + if (gateways[i] == '-' || gateways[i] == ',' || gateways[i] == '\0') { + /* If the temp IP is empty and the current list is + * empty, we are done. + */ + if (j == 0 && mptcp_gws->len[k] == 0) + break; + + /* Terminate the temp IP string, then if it is + * non-empty parse the IP and copy it. + */ + tmp_string[j] = '\0'; + if (j > 0) { + mptcp_debug("mptcp_parse_gateway_list tmp: %s i: %d\n", tmp_string, i); + + ret = in4_pton(tmp_string, strlen(tmp_string), + (u8 *)&tmp_addr.s_addr, '\0', + NULL); + + if (ret) { + mptcp_debug("mptcp_parse_gateway_list ret: %d s_addr: %pI4\n", + ret, + &tmp_addr.s_addr); + memcpy(&mptcp_gws->list[k][mptcp_gws->len[k]].s_addr, + &tmp_addr.s_addr, + sizeof(tmp_addr.s_addr)); + mptcp_gws->len[k]++; + j = 0; + tmp_string[j] = '\0'; + /* Since we can't impose a limit to + * what the user can input, make sure + * there are not too many IPs in the + * SYSCTL string. + */ + if (mptcp_gws->len[k] > MPTCP_GW_LIST_MAX_LEN) { + mptcp_debug("mptcp_parse_gateway_list too many members in list %i: max %i\n", + k, + MPTCP_GW_LIST_MAX_LEN); + goto error; + } + } else { + goto error; + } + } + + if (gateways[i] == '-' || gateways[i] == '\0') + ++k; + } else { + tmp_string[j] = gateways[i]; + ++j; + } + } + + /* Number of flows is number of gateway lists plus master flow */ + mptcp_binder_ndiffports = k+1; + + write_unlock(&mptcp_gws_lock); + kfree(tmp_string); + + return 0; + +error: + memset(mptcp_gws, 0, sizeof(struct mptcp_gw_list)); + memset(gateways, 0, sizeof(char) * MPTCP_GW_SYSCTL_MAX_LEN); + write_unlock(&mptcp_gws_lock); + kfree(tmp_string); + return -1; +} + +/** + * Create all new subflows, by doing calls to mptcp_initX_subsockets + * + * This function uses a goto next_subflow, to allow releasing the lock between + * new subflows and giving other processes a chance to do some work on the + * socket and potentially finishing the communication. + **/ +static void create_subflow_worker(struct work_struct *work) +{ + const struct binder_priv *pm_priv = container_of(work, + struct binder_priv, + subflow_work); + struct mptcp_cb *mpcb = pm_priv->mpcb; + struct sock *meta_sk = mpcb->meta_sk; + int iter = 0; + +next_subflow: + if (iter) { + release_sock(meta_sk); + mutex_unlock(&mpcb->mpcb_mutex); + + cond_resched(); + } + mutex_lock(&mpcb->mpcb_mutex); + lock_sock_nested(meta_sk, SINGLE_DEPTH_NESTING); + + iter++; + + if (sock_flag(meta_sk, SOCK_DEAD)) + goto exit; + + if (mpcb->master_sk && + !tcp_sk(mpcb->master_sk)->mptcp->fully_established) + goto exit; + + if (mptcp_binder_ndiffports > iter && + mptcp_binder_ndiffports > mpcb->cnt_subflows) { + struct mptcp_loc4 loc; + struct mptcp_rem4 rem; + + loc.addr.s_addr = inet_sk(meta_sk)->inet_saddr; + loc.loc4_id = 0; + loc.low_prio = 0; + + rem.addr.s_addr = inet_sk(meta_sk)->inet_daddr; + rem.port = inet_sk(meta_sk)->inet_dport; + rem.rem4_id = 0; /* Default 0 */ + + mptcp_init4_subsockets(meta_sk, &loc, &rem); + + goto next_subflow; + } + +exit: + release_sock(meta_sk); + mutex_unlock(&mpcb->mpcb_mutex); + sock_put(meta_sk); +} + +static void binder_new_session(const struct sock *meta_sk) +{ + struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; + struct binder_priv *fmp = (struct binder_priv *)&mpcb->mptcp_pm[0]; + static DEFINE_SPINLOCK(flow_lock); + +#if IS_ENABLED(CONFIG_IPV6) + if (meta_sk->sk_family == AF_INET6 && + !mptcp_v6_is_v4_mapped(meta_sk)) { + mptcp_fallback_default(mpcb); + return; + } +#endif + + /* Initialize workqueue-struct */ + INIT_WORK(&fmp->subflow_work, create_subflow_worker); + fmp->mpcb = mpcb; + + fmp->flow_lock = &flow_lock; +} + +static void binder_create_subflows(struct sock *meta_sk) +{ + struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; + struct binder_priv *pm_priv = (struct binder_priv *)&mpcb->mptcp_pm[0]; + + if (mpcb->infinite_mapping_snd || mpcb->infinite_mapping_rcv || + mpcb->send_infinite_mapping || + mpcb->server_side || sock_flag(meta_sk, SOCK_DEAD)) + return; + + if (!work_pending(&pm_priv->subflow_work)) { + sock_hold(meta_sk); + queue_work(mptcp_wq, &pm_priv->subflow_work); + } +} + +static int binder_get_local_id(sa_family_t family, union inet_addr *addr, + struct net *net, bool *low_prio) +{ + return 0; +} + +/* Callback functions, executed when syctl mptcp.mptcp_gateways is updated. + * Inspired from proc_tcp_congestion_control(). + */ +static int proc_mptcp_gateways(struct ctl_table *ctl, int write, + void __user *buffer, size_t *lenp, + loff_t *ppos) +{ + int ret; + struct ctl_table tbl = { + .maxlen = MPTCP_GW_SYSCTL_MAX_LEN, + }; + + if (write) { + tbl.data = kzalloc(MPTCP_GW_SYSCTL_MAX_LEN, GFP_KERNEL); + if (tbl.data == NULL) + return -1; + ret = proc_dostring(&tbl, write, buffer, lenp, ppos); + if (ret == 0) { + ret = mptcp_parse_gateway_ipv4(tbl.data); + memcpy(ctl->data, tbl.data, MPTCP_GW_SYSCTL_MAX_LEN); + } + kfree(tbl.data); + } else { + ret = proc_dostring(ctl, write, buffer, lenp, ppos); + } + + + return ret; +} + +static struct mptcp_pm_ops binder __read_mostly = { + .new_session = binder_new_session, + .fully_established = binder_create_subflows, + .get_local_id = binder_get_local_id, + .init_subsocket_v4 = mptcp_v4_add_lsrr, + .name = "binder", + .owner = THIS_MODULE, +}; + +static struct ctl_table binder_table[] = { + { + .procname = "mptcp_binder_gateways", + .data = &sysctl_mptcp_binder_gateways, + .maxlen = sizeof(char) * MPTCP_GW_SYSCTL_MAX_LEN, + .mode = 0644, + .proc_handler = &proc_mptcp_gateways + }, + { } +}; + +struct ctl_table_header *mptcp_sysctl_binder; + +/* General initialization of MPTCP_PM */ +static int __init binder_register(void) +{ + mptcp_gws = kzalloc(sizeof(*mptcp_gws), GFP_KERNEL); + if (!mptcp_gws) + return -ENOMEM; + + rwlock_init(&mptcp_gws_lock); + + BUILD_BUG_ON(sizeof(struct binder_priv) > MPTCP_PM_SIZE); + + mptcp_sysctl_binder = register_net_sysctl(&init_net, "net/mptcp", + binder_table); + if (!mptcp_sysctl_binder) + goto sysctl_fail; + + if (mptcp_register_path_manager(&binder)) + goto pm_failed; + + return 0; + +pm_failed: + unregister_net_sysctl_table(mptcp_sysctl_binder); +sysctl_fail: + kfree(mptcp_gws); + + return -1; +} + +static void binder_unregister(void) +{ + mptcp_unregister_path_manager(&binder); + unregister_net_sysctl_table(mptcp_sysctl_binder); + kfree(mptcp_gws); +} + +module_init(binder_register); +module_exit(binder_unregister); + +MODULE_AUTHOR("Luca Boccassi, Duncan Eastoe, Christoph Paasch (ndiffports)"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("BINDER MPTCP"); +MODULE_VERSION("0.1"); +#endif diff -urN linux-4.1.52-orig/net/mptcp/mptcp_coupled.c linux-4.1.52/net/mptcp/mptcp_coupled.c --- linux-4.1.52-orig/net/mptcp/mptcp_coupled.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/mptcp/mptcp_coupled.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,272 @@ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +/* + * MPTCP implementation - Linked Increase congestion control Algorithm (LIA) + * + * Initial Design & Implementation: + * Sébastien Barré + * + * Current Maintainer & Author: + * Christoph Paasch + * + * Additional authors: + * Jaakko Korkeaniemi + * Gregory Detal + * Fabien Duchêne + * Andreas Seelinger + * Lavkesh Lahngir + * Andreas Ripke + * Vlad Dogaru + * Octavian Purdila + * John Ronan + * Catalin Nicutar + * Brandon Heller + * + * + * 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 + +/* Scaling is done in the numerator with alpha_scale_num and in the denominator + * with alpha_scale_den. + * + * To downscale, we just need to use alpha_scale. + * + * We have: alpha_scale = alpha_scale_num / (alpha_scale_den ^ 2) + */ +static int alpha_scale_den = 10; +static int alpha_scale_num = 32; +static int alpha_scale = 12; + +struct mptcp_ccc { + u64 alpha; + bool forced_update; +}; + +static inline int mptcp_ccc_sk_can_send(const struct sock *sk) +{ + return mptcp_sk_can_send(sk) && tcp_sk(sk)->srtt_us; +} + +static inline u64 mptcp_get_alpha(const struct sock *meta_sk) +{ + return ((struct mptcp_ccc *)inet_csk_ca(meta_sk))->alpha; +} + +static inline void mptcp_set_alpha(const struct sock *meta_sk, u64 alpha) +{ + ((struct mptcp_ccc *)inet_csk_ca(meta_sk))->alpha = alpha; +} + +static inline u64 mptcp_ccc_scale(u32 val, int scale) +{ + return (u64) val << scale; +} + +static inline bool mptcp_get_forced(const struct sock *meta_sk) +{ + return ((struct mptcp_ccc *)inet_csk_ca(meta_sk))->forced_update; +} + +static inline void mptcp_set_forced(const struct sock *meta_sk, bool force) +{ + ((struct mptcp_ccc *)inet_csk_ca(meta_sk))->forced_update = force; +} + +static void mptcp_ccc_recalc_alpha(const struct sock *sk) +{ + const struct mptcp_cb *mpcb = tcp_sk(sk)->mpcb; + const struct sock *sub_sk; + int best_cwnd = 0, best_rtt = 0, can_send = 0; + u64 max_numerator = 0, sum_denominator = 0, alpha = 1; + + if (!mpcb) + return; + + /* Only one subflow left - fall back to normal reno-behavior + * (set alpha to 1) + */ + if (mpcb->cnt_established <= 1) + goto exit; + + /* Do regular alpha-calculation for multiple subflows */ + + /* Find the max numerator of the alpha-calculation */ + mptcp_for_each_sk(mpcb, sub_sk) { + struct tcp_sock *sub_tp = tcp_sk(sub_sk); + u64 tmp; + + if (!mptcp_ccc_sk_can_send(sub_sk)) + continue; + + can_send++; + + /* We need to look for the path, that provides the max-value. + * Integer-overflow is not possible here, because + * tmp will be in u64. + */ + tmp = div64_u64(mptcp_ccc_scale(sub_tp->snd_cwnd, + alpha_scale_num), (u64)sub_tp->srtt_us * sub_tp->srtt_us); + + if (tmp >= max_numerator) { + max_numerator = tmp; + best_cwnd = sub_tp->snd_cwnd; + best_rtt = sub_tp->srtt_us; + } + } + + /* No subflow is able to send - we don't care anymore */ + if (unlikely(!can_send)) + goto exit; + + /* Calculate the denominator */ + mptcp_for_each_sk(mpcb, sub_sk) { + struct tcp_sock *sub_tp = tcp_sk(sub_sk); + + if (!mptcp_ccc_sk_can_send(sub_sk)) + continue; + + sum_denominator += div_u64( + mptcp_ccc_scale(sub_tp->snd_cwnd, + alpha_scale_den) * best_rtt, + sub_tp->srtt_us); + } + sum_denominator *= sum_denominator; + if (unlikely(!sum_denominator)) { + pr_err("%s: sum_denominator == 0, cnt_established:%d\n", + __func__, mpcb->cnt_established); + mptcp_for_each_sk(mpcb, sub_sk) { + struct tcp_sock *sub_tp = tcp_sk(sub_sk); + pr_err("%s: pi:%d, state:%d\n, rtt:%u, cwnd: %u", + __func__, sub_tp->mptcp->path_index, + sub_sk->sk_state, sub_tp->srtt_us, + sub_tp->snd_cwnd); + } + } + + alpha = div64_u64(mptcp_ccc_scale(best_cwnd, alpha_scale_num), sum_denominator); + + if (unlikely(!alpha)) + alpha = 1; + +exit: + mptcp_set_alpha(mptcp_meta_sk(sk), alpha); +} + +static void mptcp_ccc_init(struct sock *sk) +{ + if (mptcp(tcp_sk(sk))) { + mptcp_set_forced(mptcp_meta_sk(sk), 0); + mptcp_set_alpha(mptcp_meta_sk(sk), 1); + } + /* If we do not mptcp, behave like reno: return */ +} + +static void mptcp_ccc_cwnd_event(struct sock *sk, enum tcp_ca_event event) +{ + if (event == CA_EVENT_LOSS) + mptcp_ccc_recalc_alpha(sk); +} + +static void mptcp_ccc_set_state(struct sock *sk, u8 ca_state) +{ + if (!mptcp(tcp_sk(sk))) + return; + + mptcp_set_forced(mptcp_meta_sk(sk), 1); +} + +static void mptcp_ccc_cong_avoid(struct sock *sk, u32 ack, u32 acked) +{ + struct tcp_sock *tp = tcp_sk(sk); + const struct mptcp_cb *mpcb = tp->mpcb; + int snd_cwnd; + + if (!mptcp(tp)) { + tcp_reno_cong_avoid(sk, ack, acked); + return; + } + + if (!tcp_is_cwnd_limited(sk)) + return; + + if (tp->snd_cwnd <= tp->snd_ssthresh) { + /* In "safe" area, increase. */ + tcp_slow_start(tp, acked); + mptcp_ccc_recalc_alpha(sk); + return; + } + + if (mptcp_get_forced(mptcp_meta_sk(sk))) { + mptcp_ccc_recalc_alpha(sk); + mptcp_set_forced(mptcp_meta_sk(sk), 0); + } + + if (mpcb->cnt_established > 1) { + u64 alpha = mptcp_get_alpha(mptcp_meta_sk(sk)); + + /* This may happen, if at the initialization, the mpcb + * was not yet attached to the sock, and thus + * initializing alpha failed. + */ + if (unlikely(!alpha)) + alpha = 1; + + snd_cwnd = (int) div_u64 ((u64) mptcp_ccc_scale(1, alpha_scale), + alpha); + + /* snd_cwnd_cnt >= max (scale * tot_cwnd / alpha, cwnd) + * Thus, we select here the max value. + */ + if (snd_cwnd < tp->snd_cwnd) + snd_cwnd = tp->snd_cwnd; + } else { + snd_cwnd = tp->snd_cwnd; + } + + if (tp->snd_cwnd_cnt >= snd_cwnd) { + if (tp->snd_cwnd < tp->snd_cwnd_clamp) { + tp->snd_cwnd++; + mptcp_ccc_recalc_alpha(sk); + } + + tp->snd_cwnd_cnt = 0; + } else { + tp->snd_cwnd_cnt++; + } +} + +static struct tcp_congestion_ops mptcp_ccc = { + .init = mptcp_ccc_init, + .ssthresh = tcp_reno_ssthresh, + .cong_avoid = mptcp_ccc_cong_avoid, + .cwnd_event = mptcp_ccc_cwnd_event, + .set_state = mptcp_ccc_set_state, + .owner = THIS_MODULE, + .name = "lia", +}; + +static int __init mptcp_ccc_register(void) +{ + BUILD_BUG_ON(sizeof(struct mptcp_ccc) > ICSK_CA_PRIV_SIZE); + return tcp_register_congestion_control(&mptcp_ccc); +} + +static void __exit mptcp_ccc_unregister(void) +{ + tcp_unregister_congestion_control(&mptcp_ccc); +} + +module_init(mptcp_ccc_register); +module_exit(mptcp_ccc_unregister); + +MODULE_AUTHOR("Christoph Paasch, Sébastien Barré"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MPTCP LINKED INCREASE CONGESTION CONTROL ALGORITHM"); +MODULE_VERSION("0.1"); +#endif diff -urN linux-4.1.52-orig/net/mptcp/mptcp_ctrl.c linux-4.1.52/net/mptcp/mptcp_ctrl.c --- linux-4.1.52-orig/net/mptcp/mptcp_ctrl.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/mptcp/mptcp_ctrl.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,2690 @@ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +/* + * MPTCP implementation - MPTCP-control + * + * Initial Design & Implementation: + * Sébastien Barré + * + * Current Maintainer & Author: + * Christoph Paasch + * + * Additional authors: + * Jaakko Korkeaniemi + * Gregory Detal + * Fabien Duchêne + * Andreas Seelinger + * Lavkesh Lahngir + * Andreas Ripke + * Vlad Dogaru + * Octavian Purdila + * John Ronan + * Catalin Nicutar + * Brandon Heller + * + * + * 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 +#if IS_ENABLED(CONFIG_IPV6) +#include +#include +#endif +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct kmem_cache *mptcp_sock_cache __read_mostly; +static struct kmem_cache *mptcp_cb_cache __read_mostly; +static struct kmem_cache *mptcp_tw_cache __read_mostly; + +int sysctl_mptcp_enabled __read_mostly = 1; +int sysctl_mptcp_version __read_mostly = 0; +static int min_mptcp_version; +static int max_mptcp_version = 1; +int sysctl_mptcp_checksum __read_mostly = 1; +int sysctl_mptcp_debug __read_mostly; +EXPORT_SYMBOL(sysctl_mptcp_debug); +int sysctl_mptcp_syn_retries __read_mostly = 3; + +bool mptcp_init_failed __read_mostly; + +struct static_key mptcp_static_key = STATIC_KEY_INIT_FALSE; +EXPORT_SYMBOL(mptcp_static_key); + +static int proc_mptcp_path_manager(struct ctl_table *ctl, int write, + void __user *buffer, size_t *lenp, + loff_t *ppos) +{ + char val[MPTCP_PM_NAME_MAX]; + struct ctl_table tbl = { + .data = val, + .maxlen = MPTCP_PM_NAME_MAX, + }; + int ret; + + mptcp_get_default_path_manager(val); + + ret = proc_dostring(&tbl, write, buffer, lenp, ppos); + if (write && ret == 0) + ret = mptcp_set_default_path_manager(val); + return ret; +} + +static int proc_mptcp_scheduler(struct ctl_table *ctl, int write, + void __user *buffer, size_t *lenp, + loff_t *ppos) +{ + char val[MPTCP_SCHED_NAME_MAX]; + struct ctl_table tbl = { + .data = val, + .maxlen = MPTCP_SCHED_NAME_MAX, + }; + int ret; + + mptcp_get_default_scheduler(val); + + ret = proc_dostring(&tbl, write, buffer, lenp, ppos); + if (write && ret == 0) + ret = mptcp_set_default_scheduler(val); + return ret; +} + +static struct ctl_table mptcp_table[] = { + { + .procname = "mptcp_enabled", + .data = &sysctl_mptcp_enabled, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec + }, + { + .procname = "mptcp_version", + .data = &sysctl_mptcp_version, + .mode = 0644, + .maxlen = sizeof(int), + .proc_handler = &proc_dointvec_minmax, + .extra1 = &min_mptcp_version, + .extra2 = &max_mptcp_version, + }, + { + .procname = "mptcp_checksum", + .data = &sysctl_mptcp_checksum, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec + }, + { + .procname = "mptcp_debug", + .data = &sysctl_mptcp_debug, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec + }, + { + .procname = "mptcp_syn_retries", + .data = &sysctl_mptcp_syn_retries, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec + }, + { + .procname = "mptcp_path_manager", + .mode = 0644, + .maxlen = MPTCP_PM_NAME_MAX, + .proc_handler = proc_mptcp_path_manager, + }, + { + .procname = "mptcp_scheduler", + .mode = 0644, + .maxlen = MPTCP_SCHED_NAME_MAX, + .proc_handler = proc_mptcp_scheduler, + }, + { } +}; + +static inline u32 mptcp_hash_tk(u32 token) +{ + return token % MPTCP_HASH_SIZE; +} + +struct hlist_nulls_head tk_hashtable[MPTCP_HASH_SIZE]; +EXPORT_SYMBOL(tk_hashtable); + +/* This second hashtable is needed to retrieve request socks + * created as a result of a join request. While the SYN contains + * the token, the final ack does not, so we need a separate hashtable + * to retrieve the mpcb. + */ +struct hlist_nulls_head mptcp_reqsk_htb[MPTCP_HASH_SIZE]; +spinlock_t mptcp_reqsk_hlock; /* hashtable protection */ + +/* The following hash table is used to avoid collision of token */ +static struct hlist_nulls_head mptcp_reqsk_tk_htb[MPTCP_HASH_SIZE]; +spinlock_t mptcp_tk_hashlock; /* hashtable protection */ + +static bool mptcp_reqsk_find_tk(const u32 token) +{ + const u32 hash = mptcp_hash_tk(token); + const struct mptcp_request_sock *mtreqsk; + const struct hlist_nulls_node *node; + +begin: + hlist_nulls_for_each_entry_rcu(mtreqsk, node, + &mptcp_reqsk_tk_htb[hash], hash_entry) { + if (token == mtreqsk->mptcp_loc_token) + return true; + } + /* A request-socket is destroyed by RCU. So, it might have been recycled + * and put into another hash-table list. So, after the lookup we may + * end up in a different list. So, we may need to restart. + * + * See also the comment in __inet_lookup_established. + */ + if (get_nulls_value(node) != hash) + goto begin; + return false; +} + +static void mptcp_reqsk_insert_tk(struct request_sock *reqsk, const u32 token) +{ + u32 hash = mptcp_hash_tk(token); + + hlist_nulls_add_head_rcu(&mptcp_rsk(reqsk)->hash_entry, + &mptcp_reqsk_tk_htb[hash]); +} + +static void mptcp_reqsk_remove_tk(const struct request_sock *reqsk) +{ + rcu_read_lock(); + spin_lock(&mptcp_tk_hashlock); + hlist_nulls_del_init_rcu(&mptcp_rsk(reqsk)->hash_entry); + spin_unlock(&mptcp_tk_hashlock); + rcu_read_unlock(); +} + +void mptcp_reqsk_destructor(struct request_sock *req) +{ + if (!mptcp_rsk(req)->is_sub) { + if (in_softirq()) { + mptcp_reqsk_remove_tk(req); + } else { + rcu_read_lock_bh(); + spin_lock(&mptcp_tk_hashlock); + hlist_nulls_del_init_rcu(&mptcp_rsk(req)->hash_entry); + spin_unlock(&mptcp_tk_hashlock); + rcu_read_unlock_bh(); + } + } else { + mptcp_hash_request_remove(req); + } +} + +static void __mptcp_hash_insert(struct tcp_sock *meta_tp, const u32 token) +{ + u32 hash = mptcp_hash_tk(token); + hlist_nulls_add_head_rcu(&meta_tp->tk_table, &tk_hashtable[hash]); + meta_tp->inside_tk_table = 1; +} + +static bool mptcp_find_token(u32 token) +{ + const u32 hash = mptcp_hash_tk(token); + const struct tcp_sock *meta_tp; + const struct hlist_nulls_node *node; + +begin: + hlist_nulls_for_each_entry_rcu(meta_tp, node, &tk_hashtable[hash], tk_table) { + if (token == meta_tp->mptcp_loc_token) + return true; + } + /* A TCP-socket is destroyed by RCU. So, it might have been recycled + * and put into another hash-table list. So, after the lookup we may + * end up in a different list. So, we may need to restart. + * + * See also the comment in __inet_lookup_established. + */ + if (get_nulls_value(node) != hash) + goto begin; + return false; +} + +static void mptcp_set_key_reqsk(struct request_sock *req, + const struct sk_buff *skb, + u32 seed) +{ + const struct inet_request_sock *ireq = inet_rsk(req); + struct mptcp_request_sock *mtreq = mptcp_rsk(req); + + if (skb->protocol == htons(ETH_P_IP)) { + mtreq->mptcp_loc_key = mptcp_v4_get_key(ip_hdr(skb)->saddr, + ip_hdr(skb)->daddr, + htons(ireq->ir_num), + ireq->ir_rmt_port, + seed); +#if IS_ENABLED(CONFIG_IPV6) + } else { + mtreq->mptcp_loc_key = mptcp_v6_get_key(ipv6_hdr(skb)->saddr.s6_addr32, + ipv6_hdr(skb)->daddr.s6_addr32, + htons(ireq->ir_num), + ireq->ir_rmt_port, + seed); +#endif + } + + mptcp_key_sha1(mtreq->mptcp_loc_key, &mtreq->mptcp_loc_token, NULL); +} + +/* New MPTCP-connection request, prepare a new token for the meta-socket that + * will be created in mptcp_check_req_master(), and store the received token. + */ +static void mptcp_reqsk_new_mptcp(struct request_sock *req, + struct sock *sk, + const struct mptcp_options_received *mopt, + const struct sk_buff *skb) +{ + struct mptcp_request_sock *mtreq = mptcp_rsk(req); + struct tcp_sock *tp = tcp_sk(sk); + + inet_rsk(req)->saw_mpc = 1; + /* MPTCP version agreement */ + if (mopt->mptcp_ver >= tp->mptcp_ver) + mtreq->mptcp_ver = tp->mptcp_ver; + else + mtreq->mptcp_ver = mopt->mptcp_ver; + + rcu_read_lock(); + spin_lock(&mptcp_tk_hashlock); + do { + mptcp_set_key_reqsk(req, skb, mptcp_seed++); + } while (mptcp_reqsk_find_tk(mtreq->mptcp_loc_token) || + mptcp_find_token(mtreq->mptcp_loc_token)); + mptcp_reqsk_insert_tk(req, mtreq->mptcp_loc_token); + spin_unlock(&mptcp_tk_hashlock); + rcu_read_unlock(); + mtreq->mptcp_rem_key = mopt->mptcp_sender_key; +} + +static int mptcp_reqsk_new_cookie(struct request_sock *req, + const struct mptcp_options_received *mopt, + const struct sk_buff *skb) +{ + struct mptcp_request_sock *mtreq = mptcp_rsk(req); + + rcu_read_lock(); + spin_lock(&mptcp_tk_hashlock); + + mptcp_set_key_reqsk(req, skb, tcp_rsk(req)->snt_isn); + + if (mptcp_reqsk_find_tk(mtreq->mptcp_loc_token) || + mptcp_find_token(mtreq->mptcp_loc_token)) { + spin_unlock(&mptcp_tk_hashlock); + rcu_read_unlock(); + return false; + } + + inet_rsk(req)->saw_mpc = 1; + + spin_unlock(&mptcp_tk_hashlock); + rcu_read_unlock(); + + mtreq->mptcp_rem_key = mopt->mptcp_sender_key; + + return true; +} + +static void mptcp_set_key_sk(const struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + const struct inet_sock *isk = inet_sk(sk); + + if (sk->sk_family == AF_INET) + tp->mptcp_loc_key = mptcp_v4_get_key(isk->inet_saddr, + isk->inet_daddr, + isk->inet_sport, + isk->inet_dport, + mptcp_seed++); +#if IS_ENABLED(CONFIG_IPV6) + else + tp->mptcp_loc_key = mptcp_v6_get_key(inet6_sk(sk)->saddr.s6_addr32, + sk->sk_v6_daddr.s6_addr32, + isk->inet_sport, + isk->inet_dport, + mptcp_seed++); +#endif + + mptcp_key_sha1(tp->mptcp_loc_key, + &tp->mptcp_loc_token, NULL); +} + +#ifdef HAVE_JUMP_LABEL +/* We are not allowed to call static_key_slow_dec() from irq context + * If mptcp_enable/disable_static_key() is called from irq context, + * defer the static_key_slow_dec() calls. + */ +static atomic_t mptcp_enable_deferred; +#endif + +void mptcp_enable_static_key(void) +{ +#ifdef HAVE_JUMP_LABEL + int deferred; + + if (in_interrupt()) { + atomic_inc(&mptcp_enable_deferred); + return; + } + + deferred = atomic_xchg(&mptcp_enable_deferred, 0); + + if (deferred > 0) { + while (deferred--) + static_key_slow_inc(&mptcp_static_key); + } else if (deferred < 0) { + /* Do exactly one dec less than necessary */ + while (++deferred) + static_key_slow_dec(&mptcp_static_key); + return; + } +#endif + static_key_slow_inc(&mptcp_static_key); + WARN_ON(atomic_read(&mptcp_static_key.enabled) == 0); +} + +void mptcp_disable_static_key(void) +{ +#ifdef HAVE_JUMP_LABEL + int deferred; + + if (in_interrupt()) { + atomic_dec(&mptcp_enable_deferred); + return; + } + + deferred = atomic_xchg(&mptcp_enable_deferred, 0); + + if (deferred > 0) { + /* Do exactly one inc less than necessary */ + while (--deferred) + static_key_slow_inc(&mptcp_static_key); + return; + } else if (deferred < 0) { + while (deferred++) + static_key_slow_dec(&mptcp_static_key); + } +#endif + static_key_slow_dec(&mptcp_static_key); +} + +void mptcp_enable_sock(struct sock *sk) +{ + if (!sock_flag(sk, SOCK_MPTCP)) { + sock_set_flag(sk, SOCK_MPTCP); + tcp_sk(sk)->mptcp_ver = sysctl_mptcp_version; + + /* Necessary here, because MPTCP can be enabled/disabled through + * a setsockopt. + */ + if (sk->sk_family == AF_INET) + inet_csk(sk)->icsk_af_ops = &mptcp_v4_specific; +#if IS_ENABLED(CONFIG_IPV6) + else if (mptcp_v6_is_v4_mapped(sk)) + inet_csk(sk)->icsk_af_ops = &mptcp_v6_mapped; + else + inet_csk(sk)->icsk_af_ops = &mptcp_v6_specific; +#endif + + mptcp_enable_static_key(); + } +} + +void mptcp_disable_sock(struct sock *sk) +{ + if (sock_flag(sk, SOCK_MPTCP)) { + sock_reset_flag(sk, SOCK_MPTCP); + + /* Necessary here, because MPTCP can be enabled/disabled through + * a setsockopt. + */ + if (sk->sk_family == AF_INET) + inet_csk(sk)->icsk_af_ops = &ipv4_specific; +#if IS_ENABLED(CONFIG_IPV6) + else if (mptcp_v6_is_v4_mapped(sk)) + inet_csk(sk)->icsk_af_ops = &ipv6_mapped; + else + inet_csk(sk)->icsk_af_ops = &ipv6_specific; +#endif + + mptcp_disable_static_key(); + } +} + +void mptcp_connect_init(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + + rcu_read_lock_bh(); + spin_lock(&mptcp_tk_hashlock); + do { + mptcp_set_key_sk(sk); + } while (mptcp_reqsk_find_tk(tp->mptcp_loc_token) || + mptcp_find_token(tp->mptcp_loc_token)); + + __mptcp_hash_insert(tp, tp->mptcp_loc_token); + spin_unlock(&mptcp_tk_hashlock); + rcu_read_unlock_bh(); + + MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_MPCAPABLEACTIVE); +} + +/** + * This function increments the refcount of the mpcb struct. + * It is the responsibility of the caller to decrement when releasing + * the structure. + */ +struct sock *mptcp_hash_find(const struct net *net, const u32 token) +{ + const u32 hash = mptcp_hash_tk(token); + const struct tcp_sock *meta_tp; + struct sock *meta_sk = NULL; + const struct hlist_nulls_node *node; + + rcu_read_lock(); +begin: + hlist_nulls_for_each_entry_rcu(meta_tp, node, &tk_hashtable[hash], + tk_table) { + meta_sk = (struct sock *)meta_tp; + if (token == meta_tp->mptcp_loc_token && + net_eq(net, sock_net(meta_sk))) { + if (unlikely(!atomic_inc_not_zero(&meta_sk->sk_refcnt))) + goto out; + if (unlikely(token != meta_tp->mptcp_loc_token || + !net_eq(net, sock_net(meta_sk)))) { + sock_gen_put(meta_sk); + goto begin; + } + goto found; + } + } + /* A TCP-socket is destroyed by RCU. So, it might have been recycled + * and put into another hash-table list. So, after the lookup we may + * end up in a different list. So, we may need to restart. + * + * See also the comment in __inet_lookup_established. + */ + if (get_nulls_value(node) != hash) + goto begin; +out: + meta_sk = NULL; +found: + rcu_read_unlock(); + return meta_sk; +} + +void mptcp_hash_remove_bh(struct tcp_sock *meta_tp) +{ + /* remove from the token hashtable */ + rcu_read_lock_bh(); + spin_lock(&mptcp_tk_hashlock); + hlist_nulls_del_init_rcu(&meta_tp->tk_table); + meta_tp->inside_tk_table = 0; + spin_unlock(&mptcp_tk_hashlock); + rcu_read_unlock_bh(); +} + +void mptcp_hash_remove(struct tcp_sock *meta_tp) +{ + rcu_read_lock(); + spin_lock(&mptcp_tk_hashlock); + hlist_nulls_del_init_rcu(&meta_tp->tk_table); + meta_tp->inside_tk_table = 0; + spin_unlock(&mptcp_tk_hashlock); + rcu_read_unlock(); +} + +struct sock *mptcp_select_ack_sock(const struct sock *meta_sk) +{ + const struct tcp_sock *meta_tp = tcp_sk(meta_sk); + struct sock *sk, *rttsk = NULL, *lastsk = NULL; + u32 min_time = 0, last_active = 0; + + mptcp_for_each_sk(meta_tp->mpcb, sk) { + struct tcp_sock *tp = tcp_sk(sk); + u32 elapsed; + + if (!mptcp_sk_can_send_ack(sk) || tp->pf) + continue; + + elapsed = keepalive_time_elapsed(tp); + + /* We take the one with the lowest RTT within a reasonable + * (meta-RTO)-timeframe + */ + if (elapsed < inet_csk(meta_sk)->icsk_rto) { + if (!min_time || tp->srtt_us < min_time) { + min_time = tp->srtt_us; + rttsk = sk; + } + continue; + } + + /* Otherwise, we just take the most recent active */ + if (!rttsk && (!last_active || elapsed < last_active)) { + last_active = elapsed; + lastsk = sk; + } + } + + if (rttsk) + return rttsk; + + return lastsk; +} +EXPORT_SYMBOL(mptcp_select_ack_sock); + +static void mptcp_sock_def_error_report(struct sock *sk) +{ + const struct mptcp_cb *mpcb = tcp_sk(sk)->mpcb; + + if (!sock_flag(sk, SOCK_DEAD)) + mptcp_sub_close(sk, 0); + + if (mpcb->infinite_mapping_rcv || mpcb->infinite_mapping_snd || + mpcb->send_infinite_mapping) { + struct sock *meta_sk = mptcp_meta_sk(sk); + + meta_sk->sk_err = sk->sk_err; + meta_sk->sk_err_soft = sk->sk_err_soft; + + if (!sock_flag(meta_sk, SOCK_DEAD)) + meta_sk->sk_error_report(meta_sk); + + tcp_done(meta_sk); + } + + sk->sk_err = 0; + return; +} + +static void mptcp_mpcb_put(struct mptcp_cb *mpcb) +{ + if (atomic_dec_and_test(&mpcb->mpcb_refcnt)) { + mptcp_cleanup_path_manager(mpcb); + mptcp_cleanup_scheduler(mpcb); + kmem_cache_free(mptcp_cb_cache, mpcb); + } +} + +void mptcp_sock_destruct(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + + if (!is_meta_sk(sk) && !tp->was_meta_sk) { + BUG_ON(!hlist_unhashed(&tp->mptcp->cb_list)); + + kmem_cache_free(mptcp_sock_cache, tp->mptcp); + tp->mptcp = NULL; + + /* Taken when mpcb pointer was set */ + sock_put(mptcp_meta_sk(sk)); + mptcp_mpcb_put(tp->mpcb); + } else { + struct mptcp_cb *mpcb = tp->mpcb; + struct mptcp_tw *mptw; + + /* The mpcb is disappearing - we can make the final + * update to the rcv_nxt of the time-wait-sock and remove + * its reference to the mpcb. + */ + spin_lock_bh(&mpcb->tw_lock); + list_for_each_entry_rcu(mptw, &mpcb->tw_list, list) { + list_del_rcu(&mptw->list); + mptw->in_list = 0; + mptcp_mpcb_put(mpcb); + rcu_assign_pointer(mptw->mpcb, NULL); + } + spin_unlock_bh(&mpcb->tw_lock); + + mptcp_mpcb_put(mpcb); + + mptcp_debug("%s destroying meta-sk\n", __func__); + } + + WARN_ON(!static_key_false(&mptcp_static_key)); + + /* Must be called here, because this will decrement the jump-label. */ + inet_sock_destruct(sk); +} + +void mptcp_destroy_sock(struct sock *sk) +{ + if (is_meta_sk(sk)) { + struct sock *sk_it, *tmpsk; + + __skb_queue_purge(&tcp_sk(sk)->mpcb->reinject_queue); + mptcp_purge_ofo_queue(tcp_sk(sk)); + + /* We have to close all remaining subflows. Normally, they + * should all be about to get closed. But, if the kernel is + * forcing a closure (e.g., tcp_write_err), the subflows might + * not have been closed properly (as we are waiting for the + * DATA_ACK of the DATA_FIN). + */ + mptcp_for_each_sk_safe(tcp_sk(sk)->mpcb, sk_it, tmpsk) { + /* Already did call tcp_close - waiting for graceful + * closure, or if we are retransmitting fast-close on + * the subflow. The reset (or timeout) will kill the + * subflow.. + */ + if (tcp_sk(sk_it)->closing || + tcp_sk(sk_it)->send_mp_fclose) + continue; + + /* Allow the delayed work first to prevent time-wait state */ + if (delayed_work_pending(&tcp_sk(sk_it)->mptcp->work)) + continue; + + mptcp_sub_close(sk_it, 0); + } + } else { + mptcp_del_sock(sk); + } +} + +static void mptcp_set_state(struct sock *sk) +{ + struct sock *meta_sk = mptcp_meta_sk(sk); + + /* Meta is not yet established - wake up the application */ + if ((1 << meta_sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV) && + sk->sk_state == TCP_ESTABLISHED) { + tcp_set_state(meta_sk, TCP_ESTABLISHED); + + if (!sock_flag(meta_sk, SOCK_DEAD)) { + meta_sk->sk_state_change(meta_sk); + sk_wake_async(meta_sk, SOCK_WAKE_IO, POLL_OUT); + } + } + + if (sk->sk_state == TCP_ESTABLISHED) { + tcp_sk(sk)->mptcp->establish_increased = 1; + tcp_sk(sk)->mpcb->cnt_established++; + } +} + +static void mptcp_assign_congestion_control(struct sock *sk) +{ + struct inet_connection_sock *icsk = inet_csk(sk); + struct inet_connection_sock *meta_icsk = inet_csk(mptcp_meta_sk(sk)); + const struct tcp_congestion_ops *ca = meta_icsk->icsk_ca_ops; + + /* Congestion control is the same as meta. Thus, it has been + * try_module_get'd by tcp_assign_congestion_control. + */ + if (icsk->icsk_ca_ops == ca) + return; + + /* Use the same congestion control as set on the meta-sk */ + if (!try_module_get(ca->owner)) { + /* This should never happen. The congestion control is linked + * to the meta-socket (through tcp_assign_congestion_control) + * who "holds" the refcnt on the module. + */ + WARN(1, "Could not get the congestion control!"); + return; + } + icsk->icsk_ca_ops = ca; + + /* Clear out private data before diag gets it and + * the ca has not been initialized. + */ + if (ca->get_info) + memset(icsk->icsk_ca_priv, 0, sizeof(icsk->icsk_ca_priv)); + + return; +} + +u32 mptcp_secret[MD5_MESSAGE_BYTES / 4] ____cacheline_aligned; +u32 mptcp_seed = 0; + +void mptcp_key_sha1(u64 key, u32 *token, u64 *idsn) +{ + u32 workspace[SHA_WORKSPACE_WORDS]; + u32 mptcp_hashed_key[SHA_DIGEST_WORDS]; + u8 input[64]; + int i; + + memset(workspace, 0, sizeof(workspace)); + + /* Initialize input with appropriate padding */ + memset(&input[9], 0, sizeof(input) - 10); /* -10, because the last byte + * is explicitly set too + */ + memcpy(input, &key, sizeof(key)); /* Copy key to the msg beginning */ + input[8] = 0x80; /* Padding: First bit after message = 1 */ + input[63] = 0x40; /* Padding: Length of the message = 64 bits */ + + sha_init(mptcp_hashed_key); + sha_transform(mptcp_hashed_key, input, workspace); + + for (i = 0; i < 5; i++) + mptcp_hashed_key[i] = cpu_to_be32(mptcp_hashed_key[i]); + + if (token) + *token = mptcp_hashed_key[0]; + if (idsn) + *idsn = *((u64 *)&mptcp_hashed_key[3]); +} + +void mptcp_hmac_sha1(u8 *key_1, u8 *key_2, u32 *hash_out, int arg_num, ...) +{ + u32 workspace[SHA_WORKSPACE_WORDS]; + u8 input[128]; /* 2 512-bit blocks */ + int i; + int index; + int length; + u8 *msg; + va_list list; + + memset(workspace, 0, sizeof(workspace)); + + /* Generate key xored with ipad */ + memset(input, 0x36, 64); + for (i = 0; i < 8; i++) + input[i] ^= key_1[i]; + for (i = 0; i < 8; i++) + input[i + 8] ^= key_2[i]; + + va_start(list, arg_num); + index = 64; + for (i = 0; i < arg_num; i++) { + length = va_arg(list, int); + msg = va_arg(list, u8 *); + BUG_ON(index + length > 125); /* Message is too long */ + memcpy(&input[index], msg, length); + index += length; + } + va_end(list); + + input[index] = 0x80; /* Padding: First bit after message = 1 */ + memset(&input[index + 1], 0, (126 - index)); + + /* Padding: Length of the message = 512 + message length (bits) */ + input[126] = 0x02; + input[127] = ((index - 64) * 8); /* Message length (bits) */ + + sha_init(hash_out); + sha_transform(hash_out, input, workspace); + memset(workspace, 0, sizeof(workspace)); + + sha_transform(hash_out, &input[64], workspace); + memset(workspace, 0, sizeof(workspace)); + + for (i = 0; i < 5; i++) + hash_out[i] = cpu_to_be32(hash_out[i]); + + /* Prepare second part of hmac */ + memset(input, 0x5C, 64); + for (i = 0; i < 8; i++) + input[i] ^= key_1[i]; + for (i = 0; i < 8; i++) + input[i + 8] ^= key_2[i]; + + memcpy(&input[64], hash_out, 20); + input[84] = 0x80; + memset(&input[85], 0, 41); + + /* Padding: Length of the message = 512 + 160 bits */ + input[126] = 0x02; + input[127] = 0xA0; + + sha_init(hash_out); + sha_transform(hash_out, input, workspace); + memset(workspace, 0, sizeof(workspace)); + + sha_transform(hash_out, &input[64], workspace); + + for (i = 0; i < 5; i++) + hash_out[i] = cpu_to_be32(hash_out[i]); +} +EXPORT_SYMBOL(mptcp_hmac_sha1); + +static void mptcp_mpcb_inherit_sockopts(struct sock *meta_sk, struct sock *master_sk) +{ + /* Socket-options handled by sk_clone_lock while creating the meta-sk. + * ====== + * SO_SNDBUF, SO_SNDBUFFORCE, SO_RCVBUF, SO_RCVBUFFORCE, SO_RCVLOWAT, + * SO_RCVTIMEO, SO_SNDTIMEO, SO_ATTACH_FILTER, SO_DETACH_FILTER, + * TCP_NODELAY, TCP_CORK + * + * Socket-options handled in this function here + * ====== + * TCP_DEFER_ACCEPT + * SO_KEEPALIVE + * + * Socket-options on the todo-list + * ====== + * SO_BINDTODEVICE - should probably prevent creation of new subsocks + * across other devices. - what about the api-draft? + * SO_DEBUG + * SO_REUSEADDR - probably we don't care about this + * SO_DONTROUTE, SO_BROADCAST + * SO_OOBINLINE + * SO_LINGER + * SO_TIMESTAMP* - I don't think this is of concern for a SOCK_STREAM + * SO_PASSSEC - I don't think this is of concern for a SOCK_STREAM + * SO_RXQ_OVFL + * TCP_COOKIE_TRANSACTIONS + * TCP_MAXSEG + * TCP_THIN_* - Handled by sk_clone_lock, but we need to support this + * in mptcp_meta_retransmit_timer. AND we need to check + * what is about the subsockets. + * TCP_LINGER2 + * TCP_WINDOW_CLAMP + * TCP_USER_TIMEOUT + * TCP_MD5SIG + * + * Socket-options of no concern for the meta-socket (but for the subsocket) + * ====== + * SO_PRIORITY + * SO_MARK + * TCP_CONGESTION + * TCP_SYNCNT + * TCP_QUICKACK + */ + + /* DEFER_ACCEPT should not be set on the meta, as we want to accept new subflows directly */ + inet_csk(meta_sk)->icsk_accept_queue.rskq_defer_accept = 0; + + /* Keepalives are handled entirely at the MPTCP-layer */ + if (sock_flag(meta_sk, SOCK_KEEPOPEN)) { + inet_csk_reset_keepalive_timer(meta_sk, + keepalive_time_when(tcp_sk(meta_sk))); + sock_reset_flag(master_sk, SOCK_KEEPOPEN); + inet_csk_delete_keepalive_timer(master_sk); + } + + /* Do not propagate subflow-errors up to the MPTCP-layer */ + inet_sk(master_sk)->recverr = 0; +} + +static void mptcp_sub_inherit_sockopts(const struct sock *meta_sk, struct sock *sub_sk) +{ + /* IP_TOS also goes to the subflow. */ + if (inet_sk(sub_sk)->tos != inet_sk(meta_sk)->tos) { + inet_sk(sub_sk)->tos = inet_sk(meta_sk)->tos; + sub_sk->sk_priority = meta_sk->sk_priority; + sk_dst_reset(sub_sk); + } + + /* Inherit SO_REUSEADDR */ + sub_sk->sk_reuse = meta_sk->sk_reuse; + + /* Inherit snd/rcv-buffer locks */ + sub_sk->sk_userlocks = meta_sk->sk_userlocks & ~SOCK_BINDPORT_LOCK; + + /* Nagle/Cork is forced off on the subflows. It is handled at the meta-layer */ + tcp_sk(sub_sk)->nonagle = TCP_NAGLE_OFF|TCP_NAGLE_PUSH; + + /* Keepalives are handled entirely at the MPTCP-layer */ + if (sock_flag(sub_sk, SOCK_KEEPOPEN)) { + sock_reset_flag(sub_sk, SOCK_KEEPOPEN); + inet_csk_delete_keepalive_timer(sub_sk); + } + + /* Do not propagate subflow-errors up to the MPTCP-layer */ + inet_sk(sub_sk)->recverr = 0; +} + +int mptcp_backlog_rcv(struct sock *meta_sk, struct sk_buff *skb) +{ + /* skb-sk may be NULL if we receive a packet immediatly after the + * SYN/ACK + MP_CAPABLE. + */ + struct sock *sk = skb->sk ? skb->sk : meta_sk; + int ret = 0; + + skb->sk = NULL; + + if (unlikely(!atomic_inc_not_zero(&sk->sk_refcnt))) { + kfree_skb(skb); + return 0; + } + + if (sk->sk_family == AF_INET) + ret = tcp_v4_do_rcv(sk, skb); +#if IS_ENABLED(CONFIG_IPV6) + else + ret = tcp_v6_do_rcv(sk, skb); +#endif + + sock_put(sk); + return ret; +} + +struct lock_class_key meta_key; +struct lock_class_key meta_slock_key; + +static const struct tcp_sock_ops mptcp_meta_specific = { + .__select_window = __mptcp_select_window, + .select_window = mptcp_select_window, + .select_initial_window = mptcp_select_initial_window, + .select_size = mptcp_select_size, + .init_buffer_space = mptcp_init_buffer_space, + .set_rto = mptcp_tcp_set_rto, + .should_expand_sndbuf = mptcp_should_expand_sndbuf, + .send_fin = mptcp_send_fin, + .write_xmit = mptcp_write_xmit, + .send_active_reset = mptcp_send_active_reset, + .write_wakeup = mptcp_write_wakeup, + .prune_ofo_queue = mptcp_prune_ofo_queue, + .retransmit_timer = mptcp_meta_retransmit_timer, + .time_wait = mptcp_time_wait, + .cleanup_rbuf = mptcp_cleanup_rbuf, +}; + +static const struct tcp_sock_ops mptcp_sub_specific = { + .__select_window = __mptcp_select_window, + .select_window = mptcp_select_window, + .select_initial_window = mptcp_select_initial_window, + .select_size = mptcp_select_size, + .init_buffer_space = mptcp_init_buffer_space, + .set_rto = mptcp_tcp_set_rto, + .should_expand_sndbuf = mptcp_should_expand_sndbuf, + .send_fin = tcp_send_fin, + .write_xmit = tcp_write_xmit, + .send_active_reset = tcp_send_active_reset, + .write_wakeup = tcp_write_wakeup, + .prune_ofo_queue = tcp_prune_ofo_queue, + .retransmit_timer = mptcp_sub_retransmit_timer, + .time_wait = tcp_time_wait, + .cleanup_rbuf = tcp_cleanup_rbuf, +}; + +static int mptcp_alloc_mpcb(struct sock *meta_sk, __u64 remote_key, + __u8 mptcp_ver, u32 window) +{ + struct mptcp_cb *mpcb; + struct sock *master_sk; + struct inet_connection_sock *meta_icsk = inet_csk(meta_sk); + struct tcp_sock *master_tp, *meta_tp = tcp_sk(meta_sk); + u64 idsn; + + dst_release(meta_sk->sk_rx_dst); + meta_sk->sk_rx_dst = NULL; + /* This flag is set to announce sock_lock_init to + * reclassify the lock-class of the master socket. + */ + meta_tp->is_master_sk = 1; + master_sk = sk_clone_lock(meta_sk, GFP_ATOMIC | __GFP_ZERO); + meta_tp->is_master_sk = 0; + if (!master_sk) + return -ENOBUFS; + + master_tp = tcp_sk(master_sk); + + mpcb = kmem_cache_zalloc(mptcp_cb_cache, GFP_ATOMIC); + if (!mpcb) { + /* sk_free (and __sk_free) requirese wmem_alloc to be 1. + * All the rest is set to 0 thanks to __GFP_ZERO above. + */ + atomic_set(&master_sk->sk_wmem_alloc, 1); + sk_free(master_sk); + return -ENOBUFS; + } + +#if IS_ENABLED(CONFIG_IPV6) + if (meta_icsk->icsk_af_ops == &mptcp_v6_mapped) { + struct ipv6_pinfo *newnp, *np = inet6_sk(meta_sk); + + inet_sk(master_sk)->pinet6 = &((struct tcp6_sock *)master_sk)->inet6; + + newnp = inet6_sk(master_sk); + memcpy(newnp, np, sizeof(struct ipv6_pinfo)); + + newnp->ipv6_mc_list = NULL; + newnp->ipv6_ac_list = NULL; + newnp->ipv6_fl_list = NULL; + newnp->opt = NULL; + newnp->pktoptions = NULL; + (void)xchg(&newnp->rxpmtu, NULL); + } else if (meta_sk->sk_family == AF_INET6) { + struct ipv6_pinfo *newnp, *np = inet6_sk(meta_sk); + + inet_sk(master_sk)->pinet6 = &((struct tcp6_sock *)master_sk)->inet6; + + newnp = inet6_sk(master_sk); + memcpy(newnp, np, sizeof(struct ipv6_pinfo)); + + newnp->hop_limit = -1; + newnp->mcast_hops = IPV6_DEFAULT_MCASTHOPS; + newnp->mc_loop = 1; + newnp->pmtudisc = IPV6_PMTUDISC_WANT; + master_sk->sk_ipv6only = sock_net(master_sk)->ipv6.sysctl.bindv6only; + } +#endif + + meta_tp->mptcp = NULL; + + /* Store the mptcp version agreed on initial handshake */ + mpcb->mptcp_ver = mptcp_ver; + + /* Store the keys and generate the peer's token */ + mpcb->mptcp_loc_key = meta_tp->mptcp_loc_key; + mpcb->mptcp_loc_token = meta_tp->mptcp_loc_token; + + /* Generate Initial data-sequence-numbers */ + mptcp_key_sha1(mpcb->mptcp_loc_key, NULL, &idsn); + idsn = ntohll(idsn) + 1; + mpcb->snd_high_order[0] = idsn >> 32; + mpcb->snd_high_order[1] = mpcb->snd_high_order[0] - 1; + + meta_tp->write_seq = (u32)idsn; + meta_tp->snd_sml = meta_tp->write_seq; + meta_tp->snd_una = meta_tp->write_seq; + meta_tp->snd_nxt = meta_tp->write_seq; + meta_tp->pushed_seq = meta_tp->write_seq; + meta_tp->snd_up = meta_tp->write_seq; + + mpcb->mptcp_rem_key = remote_key; + mptcp_key_sha1(mpcb->mptcp_rem_key, &mpcb->mptcp_rem_token, &idsn); + idsn = ntohll(idsn) + 1; + mpcb->rcv_high_order[0] = idsn >> 32; + mpcb->rcv_high_order[1] = mpcb->rcv_high_order[0] + 1; + meta_tp->copied_seq = (u32) idsn; + meta_tp->rcv_nxt = (u32) idsn; + meta_tp->rcv_wup = (u32) idsn; + + meta_tp->snd_wl1 = meta_tp->rcv_nxt - 1; + meta_tp->snd_wnd = window; + meta_tp->retrans_stamp = 0; /* Set in tcp_connect() */ + + meta_tp->packets_out = 0; + meta_icsk->icsk_probes_out = 0; + + /* Set mptcp-pointers */ + master_tp->mpcb = mpcb; + master_tp->meta_sk = meta_sk; + meta_tp->mpcb = mpcb; + meta_tp->meta_sk = meta_sk; + mpcb->meta_sk = meta_sk; + mpcb->master_sk = master_sk; + + meta_tp->was_meta_sk = 0; + + /* Initialize the queues */ + skb_queue_head_init(&mpcb->reinject_queue); + skb_queue_head_init(&master_tp->out_of_order_queue); + tcp_prequeue_init(master_tp); + +#if !defined(CONFIG_BCM_KF_TCP_NO_TSQ) + INIT_LIST_HEAD(&master_tp->tsq_node); +#endif + + master_tp->tsq_flags = 0; + + mutex_init(&mpcb->mpcb_mutex); + + /* Init the accept_queue structure, we support a queue of 32 pending + * connections, it does not need to be huge, since we only store here + * pending subflow creations. + */ + if (reqsk_queue_alloc(&meta_icsk->icsk_accept_queue, 32, GFP_ATOMIC)) { + inet_put_port(master_sk); + kmem_cache_free(mptcp_cb_cache, mpcb); + sk_free(master_sk); + return -ENOMEM; + } + + if (!sock_flag(meta_sk, SOCK_MPTCP)) { + mptcp_enable_static_key(); + sock_set_flag(meta_sk, SOCK_MPTCP); + } + + /* Redefine function-pointers as the meta-sk is now fully ready */ + meta_tp->mpc = 1; + meta_tp->ops = &mptcp_meta_specific; + + meta_sk->sk_backlog_rcv = mptcp_backlog_rcv; + meta_sk->sk_destruct = mptcp_sock_destruct; + + /* Meta-level retransmit timer */ + meta_icsk->icsk_rto *= 2; /* Double of initial - rto */ + + tcp_init_xmit_timers(master_sk); + /* Has been set for sending out the SYN */ + inet_csk_clear_xmit_timer(meta_sk, ICSK_TIME_RETRANS); + + if (!meta_tp->inside_tk_table) { + /* Adding the meta_tp in the token hashtable - coming from server-side */ + rcu_read_lock(); + spin_lock(&mptcp_tk_hashlock); + + __mptcp_hash_insert(meta_tp, mpcb->mptcp_loc_token); + + spin_unlock(&mptcp_tk_hashlock); + rcu_read_unlock(); + } + master_tp->inside_tk_table = 0; + + /* Init time-wait stuff */ + INIT_LIST_HEAD(&mpcb->tw_list); + spin_lock_init(&mpcb->tw_lock); + + INIT_HLIST_HEAD(&mpcb->callback_list); + + mptcp_mpcb_inherit_sockopts(meta_sk, master_sk); + + mpcb->orig_sk_rcvbuf = meta_sk->sk_rcvbuf; + mpcb->orig_sk_sndbuf = meta_sk->sk_sndbuf; + mpcb->orig_window_clamp = meta_tp->window_clamp; + + /* The meta is directly linked - set refcnt to 1 */ + atomic_set(&mpcb->mpcb_refcnt, 1); + + mptcp_init_path_manager(mpcb); + mptcp_init_scheduler(mpcb); + + if (!try_module_get(inet_csk(master_sk)->icsk_ca_ops->owner)) + tcp_assign_congestion_control(master_sk); + + + mptcp_debug("%s: created mpcb with token %#x\n", + __func__, mpcb->mptcp_loc_token); + + return 0; +} + +void mptcp_fallback_meta_sk(struct sock *meta_sk) +{ + kfree(inet_csk(meta_sk)->icsk_accept_queue.listen_opt); + kmem_cache_free(mptcp_cb_cache, tcp_sk(meta_sk)->mpcb); +} + +int mptcp_add_sock(struct sock *meta_sk, struct sock *sk, u8 loc_id, u8 rem_id, + gfp_t flags) +{ + struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; + struct tcp_sock *tp = tcp_sk(sk); + + tp->mptcp = kmem_cache_zalloc(mptcp_sock_cache, flags); + if (!tp->mptcp) + return -ENOMEM; + + tp->mptcp->path_index = mptcp_set_new_pathindex(mpcb); + /* No more space for more subflows? */ + if (!tp->mptcp->path_index) { + kmem_cache_free(mptcp_sock_cache, tp->mptcp); + return -EPERM; + } + + INIT_HLIST_NODE(&tp->mptcp->cb_list); + + tp->mptcp->tp = tp; + tp->mpcb = mpcb; + tp->meta_sk = meta_sk; + + if (!sock_flag(sk, SOCK_MPTCP)) { + mptcp_enable_static_key(); + sock_set_flag(sk, SOCK_MPTCP); + } + + tp->mpc = 1; + tp->ops = &mptcp_sub_specific; + + tp->mptcp->loc_id = loc_id; + tp->mptcp->rem_id = rem_id; + if (mpcb->sched_ops->init) + mpcb->sched_ops->init(sk); + + /* The corresponding sock_put is in mptcp_sock_destruct(). It cannot be + * included in mptcp_del_sock(), because the mpcb must remain alive + * until the last subsocket is completely destroyed. + */ + sock_hold(meta_sk); + atomic_inc(&mpcb->mpcb_refcnt); + + tp->mptcp->next = mpcb->connection_list; + mpcb->connection_list = tp; + tp->mptcp->attached = 1; + + mpcb->cnt_subflows++; + atomic_add(atomic_read(&((struct sock *)tp)->sk_rmem_alloc), + &meta_sk->sk_rmem_alloc); + + mptcp_sub_inherit_sockopts(meta_sk, sk); + INIT_DELAYED_WORK(&tp->mptcp->work, mptcp_sub_close_wq); + + /* Properly inherit CC from the meta-socket */ + mptcp_assign_congestion_control(sk); + + /* As we successfully allocated the mptcp_tcp_sock, we have to + * change the function-pointers here (for sk_destruct to work correctly) + */ + sk->sk_error_report = mptcp_sock_def_error_report; + sk->sk_data_ready = mptcp_data_ready; + sk->sk_write_space = mptcp_write_space; + sk->sk_state_change = mptcp_set_state; + sk->sk_destruct = mptcp_sock_destruct; + + if (sk->sk_family == AF_INET) + mptcp_debug("%s: token %#x pi %d, src_addr:%pI4:%d dst_addr:%pI4:%d, cnt_subflows now %d\n", + __func__ , mpcb->mptcp_loc_token, + tp->mptcp->path_index, + &((struct inet_sock *)tp)->inet_saddr, + ntohs(((struct inet_sock *)tp)->inet_sport), + &((struct inet_sock *)tp)->inet_daddr, + ntohs(((struct inet_sock *)tp)->inet_dport), + mpcb->cnt_subflows); +#if IS_ENABLED(CONFIG_IPV6) + else + mptcp_debug("%s: token %#x pi %d, src_addr:%pI6:%d dst_addr:%pI6:%d, cnt_subflows now %d\n", + __func__ , mpcb->mptcp_loc_token, + tp->mptcp->path_index, &inet6_sk(sk)->saddr, + ntohs(((struct inet_sock *)tp)->inet_sport), + &sk->sk_v6_daddr, + ntohs(((struct inet_sock *)tp)->inet_dport), + mpcb->cnt_subflows); +#endif + + return 0; +} + +void mptcp_del_sock(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk), *tp_prev; + struct mptcp_cb *mpcb; + + if (!tp->mptcp || !tp->mptcp->attached) + return; + + mpcb = tp->mpcb; + tp_prev = mpcb->connection_list; + + if (mpcb->sched_ops->release) + mpcb->sched_ops->release(sk); + + mptcp_debug("%s: Removing subsock tok %#x pi:%d state %d is_meta? %d\n", + __func__, mpcb->mptcp_loc_token, tp->mptcp->path_index, + sk->sk_state, is_meta_sk(sk)); + + if (tp_prev == tp) { + mpcb->connection_list = tp->mptcp->next; + } else { + for (; tp_prev && tp_prev->mptcp->next; tp_prev = tp_prev->mptcp->next) { + if (tp_prev->mptcp->next == tp) { + tp_prev->mptcp->next = tp->mptcp->next; + break; + } + } + } + mpcb->cnt_subflows--; + if (tp->mptcp->establish_increased) + mpcb->cnt_established--; + + tp->mptcp->next = NULL; + tp->mptcp->attached = 0; + mpcb->path_index_bits &= ~(1 << tp->mptcp->path_index); + + if (!skb_queue_empty(&sk->sk_write_queue)) + mptcp_reinject_data(sk, 0); + + if (is_master_tp(tp)) + mpcb->master_sk = NULL; + else if (tp->mptcp->pre_established) + sk_stop_timer(sk, &tp->mptcp->mptcp_ack_timer); + + rcu_assign_pointer(inet_sk(sk)->inet_opt, NULL); +} + +/* Updates the MPTCP-session based on path-manager information (e.g., addresses, + * low-prio flows,...). + */ +void mptcp_update_metasocket(struct sock *sk, const struct sock *meta_sk) +{ + if (tcp_sk(sk)->mpcb->pm_ops->new_session) + tcp_sk(sk)->mpcb->pm_ops->new_session(meta_sk); +} + +/* Clean up the receive buffer for full frames taken by the user, + * then send an ACK if necessary. COPIED is the number of bytes + * tcp_recvmsg has given to the user so far, it speeds up the + * calculation of whether or not we must ACK for the sake of + * a window update. + */ +void mptcp_cleanup_rbuf(struct sock *meta_sk, int copied) +{ + struct tcp_sock *meta_tp = tcp_sk(meta_sk); + struct sock *sk; + __u32 rcv_window_now = 0; + + if (copied > 0 && !(meta_sk->sk_shutdown & RCV_SHUTDOWN)) { + rcv_window_now = tcp_receive_window(meta_tp); + + if (2 * rcv_window_now > meta_tp->window_clamp) + rcv_window_now = 0; + } + + mptcp_for_each_sk(meta_tp->mpcb, sk) { + struct tcp_sock *tp = tcp_sk(sk); + const struct inet_connection_sock *icsk = inet_csk(sk); + + if (!mptcp_sk_can_send_ack(sk)) + continue; + + if (!inet_csk_ack_scheduled(sk)) + goto second_part; + /* Delayed ACKs frequently hit locked sockets during bulk + * receive. + */ + if (icsk->icsk_ack.blocked || + /* Once-per-two-segments ACK was not sent by tcp_input.c */ + tp->rcv_nxt - tp->rcv_wup > icsk->icsk_ack.rcv_mss || + /* If this read emptied read buffer, we send ACK, if + * connection is not bidirectional, user drained + * receive buffer and there was a small segment + * in queue. + */ + (copied > 0 && + ((icsk->icsk_ack.pending & ICSK_ACK_PUSHED2) || + ((icsk->icsk_ack.pending & ICSK_ACK_PUSHED) && + !icsk->icsk_ack.pingpong)) && + !atomic_read(&meta_sk->sk_rmem_alloc))) { + tcp_send_ack(sk); + continue; + } + +second_part: + /* This here is the second part of tcp_cleanup_rbuf */ + if (rcv_window_now) { + __u32 new_window = tp->ops->__select_window(sk); + + /* Send ACK now, if this read freed lots of space + * in our buffer. Certainly, new_window is new window. + * We can advertise it now, if it is not less than + * current one. + * "Lots" means "at least twice" here. + */ + if (new_window && new_window >= 2 * rcv_window_now) + tcp_send_ack(sk); + } + } +} + +static int mptcp_sub_send_fin(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct sk_buff *skb = tcp_write_queue_tail(sk); + int mss_now; + + /* Optimization, tack on the FIN if we have a queue of + * unsent frames. But be careful about outgoing SACKS + * and IP options. + */ + mss_now = tcp_current_mss(sk); + + if (tcp_send_head(sk) != NULL) { + TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_FIN; + TCP_SKB_CB(skb)->end_seq++; + tp->write_seq++; + } else { + skb = alloc_skb_fclone(MAX_TCP_HEADER, GFP_ATOMIC); + if (!skb) + return 1; + + /* Reserve space for headers and prepare control bits. */ + skb_reserve(skb, MAX_TCP_HEADER); + /* FIN eats a sequence byte, write_seq advanced by tcp_queue_skb(). */ + tcp_init_nondata_skb(skb, tp->write_seq, + TCPHDR_ACK | TCPHDR_FIN); + tcp_queue_skb(sk, skb); + } + __tcp_push_pending_frames(sk, mss_now, TCP_NAGLE_OFF); + + return 0; +} + +void mptcp_sub_close_wq(struct work_struct *work) +{ + struct tcp_sock *tp = container_of(work, struct mptcp_tcp_sock, work.work)->tp; + struct sock *sk = (struct sock *)tp; + struct sock *meta_sk = mptcp_meta_sk(sk); + + mutex_lock(&tp->mpcb->mpcb_mutex); + lock_sock_nested(meta_sk, SINGLE_DEPTH_NESTING); + + if (sock_flag(sk, SOCK_DEAD)) + goto exit; + + /* We come from tcp_disconnect. We are sure that meta_sk is set */ + if (!mptcp(tp)) { + tp->closing = 1; + tcp_close(sk, 0); + goto exit; + } + + if (meta_sk->sk_shutdown == SHUTDOWN_MASK || sk->sk_state == TCP_CLOSE) { + tp->closing = 1; + tcp_close(sk, 0); + } else if (tcp_close_state(sk)) { + sk->sk_shutdown |= SEND_SHUTDOWN; + tcp_send_fin(sk); + } + +exit: + release_sock(meta_sk); + mutex_unlock(&tp->mpcb->mpcb_mutex); + sock_put(sk); +} + +void mptcp_sub_close(struct sock *sk, unsigned long delay) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct delayed_work *work = &tcp_sk(sk)->mptcp->work; + + /* We are already closing - e.g., call from sock_def_error_report upon + * tcp_disconnect in tcp_close. + */ + if (tp->closing) + return; + + /* Work already scheduled ? */ + if (work_pending(&work->work)) { + /* Work present - who will be first ? */ + if (jiffies + delay > work->timer.expires) + return; + + /* Try canceling - if it fails, work will be executed soon */ + if (!cancel_delayed_work(work)) + return; + sock_put(sk); + } + + if (!delay) { + unsigned char old_state = sk->sk_state; + + /* If we are in user-context we can directly do the closing + * procedure. No need to schedule a work-queue. + */ + if (!in_softirq()) { + if (sock_flag(sk, SOCK_DEAD)) + return; + + if (!mptcp(tp)) { + tp->closing = 1; + tcp_close(sk, 0); + return; + } + + if (mptcp_meta_sk(sk)->sk_shutdown == SHUTDOWN_MASK || + sk->sk_state == TCP_CLOSE) { + tp->closing = 1; + tcp_close(sk, 0); + } else if (tcp_close_state(sk)) { + sk->sk_shutdown |= SEND_SHUTDOWN; + tcp_send_fin(sk); + } + + return; + } + + /* We directly send the FIN. Because it may take so a long time, + * untile the work-queue will get scheduled... + * + * If mptcp_sub_send_fin returns 1, it failed and thus we reset + * the old state so that tcp_close will finally send the fin + * in user-context. + */ + if (!sk->sk_err && old_state != TCP_CLOSE && + tcp_close_state(sk) && mptcp_sub_send_fin(sk)) { + if (old_state == TCP_ESTABLISHED) + TCP_INC_STATS(sock_net(sk), TCP_MIB_CURRESTAB); + sk->sk_state = old_state; + } + } + + sock_hold(sk); + queue_delayed_work(mptcp_wq, work, delay); +} + +void mptcp_sub_force_close(struct sock *sk) +{ + /* The below tcp_done may have freed the socket, if he is already dead. + * Thus, we are not allowed to access it afterwards. That's why + * we have to store the dead-state in this local variable. + */ + int sock_is_dead = sock_flag(sk, SOCK_DEAD); + + tcp_sk(sk)->mp_killed = 1; + + if (sk->sk_state != TCP_CLOSE) + tcp_done(sk); + + if (!sock_is_dead) + mptcp_sub_close(sk, 0); +} +EXPORT_SYMBOL(mptcp_sub_force_close); + +/* Update the mpcb send window, based on the contributions + * of each subflow + */ +void mptcp_update_sndbuf(const struct tcp_sock *tp) +{ + struct sock *meta_sk = tp->meta_sk, *sk; + int new_sndbuf = 0, old_sndbuf = meta_sk->sk_sndbuf; + + mptcp_for_each_sk(tp->mpcb, sk) { + if (!mptcp_sk_can_send(sk)) + continue; + + new_sndbuf += sk->sk_sndbuf; + + if (new_sndbuf > sysctl_tcp_wmem[2] || new_sndbuf < 0) { + new_sndbuf = sysctl_tcp_wmem[2]; + break; + } + } + meta_sk->sk_sndbuf = max(min(new_sndbuf, sysctl_tcp_wmem[2]), meta_sk->sk_sndbuf); + + /* The subflow's call to sk_write_space in tcp_new_space ends up in + * mptcp_write_space. + * It has nothing to do with waking up the application. + * So, we do it here. + */ + if (old_sndbuf != meta_sk->sk_sndbuf) + meta_sk->sk_write_space(meta_sk); +} + +void mptcp_close(struct sock *meta_sk, long timeout) +{ + struct tcp_sock *meta_tp = tcp_sk(meta_sk); + struct sock *sk_it, *tmpsk; + struct mptcp_cb *mpcb = meta_tp->mpcb; + struct sk_buff *skb; + int data_was_unread = 0; + int state; + + mptcp_debug("%s: Close of meta_sk with tok %#x\n", + __func__, mpcb->mptcp_loc_token); + + mutex_lock(&mpcb->mpcb_mutex); + lock_sock(meta_sk); + + if (meta_tp->inside_tk_table) { + /* Detach the mpcb from the token hashtable */ + mptcp_hash_remove_bh(meta_tp); + reqsk_queue_destroy(&inet_csk(meta_sk)->icsk_accept_queue); + } + + meta_sk->sk_shutdown = SHUTDOWN_MASK; + /* We need to flush the recv. buffs. We do this only on the + * descriptor close, not protocol-sourced closes, because the + * reader process may not have drained the data yet! + */ + while ((skb = __skb_dequeue(&meta_sk->sk_receive_queue)) != NULL) { + u32 len = TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq; + + if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) + len--; + data_was_unread += len; + __kfree_skb(skb); + } + + sk_mem_reclaim(meta_sk); + + /* If socket has been already reset (e.g. in tcp_reset()) - kill it. */ + if (meta_sk->sk_state == TCP_CLOSE) { + mptcp_for_each_sk_safe(mpcb, sk_it, tmpsk) { + if (tcp_sk(sk_it)->send_mp_fclose) + continue; + mptcp_sub_close(sk_it, 0); + } + goto adjudge_to_death; + } + + if (data_was_unread) { + /* Unread data was tossed, zap the connection. */ + NET_INC_STATS_USER(sock_net(meta_sk), LINUX_MIB_TCPABORTONCLOSE); + tcp_set_state(meta_sk, TCP_CLOSE); + tcp_sk(meta_sk)->ops->send_active_reset(meta_sk, + meta_sk->sk_allocation); + } else if (sock_flag(meta_sk, SOCK_LINGER) && !meta_sk->sk_lingertime) { + /* Check zero linger _after_ checking for unread data. */ + meta_sk->sk_prot->disconnect(meta_sk, 0); + NET_INC_STATS_USER(sock_net(meta_sk), LINUX_MIB_TCPABORTONDATA); + } else if (tcp_close_state(meta_sk)) { + mptcp_send_fin(meta_sk); + } else if (meta_tp->snd_una == meta_tp->write_seq) { + /* The DATA_FIN has been sent and acknowledged + * (e.g., by sk_shutdown). Close all the other subflows + */ + mptcp_for_each_sk_safe(mpcb, sk_it, tmpsk) { + unsigned long delay = 0; + /* If we are the passive closer, don't trigger + * subflow-fin until the subflow has been finned + * by the peer. - thus we add a delay + */ + if (mpcb->passive_close && + sk_it->sk_state == TCP_ESTABLISHED) + delay = inet_csk(sk_it)->icsk_rto << 3; + + mptcp_sub_close(sk_it, delay); + } + } + + sk_stream_wait_close(meta_sk, timeout); + +adjudge_to_death: + state = meta_sk->sk_state; + sock_hold(meta_sk); + sock_orphan(meta_sk); + + /* socket will be freed after mptcp_close - we have to prevent + * access from the subflows. + */ + mptcp_for_each_sk(mpcb, sk_it) { + /* Similar to sock_orphan, but we don't set it DEAD, because + * the callbacks are still set and must be called. + */ + write_lock_bh(&sk_it->sk_callback_lock); + sk_set_socket(sk_it, NULL); + sk_it->sk_wq = NULL; + write_unlock_bh(&sk_it->sk_callback_lock); + } + + /* It is the last release_sock in its life. It will remove backlog. */ + release_sock(meta_sk); + + /* Now socket is owned by kernel and we acquire BH lock + * to finish close. No need to check for user refs. + */ + local_bh_disable(); + bh_lock_sock(meta_sk); + WARN_ON(sock_owned_by_user(meta_sk)); + + percpu_counter_inc(meta_sk->sk_prot->orphan_count); + + /* Have we already been destroyed by a softirq or backlog? */ + if (state != TCP_CLOSE && meta_sk->sk_state == TCP_CLOSE) + goto out; + + /* This is a (useful) BSD violating of the RFC. There is a + * problem with TCP as specified in that the other end could + * keep a socket open forever with no application left this end. + * We use a 3 minute timeout (about the same as BSD) then kill + * our end. If they send after that then tough - BUT: long enough + * that we won't make the old 4*rto = almost no time - whoops + * reset mistake. + * + * Nope, it was not mistake. It is really desired behaviour + * f.e. on http servers, when such sockets are useless, but + * consume significant resources. Let's do it with special + * linger2 option. --ANK + */ + + if (meta_sk->sk_state == TCP_FIN_WAIT2) { + if (meta_tp->linger2 < 0) { + tcp_set_state(meta_sk, TCP_CLOSE); + meta_tp->ops->send_active_reset(meta_sk, GFP_ATOMIC); + NET_INC_STATS_BH(sock_net(meta_sk), + LINUX_MIB_TCPABORTONLINGER); + } else { + const int tmo = tcp_fin_time(meta_sk); + + if (tmo > TCP_TIMEWAIT_LEN) { + inet_csk_reset_keepalive_timer(meta_sk, + tmo - TCP_TIMEWAIT_LEN); + } else { + meta_tp->ops->time_wait(meta_sk, TCP_FIN_WAIT2, + tmo); + goto out; + } + } + } + if (meta_sk->sk_state != TCP_CLOSE) { + sk_mem_reclaim(meta_sk); + if (tcp_too_many_orphans(meta_sk, 0)) { + if (net_ratelimit()) + pr_info("MPTCP: too many of orphaned sockets\n"); + tcp_set_state(meta_sk, TCP_CLOSE); + meta_tp->ops->send_active_reset(meta_sk, GFP_ATOMIC); + NET_INC_STATS_BH(sock_net(meta_sk), + LINUX_MIB_TCPABORTONMEMORY); + } + } + + + if (meta_sk->sk_state == TCP_CLOSE) + inet_csk_destroy_sock(meta_sk); + /* Otherwise, socket is reprieved until protocol close. */ + +out: + bh_unlock_sock(meta_sk); + local_bh_enable(); + mutex_unlock(&mpcb->mpcb_mutex); + sock_put(meta_sk); /* Taken by sock_hold */ +} + +void mptcp_disconnect(struct sock *sk) +{ + struct sock *subsk, *tmpsk; + struct tcp_sock *tp = tcp_sk(sk); + + __skb_queue_purge(&tp->mpcb->reinject_queue); + + if (tp->inside_tk_table) { + mptcp_hash_remove_bh(tp); + reqsk_queue_destroy(&inet_csk(tp->meta_sk)->icsk_accept_queue); + } + + local_bh_disable(); + mptcp_for_each_sk_safe(tp->mpcb, subsk, tmpsk) { + /* The socket will get removed from the subsocket-list + * and made non-mptcp by setting mpc to 0. + * + * This is necessary, because tcp_disconnect assumes + * that the connection is completly dead afterwards. + * Thus we need to do a mptcp_del_sock. Due to this call + * we have to make it non-mptcp. + * + * We have to lock the socket, because we set mpc to 0. + * An incoming packet would take the subsocket's lock + * and go on into the receive-path. + * This would be a race. + */ + + bh_lock_sock(subsk); + mptcp_del_sock(subsk); + tcp_sk(subsk)->mpc = 0; + tcp_sk(subsk)->ops = &tcp_specific; + mptcp_sub_force_close(subsk); + bh_unlock_sock(subsk); + } + local_bh_enable(); + + tp->was_meta_sk = 1; + tp->mpc = 0; + tp->ops = &tcp_specific; +} + + +/* Returns 1 if we should enable MPTCP for that socket. */ +int mptcp_doit(struct sock *sk) +{ + /* Don't do mptcp over loopback */ + if (sk->sk_family == AF_INET && + (ipv4_is_loopback(inet_sk(sk)->inet_daddr) || + ipv4_is_loopback(inet_sk(sk)->inet_saddr))) + return 0; +#if IS_ENABLED(CONFIG_IPV6) + if (sk->sk_family == AF_INET6 && + (ipv6_addr_loopback(&sk->sk_v6_daddr) || + ipv6_addr_loopback(&inet6_sk(sk)->saddr))) + return 0; +#endif + if (mptcp_v6_is_v4_mapped(sk) && + ipv4_is_loopback(inet_sk(sk)->inet_saddr)) + return 0; + +#ifdef CONFIG_TCP_MD5SIG + /* If TCP_MD5SIG is enabled, do not do MPTCP - there is no Option-Space */ + if (tcp_sk(sk)->af_specific->md5_lookup(sk, sk)) + return 0; +#endif + + return 1; +} + +int mptcp_create_master_sk(struct sock *meta_sk, __u64 remote_key, + __u8 mptcp_ver, u32 window) +{ + struct tcp_sock *master_tp; + struct sock *master_sk; + + if (mptcp_alloc_mpcb(meta_sk, remote_key, mptcp_ver, window)) + goto err_alloc_mpcb; + + master_sk = tcp_sk(meta_sk)->mpcb->master_sk; + master_tp = tcp_sk(master_sk); + + if (mptcp_add_sock(meta_sk, master_sk, 0, 0, GFP_ATOMIC)) + goto err_add_sock; + + if (__inet_inherit_port(meta_sk, master_sk) < 0) + goto err_add_sock; + + meta_sk->sk_prot->unhash(meta_sk); + + if (master_sk->sk_family == AF_INET || mptcp_v6_is_v4_mapped(master_sk)) + __inet_hash_nolisten(master_sk, NULL); +#if IS_ENABLED(CONFIG_IPV6) + else + __inet_hash(master_sk, NULL); +#endif + + master_tp->mptcp->init_rcv_wnd = master_tp->rcv_wnd; + + return 0; + +err_add_sock: + mptcp_fallback_meta_sk(meta_sk); + + inet_csk_prepare_forced_close(master_sk); + tcp_done(master_sk); + inet_csk_prepare_forced_close(meta_sk); + tcp_done(meta_sk); + +err_alloc_mpcb: + return -ENOBUFS; +} + +static int __mptcp_check_req_master(struct sock *child, + struct request_sock *req) +{ + struct tcp_sock *child_tp = tcp_sk(child); + struct sock *meta_sk = child; + struct mptcp_cb *mpcb; + struct mptcp_request_sock *mtreq; + + /* Never contained an MP_CAPABLE */ + if (!inet_rsk(req)->mptcp_rqsk) + return 1; + + if (!inet_rsk(req)->saw_mpc) { + /* Fallback to regular TCP, because we saw one SYN without + * MP_CAPABLE. In tcp_check_req we continue the regular path. + * But, the socket has been added to the reqsk_tk_htb, so we + * must still remove it. + */ + MPTCP_INC_STATS_BH(sock_net(meta_sk), MPTCP_MIB_MPCAPABLEPASSIVEFALLBACK); + mptcp_reqsk_remove_tk(req); + return 1; + } + + MPTCP_INC_STATS_BH(sock_net(meta_sk), MPTCP_MIB_MPCAPABLEPASSIVEACK); + + /* Just set this values to pass them to mptcp_alloc_mpcb */ + mtreq = mptcp_rsk(req); + child_tp->mptcp_loc_key = mtreq->mptcp_loc_key; + child_tp->mptcp_loc_token = mtreq->mptcp_loc_token; + + if (mptcp_create_master_sk(meta_sk, mtreq->mptcp_rem_key, + mtreq->mptcp_ver, child_tp->snd_wnd)) + return -ENOBUFS; + + child = tcp_sk(child)->mpcb->master_sk; + child_tp = tcp_sk(child); + mpcb = child_tp->mpcb; + + child_tp->mptcp->snt_isn = tcp_rsk(req)->snt_isn; + child_tp->mptcp->rcv_isn = tcp_rsk(req)->rcv_isn; + + mpcb->dss_csum = mtreq->dss_csum; + mpcb->server_side = 1; + + /* Will be moved to ESTABLISHED by tcp_rcv_state_process() */ + mptcp_update_metasocket(child, meta_sk); + + /* Needs to be done here additionally, because when accepting a + * new connection we pass by __reqsk_free and not reqsk_free. + */ + mptcp_reqsk_remove_tk(req); + + /* Hold when creating the meta-sk in tcp_vX_syn_recv_sock. */ + sock_put(meta_sk); + + return 0; +} + +int mptcp_check_req_fastopen(struct sock *child, struct request_sock *req) +{ + struct sock *meta_sk = child, *master_sk; + struct sk_buff *skb; + u32 new_mapping; + int ret; + + ret = __mptcp_check_req_master(child, req); + if (ret) + return ret; + + master_sk = tcp_sk(meta_sk)->mpcb->master_sk; + + /* We need to rewind copied_seq as it is set to IDSN + 1 and as we have + * pre-MPTCP data in the receive queue. + */ + tcp_sk(meta_sk)->copied_seq -= tcp_sk(master_sk)->rcv_nxt - + tcp_rsk(req)->rcv_isn - 1; + + /* Map subflow sequence number to data sequence numbers. We need to map + * these data to [IDSN - len - 1, IDSN[. + */ + new_mapping = tcp_sk(meta_sk)->copied_seq - tcp_rsk(req)->rcv_isn - 1; + + /* There should be only one skb: the SYN + data. */ + skb_queue_walk(&meta_sk->sk_receive_queue, skb) { + TCP_SKB_CB(skb)->seq += new_mapping; + TCP_SKB_CB(skb)->end_seq += new_mapping; + } + + /* With fastopen we change the semantics of the relative subflow + * sequence numbers to deal with middleboxes that could add/remove + * multiple bytes in the SYN. We chose to start counting at rcv_nxt - 1 + * instead of the regular TCP ISN. + */ + tcp_sk(master_sk)->mptcp->rcv_isn = tcp_sk(master_sk)->rcv_nxt - 1; + + /* We need to update copied_seq of the master_sk to account for the + * already moved data to the meta receive queue. + */ + tcp_sk(master_sk)->copied_seq = tcp_sk(master_sk)->rcv_nxt; + + /* Handled by the master_sk */ + tcp_sk(meta_sk)->fastopen_rsk = NULL; + + return 0; +} + +int mptcp_check_req_master(struct sock *sk, struct sock *child, + struct request_sock *req, + int drop) +{ + struct sock *meta_sk = child; + int ret; + + ret = __mptcp_check_req_master(child, req); + if (ret) + return ret; + + /* drop indicates that we come from tcp_check_req and thus need to + * handle the request-socket fully. + */ + if (drop) { + inet_csk_reqsk_queue_drop(sk, req); + } else { + /* Thus, we come from syn-cookies */ + atomic_set(&req->rsk_refcnt, 1); + } + inet_csk_reqsk_queue_add(sk, req, meta_sk); + + return 0; +} + +struct sock *mptcp_check_req_child(struct sock *meta_sk, struct sock *child, + struct request_sock *req, + const struct mptcp_options_received *mopt) +{ + struct tcp_sock *child_tp = tcp_sk(child); + struct mptcp_request_sock *mtreq = mptcp_rsk(req); + struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; + u8 hash_mac_check[20]; + + child_tp->inside_tk_table = 0; + + if (!mopt->join_ack) { + MPTCP_INC_STATS_BH(sock_net(meta_sk), MPTCP_MIB_JOINACKFAIL); + goto teardown; + } + + mptcp_hmac_sha1((u8 *)&mpcb->mptcp_rem_key, + (u8 *)&mpcb->mptcp_loc_key, + (u32 *)hash_mac_check, 2, + 4, (u8 *)&mtreq->mptcp_rem_nonce, + 4, (u8 *)&mtreq->mptcp_loc_nonce); + + if (memcmp(hash_mac_check, (char *)&mopt->mptcp_recv_mac, 20)) { + MPTCP_INC_STATS_BH(sock_net(meta_sk), MPTCP_MIB_JOINACKMAC); + goto teardown; + } + + /* Point it to the same struct socket and wq as the meta_sk */ + sk_set_socket(child, meta_sk->sk_socket); + child->sk_wq = meta_sk->sk_wq; + + if (mptcp_add_sock(meta_sk, child, mtreq->loc_id, mtreq->rem_id, GFP_ATOMIC)) { + /* Has been inherited, but now child_tp->mptcp is NULL */ + child_tp->mpc = 0; + child_tp->ops = &tcp_specific; + + /* TODO when we support acking the third ack for new subflows, + * we should silently discard this third ack, by returning NULL. + * + * Maybe, at the retransmission we will have enough memory to + * fully add the socket to the meta-sk. + */ + goto teardown; + } + + /* The child is a clone of the meta socket, we must now reset + * some of the fields + */ + child_tp->mptcp->rcv_low_prio = mtreq->rcv_low_prio; + + /* We should allow proper increase of the snd/rcv-buffers. Thus, we + * use the original values instead of the bloated up ones from the + * clone. + */ + child->sk_sndbuf = mpcb->orig_sk_sndbuf; + child->sk_rcvbuf = mpcb->orig_sk_rcvbuf; + + child_tp->mptcp->slave_sk = 1; + child_tp->mptcp->snt_isn = tcp_rsk(req)->snt_isn; + child_tp->mptcp->rcv_isn = tcp_rsk(req)->rcv_isn; + child_tp->mptcp->init_rcv_wnd = req->rcv_wnd; + + child_tp->tsq_flags = 0; + + /* Subflows do not use the accept queue, as they + * are attached immediately to the mpcb. + */ + inet_csk_reqsk_queue_drop(meta_sk, req); + + /* The refcnt is initialized to 2, because regular TCP will put him + * in the socket's listener queue. However, we do not have a listener-queue. + * So, we need to make sure that this request-sock indeed gets destroyed. + */ + reqsk_put(req); + + MPTCP_INC_STATS_BH(sock_net(meta_sk), MPTCP_MIB_JOINACKRX); + return child; + +teardown: + /* Drop this request - sock creation failed. */ + inet_csk_reqsk_queue_drop(meta_sk, req); + reqsk_put(req); + inet_csk_prepare_forced_close(child); + tcp_done(child); + return meta_sk; +} + +int mptcp_init_tw_sock(struct sock *sk, struct tcp_timewait_sock *tw) +{ + struct mptcp_tw *mptw; + struct tcp_sock *tp = tcp_sk(sk); + struct mptcp_cb *mpcb = tp->mpcb; + + /* A subsocket in tw can only receive data. So, if we are in + * infinite-receive, then we should not reply with a data-ack or act + * upon general MPTCP-signaling. We prevent this by simply not creating + * the mptcp_tw_sock. + */ + if (mpcb->infinite_mapping_rcv) { + tw->mptcp_tw = NULL; + return 0; + } + + /* Alloc MPTCP-tw-sock */ + mptw = kmem_cache_alloc(mptcp_tw_cache, GFP_ATOMIC); + if (!mptw) { + tw->mptcp_tw = NULL; + return -ENOBUFS; + } + + atomic_inc(&mpcb->mpcb_refcnt); + + tw->mptcp_tw = mptw; + mptw->loc_key = mpcb->mptcp_loc_key; + mptw->meta_tw = mpcb->in_time_wait; + if (mptw->meta_tw) { + mptw->rcv_nxt = mptcp_get_rcv_nxt_64(mptcp_meta_tp(tp)); + if (mpcb->mptw_state != TCP_TIME_WAIT) + mptw->rcv_nxt++; + } + rcu_assign_pointer(mptw->mpcb, mpcb); + + spin_lock(&mpcb->tw_lock); + list_add_rcu(&mptw->list, &tp->mpcb->tw_list); + mptw->in_list = 1; + spin_unlock(&mpcb->tw_lock); + + return 0; +} + +void mptcp_twsk_destructor(struct tcp_timewait_sock *tw) +{ + struct mptcp_cb *mpcb; + + rcu_read_lock(); + mpcb = rcu_dereference(tw->mptcp_tw->mpcb); + + /* If we are still holding a ref to the mpcb, we have to remove ourself + * from the list and drop the ref properly. + */ + if (mpcb && atomic_inc_not_zero(&mpcb->mpcb_refcnt)) { + spin_lock(&mpcb->tw_lock); + if (tw->mptcp_tw->in_list) { + list_del_rcu(&tw->mptcp_tw->list); + tw->mptcp_tw->in_list = 0; + } + spin_unlock(&mpcb->tw_lock); + + /* Twice, because we increased it above */ + mptcp_mpcb_put(mpcb); + mptcp_mpcb_put(mpcb); + } + + rcu_read_unlock(); + + kmem_cache_free(mptcp_tw_cache, tw->mptcp_tw); +} + +/* Updates the rcv_nxt of the time-wait-socks and allows them to ack a + * data-fin. + */ +void mptcp_time_wait(struct sock *sk, int state, int timeo) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct mptcp_tw *mptw; + + /* Used for sockets that go into tw after the meta + * (see mptcp_init_tw_sock()) + */ + tp->mpcb->in_time_wait = 1; + tp->mpcb->mptw_state = state; + + /* Update the time-wait-sock's information */ + rcu_read_lock_bh(); + list_for_each_entry_rcu(mptw, &tp->mpcb->tw_list, list) { + mptw->meta_tw = 1; + mptw->rcv_nxt = mptcp_get_rcv_nxt_64(tp); + + /* We want to ack a DATA_FIN, but are yet in FIN_WAIT_2 - + * pretend as if the DATA_FIN has already reached us, that way + * the checks in tcp_timewait_state_process will be good as the + * DATA_FIN comes in. + */ + if (state != TCP_TIME_WAIT) + mptw->rcv_nxt++; + } + rcu_read_unlock_bh(); + + tcp_done(sk); +} + +void mptcp_tsq_flags(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct sock *meta_sk = mptcp_meta_sk(sk); + + /* It will be handled as a regular deferred-call */ + if (is_meta_sk(sk)) + return; + + if (hlist_unhashed(&tp->mptcp->cb_list)) { + hlist_add_head(&tp->mptcp->cb_list, &tp->mpcb->callback_list); + /* We need to hold it here, as the sock_hold is not assured + * by the release_sock as it is done in regular TCP. + * + * The subsocket may get inet_csk_destroy'd while it is inside + * the callback_list. + */ + sock_hold(sk); + } + + if (!test_and_set_bit(MPTCP_SUB_DEFERRED, &tcp_sk(meta_sk)->tsq_flags)) + sock_hold(meta_sk); +} + +void mptcp_tsq_sub_deferred(struct sock *meta_sk) +{ + struct tcp_sock *meta_tp = tcp_sk(meta_sk); + struct mptcp_tcp_sock *mptcp; + struct hlist_node *tmp; + + BUG_ON(!is_meta_sk(meta_sk) && !meta_tp->was_meta_sk); + + __sock_put(meta_sk); + hlist_for_each_entry_safe(mptcp, tmp, &meta_tp->mpcb->callback_list, cb_list) { + struct tcp_sock *tp = mptcp->tp; + struct sock *sk = (struct sock *)tp; + + hlist_del_init(&mptcp->cb_list); + sk->sk_prot->release_cb(sk); + /* Final sock_put (cfr. mptcp_tsq_flags */ + sock_put(sk); + } +} + +void mptcp_join_reqsk_init(struct mptcp_cb *mpcb, const struct request_sock *req, + struct sk_buff *skb) +{ + struct mptcp_request_sock *mtreq = mptcp_rsk(req); + struct mptcp_options_received mopt; + u8 mptcp_hash_mac[20]; + + mptcp_init_mp_opt(&mopt); + tcp_parse_mptcp_options(skb, &mopt); + + mtreq->mptcp_mpcb = mpcb; + mtreq->is_sub = 1; + inet_rsk(req)->mptcp_rqsk = 1; + + mtreq->mptcp_rem_nonce = mopt.mptcp_recv_nonce; + + mptcp_hmac_sha1((u8 *)&mpcb->mptcp_loc_key, + (u8 *)&mpcb->mptcp_rem_key, + (u32 *)mptcp_hash_mac, 2, + 4, (u8 *)&mtreq->mptcp_loc_nonce, + 4, (u8 *)&mtreq->mptcp_rem_nonce); + mtreq->mptcp_hash_tmac = *(u64 *)mptcp_hash_mac; + + mtreq->rem_id = mopt.rem_id; + mtreq->rcv_low_prio = mopt.low_prio; + inet_rsk(req)->saw_mpc = 1; + + MPTCP_INC_STATS_BH(sock_net(mpcb->meta_sk), MPTCP_MIB_JOINSYNRX); +} + +void mptcp_reqsk_init(struct request_sock *req, struct sock *sk, + const struct sk_buff *skb, bool want_cookie) +{ + struct mptcp_options_received mopt; + struct mptcp_request_sock *mtreq = mptcp_rsk(req); + + mptcp_init_mp_opt(&mopt); + tcp_parse_mptcp_options(skb, &mopt); + + mtreq->dss_csum = mopt.dss_csum; + + if (want_cookie) { + if (!mptcp_reqsk_new_cookie(req, &mopt, skb)) + /* No key available - back to regular TCP */ + inet_rsk(req)->mptcp_rqsk = 0; + return; + } + + mptcp_reqsk_new_mptcp(req, sk, &mopt, skb); +} + +void mptcp_cookies_reqsk_init(struct request_sock *req, + struct mptcp_options_received *mopt, + struct sk_buff *skb) +{ + struct mptcp_request_sock *mtreq = mptcp_rsk(req); + + /* Absolutely need to always initialize this. */ + mtreq->hash_entry.pprev = NULL; + + mtreq->mptcp_rem_key = mopt->mptcp_sender_key; + mtreq->mptcp_loc_key = mopt->mptcp_receiver_key; + + /* Generate the token */ + mptcp_key_sha1(mtreq->mptcp_loc_key, &mtreq->mptcp_loc_token, NULL); + + rcu_read_lock(); + spin_lock(&mptcp_tk_hashlock); + + /* Check, if the key is still free */ + if (mptcp_reqsk_find_tk(mtreq->mptcp_loc_token) || + mptcp_find_token(mtreq->mptcp_loc_token)) + goto out; + + inet_rsk(req)->saw_mpc = 1; + mtreq->is_sub = 0; + inet_rsk(req)->mptcp_rqsk = 1; + mtreq->dss_csum = mopt->dss_csum; + +out: + spin_unlock(&mptcp_tk_hashlock); + rcu_read_unlock(); +} + +int mptcp_conn_request(struct sock *sk, struct sk_buff *skb) +{ + struct mptcp_options_received mopt; + + mptcp_init_mp_opt(&mopt); + tcp_parse_mptcp_options(skb, &mopt); + + if (mopt.is_mp_join) + return mptcp_do_join_short(skb, &mopt, sock_net(sk)); + if (mopt.drop_me) + goto drop; + + if (!sock_flag(sk, SOCK_MPTCP)) + mopt.saw_mpc = 0; + + if (skb->protocol == htons(ETH_P_IP)) { + if (mopt.saw_mpc) { + if (skb_rtable(skb)->rt_flags & + (RTCF_BROADCAST | RTCF_MULTICAST)) + goto drop; + + MPTCP_INC_STATS_BH(sock_net(sk), MPTCP_MIB_MPCAPABLEPASSIVE); + return tcp_conn_request(&mptcp_request_sock_ops, + &mptcp_request_sock_ipv4_ops, + sk, skb); + } + + return tcp_v4_conn_request(sk, skb); +#if IS_ENABLED(CONFIG_IPV6) + } else { + if (mopt.saw_mpc) { + if (!ipv6_unicast_destination(skb)) + goto drop; + + MPTCP_INC_STATS_BH(sock_net(sk), MPTCP_MIB_MPCAPABLEPASSIVE); + return tcp_conn_request(&mptcp6_request_sock_ops, + &mptcp_request_sock_ipv6_ops, + sk, skb); + } + + return tcp_v6_conn_request(sk, skb); +#endif + } +drop: + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); + return 0; +} + +static const struct snmp_mib mptcp_snmp_list[] = { + SNMP_MIB_ITEM("MPCapableSYNRX", MPTCP_MIB_MPCAPABLEPASSIVE), + SNMP_MIB_ITEM("MPCapableSYNTX", MPTCP_MIB_MPCAPABLEACTIVE), + SNMP_MIB_ITEM("MPCapableSYNACKRX", MPTCP_MIB_MPCAPABLEACTIVEACK), + SNMP_MIB_ITEM("MPCapableACKRX", MPTCP_MIB_MPCAPABLEPASSIVEACK), + SNMP_MIB_ITEM("MPCapableFallbackACK", MPTCP_MIB_MPCAPABLEPASSIVEFALLBACK), + SNMP_MIB_ITEM("MPCapableFallbackSYNACK", MPTCP_MIB_MPCAPABLEACTIVEFALLBACK), + SNMP_MIB_ITEM("MPCapableRetransFallback", MPTCP_MIB_MPCAPABLERETRANSFALLBACK), + SNMP_MIB_ITEM("MPTCPCsumEnabled", MPTCP_MIB_CSUMENABLED), + SNMP_MIB_ITEM("MPTCPRetrans", MPTCP_MIB_RETRANSSEGS), + SNMP_MIB_ITEM("MPFailRX", MPTCP_MIB_MPFAILRX), + SNMP_MIB_ITEM("MPCsumFail", MPTCP_MIB_CSUMFAIL), + SNMP_MIB_ITEM("MPFastcloseRX", MPTCP_MIB_FASTCLOSERX), + SNMP_MIB_ITEM("MPFastcloseTX", MPTCP_MIB_FASTCLOSETX), + SNMP_MIB_ITEM("MPFallbackAckSub", MPTCP_MIB_FBACKSUB), + SNMP_MIB_ITEM("MPFallbackAckInit", MPTCP_MIB_FBACKINIT), + SNMP_MIB_ITEM("MPFallbackDataSub", MPTCP_MIB_FBDATASUB), + SNMP_MIB_ITEM("MPFallbackDataInit", MPTCP_MIB_FBDATAINIT), + SNMP_MIB_ITEM("MPRemoveAddrSubDelete", MPTCP_MIB_REMADDRSUB), + SNMP_MIB_ITEM("MPJoinNoTokenFound", MPTCP_MIB_JOINNOTOKEN), + SNMP_MIB_ITEM("MPJoinAlreadyFallenback", MPTCP_MIB_JOINFALLBACK), + SNMP_MIB_ITEM("MPJoinSynTx", MPTCP_MIB_JOINSYNTX), + SNMP_MIB_ITEM("MPJoinSynRx", MPTCP_MIB_JOINSYNRX), + SNMP_MIB_ITEM("MPJoinSynAckRx", MPTCP_MIB_JOINSYNACKRX), + SNMP_MIB_ITEM("MPJoinSynAckHMacFailure", MPTCP_MIB_JOINSYNACKMAC), + SNMP_MIB_ITEM("MPJoinAckRx", MPTCP_MIB_JOINACKRX), + SNMP_MIB_ITEM("MPJoinAckHMacFailure", MPTCP_MIB_JOINACKMAC), + SNMP_MIB_ITEM("MPJoinAckMissing", MPTCP_MIB_JOINACKFAIL), + SNMP_MIB_ITEM("MPJoinAckRTO", MPTCP_MIB_JOINACKRTO), + SNMP_MIB_ITEM("MPJoinAckRexmit", MPTCP_MIB_JOINACKRXMIT), + SNMP_MIB_ITEM("NoDSSInWindow", MPTCP_MIB_NODSSWINDOW), + SNMP_MIB_ITEM("DSSNotMatching", MPTCP_MIB_DSSNOMATCH), + SNMP_MIB_ITEM("InfiniteMapRx", MPTCP_MIB_INFINITEMAPRX), + SNMP_MIB_ITEM("DSSNoMatchTCP", MPTCP_MIB_DSSTCPMISMATCH), + SNMP_MIB_ITEM("DSSTrimHead", MPTCP_MIB_DSSTRIMHEAD), + SNMP_MIB_ITEM("DSSSplitTail", MPTCP_MIB_DSSSPLITTAIL), + SNMP_MIB_ITEM("DSSPurgeOldSubSegs", MPTCP_MIB_PURGEOLD), + SNMP_MIB_ITEM("AddAddrRx", MPTCP_MIB_ADDADDRRX), + SNMP_MIB_ITEM("AddAddrTx", MPTCP_MIB_ADDADDRTX), + SNMP_MIB_ITEM("RemAddrRx", MPTCP_MIB_REMADDRRX), + SNMP_MIB_ITEM("RemAddrTx", MPTCP_MIB_REMADDRTX), + SNMP_MIB_SENTINEL +}; + +struct workqueue_struct *mptcp_wq; +EXPORT_SYMBOL(mptcp_wq); + +/* Output /proc/net/mptcp */ +static int mptcp_pm_seq_show(struct seq_file *seq, void *v) +{ + struct tcp_sock *meta_tp; + const struct net *net = seq->private; + int i, n = 0; + + seq_printf(seq, " sl loc_tok rem_tok v6 local_address remote_address st ns tx_queue rx_queue inode"); + seq_putc(seq, '\n'); + + for (i = 0; i < MPTCP_HASH_SIZE; i++) { + struct hlist_nulls_node *node; + rcu_read_lock_bh(); + hlist_nulls_for_each_entry_rcu(meta_tp, node, + &tk_hashtable[i], tk_table) { + struct mptcp_cb *mpcb = meta_tp->mpcb; + struct sock *meta_sk = (struct sock *)meta_tp; + struct inet_sock *isk = inet_sk(meta_sk); + + if (!mptcp(meta_tp) || !net_eq(net, sock_net(meta_sk))) + continue; + + if (capable(CAP_NET_ADMIN)) { + seq_printf(seq, "%4d: %04X %04X ", n++, + mpcb->mptcp_loc_token, + mpcb->mptcp_rem_token); + } else { + seq_printf(seq, "%4d: %04X %04X ", n++, -1, -1); + } + if (meta_sk->sk_family == AF_INET || + mptcp_v6_is_v4_mapped(meta_sk)) { + seq_printf(seq, " 0 %08X:%04X %08X:%04X ", + isk->inet_rcv_saddr, + ntohs(isk->inet_sport), + isk->inet_daddr, + ntohs(isk->inet_dport)); +#if IS_ENABLED(CONFIG_IPV6) + } else if (meta_sk->sk_family == AF_INET6) { + struct in6_addr *src = &meta_sk->sk_v6_rcv_saddr; + struct in6_addr *dst = &meta_sk->sk_v6_daddr; + seq_printf(seq, " 1 %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X", + src->s6_addr32[0], src->s6_addr32[1], + src->s6_addr32[2], src->s6_addr32[3], + ntohs(isk->inet_sport), + dst->s6_addr32[0], dst->s6_addr32[1], + dst->s6_addr32[2], dst->s6_addr32[3], + ntohs(isk->inet_dport)); +#endif + } + seq_printf(seq, " %02X %02X %08X:%08X %lu", + meta_sk->sk_state, mpcb->cnt_subflows, + meta_tp->write_seq - meta_tp->snd_una, + max_t(int, meta_tp->rcv_nxt - + meta_tp->copied_seq, 0), + sock_i_ino(meta_sk)); + seq_putc(seq, '\n'); + } + + rcu_read_unlock_bh(); + } + + return 0; +} + +static int mptcp_pm_seq_open(struct inode *inode, struct file *file) +{ + return single_open_net(inode, file, mptcp_pm_seq_show); +} + +static const struct file_operations mptcp_pm_seq_fops = { + .owner = THIS_MODULE, + .open = mptcp_pm_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release_net, +}; + +static int mptcp_snmp_seq_show(struct seq_file *seq, void *v) +{ + struct net *net = seq->private; + int i; + + for (i = 0; mptcp_snmp_list[i].name != NULL; i++) + seq_printf(seq, "%-32s\t%ld\n", mptcp_snmp_list[i].name, + snmp_fold_field(net->mptcp.mptcp_statistics, + mptcp_snmp_list[i].entry)); + + return 0; +} + +static int mptcp_snmp_seq_open(struct inode *inode, struct file *file) +{ + return single_open_net(inode, file, mptcp_snmp_seq_show); +} + +static const struct file_operations mptcp_snmp_seq_fops = { + .owner = THIS_MODULE, + .open = mptcp_snmp_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release_net, +}; + +static int mptcp_pm_init_net(struct net *net) +{ + net->mptcp.mptcp_statistics = alloc_percpu(struct mptcp_mib); + if (!net->mptcp.mptcp_statistics) + goto out_mptcp_mibs; + +#ifdef CONFIG_PROC_FS + net->mptcp.proc_net_mptcp = proc_net_mkdir(net, "mptcp_net", net->proc_net); + if (!net->mptcp.proc_net_mptcp) + goto out_proc_net_mptcp; + if (!proc_create("mptcp", S_IRUGO, net->mptcp.proc_net_mptcp, + &mptcp_pm_seq_fops)) + goto out_mptcp_net_mptcp; + if (!proc_create("snmp", S_IRUGO, net->mptcp.proc_net_mptcp, + &mptcp_snmp_seq_fops)) + goto out_mptcp_net_snmp; +#endif + + return 0; + +#ifdef CONFIG_PROC_FS +out_mptcp_net_snmp: + remove_proc_entry("mptcp", net->mptcp.proc_net_mptcp); +out_mptcp_net_mptcp: + remove_proc_subtree("mptcp_net", net->proc_net); + net->mptcp.proc_net_mptcp = NULL; +out_proc_net_mptcp: + free_percpu(net->mptcp.mptcp_statistics); +#endif +out_mptcp_mibs: + return -ENOMEM; +} + +static void mptcp_pm_exit_net(struct net *net) +{ + remove_proc_entry("snmp", net->mptcp.proc_net_mptcp); + remove_proc_entry("mptcp", net->mptcp.proc_net_mptcp); + remove_proc_subtree("mptcp_net", net->proc_net); + free_percpu(net->mptcp.mptcp_statistics); +} + +static struct pernet_operations mptcp_pm_proc_ops = { + .init = mptcp_pm_init_net, + .exit = mptcp_pm_exit_net, +}; + +/* General initialization of mptcp */ +void __init mptcp_init(void) +{ + int i; + struct ctl_table_header *mptcp_sysctl; + + mptcp_sock_cache = kmem_cache_create("mptcp_sock", + sizeof(struct mptcp_tcp_sock), + 0, SLAB_HWCACHE_ALIGN, + NULL); + if (!mptcp_sock_cache) + goto mptcp_sock_cache_failed; + + mptcp_cb_cache = kmem_cache_create("mptcp_cb", sizeof(struct mptcp_cb), + 0, SLAB_DESTROY_BY_RCU|SLAB_HWCACHE_ALIGN, + NULL); + if (!mptcp_cb_cache) + goto mptcp_cb_cache_failed; + + mptcp_tw_cache = kmem_cache_create("mptcp_tw", sizeof(struct mptcp_tw), + 0, SLAB_DESTROY_BY_RCU|SLAB_HWCACHE_ALIGN, + NULL); + if (!mptcp_tw_cache) + goto mptcp_tw_cache_failed; + + get_random_bytes(mptcp_secret, sizeof(mptcp_secret)); + + mptcp_wq = alloc_workqueue("mptcp_wq", WQ_UNBOUND | WQ_MEM_RECLAIM, 8); + if (!mptcp_wq) + goto alloc_workqueue_failed; + + for (i = 0; i < MPTCP_HASH_SIZE; i++) { + INIT_HLIST_NULLS_HEAD(&tk_hashtable[i], i); + INIT_HLIST_NULLS_HEAD(&mptcp_reqsk_htb[i], + i + MPTCP_REQSK_NULLS_BASE); + INIT_HLIST_NULLS_HEAD(&mptcp_reqsk_tk_htb[i], i); + } + + spin_lock_init(&mptcp_reqsk_hlock); + spin_lock_init(&mptcp_tk_hashlock); + + if (register_pernet_subsys(&mptcp_pm_proc_ops)) + goto pernet_failed; + +#if IS_ENABLED(CONFIG_IPV6) + if (mptcp_pm_v6_init()) + goto mptcp_pm_v6_failed; +#endif + if (mptcp_pm_v4_init()) + goto mptcp_pm_v4_failed; + + mptcp_sysctl = register_net_sysctl(&init_net, "net/mptcp", mptcp_table); + if (!mptcp_sysctl) + goto register_sysctl_failed; + + if (mptcp_register_path_manager(&mptcp_pm_default)) + goto register_pm_failed; + + if (mptcp_register_scheduler(&mptcp_sched_default)) + goto register_sched_failed; + + pr_info("MPTCP: Stable release v0.91"); + + mptcp_init_failed = false; + + return; + +register_sched_failed: + mptcp_unregister_path_manager(&mptcp_pm_default); +register_pm_failed: + unregister_net_sysctl_table(mptcp_sysctl); +register_sysctl_failed: + mptcp_pm_v4_undo(); +mptcp_pm_v4_failed: +#if IS_ENABLED(CONFIG_IPV6) + mptcp_pm_v6_undo(); +mptcp_pm_v6_failed: +#endif + unregister_pernet_subsys(&mptcp_pm_proc_ops); +pernet_failed: + destroy_workqueue(mptcp_wq); +alloc_workqueue_failed: + kmem_cache_destroy(mptcp_tw_cache); +mptcp_tw_cache_failed: + kmem_cache_destroy(mptcp_cb_cache); +mptcp_cb_cache_failed: + kmem_cache_destroy(mptcp_sock_cache); +mptcp_sock_cache_failed: + mptcp_init_failed = true; +} +#endif diff -urN linux-4.1.52-orig/net/mptcp/mptcp_fullmesh.c linux-4.1.52/net/mptcp/mptcp_fullmesh.c --- linux-4.1.52-orig/net/mptcp/mptcp_fullmesh.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/mptcp/mptcp_fullmesh.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,1871 @@ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#include + +#include +#include + +#if IS_ENABLED(CONFIG_IPV6) +#include +#include +#endif + +enum { + MPTCP_EVENT_ADD = 1, + MPTCP_EVENT_DEL, + MPTCP_EVENT_MOD, +}; + +#define MPTCP_SUBFLOW_RETRY_DELAY 1000 + +/* Max number of local or remote addresses we can store. + * When changing, see the bitfield below in fullmesh_rem4/6. + */ +#define MPTCP_MAX_ADDR 8 + +struct fullmesh_rem4 { + u8 rem4_id; + u8 bitfield; + u8 retry_bitfield; + __be16 port; + struct in_addr addr; +}; + +struct fullmesh_rem6 { + u8 rem6_id; + u8 bitfield; + u8 retry_bitfield; + __be16 port; + struct in6_addr addr; +}; + +struct mptcp_loc_addr { + struct mptcp_loc4 locaddr4[MPTCP_MAX_ADDR]; + u8 loc4_bits; + u8 next_v4_index; + + struct mptcp_loc6 locaddr6[MPTCP_MAX_ADDR]; + u8 loc6_bits; + u8 next_v6_index; +}; + +struct mptcp_addr_event { + struct list_head list; + unsigned short family; + u8 code:7, + low_prio:1; + int if_idx; + union inet_addr addr; +}; + +struct fullmesh_priv { + /* Worker struct for subflow establishment */ + struct work_struct subflow_work; + /* Delayed worker, when the routing-tables are not yet ready. */ + struct delayed_work subflow_retry_work; + + /* Remote addresses */ + struct fullmesh_rem4 remaddr4[MPTCP_MAX_ADDR]; + struct fullmesh_rem6 remaddr6[MPTCP_MAX_ADDR]; + + struct mptcp_cb *mpcb; + + u16 remove_addrs; /* Addresses to remove */ + u8 announced_addrs_v4; /* IPv4 Addresses we did announce */ + u8 announced_addrs_v6; /* IPv6 Addresses we did announce */ + + u8 add_addr; /* Are we sending an add_addr? */ + + u8 rem4_bits; + u8 rem6_bits; + + /* Are we established the additional subflows for primary pair? */ + u8 first_pair:1; +}; + +struct mptcp_fm_ns { + struct mptcp_loc_addr __rcu *local; + spinlock_t local_lock; /* Protecting the above pointer */ + struct list_head events; + struct delayed_work address_worker; + + struct net *net; +}; + +static int num_subflows __read_mostly = 1; +module_param(num_subflows, int, 0644); +MODULE_PARM_DESC(num_subflows, "choose the number of subflows per pair of IP addresses of MPTCP connection"); + +static struct mptcp_pm_ops full_mesh __read_mostly; + +static void full_mesh_create_subflows(struct sock *meta_sk); + +static struct mptcp_fm_ns *fm_get_ns(const struct net *net) +{ + return (struct mptcp_fm_ns *)net->mptcp.path_managers[MPTCP_PM_FULLMESH]; +} + +static struct fullmesh_priv *fullmesh_get_priv(const struct mptcp_cb *mpcb) +{ + return (struct fullmesh_priv *)&mpcb->mptcp_pm[0]; +} + +/* Find the first free index in the bitfield */ +static int __mptcp_find_free_index(u8 bitfield, u8 base) +{ + int i; + + /* There are anyways no free bits... */ + if (bitfield == 0xff) + goto exit; + + i = ffs(~(bitfield >> base)) - 1; + if (i < 0) + goto exit; + + /* No free bits when starting at base, try from 0 on */ + if (i + base >= sizeof(bitfield) * 8) + return __mptcp_find_free_index(bitfield, 0); + + return i + base; +exit: + return -1; +} + +static int mptcp_find_free_index(u8 bitfield) +{ + return __mptcp_find_free_index(bitfield, 0); +} + +static void mptcp_addv4_raddr(struct mptcp_cb *mpcb, + const struct in_addr *addr, + __be16 port, u8 id) +{ + int i; + struct fullmesh_rem4 *rem4; + struct fullmesh_priv *fmp = fullmesh_get_priv(mpcb); + + mptcp_for_each_bit_set(fmp->rem4_bits, i) { + rem4 = &fmp->remaddr4[i]; + + /* Address is already in the list --- continue */ + if (rem4->rem4_id == id && + rem4->addr.s_addr == addr->s_addr && rem4->port == port) + return; + + /* This may be the case, when the peer is behind a NAT. He is + * trying to JOIN, thus sending the JOIN with a certain ID. + * However the src_addr of the IP-packet has been changed. We + * update the addr in the list, because this is the address as + * OUR BOX sees it. + */ + if (rem4->rem4_id == id && rem4->addr.s_addr != addr->s_addr) { + /* update the address */ + mptcp_debug("%s: updating old addr:%pI4 to addr %pI4 with id:%d\n", + __func__, &rem4->addr.s_addr, + &addr->s_addr, id); + rem4->addr.s_addr = addr->s_addr; + rem4->port = port; + mpcb->list_rcvd = 1; + return; + } + } + + i = mptcp_find_free_index(fmp->rem4_bits); + /* Do we have already the maximum number of local/remote addresses? */ + if (i < 0) { + mptcp_debug("%s: At max num of remote addresses: %d --- not adding address: %pI4\n", + __func__, MPTCP_MAX_ADDR, &addr->s_addr); + return; + } + + rem4 = &fmp->remaddr4[i]; + + /* Address is not known yet, store it */ + rem4->addr.s_addr = addr->s_addr; + rem4->port = port; + rem4->bitfield = 0; + rem4->retry_bitfield = 0; + rem4->rem4_id = id; + mpcb->list_rcvd = 1; + fmp->rem4_bits |= (1 << i); + + return; +} + +static void mptcp_addv6_raddr(struct mptcp_cb *mpcb, + const struct in6_addr *addr, + __be16 port, u8 id) +{ + int i; + struct fullmesh_rem6 *rem6; + struct fullmesh_priv *fmp = fullmesh_get_priv(mpcb); + + mptcp_for_each_bit_set(fmp->rem6_bits, i) { + rem6 = &fmp->remaddr6[i]; + + /* Address is already in the list --- continue */ + if (rem6->rem6_id == id && + ipv6_addr_equal(&rem6->addr, addr) && rem6->port == port) + return; + + /* This may be the case, when the peer is behind a NAT. He is + * trying to JOIN, thus sending the JOIN with a certain ID. + * However the src_addr of the IP-packet has been changed. We + * update the addr in the list, because this is the address as + * OUR BOX sees it. + */ + if (rem6->rem6_id == id) { + /* update the address */ + mptcp_debug("%s: updating old addr: %pI6 to addr %pI6 with id:%d\n", + __func__, &rem6->addr, addr, id); + rem6->addr = *addr; + rem6->port = port; + mpcb->list_rcvd = 1; + return; + } + } + + i = mptcp_find_free_index(fmp->rem6_bits); + /* Do we have already the maximum number of local/remote addresses? */ + if (i < 0) { + mptcp_debug("%s: At max num of remote addresses: %d --- not adding address: %pI6\n", + __func__, MPTCP_MAX_ADDR, addr); + return; + } + + rem6 = &fmp->remaddr6[i]; + + /* Address is not known yet, store it */ + rem6->addr = *addr; + rem6->port = port; + rem6->bitfield = 0; + rem6->retry_bitfield = 0; + rem6->rem6_id = id; + mpcb->list_rcvd = 1; + fmp->rem6_bits |= (1 << i); + + return; +} + +static void mptcp_v4_rem_raddress(struct mptcp_cb *mpcb, u8 id) +{ + int i; + struct fullmesh_priv *fmp = fullmesh_get_priv(mpcb); + + mptcp_for_each_bit_set(fmp->rem4_bits, i) { + if (fmp->remaddr4[i].rem4_id == id) { + /* remove address from bitfield */ + fmp->rem4_bits &= ~(1 << i); + + break; + } + } +} + +static void mptcp_v6_rem_raddress(const struct mptcp_cb *mpcb, u8 id) +{ + int i; + struct fullmesh_priv *fmp = fullmesh_get_priv(mpcb); + + mptcp_for_each_bit_set(fmp->rem6_bits, i) { + if (fmp->remaddr6[i].rem6_id == id) { + /* remove address from bitfield */ + fmp->rem6_bits &= ~(1 << i); + + break; + } + } +} + +/* Sets the bitfield of the remote-address field */ +static void mptcp_v4_set_init_addr_bit(const struct mptcp_cb *mpcb, + const struct in_addr *addr, u8 index) +{ + int i; + struct fullmesh_priv *fmp = fullmesh_get_priv(mpcb); + + mptcp_for_each_bit_set(fmp->rem4_bits, i) { + if (fmp->remaddr4[i].addr.s_addr == addr->s_addr) { + fmp->remaddr4[i].bitfield |= (1 << index); + return; + } + } +} + +/* Sets the bitfield of the remote-address field */ +static void mptcp_v6_set_init_addr_bit(struct mptcp_cb *mpcb, + const struct in6_addr *addr, u8 index) +{ + int i; + struct fullmesh_priv *fmp = fullmesh_get_priv(mpcb); + + mptcp_for_each_bit_set(fmp->rem6_bits, i) { + if (ipv6_addr_equal(&fmp->remaddr6[i].addr, addr)) { + fmp->remaddr6[i].bitfield |= (1 << index); + return; + } + } +} + +static void mptcp_set_init_addr_bit(struct mptcp_cb *mpcb, + const union inet_addr *addr, + sa_family_t family, u8 id) +{ + if (family == AF_INET) + mptcp_v4_set_init_addr_bit(mpcb, &addr->in, id); + else + mptcp_v6_set_init_addr_bit(mpcb, &addr->in6, id); +} + +static void mptcp_v4_subflows(struct sock *meta_sk, + const struct mptcp_loc4 *loc, + struct mptcp_rem4 *rem) +{ + int i; + + for (i = 1; i < num_subflows; i++) + mptcp_init4_subsockets(meta_sk, loc, rem); +} + +#if IS_ENABLED(CONFIG_IPV6) +static void mptcp_v6_subflows(struct sock *meta_sk, + const struct mptcp_loc6 *loc, + struct mptcp_rem6 *rem) +{ + int i; + + for (i = 1; i < num_subflows; i++) + mptcp_init6_subsockets(meta_sk, loc, rem); +} +#endif + +static void retry_subflow_worker(struct work_struct *work) +{ + struct delayed_work *delayed_work = container_of(work, + struct delayed_work, + work); + struct fullmesh_priv *fmp = container_of(delayed_work, + struct fullmesh_priv, + subflow_retry_work); + struct mptcp_cb *mpcb = fmp->mpcb; + struct sock *meta_sk = mpcb->meta_sk; + struct mptcp_loc_addr *mptcp_local; + struct mptcp_fm_ns *fm_ns = fm_get_ns(sock_net(meta_sk)); + int iter = 0, i; + + /* We need a local (stable) copy of the address-list. Really, it is not + * such a big deal, if the address-list is not 100% up-to-date. + */ + rcu_read_lock_bh(); + mptcp_local = rcu_dereference_bh(fm_ns->local); + mptcp_local = kmemdup(mptcp_local, sizeof(*mptcp_local), GFP_ATOMIC); + rcu_read_unlock_bh(); + + if (!mptcp_local) + return; + +next_subflow: + if (iter) { + release_sock(meta_sk); + mutex_unlock(&mpcb->mpcb_mutex); + + cond_resched(); + } + mutex_lock(&mpcb->mpcb_mutex); + lock_sock_nested(meta_sk, SINGLE_DEPTH_NESTING); + + iter++; + + if (sock_flag(meta_sk, SOCK_DEAD)) + goto exit; + + mptcp_for_each_bit_set(fmp->rem4_bits, i) { + struct fullmesh_rem4 *rem = &fmp->remaddr4[i]; + /* Do we need to retry establishing a subflow ? */ + if (rem->retry_bitfield) { + int i = mptcp_find_free_index(~rem->retry_bitfield); + struct mptcp_rem4 rem4; + + rem->bitfield |= (1 << i); + rem->retry_bitfield &= ~(1 << i); + + rem4.addr = rem->addr; + rem4.port = rem->port; + rem4.rem4_id = rem->rem4_id; + + mptcp_init4_subsockets(meta_sk, &mptcp_local->locaddr4[i], &rem4); + mptcp_v4_subflows(meta_sk, + &mptcp_local->locaddr4[i], + &rem4); + goto next_subflow; + } + } + +#if IS_ENABLED(CONFIG_IPV6) + mptcp_for_each_bit_set(fmp->rem6_bits, i) { + struct fullmesh_rem6 *rem = &fmp->remaddr6[i]; + + /* Do we need to retry establishing a subflow ? */ + if (rem->retry_bitfield) { + int i = mptcp_find_free_index(~rem->retry_bitfield); + struct mptcp_rem6 rem6; + + rem->bitfield |= (1 << i); + rem->retry_bitfield &= ~(1 << i); + + rem6.addr = rem->addr; + rem6.port = rem->port; + rem6.rem6_id = rem->rem6_id; + + mptcp_init6_subsockets(meta_sk, &mptcp_local->locaddr6[i], &rem6); + mptcp_v6_subflows(meta_sk, + &mptcp_local->locaddr6[i], + &rem6); + goto next_subflow; + } + } +#endif + +exit: + kfree(mptcp_local); + release_sock(meta_sk); + mutex_unlock(&mpcb->mpcb_mutex); + sock_put(meta_sk); +} + +/** + * Create all new subflows, by doing calls to mptcp_initX_subsockets + * + * This function uses a goto next_subflow, to allow releasing the lock between + * new subflows and giving other processes a chance to do some work on the + * socket and potentially finishing the communication. + **/ +static void create_subflow_worker(struct work_struct *work) +{ + struct fullmesh_priv *fmp = container_of(work, struct fullmesh_priv, + subflow_work); + struct mptcp_cb *mpcb = fmp->mpcb; + struct sock *meta_sk = mpcb->meta_sk; + struct mptcp_loc_addr *mptcp_local; + const struct mptcp_fm_ns *fm_ns = fm_get_ns(sock_net(meta_sk)); + int iter = 0, retry = 0; + int i; + + /* We need a local (stable) copy of the address-list. Really, it is not + * such a big deal, if the address-list is not 100% up-to-date. + */ + rcu_read_lock_bh(); + mptcp_local = rcu_dereference_bh(fm_ns->local); + mptcp_local = kmemdup(mptcp_local, sizeof(*mptcp_local), GFP_ATOMIC); + rcu_read_unlock_bh(); + + if (!mptcp_local) + return; + +next_subflow: + if (iter) { + release_sock(meta_sk); + mutex_unlock(&mpcb->mpcb_mutex); + + cond_resched(); + } + mutex_lock(&mpcb->mpcb_mutex); + lock_sock_nested(meta_sk, SINGLE_DEPTH_NESTING); + + /* Create the additional subflows for the first pair */ + if (fmp->first_pair == 0 && mpcb->master_sk) { + struct mptcp_loc4 loc; + struct mptcp_rem4 rem; + + loc.addr.s_addr = inet_sk(meta_sk)->inet_saddr; + loc.loc4_id = 0; + loc.low_prio = 0; + loc.if_idx = mpcb->master_sk->sk_bound_dev_if; + + rem.addr.s_addr = inet_sk(meta_sk)->inet_daddr; + rem.port = inet_sk(meta_sk)->inet_dport; + rem.rem4_id = 0; /* Default 0 */ + + mptcp_v4_subflows(meta_sk, &loc, &rem); + + fmp->first_pair = 1; + } + iter++; + + if (sock_flag(meta_sk, SOCK_DEAD)) + goto exit; + + if (mpcb->master_sk && + !tcp_sk(mpcb->master_sk)->mptcp->fully_established) + goto exit; + + mptcp_for_each_bit_set(fmp->rem4_bits, i) { + struct fullmesh_rem4 *rem; + u8 remaining_bits; + + rem = &fmp->remaddr4[i]; + remaining_bits = ~(rem->bitfield) & mptcp_local->loc4_bits; + + /* Are there still combinations to handle? */ + if (remaining_bits) { + int i = mptcp_find_free_index(~remaining_bits); + struct mptcp_rem4 rem4; + + rem->bitfield |= (1 << i); + + rem4.addr = rem->addr; + rem4.port = rem->port; + rem4.rem4_id = rem->rem4_id; + + /* If a route is not yet available then retry once */ + if (mptcp_init4_subsockets(meta_sk, &mptcp_local->locaddr4[i], + &rem4) == -ENETUNREACH) + retry = rem->retry_bitfield |= (1 << i); + else + mptcp_v4_subflows(meta_sk, + &mptcp_local->locaddr4[i], + &rem4); + goto next_subflow; + } + } + +#if IS_ENABLED(CONFIG_IPV6) + if (fmp->first_pair == 0 && mpcb->master_sk) { + struct mptcp_loc6 loc; + struct mptcp_rem6 rem; + + loc.addr = inet6_sk(meta_sk)->saddr; + loc.loc6_id = 0; + loc.low_prio = 0; + loc.if_idx = mpcb->master_sk->sk_bound_dev_if; + + rem.addr = meta_sk->sk_v6_daddr; + rem.port = inet_sk(meta_sk)->inet_dport; + rem.rem6_id = 0; /* Default 0 */ + + mptcp_v6_subflows(meta_sk, &loc, &rem); + + fmp->first_pair = 1; + } + mptcp_for_each_bit_set(fmp->rem6_bits, i) { + struct fullmesh_rem6 *rem; + u8 remaining_bits; + + rem = &fmp->remaddr6[i]; + remaining_bits = ~(rem->bitfield) & mptcp_local->loc6_bits; + + /* Are there still combinations to handle? */ + if (remaining_bits) { + int i = mptcp_find_free_index(~remaining_bits); + struct mptcp_rem6 rem6; + + rem->bitfield |= (1 << i); + + rem6.addr = rem->addr; + rem6.port = rem->port; + rem6.rem6_id = rem->rem6_id; + + /* If a route is not yet available then retry once */ + if (mptcp_init6_subsockets(meta_sk, &mptcp_local->locaddr6[i], + &rem6) == -ENETUNREACH) + retry = rem->retry_bitfield |= (1 << i); + else + mptcp_v6_subflows(meta_sk, + &mptcp_local->locaddr6[i], + &rem6); + goto next_subflow; + } + } +#endif + + if (retry && !delayed_work_pending(&fmp->subflow_retry_work)) { + sock_hold(meta_sk); + queue_delayed_work(mptcp_wq, &fmp->subflow_retry_work, + msecs_to_jiffies(MPTCP_SUBFLOW_RETRY_DELAY)); + } + +exit: + kfree(mptcp_local); + release_sock(meta_sk); + mutex_unlock(&mpcb->mpcb_mutex); + sock_put(meta_sk); +} + +static void announce_remove_addr(u8 addr_id, struct sock *meta_sk) +{ + struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; + struct fullmesh_priv *fmp = fullmesh_get_priv(mpcb); + struct sock *sk = mptcp_select_ack_sock(meta_sk); + + fmp->remove_addrs |= (1 << addr_id); + mpcb->addr_signal = 1; + + if (sk) + tcp_send_ack(sk); +} + +static void update_addr_bitfields(struct sock *meta_sk, + const struct mptcp_loc_addr *mptcp_local) +{ + struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; + struct fullmesh_priv *fmp = fullmesh_get_priv(mpcb); + int i; + + /* The bits in announced_addrs_* always match with loc*_bits. So, a + * simply & operation unsets the correct bits, because these go from + * announced to non-announced + */ + fmp->announced_addrs_v4 &= mptcp_local->loc4_bits; + + mptcp_for_each_bit_set(fmp->rem4_bits, i) { + fmp->remaddr4[i].bitfield &= mptcp_local->loc4_bits; + fmp->remaddr4[i].retry_bitfield &= mptcp_local->loc4_bits; + } + + fmp->announced_addrs_v6 &= mptcp_local->loc6_bits; + + mptcp_for_each_bit_set(fmp->rem6_bits, i) { + fmp->remaddr6[i].bitfield &= mptcp_local->loc6_bits; + fmp->remaddr6[i].retry_bitfield &= mptcp_local->loc6_bits; + } +} + +static int mptcp_find_address(const struct mptcp_loc_addr *mptcp_local, + sa_family_t family, const union inet_addr *addr, + int if_idx) +{ + int i; + u8 loc_bits; + bool found = false; + + if (family == AF_INET) + loc_bits = mptcp_local->loc4_bits; + else + loc_bits = mptcp_local->loc6_bits; + + mptcp_for_each_bit_set(loc_bits, i) { + if (family == AF_INET && + (!if_idx || mptcp_local->locaddr4[i].if_idx == if_idx) && + mptcp_local->locaddr4[i].addr.s_addr == addr->in.s_addr) { + found = true; + break; + } + if (family == AF_INET6 && + (!if_idx || mptcp_local->locaddr6[i].if_idx == if_idx) && + ipv6_addr_equal(&mptcp_local->locaddr6[i].addr, + &addr->in6)) { + found = true; + break; + } + } + + if (!found) + return -1; + + return i; +} + +static void mptcp_address_worker(struct work_struct *work) +{ + const struct delayed_work *delayed_work = container_of(work, + struct delayed_work, + work); + struct mptcp_fm_ns *fm_ns = container_of(delayed_work, + struct mptcp_fm_ns, + address_worker); + struct net *net = fm_ns->net; + struct mptcp_addr_event *event = NULL; + struct mptcp_loc_addr *mptcp_local, *old; + int i, id = -1; /* id is used in the socket-code on a delete-event */ + bool success; /* Used to indicate if we succeeded handling the event */ + +next_event: + success = false; + kfree(event); + + /* First, let's dequeue an event from our event-list */ + rcu_read_lock_bh(); + spin_lock(&fm_ns->local_lock); + + event = list_first_entry_or_null(&fm_ns->events, + struct mptcp_addr_event, list); + if (!event) { + spin_unlock(&fm_ns->local_lock); + rcu_read_unlock_bh(); + return; + } + + list_del(&event->list); + + mptcp_local = rcu_dereference_bh(fm_ns->local); + + if (event->code == MPTCP_EVENT_DEL) { + id = mptcp_find_address(mptcp_local, event->family, + &event->addr, event->if_idx); + + /* Not in the list - so we don't care */ + if (id < 0) { + mptcp_debug("%s could not find id\n", __func__); + goto duno; + } + + old = mptcp_local; + mptcp_local = kmemdup(mptcp_local, sizeof(*mptcp_local), + GFP_ATOMIC); + if (!mptcp_local) + goto duno; + + if (event->family == AF_INET) + mptcp_local->loc4_bits &= ~(1 << id); + else + mptcp_local->loc6_bits &= ~(1 << id); + + rcu_assign_pointer(fm_ns->local, mptcp_local); + kfree(old); + } else { + int i = mptcp_find_address(mptcp_local, event->family, + &event->addr, event->if_idx); + int j = i; + + if (j < 0) { + /* Not in the list, so we have to find an empty slot */ + if (event->family == AF_INET) + i = __mptcp_find_free_index(mptcp_local->loc4_bits, + mptcp_local->next_v4_index); + if (event->family == AF_INET6) + i = __mptcp_find_free_index(mptcp_local->loc6_bits, + mptcp_local->next_v6_index); + + if (i < 0) { + mptcp_debug("%s no more space\n", __func__); + goto duno; + } + + /* It might have been a MOD-event. */ + event->code = MPTCP_EVENT_ADD; + } else { + /* Let's check if anything changes */ + if (event->family == AF_INET && + event->low_prio == mptcp_local->locaddr4[i].low_prio) + goto duno; + + if (event->family == AF_INET6 && + event->low_prio == mptcp_local->locaddr6[i].low_prio) + goto duno; + } + + old = mptcp_local; + mptcp_local = kmemdup(mptcp_local, sizeof(*mptcp_local), + GFP_ATOMIC); + if (!mptcp_local) + goto duno; + + if (event->family == AF_INET) { + mptcp_local->locaddr4[i].addr.s_addr = event->addr.in.s_addr; + mptcp_local->locaddr4[i].loc4_id = i + 1; + mptcp_local->locaddr4[i].low_prio = event->low_prio; + mptcp_local->locaddr4[i].if_idx = event->if_idx; + } else { + mptcp_local->locaddr6[i].addr = event->addr.in6; + mptcp_local->locaddr6[i].loc6_id = i + MPTCP_MAX_ADDR; + mptcp_local->locaddr6[i].low_prio = event->low_prio; + mptcp_local->locaddr6[i].if_idx = event->if_idx; + } + + if (j < 0) { + if (event->family == AF_INET) { + mptcp_local->loc4_bits |= (1 << i); + mptcp_local->next_v4_index = i + 1; + } else { + mptcp_local->loc6_bits |= (1 << i); + mptcp_local->next_v6_index = i + 1; + } + } + + rcu_assign_pointer(fm_ns->local, mptcp_local); + kfree(old); + } + success = true; + +duno: + spin_unlock(&fm_ns->local_lock); + rcu_read_unlock_bh(); + + if (!success) + goto next_event; + + /* Now we iterate over the MPTCP-sockets and apply the event. */ + for (i = 0; i < MPTCP_HASH_SIZE; i++) { + const struct hlist_nulls_node *node; + struct tcp_sock *meta_tp; + + rcu_read_lock_bh(); + hlist_nulls_for_each_entry_rcu(meta_tp, node, &tk_hashtable[i], + tk_table) { + struct mptcp_cb *mpcb = meta_tp->mpcb; + struct sock *meta_sk = (struct sock *)meta_tp, *sk; + struct fullmesh_priv *fmp = fullmesh_get_priv(mpcb); + bool meta_v4 = meta_sk->sk_family == AF_INET; + + if (sock_net(meta_sk) != net) + continue; + + if (meta_v4) { + /* skip IPv6 events if meta is IPv4 */ + if (event->family == AF_INET6) + continue; + } + /* skip IPv4 events if IPV6_V6ONLY is set */ + else if (event->family == AF_INET && meta_sk->sk_ipv6only) + continue; + + if (unlikely(!atomic_inc_not_zero(&meta_sk->sk_refcnt))) + continue; + + bh_lock_sock(meta_sk); + + if (!mptcp(meta_tp) || !is_meta_sk(meta_sk) || + mpcb->infinite_mapping_snd || + mpcb->infinite_mapping_rcv || + mpcb->send_infinite_mapping) + goto next; + + /* May be that the pm has changed in-between */ + if (mpcb->pm_ops != &full_mesh) + goto next; + + if (sock_owned_by_user(meta_sk)) { + if (!test_and_set_bit(MPTCP_PATH_MANAGER, + &meta_tp->tsq_flags)) + sock_hold(meta_sk); + + goto next; + } + + if (event->code == MPTCP_EVENT_ADD) { + fmp->add_addr++; + mpcb->addr_signal = 1; + + sk = mptcp_select_ack_sock(meta_sk); + if (sk) + tcp_send_ack(sk); + + full_mesh_create_subflows(meta_sk); + } + + if (event->code == MPTCP_EVENT_DEL) { + struct sock *sk, *tmpsk; + struct mptcp_loc_addr *mptcp_local; + bool found = false; + + mptcp_local = rcu_dereference_bh(fm_ns->local); + + /* In any case, we need to update our bitfields */ + if (id >= 0) + update_addr_bitfields(meta_sk, mptcp_local); + + /* Look for the socket and remove him */ + mptcp_for_each_sk_safe(mpcb, sk, tmpsk) { + if ((event->family == AF_INET6 && + (sk->sk_family == AF_INET || + mptcp_v6_is_v4_mapped(sk))) || + (event->family == AF_INET && + (sk->sk_family == AF_INET6 && + !mptcp_v6_is_v4_mapped(sk)))) + continue; + + if (event->family == AF_INET && + (sk->sk_family == AF_INET || + mptcp_v6_is_v4_mapped(sk)) && + inet_sk(sk)->inet_saddr != event->addr.in.s_addr) + continue; + + if (event->family == AF_INET6 && + sk->sk_family == AF_INET6 && + !ipv6_addr_equal(&inet6_sk(sk)->saddr, &event->addr.in6)) + continue; + + /* Reinject, so that pf = 1 and so we + * won't select this one as the + * ack-sock. + */ + mptcp_reinject_data(sk, 0); + + /* We announce the removal of this id */ + announce_remove_addr(tcp_sk(sk)->mptcp->loc_id, meta_sk); + + mptcp_sub_force_close(sk); + found = true; + } + + if (found) + goto next; + + /* The id may have been given by the event, + * matching on a local address. And it may not + * have matched on one of the above sockets, + * because the client never created a subflow. + * So, we have to finally remove it here. + */ + if (id > 0) + announce_remove_addr(id, meta_sk); + } + + if (event->code == MPTCP_EVENT_MOD) { + struct sock *sk; + + mptcp_for_each_sk(mpcb, sk) { + struct tcp_sock *tp = tcp_sk(sk); + if (event->family == AF_INET && + (sk->sk_family == AF_INET || + mptcp_v6_is_v4_mapped(sk)) && + inet_sk(sk)->inet_saddr == event->addr.in.s_addr) { + if (event->low_prio != tp->mptcp->low_prio) { + tp->mptcp->send_mp_prio = 1; + tp->mptcp->low_prio = event->low_prio; + + tcp_send_ack(sk); + } + } + + if (event->family == AF_INET6 && + sk->sk_family == AF_INET6 && + !ipv6_addr_equal(&inet6_sk(sk)->saddr, &event->addr.in6)) { + if (event->low_prio != tp->mptcp->low_prio) { + tp->mptcp->send_mp_prio = 1; + tp->mptcp->low_prio = event->low_prio; + + tcp_send_ack(sk); + } + } + } + } +next: + bh_unlock_sock(meta_sk); + sock_put(meta_sk); + } + rcu_read_unlock_bh(); + } + goto next_event; +} + +static struct mptcp_addr_event *lookup_similar_event(const struct net *net, + const struct mptcp_addr_event *event) +{ + struct mptcp_addr_event *eventq; + struct mptcp_fm_ns *fm_ns = fm_get_ns(net); + + list_for_each_entry(eventq, &fm_ns->events, list) { + if (eventq->family != event->family) + continue; + if (event->family == AF_INET) { + if (eventq->addr.in.s_addr == event->addr.in.s_addr) + return eventq; + } else { + if (ipv6_addr_equal(&eventq->addr.in6, &event->addr.in6)) + return eventq; + } + } + return NULL; +} + +/* We already hold the net-namespace MPTCP-lock */ +static void add_pm_event(struct net *net, const struct mptcp_addr_event *event) +{ + struct mptcp_addr_event *eventq = lookup_similar_event(net, event); + struct mptcp_fm_ns *fm_ns = fm_get_ns(net); + + if (eventq) { + switch (event->code) { + case MPTCP_EVENT_DEL: + mptcp_debug("%s del old_code %u\n", __func__, eventq->code); + list_del(&eventq->list); + kfree(eventq); + break; + case MPTCP_EVENT_ADD: + mptcp_debug("%s add old_code %u\n", __func__, eventq->code); + eventq->low_prio = event->low_prio; + eventq->code = MPTCP_EVENT_ADD; + return; + case MPTCP_EVENT_MOD: + mptcp_debug("%s mod old_code %u\n", __func__, eventq->code); + eventq->low_prio = event->low_prio; + eventq->code = MPTCP_EVENT_MOD; + return; + } + } + + /* OK, we have to add the new address to the wait queue */ + eventq = kmemdup(event, sizeof(struct mptcp_addr_event), GFP_ATOMIC); + if (!eventq) + return; + + list_add_tail(&eventq->list, &fm_ns->events); + + /* Create work-queue */ + if (!delayed_work_pending(&fm_ns->address_worker)) + queue_delayed_work(mptcp_wq, &fm_ns->address_worker, + msecs_to_jiffies(500)); +} + +static void addr4_event_handler(const struct in_ifaddr *ifa, unsigned long event, + struct net *net) +{ + const struct net_device *netdev = ifa->ifa_dev->dev; + struct mptcp_fm_ns *fm_ns = fm_get_ns(net); + struct mptcp_addr_event mpevent; + + if (ifa->ifa_scope > RT_SCOPE_LINK || + ipv4_is_loopback(ifa->ifa_local)) + return; + + spin_lock_bh(&fm_ns->local_lock); + + mpevent.family = AF_INET; + mpevent.addr.in.s_addr = ifa->ifa_local; + mpevent.low_prio = (netdev->flags & IFF_MPBACKUP) ? 1 : 0; + mpevent.if_idx = netdev->ifindex; + + if (event == NETDEV_DOWN || !netif_running(netdev) || + (netdev->flags & IFF_NOMULTIPATH) || !(netdev->flags & IFF_UP)) + mpevent.code = MPTCP_EVENT_DEL; + else if (event == NETDEV_UP) + mpevent.code = MPTCP_EVENT_ADD; + else if (event == NETDEV_CHANGE) + mpevent.code = MPTCP_EVENT_MOD; + + mptcp_debug("%s created event for %pI4, code %u prio %u\n", __func__, + &ifa->ifa_local, mpevent.code, mpevent.low_prio); + add_pm_event(net, &mpevent); + + spin_unlock_bh(&fm_ns->local_lock); + return; +} + +/* React on IPv4-addr add/rem-events */ +static int mptcp_pm_inetaddr_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + const struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; + struct net *net = dev_net(ifa->ifa_dev->dev); + + if (!(event == NETDEV_UP || event == NETDEV_DOWN || + event == NETDEV_CHANGE)) + return NOTIFY_DONE; + + addr4_event_handler(ifa, event, net); + + return NOTIFY_DONE; +} + +static struct notifier_block mptcp_pm_inetaddr_notifier = { + .notifier_call = mptcp_pm_inetaddr_event, +}; + +#if IS_ENABLED(CONFIG_IPV6) + +/* IPV6-related address/interface watchers */ +struct mptcp_dad_data { + struct timer_list timer; + struct inet6_ifaddr *ifa; +}; + +static void dad_callback(unsigned long arg); +static int inet6_addr_event(struct notifier_block *this, + unsigned long event, void *ptr); + +static bool ipv6_dad_finished(const struct inet6_ifaddr *ifa) +{ + return !(ifa->flags & IFA_F_TENTATIVE) || + ifa->state > INET6_IFADDR_STATE_DAD; +} + +static void dad_init_timer(struct mptcp_dad_data *data, + struct inet6_ifaddr *ifa) +{ + data->ifa = ifa; + data->timer.data = (unsigned long)data; + data->timer.function = dad_callback; + if (ifa->idev->cnf.rtr_solicit_delay) + data->timer.expires = jiffies + ifa->idev->cnf.rtr_solicit_delay; + else + data->timer.expires = jiffies + (HZ/10); +} + +static void dad_callback(unsigned long arg) +{ + struct mptcp_dad_data *data = (struct mptcp_dad_data *)arg; + + /* DAD failed or IP brought down? */ + if (data->ifa->state == INET6_IFADDR_STATE_ERRDAD || + data->ifa->state == INET6_IFADDR_STATE_DEAD) + goto exit; + + if (!ipv6_dad_finished(data->ifa)) { + dad_init_timer(data, data->ifa); + add_timer(&data->timer); + return; + } + + inet6_addr_event(NULL, NETDEV_UP, data->ifa); + +exit: + in6_ifa_put(data->ifa); + kfree(data); +} + +static inline void dad_setup_timer(struct inet6_ifaddr *ifa) +{ + struct mptcp_dad_data *data; + + data = kmalloc(sizeof(*data), GFP_ATOMIC); + + if (!data) + return; + + init_timer(&data->timer); + dad_init_timer(data, ifa); + add_timer(&data->timer); + in6_ifa_hold(ifa); +} + +static void addr6_event_handler(const struct inet6_ifaddr *ifa, unsigned long event, + struct net *net) +{ + const struct net_device *netdev = ifa->idev->dev; + int addr_type = ipv6_addr_type(&ifa->addr); + struct mptcp_fm_ns *fm_ns = fm_get_ns(net); + struct mptcp_addr_event mpevent; + + if (ifa->scope > RT_SCOPE_LINK || + addr_type == IPV6_ADDR_ANY || + (addr_type & IPV6_ADDR_LOOPBACK) || + (addr_type & IPV6_ADDR_LINKLOCAL)) + return; + + spin_lock_bh(&fm_ns->local_lock); + + mpevent.family = AF_INET6; + mpevent.addr.in6 = ifa->addr; + mpevent.low_prio = (netdev->flags & IFF_MPBACKUP) ? 1 : 0; + mpevent.if_idx = netdev->ifindex; + + if (event == NETDEV_DOWN || !netif_running(netdev) || + (netdev->flags & IFF_NOMULTIPATH) || !(netdev->flags & IFF_UP)) + mpevent.code = MPTCP_EVENT_DEL; + else if (event == NETDEV_UP) + mpevent.code = MPTCP_EVENT_ADD; + else if (event == NETDEV_CHANGE) + mpevent.code = MPTCP_EVENT_MOD; + + mptcp_debug("%s created event for %pI6, code %u prio %u\n", __func__, + &ifa->addr, mpevent.code, mpevent.low_prio); + add_pm_event(net, &mpevent); + + spin_unlock_bh(&fm_ns->local_lock); + return; +} + +/* React on IPv6-addr add/rem-events */ +static int inet6_addr_event(struct notifier_block *this, unsigned long event, + void *ptr) +{ + struct inet6_ifaddr *ifa6 = (struct inet6_ifaddr *)ptr; + struct net *net = dev_net(ifa6->idev->dev); + + if (!(event == NETDEV_UP || event == NETDEV_DOWN || + event == NETDEV_CHANGE)) + return NOTIFY_DONE; + + if (!ipv6_dad_finished(ifa6)) + dad_setup_timer(ifa6); + else + addr6_event_handler(ifa6, event, net); + + return NOTIFY_DONE; +} + +static struct notifier_block inet6_addr_notifier = { + .notifier_call = inet6_addr_event, +}; + +#endif + +/* React on ifup/down-events */ +static int netdev_event(struct notifier_block *this, unsigned long event, + void *ptr) +{ + const struct net_device *dev = netdev_notifier_info_to_dev(ptr); + struct in_device *in_dev; +#if IS_ENABLED(CONFIG_IPV6) + struct inet6_dev *in6_dev; +#endif + + if (!(event == NETDEV_UP || event == NETDEV_DOWN || + event == NETDEV_CHANGE)) + return NOTIFY_DONE; + + rcu_read_lock(); + in_dev = __in_dev_get_rtnl(dev); + + if (in_dev) { + for_ifa(in_dev) { + mptcp_pm_inetaddr_event(NULL, event, ifa); + } endfor_ifa(in_dev); + } + +#if IS_ENABLED(CONFIG_IPV6) + in6_dev = __in6_dev_get(dev); + + if (in6_dev) { + struct inet6_ifaddr *ifa6; + list_for_each_entry(ifa6, &in6_dev->addr_list, if_list) + inet6_addr_event(NULL, event, ifa6); + } +#endif + + rcu_read_unlock(); + return NOTIFY_DONE; +} + +static struct notifier_block mptcp_pm_netdev_notifier = { + .notifier_call = netdev_event, +}; + +static void full_mesh_add_raddr(struct mptcp_cb *mpcb, + const union inet_addr *addr, + sa_family_t family, __be16 port, u8 id) +{ + if (family == AF_INET) + mptcp_addv4_raddr(mpcb, &addr->in, port, id); + else + mptcp_addv6_raddr(mpcb, &addr->in6, port, id); +} + +static void full_mesh_new_session(const struct sock *meta_sk) +{ + struct mptcp_loc_addr *mptcp_local; + struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; + struct fullmesh_priv *fmp = fullmesh_get_priv(mpcb); + const struct mptcp_fm_ns *fm_ns = fm_get_ns(sock_net(meta_sk)); + struct tcp_sock *master_tp = tcp_sk(mpcb->master_sk); + int i, index, if_idx=0; + union inet_addr saddr, daddr; + sa_family_t family; + bool meta_v4 = meta_sk->sk_family == AF_INET; + + /* Init local variables necessary for the rest */ + if (meta_sk->sk_family == AF_INET || mptcp_v6_is_v4_mapped(meta_sk)) { + saddr.ip = inet_sk(meta_sk)->inet_saddr; + daddr.ip = inet_sk(meta_sk)->inet_daddr; + if_idx = mpcb->master_sk->sk_bound_dev_if; + family = AF_INET; +#if IS_ENABLED(CONFIG_IPV6) + } else { + saddr.in6 = inet6_sk(meta_sk)->saddr; + daddr.in6 = meta_sk->sk_v6_daddr; + if_idx = mpcb->master_sk->sk_bound_dev_if; + family = AF_INET6; +#endif + } + + rcu_read_lock(); + mptcp_local = rcu_dereference(fm_ns->local); + + index = mptcp_find_address(mptcp_local, family, &saddr, if_idx); + if (index < 0) + goto fallback; + + if (family == AF_INET) + master_tp->mptcp->low_prio = mptcp_local->locaddr4[index].low_prio; + else + master_tp->mptcp->low_prio = mptcp_local->locaddr6[index].low_prio; + master_tp->mptcp->send_mp_prio = master_tp->mptcp->low_prio; + + full_mesh_add_raddr(mpcb, &daddr, family, 0, 0); + mptcp_set_init_addr_bit(mpcb, &daddr, family, index); + + /* Initialize workqueue-struct */ + INIT_WORK(&fmp->subflow_work, create_subflow_worker); + INIT_DELAYED_WORK(&fmp->subflow_retry_work, retry_subflow_worker); + fmp->mpcb = mpcb; + + if (!meta_v4 && meta_sk->sk_ipv6only) + goto skip_ipv4; + + /* Look for the address among the local addresses */ + mptcp_for_each_bit_set(mptcp_local->loc4_bits, i) { + __be32 ifa_address = mptcp_local->locaddr4[i].addr.s_addr; + + /* We do not need to announce the initial subflow's address again */ + if (family == AF_INET && + (!if_idx || mptcp_local->locaddr4[i].if_idx == if_idx) && + saddr.ip == ifa_address) + continue; + + fmp->add_addr++; + mpcb->addr_signal = 1; + } + +skip_ipv4: +#if IS_ENABLED(CONFIG_IPV6) + /* skip IPv6 addresses if meta-socket is IPv4 */ + if (meta_v4) + goto skip_ipv6; + + mptcp_for_each_bit_set(mptcp_local->loc6_bits, i) { + const struct in6_addr *ifa6 = &mptcp_local->locaddr6[i].addr; + + /* We do not need to announce the initial subflow's address again */ + if (family == AF_INET6 && + (!if_idx || mptcp_local->locaddr6[i].if_idx == if_idx) && + ipv6_addr_equal(&saddr.in6, ifa6)) + continue; + + fmp->add_addr++; + mpcb->addr_signal = 1; + } + +skip_ipv6: +#endif + + rcu_read_unlock(); + + if (family == AF_INET) + fmp->announced_addrs_v4 |= (1 << index); + else + fmp->announced_addrs_v6 |= (1 << index); + + for (i = fmp->add_addr; i && fmp->add_addr; i--) + tcp_send_ack(mpcb->master_sk); + + if (master_tp->mptcp->send_mp_prio) + tcp_send_ack(mpcb->master_sk); + + return; + +fallback: + rcu_read_unlock(); + mptcp_fallback_default(mpcb); + return; +} + +static void full_mesh_create_subflows(struct sock *meta_sk) +{ + const struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; + struct fullmesh_priv *fmp = fullmesh_get_priv(mpcb); + + if (mpcb->infinite_mapping_snd || mpcb->infinite_mapping_rcv || + mpcb->send_infinite_mapping || + mpcb->server_side || sock_flag(meta_sk, SOCK_DEAD)) + return; + + if (mpcb->master_sk && + !tcp_sk(mpcb->master_sk)->mptcp->fully_established) + return; + + if (!work_pending(&fmp->subflow_work)) { + sock_hold(meta_sk); + queue_work(mptcp_wq, &fmp->subflow_work); + } +} + +/* Called upon release_sock, if the socket was owned by the user during + * a path-management event. + */ +static void full_mesh_release_sock(struct sock *meta_sk) +{ + struct mptcp_loc_addr *mptcp_local; + struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; + struct fullmesh_priv *fmp = fullmesh_get_priv(mpcb); + const struct mptcp_fm_ns *fm_ns = fm_get_ns(sock_net(meta_sk)); + struct sock *sk, *tmpsk; + bool meta_v4 = meta_sk->sk_family == AF_INET; + int i; + + rcu_read_lock(); + mptcp_local = rcu_dereference(fm_ns->local); + + if (!meta_v4 && meta_sk->sk_ipv6only) + goto skip_ipv4; + + /* First, detect modifications or additions */ + mptcp_for_each_bit_set(mptcp_local->loc4_bits, i) { + struct in_addr ifa = mptcp_local->locaddr4[i].addr; + bool found = false; + + mptcp_for_each_sk(mpcb, sk) { + struct tcp_sock *tp = tcp_sk(sk); + + if (sk->sk_family == AF_INET6 && + !mptcp_v6_is_v4_mapped(sk)) + continue; + + if (inet_sk(sk)->inet_saddr != ifa.s_addr) + continue; + + found = true; + + if (mptcp_local->locaddr4[i].low_prio != tp->mptcp->low_prio) { + tp->mptcp->send_mp_prio = 1; + tp->mptcp->low_prio = mptcp_local->locaddr4[i].low_prio; + + tcp_send_ack(sk); + } + } + + if (!found) { + fmp->add_addr++; + mpcb->addr_signal = 1; + + sk = mptcp_select_ack_sock(meta_sk); + if (sk) + tcp_send_ack(sk); + full_mesh_create_subflows(meta_sk); + } + } + +skip_ipv4: +#if IS_ENABLED(CONFIG_IPV6) + /* skip IPv6 addresses if meta-socket is IPv4 */ + if (meta_v4) + goto removal; + + mptcp_for_each_bit_set(mptcp_local->loc6_bits, i) { + struct in6_addr ifa = mptcp_local->locaddr6[i].addr; + bool found = false; + + mptcp_for_each_sk(mpcb, sk) { + struct tcp_sock *tp = tcp_sk(sk); + + if (sk->sk_family == AF_INET || + mptcp_v6_is_v4_mapped(sk)) + continue; + + if (!ipv6_addr_equal(&inet6_sk(sk)->saddr, &ifa)) + continue; + + found = true; + + if (mptcp_local->locaddr6[i].low_prio != tp->mptcp->low_prio) { + tp->mptcp->send_mp_prio = 1; + tp->mptcp->low_prio = mptcp_local->locaddr6[i].low_prio; + + tcp_send_ack(sk); + } + } + + if (!found) { + fmp->add_addr++; + mpcb->addr_signal = 1; + + sk = mptcp_select_ack_sock(meta_sk); + if (sk) + tcp_send_ack(sk); + full_mesh_create_subflows(meta_sk); + } + } + +removal: +#endif + + /* Now, detect address-removals */ + mptcp_for_each_sk_safe(mpcb, sk, tmpsk) { + bool shall_remove = true; + + if (sk->sk_family == AF_INET || mptcp_v6_is_v4_mapped(sk)) { + mptcp_for_each_bit_set(mptcp_local->loc4_bits, i) { + if (inet_sk(sk)->inet_saddr == mptcp_local->locaddr4[i].addr.s_addr) { + shall_remove = false; + break; + } + } + } else { + mptcp_for_each_bit_set(mptcp_local->loc6_bits, i) { + if (ipv6_addr_equal(&inet6_sk(sk)->saddr, &mptcp_local->locaddr6[i].addr)) { + shall_remove = false; + break; + } + } + } + + if (shall_remove) { + /* Reinject, so that pf = 1 and so we + * won't select this one as the + * ack-sock. + */ + mptcp_reinject_data(sk, 0); + + announce_remove_addr(tcp_sk(sk)->mptcp->loc_id, + meta_sk); + + mptcp_sub_force_close(sk); + } + } + + /* Just call it optimistically. It actually cannot do any harm */ + update_addr_bitfields(meta_sk, mptcp_local); + + rcu_read_unlock(); +} + +static int full_mesh_get_local_id(sa_family_t family, union inet_addr *addr, + struct net *net, bool *low_prio) +{ + struct mptcp_loc_addr *mptcp_local; + const struct mptcp_fm_ns *fm_ns = fm_get_ns(net); + int index, id = -1; + + /* Handle the backup-flows */ + rcu_read_lock(); + mptcp_local = rcu_dereference(fm_ns->local); + + index = mptcp_find_address(mptcp_local, family, addr, 0); + + if (index != -1) { + if (family == AF_INET) { + id = mptcp_local->locaddr4[index].loc4_id; + *low_prio = mptcp_local->locaddr4[index].low_prio; + } else { + id = mptcp_local->locaddr6[index].loc6_id; + *low_prio = mptcp_local->locaddr6[index].low_prio; + } + } + + + rcu_read_unlock(); + + return id; +} + +static void full_mesh_addr_signal(struct sock *sk, unsigned *size, + struct tcp_out_options *opts, + struct sk_buff *skb) +{ + const struct tcp_sock *tp = tcp_sk(sk); + struct mptcp_cb *mpcb = tp->mpcb; + struct sock *meta_sk = mpcb->meta_sk; + struct fullmesh_priv *fmp = fullmesh_get_priv(mpcb); + struct mptcp_loc_addr *mptcp_local; + struct mptcp_fm_ns *fm_ns = fm_get_ns(sock_net(sk)); + int remove_addr_len; + u8 unannouncedv4 = 0, unannouncedv6 = 0; + bool meta_v4 = meta_sk->sk_family == AF_INET; + + mpcb->addr_signal = 0; + + if (likely(!fmp->add_addr)) + goto remove_addr; + + rcu_read_lock(); + mptcp_local = rcu_dereference(fm_ns->local); + + if (!meta_v4 && meta_sk->sk_ipv6only) + goto skip_ipv4; + + /* IPv4 */ + unannouncedv4 = (~fmp->announced_addrs_v4) & mptcp_local->loc4_bits; + if (unannouncedv4 && + ((mpcb->mptcp_ver == MPTCP_VERSION_0 && + MAX_TCP_OPTION_SPACE - *size >= MPTCP_SUB_LEN_ADD_ADDR4_ALIGN) || + (mpcb->mptcp_ver >= MPTCP_VERSION_1 && + MAX_TCP_OPTION_SPACE - *size >= MPTCP_SUB_LEN_ADD_ADDR4_ALIGN_VER1))) { + int ind = mptcp_find_free_index(~unannouncedv4); + + opts->options |= OPTION_MPTCP; + opts->mptcp_options |= OPTION_ADD_ADDR; + opts->add_addr4.addr_id = mptcp_local->locaddr4[ind].loc4_id; + opts->add_addr4.addr = mptcp_local->locaddr4[ind].addr; + opts->add_addr_v4 = 1; + if (mpcb->mptcp_ver >= MPTCP_VERSION_1) { + u8 mptcp_hash_mac[20]; + u8 no_key[8]; + + *(u64 *)no_key = 0; + mptcp_hmac_sha1((u8 *)&mpcb->mptcp_loc_key, + (u8 *)no_key, + (u32 *)mptcp_hash_mac, 2, + 1, (u8 *)&mptcp_local->locaddr4[ind].loc4_id, + 4, (u8 *)&opts->add_addr4.addr.s_addr); + opts->add_addr4.trunc_mac = *(u64 *)mptcp_hash_mac; + } + + if (skb) { + fmp->announced_addrs_v4 |= (1 << ind); + fmp->add_addr--; + } + + if (mpcb->mptcp_ver < MPTCP_VERSION_1) + *size += MPTCP_SUB_LEN_ADD_ADDR4_ALIGN; + if (mpcb->mptcp_ver >= MPTCP_VERSION_1) + *size += MPTCP_SUB_LEN_ADD_ADDR4_ALIGN_VER1; + } + + if (meta_v4) + goto skip_ipv6; +skip_ipv4: + /* IPv6 */ + unannouncedv6 = (~fmp->announced_addrs_v6) & mptcp_local->loc6_bits; + if (unannouncedv6 && + ((mpcb->mptcp_ver == MPTCP_VERSION_0 && + MAX_TCP_OPTION_SPACE - *size >= MPTCP_SUB_LEN_ADD_ADDR6_ALIGN) || + (mpcb->mptcp_ver >= MPTCP_VERSION_1 && + MAX_TCP_OPTION_SPACE - *size >= MPTCP_SUB_LEN_ADD_ADDR6_ALIGN_VER1))) { + int ind = mptcp_find_free_index(~unannouncedv6); + + opts->options |= OPTION_MPTCP; + opts->mptcp_options |= OPTION_ADD_ADDR; + opts->add_addr6.addr_id = mptcp_local->locaddr6[ind].loc6_id; + opts->add_addr6.addr = mptcp_local->locaddr6[ind].addr; + opts->add_addr_v6 = 1; + if (mpcb->mptcp_ver >= MPTCP_VERSION_1) { + u8 mptcp_hash_mac[20]; + u8 no_key[8]; + + *(u64 *)no_key = 0; + mptcp_hmac_sha1((u8 *)&mpcb->mptcp_loc_key, + (u8 *)no_key, + (u32 *)mptcp_hash_mac, 2, + 1, (u8 *)&mptcp_local->locaddr6[ind].loc6_id, + 16, (u8 *)&opts->add_addr6.addr.s6_addr); + opts->add_addr6.trunc_mac = *(u64 *)mptcp_hash_mac; + } + + if (skb) { + fmp->announced_addrs_v6 |= (1 << ind); + fmp->add_addr--; + } + if (mpcb->mptcp_ver < MPTCP_VERSION_1) + *size += MPTCP_SUB_LEN_ADD_ADDR6_ALIGN; + if (mpcb->mptcp_ver >= MPTCP_VERSION_1) + *size += MPTCP_SUB_LEN_ADD_ADDR6_ALIGN_VER1; + } + +skip_ipv6: + rcu_read_unlock(); + + if (!unannouncedv4 && !unannouncedv6 && skb) + fmp->add_addr--; + +remove_addr: + if (likely(!fmp->remove_addrs)) + goto exit; + + remove_addr_len = mptcp_sub_len_remove_addr_align(fmp->remove_addrs); + if (MAX_TCP_OPTION_SPACE - *size < remove_addr_len) + goto exit; + + opts->options |= OPTION_MPTCP; + opts->mptcp_options |= OPTION_REMOVE_ADDR; + opts->remove_addrs = fmp->remove_addrs; + *size += remove_addr_len; + if (skb) + fmp->remove_addrs = 0; + +exit: + mpcb->addr_signal = !!(fmp->add_addr || fmp->remove_addrs); +} + +static void full_mesh_rem_raddr(struct mptcp_cb *mpcb, u8 rem_id) +{ + mptcp_v4_rem_raddress(mpcb, rem_id); + mptcp_v6_rem_raddress(mpcb, rem_id); +} + +/* Output /proc/net/mptcp_fullmesh */ +static int mptcp_fm_seq_show(struct seq_file *seq, void *v) +{ + const struct net *net = seq->private; + struct mptcp_loc_addr *mptcp_local; + const struct mptcp_fm_ns *fm_ns = fm_get_ns(net); + int i; + + seq_printf(seq, "Index, Address-ID, Backup, IP-address\n"); + + rcu_read_lock_bh(); + mptcp_local = rcu_dereference(fm_ns->local); + + seq_printf(seq, "IPv4, next v4-index: %u\n", mptcp_local->next_v4_index); + + mptcp_for_each_bit_set(mptcp_local->loc4_bits, i) { + struct mptcp_loc4 *loc4 = &mptcp_local->locaddr4[i]; + + seq_printf(seq, "%u, %u, %u, %pI4\n", i, loc4->loc4_id, + loc4->low_prio, &loc4->addr); + } + + seq_printf(seq, "IPv6, next v6-index: %u\n", mptcp_local->next_v6_index); + + mptcp_for_each_bit_set(mptcp_local->loc6_bits, i) { + struct mptcp_loc6 *loc6 = &mptcp_local->locaddr6[i]; + + seq_printf(seq, "%u, %u, %u, %pI6\n", i, loc6->loc6_id, + loc6->low_prio, &loc6->addr); + } + rcu_read_unlock_bh(); + + return 0; +} + +static int mptcp_fm_seq_open(struct inode *inode, struct file *file) +{ + return single_open_net(inode, file, mptcp_fm_seq_show); +} + +static const struct file_operations mptcp_fm_seq_fops = { + .owner = THIS_MODULE, + .open = mptcp_fm_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release_net, +}; + +static int mptcp_fm_init_net(struct net *net) +{ + struct mptcp_loc_addr *mptcp_local; + struct mptcp_fm_ns *fm_ns; + int err = 0; + + fm_ns = kzalloc(sizeof(*fm_ns), GFP_KERNEL); + if (!fm_ns) + return -ENOBUFS; + + mptcp_local = kzalloc(sizeof(*mptcp_local), GFP_KERNEL); + if (!mptcp_local) { + err = -ENOBUFS; + goto err_mptcp_local; + } + + if (!proc_create("mptcp_fullmesh", S_IRUGO, net->proc_net, + &mptcp_fm_seq_fops)) { + err = -ENOMEM; + goto err_seq_fops; + } + + mptcp_local->next_v4_index = 1; + + rcu_assign_pointer(fm_ns->local, mptcp_local); + INIT_DELAYED_WORK(&fm_ns->address_worker, mptcp_address_worker); + INIT_LIST_HEAD(&fm_ns->events); + spin_lock_init(&fm_ns->local_lock); + fm_ns->net = net; + net->mptcp.path_managers[MPTCP_PM_FULLMESH] = fm_ns; + + return 0; +err_seq_fops: + kfree(mptcp_local); +err_mptcp_local: + kfree(fm_ns); + return err; +} + +static void mptcp_fm_exit_net(struct net *net) +{ + struct mptcp_addr_event *eventq, *tmp; + struct mptcp_fm_ns *fm_ns; + struct mptcp_loc_addr *mptcp_local; + + fm_ns = fm_get_ns(net); + cancel_delayed_work_sync(&fm_ns->address_worker); + + rcu_read_lock_bh(); + + mptcp_local = rcu_dereference_bh(fm_ns->local); + kfree(mptcp_local); + + spin_lock(&fm_ns->local_lock); + list_for_each_entry_safe(eventq, tmp, &fm_ns->events, list) { + list_del(&eventq->list); + kfree(eventq); + } + spin_unlock(&fm_ns->local_lock); + + rcu_read_unlock_bh(); + + remove_proc_entry("mptcp_fullmesh", net->proc_net); + + kfree(fm_ns); +} + +static struct pernet_operations full_mesh_net_ops = { + .init = mptcp_fm_init_net, + .exit = mptcp_fm_exit_net, +}; + +static struct mptcp_pm_ops full_mesh __read_mostly = { + .new_session = full_mesh_new_session, + .release_sock = full_mesh_release_sock, + .fully_established = full_mesh_create_subflows, + .new_remote_address = full_mesh_create_subflows, + .get_local_id = full_mesh_get_local_id, + .addr_signal = full_mesh_addr_signal, + .add_raddr = full_mesh_add_raddr, + .rem_raddr = full_mesh_rem_raddr, + .name = "fullmesh", + .owner = THIS_MODULE, +}; + +/* General initialization of MPTCP_PM */ +static int __init full_mesh_register(void) +{ + int ret; + + BUILD_BUG_ON(sizeof(struct fullmesh_priv) > MPTCP_PM_SIZE); + + ret = register_pernet_subsys(&full_mesh_net_ops); + if (ret) + goto out; + + ret = register_inetaddr_notifier(&mptcp_pm_inetaddr_notifier); + if (ret) + goto err_reg_inetaddr; + ret = register_netdevice_notifier(&mptcp_pm_netdev_notifier); + if (ret) + goto err_reg_netdev; + +#if IS_ENABLED(CONFIG_IPV6) + ret = register_inet6addr_notifier(&inet6_addr_notifier); + if (ret) + goto err_reg_inet6addr; +#endif + + ret = mptcp_register_path_manager(&full_mesh); + if (ret) + goto err_reg_pm; + +out: + return ret; + + +err_reg_pm: +#if IS_ENABLED(CONFIG_IPV6) + unregister_inet6addr_notifier(&inet6_addr_notifier); +err_reg_inet6addr: +#endif + unregister_netdevice_notifier(&mptcp_pm_netdev_notifier); +err_reg_netdev: + unregister_inetaddr_notifier(&mptcp_pm_inetaddr_notifier); +err_reg_inetaddr: + unregister_pernet_subsys(&full_mesh_net_ops); + goto out; +} + +static void full_mesh_unregister(void) +{ +#if IS_ENABLED(CONFIG_IPV6) + unregister_inet6addr_notifier(&inet6_addr_notifier); +#endif + unregister_netdevice_notifier(&mptcp_pm_netdev_notifier); + unregister_inetaddr_notifier(&mptcp_pm_inetaddr_notifier); + unregister_pernet_subsys(&full_mesh_net_ops); + mptcp_unregister_path_manager(&full_mesh); +} + +module_init(full_mesh_register); +module_exit(full_mesh_unregister); + +MODULE_AUTHOR("Christoph Paasch"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Full-Mesh MPTCP"); +MODULE_VERSION("0.88"); +#endif diff -urN linux-4.1.52-orig/net/mptcp/mptcp_input.c linux-4.1.52/net/mptcp/mptcp_input.c --- linux-4.1.52-orig/net/mptcp/mptcp_input.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/mptcp/mptcp_input.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,2501 @@ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +/* + * MPTCP implementation - Sending side + * + * Initial Design & Implementation: + * Sébastien Barré + * + * Current Maintainer & Author: + * Christoph Paasch + * + * Additional authors: + * Jaakko Korkeaniemi + * Gregory Detal + * Fabien Duchêne + * Andreas Seelinger + * Lavkesh Lahngir + * Andreas Ripke + * Vlad Dogaru + * Octavian Purdila + * John Ronan + * Catalin Nicutar + * Brandon Heller + * + * + * 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 + +/* is seq1 < seq2 ? */ +static inline bool before64(const u64 seq1, const u64 seq2) +{ + return (s64)(seq1 - seq2) < 0; +} + +/* is seq1 > seq2 ? */ +#define after64(seq1, seq2) before64(seq2, seq1) + +static inline void mptcp_become_fully_estab(struct sock *sk) +{ + tcp_sk(sk)->mptcp->fully_established = 1; + + if (is_master_tp(tcp_sk(sk)) && + tcp_sk(sk)->mpcb->pm_ops->fully_established) + tcp_sk(sk)->mpcb->pm_ops->fully_established(mptcp_meta_sk(sk)); +} + +/* Similar to tcp_tso_acked without any memory accounting */ +static inline int mptcp_tso_acked_reinject(const struct sock *meta_sk, + struct sk_buff *skb) +{ + const struct tcp_sock *meta_tp = tcp_sk(meta_sk); + u32 packets_acked, len; + + BUG_ON(!after(TCP_SKB_CB(skb)->end_seq, meta_tp->snd_una)); + + packets_acked = tcp_skb_pcount(skb); + + if (skb_unclone(skb, GFP_ATOMIC)) + return 0; + + len = meta_tp->snd_una - TCP_SKB_CB(skb)->seq; + __pskb_trim_head(skb, len); + + TCP_SKB_CB(skb)->seq += len; + skb->ip_summed = CHECKSUM_PARTIAL; + skb->truesize -= len; + + /* Any change of skb->len requires recalculation of tso factor. */ + if (tcp_skb_pcount(skb) > 1) + tcp_set_skb_tso_segs(meta_sk, skb, tcp_skb_mss(skb)); + packets_acked -= tcp_skb_pcount(skb); + + if (packets_acked) { + BUG_ON(tcp_skb_pcount(skb) == 0); + BUG_ON(!before(TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq)); + } + + return packets_acked; +} + +/** + * Cleans the meta-socket retransmission queue and the reinject-queue. + * @sk must be the metasocket. + */ +static void mptcp_clean_rtx_queue(struct sock *meta_sk, u32 prior_snd_una) +{ + struct sk_buff *skb, *tmp; + struct tcp_sock *meta_tp = tcp_sk(meta_sk); + struct mptcp_cb *mpcb = meta_tp->mpcb; + bool acked = false; + u32 acked_pcount; + + while ((skb = tcp_write_queue_head(meta_sk)) && + skb != tcp_send_head(meta_sk)) { + bool fully_acked = true; + + if (before(meta_tp->snd_una, TCP_SKB_CB(skb)->end_seq)) { + if (tcp_skb_pcount(skb) == 1 || + !after(meta_tp->snd_una, TCP_SKB_CB(skb)->seq)) + break; + + acked_pcount = tcp_tso_acked(meta_sk, skb); + if (!acked_pcount) + break; + + fully_acked = false; + } else { + acked_pcount = tcp_skb_pcount(skb); + } + + acked = true; + meta_tp->packets_out -= acked_pcount; + meta_tp->retrans_stamp = 0; + + if (!fully_acked) + break; + + tcp_unlink_write_queue(skb, meta_sk); + + if (mptcp_is_data_fin(skb)) { + struct sock *sk_it; + + /* DATA_FIN has been acknowledged - now we can close + * the subflows + */ + mptcp_for_each_sk(mpcb, sk_it) { + unsigned long delay = 0; + + /* If we are the passive closer, don't trigger + * subflow-fin until the subflow has been finned + * by the peer - thus we add a delay. + */ + if (mpcb->passive_close && + sk_it->sk_state == TCP_ESTABLISHED) + delay = inet_csk(sk_it)->icsk_rto << 3; + + mptcp_sub_close(sk_it, delay); + } + } + sk_wmem_free_skb(meta_sk, skb); + } + /* Remove acknowledged data from the reinject queue */ + skb_queue_walk_safe(&mpcb->reinject_queue, skb, tmp) { + if (before(meta_tp->snd_una, TCP_SKB_CB(skb)->end_seq)) { + if (tcp_skb_pcount(skb) == 1 || + !after(meta_tp->snd_una, TCP_SKB_CB(skb)->seq)) + break; + + mptcp_tso_acked_reinject(meta_sk, skb); + break; + } + + __skb_unlink(skb, &mpcb->reinject_queue); + __kfree_skb(skb); + } + + if (likely(between(meta_tp->snd_up, prior_snd_una, meta_tp->snd_una))) + meta_tp->snd_up = meta_tp->snd_una; + + if (acked) { + tcp_rearm_rto(meta_sk); + /* Normally this is done in tcp_try_undo_loss - but MPTCP + * does not call this function. + */ + inet_csk(meta_sk)->icsk_retransmits = 0; + } +} + +/* Inspired by tcp_rcv_state_process */ +static int mptcp_rcv_state_process(struct sock *meta_sk, struct sock *sk, + const struct sk_buff *skb, u32 data_seq, + u16 data_len) +{ + struct tcp_sock *meta_tp = tcp_sk(meta_sk), *tp = tcp_sk(sk); + const struct tcphdr *th = tcp_hdr(skb); + + /* State-machine handling if FIN has been enqueued and he has + * been acked (snd_una == write_seq) - it's important that this + * here is after sk_wmem_free_skb because otherwise + * sk_forward_alloc is wrong upon inet_csk_destroy_sock() + */ + switch (meta_sk->sk_state) { + case TCP_FIN_WAIT1: { + struct dst_entry *dst; + int tmo; + + if (meta_tp->snd_una != meta_tp->write_seq) + break; + + tcp_set_state(meta_sk, TCP_FIN_WAIT2); + meta_sk->sk_shutdown |= SEND_SHUTDOWN; + + dst = __sk_dst_get(sk); + if (dst) + dst_confirm(dst); + + if (!sock_flag(meta_sk, SOCK_DEAD)) { + /* Wake up lingering close() */ + meta_sk->sk_state_change(meta_sk); + break; + } + + if (meta_tp->linger2 < 0 || + (data_len && + after(data_seq + data_len - (mptcp_is_data_fin2(skb, tp) ? 1 : 0), + meta_tp->rcv_nxt))) { + mptcp_send_active_reset(meta_sk, GFP_ATOMIC); + tcp_done(meta_sk); + NET_INC_STATS_BH(sock_net(meta_sk), LINUX_MIB_TCPABORTONDATA); + return 1; + } + + tmo = tcp_fin_time(meta_sk); + if (tmo > TCP_TIMEWAIT_LEN) { + inet_csk_reset_keepalive_timer(meta_sk, tmo - TCP_TIMEWAIT_LEN); + } else if (mptcp_is_data_fin2(skb, tp) || sock_owned_by_user(meta_sk)) { + /* Bad case. We could lose such FIN otherwise. + * It is not a big problem, but it looks confusing + * and not so rare event. We still can lose it now, + * if it spins in bh_lock_sock(), but it is really + * marginal case. + */ + inet_csk_reset_keepalive_timer(meta_sk, tmo); + } else { + meta_tp->ops->time_wait(meta_sk, TCP_FIN_WAIT2, tmo); + } + break; + } + case TCP_CLOSING: + case TCP_LAST_ACK: + if (meta_tp->snd_una == meta_tp->write_seq) { + tcp_done(meta_sk); + return 1; + } + break; + } + + /* step 7: process the segment text */ + switch (meta_sk->sk_state) { + case TCP_FIN_WAIT1: + case TCP_FIN_WAIT2: + /* RFC 793 says to queue data in these states, + * RFC 1122 says we MUST send a reset. + * BSD 4.4 also does reset. + */ + if (meta_sk->sk_shutdown & RCV_SHUTDOWN) { + if (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq && + after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt) && + !mptcp_is_data_fin2(skb, tp)) { + NET_INC_STATS_BH(sock_net(meta_sk), LINUX_MIB_TCPABORTONDATA); + mptcp_send_active_reset(meta_sk, GFP_ATOMIC); + tcp_reset(meta_sk); + return 1; + } + } + break; + } + + return 0; +} + +/** + * @return: + * i) 1: Everything's fine. + * ii) -1: A reset has been sent on the subflow - csum-failure + * iii) 0: csum-failure but no reset sent, because it's the last subflow. + * Last packet should not be destroyed by the caller because it has + * been done here. + */ +static int mptcp_verif_dss_csum(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct sk_buff *tmp, *tmp1, *last = NULL; + __wsum csum_tcp = 0; /* cumulative checksum of pld + mptcp-header */ + int ans = 1, overflowed = 0, offset = 0, dss_csum_added = 0; + int iter = 0; + + skb_queue_walk_safe(&sk->sk_receive_queue, tmp, tmp1) { + unsigned int csum_len; + + if (before(tp->mptcp->map_subseq + tp->mptcp->map_data_len, TCP_SKB_CB(tmp)->end_seq)) + /* Mapping ends in the middle of the packet - + * csum only these bytes + */ + csum_len = tp->mptcp->map_subseq + tp->mptcp->map_data_len - TCP_SKB_CB(tmp)->seq; + else + csum_len = tmp->len; + + offset = 0; + if (overflowed) { + char first_word[4]; + first_word[0] = 0; + first_word[1] = 0; + first_word[2] = 0; + first_word[3] = *(tmp->data); + csum_tcp = csum_partial(first_word, 4, csum_tcp); + offset = 1; + csum_len--; + overflowed = 0; + } + + csum_tcp = skb_checksum(tmp, offset, csum_len, csum_tcp); + + /* Was it on an odd-length? Then we have to merge the next byte + * correctly (see above) + */ + if (csum_len != (csum_len & (~1))) + overflowed = 1; + + if (mptcp_is_data_seq(tmp) && !dss_csum_added) { + __be32 data_seq = htonl((u32)(tp->mptcp->map_data_seq >> 32)); + + /* If a 64-bit dss is present, we increase the offset + * by 4 bytes, as the high-order 64-bits will be added + * in the final csum_partial-call. + */ + u32 offset = skb_transport_offset(tmp) + + TCP_SKB_CB(tmp)->dss_off; + if (TCP_SKB_CB(tmp)->mptcp_flags & MPTCPHDR_SEQ64_SET) + offset += 4; + + csum_tcp = skb_checksum(tmp, offset, + MPTCP_SUB_LEN_SEQ_CSUM, + csum_tcp); + + csum_tcp = csum_partial(&data_seq, + sizeof(data_seq), csum_tcp); + + dss_csum_added = 1; /* Just do it once */ + } + last = tmp; + iter++; + + if (!skb_queue_is_last(&sk->sk_receive_queue, tmp) && + !before(TCP_SKB_CB(tmp1)->seq, + tp->mptcp->map_subseq + tp->mptcp->map_data_len)) + break; + } + + /* Now, checksum must be 0 */ + if (unlikely(csum_fold(csum_tcp))) { + pr_err("%s csum is wrong: %#x data_seq %u dss_csum_added %d overflowed %d iterations %d\n", + __func__, csum_fold(csum_tcp), TCP_SKB_CB(last)->seq, + dss_csum_added, overflowed, iter); + + MPTCP_INC_STATS_BH(sock_net(sk), MPTCP_MIB_CSUMFAIL); + tp->mptcp->send_mp_fail = 1; + + /* map_data_seq is the data-seq number of the + * mapping we are currently checking + */ + tp->mpcb->csum_cutoff_seq = tp->mptcp->map_data_seq; + + if (tp->mpcb->cnt_subflows > 1) { + mptcp_send_reset(sk); + ans = -1; + } else { + tp->mpcb->send_infinite_mapping = 1; + + /* Need to purge the rcv-queue as it's no more valid */ + while ((tmp = __skb_dequeue(&sk->sk_receive_queue)) != NULL) { + tp->copied_seq = TCP_SKB_CB(tmp)->end_seq; + kfree_skb(tmp); + } + + ans = 0; + } + } + + return ans; +} + +static inline void mptcp_prepare_skb(struct sk_buff *skb, + const struct sock *sk) +{ + const struct tcp_sock *tp = tcp_sk(sk); + struct tcp_skb_cb *tcb = TCP_SKB_CB(skb); + u32 inc = 0, end_seq = tcb->end_seq; + + if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) + end_seq--; + /* If skb is the end of this mapping (end is always at mapping-boundary + * thanks to the splitting/trimming), then we need to increase + * data-end-seq by 1 if this here is a data-fin. + * + * We need to do -1 because end_seq includes the subflow-FIN. + */ + if (tp->mptcp->map_data_fin && + end_seq == tp->mptcp->map_subseq + tp->mptcp->map_data_len) { + inc = 1; + + /* We manually set the fin-flag if it is a data-fin. For easy + * processing in tcp_recvmsg. + */ + TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_FIN; + } else { + /* We may have a subflow-fin with data but without data-fin */ + TCP_SKB_CB(skb)->tcp_flags &= ~TCPHDR_FIN; + } + + /* Adapt data-seq's to the packet itself. We kinda transform the + * dss-mapping to a per-packet granularity. This is necessary to + * correctly handle overlapping mappings coming from different + * subflows. Otherwise it would be a complete mess. + */ + tcb->seq = ((u32)tp->mptcp->map_data_seq) + tcb->seq - tp->mptcp->map_subseq; + tcb->end_seq = tcb->seq + skb->len + inc; +} + +/** + * @return: 1 if the segment has been eaten and can be suppressed, + * otherwise 0. + */ +static inline int mptcp_direct_copy(const struct sk_buff *skb, + struct sock *meta_sk) +{ + struct tcp_sock *meta_tp = tcp_sk(meta_sk); + int chunk = min_t(unsigned int, skb->len, meta_tp->ucopy.len); + int eaten = 0; + + __set_current_state(TASK_RUNNING); + + local_bh_enable(); + if (!skb_copy_datagram_msg(skb, 0, meta_tp->ucopy.msg, chunk)) { + meta_tp->ucopy.len -= chunk; + meta_tp->copied_seq += chunk; + eaten = (chunk == skb->len); + tcp_rcv_space_adjust(meta_sk); + } + local_bh_disable(); + return eaten; +} + +static inline void mptcp_reset_mapping(struct tcp_sock *tp, u32 old_copied_seq) +{ + tp->mptcp->map_data_len = 0; + tp->mptcp->map_data_seq = 0; + tp->mptcp->map_subseq = 0; + tp->mptcp->map_data_fin = 0; + tp->mptcp->mapping_present = 0; + + /* In infinite mapping receiver mode, we have to advance the implied + * data-sequence number when we progress the subflow's data. + */ + if (tp->mpcb->infinite_mapping_rcv) + tp->mpcb->infinite_rcv_seq += (tp->copied_seq - old_copied_seq); +} + +/* The DSS-mapping received on the sk only covers the second half of the skb + * (cut at seq). We trim the head from the skb. + * Data will be freed upon kfree(). + * + * Inspired by tcp_trim_head(). + */ +static void mptcp_skb_trim_head(struct sk_buff *skb, struct sock *sk, u32 seq) +{ + int len = seq - TCP_SKB_CB(skb)->seq; + u32 new_seq = TCP_SKB_CB(skb)->seq + len; + + __pskb_trim_head(skb, len); + + TCP_SKB_CB(skb)->seq = new_seq; + + skb->truesize -= len; + atomic_sub(len, &sk->sk_rmem_alloc); + sk_mem_uncharge(sk, len); +} + +/* The DSS-mapping received on the sk only covers the first half of the skb + * (cut at seq). We create a second skb (@return), and queue it in the rcv-queue + * as further packets may resolve the mapping of the second half of data. + * + * Inspired by tcp_fragment(). + */ +static int mptcp_skb_split_tail(struct sk_buff *skb, struct sock *sk, u32 seq) +{ + struct sk_buff *buff; + int nsize; + int nlen, len; + u8 flags; + + len = seq - TCP_SKB_CB(skb)->seq; + nsize = skb_headlen(skb) - len + tcp_sk(sk)->tcp_header_len; + if (nsize < 0) + nsize = 0; + + /* Get a new skb... force flag on. */ + buff = alloc_skb(nsize, GFP_ATOMIC); + if (buff == NULL) + return -ENOMEM; + + skb_reserve(buff, tcp_sk(sk)->tcp_header_len); + skb_reset_transport_header(buff); + + flags = TCP_SKB_CB(skb)->tcp_flags; + TCP_SKB_CB(skb)->tcp_flags = flags & ~(TCPHDR_FIN); + TCP_SKB_CB(buff)->tcp_flags = flags; + + /* We absolutly need to call skb_set_owner_r before refreshing the + * truesize of buff, otherwise the moved data will account twice. + */ + skb_set_owner_r(buff, sk); + nlen = skb->len - len - nsize; + buff->truesize += nlen; + skb->truesize -= nlen; + + /* Correct the sequence numbers. */ + TCP_SKB_CB(buff)->seq = TCP_SKB_CB(skb)->seq + len; + TCP_SKB_CB(buff)->end_seq = TCP_SKB_CB(skb)->end_seq; + TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(buff)->seq; + + skb_split(skb, buff, len); + + __skb_queue_after(&sk->sk_receive_queue, skb, buff); + + return 0; +} + +/* @return: 0 everything is fine. Just continue processing + * 1 subflow is broken stop everything + * -1 this packet was broken - continue with the next one. + */ +static int mptcp_prevalidate_skb(struct sock *sk, struct sk_buff *skb) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct mptcp_cb *mpcb = tp->mpcb; + + /* If we are in infinite mode, the subflow-fin is in fact a data-fin. */ + if (!skb->len && (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) && + !mptcp_is_data_fin(skb) && !mpcb->infinite_mapping_rcv) { + /* Remove a pure subflow-fin from the queue and increase + * copied_seq. + */ + tp->copied_seq = TCP_SKB_CB(skb)->end_seq; + __skb_unlink(skb, &sk->sk_receive_queue); + __kfree_skb(skb); + return -1; + } + + /* If we are not yet fully established and do not know the mapping for + * this segment, this path has to fallback to infinite or be torn down. + */ + if (!tp->mptcp->fully_established && !mptcp_is_data_seq(skb) && + !tp->mptcp->mapping_present && !mpcb->infinite_mapping_rcv) { + pr_err("%s %#x will fallback - pi %d from %pS, seq %u\n", + __func__, mpcb->mptcp_loc_token, + tp->mptcp->path_index, __builtin_return_address(0), + TCP_SKB_CB(skb)->seq); + + if (!is_master_tp(tp)) { + MPTCP_INC_STATS_BH(sock_net(sk), MPTCP_MIB_FBDATASUB); + mptcp_send_reset(sk); + return 1; + } + + MPTCP_INC_STATS_BH(sock_net(sk), MPTCP_MIB_FBDATAINIT); + + mpcb->infinite_mapping_snd = 1; + mpcb->infinite_mapping_rcv = 1; + mpcb->infinite_rcv_seq = mptcp_get_rcv_nxt_64(mptcp_meta_tp(tp)); + + mptcp_sub_force_close_all(mpcb, sk); + + /* We do a seamless fallback and should not send a inf.mapping. */ + mpcb->send_infinite_mapping = 0; + tp->mptcp->fully_established = 1; + } + + /* Receiver-side becomes fully established when a whole rcv-window has + * been received without the need to fallback due to the previous + * condition. + */ + if (!tp->mptcp->fully_established) { + tp->mptcp->init_rcv_wnd -= skb->len; + if (tp->mptcp->init_rcv_wnd < 0) + mptcp_become_fully_estab(sk); + } + + return 0; +} + +/* @return: 0 everything is fine. Just continue processing + * 1 subflow is broken stop everything + * -1 this packet was broken - continue with the next one. + */ +static int mptcp_detect_mapping(struct sock *sk, struct sk_buff *skb) +{ + struct tcp_sock *tp = tcp_sk(sk), *meta_tp = mptcp_meta_tp(tp); + struct mptcp_cb *mpcb = tp->mpcb; + struct tcp_skb_cb *tcb = TCP_SKB_CB(skb); + u32 *ptr; + u32 data_seq, sub_seq, data_len, tcp_end_seq; + bool set_infinite_rcv = false; + + /* If we are in infinite-mapping-mode, the subflow is guaranteed to be + * in-order at the data-level. Thus data-seq-numbers can be inferred + * from what is expected at the data-level. + */ + if (mpcb->infinite_mapping_rcv) { + /* copied_seq may be bigger than tcb->seq (e.g., when the peer + * retransmits data that actually has already been acknowledged with + * newer data, if he did not receive our acks). Thus, we need + * to account for this overlap as well. + */ + tp->mptcp->map_data_seq = mpcb->infinite_rcv_seq - (tp->copied_seq - tcb->seq); + tp->mptcp->map_subseq = tcb->seq; + tp->mptcp->map_data_len = skb->len; + tp->mptcp->map_data_fin = !!(TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN); + tp->mptcp->mapping_present = 1; + return 0; + } + + /* No mapping here? Exit - it is either already set or still on its way */ + if (!mptcp_is_data_seq(skb)) { + /* Too many packets without a mapping - this subflow is broken */ + if (!tp->mptcp->mapping_present && + tp->rcv_nxt - tp->copied_seq > 65536) { + MPTCP_INC_STATS_BH(sock_net(sk), MPTCP_MIB_NODSSWINDOW); + mptcp_send_reset(sk); + return 1; + } + + return 0; + } + + ptr = mptcp_skb_set_data_seq(skb, &data_seq, mpcb); + ptr++; + sub_seq = get_unaligned_be32(ptr) + tp->mptcp->rcv_isn; + ptr++; + data_len = get_unaligned_be16(ptr); + + /* If it's an empty skb with DATA_FIN, sub_seq must get fixed. + * The draft sets it to 0, but we really would like to have the + * real value, to have an easy handling afterwards here in this + * function. + */ + if (mptcp_is_data_fin(skb) && skb->len == 0) + sub_seq = TCP_SKB_CB(skb)->seq; + + /* If there is already a mapping - we check if it maps with the current + * one. If not - we reset. + */ + if (tp->mptcp->mapping_present && + (data_seq != (u32)tp->mptcp->map_data_seq || + sub_seq != tp->mptcp->map_subseq || + data_len != tp->mptcp->map_data_len + tp->mptcp->map_data_fin || + mptcp_is_data_fin(skb) != tp->mptcp->map_data_fin)) { + /* Mapping in packet is different from what we want */ + pr_err("%s Mappings do not match!\n", __func__); + pr_err("%s dseq %u mdseq %u, sseq %u msseq %u dlen %u mdlen %u dfin %d mdfin %d\n", + __func__, data_seq, (u32)tp->mptcp->map_data_seq, + sub_seq, tp->mptcp->map_subseq, data_len, + tp->mptcp->map_data_len, mptcp_is_data_fin(skb), + tp->mptcp->map_data_fin); + MPTCP_INC_STATS_BH(sock_net(sk), MPTCP_MIB_DSSNOMATCH); + mptcp_send_reset(sk); + return 1; + } + + /* If the previous check was good, the current mapping is valid and we exit. */ + if (tp->mptcp->mapping_present) + return 0; + + /* Mapping not yet set on this subflow - we set it here! */ + + if (!data_len) { + mpcb->infinite_mapping_rcv = 1; + mpcb->send_infinite_mapping = 1; + tp->mptcp->fully_established = 1; + /* We need to repeat mp_fail's until the sender felt + * back to infinite-mapping - here we stop repeating it. + */ + tp->mptcp->send_mp_fail = 0; + + /* We have to fixup data_len - it must be the same as skb->len */ + data_len = skb->len + (mptcp_is_data_fin(skb) ? 1 : 0); + sub_seq = tcb->seq; + + mptcp_sub_force_close_all(mpcb, sk); + + /* data_seq and so on are set correctly */ + + /* At this point, the meta-ofo-queue has to be emptied, + * as the following data is guaranteed to be in-order at + * the data and subflow-level + */ + mptcp_purge_ofo_queue(meta_tp); + + set_infinite_rcv = true; + MPTCP_INC_STATS_BH(sock_net(sk), MPTCP_MIB_INFINITEMAPRX); + } + + /* We are sending mp-fail's and thus are in fallback mode. + * Ignore packets which do not announce the fallback and still + * want to provide a mapping. + */ + if (tp->mptcp->send_mp_fail) { + tp->copied_seq = TCP_SKB_CB(skb)->end_seq; + __skb_unlink(skb, &sk->sk_receive_queue); + __kfree_skb(skb); + return -1; + } + + /* FIN increased the mapping-length by 1 */ + if (mptcp_is_data_fin(skb)) + data_len--; + + /* Subflow-sequences of packet must be + * (at least partially) be part of the DSS-mapping's + * subflow-sequence-space. + * + * Basically the mapping is not valid, if either of the + * following conditions is true: + * + * 1. It's not a data_fin and + * MPTCP-sub_seq >= TCP-end_seq + * + * 2. It's a data_fin and TCP-end_seq > TCP-seq and + * MPTCP-sub_seq >= TCP-end_seq + * + * The previous two can be merged into: + * TCP-end_seq > TCP-seq and MPTCP-sub_seq >= TCP-end_seq + * Because if it's not a data-fin, TCP-end_seq > TCP-seq + * + * 3. It's a data_fin and skb->len == 0 and + * MPTCP-sub_seq > TCP-end_seq + * + * 4. It's not a data_fin and TCP-end_seq > TCP-seq and + * MPTCP-sub_seq + MPTCP-data_len <= TCP-seq + */ + + /* subflow-fin is not part of the mapping - ignore it here ! */ + tcp_end_seq = tcb->end_seq; + if (tcb->tcp_flags & TCPHDR_FIN) + tcp_end_seq--; + if ((!before(sub_seq, tcb->end_seq) && after(tcp_end_seq, tcb->seq)) || + (mptcp_is_data_fin(skb) && skb->len == 0 && after(sub_seq, tcb->end_seq)) || + (!after(sub_seq + data_len, tcb->seq) && after(tcp_end_seq, tcb->seq))) { + /* Subflow-sequences of packet is different from what is in the + * packet's dss-mapping. The peer is misbehaving - reset + */ + pr_err("%s Packet's mapping does not map to the DSS sub_seq %u " + "end_seq %u, tcp_end_seq %u seq %u dfin %u len %u data_len %u" + "copied_seq %u\n", __func__, sub_seq, tcb->end_seq, tcp_end_seq, tcb->seq, mptcp_is_data_fin(skb), + skb->len, data_len, tp->copied_seq); + MPTCP_INC_STATS_BH(sock_net(sk), MPTCP_MIB_DSSTCPMISMATCH); + mptcp_send_reset(sk); + return 1; + } + + /* Does the DSS had 64-bit seqnum's ? */ + if (!(tcb->mptcp_flags & MPTCPHDR_SEQ64_SET)) { + /* Wrapped around? */ + if (unlikely(after(data_seq, meta_tp->rcv_nxt) && data_seq < meta_tp->rcv_nxt)) { + tp->mptcp->map_data_seq = mptcp_get_data_seq_64(mpcb, !mpcb->rcv_hiseq_index, data_seq); + } else { + /* Else, access the default high-order bits */ + tp->mptcp->map_data_seq = mptcp_get_data_seq_64(mpcb, mpcb->rcv_hiseq_index, data_seq); + } + } else { + tp->mptcp->map_data_seq = mptcp_get_data_seq_64(mpcb, (tcb->mptcp_flags & MPTCPHDR_SEQ64_INDEX) ? 1 : 0, data_seq); + + if (unlikely(tcb->mptcp_flags & MPTCPHDR_SEQ64_OFO)) { + /* We make sure that the data_seq is invalid. + * It will be dropped later. + */ + tp->mptcp->map_data_seq += 0xFFFFFFFF; + tp->mptcp->map_data_seq += 0xFFFFFFFF; + } + } + + if (set_infinite_rcv) + mpcb->infinite_rcv_seq = tp->mptcp->map_data_seq; + + tp->mptcp->map_data_len = data_len; + tp->mptcp->map_subseq = sub_seq; + tp->mptcp->map_data_fin = mptcp_is_data_fin(skb) ? 1 : 0; + tp->mptcp->mapping_present = 1; + + return 0; +} + +/* Similar to tcp_sequence(...) */ +static inline bool mptcp_sequence(const struct tcp_sock *meta_tp, + u64 data_seq, u64 end_data_seq) +{ + const struct mptcp_cb *mpcb = meta_tp->mpcb; + u64 rcv_wup64; + + /* Wrap-around? */ + if (meta_tp->rcv_wup > meta_tp->rcv_nxt) { + rcv_wup64 = ((u64)(mpcb->rcv_high_order[mpcb->rcv_hiseq_index] - 1) << 32) | + meta_tp->rcv_wup; + } else { + rcv_wup64 = mptcp_get_data_seq_64(mpcb, mpcb->rcv_hiseq_index, + meta_tp->rcv_wup); + } + + return !before64(end_data_seq, rcv_wup64) && + !after64(data_seq, mptcp_get_rcv_nxt_64(meta_tp) + tcp_receive_window(meta_tp)); +} + +/* @return: 0 everything is fine. Just continue processing + * -1 this packet was broken - continue with the next one. + */ +static int mptcp_validate_mapping(struct sock *sk, struct sk_buff *skb) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct sk_buff *tmp, *tmp1; + u32 tcp_end_seq; + + if (!tp->mptcp->mapping_present) + return 0; + + /* either, the new skb gave us the mapping and the first segment + * in the sub-rcv-queue has to be trimmed ... + */ + tmp = skb_peek(&sk->sk_receive_queue); + if (before(TCP_SKB_CB(tmp)->seq, tp->mptcp->map_subseq) && + after(TCP_SKB_CB(tmp)->end_seq, tp->mptcp->map_subseq)) { + MPTCP_INC_STATS_BH(sock_net(sk), MPTCP_MIB_DSSTRIMHEAD); + mptcp_skb_trim_head(tmp, sk, tp->mptcp->map_subseq); + } + + /* ... or the new skb (tail) has to be split at the end. */ + tcp_end_seq = TCP_SKB_CB(skb)->end_seq; + if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) + tcp_end_seq--; + if (after(tcp_end_seq, tp->mptcp->map_subseq + tp->mptcp->map_data_len)) { + u32 seq = tp->mptcp->map_subseq + tp->mptcp->map_data_len; + MPTCP_INC_STATS_BH(sock_net(sk), MPTCP_MIB_DSSSPLITTAIL); + if (mptcp_skb_split_tail(skb, sk, seq)) { /* Allocation failed */ + /* TODO : maybe handle this here better. + * We now just force meta-retransmission. + */ + tp->copied_seq = TCP_SKB_CB(skb)->end_seq; + __skb_unlink(skb, &sk->sk_receive_queue); + __kfree_skb(skb); + return -1; + } + } + + /* Now, remove old sk_buff's from the receive-queue. + * This may happen if the mapping has been lost for these segments and + * the next mapping has already been received. + */ + if (before(TCP_SKB_CB(skb_peek(&sk->sk_receive_queue))->seq, tp->mptcp->map_subseq)) { + skb_queue_walk_safe(&sk->sk_receive_queue, tmp1, tmp) { + if (!before(TCP_SKB_CB(tmp1)->seq, tp->mptcp->map_subseq)) + break; + + tp->copied_seq = TCP_SKB_CB(tmp1)->end_seq; + __skb_unlink(tmp1, &sk->sk_receive_queue); + + MPTCP_INC_STATS_BH(sock_net(sk), MPTCP_MIB_PURGEOLD); + /* Impossible that we could free skb here, because his + * mapping is known to be valid from previous checks + */ + __kfree_skb(tmp1); + } + } + + return 0; +} + +/* @return: 0 everything is fine. Just continue processing + * 1 subflow is broken stop everything + * -1 this mapping has been put in the meta-receive-queue + * -2 this mapping has been eaten by the application + */ +static int mptcp_queue_skb(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk), *meta_tp = mptcp_meta_tp(tp); + struct sock *meta_sk = mptcp_meta_sk(sk); + struct mptcp_cb *mpcb = tp->mpcb; + struct sk_buff *tmp, *tmp1; + u64 rcv_nxt64 = mptcp_get_rcv_nxt_64(meta_tp); + u32 old_copied_seq = tp->copied_seq; + bool data_queued = false; + + /* Have we not yet received the full mapping? */ + if (!tp->mptcp->mapping_present || + before(tp->rcv_nxt, tp->mptcp->map_subseq + tp->mptcp->map_data_len)) + return 0; + + /* Is this an overlapping mapping? rcv_nxt >= end_data_seq + * OR + * This mapping is out of window + */ + if (!before64(rcv_nxt64, tp->mptcp->map_data_seq + tp->mptcp->map_data_len + tp->mptcp->map_data_fin) || + !mptcp_sequence(meta_tp, tp->mptcp->map_data_seq, + tp->mptcp->map_data_seq + tp->mptcp->map_data_len + tp->mptcp->map_data_fin)) { + skb_queue_walk_safe(&sk->sk_receive_queue, tmp1, tmp) { + __skb_unlink(tmp1, &sk->sk_receive_queue); + tp->copied_seq = TCP_SKB_CB(tmp1)->end_seq; + __kfree_skb(tmp1); + + if (!skb_queue_empty(&sk->sk_receive_queue) && + !before(TCP_SKB_CB(tmp)->seq, + tp->mptcp->map_subseq + tp->mptcp->map_data_len)) + break; + } + + mptcp_reset_mapping(tp, old_copied_seq); + + return -1; + } + + /* Record it, because we want to send our data_fin on the same path */ + if (tp->mptcp->map_data_fin) { + mpcb->dfin_path_index = tp->mptcp->path_index; + mpcb->dfin_combined = !!(sk->sk_shutdown & RCV_SHUTDOWN); + } + + /* Verify the checksum */ + if (mpcb->dss_csum && !mpcb->infinite_mapping_rcv) { + int ret = mptcp_verif_dss_csum(sk); + + if (ret <= 0) { + mptcp_reset_mapping(tp, old_copied_seq); + return 1; + } + } + + if (before64(rcv_nxt64, tp->mptcp->map_data_seq)) { + /* Seg's have to go to the meta-ofo-queue */ + skb_queue_walk_safe(&sk->sk_receive_queue, tmp1, tmp) { + tp->copied_seq = TCP_SKB_CB(tmp1)->end_seq; + mptcp_prepare_skb(tmp1, sk); + __skb_unlink(tmp1, &sk->sk_receive_queue); + /* MUST be done here, because fragstolen may be true later. + * Then, kfree_skb_partial will not account the memory. + */ + skb_orphan(tmp1); + + if (!mpcb->in_time_wait) /* In time-wait, do not receive data */ + mptcp_add_meta_ofo_queue(meta_sk, tmp1, sk); + else + __kfree_skb(tmp1); + + if (!skb_queue_empty(&sk->sk_receive_queue) && + !before(TCP_SKB_CB(tmp)->seq, + tp->mptcp->map_subseq + tp->mptcp->map_data_len)) + break; + } + tcp_enter_quickack_mode(sk); + } else { + /* Ready for the meta-rcv-queue */ + skb_queue_walk_safe(&sk->sk_receive_queue, tmp1, tmp) { + int eaten = 0; + bool fragstolen = false; + u32 old_rcv_nxt = meta_tp->rcv_nxt; + + tp->copied_seq = TCP_SKB_CB(tmp1)->end_seq; + mptcp_prepare_skb(tmp1, sk); + __skb_unlink(tmp1, &sk->sk_receive_queue); + /* MUST be done here, because fragstolen may be true. + * Then, kfree_skb_partial will not account the memory. + */ + skb_orphan(tmp1); + + /* This segment has already been received */ + if (!after(TCP_SKB_CB(tmp1)->end_seq, meta_tp->rcv_nxt)) { + __kfree_skb(tmp1); + goto next; + } + + /* Is direct copy possible ? */ + if (TCP_SKB_CB(tmp1)->seq == meta_tp->rcv_nxt && + meta_tp->ucopy.task == current && + meta_tp->copied_seq == meta_tp->rcv_nxt && + meta_tp->ucopy.len && sock_owned_by_user(meta_sk)) + eaten = mptcp_direct_copy(tmp1, meta_sk); + + if (mpcb->in_time_wait) /* In time-wait, do not receive data */ + eaten = 1; + + if (!eaten) + eaten = tcp_queue_rcv(meta_sk, tmp1, 0, &fragstolen); + + meta_tp->rcv_nxt = TCP_SKB_CB(tmp1)->end_seq; + mptcp_check_rcvseq_wrap(meta_tp, old_rcv_nxt); + + if ((TCP_SKB_CB(tmp1)->tcp_flags & TCPHDR_FIN) && + !mpcb->in_time_wait) + mptcp_fin(meta_sk); + + /* Check if this fills a gap in the ofo queue */ + if (!skb_queue_empty(&meta_tp->out_of_order_queue)) + mptcp_ofo_queue(meta_sk); + + if (eaten) + kfree_skb_partial(tmp1, fragstolen); + + data_queued = true; +next: + if (!skb_queue_empty(&sk->sk_receive_queue) && + !before(TCP_SKB_CB(tmp)->seq, + tp->mptcp->map_subseq + tp->mptcp->map_data_len)) + break; + } + } + + inet_csk(meta_sk)->icsk_ack.lrcvtime = tcp_time_stamp; + mptcp_reset_mapping(tp, old_copied_seq); + + return data_queued ? -1 : -2; +} + +void mptcp_data_ready(struct sock *sk) +{ + struct sock *meta_sk = mptcp_meta_sk(sk); + struct sk_buff *skb, *tmp; + int queued = 0; + + /* restart before the check, because mptcp_fin might have changed the + * state. + */ +restart: + /* If the meta cannot receive data, there is no point in pushing data. + * If we are in time-wait, we may still be waiting for the final FIN. + * So, we should proceed with the processing. + */ + if (!mptcp_sk_can_recv(meta_sk) && !tcp_sk(sk)->mpcb->in_time_wait) { + skb_queue_purge(&sk->sk_receive_queue); + tcp_sk(sk)->copied_seq = tcp_sk(sk)->rcv_nxt; + goto exit; + } + + /* Iterate over all segments, detect their mapping (if we don't have + * one yet), validate them and push everything one level higher. + */ + skb_queue_walk_safe(&sk->sk_receive_queue, skb, tmp) { + int ret; + /* Pre-validation - e.g., early fallback */ + ret = mptcp_prevalidate_skb(sk, skb); + if (ret < 0) + goto restart; + else if (ret > 0) + break; + + /* Set the current mapping */ + ret = mptcp_detect_mapping(sk, skb); + if (ret < 0) + goto restart; + else if (ret > 0) + break; + + /* Validation */ + if (mptcp_validate_mapping(sk, skb) < 0) + goto restart; + + /* Push a level higher */ + ret = mptcp_queue_skb(sk); + if (ret < 0) { + if (ret == -1) + queued = ret; + goto restart; + } else if (ret == 0) { + continue; + } else { /* ret == 1 */ + break; + } + } + +exit: + if (tcp_sk(sk)->close_it) { + tcp_send_ack(sk); + tcp_sk(sk)->ops->time_wait(sk, TCP_TIME_WAIT, 0); + } + + if (queued == -1 && !sock_flag(meta_sk, SOCK_DEAD)) + meta_sk->sk_data_ready(meta_sk); +} + + +int mptcp_check_req(struct sk_buff *skb, struct net *net) +{ + const struct tcphdr *th = tcp_hdr(skb); + struct sock *meta_sk = NULL; + + /* MPTCP structures not initialized */ + if (mptcp_init_failed) + return 0; + + if (skb->protocol == htons(ETH_P_IP)) + meta_sk = mptcp_v4_search_req(th->source, ip_hdr(skb)->saddr, + ip_hdr(skb)->daddr, net); +#if IS_ENABLED(CONFIG_IPV6) + else /* IPv6 */ + meta_sk = mptcp_v6_search_req(th->source, &ipv6_hdr(skb)->saddr, + &ipv6_hdr(skb)->daddr, net); +#endif /* CONFIG_IPV6 */ + + if (!meta_sk) + return 0; + + TCP_SKB_CB(skb)->mptcp_flags |= MPTCPHDR_JOIN; + + bh_lock_sock_nested(meta_sk); + if (sock_owned_by_user(meta_sk)) { + skb->sk = meta_sk; + if (unlikely(sk_add_backlog(meta_sk, skb, + meta_sk->sk_rcvbuf + meta_sk->sk_sndbuf))) { + bh_unlock_sock(meta_sk); + NET_INC_STATS_BH(net, LINUX_MIB_TCPBACKLOGDROP); + sock_put(meta_sk); /* Taken by mptcp_search_req */ + kfree_skb(skb); + return 1; + } + } else if (skb->protocol == htons(ETH_P_IP)) { + tcp_v4_do_rcv(meta_sk, skb); +#if IS_ENABLED(CONFIG_IPV6) + } else { /* IPv6 */ + tcp_v6_do_rcv(meta_sk, skb); +#endif /* CONFIG_IPV6 */ + } + bh_unlock_sock(meta_sk); + sock_put(meta_sk); /* Taken by mptcp_vX_search_req */ + return 1; +} + +struct mp_join *mptcp_find_join(const struct sk_buff *skb) +{ + const struct tcphdr *th = tcp_hdr(skb); + unsigned char *ptr; + int length = (th->doff * 4) - sizeof(struct tcphdr); + + /* Jump through the options to check whether JOIN is there */ + ptr = (unsigned char *)(th + 1); + while (length > 0) { + int opcode = *ptr++; + int opsize; + + switch (opcode) { + case TCPOPT_EOL: + return NULL; + case TCPOPT_NOP: /* Ref: RFC 793 section 3.1 */ + length--; + continue; + default: + opsize = *ptr++; + if (opsize < 2) /* "silly options" */ + return NULL; + if (opsize > length) + return NULL; /* don't parse partial options */ + if (opcode == TCPOPT_MPTCP && + ((struct mptcp_option *)(ptr - 2))->sub == MPTCP_SUB_JOIN) { + return (struct mp_join *)(ptr - 2); + } + ptr += opsize - 2; + length -= opsize; + } + } + return NULL; +} + +int mptcp_lookup_join(struct sk_buff *skb, struct inet_timewait_sock *tw) +{ + const struct mptcp_cb *mpcb; + struct sock *meta_sk; + u32 token; + bool meta_v4; + struct mp_join *join_opt = mptcp_find_join(skb); + if (!join_opt) + return 0; + + /* MPTCP structures were not initialized, so return error */ + if (mptcp_init_failed) + return -1; + + token = join_opt->u.syn.token; + meta_sk = mptcp_hash_find(dev_net(skb_dst(skb)->dev), token); + if (!meta_sk) { + MPTCP_INC_STATS_BH(dev_net(skb_dst(skb)->dev), MPTCP_MIB_JOINNOTOKEN); + mptcp_debug("%s:mpcb not found:%x\n", __func__, token); + return -1; + } + + meta_v4 = meta_sk->sk_family == AF_INET; + if (meta_v4) { + if (skb->protocol == htons(ETH_P_IPV6)) { + mptcp_debug("SYN+MP_JOIN with IPV6 address on pure IPV4 meta\n"); + sock_put(meta_sk); /* Taken by mptcp_hash_find */ + return -1; + } + } else if (skb->protocol == htons(ETH_P_IP) && meta_sk->sk_ipv6only) { + mptcp_debug("SYN+MP_JOIN with IPV4 address on IPV6_V6ONLY meta\n"); + sock_put(meta_sk); /* Taken by mptcp_hash_find */ + return -1; + } + + mpcb = tcp_sk(meta_sk)->mpcb; + if (mpcb->infinite_mapping_rcv || mpcb->send_infinite_mapping) { + /* We are in fallback-mode on the reception-side - + * no new subflows! + */ + sock_put(meta_sk); /* Taken by mptcp_hash_find */ + MPTCP_INC_STATS_BH(sock_net(meta_sk), MPTCP_MIB_JOINFALLBACK); + return -1; + } + + /* Coming from time-wait-sock processing in tcp_v4_rcv. + * We have to deschedule it before continuing, because otherwise + * mptcp_v4_do_rcv will hit again on it inside tcp_v4_hnd_req. + */ + if (tw) { + inet_twsk_deschedule(tw); + inet_twsk_put(tw); + } + + TCP_SKB_CB(skb)->mptcp_flags |= MPTCPHDR_JOIN; + /* OK, this is a new syn/join, let's create a new open request and + * send syn+ack + */ + bh_lock_sock_nested(meta_sk); + if (sock_owned_by_user(meta_sk)) { + skb->sk = meta_sk; + if (unlikely(sk_add_backlog(meta_sk, skb, + meta_sk->sk_rcvbuf + meta_sk->sk_sndbuf))) { + bh_unlock_sock(meta_sk); + NET_INC_STATS_BH(sock_net(meta_sk), + LINUX_MIB_TCPBACKLOGDROP); + sock_put(meta_sk); /* Taken by mptcp_hash_find */ + kfree_skb(skb); + return 1; + } + } else if (skb->protocol == htons(ETH_P_IP)) { + tcp_v4_do_rcv(meta_sk, skb); +#if IS_ENABLED(CONFIG_IPV6) + } else { + tcp_v6_do_rcv(meta_sk, skb); +#endif /* CONFIG_IPV6 */ + } + bh_unlock_sock(meta_sk); + sock_put(meta_sk); /* Taken by mptcp_hash_find */ + return 1; +} + +int mptcp_do_join_short(struct sk_buff *skb, + const struct mptcp_options_received *mopt, + struct net *net) +{ + struct sock *meta_sk; + u32 token; + bool meta_v4; + + token = mopt->mptcp_rem_token; + meta_sk = mptcp_hash_find(net, token); + if (!meta_sk) { + MPTCP_INC_STATS_BH(dev_net(skb_dst(skb)->dev), MPTCP_MIB_JOINNOTOKEN); + mptcp_debug("%s:mpcb not found:%x\n", __func__, token); + return -1; + } + + meta_v4 = meta_sk->sk_family == AF_INET; + if (meta_v4) { + if (skb->protocol == htons(ETH_P_IPV6)) { + mptcp_debug("SYN+MP_JOIN with IPV6 address on pure IPV4 meta\n"); + sock_put(meta_sk); /* Taken by mptcp_hash_find */ + return -1; + } + } else if (skb->protocol == htons(ETH_P_IP) && meta_sk->sk_ipv6only) { + mptcp_debug("SYN+MP_JOIN with IPV4 address on IPV6_V6ONLY meta\n"); + sock_put(meta_sk); /* Taken by mptcp_hash_find */ + return -1; + } + + TCP_SKB_CB(skb)->mptcp_flags |= MPTCPHDR_JOIN; + + /* OK, this is a new syn/join, let's create a new open request and + * send syn+ack + */ + bh_lock_sock(meta_sk); + + /* This check is also done in mptcp_vX_do_rcv. But, there we cannot + * call tcp_vX_send_reset, because we hold already two socket-locks. + * (the listener and the meta from above) + * + * And the send-reset will try to take yet another one (ip_send_reply). + * Thus, we propagate the reset up to tcp_rcv_state_process. + */ + if (tcp_sk(meta_sk)->mpcb->infinite_mapping_rcv || + tcp_sk(meta_sk)->mpcb->send_infinite_mapping || + meta_sk->sk_state == TCP_CLOSE || !tcp_sk(meta_sk)->inside_tk_table) { + MPTCP_INC_STATS_BH(sock_net(meta_sk), MPTCP_MIB_JOINFALLBACK); + bh_unlock_sock(meta_sk); + sock_put(meta_sk); /* Taken by mptcp_hash_find */ + return -1; + } + + if (sock_owned_by_user(meta_sk)) { + skb->sk = meta_sk; + if (unlikely(sk_add_backlog(meta_sk, skb, + meta_sk->sk_rcvbuf + meta_sk->sk_sndbuf))) + NET_INC_STATS_BH(net, LINUX_MIB_TCPBACKLOGDROP); + else + /* Must make sure that upper layers won't free the + * skb if it is added to the backlog-queue. + */ + skb_get(skb); + } else { + /* mptcp_v4_do_rcv tries to free the skb - we prevent this, as + * the skb will finally be freed by tcp_v4_do_rcv (where we are + * coming from) + */ + skb_get(skb); + if (skb->protocol == htons(ETH_P_IP)) { + tcp_v4_do_rcv(meta_sk, skb); +#if IS_ENABLED(CONFIG_IPV6) + } else { /* IPv6 */ + tcp_v6_do_rcv(meta_sk, skb); +#endif /* CONFIG_IPV6 */ + } + } + + bh_unlock_sock(meta_sk); + sock_put(meta_sk); /* Taken by mptcp_hash_find */ + return 0; +} + +/** + * Equivalent of tcp_fin() for MPTCP + * Can be called only when the FIN is validly part + * of the data seqnum space. Not before when we get holes. + */ +void mptcp_fin(struct sock *meta_sk) +{ + struct sock *sk = NULL, *sk_it; + struct tcp_sock *meta_tp = tcp_sk(meta_sk); + struct mptcp_cb *mpcb = meta_tp->mpcb; + + mptcp_for_each_sk(mpcb, sk_it) { + if (tcp_sk(sk_it)->mptcp->path_index == mpcb->dfin_path_index) { + sk = sk_it; + break; + } + } + + if (!sk || sk->sk_state == TCP_CLOSE) + sk = mptcp_select_ack_sock(meta_sk); + + inet_csk_schedule_ack(sk); + + meta_sk->sk_shutdown |= RCV_SHUTDOWN; + sock_set_flag(meta_sk, SOCK_DONE); + + switch (meta_sk->sk_state) { + case TCP_SYN_RECV: + case TCP_ESTABLISHED: + /* Move to CLOSE_WAIT */ + tcp_set_state(meta_sk, TCP_CLOSE_WAIT); + inet_csk(sk)->icsk_ack.pingpong = 1; + break; + + case TCP_CLOSE_WAIT: + case TCP_CLOSING: + /* Received a retransmission of the FIN, do + * nothing. + */ + break; + case TCP_LAST_ACK: + /* RFC793: Remain in the LAST-ACK state. */ + break; + + case TCP_FIN_WAIT1: + /* This case occurs when a simultaneous close + * happens, we must ack the received FIN and + * enter the CLOSING state. + */ + tcp_send_ack(sk); + tcp_set_state(meta_sk, TCP_CLOSING); + break; + case TCP_FIN_WAIT2: + /* Received a FIN -- send ACK and enter TIME_WAIT. */ + tcp_send_ack(sk); + meta_tp->ops->time_wait(meta_sk, TCP_TIME_WAIT, 0); + break; + default: + /* Only TCP_LISTEN and TCP_CLOSE are left, in these + * cases we should never reach this piece of code. + */ + pr_err("%s: Impossible, meta_sk->sk_state=%d\n", __func__, + meta_sk->sk_state); + break; + } + + /* It _is_ possible, that we have something out-of-order _after_ FIN. + * Probably, we should reset in this case. For now drop them. + */ + mptcp_purge_ofo_queue(meta_tp); + sk_mem_reclaim(meta_sk); + + if (!sock_flag(meta_sk, SOCK_DEAD)) { + meta_sk->sk_state_change(meta_sk); + + /* Do not send POLL_HUP for half duplex close. */ + if (meta_sk->sk_shutdown == SHUTDOWN_MASK || + meta_sk->sk_state == TCP_CLOSE) + sk_wake_async(meta_sk, SOCK_WAKE_WAITD, POLL_HUP); + else + sk_wake_async(meta_sk, SOCK_WAKE_WAITD, POLL_IN); + } + + return; +} + +static void mptcp_xmit_retransmit_queue(struct sock *meta_sk) +{ + struct tcp_sock *meta_tp = tcp_sk(meta_sk); + struct sk_buff *skb; + + if (!meta_tp->packets_out) + return; + + tcp_for_write_queue(skb, meta_sk) { + if (skb == tcp_send_head(meta_sk)) + break; + + if (mptcp_retransmit_skb(meta_sk, skb)) + return; + + if (skb == tcp_write_queue_head(meta_sk)) + inet_csk_reset_xmit_timer(meta_sk, ICSK_TIME_RETRANS, + inet_csk(meta_sk)->icsk_rto, + TCP_RTO_MAX); + } +} + +/* Handle the DATA_ACK */ +static void mptcp_data_ack(struct sock *sk, const struct sk_buff *skb) +{ + struct sock *meta_sk = mptcp_meta_sk(sk); + struct tcp_sock *meta_tp = tcp_sk(meta_sk), *tp = tcp_sk(sk); + struct tcp_skb_cb *tcb = TCP_SKB_CB(skb); + u32 prior_snd_una = meta_tp->snd_una; + int prior_packets; + u32 nwin, data_ack, data_seq; + u16 data_len = 0; + + /* A valid packet came in - subflow is operational again */ + tp->pf = 0; + + /* Even if there is no data-ack, we stop retransmitting. + * Except if this is a SYN/ACK. Then it is just a retransmission + */ + if (tp->mptcp->pre_established && !tcp_hdr(skb)->syn) { + tp->mptcp->pre_established = 0; + sk_stop_timer(sk, &tp->mptcp->mptcp_ack_timer); + } + + /* If we are in infinite mapping mode, rx_opt.data_ack has been + * set by mptcp_clean_rtx_infinite. + */ + if (!(tcb->mptcp_flags & MPTCPHDR_ACK) && !tp->mpcb->infinite_mapping_snd) + goto exit; + + data_ack = tp->mptcp->rx_opt.data_ack; + + if (unlikely(!tp->mptcp->fully_established) && + tp->mptcp->snt_isn + 1 != TCP_SKB_CB(skb)->ack_seq) + /* As soon as a subflow-data-ack (not acking syn, thus snt_isn + 1) + * includes a data-ack, we are fully established + */ + mptcp_become_fully_estab(sk); + + /* Get the data_seq */ + if (mptcp_is_data_seq(skb)) { + data_seq = tp->mptcp->rx_opt.data_seq; + data_len = tp->mptcp->rx_opt.data_len; + } else { + data_seq = meta_tp->snd_wl1; + } + + /* If the ack is older than previous acks + * then we can probably ignore it. + */ + if (before(data_ack, prior_snd_una)) + goto exit; + + /* If the ack includes data we haven't sent yet, discard + * this segment (RFC793 Section 3.9). + */ + if (after(data_ack, meta_tp->snd_nxt)) + goto exit; + + /*** Now, update the window - inspired by tcp_ack_update_window ***/ + nwin = ntohs(tcp_hdr(skb)->window); + + if (likely(!tcp_hdr(skb)->syn)) + nwin <<= tp->rx_opt.snd_wscale; + + if (tcp_may_update_window(meta_tp, data_ack, data_seq, nwin)) { + tcp_update_wl(meta_tp, data_seq); + + /* Draft v09, Section 3.3.5: + * [...] It should only update its local receive window values + * when the largest sequence number allowed (i.e. DATA_ACK + + * receive window) increases. [...] + */ + if (meta_tp->snd_wnd != nwin && + !before(data_ack + nwin, tcp_wnd_end(meta_tp))) { + meta_tp->snd_wnd = nwin; + + if (nwin > meta_tp->max_window) + meta_tp->max_window = nwin; + } + } + /*** Done, update the window ***/ + + /* We passed data and got it acked, remove any soft error + * log. Something worked... + */ + sk->sk_err_soft = 0; + inet_csk(meta_sk)->icsk_probes_out = 0; + meta_tp->rcv_tstamp = tcp_time_stamp; + prior_packets = meta_tp->packets_out; + if (!prior_packets) + goto no_queue; + + meta_tp->snd_una = data_ack; + + mptcp_clean_rtx_queue(meta_sk, prior_snd_una); + + /* We are in loss-state, and something got acked, retransmit the whole + * queue now! + */ + if (inet_csk(meta_sk)->icsk_ca_state == TCP_CA_Loss && + after(data_ack, prior_snd_una)) { + mptcp_xmit_retransmit_queue(meta_sk); + inet_csk(meta_sk)->icsk_ca_state = TCP_CA_Open; + } + + /* Simplified version of tcp_new_space, because the snd-buffer + * is handled by all the subflows. + */ + if (sock_flag(meta_sk, SOCK_QUEUE_SHRUNK)) { + sock_reset_flag(meta_sk, SOCK_QUEUE_SHRUNK); + if (meta_sk->sk_socket && + test_bit(SOCK_NOSPACE, &meta_sk->sk_socket->flags)) + meta_sk->sk_write_space(meta_sk); + } + + if (meta_sk->sk_state != TCP_ESTABLISHED && + mptcp_rcv_state_process(meta_sk, sk, skb, data_seq, data_len)) + return; + +exit: + mptcp_push_pending_frames(meta_sk); + + return; + +no_queue: + if (tcp_send_head(meta_sk)) + tcp_ack_probe(meta_sk); + + mptcp_push_pending_frames(meta_sk); + + return; +} + +void mptcp_clean_rtx_infinite(const struct sk_buff *skb, struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk), *meta_tp = tcp_sk(mptcp_meta_sk(sk)); + + if (!tp->mpcb->infinite_mapping_snd) + return; + + /* The difference between both write_seq's represents the offset between + * data-sequence and subflow-sequence. As we are infinite, this must + * match. + * + * Thus, from this difference we can infer the meta snd_una. + */ + tp->mptcp->rx_opt.data_ack = meta_tp->snd_nxt - tp->snd_nxt + + tp->snd_una; + + mptcp_data_ack(sk, skb); +} + +/**** static functions used by mptcp_parse_options */ + +static void mptcp_send_reset_rem_id(const struct mptcp_cb *mpcb, u8 rem_id) +{ + struct sock *sk_it, *tmpsk; + + mptcp_for_each_sk_safe(mpcb, sk_it, tmpsk) { + if (tcp_sk(sk_it)->mptcp->rem_id == rem_id) { + mptcp_reinject_data(sk_it, 0); + mptcp_send_reset(sk_it); + } + } +} + +static inline bool is_valid_addropt_opsize(u8 mptcp_ver, + struct mp_add_addr *mpadd, + int opsize) +{ +#if IS_ENABLED(CONFIG_IPV6) + if (mptcp_ver < MPTCP_VERSION_1 && mpadd->ipver == 6) { + return opsize == MPTCP_SUB_LEN_ADD_ADDR6 || + opsize == MPTCP_SUB_LEN_ADD_ADDR6 + 2; + } + if (mptcp_ver >= MPTCP_VERSION_1 && mpadd->ipver == 6) + return opsize == MPTCP_SUB_LEN_ADD_ADDR6_VER1 || + opsize == MPTCP_SUB_LEN_ADD_ADDR6_VER1 + 2; +#endif + if (mptcp_ver < MPTCP_VERSION_1 && mpadd->ipver == 4) { + return opsize == MPTCP_SUB_LEN_ADD_ADDR4 || + opsize == MPTCP_SUB_LEN_ADD_ADDR4 + 2; + } + if (mptcp_ver >= MPTCP_VERSION_1 && mpadd->ipver == 4) { + return opsize == MPTCP_SUB_LEN_ADD_ADDR4_VER1 || + opsize == MPTCP_SUB_LEN_ADD_ADDR4_VER1 + 2; + } + return false; +} + +void mptcp_parse_options(const uint8_t *ptr, int opsize, + struct mptcp_options_received *mopt, + const struct sk_buff *skb, + struct tcp_sock *tp) +{ + const struct mptcp_option *mp_opt = (struct mptcp_option *)ptr; + + /* If the socket is mp-capable we would have a mopt. */ + if (!mopt) + return; + + switch (mp_opt->sub) { + case MPTCP_SUB_CAPABLE: + { + const struct mp_capable *mpcapable = (struct mp_capable *)ptr; + + if (opsize != MPTCP_SUB_LEN_CAPABLE_SYN && + opsize != MPTCP_SUB_LEN_CAPABLE_ACK) { + mptcp_debug("%s: mp_capable: bad option size %d\n", + __func__, opsize); + break; + } + + /* MPTCP-RFC 6824: + * "If receiving a message with the 'B' flag set to 1, and this + * is not understood, then this SYN MUST be silently ignored; + */ + if (mpcapable->b) { + mopt->drop_me = 1; + break; + } + + /* MPTCP-RFC 6824: + * "An implementation that only supports this method MUST set + * bit "H" to 1, and bits "C" through "G" to 0." + */ + if (!mpcapable->h) + break; + + mopt->saw_mpc = 1; + mopt->dss_csum = sysctl_mptcp_checksum || mpcapable->a; + + if (opsize >= MPTCP_SUB_LEN_CAPABLE_SYN) + mopt->mptcp_sender_key = mpcapable->sender_key; + if (opsize == MPTCP_SUB_LEN_CAPABLE_ACK) + mopt->mptcp_receiver_key = mpcapable->receiver_key; + + mopt->mptcp_ver = mpcapable->ver; + break; + } + case MPTCP_SUB_JOIN: + { + const struct mp_join *mpjoin = (struct mp_join *)ptr; + + if (opsize != MPTCP_SUB_LEN_JOIN_SYN && + opsize != MPTCP_SUB_LEN_JOIN_SYNACK && + opsize != MPTCP_SUB_LEN_JOIN_ACK) { + mptcp_debug("%s: mp_join: bad option size %d\n", + __func__, opsize); + break; + } + + /* saw_mpc must be set, because in tcp_check_req we assume that + * it is set to support falling back to reg. TCP if a rexmitted + * SYN has no MP_CAPABLE or MP_JOIN + */ + switch (opsize) { + case MPTCP_SUB_LEN_JOIN_SYN: + mopt->is_mp_join = 1; + mopt->saw_mpc = 1; + mopt->low_prio = mpjoin->b; + mopt->rem_id = mpjoin->addr_id; + mopt->mptcp_rem_token = mpjoin->u.syn.token; + mopt->mptcp_recv_nonce = mpjoin->u.syn.nonce; + break; + case MPTCP_SUB_LEN_JOIN_SYNACK: + mopt->saw_mpc = 1; + mopt->low_prio = mpjoin->b; + mopt->rem_id = mpjoin->addr_id; + mopt->mptcp_recv_tmac = mpjoin->u.synack.mac; + mopt->mptcp_recv_nonce = mpjoin->u.synack.nonce; + break; + case MPTCP_SUB_LEN_JOIN_ACK: + mopt->saw_mpc = 1; + mopt->join_ack = 1; + memcpy(mopt->mptcp_recv_mac, mpjoin->u.ack.mac, 20); + break; + } + break; + } + case MPTCP_SUB_DSS: + { + const struct mp_dss *mdss = (struct mp_dss *)ptr; + struct tcp_skb_cb *tcb = TCP_SKB_CB(skb); + + /* We check opsize for the csum and non-csum case. We do this, + * because the draft says that the csum SHOULD be ignored if + * it has not been negotiated in the MP_CAPABLE but still is + * present in the data. + * + * It will get ignored later in mptcp_queue_skb. + */ + if (opsize != mptcp_sub_len_dss(mdss, 0) && + opsize != mptcp_sub_len_dss(mdss, 1)) { + mptcp_debug("%s: mp_dss: bad option size %d\n", + __func__, opsize); + break; + } + + ptr += 4; + + if (mdss->A) { + tcb->mptcp_flags |= MPTCPHDR_ACK; + + if (mdss->a) { + mopt->data_ack = (u32) get_unaligned_be64(ptr); + ptr += MPTCP_SUB_LEN_ACK_64; + } else { + mopt->data_ack = get_unaligned_be32(ptr); + ptr += MPTCP_SUB_LEN_ACK; + } + } + + tcb->dss_off = (ptr - skb_transport_header(skb)); + + if (mdss->M) { + if (mdss->m) { + u64 data_seq64 = get_unaligned_be64(ptr); + + tcb->mptcp_flags |= MPTCPHDR_SEQ64_SET; + mopt->data_seq = (u32) data_seq64; + + ptr += 12; /* 64-bit dseq + subseq */ + } else { + mopt->data_seq = get_unaligned_be32(ptr); + ptr += 8; /* 32-bit dseq + subseq */ + } + mopt->data_len = get_unaligned_be16(ptr); + + tcb->mptcp_flags |= MPTCPHDR_SEQ; + + /* Is a check-sum present? */ + if (opsize == mptcp_sub_len_dss(mdss, 1)) + tcb->mptcp_flags |= MPTCPHDR_DSS_CSUM; + + /* DATA_FIN only possible with DSS-mapping */ + if (mdss->F) + tcb->mptcp_flags |= MPTCPHDR_FIN; + } + + break; + } + case MPTCP_SUB_ADD_ADDR: + { + struct mp_add_addr *mpadd = (struct mp_add_addr *)ptr; + + /* If tcp_sock is not available, MPTCP version can't be + * retrieved and ADD_ADDR opsize validation is not possible. + */ + if (!tp) + break; + + if (!is_valid_addropt_opsize(tp->mpcb->mptcp_ver, + mpadd, opsize)) { + mptcp_debug("%s: mp_add_addr: bad option size %d\n", + __func__, opsize); + break; + } + + /* We have to manually parse the options if we got two of them. */ + if (mopt->saw_add_addr) { + mopt->more_add_addr = 1; + break; + } + mopt->saw_add_addr = 1; + mopt->add_addr_ptr = ptr; + break; + } + case MPTCP_SUB_REMOVE_ADDR: + if ((opsize - MPTCP_SUB_LEN_REMOVE_ADDR) < 0) { + mptcp_debug("%s: mp_remove_addr: bad option size %d\n", + __func__, opsize); + break; + } + + if (mopt->saw_rem_addr) { + mopt->more_rem_addr = 1; + break; + } + mopt->saw_rem_addr = 1; + mopt->rem_addr_ptr = ptr; + break; + case MPTCP_SUB_PRIO: + { + const struct mp_prio *mpprio = (struct mp_prio *)ptr; + + if (opsize != MPTCP_SUB_LEN_PRIO && + opsize != MPTCP_SUB_LEN_PRIO_ADDR) { + mptcp_debug("%s: mp_prio: bad option size %d\n", + __func__, opsize); + break; + } + + mopt->saw_low_prio = 1; + mopt->low_prio = mpprio->b; + + if (opsize == MPTCP_SUB_LEN_PRIO_ADDR) { + mopt->saw_low_prio = 2; + mopt->prio_addr_id = mpprio->addr_id; + } + break; + } + case MPTCP_SUB_FAIL: + if (opsize != MPTCP_SUB_LEN_FAIL) { + mptcp_debug("%s: mp_fail: bad option size %d\n", + __func__, opsize); + break; + } + mopt->mp_fail = 1; + break; + case MPTCP_SUB_FCLOSE: + if (opsize != MPTCP_SUB_LEN_FCLOSE) { + mptcp_debug("%s: mp_fclose: bad option size %d\n", + __func__, opsize); + break; + } + + mopt->mp_fclose = 1; + mopt->mptcp_sender_key = ((struct mp_fclose *)ptr)->key; + + break; + default: + mptcp_debug("%s: Received unkown subtype: %d\n", + __func__, mp_opt->sub); + break; + } +} + +/** Parse only MPTCP options */ +void tcp_parse_mptcp_options(const struct sk_buff *skb, + struct mptcp_options_received *mopt) +{ + const struct tcphdr *th = tcp_hdr(skb); + int length = (th->doff * 4) - sizeof(struct tcphdr); + const unsigned char *ptr = (const unsigned char *)(th + 1); + + while (length > 0) { + int opcode = *ptr++; + int opsize; + + switch (opcode) { + case TCPOPT_EOL: + return; + case TCPOPT_NOP: /* Ref: RFC 793 section 3.1 */ + length--; + continue; + default: + opsize = *ptr++; + if (opsize < 2) /* "silly options" */ + return; + if (opsize > length) + return; /* don't parse partial options */ + if (opcode == TCPOPT_MPTCP) + mptcp_parse_options(ptr - 2, opsize, mopt, skb, NULL); + } + ptr += opsize - 2; + length -= opsize; + } +} + +int mptcp_check_rtt(const struct tcp_sock *tp, int time) +{ + struct mptcp_cb *mpcb = tp->mpcb; + struct sock *sk; + u32 rtt_max = 0; + + /* In MPTCP, we take the max delay across all flows, + * in order to take into account meta-reordering buffers. + */ + mptcp_for_each_sk(mpcb, sk) { + if (!mptcp_sk_can_recv(sk)) + continue; + + if (rtt_max < tcp_sk(sk)->rcv_rtt_est.rtt) + rtt_max = tcp_sk(sk)->rcv_rtt_est.rtt; + } + if (time < (rtt_max >> 3) || !rtt_max) + return 1; + + return 0; +} + +static void mptcp_handle_add_addr(const unsigned char *ptr, struct sock *sk) +{ + struct mp_add_addr *mpadd = (struct mp_add_addr *)ptr; + struct mptcp_cb *mpcb = tcp_sk(sk)->mpcb; + __be16 port = 0; + union inet_addr addr; + sa_family_t family; + + if (mpadd->ipver == 4) { + char *recv_hmac; + u8 hash_mac_check[20]; + u8 no_key[8]; + int msg_parts = 0; + + if (mpcb->mptcp_ver < MPTCP_VERSION_1) + goto skip_hmac_v4; + + *(u64 *)no_key = 0; + recv_hmac = (char *)mpadd->u.v4.mac; + if (mpadd->len == MPTCP_SUB_LEN_ADD_ADDR4_VER1) { + recv_hmac -= sizeof(mpadd->u.v4.port); + msg_parts = 2; + } else if (mpadd->len == MPTCP_SUB_LEN_ADD_ADDR4_VER1 + 2) { + msg_parts = 3; + } + mptcp_hmac_sha1((u8 *)&mpcb->mptcp_rem_key, + (u8 *)no_key, + (u32 *)hash_mac_check, msg_parts, + 1, (u8 *)&mpadd->addr_id, + 4, (u8 *)&mpadd->u.v4.addr.s_addr, + 2, (u8 *)&mpadd->u.v4.port); + if (memcmp(hash_mac_check, recv_hmac, 8) != 0) + /* ADD_ADDR2 discarded */ + return; +skip_hmac_v4: + if ((mpcb->mptcp_ver == MPTCP_VERSION_0 && + mpadd->len == MPTCP_SUB_LEN_ADD_ADDR4 + 2) || + (mpcb->mptcp_ver == MPTCP_VERSION_1 && + mpadd->len == MPTCP_SUB_LEN_ADD_ADDR4_VER1 + 2)) + port = mpadd->u.v4.port; + family = AF_INET; + addr.in = mpadd->u.v4.addr; +#if IS_ENABLED(CONFIG_IPV6) + } else if (mpadd->ipver == 6) { + char *recv_hmac; + u8 hash_mac_check[20]; + u8 no_key[8]; + int msg_parts = 0; + + if (mpcb->mptcp_ver < MPTCP_VERSION_1) + goto skip_hmac_v6; + + *(u64 *)no_key = 0; + recv_hmac = (char *)mpadd->u.v6.mac; + if (mpadd->len == MPTCP_SUB_LEN_ADD_ADDR6_VER1) { + recv_hmac -= sizeof(mpadd->u.v6.port); + msg_parts = 2; + } else if (mpadd->len == MPTCP_SUB_LEN_ADD_ADDR6_VER1 + 2) { + msg_parts = 3; + } + mptcp_hmac_sha1((u8 *)&mpcb->mptcp_rem_key, + (u8 *)no_key, + (u32 *)hash_mac_check, msg_parts, + 1, (u8 *)&mpadd->addr_id, + 16, (u8 *)&mpadd->u.v6.addr.s6_addr, + 2, (u8 *)&mpadd->u.v6.port); + if (memcmp(hash_mac_check, recv_hmac, 8) != 0) + /* ADD_ADDR2 discarded */ + return; +skip_hmac_v6: + if ((mpcb->mptcp_ver == MPTCP_VERSION_0 && + mpadd->len == MPTCP_SUB_LEN_ADD_ADDR6 + 2) || + (mpcb->mptcp_ver == MPTCP_VERSION_1 && + mpadd->len == MPTCP_SUB_LEN_ADD_ADDR6_VER1 + 2)) + port = mpadd->u.v6.port; + family = AF_INET6; + addr.in6 = mpadd->u.v6.addr; +#endif /* CONFIG_IPV6 */ + } else { + return; + } + + if (mpcb->pm_ops->add_raddr) + mpcb->pm_ops->add_raddr(mpcb, &addr, family, port, mpadd->addr_id); + + MPTCP_INC_STATS_BH(sock_net(sk), MPTCP_MIB_ADDADDRRX); +} + +static void mptcp_handle_rem_addr(const unsigned char *ptr, struct sock *sk) +{ + struct mp_remove_addr *mprem = (struct mp_remove_addr *)ptr; + int i; + u8 rem_id; + struct mptcp_cb *mpcb = tcp_sk(sk)->mpcb; + + for (i = 0; i <= mprem->len - MPTCP_SUB_LEN_REMOVE_ADDR; i++) { + rem_id = (&mprem->addrs_id)[i]; + + if (mpcb->pm_ops->rem_raddr) + mpcb->pm_ops->rem_raddr(mpcb, rem_id); + mptcp_send_reset_rem_id(mpcb, rem_id); + + MPTCP_INC_STATS_BH(sock_net(sk), MPTCP_MIB_REMADDRSUB); + } + + MPTCP_INC_STATS_BH(sock_net(sk), MPTCP_MIB_REMADDRRX); +} + +static void mptcp_parse_addropt(const struct sk_buff *skb, struct sock *sk) +{ + struct tcphdr *th = tcp_hdr(skb); + unsigned char *ptr; + int length = (th->doff * 4) - sizeof(struct tcphdr); + + /* Jump through the options to check whether ADD_ADDR is there */ + ptr = (unsigned char *)(th + 1); + while (length > 0) { + int opcode = *ptr++; + int opsize; + + switch (opcode) { + case TCPOPT_EOL: + return; + case TCPOPT_NOP: + length--; + continue; + default: + opsize = *ptr++; + if (opsize < 2) + return; + if (opsize > length) + return; /* don't parse partial options */ + if (opcode == TCPOPT_MPTCP && + ((struct mptcp_option *)ptr)->sub == MPTCP_SUB_ADD_ADDR) { + u8 mptcp_ver = tcp_sk(sk)->mpcb->mptcp_ver; + struct mp_add_addr *mpadd = (struct mp_add_addr *)ptr; + + if (!is_valid_addropt_opsize(mptcp_ver, mpadd, + opsize)) + goto cont; + + mptcp_handle_add_addr(ptr, sk); + } + if (opcode == TCPOPT_MPTCP && + ((struct mptcp_option *)ptr)->sub == MPTCP_SUB_REMOVE_ADDR) { + if ((opsize - MPTCP_SUB_LEN_REMOVE_ADDR) < 0) + goto cont; + + mptcp_handle_rem_addr(ptr, sk); + } +cont: + ptr += opsize - 2; + length -= opsize; + } + } + return; +} + +static inline int mptcp_mp_fail_rcvd(struct sock *sk, const struct tcphdr *th) +{ + struct mptcp_tcp_sock *mptcp = tcp_sk(sk)->mptcp; + struct sock *meta_sk = mptcp_meta_sk(sk); + struct mptcp_cb *mpcb = tcp_sk(sk)->mpcb; + + if (unlikely(mptcp->rx_opt.mp_fail)) { + MPTCP_INC_STATS_BH(sock_net(sk), MPTCP_MIB_MPFAILRX); + mptcp->rx_opt.mp_fail = 0; + + if (!th->rst && !mpcb->infinite_mapping_snd) { + mpcb->send_infinite_mapping = 1; + /* We resend everything that has not been acknowledged */ + meta_sk->sk_send_head = tcp_write_queue_head(meta_sk); + + /* We artificially restart the whole send-queue. Thus, + * it is as if no packets are in flight + */ + tcp_sk(meta_sk)->packets_out = 0; + + /* If the snd_nxt already wrapped around, we have to + * undo the wrapping, as we are restarting from snd_una + * on. + */ + if (tcp_sk(meta_sk)->snd_nxt < tcp_sk(meta_sk)->snd_una) { + mpcb->snd_high_order[mpcb->snd_hiseq_index] -= 2; + mpcb->snd_hiseq_index = mpcb->snd_hiseq_index ? 0 : 1; + } + tcp_sk(meta_sk)->snd_nxt = tcp_sk(meta_sk)->snd_una; + + /* Trigger a sending on the meta. */ + mptcp_push_pending_frames(meta_sk); + + mptcp_sub_force_close_all(mpcb, sk); + } + + return 0; + } + + if (unlikely(mptcp->rx_opt.mp_fclose)) { + MPTCP_INC_STATS_BH(sock_net(sk), MPTCP_MIB_FASTCLOSERX); + mptcp->rx_opt.mp_fclose = 0; + if (mptcp->rx_opt.mptcp_sender_key != mpcb->mptcp_loc_key) + return 0; + + mptcp_sub_force_close_all(mpcb, NULL); + + tcp_reset(meta_sk); + + return 1; + } + + return 0; +} + +static inline void mptcp_path_array_check(struct sock *meta_sk) +{ + struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; + + if (unlikely(mpcb->list_rcvd)) { + mpcb->list_rcvd = 0; + if (mpcb->pm_ops->new_remote_address) + mpcb->pm_ops->new_remote_address(meta_sk); + } +} + +int mptcp_handle_options(struct sock *sk, const struct tcphdr *th, + const struct sk_buff *skb) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct mptcp_options_received *mopt = &tp->mptcp->rx_opt; + + if (tp->mpcb->infinite_mapping_rcv || tp->mpcb->infinite_mapping_snd) + return 0; + + if (mptcp_mp_fail_rcvd(sk, th)) + return 1; + + /* RFC 6824, Section 3.3: + * If a checksum is not present when its use has been negotiated, the + * receiver MUST close the subflow with a RST as it is considered broken. + */ + if (mptcp_is_data_seq(skb) && tp->mpcb->dss_csum && + !(TCP_SKB_CB(skb)->mptcp_flags & MPTCPHDR_DSS_CSUM)) { + mptcp_send_reset(sk); + return 1; + } + + /* We have to acknowledge retransmissions of the third + * ack. + */ + if (mopt->join_ack) { + tcp_send_delayed_ack(sk); + mopt->join_ack = 0; + } + + if (mopt->saw_add_addr || mopt->saw_rem_addr) { + if (mopt->more_add_addr || mopt->more_rem_addr) { + mptcp_parse_addropt(skb, sk); + } else { + if (mopt->saw_add_addr) + mptcp_handle_add_addr(mopt->add_addr_ptr, sk); + if (mopt->saw_rem_addr) + mptcp_handle_rem_addr(mopt->rem_addr_ptr, sk); + } + + mopt->more_add_addr = 0; + mopt->saw_add_addr = 0; + mopt->more_rem_addr = 0; + mopt->saw_rem_addr = 0; + } + if (mopt->saw_low_prio) { + if (mopt->saw_low_prio == 1) { + tp->mptcp->rcv_low_prio = mopt->low_prio; + } else { + struct sock *sk_it; + mptcp_for_each_sk(tp->mpcb, sk_it) { + struct mptcp_tcp_sock *mptcp = tcp_sk(sk_it)->mptcp; + if (mptcp->rem_id == mopt->prio_addr_id) + mptcp->rcv_low_prio = mopt->low_prio; + } + } + mopt->saw_low_prio = 0; + } + + mptcp_data_ack(sk, skb); + + mptcp_path_array_check(mptcp_meta_sk(sk)); + /* Socket may have been mp_killed by a REMOVE_ADDR */ + if (tp->mp_killed) + return 1; + + return 0; +} + +/* In case of fastopen, some data can already be in the write queue. + * We need to update the sequence number of the segments as they + * were initially TCP sequence numbers. + */ +static void mptcp_rcv_synsent_fastopen(struct sock *meta_sk) +{ + struct tcp_sock *meta_tp = tcp_sk(meta_sk); + struct tcp_sock *master_tp = tcp_sk(meta_tp->mpcb->master_sk); + struct sk_buff *skb; + u32 new_mapping = meta_tp->write_seq - master_tp->snd_una; + + /* There should only be one skb in write queue: the data not + * acknowledged in the SYN+ACK. In this case, we need to map + * this data to data sequence numbers. + */ + skb_queue_walk(&meta_sk->sk_write_queue, skb) { + /* If the server only acknowledges partially the data sent in + * the SYN, we need to trim the acknowledged part because + * we don't want to retransmit this already received data. + * When we reach this point, tcp_ack() has already cleaned up + * fully acked segments. However, tcp trims partially acked + * segments only when retransmitting. Since MPTCP comes into + * play only now, we will fake an initial transmit, and + * retransmit_skb() will not be called. The following fragment + * comes from __tcp_retransmit_skb(). + */ + if (before(TCP_SKB_CB(skb)->seq, master_tp->snd_una)) { + BUG_ON(before(TCP_SKB_CB(skb)->end_seq, + master_tp->snd_una)); + /* tcp_trim_head can only returns ENOMEM if skb is + * cloned. It is not the case here (see + * tcp_send_syn_data). + */ + BUG_ON(tcp_trim_head(meta_sk, skb, master_tp->snd_una - + TCP_SKB_CB(skb)->seq)); + } + + TCP_SKB_CB(skb)->seq += new_mapping; + TCP_SKB_CB(skb)->end_seq += new_mapping; + } + + /* We can advance write_seq by the number of bytes unacknowledged + * and that were mapped in the previous loop. + */ + meta_tp->write_seq += master_tp->write_seq - master_tp->snd_una; + + /* The packets from the master_sk will be entailed to it later + * Until that time, its write queue is empty, and + * write_seq must align with snd_una + */ + master_tp->snd_nxt = master_tp->write_seq = master_tp->snd_una; + master_tp->packets_out = 0; + + /* Although these data have been sent already over the subsk, + * They have never been sent over the meta_sk, so we rewind + * the send_head so that tcp considers it as an initial send + * (instead of retransmit). + */ + meta_sk->sk_send_head = tcp_write_queue_head(meta_sk); +} + +/* The skptr is needed, because if we become MPTCP-capable, we have to switch + * from meta-socket to master-socket. + * + * @return: 1 - we want to reset this connection + * 2 - we want to discard the received syn/ack + * 0 - everything is fine - continue + */ +int mptcp_rcv_synsent_state_process(struct sock *sk, struct sock **skptr, + const struct sk_buff *skb, + const struct mptcp_options_received *mopt) +{ + struct tcp_sock *tp = tcp_sk(sk); + + if (mptcp(tp)) { + u8 hash_mac_check[20]; + struct mptcp_cb *mpcb = tp->mpcb; + + mptcp_hmac_sha1((u8 *)&mpcb->mptcp_rem_key, + (u8 *)&mpcb->mptcp_loc_key, + (u32 *)hash_mac_check, 2, + 4, (u8 *)&tp->mptcp->rx_opt.mptcp_recv_nonce, + 4, (u8 *)&tp->mptcp->mptcp_loc_nonce); + if (memcmp(hash_mac_check, + (char *)&tp->mptcp->rx_opt.mptcp_recv_tmac, 8)) { + MPTCP_INC_STATS_BH(sock_net(sk), MPTCP_MIB_JOINSYNACKMAC); + mptcp_sub_force_close(sk); + return 1; + } + + /* Set this flag in order to postpone data sending + * until the 4th ack arrives. + */ + tp->mptcp->pre_established = 1; + tp->mptcp->rcv_low_prio = tp->mptcp->rx_opt.low_prio; + + mptcp_hmac_sha1((u8 *)&mpcb->mptcp_loc_key, + (u8 *)&mpcb->mptcp_rem_key, + (u32 *)&tp->mptcp->sender_mac[0], 2, + 4, (u8 *)&tp->mptcp->mptcp_loc_nonce, + 4, (u8 *)&tp->mptcp->rx_opt.mptcp_recv_nonce); + + MPTCP_INC_STATS_BH(sock_net(sk), MPTCP_MIB_JOINSYNACKRX); + } else if (mopt->saw_mpc) { + struct sock *meta_sk = sk; + + MPTCP_INC_STATS_BH(sock_net(sk), MPTCP_MIB_MPCAPABLEACTIVEACK); + if (mopt->mptcp_ver > tcp_sk(sk)->mptcp_ver) + /* TODO Consider adding new MPTCP_INC_STATS entry */ + goto fallback; + + if (mptcp_create_master_sk(sk, mopt->mptcp_sender_key, + mopt->mptcp_ver, + ntohs(tcp_hdr(skb)->window))) + return 2; + + sk = tcp_sk(sk)->mpcb->master_sk; + *skptr = sk; + tp = tcp_sk(sk); + + sk->sk_bound_dev_if = skb->skb_iif; + + /* If fastopen was used data might be in the send queue. We + * need to update their sequence number to MPTCP-level seqno. + * Note that it can happen in rare cases that fastopen_req is + * NULL and syn_data is 0 but fastopen indeed occurred and + * data has been queued in the write queue (but not sent). + * Example of such rare cases: connect is non-blocking and + * TFO is configured to work without cookies. + */ + if (!skb_queue_empty(&meta_sk->sk_write_queue)) + mptcp_rcv_synsent_fastopen(meta_sk); + + /* -1, because the SYN consumed 1 byte. In case of TFO, we + * start the subflow-sequence number as if the data of the SYN + * is not part of any mapping. + */ + tp->mptcp->snt_isn = tp->snd_una - 1; + tp->mpcb->dss_csum = mopt->dss_csum; + if (tp->mpcb->dss_csum) + MPTCP_INC_STATS_BH(sock_net(sk), MPTCP_MIB_CSUMENABLED); + + tp->mptcp->include_mpc = 1; + + /* Ensure that fastopen is handled at the meta-level. */ + tp->fastopen_req = NULL; + + sk_set_socket(sk, mptcp_meta_sk(sk)->sk_socket); + sk->sk_wq = mptcp_meta_sk(sk)->sk_wq; + + /* hold in sk_clone_lock due to initialization to 2 */ + sock_put(sk); + } else { + MPTCP_INC_STATS_BH(sock_net(sk), MPTCP_MIB_MPCAPABLEACTIVEFALLBACK); +fallback: + tp->request_mptcp = 0; + + if (tp->inside_tk_table) + mptcp_hash_remove(tp); + } + + if (mptcp(tp)) + tp->mptcp->rcv_isn = TCP_SKB_CB(skb)->seq; + + return 0; +} + +bool mptcp_should_expand_sndbuf(const struct sock *sk) +{ + const struct sock *sk_it; + const struct sock *meta_sk = mptcp_meta_sk(sk); + const struct tcp_sock *meta_tp = tcp_sk(meta_sk); + int cnt_backups = 0; + int backup_available = 0; + + /* We circumvent this check in tcp_check_space, because we want to + * always call sk_write_space. So, we reproduce the check here. + */ + if (!meta_sk->sk_socket || + !test_bit(SOCK_NOSPACE, &meta_sk->sk_socket->flags)) + return false; + + /* If the user specified a specific send buffer setting, do + * not modify it. + */ + if (meta_sk->sk_userlocks & SOCK_SNDBUF_LOCK) + return false; + + /* If we are under global TCP memory pressure, do not expand. */ + if (sk_under_memory_pressure(meta_sk)) + return false; + + /* If we are under soft global TCP memory pressure, do not expand. */ + if (sk_memory_allocated(meta_sk) >= sk_prot_mem_limits(meta_sk, 0)) + return false; + + + /* For MPTCP we look for a subsocket that could send data. + * If we found one, then we update the send-buffer. + */ + mptcp_for_each_sk(meta_tp->mpcb, sk_it) { + struct tcp_sock *tp_it = tcp_sk(sk_it); + + if (!mptcp_sk_can_send(sk_it)) + continue; + + /* Backup-flows have to be counted - if there is no other + * subflow we take the backup-flow into account. + */ + if (tp_it->mptcp->rcv_low_prio || tp_it->mptcp->low_prio) + cnt_backups++; + + if (tp_it->packets_out < tp_it->snd_cwnd) { + if (tp_it->mptcp->rcv_low_prio || tp_it->mptcp->low_prio) { + backup_available = 1; + continue; + } + return true; + } + } + + /* Backup-flow is available for sending - update send-buffer */ + if (meta_tp->mpcb->cnt_established == cnt_backups && backup_available) + return true; + return false; +} + +void mptcp_init_buffer_space(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct sock *meta_sk = mptcp_meta_sk(sk); + struct tcp_sock *meta_tp = tcp_sk(meta_sk); + int space; + + tcp_init_buffer_space(sk); + + if (is_master_tp(tp)) { + meta_tp->rcvq_space.space = meta_tp->rcv_wnd; + meta_tp->rcvq_space.time = tcp_time_stamp; + meta_tp->rcvq_space.seq = meta_tp->copied_seq; + + /* If there is only one subflow, we just use regular TCP + * autotuning. User-locks are handled already by + * tcp_init_buffer_space + */ + meta_tp->window_clamp = tp->window_clamp; + meta_tp->rcv_ssthresh = tp->rcv_ssthresh; + meta_sk->sk_rcvbuf = sk->sk_rcvbuf; + meta_sk->sk_sndbuf = sk->sk_sndbuf; + + return; + } + + if (meta_sk->sk_userlocks & SOCK_RCVBUF_LOCK) + goto snd_buf; + + /* Adding a new subflow to the rcv-buffer space. We make a simple + * addition, to give some space to allow traffic on the new subflow. + * Autotuning will increase it further later on. + */ + space = min(meta_sk->sk_rcvbuf + sk->sk_rcvbuf, sysctl_tcp_rmem[2]); + if (space > meta_sk->sk_rcvbuf) { + meta_tp->window_clamp += tp->window_clamp; + meta_tp->rcv_ssthresh += tp->rcv_ssthresh; + meta_sk->sk_rcvbuf = space; + } + +snd_buf: + if (meta_sk->sk_userlocks & SOCK_SNDBUF_LOCK) + return; + + /* Adding a new subflow to the send-buffer space. We make a simple + * addition, to give some space to allow traffic on the new subflow. + * Autotuning will increase it further later on. + */ + space = min(meta_sk->sk_sndbuf + sk->sk_sndbuf, sysctl_tcp_wmem[2]); + if (space > meta_sk->sk_sndbuf) { + meta_sk->sk_sndbuf = space; + meta_sk->sk_write_space(meta_sk); + } +} + +void mptcp_tcp_set_rto(struct sock *sk) +{ + tcp_set_rto(sk); + mptcp_set_rto(sk); +} +#endif diff -urN linux-4.1.52-orig/net/mptcp/mptcp_ipv4.c linux-4.1.52/net/mptcp/mptcp_ipv4.c --- linux-4.1.52-orig/net/mptcp/mptcp_ipv4.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/mptcp/mptcp_ipv4.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,498 @@ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +/* + * MPTCP implementation - IPv4-specific functions + * + * Initial Design & Implementation: + * Sébastien Barré + * + * Current Maintainer: + * Christoph Paasch + * + * Additional authors: + * Jaakko Korkeaniemi + * Gregory Detal + * Fabien Duchêne + * Andreas Seelinger + * Lavkesh Lahngir + * Andreas Ripke + * Vlad Dogaru + * Octavian Purdila + * John Ronan + * Catalin Nicutar + * Brandon Heller + * + * + * 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 + +u32 mptcp_v4_get_nonce(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport) +{ + u32 hash[MD5_DIGEST_WORDS]; + + hash[0] = (__force u32)saddr; + hash[1] = (__force u32)daddr; + hash[2] = ((__force u16)sport << 16) + (__force u16)dport; + hash[3] = mptcp_seed++; + + md5_transform(hash, mptcp_secret); + + return hash[0]; +} + +u64 mptcp_v4_get_key(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport, + u32 seed) +{ + u32 hash[MD5_DIGEST_WORDS]; + + hash[0] = (__force u32)saddr; + hash[1] = (__force u32)daddr; + hash[2] = ((__force u16)sport << 16) + (__force u16)dport; + hash[3] = seed; + + md5_transform(hash, mptcp_secret); + + return *((u64 *)hash); +} + + +static void mptcp_v4_reqsk_destructor(struct request_sock *req) +{ + mptcp_reqsk_destructor(req); + + tcp_v4_reqsk_destructor(req); +} + +static int mptcp_v4_init_req(struct request_sock *req, struct sock *sk, + struct sk_buff *skb, bool want_cookie) +{ + tcp_request_sock_ipv4_ops.init_req(req, sk, skb, want_cookie); + + mptcp_rsk(req)->hash_entry.pprev = NULL; + mptcp_rsk(req)->is_sub = 0; + inet_rsk(req)->mptcp_rqsk = 1; + + /* In case of SYN-cookies, we wait for the isn to be generated - it is + * input to the key-generation. + */ + if (!want_cookie) + mptcp_reqsk_init(req, sk, skb, false); + + return 0; +} + +#ifdef CONFIG_SYN_COOKIES +static u32 mptcp_v4_cookie_init_seq(struct request_sock *req, struct sock *sk, + const struct sk_buff *skb, __u16 *mssp) +{ + __u32 isn = cookie_v4_init_sequence(req, sk, skb, mssp); + + tcp_rsk(req)->snt_isn = isn; + + mptcp_reqsk_init(req, sk, skb, true); + + return isn; +} +#endif + +static int mptcp_v4_join_init_req(struct request_sock *req, struct sock *sk, + struct sk_buff *skb, bool want_cookie) +{ + struct mptcp_request_sock *mtreq = mptcp_rsk(req); + struct mptcp_cb *mpcb = tcp_sk(sk)->mpcb; + union inet_addr addr; + int loc_id; + bool low_prio = false; + + /* We need to do this as early as possible. Because, if we fail later + * (e.g., get_local_id), then reqsk_free tries to remove the + * request-socket from the htb in mptcp_hash_request_remove as pprev + * may be different from NULL. + */ + mtreq->hash_entry.pprev = NULL; + + tcp_request_sock_ipv4_ops.init_req(req, sk, skb, want_cookie); + + mtreq->mptcp_loc_nonce = mptcp_v4_get_nonce(ip_hdr(skb)->saddr, + ip_hdr(skb)->daddr, + tcp_hdr(skb)->source, + tcp_hdr(skb)->dest); + addr.ip = inet_rsk(req)->ir_loc_addr; + loc_id = mpcb->pm_ops->get_local_id(AF_INET, &addr, sock_net(sk), &low_prio); + if (loc_id == -1) + return -1; + mtreq->loc_id = loc_id; + mtreq->low_prio = low_prio; + + mptcp_join_reqsk_init(mpcb, req, skb); + + return 0; +} + +/* Similar to tcp_request_sock_ops */ +struct request_sock_ops mptcp_request_sock_ops __read_mostly = { + .family = PF_INET, + .obj_size = sizeof(struct mptcp_request_sock), + .rtx_syn_ack = tcp_rtx_synack, + .send_ack = tcp_v4_reqsk_send_ack, + .destructor = mptcp_v4_reqsk_destructor, + .send_reset = tcp_v4_send_reset, + .syn_ack_timeout = tcp_syn_ack_timeout, +}; + +static void mptcp_v4_reqsk_queue_hash_add(struct sock *meta_sk, + struct request_sock *req, + const unsigned long timeout) +{ + const u32 h1 = inet_synq_hash(inet_rsk(req)->ir_rmt_addr, + inet_rsk(req)->ir_rmt_port, + 0, MPTCP_HASH_SIZE); + inet_csk_reqsk_queue_hash_add(meta_sk, req, timeout); + + rcu_read_lock(); + spin_lock(&mptcp_reqsk_hlock); + hlist_nulls_add_head_rcu(&mptcp_rsk(req)->hash_entry, &mptcp_reqsk_htb[h1]); + spin_unlock(&mptcp_reqsk_hlock); + rcu_read_unlock(); +} + +/* Similar to tcp_v4_conn_request */ +static int mptcp_v4_join_request(struct sock *meta_sk, struct sk_buff *skb) +{ + return tcp_conn_request(&mptcp_request_sock_ops, + &mptcp_join_request_sock_ipv4_ops, + meta_sk, skb); +} + +/* We only process join requests here. (either the SYN or the final ACK) */ +int mptcp_v4_do_rcv(struct sock *meta_sk, struct sk_buff *skb) +{ + const struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; + struct sock *child, *rsk = NULL; + int ret; + + if (!(TCP_SKB_CB(skb)->mptcp_flags & MPTCPHDR_JOIN)) { + struct tcphdr *th = tcp_hdr(skb); + const struct iphdr *iph = ip_hdr(skb); + struct sock *sk; + + sk = inet_lookup_established(sock_net(meta_sk), &tcp_hashinfo, + iph->saddr, th->source, iph->daddr, + th->dest, inet_iif(skb)); + + if (!sk) { + kfree_skb(skb); + return 0; + } + if (is_meta_sk(sk)) { + WARN("%s Did not find a sub-sk - did found the meta!\n", __func__); + kfree_skb(skb); + sock_put(sk); + return 0; + } + + if (sk->sk_state == TCP_TIME_WAIT) { + inet_twsk_put(inet_twsk(sk)); + kfree_skb(skb); + return 0; + } + + ret = tcp_v4_do_rcv(sk, skb); + sock_put(sk); + + return ret; + } + TCP_SKB_CB(skb)->mptcp_flags = 0; + + /* Has been removed from the tk-table. Thus, no new subflows. + * + * Check for close-state is necessary, because we may have been closed + * without passing by mptcp_close(). + * + * When falling back, no new subflows are allowed either. + */ + if (meta_sk->sk_state == TCP_CLOSE || !tcp_sk(meta_sk)->inside_tk_table || + mpcb->infinite_mapping_rcv || mpcb->send_infinite_mapping) + goto reset_and_discard; + + child = tcp_v4_hnd_req(meta_sk, skb); + + if (!child) + goto discard; + + if (child != meta_sk) { + sock_rps_save_rxhash(child, skb); + /* We don't call tcp_child_process here, because we hold + * already the meta-sk-lock and are sure that it is not owned + * by the user. + */ + ret = tcp_rcv_state_process(child, skb, tcp_hdr(skb), skb->len); + bh_unlock_sock(child); + sock_put(child); + if (ret) { + rsk = child; + goto reset_and_discard; + } + } else { + if (tcp_hdr(skb)->syn) { + mptcp_v4_join_request(meta_sk, skb); + goto discard; + } + goto reset_and_discard; + } + return 0; + +reset_and_discard: + if (reqsk_queue_len(&inet_csk(meta_sk)->icsk_accept_queue)) { + const struct tcphdr *th = tcp_hdr(skb); + const struct iphdr *iph = ip_hdr(skb); + struct request_sock *req; + /* If we end up here, it means we should not have matched on the + * request-socket. But, because the request-sock queue is only + * destroyed in mptcp_close, the socket may actually already be + * in close-state (e.g., through shutdown()) while still having + * pending request sockets. + */ + req = inet_csk_search_req(meta_sk, th->source, iph->saddr, iph->daddr); + + if (req) { + inet_csk_reqsk_queue_drop(meta_sk, req); + reqsk_put(req); + } + } + + tcp_v4_send_reset(rsk, skb); +discard: + kfree_skb(skb); + return 0; +} + +/* After this, the ref count of the meta_sk associated with the request_sock + * is incremented. Thus it is the responsibility of the caller + * to call sock_put() when the reference is not needed anymore. + */ +struct sock *mptcp_v4_search_req(const __be16 rport, const __be32 raddr, + const __be32 laddr, const struct net *net) +{ + const struct mptcp_request_sock *mtreq; + struct sock *meta_sk = NULL; + const struct hlist_nulls_node *node; + const u32 hash = inet_synq_hash(raddr, rport, 0, MPTCP_HASH_SIZE); + + rcu_read_lock(); +begin: + hlist_nulls_for_each_entry_rcu(mtreq, node, &mptcp_reqsk_htb[hash], + hash_entry) { + struct inet_request_sock *ireq = inet_rsk(rev_mptcp_rsk(mtreq)); + meta_sk = mtreq->mptcp_mpcb->meta_sk; + + if (ireq->ir_rmt_port == rport && + ireq->ir_rmt_addr == raddr && + ireq->ir_loc_addr == laddr && + rev_mptcp_rsk(mtreq)->rsk_ops->family == AF_INET && + net_eq(net, sock_net(meta_sk))) + goto found; + meta_sk = NULL; + } + /* A request-socket is destroyed by RCU. So, it might have been recycled + * and put into another hash-table list. So, after the lookup we may + * end up in a different list. So, we may need to restart. + * + * See also the comment in __inet_lookup_established. + */ + if (get_nulls_value(node) != hash + MPTCP_REQSK_NULLS_BASE) + goto begin; + +found: + if (meta_sk && unlikely(!atomic_inc_not_zero(&meta_sk->sk_refcnt))) + meta_sk = NULL; + rcu_read_unlock(); + + return meta_sk; +} + +/* Create a new IPv4 subflow. + * + * We are in user-context and meta-sock-lock is hold. + */ +int mptcp_init4_subsockets(struct sock *meta_sk, const struct mptcp_loc4 *loc, + struct mptcp_rem4 *rem) +{ + struct tcp_sock *tp; + struct sock *sk; + struct sockaddr_in loc_in, rem_in; + struct socket sock; + int ret; + + /** First, create and prepare the new socket */ + + sock.type = meta_sk->sk_socket->type; + sock.state = SS_UNCONNECTED; + sock.wq = meta_sk->sk_socket->wq; + sock.file = meta_sk->sk_socket->file; + sock.ops = NULL; + + ret = inet_create(sock_net(meta_sk), &sock, IPPROTO_TCP, 1); + if (unlikely(ret < 0)) { + mptcp_debug("%s inet_create failed ret: %d\n", __func__, ret); + return ret; + } + + sk = sock.sk; + tp = tcp_sk(sk); + + /* All subsockets need the MPTCP-lock-class */ + lockdep_set_class_and_name(&(sk)->sk_lock.slock, &meta_slock_key, "slock-AF_INET-MPTCP"); + lockdep_init_map(&(sk)->sk_lock.dep_map, "sk_lock-AF_INET-MPTCP", &meta_key, 0); + + if (mptcp_add_sock(meta_sk, sk, loc->loc4_id, rem->rem4_id, GFP_KERNEL)) + goto error; + + tp->mptcp->slave_sk = 1; + tp->mptcp->low_prio = loc->low_prio; + + /* Initializing the timer for an MPTCP subflow */ + setup_timer(&tp->mptcp->mptcp_ack_timer, mptcp_ack_handler, (unsigned long)sk); + + /** Then, connect the socket to the peer */ + loc_in.sin_family = AF_INET; + rem_in.sin_family = AF_INET; + loc_in.sin_port = 0; + if (rem->port) + rem_in.sin_port = rem->port; + else + rem_in.sin_port = inet_sk(meta_sk)->inet_dport; + loc_in.sin_addr = loc->addr; + rem_in.sin_addr = rem->addr; + + if (loc->if_idx) + sk->sk_bound_dev_if = loc->if_idx; + + ret = sock.ops->bind(&sock, (struct sockaddr *)&loc_in, sizeof(struct sockaddr_in)); + if (ret < 0) { + mptcp_debug("%s: MPTCP subsocket bind() failed, error %d\n", + __func__, ret); + goto error; + } + + mptcp_debug("%s: token %#x pi %d src_addr:%pI4:%d dst_addr:%pI4:%d ifidx: %d\n", + __func__, tcp_sk(meta_sk)->mpcb->mptcp_loc_token, + tp->mptcp->path_index, &loc_in.sin_addr, + ntohs(loc_in.sin_port), &rem_in.sin_addr, + ntohs(rem_in.sin_port), loc->if_idx); + + if (tcp_sk(meta_sk)->mpcb->pm_ops->init_subsocket_v4) + tcp_sk(meta_sk)->mpcb->pm_ops->init_subsocket_v4(sk, rem->addr); + + ret = sock.ops->connect(&sock, (struct sockaddr *)&rem_in, + sizeof(struct sockaddr_in), O_NONBLOCK); + if (ret < 0 && ret != -EINPROGRESS) { + mptcp_debug("%s: MPTCP subsocket connect() failed, error %d\n", + __func__, ret); + goto error; + } + + MPTCP_INC_STATS(sock_net(meta_sk), MPTCP_MIB_JOINSYNTX); + + sk_set_socket(sk, meta_sk->sk_socket); + sk->sk_wq = meta_sk->sk_wq; + + return 0; + +error: + /* May happen if mptcp_add_sock fails first */ + if (!mptcp(tp)) { + tcp_close(sk, 0); + } else { + local_bh_disable(); + mptcp_sub_force_close(sk); + local_bh_enable(); + } + return ret; +} +EXPORT_SYMBOL(mptcp_init4_subsockets); + +const struct inet_connection_sock_af_ops mptcp_v4_specific = { + .queue_xmit = ip_queue_xmit, + .send_check = tcp_v4_send_check, + .rebuild_header = inet_sk_rebuild_header, + .sk_rx_dst_set = inet_sk_rx_dst_set, + .conn_request = mptcp_conn_request, + .syn_recv_sock = tcp_v4_syn_recv_sock, + .net_header_len = sizeof(struct iphdr), + .setsockopt = ip_setsockopt, + .getsockopt = ip_getsockopt, + .addr2sockaddr = inet_csk_addr2sockaddr, + .sockaddr_len = sizeof(struct sockaddr_in), + .bind_conflict = inet_csk_bind_conflict, +#ifdef CONFIG_COMPAT + .compat_setsockopt = compat_ip_setsockopt, + .compat_getsockopt = compat_ip_getsockopt, +#endif + .mtu_reduced = tcp_v4_mtu_reduced, +}; + +struct tcp_request_sock_ops mptcp_request_sock_ipv4_ops; +struct tcp_request_sock_ops mptcp_join_request_sock_ipv4_ops; + +/* General initialization of IPv4 for MPTCP */ +int mptcp_pm_v4_init(void) +{ + int ret = 0; + struct request_sock_ops *ops = &mptcp_request_sock_ops; + + mptcp_request_sock_ipv4_ops = tcp_request_sock_ipv4_ops; + mptcp_request_sock_ipv4_ops.init_req = mptcp_v4_init_req; +#ifdef CONFIG_SYN_COOKIES + mptcp_request_sock_ipv4_ops.cookie_init_seq = mptcp_v4_cookie_init_seq; +#endif + mptcp_join_request_sock_ipv4_ops = tcp_request_sock_ipv4_ops; + mptcp_join_request_sock_ipv4_ops.init_req = mptcp_v4_join_init_req; + mptcp_join_request_sock_ipv4_ops.queue_hash_add = mptcp_v4_reqsk_queue_hash_add; + + ops->slab_name = kasprintf(GFP_KERNEL, "request_sock_%s", "MPTCP"); + if (ops->slab_name == NULL) { + ret = -ENOMEM; + goto out; + } + + ops->slab = kmem_cache_create(ops->slab_name, ops->obj_size, 0, + SLAB_DESTROY_BY_RCU|SLAB_HWCACHE_ALIGN, + NULL); + + if (ops->slab == NULL) { + ret = -ENOMEM; + goto err_reqsk_create; + } + +out: + return ret; + +err_reqsk_create: + kfree(ops->slab_name); + ops->slab_name = NULL; + goto out; +} + +void mptcp_pm_v4_undo(void) +{ + kmem_cache_destroy(mptcp_request_sock_ops.slab); + kfree(mptcp_request_sock_ops.slab_name); +} +#endif diff -urN linux-4.1.52-orig/net/mptcp/mptcp_ipv6.c linux-4.1.52/net/mptcp/mptcp_ipv6.c --- linux-4.1.52-orig/net/mptcp/mptcp_ipv6.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/mptcp/mptcp_ipv6.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,535 @@ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +/* + * MPTCP implementation - IPv6-specific functions + * + * Initial Design & Implementation: + * Sébastien Barré + * + * Current Maintainer: + * Jaakko Korkeaniemi + * + * Additional authors: + * Jaakko Korkeaniemi + * Gregory Detal + * Fabien Duchêne + * Andreas Seelinger + * Lavkesh Lahngir + * Andreas Ripke + * Vlad Dogaru + * Octavian Purdila + * John Ronan + * Catalin Nicutar + * Brandon Heller + * + * + * 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 + +__u32 mptcp_v6_get_nonce(const __be32 *saddr, const __be32 *daddr, + __be16 sport, __be16 dport) +{ + u32 secret[MD5_MESSAGE_BYTES / 4]; + u32 hash[MD5_DIGEST_WORDS]; + u32 i; + + memcpy(hash, saddr, 16); + for (i = 0; i < 4; i++) + secret[i] = mptcp_secret[i] + (__force u32)daddr[i]; + secret[4] = mptcp_secret[4] + + (((__force u16)sport << 16) + (__force u16)dport); + secret[5] = mptcp_seed++; + for (i = 6; i < MD5_MESSAGE_BYTES / 4; i++) + secret[i] = mptcp_secret[i]; + + md5_transform(hash, secret); + + return hash[0]; +} + +u64 mptcp_v6_get_key(const __be32 *saddr, const __be32 *daddr, + __be16 sport, __be16 dport, u32 seed) +{ + u32 secret[MD5_MESSAGE_BYTES / 4]; + u32 hash[MD5_DIGEST_WORDS]; + u32 i; + + memcpy(hash, saddr, 16); + for (i = 0; i < 4; i++) + secret[i] = mptcp_secret[i] + (__force u32)daddr[i]; + secret[4] = mptcp_secret[4] + + (((__force u16)sport << 16) + (__force u16)dport); + secret[5] = seed; + for (i = 6; i < MD5_MESSAGE_BYTES / 4; i++) + secret[i] = mptcp_secret[i]; + + md5_transform(hash, secret); + + return *((u64 *)hash); +} + +static void mptcp_v6_reqsk_destructor(struct request_sock *req) +{ + mptcp_reqsk_destructor(req); + + tcp_v6_reqsk_destructor(req); +} + +static int mptcp_v6_init_req(struct request_sock *req, struct sock *sk, + struct sk_buff *skb, bool want_cookie) +{ + tcp_request_sock_ipv6_ops.init_req(req, sk, skb, want_cookie); + + mptcp_rsk(req)->hash_entry.pprev = NULL; + mptcp_rsk(req)->is_sub = 0; + inet_rsk(req)->mptcp_rqsk = 1; + + /* In case of SYN-cookies, we wait for the isn to be generated - it is + * input to the key-generation. + */ + if (!want_cookie) + mptcp_reqsk_init(req, sk, skb, false); + + return 0; +} + +#ifdef CONFIG_SYN_COOKIES +static u32 mptcp_v6_cookie_init_seq(struct request_sock *req, struct sock *sk, + const struct sk_buff *skb, __u16 *mssp) +{ + __u32 isn = cookie_v6_init_sequence(req, sk, skb, mssp); + + tcp_rsk(req)->snt_isn = isn; + + mptcp_reqsk_init(req, sk, skb, true); + + return isn; +} +#endif + +static int mptcp_v6_join_init_req(struct request_sock *req, struct sock *sk, + struct sk_buff *skb, bool want_cookie) +{ + struct mptcp_request_sock *mtreq = mptcp_rsk(req); + struct mptcp_cb *mpcb = tcp_sk(sk)->mpcb; + union inet_addr addr; + int loc_id; + bool low_prio = false; + + /* We need to do this as early as possible. Because, if we fail later + * (e.g., get_local_id), then reqsk_free tries to remove the + * request-socket from the htb in mptcp_hash_request_remove as pprev + * may be different from NULL. + */ + mtreq->hash_entry.pprev = NULL; + + tcp_request_sock_ipv6_ops.init_req(req, sk, skb, want_cookie); + + mtreq->mptcp_loc_nonce = mptcp_v6_get_nonce(ipv6_hdr(skb)->saddr.s6_addr32, + ipv6_hdr(skb)->daddr.s6_addr32, + tcp_hdr(skb)->source, + tcp_hdr(skb)->dest); + addr.in6 = inet_rsk(req)->ir_v6_loc_addr; + loc_id = mpcb->pm_ops->get_local_id(AF_INET6, &addr, sock_net(sk), &low_prio); + if (loc_id == -1) + return -1; + mtreq->loc_id = loc_id; + mtreq->low_prio = low_prio; + + mptcp_join_reqsk_init(mpcb, req, skb); + + return 0; +} + +/* Similar to tcp6_request_sock_ops */ +struct request_sock_ops mptcp6_request_sock_ops __read_mostly = { + .family = AF_INET6, + .obj_size = sizeof(struct mptcp_request_sock), + .rtx_syn_ack = tcp_rtx_synack, + .send_ack = tcp_v6_reqsk_send_ack, + .destructor = mptcp_v6_reqsk_destructor, + .send_reset = tcp_v6_send_reset, + .syn_ack_timeout = tcp_syn_ack_timeout, +}; + +static void mptcp_v6_reqsk_queue_hash_add(struct sock *meta_sk, + struct request_sock *req, + const unsigned long timeout) +{ + const u32 h1 = inet6_synq_hash(&inet_rsk(req)->ir_v6_rmt_addr, + inet_rsk(req)->ir_rmt_port, + 0, MPTCP_HASH_SIZE); + inet6_csk_reqsk_queue_hash_add(meta_sk, req, timeout); + + rcu_read_lock(); + spin_lock(&mptcp_reqsk_hlock); + hlist_nulls_add_head_rcu(&mptcp_rsk(req)->hash_entry, &mptcp_reqsk_htb[h1]); + spin_unlock(&mptcp_reqsk_hlock); + rcu_read_unlock(); +} + +static int mptcp_v6_join_request(struct sock *meta_sk, struct sk_buff *skb) +{ + return tcp_conn_request(&mptcp6_request_sock_ops, + &mptcp_join_request_sock_ipv6_ops, + meta_sk, skb); +} + +int mptcp_v6_do_rcv(struct sock *meta_sk, struct sk_buff *skb) +{ + const struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; + struct sock *child, *rsk = NULL; + int ret; + + if (!(TCP_SKB_CB(skb)->mptcp_flags & MPTCPHDR_JOIN)) { + struct tcphdr *th = tcp_hdr(skb); + const struct ipv6hdr *ip6h = ipv6_hdr(skb); + struct sock *sk; + + sk = __inet6_lookup_established(sock_net(meta_sk), + &tcp_hashinfo, + &ip6h->saddr, th->source, + &ip6h->daddr, ntohs(th->dest), + tcp_v6_iif(skb)); + + if (!sk) { + kfree_skb(skb); + return 0; + } + if (is_meta_sk(sk)) { + WARN("%s Did not find a sub-sk!\n", __func__); + kfree_skb(skb); + sock_put(sk); + return 0; + } + + if (sk->sk_state == TCP_TIME_WAIT) { + inet_twsk_put(inet_twsk(sk)); + kfree_skb(skb); + return 0; + } + + ret = tcp_v6_do_rcv(sk, skb); + sock_put(sk); + + return ret; + } + TCP_SKB_CB(skb)->mptcp_flags = 0; + + /* Has been removed from the tk-table. Thus, no new subflows. + * + * Check for close-state is necessary, because we may have been closed + * without passing by mptcp_close(). + * + * When falling back, no new subflows are allowed either. + */ + if (meta_sk->sk_state == TCP_CLOSE || !tcp_sk(meta_sk)->inside_tk_table || + mpcb->infinite_mapping_rcv || mpcb->send_infinite_mapping) + goto reset_and_discard; + + child = tcp_v6_hnd_req(meta_sk, skb); + + if (!child) + goto discard; + + if (child != meta_sk) { + sock_rps_save_rxhash(child, skb); + /* We don't call tcp_child_process here, because we hold + * already the meta-sk-lock and are sure that it is not owned + * by the user. + */ + ret = tcp_rcv_state_process(child, skb, tcp_hdr(skb), skb->len); + bh_unlock_sock(child); + sock_put(child); + if (ret) { + rsk = child; + goto reset_and_discard; + } + } else { + if (tcp_hdr(skb)->syn) { + mptcp_v6_join_request(meta_sk, skb); + goto discard; + } + goto reset_and_discard; + } + return 0; + +reset_and_discard: + if (reqsk_queue_len(&inet_csk(meta_sk)->icsk_accept_queue)) { + const struct tcphdr *th = tcp_hdr(skb); + struct request_sock *req; + /* If we end up here, it means we should not have matched on the + * request-socket. But, because the request-sock queue is only + * destroyed in mptcp_close, the socket may actually already be + * in close-state (e.g., through shutdown()) while still having + * pending request sockets. + */ + req = inet6_csk_search_req(meta_sk, th->source, + &ipv6_hdr(skb)->saddr, + &ipv6_hdr(skb)->daddr, tcp_v6_iif(skb)); + + if (req) { + inet_csk_reqsk_queue_drop(meta_sk, req); + reqsk_put(req); + } + } + + tcp_v6_send_reset(rsk, skb); +discard: + kfree_skb(skb); + return 0; +} + +/* After this, the ref count of the meta_sk associated with the request_sock + * is incremented. Thus it is the responsibility of the caller + * to call sock_put() when the reference is not needed anymore. + */ +struct sock *mptcp_v6_search_req(const __be16 rport, const struct in6_addr *raddr, + const struct in6_addr *laddr, const struct net *net) +{ + const struct mptcp_request_sock *mtreq; + struct sock *meta_sk = NULL; + const struct hlist_nulls_node *node; + const u32 hash = inet6_synq_hash(raddr, rport, 0, MPTCP_HASH_SIZE); + + rcu_read_lock(); +begin: + hlist_nulls_for_each_entry_rcu(mtreq, node, &mptcp_reqsk_htb[hash], + hash_entry) { + struct inet_request_sock *treq = inet_rsk(rev_mptcp_rsk(mtreq)); + meta_sk = mtreq->mptcp_mpcb->meta_sk; + + if (inet_rsk(rev_mptcp_rsk(mtreq))->ir_rmt_port == rport && + rev_mptcp_rsk(mtreq)->rsk_ops->family == AF_INET6 && + ipv6_addr_equal(&treq->ir_v6_rmt_addr, raddr) && + ipv6_addr_equal(&treq->ir_v6_loc_addr, laddr) && + net_eq(net, sock_net(meta_sk))) + goto found; + meta_sk = NULL; + } + /* A request-socket is destroyed by RCU. So, it might have been recycled + * and put into another hash-table list. So, after the lookup we may + * end up in a different list. So, we may need to restart. + * + * See also the comment in __inet_lookup_established. + */ + if (get_nulls_value(node) != hash + MPTCP_REQSK_NULLS_BASE) + goto begin; + +found: + if (meta_sk && unlikely(!atomic_inc_not_zero(&meta_sk->sk_refcnt))) + meta_sk = NULL; + rcu_read_unlock(); + + return meta_sk; +} + +/* Create a new IPv6 subflow. + * + * We are in user-context and meta-sock-lock is hold. + */ +int mptcp_init6_subsockets(struct sock *meta_sk, const struct mptcp_loc6 *loc, + struct mptcp_rem6 *rem) +{ + struct tcp_sock *tp; + struct sock *sk; + struct sockaddr_in6 loc_in, rem_in; + struct socket sock; + int ret; + + /** First, create and prepare the new socket */ + + sock.type = meta_sk->sk_socket->type; + sock.state = SS_UNCONNECTED; + sock.wq = meta_sk->sk_socket->wq; + sock.file = meta_sk->sk_socket->file; + sock.ops = NULL; + + ret = inet6_create(sock_net(meta_sk), &sock, IPPROTO_TCP, 1); + if (unlikely(ret < 0)) { + mptcp_debug("%s inet6_create failed ret: %d\n", __func__, ret); + return ret; + } + + sk = sock.sk; + tp = tcp_sk(sk); + + /* All subsockets need the MPTCP-lock-class */ + lockdep_set_class_and_name(&(sk)->sk_lock.slock, &meta_slock_key, "slock-AF_INET-MPTCP"); + lockdep_init_map(&(sk)->sk_lock.dep_map, "sk_lock-AF_INET-MPTCP", &meta_key, 0); + + if (mptcp_add_sock(meta_sk, sk, loc->loc6_id, rem->rem6_id, GFP_KERNEL)) + goto error; + + tp->mptcp->slave_sk = 1; + tp->mptcp->low_prio = loc->low_prio; + + /* Initializing the timer for an MPTCP subflow */ + setup_timer(&tp->mptcp->mptcp_ack_timer, mptcp_ack_handler, (unsigned long)sk); + + /** Then, connect the socket to the peer */ + loc_in.sin6_family = AF_INET6; + rem_in.sin6_family = AF_INET6; + loc_in.sin6_port = 0; + if (rem->port) + rem_in.sin6_port = rem->port; + else + rem_in.sin6_port = inet_sk(meta_sk)->inet_dport; + loc_in.sin6_addr = loc->addr; + rem_in.sin6_addr = rem->addr; + + if (loc->if_idx) + sk->sk_bound_dev_if = loc->if_idx; + + ret = sock.ops->bind(&sock, (struct sockaddr *)&loc_in, sizeof(struct sockaddr_in6)); + if (ret < 0) { + mptcp_debug("%s: MPTCP subsocket bind()failed, error %d\n", + __func__, ret); + goto error; + } + + mptcp_debug("%s: token %#x pi %d src_addr:%pI6:%d dst_addr:%pI6:%d ifidx: %u\n", + __func__, tcp_sk(meta_sk)->mpcb->mptcp_loc_token, + tp->mptcp->path_index, &loc_in.sin6_addr, + ntohs(loc_in.sin6_port), &rem_in.sin6_addr, + ntohs(rem_in.sin6_port), loc->if_idx); + + if (tcp_sk(meta_sk)->mpcb->pm_ops->init_subsocket_v6) + tcp_sk(meta_sk)->mpcb->pm_ops->init_subsocket_v6(sk, rem->addr); + + ret = sock.ops->connect(&sock, (struct sockaddr *)&rem_in, + sizeof(struct sockaddr_in6), O_NONBLOCK); + if (ret < 0 && ret != -EINPROGRESS) { + mptcp_debug("%s: MPTCP subsocket connect() failed, error %d\n", + __func__, ret); + goto error; + } + + MPTCP_INC_STATS(sock_net(meta_sk), MPTCP_MIB_JOINSYNTX); + + sk_set_socket(sk, meta_sk->sk_socket); + sk->sk_wq = meta_sk->sk_wq; + + return 0; + +error: + /* May happen if mptcp_add_sock fails first */ + if (!mptcp(tp)) { + tcp_close(sk, 0); + } else { + local_bh_disable(); + mptcp_sub_force_close(sk); + local_bh_enable(); + } + return ret; +} +EXPORT_SYMBOL(mptcp_init6_subsockets); + +const struct inet_connection_sock_af_ops mptcp_v6_specific = { + .queue_xmit = inet6_csk_xmit, + .send_check = tcp_v6_send_check, + .rebuild_header = inet6_sk_rebuild_header, + .sk_rx_dst_set = inet6_sk_rx_dst_set, + .conn_request = mptcp_conn_request, + .syn_recv_sock = tcp_v6_syn_recv_sock, + .net_header_len = sizeof(struct ipv6hdr), + .net_frag_header_len = sizeof(struct frag_hdr), + .setsockopt = ipv6_setsockopt, + .getsockopt = ipv6_getsockopt, + .addr2sockaddr = inet6_csk_addr2sockaddr, + .sockaddr_len = sizeof(struct sockaddr_in6), + .bind_conflict = inet6_csk_bind_conflict, +#ifdef CONFIG_COMPAT + .compat_setsockopt = compat_ipv6_setsockopt, + .compat_getsockopt = compat_ipv6_getsockopt, +#endif + .mtu_reduced = tcp_v6_mtu_reduced, +}; + +const struct inet_connection_sock_af_ops mptcp_v6_mapped = { + .queue_xmit = ip_queue_xmit, + .send_check = tcp_v4_send_check, + .rebuild_header = inet_sk_rebuild_header, + .sk_rx_dst_set = inet_sk_rx_dst_set, + .conn_request = mptcp_conn_request, + .syn_recv_sock = tcp_v6_syn_recv_sock, + .net_header_len = sizeof(struct iphdr), + .setsockopt = ipv6_setsockopt, + .getsockopt = ipv6_getsockopt, + .addr2sockaddr = inet6_csk_addr2sockaddr, + .sockaddr_len = sizeof(struct sockaddr_in6), + .bind_conflict = inet6_csk_bind_conflict, +#ifdef CONFIG_COMPAT + .compat_setsockopt = compat_ipv6_setsockopt, + .compat_getsockopt = compat_ipv6_getsockopt, +#endif + .mtu_reduced = tcp_v4_mtu_reduced, +}; + +struct tcp_request_sock_ops mptcp_request_sock_ipv6_ops; +struct tcp_request_sock_ops mptcp_join_request_sock_ipv6_ops; + +int mptcp_pm_v6_init(void) +{ + int ret = 0; + struct request_sock_ops *ops = &mptcp6_request_sock_ops; + + mptcp_request_sock_ipv6_ops = tcp_request_sock_ipv6_ops; + mptcp_request_sock_ipv6_ops.init_req = mptcp_v6_init_req; +#ifdef CONFIG_SYN_COOKIES + mptcp_request_sock_ipv6_ops.cookie_init_seq = mptcp_v6_cookie_init_seq; +#endif + + mptcp_join_request_sock_ipv6_ops = tcp_request_sock_ipv6_ops; + mptcp_join_request_sock_ipv6_ops.init_req = mptcp_v6_join_init_req; + mptcp_join_request_sock_ipv6_ops.queue_hash_add = mptcp_v6_reqsk_queue_hash_add; + + ops->slab_name = kasprintf(GFP_KERNEL, "request_sock_%s", "MPTCP6"); + if (ops->slab_name == NULL) { + ret = -ENOMEM; + goto out; + } + + ops->slab = kmem_cache_create(ops->slab_name, ops->obj_size, 0, + SLAB_DESTROY_BY_RCU|SLAB_HWCACHE_ALIGN, + NULL); + + if (ops->slab == NULL) { + ret = -ENOMEM; + goto err_reqsk_create; + } + +out: + return ret; + +err_reqsk_create: + kfree(ops->slab_name); + ops->slab_name = NULL; + goto out; +} + +void mptcp_pm_v6_undo(void) +{ + kmem_cache_destroy(mptcp6_request_sock_ops.slab); + kfree(mptcp6_request_sock_ops.slab_name); +} +#endif diff -urN linux-4.1.52-orig/net/mptcp/mptcp_ndiffports.c linux-4.1.52/net/mptcp/mptcp_ndiffports.c --- linux-4.1.52-orig/net/mptcp/mptcp_ndiffports.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/mptcp/mptcp_ndiffports.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,163 @@ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +#include + +#include +#include + +#if IS_ENABLED(CONFIG_IPV6) +#include +#endif + +struct ndiffports_priv { + /* Worker struct for subflow establishment */ + struct work_struct subflow_work; + + struct mptcp_cb *mpcb; +}; + +static int num_subflows __read_mostly = 2; +module_param(num_subflows, int, 0644); +MODULE_PARM_DESC(num_subflows, "choose the number of subflows per MPTCP connection"); + +/** + * Create all new subflows, by doing calls to mptcp_initX_subsockets + * + * This function uses a goto next_subflow, to allow releasing the lock between + * new subflows and giving other processes a chance to do some work on the + * socket and potentially finishing the communication. + **/ +static void create_subflow_worker(struct work_struct *work) +{ + const struct ndiffports_priv *pm_priv = container_of(work, + struct ndiffports_priv, + subflow_work); + struct mptcp_cb *mpcb = pm_priv->mpcb; + struct sock *meta_sk = mpcb->meta_sk; + int iter = 0; + +next_subflow: + if (iter) { + release_sock(meta_sk); + mutex_unlock(&mpcb->mpcb_mutex); + + cond_resched(); + } + mutex_lock(&mpcb->mpcb_mutex); + lock_sock_nested(meta_sk, SINGLE_DEPTH_NESTING); + + iter++; + + if (sock_flag(meta_sk, SOCK_DEAD)) + goto exit; + + if (mpcb->master_sk && + !tcp_sk(mpcb->master_sk)->mptcp->fully_established) + goto exit; + + if (num_subflows > iter && num_subflows > mpcb->cnt_subflows) { + if (meta_sk->sk_family == AF_INET || + mptcp_v6_is_v4_mapped(meta_sk)) { + struct mptcp_loc4 loc; + struct mptcp_rem4 rem; + + loc.addr.s_addr = inet_sk(meta_sk)->inet_saddr; + loc.loc4_id = 0; + loc.low_prio = 0; + + rem.addr.s_addr = inet_sk(meta_sk)->inet_daddr; + rem.port = inet_sk(meta_sk)->inet_dport; + rem.rem4_id = 0; /* Default 0 */ + + mptcp_init4_subsockets(meta_sk, &loc, &rem); + } else { +#if IS_ENABLED(CONFIG_IPV6) + struct mptcp_loc6 loc; + struct mptcp_rem6 rem; + + loc.addr = inet6_sk(meta_sk)->saddr; + loc.loc6_id = 0; + loc.low_prio = 0; + + rem.addr = meta_sk->sk_v6_daddr; + rem.port = inet_sk(meta_sk)->inet_dport; + rem.rem6_id = 0; /* Default 0 */ + + mptcp_init6_subsockets(meta_sk, &loc, &rem); +#endif + } + goto next_subflow; + } + +exit: + release_sock(meta_sk); + mutex_unlock(&mpcb->mpcb_mutex); + sock_put(meta_sk); +} + +static void ndiffports_new_session(const struct sock *meta_sk) +{ + struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; + struct ndiffports_priv *fmp = (struct ndiffports_priv *)&mpcb->mptcp_pm[0]; + + /* Initialize workqueue-struct */ + INIT_WORK(&fmp->subflow_work, create_subflow_worker); + fmp->mpcb = mpcb; +} + +static void ndiffports_create_subflows(struct sock *meta_sk) +{ + const struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; + struct ndiffports_priv *pm_priv = (struct ndiffports_priv *)&mpcb->mptcp_pm[0]; + + if (mpcb->infinite_mapping_snd || mpcb->infinite_mapping_rcv || + mpcb->send_infinite_mapping || + mpcb->server_side || sock_flag(meta_sk, SOCK_DEAD)) + return; + + if (!work_pending(&pm_priv->subflow_work)) { + sock_hold(meta_sk); + queue_work(mptcp_wq, &pm_priv->subflow_work); + } +} + +static int ndiffports_get_local_id(sa_family_t family, union inet_addr *addr, + struct net *net, bool *low_prio) +{ + return 0; +} + +static struct mptcp_pm_ops ndiffports __read_mostly = { + .new_session = ndiffports_new_session, + .fully_established = ndiffports_create_subflows, + .get_local_id = ndiffports_get_local_id, + .name = "ndiffports", + .owner = THIS_MODULE, +}; + +/* General initialization of MPTCP_PM */ +static int __init ndiffports_register(void) +{ + BUILD_BUG_ON(sizeof(struct ndiffports_priv) > MPTCP_PM_SIZE); + + if (mptcp_register_path_manager(&ndiffports)) + goto exit; + + return 0; + +exit: + return -1; +} + +static void ndiffports_unregister(void) +{ + mptcp_unregister_path_manager(&ndiffports); +} + +module_init(ndiffports_register); +module_exit(ndiffports_unregister); + +MODULE_AUTHOR("Christoph Paasch"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("NDIFF-PORTS MPTCP"); +MODULE_VERSION("0.88"); +#endif diff -urN linux-4.1.52-orig/net/mptcp/mptcp_ofo_queue.c linux-4.1.52/net/mptcp/mptcp_ofo_queue.c --- linux-4.1.52-orig/net/mptcp/mptcp_ofo_queue.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/mptcp/mptcp_ofo_queue.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,297 @@ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +/* + * MPTCP implementation - Fast algorithm for MPTCP meta-reordering + * + * Initial Design & Implementation: + * Sébastien Barré + * + * Current Maintainer & Author: + * Christoph Paasch + * + * Additional authors: + * Jaakko Korkeaniemi + * Gregory Detal + * Fabien Duchêne + * Andreas Seelinger + * Lavkesh Lahngir + * Andreas Ripke + * Vlad Dogaru + * Octavian Purdila + * John Ronan + * Catalin Nicutar + * Brandon Heller + * + * 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 + +void mptcp_remove_shortcuts(const struct mptcp_cb *mpcb, + const struct sk_buff *skb) +{ + struct tcp_sock *tp; + + mptcp_for_each_tp(mpcb, tp) { + if (tp->mptcp->shortcut_ofoqueue == skb) { + tp->mptcp->shortcut_ofoqueue = NULL; + return; + } + } +} + +/* Does 'skb' fits after 'here' in the queue 'head' ? + * If yes, we queue it and return 1 + */ +static int mptcp_ofo_queue_after(struct sk_buff_head *head, + struct sk_buff *skb, struct sk_buff *here, + const struct tcp_sock *tp) +{ + struct sock *meta_sk = tp->meta_sk; + struct tcp_sock *meta_tp = tcp_sk(meta_sk); + u32 seq = TCP_SKB_CB(skb)->seq; + u32 end_seq = TCP_SKB_CB(skb)->end_seq; + + /* We want to queue skb after here, thus seq >= end_seq */ + if (before(seq, TCP_SKB_CB(here)->end_seq)) + return 0; + + if (seq == TCP_SKB_CB(here)->end_seq) { + bool fragstolen = false; + + if (!tcp_try_coalesce(meta_sk, here, skb, &fragstolen)) { + __skb_queue_after(&meta_tp->out_of_order_queue, here, skb); + return 1; + } else { + kfree_skb_partial(skb, fragstolen); + return -1; + } + } + + /* If here is the last one, we can always queue it */ + if (skb_queue_is_last(head, here)) { + __skb_queue_after(head, here, skb); + return 1; + } else { + struct sk_buff *skb1 = skb_queue_next(head, here); + /* It's not the last one, but does it fits between 'here' and + * the one after 'here' ? Thus, does end_seq <= after_here->seq + */ + if (!after(end_seq, TCP_SKB_CB(skb1)->seq)) { + __skb_queue_after(head, here, skb); + return 1; + } + } + + return 0; +} + +static void try_shortcut(struct sk_buff *shortcut, struct sk_buff *skb, + struct sk_buff_head *head, struct tcp_sock *tp) +{ + struct sock *meta_sk = tp->meta_sk; + struct tcp_sock *tp_it, *meta_tp = tcp_sk(meta_sk); + struct mptcp_cb *mpcb = meta_tp->mpcb; + struct sk_buff *skb1, *best_shortcut = NULL; + u32 seq = TCP_SKB_CB(skb)->seq; + u32 end_seq = TCP_SKB_CB(skb)->end_seq; + u32 distance = 0xffffffff; + + /* First, check the tp's shortcut */ + if (!shortcut) { + if (skb_queue_empty(head)) { + __skb_queue_head(head, skb); + goto end; + } + } else { + int ret = mptcp_ofo_queue_after(head, skb, shortcut, tp); + /* Does the tp's shortcut is a hit? If yes, we insert. */ + + if (ret) { + skb = (ret > 0) ? skb : NULL; + goto end; + } + } + + /* Check the shortcuts of the other subsockets. */ + mptcp_for_each_tp(mpcb, tp_it) { + shortcut = tp_it->mptcp->shortcut_ofoqueue; + /* Can we queue it here? If yes, do so! */ + if (shortcut) { + int ret = mptcp_ofo_queue_after(head, skb, shortcut, tp); + + if (ret) { + skb = (ret > 0) ? skb : NULL; + goto end; + } + } + + /* Could not queue it, check if we are close. + * We are looking for a shortcut, close enough to seq to + * set skb1 prematurely and thus improve the subsequent lookup, + * which tries to find a skb1 so that skb1->seq <= seq. + * + * So, here we only take shortcuts, whose shortcut->seq > seq, + * and minimize the distance between shortcut->seq and seq and + * set best_shortcut to this one with the minimal distance. + * + * That way, the subsequent while-loop is shortest. + */ + if (shortcut && after(TCP_SKB_CB(shortcut)->seq, seq)) { + /* Are we closer than the current best shortcut? */ + if ((u32)(TCP_SKB_CB(shortcut)->seq - seq) < distance) { + distance = (u32)(TCP_SKB_CB(shortcut)->seq - seq); + best_shortcut = shortcut; + } + } + } + + if (best_shortcut) + skb1 = best_shortcut; + else + skb1 = skb_peek_tail(head); + + if (seq == TCP_SKB_CB(skb1)->end_seq) { + bool fragstolen = false; + + if (!tcp_try_coalesce(meta_sk, skb1, skb, &fragstolen)) { + __skb_queue_after(&meta_tp->out_of_order_queue, skb1, skb); + } else { + kfree_skb_partial(skb, fragstolen); + skb = NULL; + } + + goto end; + } + + /* Find the insertion point, starting from best_shortcut if available. + * + * Inspired from tcp_data_queue_ofo. + */ + while (1) { + /* skb1->seq <= seq */ + if (!after(TCP_SKB_CB(skb1)->seq, seq)) + break; + if (skb_queue_is_first(head, skb1)) { + skb1 = NULL; + break; + } + skb1 = skb_queue_prev(head, skb1); + } + + /* Do skb overlap to previous one? */ + if (skb1 && before(seq, TCP_SKB_CB(skb1)->end_seq)) { + if (!after(end_seq, TCP_SKB_CB(skb1)->end_seq)) { + /* All the bits are present. */ + __kfree_skb(skb); + skb = NULL; + goto end; + } + if (seq == TCP_SKB_CB(skb1)->seq) { + if (skb_queue_is_first(head, skb1)) + skb1 = NULL; + else + skb1 = skb_queue_prev(head, skb1); + } + } + if (!skb1) + __skb_queue_head(head, skb); + else + __skb_queue_after(head, skb1, skb); + + /* And clean segments covered by new one as whole. */ + while (!skb_queue_is_last(head, skb)) { + skb1 = skb_queue_next(head, skb); + + if (!after(end_seq, TCP_SKB_CB(skb1)->seq)) + break; + + __skb_unlink(skb1, head); + mptcp_remove_shortcuts(mpcb, skb1); + __kfree_skb(skb1); + } + +end: + if (skb) { + skb_set_owner_r(skb, meta_sk); + tp->mptcp->shortcut_ofoqueue = skb; + } + + return; +} + +/** + * @sk: the subflow that received this skb. + */ +void mptcp_add_meta_ofo_queue(const struct sock *meta_sk, struct sk_buff *skb, + struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + + try_shortcut(tp->mptcp->shortcut_ofoqueue, skb, + &tcp_sk(meta_sk)->out_of_order_queue, tp); +} + +bool mptcp_prune_ofo_queue(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + bool res = false; + + if (!skb_queue_empty(&tp->out_of_order_queue)) { + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_OFOPRUNED); + mptcp_purge_ofo_queue(tp); + + /* No sack at the mptcp-level */ + sk_mem_reclaim(sk); + res = true; + } + + return res; +} + +void mptcp_ofo_queue(struct sock *meta_sk) +{ + struct tcp_sock *meta_tp = tcp_sk(meta_sk); + struct sk_buff *skb; + + while ((skb = skb_peek(&meta_tp->out_of_order_queue)) != NULL) { + u32 old_rcv_nxt = meta_tp->rcv_nxt; + if (after(TCP_SKB_CB(skb)->seq, meta_tp->rcv_nxt)) + break; + + if (!after(TCP_SKB_CB(skb)->end_seq, meta_tp->rcv_nxt)) { + __skb_unlink(skb, &meta_tp->out_of_order_queue); + mptcp_remove_shortcuts(meta_tp->mpcb, skb); + __kfree_skb(skb); + continue; + } + + __skb_unlink(skb, &meta_tp->out_of_order_queue); + mptcp_remove_shortcuts(meta_tp->mpcb, skb); + + __skb_queue_tail(&meta_sk->sk_receive_queue, skb); + meta_tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq; + mptcp_check_rcvseq_wrap(meta_tp, old_rcv_nxt); + + if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) + mptcp_fin(meta_sk); + } +} + +void mptcp_purge_ofo_queue(struct tcp_sock *meta_tp) +{ + struct sk_buff_head *head = &meta_tp->out_of_order_queue; + struct sk_buff *skb, *tmp; + + skb_queue_walk_safe(head, skb, tmp) { + __skb_unlink(skb, head); + mptcp_remove_shortcuts(meta_tp->mpcb, skb); + kfree_skb(skb); + } +} +#endif diff -urN linux-4.1.52-orig/net/mptcp/mptcp_olia.c linux-4.1.52/net/mptcp/mptcp_olia.c --- linux-4.1.52-orig/net/mptcp/mptcp_olia.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/mptcp/mptcp_olia.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,311 @@ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +/* + * MPTCP implementation - OPPORTUNISTIC LINKED INCREASES CONGESTION CONTROL: + * + * Algorithm design: + * Ramin Khalili + * Nicolas Gast + * Jean-Yves Le Boudec + * + * Implementation: + * Ramin Khalili + * + * Ported to the official MPTCP-kernel: + * Christoph Paasch + * + * 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 + +static int scale = 10; + +struct mptcp_olia { + u32 mptcp_loss1; + u32 mptcp_loss2; + u32 mptcp_loss3; + int epsilon_num; + u32 epsilon_den; + int mptcp_snd_cwnd_cnt; +}; + +static inline int mptcp_olia_sk_can_send(const struct sock *sk) +{ + return mptcp_sk_can_send(sk) && tcp_sk(sk)->srtt_us; +} + +static inline u64 mptcp_olia_scale(u64 val, int scale) +{ + return (u64) val << scale; +} + +/* take care of artificially inflate (see RFC5681) + * of cwnd during fast-retransmit phase + */ +static u32 mptcp_get_crt_cwnd(struct sock *sk) +{ + const struct inet_connection_sock *icsk = inet_csk(sk); + + if (icsk->icsk_ca_state == TCP_CA_Recovery) + return tcp_sk(sk)->snd_ssthresh; + else + return tcp_sk(sk)->snd_cwnd; +} + +/* return the dominator of the first term of the increasing term */ +static u64 mptcp_get_rate(const struct mptcp_cb *mpcb , u32 path_rtt) +{ + struct sock *sk; + u64 rate = 1; /* We have to avoid a zero-rate because it is used as a divisor */ + + mptcp_for_each_sk(mpcb, sk) { + struct tcp_sock *tp = tcp_sk(sk); + u64 scaled_num; + u32 tmp_cwnd; + + if (!mptcp_olia_sk_can_send(sk)) + continue; + + tmp_cwnd = mptcp_get_crt_cwnd(sk); + scaled_num = mptcp_olia_scale(tmp_cwnd, scale) * path_rtt; + rate += div_u64(scaled_num , tp->srtt_us); + } + rate *= rate; + return rate; +} + +/* find the maximum cwnd, used to find set M */ +static u32 mptcp_get_max_cwnd(const struct mptcp_cb *mpcb) +{ + struct sock *sk; + u32 best_cwnd = 0; + + mptcp_for_each_sk(mpcb, sk) { + u32 tmp_cwnd; + + if (!mptcp_olia_sk_can_send(sk)) + continue; + + tmp_cwnd = mptcp_get_crt_cwnd(sk); + if (tmp_cwnd > best_cwnd) + best_cwnd = tmp_cwnd; + } + return best_cwnd; +} + +static void mptcp_get_epsilon(const struct mptcp_cb *mpcb) +{ + struct mptcp_olia *ca; + struct tcp_sock *tp; + struct sock *sk; + u64 tmp_int, tmp_rtt, best_int = 0, best_rtt = 1; + u32 max_cwnd, tmp_cwnd; + u8 M = 0, B_not_M = 0; + + /* TODO - integrate this in the following loop - we just want to iterate once */ + + max_cwnd = mptcp_get_max_cwnd(mpcb); + + /* find the best path */ + mptcp_for_each_sk(mpcb, sk) { + tp = tcp_sk(sk); + ca = inet_csk_ca(sk); + + if (!mptcp_olia_sk_can_send(sk)) + continue; + + tmp_rtt = (u64)tp->srtt_us * tp->srtt_us; + /* TODO - check here and rename variables */ + tmp_int = max(ca->mptcp_loss3 - ca->mptcp_loss2, + ca->mptcp_loss2 - ca->mptcp_loss1); + + if ((u64)tmp_int * best_rtt >= (u64)best_int * tmp_rtt) { + best_rtt = tmp_rtt; + best_int = tmp_int; + } + } + + /* TODO - integrate this here in mptcp_get_max_cwnd and in the previous loop */ + /* find the size of M and B_not_M */ + mptcp_for_each_sk(mpcb, sk) { + tp = tcp_sk(sk); + ca = inet_csk_ca(sk); + + if (!mptcp_olia_sk_can_send(sk)) + continue; + + tmp_cwnd = mptcp_get_crt_cwnd(sk); + if (tmp_cwnd == max_cwnd) { + M++; + } else { + tmp_rtt = (u64)tp->srtt_us * tp->srtt_us; + tmp_int = max(ca->mptcp_loss3 - ca->mptcp_loss2, + ca->mptcp_loss2 - ca->mptcp_loss1); + + if ((u64)tmp_int * best_rtt == (u64)best_int * tmp_rtt) + B_not_M++; + } + } + + /* check if the path is in M or B_not_M and set the value of epsilon accordingly */ + mptcp_for_each_sk(mpcb, sk) { + tp = tcp_sk(sk); + ca = inet_csk_ca(sk); + + if (!mptcp_olia_sk_can_send(sk)) + continue; + + if (B_not_M == 0) { + ca->epsilon_num = 0; + ca->epsilon_den = 1; + } else { + tmp_rtt = (u64)tp->srtt_us * tp->srtt_us; + tmp_int = max(ca->mptcp_loss3 - ca->mptcp_loss2, + ca->mptcp_loss2 - ca->mptcp_loss1); + tmp_cwnd = mptcp_get_crt_cwnd(sk); + + if (tmp_cwnd < max_cwnd && + (u64)tmp_int * best_rtt == (u64)best_int * tmp_rtt) { + ca->epsilon_num = 1; + ca->epsilon_den = mpcb->cnt_established * B_not_M; + } else if (tmp_cwnd == max_cwnd) { + ca->epsilon_num = -1; + ca->epsilon_den = mpcb->cnt_established * M; + } else { + ca->epsilon_num = 0; + ca->epsilon_den = 1; + } + } + } +} + +/* setting the initial values */ +static void mptcp_olia_init(struct sock *sk) +{ + const struct tcp_sock *tp = tcp_sk(sk); + struct mptcp_olia *ca = inet_csk_ca(sk); + + if (mptcp(tp)) { + ca->mptcp_loss1 = tp->snd_una; + ca->mptcp_loss2 = tp->snd_una; + ca->mptcp_loss3 = tp->snd_una; + ca->mptcp_snd_cwnd_cnt = 0; + ca->epsilon_num = 0; + ca->epsilon_den = 1; + } +} + +/* updating inter-loss distance and ssthresh */ +static void mptcp_olia_set_state(struct sock *sk, u8 new_state) +{ + if (!mptcp(tcp_sk(sk))) + return; + + if (new_state == TCP_CA_Loss || + new_state == TCP_CA_Recovery || new_state == TCP_CA_CWR) { + struct mptcp_olia *ca = inet_csk_ca(sk); + + if (ca->mptcp_loss3 != ca->mptcp_loss2 && + !inet_csk(sk)->icsk_retransmits) { + ca->mptcp_loss1 = ca->mptcp_loss2; + ca->mptcp_loss2 = ca->mptcp_loss3; + } + } +} + +/* main algorithm */ +static void mptcp_olia_cong_avoid(struct sock *sk, u32 ack, u32 acked) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct mptcp_olia *ca = inet_csk_ca(sk); + const struct mptcp_cb *mpcb = tp->mpcb; + + u64 inc_num, inc_den, rate, cwnd_scaled; + + if (!mptcp(tp)) { + tcp_reno_cong_avoid(sk, ack, acked); + return; + } + + ca->mptcp_loss3 = tp->snd_una; + + if (!tcp_is_cwnd_limited(sk)) + return; + + /* slow start if it is in the safe area */ + if (tp->snd_cwnd <= tp->snd_ssthresh) { + tcp_slow_start(tp, acked); + return; + } + + mptcp_get_epsilon(mpcb); + rate = mptcp_get_rate(mpcb, tp->srtt_us); + cwnd_scaled = mptcp_olia_scale(tp->snd_cwnd, scale); + inc_den = ca->epsilon_den * tp->snd_cwnd * rate ? : 1; + + /* calculate the increasing term, scaling is used to reduce the rounding effect */ + if (ca->epsilon_num == -1) { + if (ca->epsilon_den * cwnd_scaled * cwnd_scaled < rate) { + inc_num = rate - ca->epsilon_den * + cwnd_scaled * cwnd_scaled; + ca->mptcp_snd_cwnd_cnt -= div64_u64( + mptcp_olia_scale(inc_num , scale) , inc_den); + } else { + inc_num = ca->epsilon_den * + cwnd_scaled * cwnd_scaled - rate; + ca->mptcp_snd_cwnd_cnt += div64_u64( + mptcp_olia_scale(inc_num , scale) , inc_den); + } + } else { + inc_num = ca->epsilon_num * rate + + ca->epsilon_den * cwnd_scaled * cwnd_scaled; + ca->mptcp_snd_cwnd_cnt += div64_u64( + mptcp_olia_scale(inc_num , scale) , inc_den); + } + + + if (ca->mptcp_snd_cwnd_cnt >= (1 << scale) - 1) { + if (tp->snd_cwnd < tp->snd_cwnd_clamp) + tp->snd_cwnd++; + ca->mptcp_snd_cwnd_cnt = 0; + } else if (ca->mptcp_snd_cwnd_cnt <= 0 - (1 << scale) + 1) { + tp->snd_cwnd = max((int) 1 , (int) tp->snd_cwnd - 1); + ca->mptcp_snd_cwnd_cnt = 0; + } +} + +static struct tcp_congestion_ops mptcp_olia = { + .init = mptcp_olia_init, + .ssthresh = tcp_reno_ssthresh, + .cong_avoid = mptcp_olia_cong_avoid, + .set_state = mptcp_olia_set_state, + .owner = THIS_MODULE, + .name = "olia", +}; + +static int __init mptcp_olia_register(void) +{ + BUILD_BUG_ON(sizeof(struct mptcp_olia) > ICSK_CA_PRIV_SIZE); + return tcp_register_congestion_control(&mptcp_olia); +} + +static void __exit mptcp_olia_unregister(void) +{ + tcp_unregister_congestion_control(&mptcp_olia); +} + +module_init(mptcp_olia_register); +module_exit(mptcp_olia_unregister); + +MODULE_AUTHOR("Ramin Khalili, Nicolas Gast, Jean-Yves Le Boudec"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MPTCP COUPLED CONGESTION CONTROL"); +MODULE_VERSION("0.1"); +#endif diff -urN linux-4.1.52-orig/net/mptcp/mptcp_output.c linux-4.1.52/net/mptcp/mptcp_output.c --- linux-4.1.52-orig/net/mptcp/mptcp_output.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/mptcp/mptcp_output.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,1773 @@ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +/* + * MPTCP implementation - Sending side + * + * Initial Design & Implementation: + * Sébastien Barré + * + * Current Maintainer & Author: + * Christoph Paasch + * + * Additional authors: + * Jaakko Korkeaniemi + * Gregory Detal + * Fabien Duchêne + * Andreas Seelinger + * Lavkesh Lahngir + * Andreas Ripke + * Vlad Dogaru + * Octavian Purdila + * John Ronan + * Catalin Nicutar + * Brandon Heller + * + * + * 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 + +static const int mptcp_dss_len = MPTCP_SUB_LEN_DSS_ALIGN + + MPTCP_SUB_LEN_ACK_ALIGN + + MPTCP_SUB_LEN_SEQ_ALIGN; + +static inline int mptcp_sub_len_remove_addr(u16 bitfield) +{ + unsigned int c; + for (c = 0; bitfield; c++) + bitfield &= bitfield - 1; + return MPTCP_SUB_LEN_REMOVE_ADDR + c - 1; +} + +int mptcp_sub_len_remove_addr_align(u16 bitfield) +{ + return ALIGN(mptcp_sub_len_remove_addr(bitfield), 4); +} +EXPORT_SYMBOL(mptcp_sub_len_remove_addr_align); + +/* get the data-seq and end-data-seq and store them again in the + * tcp_skb_cb + */ +static bool mptcp_reconstruct_mapping(struct sk_buff *skb) +{ + const struct mp_dss *mpdss = (struct mp_dss *)TCP_SKB_CB(skb)->dss; + u32 *p32; + u16 *p16; + + if (!mptcp_is_data_seq(skb)) + return false; + + if (!mpdss->M) + return false; + + /* Move the pointer to the data-seq */ + p32 = (u32 *)mpdss; + p32++; + if (mpdss->A) { + p32++; + if (mpdss->a) + p32++; + } + + TCP_SKB_CB(skb)->seq = ntohl(*p32); + + /* Get the data_len to calculate the end_data_seq */ + p32++; + p32++; + p16 = (u16 *)p32; + TCP_SKB_CB(skb)->end_seq = ntohs(*p16) + TCP_SKB_CB(skb)->seq; + + return true; +} + +static bool mptcp_is_reinjected(const struct sk_buff *skb) +{ + return TCP_SKB_CB(skb)->mptcp_flags & MPTCP_REINJECT; +} + +static void mptcp_find_and_set_pathmask(const struct sock *meta_sk, struct sk_buff *skb) +{ + struct sk_buff *skb_it; + + skb_it = tcp_write_queue_head(meta_sk); + + tcp_for_write_queue_from(skb_it, meta_sk) { + if (skb_it == tcp_send_head(meta_sk)) + break; + + if (TCP_SKB_CB(skb_it)->seq == TCP_SKB_CB(skb)->seq) { + TCP_SKB_CB(skb)->path_mask = TCP_SKB_CB(skb_it)->path_mask; + break; + } + } +} + +/* Reinject data from one TCP subflow to the meta_sk. If sk == NULL, we are + * coming from the meta-retransmit-timer + */ +static void __mptcp_reinject_data(struct sk_buff *orig_skb, struct sock *meta_sk, + struct sock *sk, int clone_it) +{ + struct sk_buff *skb, *skb1; + const struct tcp_sock *meta_tp = tcp_sk(meta_sk); + struct mptcp_cb *mpcb = meta_tp->mpcb; + u32 seq, end_seq; + + if (clone_it) { + /* pskb_copy is necessary here, because the TCP/IP-headers + * will be changed when it's going to be reinjected on another + * subflow. + */ + skb = pskb_copy_for_clone(orig_skb, GFP_ATOMIC); + } else { + __skb_unlink(orig_skb, &sk->sk_write_queue); + sock_set_flag(sk, SOCK_QUEUE_SHRUNK); + sk->sk_wmem_queued -= orig_skb->truesize; + sk_mem_uncharge(sk, orig_skb->truesize); + skb = orig_skb; + } + if (unlikely(!skb)) + return; + + if (sk && !mptcp_reconstruct_mapping(skb)) { + __kfree_skb(skb); + return; + } + + skb->sk = meta_sk; + + /* If it reached already the destination, we don't have to reinject it */ + if (!after(TCP_SKB_CB(skb)->end_seq, meta_tp->snd_una)) { + __kfree_skb(skb); + return; + } + + /* Only reinject segments that are fully covered by the mapping */ + if (skb->len + (mptcp_is_data_fin(skb) ? 1 : 0) != + TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq) { + u32 seq = TCP_SKB_CB(skb)->seq; + u32 end_seq = TCP_SKB_CB(skb)->end_seq; + + __kfree_skb(skb); + + /* Ok, now we have to look for the full mapping in the meta + * send-queue :S + */ + tcp_for_write_queue(skb, meta_sk) { + /* Not yet at the mapping? */ + if (before(TCP_SKB_CB(skb)->seq, seq)) + continue; + /* We have passed by the mapping */ + if (after(TCP_SKB_CB(skb)->end_seq, end_seq)) + return; + + __mptcp_reinject_data(skb, meta_sk, NULL, 1); + } + return; + } + + /* Segment goes back to the MPTCP-layer. So, we need to zero the + * path_mask/dss. + */ + memset(TCP_SKB_CB(skb)->dss, 0 , mptcp_dss_len); + + /* We need to find out the path-mask from the meta-write-queue + * to properly select a subflow. + */ + mptcp_find_and_set_pathmask(meta_sk, skb); + + /* If it's empty, just add */ + if (skb_queue_empty(&mpcb->reinject_queue)) { + skb_queue_head(&mpcb->reinject_queue, skb); + return; + } + + /* Find place to insert skb - or even we can 'drop' it, as the + * data is already covered by other skb's in the reinject-queue. + * + * This is inspired by code from tcp_data_queue. + */ + + skb1 = skb_peek_tail(&mpcb->reinject_queue); + seq = TCP_SKB_CB(skb)->seq; + while (1) { + if (!after(TCP_SKB_CB(skb1)->seq, seq)) + break; + if (skb_queue_is_first(&mpcb->reinject_queue, skb1)) { + skb1 = NULL; + break; + } + skb1 = skb_queue_prev(&mpcb->reinject_queue, skb1); + } + + /* Do skb overlap to previous one? */ + end_seq = TCP_SKB_CB(skb)->end_seq; + if (skb1 && before(seq, TCP_SKB_CB(skb1)->end_seq)) { + if (!after(end_seq, TCP_SKB_CB(skb1)->end_seq)) { + /* All the bits are present. Don't reinject */ + __kfree_skb(skb); + return; + } + if (seq == TCP_SKB_CB(skb1)->seq) { + if (skb_queue_is_first(&mpcb->reinject_queue, skb1)) + skb1 = NULL; + else + skb1 = skb_queue_prev(&mpcb->reinject_queue, skb1); + } + } + if (!skb1) + __skb_queue_head(&mpcb->reinject_queue, skb); + else + __skb_queue_after(&mpcb->reinject_queue, skb1, skb); + + /* And clean segments covered by new one as whole. */ + while (!skb_queue_is_last(&mpcb->reinject_queue, skb)) { + skb1 = skb_queue_next(&mpcb->reinject_queue, skb); + + if (!after(end_seq, TCP_SKB_CB(skb1)->seq)) + break; + + __skb_unlink(skb1, &mpcb->reinject_queue); + __kfree_skb(skb1); + } + return; +} + +/* Inserts data into the reinject queue */ +void mptcp_reinject_data(struct sock *sk, int clone_it) +{ + struct sk_buff *skb_it, *tmp; + struct tcp_sock *tp = tcp_sk(sk); + struct sock *meta_sk = tp->meta_sk; + + /* It has already been closed - there is really no point in reinjecting */ + if (meta_sk->sk_state == TCP_CLOSE) + return; + + skb_queue_walk_safe(&sk->sk_write_queue, skb_it, tmp) { + struct tcp_skb_cb *tcb = TCP_SKB_CB(skb_it); + /* Subflow syn's and fin's are not reinjected. + * + * As well as empty subflow-fins with a data-fin. + * They are reinjected below (without the subflow-fin-flag) + */ + if (tcb->tcp_flags & TCPHDR_SYN || + (tcb->tcp_flags & TCPHDR_FIN && !mptcp_is_data_fin(skb_it)) || + (tcb->tcp_flags & TCPHDR_FIN && mptcp_is_data_fin(skb_it) && !skb_it->len)) + continue; + + if (mptcp_is_reinjected(skb_it)) + continue; + + tcb->mptcp_flags |= MPTCP_REINJECT; + __mptcp_reinject_data(skb_it, meta_sk, sk, clone_it); + } + + skb_it = tcp_write_queue_tail(meta_sk); + /* If sk has sent the empty data-fin, we have to reinject it too. */ + if (skb_it && mptcp_is_data_fin(skb_it) && skb_it->len == 0 && + TCP_SKB_CB(skb_it)->path_mask & mptcp_pi_to_flag(tp->mptcp->path_index)) { + __mptcp_reinject_data(skb_it, meta_sk, NULL, 1); + } + + tp->pf = 1; + + mptcp_push_pending_frames(meta_sk); +} +EXPORT_SYMBOL(mptcp_reinject_data); + +static void mptcp_combine_dfin(const struct sk_buff *skb, + const struct sock *meta_sk, + struct sock *subsk) +{ + const struct tcp_sock *meta_tp = tcp_sk(meta_sk); + const struct mptcp_cb *mpcb = meta_tp->mpcb; + + /* In infinite mapping we always try to combine */ + if (mpcb->infinite_mapping_snd) + goto combine; + + /* Don't combine, if they didn't combine when closing - otherwise we end + * up in TIME_WAIT, even if our app is smart enough to avoid it. + */ + if (!mptcp_sk_can_recv(meta_sk) && !mpcb->dfin_combined) + return; + + /* Don't combine if there is still outstanding data that remains to be + * DATA_ACKed, because otherwise we may never be able to deliver this. + */ + if (meta_tp->snd_una != TCP_SKB_CB(skb)->seq) + return; + +combine: + if (tcp_close_state(subsk)) { + subsk->sk_shutdown |= SEND_SHUTDOWN; + TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_FIN; + } +} + +static int mptcp_write_dss_mapping(const struct tcp_sock *tp, const struct sk_buff *skb, + __be32 *ptr) +{ + const struct tcp_skb_cb *tcb = TCP_SKB_CB(skb); + __be32 *start = ptr; + __u16 data_len; + + *ptr++ = htonl(tcb->seq); /* data_seq */ + + /* If it's a non-data DATA_FIN, we set subseq to 0 (draft v7) */ + if (mptcp_is_data_fin(skb) && skb->len == 0) + *ptr++ = 0; /* subseq */ + else + *ptr++ = htonl(tp->write_seq - tp->mptcp->snt_isn); /* subseq */ + + if (tcb->mptcp_flags & MPTCPHDR_INF) + data_len = 0; + else + data_len = tcb->end_seq - tcb->seq; + + if (tp->mpcb->dss_csum && data_len) { + __be16 *p16 = (__be16 *)ptr; + __be32 hdseq = mptcp_get_highorder_sndbits(skb, tp->mpcb); + __wsum csum; + + *ptr = htonl(((data_len) << 16) | + (TCPOPT_EOL << 8) | + (TCPOPT_EOL)); + csum = csum_partial(ptr - 2, 12, skb->csum); + p16++; + *p16++ = csum_fold(csum_partial(&hdseq, sizeof(hdseq), csum)); + } else { + *ptr++ = htonl(((data_len) << 16) | + (TCPOPT_NOP << 8) | + (TCPOPT_NOP)); + } + + return ptr - start; +} + +static int mptcp_write_dss_data_ack(const struct tcp_sock *tp, const struct sk_buff *skb, + __be32 *ptr) +{ + struct mp_dss *mdss = (struct mp_dss *)ptr; + __be32 *start = ptr; + + mdss->kind = TCPOPT_MPTCP; + mdss->sub = MPTCP_SUB_DSS; + mdss->rsv1 = 0; + mdss->rsv2 = 0; + mdss->F = mptcp_is_data_fin(skb) ? 1 : 0; + mdss->m = 0; + mdss->M = mptcp_is_data_seq(skb) ? 1 : 0; + mdss->a = 0; + mdss->A = 1; + mdss->len = mptcp_sub_len_dss(mdss, tp->mpcb->dss_csum); + ptr++; + + *ptr++ = htonl(mptcp_meta_tp(tp)->rcv_nxt); + + return ptr - start; +} + +/* RFC6824 states that once a particular subflow mapping has been sent + * out it must never be changed. However, packets may be split while + * they are in the retransmission queue (due to SACK or ACKs) and that + * arguably means that we would change the mapping (e.g. it splits it, + * our sends out a subset of the initial mapping). + * + * Furthermore, the skb checksum is not always preserved across splits + * (e.g. mptcp_fragment) which would mean that we need to recompute + * the DSS checksum in this case. + * + * To avoid this we save the initial DSS mapping which allows us to + * send the same DSS mapping even for fragmented retransmits. + */ +static void mptcp_save_dss_data_seq(const struct tcp_sock *tp, struct sk_buff *skb) +{ + struct tcp_skb_cb *tcb = TCP_SKB_CB(skb); + __be32 *ptr = (__be32 *)tcb->dss; + + tcb->mptcp_flags |= MPTCPHDR_SEQ; + + ptr += mptcp_write_dss_data_ack(tp, skb, ptr); + ptr += mptcp_write_dss_mapping(tp, skb, ptr); +} + +/* Write the saved DSS mapping to the header */ +static int mptcp_write_dss_data_seq(const struct tcp_sock *tp, struct sk_buff *skb, + __be32 *ptr) +{ + __be32 *start = ptr; + + memcpy(ptr, TCP_SKB_CB(skb)->dss, mptcp_dss_len); + + /* update the data_ack */ + start[1] = htonl(mptcp_meta_tp(tp)->rcv_nxt); + + /* dss is in a union with inet_skb_parm and + * the IP layer expects zeroed IPCB fields. + */ + memset(TCP_SKB_CB(skb)->dss, 0 , mptcp_dss_len); + + return mptcp_dss_len/sizeof(*ptr); +} + +static bool mptcp_skb_entail(struct sock *sk, struct sk_buff *skb, int reinject) +{ + struct tcp_sock *tp = tcp_sk(sk); + const struct sock *meta_sk = mptcp_meta_sk(sk); + const struct mptcp_cb *mpcb = tp->mpcb; + struct tcp_skb_cb *tcb; + struct sk_buff *subskb = NULL; + + if (!reinject) + TCP_SKB_CB(skb)->mptcp_flags |= (mpcb->snd_hiseq_index ? + MPTCPHDR_SEQ64_INDEX : 0); + + subskb = pskb_copy_for_clone(skb, GFP_ATOMIC); + if (!subskb) + return false; + + /* At the subflow-level we need to call again tcp_init_tso_segs. We + * force this, by setting pcount to 0. It has been set to 1 prior to + * the call to mptcp_skb_entail. + */ + tcp_skb_pcount_set(subskb, 0); + + TCP_SKB_CB(skb)->path_mask |= mptcp_pi_to_flag(tp->mptcp->path_index); + + if (!(sk->sk_route_caps & NETIF_F_ALL_CSUM) && + skb->ip_summed == CHECKSUM_PARTIAL) { + subskb->csum = skb->csum = skb_checksum(skb, 0, skb->len, 0); + subskb->ip_summed = skb->ip_summed = CHECKSUM_NONE; + } + + tcb = TCP_SKB_CB(subskb); + + if (tp->mpcb->send_infinite_mapping && + !tp->mpcb->infinite_mapping_snd && + !before(tcb->seq, mptcp_meta_tp(tp)->snd_nxt)) { + tp->mptcp->fully_established = 1; + tp->mpcb->infinite_mapping_snd = 1; + tp->mptcp->infinite_cutoff_seq = tp->write_seq; + tcb->mptcp_flags |= MPTCPHDR_INF; + } + + if (mptcp_is_data_fin(subskb)) + mptcp_combine_dfin(subskb, meta_sk, sk); + + mptcp_save_dss_data_seq(tp, subskb); + + tcb->seq = tp->write_seq; + tcb->sacked = 0; /* reset the sacked field: from the point of view + * of this subflow, we are sending a brand new + * segment + */ + /* Take into account seg len */ + tp->write_seq += subskb->len + ((tcb->tcp_flags & TCPHDR_FIN) ? 1 : 0); + tcb->end_seq = tp->write_seq; + + /* If it's a non-payload DATA_FIN (also no subflow-fin), the + * segment is not part of the subflow but on a meta-only-level. + */ + if (!mptcp_is_data_fin(subskb) || tcb->end_seq != tcb->seq) { + tcp_add_write_queue_tail(sk, subskb); + sk->sk_wmem_queued += subskb->truesize; + sk_mem_charge(sk, subskb->truesize); + } else { + int err; + + /* Necessary to initialize for tcp_transmit_skb. mss of 1, as + * skb->len = 0 will force tso_segs to 1. + */ + tcp_init_tso_segs(sk, subskb, 1); + /* Empty data-fins are sent immediatly on the subflow */ + err = tcp_transmit_skb(sk, subskb, 1, GFP_ATOMIC); + + /* It has not been queued, we can free it now. */ + kfree_skb(subskb); + + if (err) + return false; + } + + if (!tp->mptcp->fully_established) { + tp->mptcp->second_packet = 1; + tp->mptcp->last_end_data_seq = TCP_SKB_CB(skb)->end_seq; + } + + return true; +} + +/* Fragment an skb and update the mptcp meta-data. Due to reinject, we + * might need to undo some operations done by tcp_fragment. + */ +static int mptcp_fragment(struct sock *meta_sk, struct sk_buff *skb, u32 len, + gfp_t gfp, int reinject) +{ + int ret, diff, old_factor; + struct sk_buff *buff; + u8 flags; + + if (skb_headlen(skb) < len) + diff = skb->len - len; + else + diff = skb->data_len; + old_factor = tcp_skb_pcount(skb); + + /* The mss_now in tcp_fragment is used to set the tso_segs of the skb. + * At the MPTCP-level we do not care about the absolute value. All we + * care about is that it is set to 1 for accurate packets_out + * accounting. + */ + ret = tcp_fragment(meta_sk, skb, len, UINT_MAX, gfp); + if (ret) + return ret; + + buff = skb->next; + + flags = TCP_SKB_CB(skb)->mptcp_flags; + TCP_SKB_CB(skb)->mptcp_flags = flags & ~(MPTCPHDR_FIN); + TCP_SKB_CB(buff)->mptcp_flags = flags; + TCP_SKB_CB(buff)->path_mask = TCP_SKB_CB(skb)->path_mask; + + /* If reinject == 1, the buff will be added to the reinject + * queue, which is currently not part of memory accounting. So + * undo the changes done by tcp_fragment and update the + * reinject queue. Also, undo changes to the packet counters. + */ + if (reinject == 1) { + int undo = buff->truesize - diff; + meta_sk->sk_wmem_queued -= undo; + sk_mem_uncharge(meta_sk, undo); + + tcp_sk(meta_sk)->mpcb->reinject_queue.qlen++; + meta_sk->sk_write_queue.qlen--; + + if (!before(tcp_sk(meta_sk)->snd_nxt, TCP_SKB_CB(buff)->end_seq)) { + undo = old_factor - tcp_skb_pcount(skb) - + tcp_skb_pcount(buff); + if (undo) + tcp_adjust_pcount(meta_sk, skb, -undo); + } + } + + return 0; +} + +/* Inspired by tcp_write_wakeup */ +int mptcp_write_wakeup(struct sock *meta_sk) +{ + struct tcp_sock *meta_tp = tcp_sk(meta_sk); + struct sk_buff *skb; + struct sock *sk_it; + int ans = 0; + + if (meta_sk->sk_state == TCP_CLOSE) + return -1; + + skb = tcp_send_head(meta_sk); + if (skb && + before(TCP_SKB_CB(skb)->seq, tcp_wnd_end(meta_tp))) { + unsigned int mss; + unsigned int seg_size = tcp_wnd_end(meta_tp) - TCP_SKB_CB(skb)->seq; + struct sock *subsk = meta_tp->mpcb->sched_ops->get_subflow(meta_sk, skb, true); + struct tcp_sock *subtp; + if (!subsk) + goto window_probe; + subtp = tcp_sk(subsk); + mss = tcp_current_mss(subsk); + + seg_size = min(tcp_wnd_end(meta_tp) - TCP_SKB_CB(skb)->seq, + tcp_wnd_end(subtp) - subtp->write_seq); + + if (before(meta_tp->pushed_seq, TCP_SKB_CB(skb)->end_seq)) + meta_tp->pushed_seq = TCP_SKB_CB(skb)->end_seq; + + /* We are probing the opening of a window + * but the window size is != 0 + * must have been a result SWS avoidance ( sender ) + */ + if (seg_size < TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq || + skb->len > mss) { + seg_size = min(seg_size, mss); + TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_PSH; + if (mptcp_fragment(meta_sk, skb, seg_size, + GFP_ATOMIC, 0)) + return -1; + } else if (!tcp_skb_pcount(skb)) { + /* see mptcp_write_xmit on why we use UINT_MAX */ + tcp_set_skb_tso_segs(meta_sk, skb, UINT_MAX); + } + + TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_PSH; + if (!mptcp_skb_entail(subsk, skb, 0)) + return -1; + skb_mstamp_get(&skb->skb_mstamp); + + mptcp_check_sndseq_wrap(meta_tp, TCP_SKB_CB(skb)->end_seq - + TCP_SKB_CB(skb)->seq); + tcp_event_new_data_sent(meta_sk, skb); + + __tcp_push_pending_frames(subsk, mss, TCP_NAGLE_PUSH); + + return 0; + } else { +window_probe: + if (between(meta_tp->snd_up, meta_tp->snd_una + 1, + meta_tp->snd_una + 0xFFFF)) { + mptcp_for_each_sk(meta_tp->mpcb, sk_it) { + if (mptcp_sk_can_send_ack(sk_it)) + tcp_xmit_probe_skb(sk_it, 1); + } + } + + /* At least one of the tcp_xmit_probe_skb's has to succeed */ + mptcp_for_each_sk(meta_tp->mpcb, sk_it) { + int ret; + + if (!mptcp_sk_can_send_ack(sk_it)) + continue; + + ret = tcp_xmit_probe_skb(sk_it, 0); + if (unlikely(ret > 0)) + ans = ret; + } + return ans; + } +} + +bool mptcp_write_xmit(struct sock *meta_sk, unsigned int mss_now, int nonagle, + int push_one, gfp_t gfp) +{ + struct tcp_sock *meta_tp = tcp_sk(meta_sk), *subtp; + struct sock *subsk = NULL; + struct mptcp_cb *mpcb = meta_tp->mpcb; + struct sk_buff *skb; + int reinject = 0; + unsigned int sublimit; + __u32 path_mask = 0; + + while ((skb = mpcb->sched_ops->next_segment(meta_sk, &reinject, &subsk, + &sublimit))) { + unsigned int limit; + + subtp = tcp_sk(subsk); + mss_now = tcp_current_mss(subsk); + + if (reinject == 1) { + if (!after(TCP_SKB_CB(skb)->end_seq, meta_tp->snd_una)) { + /* Segment already reached the peer, take the next one */ + __skb_unlink(skb, &mpcb->reinject_queue); + __kfree_skb(skb); + continue; + } + } + + /* If the segment was cloned (e.g. a meta retransmission), + * the header must be expanded/copied so that there is no + * corruption of TSO information. + */ + if (skb_unclone(skb, GFP_ATOMIC)) + break; + + if (unlikely(!tcp_snd_wnd_test(meta_tp, skb, mss_now))) + break; + + /* Force tso_segs to 1 by using UINT_MAX. + * We actually don't care about the exact number of segments + * emitted on the subflow. We need just to set tso_segs, because + * we still need an accurate packets_out count in + * tcp_event_new_data_sent. + */ + tcp_set_skb_tso_segs(meta_sk, skb, UINT_MAX); + + /* Check for nagle, irregardless of tso_segs. If the segment is + * actually larger than mss_now (TSO segment), then + * tcp_nagle_check will have partial == false and always trigger + * the transmission. + * tcp_write_xmit has a TSO-level nagle check which is not + * subject to the MPTCP-level. It is based on the properties of + * the subflow, not the MPTCP-level. + */ + if (unlikely(!tcp_nagle_test(meta_tp, skb, mss_now, + (tcp_skb_is_last(meta_sk, skb) ? + nonagle : TCP_NAGLE_PUSH)))) + break; + + limit = mss_now; + /* skb->len > mss_now is the equivalent of tso_segs > 1 in + * tcp_write_xmit. Otherwise split-point would return 0. + */ + if (skb->len > mss_now && !tcp_urg_mode(meta_tp)) + /* We limit the size of the skb so that it fits into the + * window. Call tcp_mss_split_point to avoid duplicating + * code. + * We really only care about fitting the skb into the + * window. That's why we use UINT_MAX. If the skb does + * not fit into the cwnd_quota or the NIC's max-segs + * limitation, it will be split by the subflow's + * tcp_write_xmit which does the appropriate call to + * tcp_mss_split_point. + */ + limit = tcp_mss_split_point(meta_sk, skb, mss_now, + UINT_MAX / mss_now, + nonagle); + + if (sublimit) + limit = min(limit, sublimit); + + if (skb->len > limit && + unlikely(mptcp_fragment(meta_sk, skb, limit, gfp, reinject))) + break; + + if (!mptcp_skb_entail(subsk, skb, reinject)) + break; + /* Nagle is handled at the MPTCP-layer, so + * always push on the subflow + */ + __tcp_push_pending_frames(subsk, mss_now, TCP_NAGLE_PUSH); + path_mask |= mptcp_pi_to_flag(subtp->mptcp->path_index); + skb_mstamp_get(&skb->skb_mstamp); + + if (!reinject) { + mptcp_check_sndseq_wrap(meta_tp, + TCP_SKB_CB(skb)->end_seq - + TCP_SKB_CB(skb)->seq); + tcp_event_new_data_sent(meta_sk, skb); + } + + tcp_minshall_update(meta_tp, mss_now, skb); + + if (reinject > 0) { + __skb_unlink(skb, &mpcb->reinject_queue); + kfree_skb(skb); + } + + if (push_one) + break; + } + + mptcp_for_each_sk(mpcb, subsk) { + subtp = tcp_sk(subsk); + + if (!(path_mask & mptcp_pi_to_flag(subtp->mptcp->path_index))) + continue; + + /* We have pushed data on this subflow. We ignore the call to + * cwnd_validate in tcp_write_xmit as is_cwnd_limited will never + * be true (we never push more than what the cwnd can accept). + * We need to ensure that we call tcp_cwnd_validate with + * is_cwnd_limited set to true if we have filled the cwnd. + */ + tcp_cwnd_validate(subsk, tcp_packets_in_flight(subtp) >= + subtp->snd_cwnd); + } + + return !meta_tp->packets_out && tcp_send_head(meta_sk); +} + +void mptcp_write_space(struct sock *sk) +{ + mptcp_push_pending_frames(mptcp_meta_sk(sk)); +} + +u32 __mptcp_select_window(struct sock *sk) +{ + struct inet_connection_sock *icsk = inet_csk(sk); + struct tcp_sock *tp = tcp_sk(sk), *meta_tp = mptcp_meta_tp(tp); + struct sock *meta_sk = mptcp_meta_sk(sk); + int mss, free_space, full_space, window; + + /* MSS for the peer's data. Previous versions used mss_clamp + * here. I don't know if the value based on our guesses + * of peer's MSS is better for the performance. It's more correct + * but may be worse for the performance because of rcv_mss + * fluctuations. --SAW 1998/11/1 + */ + mss = icsk->icsk_ack.rcv_mss; + free_space = tcp_space(meta_sk); + full_space = min_t(int, meta_tp->window_clamp, + tcp_full_space(meta_sk)); + + if (mss > full_space) + mss = full_space; + + if (free_space < (full_space >> 1)) { + /* If free_space is decreasing due to mostly meta-level + * out-of-order packets, don't turn off the quick-ack mode. + */ + if (meta_tp->rcv_nxt - meta_tp->copied_seq > ((full_space - free_space) >> 1)) + icsk->icsk_ack.quick = 0; + + if (tcp_memory_pressure) + /* TODO this has to be adapted when we support different + * MSS's among the subflows. + */ + meta_tp->rcv_ssthresh = min(meta_tp->rcv_ssthresh, + 4U * meta_tp->advmss); + + if (free_space < mss) + return 0; + } + + if (free_space > meta_tp->rcv_ssthresh) + free_space = meta_tp->rcv_ssthresh; + + /* Don't do rounding if we are using window scaling, since the + * scaled window will not line up with the MSS boundary anyway. + */ + window = meta_tp->rcv_wnd; + if (tp->rx_opt.rcv_wscale) { + window = free_space; + + /* Advertise enough space so that it won't get scaled away. + * Import case: prevent zero window announcement if + * 1< mss. + */ + if (((window >> tp->rx_opt.rcv_wscale) << tp-> + rx_opt.rcv_wscale) != window) + window = (((window >> tp->rx_opt.rcv_wscale) + 1) + << tp->rx_opt.rcv_wscale); + } else { + /* Get the largest window that is a nice multiple of mss. + * Window clamp already applied above. + * If our current window offering is within 1 mss of the + * free space we just keep it. This prevents the divide + * and multiply from happening most of the time. + * We also don't do any window rounding when the free space + * is too small. + */ + if (window <= free_space - mss || window > free_space) + window = (free_space / mss) * mss; + else if (mss == full_space && + free_space > window + (full_space >> 1)) + window = free_space; + } + + return window; +} + +void mptcp_syn_options(const struct sock *sk, struct tcp_out_options *opts, + unsigned *remaining) +{ + const struct tcp_sock *tp = tcp_sk(sk); + + opts->options |= OPTION_MPTCP; + if (is_master_tp(tp)) { + opts->mptcp_options |= OPTION_MP_CAPABLE | OPTION_TYPE_SYN; + opts->mptcp_ver = tcp_sk(sk)->mptcp_ver; + *remaining -= MPTCP_SUB_LEN_CAPABLE_SYN_ALIGN; + opts->mp_capable.sender_key = tp->mptcp_loc_key; + opts->dss_csum = !!sysctl_mptcp_checksum; + } else { + const struct mptcp_cb *mpcb = tp->mpcb; + + opts->mptcp_options |= OPTION_MP_JOIN | OPTION_TYPE_SYN; + *remaining -= MPTCP_SUB_LEN_JOIN_SYN_ALIGN; + opts->mp_join_syns.token = mpcb->mptcp_rem_token; + opts->mp_join_syns.low_prio = tp->mptcp->low_prio; + opts->addr_id = tp->mptcp->loc_id; + opts->mp_join_syns.sender_nonce = tp->mptcp->mptcp_loc_nonce; + } +} + +void mptcp_synack_options(struct request_sock *req, + struct tcp_out_options *opts, unsigned *remaining) +{ + struct mptcp_request_sock *mtreq; + mtreq = mptcp_rsk(req); + + opts->options |= OPTION_MPTCP; + /* MPCB not yet set - thus it's a new MPTCP-session */ + if (!mtreq->is_sub) { + opts->mptcp_options |= OPTION_MP_CAPABLE | OPTION_TYPE_SYNACK; + opts->mptcp_ver = mtreq->mptcp_ver; + opts->mp_capable.sender_key = mtreq->mptcp_loc_key; + opts->dss_csum = !!sysctl_mptcp_checksum || mtreq->dss_csum; + *remaining -= MPTCP_SUB_LEN_CAPABLE_SYN_ALIGN; + } else { + opts->mptcp_options |= OPTION_MP_JOIN | OPTION_TYPE_SYNACK; + opts->mp_join_syns.sender_truncated_mac = + mtreq->mptcp_hash_tmac; + opts->mp_join_syns.sender_nonce = mtreq->mptcp_loc_nonce; + opts->mp_join_syns.low_prio = mtreq->low_prio; + opts->addr_id = mtreq->loc_id; + *remaining -= MPTCP_SUB_LEN_JOIN_SYNACK_ALIGN; + } +} + +void mptcp_established_options(struct sock *sk, struct sk_buff *skb, + struct tcp_out_options *opts, unsigned *size) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct mptcp_cb *mpcb = tp->mpcb; + const struct tcp_skb_cb *tcb = skb ? TCP_SKB_CB(skb) : NULL; + + /* We are coming from tcp_current_mss with the meta_sk as an argument. + * It does not make sense to check for the options, because when the + * segment gets sent, another subflow will be chosen. + */ + if (!skb && is_meta_sk(sk)) + return; + + /* In fallback mp_fail-mode, we have to repeat it until the fallback + * has been done by the sender + */ + if (unlikely(tp->mptcp->send_mp_fail)) { + opts->options |= OPTION_MPTCP; + opts->mptcp_options |= OPTION_MP_FAIL; + *size += MPTCP_SUB_LEN_FAIL; + } + + if (unlikely(tp->send_mp_fclose)) { + opts->options |= OPTION_MPTCP; + opts->mptcp_options |= OPTION_MP_FCLOSE; + opts->mp_capable.receiver_key = mpcb->mptcp_rem_key; + *size += MPTCP_SUB_LEN_FCLOSE_ALIGN; + return; + } + + /* 1. If we are the sender of the infinite-mapping, we need the + * MPTCPHDR_INF-flag, because a retransmission of the + * infinite-announcment still needs the mptcp-option. + * + * We need infinite_cutoff_seq, because retransmissions from before + * the infinite-cutoff-moment still need the MPTCP-signalling to stay + * consistent. + * + * 2. If we are the receiver of the infinite-mapping, we always skip + * mptcp-options, because acknowledgments from before the + * infinite-mapping point have already been sent out. + * + * I know, the whole infinite-mapping stuff is ugly... + * + * TODO: Handle wrapped data-sequence numbers + * (even if it's very unlikely) + */ + if (unlikely(mpcb->infinite_mapping_snd) && + ((mpcb->send_infinite_mapping && tcb && + mptcp_is_data_seq(skb) && + !(tcb->mptcp_flags & MPTCPHDR_INF) && + !before(tcb->seq, tp->mptcp->infinite_cutoff_seq)) || + !mpcb->send_infinite_mapping)) + return; + + if (unlikely(tp->mptcp->include_mpc)) { + opts->options |= OPTION_MPTCP; + opts->mptcp_options |= OPTION_MP_CAPABLE | + OPTION_TYPE_ACK; + *size += MPTCP_SUB_LEN_CAPABLE_ACK_ALIGN; + opts->mptcp_ver = mpcb->mptcp_ver; + opts->mp_capable.sender_key = mpcb->mptcp_loc_key; + opts->mp_capable.receiver_key = mpcb->mptcp_rem_key; + opts->dss_csum = mpcb->dss_csum; + + if (skb) + tp->mptcp->include_mpc = 0; + } + if (unlikely(tp->mptcp->pre_established)) { + opts->options |= OPTION_MPTCP; + opts->mptcp_options |= OPTION_MP_JOIN | OPTION_TYPE_ACK; + *size += MPTCP_SUB_LEN_JOIN_ACK_ALIGN; + } + + if (unlikely(mpcb->addr_signal) && mpcb->pm_ops->addr_signal) { + mpcb->pm_ops->addr_signal(sk, size, opts, skb); + if (opts->add_addr_v6) + /* Skip subsequent options */ + return; + } + + if (!tp->mptcp->include_mpc && !tp->mptcp->pre_established) { + opts->options |= OPTION_MPTCP; + opts->mptcp_options |= OPTION_DATA_ACK; + /* If !skb, we come from tcp_current_mss and thus we always + * assume that the DSS-option will be set for the data-packet. + */ + if (skb && !mptcp_is_data_seq(skb)) { + *size += MPTCP_SUB_LEN_ACK_ALIGN; + } else { + /* Doesn't matter, if csum included or not. It will be + * either 10 or 12, and thus aligned = 12 + */ + *size += MPTCP_SUB_LEN_ACK_ALIGN + + MPTCP_SUB_LEN_SEQ_ALIGN; + } + + *size += MPTCP_SUB_LEN_DSS_ALIGN; + } + + if (unlikely(tp->mptcp->send_mp_prio) && + MAX_TCP_OPTION_SPACE - *size >= MPTCP_SUB_LEN_PRIO_ALIGN) { + opts->options |= OPTION_MPTCP; + opts->mptcp_options |= OPTION_MP_PRIO; + if (skb) + tp->mptcp->send_mp_prio = 0; + *size += MPTCP_SUB_LEN_PRIO_ALIGN; + } + + return; +} + +u16 mptcp_select_window(struct sock *sk) +{ + u16 new_win = tcp_select_window(sk); + struct tcp_sock *tp = tcp_sk(sk); + struct tcp_sock *meta_tp = mptcp_meta_tp(tp); + + meta_tp->rcv_wnd = tp->rcv_wnd; + meta_tp->rcv_wup = meta_tp->rcv_nxt; + + return new_win; +} + +void mptcp_options_write(__be32 *ptr, struct tcp_sock *tp, + const struct tcp_out_options *opts, + struct sk_buff *skb) +{ + if (unlikely(OPTION_MP_CAPABLE & opts->mptcp_options)) { + struct mp_capable *mpc = (struct mp_capable *)ptr; + + mpc->kind = TCPOPT_MPTCP; + + if ((OPTION_TYPE_SYN & opts->mptcp_options) || + (OPTION_TYPE_SYNACK & opts->mptcp_options)) { + mpc->sender_key = opts->mp_capable.sender_key; + mpc->len = MPTCP_SUB_LEN_CAPABLE_SYN; + mpc->ver = opts->mptcp_ver; + ptr += MPTCP_SUB_LEN_CAPABLE_SYN_ALIGN >> 2; + } else if (OPTION_TYPE_ACK & opts->mptcp_options) { + mpc->sender_key = opts->mp_capable.sender_key; + mpc->receiver_key = opts->mp_capable.receiver_key; + mpc->len = MPTCP_SUB_LEN_CAPABLE_ACK; + mpc->ver = opts->mptcp_ver; + ptr += MPTCP_SUB_LEN_CAPABLE_ACK_ALIGN >> 2; + } + + mpc->sub = MPTCP_SUB_CAPABLE; + mpc->a = opts->dss_csum; + mpc->b = 0; + mpc->rsv = 0; + mpc->h = 1; + } + if (unlikely(OPTION_MP_JOIN & opts->mptcp_options)) { + struct mp_join *mpj = (struct mp_join *)ptr; + + mpj->kind = TCPOPT_MPTCP; + mpj->sub = MPTCP_SUB_JOIN; + mpj->rsv = 0; + + if (OPTION_TYPE_SYN & opts->mptcp_options) { + mpj->len = MPTCP_SUB_LEN_JOIN_SYN; + mpj->u.syn.token = opts->mp_join_syns.token; + mpj->u.syn.nonce = opts->mp_join_syns.sender_nonce; + mpj->b = opts->mp_join_syns.low_prio; + mpj->addr_id = opts->addr_id; + ptr += MPTCP_SUB_LEN_JOIN_SYN_ALIGN >> 2; + } else if (OPTION_TYPE_SYNACK & opts->mptcp_options) { + mpj->len = MPTCP_SUB_LEN_JOIN_SYNACK; + mpj->u.synack.mac = + opts->mp_join_syns.sender_truncated_mac; + mpj->u.synack.nonce = opts->mp_join_syns.sender_nonce; + mpj->b = opts->mp_join_syns.low_prio; + mpj->addr_id = opts->addr_id; + ptr += MPTCP_SUB_LEN_JOIN_SYNACK_ALIGN >> 2; + } else if (OPTION_TYPE_ACK & opts->mptcp_options) { + mpj->len = MPTCP_SUB_LEN_JOIN_ACK; + mpj->addr_id = 0; /* addr_id is rsv (RFC 6824, p. 21) */ + memcpy(mpj->u.ack.mac, &tp->mptcp->sender_mac[0], 20); + ptr += MPTCP_SUB_LEN_JOIN_ACK_ALIGN >> 2; + } + } + if (unlikely(OPTION_ADD_ADDR & opts->mptcp_options)) { + struct mp_add_addr *mpadd = (struct mp_add_addr *)ptr; + struct mptcp_cb *mpcb = tp->mpcb; + + mpadd->kind = TCPOPT_MPTCP; + if (opts->add_addr_v4) { + mpadd->sub = MPTCP_SUB_ADD_ADDR; + mpadd->ipver = 4; + mpadd->addr_id = opts->add_addr4.addr_id; + mpadd->u.v4.addr = opts->add_addr4.addr; + if (mpcb->mptcp_ver < MPTCP_VERSION_1) { + mpadd->len = MPTCP_SUB_LEN_ADD_ADDR4; + ptr += MPTCP_SUB_LEN_ADD_ADDR4_ALIGN >> 2; + } else { + memcpy((char *)mpadd->u.v4.mac - 2, + (char *)&opts->add_addr4.trunc_mac, 8); + mpadd->len = MPTCP_SUB_LEN_ADD_ADDR4_VER1; + ptr += MPTCP_SUB_LEN_ADD_ADDR4_ALIGN_VER1 >> 2; + } + } else if (opts->add_addr_v6) { + mpadd->sub = MPTCP_SUB_ADD_ADDR; + mpadd->ipver = 6; + mpadd->addr_id = opts->add_addr6.addr_id; + memcpy(&mpadd->u.v6.addr, &opts->add_addr6.addr, + sizeof(mpadd->u.v6.addr)); + if (mpcb->mptcp_ver < MPTCP_VERSION_1) { + mpadd->len = MPTCP_SUB_LEN_ADD_ADDR6; + ptr += MPTCP_SUB_LEN_ADD_ADDR6_ALIGN >> 2; + } else { + memcpy((char *)mpadd->u.v6.mac - 2, + (char *)&opts->add_addr6.trunc_mac, 8); + mpadd->len = MPTCP_SUB_LEN_ADD_ADDR6_VER1; + ptr += MPTCP_SUB_LEN_ADD_ADDR6_ALIGN_VER1 >> 2; + } + } + + MPTCP_INC_STATS_BH(sock_net((struct sock *)tp), MPTCP_MIB_ADDADDRTX); + } + if (unlikely(OPTION_REMOVE_ADDR & opts->mptcp_options)) { + struct mp_remove_addr *mprem = (struct mp_remove_addr *)ptr; + u8 *addrs_id; + int id, len, len_align; + + len = mptcp_sub_len_remove_addr(opts->remove_addrs); + len_align = mptcp_sub_len_remove_addr_align(opts->remove_addrs); + + mprem->kind = TCPOPT_MPTCP; + mprem->len = len; + mprem->sub = MPTCP_SUB_REMOVE_ADDR; + mprem->rsv = 0; + addrs_id = &mprem->addrs_id; + + mptcp_for_each_bit_set(opts->remove_addrs, id) + *(addrs_id++) = id; + + /* Fill the rest with NOP's */ + if (len_align > len) { + int i; + for (i = 0; i < len_align - len; i++) + *(addrs_id++) = TCPOPT_NOP; + } + + ptr += len_align >> 2; + + MPTCP_INC_STATS_BH(sock_net((struct sock *)tp), MPTCP_MIB_REMADDRTX); + } + if (unlikely(OPTION_MP_FAIL & opts->mptcp_options)) { + struct mp_fail *mpfail = (struct mp_fail *)ptr; + + mpfail->kind = TCPOPT_MPTCP; + mpfail->len = MPTCP_SUB_LEN_FAIL; + mpfail->sub = MPTCP_SUB_FAIL; + mpfail->rsv1 = 0; + mpfail->rsv2 = 0; + mpfail->data_seq = htonll(tp->mpcb->csum_cutoff_seq); + + ptr += MPTCP_SUB_LEN_FAIL_ALIGN >> 2; + } + if (unlikely(OPTION_MP_FCLOSE & opts->mptcp_options)) { + struct mp_fclose *mpfclose = (struct mp_fclose *)ptr; + + mpfclose->kind = TCPOPT_MPTCP; + mpfclose->len = MPTCP_SUB_LEN_FCLOSE; + mpfclose->sub = MPTCP_SUB_FCLOSE; + mpfclose->rsv1 = 0; + mpfclose->rsv2 = 0; + mpfclose->key = opts->mp_capable.receiver_key; + + ptr += MPTCP_SUB_LEN_FCLOSE_ALIGN >> 2; + } + + if (OPTION_DATA_ACK & opts->mptcp_options) { + if (!mptcp_is_data_seq(skb)) + ptr += mptcp_write_dss_data_ack(tp, skb, ptr); + else + ptr += mptcp_write_dss_data_seq(tp, skb, ptr); + } + if (unlikely(OPTION_MP_PRIO & opts->mptcp_options)) { + struct mp_prio *mpprio = (struct mp_prio *)ptr; + + mpprio->kind = TCPOPT_MPTCP; + mpprio->len = MPTCP_SUB_LEN_PRIO; + mpprio->sub = MPTCP_SUB_PRIO; + mpprio->rsv = 0; + mpprio->b = tp->mptcp->low_prio; + mpprio->addr_id = TCPOPT_NOP; + + ptr += MPTCP_SUB_LEN_PRIO_ALIGN >> 2; + } +} + +/* Sends the datafin */ +void mptcp_send_fin(struct sock *meta_sk) +{ + struct tcp_sock *meta_tp = tcp_sk(meta_sk); + struct sk_buff *skb = tcp_write_queue_tail(meta_sk); + int mss_now; + + if ((1 << meta_sk->sk_state) & (TCPF_CLOSE_WAIT | TCPF_LAST_ACK)) + meta_tp->mpcb->passive_close = 1; + + /* Optimization, tack on the FIN if we have a queue of + * unsent frames. But be careful about outgoing SACKS + * and IP options. + */ + mss_now = mptcp_current_mss(meta_sk); + + if (tcp_send_head(meta_sk) != NULL) { + TCP_SKB_CB(skb)->mptcp_flags |= MPTCPHDR_FIN; + TCP_SKB_CB(skb)->end_seq++; + meta_tp->write_seq++; + } else { + /* Socket is locked, keep trying until memory is available. */ + for (;;) { + skb = alloc_skb_fclone(MAX_TCP_HEADER, + meta_sk->sk_allocation); + if (skb) + break; + yield(); + } + /* Reserve space for headers and prepare control bits. */ + skb_reserve(skb, MAX_TCP_HEADER); + + tcp_init_nondata_skb(skb, meta_tp->write_seq, TCPHDR_ACK); + TCP_SKB_CB(skb)->end_seq++; + TCP_SKB_CB(skb)->mptcp_flags |= MPTCPHDR_FIN; + tcp_queue_skb(meta_sk, skb); + } + __tcp_push_pending_frames(meta_sk, mss_now, TCP_NAGLE_OFF); +} + +void mptcp_send_active_reset(struct sock *meta_sk, gfp_t priority) +{ + struct tcp_sock *meta_tp = tcp_sk(meta_sk); + struct mptcp_cb *mpcb = meta_tp->mpcb; + struct sock *sk; + + if (!mpcb->cnt_subflows) + return; + + WARN_ON(meta_tp->send_mp_fclose); + + /* First - select a socket */ + sk = mptcp_select_ack_sock(meta_sk); + + /* May happen if no subflow is in an appropriate state, OR + * we are in infinite mode or about to go there - just send a reset */ + if (!sk || mpcb->infinite_mapping_snd || mpcb->send_infinite_mapping || + mpcb->infinite_mapping_rcv) { + + /* tcp_done must be handled with bh disabled */ + if (!in_serving_softirq()) + local_bh_disable(); + + mptcp_sub_force_close_all(mpcb, NULL); + + if (!in_serving_softirq()) + local_bh_enable(); + return; + } + + + tcp_sk(sk)->send_mp_fclose = 1; + /** Reset all other subflows */ + + /* tcp_done must be handled with bh disabled */ + if (!in_serving_softirq()) + local_bh_disable(); + + mptcp_sub_force_close_all(mpcb, sk); + + if (!in_serving_softirq()) + local_bh_enable(); + + tcp_send_ack(sk); + inet_csk_reset_keepalive_timer(sk, inet_csk(sk)->icsk_rto); + + meta_tp->send_mp_fclose = 1; + + MPTCP_INC_STATS(sock_net(meta_sk), MPTCP_MIB_FASTCLOSETX); +} + +static void mptcp_ack_retransmit_timer(struct sock *sk) +{ + struct sk_buff *skb; + struct tcp_sock *tp = tcp_sk(sk); + struct inet_connection_sock *icsk = inet_csk(sk); + + if (inet_csk(sk)->icsk_af_ops->rebuild_header(sk)) + goto out; /* Routing failure or similar */ + + if (!tp->retrans_stamp) + tp->retrans_stamp = tcp_time_stamp ? : 1; + + if (tcp_write_timeout(sk)) { + MPTCP_INC_STATS_BH(sock_net(sk), MPTCP_MIB_JOINACKRTO); + tp->mptcp->pre_established = 0; + sk_stop_timer(sk, &tp->mptcp->mptcp_ack_timer); + tp->ops->send_active_reset(sk, GFP_ATOMIC); + goto out; + } + + skb = alloc_skb(MAX_TCP_HEADER, GFP_ATOMIC); + if (skb == NULL) { + sk_reset_timer(sk, &tp->mptcp->mptcp_ack_timer, + jiffies + icsk->icsk_rto); + return; + } + + /* Reserve space for headers and prepare control bits */ + skb_reserve(skb, MAX_TCP_HEADER); + tcp_init_nondata_skb(skb, tp->snd_una, TCPHDR_ACK); + + MPTCP_INC_STATS_BH(sock_net(sk), MPTCP_MIB_JOINACKRXMIT); + + if (tcp_transmit_skb(sk, skb, 0, GFP_ATOMIC) > 0) { + /* Retransmission failed because of local congestion, + * do not backoff. + */ + if (!icsk->icsk_retransmits) + icsk->icsk_retransmits = 1; + sk_reset_timer(sk, &tp->mptcp->mptcp_ack_timer, + jiffies + icsk->icsk_rto); + return; + } + + + icsk->icsk_retransmits++; + icsk->icsk_rto = min(icsk->icsk_rto << 1, TCP_RTO_MAX); + sk_reset_timer(sk, &tp->mptcp->mptcp_ack_timer, + jiffies + icsk->icsk_rto); + if (retransmits_timed_out(sk, sysctl_tcp_retries1 + 1, 0, 0)) + __sk_dst_reset(sk); + +out:; +} + +void mptcp_ack_handler(unsigned long data) +{ + struct sock *sk = (struct sock *)data; + struct sock *meta_sk = mptcp_meta_sk(sk); + + bh_lock_sock(meta_sk); + if (sock_owned_by_user(meta_sk)) { + /* Try again later */ + sk_reset_timer(sk, &tcp_sk(sk)->mptcp->mptcp_ack_timer, + jiffies + (HZ / 20)); + goto out_unlock; + } + + if (sk->sk_state == TCP_CLOSE) + goto out_unlock; + if (!tcp_sk(sk)->mptcp->pre_established) + goto out_unlock; + + mptcp_ack_retransmit_timer(sk); + + sk_mem_reclaim(sk); + +out_unlock: + bh_unlock_sock(meta_sk); + sock_put(sk); +} + +/* Similar to tcp_retransmit_skb + * + * The diff is that we handle the retransmission-stats (retrans_stamp) at the + * meta-level. + */ +int mptcp_retransmit_skb(struct sock *meta_sk, struct sk_buff *skb) +{ + struct tcp_sock *meta_tp = tcp_sk(meta_sk); + struct sock *subsk; + unsigned int limit, mss_now; + int err = -1; + + /* Do not sent more than we queued. 1/4 is reserved for possible + * copying overhead: fragmentation, tunneling, mangling etc. + * + * This is a meta-retransmission thus we check on the meta-socket. + */ + if (atomic_read(&meta_sk->sk_wmem_alloc) > + min(meta_sk->sk_wmem_queued + (meta_sk->sk_wmem_queued >> 2), meta_sk->sk_sndbuf)) { + return -EAGAIN; + } + + /* We need to make sure that the retransmitted segment can be sent on a + * subflow right now. If it is too big, it needs to be fragmented. + */ + subsk = meta_tp->mpcb->sched_ops->get_subflow(meta_sk, skb, false); + if (!subsk) { + /* We want to increase icsk_retransmits, thus return 0, so that + * mptcp_meta_retransmit_timer enters the desired branch. + */ + err = 0; + goto failed; + } + mss_now = tcp_current_mss(subsk); + + /* If the segment was cloned (e.g. a meta retransmission), the header + * must be expanded/copied so that there is no corruption of TSO + * information. + */ + if (skb_unclone(skb, GFP_ATOMIC)) { + err = -ENOMEM; + goto failed; + } + + /* Must have been set by mptcp_write_xmit before */ + BUG_ON(!tcp_skb_pcount(skb)); + + limit = mss_now; + /* skb->len > mss_now is the equivalent of tso_segs > 1 in + * tcp_write_xmit. Otherwise split-point would return 0. + */ + if (skb->len > mss_now && !tcp_urg_mode(meta_tp)) + limit = tcp_mss_split_point(meta_sk, skb, mss_now, + UINT_MAX / mss_now, + TCP_NAGLE_OFF); + + if (skb->len > limit && + unlikely(mptcp_fragment(meta_sk, skb, limit, + GFP_ATOMIC, 0))) + goto failed; + + if (!mptcp_skb_entail(subsk, skb, -1)) + goto failed; + skb_mstamp_get(&skb->skb_mstamp); + + /* Update global TCP statistics. */ + MPTCP_INC_STATS_BH(sock_net(meta_sk), MPTCP_MIB_RETRANSSEGS); + + /* Diff to tcp_retransmit_skb */ + + /* Save stamp of the first retransmit. */ + if (!meta_tp->retrans_stamp) + meta_tp->retrans_stamp = tcp_skb_timestamp(skb); + + __tcp_push_pending_frames(subsk, mss_now, TCP_NAGLE_PUSH); + + return 0; + +failed: + NET_INC_STATS_BH(sock_net(meta_sk), LINUX_MIB_TCPRETRANSFAIL); + return err; +} + +/* Similar to tcp_retransmit_timer + * + * The diff is that we have to handle retransmissions of the FAST_CLOSE-message + * and that we don't have an srtt estimation at the meta-level. + */ +void mptcp_meta_retransmit_timer(struct sock *meta_sk) +{ + struct tcp_sock *meta_tp = tcp_sk(meta_sk); + struct mptcp_cb *mpcb = meta_tp->mpcb; + struct inet_connection_sock *meta_icsk = inet_csk(meta_sk); + int err; + + /* In fallback, retransmission is handled at the subflow-level */ + if (!meta_tp->packets_out || mpcb->infinite_mapping_snd) + return; + + WARN_ON(tcp_write_queue_empty(meta_sk)); + + if (!meta_tp->snd_wnd && !sock_flag(meta_sk, SOCK_DEAD) && + !((1 << meta_sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV))) { + /* Receiver dastardly shrinks window. Our retransmits + * become zero probes, but we should not timeout this + * connection. If the socket is an orphan, time it out, + * we cannot allow such beasts to hang infinitely. + */ + struct inet_sock *meta_inet = inet_sk(meta_sk); + if (meta_sk->sk_family == AF_INET) { + net_dbg_ratelimited("MPTCP: Peer %pI4:%u/%u unexpectedly shrunk window %u:%u (repaired)\n", + &meta_inet->inet_daddr, + ntohs(meta_inet->inet_dport), + meta_inet->inet_num, meta_tp->snd_una, + meta_tp->snd_nxt); + } +#if IS_ENABLED(CONFIG_IPV6) + else if (meta_sk->sk_family == AF_INET6) { + net_dbg_ratelimited("MPTCP: Peer %pI6:%u/%u unexpectedly shrunk window %u:%u (repaired)\n", + &meta_sk->sk_v6_daddr, + ntohs(meta_inet->inet_dport), + meta_inet->inet_num, meta_tp->snd_una, + meta_tp->snd_nxt); + } +#endif + if (tcp_time_stamp - meta_tp->rcv_tstamp > TCP_RTO_MAX) { + tcp_write_err(meta_sk); + return; + } + + mptcp_retransmit_skb(meta_sk, tcp_write_queue_head(meta_sk)); + goto out_reset_timer; + } + + if (tcp_write_timeout(meta_sk)) + return; + + if (meta_icsk->icsk_retransmits == 0) + NET_INC_STATS_BH(sock_net(meta_sk), LINUX_MIB_TCPTIMEOUTS); + + meta_icsk->icsk_ca_state = TCP_CA_Loss; + + err = mptcp_retransmit_skb(meta_sk, tcp_write_queue_head(meta_sk)); + if (err > 0) { + /* Retransmission failed because of local congestion, + * do not backoff. + */ + if (!meta_icsk->icsk_retransmits) + meta_icsk->icsk_retransmits = 1; + inet_csk_reset_xmit_timer(meta_sk, ICSK_TIME_RETRANS, + min(meta_icsk->icsk_rto, TCP_RESOURCE_PROBE_INTERVAL), + TCP_RTO_MAX); + return; + } + + /* Increase the timeout each time we retransmit. Note that + * we do not increase the rtt estimate. rto is initialized + * from rtt, but increases here. Jacobson (SIGCOMM 88) suggests + * that doubling rto each time is the least we can get away with. + * In KA9Q, Karn uses this for the first few times, and then + * goes to quadratic. netBSD doubles, but only goes up to *64, + * and clamps at 1 to 64 sec afterwards. Note that 120 sec is + * defined in the protocol as the maximum possible RTT. I guess + * we'll have to use something other than TCP to talk to the + * University of Mars. + * + * PAWS allows us longer timeouts and large windows, so once + * implemented ftp to mars will work nicely. We will have to fix + * the 120 second clamps though! + */ + meta_icsk->icsk_backoff++; + meta_icsk->icsk_retransmits++; + +out_reset_timer: + /* If stream is thin, use linear timeouts. Since 'icsk_backoff' is + * used to reset timer, set to 0. Recalculate 'icsk_rto' as this + * might be increased if the stream oscillates between thin and thick, + * thus the old value might already be too high compared to the value + * set by 'tcp_set_rto' in tcp_input.c which resets the rto without + * backoff. Limit to TCP_THIN_LINEAR_RETRIES before initiating + * exponential backoff behaviour to avoid continue hammering + * linear-timeout retransmissions into a black hole + */ + if (meta_sk->sk_state == TCP_ESTABLISHED && + (meta_tp->thin_lto || sysctl_tcp_thin_linear_timeouts) && + tcp_stream_is_thin(meta_tp) && + meta_icsk->icsk_retransmits <= TCP_THIN_LINEAR_RETRIES) { + meta_icsk->icsk_backoff = 0; + /* We cannot do the same as in tcp_write_timer because the + * srtt is not set here. + */ + mptcp_set_rto(meta_sk); + } else { + /* Use normal (exponential) backoff */ + meta_icsk->icsk_rto = min(meta_icsk->icsk_rto << 1, TCP_RTO_MAX); + } + inet_csk_reset_xmit_timer(meta_sk, ICSK_TIME_RETRANS, meta_icsk->icsk_rto, TCP_RTO_MAX); + + return; +} + +void mptcp_sub_retransmit_timer(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + + tcp_retransmit_timer(sk); + + if (!tp->fastopen_rsk) { + mptcp_reinject_data(sk, 1); + mptcp_set_rto(sk); + } +} + +/* Modify values to an mptcp-level for the initial window of new subflows */ +void mptcp_select_initial_window(int __space, __u32 mss, __u32 *rcv_wnd, + __u32 *window_clamp, int wscale_ok, + __u8 *rcv_wscale, __u32 init_rcv_wnd, + const struct sock *sk) +{ + struct mptcp_cb *mpcb = tcp_sk(sk)->mpcb; + + *window_clamp = mpcb->orig_window_clamp; + __space = tcp_win_from_space(mpcb->orig_sk_rcvbuf); + + tcp_select_initial_window(__space, mss, rcv_wnd, window_clamp, + wscale_ok, rcv_wscale, init_rcv_wnd, sk); +} + +static inline u64 mptcp_calc_rate(const struct sock *meta_sk, unsigned int mss, + unsigned int (*mss_cb)(struct sock *sk)) +{ + struct sock *sk; + u64 rate = 0; + + mptcp_for_each_sk(tcp_sk(meta_sk)->mpcb, sk) { + struct tcp_sock *tp = tcp_sk(sk); + int this_mss; + u64 this_rate; + + if (!mptcp_sk_can_send(sk)) + continue; + + /* Do not consider subflows without a RTT estimation yet + * otherwise this_rate >>> rate. + */ + if (unlikely(!tp->srtt_us)) + continue; + + this_mss = mss_cb(sk); + + /* If this_mss is smaller than mss, it means that a segment will + * be splitted in two (or more) when pushed on this subflow. If + * you consider that mss = 1428 and this_mss = 1420 then two + * segments will be generated: a 1420-byte and 8-byte segment. + * The latter will introduce a large overhead as for a single + * data segment 2 slots will be used in the congestion window. + * Therefore reducing by ~2 the potential throughput of this + * subflow. Indeed, 1428 will be send while 2840 could have been + * sent if mss == 1420 reducing the throughput by 2840 / 1428. + * + * The following algorithm take into account this overhead + * when computing the potential throughput that MPTCP can + * achieve when generating mss-byte segments. + * + * The formulae is the following: + * \sum_{\forall sub} ratio * \frac{mss * cwnd_sub}{rtt_sub} + * Where ratio is computed as follows: + * \frac{mss}{\ceil{mss / mss_sub} * mss_sub} + * + * ratio gives the reduction factor of the theoretical + * throughput a subflow can achieve if MPTCP uses a specific + * MSS value. + */ + this_rate = div64_u64((u64)mss * mss * (USEC_PER_SEC << 3) * + max(tp->snd_cwnd, tp->packets_out), + (u64)tp->srtt_us * + DIV_ROUND_UP(mss, this_mss) * this_mss); + rate += this_rate; + } + + return rate; +} + +static unsigned int __mptcp_current_mss(const struct sock *meta_sk, + unsigned int (*mss_cb)(struct sock *sk)) +{ + unsigned int mss = 0; + u64 rate = 0; + struct sock *sk; + + mptcp_for_each_sk(tcp_sk(meta_sk)->mpcb, sk) { + int this_mss; + u64 this_rate; + + if (!mptcp_sk_can_send(sk)) + continue; + + this_mss = mss_cb(sk); + + /* Same mss values will produce the same throughput. */ + if (this_mss == mss) + continue; + + /* See whether using this mss value can theoretically improve + * the performances. + */ + this_rate = mptcp_calc_rate(meta_sk, this_mss, mss_cb); + if (this_rate >= rate) { + mss = this_mss; + rate = this_rate; + } + } + + return mss; +} + +unsigned int mptcp_current_mss(struct sock *meta_sk) +{ + unsigned int mss = __mptcp_current_mss(meta_sk, tcp_current_mss); + + /* If no subflow is available, we take a default-mss from the + * meta-socket. + */ + return !mss ? tcp_current_mss(meta_sk) : mss; +} + +static unsigned int mptcp_select_size_mss(struct sock *sk) +{ + return tcp_sk(sk)->mss_cache; +} + +int mptcp_select_size(const struct sock *meta_sk, bool sg) +{ + unsigned int mss = __mptcp_current_mss(meta_sk, mptcp_select_size_mss); + + if (sg) { + if (mptcp_sk_can_gso(meta_sk)) { + mss = SKB_WITH_OVERHEAD(2048 - MAX_TCP_HEADER); + } else { + int pgbreak = SKB_MAX_HEAD(MAX_TCP_HEADER); + + if (mss >= pgbreak && + mss <= pgbreak + (MAX_SKB_FRAGS - 1) * PAGE_SIZE) + mss = pgbreak; + } + } + + return !mss ? tcp_sk(meta_sk)->mss_cache : mss; +} + +int mptcp_check_snd_buf(const struct tcp_sock *tp) +{ + const struct sock *sk; + u32 rtt_max = tp->srtt_us; + u64 bw_est; + + if (!tp->srtt_us) + return tp->reordering + 1; + + mptcp_for_each_sk(tp->mpcb, sk) { + if (!mptcp_sk_can_send(sk)) + continue; + + if (rtt_max < tcp_sk(sk)->srtt_us) + rtt_max = tcp_sk(sk)->srtt_us; + } + + bw_est = div64_u64(((u64)tp->snd_cwnd * rtt_max) << 16, + (u64)tp->srtt_us); + + return max_t(unsigned int, (u32)(bw_est >> 16), + tp->reordering + 1); +} + +unsigned int mptcp_xmit_size_goal(const struct sock *meta_sk, u32 mss_now, + int large_allowed) +{ + struct sock *sk; + u32 xmit_size_goal = 0; + + if (large_allowed && mptcp_sk_can_gso(meta_sk)) { + mptcp_for_each_sk(tcp_sk(meta_sk)->mpcb, sk) { + int this_size_goal; + + if (!mptcp_sk_can_send(sk)) + continue; + + this_size_goal = tcp_xmit_size_goal(sk, mss_now, 1); + if (this_size_goal > xmit_size_goal) + xmit_size_goal = this_size_goal; + } + } + + return max(xmit_size_goal, mss_now); +} + +#endif diff -urN linux-4.1.52-orig/net/mptcp/mptcp_pm.c linux-4.1.52/net/mptcp/mptcp_pm.c --- linux-4.1.52-orig/net/mptcp/mptcp_pm.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/mptcp/mptcp_pm.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,171 @@ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +/* + * MPTCP implementation - MPTCP-subflow-management + * + * Initial Design & Implementation: + * Sébastien Barré + * + * Current Maintainer & Author: + * Christoph Paasch + * + * Additional authors: + * Jaakko Korkeaniemi + * Gregory Detal + * Fabien Duchêne + * Andreas Seelinger + * Lavkesh Lahngir + * Andreas Ripke + * Vlad Dogaru + * Octavian Purdila + * John Ronan + * Catalin Nicutar + * Brandon Heller + * + * + * 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 + +static DEFINE_SPINLOCK(mptcp_pm_list_lock); +static LIST_HEAD(mptcp_pm_list); + +static int mptcp_default_id(sa_family_t family, union inet_addr *addr, + struct net *net, bool *low_prio) +{ + return 0; +} + +struct mptcp_pm_ops mptcp_pm_default = { + .get_local_id = mptcp_default_id, /* We do not care */ + .name = "default", + .owner = THIS_MODULE, +}; + +static struct mptcp_pm_ops *mptcp_pm_find(const char *name) +{ + struct mptcp_pm_ops *e; + + list_for_each_entry_rcu(e, &mptcp_pm_list, list) { + if (strcmp(e->name, name) == 0) + return e; + } + + return NULL; +} + +int mptcp_register_path_manager(struct mptcp_pm_ops *pm) +{ + int ret = 0; + + if (!pm->get_local_id) + return -EINVAL; + + spin_lock(&mptcp_pm_list_lock); + if (mptcp_pm_find(pm->name)) { + pr_notice("%s already registered\n", pm->name); + ret = -EEXIST; + } else { + list_add_tail_rcu(&pm->list, &mptcp_pm_list); + pr_info("%s registered\n", pm->name); + } + spin_unlock(&mptcp_pm_list_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(mptcp_register_path_manager); + +void mptcp_unregister_path_manager(struct mptcp_pm_ops *pm) +{ + spin_lock(&mptcp_pm_list_lock); + list_del_rcu(&pm->list); + spin_unlock(&mptcp_pm_list_lock); +} +EXPORT_SYMBOL_GPL(mptcp_unregister_path_manager); + +void mptcp_get_default_path_manager(char *name) +{ + struct mptcp_pm_ops *pm; + + BUG_ON(list_empty(&mptcp_pm_list)); + + rcu_read_lock(); + pm = list_entry(mptcp_pm_list.next, struct mptcp_pm_ops, list); + strncpy(name, pm->name, MPTCP_PM_NAME_MAX); + rcu_read_unlock(); +} + +int mptcp_set_default_path_manager(const char *name) +{ + struct mptcp_pm_ops *pm; + int ret = -ENOENT; + + spin_lock(&mptcp_pm_list_lock); + pm = mptcp_pm_find(name); +#ifdef CONFIG_MODULES + if (!pm && capable(CAP_NET_ADMIN)) { + spin_unlock(&mptcp_pm_list_lock); + + request_module("mptcp_%s", name); + spin_lock(&mptcp_pm_list_lock); + pm = mptcp_pm_find(name); + } +#endif + + if (pm) { + list_move(&pm->list, &mptcp_pm_list); + ret = 0; + } else { + pr_info("%s is not available\n", name); + } + spin_unlock(&mptcp_pm_list_lock); + + return ret; +} + +void mptcp_init_path_manager(struct mptcp_cb *mpcb) +{ + struct mptcp_pm_ops *pm; + + rcu_read_lock(); + list_for_each_entry_rcu(pm, &mptcp_pm_list, list) { + if (try_module_get(pm->owner)) { + mpcb->pm_ops = pm; + break; + } + } + rcu_read_unlock(); +} + +/* Manage refcounts on socket close. */ +void mptcp_cleanup_path_manager(struct mptcp_cb *mpcb) +{ + module_put(mpcb->pm_ops->owner); +} + +/* Fallback to the default path-manager. */ +void mptcp_fallback_default(struct mptcp_cb *mpcb) +{ + struct mptcp_pm_ops *pm; + + mptcp_cleanup_path_manager(mpcb); + pm = mptcp_pm_find("default"); + + /* Cannot fail - it's the default module */ + try_module_get(pm->owner); + mpcb->pm_ops = pm; +} +EXPORT_SYMBOL_GPL(mptcp_fallback_default); + +/* Set default value from kernel configuration at bootup */ +static int __init mptcp_path_manager_default(void) +{ + return mptcp_set_default_path_manager(CONFIG_DEFAULT_MPTCP_PM); +} +late_initcall(mptcp_path_manager_default); +#endif diff -urN linux-4.1.52-orig/net/mptcp/mptcp_redundant.c linux-4.1.52/net/mptcp/mptcp_redundant.c --- linux-4.1.52-orig/net/mptcp/mptcp_redundant.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/mptcp/mptcp_redundant.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,267 @@ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +/* + * MPTCP Scheduler to reduce latency and jitter. + * + * This scheduler sends all packets redundantly on all available subflows. + * + * Initial Design & Implementation: + * Tobias Erbshaeusser + * Alexander Froemmgen + * + * Initial corrections & modifications: + * Christian Pinedo + * Igor Lopez + * + * 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 + +/* Struct to store the data of a single subflow */ +struct redsched_sock_data { + /* The skb or NULL */ + struct sk_buff *skb; + /* End sequence number of the skb. This number should be checked + * to be valid before the skb field is used + */ + u32 skb_end_seq; +}; + +/* Struct to store the data of the control block */ +struct redsched_cb_data { + /* The next subflow where a skb should be sent or NULL */ + struct tcp_sock *next_subflow; +}; + +/* Returns the socket data from a given subflow socket */ +static struct redsched_sock_data *redsched_get_sock_data(struct tcp_sock *tp) +{ + return (struct redsched_sock_data *)&tp->mptcp->mptcp_sched[0]; +} + +/* Returns the control block data from a given meta socket */ +static struct redsched_cb_data *redsched_get_cb_data(struct tcp_sock *tp) +{ + return (struct redsched_cb_data *)&tp->mpcb->mptcp_sched[0]; +} + +static bool redsched_get_active_valid_sks(struct sock *meta_sk) +{ + struct tcp_sock *meta_tp = tcp_sk(meta_sk); + struct mptcp_cb *mpcb = meta_tp->mpcb; + struct sock *sk; + int active_valid_sks = 0; + + mptcp_for_each_sk(mpcb, sk) { + if (subflow_is_active((struct tcp_sock *)sk) && + !mptcp_is_def_unavailable(sk)) + active_valid_sks++; + } + + return active_valid_sks; +} + +static bool redsched_use_subflow(struct sock *meta_sk, + int active_valid_sks, + struct tcp_sock *tp, + struct sk_buff *skb) +{ + if (!skb || !mptcp_is_available((struct sock *)tp, skb, false)) + return false; + + if (TCP_SKB_CB(skb)->path_mask != 0) + return subflow_is_active(tp); + + if (TCP_SKB_CB(skb)->path_mask == 0) { + if (active_valid_sks == -1) + active_valid_sks = redsched_get_active_valid_sks(meta_sk); + + if (subflow_is_backup(tp) && active_valid_sks > 0) + return false; + else + return true; + } + + return false; +} + +static struct sock *redundant_get_subflow(struct sock *meta_sk, + struct sk_buff *skb, + bool zero_wnd_test) +{ + struct tcp_sock *meta_tp = tcp_sk(meta_sk); + struct mptcp_cb *mpcb = meta_tp->mpcb; + struct redsched_cb_data *cb_data = redsched_get_cb_data(meta_tp); + struct tcp_sock *first_tp = cb_data->next_subflow; + struct sock *sk; + struct tcp_sock *tp; + + /* Answer data_fin on same subflow */ + if (meta_sk->sk_shutdown & RCV_SHUTDOWN && + skb && mptcp_is_data_fin(skb)) { + mptcp_for_each_sk(mpcb, sk) { + if (tcp_sk(sk)->mptcp->path_index == + mpcb->dfin_path_index && + mptcp_is_available(sk, skb, zero_wnd_test)) + return sk; + } + } + + if (!first_tp) + first_tp = mpcb->connection_list; + tp = first_tp; + + /* Search for any subflow to send it */ + do { + if (mptcp_is_available((struct sock *)tp, skb, + zero_wnd_test)) { + cb_data->next_subflow = tp->mptcp->next; + return (struct sock *)tp; + } + + tp = tp->mptcp->next; + if (!tp) + tp = mpcb->connection_list; + } while (tp != first_tp); + + /* No space */ + return NULL; +} + +/* Corrects the stored skb pointers if they are invalid */ +static void redsched_correct_skb_pointers(struct sock *meta_sk, + struct redsched_sock_data *sk_data) +{ + struct tcp_sock *meta_tp = tcp_sk(meta_sk); + + if (sk_data->skb && !after(sk_data->skb_end_seq, meta_tp->snd_una)) + sk_data->skb = NULL; +} + +/* Returns the next skb from the queue */ +static struct sk_buff *redundant_next_skb_from_queue(struct sk_buff_head *queue, + struct sk_buff *previous) +{ + if (skb_queue_empty(queue)) + return NULL; + + if (!previous) + return skb_peek(queue); + + if (skb_queue_is_last(queue, previous)) + return NULL; + + return skb_queue_next(queue, previous); +} + +static struct sk_buff *redundant_next_segment(struct sock *meta_sk, + int *reinject, + struct sock **subsk, + unsigned int *limit) +{ + struct tcp_sock *meta_tp = tcp_sk(meta_sk); + struct mptcp_cb *mpcb = meta_tp->mpcb; + struct redsched_cb_data *cb_data = redsched_get_cb_data(meta_tp); + struct tcp_sock *first_tp = cb_data->next_subflow; + struct tcp_sock *tp; + struct sk_buff *skb; + int active_valid_sks = -1; + + if (skb_queue_empty(&mpcb->reinject_queue) && + skb_queue_empty(&meta_sk->sk_write_queue)) + /* Nothing to send */ + return NULL; + + /* First try reinjections */ + skb = skb_peek(&mpcb->reinject_queue); + if (skb) { + *subsk = get_available_subflow(meta_sk, skb, false); + if (!*subsk) + return NULL; + *reinject = 1; + return skb; + } + + /* Then try indistinctly redundant and normal skbs */ + + if (!first_tp) + first_tp = mpcb->connection_list; + tp = first_tp; + + *reinject = 0; + active_valid_sks = redsched_get_active_valid_sks(meta_sk); + do { + struct redsched_sock_data *sk_data; + + /* Correct the skb pointers of the current subflow */ + sk_data = redsched_get_sock_data(tp); + redsched_correct_skb_pointers(meta_sk, sk_data); + + skb = redundant_next_skb_from_queue(&meta_sk->sk_write_queue, + sk_data->skb); + if (skb && redsched_use_subflow(meta_sk, active_valid_sks, tp, + skb)) { + sk_data->skb = skb; + sk_data->skb_end_seq = TCP_SKB_CB(skb)->end_seq; + cb_data->next_subflow = tp->mptcp->next; + *subsk = (struct sock *)tp; + return skb; + } + + tp = tp->mptcp->next; + if (!tp) + tp = mpcb->connection_list; + } while (tp != first_tp); + + /* Nothing to send */ + return NULL; +} + +static void redundant_release(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct redsched_cb_data *cb_data = redsched_get_cb_data(tp); + + /* Check if the next subflow would be the released one. If yes correct + * the pointer + */ + if (cb_data->next_subflow == tp) + cb_data->next_subflow = tp->mptcp->next; +} + +struct mptcp_sched_ops mptcp_sched_redundant = { + .get_subflow = redundant_get_subflow, + .next_segment = redundant_next_segment, + .release = redundant_release, + .name = "redundant", + .owner = THIS_MODULE, +}; + +static int __init redundant_register(void) +{ + BUILD_BUG_ON(sizeof(struct redsched_sock_data) > MPTCP_SCHED_SIZE); + BUILD_BUG_ON(sizeof(struct redsched_cb_data) > MPTCP_SCHED_DATA_SIZE); + + if (mptcp_register_scheduler(&mptcp_sched_redundant)) + return -1; + + return 0; +} + +static void redundant_unregister(void) +{ + mptcp_unregister_scheduler(&mptcp_sched_redundant); +} + +module_init(redundant_register); +module_exit(redundant_unregister); + +MODULE_AUTHOR("Tobias Erbshaeusser, Alexander Froemmgen"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("REDUNDANT MPTCP"); +MODULE_VERSION("0.90"); +#endif diff -urN linux-4.1.52-orig/net/mptcp/mptcp_rr.c linux-4.1.52/net/mptcp/mptcp_rr.c --- linux-4.1.52-orig/net/mptcp/mptcp_rr.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/mptcp/mptcp_rr.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,303 @@ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +/* MPTCP Scheduler module selector. Highly inspired by tcp_cong.c */ + +#include +#include + +static unsigned char num_segments __read_mostly = 1; +module_param(num_segments, byte, 0644); +MODULE_PARM_DESC(num_segments, "The number of consecutive segments that are part of a burst"); + +static bool cwnd_limited __read_mostly = 1; +module_param(cwnd_limited, bool, 0644); +MODULE_PARM_DESC(cwnd_limited, "if set to 1, the scheduler tries to fill the congestion-window on all subflows"); + +struct rrsched_priv { + unsigned char quota; +}; + +static struct rrsched_priv *rrsched_get_priv(const struct tcp_sock *tp) +{ + return (struct rrsched_priv *)&tp->mptcp->mptcp_sched[0]; +} + +/* If the sub-socket sk available to send the skb? */ +static bool mptcp_rr_is_available(const struct sock *sk, const struct sk_buff *skb, + bool zero_wnd_test, bool cwnd_test) +{ + const struct tcp_sock *tp = tcp_sk(sk); + unsigned int space, in_flight; + + /* Set of states for which we are allowed to send data */ + if (!mptcp_sk_can_send(sk)) + return false; + + /* We do not send data on this subflow unless it is + * fully established, i.e. the 4th ack has been received. + */ + if (tp->mptcp->pre_established) + return false; + + if (tp->pf) + return false; + + if (inet_csk(sk)->icsk_ca_state == TCP_CA_Loss) { + /* If SACK is disabled, and we got a loss, TCP does not exit + * the loss-state until something above high_seq has been acked. + * (see tcp_try_undo_recovery) + * + * high_seq is the snd_nxt at the moment of the RTO. As soon + * as we have an RTO, we won't push data on the subflow. + * Thus, snd_una can never go beyond high_seq. + */ + if (!tcp_is_reno(tp)) + return false; + else if (tp->snd_una != tp->high_seq) + return false; + } + + if (!tp->mptcp->fully_established) { + /* Make sure that we send in-order data */ + if (skb && tp->mptcp->second_packet && + tp->mptcp->last_end_data_seq != TCP_SKB_CB(skb)->seq) + return false; + } + + if (!cwnd_test) + goto zero_wnd_test; + + in_flight = tcp_packets_in_flight(tp); + /* Not even a single spot in the cwnd */ + if (in_flight >= tp->snd_cwnd) + return false; + + /* Now, check if what is queued in the subflow's send-queue + * already fills the cwnd. + */ + space = (tp->snd_cwnd - in_flight) * tp->mss_cache; + + if (tp->write_seq - tp->snd_nxt > space) + return false; + +zero_wnd_test: + if (zero_wnd_test && !before(tp->write_seq, tcp_wnd_end(tp))) + return false; + + return true; +} + +/* Are we not allowed to reinject this skb on tp? */ +static int mptcp_rr_dont_reinject_skb(const struct tcp_sock *tp, const struct sk_buff *skb) +{ + /* If the skb has already been enqueued in this sk, try to find + * another one. + */ + return skb && + /* Has the skb already been enqueued into this subsocket? */ + mptcp_pi_to_flag(tp->mptcp->path_index) & TCP_SKB_CB(skb)->path_mask; +} + +/* We just look for any subflow that is available */ +static struct sock *rr_get_available_subflow(struct sock *meta_sk, + struct sk_buff *skb, + bool zero_wnd_test) +{ + const struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; + struct sock *sk, *bestsk = NULL, *backupsk = NULL; + + /* Answer data_fin on same subflow!!! */ + if (meta_sk->sk_shutdown & RCV_SHUTDOWN && + skb && mptcp_is_data_fin(skb)) { + mptcp_for_each_sk(mpcb, sk) { + if (tcp_sk(sk)->mptcp->path_index == mpcb->dfin_path_index && + mptcp_rr_is_available(sk, skb, zero_wnd_test, true)) + return sk; + } + } + + /* First, find the best subflow */ + mptcp_for_each_sk(mpcb, sk) { + struct tcp_sock *tp = tcp_sk(sk); + + if (!mptcp_rr_is_available(sk, skb, zero_wnd_test, true)) + continue; + + if (mptcp_rr_dont_reinject_skb(tp, skb)) { + backupsk = sk; + continue; + } + + bestsk = sk; + } + + if (bestsk) { + sk = bestsk; + } else if (backupsk) { + /* It has been sent on all subflows once - let's give it a + * chance again by restarting its pathmask. + */ + if (skb) + TCP_SKB_CB(skb)->path_mask = 0; + sk = backupsk; + } + + return sk; +} + +/* Returns the next segment to be sent from the mptcp meta-queue. + * (chooses the reinject queue if any segment is waiting in it, otherwise, + * chooses the normal write queue). + * Sets *@reinject to 1 if the returned segment comes from the + * reinject queue. Sets it to 0 if it is the regular send-head of the meta-sk, + * and sets it to -1 if it is a meta-level retransmission to optimize the + * receive-buffer. + */ +static struct sk_buff *__mptcp_rr_next_segment(const struct sock *meta_sk, int *reinject) +{ + const struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; + struct sk_buff *skb = NULL; + + *reinject = 0; + + /* If we are in fallback-mode, just take from the meta-send-queue */ + if (mpcb->infinite_mapping_snd || mpcb->send_infinite_mapping) + return tcp_send_head(meta_sk); + + skb = skb_peek(&mpcb->reinject_queue); + + if (skb) + *reinject = 1; + else + skb = tcp_send_head(meta_sk); + return skb; +} + +static struct sk_buff *mptcp_rr_next_segment(struct sock *meta_sk, + int *reinject, + struct sock **subsk, + unsigned int *limit) +{ + const struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; + struct sock *sk_it, *choose_sk = NULL; + struct sk_buff *skb = __mptcp_rr_next_segment(meta_sk, reinject); + unsigned char split = num_segments; + unsigned char iter = 0, full_subs = 0; + + /* As we set it, we have to reset it as well. */ + *limit = 0; + + if (!skb) + return NULL; + + if (*reinject) { + *subsk = rr_get_available_subflow(meta_sk, skb, false); + if (!*subsk) + return NULL; + + return skb; + } + +retry: + + /* First, we look for a subflow who is currently being used */ + mptcp_for_each_sk(mpcb, sk_it) { + struct tcp_sock *tp_it = tcp_sk(sk_it); + struct rrsched_priv *rsp = rrsched_get_priv(tp_it); + + if (!mptcp_rr_is_available(sk_it, skb, false, cwnd_limited)) + continue; + + iter++; + + /* Is this subflow currently being used? */ + if (rsp->quota > 0 && rsp->quota < num_segments) { + split = num_segments - rsp->quota; + choose_sk = sk_it; + goto found; + } + + /* Or, it's totally unused */ + if (!rsp->quota) { + split = num_segments; + choose_sk = sk_it; + } + + /* Or, it must then be fully used */ + if (rsp->quota >= num_segments) + full_subs++; + } + + /* All considered subflows have a full quota, and we considered at + * least one. + */ + if (iter && iter == full_subs) { + /* So, we restart this round by setting quota to 0 and retry + * to find a subflow. + */ + mptcp_for_each_sk(mpcb, sk_it) { + struct tcp_sock *tp_it = tcp_sk(sk_it); + struct rrsched_priv *rsp = rrsched_get_priv(tp_it); + + if (!mptcp_rr_is_available(sk_it, skb, false, cwnd_limited)) + continue; + + rsp->quota = 0; + } + + goto retry; + } + +found: + if (choose_sk) { + unsigned int mss_now; + struct tcp_sock *choose_tp = tcp_sk(choose_sk); + struct rrsched_priv *rsp = rrsched_get_priv(choose_tp); + + if (!mptcp_rr_is_available(choose_sk, skb, false, true)) + return NULL; + + *subsk = choose_sk; + mss_now = tcp_current_mss(*subsk); + *limit = split * mss_now; + + if (skb->len > mss_now) + rsp->quota += DIV_ROUND_UP(skb->len, mss_now); + else + rsp->quota++; + + return skb; + } + + return NULL; +} + +static struct mptcp_sched_ops mptcp_sched_rr = { + .get_subflow = rr_get_available_subflow, + .next_segment = mptcp_rr_next_segment, + .name = "roundrobin", + .owner = THIS_MODULE, +}; + +static int __init rr_register(void) +{ + BUILD_BUG_ON(sizeof(struct rrsched_priv) > MPTCP_SCHED_SIZE); + + if (mptcp_register_scheduler(&mptcp_sched_rr)) + return -1; + + return 0; +} + +static void rr_unregister(void) +{ + mptcp_unregister_scheduler(&mptcp_sched_rr); +} + +module_init(rr_register); +module_exit(rr_unregister); + +MODULE_AUTHOR("Christoph Paasch"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ROUNDROBIN MPTCP"); +MODULE_VERSION("0.89"); +#endif diff -urN linux-4.1.52-orig/net/mptcp/mptcp_sched.c linux-4.1.52/net/mptcp/mptcp_sched.c --- linux-4.1.52-orig/net/mptcp/mptcp_sched.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/mptcp/mptcp_sched.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,577 @@ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +/* MPTCP Scheduler module selector. Highly inspired by tcp_cong.c */ + +#include +#include + +static DEFINE_SPINLOCK(mptcp_sched_list_lock); +static LIST_HEAD(mptcp_sched_list); + +struct defsched_priv { + u32 last_rbuf_opti; +}; + +static struct defsched_priv *defsched_get_priv(const struct tcp_sock *tp) +{ + return (struct defsched_priv *)&tp->mptcp->mptcp_sched[0]; +} + +bool mptcp_is_def_unavailable(struct sock *sk) +{ + const struct tcp_sock *tp = tcp_sk(sk); + + /* Set of states for which we are allowed to send data */ + if (!mptcp_sk_can_send(sk)) + return true; + + /* We do not send data on this subflow unless it is + * fully established, i.e. the 4th ack has been received. + */ + if (tp->mptcp->pre_established) + return true; + + if (tp->pf) + return true; + + return false; +} +EXPORT_SYMBOL_GPL(mptcp_is_def_unavailable); + +static bool mptcp_is_temp_unavailable(struct sock *sk, + const struct sk_buff *skb, + bool zero_wnd_test) +{ + const struct tcp_sock *tp = tcp_sk(sk); + unsigned int mss_now, space, in_flight; + + if (inet_csk(sk)->icsk_ca_state == TCP_CA_Loss) { + /* If SACK is disabled, and we got a loss, TCP does not exit + * the loss-state until something above high_seq has been + * acked. (see tcp_try_undo_recovery) + * + * high_seq is the snd_nxt at the moment of the RTO. As soon + * as we have an RTO, we won't push data on the subflow. + * Thus, snd_una can never go beyond high_seq. + */ + if (!tcp_is_reno(tp)) + return true; + else if (tp->snd_una != tp->high_seq) + return true; + } + + if (!tp->mptcp->fully_established) { + /* Make sure that we send in-order data */ + if (skb && tp->mptcp->second_packet && + tp->mptcp->last_end_data_seq != TCP_SKB_CB(skb)->seq) + return true; + } + + /* If TSQ is already throttling us, do not send on this subflow. When + * TSQ gets cleared the subflow becomes eligible again. + */ + if (test_bit(TSQ_THROTTLED, &tp->tsq_flags)) + return true; + + in_flight = tcp_packets_in_flight(tp); + /* Not even a single spot in the cwnd */ + if (in_flight >= tp->snd_cwnd) + return true; + + /* Now, check if what is queued in the subflow's send-queue + * already fills the cwnd. + */ + space = (tp->snd_cwnd - in_flight) * tp->mss_cache; + + if (tp->write_seq - tp->snd_nxt > space) + return true; + + if (zero_wnd_test && !before(tp->write_seq, tcp_wnd_end(tp))) + return true; + + mss_now = tcp_current_mss(sk); + + /* Don't send on this subflow if we bypass the allowed send-window at + * the per-subflow level. Similar to tcp_snd_wnd_test, but manually + * calculated end_seq (because here at this point end_seq is still at + * the meta-level). + */ + if (skb && !zero_wnd_test && + after(tp->write_seq + min(skb->len, mss_now), tcp_wnd_end(tp))) + return true; + + return false; +} + +/* Is the sub-socket sk available to send the skb? */ +bool mptcp_is_available(struct sock *sk, const struct sk_buff *skb, + bool zero_wnd_test) +{ + return !mptcp_is_def_unavailable(sk) && + !mptcp_is_temp_unavailable(sk, skb, zero_wnd_test); +} +EXPORT_SYMBOL_GPL(mptcp_is_available); + +/* Are we not allowed to reinject this skb on tp? */ +static int mptcp_dont_reinject_skb(const struct tcp_sock *tp, const struct sk_buff *skb) +{ + /* If the skb has already been enqueued in this sk, try to find + * another one. + */ + return skb && + /* Has the skb already been enqueued into this subsocket? */ + mptcp_pi_to_flag(tp->mptcp->path_index) & TCP_SKB_CB(skb)->path_mask; +} + +bool subflow_is_backup(const struct tcp_sock *tp) +{ + return tp->mptcp->rcv_low_prio || tp->mptcp->low_prio; +} +EXPORT_SYMBOL_GPL(subflow_is_backup); + +bool subflow_is_active(const struct tcp_sock *tp) +{ + return !tp->mptcp->rcv_low_prio && !tp->mptcp->low_prio; +} +EXPORT_SYMBOL_GPL(subflow_is_active); + +/* Generic function to iterate over used and unused subflows and to select the + * best one + */ +static struct sock +*get_subflow_from_selectors(struct mptcp_cb *mpcb, struct sk_buff *skb, + bool (*selector)(const struct tcp_sock *), + bool zero_wnd_test, bool *force) +{ + struct sock *bestsk = NULL; + u32 min_srtt = 0xffffffff; + bool found_unused = false; + bool found_unused_una = false; + struct sock *sk; + + mptcp_for_each_sk(mpcb, sk) { + struct tcp_sock *tp = tcp_sk(sk); + bool unused = false; + + /* First, we choose only the wanted sks */ + if (!(*selector)(tp)) + continue; + + if (!mptcp_dont_reinject_skb(tp, skb)) + unused = true; + else if (found_unused) + /* If a unused sk was found previously, we continue - + * no need to check used sks anymore. + */ + continue; + + if (mptcp_is_def_unavailable(sk)) + continue; + + if (mptcp_is_temp_unavailable(sk, skb, zero_wnd_test)) { + if (unused) + found_unused_una = true; + continue; + } + + if (unused) { + if (!found_unused) { + /* It's the first time we encounter an unused + * sk - thus we reset the bestsk (which might + * have been set to a used sk). + */ + min_srtt = 0xffffffff; + bestsk = NULL; + } + found_unused = true; + } + + if (tp->srtt_us < min_srtt) { + min_srtt = tp->srtt_us; + bestsk = sk; + } + } + + if (bestsk) { + /* The force variable is used to mark the returned sk as + * previously used or not-used. + */ + if (found_unused) + *force = true; + else + *force = false; + } else { + /* The force variable is used to mark if there are temporally + * unavailable not-used sks. + */ + if (found_unused_una) + *force = true; + else + *force = false; + } + + return bestsk; +} + +/* This is the scheduler. This function decides on which flow to send + * a given MSS. If all subflows are found to be busy, NULL is returned + * The flow is selected based on the shortest RTT. + * If all paths have full cong windows, we simply return NULL. + * + * Additionally, this function is aware of the backup-subflows. + */ +struct sock *get_available_subflow(struct sock *meta_sk, struct sk_buff *skb, + bool zero_wnd_test) +{ + struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; + struct sock *sk; + bool force; + + /* if there is only one subflow, bypass the scheduling function */ + if (mpcb->cnt_subflows == 1) { + sk = (struct sock *)mpcb->connection_list; + if (!mptcp_is_available(sk, skb, zero_wnd_test)) + sk = NULL; + return sk; + } + + /* Answer data_fin on same subflow!!! */ + if (meta_sk->sk_shutdown & RCV_SHUTDOWN && + skb && mptcp_is_data_fin(skb)) { + mptcp_for_each_sk(mpcb, sk) { + if (tcp_sk(sk)->mptcp->path_index == mpcb->dfin_path_index && + mptcp_is_available(sk, skb, zero_wnd_test)) + return sk; + } + } + + /* Find the best subflow */ + sk = get_subflow_from_selectors(mpcb, skb, &subflow_is_active, + zero_wnd_test, &force); + if (force) + /* one unused active sk or one NULL sk when there is at least + * one temporally unavailable unused active sk + */ + return sk; + + sk = get_subflow_from_selectors(mpcb, skb, &subflow_is_backup, + zero_wnd_test, &force); + if (!force && skb) + /* one used backup sk or one NULL sk where there is no one + * temporally unavailable unused backup sk + * + * the skb passed through all the available active and backups + * sks, so clean the path mask + */ + TCP_SKB_CB(skb)->path_mask = 0; + return sk; +} +EXPORT_SYMBOL_GPL(get_available_subflow); + +static struct sk_buff *mptcp_rcv_buf_optimization(struct sock *sk, int penal) +{ + struct sock *meta_sk; + const struct tcp_sock *tp = tcp_sk(sk); + struct tcp_sock *tp_it; + struct sk_buff *skb_head; + struct defsched_priv *dsp = defsched_get_priv(tp); + + if (tp->mpcb->cnt_subflows == 1) + return NULL; + + meta_sk = mptcp_meta_sk(sk); + skb_head = tcp_write_queue_head(meta_sk); + + if (!skb_head || skb_head == tcp_send_head(meta_sk)) + return NULL; + + /* If penalization is optional (coming from mptcp_next_segment() and + * We are not send-buffer-limited we do not penalize. The retransmission + * is just an optimization to fix the idle-time due to the delay before + * we wake up the application. + */ + if (!penal && sk_stream_memory_free(meta_sk)) + goto retrans; + + /* Only penalize again after an RTT has elapsed */ + if (tcp_time_stamp - dsp->last_rbuf_opti < usecs_to_jiffies(tp->srtt_us >> 3)) + goto retrans; + + /* Half the cwnd of the slow flow */ + mptcp_for_each_tp(tp->mpcb, tp_it) { + if (tp_it != tp && + TCP_SKB_CB(skb_head)->path_mask & mptcp_pi_to_flag(tp_it->mptcp->path_index)) { + if (tp->srtt_us < tp_it->srtt_us && inet_csk((struct sock *)tp_it)->icsk_ca_state == TCP_CA_Open) { + u32 prior_cwnd = tp_it->snd_cwnd; + + tp_it->snd_cwnd = max(tp_it->snd_cwnd >> 1U, 1U); + + /* If in slow start, do not reduce the ssthresh */ + if (prior_cwnd >= tp_it->snd_ssthresh) + tp_it->snd_ssthresh = max(tp_it->snd_ssthresh >> 1U, 2U); + + dsp->last_rbuf_opti = tcp_time_stamp; + } + break; + } + } + +retrans: + + /* Segment not yet injected into this path? Take it!!! */ + if (!(TCP_SKB_CB(skb_head)->path_mask & mptcp_pi_to_flag(tp->mptcp->path_index))) { + bool do_retrans = false; + mptcp_for_each_tp(tp->mpcb, tp_it) { + if (tp_it != tp && + TCP_SKB_CB(skb_head)->path_mask & mptcp_pi_to_flag(tp_it->mptcp->path_index)) { + if (tp_it->snd_cwnd <= 4) { + do_retrans = true; + break; + } + + if (4 * tp->srtt_us >= tp_it->srtt_us) { + do_retrans = false; + break; + } else { + do_retrans = true; + } + } + } + + if (do_retrans && mptcp_is_available(sk, skb_head, false)) + return skb_head; + } + return NULL; +} + +/* Returns the next segment to be sent from the mptcp meta-queue. + * (chooses the reinject queue if any segment is waiting in it, otherwise, + * chooses the normal write queue). + * Sets *@reinject to 1 if the returned segment comes from the + * reinject queue. Sets it to 0 if it is the regular send-head of the meta-sk, + * and sets it to -1 if it is a meta-level retransmission to optimize the + * receive-buffer. + */ +static struct sk_buff *__mptcp_next_segment(struct sock *meta_sk, int *reinject) +{ + const struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; + struct sk_buff *skb = NULL; + + *reinject = 0; + + /* If we are in fallback-mode, just take from the meta-send-queue */ + if (mpcb->infinite_mapping_snd || mpcb->send_infinite_mapping) + return tcp_send_head(meta_sk); + + skb = skb_peek(&mpcb->reinject_queue); + + if (skb) { + *reinject = 1; + } else { + skb = tcp_send_head(meta_sk); + + if (!skb && meta_sk->sk_socket && + test_bit(SOCK_NOSPACE, &meta_sk->sk_socket->flags) && + sk_stream_wspace(meta_sk) < sk_stream_min_wspace(meta_sk)) { + struct sock *subsk = get_available_subflow(meta_sk, NULL, + false); + if (!subsk) + return NULL; + + skb = mptcp_rcv_buf_optimization(subsk, 0); + if (skb) + *reinject = -1; + } + } + return skb; +} + +static struct sk_buff *mptcp_next_segment(struct sock *meta_sk, + int *reinject, + struct sock **subsk, + unsigned int *limit) +{ + struct sk_buff *skb = __mptcp_next_segment(meta_sk, reinject); + unsigned int mss_now; + struct tcp_sock *subtp; + u16 gso_max_segs; + u32 max_len, max_segs, window, needed; + + /* As we set it, we have to reset it as well. */ + *limit = 0; + + if (!skb) + return NULL; + + *subsk = get_available_subflow(meta_sk, skb, false); + if (!*subsk) + return NULL; + + subtp = tcp_sk(*subsk); + mss_now = tcp_current_mss(*subsk); + + if (!*reinject && unlikely(!tcp_snd_wnd_test(tcp_sk(meta_sk), skb, mss_now))) { + skb = mptcp_rcv_buf_optimization(*subsk, 1); + if (skb) + *reinject = -1; + else + return NULL; + } + + /* No splitting required, as we will only send one single segment */ + if (skb->len <= mss_now) + return skb; + + /* The following is similar to tcp_mss_split_point, but + * we do not care about nagle, because we will anyways + * use TCP_NAGLE_PUSH, which overrides this. + * + * So, we first limit according to the cwnd/gso-size and then according + * to the subflow's window. + */ + + gso_max_segs = (*subsk)->sk_gso_max_segs; + if (!gso_max_segs) /* No gso supported on the subflow's NIC */ + gso_max_segs = 1; + max_segs = min_t(unsigned int, tcp_cwnd_test(subtp, skb), gso_max_segs); + if (!max_segs) + return NULL; + + max_len = mss_now * max_segs; + window = tcp_wnd_end(subtp) - subtp->write_seq; + + needed = min(skb->len, window); + if (max_len <= skb->len) + /* Take max_win, which is actually the cwnd/gso-size */ + *limit = max_len; + else + /* Or, take the window */ + *limit = needed; + + return skb; +} + +static void defsched_init(struct sock *sk) +{ + struct defsched_priv *dsp = defsched_get_priv(tcp_sk(sk)); + + dsp->last_rbuf_opti = tcp_time_stamp; +} + +struct mptcp_sched_ops mptcp_sched_default = { + .get_subflow = get_available_subflow, + .next_segment = mptcp_next_segment, + .init = defsched_init, + .name = "default", + .owner = THIS_MODULE, +}; + +static struct mptcp_sched_ops *mptcp_sched_find(const char *name) +{ + struct mptcp_sched_ops *e; + + list_for_each_entry_rcu(e, &mptcp_sched_list, list) { + if (strcmp(e->name, name) == 0) + return e; + } + + return NULL; +} + +int mptcp_register_scheduler(struct mptcp_sched_ops *sched) +{ + int ret = 0; + + if (!sched->get_subflow || !sched->next_segment) + return -EINVAL; + + spin_lock(&mptcp_sched_list_lock); + if (mptcp_sched_find(sched->name)) { + pr_notice("%s already registered\n", sched->name); + ret = -EEXIST; + } else { + list_add_tail_rcu(&sched->list, &mptcp_sched_list); + pr_info("%s registered\n", sched->name); + } + spin_unlock(&mptcp_sched_list_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(mptcp_register_scheduler); + +void mptcp_unregister_scheduler(struct mptcp_sched_ops *sched) +{ + spin_lock(&mptcp_sched_list_lock); + list_del_rcu(&sched->list); + spin_unlock(&mptcp_sched_list_lock); +} +EXPORT_SYMBOL_GPL(mptcp_unregister_scheduler); + +void mptcp_get_default_scheduler(char *name) +{ + struct mptcp_sched_ops *sched; + + BUG_ON(list_empty(&mptcp_sched_list)); + + rcu_read_lock(); + sched = list_entry(mptcp_sched_list.next, struct mptcp_sched_ops, list); + strncpy(name, sched->name, MPTCP_SCHED_NAME_MAX); + rcu_read_unlock(); +} + +int mptcp_set_default_scheduler(const char *name) +{ + struct mptcp_sched_ops *sched; + int ret = -ENOENT; + + spin_lock(&mptcp_sched_list_lock); + sched = mptcp_sched_find(name); +#ifdef CONFIG_MODULES + if (!sched && capable(CAP_NET_ADMIN)) { + spin_unlock(&mptcp_sched_list_lock); + + request_module("mptcp_%s", name); + spin_lock(&mptcp_sched_list_lock); + sched = mptcp_sched_find(name); + } +#endif + + if (sched) { + list_move(&sched->list, &mptcp_sched_list); + ret = 0; + } else { + pr_info("%s is not available\n", name); + } + spin_unlock(&mptcp_sched_list_lock); + + return ret; +} + +void mptcp_init_scheduler(struct mptcp_cb *mpcb) +{ + struct mptcp_sched_ops *sched; + + rcu_read_lock(); + list_for_each_entry_rcu(sched, &mptcp_sched_list, list) { + if (try_module_get(sched->owner)) { + mpcb->sched_ops = sched; + break; + } + } + rcu_read_unlock(); +} + +/* Manage refcounts on socket close. */ +void mptcp_cleanup_scheduler(struct mptcp_cb *mpcb) +{ + module_put(mpcb->sched_ops->owner); +} + +/* Set default value from kernel configuration at bootup */ +static int __init mptcp_scheduler_default(void) +{ + BUILD_BUG_ON(sizeof(struct defsched_priv) > MPTCP_SCHED_SIZE); + + return mptcp_set_default_scheduler(CONFIG_DEFAULT_MPTCP_SCHED); +} +late_initcall(mptcp_scheduler_default); +#endif diff -urN linux-4.1.52-orig/net/mptcp/mptcp_wvegas.c linux-4.1.52/net/mptcp/mptcp_wvegas.c --- linux-4.1.52-orig/net/mptcp/mptcp_wvegas.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/mptcp/mptcp_wvegas.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,270 @@ +#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) +/* + * MPTCP implementation - WEIGHTED VEGAS + * + * Algorithm design: + * Yu Cao + * Mingwei Xu + * Xiaoming Fu + * + * Implementation: + * Yu Cao + * Enhuan Dong + * + * Ported to the official MPTCP-kernel: + * Christoph Paasch + * + * 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 + +static int initial_alpha = 2; +static int total_alpha = 10; +static int gamma = 1; + +module_param(initial_alpha, int, 0644); +MODULE_PARM_DESC(initial_alpha, "initial alpha for all subflows"); +module_param(total_alpha, int, 0644); +MODULE_PARM_DESC(total_alpha, "total alpha for all subflows"); +module_param(gamma, int, 0644); +MODULE_PARM_DESC(gamma, "limit on increase (scale by 2)"); + +#define MPTCP_WVEGAS_SCALE 16 + +/* wVegas variables */ +struct wvegas { + u32 beg_snd_nxt; /* right edge during last RTT */ + u8 doing_wvegas_now;/* if true, do wvegas for this RTT */ + + u16 cnt_rtt; /* # of RTTs measured within last RTT */ + u32 sampled_rtt; /* cumulative RTTs measured within last RTT (in usec) */ + u32 base_rtt; /* the min of all wVegas RTT measurements seen (in usec) */ + + u64 instant_rate; /* cwnd / srtt_us, unit: pkts/us * 2^16 */ + u64 weight; /* the ratio of subflow's rate to the total rate, * 2^16 */ + int alpha; /* alpha for each subflows */ + + u32 queue_delay; /* queue delay*/ +}; + + +static inline u64 mptcp_wvegas_scale(u32 val, int scale) +{ + return (u64) val << scale; +} + +static void wvegas_enable(const struct sock *sk) +{ + const struct tcp_sock *tp = tcp_sk(sk); + struct wvegas *wvegas = inet_csk_ca(sk); + + wvegas->doing_wvegas_now = 1; + + wvegas->beg_snd_nxt = tp->snd_nxt; + + wvegas->cnt_rtt = 0; + wvegas->sampled_rtt = 0; + + wvegas->instant_rate = 0; + wvegas->alpha = initial_alpha; + wvegas->weight = mptcp_wvegas_scale(1, MPTCP_WVEGAS_SCALE); + + wvegas->queue_delay = 0; +} + +static inline void wvegas_disable(const struct sock *sk) +{ + struct wvegas *wvegas = inet_csk_ca(sk); + + wvegas->doing_wvegas_now = 0; +} + +static void mptcp_wvegas_init(struct sock *sk) +{ + struct wvegas *wvegas = inet_csk_ca(sk); + + wvegas->base_rtt = 0x7fffffff; + wvegas_enable(sk); +} + +static inline u64 mptcp_wvegas_rate(u32 cwnd, u32 rtt_us) +{ + return div_u64(mptcp_wvegas_scale(cwnd, MPTCP_WVEGAS_SCALE), rtt_us); +} + +static void mptcp_wvegas_pkts_acked(struct sock *sk, u32 cnt, s32 rtt_us) +{ + struct wvegas *wvegas = inet_csk_ca(sk); + u32 vrtt; + + if (rtt_us < 0) + return; + + vrtt = rtt_us + 1; + + if (vrtt < wvegas->base_rtt) + wvegas->base_rtt = vrtt; + + wvegas->sampled_rtt += vrtt; + wvegas->cnt_rtt++; +} + +static void mptcp_wvegas_state(struct sock *sk, u8 ca_state) +{ + if (ca_state == TCP_CA_Open) + wvegas_enable(sk); + else + wvegas_disable(sk); +} + +static void mptcp_wvegas_cwnd_event(struct sock *sk, enum tcp_ca_event event) +{ + if (event == CA_EVENT_CWND_RESTART) { + mptcp_wvegas_init(sk); + } else if (event == CA_EVENT_LOSS) { + struct wvegas *wvegas = inet_csk_ca(sk); + wvegas->instant_rate = 0; + } +} + +static inline u32 mptcp_wvegas_ssthresh(const struct tcp_sock *tp) +{ + return min(tp->snd_ssthresh, tp->snd_cwnd - 1); +} + +static u64 mptcp_wvegas_weight(const struct mptcp_cb *mpcb, const struct sock *sk) +{ + u64 total_rate = 0; + struct sock *sub_sk; + const struct wvegas *wvegas = inet_csk_ca(sk); + + if (!mpcb) + return wvegas->weight; + + + mptcp_for_each_sk(mpcb, sub_sk) { + struct wvegas *sub_wvegas = inet_csk_ca(sub_sk); + + /* sampled_rtt is initialized by 0 */ + if (mptcp_sk_can_send(sub_sk) && (sub_wvegas->sampled_rtt > 0)) + total_rate += sub_wvegas->instant_rate; + } + + if (total_rate && wvegas->instant_rate) + return div64_u64(mptcp_wvegas_scale(wvegas->instant_rate, MPTCP_WVEGAS_SCALE), total_rate); + else + return wvegas->weight; +} + +static void mptcp_wvegas_cong_avoid(struct sock *sk, u32 ack, u32 acked) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct wvegas *wvegas = inet_csk_ca(sk); + + if (!wvegas->doing_wvegas_now) { + tcp_reno_cong_avoid(sk, ack, acked); + return; + } + + if (after(ack, wvegas->beg_snd_nxt)) { + wvegas->beg_snd_nxt = tp->snd_nxt; + + if (wvegas->cnt_rtt <= 2) { + tcp_reno_cong_avoid(sk, ack, acked); + } else { + u32 rtt, diff, q_delay; + u64 target_cwnd; + + rtt = wvegas->sampled_rtt / wvegas->cnt_rtt; + target_cwnd = div_u64(((u64)tp->snd_cwnd * wvegas->base_rtt), rtt); + + diff = div_u64((u64)tp->snd_cwnd * (rtt - wvegas->base_rtt), rtt); + + if (diff > gamma && tp->snd_cwnd <= tp->snd_ssthresh) { + tp->snd_cwnd = min(tp->snd_cwnd, (u32)target_cwnd+1); + tp->snd_ssthresh = mptcp_wvegas_ssthresh(tp); + + } else if (tp->snd_cwnd <= tp->snd_ssthresh) { + tcp_slow_start(tp, acked); + } else { + if (diff >= wvegas->alpha) { + wvegas->instant_rate = mptcp_wvegas_rate(tp->snd_cwnd, rtt); + wvegas->weight = mptcp_wvegas_weight(tp->mpcb, sk); + wvegas->alpha = max(2U, (u32)((wvegas->weight * total_alpha) >> MPTCP_WVEGAS_SCALE)); + } + if (diff > wvegas->alpha) { + tp->snd_cwnd--; + tp->snd_ssthresh = mptcp_wvegas_ssthresh(tp); + } else if (diff < wvegas->alpha) { + tp->snd_cwnd++; + } + + /* Try to drain link queue if needed*/ + q_delay = rtt - wvegas->base_rtt; + if ((wvegas->queue_delay == 0) || (wvegas->queue_delay > q_delay)) + wvegas->queue_delay = q_delay; + + if (q_delay >= 2 * wvegas->queue_delay) { + u32 backoff_factor = div_u64(mptcp_wvegas_scale(wvegas->base_rtt, MPTCP_WVEGAS_SCALE), 2 * rtt); + tp->snd_cwnd = ((u64)tp->snd_cwnd * backoff_factor) >> MPTCP_WVEGAS_SCALE; + wvegas->queue_delay = 0; + } + } + + if (tp->snd_cwnd < 2) + tp->snd_cwnd = 2; + else if (tp->snd_cwnd > tp->snd_cwnd_clamp) + tp->snd_cwnd = tp->snd_cwnd_clamp; + + tp->snd_ssthresh = tcp_current_ssthresh(sk); + } + + wvegas->cnt_rtt = 0; + wvegas->sampled_rtt = 0; + } + /* Use normal slow start */ + else if (tp->snd_cwnd <= tp->snd_ssthresh) + tcp_slow_start(tp, acked); +} + + +static struct tcp_congestion_ops mptcp_wvegas __read_mostly = { + .init = mptcp_wvegas_init, + .ssthresh = tcp_reno_ssthresh, + .cong_avoid = mptcp_wvegas_cong_avoid, + .pkts_acked = mptcp_wvegas_pkts_acked, + .set_state = mptcp_wvegas_state, + .cwnd_event = mptcp_wvegas_cwnd_event, + + .owner = THIS_MODULE, + .name = "wvegas", +}; + +static int __init mptcp_wvegas_register(void) +{ + BUILD_BUG_ON(sizeof(struct wvegas) > ICSK_CA_PRIV_SIZE); + tcp_register_congestion_control(&mptcp_wvegas); + return 0; +} + +static void __exit mptcp_wvegas_unregister(void) +{ + tcp_unregister_congestion_control(&mptcp_wvegas); +} + +module_init(mptcp_wvegas_register); +module_exit(mptcp_wvegas_unregister); + +MODULE_AUTHOR("Yu Cao, Enhuan Dong"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MPTCP wVegas"); +MODULE_VERSION("0.1"); +#endif diff -urN linux-4.1.52-orig/net/netfilter/core.c linux-4.1.52/net/netfilter/core.c --- linux-4.1.52-orig/net/netfilter/core.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/netfilter/core.c 2020-02-07 01:47:54.000000000 +0800 @@ -27,6 +27,16 @@ #include "nf_internals.h" +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) +#if defined(CONFIG_BCM_RUNNER_RG) || defined(CONFIG_BCM_RUNNER_RG_MODULE) +#include +struct bl_ops_t *bl_ops = NULL; +EXPORT_SYMBOL(bl_ops); +#endif /* CONFIG_BCM_RUNNER_RG || CONFIG_BCM_RUNNER_RG_MODULE */ +#endif /* CONFIG_BCM_RUNNER */ +#endif /* CONFIG_BCM_KF_RUNNER */ + static DEFINE_MUTEX(afinfo_mutex); const struct nf_afinfo __rcu *nf_afinfo[NFPROTO_NUMPROTO] __read_mostly; diff -urN linux-4.1.52-orig/net/netfilter/Kconfig linux-4.1.52/net/netfilter/Kconfig --- linux-4.1.52-orig/net/netfilter/Kconfig 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/netfilter/Kconfig 2020-02-07 01:47:54.000000000 +0800 @@ -76,7 +76,6 @@ config NF_CONNTRACK_ZONES bool 'Connection tracking zones' depends on NETFILTER_ADVANCED - depends on NETFILTER_XT_TARGET_CT help This option enables support for connection tracking zones. Normally, each connection needs to have a unique system wide @@ -128,10 +127,11 @@ If unsure, say `N'. config NF_CONNTRACK_LABELS - bool + bool "Connection tracking labels" help This option enables support for assigning user-defined flag bits - to connection tracking entries. It selected by the connlabel match. + to connection tracking entries. It can be used with xtables connlabel + match and the nftables ct expression. config NF_CT_PROTO_DCCP tristate 'DCCP protocol connection tracking support' @@ -167,6 +167,15 @@ To compile it as a module, choose M here. If unsure, say N. +config NF_CT_PROTO_ESP + tristate 'ESP protocol connection tracking support' + depends on NETFILTER_ADVANCED && BCM_KF_NETFILTER + help + With this option enabled, the layer 3 ESP protocol + tracking will be able to do tracking on ESP connections + + To compile it as a module, choose M here. If unsure, say N. + config NF_CONNTRACK_AMANDA tristate "Amanda backup protocol support" depends on NETFILTER_ADVANCED @@ -312,6 +321,16 @@ To compile it as a module, choose M here. If unsure, say N. +config NF_CONNTRACK_IPSEC + tristate "IPSEC protocol support" + depends on BCM_KF_NETFILTER + default m if NETFILTER_ADVANCED=n + help + IPSec is used for for securing IP communications by authenticating and + encrypting each IP packet of a communication session + + To compile it as a module, choose M here. If unsure, say N. + config NF_CONNTRACK_TFTP tristate "TFTP protocol support" depends on NETFILTER_ADVANCED @@ -323,6 +342,45 @@ To compile it as a module, choose M here. If unsure, say N. +#BRCM begin +config NF_DYNDSCP + tristate "Dynamic DSCP Mangling support " + depends on NF_CONNTRACK && BCM_KF_NETFILTER + default n + help + This option enables support for dynamic DSCP, i.e tos will be derived from + tos value of WAN packets of each connection. + +config NF_CONNTRACK_RTSP + tristate "RTSP protocol support" + depends on NF_CONNTRACK && BCM_KF_NETFILTER + help + RTSP (Real Time Streaming Protocol) support. + + To compile it as a module, choose M here. If unsure, say N. + +config NETFILTER_XT_MATCH_LAYER7 + tristate '"layer7" match support' + depends on NF_CONNTRACK && BCM_KF_NETFILTER + help + Say Y if you want to be able to classify connections (and their + packets) based on regular expression matching of their application + layer data. This is one way to classify applications such as + peer-to-peer filesharing systems that do not always use the same + port. + + To compile it as a module, choose M here. If unsure, say N. + +config NETFILTER_XT_TARGET_DC + tristate '"DC" target support' + depends on NETFILTER_XTABLES && BCM_KF_NETFILTER + help + Say Y if you want to be able to do data connections (and their + packets) + + To compile it as a module, choose M here. If unsure, say N. +#BRCM end + config NF_CT_NETLINK tristate 'Connection tracking netlink interface' select NETFILTER_NETLINK @@ -386,6 +444,7 @@ depends on NF_NAT && NF_CT_PROTO_SCTP select LIBCRC32C + config NF_NAT_AMANDA tristate depends on NF_CONNTRACK && NF_NAT @@ -935,6 +994,16 @@ This option adds a "TCPOPTSTRIP" target, which allows you to strip TCP options from TCP packets. +config NETFILTER_XT_TARGET_SKIPLOG + tristate '"SKIPLOG" target support' + depends on NETFILTER_XTABLES && (IPV6 || IPV6=n) && BCM_KF_NETFILTER + ---help--- + configuration like: + + iptables -A FORWARD -p tcp -j SKIPLOG + + To compile it as a module, choose M here. If unsure, say N. + # alphabetically ordered list of matches comment "Xtables matches" @@ -1438,6 +1507,23 @@ Details and examples are in the kernel module source. +config NETFILTER_XT_MATCH_BLOG + tristate '"blog" blog match support' + depends on NETFILTER_XTABLES && BCM_KF_NETFILTER && BLOG + help + This option adds a `blog' match, which allows you to match based + on blog fields. + + To compile it as a module, choose M here. If unsure, say N. + +config NETFILTER_XT_MATCH_FLOWLABEL + tristate '"flowlabel" match support' + depends on NETFILTER_XTABLES && BCM_KF_NETFILTER + help + This option adds a `flowlabel' match, which allows you to match based + on IPv6 flowlabel fields. + + To compile it as a module, choose M here. If unsure, say N. endif # NETFILTER_XTABLES endmenu diff -urN linux-4.1.52-orig/net/netfilter/Makefile linux-4.1.52/net/netfilter/Makefile --- linux-4.1.52-orig/net/netfilter/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/netfilter/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -1,3 +1,7 @@ +ifdef BCM_KF # defined(CONFIG_BCM_KF_NETFILTER) +EXTRA_CFLAGS += -I$(INC_BRCMDRIVER_PUB_PATH)/$(BRCM_BOARD) -I$(INC_BRCMSHARED_PUB_PATH)/$(BRCM_BOARD) +endif #BCM_KF # defined(CONFIG_BCM_KF_NETFILTER) + netfilter-objs := core.o nf_log.o nf_queue.o nf_sockopt.o nf_conntrack-y := nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_expect.o nf_conntrack_helper.o nf_conntrack_proto.o nf_conntrack_l3proto_generic.o nf_conntrack_proto_generic.o nf_conntrack_proto_tcp.o nf_conntrack_proto_udp.o nf_conntrack_extend.o nf_conntrack_acct.o nf_conntrack_seqadj.o @@ -23,6 +27,9 @@ obj-$(CONFIG_NF_CT_PROTO_GRE) += nf_conntrack_proto_gre.o obj-$(CONFIG_NF_CT_PROTO_SCTP) += nf_conntrack_proto_sctp.o obj-$(CONFIG_NF_CT_PROTO_UDPLITE) += nf_conntrack_proto_udplite.o +ifdef BCM_KF # defined(CONFIG_BCM_KF_NETFILTER) +obj-$(CONFIG_NF_CT_PROTO_ESP) += nf_conntrack_proto_esp.o +endif #BCM_KF # netlink interface for nf_conntrack obj-$(CONFIG_NF_CT_NETLINK) += nf_conntrack_netlink.o @@ -43,6 +50,11 @@ obj-$(CONFIG_NF_CONNTRACK_SANE) += nf_conntrack_sane.o obj-$(CONFIG_NF_CONNTRACK_SIP) += nf_conntrack_sip.o obj-$(CONFIG_NF_CONNTRACK_TFTP) += nf_conntrack_tftp.o +ifdef BCM_KF # defined(CONFIG_BCM_KF_NETFILTER) +obj-$(CONFIG_NF_CONNTRACK_RTSP) += nf_conntrack_rtsp.o +obj-$(CONFIG_NF_DYNDSCP) += nf_dyndscp.o +obj-$(CONFIG_NF_CONNTRACK_IPSEC) += nf_conntrack_ipsec.o +endif #BCM_KF # defined(CONFIG_BCM_KF_NETFILTER) nf_nat-y := nf_nat_core.o nf_nat_proto_unknown.o nf_nat_proto_common.o \ nf_nat_proto_udp.o nf_nat_proto_tcp.o nf_nat_helper.o @@ -123,6 +135,10 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_TEE) += xt_TEE.o obj-$(CONFIG_NETFILTER_XT_TARGET_TRACE) += xt_TRACE.o obj-$(CONFIG_NETFILTER_XT_TARGET_IDLETIMER) += xt_IDLETIMER.o +ifdef BCM_KF # defined(CONFIG_BCM_KF_NETFILTER) +obj-$(CONFIG_NETFILTER_XT_TARGET_SKIPLOG) += xt_SKIPLOG.o +obj-$(CONFIG_NETFILTER_XT_TARGET_DC) += xt_DC.o +endif #BCM_KF # defined(CONFIG_BCM_KF_NETFILTER) # matches obj-$(CONFIG_NETFILTER_XT_MATCH_ADDRTYPE) += xt_addrtype.o @@ -169,6 +185,12 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_TCPMSS) += xt_tcpmss.o obj-$(CONFIG_NETFILTER_XT_MATCH_TIME) += xt_time.o obj-$(CONFIG_NETFILTER_XT_MATCH_U32) += xt_u32.o +ifdef BCM_KF # defined(CONFIG_BCM_KF_NETFILTER) +obj-$(CONFIG_NETFILTER_XT_MATCH_LAYER7) += xt_layer7.o +obj-$(CONFIG_NETFILTER_XT_MATCH_BLOG) += xt_blog.o +obj-$(CONFIG_NETFILTER_XT_MATCH_MAC) += xt_mac_extend.o +obj-$(CONFIG_NETFILTER_XT_MATCH_FLOWLABEL) += xt_flowlabel.o +endif #BCM_KF # defined(CONFIG_BCM_KF_NETFILTER) # ipset obj-$(CONFIG_IP_SET) += ipset/ diff -urN linux-4.1.52-orig/net/netfilter/nf_conntrack_acct.c linux-4.1.52/net/netfilter/nf_conntrack_acct.c --- linux-4.1.52-orig/net/netfilter/nf_conntrack_acct.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/netfilter/nf_conntrack_acct.c 2020-02-07 01:47:54.000000000 +0800 @@ -18,7 +18,11 @@ #include #include +#if defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_MODULE) +static bool nf_ct_acct __read_mostly = 1; +#else static bool nf_ct_acct __read_mostly; +#endif module_param_named(acct, nf_ct_acct, bool, 0644); MODULE_PARM_DESC(acct, "Enable connection tracking flow accounting."); @@ -47,14 +51,138 @@ return 0; counter = acct->counter; - seq_printf(s, "packets=%llu bytes=%llu ", +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + { + unsigned long long pkts; + unsigned long long bytes; + BlogFcStats_t fast_stats; + + pkts = (unsigned long long)atomic64_read(&counter[dir].packets); + bytes = (unsigned long long)atomic64_read(&counter[dir].bytes); + + fast_stats.rx_packets = 0; + fast_stats.rx_bytes = 0; + + if(blog_ct_get_stats(ct, ct->blog_key[dir], dir, &fast_stats) == 0) + counter[dir].ts = fast_stats.pollTS_ms; + + seq_printf(s, "packets=%llu bytes=%llu ", + pkts+counter[dir].cum_fast_pkts+fast_stats.rx_packets, + bytes+counter[dir].cum_fast_bytes+fast_stats.rx_bytes); + } +#else + seq_printf(s, "packets=%llu bytes=%llu ", (unsigned long long)atomic64_read(&counter[dir].packets), (unsigned long long)atomic64_read(&counter[dir].bytes)); +#endif return 0; }; EXPORT_SYMBOL_GPL(seq_print_acct); +#if defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_MODULE) +static void conntrack_dpi_refresh_conn_stats(struct nf_conn *ct, int dir, + struct dpi_ct_stats *stats) +{ + struct nf_conn_acct *acct; + struct nf_conn_counter *counter; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + BlogFcStats_t fast_stats; +#endif + + if (!ct->dpi.appinst) + return; + + acct = nf_conn_acct_find(ct); + if (!acct) + return; + counter = acct->counter; + + /* refresh current connection stats */ + stats->pkts = atomic64_read(&counter[dir].packets); + stats->bytes = atomic64_read(&counter[dir].bytes); + +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + fast_stats.rx_packets = 0; + fast_stats.rx_bytes = 0; + + if(blog_ct_get_stats(ct, ct->blog_key[dir], dir, &fast_stats) == 0) + counter[dir].ts = fast_stats.pollTS_ms; + + /* update stats from accelerators */ + stats->pkts +=counter[dir].cum_fast_pkts + fast_stats.rx_packets; + stats->bytes += counter[dir].cum_fast_bytes + fast_stats.rx_bytes; +#endif +} + +void conntrack_dpi_evict_conn(struct nf_conn *ct, int dir) +{ + struct dpi_ct_stats *appinst_stats; + struct dpi_ct_stats *dev_stats; + struct nf_conn_acct *acct; + struct nf_conn_counter *counter; + uint64_t packets, bytes; + + acct = nf_conn_acct_find(ct); + if (!acct) + return; + counter = acct->counter; + + packets = atomic64_read(&counter[dir].packets); + bytes = atomic64_read(&counter[dir].bytes); + + pr_debug("app<%p> dev<%p> appinst<%p> dir<%s> counter<%llu, %llu>", + ct->dpi.app, ct->dpi.dev, ct->dpi.appinst, + dir == IP_CT_DIR_ORIGINAL ? "orig" : "reply", + packets, bytes); + +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + pr_debug(" blog_counter<%lu, %llu>\n", + counter[dir].cum_fast_pkts, + counter[dir].cum_fast_bytes); + packets += counter[dir].cum_fast_pkts; + bytes += counter[dir].cum_fast_bytes; +#endif + + /* update cumulative appinst stats */ + if (ct->dpi.appinst) { + appinst_stats = dpi_appinst_stats(ct, dir); + + appinst_stats->pkts += packets; + appinst_stats->bytes += bytes; + + pr_debug(" appinst_stats<%llu, %llu>", + appinst_stats->pkts, + appinst_stats->bytes); + } + + /* update cumulative device stats */ + if (ct->dpi.dev) { + dev_stats = dpi_dev_stats(ct, dir); + + dev_stats->pkts += packets; + dev_stats->bytes += bytes; + + pr_debug(" dev_stats<%llu, %llu>", + dev_stats->pkts, + dev_stats->bytes); + } + + pr_debug("\n"); +} + +int conntrack_dpi_seq_print_stats(struct seq_file *s, struct nf_conn *ct, + int dir) +{ + struct dpi_ct_stats stats = {0}; + + /* get updated stats for the connection */ + conntrack_dpi_refresh_conn_stats(ct, dir, &stats); + + return seq_printf(s, " %llu %llu", stats.pkts, stats.bytes); +} +#endif /* defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_MODULE) */ + static struct nf_ct_ext_type acct_extend __read_mostly = { .len = sizeof(struct nf_conn_acct), .align = __alignof__(struct nf_conn_acct), @@ -110,6 +238,33 @@ } #endif +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +/* + *--------------------------------------------------------------------------- + * Function Name: flwStPushFunc + *--------------------------------------------------------------------------- + */ +void flwStPushFunc( void *ct, uint32_t dir, const BlogFcStats_t *flwSt_p ) +{ + struct nf_conn_acct *acct; + struct nf_conn_counter *counter; + + if (flwSt_p == NULL) + return ; + + if (ct != NULL) + { + acct = nf_conn_acct_find((struct nf_conn *)ct); + if (acct) + { + counter = acct->counter; + counter[dir].cum_fast_pkts += flwSt_p->rx_packets; + counter[dir].cum_fast_bytes += flwSt_p->rx_bytes; + counter[dir].ts = flwSt_p->pollTS_ms; + } + } +} +#endif int nf_conntrack_acct_pernet_init(struct net *net) { net->ct.sysctl_acct = nf_ct_acct; @@ -126,6 +281,10 @@ int ret = nf_ct_extend_register(&acct_extend); if (ret < 0) pr_err("nf_conntrack_acct: Unable to register extension\n"); +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_ct_put_stats_fn = flwStPushFunc; +#endif + return ret; } diff -urN linux-4.1.52-orig/net/netfilter/nf_conntrack_core.c linux-4.1.52/net/netfilter/nf_conntrack_core.c --- linux-4.1.52-orig/net/netfilter/nf_conntrack_core.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/netfilter/nf_conntrack_core.c 2020-02-07 01:47:54.000000000 +0800 @@ -33,6 +33,16 @@ #include #include #include +#if defined(CONFIG_BCM_KF_BLOG) +#include +#endif +#if defined(CONFIG_BCM_KF_NETFILTER) +#include +#endif + +#if defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_MODULE) +#include +#endif #include #include @@ -53,6 +63,12 @@ #include #include +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) +#include +#endif /* CONFIG_BCM_RUNNER */ +#endif /* CONFIG_BCM_KF_RUNNER */ + #define NF_CONNTRACK_VERSION "0.5.0" int (*nfnetlink_parse_nat_setup_hook)(struct nf_conn *ct, @@ -126,6 +142,51 @@ unsigned int nf_conntrack_hash_rnd __read_mostly; EXPORT_SYMBOL_GPL(nf_conntrack_hash_rnd); +#if defined(CONFIG_BCM_KF_NETFILTER) +#define NF_CT_SAFE_LISTS_MAX 6 +/* regardless drop of connection when conntrack table is full */ +struct safe_list { + spinlock_t lock; + struct list_head low; /* low priority linux only */ + struct list_head low_sw_accel; /* low priority sw acceleartor */ + struct list_head low_hw_accel; /* low priority hw accelerator */ + struct list_head hi; /* high priority linux only */ + struct list_head hi_sw_accel; /* high priority sw acceleartor */ + struct list_head hi_hw_accel; /* high priority hw accelerator */ + struct list_head *drop_list_order[NF_CT_SAFE_LISTS_MAX]; /*order in which lists are checked to drop a connection */ +}; + +struct safe_list ct_safe_lists = { + .lock = __SPIN_LOCK_UNLOCKED(ct_safe_lists.lock), + .low = LIST_HEAD_INIT(ct_safe_lists.low), + .low_sw_accel = LIST_HEAD_INIT(ct_safe_lists.low_sw_accel), + .low_hw_accel = LIST_HEAD_INIT(ct_safe_lists.low_hw_accel), + .hi = LIST_HEAD_INIT(ct_safe_lists.hi), + .hi_sw_accel = LIST_HEAD_INIT(ct_safe_lists.hi_sw_accel), + .hi_hw_accel = LIST_HEAD_INIT(ct_safe_lists.hi_hw_accel), +#if CONFIG_BCM_NETFILTER_REGARDLESS_DROP_ORDER == 1 + /* when trying to find a drop candidate search safe_list's in the order of + * non-accelerated-->sw_accelerated-->hw_accelerated + * this is the default policy + */ + .drop_list_order = {&ct_safe_lists.low, &ct_safe_lists.hi, + &ct_safe_lists.low_sw_accel,&ct_safe_lists.hi_sw_accel, + &ct_safe_lists.low_hw_accel, &ct_safe_lists.hi_hw_accel} +#elif CONFIG_BCM_NETFILTER_REGARDLESS_DROP_ORDER == 2 + .drop_list_order = {&ct_safe_lists.low, &ct_safe_lists.low_sw_accel, + &ct_safe_lists.hi, &ct_safe_lists.hi_sw_accel, + &ct_safe_lists.low_hw_accel, &ct_safe_lists.hi_hw_accel} +#elif CONFIG_BCM_NETFILTER_REGARDLESS_DROP_ORDER == 3 + .drop_list_order = {&ct_safe_lists.low, &ct_safe_lists.low_sw_accel, + &ct_safe_lists.low_hw_accel, &ct_safe_lists.hi, + &ct_safe_lists.hi_sw_accel, &ct_safe_lists.hi_hw_accel} +#else +#error "Netfilter Regardless Drop Order is not set" +#endif +}; + +#endif + static u32 hash_conntrack_raw(const struct nf_conntrack_tuple *tuple, u16 zone) { unsigned int n; @@ -287,6 +348,14 @@ spin_unlock(&pcpu->lock); } +#if defined(CONFIG_BCM_KF_NETFILTER) +static void death_by_timeout(unsigned long ul_conntrack); +#endif +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BCM_KF_NETFILTER) && defined(CONFIG_BLOG) +static void blog_death_by_timeout(unsigned long ul_conntrack); +#endif + + static void destroy_conntrack(struct nf_conntrack *nfct) { @@ -294,9 +363,34 @@ struct net *net = nf_ct_net(ct); struct nf_conntrack_l4proto *l4proto; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_lock(); + pr_debug("%s(%p) blog keys[0x%08x,0x%08x]\n", __func__, + ct, ct->blog_key[IP_CT_DIR_ORIGINAL], + ct->blog_key[IP_CT_DIR_REPLY]); + + + /* Conntrack going away, notify blog client */ + if ((ct->blog_key[IP_CT_DIR_ORIGINAL] != BLOG_KEY_FC_INVALID) || + (ct->blog_key[IP_CT_DIR_REPLY] != BLOG_KEY_FC_INVALID)) { + /* + * Blog client may perform the following blog requests: + * - FLOWTRACK_KEY_SET BLOG_PARAM1_DIR_ORIG 0 + * - FLOWTRACK_KEY_SET BLOG_PARAM1_DIR_REPLY 0 + * - FLOWTRACK_EXCLUDE + */ + blog_notify(DESTROY_FLOWTRACK, (void*)ct, + (uint32_t)ct->blog_key[IP_CT_DIR_ORIGINAL], + (uint32_t)ct->blog_key[IP_CT_DIR_REPLY]); + } + clear_bit(IPS_BLOG_BIT, &ct->status); /* Disable further blogging */ + blog_unlock(); +#else pr_debug("destroy_conntrack(%p)\n", ct); +#endif + NF_CT_ASSERT(atomic_read(&nfct->use) == 0); - NF_CT_ASSERT(!timer_pending(&ct->timeout)); + NF_CT_ASSERT(!timer_pending(&ct->timeout)); rcu_read_lock(); l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct)); @@ -314,13 +408,43 @@ nf_ct_remove_expectations(ct); nf_ct_del_from_dying_or_unconfirmed_list(ct); +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) + BL_OPS(net_netfilter_nf_conntrack_core_destroy_conntrack(ct)); +#endif /* CONFIG_BCM_RUNNER */ +#endif /* CONFIG_BCM_KF_RUNNER */ + +#if defined(CONFIG_BCM_KF_XT_MATCH_LAYER7) && \ + (defined(CONFIG_NETFILTER_XT_MATCH_LAYER7) || defined(CONFIG_NETFILTER_XT_MATCH_LAYER7_MODULE)) + if (ct->layer7.app_proto) + kfree(ct->layer7.app_proto); + if (ct->layer7.app_data) + kfree(ct->layer7.app_data); +#endif + NF_CT_STAT_INC(net, delete); local_bh_enable(); if (ct->master) +#if defined(CONFIG_BCM_KF_NETFILTER) + { + list_del(&ct->derived_list); +#endif nf_ct_put(ct->master); +#if defined(CONFIG_BCM_KF_NETFILTER) + } +#endif +#if defined(CONFIG_BCM_KF_NETFILTER) + if (test_bit(IPS_IQOS_BIT,&ct->status)) { + clear_bit(IPS_IQOS_BIT, &ct->status); + iqos_rem_L4port(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum, + ntohs(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.all), IQOS_ENT_DYN); + iqos_rem_L4port(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.protonum, + ntohs(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.all), IQOS_ENT_DYN); + } +#endif pr_debug("destroy_conntrack: returning ct=%p to slab\n", ct); nf_conntrack_free(ct); } @@ -343,6 +467,12 @@ &ct->tuplehash[IP_CT_DIR_REPLY].tuple); } while (nf_conntrack_double_lock(net, hash, reply_hash, sequence)); +#if defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_MODULE) + conntrack_dpi_evict_conn(ct, IP_CT_DIR_ORIGINAL); + if ((test_bit(IPS_SEEN_REPLY_BIT, &ct->status))) + conntrack_dpi_evict_conn(ct, IP_CT_DIR_REPLY); + dpi_ct_evicting(ct); +#endif clean_from_lists(ct); nf_conntrack_double_unlock(hash, reply_hash); @@ -382,9 +512,99 @@ static void death_by_timeout(unsigned long ul_conntrack) { +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) + BL_OPS_CR(net_netfilter_nf_conntrack_core_death_by_timeout(ct)); +#endif /* CONFIG_BCM_RUNNER */ +#endif /* CONFIG_BCM_KF_RUNNER */ + nf_ct_delete((struct nf_conn *)ul_conntrack, 0, 0); } +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BCM_KF_NETFILTER) && defined(CONFIG_BLOG) + +static void blog_death_by_timeout(unsigned long ul_conntrack) +{ + struct nf_conn *ct = (void *)ul_conntrack; + BlogCtTime_t ct_time; + uint32_t ct_blog_key = 0; + + memset(&ct_time, 0, sizeof(ct_time)); + blog_lock(); + if (ct->blog_key[BLOG_PARAM1_DIR_ORIG] != BLOG_KEY_FC_INVALID || + ct->blog_key[BLOG_PARAM1_DIR_REPLY] != BLOG_KEY_FC_INVALID) { + blog_query(QUERY_FLOWTRACK, (void*)ct, + ct->blog_key[BLOG_PARAM1_DIR_ORIG], + ct->blog_key[BLOG_PARAM1_DIR_REPLY], (unsigned long) &ct_time); + + ct_blog_key = 1; + } + blog_unlock(); + + /* Normally we should delete the connection when we are here, but + * if this connection is accelerated, we may need to rearm the + * timer based on how long the connection has been idle, + * in accelerator. + */ + if (ct_time.flags.valid && ct_blog_key) + { + /* if extra_jiffies is a very big value, kernel treats it + * as timer expired and will fire again immediately, + * so add this additional check + */ + if ((signed long) ct->extra_jiffies > 0) { + + signed long newtime; + /* make sure we have atleast HZ jiffies to rearm the timer + * as sometimes extra_jiffies can be less than idle_jiffies + */ + newtime = ct->extra_jiffies - (ct_time.idle * HZ); + if(newtime > HZ){ + mod_timer(&ct->timeout, jiffies + newtime); + return; + } + } else { + printk(KERN_ERR "%s: bad timeout value extra_jiffies=%lu, idle_jiffies=%lu\n", + __func__, ct->extra_jiffies, (unsigned long)ct_time.idle *HZ); + } + } + + death_by_timeout((unsigned long) ct); +} + +void __nf_ct_time_update(struct nf_conn *ct, BlogCtTime_t *ct_time_p) +{ + + if (!timer_pending(&ct->timeout)) + return; + + if (ct->blog_key[BLOG_PARAM1_DIR_ORIG] != BLOG_KEY_FC_INVALID || + ct->blog_key[BLOG_PARAM1_DIR_REPLY] != BLOG_KEY_FC_INVALID) { + + signed long newtime; + + /* if extra_jiffies is a very big value, kernel treats it + * as timer expired and will fire again immediately, + * so add this additional check + */ + if ((signed long) ct->extra_jiffies > 0) { + + /* to avoid triggering the timer immediately, + * add altleast 1 HZ when modifying the timer + */ + newtime = ct->extra_jiffies - (ct_time_p->idle * HZ); + if(newtime < HZ) + newtime = HZ; + } else { + printk(KERN_ERR "%s:bad timeout value extra_jiffies=%lu, idle_jiffies=%lu\n", + __func__, ct->extra_jiffies, (unsigned long)ct_time_p->idle * HZ); + newtime = HZ; + } + mod_timer_pending(&ct->timeout, jiffies + newtime); + } +} +#endif + static inline bool nf_ct_key_equal(struct nf_conntrack_tuple_hash *h, const struct nf_conntrack_tuple *tuple, @@ -640,6 +860,13 @@ weird delay cases. */ ct->timeout.expires += jiffies; add_timer(&ct->timeout); + +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) + BL_OPS(net_netfilter_nf_conntrack_core_nf_conntrack_confirm(ct, skb)); +#endif /* CONFIG_BCM_RUNNER */ +#endif /* CONFIG_BCM_KF_RUNNER */ + atomic_inc(&ct->ct_general.use); ct->status |= IPS_CONFIRMED; @@ -719,6 +946,71 @@ } EXPORT_SYMBOL_GPL(nf_conntrack_tuple_taken); +#if defined(CONFIG_BCM_KF_NETFILTER) + +/*caller must have safe_list lock acquired */ +static inline struct nf_conn* __ct_find_drop_candidate( struct list_head *list ) +{ + struct list_head *tmp; + struct nf_conn *ct, *ct_candidate=NULL; + + if (!list_empty(list)) { + list_for_each(tmp, list) { + ct = container_of(tmp, struct nf_conn, safe_list); + + if (likely(ct != NULL && nf_ct_is_confirmed(ct) && !nf_ct_is_dying(ct) && + atomic_inc_not_zero(&ct->ct_general.use))) { + + ct_candidate = ct; + /* move to the tail of the list while it's being deleted*/ + list_move_tail(&ct_candidate->safe_list, list); + break; + } + } + } + /* refcount of ct_candidate is incremented in this fucntion, + * it's callers responsibility to decrement it + */ + return ct_candidate; +} + +/* Choose a LRU connection based on the configured drop order policy */ +static int regardless_drop(struct net *net, struct sk_buff *skb) +{ + struct nf_conn *ct_candidate = NULL; + int i,dropped = 0; + + spin_lock_bh(&ct_safe_lists.lock); + + for( i=0; i < NF_CT_SAFE_LISTS_MAX; i++ ) { + ct_candidate = __ct_find_drop_candidate(ct_safe_lists.drop_list_order[i]); + if(ct_candidate) + break; + } + + spin_unlock_bh(&ct_safe_lists.lock); + + if (unlikely(ct_candidate == NULL)) + return dropped; + + if (del_timer(&ct_candidate->timeout)) { + death_by_timeout((unsigned long)ct_candidate); + if (test_bit(IPS_DYING_BIT, &ct_candidate->status)) { + dropped = 1; + NF_CT_STAT_INC_ATOMIC(net, early_drop); + } + } + /* else { + * this happens when the ct at safelist head is removed from the timer list + * but not yet freed due to ct->ct_general.use > 1. This ct will be freed when its + * ref count is dropped to zero. At this point we dont create new connections + * until some old connection are freed. + * } + */ + nf_ct_put(ct_candidate); + return dropped; +} +#else #define NF_CT_EVICTION_RANGE 8 /* There's a small race here where we may free a just-assured @@ -778,6 +1070,149 @@ nf_ct_put(ct); return dropped; } +#endif + +#if defined(CONFIG_BCM_KF_NETFILTER) + +static inline void ct_set_curr_safe_list(struct nf_conn *ct) +{ + /*first try to move to HW list */ + if(ct->hw_accel_flows) { + if (ct->iq_prio == IQOS_PRIO_HIGH) + ct->curr_safe_list = &ct_safe_lists.hi_hw_accel; + else + ct->curr_safe_list = &ct_safe_lists.low_hw_accel; + }else if(ct->sw_accel_flows) { + if (ct->iq_prio == IQOS_PRIO_HIGH) + ct->curr_safe_list = &ct_safe_lists.hi_sw_accel; + else + ct->curr_safe_list = &ct_safe_lists.low_sw_accel; + }else{ + /*move to SW only list */ + if (ct->iq_prio == IQOS_PRIO_HIGH) + ct->curr_safe_list = &ct_safe_lists.hi; + else + ct->curr_safe_list = &ct_safe_lists.low; + } +} +static inline void ct_safe_list_add_tail(struct nf_conn *ct) +{ + spin_lock_bh(&ct_safe_lists.lock); + + ct->hw_accel_flows = 0; + ct->sw_accel_flows = 0; + + ct_set_curr_safe_list(ct); + + list_add_tail(&ct->safe_list, ct->curr_safe_list); + + spin_unlock_bh(&ct_safe_lists.lock); +} + +static inline void ct_safe_list_move_tail(struct nf_conn *ct) +{ + spin_lock_bh(&ct_safe_lists.lock); + + list_move_tail(&ct->safe_list, ct->curr_safe_list); + + spin_unlock_bh(&ct_safe_lists.lock); +} + +static inline void ct_safe_list_del(struct nf_conn *ct) +{ + spin_lock_bh(&ct_safe_lists.lock); + list_del(&ct->safe_list); + spin_unlock_bh(&ct_safe_lists.lock); +} + +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + +static inline void __ct_blog_flow_accel_activate_event(struct nf_conn *ct, + BlogFlowEventInfo_t *info) +{ + spin_lock_bh(&ct_safe_lists.lock); + /* ensure ct is not being deleted */ + if(likely(atomic_read(&ct->ct_general.use) >= 1)) + { + if(info->flow_event_type == FLOW_EVENT_TYPE_HW) + ct->hw_accel_flows++; + else + ct->sw_accel_flows++; + + ct_set_curr_safe_list(ct); + + list_move_tail(&ct->safe_list, ct->curr_safe_list); + } + spin_unlock_bh(&ct_safe_lists.lock); +} + +static inline void ct_blog_flow_activate_event(BlogFlowEventInfo_t *info) +{ + + if ((info->flow_event_type == FLOW_EVENT_TYPE_FC) || + (info->flow_event_type == FLOW_EVENT_TYPE_HW)){ + + __ct_blog_flow_accel_activate_event(info->ct_pld_p, info); + + if(info->ct_del_p) + __ct_blog_flow_accel_activate_event(info->ct_del_p, info); + } +} + +static inline void __ct_blog_flow_accel_deactivate_event(struct nf_conn *ct, + BlogFlowEventInfo_t *info) +{ + spin_lock_bh(&ct_safe_lists.lock); + /* ensure ct is not being deleted */ + if(likely(atomic_read(&ct->ct_general.use) >= 1)){ + + if(info->flow_event_type == FLOW_EVENT_TYPE_HW) + ct->hw_accel_flows--; + else + ct->sw_accel_flows--; + + ct_set_curr_safe_list(ct); + + list_move_tail(&ct->safe_list, ct->curr_safe_list); + } + spin_unlock_bh(&ct_safe_lists.lock); +} + +static inline void ct_blog_flow_deactivate_event( BlogFlowEventInfo_t *info) +{ + if ((info->flow_event_type == FLOW_EVENT_TYPE_FC) || + (info->flow_event_type == FLOW_EVENT_TYPE_HW)){ + + __ct_blog_flow_accel_deactivate_event(info->ct_pld_p, info); + + if(info->ct_del_p) + __ct_blog_flow_accel_deactivate_event(info->ct_del_p, info); + } +} + +static int ct_blog_flowevent_notify(struct notifier_block * nb, + unsigned long event, void *info) +{ + switch(event){ + + case FLOW_EVENT_ACTIVATE: + ct_blog_flow_activate_event(info); + break; + + case FLOW_EVENT_DEACTIVATE: + ct_blog_flow_deactivate_event(info); + break; + + default: + break; + + } + return NOTIFY_OK; +} + +#endif /*CONFIG_BLOG */ + +#endif/*CONFIG_BCM_KF_NETFILTER*/ void init_nf_conntrack_hash_rnd(void) { @@ -794,11 +1229,26 @@ cmpxchg(&nf_conntrack_hash_rnd, 0, rand); } +#if defined(CONFIG_BCM_KF_NETFILTER) +static inline int +nf_conntrack_ipv6_is_multicast(const __be32 ip6[4]) +{ + return ((ip6[0] & htonl(0xFF000000)) == htonl(0xFF000000)); +} + static struct nf_conn * __nf_conntrack_alloc(struct net *net, u16 zone, + struct sk_buff *skb, const struct nf_conntrack_tuple *orig, const struct nf_conntrack_tuple *repl, gfp_t gfp, u32 hash) +#else +static struct nf_conn * +__nf_conntrack_alloc(struct net *net, u16 zone, + const struct nf_conntrack_tuple *orig, + const struct nf_conntrack_tuple *repl, + gfp_t gfp, u32 hash) +#endif { struct nf_conn *ct; @@ -813,11 +1263,20 @@ if (nf_conntrack_max && unlikely(atomic_read(&net->ct.count) > nf_conntrack_max)) { +#if defined(CONFIG_BCM_KF_NETFILTER) + /* Sorry, we have to kick LRU out regardlessly. */ + if (!regardless_drop(net, skb)) { + atomic_dec(&net->ct.count); + net_warn_ratelimited("nf_conntrack: table full, dropping packet\n"); + return ERR_PTR(-ENOMEM); + } +#else if (!early_drop(net, hash)) { atomic_dec(&net->ct.count); net_warn_ratelimited("nf_conntrack: table full, dropping packet\n"); return ERR_PTR(-ENOMEM); } +#endif } /* @@ -829,6 +1288,27 @@ atomic_dec(&net->ct.count); return ERR_PTR(-ENOMEM); } + +#if defined(CONFIG_BCM_KF_NETFILTER) + INIT_LIST_HEAD(&ct->safe_list); + INIT_LIST_HEAD(&ct->derived_connections); + INIT_LIST_HEAD(&ct->derived_list); + ct->derived_timeout = 0; +#endif + +#if defined(CONFIG_BCM_KF_NETFILTER) + /* Broadcom changed the position of these two fields. They used to be + in the area being memset to 0 */ + ct->master = 0; + ct->status = 0; +#endif + +#if defined(CONFIG_BCM_KF_NETFILTER) && (defined(CONFIG_NF_DYNDSCP) || defined(CONFIG_NF_DYNDSCP_MODULE)) + ct->dyndscp.status = 0; + ct->dyndscp.dscp[0] = 0; + ct->dyndscp.dscp[1] = 0; +#endif + spin_lock_init(&ct->lock); ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple = *orig; ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode.pprev = NULL; @@ -836,8 +1316,59 @@ /* save hash for reusing when confirming */ *(unsigned long *)(&ct->tuplehash[IP_CT_DIR_REPLY].hnnode.pprev) = hash; ct->status = 0; + +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + pr_debug("nf_conntrack_alloc: ct<%p> BLOGible\n", ct ); + set_bit(IPS_BLOG_BIT, &ct->status); /* Enable conntrack blogging */ + + /* new conntrack: reset blog keys */ + ct->blog_key[IP_CT_DIR_ORIGINAL] = BLOG_KEY_FC_INVALID; + ct->blog_key[IP_CT_DIR_REPLY] = BLOG_KEY_FC_INVALID; + if (skb == NULL || skb->blog_p == NULL ) { + switch (nf_ct_l3num(ct)) { + case AF_INET: + ct->iq_prio = ipv4_is_multicast(orig->dst.u3.ip) ? IQOS_PRIO_HIGH : IQOS_PRIO_LOW; + break; + case AF_INET6: + ct->iq_prio = nf_conntrack_ipv6_is_multicast(orig->dst.u3.ip6) ? IQOS_PRIO_HIGH : IQOS_PRIO_LOW; + break; + default: + ct->iq_prio = IQOS_PRIO_LOW; + } + } else { + ct->iq_prio = (blog_iq(skb) == BLOG_IQ_PRIO_HIGH) ? IQOS_PRIO_HIGH : IQOS_PRIO_LOW; + } + + /* Don't set timer yet: wait for confirmation */ + setup_timer(&ct->timeout, blog_death_by_timeout, (unsigned long)ct); +#else + /* Don't set timer yet: wait for confirmation */ setup_timer(&ct->timeout, death_by_timeout, (unsigned long)ct); +#endif + +#if defined(CONFIG_BCM_KF_XT_MATCH_LAYER7) && \ + (defined(CONFIG_NETFILTER_XT_MATCH_LAYER7) || defined(CONFIG_NETFILTER_XT_MATCH_LAYER7_MODULE)) + ct->layer7.app_proto = NULL; + ct->layer7.app_data = NULL; + ct->layer7.app_data_len = 0; +#endif + +#if defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_MODULE) + memset(&ct->dpi, 0, sizeof(ct->dpi)); + + if (skb && (skb->dev) && (skb->dev->priv_flags & IFF_WANDEV)) + set_bit(DPI_CT_INIT_FROM_WAN_BIT, &ct->dpi.flags); +#endif + +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) +#if defined(CONFIG_BCM_RUNNER_RG) || defined(CONFIG_BCM_RUNNER_RG_MODULE) + ct->bl_ctx = NULL; + BL_OPS(net_netfilter_nf_conntrack_core_nf_conntrack_alloc(ct)); +#endif /* CONFIG_BCM_RUNNER_RG || CONFIG_BCM_RUNNER_RG_MODULE */ +#endif /* CONFIG_BCM_RUNNER */ +#endif /* CONFIG_BCM_KF_RUNNER */ write_pnet(&ct->ct_net, net); memset(&ct->__nfct_init_offset[0], 0, offsetof(struct nf_conn, proto) - @@ -866,6 +1397,16 @@ #endif } +#if defined(CONFIG_BCM_KF_NETFILTER) +struct nf_conn *nf_conntrack_alloc(struct net *net, u16 zone, + struct sk_buff *skb, + const struct nf_conntrack_tuple *orig, + const struct nf_conntrack_tuple *repl, + gfp_t gfp) +{ + return __nf_conntrack_alloc(net, zone, skb, orig, repl, gfp, 0); +} +#else struct nf_conn *nf_conntrack_alloc(struct net *net, u16 zone, const struct nf_conntrack_tuple *orig, const struct nf_conntrack_tuple *repl, @@ -873,17 +1414,28 @@ { return __nf_conntrack_alloc(net, zone, orig, repl, gfp, 0); } +#endif EXPORT_SYMBOL_GPL(nf_conntrack_alloc); void nf_conntrack_free(struct nf_conn *ct) { struct net *net = nf_ct_net(ct); +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) + BL_OPS(net_netfilter_nf_conntrack_core_nf_conntrack_free(ct)); +#endif /* CONFIG_BCM_RUNNER */ +#endif /* CONFIG_BCM_KF_RUNNER */ + /* A freed object has refcnt == 0, that's * the golden rule for SLAB_DESTROY_BY_RCU */ NF_CT_ASSERT(atomic_read(&ct->ct_general.use) == 0); +#if defined(CONFIG_BCM_KF_NETFILTER) + ct_safe_list_del(ct); +#endif + nf_ct_ext_destroy(ct); nf_ct_ext_free(ct); kmem_cache_free(net->ct.nf_conntrack_cachep, ct); @@ -917,8 +1469,13 @@ return NULL; } +#if defined(CONFIG_BCM_KF_NETFILTER) + ct = __nf_conntrack_alloc(net, zone, skb, tuple, &repl_tuple, GFP_ATOMIC, + hash); +#else ct = __nf_conntrack_alloc(net, zone, tuple, &repl_tuple, GFP_ATOMIC, hash); +#endif if (IS_ERR(ct)) return (struct nf_conntrack_tuple_hash *)ct; @@ -962,6 +1519,12 @@ __set_bit(IPS_EXPECTED_BIT, &ct->status); /* exp->master safe, refcnt bumped in nf_ct_find_expectation */ ct->master = exp->master; +#if defined(CONFIG_BCM_KF_NETFILTER) + list_add(&ct->derived_list, + &exp->master->derived_connections); + if (exp->flags & NF_CT_EXPECT_DERIVED_TIMEOUT) + ct->derived_timeout = exp->derived_timeout; +#endif if (exp->helper) { help = nf_ct_helper_ext_add(ct, exp->helper, GFP_ATOMIC); @@ -988,6 +1551,10 @@ nf_conntrack_get(&ct->ct_general); nf_ct_add_to_unconfirmed_list(ct); +#if defined(CONFIG_BCM_KF_NETFILTER) + ct_safe_list_add_tail(ct); +#endif + local_bh_enable(); if (exp) { @@ -1059,6 +1626,40 @@ } skb->nfct = &ct->ct_general; skb->nfctinfo = *ctinfo; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + { + struct nf_conn_help * help = nfct_help(ct); + + blog_lock(); + if ((help != (struct nf_conn_help *)NULL) && + (help->helper != (struct nf_conntrack_helper *)NULL) && + (help->helper->name && strcmp(help->helper->name, "BCM-NAT"))) { + pr_debug("nf_conntrack_in: skb<%p> ct<%p> helper<%s> found\n", + skb, ct, help->helper->name); + clear_bit(IPS_BLOG_BIT, &ct->status); + } + if (test_bit(IPS_BLOG_BIT, &ct->status)) { /* OK to blog ? */ + uint32_t ct_type=(l3num==PF_INET)?BLOG_PARAM2_IPV4:BLOG_PARAM2_IPV6; + pr_debug("nf_conntrack_in: skb<%p> blog<%p> ct<%p>\n", + skb, blog_ptr(skb), ct); + + if (protonum == IPPROTO_GRE) + ct_type = BLOG_PARAM2_GRE_IPV4; + + if(ntohs(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.udp.port) == BLOG_L2TP_PORT) + ct_type = BLOG_PARAM2_L2TP_IPV4; + + blog_link(FLOWTRACK, blog_ptr(skb), + (void*)ct, CTINFO2DIR(skb->nfctinfo), ct_type); + } else { + pr_debug("nf_conntrack_in: skb<%p> ct<%p> NOT BLOGible<%p>\n", + skb, ct, blog_ptr(skb)); + blog_skip(skb, blog_skip_reason_ct_status_donot_blog); /* No blogging */ + } + blog_unlock(); + } +#endif + return ct; } @@ -1152,8 +1753,24 @@ goto out; } +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) + BL_OPS(net_netfilter_nf_conntrack_core_nf_conntrack_in(ct, skb)); +#endif /* CONFIG_BCM_RUNNER */ +#endif /* CONFIG_BCM_KF_RUNNER */ + if (set_reply && !test_and_set_bit(IPS_SEEN_REPLY_BIT, &ct->status)) nf_conntrack_event_cache(IPCT_REPLY, ct); + +#if defined(CONFIG_BCM_KF_NETFILTER) + /* Maintain LRU list. The least recently used ctt is on the head */ + if (ctinfo == IP_CT_ESTABLISHED || + ctinfo == IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { + /* Update ct as latest used */ + ct_safe_list_move_tail(ct); + } +#endif + out: if (tmpl) { /* Special case: we have to repeat this hook, assign the @@ -1234,6 +1851,19 @@ mod_timer_pending(&ct->timeout, newtime); } +#if defined(CONFIG_BCM_KF_NETFILTER) + + /* store the extra jfiies in ct, and use it to refresh + * when connection is accelerated + */ + ct->extra_jiffies = extra_jiffies; + /* + * safe_list through blog refresh is updated at an interval refresh is called + * If that interval is large - it is possible that a connection getting high traffic + * may be seen as LRU by conntrack. + */ + ct_safe_list_move_tail(ct); +#endif acct: if (do_acct) { struct nf_conn_acct *acct; @@ -1448,8 +2078,18 @@ RCU_INIT_POINTER(ip_ct_attach, NULL); } +#if defined(CONFIG_BCM_KF_NETFILTER) && defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +static struct notifier_block ct_blog_flowevent_notifier = { + .notifier_call = ct_blog_flowevent_notify, +}; +#endif + void nf_conntrack_cleanup_end(void) { +#if defined(CONFIG_BCM_KF_NETFILTER) && defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_flowevent_unregister_notifier(&ct_blog_flowevent_notifier); +#endif + RCU_INIT_POINTER(nf_ct_destroy, NULL); while (untrack_refs() > 0) schedule(); @@ -1484,6 +2124,11 @@ { int busy; struct net *net; +#if defined(CONFIG_BCM_KF_NETFILTER) + int try_counter = 0; + unsigned long start = jiffies; + unsigned long end = start + HZ; +#endif /* * This makes sure all current packets have passed through @@ -1499,6 +2144,15 @@ busy = 1; } if (busy) { +#if defined(CONFIG_BCM_KF_NETFILTER) + if (jiffies >= end) { + printk("waiting for %d conntrack to be cleaned, " + "tried %d times\n", + atomic_read(&net->ct.count), try_counter); + end += HZ; + } + try_counter++; +#endif schedule(); goto i_see_dead_people; } @@ -1516,6 +2170,12 @@ free_percpu(net->ct.stat); free_percpu(net->ct.pcpu_lists); } +#if defined(CONFIG_BCM_KF_NETFILTER) + end = jiffies; + if (end - start > HZ) + printk("nf_conntrack took %lu milliseconds to clean up\n", + (end - start) * 1000 / HZ); +#endif } void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls) @@ -1728,6 +2388,9 @@ void nf_conntrack_init_end(void) { +#if defined(CONFIG_BCM_KF_NETFILTER) && defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_flowevent_register_notifier(&ct_blog_flowevent_notifier); +#endif /* For use by REJECT target */ RCU_INIT_POINTER(ip_ct_attach, nf_conntrack_attach); RCU_INIT_POINTER(nf_ct_destroy, destroy_conntrack); @@ -1803,6 +2466,10 @@ ret = nf_conntrack_proto_pernet_init(net); if (ret < 0) goto err_proto; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BCM_KF_NETFILTER) && defined(CONFIG_BLOG) + blog_cttime_update_fn = (blog_cttime_upd_t)__nf_ct_time_update; +#endif + return 0; err_proto: diff -urN linux-4.1.52-orig/net/netfilter/nf_conntrack_ftp.c linux-4.1.52/net/netfilter/nf_conntrack_ftp.c --- linux-4.1.52-orig/net/netfilter/nf_conntrack_ftp.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/netfilter/nf_conntrack_ftp.c 2020-02-07 01:47:54.000000000 +0800 @@ -27,6 +27,13 @@ #include #include +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) +#include +#endif /* CONFIG_BCM_RUNNER */ +#endif /* CONFIG_BCM_KF_RUNNER */ + + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Rusty Russell "); MODULE_DESCRIPTION("ftp connection tracking helper"); @@ -35,6 +42,9 @@ /* This is slow, but it's simple. --RR */ static char *ftp_buffer; +#if defined(CONFIG_BCM_KF_NETFILTER) +static char *ftp_big_buffer = NULL; +#endif static DEFINE_SPINLOCK(nf_ftp_lock); @@ -391,7 +401,11 @@ const struct tcphdr *th; struct tcphdr _tcph; const char *fb_ptr; +#if defined(CONFIG_BCM_KF_NETFILTER) + int ret = NF_ACCEPT; +#else int ret; +#endif u32 seq; int dir = CTINFO2DIR(ctinfo); unsigned int uninitialized_var(matchlen), uninitialized_var(matchoff); @@ -424,6 +438,17 @@ datalen = skb->len - dataoff; spin_lock_bh(&nf_ftp_lock); +#if defined(CONFIG_BCM_KF_NETFILTER) + /* In worst case, the packet size will increase by 20 bytes after + * NAT modification */ + if (datalen > NF_ALG_BUFFER_SIZE - 20) { + ftp_big_buffer = kmalloc(datalen + 20, GFP_ATOMIC); + if (!ftp_big_buffer) + goto out; + fb_ptr = skb_header_pointer(skb, dataoff, datalen, + ftp_big_buffer); + } else +#endif fb_ptr = skb_header_pointer(skb, dataoff, datalen, ftp_buffer); BUG_ON(fb_ptr == NULL); @@ -541,7 +566,14 @@ nf_ct_helper_log(skb, ct, "cannot add expectation"); ret = NF_DROP; } else +#if defined(CONFIG_BCM_KF_RUNNER) && (defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE)) + { + BL_OPS(net_netfilter_nf_conntrack_ftp(ct, ctinfo, exp, search[dir][i].ftptype)); ret = NF_ACCEPT; + } +#else /* CONFIG_BCM_KF_RUNNER && CONFIG_BCM_RUNNER */ + ret = NF_ACCEPT; +#endif /* CONFIG_BCM_KF_RUNNER && CONFIG_BCM_RUNNER */ } out_put_expect: @@ -553,6 +585,12 @@ if (ends_in_nl) update_nl_seq(ct, seq, ct_ftp_info, dir, skb); out: +#if defined(CONFIG_BCM_KF_NETFILTER) + if (ftp_big_buffer) { + kfree(ftp_big_buffer); + ftp_big_buffer = NULL; + } +#endif spin_unlock_bh(&nf_ftp_lock); return ret; } @@ -600,7 +638,11 @@ { int i, j = -1, ret = 0; +#if defined(CONFIG_BCM_KF_NETFILTER) + ftp_buffer = kmalloc(NF_ALG_BUFFER_SIZE, GFP_KERNEL); +#else ftp_buffer = kmalloc(65536, GFP_KERNEL); +#endif if (!ftp_buffer) return -ENOMEM; diff -urN linux-4.1.52-orig/net/netfilter/nf_conntrack_h323_main.c linux-4.1.52/net/netfilter/nf_conntrack_h323_main.c --- linux-4.1.52-orig/net/netfilter/nf_conntrack_h323_main.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/netfilter/nf_conntrack_h323_main.c 2020-02-07 01:47:54.000000000 +0800 @@ -33,6 +33,9 @@ #include #include #include +#if defined(CONFIG_BCM_KF_NETFILTER) +#include +#endif /* Parameters */ static unsigned int default_rrq_ttl __read_mostly = 300; @@ -325,6 +328,11 @@ nf_ct_expect_put(rtp_exp); nf_ct_expect_put(rtcp_exp); +#if defined(CONFIG_BCM_KF_NETFILTER) + iqos_add_L4port( IPPROTO_UDP, rtp_port, IQOS_ENT_DYN, IQOS_PRIO_HIGH ); + iqos_add_L4port( IPPROTO_UDP, rtcp_port, IQOS_ENT_DYN, IQOS_PRIO_HIGH ); +#endif + return ret; } @@ -377,6 +385,10 @@ nf_ct_expect_put(exp); +#if defined(CONFIG_BCM_KF_NETFILTER) + iqos_add_L4port( IPPROTO_TCP, port, IQOS_ENT_DYN, IQOS_PRIO_HIGH ); +#endif + return ret; } @@ -723,6 +735,10 @@ nf_ct_expect_put(exp); +#if defined(CONFIG_BCM_KF_NETFILTER) + iqos_add_L4port( IPPROTO_TCP, port, IQOS_ENT_DYN, IQOS_PRIO_HIGH ); +#endif + return ret; } @@ -852,6 +868,10 @@ nf_ct_expect_put(exp); +#if defined(CONFIG_BCM_KF_NETFILTER) + iqos_add_L4port( IPPROTO_TCP, port, IQOS_ENT_DYN, IQOS_PRIO_HIGH ); +#endif + return ret; } @@ -1340,6 +1360,10 @@ nf_ct_expect_put(exp); +#if defined(CONFIG_BCM_KF_NETFILTER) + iqos_add_L4port( IPPROTO_TCP, port, IQOS_ENT_DYN, IQOS_PRIO_HIGH ); +#endif + return ret; } @@ -1403,6 +1427,10 @@ nf_ct_expect_put(exp); +#if defined(CONFIG_BCM_KF_NETFILTER) + iqos_add_L4port( IPPROTO_UDP, port, IQOS_ENT_DYN, IQOS_PRIO_HIGH ); +#endif + return ret; } @@ -1619,6 +1647,10 @@ nf_ct_expect_put(exp); +#if defined(CONFIG_BCM_KF_NETFILTER) + iqos_add_L4port( IPPROTO_TCP, port, IQOS_ENT_DYN, IQOS_PRIO_HIGH ); +#endif + return ret; } @@ -1677,6 +1709,10 @@ /* Ignore rasAddress */ +#if defined(CONFIG_BCM_KF_NETFILTER) + iqos_add_L4port( IPPROTO_TCP, port, IQOS_ENT_DYN, IQOS_PRIO_HIGH ); +#endif + return ret; } @@ -1834,6 +1870,16 @@ /****************************************************************************/ static void __exit nf_conntrack_h323_fini(void) { +#if defined(CONFIG_BCM_KF_NETFILTER) + /* unregister the Q.931 ports with ingress QoS classifier */ + iqos_rem_L4port( nf_conntrack_helper_q931[0].tuple.dst.protonum, + nf_conntrack_helper_q931[0].tuple.src.u.tcp.port, IQOS_ENT_STAT ); + + /* unregister the RAS ports with ingress QoS classifier */ + iqos_rem_L4port( nf_conntrack_helper_ras[0].tuple.dst.protonum, + nf_conntrack_helper_ras[0].tuple.src.u.udp.port, IQOS_ENT_STAT ); +#endif + nf_conntrack_helper_unregister(&nf_conntrack_helper_ras[1]); nf_conntrack_helper_unregister(&nf_conntrack_helper_ras[0]); nf_conntrack_helper_unregister(&nf_conntrack_helper_q931[1]); @@ -1867,6 +1913,17 @@ if (ret < 0) goto err5; pr_debug("nf_ct_h323: init success\n"); +#if defined(CONFIG_BCM_KF_NETFILTER) + /* register the Q.931 ports with ingress QoS classifier */ + iqos_add_L4port( nf_conntrack_helper_q931[0].tuple.dst.protonum, + nf_conntrack_helper_q931[0].tuple.src.u.tcp.port, + IQOS_ENT_STAT, IQOS_PRIO_HIGH ); + + /* register the RAS ports with ingress QoS classifier */ + iqos_add_L4port( nf_conntrack_helper_ras[0].tuple.dst.protonum, + nf_conntrack_helper_ras[0].tuple.src.u.udp.port, + IQOS_ENT_STAT, IQOS_PRIO_HIGH ); +#endif return 0; err5: diff -urN linux-4.1.52-orig/net/netfilter/nf_conntrack_ipsec.c linux-4.1.52/net/netfilter/nf_conntrack_ipsec.c --- linux-4.1.52-orig/net/netfilter/nf_conntrack_ipsec.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/netfilter/nf_conntrack_ipsec.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,353 @@ +#if defined(CONFIG_BCM_KF_PROTO_IPSEC) +/* +<:copyright-BRCM:2012:GPL/GPL:standard + + Copyright (c) 2012 Broadcom + All Rights Reserved + +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 (the "GPL"). + +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. + + +A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by +writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + +:> +*/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef CONFIG_NF_BL_EXT +#include +#endif /* CONFIG_NF_BL_EXT */ + +MODULE_AUTHOR("Pavan Kumar "); +MODULE_DESCRIPTION("Netfilter connection tracking module for ipsec"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("ip_conntrack_ipsec"); + +static DEFINE_SPINLOCK(nf_ipsec_lock); + +int +(*nf_nat_ipsec_hook_outbound)(struct sk_buff *skb, + struct nf_conn *ct, enum ip_conntrack_info ctinfo) __read_mostly; +EXPORT_SYMBOL_GPL(nf_nat_ipsec_hook_outbound); + +int +(*nf_nat_ipsec_hook_inbound)(struct sk_buff *skb, + struct nf_conn *ct, enum ip_conntrack_info ctinfo, __be32 lan_ip) __read_mostly; +EXPORT_SYMBOL_GPL(nf_nat_ipsec_hook_inbound); + +static void __exit nf_conntrack_ipsec_fini(void); + +#define CT_REFRESH_TIMEOUT (60 * HZ) /* KT: Changed from 13 Sec to 1 Min */ + +/* Internal table for ISAKMP */ +struct _ipsec_table +{ + u_int32_t initcookie; + __be32 lan_ip; + struct nf_conn *ct; + int pkt_rcvd; + int inuse; +} ipsec_table[MAX_VPN_CONNECTION]; + +static struct _ipsec_table *ipsec_alloc_entry(int *index) +{ + int idx = 0; + + for( ; idx < MAX_VPN_CONNECTION; idx++ ) + { + if( ipsec_table[idx].inuse ) + continue; + + *index = idx; + memset(&ipsec_table[idx], 0, sizeof(struct _ipsec_table)); + + pr_debug("([%d] alloc_entry()\n", idx); + + return (&ipsec_table[idx]); + } + + return NULL; +} + +/* + * Search an IPsec table entry by ct. + */ +struct _ipsec_table *search_ipsec_entry_by_ct(struct nf_conn *ct) +{ + int idx = 0; + + for( ; idx < MAX_VPN_CONNECTION; idx++) + { + if (!ipsec_table[idx].inuse) + continue; + + pr_debug("Searching entry->ct(%p) <--> ct(%p)\n", + ipsec_table[idx].ct, ct); + + /* check ct */ + if (ipsec_table[idx].ct == ct) + { + pr_debug("Found entry with ct(%p)\n", ct); + + return &ipsec_table[idx]; + } + } + pr_debug("No Entry for ct(%p)\n", ct); + return NULL; +} + +/* + * Search an IPSEC table entry by the initiator cookie. + */ +struct _ipsec_table * +search_ipsec_entry_by_cookie(struct isakmp_pkt_hdr *isakmph) +{ + int idx = 0; + struct _ipsec_table *ipsec_entry = ipsec_table; + + for( ; idx < MAX_VPN_CONNECTION; idx++ ) + { + pr_debug("Searching initcookie %x <-> %x\n", + ntohl(isakmph->initcookie), ntohl(ipsec_entry->initcookie)); + + if( isakmph->initcookie == ipsec_entry->initcookie ) + return ipsec_entry; + + ipsec_entry++; + } + + return NULL; +} + +/* + * Search an IPSEC table entry by the source IP address. + */ +struct _ipsec_table * +search_ipsec_entry_by_addr(const __be32 lan_ip, int *index) +{ + int idx = 0; + struct _ipsec_table *ipsec_entry = ipsec_table; + + for( ; idx < MAX_VPN_CONNECTION; idx++ ) + { + pr_debug("Looking up lan_ip=%pI4 table entry %pI4\n", + &lan_ip, &ipsec_entry->lan_ip); + + if( ntohl(ipsec_entry->lan_ip) == ntohl(lan_ip) ) + { + pr_debug("Search by addr returning entry %p\n", ipsec_entry); + + *index = idx; + return ipsec_entry; + } + ipsec_entry++; + } + + return NULL; +} + +static inline int +ipsec_inbound_pkt(struct sk_buff *skb, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, __be32 lan_ip) +{ +// struct nf_ct_ipsec_master *info = &nfct_help(ct)->help.ct_ipsec_info; + typeof(nf_nat_ipsec_hook_inbound) nf_nat_ipsec_inbound; + + pr_debug("inbound ISAKMP packet for LAN %pI4\n", &lan_ip); + + nf_nat_ipsec_inbound = rcu_dereference(nf_nat_ipsec_hook_inbound); + if (nf_nat_ipsec_inbound && ct->status & IPS_NAT_MASK) + return nf_nat_ipsec_inbound(skb, ct, ctinfo, lan_ip); + + return NF_ACCEPT; +} + +/* + * For outgoing ISAKMP packets, we need to make sure UDP ports=500 + */ +static inline int +ipsec_outbound_pkt(struct sk_buff *skb, + struct nf_conn *ct, enum ip_conntrack_info ctinfo) +{ + typeof(nf_nat_ipsec_hook_outbound) nf_nat_ipsec_outbound; + + pr_debug("outbound ISAKMP packet skb(%p)\n", skb); + + nf_nat_ipsec_outbound = rcu_dereference(nf_nat_ipsec_hook_outbound); + if( nf_nat_ipsec_outbound && ct->status & IPS_NAT_MASK ) + return nf_nat_ipsec_outbound(skb, ct, ctinfo); + + return NF_ACCEPT; +} + +/* track cookies inside ISAKMP, call expect_related */ +static int conntrack_ipsec_help(struct sk_buff *skb, unsigned int protoff, + struct nf_conn *ct, enum ip_conntrack_info ctinfo) +{ + int dir = CTINFO2DIR(ctinfo); + struct nf_ct_ipsec_master *info = nfct_help_data(ct); + struct isakmp_pkt_hdr _isakmph, *isakmph = NULL; + struct _ipsec_table *ipsec_entry = ipsec_table; + int ret, index=0; + + pr_debug("skb(%p) skb->data(%p) ct(%p) protoff(%d) offset(%d)\n", skb, skb->data, ct, protoff, (int) (protoff + sizeof(struct udphdr))); + + isakmph = skb_header_pointer(skb, protoff + sizeof(struct udphdr), sizeof(_isakmph), &_isakmph); + if (isakmph == NULL) + { + pr_debug("ERR: no full ISAKMP header, can't track. isakmph=[%p]\n", isakmph); + return NF_ACCEPT; + } + + if ( 0 == isakmph->initcookie ) + { + pr_debug("ERR: all zero ISAKMP initcookie.\n"); + return NF_ACCEPT; + } + + spin_lock_bh(&nf_ipsec_lock); + + if( dir == IP_CT_DIR_ORIGINAL ) + { + int lan_ip = ct->tuplehash[dir].tuple.src.u3.ip; + + /* create one entry in the internal table if a new connection is found */ + if( (ipsec_entry = search_ipsec_entry_by_cookie(isakmph)) == NULL ) + { + /* NOTE: cookies may be updated in the connection */ + if( (ipsec_entry = + search_ipsec_entry_by_addr(lan_ip, &index)) == NULL ) + { + ipsec_entry = ipsec_alloc_entry(&index); + if( ipsec_entry == NULL ) + { + /* All entries are currently in use */ + pr_debug("ERR: Too many sessions. ct(%p)\n", ct); + spin_unlock_bh(&nf_ipsec_lock); + return NF_DROP; + } + + ipsec_entry->ct = ct; /* KT: Guess it should be here */ + ipsec_entry->initcookie = isakmph->initcookie; /* KT: Update our cookie information - moved to here */ + ipsec_entry->lan_ip = ct->tuplehash[dir].tuple.src.u3.ip; + ipsec_entry->inuse = 1; + + pr_debug("NEW ipsec_entry[%d] with ct=%p, lan_ip=%pI4, initcookie=%x\n", + index, ipsec_entry->ct, &ipsec_entry->lan_ip, + ntohl(ipsec_entry->initcookie) ); + } else { + pr_debug("EXISTING ipsec_entry[%d] with ct=%p, lan_ip=%pI4, initcookie=%x\n", + index, ipsec_entry->ct, &ipsec_entry->lan_ip, + ntohl(ipsec_entry->initcookie) ); + } + } + ipsec_entry->pkt_rcvd++; + + info->initcookie = isakmph->initcookie; + info->lan_ip = ct->tuplehash[dir].tuple.src.u3.ip; + + pr_debug("L->W: initcookie=%x, lan_ip=%pI4, dir[%d] src.u3.ip=%pI4, dst.u3.ip=%pI4\n", + info->initcookie, &info->lan_ip, + dir, + &ct->tuplehash[dir].tuple.src.u3.ip, + &ct->tuplehash[dir].tuple.dst.u3.ip); + + nf_ct_refresh_acct(ipsec_entry->ct, 0, skb, CT_REFRESH_TIMEOUT); + + ret = ipsec_outbound_pkt(skb, ct, ctinfo); + } + else + { + pr_debug("WAN->LAN ct=%p\n", ct); + + if( (ipsec_entry = search_ipsec_entry_by_cookie(isakmph)) != NULL ) + { + nf_ct_refresh_acct(ipsec_entry->ct, 0, skb, CT_REFRESH_TIMEOUT); + ipsec_entry->pkt_rcvd++; + + pr_debug("W->L: initcookie=%x, lan_ip=%pI4, dir[%d] src.u3.ip=%pI4, dst.u3.ip=%pI4\n", + info->initcookie, &info->lan_ip, + dir, + &ct->tuplehash[dir].tuple.src.u3.ip, + &ct->tuplehash[dir].tuple.dst.u3.ip); + + ret = ipsec_inbound_pkt(skb, ct, ctinfo, ipsec_entry->lan_ip); + } + else + { + pr_debug("WARNNING: client from WAN tries to connect to VPN server in the LAN. ipsec_entry=[%p]\n", ipsec_entry); + ret = NF_ACCEPT; + } + } + + spin_unlock_bh(&nf_ipsec_lock); + + return ret; +} + +/* Called when the connection is deleted. */ +static void ipsec_destroy(struct nf_conn *ct) +{ + struct _ipsec_table *ipsec_entry = NULL; + + spin_lock_bh(&nf_ipsec_lock); + pr_debug("DEL IPsec entry ct(%p)\n", ct); + if ((ipsec_entry = search_ipsec_entry_by_ct(ct))) { + memset(ipsec_entry, 0, sizeof(struct _ipsec_table)); + } else { + pr_debug("DEL IPsec entry failed: ct(%p)\n", ct); + } + spin_unlock_bh(&nf_ipsec_lock); +} + +static const struct nf_conntrack_expect_policy ipsec_exp_policy = { + .max_expected = 3, + .timeout = 300, +}; + +/* ISAKMP protocol helper */ +static struct nf_conntrack_helper ipsec __read_mostly = { + .name = "ipsec", + .me = THIS_MODULE, + .tuple.src.l3num = AF_INET, + .tuple.dst.protonum = IPPROTO_UDP, + .tuple.src.u.udp.port = __constant_htons(IPSEC_PORT), + .data_len = sizeof(struct nf_ct_ipsec_master), + .help = conntrack_ipsec_help, + .destroy = ipsec_destroy, + .expect_policy = &ipsec_exp_policy, +}; + +static int __init nf_conntrack_ipsec_init(void) +{ + return nf_conntrack_helper_register(&ipsec); +} + +static void __exit nf_conntrack_ipsec_fini(void) +{ + nf_conntrack_helper_unregister(&ipsec); +} + +module_init(nf_conntrack_ipsec_init); +module_exit(nf_conntrack_ipsec_fini); +#endif diff -urN linux-4.1.52-orig/net/netfilter/nf_conntrack_irc.c linux-4.1.52/net/netfilter/nf_conntrack_irc.c --- linux-4.1.52-orig/net/netfilter/nf_conntrack_irc.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/netfilter/nf_conntrack_irc.c 2020-02-07 01:47:54.000000000 +0800 @@ -30,6 +30,9 @@ static unsigned int dcc_timeout __read_mostly = 300; /* This is slow, but it's simple. --RR */ static char *irc_buffer; +#if defined(CONFIG_BCM_KF_NETFILTER) +static char *irc_big_buffer = NULL; +#endif static DEFINE_SPINLOCK(irc_buffer_lock); unsigned int (*nf_nat_irc_hook)(struct sk_buff *skb, @@ -141,6 +144,19 @@ return NF_ACCEPT; spin_lock_bh(&irc_buffer_lock); +#if defined(CONFIG_BCM_KF_NETFILTER) + /* In worst case, the packet size will increase by 16 bytes after + * NAT modification */ + if (skb->len > NF_ALG_BUFFER_SIZE - 16) { + irc_big_buffer = kmalloc(skb->len - dataoff + 16, + GFP_ATOMIC); + if (!irc_big_buffer) + goto out; + ib_ptr = skb_header_pointer(skb, dataoff, + skb->len - dataoff, + irc_big_buffer); + } else +#endif ib_ptr = skb_header_pointer(skb, dataoff, skb->len - dataoff, irc_buffer); BUG_ON(ib_ptr == NULL); @@ -223,6 +239,12 @@ } } out: +#if defined(CONFIG_BCM_KF_NETFILTER) + if (irc_big_buffer) { + kfree(irc_big_buffer); + irc_big_buffer = NULL; + } +#endif spin_unlock_bh(&irc_buffer_lock); return ret; } @@ -244,7 +266,11 @@ irc_exp_policy.max_expected = max_dcc_channels; irc_exp_policy.timeout = dcc_timeout; +#if defined(CONFIG_BCM_KF_NETFILTER) + irc_buffer = kmalloc(NF_ALG_BUFFER_SIZE, GFP_KERNEL); +#else irc_buffer = kmalloc(65536, GFP_KERNEL); +#endif if (!irc_buffer) return -ENOMEM; diff -urN linux-4.1.52-orig/net/netfilter/nf_conntrack_netlink.c linux-4.1.52/net/netfilter/nf_conntrack_netlink.c --- linux-4.1.52-orig/net/netfilter/nf_conntrack_netlink.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/netfilter/nf_conntrack_netlink.c 2020-02-07 01:47:54.000000000 +0800 @@ -56,6 +56,10 @@ #include #include +#if defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_MODULE) +#include +#endif + MODULE_LICENSE("GPL"); static char __initdata version[] = "0.93"; @@ -214,6 +218,9 @@ static int dump_counters(struct sk_buff *skb, struct nf_conn_acct *acct, +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + const struct nf_conn *ct, +#endif enum ip_conntrack_dir dir, int type) { enum ctattr_type attr = dir ? CTA_COUNTERS_REPLY: CTA_COUNTERS_ORIG; @@ -228,6 +235,20 @@ pkts = atomic64_read(&counter[dir].packets); bytes = atomic64_read(&counter[dir].bytes); } +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + { + BlogFcStats_t fast_stats; + + fast_stats.rx_packets = 0; + fast_stats.rx_bytes = 0; + + if(blog_ct_get_stats(ct, ct->blog_key[dir], dir, &fast_stats) == 0) + counter[dir].ts = fast_stats.pollTS_ms; + + pkts += counter[dir].cum_fast_pkts + fast_stats.rx_packets; + bytes += counter[dir].cum_fast_bytes + fast_stats.rx_bytes; + } +#endif nest_count = nla_nest_start(skb, attr | NLA_F_NESTED); if (!nest_count) @@ -253,10 +274,17 @@ if (!acct) return 0; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + if (dump_counters(skb, acct, ct, IP_CT_DIR_ORIGINAL, type) < 0) + return -1; + if (dump_counters(skb, acct, ct, IP_CT_DIR_REPLY, type) < 0) + return -1; +#else if (dump_counters(skb, acct, IP_CT_DIR_ORIGINAL, type) < 0) return -1; if (dump_counters(skb, acct, IP_CT_DIR_REPLY, type) < 0) return -1; +#endif return 0; } @@ -456,6 +484,89 @@ return -1; } +#if defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_MODULE) +static int ctnetlink_dpi_size(const struct nf_conn *ct) +{ + int len = dpi_url_len(ct->dpi.url); + + return nla_total_size(sizeof(u_int32_t)) /* CTA_DPI_APP_ID */ + + 6 * nla_total_size(sizeof(u_int8_t)) /* CTA_DPI_MAC */ + + nla_total_size(sizeof(u_int32_t)) /* CTA_DPI_STATUS */ + + nla_total_size(sizeof(char) * len); /* CTA_DPI_URL */ + ; +} + +static inline int +ctnetlink_dump_dpi(struct sk_buff *skb, const struct nf_conn *ct) +{ + struct nlattr *nest_parms; + uint32_t app_id; + uint8_t *mac; + uint8_t empty_mac[6] = {0}; + char *url; + + app_id = dpi_app_id(ct->dpi.app); + mac = dpi_mac(ct->dpi.dev); + if (!mac) + mac = empty_mac; + url = dpi_url(ct->dpi.url); + + nest_parms = nla_nest_start(skb, CTA_DPI | NLA_F_NESTED); + if (!nest_parms) + goto nla_put_failure; + + if (nla_put_be32(skb, CTA_DPI_APP_ID, htonl(app_id))) + goto nla_put_failure; + if (nla_put(skb, CTA_DPI_MAC, sizeof(empty_mac), mac)) + goto nla_put_failure; + if (nla_put_be32(skb, CTA_DPI_STATUS, htonl(ct->dpi.flags))) + goto nla_put_failure; + if (url && nla_put_string(skb, CTA_DPI_URL, url)) + goto nla_put_failure; + + nla_nest_end(skb, nest_parms); + + return 0; + +nla_put_failure: + return -1; +} + +static const struct nla_policy dpi_policy[CTA_DPI_MAX+1] = { + [CTA_DPI_APP_ID] = { .type = NLA_U32 }, + [CTA_DPI_MAC] = { .type = NLA_BINARY, + .len = 6 * sizeof(uint8_t) }, + [CTA_DPI_STATUS] = { .type = NLA_U32 }, +}; + +static int +ctnetlink_change_dpi(struct nf_conn *ct, const struct nlattr * const cda[]) +{ + const struct nlattr *attr = cda[CTA_DPI]; + struct nlattr *tb[CTA_DPI_MAX+1]; + int err = 0; + + err = nla_parse_nested(tb, CTA_DPI_MAX, attr, dpi_policy); + if (err < 0) + return err; + + if (tb[CTA_DPI_STATUS]) { + unsigned long new = ntohl(nla_get_be32(tb[CTA_DPI_STATUS])); + unsigned long old = ct->dpi.flags; + unsigned long changed; + + ct->dpi.flags = (new & DPI_NL_CHANGE_MASK) | + (old & ~DPI_NL_CHANGE_MASK); + changed = old ^ ct->dpi.flags; + + if (test_bit(DPI_CT_BLOCK_BIT, &changed)) + dpi_block(ct); + } + + return 0; +} +#endif + static int ctnetlink_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, struct nf_conn *ct) @@ -508,6 +619,11 @@ ctnetlink_dump_ct_seq_adj(skb, ct) < 0) goto nla_put_failure; +#if defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_MODULE) + if (ctnetlink_dump_dpi(skb, ct) < 0) + goto nla_put_failure; +#endif + nlmsg_end(skb, nlh); return skb->len; @@ -604,6 +720,9 @@ #endif + ctnetlink_proto_size(ct) + ctnetlink_label_size(ct) +#if defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_MODULE) + + ctnetlink_dpi_size(ct) /* CTA_DPI */ +#endif ; } @@ -720,6 +839,11 @@ && ctnetlink_dump_mark(skb, ct) < 0) goto nla_put_failure; #endif + +#if defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_MODULE) + if (ctnetlink_dump_dpi(skb, ct) < 0) + goto nla_put_failure; +#endif rcu_read_unlock(); nlmsg_end(skb, nlh); @@ -1648,6 +1772,14 @@ return err; } +#if defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_MODULE) + if (cda[CTA_DPI]) { + err = ctnetlink_change_dpi(ct, cda); + if (err < 0) + return err; + } +#endif + return 0; } @@ -1663,7 +1795,11 @@ struct nf_conntrack_helper *helper; struct nf_conn_tstamp *tstamp; +#if defined(CONFIG_BCM_KF_NETFILTER) + ct = nf_conntrack_alloc(net, zone, NULL, otuple, rtuple, GFP_ATOMIC); +#else ct = nf_conntrack_alloc(net, zone, otuple, rtuple, GFP_ATOMIC); +#endif if (IS_ERR(ct)) return ERR_PTR(-ENOMEM); @@ -2094,6 +2230,9 @@ + nla_total_size(sizeof(u_int16_t)) /* CTA_ZONE */ #endif + ctnetlink_proto_size(ct) +#if defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_MODULE) + + ctnetlink_dpi_size(ct) /* CTA_DPI */ +#endif ; } @@ -2154,6 +2293,10 @@ #endif if (ctnetlink_dump_labels(skb, ct) < 0) goto nla_put_failure; +#if defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_MODULE) + if (ctnetlink_dump_dpi(skb, ct) < 0) + goto nla_put_failure; +#endif rcu_read_unlock(); return 0; diff -urN linux-4.1.52-orig/net/netfilter/nf_conntrack_proto_esp.c linux-4.1.52/net/netfilter/nf_conntrack_proto_esp.c --- linux-4.1.52-orig/net/netfilter/nf_conntrack_proto_esp.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/netfilter/nf_conntrack_proto_esp.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,433 @@ +#if defined(CONFIG_BCM_KF_PROTO_ESP) +/* +<:copyright-BRCM:2012:GPL/GPL:standard + + Copyright (c) 2012 Broadcom + All Rights Reserved + +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 (the "GPL"). + +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. + + +A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by +writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + +:> +*/ +/****************************************************************************** + Filename: nf_conntrack_proto_esp.c + Author: Pavan Kumar + Creation Date: 05/27/04 + + Description: + Implements the ESP ALG connection tracking. + Migrated to kernel 2.6.21.5 on April 16, 2008 by Dan-Han Tsai. + Migrated to kernel 3.4.11 on Jan 21, 2013 by Kirill Tsym +*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +enum grep_conntrack { + ESP_CT_UNREPLIED, + ESP_CT_REPLIED, + ESP_CT_MAX +}; + +static unsigned int esp_timeouts[ESP_CT_MAX] = { + [ESP_CT_UNREPLIED] = 30*HZ, + [ESP_CT_REPLIED] = 60*HZ, +}; + +#define IPSEC_INUSE 1 +#define MAX_PORTS 8 /* KT: Changed to match MAX_VPN_CONNECTION */ +#define TEMP_SPI_START 1500 + +struct _esp_table +{ + u_int32_t l_spi; + u_int32_t r_spi; + u_int32_t l_ip; + u_int32_t r_ip; + u_int32_t timeout; + u_int16_t tspi; + struct nf_conn *ct; + int pkt_rcvd; + int inuse; +}; + +static struct _esp_table esp_table[MAX_PORTS]; + +/* + * Allocate a free IPSEC table entry. + */ +struct _esp_table *alloc_esp_entry( void ) +{ + int idx = 0; + + for( ; idx < MAX_PORTS; idx++ ) + { + if( esp_table[idx].inuse == IPSEC_INUSE ) + continue; + + memset(&esp_table[idx], 0, sizeof(struct _esp_table)); + esp_table[idx].tspi = idx + TEMP_SPI_START; + esp_table[idx].inuse = IPSEC_INUSE; + + pr_debug("[%d] alloc_entry() tspi(%u)\n", idx, esp_table[idx].tspi); + + return (&esp_table[idx]); + } + return NULL; +} + +/* + * Search an ESP table entry by ct. + */ +struct _esp_table *search_esp_entry_by_ct(struct nf_conn *ct) +{ + int idx = 0; + + for( ; idx < MAX_PORTS; idx++) + { + if(esp_table[idx].inuse != IPSEC_INUSE ) + continue; + + pr_debug("Searching entry->ct(%p) <--> ct(%p)\n", + esp_table[idx].ct, ct); + + /* checking ct */ + if(esp_table[idx].ct == ct) + { + pr_debug("Found entry with ct(%p)\n", ct); + + return &esp_table[idx]; + } + } + + pr_debug("No Entry for ct(%p)\n", ct); + return NULL; +} + +/* + * Search an ESP table entry by source IP. + * If found one, update the spi value + */ +struct _esp_table *search_esp_entry_by_ip( const struct nf_conntrack_tuple *tuple, const __u32 spi ) +{ + int idx = 0; + __u32 srcIP = tuple->src.u3.ip; + __u32 dstIP = tuple->dst.u3.ip; + struct _esp_table *esp_entry = esp_table; + + for( ; idx < MAX_PORTS; idx++, esp_entry++ ) + { + pr_debug(" Searching IP %pI4 <-> %pI4, %pI4\n", + &srcIP, &esp_entry->l_ip, + &esp_entry->r_ip); + + /* make sure l_ip is LAN IP */ + if( (srcIP == esp_entry->l_ip) && (((unsigned char *)&(srcIP))[0] == 192) ) + { + pr_debug(" found entry with l_ip\n"); + esp_entry->l_spi = spi; + + /* This is a new connection of the same LAN host */ + if( dstIP != esp_entry->r_ip ) + { + esp_entry->r_ip = dstIP; + esp_entry->r_spi = 0; + } + return esp_entry; + } + else if( srcIP == esp_entry->r_ip ) + { + pr_debug(" found entry with r_ip\n"); + /* FIXME */ + if( esp_entry->r_spi == 0 ) + { + pr_debug(" found entry with r_ip and r_spi == 0\n"); + esp_entry->r_spi = spi; + return esp_entry; + } + /* We cannot handle spi changed at WAN side */ + pr_debug(" found entry with r_ip but r_spi != 0\n"); + } + } + pr_debug("No Entry for spi(0x%x)\n", spi); + return NULL; +} + +/* + * Search an ESP table entry by spi + */ +struct _esp_table *search_esp_entry_by_spi( const __u32 spi, const __u32 srcIP ) +{ + int idx = 0; + struct _esp_table *esp_entry = esp_table; + + for( ; idx < MAX_PORTS; idx++, esp_entry++ ) + { + pr_debug(" Searching spi 0x%x <-> 0x%x, 0x%x\n", + spi, esp_entry->l_spi, esp_entry->r_spi); + + if( (spi == esp_entry->l_spi) || (spi == esp_entry->r_spi) ) + { + pr_debug(" In %s, found entry %d with tspi %u\n", + __FUNCTION__, idx, esp_entry->tspi); + + /* l_spi and r_spi may be the same */ + if( (spi == esp_entry->l_spi) && (srcIP == esp_entry->r_ip) ) + { + pr_debug("l_spi(0x%x)==r_spi\n", spi); + esp_entry->r_spi = spi; + } + + return esp_entry; + } + } + pr_debug("No Entry for spi(0x%x)\n", spi); + + return NULL; +} + +/* invert esp part of tuple */ +static bool esp_invert_tuple(struct nf_conntrack_tuple *tuple, + const struct nf_conntrack_tuple *orig) +{ + pr_debug("with spi = %u\n", orig->src.u.esp.spi); + + tuple->dst.u.esp.spi = orig->dst.u.esp.spi; + tuple->src.u.esp.spi = orig->src.u.esp.spi; + return true; +} + +/* esp hdr info to tuple */ +static bool esp_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff, + struct nf_conntrack_tuple *tuple) +{ + struct esphdr _esphdr, *esphdr; + struct _esp_table *esp_entry = NULL; + + esphdr = skb_header_pointer(skb, dataoff, sizeof(_esphdr), &_esphdr); + if( !esphdr ) + { + /* try to behave like "nf_conntrack_proto_generic" */ + tuple->src.u.all = 0; + tuple->dst.u.all = 0; + return true; + } + + pr_debug("Enter pkt_to_tuple() with spi 0x%x\n", esphdr->spi); + /* check if esphdr has a new SPI: + * if no, update tuple with correct tspi and increment pkt count; + * if yes, check if we have seen the source IP: + * if yes, do the tspi and pkt count update + * if no, create a new entry + */ + + if( ((esp_entry = search_esp_entry_by_spi(esphdr->spi, tuple->src.u3.ip)) == NULL) ) + { + if( (esp_entry = + search_esp_entry_by_ip(tuple, esphdr->spi)) == NULL ) + { +#if 0 + /* Because SA is simplex, it's possible that WAN starts connection first. + * We need to make sure that the connection starts from LAN. + */ + if( ((unsigned char *)&(tuple->src.u3.ip))[0] != 192 ) + { + pr_debug("srcIP %pI4 is WAN IP, DROP packet\n", &tuple->src.u3.ip); + return false; + } +#endif + esp_entry = alloc_esp_entry(); + if( esp_entry == NULL ) + { + pr_debug("Too many entries. New spi(0x%x)\n", esphdr->spi); + return false; + } + + esp_entry->l_spi = esphdr->spi; + esp_entry->l_ip = tuple->src.u3.ip; + esp_entry->r_ip = tuple->dst.u3.ip; + } + + } + + pr_debug("esp_entry: tspi(%u) l_ip[%pI4]-->r_ip[%pI4] tuple: srcIP[%pI4]-->dstIP[%pI4]\n", + esp_entry->tspi, + &esp_entry->l_ip, &esp_entry->r_ip, + &tuple->src.u3.ip, &tuple->dst.u3.ip); + + tuple->dst.u.esp.spi = tuple->src.u.esp.spi = esp_entry->tspi; + esp_entry->pkt_rcvd++; + + return true; +} + +/* print esp part of tuple */ +static void esp_print_tuple(struct seq_file *s, + const struct nf_conntrack_tuple *tuple) +{ + seq_printf(s, "srcspi=0x%x dstspi=0x%x ", + tuple->src.u.esp.spi, tuple->dst.u.esp.spi); +} + +/* print private data for conntrack */ +static void esp_print_conntrack(struct seq_file *s, struct nf_conn *ct) +{ + seq_printf(s, "timeout=%u, stream_timeout=%u ", + (ct->proto.esp.timeout / HZ), + (ct->proto.esp.stream_timeout / HZ)); +} + +static unsigned int *esp_get_timeouts(struct net *net) +{ + return esp_timeouts; +} + +/* Returns verdict for packet, and may modify conntrack */ +static int esp_packet(struct nf_conn *ct, + const struct sk_buff *skb, + unsigned int dataoff, + enum ip_conntrack_info ctinfo, + u_int8_t pf, + unsigned int hooknum, + unsigned int *timeouts) +{ + struct esphdr _esphdr, *esphdr; + struct iphdr *iph = ip_hdr(skb); + + esphdr = skb_header_pointer(skb, dataoff, sizeof(_esphdr), &_esphdr); + + pr_debug("(0x%x) %pI4 <-> %pI4 status %s info %d %s\n", + esphdr->spi, &iph->saddr, &iph->daddr, (ct->status & IPS_SEEN_REPLY) ? "SEEN" : "NOT_SEEN", + ctinfo, (ctinfo == IP_CT_NEW ) ? "CT_NEW" : "SEEN_REPLY" ); + + /* If we've seen traffic both ways, this is some kind of ESP + stream. Extend timeout. */ + if( test_bit(IPS_SEEN_REPLY_BIT, &ct->status) ) + { + nf_ct_refresh_acct(ct, ctinfo, skb, ct->proto.esp.stream_timeout); + /* Also, more likely to be important, and not a probe */ + if( !test_and_set_bit(IPS_ASSURED_BIT, &ct->status) ) + nf_conntrack_event_cache(IPCT_ASSURED, ct); + } + else + nf_ct_refresh_acct(ct, ctinfo, skb, ct->proto.esp.timeout); + + return NF_ACCEPT; +} + +/* Called when a new connection for this protocol found. */ +static bool esp_new(struct nf_conn *ct, const struct sk_buff *skb, + unsigned int dataoff, unsigned int *timeouts) +{ + struct iphdr *iph = ip_hdr(skb); + struct _esp_table *esp_entry; + struct esphdr _esphdr, *esphdr; + + ct->proto.esp.stream_timeout = timeouts[ESP_CT_UNREPLIED]; + ct->proto.esp.timeout = timeouts[ESP_CT_UNREPLIED]; + + esphdr = skb_header_pointer(skb, dataoff, sizeof(_esphdr), &_esphdr); + + pr_debug("NEW SPI(0x%x) %pI4 <-> %pI4 ct(%p)\n", + esphdr->spi, &iph->saddr, &iph->daddr, ct); + + if( (esp_entry = search_esp_entry_by_spi(esphdr->spi, 0)) != NULL ) { + esp_entry->ct = ct; + } else { + pr_debug("ERR: In esp_new(), cannot find an entry with SPI %x\n", esphdr->spi); + return false; + } + + return true; +} + +/* Protect conntrack agaist broken packets. Code referenced from nf_conntrack_proto_tcp.c. */ +static int esp_error(struct net *net, struct nf_conn *tmpl, + struct sk_buff *skb, + unsigned int dataoff, + enum ip_conntrack_info *ctinfo, + u_int8_t pf, + unsigned int hooknum) +{ + const struct esphdr *esphdr; + struct esphdr _esphdr; + + /* smaller than minimal ESP header? */ + esphdr = skb_header_pointer(skb, dataoff, sizeof(_esphdr), &_esphdr); + if (esphdr == NULL) { + if (LOG_INVALID(net, IPPROTO_ESP)) + nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL, + "nf_ct_esp: short packet "); + return -NF_ACCEPT; + } + return NF_ACCEPT; +} + +/* Called when the connection is deleted. */ +static void esp_destroy(struct nf_conn *ct) +{ + struct _esp_table *esp_entry = NULL; + + pr_debug("DEL ESP entry ct(%p)\n", ct); + if ((esp_entry = search_esp_entry_by_ct(ct))) { + memset(esp_entry, 0, sizeof(struct _esp_table)); + } else { + pr_debug("ERR: DEL ESP Failed for ct(%p): no such entry\n", ct); + } +} + +/* protocol helper struct */ +struct nf_conntrack_l4proto nf_conntrack_l4proto_esp4 = { + .l3proto = PF_INET, + .l4proto = IPPROTO_ESP, + .name = "esp", + .pkt_to_tuple = esp_pkt_to_tuple, + .invert_tuple = esp_invert_tuple, + .print_tuple = esp_print_tuple, + .print_conntrack = esp_print_conntrack, + .get_timeouts = esp_get_timeouts, + .packet = esp_packet, + .new = esp_new, + .error = esp_error, + .destroy = esp_destroy, + .me = THIS_MODULE, +}; + +int __init nf_ct_proto_esp_init(void) +{ + return nf_ct_l4proto_register(&nf_conntrack_l4proto_esp4); +} + +void __exit nf_ct_proto_esp_fini(void) +{ + nf_ct_l4proto_unregister(&nf_conntrack_l4proto_esp4); +} +module_init(nf_ct_proto_esp_init); +module_exit(nf_ct_proto_esp_fini); + +MODULE_LICENSE("GPL"); +#endif diff -urN linux-4.1.52-orig/net/netfilter/nf_conntrack_proto_tcp.c linux-4.1.52/net/netfilter/nf_conntrack_proto_tcp.c --- linux-4.1.52-orig/net/netfilter/nf_conntrack_proto_tcp.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/netfilter/nf_conntrack_proto_tcp.c 2020-02-07 01:47:54.000000000 +0800 @@ -33,10 +33,18 @@ #include #include +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#include +#endif + +#if defined(CONFIG_BCM_KF_NETFILTER) +static int nf_ct_tcp_be_liberal __read_mostly = 1; +#else /* "Be conservative in what you do, be liberal in what you accept from others." If it's non-zero, we mark only out of window RST segments as INVALID. */ static int nf_ct_tcp_be_liberal __read_mostly = 0; +#endif /* If it is set to zero, we disable picking up already established connections. */ @@ -867,11 +875,28 @@ * context and we must give it a chance to terminate. */ if (nf_ct_kill(ct)) +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + { + if (del_timer(&ct->timeout)) { + printk("%s: delete ct right now.\n", __FUNCTION__); + nf_ct_delete(ct, 0, 0); + return NF_DROP; + } + return -NF_REPEAT; + } +#else /* ! (CONFIG_BCM_KF_BLOG && CONFIG_BLOG) */ return -NF_REPEAT; +#endif /* ! (CONFIG_BCM_KF_BLOG && CONFIG_BLOG) */ return NF_DROP; } /* Fall through */ case TCP_CONNTRACK_IGNORE: +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_lock(); + blog_skip((struct sk_buff *)skb, blog_skip_reason_ct_tcp_state_ignore); + blog_unlock(); +#endif + /* Ignored packets: * * Our connection entry may be out of sync, so ignore @@ -1051,6 +1076,7 @@ old_state, new_state); ct->proto.tcp.state = new_state; + if (old_state != new_state && new_state == TCP_CONNTRACK_FIN_WAIT) ct->proto.tcp.seen[dir].flags |= IP_CT_TCP_FLAG_CLOSE_INIT; @@ -1066,6 +1092,51 @@ timeout = timeouts[new_state]; spin_unlock_bh(&ct->lock); +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_lock(); + if (th->fin) { + /* clear the flow association with blog */ + uint32_t key_orig = BLOG_KEY_FC_INVALID; + uint32_t key_reply = BLOG_KEY_FC_INVALID; + + if (dir == IP_CT_DIR_ORIGINAL) + key_orig = ct->blog_key[IP_CT_DIR_ORIGINAL]; + else if (dir == IP_CT_DIR_REPLY) + key_reply = ct->blog_key[IP_CT_DIR_REPLY]; + + if ((key_orig != BLOG_KEY_FC_INVALID) || (key_reply != BLOG_KEY_FC_INVALID)) { + blog_notify(DESTROY_FLOWTRACK, (void*)ct, key_orig, key_reply); + } + clear_bit(IPS_BLOG_BIT, &ct->status); + } else if (th->rst) { + /* Abort and make this conntrack not BLOG eligible */ + if ((ct->blog_key[IP_CT_DIR_ORIGINAL] != BLOG_KEY_FC_INVALID) + || (ct->blog_key[IP_CT_DIR_REPLY] != BLOG_KEY_FC_INVALID)) { + blog_notify(DESTROY_FLOWTRACK, (void*)ct, + (uint32_t)ct->blog_key[IP_CT_DIR_ORIGINAL], + (uint32_t)ct->blog_key[IP_CT_DIR_REPLY]); + } + } + if (ct->proto.tcp.state != TCP_CONNTRACK_ESTABLISHED) + blog_skip((struct sk_buff *)skb, blog_skip_reason_ct_tcp_state_not_est); + else{ + /* when connection is in established state and accelerated, we cannot + * determine IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED properly, so just use the + * Established state tiemout value + * + * Worst case scenario when connection drops with out any RST/FIN, this conntrack + * will be present for 5 days(Established timeout). But with reagrdless drop + * we will drop this connection if table is full, so essentially we are not + * blocking any resources for this conntrack when they are needed + */ + + if ((ct->blog_key[IP_CT_DIR_ORIGINAL] != BLOG_KEY_FC_INVALID) + || (ct->blog_key[IP_CT_DIR_REPLY] != BLOG_KEY_FC_INVALID)) + timeout = timeouts[new_state]; + } + blog_unlock(); +#endif + if (new_state != old_state) nf_conntrack_event_cache(IPCT_PROTOINFO, ct); @@ -1094,6 +1165,14 @@ set_bit(IPS_ASSURED_BIT, &ct->status); nf_conntrack_event_cache(IPCT_ASSURED, ct); } +#if defined(CONFIG_BCM_KF_NETFILTER) + if (new_state == TCP_CONNTRACK_ESTABLISHED) { + if (ct->derived_timeout == 0xFFFFFFFF) + timeout = 0xFFFFFFFF - jiffies; + else if (ct->derived_timeout > 0) + timeout = ct->derived_timeout; + } +#endif nf_ct_refresh_acct(ct, ctinfo, skb, timeout); return NF_ACCEPT; @@ -1401,6 +1480,7 @@ }; #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ + #ifdef CONFIG_SYSCTL static struct ctl_table tcp_sysctl_table[] = { { diff -urN linux-4.1.52-orig/net/netfilter/nf_conntrack_proto_udp.c linux-4.1.52/net/netfilter/nf_conntrack_proto_udp.c --- linux-4.1.52-orig/net/netfilter/nf_conntrack_proto_udp.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/netfilter/nf_conntrack_proto_udp.c 2020-02-07 01:47:54.000000000 +0800 @@ -88,8 +88,18 @@ /* If we've seen traffic both ways, this is some kind of UDP stream. Extend timeout. */ if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) { +#if defined(CONFIG_BCM_KF_NETFILTER) + unsigned timeout = timeouts[UDP_CT_REPLIED]; + if (ct->derived_timeout == 0xFFFFFFFF){ + timeout = 60*60*HZ; + } else if(ct->derived_timeout > 0) { + timeout = ct->derived_timeout; + } + nf_ct_refresh_acct(ct, ctinfo, skb, timeout); +#else nf_ct_refresh_acct(ct, ctinfo, skb, timeouts[UDP_CT_REPLIED]); +#endif /* Also, more likely to be important, and not a probe */ if (!test_and_set_bit(IPS_ASSURED_BIT, &ct->status)) nf_conntrack_event_cache(IPCT_ASSURED, ct); @@ -201,6 +211,7 @@ }; #endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */ + #ifdef CONFIG_SYSCTL static struct ctl_table udp_sysctl_table[] = { { @@ -213,7 +224,7 @@ .procname = "nf_conntrack_udp_timeout_stream", .maxlen = sizeof(unsigned int), .mode = 0644, - .proc_handler = proc_dointvec_jiffies, + .proc_handler = proc_dointvec_jiffies, }, { } }; @@ -229,7 +240,7 @@ .procname = "ip_conntrack_udp_timeout_stream", .maxlen = sizeof(unsigned int), .mode = 0644, - .proc_handler = proc_dointvec_jiffies, + .proc_handler = proc_dointvec_jiffies, }, { } }; diff -urN linux-4.1.52-orig/net/netfilter/nf_conntrack_rtsp.c linux-4.1.52/net/netfilter/nf_conntrack_rtsp.c --- linux-4.1.52-orig/net/netfilter/nf_conntrack_rtsp.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/netfilter/nf_conntrack_rtsp.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,872 @@ +#if defined(CONFIG_BCM_KF_NETFILTER) +/* RTSP helper for connection tracking. */ + +/* (C) 2008 Broadcom Corporation + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Broadcom Corporation"); +MODULE_DESCRIPTION("RTSP connection tracking helper"); +MODULE_ALIAS("ip_conntrack_rtsp"); + +#define RTSP_PORT 554 + +/* This is slow, but it's simple. --RR */ +static char *rtsp_buffer; + +static DEFINE_SPINLOCK(nf_rtsp_lock); + +#define MAX_PORTS 8 +static u_int16_t ports[MAX_PORTS]; +static unsigned int ports_c; +module_param_array(ports, ushort, &ports_c, 0400); +MODULE_PARM_DESC(ports, "port numbers of RTSP servers"); + +#define RTSP_CHANNEL_MAX 8 +static int max_outstanding = RTSP_CHANNEL_MAX; +module_param(max_outstanding, int, 0600); +MODULE_PARM_DESC(max_outstanding, + "max number of outstanding SETUP requests per RTSP session"); + +/* Single data channel */ +int (*nat_rtsp_channel_hook) (struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int matchoff, unsigned int matchlen, + struct nf_conntrack_expect *exp, int *delta); +EXPORT_SYMBOL_GPL(nat_rtsp_channel_hook); + +/* A pair of data channels (RTP/RTCP) */ +int (*nat_rtsp_channel2_hook) (struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int matchoff, unsigned int matchlen, + struct nf_conntrack_expect *rtp_exp, + struct nf_conntrack_expect *rtcp_exp, + char dash, int *delta); +EXPORT_SYMBOL_GPL(nat_rtsp_channel2_hook); + +/* Modify parameters like client_port in Transport for single data channel */ +int (*nat_rtsp_modify_port_hook) (struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int matchoff, unsigned int matchlen, + __be16 rtpport, int *delta); +EXPORT_SYMBOL_GPL(nat_rtsp_modify_port_hook); + +/* Modify parameters like client_port in Transport for multiple data channels*/ +int (*nat_rtsp_modify_port2_hook) (struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int matchoff, unsigned int matchlen, + __be16 rtpport, __be16 rtcpport, + char dash, int *delta); +EXPORT_SYMBOL_GPL(nat_rtsp_modify_port2_hook); + +/* Modify parameters like destination in Transport */ +int (*nat_rtsp_modify_addr_hook) (struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + int matchoff, int matchlen, int *delta); +EXPORT_SYMBOL_GPL(nat_rtsp_modify_addr_hook); + +static int memmem(const char *haystack, int haystacklen, + const char *needle, int needlelen) +{ + const char *p = haystack; + int l = haystacklen - needlelen + 1; + char c = *needle; + + while(l-- > 0) { /* "!=0" won't handle haystacklen less than needlelen, need ">" */ + if (*p++ == c) { + if (memcmp(p, needle+1, needlelen-1) == 0) + return p - haystack - 1; + } + } + return -1; +} + +static int memstr(const char *haystack, int haystacklen, + const char *needle, int needlelen) +{ + const char *p = haystack; + int l = haystacklen - needlelen + 1; + char c = *needle; + + if (isalpha(c)) { + char lower = __tolower(c); + char upper = __toupper(c); + + while(l-- > 0) { /* "!=0" won't handle haystacklen less than needlelen, need ">" */ + if (*p == lower || *p == upper) { + if (strncasecmp(p, needle, needlelen) == 0) + return p - haystack; + } + p++; + } + } else { + while(l-- > 0) { + if (*p++ == c) { + if (strncasecmp(p, needle+1, needlelen-1) == 0) + return p - haystack - 1; + } + } + } + return -1; +} + +static int get_cseq(const char *str) +{ + unsigned long cseq = 0, i = 0; + char c = *str; + while(i++ < 10 && c && c != 0xd && c>='0' && c <= '9'){ + cseq = (cseq * 10) + (c - '0'); + c = *(str + i); + } + if(!cseq) + cseq = -1; + return (int) cseq; +} + +/* Get next message in a packet */ +static int get_next_message(const char *tcpdata, int tcpdatalen, + int *msgoff, int *msglen, int *msghdrlen) +{ + if (*msglen == 0) { /* The first message */ + *msgoff = 0; + } else { + *msgoff += *msglen; + if ((*msgoff + 4) >= tcpdatalen) /* No more message */ + return 0; + } + + /* Get message header length */ + *msghdrlen = memmem(tcpdata+*msgoff, tcpdatalen-*msgoff, "\r\n\r\n", 4); + if (*msghdrlen < 0) { + *msghdrlen = *msglen = tcpdatalen - *msgoff; + } else { + /* Get message length including SDP */ + int cloff = memstr(tcpdata+*msgoff, *msghdrlen, "Content-Length: ", 16); + if (cloff < 0) { + *msglen = *msghdrlen + 4; + } else { + unsigned long cl = simple_strtoul(tcpdata+*msgoff+cloff+16, NULL, 10); + *msglen = *msghdrlen + 4 + cl; + } + } + + return 1; +} + +/* Get next client_port parameter in a Transport header */ +static int get_next_client_port(const char *tcpdata, int tpoff, int tplen, + int *portoff, int *portlen, + __be16 *rtpport, __be16 *rtcpport, + char *dash) +{ + int off; + char *p; + + if (*portlen == 0) { /* The first client_port */ + *portoff = tpoff; + } else { + *portoff += *portlen; + if (*portoff >= tpoff + tplen) /* No more data */ + return 0; + } + + off = memmem(tcpdata+*portoff, tplen-(*portoff-tpoff), + ";client_port=", 13); + if (off < 0) + return 0; + *portoff += off + 13; + + *rtpport = htons((unsigned short)simple_strtoul(tcpdata+*portoff, + &p, 10)); + if (*p != '-' && *p != '/') { + *dash = 0; + } else { + *dash = *p++; + *rtcpport = htons((unsigned short)simple_strtoul(p, &p, 10)); + } + *portlen = p - tcpdata - *portoff; + return 1; +} + +/* Get next destination=: parameter in a Transport header + * This is not a standard parameter, so far, it's only seen in some customers' + * products. + */ +static int get_next_dest_ipport(const char *tcpdata, int tpoff, int tplen, + int *destoff, int *destlen, __be32 *dest, + int *portoff, int *portlen, __be16 *port) +{ + int off; + char *p; + + if (*destlen == 0) { /* The first destination */ + *destoff = tpoff; + } else { + *destoff += *destlen + 1 + *portlen; + if (*destoff >= tpoff + tplen) /* No more data */ + return 0; + } + + off = memmem(tcpdata+*destoff, tplen-(*destoff-tpoff), + ";destination=", 13); + if (off < 0) + return 0; + *destoff += off + 13; + + if (in4_pton(tcpdata+*destoff, tplen-(*destoff-tpoff), (u8 *)dest, + -1, (const char **)&p) == 0) { + return 0; + } + *destlen = p - tcpdata - *destoff; + + if (*p != ':') { + return 0; + } + *portoff = p - tcpdata + 1; + + *port = htons((unsigned short)simple_strtoul(tcpdata+*portoff, &p, 10)); + *portlen = p - tcpdata - *portoff; + + return 1; +} + +/* Get next destination parameter in a Transport header */ +static int get_next_destination(const char *tcpdata, int tpoff, int tplen, + int *destoff, int *destlen, __be32 *dest) +{ + int off; + char *p; + + if (*destlen == 0) { /* The first destination */ + *destoff = tpoff; + } else { + *destoff += *destlen; + if (*destoff >= tpoff + tplen) /* No more data */ + return 0; + } + + off = memmem(tcpdata+*destoff, tplen-(*destoff-tpoff), + ";destination=", 13); + if (off < 0) + return 0; + *destoff += off + 13; + + if (in4_pton(tcpdata+*destoff, tplen-(*destoff-tpoff), (u8 *)dest, + -1, (const char **)&p)) { + *destlen = p - tcpdata - *destoff; + return 1; + } else { + return 0; + } +} + +static int expect_rtsp_channel(struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + int portoff, int portlen, + __be16 rtpport, int *delta) +{ + int ret = 0; + int dir = CTINFO2DIR(ctinfo); + struct nf_conntrack_expect *rtp_exp; + typeof(nat_rtsp_channel_hook) nat_rtsp_channel; + + if (rtpport == 0) + return -1; + + /* Create expect for RTP */ + if ((rtp_exp = nf_ct_expect_alloc(ct)) == NULL) + return -1; + + nf_ct_expect_init(rtp_exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct), + NULL, &ct->tuplehash[!dir].tuple.dst.u3, + IPPROTO_UDP, NULL, &rtpport); + + if ((nat_rtsp_channel = rcu_dereference(nat_rtsp_channel_hook)) && + ct->status & IPS_NAT_MASK) { + /* NAT needed */ + ret = nat_rtsp_channel(skb, protoff, ct, ctinfo, portoff, portlen, + rtp_exp, delta); + } else { /* Conntrack only */ + if (nf_ct_expect_related(rtp_exp) == 0) { + pr_debug("nf_ct_rtsp: expect RTP "); + nf_ct_dump_tuple(&rtp_exp->tuple); + } else + ret = -1; + } + + nf_ct_expect_put(rtp_exp); + + return ret; +} + +static int expect_rtsp_channel2(struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + int portoff, int portlen, + __be16 rtpport, __be16 rtcpport, + char dash, int *delta) +{ + int ret = 0; + int dir = CTINFO2DIR(ctinfo); + struct nf_conntrack_expect *rtp_exp; + struct nf_conntrack_expect *rtcp_exp; + typeof(nat_rtsp_channel2_hook) nat_rtsp_channel2; + + if (rtpport == 0 || rtcpport == 0) + return -1; + + /* Create expect for RTP */ + if ((rtp_exp = nf_ct_expect_alloc(ct)) == NULL) + return -1; + nf_ct_expect_init(rtp_exp, NF_CT_EXPECT_CLASS_DEFAULT ,nf_ct_l3num(ct), + NULL, &ct->tuplehash[!dir].tuple.dst.u3, + IPPROTO_UDP, NULL, &rtpport); + + /* Create expect for RTCP */ + if ((rtcp_exp = nf_ct_expect_alloc(ct)) == NULL) { + nf_ct_expect_put(rtp_exp); + return -1; + } + nf_ct_expect_init(rtcp_exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct), + NULL, &ct->tuplehash[!dir].tuple.dst.u3, + IPPROTO_UDP, NULL, &rtcpport); + + if ((nat_rtsp_channel2 = rcu_dereference(nat_rtsp_channel2_hook)) && + ct->status & IPS_NAT_MASK) { + /* NAT needed */ + ret = nat_rtsp_channel2(skb, protoff, ct, ctinfo, portoff, portlen, + rtp_exp, rtcp_exp, dash, delta); + } else { /* Conntrack only */ + if (nf_ct_expect_related(rtp_exp) == 0) { + if (nf_ct_expect_related(rtcp_exp) == 0) { + pr_debug("nf_ct_rtsp: expect RTP "); + nf_ct_dump_tuple(&rtp_exp->tuple); + pr_debug("nf_ct_rtsp: expect RTCP "); + nf_ct_dump_tuple(&rtcp_exp->tuple); + } else { + nf_ct_unexpect_related(rtp_exp); + ret = -1; + } + } else + ret = -1; + } + + nf_ct_expect_put(rtp_exp); + nf_ct_expect_put(rtcp_exp); + + return ret; +} + +static void set_normal_timeout(struct nf_conn *ct, struct sk_buff *skb) +{ + struct nf_conn *child; + + /* nf_conntrack_lock is locked inside __nf_ct_refresh_acct, locking here results in a deadlock */ + /* write_lock_bh(&nf_conntrack_lock); */ + list_for_each_entry(child, &ct->derived_connections, derived_list) { + child->derived_timeout = 5*HZ; + nf_ct_refresh(child, skb, 5*HZ); + } + /* write_unlock_bh(&nf_conntrack_lock); */ +} + +static void set_long_timeout(struct nf_conn *ct, struct sk_buff *skb) +{ + struct nf_conn *child; + + /* write_lock_bh(&nf_conntrack_lock); */ + list_for_each_entry(child, &ct->derived_connections, derived_list) { +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_lock(); + if ((child->blog_key[IP_CT_DIR_ORIGINAL] != BLOG_KEY_FC_INVALID) + || (child->blog_key[IP_CT_DIR_REPLY] != BLOG_KEY_FC_INVALID)) { + /* remove flow from flow cache */ + blog_notify(DESTROY_FLOWTRACK, (void*)child, + (uint32_t)child->blog_key[IP_CT_DIR_ORIGINAL], + (uint32_t)child->blog_key[IP_CT_DIR_REPLY]); + + set_bit(IPS_BLOG_BIT, &child->status); /* Enable conntrack blogging */ + } + blog_unlock(); +#endif + nf_ct_refresh(child, skb, 3600*HZ); + } + /* write_unlock_bh(&nf_conntrack_lock); */ +} + + +static int help(struct sk_buff *skb, unsigned int protoff, + struct nf_conn *ct, enum ip_conntrack_info ctinfo) +{ + int dir = CTINFO2DIR(ctinfo); +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + struct nf_conn_help *hlp = nfct_help(ct); +#endif + struct nf_ct_rtsp_master *info = nfct_help_data(ct); + struct tcphdr _tcph, *th; + unsigned int tcpdataoff, tcpdatalen; + char *tcpdata; + int msgoff, msglen, msghdrlen; + int tpoff, tplen; + int portlen = 0; + int portoff = 0; + __be16 rtpport = 0; + __be16 rtcpport = 0; + char dash = 0; + int destlen = 0; + int destoff = 0; + int totaldelta = 0; + __be32 dest = 0; + typeof(nat_rtsp_modify_addr_hook) nat_rtsp_modify_addr; + typeof(nat_rtsp_modify_port_hook) nat_rtsp_modify_port; + typeof(nat_rtsp_modify_port2_hook) nat_rtsp_modify_port2; + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED + && ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) { + return NF_ACCEPT; + } + pr_debug("nf_ct_rtsp: skblen = %u\n", skb->len); + + /* Get TCP header */ + th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph); + if (th == NULL) { + return NF_ACCEPT; + } + + /* Get TCP payload offset */ + tcpdataoff = protoff + th->doff * 4; + if (tcpdataoff >= skb->len) { /* No data? */ + return NF_ACCEPT; + } + + /* Get TCP payload length */ + tcpdatalen = skb->len - tcpdataoff; + + spin_lock_bh(&nf_rtsp_lock); + + /* Get TCP payload pointer */ + tcpdata = skb_header_pointer(skb, tcpdataoff, tcpdatalen, rtsp_buffer); + BUG_ON(tcpdata == NULL); + + /* There may be more than one message in a packet, check them + * one by one */ + msgoff = msglen = msghdrlen = 0; + while(get_next_message(tcpdata, tcpdatalen, &msgoff, &msglen, + &msghdrlen)) { + /* Messages from LAN side through MASQUERADED connections */ + if (memcmp(&ct->tuplehash[dir].tuple.src.u3, + &ct->tuplehash[!dir].tuple.dst.u3, + sizeof(ct->tuplehash[dir].tuple.src.u3)) != 0) { + if(memcmp(tcpdata+msgoff, "PAUSE ", 6) == 0) { + int cseq = memmem(tcpdata+msgoff, msglen, "CSeq: ", 6); + if(cseq == -1) { + /* Fix the IOP issue with DSS on Drawin system */ + cseq = memmem(tcpdata+msgoff, msglen, "Cseq: ", 6); + if(cseq == -1) { + pr_debug("nf_ct_rtsp: wrong PAUSE msg\n"); + } else { + cseq = get_cseq(tcpdata+msgoff+cseq+6); + } + } else { + cseq = get_cseq(tcpdata+msgoff+cseq+6); + } + + pr_debug("nf_ct_rtsp: PAUSE, CSeq=%d\n", cseq); + info->paused = cseq; + continue; + } else { + info->paused = 0; + } + if(memcmp(tcpdata+msgoff, "TEARDOWN ", 9) == 0) { + pr_debug("nf_ct_rtsp: TEARDOWN\n"); + set_normal_timeout(ct, skb); + continue; + } else if(memcmp(tcpdata+msgoff, "SETUP ", 6) != 0) { + continue; + } + + /* Now begin to process SETUP message */ + pr_debug("nf_ct_rtsp: SETUP\n"); + /* Reply message that's from WAN side. */ + } else { + /* We only check replies */ + if(memcmp(tcpdata+msgoff, "RTSP/", 5) != 0) + continue; + + pr_debug("nf_ct_rtsp: Reply message\n"); + + /* Response to a previous PAUSE message */ + if (info->paused) { + int cseq = memmem(tcpdata+msgoff, msglen, "CSeq: ", 6); + if(cseq == -1) { + /* Fix the IOP issue with DSS on Drawin system */ + cseq = memmem(tcpdata+msgoff, msglen, "Cseq: ", 6); + if(cseq == -1) { + pr_debug("nf_ct_rtsp: wrong reply msg\n"); + } else { + cseq = get_cseq(tcpdata+msgoff+cseq+6); + } + } else { + cseq = get_cseq(tcpdata+msgoff+cseq+6); + } + if(cseq == info->paused) { + pr_debug("nf_ct_rtsp: Reply to PAUSE\n"); + set_long_timeout(ct, skb); + info->paused = 0; + goto end; + } + + } + + /* Now begin to process other reply message */ + } + + /* Get Transport header offset */ + tpoff = memmem(tcpdata+msgoff+6, msghdrlen-6, + "\r\nTransport: ", 13); + if (tpoff < 0) + continue; + tpoff += msgoff + 6 + 13; + + /* Get Transport header length */ + tplen = memmem(tcpdata+tpoff, msghdrlen-(tpoff - msgoff), + "\r\n", 2); + if (tplen < 0) + tplen = msghdrlen - (tpoff - msgoff); + +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + if (!test_bit(IPS_SEQ_ADJUST_BIT, &ct->status)) { + /* Check for a server reply containing interleaved transport */ + if (dir && memmem(tcpdata+tpoff, msghdrlen-(tpoff - msgoff), + "interleaved=", 12) >= 0) { + pr_debug("Found RTSP interleaved transport: make it accelerated\n"); + RCU_INIT_POINTER(hlp->helper, NULL); + set_bit(IPS_BLOG_BIT, &ct->status); + goto end; + } + } +#endif + + /* There maybe more than one client_port parameter in this + * field, we'll process each of them. I know not all of them + * are unicast UDP ports, but that is the only situation we + * care about so far. So just KISS. */ + portoff = portlen = 0; + while(get_next_client_port(tcpdata, tpoff, tplen, + &portoff, &portlen, + &rtpport, &rtcpport, &dash)) { + int ret=0, delta; + + if (memcmp(&ct->tuplehash[dir].tuple.src.u3, + &ct->tuplehash[!dir].tuple.dst.u3, + sizeof(ct->tuplehash[dir].tuple.src.u3)) + != 0) { + /* LAN to WAN */ + if (dash == 0) { + /* Single data channel */ + ret = expect_rtsp_channel(skb, protoff, ct, + ctinfo, + portoff, + portlen, + rtpport, + &delta); + } else { + /* A pair of data channels (RTP/RTCP)*/ + ret = expect_rtsp_channel2(skb, protoff, ct, + ctinfo, + portoff, + portlen, + rtpport, + rtcpport, + dash, + &delta); + } + } else { + nat_rtsp_modify_port = rcu_dereference( + nat_rtsp_modify_port_hook); + nat_rtsp_modify_port2 = rcu_dereference( + nat_rtsp_modify_port2_hook); + /* WAN to LAN */ + if (dash == 0 ) { + /* Single data channel */ + if (nat_rtsp_modify_port) { + ret = nat_rtsp_modify_port(skb, protoff, ct, + ctinfo, + portoff, + portlen, + rtpport, + &delta); + } + } else { + /* A pair of data channels (RTP/RTCP)*/ + if (nat_rtsp_modify_port2) { + ret = nat_rtsp_modify_port2(skb, protoff, ct, + ctinfo, + portoff, + portlen, + rtpport, + rtcpport, + dash, + &delta); + } + } + } + + /* register the RTP ports with ingress QoS classifier */ + pr_debug("\n RTP Port = %d, RTCP Port = %d\n", rtpport, rtcpport); + iqos_add_L4port(IPPROTO_UDP, rtpport, IQOS_ENT_DYN, IQOS_PRIO_HIGH); + iqos_add_L4port(IPPROTO_UDP, rtcpport, IQOS_ENT_DYN, IQOS_PRIO_HIGH); + + if (ret < 0) + goto end; + + if (delta) { + /* Packet length has changed, we need to adjust + * everthing */ + tcpdatalen += delta; + msglen += delta; + msghdrlen += delta; + tplen += delta; + portlen += delta; + + /* Relocate TCP payload pointer */ + tcpdata = skb_header_pointer(skb, + tcpdataoff, + tcpdatalen, + rtsp_buffer); + BUG_ON(tcpdata == NULL); + totaldelta += delta; + } + } + + /* Process special destination=: parameter in + * Transport header. This is not a standard parameter, + * so far, it's only seen in some customers' products. + */ + while(get_next_dest_ipport(tcpdata, tpoff, tplen, + &destoff, &destlen, &dest, + &portoff, &portlen, &rtpport)) { + int ret = 0, delta; + + /* Process the port part */ + if (memcmp(&ct->tuplehash[dir].tuple.src.u3, + &ct->tuplehash[!dir].tuple.dst.u3, + sizeof(ct->tuplehash[dir].tuple.src.u3)) + != 0) { + /* LAN to WAN */ + ret = expect_rtsp_channel(skb, protoff, ct, ctinfo, + portoff, portlen, + rtpport, &delta); + } else { + /* WAN to LAN */ + if ((nat_rtsp_modify_port = rcu_dereference( + nat_rtsp_modify_port_hook))) { + ret = nat_rtsp_modify_port(skb, protoff, ct, + ctinfo, + portoff, + portlen, + rtpport, + &delta); + } + } + + /* register the RTP ports with ingress QoS classifier */ + pr_debug("\n RTP Port = %d\n", rtpport); + iqos_add_L4port(IPPROTO_UDP, rtpport, IQOS_ENT_DYN, IQOS_PRIO_HIGH); + + if (ret < 0) + goto end; + + if (delta) { + /* Packet length has changed, we need to adjust + * everthing */ + tcpdatalen += delta; + msglen += delta; + msghdrlen += delta; + tplen += delta; + portlen += delta; + + /* Relocate TCP payload pointer */ + tcpdata = skb_header_pointer(skb, + tcpdataoff, + tcpdatalen, + rtsp_buffer); + BUG_ON(tcpdata == NULL); + totaldelta += delta; + } + + /* Then the IP part */ + if (dest != ct->tuplehash[dir].tuple.src.u3.ip) + continue; + if ((nat_rtsp_modify_addr = + rcu_dereference(nat_rtsp_modify_addr_hook)) && + ct->status & IPS_NAT_MASK) { + } + /* NAT needed */ + ret = nat_rtsp_modify_addr(skb, protoff, ct, ctinfo, + destoff, destlen, &delta); + if (ret < 0) + goto end; + + if (delta) { + /* Packet length has changed, we need + * to adjust everthing */ + tcpdatalen += delta; + msglen += delta; + msghdrlen += delta; + tplen += delta; + portlen += delta; + + /* Relocate TCP payload pointer */ + tcpdata = skb_header_pointer(skb, tcpdataoff, + tcpdatalen, + rtsp_buffer); + BUG_ON(tcpdata == NULL); + totaldelta += delta; + } + } + + if ((nat_rtsp_modify_addr = + rcu_dereference(nat_rtsp_modify_addr_hook)) && + ct->status & IPS_NAT_MASK) { + destoff = destlen = 0; + while(get_next_destination(tcpdata, tpoff, tplen, + &destoff, &destlen, &dest)) { + int ret, delta; + + if (dest != ct->tuplehash[dir].tuple.src.u3.ip) + continue; + + /* NAT needed */ + ret = nat_rtsp_modify_addr(skb, protoff, ct, ctinfo, + destoff, destlen, + &delta); + if (ret < 0) + goto end; + + if (delta) { + /* Packet length has changed, we need + * to adjust everthing */ + tcpdatalen += delta; + msglen += delta; + msghdrlen += delta; + tplen += delta; + portlen += delta; + + /* Relocate TCP payload pointer */ + tcpdata = skb_header_pointer(skb, + tcpdataoff, + tcpdatalen, + rtsp_buffer); + BUG_ON(tcpdata == NULL); + totaldelta += delta; + } + + } + } + } + nf_ct_seqadj_set(ct, ctinfo, th->seq, totaldelta); + +end: + spin_unlock_bh(&nf_rtsp_lock); + return NF_ACCEPT; +} + +static struct nf_conntrack_helper rtsp[MAX_PORTS]; +static char rtsp_names[MAX_PORTS][sizeof("rtsp-65535")]; +static struct nf_conntrack_expect_policy rtsp_exp_policy; + +/* don't make this __exit, since it's called from __init ! */ +static void nf_conntrack_rtsp_fini(void) +{ + int i; + + for (i = 0; i < ports_c; i++) { + if (rtsp[i].me == NULL) + continue; + + /* unregister the RTSP ports with ingress QoS classifier */ + iqos_rem_L4port( rtsp[i].tuple.dst.protonum, + rtsp[i].tuple.src.u.tcp.port, IQOS_ENT_STAT ); + pr_debug("nf_ct_rtsp: unregistering helper for port %d\n", + ports[i]); + nf_conntrack_helper_unregister(&rtsp[i]); + } + + kfree(rtsp_buffer); +} + +static int __init nf_conntrack_rtsp_init(void) +{ + int i, ret = 0; + char *tmpname; + + rtsp_buffer = kmalloc(4000, GFP_KERNEL); + if (!rtsp_buffer) + return -ENOMEM; + + if (ports_c == 0) + ports[ports_c++] = RTSP_PORT; + + rtsp_exp_policy.max_expected = max_outstanding; + rtsp_exp_policy.timeout = 5 * 60; + for (i = 0; i < ports_c; i++) { + rtsp[i].tuple.src.l3num = PF_INET; + rtsp[i].tuple.src.u.tcp.port = htons(ports[i]); + rtsp[i].tuple.dst.protonum = IPPROTO_TCP; + rtsp[i].data_len = sizeof(struct nf_ct_rtsp_master); + rtsp[i].expect_policy = &rtsp_exp_policy; + rtsp[i].expect_class_max = 1; + rtsp[i].me = THIS_MODULE; + rtsp[i].help = help; + tmpname = &rtsp_names[i][0]; + if (ports[i] == RTSP_PORT) + sprintf(tmpname, "rtsp"); + else + sprintf(tmpname, "rtsp-%d", ports[i]); + strncpy(rtsp[i].name, tmpname, NF_CT_HELPER_NAME_LEN); + + pr_debug("nf_ct_rtsp: registering helper for port %d\n", + ports[i]); + ret = nf_conntrack_helper_register(&rtsp[i]); + if (ret) { + printk("nf_ct_rtsp: failed to register helper " + "for port %d\n", ports[i]); + nf_conntrack_rtsp_fini(); + return ret; + } + + /* register the RTSP ports with ingress QoS classifier */ + iqos_add_L4port( IPPROTO_TCP, ports[i], IQOS_ENT_STAT, IQOS_PRIO_HIGH ); + } + + return 0; +} + +module_init(nf_conntrack_rtsp_init); +module_exit(nf_conntrack_rtsp_fini); +#endif diff -urN linux-4.1.52-orig/net/netfilter/nf_conntrack_sip.c linux-4.1.52/net/netfilter/nf_conntrack_sip.c --- linux-4.1.52-orig/net/netfilter/nf_conntrack_sip.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/netfilter/nf_conntrack_sip.c 2020-02-07 01:47:54.000000000 +0800 @@ -25,6 +25,10 @@ #include #include #include +#if defined(CONFIG_BCM_KF_NETFILTER_SIP) +#include +#include +#endif MODULE_LICENSE("GPL"); MODULE_AUTHOR("Christian Hentschel "); @@ -790,6 +794,19 @@ return 1; } +#if defined(CONFIG_BCM_KF_NETFILTER_SIP) +static void release_conflicting_expect(const struct nf_conn *ct, const struct nf_conntrack_expect *expect, const enum sip_expectation_classes class) +{ + struct nf_conntrack_expect *exp; + struct net *net = nf_ct_net(ct); + exp = __nf_ct_expect_find(net, nf_ct_zone(ct), &expect->tuple); + if (exp && exp->master != ct && + nfct_help(exp->master)->helper == nfct_help(ct)->helper && + exp->class == class) + nf_ct_unexpect_related(exp); +} +#endif /* CONFIG_BCM_KF_NETFILTER_SIP */ + static int refresh_signalling_expectation(struct nf_conn *ct, union nf_inet_addr *addr, u8 proto, __be16 port, @@ -839,6 +856,45 @@ spin_unlock_bh(&nf_conntrack_expect_lock); } +#if defined(CONFIG_BCM_KF_NETFILTER_SIP) +static void bcm_sip_expectfn(struct nf_conn *ct, + struct nf_conntrack_expect *exp) +{ + iqos_add_L4port(IPPROTO_UDP, + ntohs(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.udp.port), + IQOS_ENT_DYN, IQOS_PRIO_HIGH ); + iqos_add_L4port( IPPROTO_UDP, + ntohs(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.udp.port), + IQOS_ENT_DYN, + IQOS_PRIO_HIGH ); + set_bit(IPS_IQOS_BIT, &ct->status); +} +static inline unsigned int bcm_nf_sip_sdp_media(struct sk_buff *skb, unsigned int protoff, + unsigned int dataoff, + const char **dptr, unsigned int *datalen, + struct nf_conntrack_expect *rtp_exp, + struct nf_conntrack_expect *rtcp_exp, + unsigned int mediaoff, + unsigned int medialen, + union nf_inet_addr *rtp_addr) +{ + /* even when NAT is not present we need to call expectfn to add RTP&RTCP + * ports to IQ table + */ + rtp_exp->expectfn = bcm_sip_expectfn; + rtcp_exp->expectfn = bcm_sip_expectfn; + + if (nf_ct_expect_related(rtp_exp) == 0) { + if (nf_ct_expect_related(rtcp_exp) != 0) + nf_ct_unexpect_related(rtp_exp); + else{ + return NF_ACCEPT; + } + } + return NF_DROP; +} +#endif + static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, unsigned int dataoff, const char **dptr, unsigned int *datalen, @@ -899,9 +955,17 @@ (!nf_inet_addr_cmp(&exp->saved_addr, &exp->tuple.dst.u3) || exp->saved_proto.udp.port != exp->tuple.dst.u.udp.port) && ct->status & IPS_NAT_MASK) { +#if defined(CONFIG_BCM_KF_NETFILTER_SIP) + if (exp->saved_addr.ip != 0) { + *daddr = exp->saved_addr; + tuple.dst.u3 = exp->saved_addr; + tuple.dst.u.udp.port = exp->saved_proto.udp.port; + } +#else *daddr = exp->saved_addr; tuple.dst.u3 = exp->saved_addr; tuple.dst.u.udp.port = exp->saved_proto.udp.port; +#endif direct_rtp = 1; } else #endif @@ -943,12 +1007,21 @@ datalen, rtp_exp, rtcp_exp, mediaoff, medialen, daddr); else { +#if defined(CONFIG_BCM_KF_NETFILTER_SIP) + release_conflicting_expect(ct, rtp_exp, class); + release_conflicting_expect(ct, rtcp_exp, class); + ret = bcm_nf_sip_sdp_media(skb, protoff, dataoff, dptr, + datalen, rtp_exp, rtcp_exp, + mediaoff, medialen, daddr); +#else if (nf_ct_expect_related(rtp_exp) == 0) { if (nf_ct_expect_related(rtcp_exp) != 0) nf_ct_unexpect_related(rtp_exp); else ret = NF_ACCEPT; } +#endif + } nf_ct_expect_put(rtcp_exp); err2: @@ -1165,6 +1238,35 @@ { enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); +#if defined(CONFIG_BCM_KF_NETFILTER_SIP) + struct nf_conntrack_helper *helper = nfct_help(ct)->helper; + struct nf_conn *child; + + /* cdrouter_sip_60 */ + list_for_each_entry(child, &ct->derived_connections, derived_list) { + struct nf_conn_help *child_help = nfct_help(child); + + /* Leave signalling children alone, we need to close only media ones. */ + if (child_help != NULL && child_help->helper == helper) + continue; + +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_lock(); + if ((child->blog_key[IP_CT_DIR_ORIGINAL] != BLOG_KEY_FC_INVALID) + || (child->blog_key[IP_CT_DIR_REPLY] != BLOG_KEY_FC_INVALID)) { + /* remove flow from flow cache */ + blog_notify(DESTROY_FLOWTRACK, (void*)child, + (uint32_t)child->blog_key[IP_CT_DIR_ORIGINAL], + (uint32_t)child->blog_key[IP_CT_DIR_REPLY]); + + set_bit(IPS_BLOG_BIT, &child->status); /* Enable conntrack blogging */ + } + blog_unlock(); +#endif + child->derived_timeout = 5*HZ; + nf_ct_refresh(child, skb, 5*HZ); + } +#endif flush_expectations(ct, true); return NF_ACCEPT; @@ -1355,6 +1457,9 @@ SIP_HANDLER("ACK", process_sdp, NULL), SIP_HANDLER("PRACK", process_sdp, process_prack_response), SIP_HANDLER("BYE", process_bye_request, NULL), +#if defined(CONFIG_BCM_KF_NETFILTER_SIP) + SIP_HANDLER("CANCEL", process_bye_request, NULL), /*cdrouter_sip_62*/ +#endif SIP_HANDLER("REGISTER", process_register_request, process_register_response), }; @@ -1625,6 +1730,11 @@ continue; nf_conntrack_helper_unregister(&sip[i][j]); } +#if defined(CONFIG_BCM_KF_NETFILTER_SIP) + /* unregister the SIP ports with ingress QoS classifier */ + iqos_rem_L4port( IPPROTO_UDP, ports[i], IQOS_ENT_STAT ); + iqos_rem_L4port( IPPROTO_TCP, ports[i], IQOS_ENT_STAT ); +#endif } } @@ -1675,9 +1785,15 @@ return ret; } } +#if defined(CONFIG_BCM_KF_NETFILTER_SIP) + /* register the SIP ports with ingress QoS classifier */ + iqos_add_L4port( IPPROTO_UDP, ports[i], IQOS_ENT_STAT, IQOS_PRIO_HIGH ); + iqos_add_L4port( IPPROTO_TCP, ports[i], IQOS_ENT_STAT, IQOS_PRIO_HIGH ); +#endif } return 0; } module_init(nf_conntrack_sip_init); module_exit(nf_conntrack_sip_fini); + diff -urN linux-4.1.52-orig/net/netfilter/nf_conntrack_standalone.c linux-4.1.52/net/netfilter/nf_conntrack_standalone.c --- linux-4.1.52-orig/net/netfilter/nf_conntrack_standalone.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/netfilter/nf_conntrack_standalone.c 2020-02-07 01:47:54.000000000 +0800 @@ -33,6 +33,10 @@ #include #include +#if defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_MODULE) +#include +#endif + MODULE_LICENSE("GPL"); #ifdef CONFIG_NF_CONNTRACK_PROCFS @@ -167,6 +171,7 @@ } #endif + /* return 0 on success, 1 in case of error */ static int ct_seq_show(struct seq_file *s, void *v) { @@ -175,6 +180,10 @@ const struct nf_conntrack_l3proto *l3proto; const struct nf_conntrack_l4proto *l4proto; int ret = 0; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BCM_KF_NETFILTER) && defined(CONFIG_BLOG) + BlogCtTime_t ct_time; + long ctExpiryVal = 0; +#endif NF_CT_ASSERT(ct); if (unlikely(!atomic_inc_not_zero(&ct->ct_general.use))) @@ -190,11 +199,35 @@ NF_CT_ASSERT(l4proto); ret = -ENOSPC; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BCM_KF_NETFILTER) && defined(CONFIG_BLOG) + if (timer_pending(&ct->timeout)) + { + memset(&ct_time, 0, sizeof(ct_time)); + blog_lock(); + if (ct->blog_key[BLOG_PARAM1_DIR_ORIG] != BLOG_KEY_FC_INVALID || + ct->blog_key[BLOG_PARAM1_DIR_REPLY] != BLOG_KEY_FC_INVALID) { + blog_query(QUERY_FLOWTRACK, (void*)ct, + ct->blog_key[BLOG_PARAM1_DIR_ORIG], + ct->blog_key[BLOG_PARAM1_DIR_REPLY], (unsigned long) &ct_time); + + ctExpiryVal = (long)(ct->extra_jiffies)/HZ - ct_time.idle; + + } else + ctExpiryVal = (long)(ct->timeout.expires - jiffies)/HZ; + + blog_unlock(); + } + seq_printf(s, "%-8s %u %-8s %u %ld ", + l3proto->name, nf_ct_l3num(ct), + l4proto->name, nf_ct_protonum(ct), + ctExpiryVal); +#else seq_printf(s, "%-8s %u %-8s %u %ld ", l3proto->name, nf_ct_l3num(ct), l4proto->name, nf_ct_protonum(ct), timer_pending(&ct->timeout) ? (long)(ct->timeout.expires - jiffies)/HZ : 0); +#endif if (l4proto->print_conntrack) l4proto->print_conntrack(s, ct); @@ -235,6 +268,24 @@ ct_show_delta_time(s, ct); +#if defined(CONFIG_BCM_KF_NETFILTER) + if (seq_printf(s, "iqprio=%u ", ct->iq_prio)) + goto release; + + if (seq_printf(s, "swaccel=%u ", ct->sw_accel_flows)) + goto release; + + if (seq_printf(s, "hwaccel=%u ", ct->hw_accel_flows)) + goto release; +#endif + +#if defined(CONFIG_BCM_KF_XT_MATCH_LAYER7) && \ + (defined(CONFIG_NETFILTER_XT_MATCH_LAYER7) || defined(CONFIG_NETFILTER_XT_MATCH_LAYER7_MODULE)) + if(ct->layer7.app_proto && + seq_printf(s, "l7proto=%s ", ct->layer7.app_proto)) + return -ENOSPC; +#endif + seq_printf(s, "use=%u\n", atomic_read(&ct->ct_general.use)); if (seq_has_overflowed(s)) @@ -267,6 +318,125 @@ .release = seq_release_net, }; +#if defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_MODULE) +static void *ct_dpi_seq_start(struct seq_file *seq, loff_t *pos) + __acquires(RCU) +{ + struct ct_iter_state *st = seq->private; + + st->time_now = ktime_to_ns(ktime_get_real()); + rcu_read_lock(); + if (*pos == 0) + return SEQ_START_TOKEN; + + return ct_get_idx(seq, *pos); +} + +static void *ct_dpi_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + if (v == SEQ_START_TOKEN) + return ct_get_idx(s, *pos); + + (*pos)++; + return ct_get_next(s, v); +} + +static void ct_dpi_seq_stop(struct seq_file *s, void *v) + __releases(RCU) +{ + rcu_read_unlock(); +} + +static int ct_dpi_seq_show(struct seq_file *s, void *v) +{ + struct nf_conntrack_tuple_hash *hash; + struct nf_conn *ct; + const struct nf_conntrack_l3proto *l3proto; + const struct nf_conntrack_l4proto *l4proto; + int ret = 0; + + if (v == SEQ_START_TOKEN) { + seq_puts(s, "app_id mac dev_id us_pkt us_byte ds_pkt ds_byte status us_tuple ds_tuple\n"); + return 0; + } + + hash = v; + ct = nf_ct_tuplehash_to_ctrack(hash); + + NF_CT_ASSERT(ct); + if (unlikely(!atomic_inc_not_zero(&ct->ct_general.use))) + return 0; + + /* only print if app & dev exist, and only for DIR_ORIGINAL */ + if (!ct->dpi.app || !ct->dpi.dev || NF_CT_DIRECTION(hash)) + goto release; + + ret = -ENOSPC; + + l3proto = __nf_ct_l3proto_find(nf_ct_l3num(ct)); + NF_CT_ASSERT(l3proto); + l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct)); + NF_CT_ASSERT(l4proto); + + dpi_print_flow(s, ct); + + if (!dpi_ct_init_from_wan(ct)) { + /* LAN-side */ + conntrack_dpi_seq_print_stats(s, ct, IP_CT_DIR_ORIGINAL); + conntrack_dpi_seq_print_stats(s, ct, IP_CT_DIR_REPLY); + } else { + /* WAN-side */ + conntrack_dpi_seq_print_stats(s, ct, IP_CT_DIR_REPLY); + conntrack_dpi_seq_print_stats(s, ct, IP_CT_DIR_ORIGINAL); + } + + seq_printf(s, " %lX ", ct->dpi.flags); + + if (!dpi_ct_init_from_wan(ct)) { + print_tuple(s, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, + l3proto, l4proto); + print_tuple(s, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, + l3proto, l4proto); + } else { + print_tuple(s, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, + l3proto, l4proto); + print_tuple(s, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, + l3proto, l4proto); + } + + seq_puts(s, "\n"); + + if (seq_has_overflowed(s)) + goto release; + + ret = 0; +release: + nf_ct_put(ct); + return ret; +} + +static const struct seq_operations ct_dpi_seq_ops = { + .start = ct_dpi_seq_start, + .next = ct_dpi_seq_next, + .stop = ct_dpi_seq_stop, + .show = ct_dpi_seq_show +}; + +static int ct_dpi_open(struct inode *inode, struct file *file) +{ + return seq_open_net(inode, file, &ct_dpi_seq_ops, + sizeof(struct ct_iter_state)); +} + +static const struct file_operations ct_dpi_file_ops = { + .owner = THIS_MODULE, + .open = ct_dpi_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_net, +}; +#endif /* defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_MODULE) */ + static void *ct_cpu_seq_start(struct seq_file *seq, loff_t *pos) { struct net *net = seq_file_net(seq); @@ -372,8 +542,19 @@ &ct_cpu_seq_fops); if (!pde) goto out_stat_nf_conntrack; +#if defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_MODULE) + pde = proc_create("nf_conntrack_dpi", 0440, net->proc_net, + &ct_dpi_file_ops); + if (!pde) + goto out_nf_conntrack_dpi; +#endif + return 0; +#if defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_MODULE) +out_nf_conntrack_dpi: + remove_proc_entry("nf_conntrack", net->proc_net_stat); +#endif out_stat_nf_conntrack: remove_proc_entry("nf_conntrack", net->proc_net); out_nf_conntrack: @@ -382,6 +563,9 @@ static void nf_conntrack_standalone_fini_proc(struct net *net) { +#if defined(CONFIG_BCM_KF_DPI) && defined(CONFIG_BCM_DPI_MODULE) + remove_proc_entry("nf_conntrack_dpi", net->proc_net); +#endif remove_proc_entry("nf_conntrack", net->proc_net_stat); remove_proc_entry("nf_conntrack", net->proc_net); } diff -urN linux-4.1.52-orig/net/netfilter/nf_dyndscp.c linux-4.1.52/net/netfilter/nf_dyndscp.c --- linux-4.1.52-orig/net/netfilter/nf_dyndscp.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/netfilter/nf_dyndscp.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,770 @@ +#if defined(CONFIG_BCM_KF_NETFILTER) +/* +<:copyright-BRCM:2012:GPL/GPL:standard + + Copyright (c) 2012 Broadcom + All Rights Reserved + +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 (the "GPL"). + +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. + + +A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by +writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + +:> +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_IPV6) +#include +#include +#include +#endif +#include "skb_defines.h" +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#include +#endif + +#if 0 + +#define DEBUG_DSCP(args) printk args +#define DEBUG_DSCP1(args) printk args + +#else + +#define DEBUG_DSCP(args) +#define DEBUG_DSCP1(args) + +#endif + +#define DUMP_TUPLE_IPV4(tp) \ + DEBUG_DSCP(("tuple %p: %u %pI4:%hu %pI4:%hu \n", \ + (tp), (tp)->dst.protonum, \ + &(tp)->src.u3.ip, ntohs((tp)->src.u.all), \ + &(tp)->dst.u3.ip, ntohs((tp)->dst.u.all))) + + +#define DUMP_TUPLE_IPV6(tp) \ + DEBUG_DSCP(("tuple %p: %u %pI6 %hu -> %pI6 %hu \n", \ + (tp), (tp)->dst.protonum, \ + (tp)->src.u3.all, ntohs((tp)->src.u.all), \ + (tp)->dst.u3.all, ntohs((tp)->dst.u.all))) + +#define DYNDSCP_DSCP_MASK 0xfc /* 11111100 */ +#define DYNDSCP_DSCP_SHIFT 2 +#define DYNDSCP_DSCP_MAX 0x3f /* 00111111 */ + +#define DYNDSCP_INITIALIZING 0 +#define DYNDSCP_INHERITED 1 +#define DYNDSCP_SKIP 2 + +#define DYNDSCP_LAN2WAN_DEFAULT_DSCP 0 +#define DYNDSCP_WAN2LAN_DEFAULT_DSCP 0 + +#define DYNDSCP_PROC_TRANSTBL_FILENAME "nf_dyndscp_w2ldscp_transtbl" +#define DSCP_MAPPINGTABLE_MAX_SIZE 64 +#define DYNDSCP_MAX_PROC_WRITE_BUFLEN 64 + +static DEFINE_SPINLOCK(nf_dyndscp_lock); + +static char dyndscp_proc_buffer[DYNDSCP_MAX_PROC_WRITE_BUFLEN]; +static struct proc_dir_entry * dyndscp_proc_file = NULL; + +/* structure used to maintain dscp transmarking table entries*/ + +struct dscpMapping { + uint8_t orig; + uint8_t new; +}; + +/* dscp transmarking table entries*/ +struct transMarkTable { + uint16_t size; + uint16_t used; + struct dscpMapping *dscp; +}; + +static struct transMarkTable transMarkTbl; + +/*finds the dscp mapping and returns new dscp value + * returns DYNDSCP_WAN2LAN_DEFAULT_DSCP if no match */ + +uint8_t getDscpfromTransTbl(uint8_t orig) +{ + int i; + spin_lock_bh(&nf_dyndscp_lock); + for(i=0; i < transMarkTbl.size; i++) + { + if(transMarkTbl.dscp[i].orig == orig) + { + spin_unlock_bh(&nf_dyndscp_lock); + return transMarkTbl.dscp[i].new; + } + + } + + spin_unlock_bh(&nf_dyndscp_lock); + return DYNDSCP_WAN2LAN_DEFAULT_DSCP; +} + +/* Adds a new DSCP mapping,(over writes the existing mapping for + * origDscp value if present) + * an entry is free if both orig and new are 0 */ +int addDscpinTransTbl(uint8_t origDscp, uint8_t newDscp) +{ + int i; + + spin_lock_bh(&nf_dyndscp_lock); + /*replace entry */ + for(i=0; i < transMarkTbl.size; i++) + { + if(transMarkTbl.dscp[i].orig == origDscp) + { + + if((transMarkTbl.dscp[i].orig == 0) && (transMarkTbl.dscp[i].new == 0 ) &&(newDscp != 0 )) + transMarkTbl.used++;/* new entry special case as intially entries are set to 0*/ + + if((transMarkTbl.dscp[i].orig == 0) && (transMarkTbl.dscp[i].new != 0 ) &&(newDscp == 0 )) + transMarkTbl.used--;/* remove entry special case as intially entries are set to 0*/ + + transMarkTbl.dscp[i].new = newDscp; + + + spin_unlock_bh(&nf_dyndscp_lock); + return 0; + } + } + + /*new entry */ + for(i=0; i < transMarkTbl.size; i++) + { + if((transMarkTbl.dscp[i].orig == 0) && (transMarkTbl.dscp[i].new == 0 )) + { + transMarkTbl.dscp[i].orig = origDscp; + transMarkTbl.dscp[i].new = newDscp; + transMarkTbl.used++; + spin_unlock_bh(&nf_dyndscp_lock); + return 0; + } + } + + spin_unlock_bh(&nf_dyndscp_lock); + /*table full */ + printk(KERN_ERR "%s:Transmark Table is Full\n",__FUNCTION__); + return -1; +} + +/* delete a DSCP mapping from trans table */ +int delDscpinTransTbl(uint8_t origDscp) +{ + int i; + + spin_lock_bh(&nf_dyndscp_lock); + for(i=0; i < transMarkTbl.size; i++) + { + if((transMarkTbl.dscp[i].orig == origDscp) ) + { + transMarkTbl.dscp[i].orig = 0; + transMarkTbl.dscp[i].new = 0; + transMarkTbl.used--; + spin_unlock_bh(&nf_dyndscp_lock); + return 0; + } + } + + printk(KERN_ERR "%s: Entry not found in Transmark Table\n",__FUNCTION__); + spin_unlock_bh(&nf_dyndscp_lock); + return -1; +} + +#if defined(CONFIG_BCM_KF_WANDEV) +/*for setting interface's IFF_WANDEV flag ;LAB testing purpose */ +int setWanIfFlag(char *name) +{ + struct net_device *dev = NULL; + + dev = dev_get_by_name(&init_net, name); + if(dev){ + printk(KERN_INFO "setting %s IFF_WANDEV flag\n",name); + dev->priv_flags |= IFF_WANDEV; + return 0; + } else { + printk(KERN_ERR "interface %s not found\n",name); + return -1; + } +} +#endif + +/* Entry point into dyndscp module at pre-routing + * this function is the core engine of this module + * */ +static unsigned int nf_dyndscp_in(const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct nf_hook_state *state) +{ + struct nf_conn *ct; + enum ip_conntrack_info ctinfo; + u_int8_t pktDscp; + + ct = nf_ct_get(skb, &ctinfo); + + DEBUG_DSCP1((" %s: seen packet \n",__FUNCTION__)); + + if(!ct) { + DEBUG_DSCP1((KERN_INFO " %s: seen packet with out flow\n",__FUNCTION__)); + return NF_ACCEPT; + } + + if(ct->dyndscp.status == DYNDSCP_INHERITED) { + DEBUG_DSCP1((KERN_INFO "%s: changing tos in pkt to %x \n",__FUNCTION__, + ct->dyndscp.dscp[CTINFO2DIR(ctinfo)])); + + if (!skb_make_writable(skb, sizeof(struct iphdr))) + return NF_DROP; + + ipv4_change_dsfield(ip_hdr(skb), (__u8)(~DYNDSCP_DSCP_MASK), + ct->dyndscp.dscp[CTINFO2DIR(ctinfo)] << DYNDSCP_DSCP_SHIFT); + + } else if(ct->dyndscp.status == DYNDSCP_INITIALIZING) { + + if (ct == &nf_conntrack_untracked) { + + ct->dyndscp.status = DYNDSCP_SKIP; + DEBUG_DSCP((KERN_INFO "skipping tos mangling for untracked flow\n")); + return NF_ACCEPT; + } + + /*for now we change DSCP only for TCP/UDP */ + if(!((ip_hdr(skb)->protocol == IPPROTO_UDP) || (ip_hdr(skb)->protocol == IPPROTO_TCP))){ + ct->dyndscp.status = DYNDSCP_SKIP; + return NF_ACCEPT; + } + + /*TODO: should we skip broadcast packets ?? */ + + pktDscp = ipv4_get_dsfield(ip_hdr(skb)) >> DYNDSCP_DSCP_SHIFT; + + if(!SKBMARK_GET_IFFWAN_MARK(skb->mark)) { + /* LAN -> WAN packet */ + + DEBUG_DSCP1((" %s: initializing case lan->wan packet \n",__FUNCTION__)); + + if(pktDscp != DYNDSCP_LAN2WAN_DEFAULT_DSCP) { + + if (!skb_make_writable(skb, sizeof(struct iphdr))) + return NF_DROP; + + ipv4_change_dsfield(ip_hdr(skb), (__u8)(~DYNDSCP_DSCP_MASK), + DYNDSCP_LAN2WAN_DEFAULT_DSCP << DYNDSCP_DSCP_SHIFT); + } + + } else { + /* WAN -> LAN packet */ + + DEBUG_DSCP1(("%s: initializing case wan->lan packet \n",__FUNCTION__)); + if (!skb_make_writable(skb, sizeof(struct iphdr))) + return NF_DROP; + + /* inherit tos from packet */ + if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) { + /*connection intiated from WAN */ + ct->dyndscp.dscp[IP_CT_DIR_REPLY] = pktDscp; + ct->dyndscp.dscp[IP_CT_DIR_ORIGINAL] = getDscpfromTransTbl(pktDscp); + + ipv4_change_dsfield(ip_hdr(skb), (__u8)(~DYNDSCP_DSCP_MASK), + ct->dyndscp.dscp[IP_CT_DIR_ORIGINAL] << DYNDSCP_DSCP_SHIFT); + } else { + /*connection intiated from LAN or LOCAL*/ + ct->dyndscp.dscp[IP_CT_DIR_ORIGINAL] = pktDscp; + ct->dyndscp.dscp[IP_CT_DIR_REPLY] = getDscpfromTransTbl(pktDscp); + + ipv4_change_dsfield(ip_hdr(skb), (__u8)(~DYNDSCP_DSCP_MASK), + ct->dyndscp.dscp[IP_CT_DIR_REPLY] << DYNDSCP_DSCP_SHIFT); +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + /* Notify associated flows in flow cache, so they will relearn with + * new tos values this is needed only for UDP, as TCP flows + * are created only when packet are seen from both directions + */ + + if(ip_hdr(skb)->protocol == IPPROTO_UDP){ + blog_lock(); + blog_notify(DYNAMIC_DSCP_EVENT, (void*)ct, 0, 0); + blog_unlock(); + + DEBUG_DSCP(("%s:blog_notify:DYNAMIC_DSCP_EVENT for\n",__FUNCTION__)); + DUMP_TUPLE_IPV4(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + } +#endif + } + + /*update tos status in nf_conn */ + ct->dyndscp.status = DYNDSCP_INHERITED; + + DEBUG_DSCP((KERN_INFO "dynamic tos values(%X, %X) inherited forflow\n", + ct->dyndscp.dscp[IP_CT_DIR_ORIGINAL], + ct->dyndscp.dscp[IP_CT_DIR_REPLY])); + DUMP_TUPLE_IPV4(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + } + + } else if(ct->dyndscp.status == DYNDSCP_SKIP){ + /*handle untracked connections */ + + } else { + + printk(KERN_WARNING " %s :dyndscp unknown status(%d) for flow\n", + __FUNCTION__, ct->dyndscp.status); + DUMP_TUPLE_IPV4(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + } + + return NF_ACCEPT; +} + +static unsigned int nf_dyndscp_local(const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct nf_hook_state *state) +{ + struct nf_conn *ct; + enum ip_conntrack_info ctinfo; + + /* root is playing with raw sockets. */ + if (skb->len < sizeof(struct iphdr) + || ip_hdr(skb)->ihl * 4 < sizeof(struct iphdr)) { + if (net_ratelimit()) + DEBUG_DSCP((KERN_INFO "nf_dyndscp_local: happy cracking.\n")); + return NF_ACCEPT; + } + + ct = nf_ct_get(skb, &ctinfo); + + if(!ct){ + DEBUG_DSCP((KERN_INFO "%s: seen packet with out flow\n",__FUNCTION__)); + return NF_ACCEPT; + } + + if(ct->dyndscp.status == DYNDSCP_INHERITED) { + + if (!skb_make_writable(skb, sizeof(struct iphdr))) + return NF_DROP; + + /* LOCAL -> WAN packet */ + ipv4_change_dsfield(ip_hdr(skb), (__u8)(~DYNDSCP_DSCP_MASK), + ct->dyndscp.dscp[CTINFO2DIR(ctinfo)] << DYNDSCP_DSCP_SHIFT); + + } else if(ct->dyndscp.status == DYNDSCP_INITIALIZING) { + + if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) { + /*this happens only with LAN <-> LOCAL so just skip */ + ct->dyndscp.status = DYNDSCP_SKIP; + } + + } + + return NF_ACCEPT; +} + +#if defined(CONFIG_IPV6) +/* Entry point into dyndscp module at pre-routing + * this function is the core engine of this module + * */ +static unsigned int nf_dyndscp_in6(const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct nf_hook_state *state) +{ + struct nf_conn *ct; + enum ip_conntrack_info ctinfo; + u_int8_t pktDscp; + + ct = nf_ct_get(skb, &ctinfo); + + DEBUG_DSCP1((" %s: seen packet \n",__FUNCTION__)); + + if(!ct) { + DEBUG_DSCP1((KERN_INFO " %s: seen packet with out flow\n",__FUNCTION__)); + return NF_ACCEPT; + } + + if(ct->dyndscp.status == DYNDSCP_INHERITED) { + DEBUG_DSCP1((KERN_INFO "%s: changing tos in pkt to %x \n",__FUNCTION__, + ct->dyndscp.dscp[CTINFO2DIR(ctinfo)])); + + if (!skb_make_writable(skb, sizeof(struct ipv6hdr))) + return NF_DROP; + + ipv6_change_dsfield(ipv6_hdr(skb), (__u8)(~DYNDSCP_DSCP_MASK), + ct->dyndscp.dscp[CTINFO2DIR(ctinfo)] << DYNDSCP_DSCP_SHIFT); + + } else if(ct->dyndscp.status == DYNDSCP_INITIALIZING) { + + if (ct == &nf_conntrack_untracked) { + + ct->dyndscp.status = DYNDSCP_SKIP; + DEBUG_DSCP((KERN_INFO "%s:skipping tos mangling for untracked flow\n",__FUNCTION__)); + return NF_ACCEPT; + } + + /*for now we change DSCP only for TCP/UDP */ + if(!((ipv6_hdr(skb)->nexthdr == IPPROTO_UDP) || (ipv6_hdr(skb)->nexthdr == IPPROTO_TCP))){ + ct->dyndscp.status = DYNDSCP_SKIP; + return NF_ACCEPT; + } + + /*TODO: should we skip broadcast packets ?? */ + + + pktDscp = ipv6_get_dsfield(ipv6_hdr(skb)) >> DYNDSCP_DSCP_SHIFT; + + if(!SKBMARK_GET_IFFWAN_MARK(skb->mark)) { + /* LAN -> WAN packet */ + + DEBUG_DSCP1((" %s: initializing case lan->wan packet \n",__FUNCTION__)); + + if(pktDscp != DYNDSCP_LAN2WAN_DEFAULT_DSCP) { + + if (!skb_make_writable(skb, sizeof(struct ipv6hdr))) + return NF_DROP; + + ipv6_change_dsfield(ipv6_hdr(skb), (__u8)(~DYNDSCP_DSCP_MASK), + DYNDSCP_LAN2WAN_DEFAULT_DSCP << DYNDSCP_DSCP_SHIFT); + } + + } else { + /* WAN -> LAN packet */ + + DEBUG_DSCP1(("%s: initializing case wan->lan packet \n",__FUNCTION__)); + if (!skb_make_writable(skb, sizeof(struct ipv6hdr))) + return NF_DROP; + + /* inherit tos from packet */ + if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) { + /*connection intiated from WAN */ + ct->dyndscp.dscp[IP_CT_DIR_REPLY] = pktDscp; + ct->dyndscp.dscp[IP_CT_DIR_ORIGINAL] = getDscpfromTransTbl(pktDscp); + + ipv6_change_dsfield(ipv6_hdr(skb), (__u8)(~DYNDSCP_DSCP_MASK), + ct->dyndscp.dscp[IP_CT_DIR_ORIGINAL] << DYNDSCP_DSCP_SHIFT); + } else { + /*connection intiated from LAN or LOCAL*/ + ct->dyndscp.dscp[IP_CT_DIR_ORIGINAL] = pktDscp; + ct->dyndscp.dscp[IP_CT_DIR_REPLY] = getDscpfromTransTbl(pktDscp); + + ipv6_change_dsfield(ipv6_hdr(skb), (__u8)(~DYNDSCP_DSCP_MASK), + ct->dyndscp.dscp[IP_CT_DIR_REPLY] << DYNDSCP_DSCP_SHIFT); +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + /* Notify associated flows in flow cache, so they will relearn with + * new tos values this is needed only for UDP, as TCP flows + * are created only when packet are seen from both directions + */ + + if(ipv6_hdr(skb)->nexthdr == IPPROTO_UDP){ + + blog_lock(); + blog_notify(DYNAMIC_DSCP_EVENT, (void*)ct, 0, 0); + blog_unlock(); + + DEBUG_DSCP(("%s:blog_notify:DYNAMIC_DSCP_EVENT for\n",__FUNCTION__)); + DUMP_TUPLE_IPV6(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + } +#endif + } + + /*update tos status in nf_conn */ + ct->dyndscp.status = DYNDSCP_INHERITED; + + DEBUG_DSCP((KERN_INFO "dynamic tos values(%X, %X) inherited forflow\n", + ct->dyndscp.dscp[IP_CT_DIR_ORIGINAL], + ct->dyndscp.dscp[IP_CT_DIR_REPLY])); + DUMP_TUPLE_IPV6(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + } + + } else if(ct->dyndscp.status == DYNDSCP_SKIP){ + /*handle untracked connections */ + + } else { + + printk(KERN_WARNING " %s :dyndscp unknown status(%d) for flow\n", + __FUNCTION__, ct->dyndscp.status); + DUMP_TUPLE_IPV6(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + } + + return NF_ACCEPT; +} + +static unsigned int nf_dyndscp_local6(const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct nf_hook_state *state) +{ + struct nf_conn *ct; + enum ip_conntrack_info ctinfo; + + /* root is playing with raw sockets. */ + if (skb->len < sizeof(struct ipv6hdr)){ + if (net_ratelimit()) + DEBUG_DSCP((KERN_INFO "nf_dyndscp_local6: happy cracking.\n")); + return NF_ACCEPT; + } + + ct = nf_ct_get(skb, &ctinfo); + + if(!ct){ + DEBUG_DSCP((KERN_INFO "%s: seen packet with out flow\n",__FUNCTION__)); + return NF_ACCEPT; + } + + if(ct->dyndscp.status == DYNDSCP_INHERITED) { + + if (!skb_make_writable(skb, sizeof(struct ipv6hdr))) + return NF_DROP; + + /* LOCAL -> WAN packet */ + ipv6_change_dsfield(ipv6_hdr(skb), (__u8)(~DYNDSCP_DSCP_MASK), + ct->dyndscp.dscp[CTINFO2DIR(ctinfo)] << DYNDSCP_DSCP_SHIFT); + + } else if(ct->dyndscp.status == DYNDSCP_INITIALIZING) { + + if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) { + /*this happens only with LAN <-> LOCAL so just skip */ + ct->dyndscp.status = DYNDSCP_SKIP; + } + + } + + return NF_ACCEPT; +} +#endif + +static struct nf_hook_ops nf_dyndscp_ops[] = { + { + .hook = nf_dyndscp_in, + .owner = THIS_MODULE, + .pf = PF_INET, + .hooknum = NF_INET_PRE_ROUTING, + .priority = NF_IP_PRI_MANGLE - 10,/*pre routing do it before mangle table */ + }, + { + .hook = nf_dyndscp_local, + .owner = THIS_MODULE, + .pf = PF_INET, + .hooknum = NF_INET_LOCAL_OUT, + .priority = NF_IP_PRI_MANGLE + 10,/*local out do it after mangle table */ + + }, +#if defined(CONFIG_IPV6) + { + .hook = nf_dyndscp_in6, + .owner = THIS_MODULE, + .pf = PF_INET6, + .hooknum = NF_INET_PRE_ROUTING, + .priority = NF_IP6_PRI_MANGLE - 10,/*pre routing do it before mangle table */ + }, + { + .hook = nf_dyndscp_local6, + .owner = THIS_MODULE, + .pf = PF_INET6, + .hooknum = NF_INET_LOCAL_OUT, + .priority = NF_IP6_PRI_MANGLE + 10,/*local out do it after mangle table */ + + }, +#endif +}; + +/* proc interface functions for configuring/reading transmark table + * from userspace + * */ + +static void *dyndscp_seq_start(struct seq_file *seq, loff_t *pos) +{ + if(*pos > transMarkTbl.size) + return NULL; + + return *pos ? pos : SEQ_START_TOKEN; +} + +static void *dyndscp_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + ++(*pos); + if(*pos > transMarkTbl.size) + return NULL; + + return &transMarkTbl.dscp[(*pos)-1]; +} + +static void dyndscp_seq_stop(struct seq_file *seq, void *v) +{ + return; +} + +static int dyndscp_seq_show(struct seq_file *seq, void *v) +{ + if (v == SEQ_START_TOKEN){ + seq_printf(seq,"WANDSCP\t-->\tLANDSCP Max num entries:%d," + "Current num Entries:%d\n", + transMarkTbl.size, transMarkTbl.used); + } else { + struct dscpMapping *tos = (struct dscpMapping *)v; + if((tos->orig !=0) && (tos->new !=0))/*show only used entries*/ + seq_printf(seq, "%02x\t \t%02x\n",tos->orig,tos->new); + } + return 0; +} + +static struct seq_operations dyndscp_seq_ops = { + .start = dyndscp_seq_start, + .next = dyndscp_seq_next, + .stop = dyndscp_seq_stop, + .show = dyndscp_seq_show, +}; + +int nf_dyndscp_proc_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &dyndscp_seq_ops); +} + + +static ssize_t nf_dyndscp_proc_write(struct file *file, const char *buffer, + size_t len, loff_t *offset) +{ + uint8_t origDscp, newDscp; + char wanIfname[32]; + + if(len > DYNDSCP_MAX_PROC_WRITE_BUFLEN) + { + printk(KERN_ALERT "%s: User datalen > max kernel buffer len=%zu\n", + __FUNCTION__, len); + return -EFAULT; + } + + if ( copy_from_user(dyndscp_proc_buffer, buffer, len) ) + { + printk(KERN_ALERT "%s copy_from_user failure.\n", __FUNCTION__ ); + //kfree( kbuffer ); + return -EFAULT; + } + + DEBUG_DSCP((KERN_INFO "Applying %u bytes configuration\n", len)); + + if(sscanf(dyndscp_proc_buffer,"add %hhi %hhi",&origDscp, &newDscp)) { + if(addDscpinTransTbl(origDscp,newDscp) < 0) + return -EFAULT; + } else if(sscanf(dyndscp_proc_buffer,"delete %hhi",&origDscp)) { + if(delDscpinTransTbl(origDscp) < 0) + return -EFAULT; + } +#if defined(CONFIG_BCM_KF_WANDEV) + else if(sscanf(dyndscp_proc_buffer,"setwanif %s", wanIfname)) { + if(setWanIfFlag(wanIfname) < 0) + return -EFAULT; + } +#endif + else { + printk(KERN_ALERT " unknown command/syntax in %s .\n", __FUNCTION__ ); + printk(KERN_ALERT "use 'add' or 'delete' commands Ex: \n"); + printk(KERN_ALERT "add origDscp newDscp >/proc/.../.. \n"); + printk(KERN_ALERT "delete origDscp >/proc/.../.. \n"); + return -EFAULT; + } + + return len; +} + +static struct file_operations dyndscp_proc_fops = { + .owner = THIS_MODULE, + .open = nf_dyndscp_proc_open, + .read = seq_read, + .write = nf_dyndscp_proc_write, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int __net_init nf_dyndscp_proc_init(struct net *net) +{ + dyndscp_proc_file = proc_create(DYNDSCP_PROC_TRANSTBL_FILENAME, S_IFREG|S_IRUGO|S_IWUSR, net->nf.proc_netfilter, &dyndscp_proc_fops); + if ( dyndscp_proc_file == (struct proc_dir_entry *)NULL ) + { + printk(KERN_ALERT "Error: Could not initialize /proc/net/netfilter/%s\n", + DYNDSCP_PROC_TRANSTBL_FILENAME); + return -ENOMEM; + } + proc_set_size(dyndscp_proc_file, 80); + proc_set_user(dyndscp_proc_file, KUIDT_INIT(0), KGIDT_INIT(0)); + + printk(KERN_INFO "/proc/net/netfilter/%s created\n", DYNDSCP_PROC_TRANSTBL_FILENAME); + + return 0; /* success */ +} + +static void __net_exit nf_dyndscp_proc_fini(struct net *net) +{ + remove_proc_entry(DYNDSCP_PROC_TRANSTBL_FILENAME, net->proc_net); + printk(KERN_INFO "/proc/net/netfilter/%s removed\n", DYNDSCP_PROC_TRANSTBL_FILENAME); +} + +static struct pernet_operations nf_dyndscp_net_ops = { + .init = nf_dyndscp_proc_init, + .exit = nf_dyndscp_proc_fini, +}; + +static int __init nf_dyndscp_init(void) +{ + int ret = 0; + + need_conntrack(); + + transMarkTbl.size = DSCP_MAPPINGTABLE_MAX_SIZE; + transMarkTbl.dscp = kmalloc((transMarkTbl.size * sizeof(struct dscpMapping)), + GFP_KERNEL); + memset(transMarkTbl.dscp, 0, (transMarkTbl.size * sizeof(struct dscpMapping))); + + ret = nf_register_hooks(nf_dyndscp_ops, + ARRAY_SIZE(nf_dyndscp_ops)); + if (ret < 0) { + printk("nf_dyndscp: can't register hooks.\n"); + goto cleanup_tbl; + } +#if defined(CONFIG_PROC_FS) + ret = register_pernet_subsys(&nf_dyndscp_net_ops); + if (ret < 0) + goto cleanup_hooks; + + return ret; + +cleanup_hooks: + nf_unregister_hooks(nf_dyndscp_ops, ARRAY_SIZE(nf_dyndscp_ops)); +#endif +cleanup_tbl: + return ret; +} + +static void __exit nf_dyndscp_fini(void) +{ +#if defined(CONFIG_PROC_FS) + unregister_pernet_subsys(&nf_dyndscp_net_ops); +#endif + nf_unregister_hooks(nf_dyndscp_ops, ARRAY_SIZE(nf_dyndscp_ops)); +} + +MODULE_AUTHOR("broadcom.com"); +MODULE_DESCRIPTION("DSCP Inheritance from WAN"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("nf_dyndscp-" __stringify(AF_INET)); +MODULE_ALIAS("nf_dyndscp"); + +module_init(nf_dyndscp_init); +module_exit(nf_dyndscp_fini); +#endif diff -urN linux-4.1.52-orig/net/netfilter/nf_nat_sip.c linux-4.1.52/net/netfilter/nf_nat_sip.c --- linux-4.1.52-orig/net/netfilter/nf_nat_sip.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/netfilter/nf_nat_sip.c 2020-02-07 01:47:54.000000000 +0800 @@ -23,12 +23,21 @@ #include #include +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) +#include +#endif /* CONFIG_BCM_RUNNER */ +#endif /* CONFIG_BCM_KF_RUNNER */ + +#if defined(CONFIG_BCM_KF_NETFILTER_SIP) +#include +#endif + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Christian Hentschel "); MODULE_DESCRIPTION("SIP NAT helper"); MODULE_ALIAS("ip_nat_sip"); - static unsigned int mangle_packet(struct sk_buff *skb, unsigned int protoff, unsigned int dataoff, const char **dptr, unsigned int *datalen, @@ -336,6 +345,22 @@ = ct->master->tuplehash[!exp->dir].tuple.dst.u3; nf_nat_setup_info(ct, &range, NF_NAT_MANIP_SRC); } +#if defined(CONFIG_BCM_KF_NETFILTER_SIP) + + /* + * added iqos here + */ + + iqos_add_L4port(IPPROTO_UDP, + ntohs(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.udp.port), + IQOS_ENT_DYN, IQOS_PRIO_HIGH ); + iqos_add_L4port( IPPROTO_UDP, + ntohs(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.udp.port), + IQOS_ENT_DYN, + IQOS_PRIO_HIGH ); + + set_bit(IPS_IQOS_BIT, &ct->status); +#endif } static unsigned int nf_nat_sip_expect(struct sk_buff *skb, unsigned int protoff, @@ -609,6 +634,12 @@ goto err2; } +#if defined(CONFIG_BCM_KF_RUNNER) +#if defined(CONFIG_BCM_RDPA) || defined(CONFIG_BCM_RDPA_MODULE) + BL_OPS(net_ipv4_netfilter_nf_nat_sip(ct, port, dir)); +#endif /* CONFIG_BCM_RUNNER */ +#endif /* CONFIG_BCM_KF_RUNNER */ + return NF_ACCEPT; err2: diff -urN linux-4.1.52-orig/net/netfilter/xt_blog.c linux-4.1.52/net/netfilter/xt_blog.c --- linux-4.1.52-orig/net/netfilter/xt_blog.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/netfilter/xt_blog.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,82 @@ +#if defined(CONFIG_BCM_KF_NETFILTER) && defined(CONFIG_BCM_KF_BLOG) +/* +* Copyright (c) 2003-2018 Broadcom Corporation +* All Rights Reserved +* +<:label-BRCM:2018:DUAL/GPL:standard + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + +#include +#include + +#include +#include + +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Broadcom"); +MODULE_DESCRIPTION("iptables blog match"); +MODULE_ALIAS("ipt_blog"); +MODULE_ALIAS("ip6t_blog"); + +static bool +blog_mt(const struct sk_buff *skb, struct xt_action_param *par) +{ + const struct xt_blog *info = par->matchinfo; + + if (skb->blog_p) { + if (info->tcp_pure_ack) + { + return (skb->blog_p->ack_done || skb->blog_p->key.tcp_pure_ack) ^ + info->invert; + } + } + + return false; +} + +static struct xt_match blog_mt_reg __read_mostly = { + .name = "blog", + .revision = 0, + .family = NFPROTO_UNSPEC, + .match = blog_mt, + .matchsize = sizeof(struct xt_blog), + .me = THIS_MODULE, +}; + +static int __init blog_mt_init(void) +{ + return xt_register_match(&blog_mt_reg); +} + +static void __exit blog_mt_exit(void) +{ + xt_unregister_match(&blog_mt_reg); +} + +module_init(blog_mt_init); +module_exit(blog_mt_exit); +#endif diff -urN linux-4.1.52-orig/net/netfilter/xt_CLASSIFY.c linux-4.1.52/net/netfilter/xt_CLASSIFY.c --- linux-4.1.52-orig/net/netfilter/xt_CLASSIFY.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/netfilter/xt_CLASSIFY.c 2020-02-07 01:47:54.000000000 +0800 @@ -33,6 +33,12 @@ { const struct xt_classify_target_info *clinfo = par->targinfo; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG_FEATURE) + skb->ipt_check |= IPT_TARGET_CLASSIFY; + if ( skb->ipt_check & IPT_TARGET_CHECK ) + return XT_CONTINUE; +#endif + skb->priority = clinfo->priority; return XT_CONTINUE; } diff -urN linux-4.1.52-orig/net/netfilter/xt_CT.c linux-4.1.52/net/netfilter/xt_CT.c --- linux-4.1.52-orig/net/netfilter/xt_CT.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/netfilter/xt_CT.c 2020-02-07 01:47:54.000000000 +0800 @@ -203,7 +203,11 @@ goto err1; memset(&t, 0, sizeof(t)); +#if defined(CONFIG_BCM_KF_NETFILTER) + ct = nf_conntrack_alloc(par->net, info->zone, NULL, &t, &t, GFP_KERNEL); +#else ct = nf_conntrack_alloc(par->net, info->zone, &t, &t, GFP_KERNEL); +#endif ret = PTR_ERR(ct); if (IS_ERR(ct)) goto err2; @@ -380,6 +384,11 @@ static unsigned int notrack_tg(struct sk_buff *skb, const struct xt_action_param *par) { +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG_FEATURE) + skb->ipt_check |= IPT_TARGET_NOTRACK; + if ( skb->ipt_check & IPT_TARGET_CHECK ) + return XT_CONTINUE; +#endif /* Previously seen (loopback)? Ignore. */ if (skb->nfct != NULL) return XT_CONTINUE; diff -urN linux-4.1.52-orig/net/netfilter/xt_DC.c linux-4.1.52/net/netfilter/xt_DC.c --- linux-4.1.52-orig/net/netfilter/xt_DC.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/netfilter/xt_DC.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,966 @@ +#if defined(CONFIG_BCM_KF_XT_TARGET_DC) +/* +* published by the free software foundation +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Broadcom"); +MODULE_DESCRIPTION("ddc: dpi data collection module"); + +//#define DDC_DEBUG +struct proc_dir_entry *proc_dpi_dir = NULL; +struct proc_dir_entry *proc_dpi_entry = NULL; +#define PROC_DPI_FILE_SZ (1024) +#define DPI_DIRECTORY "dpi" +#define DPI_PROC_FILE_NAME "http" +#define DPI_INFO_BUF_LEN (1024) +static char* pdpi_buf = NULL; +static unsigned int total_bytes = 0; + +//#define round_up(x, y) (((x) + (y) - 1) & ~((y) - 1)) + +typedef enum +{ + DPI_PROTO_INVALID = 0, + DPI_PROTO_HTTP, + DPI_PROTO_FTP, + DPI_PROTO_POP3, + DPI_PROTO_TELNET, + DPI_PROTO_MAX, +}DPI_PROTOCOL; + +#define MAX_HOST_LENGTH 64 +#define MAX_SEARCH_KEY_LEN 128 +static char host[MAX_HOST_LENGTH] = {0}; +//static char referer[MAX_HOST_LENGTH] = {0}; +static char search_key[MAX_SEARCH_KEY_LEN] = {0}; + +//static char http_ver[16] ={0}; +static char http_date[48] = {0}; +static char http_charset[16] = {0}; + +#define PARSE_DATE (1<<0) +#define PARSE_CHARSET (1<<1) +#define DATE_VALID (1 <<16) +#define CHARSET_VALID (1 <<17) + +static unsigned int bparse_flag = 0; + +typedef enum +{ + METHOD_GET, + METHOD_POST, + METHOD_HEAD, + METHOD_PUT, + METHOD_DELETE, + METHOD_TRACE, + METHOD_OPTIONS, + METHOD_CONNECT, + METHOD_PATCH, + METHOD_MAX, +}http_method; + +struct dpi_packet_info +{ + char * ip_hdr; + char * tcp_hdr; + char * http_hdr; + char * host; + char * referer; + char * key_end; //pointer to the last character of searching key + http_method method; + int src_ptn_idx; +}; + +typedef enum +{ + SESSION_STATE_EMPTY, + SESSION_STATE_NEW, + SESSION_STATE_CONFIRMED, + SESSION_STATE_SEARCHING, + SESSION_STATE_MAX, +}session_state; + +struct http_session +{ + session_state state; + int index; //tell the updator the index of the session + int update_cnt; //record how many http packet comes + char * keyword; + //bool record_key; + int keycnt; + char host_name[MAX_HOST_LENGTH]; + // add member later: mac, first access time, when to leave, and so on +}; + +struct srch_ptn +{ + int ptn_len; + char * ptn; +}; + +static struct srch_ptn wld_ptn[]= //for ns?, i? and v? +{ + {5, "word="}, + {0, NULL}, +}; + +static struct srch_ptn search_ptn[]= //search? +{ + {2, "q="}, + {5, "word="}, + {4, "key="}, + {0, NULL}, +}; + +static struct srch_ptn sq[]= //s? +{ + {3, "wd="}, + {0, NULL}, +}; + +static struct srch_ptn kw[]= //s? +{ + {3, "kw="}, + {0, NULL}, +}; + + +static struct srch_ptn q[]= //? +{ + {2, "q="}, + {0, NULL}, +}; + +struct search_mkptn +{ + int marklen; + char* mark; + struct srch_ptn *wdp; +}; +/* +to make it simple, only 2. one is active page, the other is inactive +*/ +#define SESSION_POOL_MAX 2 +#define UPDATE_CNT_THRESHOLD 4 + +static struct dpi_packet_info pkt_info; +//static struct http_session session[SESSION_POOL_MAX]; +//static int active = 0; +//static int last_update = 0; + +const char* search_engine[] = +{ + "www.baidu.com", + "www.google.com", + "www.google.com.hk", + NULL, +}; + +//put in right sequence! +const struct search_mkptn search_pat[] = //profiled search pattern: mark and pattern pairs +{ + {7, "search?", search_ptn}, + {2, "s?", sq}, + {2, "f?", kw}, + {2, "v?", wld_ptn}, + {2, "i?", wld_ptn}, + {2, "ns?", wld_ptn}, + {3, "?q=", q}, + {5, "word?", wld_ptn}, +}; + +#define SEARCH_PATTERN_NBR (sizeof(search_pat)/sizeof(struct search_mkptn)) + +typedef enum +{ + RECORD_PCONLINE = 1, + RECORD_GATEWAY_STATE, + RECORD_FLOW, + RECORD_NETWORK_PERF, + RECORD_URL, + RECORD_MAIL, + RECORD_APP, + RECORD_MAX, +}record_type; + +typedef enum +{ + ACTION_WEB, + ACTION_SEARCH, + ACTION_MAX, +}action_type; + +typedef enum +{ + CHARSET_INVALID, + CHARSET_UTF_8, + CHARSET_GBK, + CHARSET_GB2312, + //add other members + CHARSET_MAX, +}charset; + +struct host_key_record +{ + /* PC online =1; + * gateway state + * flow + * network performance + * url + * mail + * app + */ + unsigned short rcdtype; + unsigned short item_len; //dont change the order of first two member + unsigned char actiontype; /*web =1, search =2*/ + unsigned char url_len; + unsigned char mac[6]; + char date[32]; + unsigned char charset; + unsigned char unused; + unsigned short key_len; + char data[]; +}; + +struct host_key_record *phn_rcd = NULL; /*pointer of host-name record*/ + +DEFINE_SPINLOCK(ddc_lock); + + +#if 1 +static inline int isPrintable(char c) +{ + if((c>= 0x20) && (c<=0x7E)) + return 1; + else + return 0; +} + +/*len = 16. for last line, len<16*/ +static void printLine(const char * buf, unsigned int len) +{ + unsigned int i; + unsigned char c; + for(i=0; i= start && ptr < end) + { + return true; + } + return false; +} +static inline char* skipIpheader(char *ip_hdr) +{ + unsigned int iphdr_len; + char * iphdr = ip_hdr; + pkt_info.ip_hdr = ip_hdr; + iphdr_len = ((*iphdr) & 0x0F)<<2; + + return iphdr+ iphdr_len; +} + +static inline char* skipTcpheader(const char * tcp_hdr) +{ + unsigned int tcphdr_len; + char *tcphdr = (char *)tcp_hdr; + pkt_info.tcp_hdr = (char *)tcp_hdr; + tcphdr_len = ((*(tcphdr + 12)) & 0xF0) >> 2; + + return (tcphdr + tcphdr_len); +} + +static inline bool pktdata_match(const char * src, const char * dst, int len) +{ + int i; + for(i=0; idata; + #if 0 + if((*proto_hdr >>4) == 4) + { + printk("IPv4 packet\n"); + } + #endif + + proto_hdr = skipIpheader(proto_hdr); + proto_hdr = skipTcpheader(proto_hdr); //later add code to check UDP header + + if(proto_hdr <=(char *)skb->tail) + { + pkt_info.http_hdr = proto_hdr; + } + else + { + pkt_info.http_hdr = NULL; //just tcp ack packet + } +} + +/* + changed to char * left, char * right, char * keyword, char* start, int mode + left | right + right mode: start -> + +*/ +static char * find_keywords_r(const char* right, const char *keyword, char *start) +{ + char * ptr = start; + char * arch = NULL; + int len = strlen(keyword); + + while((ptr +len) <= (right +1)) + { + if(*ptr == *keyword) + { + if(pktdata_match(ptr, keyword, len)) + { + arch = ptr; + return arch; + } + } + ptr++; + } + + return arch; +} + +#if 0 +/*search from right to the left + caller should give left edge, keyword and start = right -len +1 +*/ +static char * find_keywords_l(const char* left, const char *keyword, char *start) +{ + char * ptr = start; + char * arch = NULL; + int len = strlen(keyword); + + while(ptr >=left) + { + if(*ptr == *keyword) + { + if(pktdata_match(ptr, keyword, len)) + { + arch = ptr; + return arch; + } + } + ptr--; + } + + return arch; +} + +/* +* only search/record user's searching keyword for some profiled search engine +*/ +static bool match_search_engine(char* host_name) +{ + int i = 0; + + //search profiled search engine + const char * phost = search_engine[i]; + while(phost !=NULL) + { + if(strcmp(phost, host_name) == 0) + return true; + phost = search_engine[++i]; + } + + //later add code to search configured search engine + return false; +} +#endif + +/* +* extract host name from the HTTP GET packet and save this info into +* the array host[]. +*/ +static bool extract_http_host(const char * left, const char * right, char * start) +{ + int i =0; + char * ptr = start; + char * http_host = NULL; + + if(!utl_in_range(left, right, ptr)) + return false; + + http_host = find_keywords_r(right, "Host: ", ptr); + if(http_host == NULL) return false; + + /* + now we dont consider the status of HTTP response. actually when the + return code is 301, which means the host has been moved permanently. + then we should extract the valid host name from "Location: xxxx\r\d" + */ + http_host +=6; //skip "Host: " + pkt_info.host = http_host; + while((http_host <=right) + && (*http_host != 0x0d) + && (*http_host != 0x0a)) + { + host[i++] = *http_host; + http_host++; + if(i>=MAX_HOST_LENGTH) break; + } + host[i] = '\0'; + return true; +} + +static void update_search_key(struct sk_buff *skb, char * host_name, char * key) +{ + unsigned short key_len = round_up(strlen(key), 4); + unsigned short url_len = round_up(strlen(host_name), 4); + unsigned short item_len = key_len + url_len + round_up(sizeof(struct host_key_record), 4); + + phn_rcd = (struct host_key_record *)(pdpi_buf + total_bytes); + + if(!utl_in_range(pdpi_buf, pdpi_buf+DPI_INFO_BUF_LEN, (char*)phn_rcd + item_len)) + { + //debug + pr_debug("out of dpi buffer\n"); + return; + } + + phn_rcd->rcdtype = RECORD_URL; + phn_rcd->actiontype = 2; //search + phn_rcd->url_len = url_len; + phn_rcd->charset = CHARSET_INVALID; + phn_rcd->key_len = key_len; + phn_rcd->item_len =item_len; + phn_rcd->date[0] = '\0'; //now date is invalid + memcpy(phn_rcd->mac, skb_mac_header(skb)+6, 6); + + strcpy(phn_rcd->data, host_name); + strcpy(phn_rcd->data + url_len, key); + + total_bytes += item_len; + + //debug + //printk("total bytes: %d\n", total_bytes); + //ddc_printPacket( (char*)pdpi_buf, (char*)pdpi_buf + total_bytes); +} + +static void update_charset(char * charset) +{ + char * ptr = charset + 8; //skip "charset=" + + if(!utl_in_range(pdpi_buf, pdpi_buf+DPI_INFO_BUF_LEN, (char*)phn_rcd)) + return; + + if(strcmp(ptr, "utf-8") == 0) + phn_rcd->charset = CHARSET_UTF_8; + else if(strcmp(ptr, "gbk") == 0) + phn_rcd->charset = CHARSET_GBK; + else if(strcmp(ptr, "gb2312") == 0) + phn_rcd->charset = CHARSET_GB2312; + else + phn_rcd->charset = CHARSET_INVALID; + + //debug + //ddc_printPacket( (char*)pdpi_buf, (char*)pdpi_buf + total_bytes); +} + +/*total 29 bytes.*/ +static void update_date(char * date) +{ + if(!utl_in_range(pdpi_buf, pdpi_buf+DPI_INFO_BUF_LEN, (char*)phn_rcd)) + return; + strcpy(phn_rcd->date, date); //have enough space + + //debug + //ddc_printPacket( (char*)pdpi_buf, (char*)pdpi_buf + total_bytes); +} +static void update_host_name(struct sk_buff *skb, char * host_name) +{ + unsigned short url_len, item_len; + phn_rcd = (struct host_key_record *)(pdpi_buf + total_bytes); + + url_len = round_up(strlen(host_name), 4); + item_len = url_len + round_up(sizeof(struct host_key_record), 4); + + if(!utl_in_range(pdpi_buf, pdpi_buf+DPI_INFO_BUF_LEN, (char*)phn_rcd+item_len)) + { + pr_debug("out of dpi buffer\n"); + return; + } + + phn_rcd->rcdtype = RECORD_URL; + phn_rcd->actiontype = 1; //web + phn_rcd->url_len = url_len; + phn_rcd->charset = CHARSET_INVALID; + + memcpy(phn_rcd->mac, skb_mac_header(skb)+6, 6); + phn_rcd->date[0] = '\0'; //now date is invalid + phn_rcd->item_len = item_len; + + strcpy(phn_rcd->data, host_name); + total_bytes += phn_rcd->item_len ; + phn_rcd->key_len = 0; + + //debug + //ddc_printPacket( (char*)pdpi_buf, (char*)pdpi_buf + total_bytes); + return; +} + +static bool check_search_pattern(const char * start) +{ + int i =0; + char * ptr = (char *)start; + struct search_mkptn * ptn = NULL; + + //skip the additional '/' + while(i<10) //max we what skip. tunable + { + if(*ptr ==0x2F) //'/' + { + ptr++; + goto PATTERN_SEARCH; + } + i++; + ptr++; + } + ptr = (char *)start; //reset + + /*later add code to read additional pattern configured through ctms + */ +PATTERN_SEARCH: + i=0; + while(imark, ptn->marklen)) + { + pkt_info.src_ptn_idx = i; + return true; + } + i++; + }; + return false; +} + +static bool extract_search_word(const char* start, const char* end) +{ + char * ptr = (char *)start; + struct srch_ptn * ptrn = NULL; + + int i = 0; + + if(pkt_info.src_ptn_idx < SEARCH_PATTERN_NBR) + { + ptrn = search_pat[pkt_info.src_ptn_idx].wdp; + } + else + { + return false; + } + + while(ptr <=(end-ptrn[i].ptn_len+1) && (*ptr!='\r') && (*ptr!='\n')&& (*ptr!=' ')) + { + while(ptrn[i].ptn != NULL) + { + if(pktdata_match(ptr, ptrn[i].ptn, ptrn[i].ptn_len)) + { + ptr +=ptrn[i].ptn_len; + goto CPY_SEARCH_KEY; + } + i++; + } + ptr++; + i=0; + } + return false; + +CPY_SEARCH_KEY: + i =0; + while((*ptr !='&') && (*ptr !=' ') && (i <(MAX_SEARCH_KEY_LEN-1))) + { + search_key[i++] = *ptr; + ptr++; + } + search_key[i] = '\0'; + pkt_info.key_end = ptr; + return (i>0)? true : false; +} + +static bool extract_http_date(const char * left, const char * right, const char * start) +{ + int i =0; + char * ptr = (char *)start; + + while(ptr <=(right-3)) + { + if(pktdata_match(ptr, "Date", 4)) + { + goto CPY_DATE; + } + ptr++; + } + http_date[0] ='\0'; + return false; + +CPY_DATE: + while((*ptr!=0x0d) &&(*ptr!=0x0a)) + { + http_date[i++] = *ptr++; + } + http_date[i] ='\0'; + return true; +} + +static bool extract_http_charset(const char * left, const char * right, const char * start) +{ + int i =0; + char * ptr = (char *)start; + + while(ptr <=(right-6)) + { + if(pktdata_match(ptr, "charset", 7)) + { + goto CPY_CHARSET; + } + ptr++; + } + http_charset[0] ='\0'; + return false; + +CPY_CHARSET: + while((*ptr!=0x0d) &&(*ptr!=0x0a)) + { + http_charset[i++] = isascii(*ptr)? tolower(*ptr) : *ptr; + ptr++; + } + http_charset[i] ='\0'; + return true; +} + +static void http_handler_ds(struct sk_buff *skb) +{ + if(bparse_flag & (PARSE_DATE |PARSE_CHARSET)) + { + locateHTTPHeader(skb); + } + + if(bparse_flag & PARSE_DATE) + { + if(pkt_info.http_hdr && pktdata_match(pkt_info.http_hdr,"HTTP/",5)) + { + if(extract_http_date(pkt_info.http_hdr, (const char *)skb->tail, pkt_info.http_hdr )) + { + bparse_flag &= ~PARSE_DATE; + //printk("access time: %s\n", http_date+6); //just skip "Date: + update_date(http_date+6); + } + } + } + + /* + * parese character set is just a demo + */ + if(bparse_flag & PARSE_CHARSET) + { + if(pkt_info.http_hdr && pktdata_match(pkt_info.http_hdr,"HTTP/",5)) + { + if(extract_http_charset(pkt_info.http_hdr, (const char *)skb->tail, pkt_info.http_hdr + 7)) + { + bparse_flag &= ~PARSE_CHARSET; + bparse_flag |= CHARSET_VALID; + //printk("%s\n", http_charset); + update_charset(http_charset); + } + } + } +} +static void http_handler_us(struct sk_buff *skb) +{ + char* ptr; + bool spkt = false; + locateHTTPHeader(skb); + extract_http_method(pkt_info.http_hdr); + + if(pkt_info.method == METHOD_GET) + { + if(pkt_info.http_hdr && pktdata_match(pkt_info.http_hdr + 6, "HTTP/", 5)) + { + bparse_flag |= PARSE_DATE | PARSE_CHARSET; + if(extract_http_host((char *)skb->data, (char *) skb->tail, pkt_info.http_hdr + 15)) + { + //printk("\n\nHost: %s\n", host); //debug + update_host_name(skb, host); + } + } + + /* now all web sites will be examined to extract searching key word. + * later add code to extract key word only from specified web site based + * on ctms config + */ + ptr = pkt_info.http_hdr + 5; //skip "GET /" + spkt = check_search_pattern(ptr); + if(spkt) + { + if(extract_search_word(pkt_info.http_hdr + 5, (char *) skb->tail)) //skip space + { + extract_http_host((char *)skb->data, (char *) skb->tail, pkt_info.key_end ? pkt_info.key_end : (char *)skb->data); + //printk("search key: %s\n", search_key); //debug + update_search_key(skb, host, search_key); + bparse_flag |= PARSE_DATE; + } + } + } + //add function to process other method, such as POST to parse webmail + +} + +static unsigned int +dc_tg(struct sk_buff *skb, const struct xt_action_param *par) +{ + struct nf_conn * conntrack = NULL; + enum ip_conntrack_info ctinfo; + + + conntrack = nf_ct_get(skb, &ctinfo); + if(unlikely(conntrack == NULL)) + { + return XT_CONTINUE; + } + + memset(&pkt_info, 0, sizeof(pkt_info)); + + spin_lock_bh(&ddc_lock); + + if(((skb_dst(skb)->flags & DST_NOXFRM) && (skb->dev->priv_flags & IFF_WANDEV)) || + (!(skb_dst(skb)->flags & DST_NOXFRM) && (skb_dst(skb)->dev->priv_flags & IFF_WANDEV))) + { + /*NOTE: all http protocol pattern file should start with "http" + now only handle http packet (only http rule is inserted) + */ + if(pktdata_match(conntrack->layer7.app_proto, "http", 4)) + http_handler_us(skb); + //add other protocol handler + } + else + { + if(pktdata_match(conntrack->layer7.app_proto, "http", 4)) + http_handler_ds(skb); + } + spin_unlock_bh(&ddc_lock); + + return XT_CONTINUE; +} + +/* + * internal DPI buffer is 1024 now(tunable). so the count should >=1024. +*/ + +static int dc_read_proc(struct file *f, char *buf, size_t cnt, loff_t *pos) +{ + int r = 0; + + //lock. need to define a lock + #if 1 + if(pdpi_buf == NULL) + return -EFAULT; + + r = simple_read_from_buffer(buf, cnt, pos, pdpi_buf, total_bytes); + #else + //here provide an example of how to parse data in /proc/dpi/http + char * ptr = pdpi_buf; + unsigned short items = 0; + struct host_key_record * prcd; + + if(pdpi_buf == NULL) + return 0; + printk("\n--dump contents in dpi buffer--\n"); + while((char *)ptr < (pdpi_buf + total_bytes)) + { + prcd = (struct host_key_record *)ptr; + if(prcd->rcdtype == 5) + printk("RT: URL\n"); + else + printk("RT: other\n"); + printk("MC: "); + { + int i; + for(i=0; i<6; i++) + { + if(i!=5) + printk("%02x-", prcd->mac[i]); + else + printk("%02x\n", prcd->mac[i]); + } + } + printk("DT: %s\n", prcd->date); + printk("HN: %s\n", prcd->data); + if(prcd->actiontype ==2) + printk("KY: %s\n", (char *)prcd->data + prcd->url_len); + + ptr += prcd->item_len; + items++; + printk("\n\n"); + } + printk("\--total %d items--\n\n", items); + #endif + + total_bytes = 0; + //unlock + + return r; +} + +static struct file_operations dc_proc_fops = { + .owner = THIS_MODULE, + .read = dc_read_proc, +}; + +static void dc_init_proc(void) +{ + proc_dpi_dir = proc_mkdir(DPI_DIRECTORY, NULL); + if(!proc_dpi_dir) + { + return; + } + + proc_dpi_entry = proc_create(DPI_PROC_FILE_NAME, 0644, proc_dpi_dir, &dc_proc_fops); + if(!proc_dpi_entry) + { + remove_proc_entry(DPI_DIRECTORY, NULL); + return; + } +} + +static void dc_cleanup_proc(void) +{ + if( proc_dpi_entry ) + remove_proc_entry(DPI_PROC_FILE_NAME, proc_dpi_dir); + if( proc_dpi_dir ) + remove_proc_entry(DPI_DIRECTORY, NULL); +} + + +static struct xt_target dc_tg_reg __read_mostly = +{ + .name = "DC", + .revision = 0, + .family = NFPROTO_UNSPEC, + .target = dc_tg, + .me = THIS_MODULE, +}; + +static int __init dc_tg_init(void) +{ + pdpi_buf = (char *)kmalloc(DPI_INFO_BUF_LEN, GFP_KERNEL); + if(pdpi_buf == NULL) + { + pr_debug("%s: kmalloc %d bytes failed\n", __FUNCTION__, DPI_INFO_BUF_LEN); + return -ENOMEM; + } + + dc_init_proc(); + return xt_register_target(&dc_tg_reg); +} + +static void __exit dc_tg_exit(void) +{ + if(pdpi_buf != NULL) + kfree(pdpi_buf); + + dc_cleanup_proc(); + + xt_unregister_target(&dc_tg_reg); +} + +module_init(dc_tg_init); +module_exit(dc_tg_exit); +#endif diff -urN linux-4.1.52-orig/net/netfilter/xt_flowlabel.c linux-4.1.52/net/netfilter/xt_flowlabel.c --- linux-4.1.52-orig/net/netfilter/xt_flowlabel.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/netfilter/xt_flowlabel.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,94 @@ +#if defined(CONFIG_BCM_KF_NETFILTER) +/* +* Copyright (c) 2003-2019 Broadcom +* All Rights Reserved +* +<:label-BRCM:2019:DUAL/GPL:standard + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + + +/* IP tables module for matching the value of the IPv6 flowlabel field + * + * BRCM, Feb, 1. 2019. + */ + +#include +#include +#include +#include + +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Broadcom"); +MODULE_DESCRIPTION("Xtables: IPv6 flowlabel field match"); +MODULE_ALIAS("ip6t_flowlabel"); + + +static bool +flowlabel_mt(const struct sk_buff *skb, struct xt_action_param *par) +{ + const struct xt_flowlabel_info *info = par->matchinfo; + + return (ip6_flowlabel(ipv6_hdr(skb)) == info->flowlabel) ^ !!info->invert; +} + +static int flowlabel_mt_check(const struct xt_mtchk_param *par) +{ + const struct xt_flowlabel_info *info = par->matchinfo; + + if (info->flowlabel > XT_FLOWLABEL_MAX) { + pr_err("flowlabel 0x%x out of range\n", info->flowlabel); + return -EDOM; + } + + return 0; +} + + +static struct xt_match flowlabel_mt_reg __read_mostly = { + .name = "flowlabel", + .family = NFPROTO_IPV6, + .checkentry = flowlabel_mt_check, + .match = flowlabel_mt, + .matchsize = sizeof(struct xt_flowlabel_info), + .me = THIS_MODULE, +}; + +static int __init flowlabel_mt_init(void) +{ + return xt_register_match(&flowlabel_mt_reg); +} + +static void __exit flowlabel_mt_exit(void) +{ + xt_unregister_match(&flowlabel_mt_reg); +} + +module_init(flowlabel_mt_init); +module_exit(flowlabel_mt_exit); + +#endif // defined(CONFIG_BCM_KF_NETFILTER) \ No newline at end of file diff -urN linux-4.1.52-orig/net/netfilter/xt_HL.c linux-4.1.52/net/netfilter/xt_HL.c --- linux-4.1.52-orig/net/netfilter/xt_HL.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/netfilter/xt_HL.c 2020-02-07 01:47:54.000000000 +0800 @@ -32,6 +32,12 @@ const struct ipt_TTL_info *info = par->targinfo; int new_ttl; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG_FEATURE) + skb->ipt_check |= IPT_TARGET_TTL; + if ( skb->ipt_check & IPT_TARGET_CHECK ) + return XT_CONTINUE; +#endif + if (!skb_make_writable(skb, skb->len)) return NF_DROP; @@ -72,6 +78,12 @@ const struct ip6t_HL_info *info = par->targinfo; int new_hl; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG_FEATURE) + skb->ipt_check |= IPT_TARGET_HL; + if ( skb->ipt_check & IPT_TARGET_CHECK ) + return XT_CONTINUE; +#endif + if (!skb_make_writable(skb, skb->len)) return NF_DROP; diff -urN linux-4.1.52-orig/net/netfilter/xt_layer7.c linux-4.1.52/net/netfilter/xt_layer7.c --- linux-4.1.52-orig/net/netfilter/xt_layer7.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/netfilter/xt_layer7.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,625 @@ +#if defined(CONFIG_BCM_KF_XT_MATCH_LAYER7) +/* + Kernel module to match application layer (OSI layer 7) data in connections. + + http://l7-filter.sf.net + + (C) 2003-2009 Matthew Strait and Ethan Sommer. + + 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. + http://www.gnu.org/licenses/gpl.txt + + Based on ipt_string.c (C) 2000 Emmanuel Roger , + xt_helper.c (C) 2002 Harald Welte and cls_layer7.c (C) 2003 Matthew Strait, + Ethan Sommer, Justin Levandoski. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "regexp/regexp.c" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Matthew Strait , Ethan Sommer "); +MODULE_DESCRIPTION("iptables application layer match module"); +MODULE_ALIAS("ipt_layer7"); +MODULE_VERSION("2.21"); + +static int maxdatalen = 2048; // this is the default +module_param(maxdatalen, int, 0444); +MODULE_PARM_DESC(maxdatalen, "maximum bytes of data looked at by l7-filter"); +#ifdef CONFIG_NETFILTER_XT_MATCH_LAYER7_DEBUG + #define DPRINTK(format,args...) printk(format,##args) +#else + #define DPRINTK(format,args...) +#endif + +/* Number of packets whose data we look at. +This can be modified through /proc/net/layer7_numpackets */ +static int num_packets = 10; + +static struct pattern_cache { + char * regex_string; + regexp * pattern; + struct pattern_cache * next; +} * first_pattern_cache = NULL; + +DEFINE_SPINLOCK(l7_lock); + +static int total_acct_packets(struct nf_conn *ct) +{ + struct nf_conn_acct *acct; + struct nf_conn_counter *counter; + + BUG_ON(ct == NULL); + acct = nf_conn_acct_find(ct); + if (!acct) + return 0; + counter = acct->counter; + return (atomic64_read(&counter[IP_CT_DIR_ORIGINAL].packets) + atomic64_read(&counter[IP_CT_DIR_REPLY].packets)); +} + +#ifdef CONFIG_NETFILTER_XT_MATCH_LAYER7_DEBUG +/* Converts an unfriendly string into a friendly one by +replacing unprintables with periods and all whitespace with " ". */ +static char * friendly_print(unsigned char * s) +{ + char * f = kmalloc(strlen(s) + 1, GFP_ATOMIC); + int i; + + if(!f) { + if (net_ratelimit()) + pr_debug("layer7: out of memory in " + "friendly_print, bailing.\n"); + return NULL; + } + + for(i = 0; i < strlen(s); i++){ + if(isprint(s[i]) && s[i] < 128) f[i] = s[i]; + else if(isspace(s[i])) f[i] = ' '; + else f[i] = '.'; + } + f[i] = '\0'; + return f; +} + +static char dec2hex(int i) +{ + switch (i) { + case 0 ... 9: + return (i + '0'); + break; + case 10 ... 15: + return (i - 10 + 'a'); + break; + default: + if (net_ratelimit()) + pr_debug("layer7: Problem in dec2hex\n"); + return '\0'; + } +} + +static char * hex_print(unsigned char * s) +{ + char * g = kmalloc(strlen(s)*3 + 1, GFP_ATOMIC); + int i; + + if(!g) { + if (net_ratelimit()) + pr_debug("layer7: out of memory in hex_print, " + "bailing.\n"); + return NULL; + } + + for(i = 0; i < strlen(s); i++) { + g[i*3 ] = dec2hex(s[i]/16); + g[i*3 + 1] = dec2hex(s[i]%16); + g[i*3 + 2] = ' '; + } + g[i*3] = '\0'; + + return g; +} +#endif // DEBUG + +/* Use instead of regcomp. As we expect to be seeing the same regexps over and +* over again, it make sense to cache the results. +*/ +static regexp * compile_and_cache(const char * regex_string, + const char * protocol) +{ + struct pattern_cache * node = first_pattern_cache; + struct pattern_cache * last_pattern_cache = first_pattern_cache; + struct pattern_cache * tmp; + unsigned int len; + + while (node != NULL) { + if (!strcmp(node->regex_string, regex_string)) + return node->pattern; + + last_pattern_cache = node;/* points at the last non-NULL node */ + node = node->next; + } + + /* If we reach the end of the list, then we have not yet cached + the pattern for this regex. Let's do that now. + Be paranoid about running out of memory to avoid list corruption. */ + tmp = kmalloc(sizeof(struct pattern_cache), GFP_ATOMIC); + + if(!tmp) { + if (net_ratelimit()) + pr_debug("layer7: out of memory in " + "compile_and_cache, bailing.\n"); + return NULL; + } + + tmp->regex_string = kmalloc(strlen(regex_string) + 1, GFP_ATOMIC); + tmp->pattern = kmalloc(sizeof(struct regexp), GFP_ATOMIC); + tmp->next = NULL; + + if(!tmp->regex_string || !tmp->pattern) { + if (net_ratelimit()) + pr_debug("layer7: out of memory in " + "compile_and_cache, bailing.\n"); + kfree(tmp->regex_string); + kfree(tmp->pattern); + kfree(tmp); + return NULL; + } + + /* Ok. The new node is all ready now. */ + node = tmp; + + if(first_pattern_cache == NULL) /* list is empty */ + first_pattern_cache = node; /* make node the beginning */ + else + last_pattern_cache->next = node; /* attach node to the end */ + + /* copy the string and compile the regex */ + len = strlen(regex_string); + node->pattern = regcomp((char *)regex_string, &len); + if ( !node->pattern ) { + //if (net_ratelimit()) + pr_debug("layer7: Error compiling regexp " + "\"%s\" (%s)\n", + regex_string, protocol); + /* pattern is now cached as NULL, so we won't try again. */ + } + + strcpy(node->regex_string, regex_string); + + return node->pattern; +} + +static int can_handle(const struct sk_buff *skb) +{ + if(!ip_hdr(skb)) /* not IP */ + return 0; + if(ip_hdr(skb)->protocol != IPPROTO_TCP && + ip_hdr(skb)->protocol != IPPROTO_UDP && + ip_hdr(skb)->protocol != IPPROTO_ICMP) + return 0; + return 1; +} + +/* Returns offset the into the skb->data that the application data starts */ +static int app_data_offset(const struct sk_buff *skb) +{ + /* In case we are ported somewhere (ebtables?) where ip_hdr(skb) + isn't set, this can be gotten from 4*(skb->data[0] & 0x0f) as well. */ + int ip_hl = 4*ip_hdr(skb)->ihl; + + if( ip_hdr(skb)->protocol == IPPROTO_TCP ) { + /* 12 == offset into TCP header for the header length field. + Can't get this with skb->h.th->doff because the tcphdr + struct doesn't get set when routing (this is confirmed to be + true in Netfilter as well as QoS.) */ + int tcp_hl = 4*(skb->data[ip_hl + 12] >> 4); + + return ip_hl + tcp_hl; + } else if( ip_hdr(skb)->protocol == IPPROTO_UDP ) { + return ip_hl + 8; /* UDP header is always 8 bytes */ + } else if( ip_hdr(skb)->protocol == IPPROTO_ICMP ) { + return ip_hl + 8; /* ICMP header is 8 bytes */ + } else { + if (net_ratelimit()) + pr_debug("layer7: tried to handle unknown " + "protocol!\n"); + return ip_hl + 8; /* something reasonable */ + } +} + +/* handles whether there's a match when we aren't appending data anymore */ +static int match_no_append(struct nf_conn * conntrack, + struct nf_conn * master_conntrack, + enum ip_conntrack_info ctinfo, + enum ip_conntrack_info master_ctinfo, + const struct xt_layer7_info * info) +{ + /* If we're in here, throw the app data away */ + if(master_conntrack->layer7.app_data != NULL) { + +#ifdef CONFIG_NETFILTER_XT_MATCH_LAYER7_DEBUG + if(!master_conntrack->layer7.app_proto) { + char * f = + friendly_print(master_conntrack->layer7.app_data); + char * g = + hex_print(master_conntrack->layer7.app_data); + pr_debug("\nl7-filter gave up after %d bytes " + "(%d packets):\n%s\n", + strlen(f), total_acct_packets(master_conntrack), f); + kfree(f); + pr_debug("In hex: %s\n", g); + kfree(g); + } +#endif + + kfree(master_conntrack->layer7.app_data); + master_conntrack->layer7.app_data = NULL; /* don't free again */ + } + + if(master_conntrack->layer7.app_proto){ + /* Here child connections set their .app_proto (for /proc) */ + if(!conntrack->layer7.app_proto) { + conntrack->layer7.app_proto = + kmalloc(strlen(master_conntrack->layer7.app_proto)+1, + GFP_ATOMIC); + if(!conntrack->layer7.app_proto){ + if (net_ratelimit()) + pr_debug("layer7: out of memory " + "in match_no_append, " + "bailing.\n"); + return 1; + } + strcpy(conntrack->layer7.app_proto, + master_conntrack->layer7.app_proto); + } + + return (!strcmp(master_conntrack->layer7.app_proto, + info->protocol)); + } + else { + /* If not classified even num_packets have been checked, + *set to "unknown" to distinguish from + *connections that are still being tested. */ + master_conntrack->layer7.app_proto = + kmalloc(strlen("unknown")+1, GFP_ATOMIC); + if(!master_conntrack->layer7.app_proto){ + if (net_ratelimit()) + pr_debug("layer7: out of memory in " + "match_no_append, bailing.\n"); + return 1; + } + strcpy(master_conntrack->layer7.app_proto, "unknown"); + return 0; + } +} + +/* add the new app data to the conntrack. Return number of bytes added. */ +static int add_data(struct nf_conn * master_conntrack, + char * app_data, int appdatalen) +{ + int length = 0, i; + int oldlength = master_conntrack->layer7.app_data_len; + + /* This is a fix for a race condition by Deti Fliegl. However, I'm not + clear on whether the race condition exists or whether this really + fixes it. I might just be being dense... Anyway, if it's not really + a fix, all it does is waste a very small amount of time. */ + if(!master_conntrack->layer7.app_data) return 0; + + /* Strip nulls. Make everything lower case (our regex lib doesn't + do case insensitivity). Add it to the end of the current data. */ + for(i = 0; i < maxdatalen-oldlength-1 && + i < appdatalen; i++) { + if(app_data[i] != '\0') { + /* the kernel version of tolower mungs 'upper ascii' */ + master_conntrack->layer7.app_data[length+oldlength] = + isascii(app_data[i])? + tolower(app_data[i]) : app_data[i]; + length++; + } + } + + master_conntrack->layer7.app_data[length+oldlength] = '\0'; + master_conntrack->layer7.app_data_len = length + oldlength; + + return length; +} + +/* taken from drivers/video/modedb.c */ +static int my_atoi(const char *s) +{ + int val = 0; + + for (;; s++) { + switch (*s) { + case '0'...'9': + val = 10*val+(*s-'0'); + break; + default: + return val; + } + } +} + +/* write out num_packets to userland. */ +static int layer7_read_proc(struct file *f, char *buf, size_t cnt, loff_t *pos) +{ + char page[4]; + + if(num_packets > 99 && net_ratelimit()) + pr_debug("layer7: NOT REACHED. num_packets too big\n"); + + page[0] = num_packets/10 + '0'; + page[1] = num_packets%10 + '0'; + page[2] = '\n'; + page[3] = '\0'; + + return simple_read_from_buffer(buf, cnt, pos, page, 3); +} + +/* Read in num_packets from userland */ +static ssize_t layer7_write_proc(struct file *f, const char *buffer, size_t count, loff_t *pos) +{ + char * foo = kmalloc(count, GFP_ATOMIC); + + if(!foo){ + if (net_ratelimit()) + pr_debug("layer7: out of memory, bailing. " + "num_packets unchanged.\n"); + return count; + } + + if(copy_from_user(foo, buffer, count)) { + return -EFAULT; + } + + num_packets = my_atoi(foo); + kfree (foo); + + /* This has an arbitrary limit to make the math easier. I'm lazy. + But anyway, 99 is a LOT! If you want more, you're doing it wrong! */ + if(num_packets > 99) { + pr_debug("layer7: num_packets can't be > 99.\n"); + num_packets = 99; + } else if(num_packets < 1) { + pr_debug("layer7: num_packets can't be < 1.\n"); + num_packets = 1; + } + + return count; +} + +static bool l7_mt4(const struct sk_buff *skbin, + struct xt_action_param *par) +{ + /* sidestep const without getting a compiler warning... */ + struct sk_buff * skb = (struct sk_buff *)skbin; + const struct xt_layer7_info * info = par->matchinfo; + enum ip_conntrack_info master_ctinfo, ctinfo; + struct nf_conn *master_conntrack, *conntrack; + unsigned char * app_data; + unsigned int pattern_result, appdatalen; + regexp * comppattern; + + /* Be paranoid/incompetent - lock the entire match function. */ + spin_lock_bh(&l7_lock); + + if(!can_handle(skb)){ + pr_debug("layer7: This is some protocol I can't handle. skb(%p)\n", skb); + spin_unlock_bh(&l7_lock); + return (bool)(info->invert); + } + + /* Treat parent & all its children together as one connection, except + * for the purpose of setting conntrack->layer7.app_proto in the actual + * connection. This makes /proc/net/ip_conntrack more satisfying. + */ + if(!(conntrack = nf_ct_get(skb, &ctinfo)) || + !(master_conntrack=nf_ct_get(skb,&master_ctinfo))){ + pr_debug("layer7: couldn't get conntrack. skb(%p)\n", skb); + spin_unlock_bh(&l7_lock); + return (bool)(info->invert); + } + + /* Try to get a master conntrack (and its master etc) for FTP, etc. */ + while (master_ct(master_conntrack) != NULL) + master_conntrack = master_ct(master_conntrack); + + /* if we've classified it or seen too many packets or mast conntrack has its + * protocol name + */ + if(total_acct_packets(master_conntrack) > num_packets || + master_conntrack->layer7.app_proto) { + + pattern_result = match_no_append(conntrack, master_conntrack, + ctinfo, master_ctinfo, info); + + /* skb->cb[0] == seen. Don't do things twice if there are + multiple l7 rules. I'm not sure that using cb for this purpose + is correct, even though it says "put your private variables + there". But it doesn't look like it is being used for anything + else in the skbs that make it here. */ + skb->cb[0] = 1; /* marking it seen here's probably irrelevant */ + + spin_unlock_bh(&l7_lock); + return (bool)(pattern_result ^ info->invert); + } + + if(skb_is_nonlinear(skb)){ + if(skb_linearize(skb) != 0){ + if (net_ratelimit()) + pr_debug("layer7: failed to linearize " + "packet, bailing.\n"); + spin_unlock_bh(&l7_lock); + return (bool)(info->invert); + } + } + + /* now that the skb is linearized, it's safe to set these. */ + app_data = skb->data + app_data_offset(skb); + appdatalen = skb_tail_pointer(skb) - app_data; + + /* the return value gets checked later, when we're ready to use it */ + comppattern = compile_and_cache(info->pattern, info->protocol); + + /* On the first packet of a connection, allocate space for app data */ + if(total_acct_packets(master_conntrack) == 1 && !skb->cb[0] && + !master_conntrack->layer7.app_data){ + master_conntrack->layer7.app_data = + kmalloc(maxdatalen, GFP_ATOMIC); + if(!master_conntrack->layer7.app_data){ + if (net_ratelimit()) + pr_debug("layer7: out of memory in " + "l7_mt4, bailing.\n"); + spin_unlock_bh(&l7_lock); + return (bool)(info->invert); + } + + master_conntrack->layer7.app_data[0] = '\0'; + } + + /* Can be here, but unallocated, if numpackets is increased near + the beginning of a connection */ + if(master_conntrack->layer7.app_data == NULL){ + spin_unlock_bh(&l7_lock); + return (bool)(info->invert); /* unmatched */ + } + + if(!skb->cb[0]){ + int newbytes; + newbytes = add_data(master_conntrack, app_data, appdatalen); + + if(newbytes == 0) { /* didn't add any data */ + skb->cb[0] = 1; + /* Didn't match before, not going to match now */ + spin_unlock_bh(&l7_lock); + return (bool)(info->invert); + } + } + + /* If looking for "unknown", then never match. "Unknown" means that + we've given up; we're still trying with these packets. */ + if(!strcmp(info->protocol, "unknown")) { + pattern_result = 0; + /* If looking for "unset", then always match. "Unset" means that we + haven't yet classified the connection. */ + } else if(!strcmp(info->protocol, "unset")) { + pattern_result = 2; + pr_debug("layer7: matched unset: not yet classified " + "(%d/%d packets)\n", + total_acct_packets(master_conntrack), num_packets); + /* If the regexp failed to compile, don't bother running it */ + } else if(comppattern && + regexec(comppattern, master_conntrack->layer7.app_data)){ + char tmp_commpattern[21]; + char tmp_app_data[21]; + + memcpy(tmp_commpattern, comppattern, 20); + tmp_commpattern[20] = '\0'; + memcpy(tmp_app_data, master_conntrack->layer7.app_data, 20); + tmp_app_data[20] = '\0'; + pattern_result = 1; + } else pattern_result = 0; + + if(pattern_result == 1) { + master_conntrack->layer7.app_proto = + kmalloc(strlen(info->protocol)+1, GFP_ATOMIC); + if(!master_conntrack->layer7.app_proto){ + if (net_ratelimit()) + pr_debug("layer7: out of memory in " + "l7_mt4, bailing.\n"); + spin_unlock_bh(&l7_lock); + return (bool)(pattern_result ^ info->invert); + } + strcpy(master_conntrack->layer7.app_proto, info->protocol); + } else if(pattern_result > 1) { /* cleanup from "unset" */ + pattern_result = 1; + } + + /* mark the packet seen */ + skb->cb[0] = 1; + + spin_unlock_bh(&l7_lock); + return (bool)(pattern_result ^ info->invert); +} + +static void l7_destroy(const struct xt_mtdtor_param *par) +{ + nf_ct_l3proto_module_put(par->match->family); +} + +static struct xt_match l7_mt_reg __read_mostly = { + .name = "layer7", + .revision = 0, + .family = NFPROTO_IPV4, + .match = l7_mt4, + .destroy = l7_destroy, + .matchsize = sizeof(struct xt_layer7_info), + .me = THIS_MODULE, +}; + +static struct file_operations layer7_proc_fops = { + .owner = THIS_MODULE, + .read = layer7_read_proc, + .write = layer7_write_proc, +}; + +static void layer7_cleanup_proc(void) +{ + remove_proc_entry("layer7_numpackets", init_net.proc_net); +} + +/* register the proc file */ +static void layer7_init_proc(void) +{ + struct proc_dir_entry* entry; + entry = proc_create("layer7_numpackets", 0644, init_net.proc_net, &layer7_proc_fops); +} + +static int __init xt_layer7_init(void) +{ + need_conntrack(); + + layer7_init_proc(); + if(maxdatalen < 1) { + pr_debug("layer7: maxdatalen can't be < 1, " + "using 1\n"); + maxdatalen = 1; + } + /* This is not a hard limit. It's just here to prevent people from + bringing their slow machines to a grinding halt. */ + else if(maxdatalen > 65536) { + pr_debug("layer7: maxdatalen can't be > 65536, " + "using 65536\n"); + maxdatalen = 65536; + } + return xt_register_match(&l7_mt_reg); +} + +static void __exit xt_layer7_fini(void) +{ + layer7_cleanup_proc(); + xt_unregister_match(&l7_mt_reg); +} + +module_init(xt_layer7_init); +module_exit(xt_layer7_fini); +#endif diff -urN linux-4.1.52-orig/net/netfilter/xt_LED.c linux-4.1.52/net/netfilter/xt_LED.c --- linux-4.1.52-orig/net/netfilter/xt_LED.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/netfilter/xt_LED.c 2020-02-07 01:47:54.000000000 +0800 @@ -59,6 +59,12 @@ struct xt_led_info_internal *ledinternal = ledinfo->internal_data; unsigned long led_delay = XT_LED_BLINK_DELAY; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG_FEATURE) + skb->ipt_check |= IPT_TARGET_LED; + if ( skb->ipt_check & IPT_TARGET_CHECK ) + return XT_CONTINUE; +#endif + /* * If "always blink" is enabled, and there's still some time until the * LED will switch off, briefly switch it off now. diff -urN linux-4.1.52-orig/net/netfilter/xt_length.c linux-4.1.52/net/netfilter/xt_length.c --- linux-4.1.52-orig/net/netfilter/xt_length.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/netfilter/xt_length.c 2020-02-07 01:47:54.000000000 +0800 @@ -26,6 +26,13 @@ const struct xt_length_info *info = par->matchinfo; u_int16_t pktlen = ntohs(ip_hdr(skb)->tot_len); +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG_FEATURE) + struct sk_buff *skb_p; + skb_p = (struct sk_buff *)skb; + skb_p->ipt_check |= IPT_MATCH_LENGTH; + skb_p->ipt_log.u32[BLOG_MIN_LEN_INDEX] = info->min; + skb_p->ipt_log.u32[BLOG_MAX_LEN_INDEX] = info->max; +#endif return (pktlen >= info->min && pktlen <= info->max) ^ info->invert; } @@ -36,6 +43,13 @@ const u_int16_t pktlen = ntohs(ipv6_hdr(skb)->payload_len) + sizeof(struct ipv6hdr); +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG_FEATURE) + struct sk_buff *skb_p; + skb_p = (struct sk_buff *)skb; + skb_p->ipt_check |= IPT_MATCH_LENGTH; + skb_p->ipt_log.u32[BLOG_MIN_LEN_INDEX] = info->min; + skb_p->ipt_log.u32[BLOG_MAX_LEN_INDEX] = info->max; +#endif return (pktlen >= info->min && pktlen <= info->max) ^ info->invert; } diff -urN linux-4.1.52-orig/net/netfilter/xt_mac_extend.c linux-4.1.52/net/netfilter/xt_mac_extend.c --- linux-4.1.52-orig/net/netfilter/xt_mac_extend.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/netfilter/xt_mac_extend.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,80 @@ +/* Kernel module to match MAC address parameters. */ + +/* (C) 1999-2001 Paul `Rusty' Russell + * (C) 2002-2004 Netfilter Core Team + * + * 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. + */ + +/* extend from xt_mac.c for MAC address extend match operations, + * i.e, MAC/mask. + * BRCM, Jan, 31. 2019. + */ + + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Netfilter Core Team , Broadcom"); +MODULE_DESCRIPTION("Xtables: MAC address extend match"); +MODULE_ALIAS("ipt_mac_extend"); +MODULE_ALIAS("ip6t_mac_extend"); + +static bool mac_mt_extend(const struct sk_buff *skb, struct xt_action_param *par) +{ + const struct xt_mac_info_extend *info = par->matchinfo; + bool ret; + unsigned char skb_mac[ETH_ALEN], xt_mac[ETH_ALEN]; + int i; + + if (skb->dev == NULL || skb->dev->type != ARPHRD_ETHER) + return false; + if (skb_mac_header(skb) < skb->head) + return false; + if (skb_mac_header(skb) + ETH_HLEN > skb->data) + return false; + + for (i = 0; i < ETH_ALEN; i++) + { + skb_mac[i] = eth_hdr(skb)->h_source[i] & info->msk[i]; + xt_mac[i] = info->srcaddr[i] & info->msk[i]; + } + ret = ether_addr_equal(skb_mac, xt_mac); + ret ^= info->invert; + return ret; +} + +static struct xt_match mac_mt_reg_extend __read_mostly = { + .name = "mac-extend", + .revision = 0, + .family = NFPROTO_UNSPEC, + .match = mac_mt_extend, + .matchsize = sizeof(struct xt_mac_info_extend), + .hooks = (1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_IN) | + (1 << NF_INET_FORWARD), + .me = THIS_MODULE, +}; + +static int __init mac_mt_init_extend(void) +{ + return xt_register_match(&mac_mt_reg_extend); +} + +static void __exit mac_mt_exit_extend(void) +{ + xt_unregister_match(&mac_mt_reg_extend); +} + +module_init(mac_mt_init_extend); +module_exit(mac_mt_exit_extend); diff -urN linux-4.1.52-orig/net/netfilter/xt_mark.c linux-4.1.52/net/netfilter/xt_mark.c --- linux-4.1.52-orig/net/netfilter/xt_mark.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/netfilter/xt_mark.c 2020-02-07 01:47:54.000000000 +0800 @@ -28,6 +28,15 @@ mark_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_mark_tginfo2 *info = par->targinfo; + +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG_FEATURE) + skb->ipt_check |= IPT_TARGET_MARK; + skb->ipt_log.u32[BLOG_ORIGINAL_MARK_INDEX] = skb->mark; + skb->ipt_log.u32[BLOG_TARGET_MARK_INDEX] = (skb->mark & ~info->mask) ^ + info->mark; + if ( skb->ipt_check & IPT_TARGET_CHECK ) + return XT_CONTINUE; +#endif skb->mark = (skb->mark & ~info->mask) ^ info->mark; return XT_CONTINUE; diff -urN linux-4.1.52-orig/net/netfilter/xt_NFLOG.c linux-4.1.52/net/netfilter/xt_NFLOG.c --- linux-4.1.52-orig/net/netfilter/xt_NFLOG.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/netfilter/xt_NFLOG.c 2020-02-07 01:47:54.000000000 +0800 @@ -27,6 +27,11 @@ const struct xt_nflog_info *info = par->targinfo; struct nf_loginfo li; struct net *net = dev_net(par->in ? par->in : par->out); +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG_FEATURE) + skb->ipt_check |= IPT_TARGET_NFLOG; + if ( skb->ipt_check & IPT_TARGET_CHECK ) + return XT_CONTINUE; +#endif li.type = NF_LOG_TYPE_ULOG; li.u.ulog.copy_len = info->len; diff -urN linux-4.1.52-orig/net/netfilter/xt_NFQUEUE.c linux-4.1.52/net/netfilter/xt_NFQUEUE.c --- linux-4.1.52-orig/net/netfilter/xt_NFQUEUE.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/netfilter/xt_NFQUEUE.c 2020-02-07 01:47:54.000000000 +0800 @@ -32,6 +32,12 @@ { const struct xt_NFQ_info *tinfo = par->targinfo; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG_FEATURE) + skb->ipt_check |= IPT_TARGET_NFQUEUE; + if ( skb->ipt_check & IPT_TARGET_CHECK ) + return XT_CONTINUE; +#endif + return NF_QUEUE_NR(tinfo->queuenum); } diff -urN linux-4.1.52-orig/net/netfilter/xt_RATEEST.c linux-4.1.52/net/netfilter/xt_RATEEST.c --- linux-4.1.52-orig/net/netfilter/xt_RATEEST.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/netfilter/xt_RATEEST.c 2020-02-07 01:47:54.000000000 +0800 @@ -89,6 +89,12 @@ const struct xt_rateest_target_info *info = par->targinfo; struct gnet_stats_basic_packed *stats = &info->est->bstats; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG_FEATURE) + skb->ipt_check |= IPT_TARGET_RATEEST; + if ( skb->ipt_check & IPT_TARGET_CHECK ) + return XT_CONTINUE; +#endif + spin_lock_bh(&info->est->lock); stats->bytes += skb->len; stats->packets++; diff -urN linux-4.1.52-orig/net/netfilter/xt_SECMARK.c linux-4.1.52/net/netfilter/xt_SECMARK.c --- linux-4.1.52-orig/net/netfilter/xt_SECMARK.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/netfilter/xt_SECMARK.c 2020-02-07 01:47:54.000000000 +0800 @@ -35,6 +35,12 @@ u32 secmark = 0; const struct xt_secmark_target_info *info = par->targinfo; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG_FEATURE) + skb->ipt_check |= IPT_TARGET_SECMARK; + if ( skb->ipt_check & IPT_TARGET_CHECK ) + return XT_CONTINUE; +#endif + BUG_ON(info->mode != mode); switch (mode) { diff -urN linux-4.1.52-orig/net/netfilter/xt_SKIPLOG.c linux-4.1.52/net/netfilter/xt_SKIPLOG.c --- linux-4.1.52-orig/net/netfilter/xt_SKIPLOG.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/netfilter/xt_SKIPLOG.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,79 @@ +#if defined(CONFIG_BCM_KF_NETFILTER) +/* +* Copyright (c) 2003-2012 Broadcom Corporation +* All Rights Reserved +* +<:label-BRCM:2012:DUAL/GPL:standard + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ + +#include +#include +#include +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#include +#endif + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Broadcom"); +MODULE_DESCRIPTION("iptables stop logging module"); +MODULE_ALIAS("ipt_SKIPLOG"); + +static unsigned int +skiplog_tg(struct sk_buff *skb, const struct xt_action_param *par) +{ +#if defined(CONFIG_BLOG_FEATURE) + skb->ipt_check |= IPT_TARGET_SKIPLOG; + if ( skb->ipt_check & IPT_TARGET_CHECK ) + return XT_CONTINUE; +#endif + +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_skip(skb, blog_skip_reason_nf_xt_skiplog); +#endif + + return XT_CONTINUE; +} + +static struct xt_target skiplog_tg_reg __read_mostly = { + .name = "SKIPLOG", + .revision = 0, + .family = NFPROTO_UNSPEC, + .target = skiplog_tg, + .me = THIS_MODULE, +}; + +static int __init skiplog_tg_init(void) +{ + return xt_register_target(&skiplog_tg_reg); +} + +static void __exit skiplog_tg_exit(void) +{ + xt_unregister_target(&skiplog_tg_reg); +} + +module_init(skiplog_tg_init); +module_exit(skiplog_tg_exit); +#endif diff -urN linux-4.1.52-orig/net/netfilter/xt_TCPMSS.c linux-4.1.52/net/netfilter/xt_TCPMSS.c --- linux-4.1.52-orig/net/netfilter/xt_TCPMSS.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/netfilter/xt_TCPMSS.c 2020-02-07 01:47:54.000000000 +0800 @@ -211,6 +211,11 @@ __be16 newlen; int ret; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG_FEATURE) + skb->ipt_check |= IPT_TARGET_TCPMSS; + if ( skb->ipt_check & IPT_TARGET_CHECK ) + return XT_CONTINUE; +#endif ret = tcpmss_mangle_packet(skb, par, PF_INET, iph->ihl * 4, @@ -236,6 +241,12 @@ int tcphoff; int ret; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG_FEATURE) + skb->ipt_check |= IPT_TARGET_TCPMSS; + if ( skb->ipt_check & IPT_TARGET_CHECK ) + return XT_CONTINUE; +#endif + nexthdr = ipv6h->nexthdr; tcphoff = ipv6_skip_exthdr(skb, sizeof(*ipv6h), &nexthdr, &frag_off); if (tcphoff < 0) diff -urN linux-4.1.52-orig/net/netfilter/xt_TCPOPTSTRIP.c linux-4.1.52/net/netfilter/xt_TCPOPTSTRIP.c --- linux-4.1.52-orig/net/netfilter/xt_TCPOPTSTRIP.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/netfilter/xt_TCPOPTSTRIP.c 2020-02-07 01:47:54.000000000 +0800 @@ -91,6 +91,11 @@ static unsigned int tcpoptstrip_tg4(struct sk_buff *skb, const struct xt_action_param *par) { +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG_FEATURE) + skb->ipt_check |= IPT_TARGET_TCPOPTSTRIP; + if ( skb->ipt_check & IPT_TARGET_CHECK ) + return XT_CONTINUE; +#endif return tcpoptstrip_mangle_packet(skb, par, ip_hdrlen(skb), sizeof(struct iphdr) + sizeof(struct tcphdr)); } @@ -104,6 +109,12 @@ u_int8_t nexthdr; __be16 frag_off; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG_FEATURE) + skb->ipt_check |= IPT_TARGET_TCPOPTSTRIP; + if ( skb->ipt_check & IPT_TARGET_CHECK ) + return XT_CONTINUE; +#endif + nexthdr = ipv6h->nexthdr; tcphoff = ipv6_skip_exthdr(skb, sizeof(*ipv6h), &nexthdr, &frag_off); if (tcphoff < 0) diff -urN linux-4.1.52-orig/net/netfilter/xt_tcpudp.c linux-4.1.52/net/netfilter/xt_tcpudp.c --- linux-4.1.52-orig/net/netfilter/xt_tcpudp.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/netfilter/xt_tcpudp.c 2020-02-07 01:47:54.000000000 +0800 @@ -102,6 +102,14 @@ ntohs(th->dest), !!(tcpinfo->invflags & XT_TCP_INV_DSTPT))) return false; + +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG_FEATURE) + if ( tcpinfo->flg_mask & 0x10 ) { + struct sk_buff *skb_p; + skb_p = (struct sk_buff *)skb; + } +#endif + if (!FWINVTCP((((unsigned char *)th)[13] & tcpinfo->flg_mask) == tcpinfo->flg_cmp, XT_TCP_INV_FLAGS)) @@ -117,6 +125,30 @@ &par->hotdrop)) return false; } +#if defined(CONFIG_BCM_KF_NETFILTER) + if (tcpinfo->pure_ack) { + __u32 payload_len = 0xFFFFFFFF; + if (par->family == NFPROTO_IPV4) { + const struct iphdr *ih = (struct iphdr *)skb_network_header(skb); + struct iphdr _iph; + if (!ih || ih->version != 4 || ((ih->ihl * 4) < sizeof(_iph))) { + par->hotdrop = true; + return false; + } + payload_len = ntohs(ih->tot_len) - (ih->ihl * 4) - (th->doff * 4); + } else if (par->family == NFPROTO_IPV6) { + const struct ipv6hdr *i6h = (struct ipv6hdr *)skb_network_header(skb); + if (!i6h || i6h->version != 6) { + par->hotdrop = true; + return false; + } + payload_len = ntohs(i6h->payload_len) - (th->doff * 4); + } else { + return false; + } + return FWINVTCP(!payload_len, XT_TCP_INV_FLAGS); + } +#endif return true; } diff -urN linux-4.1.52-orig/net/netfilter/xt_TPROXY.c linux-4.1.52/net/netfilter/xt_TPROXY.c --- linux-4.1.52-orig/net/netfilter/xt_TPROXY.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/netfilter/xt_TPROXY.c 2020-02-07 01:47:54.000000000 +0800 @@ -298,6 +298,12 @@ struct udphdr _hdr, *hp; struct sock *sk; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG_FEATURE) + skb->ipt_check |= IPT_TARGET_TPROXY; + if ( skb->ipt_check & IPT_TARGET_CHECK ) + return XT_CONTINUE; +#endif + hp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_hdr), &_hdr); if (hp == NULL) return NF_DROP; diff -urN linux-4.1.52-orig/net/netfilter/xt_TRACE.c linux-4.1.52/net/netfilter/xt_TRACE.c --- linux-4.1.52-orig/net/netfilter/xt_TRACE.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/netfilter/xt_TRACE.c 2020-02-07 01:47:54.000000000 +0800 @@ -13,6 +13,13 @@ static unsigned int trace_tg(struct sk_buff *skb, const struct xt_action_param *par) { + +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG_FEATURE) + skb->ipt_check |= IPT_TARGET_TRACE; + if ( skb->ipt_check & IPT_TARGET_CHECK ) + return XT_CONTINUE; +#endif + skb->nf_trace = 1; return XT_CONTINUE; } diff -urN linux-4.1.52-orig/net/packet/af_packet.c linux-4.1.52/net/packet/af_packet.c --- linux-4.1.52-orig/net/packet/af_packet.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/packet/af_packet.c 2020-02-07 01:47:54.000000000 +0800 @@ -2692,6 +2692,7 @@ { struct packet_sock *po = pkt_sk(sk); struct net_device *dev_curr; + __be16 proto_curr; bool need_rehook; struct net_device *dev = NULL; @@ -2747,6 +2748,8 @@ BUG_ON(po->running); po->num = proto; po->prot_hook.type = proto; +#if !defined(CONFIG_BCM_KF_MISC_BACKPORTS) +#endif if (unlikely(unlisted)) { dev_put(dev); diff -urN linux-4.1.52-orig/net/phonet/af_phonet.c linux-4.1.52/net/phonet/af_phonet.c --- linux-4.1.52-orig/net/phonet/af_phonet.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/phonet/af_phonet.c 2020-02-07 01:47:54.000000000 +0800 @@ -34,6 +34,52 @@ #include #include +#ifdef CONFIG_BCM_KF_PHONET +#ifdef ACTIVATE_PHONET_DEBUG + +phonet_debug_state phonet_dbg_state = OFF; + +static ssize_t phonet_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + + switch (phonet_dbg_state) { + case ON: + return sprintf(buf, "on\n"); + case OFF: + return sprintf(buf, "off\n"); + case DATA: + return sprintf(buf, "data\n"); + default: + return -ENODEV; + } + + return -ENODEV; +} + +static ssize_t phonet_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t n) +{ + + if (sysfs_streq(buf, "on")) { + phonet_dbg_state = ON; + pr_alert( + "Phonet traces activated\nBe Careful do not trace Dmesg in MTD\n"); + } else if (sysfs_streq(buf, "off")) { + phonet_dbg_state = OFF; + } else if (sysfs_streq(buf, "data")) { + phonet_dbg_state = DATA; + } else { + pr_alert("please use on/off/data\n"); + } + return -EINVAL; +} + +static struct kobj_attribute phonet_attr = +__ATTR(phonet_dbg, 0644, phonet_show, phonet_store); +#endif +#endif /* CONFIG_BCM_KF_PHONET */ + /* Transport protocol registration */ static struct phonet_protocol *proto_tab[PHONET_NPROTO] __read_mostly; @@ -68,8 +114,13 @@ struct phonet_protocol *pnp; int err; +#ifdef CONFIG_BCM_KF_PHONET + /* skip the capable check in order to allow user + * applications creating phonet socket */ +#else if (!capable(CAP_SYS_ADMIN)) return -EPERM; +#endif if (protocol == 0) { /* Default protocol selection */ @@ -112,6 +163,10 @@ pn->sobject = 0; pn->dobject = 0; pn->resource = 0; +#ifdef CONFIG_BCM_KF_PHONET + pn->resource_type = 0; + pn->resource_subtype = 0; +#endif sk->sk_prot->init(sk); err = 0; @@ -163,6 +218,9 @@ { struct phonethdr *ph; int err; +#ifdef CONFIG_BCM_KF_PHONET + int i; +#endif if (skb->len + 2 > 0xffff /* Phonet length field limit */ || skb->len + sizeof(struct phonethdr) > dev->mtu) { @@ -192,6 +250,18 @@ skb->priority = 0; skb->dev = dev; +#ifdef CONFIG_BCM_KF_PHONET + PN_PRINTK("pn_send rdev %x sdev %x res %x robj %x sobj %x netdev=%s\n", + ph->pn_rdev, ph->pn_sdev, ph->pn_res, ph->pn_robj, + ph->pn_sobj, dev->name); + PN_DATA_PRINTK("PHONET : skb data = %d\nPHONET :", skb->len); + for (i = 1; i <= skb->len; i++) { + PN_DATA_PRINTK(" %02x", skb->data[i - 1]); + if ((i % 8) == 0) + PN_DATA_PRINTK("\n"); + } +#endif + if (skb->pkt_type == PACKET_LOOPBACK) { skb_reset_mac_header(skb); skb_orphan(skb); @@ -280,7 +350,11 @@ goto drop; if (!pn_addr(src)) +#ifdef CONFIG_BCM_KF_PHONET + src = pn_object(saddr, pn_port(src)); +#else src = pn_object(saddr, pn_obj(src)); +#endif err = pn_send(skb, dev, dst, src, res, 0); dev_put(dev); @@ -376,6 +450,9 @@ struct phonethdr *ph; struct sockaddr_pn sa; u16 len; +#ifdef CONFIG_BCM_KF_PHONET + int i; +#endif skb = skb_share_check(skb, GFP_ATOMIC); if (!skb) @@ -397,6 +474,26 @@ pn_skb_get_dst_sockaddr(skb, &sa); +#ifdef CONFIG_BCM_KF_PHONET + PN_PRINTK( + "phonet rcv : phonet hdr rdev %x sdev %x res %x robj %x sobj %x netdev=%s orig_netdev=%s\n", + ph->pn_rdev, ph->pn_sdev, ph->pn_res, ph->pn_robj, ph->pn_sobj, + dev->name, orig_dev->name); + + PN_DATA_PRINTK("PHONET : skb data = %d\nPHONET :", skb->len); + for (i = 1; i <= skb->len; i++) { + PN_DATA_PRINTK(" %02x", skb->data[i - 1]); + if ((i % 8) == 0) + PN_DATA_PRINTK("\n"); + } + + /* check if this is multicasted */ + if (pn_sockaddr_get_object(&sa) == PNOBJECT_MULTICAST) { + pn_deliver_sock_broadcast(net, skb); + goto out; + } +#endif + /* check if this is broadcasted */ if (pn_sockaddr_get_addr(&sa) == PNADDR_BROADCAST) { pn_deliver_sock_broadcast(net, skb); @@ -413,7 +510,11 @@ /* check if we are the destination */ if (phonet_address_lookup(net, pn_sockaddr_get_addr(&sa)) == 0) { /* Phonet packet input */ +#ifdef CONFIG_BCM_KF_PHONET + struct sock *sk = pn_find_sock_by_sa_and_skb(net, &sa, skb); +#else struct sock *sk = pn_find_sock_by_sa(net, &sa); +#endif if (sk) return sk_receive_skb(sk, skb, 0); @@ -438,9 +539,16 @@ __skb_push(skb, sizeof(struct phonethdr)); skb->dev = out_dev; if (out_dev == dev) { +#ifdef CONFIG_BCM_KF_PHONET + net_dbg_ratelimited(KERN_ERR + "Phonet loop to %02X on %s ori=%s\n", + pn_sockaddr_get_addr(&sa), dev->name, + orig_dev->name); +#else net_dbg_ratelimited("Phonet loop to %02X on %s\n", pn_sockaddr_get_addr(&sa), dev->name); +#endif goto out_dev; } /* Some drivers (e.g. TUN) do not allocate HW header space */ @@ -507,6 +615,13 @@ static int __init phonet_init(void) { int err; +#ifdef CONFIG_BCM_KF_PHONET +#ifdef ACTIVATE_PHONET_DEBUG + err = sysfs_create_file(kernel_kobj, &phonet_attr.attr); + if (err) + pr_alert("phonet sysfs_create_file failed: %d\n", err); +#endif +#endif err = phonet_device_init(); if (err) diff -urN linux-4.1.52-orig/net/phonet/Kconfig linux-4.1.52/net/phonet/Kconfig --- linux-4.1.52-orig/net/phonet/Kconfig 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/phonet/Kconfig 2020-02-07 01:47:54.000000000 +0800 @@ -14,3 +14,28 @@ To compile this driver as a module, choose M here: the module will be called phonet. If unsure, say N. +if BCM_KF_PHONET +if PHONET +config LD_PHONET + tristate "Line Discipline for Phonet" + help + Line discipline for transmitting and receiving phonet packet to modem + via TTY device + +config LD_TMODEM + tristate "Line Discipline for Thin Modem" + help + Line discipline for transmitting and receiving thin modem packet to + modem via TTY device + +config PHONET_DEBUG + boolean "Debug support for PHONET drivers" + depends on DEBUG_KERNEL + help + Say "yes" to enable phonet debug messaging + + Activate Phonet header logging + + Activate Phonet data logging +endif +endif diff -urN linux-4.1.52-orig/net/phonet/ld_phonet.c linux-4.1.52/net/phonet/ld_phonet.c --- linux-4.1.52-orig/net/phonet/ld_phonet.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/phonet/ld_phonet.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,1004 @@ +#ifdef CONFIG_BCM_KF_PHONET +/* +<:copyright-BRCM:2011:DUAL/GPL:standard + + Copyright (c) 2011 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +/* + * File: ld_pnonet.c + * + * Phonet device TTY line discipline + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +//#include /* AT-ISI Separation */ // TODO: do we need this header? it is not in 3.4.11 +#include +MODULE_AUTHOR("david RMC"); +MODULE_DESCRIPTION("Phonet TTY line discipline"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS_LDISC(N_PHONET); + +/* Comment - 01 */ +/*AT+ATSTART will be entered by closing ISI application. By the methods under + implementation for disconnecting an application in NTrace)the same is + expected to be available for TnT), it is expected that, congestion condition + will be present when executing AT+ATSTART allowing a few bytes of room from + underlying layer. Hence, keeping simplicity later write_back functionality + is not used here as it is done for normal transfer.*/ + +/* Comment - 02 */ +/*If control is transferred to AT Parser, activateld can close the tty + interfering tty->write. Hence, tty->write is done first. Only + programming error can fail AT switch . practically, no other reasons apply. + Tty->write will synchronously write to the lower driver which can later + transfer the data in tty independent way. In testing no synchronization + issue is seen.*/ + +#define SEND_QUEUE_LOW 10 +#define SEND_QUEUE_HIGH 100 +#define PHONET_SENDING 1 /* Bit 1 = 0x02 */ +#define PHONET_FLOW_OFF_SENT 4 /* Bit 4 = 0x10 */ +#define MAX_WRITE_CHUNK 8192 +#define ISI_MSG_HEADER_SIZE 6 +/*#define MAX_BUFF_SIZE 20000*/ +#define MAX_BUFF_SIZE 65535 + +#define LD_PHONET_SWITCH 4 +#define LD_PHONET_NEW_ISI_MSG 0 +#define LD_PHONET_ISI_MSG_LEN 1 +#define LD_PHONET_ISI_MSG_NO_LEN 2 + +#define LD_PHONET_BUFFER_LEN 1048576 +#define LD_PHONET_INIT_LEN 0 + +#define LD_ATCMD_BUFFER_LEN 1024 + +#define LD_WAKEUP_DATA_INIT 0 +#define ATPLIB_AT_CMD_MAX 1024 + +#define LD_SWITCH_ATSTART_RESP 1 +#define LD_SWITCH_MODECHAN02_RESP 2 + +#define GUID_HEADER_BYTE1 0xdd +#define GUID_HEADER_BYTE2 0x7f +#define GUID_HEADER_BYTE3 0x21 +#define GUID_HEADER_BYTE4 0x9a + + +struct ld_phonet { + struct tty_struct *tty; + wait_queue_head_t wait; + spinlock_t lock; + unsigned long flags; + struct sk_buff *skb; + unsigned long len; + unsigned long lentorcv; + unsigned long datarcv; + unsigned long state; + struct net_device *dev; + struct list_head node; + struct sk_buff_head head; + char *tty_name; + int ld_phonet_state; + int n_data_processed; + int n_data_sent; + int n_remaining_data; + bool link_up; + int nb_try_to_tx; + unsigned char *ld_atcmd_buffer; +}; + +static int ld_buff_len; /* LD Phonet Tx Backlog buffer Len */ +static struct workqueue_struct *ld_phonet_wq; + +/* Work to hanlde TTY wake up */ +struct ld_tty_wakeup_work_t { + struct work_struct ld_work; + /*This holds TTY info for TTY wakeup */ + struct tty_struct *ld_work_write_wakeup_tty; +}; +static struct ld_tty_wakeup_work_t *ld_tty_wakeup_work; + +/* Wotk to Handle AT+ATSTART Switch */ +struct ld_uart_switch_work_t { + struct work_struct ld_work; + unsigned long at_modechan02_mode; +}; +static struct ld_uart_switch_work_t *ld_uart_switch_work; + +/* Ld phonet statistics */ +static unsigned long ld_phonet_tx_request_count; +static unsigned long ld_phonet_rx_request_count; +static unsigned long ld_phonet_tx_bytes; +static unsigned long ld_phonet_rx_bytes; +static unsigned long ld_phonet_hangup_events; +static unsigned long ld_phonet_drop_events; + +/* AT-ISI Separation ends */ +#define LD_PHONET_DEBUG 0 +#if LD_PHONET_DEBUG +#define dbg(fmt, ...) printk(fmt, ## __VA_ARGS__) +#else +#define dbg(fmt, ...) +#endif + +static int ld_pn_net_open(struct net_device *dev) +{ + netif_wake_queue(dev); + return 0; +} + +static int ld_pn_net_close(struct net_device *dev) +{ + netif_stop_queue(dev); + return 0; +} + +static void ld_tx_overflow(void) +{ + ld_phonet_drop_events++; + pr_crit( + "##### ATTENTION : LD Phonet Transmit overflow events %lu : 1 MB #####", + ld_phonet_drop_events); +} + +static int ld_pn_handle_tx(struct ld_phonet *ld_pn) +{ + struct tty_struct *tty = ld_pn->tty; + struct sk_buff *skb; + int tty_wr, len, room, i; + + if (tty == NULL) + return 0; + /* Enter critical section */ + if (test_and_set_bit(PHONET_SENDING, &ld_pn->state)) + return 0; + + /* skb_peek is safe because handle_tx is called after skb_queue_tail */ + while ((skb = skb_peek(&ld_pn->head)) != NULL) { + + /* Make sure you don't write too much */ + len = skb->len; + room = tty_write_room(tty); + + if (!room) { + if (ld_buff_len > LD_PHONET_BUFFER_LEN) { + if (ld_pn->link_up == true) + ld_tx_overflow(); + ld_pn->link_up = false; + /* Flush TX queue */ + while ((skb = + skb_dequeue(&ld_pn->head)) != NULL) { + skb->dev->stats.tx_dropped++; + dbg("Flush TX queue tx_dropped = %d", + skb->dev->stats.tx_dropped); + if (in_interrupt()) + dev_kfree_skb_irq(skb); + else + kfree_skb(skb); + } + ld_buff_len = LD_PHONET_INIT_LEN; + goto error; + } else { /* FALLBACK TRIAL */ + dbg( + "ld_pn_handle_tx no room, waiting for previous to be sent..:\n"); + + if (!test_bit(TTY_DO_WRITE_WAKEUP, + &tty->flags)) { + /* wakeup bit is not set, set it */ + dbg( + "ld_pn_handle_tx Setting TTY_DO_WRITE_WAKEUP bit...\n"); + set_bit(TTY_DO_WRITE_WAKEUP, + &tty->flags); + } else { + dbg( + "ld_pn_handle_tx TTY_DO_WRITE_WAKEUP bit already set!...\n"); + } + } + break; + } + + /* Get room => reset nb_try_to_tx counter */ + ld_pn->nb_try_to_tx = 0; + + if (len > room) + len = room; + + tty_wr = tty->ops->write(tty, skb->data, len); + ld_buff_len -= tty_wr; + if (tty_wr > 0) + ld_phonet_tx_bytes += tty_wr; + if (ld_buff_len < LD_PHONET_INIT_LEN) + ld_buff_len = LD_PHONET_INIT_LEN; + ld_pn->dev->stats.tx_packets++; + ld_pn->dev->stats.tx_bytes += tty_wr; + dbg(" Response start\n"); + for (i = 1; i <= len; i++) { + dbg(" %02x", skb->data[i - 1]); + if ((i % 8) == 0) + dbg("\n"); + } + dbg("\n"); + dbg(" Response stop\n"); + /* Error on TTY ?! */ + if (tty_wr < 0) + goto error; + /* Reduce buffer written, and discard if empty */ + skb_pull(skb, tty_wr); + if (skb->len == 0) { + struct sk_buff *tmp = skb_dequeue(&ld_pn->head); + BUG_ON(tmp != skb); + if (in_interrupt()) + dev_kfree_skb_irq(skb); + else + kfree_skb(skb); + } + } + /* Send flow off if queue is empty */ + clear_bit(PHONET_SENDING, &ld_pn->state); + return NETDEV_TX_OK; +error: + clear_bit(PHONET_SENDING, &ld_pn->state); + return NETDEV_TX_OK; +} + +static int ld_pn_net_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct ld_phonet *ld_pn; + u8 *ptr; + + BUG_ON(dev == NULL); + ld_pn = netdev_priv(dev); + if ((ld_pn == NULL) || (ld_pn->tty == NULL)) { + if (in_interrupt()) + dev_kfree_skb_irq(skb); + else + kfree_skb(skb); + return NETDEV_TX_OK; + } + ld_phonet_tx_request_count++; + ptr = skb_push(skb, 6); + ptr[0] = GUID_HEADER_BYTE1; + ptr[1] = GUID_HEADER_BYTE2; + ptr[2] = GUID_HEADER_BYTE3; + ptr[3] = GUID_HEADER_BYTE4; + ptr[4] = skb->data[10]; + ptr[5] = skb->data[11]; + PN_PRINTK("ld_pn_net_xmit: send skb to %s", dev->name); + if (ld_pn->link_up == true) { + skb_queue_tail(&ld_pn->head, skb); + ld_buff_len += skb->len; + return ld_pn_handle_tx(ld_pn); + } else { + if (tty_write_room(ld_pn->tty)) { + /* link is up again */ + ld_pn->link_up = true; + ld_pn->nb_try_to_tx = 0; + skb_queue_tail(&ld_pn->head, skb); + ld_buff_len += skb->len; + return ld_pn_handle_tx(ld_pn); + } else { + if (in_interrupt()) + dev_kfree_skb_irq(skb); + else + kfree_skb(skb); + dev->stats.tx_dropped++; + dbg("tx_dropped = %d", dev->stats.tx_dropped); + return NETDEV_TX_OK; + } + } +} + +static int ld_pn_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + switch (cmd) { + case SIOCPNGAUTOCONF: + phonet_route_add(dev, PN_DEV_PC); + dev_open(dev); + netif_carrier_on(dev); + /* Return NOIOCTLCMD so Phonet won't do it again */ + return -ENOIOCTLCMD; + } + return -ENOIOCTLCMD; +} + +static int ld_pn_net_mtu(struct net_device *dev, int new_mtu) +{ + if ((new_mtu < PHONET_MIN_MTU) || (new_mtu > PHONET_MAX_MTU)) + return -EINVAL; + dev->mtu = new_mtu; + return 0; +} + +static const struct net_device_ops ld_pn_netdev_ops = { + .ndo_open = ld_pn_net_open, + .ndo_stop = ld_pn_net_close, + .ndo_start_xmit = ld_pn_net_xmit, + .ndo_do_ioctl = ld_pn_net_ioctl, + .ndo_change_mtu = ld_pn_net_mtu, +}; + +#define PN_ADDR_LEN 1 +#define PN_QUEUE_LEN 5 +#define PN_HARD_HEADER_LEN 1 +static void ld_pn_net_setup(struct net_device *dev) +{ + dev->features = 0; + dev->type = ARPHRD_PHONET; + dev->flags = IFF_POINTOPOINT | IFF_NOARP; + dev->mtu = PHONET_DEV_MTU; + dev->hard_header_len = PN_HARD_HEADER_LEN; + dev->dev_addr[0] = PN_MEDIA_USB; + dev->addr_len = PN_ADDR_LEN; + dev->tx_queue_len = PN_QUEUE_LEN; + + dev->netdev_ops = &ld_pn_netdev_ops; + dev->destructor = free_netdev; + dev->header_ops = &phonet_header_ops; +}; + +/***************************************** +*** TTY +******************************************/ +#define LD_RECEIVE_ROOM 65536 +static int ld_phonet_ldisc_open(struct tty_struct *tty) +{ + + struct ld_phonet *ld_pn; + struct net_device *dev; + int err = 0; + dbg("ld_phonet_ldisc_open starts\n"); + + /* Create net device */ + dev = alloc_netdev(sizeof(*ld_pn), "upnlink%d", ld_pn_net_setup); + if (!dev) + return -ENOMEM; + + ld_pn = netdev_priv(dev); + spin_lock_init(&ld_pn->lock); + netif_carrier_off(dev); + skb_queue_head_init(&ld_pn->head); + ld_pn->tty = tty; + tty->disc_data = ld_pn; + tty->receive_room = LD_RECEIVE_ROOM; + ld_pn->dev = dev; + ld_pn->skb = NULL; + ld_pn->len = 0; + ld_pn->lentorcv = 0; + ld_pn->datarcv = 0; + ld_pn->ld_phonet_state = LD_PHONET_NEW_ISI_MSG; + ld_pn->n_data_processed = 0; + ld_pn->n_data_sent = 0; + ld_pn->n_remaining_data = 0; + ld_pn->link_up = true; + ld_pn->nb_try_to_tx = 0; + ld_pn->ld_atcmd_buffer = kmalloc(LD_ATCMD_BUFFER_LEN, GFP_KERNEL); + if (ld_pn->ld_atcmd_buffer == NULL) + goto LDISC_ERROR; + err = register_netdev(dev); + if (err) +LDISC_ERROR: + free_netdev(dev); + else + ld_tty_wakeup_work->ld_work_write_wakeup_tty = tty; + + dbg("ld_phonet_ldisc_open exits err = %d\n", err); + return err; + +} + +static void ld_phonet_ldisc_close(struct tty_struct *tty) +{ + struct ld_phonet *ld_pn = tty->disc_data; + + tty->disc_data = NULL; + kfree(ld_pn->ld_atcmd_buffer); + ld_pn->tty = NULL; + ld_tty_wakeup_work->ld_work_write_wakeup_tty = NULL; + unregister_netdev(ld_pn->dev); +} + +static void ld_phonet_ldisc_init_transfer + (struct ld_phonet *ld_pn, const unsigned char *cp, int count) +{ + struct sk_buff *skb = NULL; + unsigned int msglen = 0; + + struct phonethdr *ph = NULL; + int i; + + dbg("ld_phonet: initiate transfer Data Sent = %d ", ld_pn->n_data_sent); + dbg("Data Processed = %d ", ld_pn->n_data_processed); + dbg("Data Remaining = %d\n", ld_pn->n_remaining_data); + + /* Check if there is still data in cp */ + while (ld_pn->n_data_processed < count) { + /* Check if extract length is possible */ + if ((count - ld_pn->n_data_processed) > ISI_MSG_HEADER_SIZE) { + /* Extract length */ + /* Move 1 byte since media parameter is not there in */ + /* phonethdr structure */ + ph = (struct phonethdr *) + (cp + ld_pn->n_data_processed + sizeof(char)); + msglen = get_unaligned_be16(&ph->pn_length); + ld_pn->len = msglen + ISI_MSG_HEADER_SIZE; + + if (ld_pn->len == ISI_MSG_HEADER_SIZE) { + printk( + "ld_phonet: Extracted ISI msg len = ISI_MSG_HEADER_SIZE, dumping rest of buffer"); + goto out; + } + + /* Alloc SKBuff */ + skb = netdev_alloc_skb(ld_pn->dev, ld_pn->len); + if (NULL == skb) { + /* TBD handle error */ + return; + } + + skb->dev = ld_pn->dev; + skb->protocol = htons(ETH_P_PHONET); + skb_reset_mac_header(skb); + ld_pn->skb = skb; + + /* check if we receive complete data in this */ + /* usb frame */ + if (ld_pn->len <= (count - ld_pn->n_data_processed)) { + /* We received complete data in this usb */ + /* frame */ + /* copy the ISI buffer */ + memcpy(skb_put(skb, ld_pn->len), + cp + ld_pn->n_data_processed, + ld_pn->len); + ld_pn->n_data_processed += ld_pn->len; + + /* Send to Phonet */ + ld_pn->dev->stats.rx_packets++; + ld_pn->dev->stats.rx_bytes += skb->len; + __skb_pull(skb, 1); + dbg("Request buffer start\n"); + for (i = 1; i <= skb->len; i++) { + dbg("%02x", skb->data[i - 1]); + if (i % 8 == 0) + dbg("\n"); + } + + dbg("Request buffer end\n"); + dbg( + "calling netif_rx inside initiate_transfer ld_pn->len=%d\n", + ld_pn->len); + ld_pn->n_data_sent += ld_pn->len; + ld_phonet_rx_bytes += skb->len; + netif_rx(skb); + + /* TBD : Reset pointers */ + ld_pn->len = LD_PHONET_INIT_LEN; + } else { + /* We receive only partial ISI message */ + /* Copy the partial ISI message */ + memcpy(skb_put(skb, count - + ld_pn->n_data_processed), cp + + ld_pn->n_data_processed, count - + ld_pn->n_data_processed); + ld_pn->ld_phonet_state = LD_PHONET_ISI_MSG_LEN; + ld_pn->n_remaining_data = ld_pn->len - + (count - ld_pn->n_data_processed); + ld_pn->n_data_processed += count - + ld_pn->n_data_processed; + + return; + } + } else { + /* Not able to extract length since received */ + /* usb frame length is */ + /* less than ISI message header size */ + + /* Alloc SKBuff with max size */ + skb = netdev_alloc_skb(ld_pn->dev, MAX_BUFF_SIZE); + if (NULL == skb) { + /* TBD handle error */ + return; + } + + skb->dev = ld_pn->dev; + skb->protocol = htons(ETH_P_PHONET); + skb_reset_mac_header(skb); + ld_pn->skb = skb; + + /* Copy available data */ + memcpy(skb_put(skb, count - ld_pn->n_data_processed), + cp + ld_pn->n_data_processed, count - + ld_pn->n_data_processed); + ld_pn->ld_phonet_state = LD_PHONET_ISI_MSG_NO_LEN; + + ld_pn->len += count - ld_pn->n_data_processed; + ld_pn->n_data_processed += + count - ld_pn->n_data_processed; + + return; + } + } + +out: + /* No more data in cp */ + ld_pn->ld_phonet_state = LD_PHONET_NEW_ISI_MSG; + ld_pn->len = 0; + ld_pn->n_data_processed = 0; + ld_pn->n_data_sent = 0; + ld_pn->n_remaining_data = 0; + + return; +} + +/* AT-ISI Message Separation Starts */ + +ssize_t ld_set_manualsw(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +int stop_isi; + +/* AT-ISI Message Separation Ends */ +static void ld_phonet_ldisc_receive + (struct tty_struct *tty, const unsigned char *cp, char *fp, int count) +{ + struct ld_phonet *ld_pn = tty->disc_data; + struct sk_buff *skb = ld_pn->skb; + unsigned long flags = 0; + unsigned int msglen = 0, i; + int check_at = 27; + struct phonethdr *ph = NULL; + + ld_phonet_rx_request_count++; + if (ld_pn->link_up == false) { + /* data received from PC => can TX */ + ld_pn->link_up = true; + + ld_pn->nb_try_to_tx = 0; + } + PN_PRINTK("ld_phonet_ldisc_receive: receive %d data", count); + for (i = 1; i <= count; i++) { + PN_DATA_PRINTK(" %02x", cp[i - 1]); + if ((i % 8) == 0) + PN_DATA_PRINTK("\n"); + } + + spin_lock_irqsave(&ld_pn->lock, flags); + + /* Whenever you receive a new USB frame Data Processed should be reset */ + ld_pn->n_data_processed = 0; + + while (1) { + switch (ld_pn->ld_phonet_state) { + case LD_PHONET_NEW_ISI_MSG: + { + int first_byte = 0; + if (count >= 1) { + if (*cp) { + first_byte = *cp; + dbg( + "case LD_PHONET_NEW_ISI_MSG: %d\n", + *cp); + } + } else + dbg("case LD_PHONET_NEW_ISI_MSG\n"); + + if ((count >= 1) && (first_byte != check_at)) { + dbg("MATCH FOR change mode %c\n", *cp); + ld_pn->ld_phonet_state = + LD_PHONET_SWITCH; + continue; + } + + /* AT-ISI Message Separation Ends */ + ld_phonet_ldisc_init_transfer(ld_pn, cp, + count); + break; + } + case LD_PHONET_ISI_MSG_LEN: + /* check if Remaining Data is complete */ + if (ld_pn->n_remaining_data > count) { + /* We dont receive complete data */ + /* Copy the available data */ + memcpy(skb_put(skb, count), cp + + ld_pn->n_data_processed, count); + ld_pn->n_data_processed += count; + ld_pn->ld_phonet_state = LD_PHONET_ISI_MSG_LEN; + ld_pn->n_remaining_data -= count; + } else { + /* We have complete data available */ + /* Copy remaining data */ + memcpy(skb_put(skb, ld_pn->n_remaining_data), + cp + ld_pn->n_data_processed, + ld_pn->n_remaining_data); + /* Send to Phonet */ + ld_pn->dev->stats.rx_packets++; + ld_pn->dev->stats.rx_bytes += skb->len; + __skb_pull(skb, sizeof(char)); + dbg("Request buffer start\n"); + for (i = 1; i <= skb->len; i++) { + dbg("%02x", skb->data[i - 1]); + if (i % 8 == 0) + dbg("\n"); + } + dbg("Request buffer end\n"); + dbg( + "calling netif_rx inside ldisc_receive first ld_pn->len=%d\n", + ld_pn->len); + ld_pn->n_data_sent += ld_pn->len; + ld_phonet_rx_bytes += skb->len; + netif_rx(skb); + + /* TBD : Update pointers */ + ld_pn->n_data_sent += ld_pn->n_remaining_data; + ld_pn->n_data_processed += + ld_pn->n_remaining_data; + ld_pn->len = LD_PHONET_INIT_LEN; + + /* Initiate a new ISI transfer */ + ld_phonet_ldisc_init_transfer + (ld_pn, cp, count); + } + break; + + case LD_PHONET_ISI_MSG_NO_LEN: + /*Check if we can extact length */ + if ((ld_pn->len + count) >= ISI_MSG_HEADER_SIZE) { + + /* Copy remaining header to SKBuff to extract */ + /* length */ + memcpy(skb_put(skb, ISI_MSG_HEADER_SIZE - + ld_pn->len), + cp + ld_pn->n_data_processed, + ISI_MSG_HEADER_SIZE - ld_pn->len); + ph = (struct phonethdr *) + (skb->data + sizeof(char)); + msglen = get_unaligned_be16(&ph->pn_length); + + ld_pn->n_data_processed += + ISI_MSG_HEADER_SIZE - ld_pn->len; + + /* Check if we receive complete data */ + if ((count + ld_pn->len) < + (msglen + ISI_MSG_HEADER_SIZE)) { + /* We have not received complete data */ + /* Copy available data */ + memcpy(skb_put(skb, count - + (ISI_MSG_HEADER_SIZE - + ld_pn->len)), + cp + ld_pn->n_data_processed, + count - (ISI_MSG_HEADER_SIZE - + ld_pn->len)); + ld_pn->ld_phonet_state = + LD_PHONET_ISI_MSG_LEN; + ld_pn->n_remaining_data = + (msglen + ISI_MSG_HEADER_SIZE) - + (count + ld_pn->len); + ld_pn->n_data_processed += + count - (ISI_MSG_HEADER_SIZE - + ld_pn->len); + + /* Reset pointers */ + ld_pn->len = msglen + + ISI_MSG_HEADER_SIZE; + + /* return; */ + break; + } else { + /* We receive complete data */ + /* Copy remaining data */ + memcpy(skb_put(skb, + (msglen + ISI_MSG_HEADER_SIZE) + - (ld_pn->len + + ld_pn->n_data_processed)), + cp + ld_pn->n_data_processed, + (msglen + ISI_MSG_HEADER_SIZE) - + (ld_pn->len + + ld_pn->n_data_processed)); + + /* Send to Phonet */ + ld_pn->dev->stats.rx_packets++; + ld_pn->dev->stats.rx_bytes += skb->len; + __skb_pull(skb, sizeof(char)); + dbg("Request buffer start\n"); + for (i = 1; i <= skb->len; i++) { + dbg("%02x", skb->data[i - 1]); + if (i % 8 == 0) + dbg("\n"); + } + + dbg("Request buffer end\n"); + dbg( + "calling netif_rx inside ldisc_receive second ld_pn->len= %d\n", + ld_pn->len); + ld_phonet_rx_bytes += skb->len; + netif_rx(skb); + + ld_pn->n_data_sent += (msglen + + ISI_MSG_HEADER_SIZE) + - (ld_pn->len + + ld_pn->n_data_processed); + + ld_pn->n_data_processed += (msglen + + ISI_MSG_HEADER_SIZE) + - (ld_pn->len + + ld_pn->n_data_processed); + + /* Reset len as skb buffer */ + /* is sent to phonet */ + ld_pn->len = LD_PHONET_INIT_LEN; + + /* Check if we still have data in cp */ + if (count > ld_pn->n_data_processed) { + /* We still have data in cp */ + /* Initiate new ISI transfer */ + ld_phonet_ldisc_init_transfer( + ld_pn, cp, count); + } else { + /* No more data in cp */ + ld_pn->ld_phonet_state = + LD_PHONET_NEW_ISI_MSG; + + /* Reset pointers */ + ld_pn->len = 0; + ld_pn->n_data_processed = 0; + ld_pn->n_data_sent = 0; + ld_pn->n_remaining_data = 0; + } + } + } else { + /* Cannot extract length */ + /* Copy available data */ + memcpy(skb_put(skb, count), cp + + ld_pn->n_data_processed, count); + ld_pn->len += count; + ld_pn->ld_phonet_state = + LD_PHONET_ISI_MSG_NO_LEN; + ld_pn->n_data_processed += count; + } + break; + + default: + break; + } + break; + } + + spin_unlock_irqrestore(&ld_pn->lock, flags); +} + +#define AT_START_LEN 10 +#define AT_MODECHAN_LEN 15 +static void ld_uart_switch_function(struct work_struct *work) +{ + struct ld_uart_switch_work_t *at_mode_work; + at_mode_work = (struct ld_uart_switch_work_t *)work; + set_current_state(TASK_INTERRUPTIBLE); + ld_uart_switch_work->at_modechan02_mode = 0; + return; +} + +static void ld_tty_wakeup_workfunction(struct work_struct *work) +{ + struct tty_struct *tty; + struct ld_phonet *ld_pn; + struct ld_tty_wakeup_work_t *ld_work_tty_wk = + (struct ld_tty_wakeup_work_t *)work; + + if (ld_work_tty_wk == NULL) { + dbg("TTY work NULL\n"); + return; + } + + tty = ld_work_tty_wk->ld_work_write_wakeup_tty; + if (tty == NULL) { + dbg("LD Work Queue tty Data NULL\n"); + return; + } + + clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); + + ld_pn = tty->disc_data; + if (ld_pn == NULL) { + dbg("LD PN Work Queue DATA NULL\n"); + return; + } + + BUG_ON(ld_pn->tty != tty); + ld_pn_handle_tx(ld_pn); + return; +} + +static void ld_phonet_ldisc_write_wakeup(struct tty_struct *tty) +{ + ld_tty_wakeup_work->ld_work_write_wakeup_tty = tty; + queue_work(ld_phonet_wq, (struct work_struct *)ld_tty_wakeup_work); +} + +int ld_phonet_hangup_wait(void *data) +{ + return NETDEV_TX_OK; +} + +static int ld_phonet_ldisc_hangup(struct tty_struct *tty) +{ + struct ld_phonet *ld_pn; + struct sk_buff *skb; + + /* Flush TX queue */ + ld_pn = tty->disc_data; + ld_phonet_hangup_events++; + wait_on_bit_lock(&ld_pn->state, PHONET_SENDING, + ld_phonet_hangup_wait, TASK_KILLABLE); + + while ((skb = skb_dequeue(&ld_pn->head)) != NULL) { + skb->dev->stats.tx_dropped++; + if (in_interrupt()) + dev_kfree_skb_irq(skb); + else + kfree_skb(skb); + } + ld_buff_len = LD_PHONET_INIT_LEN; + clear_bit(PHONET_SENDING, &ld_pn->state); + return NETDEV_TX_OK; +} + +static struct tty_ldisc_ops ld_phonet_ldisc = { + .owner = THIS_MODULE, + .name = "phonet", + .open = ld_phonet_ldisc_open, + .close = ld_phonet_ldisc_close, + .receive_buf = ld_phonet_ldisc_receive, + .write_wakeup = ld_phonet_ldisc_write_wakeup, + .hangup = ld_phonet_ldisc_hangup +}; + +static ssize_t ld_phonet_show_stats(struct device *dev, + struct device_attribute *attr, char *buf) +{ + pr_crit("LD Phonet Tx request %lu\n", + ld_phonet_tx_request_count); + pr_crit("LD Phonet Rx request %lu\n", + ld_phonet_rx_request_count); + pr_crit("LD Phonet Tx Bytes %lu\n", ld_phonet_tx_bytes); + pr_crit("LD Phonet Rx Bytes %lu\n", ld_phonet_rx_bytes); + pr_crit("LD Phonet TTY hangup events %lu\n", + ld_phonet_hangup_events); + pr_crit("LD Phonet Tx overflow events %lu\n", + ld_phonet_drop_events); + pr_crit("LD Phonet TX buffer len %d\n", ld_buff_len); + return 0; +} + +static ssize_t ld_phonet_reset_stats(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + ld_phonet_tx_request_count = ld_phonet_rx_request_count + = ld_phonet_tx_bytes + = ld_phonet_rx_bytes = ld_phonet_hangup_events = 0; + return 0; +} + +static DEVICE_ATTR(ld_phonet_stats, S_IRUGO | S_IWUSR, + ld_phonet_show_stats, ld_phonet_reset_stats); + +static struct attribute *ld_phonet_attributes[] = { + &dev_attr_ld_phonet_stats.attr, + NULL +}; + +static struct kobject *ld_phonet_kobj; +#define LD_PHONET_FS "ld_phonet_isi" + +static const struct attribute_group ld_phonet_group = { + .attrs = ld_phonet_attributes, +}; + +static int ld_phonet_sysfs_init(void) +{ + int ret = 1; + ld_phonet_kobj = kobject_create_and_add(LD_PHONET_FS, kernel_kobj); + if (!ld_phonet_kobj) { + pr_err("LD Sysfs Kojb failed"); + return -ENOMEM; + } + ret = sysfs_create_group(ld_phonet_kobj, &ld_phonet_group); + if (ret) + kobject_put(kernel_kobj); + return ret; +} + +static int __init ld_phonet_init(void) +{ + int retval; + retval = tty_register_ldisc(N_PHONET, &ld_phonet_ldisc); + ld_buff_len = LD_PHONET_INIT_LEN; + + ld_phonet_wq = create_workqueue("ld_queue"); + if (NULL == ld_phonet_wq) { + pr_err("Create Workqueue failed\n"); + tty_unregister_ldisc(N_PHONET); + return -ENOMEM; + } + + /* Work for handling TTY wakr up */ + ld_tty_wakeup_work = kmalloc(sizeof(struct ld_tty_wakeup_work_t), + GFP_KERNEL); + if (ld_tty_wakeup_work) { + INIT_WORK((struct work_struct *)ld_tty_wakeup_work, + ld_tty_wakeup_workfunction); + ld_tty_wakeup_work->ld_work_write_wakeup_tty = NULL; + } else { + pr_err("TTY Wake up work Error\n"); + tty_unregister_ldisc(N_PHONET); + return false; + } + /* Work for handling AT+ATSTART switch */ + ld_uart_switch_work = kmalloc(sizeof(struct ld_uart_switch_work_t), + GFP_KERNEL); + if (ld_uart_switch_work) { + INIT_WORK((struct work_struct *)ld_uart_switch_work, + ld_uart_switch_function); + } else { + pr_crit("UART Switch Work Failed"); + } + + retval = ld_phonet_sysfs_init(); + return retval; +} + +static void __exit ld_phonet_exit(void) +{ + flush_workqueue(ld_phonet_wq); + destroy_workqueue(ld_phonet_wq); + kfree(ld_tty_wakeup_work); + kfree(ld_uart_switch_work); + tty_unregister_ldisc(N_PHONET); + sysfs_remove_group(ld_phonet_kobj, &ld_phonet_group); + kobject_put(ld_phonet_kobj); +} + +module_init(ld_phonet_init); +module_exit(ld_phonet_exit); +#endif /* CONFIG_BCM_KF_PHONET */ diff -urN linux-4.1.52-orig/net/phonet/ld_tmodem.c linux-4.1.52/net/phonet/ld_tmodem.c --- linux-4.1.52-orig/net/phonet/ld_tmodem.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/net/phonet/ld_tmodem.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,1060 @@ +#ifdef CONFIG_BCM_KF_PHONET +/* +<:copyright-BRCM:2014:DUAL/GPL:standard + + Copyright (c) 2014 Broadcom + All Rights Reserved + +Unless you and Broadcom execute a separate written software license +agreement governing use of this software, this software is licensed +to you under the terms of the GNU General Public License version 2 +(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, +with the following added to such license: + + As a special exception, the copyright holders of this software give + you permission to link this software with independent modules, and + to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent + module, the terms and conditions of the license of that module. + An independent module is a module which is not derived from this + software. The special exception does not apply to any modifications + of the software. + +Not withstanding the above, under no circumstances may you combine +this software in any way with any other Broadcom software provided +under a license other than the GPL, without Broadcom's express prior +written consent. + +:> +*/ +/* + * File: ld_tmodem.c + * Thin Modem device TTY line discipline + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LD_TMODEM_PLATFORM_DRIVER_NAME "ld_tmodem" +#define PN_HARD_HEADER_LEN 1 +#define NETDEV_HARD_HEADER_LEN (PN_HARD_HEADER_LEN + L2MUX_HDR_SIZE) +#define TMODEM_SENDING_BIT 1 /* Bit 1 = 0x02 */ +#define LD_TMODEM_BUFFER_LEN SZ_1M +#define LD_TMODEM_INIT_LEN 0 +#define LD_WAKEUP_DATA_INIT 0 +#define MHI_MAX_MTU 65540 +#define LD_RECEIVE_ROOM SZ_64K + +#define PN_DEV_AUX_HOST 0x44 /* Additional host */ +#define PN_DEV_MODEM 0x60 /* Modem */ +#define PN_DEV_MODEM_1 0x64 /* Modem 1 */ +#define PN_DEV_HOST 0 + +#define LOW_PRIORITY 0 +#define MEDIUM_PRIORITY 1 +#define HIGH_PRIORITY 6 + +struct ldtmdm_dbg_stats { + u64 ld_tmodem_tx_request_count; + u64 ld_tmodem_rx_request_count; + u64 ld_tmodem_tx_bytes; + u64 ld_tmodem_rx_bytes; + u64 ld_tmodem_hangup_events; + u64 ld_tmodem_drop_events; + u64 ld_tmodem_skb_tx_err; +}; + +struct ld_tty_wakeup_work_t { + struct work_struct ld_work; + /*This holds TTY info for TTY wakeup */ + struct tty_struct *ld_work_write_wakeup_tty; +}; + +struct ld_tmodem { + struct tty_struct *tty; + wait_queue_head_t wait; + spinlock_t lock; + unsigned long flags; + struct sk_buff *skb; + unsigned long len; + unsigned long state; + struct net_device *dev; + struct list_head node; + struct sk_buff_head head; + char *tty_name; + bool link_up; + char *pending_buffer; + unsigned int pending_length; + struct dentry *root; + struct workqueue_struct *ld_tmodem_wq; + struct ld_tty_wakeup_work_t *ld_tty_wakeup_work; + struct ldtmdm_dbg_stats ldtmdm_dbg; + int ld_backlog_len; /* LD Phonet Tx Backlog buffer Len */ +}; + +struct ld_tmodem *gb_ld_tmodem; + +struct prop_ctrl_msg { + struct l2muxhdr l2hdr; + u8 ch_id; + u8 event; + u8 rsvd[2]; +}; + +/* L2MUX Queue mapping for protocols */ +enum tmodem_l2mux_queue { + LD_TMODEM_L2MUX_QUEUE_1_PHONET, + LD_TMODEM_L2MUX_QUEUE_2_MHI, +}; + +enum tmodem_flow_ctrl_events { + HOST_STOP_SENDING_MSG = 1, + HOST_RESUME_SENDING_MSG, +}; + +static const char ld_tmodem_ifname[] = "smc0"; + +static void free_ld_tmodem_skb(struct sk_buff *skb) +{ + if (in_interrupt()) + dev_kfree_skb_irq(skb); + else + kfree_skb(skb); +} + +static int mhi_flow_ctrl_rx(struct sk_buff *skb, struct net_device *dev) +{ + struct prop_ctrl_msg *ctrl_ptr = (struct prop_ctrl_msg *)skb->data; + int subqueue; + + BUG_ON(dev == NULL); + + netdev_dbg(dev, "channel id %d, event id %d\n", + ctrl_ptr->ch_id, ctrl_ptr->event); + + if (skb == NULL) { + netdev_err(dev, "skb received is NULL"); + return -ENOMEM; + } + + switch (ctrl_ptr->ch_id) { + case LD_TMODEM_L2MUX_QUEUE_1_PHONET: + subqueue = LD_TMODEM_L2MUX_QUEUE_1_PHONET; + break; + case LD_TMODEM_L2MUX_QUEUE_2_MHI: + subqueue = LD_TMODEM_L2MUX_QUEUE_2_MHI; + break; + default: + netdev_err(dev, "ch id not supported %d", + ctrl_ptr->ch_id); + free_ld_tmodem_skb(skb); + return 0; + } + + switch (ctrl_ptr->event) { + case HOST_STOP_SENDING_MSG: + netif_stop_subqueue(dev, subqueue); + break; + case HOST_RESUME_SENDING_MSG: + netif_wake_subqueue(dev, subqueue); + break; + default: + netdev_err(dev, "event not supported %d", + ctrl_ptr->event); + free_ld_tmodem_skb(skb); + return 0; + } + free_ld_tmodem_skb(skb); + return 0; +} + +static int ld_tmdm_net_open(struct net_device *dev) +{ + netdev_dbg(dev, "ld_tmdm_net_open: wakeup queues"); + netif_tx_wake_all_queues(dev); + phonet_route_add(dev, PN_DEV_MODEM); + phonet_route_add(dev, PN_DEV_MODEM_1); + phonet_route_add(dev, PN_DEV_AUX_HOST); + netif_carrier_on(dev); + return 0; +} + +static int ld_tmdm_net_close(struct net_device *dev) +{ + netdev_dbg(dev, "ld_tmdm_net_close: stop all queues"); + netif_tx_stop_all_queues(dev); + phonet_route_del(dev, PN_DEV_MODEM); + phonet_route_del(dev, PN_DEV_MODEM_1); + phonet_route_del(dev, PN_DEV_AUX_HOST); + netif_carrier_off(dev); + return 0; +} + +static void ld_tx_overflow(struct ld_tmodem *ld_tmdm) +{ + struct ldtmdm_dbg_stats *dbg; + dbg = &ld_tmdm->ldtmdm_dbg; + dbg->ld_tmodem_drop_events++; + pr_debug( + "##### ATTENTION : LD Phonet Transmit overflow events %llu : %u #####", + dbg->ld_tmodem_drop_events, LD_TMODEM_BUFFER_LEN); +} + +static int ld_tmdm_handle_tx(struct ld_tmodem *ld_tmdm) +{ + struct tty_struct *tty = ld_tmdm->tty; + struct sk_buff *skb; + unsigned int tty_wr, room; + struct sk_buff *tmp; + int ret; + + if (tty == NULL) + return 0; + /* Enter critical section */ + if (test_and_set_bit(TMODEM_SENDING_BIT, &ld_tmdm->state)) + return 0; + + /* skb_peek is safe because handle_tx is called after skb_queue_tail */ + while ((skb = skb_peek(&ld_tmdm->head)) != NULL) { + room = tty_write_room(tty); + + if (!room && ld_tmdm->ld_backlog_len > LD_TMODEM_BUFFER_LEN) { + if (ld_tmdm->link_up == true) + ld_tx_overflow(ld_tmdm); + ld_tmdm->link_up = false; + /* Flush TX queue */ + while ((skb = skb_dequeue(&ld_tmdm->head)) != NULL) { + skb->dev->stats.tx_dropped++; + netdev_dbg(ld_tmdm->dev, "Flush TX queue tx_dropped = %ld", + skb->dev->stats.tx_dropped); + free_ld_tmodem_skb(skb); + } + ld_tmdm->ld_backlog_len = LD_TMODEM_INIT_LEN; + goto out; + } else if (!room) { + netdev_dbg(ld_tmdm->dev, + "ld_tmdm_handle_tx no room, waiting for previous to be sent..:\n"); + + if (!test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) { + /* wakeup bit is not set, set it */ + netdev_dbg(ld_tmdm->dev, + "ld_tmdm_handle_tx Setting TTY_DO_WRITE_WAKEUP bit\n"); + set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); + } else { + netdev_dbg(ld_tmdm->dev, + "ld_tmdm_handle_tx TTY_DO_WRITE_WAKEUP bit already set!\n"); + } + goto out; + } + + if (skb->len > (room + NETDEV_HARD_HEADER_LEN)) { + netdev_warn(ld_tmdm->dev, "%s :room not available buf len:%d, room:%d\n" + , __func__, skb->len, room); + goto out; + } + + ret = l2mux_skb_tx(skb, ld_tmdm->dev); + if (ret) { + /*drop this packet...*/ + struct sk_buff *tmp = skb_dequeue(&ld_tmdm->head); + BUG_ON(tmp != skb); + free_ld_tmodem_skb(skb); + ld_tmdm->ldtmdm_dbg.ld_tmodem_skb_tx_err++; + goto out; + } + + tty_wr = tty->ops->write(tty, skb->data, skb->len); + + print_hex_dump(KERN_DEBUG, "ld_tm_write: ", DUMP_PREFIX_NONE, 16, 1, + skb->data, skb->len, 1); + + if (unlikely(tty_wr != skb->len)) { + netdev_err(ld_tmdm->dev, + "buffer split...req len :%d, act written:%d\n", + skb->len, tty_wr); + } + ld_tmdm->ld_backlog_len -= skb->len; + + ld_tmdm->ldtmdm_dbg.ld_tmodem_tx_bytes += tty_wr; + if (ld_tmdm->ld_backlog_len < LD_TMODEM_INIT_LEN) + ld_tmdm->ld_backlog_len = LD_TMODEM_INIT_LEN; + + ld_tmdm->dev->stats.tx_packets++; + ld_tmdm->dev->stats.tx_bytes += tty_wr; + + tmp = skb_dequeue(&ld_tmdm->head); + + BUG_ON(tmp != skb); + free_ld_tmodem_skb(skb); + + } +out: + clear_bit(TMODEM_SENDING_BIT, &ld_tmdm->state); + return NETDEV_TX_OK; +} + +int l2mux_send(char *data, int len, int l3_id, int res) +{ + struct net_device *dev = NULL; + struct l2muxhdr *l2hdr; + struct sk_buff *skb; + struct net *net; + int ret; + + skb = alloc_skb(len + L2MUX_HDR_SIZE, GFP_ATOMIC); + if (!skb) { + pr_err("l2mux_send: skb_alloc failed\n"); + return -ENOMEM; + } + + skb_reserve(skb, L2MUX_HDR_SIZE); + skb_reset_transport_header(skb); + + memcpy(skb_put(skb, len), data, len); + + for_each_net(net) { + dev = dev_get_by_name(net, ld_tmodem_ifname); + if (dev) + break; + } + + if (!dev) { + ret = -ENODEV; + pr_err("l2mux_send: interface:%s not found\n", + ld_tmodem_ifname); + goto drop; + } + + if (!(dev->flags & IFF_UP)) { + netdev_err(dev, "l2mux_send: device %s not IFF_UP\n", + ld_tmodem_ifname); + ret = -ENETDOWN; + goto drop; + } + + if (len + L2MUX_HDR_SIZE > dev->mtu) { + netdev_err(dev, "l2mux_send: device %s not IFF_UP\n", + ld_tmodem_ifname); + ret = -EMSGSIZE; + goto drop; + } + + skb_reset_network_header(skb); + + skb_push(skb, L2MUX_HDR_SIZE); + skb_reset_mac_header(skb); + + l2hdr = l2mux_hdr(skb); + l2mux_set_proto(l2hdr, l3_id); + l2mux_set_length(l2hdr, len); + netdev_dbg(dev, "l2mux_send: proto:%d skb_len:%d\n", + l3_id, skb->len); + skb->protocol = htons(ETH_P_MHI); + skb->dev = dev; + + switch (l3_id) { + case MHI_L3_XFILE: + case MHI_L3_LOW_PRIO_TEST: + skb->priority = MEDIUM_PRIORITY; + break; + case MHI_L3_AUDIO: + case MHI_L3_TEST_PRIO: + case MHI_L3_HIGH_PRIO_TEST: + skb->priority = HIGH_PRIORITY; + break; + default: + skb->priority = LOW_PRIORITY; + break; + } + ret = dev_queue_xmit(skb); /*this will consume irrespective of status*/ + + dev_put(dev); + return ret; + +drop: + if (skb) + kfree_skb(skb); + if (dev) + dev_put(dev); + + return ret; +} +EXPORT_SYMBOL(l2mux_send); + +static int ld_tmdm_net_xmit(struct sk_buff *skb, struct net_device *dev) +{ + + struct ld_tmodem *ld_tmdm; + + BUG_ON(dev == NULL); + + print_hex_dump(KERN_DEBUG, "ld_tm_xmit: ", DUMP_PREFIX_NONE, + 16, 1, skb->data, skb->len, 1); + + ld_tmdm = netdev_priv(dev); + if ((ld_tmdm == NULL)) { + free_ld_tmodem_skb(skb); + return NETDEV_TX_OK; + } + ld_tmdm->ldtmdm_dbg.ld_tmodem_tx_request_count++; + netdev_dbg(dev, "ld_tmdm_net_xmit: send skb to %s", dev->name); + if (ld_tmdm->link_up == true) { + skb_queue_tail(&ld_tmdm->head, skb); + ld_tmdm->ld_backlog_len += skb->len; + return ld_tmdm_handle_tx(ld_tmdm); + } else if (tty_write_room(ld_tmdm->tty)) { + /* link is up again */ + ld_tmdm->link_up = true; + skb_queue_tail(&ld_tmdm->head, skb); + ld_tmdm->ld_backlog_len += skb->len; + return ld_tmdm_handle_tx(ld_tmdm); + } else { + free_ld_tmodem_skb(skb); + dev->stats.tx_dropped++; + netdev_dbg(dev, "tx_dropped = %ld", + dev->stats.tx_dropped); + return NETDEV_TX_OK; + } + +} + +static int ld_tmdm_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct if_phonet_req *req = (struct if_phonet_req *)ifr; + + netdev_dbg(dev, "IOCTL called\n"); + switch (cmd) { + case SIOCPNGAUTOCONF: + netdev_dbg(dev, "IOCTL SIOCPNGAUTOCON called, adding routes\n"); + req->ifr_phonet_autoconf.device = PN_DEV_HOST; + //dev_open(dev); + return 0; + } + /* Return NOIOCTLCMD so Phonet won't do it again */ + return -ENOIOCTLCMD; +} + +static u16 +ld_tmdm_net_select_queue(struct net_device *dev, struct sk_buff *skb, + void *accel_priv, select_queue_fallback_t fallback) +{ + + struct ld_tmodem *ld_tmdm; + u16 subqueue = 0;/*default return value is phonet sub queue*/ + + BUG_ON(dev == NULL); + ld_tmdm = netdev_priv(dev); + + if (skb->protocol == htons(ETH_P_PHONET)) { + netdev_dbg(dev, "ld_tmdm_net_select_queue: protocol ETH_P_PHONET"); + subqueue = LD_TMODEM_L2MUX_QUEUE_1_PHONET; + } else if (skb->protocol == htons(ETH_P_MHI)) { + netdev_dbg(dev, "ld_tmdm_net_select_queue: protocol ETH_P_MHI"); + subqueue = LD_TMODEM_L2MUX_QUEUE_2_MHI; + } else + netdev_err(dev, "unsupported protocol device %p, 0x%04X", + dev, skb->protocol); + + return subqueue; +} + +static int ld_tmdm_set_mtu(struct net_device *dev, int new_mtu) +{ + dev->mtu = new_mtu; + return 0; +} + +static const struct net_device_ops ld_tmdm_netdev_ops = { + .ndo_open = ld_tmdm_net_open, + .ndo_stop = ld_tmdm_net_close, + .ndo_select_queue = ld_tmdm_net_select_queue, + .ndo_start_xmit = ld_tmdm_net_xmit, + .ndo_do_ioctl = ld_tmdm_net_ioctl, + .ndo_change_mtu = ld_tmdm_set_mtu, +}; + +static void ld_tmdm_net_setup(struct net_device *dev) +{ + dev->features = NETIF_F_SG; + dev->type = ARPHRD_MHI; + dev->flags = IFF_POINTOPOINT | IFF_NOARP; + dev->mtu = MHI_MAX_MTU; + dev->hard_header_len = NETDEV_HARD_HEADER_LEN; + dev->dev_addr[0] = PN_MEDIA_AUX_HOST_HOST_IF; + dev->addr_len = 1; + dev->tx_queue_len = 500; + dev->netdev_ops = &ld_tmdm_netdev_ops; + dev->destructor = free_netdev; +}; + +static int ld_tmodem_ldisc_open(struct tty_struct *tty) +{ + struct ld_tmodem *ld_tmdm = gb_ld_tmodem; + ld_tmdm->tty = tty; + tty->disc_data = ld_tmdm; + + gb_ld_tmodem->tty = tty; + tty->receive_room = LD_RECEIVE_ROOM; + tty->port->low_latency = 1; + return 0; +} + +static void ld_tmodem_ldisc_close(struct tty_struct *tty) +{ + struct ld_tmodem *ld_tmdm = tty->disc_data; + netdev_dbg(ld_tmdm->dev, "ld_tmodem_ldisc_close\n"); + tty->disc_data = NULL; + ld_tmdm->tty = NULL; + ld_tmdm->ld_tty_wakeup_work->ld_work_write_wakeup_tty = NULL; +} + +static void send_buffer_to_l2mux(struct ld_tmodem *ld_tmdm, + const unsigned char *buff_ptr, + unsigned int count) +{ + struct sk_buff *skb; + + skb = netdev_alloc_skb(ld_tmdm->dev, count + NET_IP_ALIGN); + if (NULL == skb) + return; + + skb_reserve(skb, NET_IP_ALIGN); + + memcpy(skb->data, buff_ptr, count); + skb_put(skb, count); + + skb_reset_mac_header(skb); + skb->pkt_type = PACKET_HOST; + + /*no need to free skb: skb will be consumed by callee in all the cases*/ + if (l2mux_skb_rx(skb, ld_tmdm->dev) != 0) + netdev_err(ld_tmdm->dev, "not able to send skb"); + + ld_tmdm->ldtmdm_dbg.ld_tmodem_rx_bytes += count; + +} + +static void process_rx_buffer(struct ld_tmodem *ld_tmdm, int curr_len, + const unsigned char *buff_ptr) +{ + unsigned int pending_len = ld_tmdm->pending_length; + const unsigned char *pbuff_start; + struct l2muxhdr *pl2msg; + int l3len; + + if (pending_len + curr_len < L2MUX_HDR_SIZE) { + memcpy(ld_tmdm->pending_buffer + pending_len, + buff_ptr, + curr_len); + ld_tmdm->pending_length += curr_len; + return; + } + + netdev_dbg(ld_tmdm->dev, "pending_length %d", pending_len); + while (1) { + if (pending_len && pending_len < L2MUX_HDR_SIZE) { + memcpy(ld_tmdm->pending_buffer + pending_len, + buff_ptr, + L2MUX_HDR_SIZE - pending_len); + curr_len -= (L2MUX_HDR_SIZE - pending_len); + buff_ptr += (L2MUX_HDR_SIZE - pending_len); + + ld_tmdm->pending_length = L2MUX_HDR_SIZE; + pending_len = ld_tmdm->pending_length; + } + + if (pending_len) + pbuff_start = ld_tmdm->pending_buffer; + else + pbuff_start = buff_ptr; + + pl2msg = (struct l2muxhdr *)pbuff_start; + l3len = l2mux_get_length(pl2msg); + + if (pending_len + curr_len < l3len + L2MUX_HDR_SIZE) { + memcpy(ld_tmdm->pending_buffer + pending_len, + buff_ptr, + curr_len); + + ld_tmdm->pending_length += curr_len; + break; + } + + if (pending_len) { + memcpy(ld_tmdm->pending_buffer + pending_len, + buff_ptr, + l3len + L2MUX_HDR_SIZE - pending_len); + } + + print_hex_dump(KERN_DEBUG, "ld_tm_process_buff: ", + DUMP_PREFIX_NONE, + 16, 1, + pbuff_start, l3len + L2MUX_HDR_SIZE, + 1); + + netdev_dbg(ld_tmdm->dev, + "total pending length %d", + pending_len); + + send_buffer_to_l2mux(ld_tmdm, pbuff_start, + l3len + L2MUX_HDR_SIZE); + + curr_len -= (l3len + L2MUX_HDR_SIZE - pending_len); + buff_ptr += (l3len + L2MUX_HDR_SIZE - pending_len); + + ld_tmdm->pending_length = 0; + pending_len = ld_tmdm->pending_length; + if (curr_len > 0) { + netdev_dbg(ld_tmdm->dev, + "current length is more than zero %d", + curr_len); + continue; + } else + break; + } + +} + +static void ld_tmodem_ldisc_receive + (struct tty_struct *tty, const unsigned char *cp, char *fp, int count) +{ + struct ld_tmodem *ld_tmdm = tty->disc_data; + ld_tmdm->ldtmdm_dbg.ld_tmodem_rx_request_count++; + if (ld_tmdm->link_up == false) { + /* data received from PC => can TX */ + ld_tmdm->link_up = true; + } + process_rx_buffer(ld_tmdm, count, cp); +} + +static void ld_tty_wakeup_workfunction(struct work_struct *work) +{ + struct tty_struct *tty; + struct ld_tmodem *ld_tmdm; + struct ld_tty_wakeup_work_t *ld_work_tty_wk = + (struct ld_tty_wakeup_work_t *)work; + + if (ld_work_tty_wk == NULL) { + pr_err("TTY work NULL\n"); + return; + } + + tty = ld_work_tty_wk->ld_work_write_wakeup_tty; + if (tty == NULL) { + pr_err("LD Work Queue tty Data NULL\n"); + return; + } + + clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); + + ld_tmdm = tty->disc_data; + if (ld_tmdm == NULL) { + pr_err("LD PN Work Queue DATA NULL\n"); + return; + } + + BUG_ON(ld_tmdm->tty != tty); + ld_tmdm_handle_tx(ld_tmdm); +} + +static void ld_tmodem_ldisc_write_wakeup(struct tty_struct *tty) +{ + struct ld_tmodem *ld_tmdm; + ld_tmdm = tty->disc_data; + if (!ld_tmdm) { + pr_err("ld modem ldisc write wakeup: ld_tmdm is NULL\n"); + return; + } + ld_tmdm->ld_tty_wakeup_work->ld_work_write_wakeup_tty = tty; + queue_work(ld_tmdm->ld_tmodem_wq, + (struct work_struct *)ld_tmdm->ld_tty_wakeup_work); +} + +static int ld_tmodem_hangup_wait(void *data) +{ + return NETDEV_TX_OK; +} + +static int ld_tmodem_ldisc_hangup(struct tty_struct *tty) +{ + struct ld_tmodem *ld_tmdm; + struct sk_buff *skb; + + ld_tmdm = tty->disc_data; + ld_tmdm->ldtmdm_dbg.ld_tmodem_hangup_events++; + wait_on_bit_lock(&ld_tmdm->state, TMODEM_SENDING_BIT, + ld_tmodem_hangup_wait, TASK_KILLABLE); + + while ((skb = skb_dequeue(&ld_tmdm->head)) != NULL) { + skb->dev->stats.tx_dropped++; + free_ld_tmodem_skb(skb); + } + ld_tmdm->ld_backlog_len = LD_TMODEM_INIT_LEN; + clear_bit(TMODEM_SENDING_BIT, &ld_tmdm->state); + return NETDEV_TX_OK; +} + +static struct tty_ldisc_ops ld_tmodem_ldisc = { + .owner = THIS_MODULE, + .name = "tmodem", + .open = ld_tmodem_ldisc_open, + .close = ld_tmodem_ldisc_close, + .receive_buf = ld_tmodem_ldisc_receive, + .write_wakeup = ld_tmodem_ldisc_write_wakeup, + .hangup = ld_tmodem_ldisc_hangup +}; + +static ssize_t tmdm_dbg_write(struct file *file, const char __user *buf, + size_t size, loff_t *ppos) +{ + if (!gb_ld_tmodem) { + pr_err("gb_ld_tmodem is NULL"); + return -EBUSY; + } + /*resetting received and transmitted stats; while keeping the + *error stats intact + */ + gb_ld_tmodem->ldtmdm_dbg.ld_tmodem_tx_request_count = + gb_ld_tmodem->ldtmdm_dbg.ld_tmodem_rx_request_count = + gb_ld_tmodem->ldtmdm_dbg.ld_tmodem_tx_bytes = + gb_ld_tmodem->ldtmdm_dbg.ld_tmodem_rx_bytes = 0; + + return size; +} + +static ssize_t tmdm_dbg_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + char *buf; + int used = 0; + int ret; + struct ldtmdm_dbg_stats *dbg; + if (!gb_ld_tmodem) { + pr_err("gb_ld_tmodem is NULL"); + return -EBUSY; + } + + dbg = &gb_ld_tmodem->ldtmdm_dbg; + + buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = snprintf(buf + used, PAGE_SIZE - used, + "LD Thin Modem Tx request: %llu\n", + dbg->ld_tmodem_tx_request_count); + if (ret < 0) + goto out; + + used = used + ret; + + if (used >= PAGE_SIZE - 1) + goto out; + + ret = snprintf(buf + used, + PAGE_SIZE - used, + "LD Thin Modem Rx request: %llu\n", + dbg->ld_tmodem_rx_request_count); + if (ret < 0) + goto out; + + used = used + ret; + + if (used >= PAGE_SIZE - 1) + goto out; + + ret = snprintf(buf + used, + PAGE_SIZE - used, + "LD Thin Modem Tx Bytes: %llu\n", + dbg->ld_tmodem_tx_bytes); + if (ret < 0) + goto out; + + used = used + ret; + + if (used >= PAGE_SIZE - 1) + goto out; + + ret = snprintf(buf + used, + PAGE_SIZE - used, + "LD Thin Modem Tx Bytes: %llu\n", + dbg->ld_tmodem_rx_bytes); + if (ret < 0) + goto out; + + used = used + ret; + + if (used >= PAGE_SIZE - 1) + goto out; + + ret = snprintf(buf + used, + PAGE_SIZE - used, + "LD Thin Modem hangup events: %llu\n", + dbg->ld_tmodem_hangup_events); + + if (ret < 0) + goto out; + + used = used + ret; + + if (used >= PAGE_SIZE - 1) + goto out; + + ret = snprintf(buf + used, + PAGE_SIZE - used, + "LD Thin Modem drop events: %llu\n", + dbg->ld_tmodem_drop_events); + if (ret < 0) + goto out; + + used = used + ret; + + if (used >= PAGE_SIZE - 1) + goto out; + + ret = snprintf(buf + used, + PAGE_SIZE - used, + "LD Thin Modem Tx error: %llu\n", + dbg->ld_tmodem_skb_tx_err); + if (ret < 0) + goto out; + + used = used + ret; + + if (used >= PAGE_SIZE - 1) + goto out; + + ret = snprintf(buf + used, + PAGE_SIZE - used, + "LD Thin Modem buff len: %d\n", + gb_ld_tmodem->ld_backlog_len); + +out: + ret = simple_read_from_buffer(user_buf, count, ppos, + buf, + strlen(buf)); + + kfree(buf); + + return ret; + +} + +static const struct file_operations tmdm_dbg_fops = { + .read = tmdm_dbg_read, + .write = tmdm_dbg_write, + .open = simple_open, + .llseek = default_llseek, +}; + +static int ld_tmodem_dbgfs_init(struct net_device *ndev) +{ + int ret = -ENOMEM; + struct ld_tmodem *ld_tmdm; + struct dentry *root; + struct dentry *node; + + ld_tmdm = netdev_priv(ndev); + root = debugfs_create_dir("ld_tmodem", NULL); + if (IS_ERR(root)) + return PTR_ERR(root); + + if (!root) + goto err_root; + + node = debugfs_create_file("ld_dbg", S_IRUGO | S_IWUSR, root, + NULL, &tmdm_dbg_fops); + if (!node) + goto err_node; + + ld_tmdm->root = root; + return 0; + +err_node: + debugfs_remove_recursive(root); +err_root: + pr_err("ld_tmodem_dbgfs_init: Failed to initialize debugfs\n"); + return ret; +} + +static void +remove_nw_interface(struct net_device *ndev) +{ + struct ld_tmodem *ld_tmdm; + ld_tmdm = netdev_priv(ndev); + kfree(ld_tmdm->pending_buffer); + unregister_netdev(ld_tmdm->dev); +} + +static void +ld_tmodem_l2_mux_deinit(void) +{ + int err; + err = l2mux_netif_rx_unregister(MHI_L3_CTRL_TMODEM); + if (err) { + pr_err("l2mux_netif_rx_unregister fails l3 id %d, error %d\n", + MHI_L3_CTRL_TMODEM, err); + } + err = mhi_unregister_protocol(MHI_L3_CTRL_TMODEM); + if (err) { + pr_err("mhi_unregister_protocol fails l3 id %d, error %d\n", + MHI_L3_CTRL_TMODEM, err); + } +} + +static int ld_tmodem_l2_mux_init(void) +{ + int err; + err = l2mux_netif_rx_register(MHI_L3_CTRL_TMODEM, mhi_flow_ctrl_rx); + if (err) { + pr_err("l2mux_netif_rx_register fails l3 id %d, error %d\n", + MHI_L3_CTRL_TMODEM, err); + return err; + } + err = mhi_register_protocol(MHI_L3_CTRL_TMODEM); + if (err) { + l2mux_netif_rx_unregister(MHI_L3_CTRL_TMODEM); + pr_err("mhi_register_protocol fails l3 id %d, error %d\n", + MHI_L3_CTRL_TMODEM, err); + return err; + } + return 0; +} + +static int create_nw_interface(void) +{ + struct net_device *ndev; + struct ld_tmodem *ld_tmdm; + int err; + ndev = alloc_netdev_mq(sizeof(*ld_tmdm), ld_tmodem_ifname, + ld_tmdm_net_setup, 2); + if (!ndev) + return -ENOMEM; + + netdev_dbg(ndev, "ld_tmodem_ldisc_open starts\n"); + + ld_tmdm = netdev_priv(ndev); + spin_lock_init(&ld_tmdm->lock); + netif_carrier_off(ndev); + skb_queue_head_init(&ld_tmdm->head); + ld_tmdm->dev = ndev; + ld_tmdm->skb = NULL; + ld_tmdm->len = 0; + + ld_tmdm->pending_length = 0; + + netif_tx_stop_all_queues(ndev); + + ld_tmdm->link_up = true; + + ld_tmdm->pending_buffer = kmalloc(MHI_MAX_MTU, GFP_KERNEL); + if (!ld_tmdm->pending_buffer) { + err = -ENOMEM; + goto out; + } + + err = register_netdev(ndev); +out: + if (err) { + kfree(ld_tmdm->pending_buffer); + free_netdev(ndev); + } else { + gb_ld_tmodem = ld_tmdm; + } + return err; +} + +static int __init tmodem_net_init(void) +{ + int retval; + struct workqueue_struct *ld_tmodem_wq; + struct ld_tty_wakeup_work_t *ld_tty_wakeup_work; + retval = tty_register_ldisc(N_LDTMODEM, &ld_tmodem_ldisc); + + printk(KERN_INFO "%s called\n", __func__); + + ld_tmodem_wq = create_workqueue("ld_queue"); + if (NULL == ld_tmodem_wq) { + pr_err("Create Workqueue failed\n"); + retval = -ENOMEM; + goto error; + } + ld_tty_wakeup_work = kmalloc(sizeof(struct ld_tty_wakeup_work_t), + GFP_KERNEL); + if (ld_tty_wakeup_work) { + INIT_WORK((struct work_struct *)ld_tty_wakeup_work, + ld_tty_wakeup_workfunction); + ld_tty_wakeup_work->ld_work_write_wakeup_tty = NULL; + } else { + pr_err("TTY Wake up work Error\n"); + goto error1; + } + + retval = ld_tmodem_l2_mux_init(); + if (retval) { + pr_err("ld_tmodem_l2_mux_init fails\n"); + goto error1; + } + + retval = create_nw_interface(); + if (retval) { + pr_err("create_nw_interface fails\n"); + goto error2; + } + + gb_ld_tmodem->ld_tmodem_wq = ld_tmodem_wq; + gb_ld_tmodem->ld_tty_wakeup_work = ld_tty_wakeup_work; + + retval = ld_tmodem_dbgfs_init(gb_ld_tmodem->dev); + if (retval) { + pr_err("ld_tmodem_dbgfs_init fails\n"); + goto error3; + } + + return retval; + +error3: + remove_nw_interface(gb_ld_tmodem->dev); + +error2: + ld_tmodem_l2_mux_deinit(); +error1: + flush_workqueue(ld_tmodem_wq); + destroy_workqueue(ld_tmodem_wq); + kfree(ld_tty_wakeup_work); +error: + tty_unregister_ldisc(N_LDTMODEM); + return retval; + +} + +static void __exit tmodem_net_remove(void) +{ + + struct ld_tmodem *ld_tmdm = gb_ld_tmodem; + flush_workqueue(ld_tmdm->ld_tmodem_wq); + destroy_workqueue(ld_tmdm->ld_tmodem_wq); + kfree(ld_tmdm->ld_tty_wakeup_work); + debugfs_remove_recursive(ld_tmdm->root); + remove_nw_interface(ld_tmdm->dev); + ld_tmodem_l2_mux_deinit(); + tty_unregister_ldisc(N_LDTMODEM); +} + +module_init(tmodem_net_init); +module_exit(tmodem_net_remove); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("pranav@broadcom.com"); +MODULE_DESCRIPTION("TMODEM TTY line discipline"); +MODULE_ALIAS_LDISC(N_LDTMODEM); +#endif /* CONFIG_BCM_KF_PHONET */ diff -urN linux-4.1.52-orig/net/phonet/Makefile linux-4.1.52/net/phonet/Makefile --- linux-4.1.52-orig/net/phonet/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/phonet/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -9,3 +9,10 @@ af_phonet.o pn_pep-y := pep.o pep-gprs.o + +ifdef BCM_KF # defined(CONFIG_BCM_KF_PHONET) +obj-$(CONFIG_LD_PHONET) += ld_phonet.o +obj-$(CONFIG_LD_TMODEM) += ld_tmodem.o + +subdir-ccflags-y += -Werror +endif # BCM_KF diff -urN linux-4.1.52-orig/net/phonet/pep.c linux-4.1.52/net/phonet/pep.c --- linux-4.1.52-orig/net/phonet/pep.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/phonet/pep.c 2020-02-07 01:47:54.000000000 +0800 @@ -163,6 +163,10 @@ PN_PIPE_SB_NEGOTIATED_FC, pep_sb_size(2), pn->tx_fc, pn->rx_fc, }; +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK( + "pipe_handler_send_created_ind : send PNS_PIPE_CREATED_IND\n"); +#endif return pep_indicate(sk, PNS_PIPE_CREATED_IND, 1 /* sub-blocks */, data, 4, GFP_ATOMIC); @@ -183,6 +187,9 @@ PN_LEGACY_FLOW_CONTROL, PAD, }; +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK("pep_accept_conn : send PN_PIPE_NO_ERROR\n"); +#endif might_sleep(); return pep_reply(sk, skb, PN_PIPE_NO_ERROR, data, sizeof(data), @@ -229,6 +236,10 @@ static int pipe_snd_status(struct sock *sk, u8 type, u8 status, gfp_t priority) { u8 data[4] = { type, PAD, PAD, status }; +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK("pipe_snd_status : PNS_PEP_STATUS_IND type=%d status=%d\n", + type, status); +#endif return pep_indicate(sk, PNS_PEP_STATUS_IND, PN_PEP_TYPE_COMMON, data, 4, priority); @@ -279,18 +290,33 @@ switch (hdr->data[1]) { case PN_PEP_IND_FLOW_CONTROL: +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK("pipe_rcv_status : PN_PEP_IND_FLOW_CONTROL\n"); +#endif switch (pn->tx_fc) { case PN_LEGACY_FLOW_CONTROL: switch (hdr->data[4]) { case PEP_IND_BUSY: +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK( + "pipe_rcv_status : PN_LEGACY_FLOW_CONTROL / PEP_IND_BUSY\n"); +#endif atomic_set(&pn->tx_credits, 0); break; case PEP_IND_READY: +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK( + "pipe_rcv_status : PN_LEGACY_FLOW_CONTROL / PEP_IND_READY\n"); +#endif atomic_set(&pn->tx_credits, wake = 1); break; } break; case PN_ONE_CREDIT_FLOW_CONTROL: +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK( + "pipe_rcv_status : ONE_CREDIT_FLOW_CONTROL\n"); +#endif if (hdr->data[4] == PEP_IND_READY) atomic_set(&pn->tx_credits, wake = 1); break; @@ -298,6 +324,10 @@ break; case PN_PEP_IND_ID_MCFC_GRANT_CREDITS: +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK( + "pipe_rcv_status : PN_PEP_IND_ID_MCFC_GRANT_CREDITS\n"); +#endif if (pn->tx_fc != PN_MULTI_CREDIT_FLOW_CONTROL) break; atomic_add(wake = hdr->data[4], &pn->tx_credits); @@ -319,6 +349,9 @@ struct pnpipehdr *hdr = pnp_hdr(skb); u8 n_sb = hdr->data[0]; +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK("pipe_rcv_created\n"); +#endif pn->rx_fc = pn->tx_fc = PN_LEGACY_FLOW_CONTROL; __skb_pull(skb, sizeof(*hdr)); while (n_sb > 0) { @@ -353,10 +386,16 @@ switch (hdr->message_id) { case PNS_PEP_CONNECT_REQ: +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK("pipe_do_rcv : PNS_PEP_CONNECT_REQ\n"); +#endif pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE, GFP_ATOMIC); break; case PNS_PEP_DISCONNECT_REQ: +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK("pipe_do_rcv : PNS_PEP_DISCONNECT_REQ\n"); +#endif pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); sk->sk_state = TCP_CLOSE_WAIT; if (!sock_flag(sk, SOCK_DEAD)) @@ -364,6 +403,9 @@ break; case PNS_PEP_ENABLE_REQ: +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK("pipe_do_rcv : PNS_PEP_ENABLE_REQ\n"); +#endif /* Wait for PNS_PIPE_(ENABLED|REDIRECTED)_IND */ pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); break; @@ -371,9 +413,17 @@ case PNS_PEP_RESET_REQ: switch (hdr->state_after_reset) { case PN_PIPE_DISABLE: +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK( + "pipe_do_rcv : PNS_PEP_RESET_REQ / PN_PIPE_DISABLE\n"); +#endif pn->init_enable = 0; break; case PN_PIPE_ENABLE: +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK( + "pipe_do_rcv : PNS_PEP_RESET_REQ / PN_PIPE_ENABLE\n"); +#endif pn->init_enable = 1; break; default: /* not allowed to send an error here!? */ @@ -382,11 +432,17 @@ } /* fall through */ case PNS_PEP_DISABLE_REQ: +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK("pipe_do_rcv : PNS_PEP_DISABLE_REQ\n"); +#endif atomic_set(&pn->tx_credits, 0); pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); break; case PNS_PEP_CTRL_REQ: +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK("pipe_do_rcv : PNS_PEP_CTRL_REQ\n"); +#endif if (skb_queue_len(&pn->ctrlreq_queue) >= PNPIPE_CTRLREQ_MAX) { atomic_inc(&sk->sk_drops); break; @@ -396,9 +452,15 @@ goto queue; case PNS_PIPE_ALIGNED_DATA: +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK("pipe_do_rcv : PNS_PIPE_ALIGNED_DATA\n"); +#endif __skb_pull(skb, 1); /* fall through */ case PNS_PIPE_DATA: +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK("pipe_do_rcv : PNS_PIPE_DATA\n"); +#endif __skb_pull(skb, 3); /* Pipe data header */ if (!pn_flow_safe(pn->rx_fc)) { err = sock_queue_rcv_skb(sk, skb); @@ -418,23 +480,38 @@ goto queue; case PNS_PEP_STATUS_IND: +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK("pipe_do_rcv : PNS_PEP_STATUS_IND\n"); +#endif pipe_rcv_status(sk, skb); break; case PNS_PIPE_REDIRECTED_IND: +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK("pipe_do_rcv : PNS_PIPE_REDIRECTED_IND\n"); +#endif err = pipe_rcv_created(sk, skb); break; case PNS_PIPE_CREATED_IND: +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK("pipe_do_rcv : PNS_PIPE_CREATED_IND\n"); +#endif err = pipe_rcv_created(sk, skb); if (err) break; /* fall through */ case PNS_PIPE_RESET_IND: +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK("pipe_do_rcv : PNS_PIPE_RESET_IND\n"); +#endif if (!pn->init_enable) break; /* fall through */ case PNS_PIPE_ENABLED_IND: +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK("pipe_do_rcv : PNS_PIPE_ENABLED_IND\n"); +#endif if (!pn_flow_safe(pn->tx_fc)) { atomic_set(&pn->tx_credits, 1); sk->sk_write_space(sk); @@ -446,6 +523,9 @@ break; case PNS_PIPE_DISABLED_IND: +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK("pipe_do_rcv : PNS_PIPE_DISABLED_IND\n"); +#endif sk->sk_state = TCP_SYN_RECV; pn->rx_credits = 0; break; @@ -496,7 +576,9 @@ struct pep_sock *pn = pep_sk(sk); struct pnpipehdr *hdr; u8 n_sb; - +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK("pep_connresp_rcv\n"); +#endif if (!pskb_pull(skb, sizeof(*hdr) + 4)) return -EINVAL; @@ -566,9 +648,15 @@ switch (hdr->message_id) { case PNS_PIPE_ALIGNED_DATA: +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK("pipe_handler_do_rcv: PNS_PIPE_ALIGNED_DATA\n"); +#endif __skb_pull(skb, 1); /* fall through */ case PNS_PIPE_DATA: +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK("pipe_handler_do_rcv: PNS_PIPE_DATA\n"); +#endif __skb_pull(skb, 3); /* Pipe data header */ if (!pn_flow_safe(pn->rx_fc)) { err = sock_queue_rcv_skb(sk, skb); @@ -592,11 +680,29 @@ return NET_RX_SUCCESS; case PNS_PEP_CONNECT_RESP: +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK("pipe_handler_do_rcv: PNS_PEP_CONNECT_RESP\n"); + if (sk->sk_state != TCP_SYN_SENT) { + PEP_PRINTK( + "PNS_PEP_CONNECT_RESP sk->sk_state != TCP_SYN_SENT\n"); + break; + } + if (!sock_flag(sk, SOCK_DEAD)) { + sk->sk_state_change(sk); + PEP_PRINTK( + "PNS_PEP_CONNECT_RESP sock flag != SOCK_DEAD\n"); + } +#else if (sk->sk_state != TCP_SYN_SENT) break; if (!sock_flag(sk, SOCK_DEAD)) sk->sk_state_change(sk); +#endif if (pep_connresp_rcv(sk, skb)) { +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK( + "PNS_PEP_CONNECT_RESP pep_connresp_rcv failed\n"); +#endif sk->sk_state = TCP_CLOSE_WAIT; break; } @@ -622,10 +728,16 @@ break; case PNS_PEP_DISCONNECT_RESP: +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK("pipe_handler_do_rcv: PNS_PEP_DISCONNECT_RESP\n"); +#endif /* sock should already be dead, nothing to do */ break; case PNS_PEP_STATUS_IND: +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK("pipe_handler_do_rcv: PNS_PEP_DISCONNECT_RESP\n"); +#endif pipe_rcv_status(sk, skb); break; } @@ -671,6 +783,9 @@ struct sockaddr_pn dst; u8 pipe_handle; +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK("pep_do_rcv\n"); +#endif if (!pskb_may_pull(skb, sizeof(*hdr))) goto drop; @@ -688,7 +803,14 @@ switch (hdr->message_id) { case PNS_PEP_CONNECT_REQ: +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK("pep_do_rcv: PNS_PEP_CONNECT_REQ\n"); +#endif if (sk->sk_state != TCP_LISTEN || sk_acceptq_is_full(sk)) { +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK( + "pep_do_rcv: PNS_PEP_CONNECT_REQ reject PN_PIPE_ERR_PEP_IN_USE\n"); +#endif pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE, GFP_ATOMIC); break; @@ -700,10 +822,16 @@ return NET_RX_SUCCESS; case PNS_PEP_DISCONNECT_REQ: +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK("pep_do_rcv: PNS_PEP_DISCONNECT_REQ\n"); +#endif pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); break; case PNS_PEP_CTRL_REQ: +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK("pep_do_rcv: PNS_PEP_CTRL_REQ\n"); +#endif pep_ctrlreq_error(sk, skb, PN_PIPE_INVALID_HANDLE, GFP_ATOMIC); break; @@ -896,6 +1024,9 @@ int err; u8 data[4] = { 0 /* sub-blocks */, PAD, PAD, PAD }; +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK("pep_sock_connect\n"); +#endif if (pn->pipe_handle == PN_PIPE_INVALID_HANDLE) pn->pipe_handle = 1; /* anything but INVALID_HANDLE */ @@ -969,6 +1100,9 @@ { struct pep_sock *pn = pep_sk(sk); +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK("pep_init\n"); +#endif sk->sk_destruct = pipe_destruct; INIT_HLIST_HEAD(&pn->hlist); pn->listener = NULL; @@ -1357,6 +1491,10 @@ static int __init pep_register(void) { +#ifdef CONFIG_BCM_KF_PHONET + PEP_PRINTK("== initialization\n"); + pr_debug("PEP initialization\n"); +#endif return phonet_proto_register(PN_PROTO_PIPE, &pep_pn_proto); } diff -urN linux-4.1.52-orig/net/phonet/pn_dev.c linux-4.1.52/net/phonet/pn_dev.c --- linux-4.1.52-orig/net/phonet/pn_dev.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/phonet/pn_dev.c 2020-02-07 01:47:54.000000000 +0800 @@ -33,6 +33,10 @@ #include #include #include +#ifdef CONFIG_BCM_KF_PHONET +#include +#include +#endif struct phonet_routes { struct mutex lock; @@ -270,6 +274,9 @@ struct phonet_net *pnn = phonet_pernet(dev_net(dev)); unsigned int i; DECLARE_BITMAP(deleted, 64); +#ifdef CONFIG_BCM_KF_PHONET + net_dbg_ratelimited(KERN_WARNING "phonet_route_autodel : %s\n", dev->name); +#endif /* Remove left-over Phonet routes */ bitmap_zero(deleted, 64); @@ -298,7 +305,11 @@ switch (what) { case NETDEV_REGISTER: +#if defined(CONFIG_BCM_KF_PHONET) && defined(CONFIG_BCM_KF_MHI) + if ((dev->type == ARPHRD_PHONET) || (dev->type == ARPHRD_MHI)) +#else if (dev->type == ARPHRD_PHONET) +#endif phonet_device_autoconf(dev); break; case NETDEV_UNREGISTER: @@ -380,12 +391,19 @@ mutex_unlock(&routes->lock); return err; } +#ifdef CONFIG_BCM_KF_PHONET +EXPORT_SYMBOL(phonet_route_add); +#endif int phonet_route_del(struct net_device *dev, u8 daddr) { struct phonet_net *pnn = phonet_pernet(dev_net(dev)); struct phonet_routes *routes = &pnn->routes; +#ifdef CONFIG_BCM_KF_PHONET + net_dbg_ratelimited(KERN_WARNING "phonet_route_del : %s addr %x\n", + dev->name, daddr); +#endif daddr = daddr >> 2; mutex_lock(&routes->lock); if (rcu_access_pointer(routes->table[daddr]) == dev) @@ -400,6 +418,9 @@ dev_put(dev); return 0; } +#ifdef CONFIG_BCM_KF_PHONET +EXPORT_SYMBOL(phonet_route_del); +#endif struct net_device *phonet_route_get_rcu(struct net *net, u8 daddr) { @@ -425,7 +446,17 @@ dev_hold(dev); rcu_read_unlock(); +#ifdef CONFIG_BCM_KF_PHONET + if (!dev) { + /* avoid to send message on the default route + * if no route fond skb is dropped */ + //dev = phonet_device_get(net); /* Default route */ + net_dbg_ratelimited(KERN_ERR + "phonet_route_output : no route found !!!\n"); + } +#else if (!dev) dev = phonet_device_get(net); /* Default route */ +#endif return dev; } diff -urN linux-4.1.52-orig/net/phonet/pn_netlink.c linux-4.1.52/net/phonet/pn_netlink.c --- linux-4.1.52-orig/net/phonet/pn_netlink.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/phonet/pn_netlink.c 2020-02-07 01:47:54.000000000 +0800 @@ -73,8 +73,10 @@ if (!netlink_capable(skb, CAP_NET_ADMIN)) return -EPERM; +#if !defined(CONFIG_BCM_KF_PHONET) if (!netlink_capable(skb, CAP_SYS_ADMIN)) return -EPERM; +#endif ASSERT_RTNL(); @@ -238,8 +240,10 @@ if (!netlink_capable(skb, CAP_NET_ADMIN)) return -EPERM; +#if !defined(CONFIG_BCM_KF_PHONET) if (!netlink_capable(skb, CAP_SYS_ADMIN)) return -EPERM; +#endif ASSERT_RTNL(); diff -urN linux-4.1.52-orig/net/phonet/socket.c linux-4.1.52/net/phonet/socket.c --- linux-4.1.52-orig/net/phonet/socket.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/phonet/socket.c 2020-02-07 01:47:54.000000000 +0800 @@ -70,6 +70,90 @@ return pnsocks.hlist + (obj & PN_HASHMASK); } +#ifdef CONFIG_BCM_KF_PHONET +/* + * Find address based on socket address, match only certain fields. + * Also grab sock if it was found. Remember to sock_put it later. + */ +struct sock *pn_find_sock_by_sa_and_skb(struct net *net, + const struct sockaddr_pn *spn, + struct sk_buff *skb) +{ + struct sock *sknode; + struct sock *rval = NULL; + u16 obj = pn_sockaddr_get_object(spn); + u8 res = spn->spn_resource; + struct hlist_head *hlist = pnsocks.hlist; + unsigned h; + u8 type; + u8 subtype; + + rcu_read_lock(); + + for (h = 0; h < PN_HASHSIZE; h++) { + sk_for_each_rcu(sknode, hlist) { + struct pn_sock *pn = pn_sk(sknode); + BUG_ON(!pn->sobject); /* unbound socket */ + if (!net_eq(sock_net(sknode), net)) + continue; + + if ((PN_PREFIX == pn->resource) && (PN_PREFIX == res)) { + + if (skb_shinfo(skb)->nr_frags) { + struct page *msg_page; + u8 *msg; + skb_frag_t *msg_frag = + &skb_shinfo(skb)->frags[0]; + + msg_page = skb_frag_page(msg_frag); + msg = page_address(msg_page); + + type = msg[msg_frag->page_offset + 2]; + subtype = + msg[msg_frag->page_offset + 3]; + + } else { + type = *(skb->data + 2); + subtype = *(skb->data + 3); + } + + if (type != pn->resource_type) + continue; + + if (subtype != pn->resource_subtype) + continue; + } + + /* If port is zero, look up by resource */ + if (pn_port(obj)) { + /* Look up socket by port */ + if (pn_port(pn->sobject) != pn_port(obj)) + continue; + } else { + + /* If port is zero, look up by resource */ + if (pn->resource != res) + continue; + } + + if (pn_addr(pn->sobject) && + pn_addr(pn->sobject) != pn_addr(obj)) + continue; + + rval = sknode; + sock_hold(sknode); + goto out; + } + hlist++; + } + +out: + rcu_read_unlock(); + + return rval; +} +#endif + /* * Find address based on socket address, match only certain fields. * Also grab sock if it was found. Remember to sock_put it later. @@ -369,6 +453,27 @@ struct sock *sk = sock->sk; struct pn_sock *pn = pn_sk(sk); +#ifdef CONFIG_BCM_KF_PHONET + if (cmd == SIOCCONFIGTYPE) { + u16 type; + if (get_user(type, (__u16 __user *) arg)) + return -EFAULT; + + pn->resource_type = type; + return 0; + } + + if (cmd == SIOCCONFIGSUBTYPE) { + u16 subtype; + + if (get_user(subtype, (__u16 __user *) arg)) + return -EFAULT; + + pn->resource_subtype = subtype; + return 0; + } +#endif + if (cmd == SIOCPNGETOBJECT) { struct net_device *dev; u16 handle; @@ -669,8 +774,10 @@ if (!net_eq(sock_net(sk), &init_net)) return -ENOIOCTLCMD; +#ifndef CONFIG_BCM_KF_PHONET if (!capable(CAP_SYS_ADMIN)) return -EPERM; +#endif if (pn_socket_autobind(sk->sk_socket)) return -EAGAIN; @@ -688,8 +795,10 @@ { int ret = -ENOENT; +#ifndef CONFIG_BCM_KF_PHONET if (!capable(CAP_SYS_ADMIN)) return -EPERM; +#endif mutex_lock(&resource_mutex); if (pnres.sk[res] == sk) { diff -urN linux-4.1.52-orig/net/sched/sch_dsmark.c linux-4.1.52/net/sched/sch_dsmark.c --- linux-4.1.52-orig/net/sched/sch_dsmark.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/sched/sch_dsmark.c 2020-02-07 01:47:54.000000000 +0800 @@ -16,6 +16,9 @@ #include #include #include +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#include +#endif /* * classid class marking @@ -306,6 +309,9 @@ break; } +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + blog_skip(skb, blog_skip_reason_sch_dsmark); +#endif return skb; } diff -urN linux-4.1.52-orig/net/sched/sch_htb.c linux-4.1.52/net/sched/sch_htb.c --- linux-4.1.52-orig/net/sched/sch_htb.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/sched/sch_htb.c 2020-02-07 01:47:54.000000000 +0800 @@ -40,6 +40,9 @@ #include #include #include +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) +#include +#endif /* HTB algorithm. Author: devik@cdi.cz @@ -924,7 +927,14 @@ m |= 1 << prio; skb = htb_dequeue_tree(q, prio, level); if (likely(skb != NULL)) +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + { + blog_skip(skb, blog_skip_reason_sch_htb); +#endif goto ok; +#if defined(CONFIG_BCM_KF_BLOG) && defined(CONFIG_BLOG) + } +#endif } } qdisc_qstats_overlimit(sch); diff -urN linux-4.1.52-orig/net/socket.c linux-4.1.52/net/socket.c --- linux-4.1.52-orig/net/socket.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/socket.c 2020-02-07 01:47:54.000000000 +0800 @@ -2904,6 +2904,10 @@ case SIOCGIFTXQLEN: case SIOCGMIIPHY: case SIOCGMIIREG: +#if defined(CONFIG_BCM_KF_WANDEV) + case SIOCGIFTRANSSTART: + case SIOCDEVISWANDEV: +#endif if (copy_in_user(uifr32, uifr, sizeof(*uifr32))) err = -EFAULT; break; @@ -3141,6 +3145,13 @@ case SIOCGMIIPHY: case SIOCGMIIREG: case SIOCSMIIREG: +#if defined(CONFIG_BCM_KF_MISC_IOCTLS) + case SIOCGIFTRANSSTART: + case SIOCCIFSTATS: +#endif +#if defined(CONFIG_BCM_KF_WANDEV) + case SIOCDEVISWANDEV: +#endif return dev_ifsioc(net, sock, cmd, argp); case SIOCSARP: diff -urN linux-4.1.52-orig/net/wireless/chan.c linux-4.1.52/net/wireless/chan.c --- linux-4.1.52-orig/net/wireless/chan.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/wireless/chan.c 2020-02-07 01:47:54.000000000 +0800 @@ -462,6 +462,12 @@ { struct ieee80211_channel *c; u32 freq, start_freq, end_freq; +#ifdef CONFIG_BCM_KF_CFG80211_BACKPORT + bool dfs_offload; + + dfs_offload = wiphy_ext_feature_isset(wiphy, + NL80211_EXT_FEATURE_DFS_OFFLOAD); +#endif /* CONFIG_BCM_KF_CFG80211_BACKPORT */ start_freq = cfg80211_get_start_freq(center_freq, bandwidth); end_freq = cfg80211_get_end_freq(center_freq, bandwidth); @@ -480,7 +486,12 @@ return false; if ((c->flags & IEEE80211_CHAN_RADAR) && +#ifdef CONFIG_BCM_KF_CFG80211_BACKPORT + (c->dfs_state != NL80211_DFS_AVAILABLE) && + !(c->dfs_state == NL80211_DFS_USABLE && dfs_offload)) +#else (c->dfs_state != NL80211_DFS_AVAILABLE)) +#endif /* CONFIG_BCM_KF_CFG80211_BACKPORT */ return false; } diff -urN linux-4.1.52-orig/net/wireless/core.c linux-4.1.52/net/wireless/core.c --- linux-4.1.52-orig/net/wireless/core.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/wireless/core.c 2020-02-07 01:47:54.000000000 +0800 @@ -1023,8 +1023,13 @@ wdev->ps = false; /* allow mac80211 to determine the timeout */ wdev->ps_timeout = -1; - +#if defined(CONFIG_BCM_KF_WL) && defined(CONFIG_BCM_HOSTAPD) + if (wdev->iftype == NL80211_IFTYPE_STATION) + break; + if (( +#else /* CONFIG_BCM_KF_WL && CONFIG_BCM_HOSTAPD */ if ((wdev->iftype == NL80211_IFTYPE_STATION || +#endif /* !CONFIG_BCM_KF_WL || !CONFIG_BCM_HOSTAPD */ wdev->iftype == NL80211_IFTYPE_P2P_CLIENT || wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr) dev->priv_flags |= IFF_DONT_BRIDGE; diff -urN linux-4.1.52-orig/net/wireless/nl80211.c linux-4.1.52/net/wireless/nl80211.c --- linux-4.1.52-orig/net/wireless/nl80211.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/wireless/nl80211.c 2020-02-07 01:47:54.000000000 +0800 @@ -887,6 +887,9 @@ break; case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_STATION: +#if defined(CONFIG_BCM_KF_WL_HOSTAPD) + case NL80211_IFTYPE_WDS: +#endif case NL80211_IFTYPE_P2P_CLIENT: if (!wdev->current_bss) return -ENOLINK; @@ -895,7 +898,9 @@ case NL80211_IFTYPE_OCB: case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_P2P_DEVICE: +#if !defined(CONFIG_BCM_KF_WL_HOSTAPD) case NL80211_IFTYPE_WDS: +#endif case NUM_NL80211_IFTYPES: return -EINVAL; } @@ -2525,6 +2530,9 @@ return 0; break; case NL80211_IFTYPE_STATION: +#if defined(CONFIG_BCM_KF_WL_HOSTAPD) + case NL80211_IFTYPE_WDS: +#endif if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_STATION) return 0; break; @@ -3274,6 +3282,12 @@ return false; return true; case NL80211_CMD_CONNECT: +#ifdef CONFIG_BCM_KF_CFG80211_BACKPORT + if (!(rdev->wiphy.features & NL80211_FEATURE_SAE) && + auth_type == NL80211_AUTHTYPE_SAE) + return false; + return true; +#endif case NL80211_CMD_START_AP: /* SAE not supported yet */ if (auth_type == NL80211_AUTHTYPE_SAE) @@ -4299,6 +4313,9 @@ case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_STATION: +#if defined(CONFIG_BCM_KF_WL_HOSTAPD) + case NL80211_IFTYPE_WDS: +#endif case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MESH_POINT: break; @@ -6344,6 +6361,11 @@ if (!cfg80211_chandef_dfs_usable(wdev->wiphy, &chandef)) return -EINVAL; +#ifdef CONFIG_BCM_KF_CFG80211_BACKPORT + /* CAC start is offloaded to HW and can't be started manually */ + if (wiphy_ext_feature_isset(wdev->wiphy, NL80211_EXT_FEATURE_DFS_OFFLOAD)) + return -EOPNOTSUPP; +#endif /* CONFIG_BCM_KF_CFG80211_BACKPORT */ if (!rdev->ops->start_radar_detection) return -EOPNOTSUPP; @@ -7688,6 +7710,9 @@ return err; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && +#if defined(CONFIG_BCM_KF_WL_HOSTAPD) + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_WDS && +#endif dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) return -EOPNOTSUPP; @@ -7809,6 +7834,9 @@ return -EINVAL; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && +#if defined(CONFIG_BCM_KF_WL_HOSTAPD) + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_WDS && +#endif dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) return -EOPNOTSUPP; @@ -7869,6 +7897,9 @@ pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && +#if defined(CONFIG_BCM_KF_WL_HOSTAPD) + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_WDS && +#endif dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) return -EOPNOTSUPP; @@ -7896,6 +7927,9 @@ struct net_device *dev = info->user_ptr[1]; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && +#if defined(CONFIG_BCM_KF_WL_HOSTAPD) + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_WDS && +#endif dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) return -EOPNOTSUPP; @@ -10229,6 +10263,43 @@ return 0; } +#ifdef CONFIG_BCM_KF_CFG80211_BACKPORT +static int nl80211_external_auth(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct cfg80211_external_auth_params params; + + if (!rdev->ops->external_auth) + return -EOPNOTSUPP; + + if (!info->attrs[NL80211_ATTR_SSID]) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_BSSID]) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_STATUS_CODE]) + return -EINVAL; + + memset(¶ms, 0, sizeof(params)); + + params.ssid.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); + if (params.ssid.ssid_len == 0 || + params.ssid.ssid_len > IEEE80211_MAX_SSID_LEN) + return -EINVAL; + memcpy(params.ssid.ssid, nla_data(info->attrs[NL80211_ATTR_SSID]), + params.ssid.ssid_len); + + memcpy(params.bssid, nla_data(info->attrs[NL80211_ATTR_BSSID]), + ETH_ALEN); + + params.status = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]); + + return rdev_external_auth(rdev, dev, ¶ms); +} + +#endif #define NL80211_FLAG_NEED_WIPHY 0x01 #define NL80211_FLAG_NEED_NETDEV 0x02 #define NL80211_FLAG_NEED_RTNL 0x04 @@ -11044,6 +11115,16 @@ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, +#ifdef CONFIG_BCM_KF_CFG80211_BACKPORT + { + .cmd = NL80211_CMD_EXTERNAL_AUTH, + .doit = nl80211_external_auth, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, +#endif }; /* notification functions */ @@ -12963,6 +13044,46 @@ out: nlmsg_free(msg); } +#ifdef CONFIG_BCM_KF_CFG80211_BACKPORT +int cfg80211_external_auth_request(struct net_device *dev, + struct cfg80211_external_auth_params *params, + gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + if (!msg) + return -ENOMEM; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_EXTERNAL_AUTH); + if (!hdr) + goto nla_put_failure; + + if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || + nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) || + nla_put_u32(msg, NL80211_ATTR_AKM_SUITES, params->key_mgmt_suite) || + nla_put_u32(msg, NL80211_ATTR_EXTERNAL_AUTH_ACTION, + params->action) || + nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, params->bssid) || + nla_put(msg, NL80211_ATTR_SSID, params->ssid.ssid_len, + params->ssid.ssid)) + goto nla_put_failure; + + genlmsg_end(msg, hdr); + + genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, + NL80211_MCGRP_MLME, gfp); + return 0; + + nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} +EXPORT_SYMBOL(cfg80211_external_auth_request); +#endif /* initialisation/exit functions */ diff -urN linux-4.1.52-orig/net/wireless/rdev-ops.h linux-4.1.52/net/wireless/rdev-ops.h --- linux-4.1.52-orig/net/wireless/rdev-ops.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/wireless/rdev-ops.h 2020-02-07 01:47:54.000000000 +0800 @@ -1018,4 +1018,20 @@ trace_rdev_return_void(&rdev->wiphy); } +#ifdef CONFIG_BCM_KF_CFG80211_BACKPORT +static inline int +rdev_external_auth(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_external_auth_params *params) +{ + int ret = -EOPNOTSUPP; + + trace_rdev_external_auth(&rdev->wiphy, dev, params); + if (rdev->ops->external_auth) + ret = rdev->ops->external_auth(&rdev->wiphy, dev, params); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +#endif #endif /* __CFG80211_RDEV_OPS */ diff -urN linux-4.1.52-orig/net/wireless/sme.c linux-4.1.52/net/wireless/sme.c --- linux-4.1.52-orig/net/wireless/sme.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/wireless/sme.c 2020-02-07 01:47:54.000000000 +0800 @@ -648,6 +648,9 @@ ASSERT_WDEV_LOCK(wdev); if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && +#if defined(CONFIG_BCM_KF_WL_HOSTAPD) + wdev->iftype != NL80211_IFTYPE_WDS && +#endif wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) { cfg80211_put_bss(wdev->wiphy, bss); return; @@ -904,6 +907,9 @@ ASSERT_WDEV_LOCK(wdev); if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && +#if defined(CONFIG_BCM_KF_WL_HOSTAPD) + wdev->iftype != NL80211_IFTYPE_WDS && +#endif wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) return; diff -urN linux-4.1.52-orig/net/wireless/trace.h linux-4.1.52/net/wireless/trace.h --- linux-4.1.52-orig/net/wireless/trace.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/wireless/trace.h 2020-02-07 01:47:54.000000000 +0800 @@ -2082,6 +2082,31 @@ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(addr)) ); +#ifdef CONFIG_BCM_KF_CFG80211_BACKPORT +TRACE_EVENT(rdev_external_auth, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_external_auth_params *params), + TP_ARGS(wiphy, netdev, params), + TP_STRUCT__entry(WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(bssid) + __array(u8, ssid, IEEE80211_MAX_SSID_LEN + 1) + __field(u16, status) + ), + TP_fast_assign(WIPHY_ASSIGN; + NETDEV_ASSIGN; + MAC_ASSIGN(bssid, params->bssid); + memset(__entry->ssid, 0, IEEE80211_MAX_SSID_LEN + 1); + memcpy(__entry->ssid, params->ssid.ssid, + params->ssid.ssid_len); + __entry->status = params->status; + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT + ", ssid: %s, status: %u", WIPHY_PR_ARG, NETDEV_PR_ARG, + __entry->bssid, __entry->ssid, __entry->status) + ); + +#endif /************************************************************* * cfg80211 exported functions traces * *************************************************************/ diff -urN linux-4.1.52-orig/net/wireless/util.c linux-4.1.52/net/wireless/util.c --- linux-4.1.52-orig/net/wireless/util.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/wireless/util.c 2020-02-07 01:47:54.000000000 +0800 @@ -88,6 +88,12 @@ if (chan < 5) return 56160 + chan * 2160; break; +#ifdef CONFIG_BCM_KF_NL80211_6G_BAND_SUPPORT + case IEEE80211_BAND_6GHZ: + if (chan >= 1 && chan <= 233) + return 5940 + chan * 5; + break; +#endif /* CONFIG_BCM_KF_NL80211_6G_BAND_SUPPORT */ default: ; } @@ -104,6 +110,10 @@ return (freq - 2407) / 5; else if (freq >= 4910 && freq <= 4980) return (freq - 4000) / 5; +#ifdef CONFIG_BCM_KF_NL80211_6G_BAND_SUPPORT + else if (freq >= 5945 && freq <= 7125) + return (freq - 5940) / 5; +#endif /* CONFIG_BCM_KF_NL80211_6G_BAND_SUPPORT */ else if (freq <= 45000) /* DMG band lower limit */ return (freq - 5000) / 5; else if (freq >= 58320 && freq <= 64800) @@ -142,6 +152,9 @@ int i, want; switch (band) { +#ifdef CONFIG_BCM_KF_NL80211_6G_BAND_SUPPORT + case IEEE80211_BAND_6GHZ: +#endif /* CONFIG_BCM_KF_NL80211_6G_BAND_SUPPORT */ case IEEE80211_BAND_5GHZ: want = 3; for (i = 0; i < sband->n_bitrates; i++) { @@ -989,6 +1002,9 @@ if (dev->ieee80211_ptr->use_4addr) break; /* fall through */ +#if defined(CONFIG_BCM_KF_WL) && defined(CONFIG_BCM_HOSTAPD) + break; +#endif /* CONFIG_BCM_KF_WL && CONFIG_BCM_HOSTAPD */ case NL80211_IFTYPE_OCB: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_ADHOC: diff -urN linux-4.1.52-orig/net/xfrm/xfrm_replay.c linux-4.1.52/net/xfrm/xfrm_replay.c --- linux-4.1.52-orig/net/xfrm/xfrm_replay.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/net/xfrm/xfrm_replay.c 2020-02-07 01:47:54.000000000 +0800 @@ -45,6 +45,9 @@ return seq_hi; } +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) && defined(CONFIG_BLOG) +EXPORT_SYMBOL(xfrm_replay_seqhi); +#endif static void xfrm_replay_notify(struct xfrm_state *x, int event) { @@ -98,10 +101,20 @@ struct net *net = xs_net(x); if (x->type->flags & XFRM_TYPE_REPLAY_PROT) { +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) && defined(CONFIG_BLOG) + if ( skb == NULL ) ++x->replay.oseq; + else { +#endif XFRM_SKB_CB(skb)->seq.output.low = ++x->replay.oseq; XFRM_SKB_CB(skb)->seq.output.hi = 0; +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) && defined(CONFIG_BLOG) + } +#endif if (unlikely(x->replay.oseq == 0)) { x->replay.oseq--; +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) && defined(CONFIG_BLOG) + if ( skb != NULL ) +#endif xfrm_audit_state_replay_overflow(x, skb); err = -EOVERFLOW; @@ -142,6 +155,9 @@ return 0; err: +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) && defined(CONFIG_BLOG) + if ( skb != NULL ) +#endif xfrm_audit_state_replay(x, skb, net_seq); return -EINVAL; } @@ -177,10 +193,20 @@ struct net *net = xs_net(x); if (x->type->flags & XFRM_TYPE_REPLAY_PROT) { +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) && defined(CONFIG_BLOG) + if ( skb == NULL ) ++replay_esn->oseq; + else { +#endif XFRM_SKB_CB(skb)->seq.output.low = ++replay_esn->oseq; XFRM_SKB_CB(skb)->seq.output.hi = 0; +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) && defined(CONFIG_BLOG) + } +#endif if (unlikely(replay_esn->oseq == 0)) { replay_esn->oseq--; +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) && defined(CONFIG_BLOG) + if ( skb != NULL ) +#endif xfrm_audit_state_replay_overflow(x, skb); err = -EOVERFLOW; @@ -233,6 +259,9 @@ err_replay: x->stats.replay++; err: +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) && defined(CONFIG_BLOG) + if ( skb != NULL ) +#endif xfrm_audit_state_replay(x, skb, net_seq); return -EINVAL; } @@ -409,6 +438,23 @@ struct net *net = xs_net(x); if (x->type->flags & XFRM_TYPE_REPLAY_PROT) { +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) && defined(CONFIG_BLOG) + if ( skb == NULL ) + { + ++replay_esn->oseq; + if (unlikely(replay_esn->oseq == 0)) { + ++replay_esn->oseq_hi; + if (replay_esn->oseq_hi == 0) { + replay_esn->oseq--; + replay_esn->oseq_hi--; + err = -EOVERFLOW; + return err; + } + } + } + else + { +#endif XFRM_SKB_CB(skb)->seq.output.low = ++replay_esn->oseq; XFRM_SKB_CB(skb)->seq.output.hi = replay_esn->oseq_hi; @@ -424,6 +470,9 @@ return err; } } +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) && defined(CONFIG_BLOG) + } +#endif if (xfrm_aevent_is_on(net)) x->repl->notify(x, XFRM_REPLAY_UPDATE); } @@ -486,6 +535,9 @@ err_replay: x->stats.replay++; err: +#if defined(CONFIG_BCM_KF_SPU) && (defined(CONFIG_BCM_SPU) || defined(CONFIG_BCM_SPU_MODULE)) && defined(CONFIG_BLOG) + if ( skb != NULL ) +#endif xfrm_audit_state_replay(x, skb, net_seq); return -EINVAL; } diff -urN linux-4.1.52-orig/scripts/kconfig/conf.c linux-4.1.52/scripts/kconfig/conf.c --- linux-4.1.52-orig/scripts/kconfig/conf.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/scripts/kconfig/conf.c 2020-02-07 01:47:54.000000000 +0800 @@ -110,6 +110,12 @@ case oldaskconfig: fflush(stdout); xfgets(line, 128, stdin); +#if defined(CONFIG_BCM_KF_FAIL_CONFIG_ON_EOF) || !defined(CONFIG_BCM_IN_KERNEL) + if (feof(stdin)) { + fprintf(stderr, "Unexpected EOF\n"); + exit(1); + } +#endif if (!tty_stdio) printf("\n"); return 1; @@ -312,6 +318,12 @@ case oldaskconfig: fflush(stdout); xfgets(line, 128, stdin); +#if defined(CONFIG_BCM_KF_FAIL_CONFIG_ON_EOF) || !defined(CONFIG_BCM_IN_KERNEL) + if (feof(stdin)) { + fprintf(stderr, "Unexpected EOF\n"); + exit(1); + } +#endif strip(line); if (line[0] == '?') { print_help(menu); diff -urN linux-4.1.52-orig/scripts/Makefile.build linux-4.1.52/scripts/Makefile.build --- linux-4.1.52-orig/scripts/Makefile.build 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/scripts/Makefile.build 2020-02-07 01:47:54.000000000 +0800 @@ -269,6 +269,26 @@ $(call cmd,force_checksrc) $(call if_changed_rule,cc_o_c) +ifdef BCM_KF # defined(CONFIG_BCM_KF_ATM_BACKEND) +# C++ support +cmd_cc_o_cpp = $(CXX) $(c_flags) -c -o $@ $< +quiet_cmd_cc_o_cpp = C++ $(quiet_modtag) $@ + +define rule_cc_o_cpp + $(call echo-cmd,checksrc) $(cmd_checksrc) \ + $(call echo-cmd,cc_o_cpp) $(cmd_cc_o_cpp); \ + $(cmd_modversions) \ + $(cmd_record_mcount) \ + scripts/basic/fixdep $(depfile) $@ '$(call make-cmd,cc_o_cpp)' > \ + $(dot-target).tmp; \ + rm -f $(depfile); \ + mv -f $(dot-target).tmp $(dot-target).cmd +endef + +%.o: %.cpp FORCE + $(call if_changed_rule,cc_o_cpp) +endif # BCM_KF # (CONFIG_BCM_KF_ATM_BACKEND) + # Single-part modules are special since we need to mark them in $(MODVERDIR) $(single-used-m): $(obj)/%.o: $(src)/%.c $(recordmcount_source) FORCE diff -urN linux-4.1.52-orig/scripts/Makefile.lib linux-4.1.52/scripts/Makefile.lib --- linux-4.1.52-orig/scripts/Makefile.lib 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/scripts/Makefile.lib 2020-02-07 01:47:54.000000000 +0800 @@ -101,8 +101,13 @@ modname_flags = $(if $(filter 1,$(words $(modname))),\ -D"KBUILD_MODNAME=KBUILD_STR($(call name-fix,$(modname)))") +ifdef BCM_KF # defined(CONFIG_BCM_KF_MISC_MAKEFILE) +orig_c_flags = $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) $(KBUILD_SUBDIR_CCFLAGS) \ + $(BCM_KBUILD_CMDLINE_FLAGS) $(ccflags-y) $(CFLAGS_$(basetarget).o) +else # BCM_KF orig_c_flags = $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) $(KBUILD_SUBDIR_CCFLAGS) \ $(ccflags-y) $(CFLAGS_$(basetarget).o) +endif # BCM_KF _c_flags = $(filter-out $(CFLAGS_REMOVE_$(basetarget).o), $(orig_c_flags)) _a_flags = $(KBUILD_CPPFLAGS) $(KBUILD_AFLAGS) $(KBUILD_SUBDIR_ASFLAGS) \ $(asflags-y) $(AFLAGS_$(basetarget).o) diff -urN linux-4.1.52-orig/tools/build/feature/Makefile linux-4.1.52/tools/build/feature/Makefile --- linux-4.1.52-orig/tools/build/feature/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/tools/build/feature/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -67,7 +67,15 @@ $(BUILD) -lelf test-glibc.bin: - $(BUILD) +ifdef BCM_KF +ifneq ($(findstring _$(strip $(BRCM_CHIP))_,_6848_6838_),) + $(BUILD) -DCONFIG_BCM_KF_MIPS_4350 +else + $(BUILD) +endif +else + $(BUILD) +endif test-dwarf.bin: $(BUILD) -ldw diff -urN linux-4.1.52-orig/tools/build/feature/test-glibc.c linux-4.1.52/tools/build/feature/test-glibc.c --- linux-4.1.52-orig/tools/build/feature/test-glibc.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/tools/build/feature/test-glibc.c 2020-02-07 01:47:54.000000000 +0800 @@ -1,8 +1,28 @@ +#ifdef CONFIG_BCM_KF_MIPS_4350 +#include +#endif + +#ifdef CONFIG_BCM_KF_MIPS_4350 +#if !defined(__UCLIBC__) +#include +#else +#define XSTR(s) STR(s) +#define STR(s) #s +#endif +#else #include +#endif int main(void) { +#ifdef CONFIG_BCM_KF_MIPS_4350 +#if !defined(__UCLIBC__) + const char *version = gnu_get_libc_version(); + #else + const char *version = XSTR(__GLIBC__) "." XSTR(__GLIBC_MINOR__); +#endif +#else const char *version = gnu_get_libc_version(); - - return (long)version; +#endif + return (long)version; } diff -urN linux-4.1.52-orig/tools/perf/arch/mips/Build linux-4.1.52/tools/perf/arch/mips/Build --- linux-4.1.52-orig/tools/perf/arch/mips/Build 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/tools/perf/arch/mips/Build 2020-02-07 01:47:54.000000000 +0800 @@ -1 +1 @@ -# empty +libperf-y += util/ \ No newline at end of file diff -urN linux-4.1.52-orig/tools/perf/arch/mips/dwarf-regs.c linux-4.1.52/tools/perf/arch/mips/dwarf-regs.c --- linux-4.1.52-orig/tools/perf/arch/mips/dwarf-regs.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/tools/perf/arch/mips/dwarf-regs.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,37 @@ +/* + + * dwarf-regs.c : Mapping of DWARF debug register numbers into register names. + + * + + * Copyright (C) 2013 Cavium, Inc. + * + * 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. + * + */ + +#include +#include + +static const char *mips_gpr_names[32] = { + "$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" +}; + +const char *get_arch_regstr(unsigned int n) +{ + if (n < 32) + return mips_gpr_names[n]; + if (n == 64) + return "hi"; + if (n == 65) + return "lo"; + return NULL; +} \ No newline at end of file diff -urN linux-4.1.52-orig/tools/perf/arch/mips/include/libunwind_mips.h linux-4.1.52/tools/perf/arch/mips/include/libunwind_mips.h --- linux-4.1.52-orig/tools/perf/arch/mips/include/libunwind_mips.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/tools/perf/arch/mips/include/libunwind_mips.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,119 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2008 CodeSourcery + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef LIBUNWIND_H +#define LIBUNWIND_H + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + + +#ifdef mips +# undef mips +#endif + +#define UNW_TARGET mips +#define UNW_TARGET_MIPS 1 + +#define _U_TDEP_QP_TRUE 0 /* see libunwind-dynamic.h */ + +/* This needs to be big enough to accommodate "struct cursor", while + leaving some slack for future expansion. Changing this value will + require recompiling all users of this library. Stack allocation is + relatively cheap and unwind-state copying is relatively rare, so we + want to err on making it rather too big than too small. */ + +/* FIXME for MIPS. Too big? What do other things use for similar tasks? */ +#define UNW_TDEP_CURSOR_LEN 4096 + + + +typedef enum + { + UNW_MIPS_R0, + UNW_MIPS_R1, + UNW_MIPS_R2, + UNW_MIPS_R3, + UNW_MIPS_R4, + UNW_MIPS_R5, + UNW_MIPS_R6, + UNW_MIPS_R7, + UNW_MIPS_R8, + UNW_MIPS_R9, + UNW_MIPS_R10, + UNW_MIPS_R11, + UNW_MIPS_R12, + UNW_MIPS_R13, + UNW_MIPS_R14, + UNW_MIPS_R15, + UNW_MIPS_R16, + UNW_MIPS_R17, + UNW_MIPS_R18, + UNW_MIPS_R19, + UNW_MIPS_R20, + UNW_MIPS_R21, + UNW_MIPS_R22, + UNW_MIPS_R23, + UNW_MIPS_R24, + UNW_MIPS_R25, + UNW_MIPS_R26, + UNW_MIPS_R27, + UNW_MIPS_R28, + UNW_MIPS_R29, + UNW_MIPS_R30, + UNW_MIPS_R31, + + UNW_MIPS_PC = 34, + + /* FIXME: Other registers! */ + + /* For MIPS, the CFA is the value of SP (r29) at the call site in the + previous frame. */ + UNW_MIPS_CFA, + + UNW_TDEP_LAST_REG = UNW_MIPS_R31, + + UNW_TDEP_IP = UNW_MIPS_R31, + UNW_TDEP_SP = UNW_MIPS_R29, + UNW_TDEP_EH = UNW_MIPS_R0 /* FIXME. */ + } +mips_regnum_t; + +typedef enum + { + UNW_MIPS_ABI_O32, + UNW_MIPS_ABI_N32, + UNW_MIPS_ABI_N64 + } +mips_abi_t; + +#define UNW_TDEP_NUM_EH_REGS 2 /* FIXME for MIPS. */ + + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + +#endif /* LIBUNWIND_H */ \ No newline at end of file diff -urN linux-4.1.52-orig/tools/perf/arch/mips/include/perf_regs.h linux-4.1.52/tools/perf/arch/mips/include/perf_regs.h --- linux-4.1.52-orig/tools/perf/arch/mips/include/perf_regs.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/tools/perf/arch/mips/include/perf_regs.h 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,86 @@ +#ifndef ARCH_PERF_REGS_H +#define ARCH_PERF_REGS_H + +#include +#include +#include + +#define PERF_REG_IP PERF_REG_MIPS_PC +#define PERF_REG_SP PERF_REG_MIPS_R29 + +#define PERF_REGS_MASK ((1ULL << PERF_REG_MIPS_MAX) - 1) +#define PERF_REGS_MAX PERF_REG_MIPS_MAX + + +static inline const char *perf_reg_name(int id) +{ + switch (id) { + case PERF_REG_MIPS_PC: + return "PC"; + case PERF_REG_MIPS_R1: + return "$1"; + case PERF_REG_MIPS_R2: + return "$2"; + case PERF_REG_MIPS_R3: + return "$3"; + case PERF_REG_MIPS_R4: + return "$4"; + case PERF_REG_MIPS_R5: + return "$5"; + case PERF_REG_MIPS_R6: + return "$6"; + case PERF_REG_MIPS_R7: + return "$7"; + case PERF_REG_MIPS_R8: + return "$8"; + case PERF_REG_MIPS_R9: + return "$9"; + case PERF_REG_MIPS_R10: + return "$10"; + case PERF_REG_MIPS_R11: + return "$11"; + case PERF_REG_MIPS_R12: + return "$12"; + case PERF_REG_MIPS_R13: + return "$13"; + case PERF_REG_MIPS_R14: + return "$14"; + case PERF_REG_MIPS_R15: + return "$15"; + case PERF_REG_MIPS_R16: + return "$16"; + case PERF_REG_MIPS_R17: + return "$17"; + case PERF_REG_MIPS_R18: + return "$18"; + case PERF_REG_MIPS_R19: + return "$19"; + case PERF_REG_MIPS_R20: + return "$20"; + case PERF_REG_MIPS_R21: + return "$21"; + case PERF_REG_MIPS_R22: + return "$22"; + case PERF_REG_MIPS_R23: + return "$23"; + case PERF_REG_MIPS_R24: + return "$24"; + case PERF_REG_MIPS_R25: + return "$25"; + + case PERF_REG_MIPS_R28: + return "$28"; + case PERF_REG_MIPS_R29: + return "$29"; + case PERF_REG_MIPS_R30: + return "$30"; + case PERF_REG_MIPS_R31: + return "$31"; + default: + break; + } + return NULL; +} + + +#endif /* ARCH_PERF_REGS_H */ \ No newline at end of file diff -urN linux-4.1.52-orig/tools/perf/arch/mips/Makefile linux-4.1.52/tools/perf/arch/mips/Makefile --- linux-4.1.52-orig/tools/perf/arch/mips/Makefile 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/tools/perf/arch/mips/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,4 @@ + +ifndef NO_DWARF +PERF_HAVE_DWARF_REGS := 1 +endif diff -urN linux-4.1.52-orig/tools/perf/arch/mips/util/Build linux-4.1.52/tools/perf/arch/mips/util/Build --- linux-4.1.52-orig/tools/perf/arch/mips/util/Build 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/tools/perf/arch/mips/util/Build 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,4 @@ +#libperf-$(CONFIG_DWARF) += dwarf-regs.o +#libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind.o +libperf-y += dwarf-regs.o +libperf-y += unwind-libunwind.o \ No newline at end of file diff -urN linux-4.1.52-orig/tools/perf/arch/mips/util/dwarf-regs.c linux-4.1.52/tools/perf/arch/mips/util/dwarf-regs.c --- linux-4.1.52-orig/tools/perf/arch/mips/util/dwarf-regs.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/tools/perf/arch/mips/util/dwarf-regs.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,41 @@ +/* + * dwarf-regs.c : Mapping of DWARF debug register numbers into register names. + * + * Copyright (C) 2013 Cavium, Inc. + * + * 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. + * + */ + +//#include +#ifndef NULL + #define NULL 0 +#endif + +#include + +static const char *mips_gpr_names[32] = { + "$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" +}; + +const char *get_arch_regstr(unsigned int n) +{ + if (n < 32) + return mips_gpr_names[n]; + if (n == 64) + return "hi"; + if (n == 65) + return "lo"; + return NULL; +} diff -urN linux-4.1.52-orig/tools/perf/arch/mips/util/unwind.c linux-4.1.52/tools/perf/arch/mips/util/unwind.c --- linux-4.1.52-orig/tools/perf/arch/mips/util/unwind.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/tools/perf/arch/mips/util/unwind.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,20 @@ + + #include + #include + #include "perf_regs.h" + #include "../../util/unwind.h" + + int unwind_arch_reg_id(int regnum) + { + switch (regnum) { + case UNW_MIPS_R1 ... UNW_MIPS_R25: + return regnum - UNW_MIPS_R1 + PERF_REG_MIPS_R1; + case UNW_MIPS_R28 ... UNW_MIPS_R31: + return regnum - UNW_MIPS_R28 + PERF_REG_MIPS_R28; + case UNW_MIPS_PC: + return PERF_REG_MIPS_PC; + default: + pr_err("unwind: invalid reg id %d\n", regnum); + return -EINVAL; + } + } \ No newline at end of file diff -urN linux-4.1.52-orig/tools/perf/arch/mips/util/unwind-libunwind.c linux-4.1.52/tools/perf/arch/mips/util/unwind-libunwind.c --- linux-4.1.52-orig/tools/perf/arch/mips/util/unwind-libunwind.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/tools/perf/arch/mips/util/unwind-libunwind.c 2020-02-07 01:47:54.000000000 +0800 @@ -0,0 +1,21 @@ + + #include + #include "libunwind_mips.h" + #include "perf_regs.h" + #include "../../util/unwind.h" + #include "../../util/debug.h" + + int libunwind__arch_reg_id(int regnum) + { + switch (regnum) { + case UNW_MIPS_R1 ... UNW_MIPS_R25: + return regnum - UNW_MIPS_R1 + PERF_REG_MIPS_R1; + case UNW_MIPS_R28 ... UNW_MIPS_R31: + return regnum - UNW_MIPS_R28 + PERF_REG_MIPS_R28; + case UNW_MIPS_PC: + return PERF_REG_MIPS_PC; + default: + pr_err("unwind: invalid reg id %d\n", regnum); + return -EINVAL; + } + } \ No newline at end of file diff -urN linux-4.1.52-orig/tools/perf/config/Makefile linux-4.1.52/tools/perf/config/Makefile --- linux-4.1.52-orig/tools/perf/config/Makefile 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/tools/perf/config/Makefile 2020-02-07 01:47:54.000000000 +0800 @@ -47,6 +47,14 @@ LIBUNWIND_LIBS = -lunwind -lunwind-aarch64 endif +ifdef BCM_KF #fdef CONFIG_BCM_KF_MIPS_4350 +ifeq ($(ARCH),mips) + ARCH_SUPPORTS_LIBUNWIND := 1 + NO_PERF_REGS := 0 + LIBUNWIND_LIBS = -lunwind -lunwind-mips +endif +endif #BCM_KF + ifeq ($(NO_PERF_REGS),0) $(call detected,CONFIG_PERF_REGS) endif diff -urN linux-4.1.52-orig/tools/perf/util/dso.c linux-4.1.52/tools/perf/util/dso.c --- linux-4.1.52-orig/tools/perf/util/dso.c 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/tools/perf/util/dso.c 2020-02-07 01:47:54.000000000 +0800 @@ -193,6 +193,56 @@ dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE_COMP; } +#define IGNORE_BCM_KF_EXCEPTION +#if 1 //1 defined(CONFIG_BCM_KF_MISC_BACKPORTS) +static int decompress_kmodule(struct dso *dso, const char *name, char *tmpbuf) +{ + int fd = -1; + struct kmod_path m; + + if (!dso__needs_decompress(dso)) + return -1; + + if (kmod_path__parse_ext(&m, dso->long_name)) + return -1; + + if (!m.comp) + goto out; + + fd = mkstemp(tmpbuf); + if (fd < 0) { + dso->load_errno = errno; + goto out; + } + + if (!decompress_to_file(m.ext, name, fd)) { + dso->load_errno = DSO_LOAD_ERRNO__DECOMPRESSION_FAILURE; + close(fd); + fd = -1; + } + +out: + free(m.ext); + return fd; +} + +int dso__decompress_kmodule_path(struct dso *dso, const char *name, + char *pathname, size_t len) +{ + char tmpbuf[] = KMOD_DECOMP_NAME; + int fd; + + fd = decompress_kmodule(dso, name, tmpbuf); + if (fd < 0) { + unlink(tmpbuf); + return -1; + } + + strncpy(pathname, tmpbuf, len); + close(fd); + return 0; +} +#endif /* * Parses kernel module specified in @path and updates * @m argument like: diff -urN linux-4.1.52-orig/tools/perf/util/dso.h linux-4.1.52/tools/perf/util/dso.h --- linux-4.1.52-orig/tools/perf/util/dso.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/tools/perf/util/dso.h 2020-02-07 01:47:54.000000000 +0800 @@ -220,6 +220,14 @@ bool decompress_to_file(const char *ext, const char *filename, int output_fd); bool dso__needs_decompress(struct dso *dso); +#define IGNORE_BCM_KF_EXCEPTION +#if 1 // defined(CONFIG_BCM_KF_MISC_BACKPORTS) +int dso__decompress_kmodule_path(struct dso *dso, const char *name, + char *pathname, size_t len); +#define KMOD_DECOMP_NAME "/tmp/perf-kmod-XXXXXX" +#define KMOD_DECOMP_LEN sizeof(KMOD_DECOMP_NAME) +#endif + struct kmod_path { char *name; char *ext; diff -urN linux-4.1.52-orig/tools/perf/util/include/dwarf-regs.h linux-4.1.52/tools/perf/util/include/dwarf-regs.h --- linux-4.1.52-orig/tools/perf/util/include/dwarf-regs.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/tools/perf/util/include/dwarf-regs.h 2020-02-07 01:47:54.000000000 +0800 @@ -1,8 +1,12 @@ #ifndef _PERF_DWARF_REGS_H_ #define _PERF_DWARF_REGS_H_ -#ifdef HAVE_DWARF_SUPPORT +#if defined(CONFIG_BCM_KF_MIPS_4350) && (defined(CONFIG_BCM96838) || defined(CONFIG_BCM96848)) const char *get_arch_regstr(unsigned int n); #endif +#ifdef HAVE_DWARF_SUPPORT +const char *get_arch_regstr(unsigned int n); #endif +#endif + diff -urN linux-4.1.52-orig/tools/perf/util/unwind.h linux-4.1.52/tools/perf/util/unwind.h --- linux-4.1.52-orig/tools/perf/util/unwind.h 2018-05-28 10:26:45.000000000 +0800 +++ linux-4.1.52/tools/perf/util/unwind.h 2020-02-07 01:47:54.000000000 +0800 @@ -14,6 +14,10 @@ typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg); +#if defined(CONFIG_BCM_KF_MIPS_4350) && (defined(CONFIG_BCM96838) || defined(CONFIG_BCM96848)) +int libunwind__arch_reg_id(int regnum); +#endif + #ifdef HAVE_DWARF_UNWIND_SUPPORT int unwind__get_entries(unwind_entry_cb_t cb, void *arg, struct thread *thread, diff -urN linux-4.1.52-orig/wlconf.h linux-4.1.52/wlconf.h --- linux-4.1.52-orig/wlconf.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-4.1.52/wlconf.h 2020-02-07 12:43:05.000000000 +0800 @@ -0,0 +1,102 @@ +/* + * Broadcom 802.11 Networking Device Driver Configuration file + * + * Copyright (C) 2020, Broadcom. All Rights Reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * + * <> + * + * $Id: wltunable_lx_6878.h 772733 2019-03-04 01:50:03Z $ + * + * wl driver tunables + */ + +#define D11CONF 0x40000000 /* D11 Core Rev + * 30 (43217). + */ +#define D11CONF2 0x20000400 /* D11 Core Rev > 31, Rev 42(4360b0), + * 61(6878) + */ +#define D11CONF3 0x00000000 /* D11 Core Rev > 63 */ +#define D11CONF5 0x00000000 /* D11 Core Rev > 127 */ +#define ACCONF 0x00000002 /* AC-Phy Rev 1(4360b0) */ +#define ACCONF2 0x00000000 /* AC-Phy Rev > 31 */ + +#define D11CONF4 0x0 + +#define ACCONF5 0x00000001 /* AC-Phy Rev > 127, Rev 128 (6878) */ +#define NCONF 0x00020000 /* Rev 17(43217) */ +#define LCN20CONF 0x0 + +#define NRXBUFPOST 56 /* # rx buffers posted */ +#define RXBND 24 /* max # rx frames to process */ +#define PKTCBND 36 /* max # rx frames to chain */ +#ifdef __ARM_ARCH_7A__ +#define CTFPOOLSZ 512 /* max buffers in ctfpool */ +#else +#define CTFPOOLSZ 192 /* max buffers in ctfpool */ +#endif // endif + +#define WME_PER_AC_TX_PARAMS 1 +#define WME_PER_AC_TUNING 1 + +#define NTXD_AC3X3 512 /* TX descriptor ring */ +#define NRXD_AC3X3 512 /* RX descriptor ring */ +#define NTXD_LARGE_AC3X3 2048 /* TX descriptor ring */ +#define NRXD_LARGE_AC3X3 2048 /* RX descriptor ring */ +#define NRXBUFPOST_AC3X3 500 /* # rx buffers posted */ +#define RXBND_AC3X3 36 /* max # rx frames to process */ + +#define STSBUF_MP_N_OBJ 2048 +#define NRXD_STS 2048 +#define NRXBUFPOST_STS 512 + +#ifdef __ARM_ARCH_7A__ +#if defined(BCM_GMAC3) +#define CTFPOOLSZ_AC3X3 1536 /* max buffers in ctfpool */ +#else +#define CTFPOOLSZ_AC3X3 1024 /* max buffers in ctfpool */ +#endif /* ! BCM_GMAC3 */ +#else +#define CTFPOOLSZ_AC3X3 512 /* max buffers in ctfpool */ +#endif /* ! __ARM_ARCH_7A__ */ +#define PKTCBND_AC3X3 48 /* max # rx frames to chain */ + +#define TXMR 2 /* number of outstanding reads */ +#define TXPREFTHRESH 8 /* prefetch threshold */ +#define TXPREFCTL 16 /* max descr allowed in prefetch request */ +#define TXBURSTLEN 256 /* burst length for dma reads */ + +#define RXPREFTHRESH 1 /* prefetch threshold */ +#define RXPREFCTL 8 /* max descr allowed in prefetch request */ +#define RXBURSTLEN 256 /* burst length for dma writes */ + +#define MRRS 512 /* Max read request size */ + +/* AC2 settings */ +#define TXMR_AC2 12 /* number of outstanding reads */ +#define TXPREFTHRESH_AC2 8 /* prefetch threshold */ +#define TXPREFCTL_AC2 16 /* max descr allowed in prefetch request */ +#define TXBURSTLEN_AC2 1024 /* burst length for dma reads */ +#define RXPREFTHRESH_AC2 8 /* prefetch threshold */ +#define RXPREFCTL_AC2 16 /* max descr allowed in prefetch request */ +#define RXBURSTLEN_AC2 128 /* burst length for dma writes */ +#define MRRS_AC2 1024 /* Max read request size */ +/* AC2 settings */ + +#define AMPDU_PKTQ_LEN 8192 +#define AMPDU_PKTQ_FAVORED_LEN 4096 + +#define WLRXEXTHDROOM -1 /* to reserve extra headroom in DMA Rx buffer */