diff --git a/kernel/kernel/files/configs/kernel-x86_64-config b/kernel/kernel/files/configs/kernel-x86_64-config index cff691dd..7de1fbcd 100644 --- a/kernel/kernel/files/configs/kernel-x86_64-config +++ b/kernel/kernel/files/configs/kernel-x86_64-config @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/x86_64 4.3.2 Kernel Configuration +# Linux/x86_64 4.4.0 Kernel Configuration # CONFIG_64BIT=y CONFIG_X86_64=y @@ -311,7 +311,6 @@ CONFIG_MODULE_FORCE_UNLOAD=y # CONFIG_MODULE_SIG is not set # CONFIG_MODULE_COMPRESS is not set CONFIG_MODULES_TREE_LOOKUP=y -CONFIG_STOP_MACHINE=y CONFIG_BLOCK=y CONFIG_BLK_DEV_BSG=y CONFIG_BLK_DEV_BSGLIB=y @@ -449,7 +448,7 @@ CONFIG_X86_16BIT=y CONFIG_X86_ESPFIX64=y CONFIG_X86_VSYSCALL_EMULATION=y CONFIG_I8K=m -CONFIG_MICROCODE=m +CONFIG_MICROCODE=y CONFIG_MICROCODE_INTEL=y CONFIG_MICROCODE_AMD=y CONFIG_MICROCODE_OLD_INTERFACE=y @@ -488,7 +487,6 @@ CONFIG_HAVE_BOOTMEM_INFO_NODE=y CONFIG_MEMORY_HOTPLUG=y CONFIG_MEMORY_HOTPLUG_SPARSE=y CONFIG_MEMORY_HOTREMOVE=y -CONFIG_PAGEFLAGS_EXTENDED=y CONFIG_SPLIT_PTLOCK_CPUS=4 CONFIG_ARCH_ENABLE_SPLIT_PMD_PTLOCK=y CONFIG_MEMORY_BALLOON=y @@ -557,6 +555,9 @@ CONFIG_HOTPLUG_CPU=y # CONFIG_BOOTPARAM_HOTPLUG_CPU0 is not set # CONFIG_DEBUG_HOTPLUG_CPU0 is not set # CONFIG_COMPAT_VDSO is not set +# CONFIG_LEGACY_VSYSCALL_NATIVE is not set +CONFIG_LEGACY_VSYSCALL_EMULATE=y +# CONFIG_LEGACY_VSYSCALL_NONE is not set # CONFIG_CMDLINE_BOOL is not set CONFIG_MODIFY_LDT_SYSCALL=y CONFIG_HAVE_LIVEPATCH=y @@ -591,6 +592,7 @@ CONFIG_ACPI=y CONFIG_ACPI_LEGACY_TABLES_LOOKUP=y CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC=y CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT=y +# CONFIG_ACPI_DEBUGGER is not set CONFIG_ACPI_SLEEP=y # CONFIG_ACPI_PROCFS_POWER is not set CONFIG_ACPI_REV_OVERRIDE_POSSIBLE=y @@ -910,8 +912,7 @@ CONFIG_NF_CONNTRACK_SIP=m CONFIG_NF_CONNTRACK_TFTP=m CONFIG_NF_CT_NETLINK=m # CONFIG_NF_CT_NETLINK_TIMEOUT is not set -# CONFIG_NF_CT_NETLINK_HELPER is not set -CONFIG_NETFILTER_NETLINK_QUEUE_CT=y +# CONFIG_NETFILTER_NETLINK_GLUE_CT is not set CONFIG_NF_NAT=m CONFIG_NF_NAT_NEEDED=y CONFIG_NF_NAT_PROTO_DCCP=m @@ -1262,6 +1263,7 @@ CONFIG_6LOWPAN_NHC_MOBILITY=m CONFIG_6LOWPAN_NHC_ROUTING=m CONFIG_6LOWPAN_NHC_UDP=m CONFIG_IEEE802154=m +# CONFIG_IEEE802154_NL802154_EXPERIMENTAL is not set CONFIG_IEEE802154_SOCKET=m CONFIG_IEEE802154_6LOWPAN=m CONFIG_MAC802154=m @@ -1359,6 +1361,7 @@ CONFIG_MPLS_ROUTING=m CONFIG_MPLS_IPTUNNEL=m CONFIG_HSR=m # CONFIG_NET_SWITCHDEV is not set +# CONFIG_NET_L3_MASTER_DEV is not set CONFIG_RPS=y CONFIG_RFS_ACCEL=y CONFIG_XPS=y @@ -1513,6 +1516,7 @@ CONFIG_CFG80211=m # CONFIG_CFG80211_DEFAULT_PS is not set CONFIG_CFG80211_DEBUGFS=y # CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_CRDA_SUPPORT=y CONFIG_CFG80211_WEXT=y CONFIG_CFG80211_WEXT_EXPORT=y CONFIG_LIB80211=m @@ -1562,6 +1566,8 @@ CONFIG_NFC_WILINK=m CONFIG_NFC_MEI_PHY=m # CONFIG_NFC_SIM is not set CONFIG_NFC_PORT100=m +CONFIG_NFC_FDP=m +CONFIG_NFC_FDP_I2C=m CONFIG_NFC_PN544=m CONFIG_NFC_PN544_I2C=m CONFIG_NFC_PN544_MEI=m @@ -1570,6 +1576,7 @@ CONFIG_NFC_MICROREAD_I2C=m CONFIG_NFC_MICROREAD_MEI=m CONFIG_NFC_MRVL=m CONFIG_NFC_MRVL_USB=m +CONFIG_NFC_MRVL_I2C=m CONFIG_NFC_ST21NFCA=m CONFIG_NFC_ST21NFCA_I2C=m # CONFIG_NFC_ST_NCI is not set @@ -1724,7 +1731,6 @@ CONFIG_BLK_DEV_CRYPTOLOOP=m CONFIG_BLK_DEV_DRBD=m # CONFIG_DRBD_FAULT_INJECTION is not set CONFIG_BLK_DEV_NBD=m -CONFIG_BLK_DEV_NVME=m CONFIG_BLK_DEV_SKD=m CONFIG_BLK_DEV_OSD=m CONFIG_BLK_DEV_SX8=m @@ -1742,6 +1748,7 @@ CONFIG_VIRTIO_BLK=m # CONFIG_BLK_DEV_HD is not set CONFIG_BLK_DEV_RBD=m CONFIG_BLK_DEV_RSXX=m +CONFIG_BLK_DEV_NVME=m # # Misc devices @@ -1819,6 +1826,10 @@ CONFIG_INTEL_MIC_BUS=m # # SCIF Driver # + +# +# Intel MIC Coprocessor State Management (COSM) Drivers +# CONFIG_GENWQE=m CONFIG_GENWQE_PLATFORM_ERROR_RECOVERY=1 # CONFIG_ECHO is not set @@ -1906,12 +1917,10 @@ CONFIG_MEGARAID_MM=m CONFIG_MEGARAID_MAILBOX=m CONFIG_MEGARAID_LEGACY=m CONFIG_MEGARAID_SAS=m -CONFIG_SCSI_MPT2SAS=m -CONFIG_SCSI_MPT2SAS_MAX_SGE=128 -CONFIG_SCSI_MPT2SAS_LOGGING=y CONFIG_SCSI_MPT3SAS=m +CONFIG_SCSI_MPT2SAS_MAX_SGE=128 CONFIG_SCSI_MPT3SAS_MAX_SGE=128 -CONFIG_SCSI_MPT3SAS_LOGGING=y +CONFIG_SCSI_MPT2SAS=m CONFIG_SCSI_UFSHCD=m CONFIG_SCSI_UFSHCD_PCI=m CONFIG_SCSI_UFSHCD_PLATFORM=m @@ -2160,7 +2169,6 @@ CONFIG_TUN=m CONFIG_VETH=m CONFIG_VIRTIO_NET=m # CONFIG_NLMON is not set -CONFIG_NET_VRF=m CONFIG_SUNGEM_PHY=m # CONFIG_ARCNET is not set CONFIG_ATM_DRIVERS=y @@ -2224,6 +2232,7 @@ CONFIG_ATL1=m CONFIG_ATL1E=m CONFIG_ATL1C=m CONFIG_ALX=m +# CONFIG_NET_VENDOR_AURORA is not set CONFIG_NET_CADENCE=y CONFIG_MACB=m CONFIG_NET_VENDOR_BROADCOM=y @@ -2238,6 +2247,8 @@ CONFIG_TIGON3=m CONFIG_BNX2X=m CONFIG_BNX2X_SRIOV=y CONFIG_BNX2X_VXLAN=y +CONFIG_BNXT=m +CONFIG_BNXT_SRIOV=y CONFIG_NET_VENDOR_BROCADE=y CONFIG_BNA=m CONFIG_NET_VENDOR_CAVIUM=y @@ -2307,7 +2318,6 @@ CONFIG_I40E_DCB=y CONFIG_I40EVF=m # CONFIG_FM10K is not set # CONFIG_NET_VENDOR_I825XX is not set -CONFIG_IP1000=m CONFIG_JME=m CONFIG_NET_VENDOR_MARVELL=y CONFIG_MVMDIO=m @@ -2356,6 +2366,8 @@ CONFIG_QLCNIC_VXLAN=y CONFIG_QLCNIC_HWMON=y CONFIG_QLGE=m CONFIG_NETXEN_NIC=m +CONFIG_QED=m +CONFIG_QEDE=m CONFIG_NET_VENDOR_QUALCOMM=y CONFIG_NET_VENDOR_REALTEK=y CONFIG_ATP=m @@ -2435,6 +2447,7 @@ CONFIG_CICADA_PHY=m CONFIG_VITESSE_PHY=m CONFIG_TERANETICS_PHY=m CONFIG_SMSC_PHY=m +CONFIG_BCM_NET_PHYLIB=m CONFIG_BROADCOM_PHY=m CONFIG_BCM7XXX_PHY=m CONFIG_BCM87XX_PHY=m @@ -2523,10 +2536,10 @@ CONFIG_AT76C50X_USB=m # CONFIG_PRISM54 is not set # CONFIG_USB_ZD1201 is not set CONFIG_USB_NET_RNDIS_WLAN=m +# CONFIG_ADM8211 is not set CONFIG_RTL8180=m CONFIG_RTL8187=m CONFIG_RTL8187_LEDS=y -# CONFIG_ADM8211 is not set CONFIG_MAC80211_HWSIM=m CONFIG_MWL8K=m CONFIG_ATH_COMMON=m @@ -2580,7 +2593,6 @@ CONFIG_B43_BUSES_BCMA_AND_SSB=y # CONFIG_B43_BUSES_SSB is not set CONFIG_B43_PCI_AUTOSELECT=y CONFIG_B43_PCICORE_AUTOSELECT=y -CONFIG_B43_PCMCIA=y CONFIG_B43_SDIO=y CONFIG_B43_BCMA_PIO=y CONFIG_B43_PIO=y @@ -2696,8 +2708,8 @@ CONFIG_RTL8192DE=m CONFIG_RTL8723AE=m CONFIG_RTL8723BE=m CONFIG_RTL8188EE=m -# CONFIG_RTL8192EE is not set -# CONFIG_RTL8821AE is not set +CONFIG_RTL8192EE=m +CONFIG_RTL8821AE=m CONFIG_RTL8192CU=m CONFIG_RTLWIFI=m CONFIG_RTLWIFI_PCI=m @@ -2706,6 +2718,8 @@ CONFIG_RTLWIFI_USB=m CONFIG_RTL8192C_COMMON=m CONFIG_RTL8723_COMMON=m CONFIG_RTLBTCOEXIST=m +CONFIG_RTL8XXXU=m +# CONFIG_RTL8XXXU_UNTESTED is not set # CONFIG_WL_TI is not set CONFIG_ZD1211RW=m # CONFIG_ZD1211RW_DEBUG is not set @@ -2865,6 +2879,7 @@ CONFIG_MISDN_NETJET=m CONFIG_MISDN_IPAC=m CONFIG_MISDN_ISAR=m CONFIG_ISDN_HDLC=m +# CONFIG_NVM is not set # # Input device support @@ -2989,6 +3004,7 @@ CONFIG_TOUCHSCREEN_CYTTSP4_I2C=m CONFIG_TOUCHSCREEN_DYNAPRO=m CONFIG_TOUCHSCREEN_HAMPSHIRE=m CONFIG_TOUCHSCREEN_EETI=m +CONFIG_TOUCHSCREEN_FT6236=m CONFIG_TOUCHSCREEN_FUJITSU=m # CONFIG_TOUCHSCREEN_GOODIX is not set CONFIG_TOUCHSCREEN_ILI210X=m @@ -3036,12 +3052,15 @@ CONFIG_TOUCHSCREEN_USB_NEXIO=y CONFIG_TOUCHSCREEN_USB_EASYTOUCH=y CONFIG_TOUCHSCREEN_TOUCHIT213=m CONFIG_TOUCHSCREEN_TSC_SERIO=m +CONFIG_TOUCHSCREEN_TSC200X_CORE=m +CONFIG_TOUCHSCREEN_TSC2004=m CONFIG_TOUCHSCREEN_TSC2007=m CONFIG_TOUCHSCREEN_ST1232=m CONFIG_TOUCHSCREEN_SUR40=m CONFIG_TOUCHSCREEN_SX8654=m CONFIG_TOUCHSCREEN_TPS6507X=m CONFIG_TOUCHSCREEN_ZFORCE=m +CONFIG_TOUCHSCREEN_ROHM_BU21023=m CONFIG_INPUT_MISC=y # CONFIG_INPUT_AD714X is not set # CONFIG_INPUT_BMA150 is not set @@ -3094,6 +3113,7 @@ CONFIG_SERIO_ALTERA_PS2=m CONFIG_SERIO_PS2MULT=m CONFIG_SERIO_ARC_PS2=m CONFIG_HYPERV_KEYBOARD=m +CONFIG_USERIO=m CONFIG_GAMEPORT=m CONFIG_GAMEPORT_NS558=m CONFIG_GAMEPORT_L4=m @@ -3148,8 +3168,11 @@ CONFIG_SERIAL_8250_MANY_PORTS=y CONFIG_SERIAL_8250_SHARE_IRQ=y # CONFIG_SERIAL_8250_DETECT_IRQ is not set CONFIG_SERIAL_8250_RSA=y +# CONFIG_SERIAL_8250_FSL is not set CONFIG_SERIAL_8250_DW=m +# CONFIG_SERIAL_8250_RT288X is not set # CONFIG_SERIAL_8250_FINTEK is not set +CONFIG_SERIAL_8250_MID=m # # Non-8250 serial port support @@ -3354,6 +3377,7 @@ CONFIG_PINCTRL_AMD=y CONFIG_PINCTRL_BAYTRAIL=y CONFIG_PINCTRL_CHERRYVIEW=m CONFIG_PINCTRL_INTEL=m +CONFIG_PINCTRL_BROXTON=m CONFIG_PINCTRL_SUNRISEPOINT=m CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y CONFIG_GPIOLIB=y @@ -3367,15 +3391,22 @@ CONFIG_GPIO_MAX730X=m # # Memory mapped GPIO drivers # +CONFIG_GPIO_AMDPT=m CONFIG_GPIO_DWAPB=m -CONFIG_GPIO_F7188X=m CONFIG_GPIO_GENERIC_PLATFORM=y CONFIG_GPIO_ICH=m -CONFIG_GPIO_IT8761E=m CONFIG_GPIO_LYNXPOINT=m +CONFIG_GPIO_VX855=m +# CONFIG_GPIO_ZX is not set + +# +# Port-mapped I/O GPIO drivers +# +CONFIG_GPIO_104_IDIO_16=m +CONFIG_GPIO_F7188X=m +CONFIG_GPIO_IT87=m CONFIG_GPIO_SCH=m CONFIG_GPIO_SCH311X=m -CONFIG_GPIO_VX855=m # # I2C GPIO expanders @@ -3400,6 +3431,11 @@ CONFIG_GPIO_INTEL_MID=y CONFIG_GPIO_ML_IOH=m CONFIG_GPIO_RDC321X=m +# +# SPI or I2C GPIO expanders +# +CONFIG_GPIO_MCP23S08=m + # # USB GPIO expanders # @@ -3443,7 +3479,9 @@ CONFIG_POWER_SUPPLY=y # CONFIG_BATTERY_DS2781 is not set # CONFIG_BATTERY_DS2782 is not set # CONFIG_BATTERY_SBS is not set -# CONFIG_BATTERY_BQ27x00 is not set +CONFIG_BATTERY_BQ27XXX=m +CONFIG_BATTERY_BQ27XXX_I2C=y +CONFIG_BATTERY_BQ27XXX_PLATFORM=y # CONFIG_BATTERY_MAX17040 is not set # CONFIG_BATTERY_MAX17042 is not set # CONFIG_CHARGER_ISP1704 is not set @@ -3527,6 +3565,7 @@ CONFIG_SENSORS_MAX6639=m CONFIG_SENSORS_MAX6642=m CONFIG_SENSORS_MAX6650=m CONFIG_SENSORS_MAX6697=m +CONFIG_SENSORS_MAX31790=m CONFIG_SENSORS_HTU21=m CONFIG_SENSORS_MCP3021=m CONFIG_SENSORS_LM63=m @@ -3614,7 +3653,7 @@ CONFIG_SENSORS_ACPI_POWER=m CONFIG_SENSORS_ATK0110=m CONFIG_THERMAL=y CONFIG_THERMAL_HWMON=y -# CONFIG_THERMAL_WRITABLE_TRIPS is not set +CONFIG_THERMAL_WRITABLE_TRIPS=y CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y # CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set # CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set @@ -3676,6 +3715,7 @@ CONFIG_W83877F_WDT=m CONFIG_W83977F_WDT=m CONFIG_MACHZ_WDT=m # CONFIG_SBC_EPX_C3_WATCHDOG is not set +CONFIG_BCM7038_WDT=m CONFIG_MEN_A21_WDT=m CONFIG_XEN_WDT=m @@ -3704,6 +3744,7 @@ CONFIG_SSB_PCMCIAHOST_POSSIBLE=y CONFIG_SSB_PCMCIAHOST=y CONFIG_SSB_SDIOHOST_POSSIBLE=y CONFIG_SSB_SDIOHOST=y +# CONFIG_SSB_HOST_SOC is not set # CONFIG_SSB_DEBUG is not set CONFIG_SSB_DRIVER_PCICORE_POSSIBLE=y CONFIG_SSB_DRIVER_PCICORE=y @@ -4527,6 +4568,7 @@ CONFIG_FB_VOODOO1=m # CONFIG_FB_SM501 is not set # CONFIG_FB_SMSCUFX is not set # CONFIG_FB_UDL is not set +CONFIG_FB_IBM_GXT4500=m CONFIG_FB_VIRTUAL=m CONFIG_XEN_FBDEV_FRONTEND=y # CONFIG_FB_METRONOME is not set @@ -4585,6 +4627,7 @@ CONFIG_SND_OSSEMUL=y CONFIG_SND_MIXER_OSS=m CONFIG_SND_PCM_OSS=m CONFIG_SND_PCM_OSS_PLUGINS=y +CONFIG_SND_PCM_TIMER=y CONFIG_SND_SEQUENCER_OSS=y CONFIG_SND_HRTIMER=m CONFIG_SND_SEQ_HRTIMER_DEFAULT=y @@ -4743,6 +4786,8 @@ CONFIG_SND_ISIGHT=m CONFIG_SND_SCS1X=m CONFIG_SND_FIREWORKS=m CONFIG_SND_BEBOB=m +CONFIG_SND_FIREWIRE_DIGI00X=m +CONFIG_SND_FIREWIRE_TASCAM=m # CONFIG_SND_PCMCIA is not set # CONFIG_SND_SOC is not set # CONFIG_SOUND_PRIME is not set @@ -4770,6 +4815,7 @@ CONFIG_HID_BELKIN=y CONFIG_HID_BETOP_FF=m CONFIG_HID_CHERRY=y CONFIG_HID_CHICONY=y +CONFIG_HID_CORSAIR=m CONFIG_HID_PRODIKEYS=m CONFIG_HID_CP2112=m CONFIG_HID_CYPRESS=y @@ -4780,6 +4826,7 @@ CONFIG_HID_ELECOM=m CONFIG_HID_ELO=m CONFIG_HID_EZKEY=y CONFIG_HID_GEMBIRD=m +CONFIG_HID_GFRM=m CONFIG_HID_HOLTEK=m CONFIG_HOLTEK_FF=y CONFIG_HID_GT683R=m @@ -4894,7 +4941,6 @@ CONFIG_USB_EHCI_PCI=y # CONFIG_USB_OXU210HP_HCD is not set # CONFIG_USB_ISP116X_HCD is not set CONFIG_USB_ISP1362_HCD=m -CONFIG_USB_FUSBH200_HCD=m CONFIG_USB_FOTG210_HCD=m CONFIG_USB_OHCI_HCD=y CONFIG_USB_OHCI_HCD_PCI=y @@ -5092,7 +5138,6 @@ CONFIG_UWB_WHCI=m CONFIG_UWB_I1480U=m CONFIG_MMC=m # CONFIG_MMC_DEBUG is not set -# CONFIG_MMC_CLKGATE is not set # # MMC/SD/SDIO Card Drivers @@ -5290,6 +5335,7 @@ CONFIG_RTC_DRV_RX8581=m CONFIG_RTC_DRV_RX8025=m CONFIG_RTC_DRV_EM3027=m CONFIG_RTC_DRV_RV3029C2=m +CONFIG_RTC_DRV_RV8803=m # # SPI RTC drivers @@ -5339,13 +5385,12 @@ CONFIG_DMADEVICES=y CONFIG_DMA_ENGINE=y CONFIG_DMA_VIRTUAL_CHANNELS=m CONFIG_DMA_ACPI=y -CONFIG_IDMA64=m +CONFIG_INTEL_IDMA64=m CONFIG_INTEL_IOATDMA=m CONFIG_INTEL_MIC_X100_DMA=m # CONFIG_DW_DMAC is not set # CONFIG_DW_DMAC_PCI is not set CONFIG_HSU_DMA=m -CONFIG_HSU_DMA_PCI=m # # DMA Clients @@ -5377,6 +5422,7 @@ CONFIG_VFIO_PCI=y CONFIG_VFIO_PCI_VGA=y CONFIG_VFIO_PCI_MMAP=y CONFIG_VFIO_PCI_INTX=y +CONFIG_IRQ_BYPASS_MANAGER=y # CONFIG_VIRT_DRIVERS is not set CONFIG_VIRTIO=y @@ -5443,7 +5489,6 @@ CONFIG_RTS5208=m # CONFIG_VT6656 is not set CONFIG_FB_SM750=m # CONFIG_FB_XGI is not set -# CONFIG_FT1000 is not set # # Speakup console speech @@ -5464,7 +5509,7 @@ CONFIG_LIRC_SERIAL=m CONFIG_LIRC_SERIAL_TRANSMITTER=y CONFIG_LIRC_SIR=m CONFIG_LIRC_ZILOG=m -CONFIG_STAGING_RDMA=y +CONFIG_STAGING_RDMA=m CONFIG_INFINIBAND_AMSO1100=m # CONFIG_INFINIBAND_AMSO1100_DEBUG is not set CONFIG_INFINIBAND_HFI1=m @@ -5503,6 +5548,7 @@ CONFIG_DGAP=m # CONFIG_CRYPTO_SKEIN is not set CONFIG_UNISYSSPAR=y # CONFIG_UNISYS_VISORBUS is not set +# CONFIG_WILC1000_DRIVER is not set CONFIG_MOST=m CONFIG_MOSTCORE=m CONFIG_AIM_CDEV=m @@ -5554,6 +5600,7 @@ CONFIG_TOPSTAR_LAPTOP=m CONFIG_ACPI_TOSHIBA=m CONFIG_TOSHIBA_BT_RFKILL=m CONFIG_TOSHIBA_HAPS=m +CONFIG_TOSHIBA_WMI=m CONFIG_ACPI_CMPC=m CONFIG_INTEL_IPS=m # CONFIG_IBM_RTL is not set @@ -5616,6 +5663,7 @@ CONFIG_AMD_IOMMU_STATS=y CONFIG_AMD_IOMMU_V2=m CONFIG_DMAR_TABLE=y CONFIG_INTEL_IOMMU=y +# CONFIG_INTEL_IOMMU_SVM is not set # CONFIG_INTEL_IOMMU_DEFAULT_ON is not set CONFIG_INTEL_IOMMU_FLOPPY_WA=y CONFIG_IRQ_REMAP=y @@ -5664,6 +5712,7 @@ CONFIG_PWM_LP3943=m CONFIG_PWM_LPSS=m # CONFIG_PWM_LPSS_PCI is not set # CONFIG_PWM_LPSS_PLATFORM is not set +CONFIG_PWM_PCA9685=m # CONFIG_IPACK_BUS is not set CONFIG_RESET_CONTROLLER=y # CONFIG_FMC is not set @@ -5697,6 +5746,22 @@ CONFIG_ANDROID=y CONFIG_ANDROID_BINDER_IPC=y # CONFIG_LIBNVDIMM is not set CONFIG_NVMEM=m +CONFIG_STM=m +CONFIG_STM_DUMMY=m +CONFIG_STM_SOURCE_CONSOLE=m +CONFIG_INTEL_TH=m +CONFIG_INTEL_TH_PCI=m +CONFIG_INTEL_TH_GTH=m +CONFIG_INTEL_TH_STH=m +CONFIG_INTEL_TH_MSU=m +CONFIG_INTEL_TH_PTI=m +# CONFIG_INTEL_TH_DEBUG is not set + +# +# FPGA Configuration Support +# +CONFIG_FPGA=m +CONFIG_FPGA_MGR_ZYNQ_FPGA=m # # Firmware Drivers @@ -5727,6 +5792,7 @@ CONFIG_EFI_ESRT=y CONFIG_EFI_VARS_PSTORE=y # CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE is not set CONFIG_EFI_RUNTIME_MAP=y +# CONFIG_EFI_FAKE_MEMMAP is not set CONFIG_EFI_RUNTIME_WRAPPERS=y CONFIG_UEFI_CPER=y @@ -5879,6 +5945,7 @@ CONFIG_UBIFS_FS=m # CONFIG_UBIFS_FS_ADVANCED_COMPR is not set CONFIG_UBIFS_FS_LZO=y CONFIG_UBIFS_FS_ZLIB=y +# CONFIG_UBIFS_ATIME_SUPPORT is not set # CONFIG_LOGFS is not set CONFIG_CRAMFS=m CONFIG_SQUASHFS=m @@ -6083,6 +6150,7 @@ CONFIG_UNUSED_SYMBOLS=y CONFIG_DEBUG_FS=y CONFIG_HEADERS_CHECK=y CONFIG_DEBUG_SECTION_MISMATCH=y +CONFIG_SECTION_MISMATCH_WARN_ONLY=y CONFIG_ARCH_WANT_FRAME_POINTERS=y CONFIG_FRAME_POINTER=y CONFIG_MAGIC_SYSRQ=y @@ -6168,6 +6236,7 @@ CONFIG_FTRACE_MCOUNT_RECORD=y CONFIG_RING_BUFFER_BENCHMARK=m # CONFIG_RING_BUFFER_STARTUP_TEST is not set CONFIG_TRACE_ENUM_MAP_FILE=y +CONFIG_TRACING_EVENTS_GPIO=y # # Runtime Testing @@ -6178,6 +6247,7 @@ CONFIG_ASYNC_RAID6_TEST=m CONFIG_TEST_HEXDUMP=m # CONFIG_TEST_STRING_HELPERS is not set CONFIG_TEST_KSTRTOX=y +CONFIG_TEST_PRINTF=m # CONFIG_TEST_RHASHTABLE is not set # CONFIG_PROVIDE_OHCI1394_DMA_INIT is not set # CONFIG_BUILD_DOCSRC is not set @@ -6196,6 +6266,8 @@ CONFIG_STRICT_DEVMEM=y CONFIG_EARLY_PRINTK=y CONFIG_EARLY_PRINTK_DBGP=y # CONFIG_EARLY_PRINTK_EFI is not set +# CONFIG_X86_PTDUMP_CORE is not set +# CONFIG_EFI_PGT_DUMP is not set CONFIG_DEBUG_SET_MODULE_RONX=y CONFIG_DOUBLEFAULT=y # CONFIG_IOMMU_STRESS is not set @@ -6310,6 +6382,7 @@ CONFIG_CRYPTO_ECB=y CONFIG_CRYPTO_LRW=y CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=y +CONFIG_CRYPTO_KEYWRAP=m # # Hash modes @@ -6448,6 +6521,7 @@ CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT=y CONFIG_KVM_VFIO=y CONFIG_KVM_GENERIC_DIRTYLOG_READ_PROTECT=y CONFIG_KVM_COMPAT=y +CONFIG_HAVE_KVM_IRQ_BYPASS=y CONFIG_VIRTUALIZATION=y CONFIG_KVM=m CONFIG_KVM_INTEL=m diff --git a/kernel/kernel/files/patches/mageia/3rd-3rdparty-merge.patch b/kernel/kernel/files/patches/mageia/3rd-3rdparty-merge.patch index 905c6e6f..fb5e2b7e 100644 --- a/kernel/kernel/files/patches/mageia/3rd-3rdparty-merge.patch +++ b/kernel/kernel/files/patches/mageia/3rd-3rdparty-merge.patch @@ -83,9 +83,9 @@ diff -Nurp linux-4.2.2/Makefile linux-4.2.2-3rd/Makefile net-y := net/ libs-y := lib/ core-y := usr/ -diff -Nurp linux-4.2.2/scripts/kconfig/Makefile linux-4.2.2-3rd/scripts/kconfig/Makefile ---- linux-4.2.2/scripts/kconfig/Makefile 2015-08-30 21:34:09.000000000 +0300 -+++ linux-4.2.2-3rd/scripts/kconfig/Makefile 2015-09-29 23:04:06.298700940 +0300 +diff -Nurp linux-4.4.0/scripts/kconfig/Makefile linux-4.4.0-3rd/scripts/kconfig/Makefile +--- linux-4.4.0/scripts/kconfig/Makefile ++++ linux-4.4.0-3rd/scripts/kconfig/Makefile @@ -18,26 +18,26 @@ endif # We need this, in case the user has it in its environment unexport CONFIG_ @@ -144,9 +144,9 @@ diff -Nurp linux-4.2.2/scripts/kconfig/Makefile linux-4.2.2-3rd/scripts/kconfig/ +defconfig: $(obj)/conf 3rdparty/Makefile ifeq ($(KBUILD_DEFCONFIG),) $< $(silent) --defconfig $(Kconfig) - else -@@ -101,7 +101,7 @@ else - $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$(KBUILD_DEFCONFIG) $(Kconfig) + else ifneq ($(wildcard $(srctree)/arch/$(SRCARCH)/configs/$(KBUILD_DEFCONFIG)),) +@@ -104,26 +104,26 @@ else + $(Q)$(MAKE) -f $(srctree)/Makefile $(KBUILD_DEFCONFIG) endif -%_defconfig: $(obj)/conf @@ -154,7 +154,11 @@ diff -Nurp linux-4.2.2/scripts/kconfig/Makefile linux-4.2.2-3rd/scripts/kconfig/ $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig) configfiles=$(wildcard $(srctree)/kernel/configs/$@ $(srctree)/arch/$(SRCARCH)/configs/$@) -@@ -112,14 +112,14 @@ configfiles=$(wildcard $(srctree)/kernel + +-%.config: $(obj)/conf ++%.config: $(obj)/conf 3rdparty/Makefile + $(if $(call configfiles),, $(error No configuration exists for this target on this architecture)) + $(Q)$(CONFIG_SHELL) $(srctree)/scripts/kconfig/merge_config.sh -m .config $(configfiles) +$(Q)yes "" | $(MAKE) -f $(srctree)/Makefile oldconfig PHONY += kvmconfig @@ -167,12 +171,13 @@ diff -Nurp linux-4.2.2/scripts/kconfig/Makefile linux-4.2.2-3rd/scripts/kconfig/ +xenconfig: xen.config 3rdparty/Makefile @: --PHONY += tinyconfig -+PHONY += tinyconfig 3rdparty/Makefile - tinyconfig: + PHONY += tinyconfig +-tinyconfig: ++tinyconfig: 3rdparty/Makefile $(Q)$(MAKE) -f $(srctree)/Makefile allnoconfig tiny.config -@@ -183,6 +183,9 @@ gconf-objs := gconf.o zconf.tab.o + # Help text used by make help +@@ -186,6 +186,9 @@ gconf-objs := gconf.o zconf.tab.o hostprogs-y := conf nconf mconf kxgettext qconf gconf diff --git a/kernel/kernel/files/patches/mageia/ACPI-video-Add-a-quirk-to-force-native-backlight-on-.patch b/kernel/kernel/files/patches/mageia/ACPI-video-Add-a-quirk-to-force-native-backlight-on-.patch deleted file mode 100644 index 5a30328e..00000000 --- a/kernel/kernel/files/patches/mageia/ACPI-video-Add-a-quirk-to-force-native-backlight-on-.patch +++ /dev/null @@ -1,40 +0,0 @@ -From 584d8d1eb123b8be1274bf69f3ce07cee848d40d Mon Sep 17 00:00:00 2001 -From: Hans de Goede -Date: Wed, 21 Oct 2015 13:45:03 +0200 -Subject: [PATCH] ACPI / video: Add a quirk to force native backlight on Lenovo - IdeaPad S405 - -The Lenovo IdeaPad S405 is a not "Windows8 ready" machine which still has -a broken ACPI video backlight implementation. Add a quirk to force use -of native backlight on this machine. - -Link: https://bugzilla.redhat.com/show_bug.cgi?id=1201530 -Signed-off-by: Hans de Goede -Signed-off-by: Rafael J. Wysocki ---- - drivers/acpi/video_detect.c | 9 +++++++++ - 1 file changed, 9 insertions(+) - -diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c -index 2922f1f..0d3a384 100644 ---- a/drivers/acpi/video_detect.c -+++ b/drivers/acpi/video_detect.c -@@ -244,6 +244,15 @@ static const struct dmi_system_id video_detect_dmi_table[] = { - - /* Non win8 machines which need native backlight nevertheless */ - { -+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1201530 */ -+ .callback = video_detect_force_native, -+ .ident = "Lenovo Ideapad S405", -+ .matches = { -+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), -+ DMI_MATCH(DMI_BOARD_NAME, "Lenovo IdeaPad S405"), -+ }, -+ }, -+ { - /* https://bugzilla.redhat.com/show_bug.cgi?id=1187004 */ - .callback = video_detect_force_native, - .ident = "Lenovo Ideapad Z570", --- -2.3.10 - diff --git a/kernel/kernel/files/patches/mageia/Revert-x86-efi-Request-desired-alignment-via-the-PE-.patch b/kernel/kernel/files/patches/mageia/Revert-x86-efi-Request-desired-alignment-via-the-PE-.patch deleted file mode 100644 index a15e239c..00000000 --- a/kernel/kernel/files/patches/mageia/Revert-x86-efi-Request-desired-alignment-via-the-PE-.patch +++ /dev/null @@ -1,51 +0,0 @@ -From fa5c35011a8d5f3d0c597a6336107eafd1b6046c Mon Sep 17 00:00:00 2001 -From: Matt Fleming -Date: Fri, 7 Aug 2015 09:36:56 +0100 -Subject: [PATCH] Revert "x86/efi: Request desired alignment via the PE/COFF - headers" - -This reverts commit: - - aeffc4928ea2 ("x86/efi: Request desired alignment via the PE/COFF headers") - -Linn reports that Signtool complains that kernels built with -CONFIG_EFI_STUB=y are violating the PE/COFF specification because -the 'SizeOfImage' field is not a multiple of 'SectionAlignment'. - -This violation was introduced as an optimisation to skip having -the kernel relocate itself during boot and instead have the -firmware place it at a correctly aligned address. - -No one else has complained and I'm not aware of any firmware -implementations that refuse to boot with commit aeffc4928ea2, -but it's a real bug, so revert the offending commit. - -Reported-by: Linn Crosetto -Signed-off-by: Matt Fleming -Cc: H. Peter Anvin -Cc: Linus Torvalds -Cc: Michael Brown -Cc: Peter Zijlstra -Cc: Thomas Gleixner -Link: http://lkml.kernel.org/r/1438936621-5215-3-git-send-email-matt@codeblueprint.co.uk -Signed-off-by: Ingo Molnar ---- - arch/x86/boot/header.S | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/arch/x86/boot/header.S b/arch/x86/boot/header.S -index 16ef025..7a6d43a 100644 ---- a/arch/x86/boot/header.S -+++ b/arch/x86/boot/header.S -@@ -154,7 +154,7 @@ extra_header_fields: - #else - .quad 0 # ImageBase - #endif -- .long CONFIG_PHYSICAL_ALIGN # SectionAlignment -+ .long 0x20 # SectionAlignment - .long 0x20 # FileAlignment - .word 0 # MajorOperatingSystemVersion - .word 0 # MinorOperatingSystemVersion --- -2.3.10 - diff --git a/kernel/kernel/files/patches/mageia/ahci-Add-Marvell-88se91a2-device-id.patch b/kernel/kernel/files/patches/mageia/ahci-Add-Marvell-88se91a2-device-id.patch deleted file mode 100644 index 25cd39ad..00000000 --- a/kernel/kernel/files/patches/mageia/ahci-Add-Marvell-88se91a2-device-id.patch +++ /dev/null @@ -1,29 +0,0 @@ -From a40cf3f38881ce8543ceb9667150b4f2ead4c437 Mon Sep 17 00:00:00 2001 -From: Johannes Thumshirn -Date: Tue, 20 Oct 2015 09:31:22 +0200 -Subject: [PATCH] ahci: Add Marvell 88se91a2 device id - -Add device id for Marvell 88se91a2 - -Signed-off-by: Johannes Thumshirn -Signed-off-by: Tejun Heo ---- - drivers/ata/ahci.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c -index a466602..ff343c4 100644 ---- a/drivers/ata/ahci.c -+++ b/drivers/ata/ahci.c -@@ -489,6 +489,8 @@ static const struct pci_device_id ahci_pci_tbl[] = { - .driver_data = board_ahci_yes_fbs }, /* 88se9172 on some Gigabyte */ - { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x91a0), - .driver_data = board_ahci_yes_fbs }, -+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x91a2), /* 88se91a2 */ -+ .driver_data = board_ahci_yes_fbs }, - { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x91a3), - .driver_data = board_ahci_yes_fbs }, - { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x9230), --- -2.3.10 - diff --git a/kernel/kernel/files/patches/mageia/arm-0001-dt-bindings-Add-root-properties-for-Raspberry-Pi-2.patch b/kernel/kernel/files/patches/mageia/arm-0001-dt-bindings-Add-root-properties-for-Raspberry-Pi-2.patch new file mode 100644 index 00000000..4ffdb909 --- /dev/null +++ b/kernel/kernel/files/patches/mageia/arm-0001-dt-bindings-Add-root-properties-for-Raspberry-Pi-2.patch @@ -0,0 +1,29 @@ +From 57e5c6d95b2cde884634586d833b02f54ba1c79d Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Tue, 21 Apr 2015 09:42:21 -0700 +Subject: [PATCH 1/3] dt-bindings: Add root properties for Raspberry Pi 2 + +Signed-off-by: Eric Anholt +Acked-by: Rob Herring +--- + Documentation/devicetree/bindings/arm/bcm/brcm,bcm2835.txt | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/Documentation/devicetree/bindings/arm/bcm/brcm,bcm2835.txt b/Documentation/devicetree/bindings/arm/bcm/brcm,bcm2835.txt +index c78576b..11d3056 100644 +--- a/Documentation/devicetree/bindings/arm/bcm/brcm,bcm2835.txt ++++ b/Documentation/devicetree/bindings/arm/bcm/brcm,bcm2835.txt +@@ -26,6 +26,10 @@ Raspberry Pi Model B+ + Required root node properties: + compatible = "raspberrypi,model-b-plus", "brcm,bcm2835"; + ++Raspberry Pi 2 Model B ++Required root node properties: ++compatible = "raspberrypi,2-model-b", "brcm,bcm2836"; ++ + Raspberry Pi Compute Module + Required root node properties: + compatible = "raspberrypi,compute-module", "brcm,bcm2835"; +-- +2.3.10 + diff --git a/kernel/kernel/files/patches/mageia/arm-0002-ARM-bcm2835-Add-a-compat-string-for-bcm2836-machine-.patch b/kernel/kernel/files/patches/mageia/arm-0002-ARM-bcm2835-Add-a-compat-string-for-bcm2836-machine-.patch new file mode 100644 index 00000000..8504dde0 --- /dev/null +++ b/kernel/kernel/files/patches/mageia/arm-0002-ARM-bcm2835-Add-a-compat-string-for-bcm2836-machine-.patch @@ -0,0 +1,34 @@ +From c1be3c1fc6178ca48750b4e66f1acb7c22b64997 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Wed, 16 Dec 2015 15:55:14 -0800 +Subject: [PATCH 2/3] ARM: bcm2835: Add a compat string for bcm2836 machine + probe + +Supporting the 2836 requires using the new interrupt controller, which +we have support for. + +Signed-off-by: Eric Anholt +--- + arch/arm/mach-bcm/board_bcm2835.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/arch/arm/mach-bcm/board_bcm2835.c b/arch/arm/mach-bcm/board_bcm2835.c +index 0f7b9ea..834d676 100644 +--- a/arch/arm/mach-bcm/board_bcm2835.c ++++ b/arch/arm/mach-bcm/board_bcm2835.c +@@ -36,7 +36,12 @@ static void __init bcm2835_init(void) + } + + static const char * const bcm2835_compat[] = { ++#ifdef CONFIG_ARCH_MULTI_V6 + "brcm,bcm2835", ++#endif ++#ifdef CONFIG_ARCH_MULTI_V7 ++ "brcm,bcm2836", ++#endif + NULL + }; + +-- +2.3.10 + diff --git a/kernel/kernel/files/patches/mageia/arm-0003-ARM-bcm2835-Add-Kconfig-support-for-bcm2836.patch b/kernel/kernel/files/patches/mageia/arm-0003-ARM-bcm2835-Add-Kconfig-support-for-bcm2836.patch new file mode 100644 index 00000000..24b15a66 --- /dev/null +++ b/kernel/kernel/files/patches/mageia/arm-0003-ARM-bcm2835-Add-Kconfig-support-for-bcm2836.patch @@ -0,0 +1,79 @@ +From 5234c34e4cd7695647ccc1cabb50c3e7720dd3fb Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Tue, 24 Feb 2015 15:07:55 +0000 +Subject: [PATCH 3/3] ARM: bcm2835: Add Kconfig support for bcm2836 + +This should be a complete port of bcm2835 functionality to bcm2836 +(Raspberry Pi 2). + +Signed-off-by: Eric Anholt +--- + arch/arm/Kconfig.debug | 10 ++++++++-- + arch/arm/mach-bcm/Kconfig | 9 +++++---- + 2 files changed, 13 insertions(+), 6 deletions(-) + +diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug +index 259c0ca..957b876 100644 +--- a/arch/arm/Kconfig.debug ++++ b/arch/arm/Kconfig.debug +@@ -143,7 +143,12 @@ choice + + config DEBUG_BCM2835 + bool "Kernel low-level debugging on BCM2835 PL011 UART" +- depends on ARCH_BCM2835 ++ depends on ARCH_BCM2835 && ARCH_MULTI_V6 ++ select DEBUG_UART_PL01X ++ ++ config DEBUG_BCM2836 ++ bool "Kernel low-level debugging on BCM2836 PL011 UART" ++ depends on ARCH_BCM2835 && ARCH_MULTI_V7 + select DEBUG_UART_PL01X + + config DEBUG_BCM_5301X +@@ -1402,6 +1407,7 @@ config DEBUG_UART_PHYS + default 0x20064000 if DEBUG_RK29_UART1 || DEBUG_RK3X_UART2 + default 0x20068000 if DEBUG_RK29_UART2 || DEBUG_RK3X_UART3 + default 0x20201000 if DEBUG_BCM2835 ++ default 0x3f201000 if DEBUG_BCM2836 + default 0x3e000000 if DEBUG_BCM_KONA_UART + default 0x4000e400 if DEBUG_LL_UART_EFM32 + default 0x40081000 if DEBUG_LPC18XX_UART0 +@@ -1485,7 +1491,7 @@ config DEBUG_UART_VIRT + default 0xf0000be0 if ARCH_EBSA110 + default 0xf0010000 if DEBUG_ASM9260_UART + default 0xf01fb000 if DEBUG_NOMADIK_UART +- default 0xf0201000 if DEBUG_BCM2835 ++ default 0xf0201000 if DEBUG_BCM2835 || DEBUG_BCM2836 + default 0xf1000300 if DEBUG_BCM_5301X + default 0xf1002000 if DEBUG_MT8127_UART0 + default 0xf1006000 if DEBUG_MT6589_UART0 +diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig +index 8c53c55..3b2acf4 100644 +--- a/arch/arm/mach-bcm/Kconfig ++++ b/arch/arm/mach-bcm/Kconfig +@@ -122,17 +122,18 @@ config ARCH_BCM_MOBILE_SMP + comment "Other Architectures" + + config ARCH_BCM2835 +- bool "Broadcom BCM2835 family" if ARCH_MULTI_V6 ++ bool "Broadcom BCM2835 family" if ARCH_MULTI_V6 || ARCH_MULTI_V7 + select ARCH_REQUIRE_GPIOLIB + select ARM_AMBA +- select ARM_ERRATA_411920 ++ select ARM_ERRATA_411920 if ARCH_MULTI_V6 + select ARM_TIMER_SP804 ++ select HAVE_ARM_ARCH_TIMER if ARCH_MULTI_V7 + select CLKSRC_OF + select PINCTRL + select PINCTRL_BCM2835 + help +- This enables support for the Broadcom BCM2835 SoC. This SoC is +- used in the Raspberry Pi and Roku 2 devices. ++ This enables support for the Broadcom BCM2835 and BCM2836 SoCs. ++ This SoC is used in the Raspberry Pi and Roku 2 devices. + + config ARCH_BCM_63XX + bool "Broadcom BCM63xx DSL SoC" if ARCH_MULTI_V7 +-- +2.3.10 + diff --git a/kernel/kernel/files/patches/mageia/arm-0011-ARM-bcm2835-Define-two-new-packets-from-the-latest-f.patch b/kernel/kernel/files/patches/mageia/arm-0011-ARM-bcm2835-Define-two-new-packets-from-the-latest-f.patch new file mode 100644 index 00000000..b1035384 --- /dev/null +++ b/kernel/kernel/files/patches/mageia/arm-0011-ARM-bcm2835-Define-two-new-packets-from-the-latest-f.patch @@ -0,0 +1,36 @@ +From 60d56333e869be6ad6926cdba3ba974512b2183b Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Tue, 1 Dec 2015 16:49:12 -0800 +Subject: [PATCH 1/3] ARM: bcm2835: Define two new packets from the latest + firmware. + +These packets give us direct access to the firmware's power management +code, as opposed to GET/SET_POWER_STATE packets that only had a couple +of domains implemented. + +Signed-off-by: Eric Anholt +Reviewed-by: Kevin Hilman +--- + include/soc/bcm2835/raspberrypi-firmware.h | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/include/soc/bcm2835/raspberrypi-firmware.h b/include/soc/bcm2835/raspberrypi-firmware.h +index c07d74a..3fb3571 100644 +--- a/include/soc/bcm2835/raspberrypi-firmware.h ++++ b/include/soc/bcm2835/raspberrypi-firmware.h +@@ -72,10 +72,12 @@ enum rpi_firmware_property_tag { + RPI_FIRMWARE_SET_ENABLE_QPU = 0x00030012, + RPI_FIRMWARE_GET_DISPMANX_RESOURCE_MEM_HANDLE = 0x00030014, + RPI_FIRMWARE_GET_EDID_BLOCK = 0x00030020, ++ RPI_FIRMWARE_GET_DOMAIN_STATE = 0x00030030, + RPI_FIRMWARE_SET_CLOCK_STATE = 0x00038001, + RPI_FIRMWARE_SET_CLOCK_RATE = 0x00038002, + RPI_FIRMWARE_SET_VOLTAGE = 0x00038003, + RPI_FIRMWARE_SET_TURBO = 0x00038009, ++ RPI_FIRMWARE_SET_DOMAIN_STATE = 0x00038030, + + /* Dispmanx TAGS */ + RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE = 0x00040001, +-- +2.3.10 + diff --git a/kernel/kernel/files/patches/mageia/arm-0012-dt-bindings-add-rpi-power-domain-driver-bindings.patch b/kernel/kernel/files/patches/mageia/arm-0012-dt-bindings-add-rpi-power-domain-driver-bindings.patch new file mode 100644 index 00000000..eb730a35 --- /dev/null +++ b/kernel/kernel/files/patches/mageia/arm-0012-dt-bindings-add-rpi-power-domain-driver-bindings.patch @@ -0,0 +1,74 @@ +From 4c8b338f9ae38dee9c77bda023babc7f7543f52c Mon Sep 17 00:00:00 2001 +From: Alexander Aring +Date: Wed, 16 Dec 2015 16:26:48 -0800 +Subject: [PATCH 2/3] dt-bindings: add rpi power domain driver bindings + +This patch adds devicetree tree bindings for the Raspberry Pi power +domain driver. + +Signed-off-by: Alexander Aring +Signed-off-by: Eric Anholt +Acked-by: Rob Herring +Reviewed-by: Ulf Hansson +Reviewed-by: Kevin Hilman +--- + .../bindings/soc/bcm/raspberrypi,bcm2835-power.txt | 47 ++++++++++++++++++++++ + 1 file changed, 47 insertions(+) + create mode 100644 Documentation/devicetree/bindings/soc/bcm/raspberrypi,bcm2835-power.txt + +diff --git a/Documentation/devicetree/bindings/soc/bcm/raspberrypi,bcm2835-power.txt b/Documentation/devicetree/bindings/soc/bcm/raspberrypi,bcm2835-power.txt +new file mode 100644 +index 0000000..30942cf +--- /dev/null ++++ b/Documentation/devicetree/bindings/soc/bcm/raspberrypi,bcm2835-power.txt +@@ -0,0 +1,47 @@ ++Raspberry Pi power domain driver ++ ++Required properties: ++ ++- compatible: Should be "raspberrypi,bcm2835-power". ++- firmware: Reference to the RPi firmware device node. ++- #power-domain-cells: Should be <1>, we providing multiple power domains. ++ ++The valid defines for power domain are: ++ ++ RPI_POWER_DOMAIN_I2C0 ++ RPI_POWER_DOMAIN_I2C1 ++ RPI_POWER_DOMAIN_I2C2 ++ RPI_POWER_DOMAIN_VIDEO_SCALER ++ RPI_POWER_DOMAIN_VPU1 ++ RPI_POWER_DOMAIN_HDMI ++ RPI_POWER_DOMAIN_USB ++ RPI_POWER_DOMAIN_VEC ++ RPI_POWER_DOMAIN_JPEG ++ RPI_POWER_DOMAIN_H264 ++ RPI_POWER_DOMAIN_V3D ++ RPI_POWER_DOMAIN_ISP ++ RPI_POWER_DOMAIN_UNICAM0 ++ RPI_POWER_DOMAIN_UNICAM1 ++ RPI_POWER_DOMAIN_CCP2RX ++ RPI_POWER_DOMAIN_CSI2 ++ RPI_POWER_DOMAIN_CPI ++ RPI_POWER_DOMAIN_DSI0 ++ RPI_POWER_DOMAIN_DSI1 ++ RPI_POWER_DOMAIN_TRANSPOSER ++ RPI_POWER_DOMAIN_CCP2TX ++ RPI_POWER_DOMAIN_CDP ++ RPI_POWER_DOMAIN_ARM ++ ++Example: ++ ++power: power { ++ compatible = "raspberrypi,bcm2835-power"; ++ firmware = <&firmware>; ++ #power-domain-cells = <1>; ++}; ++ ++Example for using power domain: ++ ++&usb { ++ power-domains = <&power RPI_POWER_DOMAIN_USB>; ++}; +-- +2.3.10 + diff --git a/kernel/kernel/files/patches/mageia/arm-0013-ARM-bcm2835-add-rpi-power-domain-driver.patch b/kernel/kernel/files/patches/mageia/arm-0013-ARM-bcm2835-add-rpi-power-domain-driver.patch new file mode 100644 index 00000000..5f250719 --- /dev/null +++ b/kernel/kernel/files/patches/mageia/arm-0013-ARM-bcm2835-add-rpi-power-domain-driver.patch @@ -0,0 +1,379 @@ +From a09cd356586d33f64cbe64ee4f5c1a7c4a6abee5 Mon Sep 17 00:00:00 2001 +From: Alexander Aring +Date: Wed, 16 Dec 2015 16:26:47 -0800 +Subject: [PATCH 3/3] ARM: bcm2835: add rpi power domain driver + +This patch adds support for several power domains on Raspberry Pi, +including USB (so it can be enabled even if the bootloader didn't do +it), and graphics. + +This patch is the combined work of Eric Anholt (who wrote USB support +inside of the Raspberry Pi firmware driver, and wrote the non-USB +domain support) and Alexander Aring (who separated the original USB +work out from the firmware driver). + +Signed-off-by: Alexander Aring +Signed-off-by: Eric Anholt +Reviewed-by: Ulf Hansson +Reviewed-by: Kevin Hilman +--- + drivers/soc/Kconfig | 1 + + drivers/soc/Makefile | 1 + + drivers/soc/bcm/Kconfig | 9 + + drivers/soc/bcm/Makefile | 1 + + drivers/soc/bcm/raspberrypi-power.c | 247 ++++++++++++++++++++++++++ + include/dt-bindings/power/raspberrypi-power.h | 41 +++++ + 6 files changed, 300 insertions(+) + create mode 100644 drivers/soc/bcm/Kconfig + create mode 100644 drivers/soc/bcm/Makefile + create mode 100644 drivers/soc/bcm/raspberrypi-power.c + create mode 100644 include/dt-bindings/power/raspberrypi-power.h + +diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig +index 4e853ed..8441426 100644 +--- a/drivers/soc/Kconfig ++++ b/drivers/soc/Kconfig +@@ -1,5 +1,6 @@ + menu "SOC (System On Chip) specific Drivers" + ++source "drivers/soc/bcm/Kconfig" + source "drivers/soc/brcmstb/Kconfig" + source "drivers/soc/mediatek/Kconfig" + source "drivers/soc/qcom/Kconfig" +diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile +index f2ba2e9..f3f955c 100644 +--- a/drivers/soc/Makefile ++++ b/drivers/soc/Makefile +@@ -2,6 +2,7 @@ + # Makefile for the Linux Kernel SOC specific device drivers. + # + ++obj-y += bcm/ + obj-$(CONFIG_SOC_BRCMSTB) += brcmstb/ + obj-$(CONFIG_MACH_DOVE) += dove/ + obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/ +diff --git a/drivers/soc/bcm/Kconfig b/drivers/soc/bcm/Kconfig +new file mode 100644 +index 0000000..5ba1827 +--- /dev/null ++++ b/drivers/soc/bcm/Kconfig +@@ -0,0 +1,9 @@ ++config RASPBERRYPI_POWER ++ bool "Raspberry Pi power domain driver" ++ depends on ARCH_BCM2835 || COMPILE_TEST ++ depends on RASPBERRYPI_FIRMWARE ++ select PM_GENERIC_DOMAINS if PM ++ select PM_GENERIC_DOMAINS_OF if PM ++ help ++ This enables support for the RPi power domains which can be enabled ++ or disabled via the RPi firmware. +diff --git a/drivers/soc/bcm/Makefile b/drivers/soc/bcm/Makefile +new file mode 100644 +index 0000000..63aa3eb +--- /dev/null ++++ b/drivers/soc/bcm/Makefile +@@ -0,0 +1 @@ ++obj-$(CONFIG_RASPBERRYPI_POWER) += raspberrypi-power.o +diff --git a/drivers/soc/bcm/raspberrypi-power.c b/drivers/soc/bcm/raspberrypi-power.c +new file mode 100644 +index 0000000..fe96a8b +--- /dev/null ++++ b/drivers/soc/bcm/raspberrypi-power.c +@@ -0,0 +1,247 @@ ++/* (C) 2015 Pengutronix, Alexander Aring ++ * ++ * 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. ++ * ++ * Authors: ++ * Alexander Aring ++ * Eric Anholt ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * Firmware indices for the old power domains interface. Only a few ++ * of them were actually implemented. ++ */ ++#define RPI_OLD_POWER_DOMAIN_USB 3 ++#define RPI_OLD_POWER_DOMAIN_V3D 10 ++ ++struct rpi_power_domain { ++ u32 domain; ++ bool enabled; ++ bool old_interface; ++ struct generic_pm_domain base; ++ struct rpi_firmware *fw; ++}; ++ ++struct rpi_power_domains { ++ bool has_new_interface; ++ struct genpd_onecell_data xlate; ++ struct rpi_firmware *fw; ++ struct rpi_power_domain domains[RPI_POWER_DOMAIN_COUNT]; ++}; ++ ++/* ++ * Packet definition used by RPI_FIRMWARE_SET_POWER_STATE and ++ * RPI_FIRMWARE_SET_DOMAIN_STATE ++ */ ++struct rpi_power_domain_packet { ++ u32 domain; ++ u32 on; ++} __packet; ++ ++/* ++ * Asks the firmware to enable or disable power on a specific power ++ * domain. ++ */ ++static int rpi_firmware_set_power(struct rpi_power_domain *rpi_domain, bool on) ++{ ++ struct rpi_power_domain_packet packet; ++ ++ packet.domain = rpi_domain->domain; ++ packet.on = on; ++ return rpi_firmware_property(rpi_domain->fw, ++ rpi_domain->old_interface ? ++ RPI_FIRMWARE_SET_POWER_STATE : ++ RPI_FIRMWARE_SET_DOMAIN_STATE, ++ &packet, sizeof(packet)); ++} ++ ++static int rpi_domain_off(struct generic_pm_domain *domain) ++{ ++ struct rpi_power_domain *rpi_domain = ++ container_of(domain, struct rpi_power_domain, base); ++ ++ return rpi_firmware_set_power(rpi_domain, false); ++} ++ ++static int rpi_domain_on(struct generic_pm_domain *domain) ++{ ++ struct rpi_power_domain *rpi_domain = ++ container_of(domain, struct rpi_power_domain, base); ++ ++ return rpi_firmware_set_power(rpi_domain, true); ++} ++ ++static void rpi_common_init_power_domain(struct rpi_power_domains *rpi_domains, ++ int xlate_index, const char *name) ++{ ++ struct rpi_power_domain *dom = &rpi_domains->domains[xlate_index]; ++ ++ dom->fw = rpi_domains->fw; ++ ++ dom->base.name = name; ++ dom->base.power_on = rpi_domain_on; ++ dom->base.power_off = rpi_domain_off; ++ ++ /* ++ * Treat all power domains as off at boot. ++ * ++ * The firmware itself may be keeping some domains on, but ++ * from Linux's perspective all we control is the refcounts ++ * that we give to the firmware, and we can't ask the firmware ++ * to turn off something that we haven't ourselves turned on. ++ */ ++ pm_genpd_init(&dom->base, NULL, true); ++ ++ rpi_domains->xlate.domains[xlate_index] = &dom->base; ++} ++ ++static void rpi_init_power_domain(struct rpi_power_domains *rpi_domains, ++ int xlate_index, const char *name) ++{ ++ struct rpi_power_domain *dom = &rpi_domains->domains[xlate_index]; ++ ++ if (!rpi_domains->has_new_interface) ++ return; ++ ++ /* The DT binding index is the firmware's domain index minus one. */ ++ dom->domain = xlate_index + 1; ++ ++ rpi_common_init_power_domain(rpi_domains, xlate_index, name); ++} ++ ++static void rpi_init_old_power_domain(struct rpi_power_domains *rpi_domains, ++ int xlate_index, int domain, ++ const char *name) ++{ ++ struct rpi_power_domain *dom = &rpi_domains->domains[xlate_index]; ++ ++ dom->old_interface = true; ++ dom->domain = domain; ++ ++ rpi_common_init_power_domain(rpi_domains, xlate_index, name); ++} ++ ++/* ++ * Detects whether the firmware supports the new power domains interface. ++ * ++ * The firmware doesn't actually return an error on an unknown tag, ++ * and just skips over it, so we do the detection by putting an ++ * unexpected value in the return field and checking if it was ++ * unchanged. ++ */ ++static bool ++rpi_has_new_domain_support(struct rpi_power_domains *rpi_domains) ++{ ++ struct rpi_power_domain_packet packet; ++ int ret; ++ ++ packet.domain = RPI_POWER_DOMAIN_ARM; ++ packet.on = ~0; ++ ++ ret = rpi_firmware_property(rpi_domains->fw, ++ RPI_FIRMWARE_GET_DOMAIN_STATE, ++ &packet, sizeof(packet)); ++ ++ return ret == 0 && packet.on != ~0; ++} ++ ++static int rpi_power_probe(struct platform_device *pdev) ++{ ++ struct device_node *fw_np; ++ struct device *dev = &pdev->dev; ++ struct rpi_power_domains *rpi_domains; ++ ++ rpi_domains = devm_kzalloc(dev, sizeof(*rpi_domains), GFP_KERNEL); ++ if (!rpi_domains) ++ return -ENOMEM; ++ ++ rpi_domains->xlate.domains = ++ devm_kzalloc(dev, sizeof(*rpi_domains->xlate.domains) * ++ RPI_POWER_DOMAIN_COUNT, GFP_KERNEL); ++ if (!rpi_domains->xlate.domains) ++ return -ENOMEM; ++ ++ rpi_domains->xlate.num_domains = RPI_POWER_DOMAIN_COUNT; ++ ++ fw_np = of_parse_phandle(pdev->dev.of_node, "firmware", 0); ++ if (!fw_np) { ++ dev_err(&pdev->dev, "no firmware node\n"); ++ return -ENODEV; ++ } ++ ++ rpi_domains->fw = rpi_firmware_get(fw_np); ++ of_node_put(fw_np); ++ if (!rpi_domains->fw) ++ return -EPROBE_DEFER; ++ ++ rpi_domains->has_new_interface = ++ rpi_has_new_domain_support(rpi_domains); ++ ++ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_I2C0, "I2C0"); ++ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_I2C1, "I2C1"); ++ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_I2C2, "I2C2"); ++ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_VIDEO_SCALER, ++ "VIDEO_SCALER"); ++ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_VPU1, "VPU1"); ++ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_HDMI, "HDMI"); ++ ++ /* ++ * Use the old firmware interface for USB power, so that we ++ * can turn it on even if the firmware hasn't been updated. ++ */ ++ rpi_init_old_power_domain(rpi_domains, RPI_POWER_DOMAIN_USB, ++ RPI_OLD_POWER_DOMAIN_USB, "USB"); ++ ++ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_VEC, "VEC"); ++ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_JPEG, "JPEG"); ++ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_H264, "H264"); ++ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_V3D, "V3D"); ++ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_ISP, "ISP"); ++ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_UNICAM0, "UNICAM0"); ++ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_UNICAM1, "UNICAM1"); ++ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CCP2RX, "CCP2RX"); ++ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CSI2, "CSI2"); ++ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CPI, "CPI"); ++ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_DSI0, "DSI0"); ++ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_DSI1, "DSI1"); ++ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_TRANSPOSER, ++ "TRANSPOSER"); ++ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CCP2TX, "CCP2TX"); ++ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CDP, "CDP"); ++ rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_ARM, "ARM"); ++ ++ of_genpd_add_provider_onecell(dev->of_node, &rpi_domains->xlate); ++ ++ platform_set_drvdata(pdev, rpi_domains); ++ ++ return 0; ++} ++ ++static const struct of_device_id rpi_power_of_match[] = { ++ { .compatible = "raspberrypi,bcm2835-power", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, rpi_power_of_match); ++ ++static struct platform_driver rpi_power_driver = { ++ .driver = { ++ .name = "raspberrypi-power", ++ .of_match_table = rpi_power_of_match, ++ }, ++ .probe = rpi_power_probe, ++}; ++builtin_platform_driver(rpi_power_driver); ++ ++MODULE_AUTHOR("Alexander Aring "); ++MODULE_AUTHOR("Eric Anholt "); ++MODULE_DESCRIPTION("Raspberry Pi power domain driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/include/dt-bindings/power/raspberrypi-power.h b/include/dt-bindings/power/raspberrypi-power.h +new file mode 100644 +index 0000000..b3ff8e0 +--- /dev/null ++++ b/include/dt-bindings/power/raspberrypi-power.h +@@ -0,0 +1,41 @@ ++/* ++ * Copyright © 2015 Broadcom ++ * ++ * 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. ++ */ ++ ++#ifndef _DT_BINDINGS_ARM_BCM2835_RPI_POWER_H ++#define _DT_BINDINGS_ARM_BCM2835_RPI_POWER_H ++ ++/* These power domain indices are the firmware interface's indices ++ * minus one. ++ */ ++#define RPI_POWER_DOMAIN_I2C0 0 ++#define RPI_POWER_DOMAIN_I2C1 1 ++#define RPI_POWER_DOMAIN_I2C2 2 ++#define RPI_POWER_DOMAIN_VIDEO_SCALER 3 ++#define RPI_POWER_DOMAIN_VPU1 4 ++#define RPI_POWER_DOMAIN_HDMI 5 ++#define RPI_POWER_DOMAIN_USB 6 ++#define RPI_POWER_DOMAIN_VEC 7 ++#define RPI_POWER_DOMAIN_JPEG 8 ++#define RPI_POWER_DOMAIN_H264 9 ++#define RPI_POWER_DOMAIN_V3D 10 ++#define RPI_POWER_DOMAIN_ISP 11 ++#define RPI_POWER_DOMAIN_UNICAM0 12 ++#define RPI_POWER_DOMAIN_UNICAM1 13 ++#define RPI_POWER_DOMAIN_CCP2RX 14 ++#define RPI_POWER_DOMAIN_CSI2 15 ++#define RPI_POWER_DOMAIN_CPI 16 ++#define RPI_POWER_DOMAIN_DSI0 17 ++#define RPI_POWER_DOMAIN_DSI1 18 ++#define RPI_POWER_DOMAIN_TRANSPOSER 19 ++#define RPI_POWER_DOMAIN_CCP2TX 20 ++#define RPI_POWER_DOMAIN_CDP 21 ++#define RPI_POWER_DOMAIN_ARM 22 ++ ++#define RPI_POWER_DOMAIN_COUNT 23 ++ ++#endif /* _DT_BINDINGS_ARM_BCM2835_RPI_POWER_H */ +-- +2.3.10 + diff --git a/kernel/kernel/files/patches/mageia/arm-0021-ARM-bcm2835-Split-the-DT-for-peripherals-from-the-DT.patch b/kernel/kernel/files/patches/mageia/arm-0021-ARM-bcm2835-Split-the-DT-for-peripherals-from-the-DT.patch new file mode 100644 index 00000000..2111be1e --- /dev/null +++ b/kernel/kernel/files/patches/mageia/arm-0021-ARM-bcm2835-Split-the-DT-for-peripherals-from-the-DT.patch @@ -0,0 +1,443 @@ +From 482626063d446eac1809e025a79ad0a7d45bc22d Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Wed, 16 Dec 2015 13:24:40 -0800 +Subject: [PATCH 1/4] ARM: bcm2835: Split the DT for peripherals from the DT + for the CPU + +The set of peripherals remained constant across bcm2835 (Raspberry Pi +1) and bcm2836 (Raspberry Pi 2), but the CPU was swapped out. Split +the files so that we can include just peripheral setup in 2836. + +Signed-off-by: Eric Anholt +--- + arch/arm/boot/dts/bcm2835.dtsi | 194 +------------------------------------- + arch/arm/boot/dts/bcm283x.dtsi | 205 +++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 206 insertions(+), 193 deletions(-) + create mode 100644 arch/arm/boot/dts/bcm283x.dtsi + +diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi +index aef64de..b83b326 100644 +--- a/arch/arm/boot/dts/bcm2835.dtsi ++++ b/arch/arm/boot/dts/bcm2835.dtsi +@@ -1,206 +1,14 @@ +-#include +-#include +-#include "skeleton.dtsi" ++#include "bcm283x.dtsi" + + / { + compatible = "brcm,bcm2835"; +- model = "BCM2835"; +- interrupt-parent = <&intc>; +- +- chosen { +- bootargs = "earlyprintk console=ttyAMA0"; +- }; + + soc { +- compatible = "simple-bus"; +- #address-cells = <1>; +- #size-cells = <1>; + ranges = <0x7e000000 0x20000000 0x02000000>; + dma-ranges = <0x40000000 0x00000000 0x20000000>; + +- timer@7e003000 { +- compatible = "brcm,bcm2835-system-timer"; +- reg = <0x7e003000 0x1000>; +- interrupts = <1 0>, <1 1>, <1 2>, <1 3>; +- /* This could be a reference to BCM2835_CLOCK_TIMER, +- * but we don't have the driver using the common clock +- * support yet. +- */ +- clock-frequency = <1000000>; +- }; +- +- dma: dma@7e007000 { +- compatible = "brcm,bcm2835-dma"; +- reg = <0x7e007000 0xf00>; +- interrupts = <1 16>, +- <1 17>, +- <1 18>, +- <1 19>, +- <1 20>, +- <1 21>, +- <1 22>, +- <1 23>, +- <1 24>, +- <1 25>, +- <1 26>, +- <1 27>, +- <1 28>; +- +- #dma-cells = <1>; +- brcm,dma-channel-mask = <0x7f35>; +- }; +- +- intc: interrupt-controller@7e00b200 { +- compatible = "brcm,bcm2835-armctrl-ic"; +- reg = <0x7e00b200 0x200>; +- interrupt-controller; +- #interrupt-cells = <2>; +- }; +- +- watchdog@7e100000 { +- compatible = "brcm,bcm2835-pm-wdt"; +- reg = <0x7e100000 0x28>; +- }; +- +- clocks: cprman@7e101000 { +- compatible = "brcm,bcm2835-cprman"; +- #clock-cells = <1>; +- reg = <0x7e101000 0x2000>; +- +- /* CPRMAN derives everything from the platform's +- * oscillator. +- */ +- clocks = <&clk_osc>; +- }; +- +- rng@7e104000 { +- compatible = "brcm,bcm2835-rng"; +- reg = <0x7e104000 0x10>; +- }; +- +- mailbox: mailbox@7e00b800 { +- compatible = "brcm,bcm2835-mbox"; +- reg = <0x7e00b880 0x40>; +- interrupts = <0 1>; +- #mbox-cells = <0>; +- }; +- +- gpio: gpio@7e200000 { +- compatible = "brcm,bcm2835-gpio"; +- reg = <0x7e200000 0xb4>; +- /* +- * The GPIO IP block is designed for 3 banks of GPIOs. +- * Each bank has a GPIO interrupt for itself. +- * There is an overall "any bank" interrupt. +- * In order, these are GIC interrupts 17, 18, 19, 20. +- * Since the BCM2835 only has 2 banks, the 2nd bank +- * interrupt output appears to be mirrored onto the +- * 3rd bank's interrupt signal. +- * So, a bank0 interrupt shows up on 17, 20, and +- * a bank1 interrupt shows up on 18, 19, 20! +- */ +- interrupts = <2 17>, <2 18>, <2 19>, <2 20>; +- +- gpio-controller; +- #gpio-cells = <2>; +- +- interrupt-controller; +- #interrupt-cells = <2>; +- }; +- +- uart0: uart@7e201000 { +- compatible = "brcm,bcm2835-pl011", "arm,pl011", "arm,primecell"; +- reg = <0x7e201000 0x1000>; +- interrupts = <2 25>; +- clocks = <&clocks BCM2835_CLOCK_UART>, +- <&clocks BCM2835_CLOCK_VPU>; +- clock-names = "uartclk", "apb_pclk"; +- arm,primecell-periphid = <0x00241011>; +- }; +- +- i2s: i2s@7e203000 { +- compatible = "brcm,bcm2835-i2s"; +- reg = <0x7e203000 0x20>, +- <0x7e101098 0x02>; +- +- dmas = <&dma 2>, +- <&dma 3>; +- dma-names = "tx", "rx"; +- status = "disabled"; +- }; +- +- spi: spi@7e204000 { +- compatible = "brcm,bcm2835-spi"; +- reg = <0x7e204000 0x1000>; +- interrupts = <2 22>; +- clocks = <&clocks BCM2835_CLOCK_VPU>; +- #address-cells = <1>; +- #size-cells = <0>; +- status = "disabled"; +- }; +- +- i2c0: i2c@7e205000 { +- compatible = "brcm,bcm2835-i2c"; +- reg = <0x7e205000 0x1000>; +- interrupts = <2 21>; +- clocks = <&clocks BCM2835_CLOCK_VPU>; +- #address-cells = <1>; +- #size-cells = <0>; +- status = "disabled"; +- }; +- +- sdhci: sdhci@7e300000 { +- compatible = "brcm,bcm2835-sdhci"; +- reg = <0x7e300000 0x100>; +- interrupts = <2 30>; +- clocks = <&clocks BCM2835_CLOCK_EMMC>; +- status = "disabled"; +- }; +- +- i2c1: i2c@7e804000 { +- compatible = "brcm,bcm2835-i2c"; +- reg = <0x7e804000 0x1000>; +- interrupts = <2 21>; +- clocks = <&clocks BCM2835_CLOCK_VPU>; +- #address-cells = <1>; +- #size-cells = <0>; +- status = "disabled"; +- }; +- +- i2c2: i2c@7e805000 { +- compatible = "brcm,bcm2835-i2c"; +- reg = <0x7e805000 0x1000>; +- interrupts = <2 21>; +- clocks = <&clocks BCM2835_CLOCK_VPU>; +- #address-cells = <1>; +- #size-cells = <0>; +- status = "disabled"; +- }; +- +- usb@7e980000 { +- compatible = "brcm,bcm2835-usb"; +- reg = <0x7e980000 0x10000>; +- interrupts = <1 9>; +- }; +- + arm-pmu { + compatible = "arm,arm1176-pmu"; + }; + }; +- +- clocks { +- compatible = "simple-bus"; +- #address-cells = <1>; +- #size-cells = <0>; +- +- /* The oscillator is the root of the clock tree. */ +- clk_osc: clock@3 { +- compatible = "fixed-clock"; +- reg = <3>; +- #clock-cells = <0>; +- clock-output-names = "osc"; +- clock-frequency = <19200000>; +- }; +- +- }; + }; +diff --git a/arch/arm/boot/dts/bcm283x.dtsi b/arch/arm/boot/dts/bcm283x.dtsi +new file mode 100644 +index 0000000..8a7e727 +--- /dev/null ++++ b/arch/arm/boot/dts/bcm283x.dtsi +@@ -0,0 +1,205 @@ ++#include ++#include ++#include "skeleton.dtsi" ++ ++/* This include file covers the common peripherals and configuration between ++ * bcm2835 and bcm2836 implementations, leaving the CPU configuration to ++ * bcm2835.dtsi and bcm2836.dtsi. ++ */ ++ ++/ { ++ compatible = "brcm,bcm2835"; ++ model = "BCM2835"; ++ interrupt-parent = <&intc>; ++ ++ chosen { ++ bootargs = "earlyprintk console=ttyAMA0"; ++ }; ++ ++ soc { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ timer@7e003000 { ++ compatible = "brcm,bcm2835-system-timer"; ++ reg = <0x7e003000 0x1000>; ++ interrupts = <1 0>, <1 1>, <1 2>, <1 3>; ++ /* This could be a reference to BCM2835_CLOCK_TIMER, ++ * but we don't have the driver using the common clock ++ * support yet. ++ */ ++ clock-frequency = <1000000>; ++ }; ++ ++ dma: dma@7e007000 { ++ compatible = "brcm,bcm2835-dma"; ++ reg = <0x7e007000 0xf00>; ++ interrupts = <1 16>, ++ <1 17>, ++ <1 18>, ++ <1 19>, ++ <1 20>, ++ <1 21>, ++ <1 22>, ++ <1 23>, ++ <1 24>, ++ <1 25>, ++ <1 26>, ++ <1 27>, ++ <1 28>; ++ ++ #dma-cells = <1>; ++ brcm,dma-channel-mask = <0x7f35>; ++ }; ++ ++ intc: interrupt-controller@7e00b200 { ++ compatible = "brcm,bcm2835-armctrl-ic"; ++ reg = <0x7e00b200 0x200>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; ++ ++ watchdog@7e100000 { ++ compatible = "brcm,bcm2835-pm-wdt"; ++ reg = <0x7e100000 0x28>; ++ }; ++ ++ clocks: cprman@7e101000 { ++ compatible = "brcm,bcm2835-cprman"; ++ #clock-cells = <1>; ++ reg = <0x7e101000 0x2000>; ++ ++ /* CPRMAN derives everything from the platform's ++ * oscillator. ++ */ ++ clocks = <&clk_osc>; ++ }; ++ ++ rng@7e104000 { ++ compatible = "brcm,bcm2835-rng"; ++ reg = <0x7e104000 0x10>; ++ }; ++ ++ mailbox: mailbox@7e00b800 { ++ compatible = "brcm,bcm2835-mbox"; ++ reg = <0x7e00b880 0x40>; ++ interrupts = <0 1>; ++ #mbox-cells = <0>; ++ }; ++ ++ gpio: gpio@7e200000 { ++ compatible = "brcm,bcm2835-gpio"; ++ reg = <0x7e200000 0xb4>; ++ /* ++ * The GPIO IP block is designed for 3 banks of GPIOs. ++ * Each bank has a GPIO interrupt for itself. ++ * There is an overall "any bank" interrupt. ++ * In order, these are GIC interrupts 17, 18, 19, 20. ++ * Since the BCM2835 only has 2 banks, the 2nd bank ++ * interrupt output appears to be mirrored onto the ++ * 3rd bank's interrupt signal. ++ * So, a bank0 interrupt shows up on 17, 20, and ++ * a bank1 interrupt shows up on 18, 19, 20! ++ */ ++ interrupts = <2 17>, <2 18>, <2 19>, <2 20>; ++ ++ gpio-controller; ++ #gpio-cells = <2>; ++ ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; ++ ++ uart0: uart@7e201000 { ++ compatible = "brcm,bcm2835-pl011", "arm,pl011", "arm,primecell"; ++ reg = <0x7e201000 0x1000>; ++ interrupts = <2 25>; ++ clocks = <&clocks BCM2835_CLOCK_UART>, ++ <&clocks BCM2835_CLOCK_VPU>; ++ clock-names = "uartclk", "apb_pclk"; ++ arm,primecell-periphid = <0x00241011>; ++ }; ++ ++ i2s: i2s@7e203000 { ++ compatible = "brcm,bcm2835-i2s"; ++ reg = <0x7e203000 0x20>, ++ <0x7e101098 0x02>; ++ ++ dmas = <&dma 2>, ++ <&dma 3>; ++ dma-names = "tx", "rx"; ++ status = "disabled"; ++ }; ++ ++ spi: spi@7e204000 { ++ compatible = "brcm,bcm2835-spi"; ++ reg = <0x7e204000 0x1000>; ++ interrupts = <2 22>; ++ clocks = <&clocks BCM2835_CLOCK_VPU>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ i2c0: i2c@7e205000 { ++ compatible = "brcm,bcm2835-i2c"; ++ reg = <0x7e205000 0x1000>; ++ interrupts = <2 21>; ++ clocks = <&clocks BCM2835_CLOCK_VPU>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ sdhci: sdhci@7e300000 { ++ compatible = "brcm,bcm2835-sdhci"; ++ reg = <0x7e300000 0x100>; ++ interrupts = <2 30>; ++ clocks = <&clocks BCM2835_CLOCK_EMMC>; ++ status = "disabled"; ++ }; ++ ++ i2c1: i2c@7e804000 { ++ compatible = "brcm,bcm2835-i2c"; ++ reg = <0x7e804000 0x1000>; ++ interrupts = <2 21>; ++ clocks = <&clocks BCM2835_CLOCK_VPU>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ i2c2: i2c@7e805000 { ++ compatible = "brcm,bcm2835-i2c"; ++ reg = <0x7e805000 0x1000>; ++ interrupts = <2 21>; ++ clocks = <&clocks BCM2835_CLOCK_VPU>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ usb@7e980000 { ++ compatible = "brcm,bcm2835-usb"; ++ reg = <0x7e980000 0x10000>; ++ interrupts = <1 9>; ++ }; ++ }; ++ ++ clocks { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ /* The oscillator is the root of the clock tree. */ ++ clk_osc: clock@3 { ++ compatible = "fixed-clock"; ++ reg = <3>; ++ #clock-cells = <0>; ++ clock-output-names = "osc"; ++ clock-frequency = <19200000>; ++ }; ++ ++ }; ++}; +-- +2.3.10 + diff --git a/kernel/kernel/files/patches/mageia/arm-0022-ARM-bcm2835-Move-the-CPU-peripheral-include-out-of-c.patch b/kernel/kernel/files/patches/mageia/arm-0022-ARM-bcm2835-Move-the-CPU-peripheral-include-out-of-c.patch new file mode 100644 index 00000000..b2b8ddb0 --- /dev/null +++ b/kernel/kernel/files/patches/mageia/arm-0022-ARM-bcm2835-Move-the-CPU-peripheral-include-out-of-c.patch @@ -0,0 +1,71 @@ +From bafa68c08c33ddde3bc10d2d7e5d3b77b4a6c8ed Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Wed, 16 Dec 2015 15:55:12 -0800 +Subject: [PATCH 2/4] ARM: bcm2835: Move the CPU/peripheral include out of + common RPi DT. + +For Raspberry Pi 2, we want to use the same general pin assignment +bits, but need to use bcm2836.dtsi for the CPU instead. + +Signed-off-by: Eric Anholt +--- + arch/arm/boot/dts/bcm2835-rpi-a-plus.dts | 1 + + arch/arm/boot/dts/bcm2835-rpi-b-plus.dts | 1 + + arch/arm/boot/dts/bcm2835-rpi-b-rev2.dts | 1 + + arch/arm/boot/dts/bcm2835-rpi-b.dts | 1 + + arch/arm/boot/dts/bcm2835-rpi.dtsi | 2 -- + 5 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/boot/dts/bcm2835-rpi-a-plus.dts b/arch/arm/boot/dts/bcm2835-rpi-a-plus.dts +index b2bff43..228614f 100644 +--- a/arch/arm/boot/dts/bcm2835-rpi-a-plus.dts ++++ b/arch/arm/boot/dts/bcm2835-rpi-a-plus.dts +@@ -1,4 +1,5 @@ + /dts-v1/; ++#include "bcm2835.dtsi" + #include "bcm2835-rpi.dtsi" + + / { +diff --git a/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts b/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts +index 668442b..ef54050 100644 +--- a/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts ++++ b/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts +@@ -1,4 +1,5 @@ + /dts-v1/; ++#include "bcm2835.dtsi" + #include "bcm2835-rpi.dtsi" + + / { +diff --git a/arch/arm/boot/dts/bcm2835-rpi-b-rev2.dts b/arch/arm/boot/dts/bcm2835-rpi-b-rev2.dts +index eab8b591..86f1f2f 100644 +--- a/arch/arm/boot/dts/bcm2835-rpi-b-rev2.dts ++++ b/arch/arm/boot/dts/bcm2835-rpi-b-rev2.dts +@@ -1,4 +1,5 @@ + /dts-v1/; ++#include "bcm2835.dtsi" + #include "bcm2835-rpi.dtsi" + + / { +diff --git a/arch/arm/boot/dts/bcm2835-rpi-b.dts b/arch/arm/boot/dts/bcm2835-rpi-b.dts +index ff6b2d1..4859e9d 100644 +--- a/arch/arm/boot/dts/bcm2835-rpi-b.dts ++++ b/arch/arm/boot/dts/bcm2835-rpi-b.dts +@@ -1,4 +1,5 @@ + /dts-v1/; ++#include "bcm2835.dtsi" + #include "bcm2835-rpi.dtsi" + + / { +diff --git a/arch/arm/boot/dts/bcm2835-rpi.dtsi b/arch/arm/boot/dts/bcm2835-rpi.dtsi +index 3572f03..3afb9fe 100644 +--- a/arch/arm/boot/dts/bcm2835-rpi.dtsi ++++ b/arch/arm/boot/dts/bcm2835-rpi.dtsi +@@ -1,5 +1,3 @@ +-#include "bcm2835.dtsi" +- + / { + memory { + reg = <0 0x10000000>; +-- +2.3.10 + diff --git a/kernel/kernel/files/patches/mageia/arm-0023-ARM-bcm2835-Add-devicetree-for-bcm2836-and-Raspberry.patch b/kernel/kernel/files/patches/mageia/arm-0023-ARM-bcm2835-Add-devicetree-for-bcm2836-and-Raspberry.patch new file mode 100644 index 00000000..33c91d5d --- /dev/null +++ b/kernel/kernel/files/patches/mageia/arm-0023-ARM-bcm2835-Add-devicetree-for-bcm2836-and-Raspberry.patch @@ -0,0 +1,160 @@ +From c33319cd945001741d1b381655c8b7310d756163 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Thu, 16 Apr 2015 15:26:45 -0700 +Subject: [PATCH 3/4] ARM: bcm2835: Add devicetree for bcm2836 and Raspberry Pi + 2 B + +The Pi 2 B ends up like a Pi 1 B+, with the same peripherals and +pinout, but the CPU and memory layout changed to use the 2836. + +Signed-off-by: Eric Anholt +--- + arch/arm/boot/dts/Makefile | 3 +- + arch/arm/boot/dts/bcm2836-rpi-2-b.dts | 35 ++++++++++++++++ + arch/arm/boot/dts/bcm2836.dtsi | 78 +++++++++++++++++++++++++++++++++++ + 3 files changed, 115 insertions(+), 1 deletion(-) + create mode 100644 arch/arm/boot/dts/bcm2836-rpi-2-b.dts + create mode 100644 arch/arm/boot/dts/bcm2836.dtsi + +diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile +index 30bbc37..54e8f6b 100644 +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -60,7 +60,8 @@ dtb-$(CONFIG_ARCH_BCM2835) += \ + bcm2835-rpi-b.dtb \ + bcm2835-rpi-b-rev2.dtb \ + bcm2835-rpi-b-plus.dtb \ +- bcm2835-rpi-a-plus.dtb ++ bcm2835-rpi-a-plus.dtb \ ++ bcm2836-rpi-2-b.dtb + dtb-$(CONFIG_ARCH_BCM_5301X) += \ + bcm4708-asus-rt-ac56u.dtb \ + bcm4708-asus-rt-ac68u.dtb \ +diff --git a/arch/arm/boot/dts/bcm2836-rpi-2-b.dts b/arch/arm/boot/dts/bcm2836-rpi-2-b.dts +new file mode 100644 +index 0000000..ff94666 +--- /dev/null ++++ b/arch/arm/boot/dts/bcm2836-rpi-2-b.dts +@@ -0,0 +1,35 @@ ++/dts-v1/; ++#include "bcm2836.dtsi" ++#include "bcm2835-rpi.dtsi" ++ ++/ { ++ compatible = "raspberrypi,2-model-b", "brcm,bcm2836"; ++ model = "Raspberry Pi 2 Model B"; ++ ++ memory { ++ reg = <0 0x40000000>; ++ }; ++ ++ leds { ++ act { ++ gpios = <&gpio 47 0>; ++ }; ++ ++ pwr { ++ label = "PWR"; ++ gpios = <&gpio 35 0>; ++ default-state = "keep"; ++ linux,default-trigger = "default-on"; ++ }; ++ }; ++}; ++ ++&gpio { ++ pinctrl-0 = <&gpioout &alt0 &i2s_alt0 &alt3>; ++ ++ /* I2S interface */ ++ i2s_alt0: i2s_alt0 { ++ brcm,pins = <18 19 20 21>; ++ brcm,function = ; ++ }; ++}; +diff --git a/arch/arm/boot/dts/bcm2836.dtsi b/arch/arm/boot/dts/bcm2836.dtsi +new file mode 100644 +index 0000000..9d0651d +--- /dev/null ++++ b/arch/arm/boot/dts/bcm2836.dtsi +@@ -0,0 +1,78 @@ ++#include "bcm283x.dtsi" ++ ++/ { ++ compatible = "brcm,bcm2836"; ++ ++ soc { ++ ranges = <0x7e000000 0x3f000000 0x1000000>, ++ <0x40000000 0x40000000 0x00001000>; ++ dma-ranges = <0xc0000000 0x00000000 0x3f000000>; ++ ++ local_intc: local_intc { ++ compatible = "brcm,bcm2836-l1-intc"; ++ reg = <0x40000000 0x100>; ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ interrupt-parent = <&local_intc>; ++ }; ++ ++ arm-pmu { ++ compatible = "arm,cortex-a7-pmu"; ++ interrupt-parent = <&local_intc>; ++ interrupts = <9>; ++ }; ++ }; ++ ++ timer { ++ compatible = "arm,armv7-timer"; ++ interrupt-parent = <&local_intc>; ++ interrupts = <0>, // PHYS_SECURE_PPI ++ <1>, // PHYS_NONSECURE_PPI ++ <3>, // VIRT_PPI ++ <2>; // HYP_PPI ++ always-on; ++ }; ++ ++ cpus: cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ v7_cpu0: cpu@0 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a7"; ++ reg = <0xf00>; ++ clock-frequency = <800000000>; ++ }; ++ ++ v7_cpu1: cpu@1 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a7"; ++ reg = <0xf01>; ++ clock-frequency = <800000000>; ++ }; ++ ++ v7_cpu2: cpu@2 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a7"; ++ reg = <0xf02>; ++ clock-frequency = <800000000>; ++ }; ++ ++ v7_cpu3: cpu@3 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a7"; ++ reg = <0xf03>; ++ clock-frequency = <800000000>; ++ }; ++ }; ++}; ++ ++/* Make the BCM2835-style global interrupt controller be a child of the ++ * CPU-local interrupt controller. ++ */ ++&intc { ++ compatible = "brcm,bcm2836-armctrl-ic"; ++ reg = <0x7e00b200 0x200>; ++ interrupt-parent = <&local_intc>; ++ interrupts = <8>; ++}; +-- +2.3.10 + diff --git a/kernel/kernel/files/patches/mageia/arm-0024-ARM-bcm2835-Add-the-auxiliary-clocks-to-the-device-t.patch b/kernel/kernel/files/patches/mageia/arm-0024-ARM-bcm2835-Add-the-auxiliary-clocks-to-the-device-t.patch new file mode 100644 index 00000000..ba32aae4 --- /dev/null +++ b/kernel/kernel/files/patches/mageia/arm-0024-ARM-bcm2835-Add-the-auxiliary-clocks-to-the-device-t.patch @@ -0,0 +1,34 @@ +From 53b6084357a44d7c34044504e1bf149d9156934f Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Tue, 15 Dec 2015 15:35:59 -0800 +Subject: [PATCH 4/4] ARM: bcm2835: Add the auxiliary clocks to the device + tree. + +These will be used for enabling UART1, SPI1, and SPI2. + +Signed-off-by: Eric Anholt +--- + arch/arm/boot/dts/bcm283x.dtsi | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/arch/arm/boot/dts/bcm283x.dtsi b/arch/arm/boot/dts/bcm283x.dtsi +index 8a7e727..971e741 100644 +--- a/arch/arm/boot/dts/bcm283x.dtsi ++++ b/arch/arm/boot/dts/bcm283x.dtsi +@@ -152,6 +152,13 @@ + status = "disabled"; + }; + ++ aux: aux@0x7e215000 { ++ compatible = "brcm,bcm2835-aux"; ++ #clock-cells = <1>; ++ reg = <0x7e215000 0x8>; ++ clocks = <&clocks BCM2835_CLOCK_VPU>; ++ }; ++ + sdhci: sdhci@7e300000 { + compatible = "brcm,bcm2835-sdhci"; + reg = <0x7e300000 0x100>; +-- +2.3.10 + diff --git a/kernel/kernel/files/patches/mageia/arm-0031-ARM-bcm2835-enable-all-bcm2835-relevant-in-defconfig.patch b/kernel/kernel/files/patches/mageia/arm-0031-ARM-bcm2835-enable-all-bcm2835-relevant-in-defconfig.patch new file mode 100644 index 00000000..00e62d25 --- /dev/null +++ b/kernel/kernel/files/patches/mageia/arm-0031-ARM-bcm2835-enable-all-bcm2835-relevant-in-defconfig.patch @@ -0,0 +1,132 @@ +From 8c821c590f791ade672609c2f59b1ce0af750653 Mon Sep 17 00:00:00 2001 +From: Stefan Wahren +Date: Tue, 27 Oct 2015 19:08:58 +0100 +Subject: [PATCH 1/2] ARM: bcm2835: enable all bcm2835-relevant in defconfig + +Rebuild bcm2835_defconfig using "make bcm2835_defconfig; +make savedefconfig", and enable manually the following features: + +* all bcm2835-relevant drivers (MBOX, WDT, DMA, PWM, SND) +* enable regular stackprotector because CONFIG_CC_STACKPROTECTOR + disappear +* enable some new dependencies in order to keep LED heartbeat + ( CONFIG_NEW_LEDS, CONFIG_LEDS_CLASS, CONFIG_LEDS_TRIGGERS ) + +The following options were removed, because they are enabled implicit: + +CONFIG_RD_BZIP2, CONFIG_RD_LZMA, CONFIG_RD_XZ, CONFIG_RD_LZO, +CONFIG_USB_DWC2_HOST, CONFIG_EXT4_FS, CONFIG_EXT4_FS_POSIX_ACL + +These options became obsolete: + +CONFIG_RESOURCE_COUNTERS, CONFIG_SCSI_MULTI_LUN + +Signed-off-by: Stefan Wahren +Signed-off-by: Eric Anholt +Acked-by: Stephen Warren +--- + arch/arm/configs/bcm2835_defconfig | 28 +++++++++++++++++----------- + 1 file changed, 17 insertions(+), 11 deletions(-) + +diff --git a/arch/arm/configs/bcm2835_defconfig b/arch/arm/configs/bcm2835_defconfig +index 31cb073..0fda844 100644 +--- a/arch/arm/configs/bcm2835_defconfig ++++ b/arch/arm/configs/bcm2835_defconfig +@@ -10,7 +10,6 @@ CONFIG_CGROUP_FREEZER=y + CONFIG_CGROUP_DEVICE=y + CONFIG_CPUSETS=y + CONFIG_CGROUP_CPUACCT=y +-CONFIG_RESOURCE_COUNTERS=y + CONFIG_CGROUP_PERF=y + CONFIG_CFS_BANDWIDTH=y + CONFIG_RT_GROUP_SCHED=y +@@ -18,10 +17,6 @@ CONFIG_NAMESPACES=y + CONFIG_SCHED_AUTOGROUP=y + CONFIG_RELAY=y + CONFIG_BLK_DEV_INITRD=y +-CONFIG_RD_BZIP2=y +-CONFIG_RD_LZMA=y +-CONFIG_RD_XZ=y +-CONFIG_RD_LZO=y + CONFIG_CC_OPTIMIZE_FOR_SIZE=y + CONFIG_KALLSYMS_ALL=y + CONFIG_EMBEDDED=y +@@ -29,6 +24,7 @@ CONFIG_EMBEDDED=y + CONFIG_PROFILING=y + CONFIG_OPROFILE=y + CONFIG_JUMP_LABEL=y ++CONFIG_CC_STACKPROTECTOR_REGULAR=y + CONFIG_ARCH_MULTI_V6=y + # CONFIG_ARCH_MULTI_V7 is not set + CONFIG_ARCH_BCM=y +@@ -38,7 +34,6 @@ CONFIG_AEABI=y + CONFIG_KSM=y + CONFIG_CLEANCACHE=y + CONFIG_SECCOMP=y +-CONFIG_CC_STACKPROTECTOR=y + CONFIG_KEXEC=y + CONFIG_CRASH_DUMP=y + CONFIG_VFP=y +@@ -57,7 +52,6 @@ CONFIG_DEVTMPFS_MOUNT=y + # CONFIG_STANDALONE is not set + CONFIG_SCSI=y + CONFIG_BLK_DEV_SD=y +-CONFIG_SCSI_MULTI_LUN=y + CONFIG_SCSI_CONSTANTS=y + CONFIG_SCSI_SCAN_ASYNC=y + CONFIG_NETDEVICES=y +@@ -77,17 +71,27 @@ CONFIG_SPI=y + CONFIG_SPI_BCM2835=y + CONFIG_GPIO_SYSFS=y + # CONFIG_HWMON is not set ++CONFIG_WATCHDOG=y ++CONFIG_BCM2835_WDT=y + CONFIG_FB=y + CONFIG_FB_SIMPLE=y + CONFIG_FRAMEBUFFER_CONSOLE=y + CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y ++CONFIG_SOUND=y ++CONFIG_SND=y ++CONFIG_SND_SOC=y ++CONFIG_SND_BCM2835_SOC_I2S=y + CONFIG_USB=y + CONFIG_USB_STORAGE=y ++CONFIG_USB_DWC2=y + CONFIG_MMC=y + CONFIG_MMC_SDHCI=y + CONFIG_MMC_SDHCI_PLTFM=y + CONFIG_MMC_SDHCI_BCM2835=y ++CONFIG_NEW_LEDS=y ++CONFIG_LEDS_CLASS=y + CONFIG_LEDS_GPIO=y ++CONFIG_LEDS_TRIGGERS=y + CONFIG_LEDS_TRIGGER_TIMER=y + CONFIG_LEDS_TRIGGER_ONESHOT=y + CONFIG_LEDS_TRIGGER_HEARTBEAT=y +@@ -96,17 +100,19 @@ CONFIG_LEDS_TRIGGER_GPIO=y + CONFIG_LEDS_TRIGGER_DEFAULT_ON=y + CONFIG_LEDS_TRIGGER_TRANSIENT=y + CONFIG_LEDS_TRIGGER_CAMERA=y ++CONFIG_DMADEVICES=y ++CONFIG_DMA_BCM2835=y + CONFIG_STAGING=y +-CONFIG_USB_DWC2=y +-CONFIG_USB_DWC2_HOST=y ++CONFIG_MAILBOX=y ++CONFIG_BCM2835_MBOX=y + # CONFIG_IOMMU_SUPPORT is not set ++CONFIG_PWM=y ++CONFIG_PWM_BCM2835=y + CONFIG_EXT2_FS=y + CONFIG_EXT2_FS_XATTR=y + CONFIG_EXT2_FS_POSIX_ACL=y + CONFIG_EXT3_FS=y + CONFIG_EXT3_FS_POSIX_ACL=y +-CONFIG_EXT4_FS=y +-CONFIG_EXT4_FS_POSIX_ACL=y + CONFIG_FANOTIFY=y + CONFIG_MSDOS_FS=y + CONFIG_VFAT_FS=y +-- +2.3.10 + diff --git a/kernel/kernel/files/patches/mageia/arm-0032-ARM-bcm2835-enable-auxiliary-spi-driver-in-defconfig.patch b/kernel/kernel/files/patches/mageia/arm-0032-ARM-bcm2835-enable-auxiliary-spi-driver-in-defconfig.patch new file mode 100644 index 00000000..9a9c8678 --- /dev/null +++ b/kernel/kernel/files/patches/mageia/arm-0032-ARM-bcm2835-enable-auxiliary-spi-driver-in-defconfig.patch @@ -0,0 +1,28 @@ +From b63074fccbeb81e46b2553a200de54ff3bfadd10 Mon Sep 17 00:00:00 2001 +From: Martin Sperl +Date: Fri, 11 Sep 2015 11:22:06 +0000 +Subject: [PATCH 2/2] ARM: bcm2835: enable auxiliary spi driver in defconfig + +add the auxiliary spi driver to the default config + +Signed-off-by: Martin Sperl +Signed-off-by: Eric Anholt +--- + arch/arm/configs/bcm2835_defconfig | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm/configs/bcm2835_defconfig b/arch/arm/configs/bcm2835_defconfig +index 0fda844..72def20 100644 +--- a/arch/arm/configs/bcm2835_defconfig ++++ b/arch/arm/configs/bcm2835_defconfig +@@ -69,6 +69,7 @@ CONFIG_I2C_CHARDEV=y + CONFIG_I2C_BCM2835=y + CONFIG_SPI=y + CONFIG_SPI_BCM2835=y ++CONFIG_SPI_BCM2835AUX=y + CONFIG_GPIO_SYSFS=y + # CONFIG_HWMON is not set + CONFIG_WATCHDOG=y +-- +2.3.10 + diff --git a/kernel/kernel/files/patches/mageia/ata-prefer-ata-drivers-over-ide-drivers-when-both-are-built.patch b/kernel/kernel/files/patches/mageia/ata-prefer-ata-drivers-over-ide-drivers-when-both-are-built.patch index 2990886e..8ba0f029 100644 --- a/kernel/kernel/files/patches/mageia/ata-prefer-ata-drivers-over-ide-drivers-when-both-are-built.patch +++ b/kernel/kernel/files/patches/mageia/ata-prefer-ata-drivers-over-ide-drivers-when-both-are-built.patch @@ -20,12 +20,13 @@ diff --git a/drivers/Makefile b/drivers/Makefile index 932e8bf..e8df3d0 100644 --- a/drivers/Makefile +++ b/drivers/Makefile -@@ -64,9 +64,9 @@ obj-y += base/ block/ misc/ mfd/ nfc/ +@@ -69,10 +69,10 @@ obj-$(CONFIG_LIBNVDIMM) += nvdimm/ obj-$(CONFIG_DMA_SHARED_BUFFER) += dma-buf/ obj-$(CONFIG_NUBUS) += nubus/ obj-y += macintosh/ -obj-$(CONFIG_IDE) += ide/ obj-$(CONFIG_SCSI) += scsi/ + obj-y += nvme/ obj-$(CONFIG_ATA) += ata/ +obj-$(CONFIG_IDE) += ide/ obj-$(CONFIG_TARGET_CORE) += target/ diff --git a/kernel/kernel/files/patches/mageia/certs-add-.gitignore-to-stop-git-nagging-about-x509_.patch b/kernel/kernel/files/patches/mageia/certs-add-.gitignore-to-stop-git-nagging-about-x509_.patch deleted file mode 100644 index 8070e3b7..00000000 --- a/kernel/kernel/files/patches/mageia/certs-add-.gitignore-to-stop-git-nagging-about-x509_.patch +++ /dev/null @@ -1,49 +0,0 @@ -From 48dbc164b40dd9195dea8cd966e394819e420b64 Mon Sep 17 00:00:00 2001 -From: Paul Gortmaker -Date: Wed, 21 Oct 2015 14:04:47 +0100 -Subject: [PATCH] certs: add .gitignore to stop git nagging about - x509_certificate_list - -Currently we see this in "git status" if we build in the source dir: - -Untracked files: - (use "git add ..." to include in what will be committed) - - certs/x509_certificate_list - -It looks like it used to live in kernel/ so we squash that .gitignore -entry at the same time. I didn't bother to dig through git history to -see when it moved, since it is just a minor annoyance at most. - -Cc: David Woodhouse -Cc: keyrings@linux-nfs.org -Signed-off-by: Paul Gortmaker -Signed-off-by: David Howells ---- - certs/.gitignore | 4 ++++ - kernel/.gitignore | 1 - - 2 files changed, 4 insertions(+), 1 deletion(-) - create mode 100644 certs/.gitignore - -diff --git a/certs/.gitignore b/certs/.gitignore -new file mode 100644 -index 0000000..f51aea4 ---- /dev/null -+++ b/certs/.gitignore -@@ -0,0 +1,4 @@ -+# -+# Generated files -+# -+x509_certificate_list -diff --git a/kernel/.gitignore b/kernel/.gitignore -index 790d83c..b3097bd 100644 ---- a/kernel/.gitignore -+++ b/kernel/.gitignore -@@ -5,4 +5,3 @@ config_data.h - config_data.gz - timeconst.h - hz.bc --x509_certificate_list --- -2.6.4 - diff --git a/kernel/kernel/files/patches/mageia/fs-aufs-4.3-modular.patch b/kernel/kernel/files/patches/mageia/fs-aufs-4.4-modular.patch similarity index 71% rename from kernel/kernel/files/patches/mageia/fs-aufs-4.3-modular.patch rename to kernel/kernel/files/patches/mageia/fs-aufs-4.4-modular.patch index 9f1e899d..44d224bc 100644 --- a/kernel/kernel/files/patches/mageia/fs-aufs-4.3-modular.patch +++ b/kernel/kernel/files/patches/mageia/fs-aufs-4.4-modular.patch @@ -14,9 +14,9 @@ security/security.c | 10 ++++++++++ 13 files changed, 35 insertions(+), 1 deletion(-) -diff -Nurp linux-4.3.aufs/fs/aufs/Kconfig linux-4.3.aufs.mod/fs/aufs/Kconfig ---- linux-4.3.aufs/fs/aufs/Kconfig 2015-11-19 22:31:36.517207700 +0200 -+++ linux-4.3.aufs.mod/fs/aufs/Kconfig 2015-11-19 22:32:42.497511385 +0200 +diff -Nurp linux-4.4-rc6.aufs/fs/aufs/Kconfig linux-4.4-rc6.aufs.mod/fs/aufs/Kconfig +--- linux-4.4-rc6.aufs/fs/aufs/Kconfig 2015-12-21 19:54:42.973923445 +0200 ++++ linux-4.4-rc6.aufs.mod/fs/aufs/Kconfig 2015-12-21 19:55:29.522275768 +0200 @@ -1,5 +1,5 @@ config AUFS_FS - bool "Aufs (Advanced multi layered unification filesystem) support" @@ -24,9 +24,9 @@ diff -Nurp linux-4.3.aufs/fs/aufs/Kconfig linux-4.3.aufs.mod/fs/aufs/Kconfig help Aufs is a stackable unification filesystem such as Unionfs, which unifies several directories and provides a merged single -diff -Nurp linux-4.3.aufs/fs/dcache.c linux-4.3.aufs.mod/fs/dcache.c ---- linux-4.3.aufs/fs/dcache.c 2015-11-19 22:31:36.529207756 +0200 -+++ linux-4.3.aufs.mod/fs/dcache.c 2015-11-19 22:32:42.497511385 +0200 +diff -Nurp linux-4.4-rc6.aufs/fs/dcache.c linux-4.4-rc6.aufs.mod/fs/dcache.c +--- linux-4.4-rc6.aufs/fs/dcache.c 2015-12-21 19:54:42.986923543 +0200 ++++ linux-4.4-rc6.aufs.mod/fs/dcache.c 2015-12-21 19:55:29.523275775 +0200 @@ -1272,6 +1272,7 @@ rename_retry: seq = 1; goto again; @@ -35,9 +35,9 @@ diff -Nurp linux-4.3.aufs/fs/dcache.c linux-4.3.aufs.mod/fs/dcache.c /* * Search for at least 1 mount point in the dentry's subdirs. -diff -Nurp linux-4.3.aufs/fs/file_table.c linux-4.3.aufs.mod/fs/file_table.c ---- linux-4.3.aufs/fs/file_table.c 2015-11-02 02:05:25.000000000 +0200 -+++ linux-4.3.aufs.mod/fs/file_table.c 2015-11-19 22:32:42.497511385 +0200 +diff -Nurp linux-4.4-rc6.aufs/fs/file_table.c linux-4.4-rc6.aufs.mod/fs/file_table.c +--- linux-4.4-rc6.aufs/fs/file_table.c 2015-11-02 02:05:25.000000000 +0200 ++++ linux-4.4-rc6.aufs.mod/fs/file_table.c 2015-12-21 19:55:29.523275775 +0200 @@ -147,6 +147,7 @@ over: } return ERR_PTR(-ENFILE); @@ -54,9 +54,9 @@ diff -Nurp linux-4.3.aufs/fs/file_table.c linux-4.3.aufs.mod/fs/file_table.c void __init files_init(void) { -diff -Nurp linux-4.3.aufs/fs/inode.c linux-4.3.aufs.mod/fs/inode.c ---- linux-4.3.aufs/fs/inode.c 2015-11-02 02:05:25.000000000 +0200 -+++ linux-4.3.aufs.mod/fs/inode.c 2015-11-19 22:32:42.498511390 +0200 +diff -Nurp linux-4.4-rc6.aufs/fs/inode.c linux-4.4-rc6.aufs.mod/fs/inode.c +--- linux-4.4-rc6.aufs/fs/inode.c 2015-12-21 13:06:55.265034468 +0200 ++++ linux-4.4-rc6.aufs.mod/fs/inode.c 2015-12-21 19:55:29.524275783 +0200 @@ -1593,6 +1593,7 @@ static int update_time(struct inode *ino return update_time(inode, time, flags); @@ -65,9 +65,9 @@ diff -Nurp linux-4.3.aufs/fs/inode.c linux-4.3.aufs.mod/fs/inode.c /** * touch_atime - update the access time -diff -Nurp linux-4.3.aufs/fs/namespace.c linux-4.3.aufs.mod/fs/namespace.c ---- linux-4.3.aufs/fs/namespace.c 2015-11-02 02:05:25.000000000 +0200 -+++ linux-4.3.aufs.mod/fs/namespace.c 2015-11-19 22:32:42.499511394 +0200 +diff -Nurp linux-4.4-rc6.aufs/fs/namespace.c linux-4.4-rc6.aufs.mod/fs/namespace.c +--- linux-4.4-rc6.aufs/fs/namespace.c 2015-11-02 02:05:25.000000000 +0200 ++++ linux-4.4-rc6.aufs.mod/fs/namespace.c 2015-12-21 19:55:29.525275790 +0200 @@ -463,6 +463,7 @@ void __mnt_drop_write(struct vfsmount *m mnt_dec_writers(real_mount(mnt)); preempt_enable(); @@ -84,9 +84,9 @@ diff -Nurp linux-4.3.aufs/fs/namespace.c linux-4.3.aufs.mod/fs/namespace.c static void cleanup_group_ids(struct mount *mnt, struct mount *end) { -diff -Nurp linux-4.3.aufs/fs/notify/group.c linux-4.3.aufs.mod/fs/notify/group.c ---- linux-4.3.aufs/fs/notify/group.c 2015-11-02 02:05:25.000000000 +0200 -+++ linux-4.3.aufs.mod/fs/notify/group.c 2015-11-19 22:32:42.500511399 +0200 +diff -Nurp linux-4.4-rc6.aufs/fs/notify/group.c linux-4.4-rc6.aufs.mod/fs/notify/group.c +--- linux-4.4-rc6.aufs/fs/notify/group.c 2015-11-02 02:05:25.000000000 +0200 ++++ linux-4.4-rc6.aufs.mod/fs/notify/group.c 2015-12-21 19:55:29.525275790 +0200 @@ -22,6 +22,7 @@ #include #include @@ -119,9 +119,9 @@ diff -Nurp linux-4.3.aufs/fs/notify/group.c linux-4.3.aufs.mod/fs/notify/group.c int fsnotify_fasync(int fd, struct file *file, int on) { -diff -Nurp linux-4.3.aufs/fs/notify/mark.c linux-4.3.aufs.mod/fs/notify/mark.c ---- linux-4.3.aufs/fs/notify/mark.c 2015-11-02 02:05:25.000000000 +0200 -+++ linux-4.3.aufs.mod/fs/notify/mark.c 2015-11-19 22:32:42.500511399 +0200 +diff -Nurp linux-4.4-rc6.aufs/fs/notify/mark.c linux-4.4-rc6.aufs.mod/fs/notify/mark.c +--- linux-4.4-rc6.aufs/fs/notify/mark.c 2015-11-02 02:05:25.000000000 +0200 ++++ linux-4.4-rc6.aufs.mod/fs/notify/mark.c 2015-12-21 19:55:29.525275790 +0200 @@ -109,6 +109,7 @@ void fsnotify_put_mark(struct fsnotify_m mark->free_mark(mark); } @@ -154,9 +154,9 @@ diff -Nurp linux-4.3.aufs/fs/notify/mark.c linux-4.3.aufs.mod/fs/notify/mark.c static int fsnotify_mark_destroy(void *ignored) { -diff -Nurp linux-4.3.aufs/fs/open.c linux-4.3.aufs.mod/fs/open.c ---- linux-4.3.aufs/fs/open.c 2015-11-02 02:05:25.000000000 +0200 -+++ linux-4.3.aufs.mod/fs/open.c 2015-11-19 22:32:42.500511399 +0200 +diff -Nurp linux-4.4-rc6.aufs/fs/open.c linux-4.4-rc6.aufs.mod/fs/open.c +--- linux-4.4-rc6.aufs/fs/open.c 2015-11-02 02:05:25.000000000 +0200 ++++ linux-4.4-rc6.aufs.mod/fs/open.c 2015-12-21 19:55:29.526275798 +0200 @@ -64,6 +64,7 @@ int do_truncate(struct dentry *dentry, l mutex_unlock(&dentry->d_inode->i_mutex); return ret; @@ -173,9 +173,9 @@ diff -Nurp linux-4.3.aufs/fs/open.c linux-4.3.aufs.mod/fs/open.c static int do_dentry_open(struct file *f, struct inode *inode, -diff -Nurp linux-4.3.aufs/fs/read_write.c linux-4.3.aufs.mod/fs/read_write.c ---- linux-4.3.aufs/fs/read_write.c 2015-11-19 22:31:36.530207761 +0200 -+++ linux-4.3.aufs.mod/fs/read_write.c 2015-11-19 22:32:42.501511403 +0200 +diff -Nurp linux-4.4-rc6.aufs/fs/read_write.c linux-4.4-rc6.aufs.mod/fs/read_write.c +--- linux-4.4-rc6.aufs/fs/read_write.c 2015-12-21 19:54:42.987923551 +0200 ++++ linux-4.4-rc6.aufs.mod/fs/read_write.c 2015-12-21 19:55:29.526275798 +0200 @@ -504,6 +504,7 @@ vfs_readf_t vfs_readf(struct file *file) return new_sync_read; return ERR_PTR(-ENOSYS); @@ -192,10 +192,10 @@ diff -Nurp linux-4.3.aufs/fs/read_write.c linux-4.3.aufs.mod/fs/read_write.c ssize_t __kernel_write(struct file *file, const char *buf, size_t count, loff_t *pos) { -diff -Nurp linux-4.3.aufs/fs/splice.c linux-4.3.aufs.mod/fs/splice.c ---- linux-4.3.aufs/fs/splice.c 2015-11-19 22:31:36.531207765 +0200 -+++ linux-4.3.aufs.mod/fs/splice.c 2015-11-19 22:32:42.501511403 +0200 -@@ -1115,6 +1115,7 @@ long do_splice_from(struct pipe_inode_in +diff -Nurp linux-4.4-rc6.aufs/fs/splice.c linux-4.4-rc6.aufs.mod/fs/splice.c +--- linux-4.4-rc6.aufs/fs/splice.c 2015-12-21 19:54:42.987923551 +0200 ++++ linux-4.4-rc6.aufs.mod/fs/splice.c 2015-12-21 19:55:29.526275798 +0200 +@@ -1123,6 +1123,7 @@ long do_splice_from(struct pipe_inode_in return splice_write(pipe, out, ppos, len, flags); } @@ -203,7 +203,7 @@ diff -Nurp linux-4.3.aufs/fs/splice.c linux-4.3.aufs.mod/fs/splice.c /* * Attempt to initiate a splice from a file to a pipe. -@@ -1141,6 +1142,7 @@ long do_splice_to(struct file *in, loff_ +@@ -1149,6 +1150,7 @@ long do_splice_to(struct file *in, loff_ return splice_read(in, ppos, pipe, len, flags); } @@ -211,9 +211,9 @@ diff -Nurp linux-4.3.aufs/fs/splice.c linux-4.3.aufs.mod/fs/splice.c /** * splice_direct_to_actor - splices data directly between two non-pipes -diff -Nurp linux-4.3.aufs/security/commoncap.c linux-4.3.aufs.mod/security/commoncap.c ---- linux-4.3.aufs/security/commoncap.c 2015-11-02 02:05:25.000000000 +0200 -+++ linux-4.3.aufs.mod/security/commoncap.c 2015-11-19 22:32:42.501511403 +0200 +diff -Nurp linux-4.4-rc6.aufs/security/commoncap.c linux-4.4-rc6.aufs.mod/security/commoncap.c +--- linux-4.4-rc6.aufs/security/commoncap.c 2015-11-02 02:05:25.000000000 +0200 ++++ linux-4.4-rc6.aufs.mod/security/commoncap.c 2015-12-21 19:55:29.527275805 +0200 @@ -1053,12 +1053,14 @@ int cap_mmap_addr(unsigned long addr) } return ret; @@ -229,9 +229,9 @@ diff -Nurp linux-4.3.aufs/security/commoncap.c linux-4.3.aufs.mod/security/commo #ifdef CONFIG_SECURITY -diff -Nurp linux-4.3.aufs/security/device_cgroup.c linux-4.3.aufs.mod/security/device_cgroup.c ---- linux-4.3.aufs/security/device_cgroup.c 2015-11-02 02:05:25.000000000 +0200 -+++ linux-4.3.aufs.mod/security/device_cgroup.c 2015-11-19 22:32:42.502511407 +0200 +diff -Nurp linux-4.4-rc6.aufs/security/device_cgroup.c linux-4.4-rc6.aufs.mod/security/device_cgroup.c +--- linux-4.4-rc6.aufs/security/device_cgroup.c 2015-11-02 02:05:25.000000000 +0200 ++++ linux-4.4-rc6.aufs.mod/security/device_cgroup.c 2015-12-21 19:55:29.527275805 +0200 @@ -7,6 +7,7 @@ #include #include @@ -248,9 +248,9 @@ diff -Nurp linux-4.3.aufs/security/device_cgroup.c linux-4.3.aufs.mod/security/d int devcgroup_inode_mknod(int mode, dev_t dev) { -diff -Nurp linux-4.3.aufs/security/security.c linux-4.3.aufs.mod/security/security.c ---- linux-4.3.aufs/security/security.c 2015-11-02 02:05:25.000000000 +0200 -+++ linux-4.3.aufs.mod/security/security.c 2015-11-19 22:32:42.503511412 +0200 +diff -Nurp linux-4.4-rc6.aufs/security/security.c linux-4.4-rc6.aufs.mod/security/security.c +--- linux-4.4-rc6.aufs/security/security.c 2015-11-02 02:05:25.000000000 +0200 ++++ linux-4.4-rc6.aufs.mod/security/security.c 2015-12-21 19:55:29.528275813 +0200 @@ -433,6 +433,7 @@ int security_path_rmdir(struct path *dir return 0; return call_int_hook(path_rmdir, 0, dir, dentry); diff --git a/kernel/kernel/files/patches/mageia/fs-aufs-4.3.patch b/kernel/kernel/files/patches/mageia/fs-aufs-4.4.patch similarity index 99% rename from kernel/kernel/files/patches/mageia/fs-aufs-4.3.patch rename to kernel/kernel/files/patches/mageia/fs-aufs-4.4.patch index 90b15aa2..dde11710 100644 --- a/kernel/kernel/files/patches/mageia/fs-aufs-4.3.patch +++ b/kernel/kernel/files/patches/mageia/fs-aufs-4.4.patch @@ -1,4 +1,3 @@ - Documentation/ABI/testing/debugfs-aufs | 50 + Documentation/ABI/testing/sysfs-aufs | 31 + Documentation/filesystems/aufs/README | 383 ++++ @@ -21,7 +20,7 @@ fs/aufs/Kconfig | 185 ++ fs/aufs/Makefile | 36 + fs/aufs/aufs.h | 46 + - fs/aufs/branch.c | 1400 +++++++++++++++ + fs/aufs/branch.c | 1394 +++++++++++++++ fs/aufs/branch.h | 266 +++ fs/aufs/cpup.c | 1306 ++++++++++++++ fs/aufs/cpup.h | 81 + @@ -29,7 +28,7 @@ fs/aufs/dbgaufs.h | 35 + fs/aufs/dcsub.c | 211 +++ fs/aufs/dcsub.h | 123 ++ - fs/aufs/debug.c | 427 +++++ + fs/aufs/debug.c | 425 +++++ fs/aufs/debug.h | 212 +++ fs/aufs/dentry.c | 1123 ++++++++++++ fs/aufs/dentry.h | 221 +++ @@ -43,7 +42,7 @@ fs/aufs/fhsm.c | 412 +++++ fs/aufs/file.c | 831 +++++++++ fs/aufs/file.h | 278 +++ - fs/aufs/finfo.c | 144 ++ + fs/aufs/finfo.c | 143 ++ fs/aufs/fstype.h | 387 ++++ fs/aufs/hfsnotify.c | 275 +++ fs/aufs/hfsplus.c | 43 + @@ -54,7 +53,7 @@ fs/aufs/i_op_ren.c | 1002 +++++++++++ fs/aufs/iinfo.c | 264 +++ fs/aufs/inode.c | 515 ++++++ - fs/aufs/inode.h | 668 +++++++ + fs/aufs/inode.h | 672 +++++++ fs/aufs/ioctl.c | 206 +++ fs/aufs/loop.c | 133 ++ fs/aufs/loop.h | 39 + @@ -72,8 +71,8 @@ fs/aufs/rwsem.h | 178 ++ fs/aufs/sbinfo.c | 353 ++++ fs/aufs/spl.h | 98 ++ - fs/aufs/super.c | 1034 +++++++++++ - fs/aufs/super.h | 629 +++++++ + fs/aufs/super.c | 1026 +++++++++++ + fs/aufs/super.h | 628 +++++++ fs/aufs/sysaufs.c | 91 + fs/aufs/sysaufs.h | 88 + fs/aufs/sysfs.c | 340 ++++ @@ -82,13 +81,12 @@ fs/aufs/vfsub.c | 835 +++++++++ fs/aufs/vfsub.h | 274 +++ fs/aufs/wbr_policy.c | 752 ++++++++ - fs/aufs/whout.c | 1050 +++++++++++ + fs/aufs/whout.c | 1047 +++++++++++ fs/aufs/whout.h | 72 + fs/aufs/wkq.c | 200 +++ fs/aufs/wkq.h | 78 + fs/aufs/xattr.c | 331 ++++ fs/aufs/xino.c | 1283 ++++++++++++++ - fs/buffer.c | 2 +- fs/dcache.c | 2 +- fs/proc/base.c | 2 +- fs/proc/nommu.c | 5 +- @@ -110,7 +108,7 @@ mm/mmap.c | 17 +- mm/nommu.c | 10 +- mm/prfile.c | 86 + - 111 files changed, 33302 insertions(+), 30 deletions(-) + 110 files changed, 33284 insertions(+), 29 deletions(-) diff --git a/Documentation/ABI/testing/debugfs-aufs b/Documentation/ABI/testing/debugfs-aufs new file mode 100644 @@ -1716,10 +1714,10 @@ index 0000000..9d502b5 +Currently this approach is applied to address_space_operations for +regular files only. diff --git a/MAINTAINERS b/MAINTAINERS -index 747c653..53ecc33 100644 +index 9bff63c..093dd5b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -1985,6 +1985,19 @@ F: include/linux/audit.h +@@ -2029,6 +2029,19 @@ F: include/linux/audit.h F: include/uapi/linux/audit.h F: kernel/audit* @@ -1740,10 +1738,10 @@ index 747c653..53ecc33 100644 M: Miguel Ojeda Sandonis W: http://miguelojeda.es/auxdisplay.htm diff --git a/drivers/block/loop.c b/drivers/block/loop.c -index 674f800..291ec9e 100644 +index 423f4ca..abfdd2b 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c -@@ -560,6 +560,24 @@ static inline int is_loop_device(struct file *file) +@@ -706,6 +706,24 @@ static inline int is_loop_device(struct file *file) return i && S_ISBLK(i->i_mode) && MAJOR(i->i_rdev) == LOOP_MAJOR; } @@ -1769,10 +1767,10 @@ index 674f800..291ec9e 100644 static ssize_t loop_attr_show(struct device *dev, char *page, diff --git a/fs/Kconfig b/fs/Kconfig -index da3f32f..b9879fe 100644 +index 6ce72d8..4aa31ea 100644 --- a/fs/Kconfig +++ b/fs/Kconfig -@@ -215,6 +215,7 @@ source "fs/pstore/Kconfig" +@@ -221,6 +221,7 @@ source "fs/pstore/Kconfig" source "fs/sysv/Kconfig" source "fs/ufs/Kconfig" source "fs/exofs/Kconfig" @@ -1781,10 +1779,10 @@ index da3f32f..b9879fe 100644 endif # MISC_FILESYSTEMS diff --git a/fs/Makefile b/fs/Makefile -index f79cf40..7562a4d 100644 +index 79f5225..a7c7f16 100644 --- a/fs/Makefile +++ b/fs/Makefile -@@ -125,3 +125,4 @@ obj-y += exofs/ # Multiple modules +@@ -126,3 +126,4 @@ obj-y += exofs/ # Multiple modules obj-$(CONFIG_CEPH_FS) += ceph/ obj-$(CONFIG_PSTORE) += pstore/ obj-$(CONFIG_EFIVAR_FS) += efivarfs/ @@ -2076,10 +2074,10 @@ index 0000000..75290bd +#endif /* __AUFS_H__ */ diff --git a/fs/aufs/branch.c b/fs/aufs/branch.c new file mode 100644 -index 0000000..72a8ee6 +index 0000000..f491422 --- /dev/null +++ b/fs/aufs/branch.c -@@ -0,0 +1,1400 @@ +@@ -0,0 +1,1394 @@ +/* + * Copyright (C) 2005-2015 Junjiro R. Okajima + */ @@ -2199,7 +2197,7 @@ index 0000000..72a8ee6 + + err = -ENOMEM; + root = sb->s_root; -+ add_branch = kmalloc(sizeof(*add_branch), GFP_NOFS); ++ add_branch = kzalloc(sizeof(*add_branch), GFP_NOFS); + if (unlikely(!add_branch)) + goto out; + @@ -2207,16 +2205,14 @@ index 0000000..72a8ee6 + if (unlikely(err)) + goto out_br; + -+ add_branch->br_wbr = NULL; + if (au_br_writable(perm)) { + /* may be freed separately at changing the branch permission */ -+ add_branch->br_wbr = kmalloc(sizeof(*add_branch->br_wbr), ++ add_branch->br_wbr = kzalloc(sizeof(*add_branch->br_wbr), + GFP_NOFS); + if (unlikely(!add_branch->br_wbr)) + goto out_hnotify; + } + -+ add_branch->br_fhsm = NULL; + if (au_br_fhsm(perm)) { + err = au_fhsm_br_alloc(add_branch); + if (unlikely(err)) @@ -2421,9 +2417,7 @@ index 0000000..72a8ee6 + + wbr = br->br_wbr; + au_rw_init(&wbr->wbr_wh_rwsem); -+ memset(wbr->wbr_wh, 0, sizeof(wbr->wbr_wh)); + atomic_set(&wbr->wbr_wh_running, 0); -+ wbr->wbr_bytes = 0; + + /* + * a limit for rmdir/rename a dir @@ -2452,12 +2446,10 @@ index 0000000..72a8ee6 + struct inode *h_inode; + + err = 0; -+ memset(&br->br_xino, 0, sizeof(br->br_xino)); + mutex_init(&br->br_xino.xi_nondir_mtx); + br->br_perm = add->perm; + br->br_path = add->path; /* set first, path_get() later */ + spin_lock_init(&br->br_dykey_lock); -+ memset(br->br_dykey, 0, sizeof(br->br_dykey)); + atomic_set(&br->br_count, 0); + atomic_set(&br->br_xino_running, 0); + br->br_id = au_new_br_id(sb); @@ -2665,7 +2657,7 @@ index 0000000..72a8ee6 + for (ull = 0; ull < max; ull++) + if (a[ull]) + fput(a[ull]); -+ au_array_free(a); ++ kvfree(a); +} + +/* ---------------------------------------------------------------------- */ @@ -3415,7 +3407,7 @@ index 0000000..72a8ee6 + + if (unlikely(err)) { + rerr = -ENOMEM; -+ br->br_wbr = kmalloc(sizeof(*br->br_wbr), ++ br->br_wbr = kzalloc(sizeof(*br->br_wbr), + GFP_NOFS); + if (br->br_wbr) + rerr = au_wbr_init(br, sb, br->br_perm); @@ -3429,7 +3421,7 @@ index 0000000..72a8ee6 + } else if (au_br_writable(mod->perm)) { + /* ro --> rw */ + err = -ENOMEM; -+ br->br_wbr = kmalloc(sizeof(*br->br_wbr), GFP_NOFS); ++ br->br_wbr = kzalloc(sizeof(*br->br_wbr), GFP_NOFS); + if (br->br_wbr) { + err = au_wbr_init(br, sb, mod->perm); + if (unlikely(err)) { @@ -5965,10 +5957,10 @@ index 0000000..2aa87ac +#endif /* __AUFS_DCSUB_H__ */ diff --git a/fs/aufs/debug.c b/fs/aufs/debug.c new file mode 100644 -index 0000000..e553cef +index 0000000..42053aa --- /dev/null +++ b/fs/aufs/debug.c -@@ -0,0 +1,427 @@ +@@ -0,0 +1,425 @@ +/* + * Copyright (C) 2005-2015 Junjiro R. Okajima + */ @@ -6288,9 +6280,7 @@ index 0000000..e553cef + } + + a->mnt.mnt_sb = sb; -+ a->fake.br_perm = 0; + a->fake.br_path.mnt = &a->mnt; -+ a->fake.br_xino.xi_file = NULL; + atomic_set(&a->fake.br_count, 0); + smp_mb(); /* atomic_set */ + err = do_pri_br(-1, &a->fake); @@ -6616,7 +6606,7 @@ index 0000000..0a2e7e7 +#endif /* __AUFS_DEBUG_H__ */ diff --git a/fs/aufs/dentry.c b/fs/aufs/dentry.c new file mode 100644 -index 0000000..c33fb83 +index 0000000..aad25be --- /dev/null +++ b/fs/aufs/dentry.c @@ -0,0 +1,1123 @@ @@ -7176,7 +7166,9 @@ index 0000000..c33fb83 + struct dentry *dentry; + struct inode *inode; + mode_t mode; -+ } orig_h, tmp_h; ++ } orig_h, tmp_h = { ++ .dentry = NULL ++ }; + struct au_hdentry *hd; + struct inode *inode, *h_inode; + struct dentry *h_dentry; @@ -7190,10 +7182,8 @@ index 0000000..c33fb83 + orig_h.inode = d_inode(orig_h.dentry); + orig_h.mode = orig_h.inode->i_mode & S_IFMT; + } -+ memset(&tmp_h, 0, sizeof(tmp_h)); + if (tmp->di_bstart >= 0) { + tmp_h.dentry = tmp->di_hdentry[tmp->di_bstart].hd_dentry; -+ tmp_h.inode = NULL; + if (d_is_positive(tmp_h.dentry)) { + tmp_h.inode = d_inode(tmp_h.dentry); + tmp_h.mode = tmp_h.inode->i_mode & S_IFMT; @@ -12909,10 +12899,10 @@ index 0000000..488473e +#endif /* __AUFS_FILE_H__ */ diff --git a/fs/aufs/finfo.c b/fs/aufs/finfo.c new file mode 100644 -index 0000000..05732c9 +index 0000000..e709205 --- /dev/null +++ b/fs/aufs/finfo.c -@@ -0,0 +1,144 @@ +@@ -0,0 +1,143 @@ +/* + * Copyright (C) 2005-2015 Junjiro R. Okajima + */ @@ -12977,7 +12967,6 @@ index 0000000..05732c9 + if (fidir) { + fidir->fd_bbot = -1; + fidir->fd_nent = nbr; -+ fidir->fd_vdir_cache = NULL; + } + + return fidir; @@ -18398,7 +18387,7 @@ index 0000000..fd815fd +} diff --git a/fs/aufs/iinfo.c b/fs/aufs/iinfo.c new file mode 100644 -index 0000000..b8efd45 +index 0000000..c604f69 --- /dev/null +++ b/fs/aufs/iinfo.c @@ -0,0 +1,264 @@ @@ -18509,13 +18498,13 @@ index 0000000..b8efd45 + sigen = au_sigen(inode->i_sb); + iinfo = au_ii(inode); + iigen = &iinfo->ii_generation; -+ spin_lock(&iinfo->ii_genspin); ++ spin_lock(&iigen->ig_spin); + iigen->ig_generation = sigen; + if (half) + au_ig_fset(iigen->ig_flags, HALF_REFRESHED); + else + au_ig_fclr(iigen->ig_flags, HALF_REFRESHED); -+ spin_unlock(&iinfo->ii_genspin); ++ spin_unlock(&iigen->ig_spin); +} + +/* it may be called at remount time, too */ @@ -18568,7 +18557,7 @@ index 0000000..b8efd45 + struct au_iinfo *iinfo = &c->iinfo; + static struct lock_class_key aufs_ii; + -+ spin_lock_init(&iinfo->ii_genspin); ++ spin_lock_init(&iinfo->ii_generation.ig_spin); + au_rw_init(&iinfo->ii_rwsem); + au_rw_class(&iinfo->ii_rwsem, &aufs_ii); + inode_init_once(&c->vfs_inode); @@ -19189,10 +19178,10 @@ index 0000000..6db3d6f +} diff --git a/fs/aufs/inode.h b/fs/aufs/inode.h new file mode 100644 -index 0000000..a3a1876 +index 0000000..31e31f4 --- /dev/null +++ b/fs/aufs/inode.h -@@ -0,0 +1,668 @@ +@@ -0,0 +1,672 @@ +/* + * Copyright (C) 2005-2015 Junjiro R. Okajima + */ @@ -19241,12 +19230,12 @@ index 0000000..a3a1876 + do { (flags) &= ~AuIG_##name; } while (0) + +struct au_iigen { ++ spinlock_t ig_spin; + __u32 ig_generation, ig_flags; +}; + +struct au_vdir; +struct au_iinfo { -+ spinlock_t ii_genspin; + struct au_iigen ii_generation; + struct super_block *ii_hsb1; /* no get/put */ + @@ -19604,17 +19593,19 @@ index 0000000..a3a1876 +#endif +} + -+static inline unsigned int au_iigen(struct inode *inode, struct au_iigen *iigen) ++static inline unsigned int au_iigen(struct inode *inode, struct au_iigen *iigen_arg) +{ + unsigned int gen; + struct au_iinfo *iinfo; ++ struct au_iigen *iigen; + + iinfo = au_ii(inode); -+ spin_lock(&iinfo->ii_genspin); -+ if (iigen) -+ *iigen = iinfo->ii_generation; -+ gen = iinfo->ii_generation.ig_generation; -+ spin_unlock(&iinfo->ii_genspin); ++ iigen = &iinfo->ii_generation; ++ spin_lock(&iigen->ig_spin); ++ if (iigen_arg) ++ *iigen_arg = *iigen; ++ gen = iigen->ig_generation; ++ spin_unlock(&iigen->ig_spin); + + return gen; +} @@ -19634,11 +19625,13 @@ index 0000000..a3a1876 +static inline void au_iigen_dec(struct inode *inode) +{ + struct au_iinfo *iinfo; ++ struct au_iigen *iigen; + + iinfo = au_ii(inode); -+ spin_lock(&iinfo->ii_genspin); -+ iinfo->ii_generation.ig_generation--; -+ spin_unlock(&iinfo->ii_genspin); ++ iigen = &iinfo->ii_generation; ++ spin_lock(&iigen->ig_spin); ++ iigen->ig_generation--; ++ spin_unlock(&iigen->ig_spin); +} + +static inline int au_iigen_test(struct inode *inode, unsigned int sigen) @@ -25205,10 +25198,10 @@ index 0000000..a66d39e +#endif /* __AUFS_SPL_H__ */ diff --git a/fs/aufs/super.c b/fs/aufs/super.c new file mode 100644 -index 0000000..3fe10d3 +index 0000000..98cfd64 --- /dev/null +++ b/fs/aufs/super.c -@@ -0,0 +1,1034 @@ +@@ -0,0 +1,1026 @@ +/* + * Copyright (C) 2005-2015 Junjiro R. Okajima + */ @@ -25667,16 +25660,6 @@ index 0000000..3fe10d3 + +/* ---------------------------------------------------------------------- */ + -+void au_array_free(void *array) -+{ -+ if (array) { -+ if (!is_vmalloc_addr(array)) -+ kfree(array); -+ else -+ vfree(array); -+ } -+} -+ +void *au_array_alloc(unsigned long long *hint, au_arraycb_t cb, + struct super_block *sb, void *arg) +{ @@ -25753,7 +25736,7 @@ index 0000000..3fe10d3 + + for (ull = 0; ull < max; ull++) + iput(a[ull]); -+ au_array_free(a); ++ kvfree(a); +} + +/* ---------------------------------------------------------------------- */ @@ -25978,7 +25961,9 @@ index 0000000..3fe10d3 +{ + int err, do_dx; + unsigned int mntflags; -+ struct au_opts opts; ++ struct au_opts opts = { ++ .opt = NULL ++ }; + struct dentry *root; + struct inode *inode; + struct au_sbinfo *sbinfo; @@ -25996,7 +25981,6 @@ index 0000000..3fe10d3 + } + + err = -ENOMEM; -+ memset(&opts, 0, sizeof(opts)); + opts.opt = (void *)__get_free_page(GFP_NOFS); + if (unlikely(!opts.opt)) + goto out; @@ -26097,7 +26081,9 @@ index 0000000..3fe10d3 + int silent __maybe_unused) +{ + int err; -+ struct au_opts opts; ++ struct au_opts opts = { ++ .opt = NULL ++ }; + struct au_sbinfo *sbinfo; + struct dentry *root; + struct inode *inode; @@ -26110,7 +26096,6 @@ index 0000000..3fe10d3 + } + + err = -ENOMEM; -+ memset(&opts, 0, sizeof(opts)); + opts.opt = (void *)__get_free_page(GFP_NOFS); + if (unlikely(!opts.opt)) + goto out; @@ -26245,10 +26230,10 @@ index 0000000..3fe10d3 +}; diff --git a/fs/aufs/super.h b/fs/aufs/super.h new file mode 100644 -index 0000000..e0f588e +index 0000000..d151729 --- /dev/null +++ b/fs/aufs/super.h -@@ -0,0 +1,629 @@ +@@ -0,0 +1,628 @@ +/* + * Copyright (C) 2005-2015 Junjiro R. Okajima + */ @@ -26517,7 +26502,6 @@ index 0000000..e0f588e +struct inode *au_iget_locked(struct super_block *sb, ino_t ino); +typedef unsigned long long (*au_arraycb_t)(struct super_block *sb, void *array, + unsigned long long max, void *arg); -+void au_array_free(void *array); +void *au_array_alloc(unsigned long long *hint, au_arraycb_t cb, + struct super_block *sb, void *arg); +struct inode **au_iarray_alloc(struct super_block *sb, unsigned long long *max); @@ -30327,10 +30311,10 @@ index 0000000..53a01fb +}; diff --git a/fs/aufs/whout.c b/fs/aufs/whout.c new file mode 100644 -index 0000000..a7f160e +index 0000000..05ba085 --- /dev/null +++ b/fs/aufs/whout.c -@@ -0,0 +1,1050 @@ +@@ -0,0 +1,1047 @@ +/* + * Copyright (C) 2005-2015 Junjiro R. Okajima + */ @@ -31214,15 +31198,12 @@ index 0000000..a7f160e + + SiMustAnyLock(sb); + -+ whtmp = kmalloc(sizeof(*whtmp), gfp); ++ whtmp = kzalloc(sizeof(*whtmp), gfp); + if (unlikely(!whtmp)) { + whtmp = ERR_PTR(-ENOMEM); + goto out; + } + -+ whtmp->dir = NULL; -+ whtmp->br = NULL; -+ whtmp->wh_dentry = NULL; + /* no estimation for dir size */ + rdhash = au_sbi(sb)->si_rdhash; + if (!rdhash) @@ -32088,7 +32069,7 @@ index 0000000..c1695b3 +#endif diff --git a/fs/aufs/xino.c b/fs/aufs/xino.c new file mode 100644 -index 0000000..f377ce5 +index 0000000..90de8c5 --- /dev/null +++ b/fs/aufs/xino.c @@ -0,0 +1,1283 @@ @@ -32339,7 +32320,7 @@ index 0000000..f377ce5 + struct au_xino_lock_dir ldir; + + err = -ENOMEM; -+ st = kzalloc(sizeof(*st), GFP_NOFS); ++ st = kmalloc(sizeof(*st), GFP_NOFS); + if (unlikely(!st)) + goto out; + @@ -33375,19 +33356,6 @@ index 0000000..f377ce5 +out: + return err; +} -diff --git a/fs/buffer.c b/fs/buffer.c -index 82283ab..477e5f3 100644 ---- a/fs/buffer.c -+++ b/fs/buffer.c -@@ -2473,7 +2473,7 @@ int block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf, - * Update file times before taking page lock. We may end up failing the - * fault so this update may be superfluous but who really cares... - */ -- file_update_time(vma->vm_file); -+ vma_file_update_time(vma); - - ret = __block_page_mkwrite(vma, vmf, get_block); - sb_end_pagefault(sb); diff --git a/fs/dcache.c b/fs/dcache.c index 5c33aeb..8aa7f26 100644 --- a/fs/dcache.c @@ -33402,10 +33370,10 @@ index 5c33aeb..8aa7f26 100644 void (*finish)(void *)) { diff --git a/fs/proc/base.c b/fs/proc/base.c -index b25eee4..c83d588 100644 +index bd3e9e6..fc42216 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c -@@ -1914,7 +1914,7 @@ static int proc_map_files_get_link(struct dentry *dentry, struct path *path) +@@ -1921,7 +1921,7 @@ static int proc_map_files_get_link(struct dentry *dentry, struct path *path) down_read(&mm->mmap_sem); vma = find_exact_vma(mm, vm_start, vm_end); if (vma && vma->vm_file) { @@ -33431,10 +33399,10 @@ index f8595e8..cb8eda0 100644 ino = inode->i_ino; } diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c -index e2d46ad..5e7e631 100644 +index 187b3b5..e03793e 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c -@@ -280,7 +280,10 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid) +@@ -281,7 +281,10 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid) const char *name = NULL; if (file) { @@ -33446,7 +33414,7 @@ index e2d46ad..5e7e631 100644 dev = inode->i_sb->s_dev; ino = inode->i_ino; pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT; -@@ -1465,7 +1468,7 @@ static int show_numa_map(struct seq_file *m, void *v, int is_pid) +@@ -1505,7 +1508,7 @@ static int show_numa_map(struct seq_file *m, void *v, int is_pid) struct proc_maps_private *proc_priv = &numa_priv->proc_maps; struct vm_area_struct *vma = v; struct numa_maps *md = &numa_priv->md; @@ -33505,10 +33473,10 @@ index 819ef3f..fd0414e 100644 { mm_segment_t old_fs; diff --git a/fs/splice.c b/fs/splice.c -index 5fc1e50..5f8385a 100644 +index 4cf700d..30a091d 100644 --- a/fs/splice.c +++ b/fs/splice.c -@@ -1102,8 +1102,8 @@ EXPORT_SYMBOL(generic_splice_sendpage); +@@ -1110,8 +1110,8 @@ EXPORT_SYMBOL(generic_splice_sendpage); /* * Attempt to initiate a splice from pipe to file. */ @@ -33519,7 +33487,7 @@ index 5fc1e50..5f8385a 100644 { ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); -@@ -1119,9 +1119,9 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, +@@ -1127,9 +1127,9 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, /* * Attempt to initiate a splice from a file to a pipe. */ @@ -33545,10 +33513,10 @@ index f87d308..9a290b3 100644 static inline void fput_light(struct file *file, int fput_needed) { diff --git a/include/linux/fs.h b/include/linux/fs.h -index 72d8a84..fabd9d7a 100644 +index 3aa5142..8d48506 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h -@@ -1687,6 +1687,12 @@ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector, +@@ -1672,6 +1672,12 @@ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector, struct iovec *fast_pointer, struct iovec **ret_pointer); @@ -33562,10 +33530,10 @@ index 72d8a84..fabd9d7a 100644 extern ssize_t __vfs_write(struct file *, const char __user *, size_t, loff_t *); extern ssize_t vfs_read(struct file *, char __user *, size_t, loff_t *); diff --git a/include/linux/mm.h b/include/linux/mm.h -index 80001de..9248b97 100644 +index 00bad77..cc616b0 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h -@@ -1211,6 +1211,28 @@ static inline int fixup_user_fault(struct task_struct *tsk, +@@ -1183,6 +1183,28 @@ static inline int fixup_user_fault(struct task_struct *tsk, } #endif @@ -33595,10 +33563,10 @@ index 80001de..9248b97 100644 extern int access_remote_vm(struct mm_struct *mm, unsigned long addr, void *buf, int len, int write); diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h -index 3d6baa7..750ca95 100644 +index f8d1492..c3a3760 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h -@@ -250,6 +250,7 @@ struct vm_region { +@@ -272,6 +272,7 @@ struct vm_region { unsigned long vm_top; /* region allocated to here */ unsigned long vm_pgoff; /* the offset in vm_file corresponding to vm_start */ struct file *vm_file; /* the backing file or NULL */ @@ -33606,7 +33574,7 @@ index 3d6baa7..750ca95 100644 int vm_usage; /* region usage count (access under nommu_region_sem) */ bool vm_icache_flushed : 1; /* true if the icache has been flushed for -@@ -324,6 +325,7 @@ struct vm_area_struct { +@@ -346,6 +347,7 @@ struct vm_area_struct { unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_SIZE units, *not* PAGE_CACHE_SIZE */ struct file * vm_file; /* File we map to (can be NULL). */ @@ -33630,7 +33598,7 @@ index da2751d..2e0fca6 100644 + unsigned int flags); #endif diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild -index f7b2db4..47098ae 100644 +index 628e6e6..6dd5765 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -59,6 +59,7 @@ header-y += atmsvc.h @@ -33643,7 +33611,7 @@ index f7b2db4..47098ae 100644 header-y += auxvec.h diff --git a/include/uapi/linux/aufs_type.h b/include/uapi/linux/aufs_type.h new file mode 100644 -index 0000000..0eb09ee +index 0000000..a06c985 --- /dev/null +++ b/include/uapi/linux/aufs_type.h @@ -0,0 +1,406 @@ @@ -33675,7 +33643,7 @@ index 0000000..0eb09ee + +#include + -+#define AUFS_VERSION "4.3" ++#define AUFS_VERSION "4.x-rcN" + +/* todo? move this to linux-2.6.19/include/magic.h */ +#define AUFS_SUPER_MAGIC ('a' << 24 | 'u' << 16 | 'f' << 8 | 's') @@ -34054,10 +34022,10 @@ index 0000000..0eb09ee + +#endif /* __AUFS_TYPE_H__ */ diff --git a/kernel/fork.c b/kernel/fork.c -index 2845623..71004bd 100644 +index fce002e..2230a0f 100644 --- a/kernel/fork.c +++ b/kernel/fork.c -@@ -462,7 +462,7 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) +@@ -464,7 +464,7 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) struct inode *inode = file_inode(file); struct address_space *mapping = file->f_mapping; @@ -34080,10 +34048,10 @@ index 2ed4319..e3a53f5 100644 obj-y += init-mm.o diff --git a/mm/filemap.c b/mm/filemap.c -index 327910c..7bbc372 100644 +index 1bb0076..8eaece8 100644 --- a/mm/filemap.c +++ b/mm/filemap.c -@@ -2089,7 +2089,7 @@ int filemap_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) +@@ -2128,7 +2128,7 @@ int filemap_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) int ret = VM_FAULT_LOCKED; sb_start_pagefault(inode->i_sb); @@ -34093,7 +34061,7 @@ index 327910c..7bbc372 100644 if (page->mapping != inode->i_mapping) { unlock_page(page); diff --git a/mm/memory.c b/mm/memory.c -index deb679c..df2ce3e 100644 +index c387430..d434404 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -2035,7 +2035,7 @@ static inline int wp_page_reuse(struct mm_struct *mm, @@ -34106,7 +34074,7 @@ index deb679c..df2ce3e 100644 return VM_FAULT_WRITE; diff --git a/mm/mmap.c b/mm/mmap.c -index 79bcc9f..da28c8a 100644 +index 2ce04a6..f555c0a 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -275,7 +275,7 @@ static struct vm_area_struct *remove_vma(struct vm_area_struct *vma) @@ -34127,7 +34095,7 @@ index 79bcc9f..da28c8a 100644 } if (next->anon_vma) anon_vma_merge(vma, next); -@@ -1683,8 +1683,8 @@ out: +@@ -1681,8 +1681,8 @@ out: return addr; unmap_and_free_vma: @@ -34137,7 +34105,7 @@ index 79bcc9f..da28c8a 100644 /* Undo any partial mapping done by a device driver. */ unmap_region(mm, vma, prev, vma->vm_start, vma->vm_end); -@@ -2485,7 +2485,7 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma, +@@ -2488,7 +2488,7 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma, goto out_free_mpol; if (new->vm_file) @@ -34146,7 +34114,7 @@ index 79bcc9f..da28c8a 100644 if (new->vm_ops && new->vm_ops->open) new->vm_ops->open(new); -@@ -2504,7 +2504,7 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma, +@@ -2507,7 +2507,7 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma, if (new->vm_ops && new->vm_ops->close) new->vm_ops->close(new); if (new->vm_file) @@ -34155,7 +34123,7 @@ index 79bcc9f..da28c8a 100644 unlink_anon_vmas(new); out_free_mpol: mpol_put(vma_policy(new)); -@@ -2646,7 +2646,6 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size, +@@ -2649,7 +2649,6 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size, struct vm_area_struct *vma; unsigned long populate = 0; unsigned long ret = -EINVAL; @@ -34163,7 +34131,7 @@ index 79bcc9f..da28c8a 100644 pr_warn_once("%s (%d) uses deprecated remap_file_pages() syscall. " "See Documentation/vm/remap_file_pages.txt.\n", -@@ -2690,10 +2689,10 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size, +@@ -2693,10 +2692,10 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size, munlock_vma_pages_range(vma, start, start + size); } @@ -34176,7 +34144,7 @@ index 79bcc9f..da28c8a 100644 out: up_write(&mm->mmap_sem); if (populate) -@@ -2963,7 +2962,7 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap, +@@ -2966,7 +2965,7 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap, if (anon_vma_clone(new_vma, vma)) goto out_free_mempol; if (new_vma->vm_file) @@ -34186,7 +34154,7 @@ index 79bcc9f..da28c8a 100644 new_vma->vm_ops->open(new_vma); vma_link(mm, new_vma, prev, rb_link, rb_parent); diff --git a/mm/nommu.c b/mm/nommu.c -index ab14a20..fffc566 100644 +index 92be862..29179f7 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -671,7 +671,7 @@ static void __put_nommu_region(struct vm_region *region) diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-0001-devicetree-add-vendor-prefix-for-Vivante-Corporation.patch b/kernel/kernel/files/patches/mageia/gpu-drm-0001-devicetree-add-vendor-prefix-for-Vivante-Corporation.patch new file mode 100644 index 00000000..63166e4f --- /dev/null +++ b/kernel/kernel/files/patches/mageia/gpu-drm-0001-devicetree-add-vendor-prefix-for-Vivante-Corporation.patch @@ -0,0 +1,30 @@ +From a6351c68fe1ea89ebb39e8c405d622464bcb339e Mon Sep 17 00:00:00 2001 +From: Philipp Zabel +Date: Thu, 2 Apr 2015 17:29:03 +0200 +Subject: [PATCH 1/4] devicetree: add vendor prefix for Vivante Corporation + +Trivial patch to add Vivante Corporation to the list of +devicetree vendor prefixes. + +Signed-off-by: Philipp Zabel +Signed-off-by: Lucas Stach +Acked-by: Rob Herring +--- + Documentation/devicetree/bindings/vendor-prefixes.txt | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt +index 55df1d4..c2767a7 100644 +--- a/Documentation/devicetree/bindings/vendor-prefixes.txt ++++ b/Documentation/devicetree/bindings/vendor-prefixes.txt +@@ -238,6 +238,7 @@ v3 V3 Semiconductor + variscite Variscite Ltd. + via VIA Technologies, Inc. + virtio Virtual I/O Device Specification, developed by the OASIS consortium ++vivante Vivante Corporation + voipac Voipac Technologies s.r.o. + wexler Wexler + winbond Winbond Electronics corp. +-- +2.6.4 + diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-0002-drm-etnaviv-add-devicetree-bindings.patch b/kernel/kernel/files/patches/mageia/gpu-drm-0002-drm-etnaviv-add-devicetree-bindings.patch new file mode 100644 index 00000000..afe6d580 --- /dev/null +++ b/kernel/kernel/files/patches/mageia/gpu-drm-0002-drm-etnaviv-add-devicetree-bindings.patch @@ -0,0 +1,79 @@ +From f04b205ac143413831b193f39fd9592665111d4b Mon Sep 17 00:00:00 2001 +From: Lucas Stach +Date: Thu, 2 Apr 2015 17:29:04 +0200 +Subject: [PATCH 2/4] drm/etnaviv: add devicetree bindings + +Etnaviv follows the same priciple as imx-drm to have a virtual +master device node to bind all the individual GPU cores together +into one DRM device. + +Signed-off-by: Lucas Stach +Acked-by: Rob Herring +--- + .../bindings/display/etnaviv/etnaviv-drm.txt | 54 ++++++++++++++++++++++ + 1 file changed, 54 insertions(+) + create mode 100644 Documentation/devicetree/bindings/display/etnaviv/etnaviv-drm.txt + +diff --git a/Documentation/devicetree/bindings/display/etnaviv/etnaviv-drm.txt b/Documentation/devicetree/bindings/display/etnaviv/etnaviv-drm.txt +new file mode 100644 +index 0000000..ed5e0a7 +--- /dev/null ++++ b/Documentation/devicetree/bindings/display/etnaviv/etnaviv-drm.txt +@@ -0,0 +1,54 @@ ++Etnaviv DRM master device ++========================= ++ ++The Etnaviv DRM master device is a virtual device needed to list all ++Vivante GPU cores that comprise the GPU subsystem. ++ ++Required properties: ++- compatible: Should be one of ++ "fsl,imx-gpu-subsystem" ++ "marvell,dove-gpu-subsystem" ++- cores: Should contain a list of phandles pointing to Vivante GPU devices ++ ++example: ++ ++gpu-subsystem { ++ compatible = "fsl,imx-gpu-subsystem"; ++ cores = <&gpu_2d>, <&gpu_3d>; ++}; ++ ++ ++Vivante GPU core devices ++======================== ++ ++Required properties: ++- compatible: Should be "vivante,gc" ++ A more specific compatible is not needed, as the cores contain chip ++ identification registers at fixed locations, which provide all the ++ necessary information to the driver. ++- reg: should be register base and length as documented in the ++ datasheet ++- interrupts: Should contain the cores interrupt line ++- clocks: should contain one clock for entry in clock-names ++ see Documentation/devicetree/bindings/clock/clock-bindings.txt ++- clock-names: ++ - "bus": AXI/register clock ++ - "core": GPU core clock ++ - "shader": Shader clock (only required if GPU has feature PIPE_3D) ++ ++Optional properties: ++- power-domains: a power domain consumer specifier according to ++ Documentation/devicetree/bindings/power/power_domain.txt ++ ++example: ++ ++gpu_3d: gpu@00130000 { ++ compatible = "vivante,gc"; ++ reg = <0x00130000 0x4000>; ++ interrupts = <0 9 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&clks IMX6QDL_CLK_GPU3D_AXI>, ++ <&clks IMX6QDL_CLK_GPU3D_CORE>, ++ <&clks IMX6QDL_CLK_GPU3D_SHADER>; ++ clock-names = "bus", "core", "shader"; ++ power-domains = <&gpc 1>; ++}; +-- +2.6.4 + diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-0003-drm-etnaviv-add-initial-etnaviv-DRM-driver.patch b/kernel/kernel/files/patches/mageia/gpu-drm-0003-drm-etnaviv-add-initial-etnaviv-DRM-driver.patch new file mode 100644 index 00000000..d50562e0 --- /dev/null +++ b/kernel/kernel/files/patches/mageia/gpu-drm-0003-drm-etnaviv-add-initial-etnaviv-DRM-driver.patch @@ -0,0 +1,7474 @@ +From a8c21a5451d831e67b7a6fb910f9ca8bc7b43554 Mon Sep 17 00:00:00 2001 +From: The etnaviv authors +Date: Thu, 3 Dec 2015 18:21:29 +0100 +Subject: [PATCH 3/4] drm/etnaviv: add initial etnaviv DRM driver + +This adds the etnaviv DRM driver and hooks it up in Makefiles +and Kconfig. + +Signed-off-by: Christian Gmeiner +Signed-off-by: Russell King +Signed-off-by: Lucas Stach +Acked-by: Daniel Vetter +--- + drivers/gpu/drm/Kconfig | 2 + + drivers/gpu/drm/Makefile | 1 + + drivers/gpu/drm/etnaviv/Kconfig | 20 + + drivers/gpu/drm/etnaviv/Makefile | 14 + + drivers/gpu/drm/etnaviv/cmdstream.xml.h | 218 ++++ + drivers/gpu/drm/etnaviv/common.xml.h | 249 ++++ + drivers/gpu/drm/etnaviv/etnaviv_buffer.c | 268 +++++ + drivers/gpu/drm/etnaviv/etnaviv_cmd_parser.c | 209 ++++ + drivers/gpu/drm/etnaviv/etnaviv_drv.c | 707 +++++++++++ + drivers/gpu/drm/etnaviv/etnaviv_drv.h | 161 +++ + drivers/gpu/drm/etnaviv/etnaviv_dump.c | 227 ++++ + drivers/gpu/drm/etnaviv/etnaviv_dump.h | 54 + + drivers/gpu/drm/etnaviv/etnaviv_gem.c | 897 ++++++++++++++ + drivers/gpu/drm/etnaviv/etnaviv_gem.h | 117 ++ + drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c | 122 ++ + drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c | 443 +++++++ + drivers/gpu/drm/etnaviv/etnaviv_gpu.c | 1644 ++++++++++++++++++++++++++ + drivers/gpu/drm/etnaviv/etnaviv_gpu.h | 209 ++++ + drivers/gpu/drm/etnaviv/etnaviv_iommu.c | 240 ++++ + drivers/gpu/drm/etnaviv/etnaviv_iommu.h | 28 + + drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c | 33 + + drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.h | 25 + + drivers/gpu/drm/etnaviv/etnaviv_mmu.c | 299 +++++ + drivers/gpu/drm/etnaviv/etnaviv_mmu.h | 71 ++ + drivers/gpu/drm/etnaviv/state.xml.h | 351 ++++++ + drivers/gpu/drm/etnaviv/state_hi.xml.h | 407 +++++++ + include/uapi/drm/etnaviv_drm.h | 222 ++++ + 27 files changed, 7238 insertions(+) + create mode 100644 drivers/gpu/drm/etnaviv/Kconfig + create mode 100644 drivers/gpu/drm/etnaviv/Makefile + create mode 100644 drivers/gpu/drm/etnaviv/cmdstream.xml.h + create mode 100644 drivers/gpu/drm/etnaviv/common.xml.h + create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_buffer.c + create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_cmd_parser.c + create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_drv.c + create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_drv.h + create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_dump.c + create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_dump.h + create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_gem.c + create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_gem.h + create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c + create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c + create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_gpu.c + create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_gpu.h + create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_iommu.c + create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_iommu.h + create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c + create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.h + create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_mmu.c + create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_mmu.h + create mode 100644 drivers/gpu/drm/etnaviv/state.xml.h + create mode 100644 drivers/gpu/drm/etnaviv/state_hi.xml.h + create mode 100644 include/uapi/drm/etnaviv_drm.h + +diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig +index c4bf9a1..b02ac62 100644 +--- a/drivers/gpu/drm/Kconfig ++++ b/drivers/gpu/drm/Kconfig +@@ -266,3 +266,5 @@ source "drivers/gpu/drm/amd/amdkfd/Kconfig" + source "drivers/gpu/drm/imx/Kconfig" + + source "drivers/gpu/drm/vc4/Kconfig" ++ ++source "drivers/gpu/drm/etnaviv/Kconfig" +diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile +index 1e9ff4c..f858aa2 100644 +--- a/drivers/gpu/drm/Makefile ++++ b/drivers/gpu/drm/Makefile +@@ -75,3 +75,4 @@ obj-y += i2c/ + obj-y += panel/ + obj-y += bridge/ + obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu/ ++obj-$(CONFIG_DRM_ETNAVIV) += etnaviv/ +diff --git a/drivers/gpu/drm/etnaviv/Kconfig b/drivers/gpu/drm/etnaviv/Kconfig +new file mode 100644 +index 0000000..2cde7a5 +--- /dev/null ++++ b/drivers/gpu/drm/etnaviv/Kconfig +@@ -0,0 +1,20 @@ ++ ++config DRM_ETNAVIV ++ tristate "ETNAVIV (DRM support for Vivante GPU IP cores)" ++ depends on DRM ++ depends on ARCH_MXC || ARCH_DOVE ++ select SHMEM ++ select TMPFS ++ select IOMMU_API ++ select IOMMU_SUPPORT ++ select WANT_DEV_COREDUMP ++ help ++ DRM driver for Vivante GPUs. ++ ++config DRM_ETNAVIV_REGISTER_LOGGING ++ bool "enable ETNAVIV register logging" ++ depends on DRM_ETNAVIV ++ help ++ Compile in support for logging register reads/writes in a format ++ that can be parsed by envytools demsm tool. If enabled, register ++ logging can be switched on via etnaviv.reglog=y module param. +diff --git a/drivers/gpu/drm/etnaviv/Makefile b/drivers/gpu/drm/etnaviv/Makefile +new file mode 100644 +index 0000000..1086e98 +--- /dev/null ++++ b/drivers/gpu/drm/etnaviv/Makefile +@@ -0,0 +1,14 @@ ++etnaviv-y := \ ++ etnaviv_buffer.o \ ++ etnaviv_cmd_parser.o \ ++ etnaviv_drv.o \ ++ etnaviv_dump.o \ ++ etnaviv_gem_prime.o \ ++ etnaviv_gem_submit.o \ ++ etnaviv_gem.o \ ++ etnaviv_gpu.o \ ++ etnaviv_iommu_v2.o \ ++ etnaviv_iommu.o \ ++ etnaviv_mmu.o ++ ++obj-$(CONFIG_DRM_ETNAVIV) += etnaviv.o +diff --git a/drivers/gpu/drm/etnaviv/cmdstream.xml.h b/drivers/gpu/drm/etnaviv/cmdstream.xml.h +new file mode 100644 +index 0000000..8c44ba9 +--- /dev/null ++++ b/drivers/gpu/drm/etnaviv/cmdstream.xml.h +@@ -0,0 +1,218 @@ ++#ifndef CMDSTREAM_XML ++#define CMDSTREAM_XML ++ ++/* Autogenerated file, DO NOT EDIT manually! ++ ++This file was generated by the rules-ng-ng headergen tool in this git repository: ++http://0x04.net/cgit/index.cgi/rules-ng-ng ++git clone git://0x04.net/rules-ng-ng ++ ++The rules-ng-ng source files this header was generated from are: ++- cmdstream.xml ( 12589 bytes, from 2014-02-17 14:57:56) ++- common.xml ( 18437 bytes, from 2015-03-25 11:27:41) ++ ++Copyright (C) 2014 ++*/ ++ ++ ++#define FE_OPCODE_LOAD_STATE 0x00000001 ++#define FE_OPCODE_END 0x00000002 ++#define FE_OPCODE_NOP 0x00000003 ++#define FE_OPCODE_DRAW_2D 0x00000004 ++#define FE_OPCODE_DRAW_PRIMITIVES 0x00000005 ++#define FE_OPCODE_DRAW_INDEXED_PRIMITIVES 0x00000006 ++#define FE_OPCODE_WAIT 0x00000007 ++#define FE_OPCODE_LINK 0x00000008 ++#define FE_OPCODE_STALL 0x00000009 ++#define FE_OPCODE_CALL 0x0000000a ++#define FE_OPCODE_RETURN 0x0000000b ++#define FE_OPCODE_CHIP_SELECT 0x0000000d ++#define PRIMITIVE_TYPE_POINTS 0x00000001 ++#define PRIMITIVE_TYPE_LINES 0x00000002 ++#define PRIMITIVE_TYPE_LINE_STRIP 0x00000003 ++#define PRIMITIVE_TYPE_TRIANGLES 0x00000004 ++#define PRIMITIVE_TYPE_TRIANGLE_STRIP 0x00000005 ++#define PRIMITIVE_TYPE_TRIANGLE_FAN 0x00000006 ++#define PRIMITIVE_TYPE_LINE_LOOP 0x00000007 ++#define PRIMITIVE_TYPE_QUADS 0x00000008 ++#define VIV_FE_LOAD_STATE 0x00000000 ++ ++#define VIV_FE_LOAD_STATE_HEADER 0x00000000 ++#define VIV_FE_LOAD_STATE_HEADER_OP__MASK 0xf8000000 ++#define VIV_FE_LOAD_STATE_HEADER_OP__SHIFT 27 ++#define VIV_FE_LOAD_STATE_HEADER_OP_LOAD_STATE 0x08000000 ++#define VIV_FE_LOAD_STATE_HEADER_FIXP 0x04000000 ++#define VIV_FE_LOAD_STATE_HEADER_COUNT__MASK 0x03ff0000 ++#define VIV_FE_LOAD_STATE_HEADER_COUNT__SHIFT 16 ++#define VIV_FE_LOAD_STATE_HEADER_COUNT(x) (((x) << VIV_FE_LOAD_STATE_HEADER_COUNT__SHIFT) & VIV_FE_LOAD_STATE_HEADER_COUNT__MASK) ++#define VIV_FE_LOAD_STATE_HEADER_OFFSET__MASK 0x0000ffff ++#define VIV_FE_LOAD_STATE_HEADER_OFFSET__SHIFT 0 ++#define VIV_FE_LOAD_STATE_HEADER_OFFSET(x) (((x) << VIV_FE_LOAD_STATE_HEADER_OFFSET__SHIFT) & VIV_FE_LOAD_STATE_HEADER_OFFSET__MASK) ++#define VIV_FE_LOAD_STATE_HEADER_OFFSET__SHR 2 ++ ++#define VIV_FE_END 0x00000000 ++ ++#define VIV_FE_END_HEADER 0x00000000 ++#define VIV_FE_END_HEADER_EVENT_ID__MASK 0x0000001f ++#define VIV_FE_END_HEADER_EVENT_ID__SHIFT 0 ++#define VIV_FE_END_HEADER_EVENT_ID(x) (((x) << VIV_FE_END_HEADER_EVENT_ID__SHIFT) & VIV_FE_END_HEADER_EVENT_ID__MASK) ++#define VIV_FE_END_HEADER_EVENT_ENABLE 0x00000100 ++#define VIV_FE_END_HEADER_OP__MASK 0xf8000000 ++#define VIV_FE_END_HEADER_OP__SHIFT 27 ++#define VIV_FE_END_HEADER_OP_END 0x10000000 ++ ++#define VIV_FE_NOP 0x00000000 ++ ++#define VIV_FE_NOP_HEADER 0x00000000 ++#define VIV_FE_NOP_HEADER_OP__MASK 0xf8000000 ++#define VIV_FE_NOP_HEADER_OP__SHIFT 27 ++#define VIV_FE_NOP_HEADER_OP_NOP 0x18000000 ++ ++#define VIV_FE_DRAW_2D 0x00000000 ++ ++#define VIV_FE_DRAW_2D_HEADER 0x00000000 ++#define VIV_FE_DRAW_2D_HEADER_COUNT__MASK 0x0000ff00 ++#define VIV_FE_DRAW_2D_HEADER_COUNT__SHIFT 8 ++#define VIV_FE_DRAW_2D_HEADER_COUNT(x) (((x) << VIV_FE_DRAW_2D_HEADER_COUNT__SHIFT) & VIV_FE_DRAW_2D_HEADER_COUNT__MASK) ++#define VIV_FE_DRAW_2D_HEADER_DATA_COUNT__MASK 0x07ff0000 ++#define VIV_FE_DRAW_2D_HEADER_DATA_COUNT__SHIFT 16 ++#define VIV_FE_DRAW_2D_HEADER_DATA_COUNT(x) (((x) << VIV_FE_DRAW_2D_HEADER_DATA_COUNT__SHIFT) & VIV_FE_DRAW_2D_HEADER_DATA_COUNT__MASK) ++#define VIV_FE_DRAW_2D_HEADER_OP__MASK 0xf8000000 ++#define VIV_FE_DRAW_2D_HEADER_OP__SHIFT 27 ++#define VIV_FE_DRAW_2D_HEADER_OP_DRAW_2D 0x20000000 ++ ++#define VIV_FE_DRAW_2D_TOP_LEFT 0x00000008 ++#define VIV_FE_DRAW_2D_TOP_LEFT_X__MASK 0x0000ffff ++#define VIV_FE_DRAW_2D_TOP_LEFT_X__SHIFT 0 ++#define VIV_FE_DRAW_2D_TOP_LEFT_X(x) (((x) << VIV_FE_DRAW_2D_TOP_LEFT_X__SHIFT) & VIV_FE_DRAW_2D_TOP_LEFT_X__MASK) ++#define VIV_FE_DRAW_2D_TOP_LEFT_Y__MASK 0xffff0000 ++#define VIV_FE_DRAW_2D_TOP_LEFT_Y__SHIFT 16 ++#define VIV_FE_DRAW_2D_TOP_LEFT_Y(x) (((x) << VIV_FE_DRAW_2D_TOP_LEFT_Y__SHIFT) & VIV_FE_DRAW_2D_TOP_LEFT_Y__MASK) ++ ++#define VIV_FE_DRAW_2D_BOTTOM_RIGHT 0x0000000c ++#define VIV_FE_DRAW_2D_BOTTOM_RIGHT_X__MASK 0x0000ffff ++#define VIV_FE_DRAW_2D_BOTTOM_RIGHT_X__SHIFT 0 ++#define VIV_FE_DRAW_2D_BOTTOM_RIGHT_X(x) (((x) << VIV_FE_DRAW_2D_BOTTOM_RIGHT_X__SHIFT) & VIV_FE_DRAW_2D_BOTTOM_RIGHT_X__MASK) ++#define VIV_FE_DRAW_2D_BOTTOM_RIGHT_Y__MASK 0xffff0000 ++#define VIV_FE_DRAW_2D_BOTTOM_RIGHT_Y__SHIFT 16 ++#define VIV_FE_DRAW_2D_BOTTOM_RIGHT_Y(x) (((x) << VIV_FE_DRAW_2D_BOTTOM_RIGHT_Y__SHIFT) & VIV_FE_DRAW_2D_BOTTOM_RIGHT_Y__MASK) ++ ++#define VIV_FE_DRAW_PRIMITIVES 0x00000000 ++ ++#define VIV_FE_DRAW_PRIMITIVES_HEADER 0x00000000 ++#define VIV_FE_DRAW_PRIMITIVES_HEADER_OP__MASK 0xf8000000 ++#define VIV_FE_DRAW_PRIMITIVES_HEADER_OP__SHIFT 27 ++#define VIV_FE_DRAW_PRIMITIVES_HEADER_OP_DRAW_PRIMITIVES 0x28000000 ++ ++#define VIV_FE_DRAW_PRIMITIVES_COMMAND 0x00000004 ++#define VIV_FE_DRAW_PRIMITIVES_COMMAND_TYPE__MASK 0x000000ff ++#define VIV_FE_DRAW_PRIMITIVES_COMMAND_TYPE__SHIFT 0 ++#define VIV_FE_DRAW_PRIMITIVES_COMMAND_TYPE(x) (((x) << VIV_FE_DRAW_PRIMITIVES_COMMAND_TYPE__SHIFT) & VIV_FE_DRAW_PRIMITIVES_COMMAND_TYPE__MASK) ++ ++#define VIV_FE_DRAW_PRIMITIVES_START 0x00000008 ++ ++#define VIV_FE_DRAW_PRIMITIVES_COUNT 0x0000000c ++ ++#define VIV_FE_DRAW_INDEXED_PRIMITIVES 0x00000000 ++ ++#define VIV_FE_DRAW_INDEXED_PRIMITIVES_HEADER 0x00000000 ++#define VIV_FE_DRAW_INDEXED_PRIMITIVES_HEADER_OP__MASK 0xf8000000 ++#define VIV_FE_DRAW_INDEXED_PRIMITIVES_HEADER_OP__SHIFT 27 ++#define VIV_FE_DRAW_INDEXED_PRIMITIVES_HEADER_OP_DRAW_INDEXED_PRIMITIVES 0x30000000 ++ ++#define VIV_FE_DRAW_INDEXED_PRIMITIVES_COMMAND 0x00000004 ++#define VIV_FE_DRAW_INDEXED_PRIMITIVES_COMMAND_TYPE__MASK 0x000000ff ++#define VIV_FE_DRAW_INDEXED_PRIMITIVES_COMMAND_TYPE__SHIFT 0 ++#define VIV_FE_DRAW_INDEXED_PRIMITIVES_COMMAND_TYPE(x) (((x) << VIV_FE_DRAW_INDEXED_PRIMITIVES_COMMAND_TYPE__SHIFT) & VIV_FE_DRAW_INDEXED_PRIMITIVES_COMMAND_TYPE__MASK) ++ ++#define VIV_FE_DRAW_INDEXED_PRIMITIVES_START 0x00000008 ++ ++#define VIV_FE_DRAW_INDEXED_PRIMITIVES_COUNT 0x0000000c ++ ++#define VIV_FE_DRAW_INDEXED_PRIMITIVES_OFFSET 0x00000010 ++ ++#define VIV_FE_WAIT 0x00000000 ++ ++#define VIV_FE_WAIT_HEADER 0x00000000 ++#define VIV_FE_WAIT_HEADER_DELAY__MASK 0x0000ffff ++#define VIV_FE_WAIT_HEADER_DELAY__SHIFT 0 ++#define VIV_FE_WAIT_HEADER_DELAY(x) (((x) << VIV_FE_WAIT_HEADER_DELAY__SHIFT) & VIV_FE_WAIT_HEADER_DELAY__MASK) ++#define VIV_FE_WAIT_HEADER_OP__MASK 0xf8000000 ++#define VIV_FE_WAIT_HEADER_OP__SHIFT 27 ++#define VIV_FE_WAIT_HEADER_OP_WAIT 0x38000000 ++ ++#define VIV_FE_LINK 0x00000000 ++ ++#define VIV_FE_LINK_HEADER 0x00000000 ++#define VIV_FE_LINK_HEADER_PREFETCH__MASK 0x0000ffff ++#define VIV_FE_LINK_HEADER_PREFETCH__SHIFT 0 ++#define VIV_FE_LINK_HEADER_PREFETCH(x) (((x) << VIV_FE_LINK_HEADER_PREFETCH__SHIFT) & VIV_FE_LINK_HEADER_PREFETCH__MASK) ++#define VIV_FE_LINK_HEADER_OP__MASK 0xf8000000 ++#define VIV_FE_LINK_HEADER_OP__SHIFT 27 ++#define VIV_FE_LINK_HEADER_OP_LINK 0x40000000 ++ ++#define VIV_FE_LINK_ADDRESS 0x00000004 ++ ++#define VIV_FE_STALL 0x00000000 ++ ++#define VIV_FE_STALL_HEADER 0x00000000 ++#define VIV_FE_STALL_HEADER_OP__MASK 0xf8000000 ++#define VIV_FE_STALL_HEADER_OP__SHIFT 27 ++#define VIV_FE_STALL_HEADER_OP_STALL 0x48000000 ++ ++#define VIV_FE_STALL_TOKEN 0x00000004 ++#define VIV_FE_STALL_TOKEN_FROM__MASK 0x0000001f ++#define VIV_FE_STALL_TOKEN_FROM__SHIFT 0 ++#define VIV_FE_STALL_TOKEN_FROM(x) (((x) << VIV_FE_STALL_TOKEN_FROM__SHIFT) & VIV_FE_STALL_TOKEN_FROM__MASK) ++#define VIV_FE_STALL_TOKEN_TO__MASK 0x00001f00 ++#define VIV_FE_STALL_TOKEN_TO__SHIFT 8 ++#define VIV_FE_STALL_TOKEN_TO(x) (((x) << VIV_FE_STALL_TOKEN_TO__SHIFT) & VIV_FE_STALL_TOKEN_TO__MASK) ++ ++#define VIV_FE_CALL 0x00000000 ++ ++#define VIV_FE_CALL_HEADER 0x00000000 ++#define VIV_FE_CALL_HEADER_PREFETCH__MASK 0x0000ffff ++#define VIV_FE_CALL_HEADER_PREFETCH__SHIFT 0 ++#define VIV_FE_CALL_HEADER_PREFETCH(x) (((x) << VIV_FE_CALL_HEADER_PREFETCH__SHIFT) & VIV_FE_CALL_HEADER_PREFETCH__MASK) ++#define VIV_FE_CALL_HEADER_OP__MASK 0xf8000000 ++#define VIV_FE_CALL_HEADER_OP__SHIFT 27 ++#define VIV_FE_CALL_HEADER_OP_CALL 0x50000000 ++ ++#define VIV_FE_CALL_ADDRESS 0x00000004 ++ ++#define VIV_FE_CALL_RETURN_PREFETCH 0x00000008 ++ ++#define VIV_FE_CALL_RETURN_ADDRESS 0x0000000c ++ ++#define VIV_FE_RETURN 0x00000000 ++ ++#define VIV_FE_RETURN_HEADER 0x00000000 ++#define VIV_FE_RETURN_HEADER_OP__MASK 0xf8000000 ++#define VIV_FE_RETURN_HEADER_OP__SHIFT 27 ++#define VIV_FE_RETURN_HEADER_OP_RETURN 0x58000000 ++ ++#define VIV_FE_CHIP_SELECT 0x00000000 ++ ++#define VIV_FE_CHIP_SELECT_HEADER 0x00000000 ++#define VIV_FE_CHIP_SELECT_HEADER_OP__MASK 0xf8000000 ++#define VIV_FE_CHIP_SELECT_HEADER_OP__SHIFT 27 ++#define VIV_FE_CHIP_SELECT_HEADER_OP_CHIP_SELECT 0x68000000 ++#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP15 0x00008000 ++#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP14 0x00004000 ++#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP13 0x00002000 ++#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP12 0x00001000 ++#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP11 0x00000800 ++#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP10 0x00000400 ++#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP9 0x00000200 ++#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP8 0x00000100 ++#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP7 0x00000080 ++#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP6 0x00000040 ++#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP5 0x00000020 ++#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP4 0x00000010 ++#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP3 0x00000008 ++#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP2 0x00000004 ++#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP1 0x00000002 ++#define VIV_FE_CHIP_SELECT_HEADER_ENABLE_CHIP0 0x00000001 ++ ++ ++#endif /* CMDSTREAM_XML */ +diff --git a/drivers/gpu/drm/etnaviv/common.xml.h b/drivers/gpu/drm/etnaviv/common.xml.h +new file mode 100644 +index 0000000..9e585d5 +--- /dev/null ++++ b/drivers/gpu/drm/etnaviv/common.xml.h +@@ -0,0 +1,249 @@ ++#ifndef COMMON_XML ++#define COMMON_XML ++ ++/* Autogenerated file, DO NOT EDIT manually! ++ ++This file was generated by the rules-ng-ng headergen tool in this git repository: ++http://0x04.net/cgit/index.cgi/rules-ng-ng ++git clone git://0x04.net/rules-ng-ng ++ ++The rules-ng-ng source files this header was generated from are: ++- state_vg.xml ( 5973 bytes, from 2015-03-25 11:26:01) ++- common.xml ( 18437 bytes, from 2015-03-25 11:27:41) ++ ++Copyright (C) 2015 ++*/ ++ ++ ++#define PIPE_ID_PIPE_3D 0x00000000 ++#define PIPE_ID_PIPE_2D 0x00000001 ++#define SYNC_RECIPIENT_FE 0x00000001 ++#define SYNC_RECIPIENT_RA 0x00000005 ++#define SYNC_RECIPIENT_PE 0x00000007 ++#define SYNC_RECIPIENT_DE 0x0000000b ++#define SYNC_RECIPIENT_VG 0x0000000f ++#define SYNC_RECIPIENT_TESSELATOR 0x00000010 ++#define SYNC_RECIPIENT_VG2 0x00000011 ++#define SYNC_RECIPIENT_TESSELATOR2 0x00000012 ++#define SYNC_RECIPIENT_VG3 0x00000013 ++#define SYNC_RECIPIENT_TESSELATOR3 0x00000014 ++#define ENDIAN_MODE_NO_SWAP 0x00000000 ++#define ENDIAN_MODE_SWAP_16 0x00000001 ++#define ENDIAN_MODE_SWAP_32 0x00000002 ++#define chipModel_GC300 0x00000300 ++#define chipModel_GC320 0x00000320 ++#define chipModel_GC350 0x00000350 ++#define chipModel_GC355 0x00000355 ++#define chipModel_GC400 0x00000400 ++#define chipModel_GC410 0x00000410 ++#define chipModel_GC420 0x00000420 ++#define chipModel_GC450 0x00000450 ++#define chipModel_GC500 0x00000500 ++#define chipModel_GC530 0x00000530 ++#define chipModel_GC600 0x00000600 ++#define chipModel_GC700 0x00000700 ++#define chipModel_GC800 0x00000800 ++#define chipModel_GC860 0x00000860 ++#define chipModel_GC880 0x00000880 ++#define chipModel_GC1000 0x00001000 ++#define chipModel_GC2000 0x00002000 ++#define chipModel_GC2100 0x00002100 ++#define chipModel_GC4000 0x00004000 ++#define RGBA_BITS_R 0x00000001 ++#define RGBA_BITS_G 0x00000002 ++#define RGBA_BITS_B 0x00000004 ++#define RGBA_BITS_A 0x00000008 ++#define chipFeatures_FAST_CLEAR 0x00000001 ++#define chipFeatures_SPECIAL_ANTI_ALIASING 0x00000002 ++#define chipFeatures_PIPE_3D 0x00000004 ++#define chipFeatures_DXT_TEXTURE_COMPRESSION 0x00000008 ++#define chipFeatures_DEBUG_MODE 0x00000010 ++#define chipFeatures_Z_COMPRESSION 0x00000020 ++#define chipFeatures_YUV420_SCALER 0x00000040 ++#define chipFeatures_MSAA 0x00000080 ++#define chipFeatures_DC 0x00000100 ++#define chipFeatures_PIPE_2D 0x00000200 ++#define chipFeatures_ETC1_TEXTURE_COMPRESSION 0x00000400 ++#define chipFeatures_FAST_SCALER 0x00000800 ++#define chipFeatures_HIGH_DYNAMIC_RANGE 0x00001000 ++#define chipFeatures_YUV420_TILER 0x00002000 ++#define chipFeatures_MODULE_CG 0x00004000 ++#define chipFeatures_MIN_AREA 0x00008000 ++#define chipFeatures_NO_EARLY_Z 0x00010000 ++#define chipFeatures_NO_422_TEXTURE 0x00020000 ++#define chipFeatures_BUFFER_INTERLEAVING 0x00040000 ++#define chipFeatures_BYTE_WRITE_2D 0x00080000 ++#define chipFeatures_NO_SCALER 0x00100000 ++#define chipFeatures_YUY2_AVERAGING 0x00200000 ++#define chipFeatures_HALF_PE_CACHE 0x00400000 ++#define chipFeatures_HALF_TX_CACHE 0x00800000 ++#define chipFeatures_YUY2_RENDER_TARGET 0x01000000 ++#define chipFeatures_MEM32 0x02000000 ++#define chipFeatures_PIPE_VG 0x04000000 ++#define chipFeatures_VGTS 0x08000000 ++#define chipFeatures_FE20 0x10000000 ++#define chipFeatures_BYTE_WRITE_3D 0x20000000 ++#define chipFeatures_RS_YUV_TARGET 0x40000000 ++#define chipFeatures_32_BIT_INDICES 0x80000000 ++#define chipMinorFeatures0_FLIP_Y 0x00000001 ++#define chipMinorFeatures0_DUAL_RETURN_BUS 0x00000002 ++#define chipMinorFeatures0_ENDIANNESS_CONFIG 0x00000004 ++#define chipMinorFeatures0_TEXTURE_8K 0x00000008 ++#define chipMinorFeatures0_CORRECT_TEXTURE_CONVERTER 0x00000010 ++#define chipMinorFeatures0_SPECIAL_MSAA_LOD 0x00000020 ++#define chipMinorFeatures0_FAST_CLEAR_FLUSH 0x00000040 ++#define chipMinorFeatures0_2DPE20 0x00000080 ++#define chipMinorFeatures0_CORRECT_AUTO_DISABLE 0x00000100 ++#define chipMinorFeatures0_RENDERTARGET_8K 0x00000200 ++#define chipMinorFeatures0_2BITPERTILE 0x00000400 ++#define chipMinorFeatures0_SEPARATE_TILE_STATUS_WHEN_INTERLEAVED 0x00000800 ++#define chipMinorFeatures0_SUPER_TILED 0x00001000 ++#define chipMinorFeatures0_VG_20 0x00002000 ++#define chipMinorFeatures0_TS_EXTENDED_COMMANDS 0x00004000 ++#define chipMinorFeatures0_COMPRESSION_FIFO_FIXED 0x00008000 ++#define chipMinorFeatures0_HAS_SIGN_FLOOR_CEIL 0x00010000 ++#define chipMinorFeatures0_VG_FILTER 0x00020000 ++#define chipMinorFeatures0_VG_21 0x00040000 ++#define chipMinorFeatures0_SHADER_HAS_W 0x00080000 ++#define chipMinorFeatures0_HAS_SQRT_TRIG 0x00100000 ++#define chipMinorFeatures0_MORE_MINOR_FEATURES 0x00200000 ++#define chipMinorFeatures0_MC20 0x00400000 ++#define chipMinorFeatures0_MSAA_SIDEBAND 0x00800000 ++#define chipMinorFeatures0_BUG_FIXES0 0x01000000 ++#define chipMinorFeatures0_VAA 0x02000000 ++#define chipMinorFeatures0_BYPASS_IN_MSAA 0x04000000 ++#define chipMinorFeatures0_HZ 0x08000000 ++#define chipMinorFeatures0_NEW_TEXTURE 0x10000000 ++#define chipMinorFeatures0_2D_A8_TARGET 0x20000000 ++#define chipMinorFeatures0_CORRECT_STENCIL 0x40000000 ++#define chipMinorFeatures0_ENHANCE_VR 0x80000000 ++#define chipMinorFeatures1_RSUV_SWIZZLE 0x00000001 ++#define chipMinorFeatures1_V2_COMPRESSION 0x00000002 ++#define chipMinorFeatures1_VG_DOUBLE_BUFFER 0x00000004 ++#define chipMinorFeatures1_EXTRA_EVENT_STATES 0x00000008 ++#define chipMinorFeatures1_NO_STRIPING_NEEDED 0x00000010 ++#define chipMinorFeatures1_TEXTURE_STRIDE 0x00000020 ++#define chipMinorFeatures1_BUG_FIXES3 0x00000040 ++#define chipMinorFeatures1_AUTO_DISABLE 0x00000080 ++#define chipMinorFeatures1_AUTO_RESTART_TS 0x00000100 ++#define chipMinorFeatures1_DISABLE_PE_GATING 0x00000200 ++#define chipMinorFeatures1_L2_WINDOWING 0x00000400 ++#define chipMinorFeatures1_HALF_FLOAT 0x00000800 ++#define chipMinorFeatures1_PIXEL_DITHER 0x00001000 ++#define chipMinorFeatures1_TWO_STENCIL_REFERENCE 0x00002000 ++#define chipMinorFeatures1_EXTENDED_PIXEL_FORMAT 0x00004000 ++#define chipMinorFeatures1_CORRECT_MIN_MAX_DEPTH 0x00008000 ++#define chipMinorFeatures1_2D_DITHER 0x00010000 ++#define chipMinorFeatures1_BUG_FIXES5 0x00020000 ++#define chipMinorFeatures1_NEW_2D 0x00040000 ++#define chipMinorFeatures1_NEW_FP 0x00080000 ++#define chipMinorFeatures1_TEXTURE_HALIGN 0x00100000 ++#define chipMinorFeatures1_NON_POWER_OF_TWO 0x00200000 ++#define chipMinorFeatures1_LINEAR_TEXTURE_SUPPORT 0x00400000 ++#define chipMinorFeatures1_HALTI0 0x00800000 ++#define chipMinorFeatures1_CORRECT_OVERFLOW_VG 0x01000000 ++#define chipMinorFeatures1_NEGATIVE_LOG_FIX 0x02000000 ++#define chipMinorFeatures1_RESOLVE_OFFSET 0x04000000 ++#define chipMinorFeatures1_OK_TO_GATE_AXI_CLOCK 0x08000000 ++#define chipMinorFeatures1_MMU_VERSION 0x10000000 ++#define chipMinorFeatures1_WIDE_LINE 0x20000000 ++#define chipMinorFeatures1_BUG_FIXES6 0x40000000 ++#define chipMinorFeatures1_FC_FLUSH_STALL 0x80000000 ++#define chipMinorFeatures2_LINE_LOOP 0x00000001 ++#define chipMinorFeatures2_LOGIC_OP 0x00000002 ++#define chipMinorFeatures2_UNK2 0x00000004 ++#define chipMinorFeatures2_SUPERTILED_TEXTURE 0x00000008 ++#define chipMinorFeatures2_UNK4 0x00000010 ++#define chipMinorFeatures2_RECT_PRIMITIVE 0x00000020 ++#define chipMinorFeatures2_COMPOSITION 0x00000040 ++#define chipMinorFeatures2_CORRECT_AUTO_DISABLE_COUNT 0x00000080 ++#define chipMinorFeatures2_UNK8 0x00000100 ++#define chipMinorFeatures2_UNK9 0x00000200 ++#define chipMinorFeatures2_UNK10 0x00000400 ++#define chipMinorFeatures2_SAMPLERBASE_16 0x00000800 ++#define chipMinorFeatures2_UNK12 0x00001000 ++#define chipMinorFeatures2_UNK13 0x00002000 ++#define chipMinorFeatures2_UNK14 0x00004000 ++#define chipMinorFeatures2_EXTRA_TEXTURE_STATE 0x00008000 ++#define chipMinorFeatures2_FULL_DIRECTFB 0x00010000 ++#define chipMinorFeatures2_2D_TILING 0x00020000 ++#define chipMinorFeatures2_THREAD_WALKER_IN_PS 0x00040000 ++#define chipMinorFeatures2_TILE_FILLER 0x00080000 ++#define chipMinorFeatures2_UNK20 0x00100000 ++#define chipMinorFeatures2_2D_MULTI_SOURCE_BLIT 0x00200000 ++#define chipMinorFeatures2_UNK22 0x00400000 ++#define chipMinorFeatures2_UNK23 0x00800000 ++#define chipMinorFeatures2_UNK24 0x01000000 ++#define chipMinorFeatures2_MIXED_STREAMS 0x02000000 ++#define chipMinorFeatures2_2D_420_L2CACHE 0x04000000 ++#define chipMinorFeatures2_UNK27 0x08000000 ++#define chipMinorFeatures2_2D_NO_INDEX8_BRUSH 0x10000000 ++#define chipMinorFeatures2_TEXTURE_TILED_READ 0x20000000 ++#define chipMinorFeatures2_UNK30 0x40000000 ++#define chipMinorFeatures2_UNK31 0x80000000 ++#define chipMinorFeatures3_ROTATION_STALL_FIX 0x00000001 ++#define chipMinorFeatures3_UNK1 0x00000002 ++#define chipMinorFeatures3_2D_MULTI_SOURCE_BLT_EX 0x00000004 ++#define chipMinorFeatures3_UNK3 0x00000008 ++#define chipMinorFeatures3_UNK4 0x00000010 ++#define chipMinorFeatures3_UNK5 0x00000020 ++#define chipMinorFeatures3_UNK6 0x00000040 ++#define chipMinorFeatures3_UNK7 0x00000080 ++#define chipMinorFeatures3_UNK8 0x00000100 ++#define chipMinorFeatures3_UNK9 0x00000200 ++#define chipMinorFeatures3_BUG_FIXES10 0x00000400 ++#define chipMinorFeatures3_UNK11 0x00000800 ++#define chipMinorFeatures3_BUG_FIXES11 0x00001000 ++#define chipMinorFeatures3_UNK13 0x00002000 ++#define chipMinorFeatures3_UNK14 0x00004000 ++#define chipMinorFeatures3_UNK15 0x00008000 ++#define chipMinorFeatures3_UNK16 0x00010000 ++#define chipMinorFeatures3_UNK17 0x00020000 ++#define chipMinorFeatures3_UNK18 0x00040000 ++#define chipMinorFeatures3_UNK19 0x00080000 ++#define chipMinorFeatures3_UNK20 0x00100000 ++#define chipMinorFeatures3_UNK21 0x00200000 ++#define chipMinorFeatures3_UNK22 0x00400000 ++#define chipMinorFeatures3_UNK23 0x00800000 ++#define chipMinorFeatures3_UNK24 0x01000000 ++#define chipMinorFeatures3_UNK25 0x02000000 ++#define chipMinorFeatures3_UNK26 0x04000000 ++#define chipMinorFeatures3_UNK27 0x08000000 ++#define chipMinorFeatures3_UNK28 0x10000000 ++#define chipMinorFeatures3_UNK29 0x20000000 ++#define chipMinorFeatures3_UNK30 0x40000000 ++#define chipMinorFeatures3_UNK31 0x80000000 ++#define chipMinorFeatures4_UNK0 0x00000001 ++#define chipMinorFeatures4_UNK1 0x00000002 ++#define chipMinorFeatures4_UNK2 0x00000004 ++#define chipMinorFeatures4_UNK3 0x00000008 ++#define chipMinorFeatures4_UNK4 0x00000010 ++#define chipMinorFeatures4_UNK5 0x00000020 ++#define chipMinorFeatures4_UNK6 0x00000040 ++#define chipMinorFeatures4_UNK7 0x00000080 ++#define chipMinorFeatures4_UNK8 0x00000100 ++#define chipMinorFeatures4_UNK9 0x00000200 ++#define chipMinorFeatures4_UNK10 0x00000400 ++#define chipMinorFeatures4_UNK11 0x00000800 ++#define chipMinorFeatures4_UNK12 0x00001000 ++#define chipMinorFeatures4_UNK13 0x00002000 ++#define chipMinorFeatures4_UNK14 0x00004000 ++#define chipMinorFeatures4_UNK15 0x00008000 ++#define chipMinorFeatures4_UNK16 0x00010000 ++#define chipMinorFeatures4_UNK17 0x00020000 ++#define chipMinorFeatures4_UNK18 0x00040000 ++#define chipMinorFeatures4_UNK19 0x00080000 ++#define chipMinorFeatures4_UNK20 0x00100000 ++#define chipMinorFeatures4_UNK21 0x00200000 ++#define chipMinorFeatures4_UNK22 0x00400000 ++#define chipMinorFeatures4_UNK23 0x00800000 ++#define chipMinorFeatures4_UNK24 0x01000000 ++#define chipMinorFeatures4_UNK25 0x02000000 ++#define chipMinorFeatures4_UNK26 0x04000000 ++#define chipMinorFeatures4_UNK27 0x08000000 ++#define chipMinorFeatures4_UNK28 0x10000000 ++#define chipMinorFeatures4_UNK29 0x20000000 ++#define chipMinorFeatures4_UNK30 0x40000000 ++#define chipMinorFeatures4_UNK31 0x80000000 ++ ++#endif /* COMMON_XML */ +diff --git a/drivers/gpu/drm/etnaviv/etnaviv_buffer.c b/drivers/gpu/drm/etnaviv/etnaviv_buffer.c +new file mode 100644 +index 0000000..332c55e +--- /dev/null ++++ b/drivers/gpu/drm/etnaviv/etnaviv_buffer.c +@@ -0,0 +1,268 @@ ++/* ++ * Copyright (C) 2014 Etnaviv Project ++ * Author: Christian Gmeiner ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ */ ++ ++#include "etnaviv_gpu.h" ++#include "etnaviv_gem.h" ++#include "etnaviv_mmu.h" ++ ++#include "common.xml.h" ++#include "state.xml.h" ++#include "cmdstream.xml.h" ++ ++/* ++ * Command Buffer helper: ++ */ ++ ++ ++static inline void OUT(struct etnaviv_cmdbuf *buffer, u32 data) ++{ ++ u32 *vaddr = (u32 *)buffer->vaddr; ++ ++ BUG_ON(buffer->user_size >= buffer->size); ++ ++ vaddr[buffer->user_size / 4] = data; ++ buffer->user_size += 4; ++} ++ ++static inline void CMD_LOAD_STATE(struct etnaviv_cmdbuf *buffer, ++ u32 reg, u32 value) ++{ ++ u32 index = reg >> VIV_FE_LOAD_STATE_HEADER_OFFSET__SHR; ++ ++ buffer->user_size = ALIGN(buffer->user_size, 8); ++ ++ /* write a register via cmd stream */ ++ OUT(buffer, VIV_FE_LOAD_STATE_HEADER_OP_LOAD_STATE | ++ VIV_FE_LOAD_STATE_HEADER_COUNT(1) | ++ VIV_FE_LOAD_STATE_HEADER_OFFSET(index)); ++ OUT(buffer, value); ++} ++ ++static inline void CMD_END(struct etnaviv_cmdbuf *buffer) ++{ ++ buffer->user_size = ALIGN(buffer->user_size, 8); ++ ++ OUT(buffer, VIV_FE_END_HEADER_OP_END); ++} ++ ++static inline void CMD_WAIT(struct etnaviv_cmdbuf *buffer) ++{ ++ buffer->user_size = ALIGN(buffer->user_size, 8); ++ ++ OUT(buffer, VIV_FE_WAIT_HEADER_OP_WAIT | 200); ++} ++ ++static inline void CMD_LINK(struct etnaviv_cmdbuf *buffer, ++ u16 prefetch, u32 address) ++{ ++ buffer->user_size = ALIGN(buffer->user_size, 8); ++ ++ OUT(buffer, VIV_FE_LINK_HEADER_OP_LINK | ++ VIV_FE_LINK_HEADER_PREFETCH(prefetch)); ++ OUT(buffer, address); ++} ++ ++static inline void CMD_STALL(struct etnaviv_cmdbuf *buffer, ++ u32 from, u32 to) ++{ ++ buffer->user_size = ALIGN(buffer->user_size, 8); ++ ++ OUT(buffer, VIV_FE_STALL_HEADER_OP_STALL); ++ OUT(buffer, VIV_FE_STALL_TOKEN_FROM(from) | VIV_FE_STALL_TOKEN_TO(to)); ++} ++ ++static void etnaviv_cmd_select_pipe(struct etnaviv_cmdbuf *buffer, u8 pipe) ++{ ++ u32 flush; ++ u32 stall; ++ ++ /* ++ * This assumes that if we're switching to 2D, we're switching ++ * away from 3D, and vice versa. Hence, if we're switching to ++ * the 2D core, we need to flush the 3D depth and color caches, ++ * otherwise we need to flush the 2D pixel engine cache. ++ */ ++ if (pipe == ETNA_PIPE_2D) ++ flush = VIVS_GL_FLUSH_CACHE_DEPTH | VIVS_GL_FLUSH_CACHE_COLOR; ++ else ++ flush = VIVS_GL_FLUSH_CACHE_PE2D; ++ ++ stall = VIVS_GL_SEMAPHORE_TOKEN_FROM(SYNC_RECIPIENT_FE) | ++ VIVS_GL_SEMAPHORE_TOKEN_TO(SYNC_RECIPIENT_PE); ++ ++ CMD_LOAD_STATE(buffer, VIVS_GL_FLUSH_CACHE, flush); ++ CMD_LOAD_STATE(buffer, VIVS_GL_SEMAPHORE_TOKEN, stall); ++ ++ CMD_STALL(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE); ++ ++ CMD_LOAD_STATE(buffer, VIVS_GL_PIPE_SELECT, ++ VIVS_GL_PIPE_SELECT_PIPE(pipe)); ++} ++ ++static u32 gpu_va(struct etnaviv_gpu *gpu, struct etnaviv_cmdbuf *buf) ++{ ++ return buf->paddr - gpu->memory_base; ++} ++ ++static void etnaviv_buffer_dump(struct etnaviv_gpu *gpu, ++ struct etnaviv_cmdbuf *buf, u32 off, u32 len) ++{ ++ u32 size = buf->size; ++ u32 *ptr = buf->vaddr + off; ++ ++ dev_info(gpu->dev, "virt %p phys 0x%08x free 0x%08x\n", ++ ptr, gpu_va(gpu, buf) + off, size - len * 4 - off); ++ ++ print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 4, ++ ptr, len * 4, 0); ++} ++ ++u16 etnaviv_buffer_init(struct etnaviv_gpu *gpu) ++{ ++ struct etnaviv_cmdbuf *buffer = gpu->buffer; ++ ++ /* initialize buffer */ ++ buffer->user_size = 0; ++ ++ CMD_WAIT(buffer); ++ CMD_LINK(buffer, 2, gpu_va(gpu, buffer) + buffer->user_size - 4); ++ ++ return buffer->user_size / 8; ++} ++ ++void etnaviv_buffer_end(struct etnaviv_gpu *gpu) ++{ ++ struct etnaviv_cmdbuf *buffer = gpu->buffer; ++ ++ /* Replace the last WAIT with an END */ ++ buffer->user_size -= 16; ++ ++ CMD_END(buffer); ++ mb(); ++} ++ ++void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, ++ struct etnaviv_cmdbuf *cmdbuf) ++{ ++ struct etnaviv_cmdbuf *buffer = gpu->buffer; ++ u32 *lw = buffer->vaddr + buffer->user_size - 16; ++ u32 back, link_target, link_size, reserve_size, extra_size = 0; ++ ++ if (drm_debug & DRM_UT_DRIVER) ++ etnaviv_buffer_dump(gpu, buffer, 0, 0x50); ++ ++ /* ++ * If we need to flush the MMU prior to submitting this buffer, we ++ * will need to append a mmu flush load state, followed by a new ++ * link to this buffer - a total of four additional words. ++ */ ++ if (gpu->mmu->need_flush || gpu->switch_context) { ++ /* link command */ ++ extra_size += 2; ++ /* flush command */ ++ if (gpu->mmu->need_flush) ++ extra_size += 2; ++ /* pipe switch commands */ ++ if (gpu->switch_context) ++ extra_size += 8; ++ } ++ ++ reserve_size = (6 + extra_size) * 4; ++ ++ /* ++ * if we are going to completely overflow the buffer, we need to wrap. ++ */ ++ if (buffer->user_size + reserve_size > buffer->size) ++ buffer->user_size = 0; ++ ++ /* save offset back into main buffer */ ++ back = buffer->user_size + reserve_size - 6 * 4; ++ link_target = gpu_va(gpu, buffer) + buffer->user_size; ++ link_size = 6; ++ ++ /* Skip over any extra instructions */ ++ link_target += extra_size * sizeof(u32); ++ ++ if (drm_debug & DRM_UT_DRIVER) ++ pr_info("stream link to 0x%08x @ 0x%08x %p\n", ++ link_target, gpu_va(gpu, cmdbuf), cmdbuf->vaddr); ++ ++ /* jump back from cmd to main buffer */ ++ CMD_LINK(cmdbuf, link_size, link_target); ++ ++ link_target = gpu_va(gpu, cmdbuf); ++ link_size = cmdbuf->size / 8; ++ ++ ++ ++ if (drm_debug & DRM_UT_DRIVER) { ++ print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 4, ++ cmdbuf->vaddr, cmdbuf->size, 0); ++ ++ pr_info("link op: %p\n", lw); ++ pr_info("link addr: %p\n", lw + 1); ++ pr_info("addr: 0x%08x\n", link_target); ++ pr_info("back: 0x%08x\n", gpu_va(gpu, buffer) + back); ++ pr_info("event: %d\n", event); ++ } ++ ++ if (gpu->mmu->need_flush || gpu->switch_context) { ++ u32 new_target = gpu_va(gpu, buffer) + buffer->user_size; ++ ++ if (gpu->mmu->need_flush) { ++ /* Add the MMU flush */ ++ CMD_LOAD_STATE(buffer, VIVS_GL_FLUSH_MMU, ++ VIVS_GL_FLUSH_MMU_FLUSH_FEMMU | ++ VIVS_GL_FLUSH_MMU_FLUSH_UNK1 | ++ VIVS_GL_FLUSH_MMU_FLUSH_UNK2 | ++ VIVS_GL_FLUSH_MMU_FLUSH_PEMMU | ++ VIVS_GL_FLUSH_MMU_FLUSH_UNK4); ++ ++ gpu->mmu->need_flush = false; ++ } ++ ++ if (gpu->switch_context) { ++ etnaviv_cmd_select_pipe(buffer, cmdbuf->exec_state); ++ gpu->switch_context = false; ++ } ++ ++ /* And the link to the first buffer */ ++ CMD_LINK(buffer, link_size, link_target); ++ ++ /* Update the link target to point to above instructions */ ++ link_target = new_target; ++ link_size = extra_size; ++ } ++ ++ /* trigger event */ ++ CMD_LOAD_STATE(buffer, VIVS_GL_EVENT, VIVS_GL_EVENT_EVENT_ID(event) | ++ VIVS_GL_EVENT_FROM_PE); ++ ++ /* append WAIT/LINK to main buffer */ ++ CMD_WAIT(buffer); ++ CMD_LINK(buffer, 2, gpu_va(gpu, buffer) + (buffer->user_size - 4)); ++ ++ /* Change WAIT into a LINK command; write the address first. */ ++ *(lw + 1) = link_target; ++ mb(); ++ *(lw) = VIV_FE_LINK_HEADER_OP_LINK | ++ VIV_FE_LINK_HEADER_PREFETCH(link_size); ++ mb(); ++ ++ if (drm_debug & DRM_UT_DRIVER) ++ etnaviv_buffer_dump(gpu, buffer, 0, 0x50); ++} +diff --git a/drivers/gpu/drm/etnaviv/etnaviv_cmd_parser.c b/drivers/gpu/drm/etnaviv/etnaviv_cmd_parser.c +new file mode 100644 +index 0000000..dcfd565 +--- /dev/null ++++ b/drivers/gpu/drm/etnaviv/etnaviv_cmd_parser.c +@@ -0,0 +1,209 @@ ++/* ++ * Copyright (C) 2015 Etnaviv Project ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ */ ++ ++#include ++ ++#include "etnaviv_gem.h" ++#include "etnaviv_gpu.h" ++ ++#include "cmdstream.xml.h" ++ ++#define EXTRACT(val, field) (((val) & field##__MASK) >> field##__SHIFT) ++ ++struct etna_validation_state { ++ struct etnaviv_gpu *gpu; ++ const struct drm_etnaviv_gem_submit_reloc *relocs; ++ unsigned int num_relocs; ++ u32 *start; ++}; ++ ++static const struct { ++ u16 offset; ++ u16 size; ++} etnaviv_sensitive_states[] __initconst = { ++#define ST(start, num) { (start) >> 2, (num) } ++ /* 2D */ ++ ST(0x1200, 1), ++ ST(0x1228, 1), ++ ST(0x1238, 1), ++ ST(0x1284, 1), ++ ST(0x128c, 1), ++ ST(0x1304, 1), ++ ST(0x1310, 1), ++ ST(0x1318, 1), ++ ST(0x12800, 4), ++ ST(0x128a0, 4), ++ ST(0x128c0, 4), ++ ST(0x12970, 4), ++ ST(0x12a00, 8), ++ ST(0x12b40, 8), ++ ST(0x12b80, 8), ++ ST(0x12ce0, 8), ++ /* 3D */ ++ ST(0x0644, 1), ++ ST(0x064c, 1), ++ ST(0x0680, 8), ++ ST(0x1410, 1), ++ ST(0x1430, 1), ++ ST(0x1458, 1), ++ ST(0x1460, 8), ++ ST(0x1480, 8), ++ ST(0x1500, 8), ++ ST(0x1520, 8), ++ ST(0x1608, 1), ++ ST(0x1610, 1), ++ ST(0x1658, 1), ++ ST(0x165c, 1), ++ ST(0x1664, 1), ++ ST(0x1668, 1), ++ ST(0x16a4, 1), ++ ST(0x16c0, 8), ++ ST(0x16e0, 8), ++ ST(0x1740, 8), ++ ST(0x2400, 14 * 16), ++ ST(0x10800, 32 * 16), ++#undef ST ++}; ++ ++#define ETNAVIV_STATES_SIZE (VIV_FE_LOAD_STATE_HEADER_OFFSET__MASK + 1u) ++static DECLARE_BITMAP(etnaviv_states, ETNAVIV_STATES_SIZE); ++ ++void __init etnaviv_validate_init(void) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(etnaviv_sensitive_states); i++) ++ bitmap_set(etnaviv_states, etnaviv_sensitive_states[i].offset, ++ etnaviv_sensitive_states[i].size); ++} ++ ++static void etnaviv_warn_if_non_sensitive(struct etna_validation_state *state, ++ unsigned int buf_offset, unsigned int state_addr) ++{ ++ if (state->num_relocs && state->relocs->submit_offset < buf_offset) { ++ dev_warn_once(state->gpu->dev, ++ "%s: relocation for non-sensitive state 0x%x at offset %u\n", ++ __func__, state_addr, ++ state->relocs->submit_offset); ++ while (state->num_relocs && ++ state->relocs->submit_offset < buf_offset) { ++ state->relocs++; ++ state->num_relocs--; ++ } ++ } ++} ++ ++static bool etnaviv_validate_load_state(struct etna_validation_state *state, ++ u32 *ptr, unsigned int state_offset, unsigned int num) ++{ ++ unsigned int size = min(ETNAVIV_STATES_SIZE, state_offset + num); ++ unsigned int st_offset = state_offset, buf_offset; ++ ++ for_each_set_bit_from(st_offset, etnaviv_states, size) { ++ buf_offset = (ptr - state->start + ++ st_offset - state_offset) * 4; ++ ++ etnaviv_warn_if_non_sensitive(state, buf_offset, st_offset * 4); ++ if (state->num_relocs && ++ state->relocs->submit_offset == buf_offset) { ++ state->relocs++; ++ state->num_relocs--; ++ continue; ++ } ++ ++ dev_warn_ratelimited(state->gpu->dev, ++ "%s: load state touches restricted state 0x%x at offset %u\n", ++ __func__, st_offset * 4, buf_offset); ++ return false; ++ } ++ ++ if (state->num_relocs) { ++ buf_offset = (ptr - state->start + num) * 4; ++ etnaviv_warn_if_non_sensitive(state, buf_offset, st_offset * 4 + ++ state->relocs->submit_offset - ++ buf_offset); ++ } ++ ++ return true; ++} ++ ++static uint8_t cmd_length[32] = { ++ [FE_OPCODE_DRAW_PRIMITIVES] = 4, ++ [FE_OPCODE_DRAW_INDEXED_PRIMITIVES] = 6, ++ [FE_OPCODE_NOP] = 2, ++ [FE_OPCODE_STALL] = 2, ++}; ++ ++bool etnaviv_cmd_validate_one(struct etnaviv_gpu *gpu, u32 *stream, ++ unsigned int size, ++ struct drm_etnaviv_gem_submit_reloc *relocs, ++ unsigned int reloc_size) ++{ ++ struct etna_validation_state state; ++ u32 *buf = stream; ++ u32 *end = buf + size; ++ ++ state.gpu = gpu; ++ state.relocs = relocs; ++ state.num_relocs = reloc_size; ++ state.start = stream; ++ ++ while (buf < end) { ++ u32 cmd = *buf; ++ unsigned int len, n, off; ++ unsigned int op = cmd >> 27; ++ ++ switch (op) { ++ case FE_OPCODE_LOAD_STATE: ++ n = EXTRACT(cmd, VIV_FE_LOAD_STATE_HEADER_COUNT); ++ len = ALIGN(1 + n, 2); ++ if (buf + len > end) ++ break; ++ ++ off = EXTRACT(cmd, VIV_FE_LOAD_STATE_HEADER_OFFSET); ++ if (!etnaviv_validate_load_state(&state, buf + 1, ++ off, n)) ++ return false; ++ break; ++ ++ case FE_OPCODE_DRAW_2D: ++ n = EXTRACT(cmd, VIV_FE_DRAW_2D_HEADER_COUNT); ++ if (n == 0) ++ n = 256; ++ len = 2 + n * 2; ++ break; ++ ++ default: ++ len = cmd_length[op]; ++ if (len == 0) { ++ dev_err(gpu->dev, "%s: op %u not permitted at offset %tu\n", ++ __func__, op, buf - state.start); ++ return false; ++ } ++ break; ++ } ++ ++ buf += len; ++ } ++ ++ if (buf > end) { ++ dev_err(gpu->dev, "%s: commands overflow end of buffer: %tu > %u\n", ++ __func__, buf - state.start, size); ++ return false; ++ } ++ ++ return true; ++} +diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c +new file mode 100644 +index 0000000..5c89ebb +--- /dev/null ++++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c +@@ -0,0 +1,707 @@ ++/* ++ * Copyright (C) 2015 Etnaviv Project ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ */ ++ ++#include ++#include ++ ++#include "etnaviv_drv.h" ++#include "etnaviv_gpu.h" ++#include "etnaviv_gem.h" ++#include "etnaviv_mmu.h" ++#include "etnaviv_gem.h" ++ ++#ifdef CONFIG_DRM_ETNAVIV_REGISTER_LOGGING ++static bool reglog; ++MODULE_PARM_DESC(reglog, "Enable register read/write logging"); ++module_param(reglog, bool, 0600); ++#else ++#define reglog 0 ++#endif ++ ++void __iomem *etnaviv_ioremap(struct platform_device *pdev, const char *name, ++ const char *dbgname) ++{ ++ struct resource *res; ++ void __iomem *ptr; ++ ++ if (name) ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); ++ else ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ ++ ptr = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(ptr)) { ++ dev_err(&pdev->dev, "failed to ioremap %s: %ld\n", name, ++ PTR_ERR(ptr)); ++ return ptr; ++ } ++ ++ if (reglog) ++ dev_printk(KERN_DEBUG, &pdev->dev, "IO:region %s 0x%p %08zx\n", ++ dbgname, ptr, (size_t)resource_size(res)); ++ ++ return ptr; ++} ++ ++void etnaviv_writel(u32 data, void __iomem *addr) ++{ ++ if (reglog) ++ printk(KERN_DEBUG "IO:W %p %08x\n", addr, data); ++ ++ writel(data, addr); ++} ++ ++u32 etnaviv_readl(const void __iomem *addr) ++{ ++ u32 val = readl(addr); ++ ++ if (reglog) ++ printk(KERN_DEBUG "IO:R %p %08x\n", addr, val); ++ ++ return val; ++} ++ ++/* ++ * DRM operations: ++ */ ++ ++ ++static void load_gpu(struct drm_device *dev) ++{ ++ struct etnaviv_drm_private *priv = dev->dev_private; ++ unsigned int i; ++ ++ for (i = 0; i < ETNA_MAX_PIPES; i++) { ++ struct etnaviv_gpu *g = priv->gpu[i]; ++ ++ if (g) { ++ int ret; ++ ++ ret = etnaviv_gpu_init(g); ++ if (ret) { ++ dev_err(g->dev, "hw init failed: %d\n", ret); ++ priv->gpu[i] = NULL; ++ } ++ } ++ } ++} ++ ++static int etnaviv_open(struct drm_device *dev, struct drm_file *file) ++{ ++ struct etnaviv_file_private *ctx; ++ ++ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); ++ if (!ctx) ++ return -ENOMEM; ++ ++ file->driver_priv = ctx; ++ ++ return 0; ++} ++ ++static void etnaviv_preclose(struct drm_device *dev, struct drm_file *file) ++{ ++ struct etnaviv_drm_private *priv = dev->dev_private; ++ struct etnaviv_file_private *ctx = file->driver_priv; ++ unsigned int i; ++ ++ for (i = 0; i < ETNA_MAX_PIPES; i++) { ++ struct etnaviv_gpu *gpu = priv->gpu[i]; ++ ++ if (gpu) { ++ mutex_lock(&gpu->lock); ++ if (gpu->lastctx == ctx) ++ gpu->lastctx = NULL; ++ mutex_unlock(&gpu->lock); ++ } ++ } ++ ++ kfree(ctx); ++} ++ ++/* ++ * DRM debugfs: ++ */ ++ ++#ifdef CONFIG_DEBUG_FS ++static int etnaviv_gem_show(struct drm_device *dev, struct seq_file *m) ++{ ++ struct etnaviv_drm_private *priv = dev->dev_private; ++ ++ etnaviv_gem_describe_objects(priv, m); ++ ++ return 0; ++} ++ ++static int etnaviv_mm_show(struct drm_device *dev, struct seq_file *m) ++{ ++ int ret; ++ ++ read_lock(&dev->vma_offset_manager->vm_lock); ++ ret = drm_mm_dump_table(m, &dev->vma_offset_manager->vm_addr_space_mm); ++ read_unlock(&dev->vma_offset_manager->vm_lock); ++ ++ return ret; ++} ++ ++static int etnaviv_mmu_show(struct etnaviv_gpu *gpu, struct seq_file *m) ++{ ++ seq_printf(m, "Active Objects (%s):\n", dev_name(gpu->dev)); ++ ++ mutex_lock(&gpu->mmu->lock); ++ drm_mm_dump_table(m, &gpu->mmu->mm); ++ mutex_unlock(&gpu->mmu->lock); ++ ++ return 0; ++} ++ ++static void etnaviv_buffer_dump(struct etnaviv_gpu *gpu, struct seq_file *m) ++{ ++ struct etnaviv_cmdbuf *buf = gpu->buffer; ++ u32 size = buf->size; ++ u32 *ptr = buf->vaddr; ++ u32 i; ++ ++ seq_printf(m, "virt %p - phys 0x%llx - free 0x%08x\n", ++ buf->vaddr, (u64)buf->paddr, size - buf->user_size); ++ ++ for (i = 0; i < size / 4; i++) { ++ if (i && !(i % 4)) ++ seq_puts(m, "\n"); ++ if (i % 4 == 0) ++ seq_printf(m, "\t0x%p: ", ptr + i); ++ seq_printf(m, "%08x ", *(ptr + i)); ++ } ++ seq_puts(m, "\n"); ++} ++ ++static int etnaviv_ring_show(struct etnaviv_gpu *gpu, struct seq_file *m) ++{ ++ seq_printf(m, "Ring Buffer (%s): ", dev_name(gpu->dev)); ++ ++ mutex_lock(&gpu->lock); ++ etnaviv_buffer_dump(gpu, m); ++ mutex_unlock(&gpu->lock); ++ ++ return 0; ++} ++ ++static int show_unlocked(struct seq_file *m, void *arg) ++{ ++ struct drm_info_node *node = (struct drm_info_node *) m->private; ++ struct drm_device *dev = node->minor->dev; ++ int (*show)(struct drm_device *dev, struct seq_file *m) = ++ node->info_ent->data; ++ ++ return show(dev, m); ++} ++ ++static int show_each_gpu(struct seq_file *m, void *arg) ++{ ++ struct drm_info_node *node = (struct drm_info_node *) m->private; ++ struct drm_device *dev = node->minor->dev; ++ struct etnaviv_drm_private *priv = dev->dev_private; ++ struct etnaviv_gpu *gpu; ++ int (*show)(struct etnaviv_gpu *gpu, struct seq_file *m) = ++ node->info_ent->data; ++ unsigned int i; ++ int ret = 0; ++ ++ for (i = 0; i < ETNA_MAX_PIPES; i++) { ++ gpu = priv->gpu[i]; ++ if (!gpu) ++ continue; ++ ++ ret = show(gpu, m); ++ if (ret < 0) ++ break; ++ } ++ ++ return ret; ++} ++ ++static struct drm_info_list etnaviv_debugfs_list[] = { ++ {"gpu", show_each_gpu, 0, etnaviv_gpu_debugfs}, ++ {"gem", show_unlocked, 0, etnaviv_gem_show}, ++ { "mm", show_unlocked, 0, etnaviv_mm_show }, ++ {"mmu", show_each_gpu, 0, etnaviv_mmu_show}, ++ {"ring", show_each_gpu, 0, etnaviv_ring_show}, ++}; ++ ++static int etnaviv_debugfs_init(struct drm_minor *minor) ++{ ++ struct drm_device *dev = minor->dev; ++ int ret; ++ ++ ret = drm_debugfs_create_files(etnaviv_debugfs_list, ++ ARRAY_SIZE(etnaviv_debugfs_list), ++ minor->debugfs_root, minor); ++ ++ if (ret) { ++ dev_err(dev->dev, "could not install etnaviv_debugfs_list\n"); ++ return ret; ++ } ++ ++ return ret; ++} ++ ++static void etnaviv_debugfs_cleanup(struct drm_minor *minor) ++{ ++ drm_debugfs_remove_files(etnaviv_debugfs_list, ++ ARRAY_SIZE(etnaviv_debugfs_list), minor); ++} ++#endif ++ ++/* ++ * DRM ioctls: ++ */ ++ ++static int etnaviv_ioctl_get_param(struct drm_device *dev, void *data, ++ struct drm_file *file) ++{ ++ struct etnaviv_drm_private *priv = dev->dev_private; ++ struct drm_etnaviv_param *args = data; ++ struct etnaviv_gpu *gpu; ++ ++ if (args->pipe >= ETNA_MAX_PIPES) ++ return -EINVAL; ++ ++ gpu = priv->gpu[args->pipe]; ++ if (!gpu) ++ return -ENXIO; ++ ++ return etnaviv_gpu_get_param(gpu, args->param, &args->value); ++} ++ ++static int etnaviv_ioctl_gem_new(struct drm_device *dev, void *data, ++ struct drm_file *file) ++{ ++ struct drm_etnaviv_gem_new *args = data; ++ ++ if (args->flags & ~(ETNA_BO_CACHED | ETNA_BO_WC | ETNA_BO_UNCACHED | ++ ETNA_BO_FORCE_MMU)) ++ return -EINVAL; ++ ++ return etnaviv_gem_new_handle(dev, file, args->size, ++ args->flags, &args->handle); ++} ++ ++#define TS(t) ((struct timespec){ \ ++ .tv_sec = (t).tv_sec, \ ++ .tv_nsec = (t).tv_nsec \ ++}) ++ ++static int etnaviv_ioctl_gem_cpu_prep(struct drm_device *dev, void *data, ++ struct drm_file *file) ++{ ++ struct drm_etnaviv_gem_cpu_prep *args = data; ++ struct drm_gem_object *obj; ++ int ret; ++ ++ if (args->op & ~(ETNA_PREP_READ | ETNA_PREP_WRITE | ETNA_PREP_NOSYNC)) ++ return -EINVAL; ++ ++ obj = drm_gem_object_lookup(dev, file, args->handle); ++ if (!obj) ++ return -ENOENT; ++ ++ ret = etnaviv_gem_cpu_prep(obj, args->op, &TS(args->timeout)); ++ ++ drm_gem_object_unreference_unlocked(obj); ++ ++ return ret; ++} ++ ++static int etnaviv_ioctl_gem_cpu_fini(struct drm_device *dev, void *data, ++ struct drm_file *file) ++{ ++ struct drm_etnaviv_gem_cpu_fini *args = data; ++ struct drm_gem_object *obj; ++ int ret; ++ ++ if (args->flags) ++ return -EINVAL; ++ ++ obj = drm_gem_object_lookup(dev, file, args->handle); ++ if (!obj) ++ return -ENOENT; ++ ++ ret = etnaviv_gem_cpu_fini(obj); ++ ++ drm_gem_object_unreference_unlocked(obj); ++ ++ return ret; ++} ++ ++static int etnaviv_ioctl_gem_info(struct drm_device *dev, void *data, ++ struct drm_file *file) ++{ ++ struct drm_etnaviv_gem_info *args = data; ++ struct drm_gem_object *obj; ++ int ret; ++ ++ if (args->pad) ++ return -EINVAL; ++ ++ obj = drm_gem_object_lookup(dev, file, args->handle); ++ if (!obj) ++ return -ENOENT; ++ ++ ret = etnaviv_gem_mmap_offset(obj, &args->offset); ++ drm_gem_object_unreference_unlocked(obj); ++ ++ return ret; ++} ++ ++static int etnaviv_ioctl_wait_fence(struct drm_device *dev, void *data, ++ struct drm_file *file) ++{ ++ struct drm_etnaviv_wait_fence *args = data; ++ struct etnaviv_drm_private *priv = dev->dev_private; ++ struct timespec *timeout = &TS(args->timeout); ++ struct etnaviv_gpu *gpu; ++ ++ if (args->flags & ~(ETNA_WAIT_NONBLOCK)) ++ return -EINVAL; ++ ++ if (args->pipe >= ETNA_MAX_PIPES) ++ return -EINVAL; ++ ++ gpu = priv->gpu[args->pipe]; ++ if (!gpu) ++ return -ENXIO; ++ ++ if (args->flags & ETNA_WAIT_NONBLOCK) ++ timeout = NULL; ++ ++ return etnaviv_gpu_wait_fence_interruptible(gpu, args->fence, ++ timeout); ++} ++ ++static int etnaviv_ioctl_gem_userptr(struct drm_device *dev, void *data, ++ struct drm_file *file) ++{ ++ struct drm_etnaviv_gem_userptr *args = data; ++ int access; ++ ++ if (args->flags & ~(ETNA_USERPTR_READ|ETNA_USERPTR_WRITE) || ++ args->flags == 0) ++ return -EINVAL; ++ ++ if (offset_in_page(args->user_ptr | args->user_size) || ++ (uintptr_t)args->user_ptr != args->user_ptr || ++ (u32)args->user_size != args->user_size || ++ args->user_ptr & ~PAGE_MASK) ++ return -EINVAL; ++ ++ if (args->flags & ETNA_USERPTR_WRITE) ++ access = VERIFY_WRITE; ++ else ++ access = VERIFY_READ; ++ ++ if (!access_ok(access, (void __user *)(unsigned long)args->user_ptr, ++ args->user_size)) ++ return -EFAULT; ++ ++ return etnaviv_gem_new_userptr(dev, file, args->user_ptr, ++ args->user_size, args->flags, ++ &args->handle); ++} ++ ++static int etnaviv_ioctl_gem_wait(struct drm_device *dev, void *data, ++ struct drm_file *file) ++{ ++ struct etnaviv_drm_private *priv = dev->dev_private; ++ struct drm_etnaviv_gem_wait *args = data; ++ struct timespec *timeout = &TS(args->timeout); ++ struct drm_gem_object *obj; ++ struct etnaviv_gpu *gpu; ++ int ret; ++ ++ if (args->flags & ~(ETNA_WAIT_NONBLOCK)) ++ return -EINVAL; ++ ++ if (args->pipe >= ETNA_MAX_PIPES) ++ return -EINVAL; ++ ++ gpu = priv->gpu[args->pipe]; ++ if (!gpu) ++ return -ENXIO; ++ ++ obj = drm_gem_object_lookup(dev, file, args->handle); ++ if (!obj) ++ return -ENOENT; ++ ++ if (args->flags & ETNA_WAIT_NONBLOCK) ++ timeout = NULL; ++ ++ ret = etnaviv_gem_wait_bo(gpu, obj, timeout); ++ ++ drm_gem_object_unreference_unlocked(obj); ++ ++ return ret; ++} ++ ++static const struct drm_ioctl_desc etnaviv_ioctls[] = { ++#define ETNA_IOCTL(n, func, flags) \ ++ DRM_IOCTL_DEF_DRV(ETNAVIV_##n, etnaviv_ioctl_##func, flags) ++ ETNA_IOCTL(GET_PARAM, get_param, DRM_AUTH|DRM_RENDER_ALLOW), ++ ETNA_IOCTL(GEM_NEW, gem_new, DRM_AUTH|DRM_RENDER_ALLOW), ++ ETNA_IOCTL(GEM_INFO, gem_info, DRM_AUTH|DRM_RENDER_ALLOW), ++ ETNA_IOCTL(GEM_CPU_PREP, gem_cpu_prep, DRM_AUTH|DRM_RENDER_ALLOW), ++ ETNA_IOCTL(GEM_CPU_FINI, gem_cpu_fini, DRM_AUTH|DRM_RENDER_ALLOW), ++ ETNA_IOCTL(GEM_SUBMIT, gem_submit, DRM_AUTH|DRM_RENDER_ALLOW), ++ ETNA_IOCTL(WAIT_FENCE, wait_fence, DRM_AUTH|DRM_RENDER_ALLOW), ++ ETNA_IOCTL(GEM_USERPTR, gem_userptr, DRM_AUTH|DRM_RENDER_ALLOW), ++ ETNA_IOCTL(GEM_WAIT, gem_wait, DRM_AUTH|DRM_RENDER_ALLOW), ++}; ++ ++static const struct vm_operations_struct vm_ops = { ++ .fault = etnaviv_gem_fault, ++ .open = drm_gem_vm_open, ++ .close = drm_gem_vm_close, ++}; ++ ++static const struct file_operations fops = { ++ .owner = THIS_MODULE, ++ .open = drm_open, ++ .release = drm_release, ++ .unlocked_ioctl = drm_ioctl, ++#ifdef CONFIG_COMPAT ++ .compat_ioctl = drm_compat_ioctl, ++#endif ++ .poll = drm_poll, ++ .read = drm_read, ++ .llseek = no_llseek, ++ .mmap = etnaviv_gem_mmap, ++}; ++ ++static struct drm_driver etnaviv_drm_driver = { ++ .driver_features = DRIVER_HAVE_IRQ | ++ DRIVER_GEM | ++ DRIVER_PRIME | ++ DRIVER_RENDER, ++ .open = etnaviv_open, ++ .preclose = etnaviv_preclose, ++ .set_busid = drm_platform_set_busid, ++ .gem_free_object = etnaviv_gem_free_object, ++ .gem_vm_ops = &vm_ops, ++ .prime_handle_to_fd = drm_gem_prime_handle_to_fd, ++ .prime_fd_to_handle = drm_gem_prime_fd_to_handle, ++ .gem_prime_export = drm_gem_prime_export, ++ .gem_prime_import = drm_gem_prime_import, ++ .gem_prime_pin = etnaviv_gem_prime_pin, ++ .gem_prime_unpin = etnaviv_gem_prime_unpin, ++ .gem_prime_get_sg_table = etnaviv_gem_prime_get_sg_table, ++ .gem_prime_import_sg_table = etnaviv_gem_prime_import_sg_table, ++ .gem_prime_vmap = etnaviv_gem_prime_vmap, ++ .gem_prime_vunmap = etnaviv_gem_prime_vunmap, ++#ifdef CONFIG_DEBUG_FS ++ .debugfs_init = etnaviv_debugfs_init, ++ .debugfs_cleanup = etnaviv_debugfs_cleanup, ++#endif ++ .ioctls = etnaviv_ioctls, ++ .num_ioctls = DRM_ETNAVIV_NUM_IOCTLS, ++ .fops = &fops, ++ .name = "etnaviv", ++ .desc = "etnaviv DRM", ++ .date = "20151214", ++ .major = 1, ++ .minor = 0, ++}; ++ ++/* ++ * Platform driver: ++ */ ++static int etnaviv_bind(struct device *dev) ++{ ++ struct etnaviv_drm_private *priv; ++ struct drm_device *drm; ++ int ret; ++ ++ drm = drm_dev_alloc(&etnaviv_drm_driver, dev); ++ if (!drm) ++ return -ENOMEM; ++ ++ drm->platformdev = to_platform_device(dev); ++ ++ priv = kzalloc(sizeof(*priv), GFP_KERNEL); ++ if (!priv) { ++ dev_err(dev, "failed to allocate private data\n"); ++ ret = -ENOMEM; ++ goto out_unref; ++ } ++ drm->dev_private = priv; ++ ++ priv->wq = alloc_ordered_workqueue("etnaviv", 0); ++ if (!priv->wq) { ++ ret = -ENOMEM; ++ goto out_wq; ++ } ++ ++ mutex_init(&priv->gem_lock); ++ INIT_LIST_HEAD(&priv->gem_list); ++ priv->num_gpus = 0; ++ ++ dev_set_drvdata(dev, drm); ++ ++ ret = component_bind_all(dev, drm); ++ if (ret < 0) ++ goto out_bind; ++ ++ load_gpu(drm); ++ ++ ret = drm_dev_register(drm, 0); ++ if (ret) ++ goto out_register; ++ ++ return 0; ++ ++out_register: ++ component_unbind_all(dev, drm); ++out_bind: ++ flush_workqueue(priv->wq); ++ destroy_workqueue(priv->wq); ++out_wq: ++ kfree(priv); ++out_unref: ++ drm_dev_unref(drm); ++ ++ return ret; ++} ++ ++static void etnaviv_unbind(struct device *dev) ++{ ++ struct drm_device *drm = dev_get_drvdata(dev); ++ struct etnaviv_drm_private *priv = drm->dev_private; ++ ++ drm_dev_unregister(drm); ++ ++ flush_workqueue(priv->wq); ++ destroy_workqueue(priv->wq); ++ ++ component_unbind_all(dev, drm); ++ ++ drm->dev_private = NULL; ++ kfree(priv); ++ ++ drm_put_dev(drm); ++} ++ ++static const struct component_master_ops etnaviv_master_ops = { ++ .bind = etnaviv_bind, ++ .unbind = etnaviv_unbind, ++}; ++ ++static int compare_of(struct device *dev, void *data) ++{ ++ struct device_node *np = data; ++ ++ return dev->of_node == np; ++} ++ ++static int compare_str(struct device *dev, void *data) ++{ ++ return !strcmp(dev_name(dev), data); ++} ++ ++static int etnaviv_pdev_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *node = dev->of_node; ++ struct component_match *match = NULL; ++ ++ dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); ++ ++ if (node) { ++ struct device_node *core_node; ++ int i; ++ ++ for (i = 0; ; i++) { ++ core_node = of_parse_phandle(node, "cores", i); ++ if (!core_node) ++ break; ++ ++ component_match_add(&pdev->dev, &match, compare_of, ++ core_node); ++ of_node_put(core_node); ++ } ++ } else if (dev->platform_data) { ++ char **names = dev->platform_data; ++ unsigned i; ++ ++ for (i = 0; names[i]; i++) ++ component_match_add(dev, &match, compare_str, names[i]); ++ } ++ ++ return component_master_add_with_match(dev, &etnaviv_master_ops, match); ++} ++ ++static int etnaviv_pdev_remove(struct platform_device *pdev) ++{ ++ component_master_del(&pdev->dev, &etnaviv_master_ops); ++ ++ return 0; ++} ++ ++static const struct of_device_id dt_match[] = { ++ { .compatible = "fsl,imx-gpu-subsystem" }, ++ { .compatible = "marvell,dove-gpu-subsystem" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, dt_match); ++ ++static struct platform_driver etnaviv_platform_driver = { ++ .probe = etnaviv_pdev_probe, ++ .remove = etnaviv_pdev_remove, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "etnaviv", ++ .of_match_table = dt_match, ++ }, ++}; ++ ++static int __init etnaviv_init(void) ++{ ++ int ret; ++ ++ etnaviv_validate_init(); ++ ++ ret = platform_driver_register(&etnaviv_gpu_driver); ++ if (ret != 0) ++ return ret; ++ ++ ret = platform_driver_register(&etnaviv_platform_driver); ++ if (ret != 0) ++ platform_driver_unregister(&etnaviv_gpu_driver); ++ ++ return ret; ++} ++module_init(etnaviv_init); ++ ++static void __exit etnaviv_exit(void) ++{ ++ platform_driver_unregister(&etnaviv_gpu_driver); ++ platform_driver_unregister(&etnaviv_platform_driver); ++} ++module_exit(etnaviv_exit); ++ ++MODULE_AUTHOR("Christian Gmeiner "); ++MODULE_AUTHOR("Russell King "); ++MODULE_AUTHOR("Lucas Stach "); ++MODULE_DESCRIPTION("etnaviv DRM Driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:etnaviv"); +diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.h b/drivers/gpu/drm/etnaviv/etnaviv_drv.h +new file mode 100644 +index 0000000..d6bd438 +--- /dev/null ++++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.h +@@ -0,0 +1,161 @@ ++/* ++ * Copyright (C) 2015 Etnaviv Project ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ */ ++ ++#ifndef __ETNAVIV_DRV_H__ ++#define __ETNAVIV_DRV_H__ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++struct etnaviv_cmdbuf; ++struct etnaviv_gpu; ++struct etnaviv_mmu; ++struct etnaviv_gem_object; ++struct etnaviv_gem_submit; ++ ++struct etnaviv_file_private { ++ /* currently we don't do anything useful with this.. but when ++ * per-context address spaces are supported we'd keep track of ++ * the context's page-tables here. ++ */ ++ int dummy; ++}; ++ ++struct etnaviv_drm_private { ++ int num_gpus; ++ struct etnaviv_gpu *gpu[ETNA_MAX_PIPES]; ++ ++ /* list of GEM objects: */ ++ struct mutex gem_lock; ++ struct list_head gem_list; ++ ++ struct workqueue_struct *wq; ++}; ++ ++static inline void etnaviv_queue_work(struct drm_device *dev, ++ struct work_struct *w) ++{ ++ struct etnaviv_drm_private *priv = dev->dev_private; ++ ++ queue_work(priv->wq, w); ++} ++ ++int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data, ++ struct drm_file *file); ++ ++int etnaviv_gem_mmap(struct file *filp, struct vm_area_struct *vma); ++int etnaviv_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); ++int etnaviv_gem_mmap_offset(struct drm_gem_object *obj, u64 *offset); ++int etnaviv_gem_get_iova(struct etnaviv_gpu *gpu, ++ struct drm_gem_object *obj, u32 *iova); ++void etnaviv_gem_put_iova(struct etnaviv_gpu *gpu, struct drm_gem_object *obj); ++struct sg_table *etnaviv_gem_prime_get_sg_table(struct drm_gem_object *obj); ++void *etnaviv_gem_prime_vmap(struct drm_gem_object *obj); ++void etnaviv_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr); ++struct drm_gem_object *etnaviv_gem_prime_import_sg_table(struct drm_device *dev, ++ struct dma_buf_attachment *attach, struct sg_table *sg); ++int etnaviv_gem_prime_pin(struct drm_gem_object *obj); ++void etnaviv_gem_prime_unpin(struct drm_gem_object *obj); ++void *etnaviv_gem_vaddr(struct drm_gem_object *obj); ++int etnaviv_gem_cpu_prep(struct drm_gem_object *obj, u32 op, ++ struct timespec *timeout); ++int etnaviv_gem_cpu_fini(struct drm_gem_object *obj); ++void etnaviv_gem_free_object(struct drm_gem_object *obj); ++int etnaviv_gem_new_handle(struct drm_device *dev, struct drm_file *file, ++ u32 size, u32 flags, u32 *handle); ++struct drm_gem_object *etnaviv_gem_new_locked(struct drm_device *dev, ++ u32 size, u32 flags); ++struct drm_gem_object *etnaviv_gem_new(struct drm_device *dev, ++ u32 size, u32 flags); ++int etnaviv_gem_new_userptr(struct drm_device *dev, struct drm_file *file, ++ uintptr_t ptr, u32 size, u32 flags, u32 *handle); ++u16 etnaviv_buffer_init(struct etnaviv_gpu *gpu); ++void etnaviv_buffer_end(struct etnaviv_gpu *gpu); ++void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, ++ struct etnaviv_cmdbuf *cmdbuf); ++void etnaviv_validate_init(void); ++bool etnaviv_cmd_validate_one(struct etnaviv_gpu *gpu, ++ u32 *stream, unsigned int size, ++ struct drm_etnaviv_gem_submit_reloc *relocs, unsigned int reloc_size); ++ ++#ifdef CONFIG_DEBUG_FS ++void etnaviv_gem_describe_objects(struct etnaviv_drm_private *priv, ++ struct seq_file *m); ++#endif ++ ++void __iomem *etnaviv_ioremap(struct platform_device *pdev, const char *name, ++ const char *dbgname); ++void etnaviv_writel(u32 data, void __iomem *addr); ++u32 etnaviv_readl(const void __iomem *addr); ++ ++#define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) ++#define VERB(fmt, ...) if (0) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) ++ ++/* ++ * Return the storage size of a structure with a variable length array. ++ * The array is nelem elements of elem_size, where the base structure ++ * is defined by base. If the size overflows size_t, return zero. ++ */ ++static inline size_t size_vstruct(size_t nelem, size_t elem_size, size_t base) ++{ ++ if (elem_size && nelem > (SIZE_MAX - base) / elem_size) ++ return 0; ++ return base + nelem * elem_size; ++} ++ ++/* returns true if fence a comes after fence b */ ++static inline bool fence_after(u32 a, u32 b) ++{ ++ return (s32)(a - b) > 0; ++} ++ ++static inline bool fence_after_eq(u32 a, u32 b) ++{ ++ return (s32)(a - b) >= 0; ++} ++ ++static inline unsigned long etnaviv_timeout_to_jiffies( ++ const struct timespec *timeout) ++{ ++ unsigned long timeout_jiffies = timespec_to_jiffies(timeout); ++ unsigned long start_jiffies = jiffies; ++ unsigned long remaining_jiffies; ++ ++ if (time_after(start_jiffies, timeout_jiffies)) ++ remaining_jiffies = 0; ++ else ++ remaining_jiffies = timeout_jiffies - start_jiffies; ++ ++ return remaining_jiffies; ++} ++ ++#endif /* __ETNAVIV_DRV_H__ */ +diff --git a/drivers/gpu/drm/etnaviv/etnaviv_dump.c b/drivers/gpu/drm/etnaviv/etnaviv_dump.c +new file mode 100644 +index 0000000..bf8fa85 +--- /dev/null ++++ b/drivers/gpu/drm/etnaviv/etnaviv_dump.c +@@ -0,0 +1,227 @@ ++/* ++ * Copyright (C) 2015 Etnaviv Project ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ */ ++ ++#include ++#include "etnaviv_dump.h" ++#include "etnaviv_gem.h" ++#include "etnaviv_gpu.h" ++#include "etnaviv_mmu.h" ++#include "state.xml.h" ++#include "state_hi.xml.h" ++ ++struct core_dump_iterator { ++ void *start; ++ struct etnaviv_dump_object_header *hdr; ++ void *data; ++}; ++ ++static const unsigned short etnaviv_dump_registers[] = { ++ VIVS_HI_AXI_STATUS, ++ VIVS_HI_CLOCK_CONTROL, ++ VIVS_HI_IDLE_STATE, ++ VIVS_HI_AXI_CONFIG, ++ VIVS_HI_INTR_ENBL, ++ VIVS_HI_CHIP_IDENTITY, ++ VIVS_HI_CHIP_FEATURE, ++ VIVS_HI_CHIP_MODEL, ++ VIVS_HI_CHIP_REV, ++ VIVS_HI_CHIP_DATE, ++ VIVS_HI_CHIP_TIME, ++ VIVS_HI_CHIP_MINOR_FEATURE_0, ++ VIVS_HI_CACHE_CONTROL, ++ VIVS_HI_AXI_CONTROL, ++ VIVS_PM_POWER_CONTROLS, ++ VIVS_PM_MODULE_CONTROLS, ++ VIVS_PM_MODULE_STATUS, ++ VIVS_PM_PULSE_EATER, ++ VIVS_MC_MMU_FE_PAGE_TABLE, ++ VIVS_MC_MMU_TX_PAGE_TABLE, ++ VIVS_MC_MMU_PE_PAGE_TABLE, ++ VIVS_MC_MMU_PEZ_PAGE_TABLE, ++ VIVS_MC_MMU_RA_PAGE_TABLE, ++ VIVS_MC_DEBUG_MEMORY, ++ VIVS_MC_MEMORY_BASE_ADDR_RA, ++ VIVS_MC_MEMORY_BASE_ADDR_FE, ++ VIVS_MC_MEMORY_BASE_ADDR_TX, ++ VIVS_MC_MEMORY_BASE_ADDR_PEZ, ++ VIVS_MC_MEMORY_BASE_ADDR_PE, ++ VIVS_MC_MEMORY_TIMING_CONTROL, ++ VIVS_MC_BUS_CONFIG, ++ VIVS_FE_DMA_STATUS, ++ VIVS_FE_DMA_DEBUG_STATE, ++ VIVS_FE_DMA_ADDRESS, ++ VIVS_FE_DMA_LOW, ++ VIVS_FE_DMA_HIGH, ++ VIVS_FE_AUTO_FLUSH, ++}; ++ ++static void etnaviv_core_dump_header(struct core_dump_iterator *iter, ++ u32 type, void *data_end) ++{ ++ struct etnaviv_dump_object_header *hdr = iter->hdr; ++ ++ hdr->magic = cpu_to_le32(ETDUMP_MAGIC); ++ hdr->type = cpu_to_le32(type); ++ hdr->file_offset = cpu_to_le32(iter->data - iter->start); ++ hdr->file_size = cpu_to_le32(data_end - iter->data); ++ ++ iter->hdr++; ++ iter->data += hdr->file_size; ++} ++ ++static void etnaviv_core_dump_registers(struct core_dump_iterator *iter, ++ struct etnaviv_gpu *gpu) ++{ ++ struct etnaviv_dump_registers *reg = iter->data; ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(etnaviv_dump_registers); i++, reg++) { ++ reg->reg = etnaviv_dump_registers[i]; ++ reg->value = gpu_read(gpu, etnaviv_dump_registers[i]); ++ } ++ ++ etnaviv_core_dump_header(iter, ETDUMP_BUF_REG, reg); ++} ++ ++static void etnaviv_core_dump_mmu(struct core_dump_iterator *iter, ++ struct etnaviv_gpu *gpu, size_t mmu_size) ++{ ++ etnaviv_iommu_dump(gpu->mmu, iter->data); ++ ++ etnaviv_core_dump_header(iter, ETDUMP_BUF_MMU, iter->data + mmu_size); ++} ++ ++static void etnaviv_core_dump_mem(struct core_dump_iterator *iter, u32 type, ++ void *ptr, size_t size, u64 iova) ++{ ++ memcpy(iter->data, ptr, size); ++ ++ iter->hdr->iova = cpu_to_le64(iova); ++ ++ etnaviv_core_dump_header(iter, type, iter->data + size); ++} ++ ++void etnaviv_core_dump(struct etnaviv_gpu *gpu) ++{ ++ struct core_dump_iterator iter; ++ struct etnaviv_vram_mapping *vram; ++ struct etnaviv_gem_object *obj; ++ struct etnaviv_cmdbuf *cmd; ++ unsigned int n_obj, n_bomap_pages; ++ size_t file_size, mmu_size; ++ __le64 *bomap, *bomap_start; ++ ++ mmu_size = etnaviv_iommu_dump_size(gpu->mmu); ++ ++ /* We always dump registers, mmu, ring and end marker */ ++ n_obj = 4; ++ n_bomap_pages = 0; ++ file_size = ARRAY_SIZE(etnaviv_dump_registers) * ++ sizeof(struct etnaviv_dump_registers) + ++ mmu_size + gpu->buffer->size; ++ ++ /* Add in the active command buffers */ ++ list_for_each_entry(cmd, &gpu->active_cmd_list, node) { ++ file_size += cmd->size; ++ n_obj++; ++ } ++ ++ /* Add in the active buffer objects */ ++ list_for_each_entry(vram, &gpu->mmu->mappings, mmu_node) { ++ if (!vram->use) ++ continue; ++ ++ obj = vram->object; ++ file_size += obj->base.size; ++ n_bomap_pages += obj->base.size >> PAGE_SHIFT; ++ n_obj++; ++ } ++ ++ /* If we have any buffer objects, add a bomap object */ ++ if (n_bomap_pages) { ++ file_size += n_bomap_pages * sizeof(__le64); ++ n_obj++; ++ } ++ ++ /* Add the size of the headers */ ++ file_size += sizeof(*iter.hdr) * n_obj; ++ ++ /* Allocate the file in vmalloc memory, it's likely to be big */ ++ iter.start = vmalloc(file_size); ++ if (!iter.start) { ++ dev_warn(gpu->dev, "failed to allocate devcoredump file\n"); ++ return; ++ } ++ ++ /* Point the data member after the headers */ ++ iter.hdr = iter.start; ++ iter.data = &iter.hdr[n_obj]; ++ ++ memset(iter.hdr, 0, iter.data - iter.start); ++ ++ etnaviv_core_dump_registers(&iter, gpu); ++ etnaviv_core_dump_mmu(&iter, gpu, mmu_size); ++ etnaviv_core_dump_mem(&iter, ETDUMP_BUF_RING, gpu->buffer->vaddr, ++ gpu->buffer->size, gpu->buffer->paddr); ++ ++ list_for_each_entry(cmd, &gpu->active_cmd_list, node) ++ etnaviv_core_dump_mem(&iter, ETDUMP_BUF_CMD, cmd->vaddr, ++ cmd->size, cmd->paddr); ++ ++ /* Reserve space for the bomap */ ++ if (n_bomap_pages) { ++ bomap_start = bomap = iter.data; ++ memset(bomap, 0, sizeof(*bomap) * n_bomap_pages); ++ etnaviv_core_dump_header(&iter, ETDUMP_BUF_BOMAP, ++ bomap + n_bomap_pages); ++ } else { ++ /* Silence warning */ ++ bomap_start = bomap = NULL; ++ } ++ ++ list_for_each_entry(vram, &gpu->mmu->mappings, mmu_node) { ++ struct page **pages; ++ void *vaddr; ++ ++ if (vram->use == 0) ++ continue; ++ ++ obj = vram->object; ++ ++ pages = etnaviv_gem_get_pages(obj); ++ if (pages) { ++ int j; ++ ++ iter.hdr->data[0] = bomap - bomap_start; ++ ++ for (j = 0; j < obj->base.size >> PAGE_SHIFT; j++) ++ *bomap++ = cpu_to_le64(page_to_phys(*pages++)); ++ } ++ ++ iter.hdr->iova = cpu_to_le64(vram->iova); ++ ++ vaddr = etnaviv_gem_vaddr(&obj->base); ++ if (vaddr && !IS_ERR(vaddr)) ++ memcpy(iter.data, vaddr, obj->base.size); ++ ++ etnaviv_core_dump_header(&iter, ETDUMP_BUF_BO, iter.data + ++ obj->base.size); ++ } ++ ++ etnaviv_core_dump_header(&iter, ETDUMP_BUF_END, iter.data); ++ ++ dev_coredumpv(gpu->dev, iter.start, iter.data - iter.start, GFP_KERNEL); ++} +diff --git a/drivers/gpu/drm/etnaviv/etnaviv_dump.h b/drivers/gpu/drm/etnaviv/etnaviv_dump.h +new file mode 100644 +index 0000000..97f2f8d +--- /dev/null ++++ b/drivers/gpu/drm/etnaviv/etnaviv_dump.h +@@ -0,0 +1,54 @@ ++/* ++ * Copyright (C) 2015 Etnaviv Project ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ * ++ * Etnaviv devcoredump file definitions ++ */ ++#ifndef ETNAVIV_DUMP_H ++#define ETNAVIV_DUMP_H ++ ++#include ++ ++enum { ++ ETDUMP_MAGIC = 0x414e5445, ++ ETDUMP_BUF_REG = 0, ++ ETDUMP_BUF_MMU, ++ ETDUMP_BUF_RING, ++ ETDUMP_BUF_CMD, ++ ETDUMP_BUF_BOMAP, ++ ETDUMP_BUF_BO, ++ ETDUMP_BUF_END, ++}; ++ ++struct etnaviv_dump_object_header { ++ __le32 magic; ++ __le32 type; ++ __le32 file_offset; ++ __le32 file_size; ++ __le64 iova; ++ __le32 data[2]; ++}; ++ ++/* Registers object, an array of these */ ++struct etnaviv_dump_registers { ++ __le32 reg; ++ __le32 value; ++}; ++ ++#ifdef __KERNEL__ ++struct etnaviv_gpu; ++void etnaviv_core_dump(struct etnaviv_gpu *gpu); ++#endif ++ ++#endif +diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c +new file mode 100644 +index 0000000..8d6f859 +--- /dev/null ++++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c +@@ -0,0 +1,897 @@ ++/* ++ * Copyright (C) 2015 Etnaviv Project ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ */ ++ ++#include ++#include ++ ++#include "etnaviv_drv.h" ++#include "etnaviv_gem.h" ++#include "etnaviv_gpu.h" ++#include "etnaviv_mmu.h" ++ ++static void etnaviv_gem_scatter_map(struct etnaviv_gem_object *etnaviv_obj) ++{ ++ struct drm_device *dev = etnaviv_obj->base.dev; ++ struct sg_table *sgt = etnaviv_obj->sgt; ++ ++ /* ++ * For non-cached buffers, ensure the new pages are clean ++ * because display controller, GPU, etc. are not coherent. ++ */ ++ if (etnaviv_obj->flags & ETNA_BO_CACHE_MASK) ++ dma_map_sg(dev->dev, sgt->sgl, sgt->nents, DMA_BIDIRECTIONAL); ++} ++ ++static void etnaviv_gem_scatterlist_unmap(struct etnaviv_gem_object *etnaviv_obj) ++{ ++ struct drm_device *dev = etnaviv_obj->base.dev; ++ struct sg_table *sgt = etnaviv_obj->sgt; ++ ++ /* ++ * For non-cached buffers, ensure the new pages are clean ++ * because display controller, GPU, etc. are not coherent: ++ * ++ * WARNING: The DMA API does not support concurrent CPU ++ * and device access to the memory area. With BIDIRECTIONAL, ++ * we will clean the cache lines which overlap the region, ++ * and invalidate all cache lines (partially) contained in ++ * the region. ++ * ++ * If you have dirty data in the overlapping cache lines, ++ * that will corrupt the GPU-written data. If you have ++ * written into the remainder of the region, this can ++ * discard those writes. ++ */ ++ if (etnaviv_obj->flags & ETNA_BO_CACHE_MASK) ++ dma_unmap_sg(dev->dev, sgt->sgl, sgt->nents, DMA_BIDIRECTIONAL); ++} ++ ++/* called with etnaviv_obj->lock held */ ++static int etnaviv_gem_shmem_get_pages(struct etnaviv_gem_object *etnaviv_obj) ++{ ++ struct drm_device *dev = etnaviv_obj->base.dev; ++ struct page **p = drm_gem_get_pages(&etnaviv_obj->base); ++ ++ if (IS_ERR(p)) { ++ dev_err(dev->dev, "could not get pages: %ld\n", PTR_ERR(p)); ++ return PTR_ERR(p); ++ } ++ ++ etnaviv_obj->pages = p; ++ ++ return 0; ++} ++ ++static void put_pages(struct etnaviv_gem_object *etnaviv_obj) ++{ ++ if (etnaviv_obj->sgt) { ++ etnaviv_gem_scatterlist_unmap(etnaviv_obj); ++ sg_free_table(etnaviv_obj->sgt); ++ kfree(etnaviv_obj->sgt); ++ etnaviv_obj->sgt = NULL; ++ } ++ if (etnaviv_obj->pages) { ++ drm_gem_put_pages(&etnaviv_obj->base, etnaviv_obj->pages, ++ true, false); ++ ++ etnaviv_obj->pages = NULL; ++ } ++} ++ ++struct page **etnaviv_gem_get_pages(struct etnaviv_gem_object *etnaviv_obj) ++{ ++ int ret; ++ ++ lockdep_assert_held(&etnaviv_obj->lock); ++ ++ if (!etnaviv_obj->pages) { ++ ret = etnaviv_obj->ops->get_pages(etnaviv_obj); ++ if (ret < 0) ++ return ERR_PTR(ret); ++ } ++ ++ if (!etnaviv_obj->sgt) { ++ struct drm_device *dev = etnaviv_obj->base.dev; ++ int npages = etnaviv_obj->base.size >> PAGE_SHIFT; ++ struct sg_table *sgt; ++ ++ sgt = drm_prime_pages_to_sg(etnaviv_obj->pages, npages); ++ if (IS_ERR(sgt)) { ++ dev_err(dev->dev, "failed to allocate sgt: %ld\n", ++ PTR_ERR(sgt)); ++ return ERR_CAST(sgt); ++ } ++ ++ etnaviv_obj->sgt = sgt; ++ ++ etnaviv_gem_scatter_map(etnaviv_obj); ++ } ++ ++ return etnaviv_obj->pages; ++} ++ ++void etnaviv_gem_put_pages(struct etnaviv_gem_object *etnaviv_obj) ++{ ++ lockdep_assert_held(&etnaviv_obj->lock); ++ /* when we start tracking the pin count, then do something here */ ++} ++ ++static int etnaviv_gem_mmap_obj(struct drm_gem_object *obj, ++ struct vm_area_struct *vma) ++{ ++ struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); ++ pgprot_t vm_page_prot; ++ ++ vma->vm_flags &= ~VM_PFNMAP; ++ vma->vm_flags |= VM_MIXEDMAP; ++ ++ vm_page_prot = vm_get_page_prot(vma->vm_flags); ++ ++ if (etnaviv_obj->flags & ETNA_BO_WC) { ++ vma->vm_page_prot = pgprot_writecombine(vm_page_prot); ++ } else if (etnaviv_obj->flags & ETNA_BO_UNCACHED) { ++ vma->vm_page_prot = pgprot_noncached(vm_page_prot); ++ } else { ++ /* ++ * Shunt off cached objs to shmem file so they have their own ++ * address_space (so unmap_mapping_range does what we want, ++ * in particular in the case of mmap'd dmabufs) ++ */ ++ fput(vma->vm_file); ++ get_file(obj->filp); ++ vma->vm_pgoff = 0; ++ vma->vm_file = obj->filp; ++ ++ vma->vm_page_prot = vm_page_prot; ++ } ++ ++ return 0; ++} ++ ++int etnaviv_gem_mmap(struct file *filp, struct vm_area_struct *vma) ++{ ++ struct etnaviv_gem_object *obj; ++ int ret; ++ ++ ret = drm_gem_mmap(filp, vma); ++ if (ret) { ++ DBG("mmap failed: %d", ret); ++ return ret; ++ } ++ ++ obj = to_etnaviv_bo(vma->vm_private_data); ++ return etnaviv_gem_mmap_obj(vma->vm_private_data, vma); ++} ++ ++int etnaviv_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) ++{ ++ struct drm_gem_object *obj = vma->vm_private_data; ++ struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); ++ struct page **pages, *page; ++ pgoff_t pgoff; ++ int ret; ++ ++ /* ++ * Make sure we don't parallel update on a fault, nor move or remove ++ * something from beneath our feet. Note that vm_insert_page() is ++ * specifically coded to take care of this, so we don't have to. ++ */ ++ ret = mutex_lock_interruptible(&etnaviv_obj->lock); ++ if (ret) ++ goto out; ++ ++ /* make sure we have pages attached now */ ++ pages = etnaviv_gem_get_pages(etnaviv_obj); ++ mutex_unlock(&etnaviv_obj->lock); ++ ++ if (IS_ERR(pages)) { ++ ret = PTR_ERR(pages); ++ goto out; ++ } ++ ++ /* We don't use vmf->pgoff since that has the fake offset: */ ++ pgoff = ((unsigned long)vmf->virtual_address - ++ vma->vm_start) >> PAGE_SHIFT; ++ ++ page = pages[pgoff]; ++ ++ VERB("Inserting %p pfn %lx, pa %lx", vmf->virtual_address, ++ page_to_pfn(page), page_to_pfn(page) << PAGE_SHIFT); ++ ++ ret = vm_insert_page(vma, (unsigned long)vmf->virtual_address, page); ++ ++out: ++ switch (ret) { ++ case -EAGAIN: ++ case 0: ++ case -ERESTARTSYS: ++ case -EINTR: ++ case -EBUSY: ++ /* ++ * EBUSY is ok: this just means that another thread ++ * already did the job. ++ */ ++ return VM_FAULT_NOPAGE; ++ case -ENOMEM: ++ return VM_FAULT_OOM; ++ default: ++ return VM_FAULT_SIGBUS; ++ } ++} ++ ++int etnaviv_gem_mmap_offset(struct drm_gem_object *obj, u64 *offset) ++{ ++ int ret; ++ ++ /* Make it mmapable */ ++ ret = drm_gem_create_mmap_offset(obj); ++ if (ret) ++ dev_err(obj->dev->dev, "could not allocate mmap offset\n"); ++ else ++ *offset = drm_vma_node_offset_addr(&obj->vma_node); ++ ++ return ret; ++} ++ ++static struct etnaviv_vram_mapping * ++etnaviv_gem_get_vram_mapping(struct etnaviv_gem_object *obj, ++ struct etnaviv_iommu *mmu) ++{ ++ struct etnaviv_vram_mapping *mapping; ++ ++ list_for_each_entry(mapping, &obj->vram_list, obj_node) { ++ if (mapping->mmu == mmu) ++ return mapping; ++ } ++ ++ return NULL; ++} ++ ++int etnaviv_gem_get_iova(struct etnaviv_gpu *gpu, ++ struct drm_gem_object *obj, u32 *iova) ++{ ++ struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); ++ struct etnaviv_vram_mapping *mapping; ++ struct page **pages; ++ int ret = 0; ++ ++ mutex_lock(&etnaviv_obj->lock); ++ mapping = etnaviv_gem_get_vram_mapping(etnaviv_obj, gpu->mmu); ++ if (mapping) { ++ /* ++ * Holding the object lock prevents the use count changing ++ * beneath us. If the use count is zero, the MMU might be ++ * reaping this object, so take the lock and re-check that ++ * the MMU owns this mapping to close this race. ++ */ ++ if (mapping->use == 0) { ++ mutex_lock(&gpu->mmu->lock); ++ if (mapping->mmu == gpu->mmu) ++ mapping->use += 1; ++ else ++ mapping = NULL; ++ mutex_unlock(&gpu->mmu->lock); ++ if (mapping) ++ goto out; ++ } else { ++ mapping->use += 1; ++ goto out; ++ } ++ } ++ ++ pages = etnaviv_gem_get_pages(etnaviv_obj); ++ if (IS_ERR(pages)) { ++ ret = PTR_ERR(pages); ++ goto out; ++ } ++ ++ /* ++ * See if we have a reaped vram mapping we can re-use before ++ * allocating a fresh mapping. ++ */ ++ mapping = etnaviv_gem_get_vram_mapping(etnaviv_obj, NULL); ++ if (!mapping) { ++ mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); ++ if (!mapping) ++ return -ENOMEM; ++ ++ INIT_LIST_HEAD(&mapping->scan_node); ++ mapping->object = etnaviv_obj; ++ } else { ++ list_del(&mapping->obj_node); ++ } ++ ++ mapping->mmu = gpu->mmu; ++ mapping->use = 1; ++ ++ ret = etnaviv_iommu_map_gem(gpu->mmu, etnaviv_obj, gpu->memory_base, ++ mapping); ++ if (ret < 0) ++ kfree(mapping); ++ else ++ list_add_tail(&mapping->obj_node, &etnaviv_obj->vram_list); ++ ++out: ++ mutex_unlock(&etnaviv_obj->lock); ++ ++ if (!ret) { ++ /* Take a reference on the object */ ++ drm_gem_object_reference(obj); ++ *iova = mapping->iova; ++ } ++ ++ return ret; ++} ++ ++void etnaviv_gem_put_iova(struct etnaviv_gpu *gpu, struct drm_gem_object *obj) ++{ ++ struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); ++ struct etnaviv_vram_mapping *mapping; ++ ++ mutex_lock(&etnaviv_obj->lock); ++ mapping = etnaviv_gem_get_vram_mapping(etnaviv_obj, gpu->mmu); ++ ++ WARN_ON(mapping->use == 0); ++ mapping->use -= 1; ++ mutex_unlock(&etnaviv_obj->lock); ++ ++ drm_gem_object_unreference_unlocked(obj); ++} ++ ++void *etnaviv_gem_vaddr(struct drm_gem_object *obj) ++{ ++ struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); ++ ++ mutex_lock(&etnaviv_obj->lock); ++ if (!etnaviv_obj->vaddr) { ++ struct page **pages = etnaviv_gem_get_pages(etnaviv_obj); ++ ++ if (IS_ERR(pages)) ++ return ERR_CAST(pages); ++ ++ etnaviv_obj->vaddr = vmap(pages, obj->size >> PAGE_SHIFT, ++ VM_MAP, pgprot_writecombine(PAGE_KERNEL)); ++ } ++ mutex_unlock(&etnaviv_obj->lock); ++ ++ return etnaviv_obj->vaddr; ++} ++ ++static inline enum dma_data_direction etnaviv_op_to_dma_dir(u32 op) ++{ ++ if (op & ETNA_PREP_READ) ++ return DMA_FROM_DEVICE; ++ else if (op & ETNA_PREP_WRITE) ++ return DMA_TO_DEVICE; ++ else ++ return DMA_BIDIRECTIONAL; ++} ++ ++int etnaviv_gem_cpu_prep(struct drm_gem_object *obj, u32 op, ++ struct timespec *timeout) ++{ ++ struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); ++ struct drm_device *dev = obj->dev; ++ bool write = !!(op & ETNA_PREP_WRITE); ++ int ret; ++ ++ if (op & ETNA_PREP_NOSYNC) { ++ if (!reservation_object_test_signaled_rcu(etnaviv_obj->resv, ++ write)) ++ return -EBUSY; ++ } else { ++ unsigned long remain = etnaviv_timeout_to_jiffies(timeout); ++ ++ ret = reservation_object_wait_timeout_rcu(etnaviv_obj->resv, ++ write, true, remain); ++ if (ret <= 0) ++ return ret == 0 ? -ETIMEDOUT : ret; ++ } ++ ++ if (etnaviv_obj->flags & ETNA_BO_CACHED) { ++ if (!etnaviv_obj->sgt) { ++ void *ret; ++ ++ mutex_lock(&etnaviv_obj->lock); ++ ret = etnaviv_gem_get_pages(etnaviv_obj); ++ mutex_unlock(&etnaviv_obj->lock); ++ if (IS_ERR(ret)) ++ return PTR_ERR(ret); ++ } ++ ++ dma_sync_sg_for_cpu(dev->dev, etnaviv_obj->sgt->sgl, ++ etnaviv_obj->sgt->nents, ++ etnaviv_op_to_dma_dir(op)); ++ etnaviv_obj->last_cpu_prep_op = op; ++ } ++ ++ return 0; ++} ++ ++int etnaviv_gem_cpu_fini(struct drm_gem_object *obj) ++{ ++ struct drm_device *dev = obj->dev; ++ struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); ++ ++ if (etnaviv_obj->flags & ETNA_BO_CACHED) { ++ /* fini without a prep is almost certainly a userspace error */ ++ WARN_ON(etnaviv_obj->last_cpu_prep_op == 0); ++ dma_sync_sg_for_device(dev->dev, etnaviv_obj->sgt->sgl, ++ etnaviv_obj->sgt->nents, ++ etnaviv_op_to_dma_dir(etnaviv_obj->last_cpu_prep_op)); ++ etnaviv_obj->last_cpu_prep_op = 0; ++ } ++ ++ return 0; ++} ++ ++int etnaviv_gem_wait_bo(struct etnaviv_gpu *gpu, struct drm_gem_object *obj, ++ struct timespec *timeout) ++{ ++ struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); ++ ++ return etnaviv_gpu_wait_obj_inactive(gpu, etnaviv_obj, timeout); ++} ++ ++#ifdef CONFIG_DEBUG_FS ++static void etnaviv_gem_describe_fence(struct fence *fence, ++ const char *type, struct seq_file *m) ++{ ++ if (!test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->flags)) ++ seq_printf(m, "\t%9s: %s %s seq %u\n", ++ type, ++ fence->ops->get_driver_name(fence), ++ fence->ops->get_timeline_name(fence), ++ fence->seqno); ++} ++ ++static void etnaviv_gem_describe(struct drm_gem_object *obj, struct seq_file *m) ++{ ++ struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); ++ struct reservation_object *robj = etnaviv_obj->resv; ++ struct reservation_object_list *fobj; ++ struct fence *fence; ++ unsigned long off = drm_vma_node_start(&obj->vma_node); ++ ++ seq_printf(m, "%08x: %c %2d (%2d) %08lx %p %zd\n", ++ etnaviv_obj->flags, is_active(etnaviv_obj) ? 'A' : 'I', ++ obj->name, obj->refcount.refcount.counter, ++ off, etnaviv_obj->vaddr, obj->size); ++ ++ rcu_read_lock(); ++ fobj = rcu_dereference(robj->fence); ++ if (fobj) { ++ unsigned int i, shared_count = fobj->shared_count; ++ ++ for (i = 0; i < shared_count; i++) { ++ fence = rcu_dereference(fobj->shared[i]); ++ etnaviv_gem_describe_fence(fence, "Shared", m); ++ } ++ } ++ ++ fence = rcu_dereference(robj->fence_excl); ++ if (fence) ++ etnaviv_gem_describe_fence(fence, "Exclusive", m); ++ rcu_read_unlock(); ++} ++ ++void etnaviv_gem_describe_objects(struct etnaviv_drm_private *priv, ++ struct seq_file *m) ++{ ++ struct etnaviv_gem_object *etnaviv_obj; ++ int count = 0; ++ size_t size = 0; ++ ++ mutex_lock(&priv->gem_lock); ++ list_for_each_entry(etnaviv_obj, &priv->gem_list, gem_node) { ++ struct drm_gem_object *obj = &etnaviv_obj->base; ++ ++ seq_puts(m, " "); ++ etnaviv_gem_describe(obj, m); ++ count++; ++ size += obj->size; ++ } ++ mutex_unlock(&priv->gem_lock); ++ ++ seq_printf(m, "Total %d objects, %zu bytes\n", count, size); ++} ++#endif ++ ++static void etnaviv_gem_shmem_release(struct etnaviv_gem_object *etnaviv_obj) ++{ ++ if (etnaviv_obj->vaddr) ++ vunmap(etnaviv_obj->vaddr); ++ put_pages(etnaviv_obj); ++} ++ ++static const struct etnaviv_gem_ops etnaviv_gem_shmem_ops = { ++ .get_pages = etnaviv_gem_shmem_get_pages, ++ .release = etnaviv_gem_shmem_release, ++}; ++ ++void etnaviv_gem_free_object(struct drm_gem_object *obj) ++{ ++ struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); ++ struct etnaviv_vram_mapping *mapping, *tmp; ++ ++ /* object should not be active */ ++ WARN_ON(is_active(etnaviv_obj)); ++ ++ list_del(&etnaviv_obj->gem_node); ++ ++ list_for_each_entry_safe(mapping, tmp, &etnaviv_obj->vram_list, ++ obj_node) { ++ struct etnaviv_iommu *mmu = mapping->mmu; ++ ++ WARN_ON(mapping->use); ++ ++ if (mmu) ++ etnaviv_iommu_unmap_gem(mmu, mapping); ++ ++ list_del(&mapping->obj_node); ++ kfree(mapping); ++ } ++ ++ drm_gem_free_mmap_offset(obj); ++ etnaviv_obj->ops->release(etnaviv_obj); ++ if (etnaviv_obj->resv == &etnaviv_obj->_resv) ++ reservation_object_fini(&etnaviv_obj->_resv); ++ drm_gem_object_release(obj); ++ ++ kfree(etnaviv_obj); ++} ++ ++int etnaviv_gem_obj_add(struct drm_device *dev, struct drm_gem_object *obj) ++{ ++ struct etnaviv_drm_private *priv = dev->dev_private; ++ struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); ++ ++ mutex_lock(&priv->gem_lock); ++ list_add_tail(&etnaviv_obj->gem_node, &priv->gem_list); ++ mutex_unlock(&priv->gem_lock); ++ ++ return 0; ++} ++ ++static int etnaviv_gem_new_impl(struct drm_device *dev, u32 size, u32 flags, ++ struct reservation_object *robj, const struct etnaviv_gem_ops *ops, ++ struct drm_gem_object **obj) ++{ ++ struct etnaviv_gem_object *etnaviv_obj; ++ unsigned sz = sizeof(*etnaviv_obj); ++ bool valid = true; ++ ++ /* validate flags */ ++ switch (flags & ETNA_BO_CACHE_MASK) { ++ case ETNA_BO_UNCACHED: ++ case ETNA_BO_CACHED: ++ case ETNA_BO_WC: ++ break; ++ default: ++ valid = false; ++ } ++ ++ if (!valid) { ++ dev_err(dev->dev, "invalid cache flag: %x\n", ++ (flags & ETNA_BO_CACHE_MASK)); ++ return -EINVAL; ++ } ++ ++ etnaviv_obj = kzalloc(sz, GFP_KERNEL); ++ if (!etnaviv_obj) ++ return -ENOMEM; ++ ++ etnaviv_obj->flags = flags; ++ etnaviv_obj->ops = ops; ++ if (robj) { ++ etnaviv_obj->resv = robj; ++ } else { ++ etnaviv_obj->resv = &etnaviv_obj->_resv; ++ reservation_object_init(&etnaviv_obj->_resv); ++ } ++ ++ mutex_init(&etnaviv_obj->lock); ++ INIT_LIST_HEAD(&etnaviv_obj->vram_list); ++ ++ *obj = &etnaviv_obj->base; ++ ++ return 0; ++} ++ ++static struct drm_gem_object *__etnaviv_gem_new(struct drm_device *dev, ++ u32 size, u32 flags) ++{ ++ struct drm_gem_object *obj = NULL; ++ int ret; ++ ++ size = PAGE_ALIGN(size); ++ ++ ret = etnaviv_gem_new_impl(dev, size, flags, NULL, ++ &etnaviv_gem_shmem_ops, &obj); ++ if (ret) ++ goto fail; ++ ++ ret = drm_gem_object_init(dev, obj, size); ++ if (ret == 0) { ++ struct address_space *mapping; ++ ++ /* ++ * Our buffers are kept pinned, so allocating them ++ * from the MOVABLE zone is a really bad idea, and ++ * conflicts with CMA. See coments above new_inode() ++ * why this is required _and_ expected if you're ++ * going to pin these pages. ++ */ ++ mapping = file_inode(obj->filp)->i_mapping; ++ mapping_set_gfp_mask(mapping, GFP_HIGHUSER); ++ } ++ ++ if (ret) ++ goto fail; ++ ++ return obj; ++ ++fail: ++ if (obj) ++ drm_gem_object_unreference_unlocked(obj); ++ ++ return ERR_PTR(ret); ++} ++ ++/* convenience method to construct a GEM buffer object, and userspace handle */ ++int etnaviv_gem_new_handle(struct drm_device *dev, struct drm_file *file, ++ u32 size, u32 flags, u32 *handle) ++{ ++ struct drm_gem_object *obj; ++ int ret; ++ ++ obj = __etnaviv_gem_new(dev, size, flags); ++ if (IS_ERR(obj)) ++ return PTR_ERR(obj); ++ ++ ret = etnaviv_gem_obj_add(dev, obj); ++ if (ret < 0) { ++ drm_gem_object_unreference_unlocked(obj); ++ return ret; ++ } ++ ++ ret = drm_gem_handle_create(file, obj, handle); ++ ++ /* drop reference from allocate - handle holds it now */ ++ drm_gem_object_unreference_unlocked(obj); ++ ++ return ret; ++} ++ ++struct drm_gem_object *etnaviv_gem_new(struct drm_device *dev, ++ u32 size, u32 flags) ++{ ++ struct drm_gem_object *obj; ++ int ret; ++ ++ obj = __etnaviv_gem_new(dev, size, flags); ++ if (IS_ERR(obj)) ++ return obj; ++ ++ ret = etnaviv_gem_obj_add(dev, obj); ++ if (ret < 0) { ++ drm_gem_object_unreference_unlocked(obj); ++ return ERR_PTR(ret); ++ } ++ ++ return obj; ++} ++ ++int etnaviv_gem_new_private(struct drm_device *dev, size_t size, u32 flags, ++ struct reservation_object *robj, const struct etnaviv_gem_ops *ops, ++ struct etnaviv_gem_object **res) ++{ ++ struct drm_gem_object *obj; ++ int ret; ++ ++ ret = etnaviv_gem_new_impl(dev, size, flags, robj, ops, &obj); ++ if (ret) ++ return ret; ++ ++ drm_gem_private_object_init(dev, obj, size); ++ ++ *res = to_etnaviv_bo(obj); ++ ++ return 0; ++} ++ ++struct get_pages_work { ++ struct work_struct work; ++ struct mm_struct *mm; ++ struct task_struct *task; ++ struct etnaviv_gem_object *etnaviv_obj; ++}; ++ ++static struct page **etnaviv_gem_userptr_do_get_pages( ++ struct etnaviv_gem_object *etnaviv_obj, struct mm_struct *mm, struct task_struct *task) ++{ ++ int ret = 0, pinned, npages = etnaviv_obj->base.size >> PAGE_SHIFT; ++ struct page **pvec; ++ uintptr_t ptr; ++ ++ pvec = drm_malloc_ab(npages, sizeof(struct page *)); ++ if (!pvec) ++ return ERR_PTR(-ENOMEM); ++ ++ pinned = 0; ++ ptr = etnaviv_obj->userptr.ptr; ++ ++ down_read(&mm->mmap_sem); ++ while (pinned < npages) { ++ ret = get_user_pages(task, mm, ptr, npages - pinned, ++ !etnaviv_obj->userptr.ro, 0, ++ pvec + pinned, NULL); ++ if (ret < 0) ++ break; ++ ++ ptr += ret * PAGE_SIZE; ++ pinned += ret; ++ } ++ up_read(&mm->mmap_sem); ++ ++ if (ret < 0) { ++ release_pages(pvec, pinned, 0); ++ drm_free_large(pvec); ++ return ERR_PTR(ret); ++ } ++ ++ return pvec; ++} ++ ++static void __etnaviv_gem_userptr_get_pages(struct work_struct *_work) ++{ ++ struct get_pages_work *work = container_of(_work, typeof(*work), work); ++ struct etnaviv_gem_object *etnaviv_obj = work->etnaviv_obj; ++ struct page **pvec; ++ ++ pvec = etnaviv_gem_userptr_do_get_pages(etnaviv_obj, work->mm, work->task); ++ ++ mutex_lock(&etnaviv_obj->lock); ++ if (IS_ERR(pvec)) { ++ etnaviv_obj->userptr.work = ERR_CAST(pvec); ++ } else { ++ etnaviv_obj->userptr.work = NULL; ++ etnaviv_obj->pages = pvec; ++ } ++ ++ mutex_unlock(&etnaviv_obj->lock); ++ drm_gem_object_unreference_unlocked(&etnaviv_obj->base); ++ ++ mmput(work->mm); ++ put_task_struct(work->task); ++ kfree(work); ++} ++ ++static int etnaviv_gem_userptr_get_pages(struct etnaviv_gem_object *etnaviv_obj) ++{ ++ struct page **pvec = NULL; ++ struct get_pages_work *work; ++ struct mm_struct *mm; ++ int ret, pinned, npages = etnaviv_obj->base.size >> PAGE_SHIFT; ++ ++ if (etnaviv_obj->userptr.work) { ++ if (IS_ERR(etnaviv_obj->userptr.work)) { ++ ret = PTR_ERR(etnaviv_obj->userptr.work); ++ etnaviv_obj->userptr.work = NULL; ++ } else { ++ ret = -EAGAIN; ++ } ++ return ret; ++ } ++ ++ mm = get_task_mm(etnaviv_obj->userptr.task); ++ pinned = 0; ++ if (mm == current->mm) { ++ pvec = drm_malloc_ab(npages, sizeof(struct page *)); ++ if (!pvec) { ++ mmput(mm); ++ return -ENOMEM; ++ } ++ ++ pinned = __get_user_pages_fast(etnaviv_obj->userptr.ptr, npages, ++ !etnaviv_obj->userptr.ro, pvec); ++ if (pinned < 0) { ++ drm_free_large(pvec); ++ mmput(mm); ++ return pinned; ++ } ++ ++ if (pinned == npages) { ++ etnaviv_obj->pages = pvec; ++ mmput(mm); ++ return 0; ++ } ++ } ++ ++ release_pages(pvec, pinned, 0); ++ drm_free_large(pvec); ++ ++ work = kmalloc(sizeof(*work), GFP_KERNEL); ++ if (!work) { ++ mmput(mm); ++ return -ENOMEM; ++ } ++ ++ get_task_struct(current); ++ drm_gem_object_reference(&etnaviv_obj->base); ++ ++ work->mm = mm; ++ work->task = current; ++ work->etnaviv_obj = etnaviv_obj; ++ ++ etnaviv_obj->userptr.work = &work->work; ++ INIT_WORK(&work->work, __etnaviv_gem_userptr_get_pages); ++ ++ etnaviv_queue_work(etnaviv_obj->base.dev, &work->work); ++ ++ return -EAGAIN; ++} ++ ++static void etnaviv_gem_userptr_release(struct etnaviv_gem_object *etnaviv_obj) ++{ ++ if (etnaviv_obj->sgt) { ++ etnaviv_gem_scatterlist_unmap(etnaviv_obj); ++ sg_free_table(etnaviv_obj->sgt); ++ kfree(etnaviv_obj->sgt); ++ } ++ if (etnaviv_obj->pages) { ++ int npages = etnaviv_obj->base.size >> PAGE_SHIFT; ++ ++ release_pages(etnaviv_obj->pages, npages, 0); ++ drm_free_large(etnaviv_obj->pages); ++ } ++ put_task_struct(etnaviv_obj->userptr.task); ++} ++ ++static const struct etnaviv_gem_ops etnaviv_gem_userptr_ops = { ++ .get_pages = etnaviv_gem_userptr_get_pages, ++ .release = etnaviv_gem_userptr_release, ++}; ++ ++int etnaviv_gem_new_userptr(struct drm_device *dev, struct drm_file *file, ++ uintptr_t ptr, u32 size, u32 flags, u32 *handle) ++{ ++ struct etnaviv_gem_object *etnaviv_obj; ++ int ret; ++ ++ ret = etnaviv_gem_new_private(dev, size, ETNA_BO_CACHED, NULL, ++ &etnaviv_gem_userptr_ops, &etnaviv_obj); ++ if (ret) ++ return ret; ++ ++ etnaviv_obj->userptr.ptr = ptr; ++ etnaviv_obj->userptr.task = current; ++ etnaviv_obj->userptr.ro = !(flags & ETNA_USERPTR_WRITE); ++ get_task_struct(current); ++ ++ ret = etnaviv_gem_obj_add(dev, &etnaviv_obj->base); ++ if (ret) { ++ drm_gem_object_unreference_unlocked(&etnaviv_obj->base); ++ return ret; ++ } ++ ++ ret = drm_gem_handle_create(file, &etnaviv_obj->base, handle); ++ ++ /* drop reference from allocate - handle holds it now */ ++ drm_gem_object_unreference_unlocked(&etnaviv_obj->base); ++ ++ return ret; ++} +diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.h b/drivers/gpu/drm/etnaviv/etnaviv_gem.h +new file mode 100644 +index 0000000..a300b4b +--- /dev/null ++++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.h +@@ -0,0 +1,117 @@ ++/* ++ * Copyright (C) 2015 Etnaviv Project ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ */ ++ ++#ifndef __ETNAVIV_GEM_H__ ++#define __ETNAVIV_GEM_H__ ++ ++#include ++#include "etnaviv_drv.h" ++ ++struct etnaviv_gem_ops; ++struct etnaviv_gem_object; ++ ++struct etnaviv_gem_userptr { ++ uintptr_t ptr; ++ struct task_struct *task; ++ struct work_struct *work; ++ bool ro; ++}; ++ ++struct etnaviv_vram_mapping { ++ struct list_head obj_node; ++ struct list_head scan_node; ++ struct list_head mmu_node; ++ struct etnaviv_gem_object *object; ++ struct etnaviv_iommu *mmu; ++ struct drm_mm_node vram_node; ++ unsigned int use; ++ u32 iova; ++}; ++ ++struct etnaviv_gem_object { ++ struct drm_gem_object base; ++ const struct etnaviv_gem_ops *ops; ++ struct mutex lock; ++ ++ u32 flags; ++ ++ struct list_head gem_node; ++ struct etnaviv_gpu *gpu; /* non-null if active */ ++ atomic_t gpu_active; ++ u32 access; ++ ++ struct page **pages; ++ struct sg_table *sgt; ++ void *vaddr; ++ ++ /* normally (resv == &_resv) except for imported bo's */ ++ struct reservation_object *resv; ++ struct reservation_object _resv; ++ ++ struct list_head vram_list; ++ ++ /* cache maintenance */ ++ u32 last_cpu_prep_op; ++ ++ struct etnaviv_gem_userptr userptr; ++}; ++ ++static inline ++struct etnaviv_gem_object *to_etnaviv_bo(struct drm_gem_object *obj) ++{ ++ return container_of(obj, struct etnaviv_gem_object, base); ++} ++ ++struct etnaviv_gem_ops { ++ int (*get_pages)(struct etnaviv_gem_object *); ++ void (*release)(struct etnaviv_gem_object *); ++}; ++ ++static inline bool is_active(struct etnaviv_gem_object *etnaviv_obj) ++{ ++ return atomic_read(&etnaviv_obj->gpu_active) != 0; ++} ++ ++#define MAX_CMDS 4 ++ ++/* Created per submit-ioctl, to track bo's and cmdstream bufs, etc, ++ * associated with the cmdstream submission for synchronization (and ++ * make it easier to unwind when things go wrong, etc). This only ++ * lasts for the duration of the submit-ioctl. ++ */ ++struct etnaviv_gem_submit { ++ struct drm_device *dev; ++ struct etnaviv_gpu *gpu; ++ struct ww_acquire_ctx ticket; ++ u32 fence; ++ unsigned int nr_bos; ++ struct { ++ u32 flags; ++ struct etnaviv_gem_object *obj; ++ u32 iova; ++ } bos[0]; ++}; ++ ++int etnaviv_gem_wait_bo(struct etnaviv_gpu *gpu, struct drm_gem_object *obj, ++ struct timespec *timeout); ++int etnaviv_gem_new_private(struct drm_device *dev, size_t size, u32 flags, ++ struct reservation_object *robj, const struct etnaviv_gem_ops *ops, ++ struct etnaviv_gem_object **res); ++int etnaviv_gem_obj_add(struct drm_device *dev, struct drm_gem_object *obj); ++struct page **etnaviv_gem_get_pages(struct etnaviv_gem_object *obj); ++void etnaviv_gem_put_pages(struct etnaviv_gem_object *obj); ++ ++#endif /* __ETNAVIV_GEM_H__ */ +diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c +new file mode 100644 +index 0000000..e94db4f +--- /dev/null ++++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_prime.c +@@ -0,0 +1,122 @@ ++/* ++ * Copyright (C) 2013 Red Hat ++ * Author: Rob Clark ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ */ ++ ++#include ++#include "etnaviv_drv.h" ++#include "etnaviv_gem.h" ++ ++ ++struct sg_table *etnaviv_gem_prime_get_sg_table(struct drm_gem_object *obj) ++{ ++ struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); ++ ++ BUG_ON(!etnaviv_obj->sgt); /* should have already pinned! */ ++ ++ return etnaviv_obj->sgt; ++} ++ ++void *etnaviv_gem_prime_vmap(struct drm_gem_object *obj) ++{ ++ return etnaviv_gem_vaddr(obj); ++} ++ ++void etnaviv_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) ++{ ++ /* TODO msm_gem_vunmap() */ ++} ++ ++int etnaviv_gem_prime_pin(struct drm_gem_object *obj) ++{ ++ if (!obj->import_attach) { ++ struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); ++ ++ mutex_lock(&etnaviv_obj->lock); ++ etnaviv_gem_get_pages(etnaviv_obj); ++ mutex_unlock(&etnaviv_obj->lock); ++ } ++ return 0; ++} ++ ++void etnaviv_gem_prime_unpin(struct drm_gem_object *obj) ++{ ++ if (!obj->import_attach) { ++ struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); ++ ++ mutex_lock(&etnaviv_obj->lock); ++ etnaviv_gem_put_pages(to_etnaviv_bo(obj)); ++ mutex_unlock(&etnaviv_obj->lock); ++ } ++} ++ ++static void etnaviv_gem_prime_release(struct etnaviv_gem_object *etnaviv_obj) ++{ ++ if (etnaviv_obj->vaddr) ++ dma_buf_vunmap(etnaviv_obj->base.import_attach->dmabuf, ++ etnaviv_obj->vaddr); ++ ++ /* Don't drop the pages for imported dmabuf, as they are not ++ * ours, just free the array we allocated: ++ */ ++ if (etnaviv_obj->pages) ++ drm_free_large(etnaviv_obj->pages); ++ ++ drm_prime_gem_destroy(&etnaviv_obj->base, etnaviv_obj->sgt); ++} ++ ++static const struct etnaviv_gem_ops etnaviv_gem_prime_ops = { ++ /* .get_pages should never be called */ ++ .release = etnaviv_gem_prime_release, ++}; ++ ++struct drm_gem_object *etnaviv_gem_prime_import_sg_table(struct drm_device *dev, ++ struct dma_buf_attachment *attach, struct sg_table *sgt) ++{ ++ struct etnaviv_gem_object *etnaviv_obj; ++ size_t size = PAGE_ALIGN(attach->dmabuf->size); ++ int ret, npages; ++ ++ ret = etnaviv_gem_new_private(dev, size, ETNA_BO_WC, ++ attach->dmabuf->resv, ++ &etnaviv_gem_prime_ops, &etnaviv_obj); ++ if (ret < 0) ++ return ERR_PTR(ret); ++ ++ npages = size / PAGE_SIZE; ++ ++ etnaviv_obj->sgt = sgt; ++ etnaviv_obj->pages = drm_malloc_ab(npages, sizeof(struct page *)); ++ if (!etnaviv_obj->pages) { ++ ret = -ENOMEM; ++ goto fail; ++ } ++ ++ ret = drm_prime_sg_to_page_addr_arrays(sgt, etnaviv_obj->pages, ++ NULL, npages); ++ if (ret) ++ goto fail; ++ ++ ret = etnaviv_gem_obj_add(dev, &etnaviv_obj->base); ++ if (ret) ++ goto fail; ++ ++ return &etnaviv_obj->base; ++ ++fail: ++ drm_gem_object_unreference_unlocked(&etnaviv_obj->base); ++ ++ return ERR_PTR(ret); ++} +diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c +new file mode 100644 +index 0000000..1aba01a +--- /dev/null ++++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c +@@ -0,0 +1,443 @@ ++/* ++ * Copyright (C) 2015 Etnaviv Project ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ */ ++ ++#include ++#include "etnaviv_drv.h" ++#include "etnaviv_gpu.h" ++#include "etnaviv_gem.h" ++ ++/* ++ * Cmdstream submission: ++ */ ++ ++#define BO_INVALID_FLAGS ~(ETNA_SUBMIT_BO_READ | ETNA_SUBMIT_BO_WRITE) ++/* make sure these don't conflict w/ ETNAVIV_SUBMIT_BO_x */ ++#define BO_LOCKED 0x4000 ++#define BO_PINNED 0x2000 ++ ++static inline void __user *to_user_ptr(u64 address) ++{ ++ return (void __user *)(uintptr_t)address; ++} ++ ++static struct etnaviv_gem_submit *submit_create(struct drm_device *dev, ++ struct etnaviv_gpu *gpu, size_t nr) ++{ ++ struct etnaviv_gem_submit *submit; ++ size_t sz = size_vstruct(nr, sizeof(submit->bos[0]), sizeof(*submit)); ++ ++ submit = kmalloc(sz, GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY); ++ if (submit) { ++ submit->dev = dev; ++ submit->gpu = gpu; ++ ++ /* initially, until copy_from_user() and bo lookup succeeds: */ ++ submit->nr_bos = 0; ++ ++ ww_acquire_init(&submit->ticket, &reservation_ww_class); ++ } ++ ++ return submit; ++} ++ ++static int submit_lookup_objects(struct etnaviv_gem_submit *submit, ++ struct drm_file *file, struct drm_etnaviv_gem_submit_bo *submit_bos, ++ unsigned nr_bos) ++{ ++ struct drm_etnaviv_gem_submit_bo *bo; ++ unsigned i; ++ int ret = 0; ++ ++ spin_lock(&file->table_lock); ++ ++ for (i = 0, bo = submit_bos; i < nr_bos; i++, bo++) { ++ struct drm_gem_object *obj; ++ ++ if (bo->flags & BO_INVALID_FLAGS) { ++ DRM_ERROR("invalid flags: %x\n", bo->flags); ++ ret = -EINVAL; ++ goto out_unlock; ++ } ++ ++ submit->bos[i].flags = bo->flags; ++ ++ /* normally use drm_gem_object_lookup(), but for bulk lookup ++ * all under single table_lock just hit object_idr directly: ++ */ ++ obj = idr_find(&file->object_idr, bo->handle); ++ if (!obj) { ++ DRM_ERROR("invalid handle %u at index %u\n", ++ bo->handle, i); ++ ret = -EINVAL; ++ goto out_unlock; ++ } ++ ++ /* ++ * Take a refcount on the object. The file table lock ++ * prevents the object_idr's refcount on this being dropped. ++ */ ++ drm_gem_object_reference(obj); ++ ++ submit->bos[i].obj = to_etnaviv_bo(obj); ++ } ++ ++out_unlock: ++ submit->nr_bos = i; ++ spin_unlock(&file->table_lock); ++ ++ return ret; ++} ++ ++static void submit_unlock_object(struct etnaviv_gem_submit *submit, int i) ++{ ++ if (submit->bos[i].flags & BO_LOCKED) { ++ struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj; ++ ++ ww_mutex_unlock(&etnaviv_obj->resv->lock); ++ submit->bos[i].flags &= ~BO_LOCKED; ++ } ++} ++ ++static int submit_lock_objects(struct etnaviv_gem_submit *submit) ++{ ++ int contended, slow_locked = -1, i, ret = 0; ++ ++retry: ++ for (i = 0; i < submit->nr_bos; i++) { ++ struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj; ++ ++ if (slow_locked == i) ++ slow_locked = -1; ++ ++ contended = i; ++ ++ if (!(submit->bos[i].flags & BO_LOCKED)) { ++ ret = ww_mutex_lock_interruptible(&etnaviv_obj->resv->lock, ++ &submit->ticket); ++ if (ret == -EALREADY) ++ DRM_ERROR("BO at index %u already on submit list\n", ++ i); ++ if (ret) ++ goto fail; ++ submit->bos[i].flags |= BO_LOCKED; ++ } ++ } ++ ++ ww_acquire_done(&submit->ticket); ++ ++ return 0; ++ ++fail: ++ for (; i >= 0; i--) ++ submit_unlock_object(submit, i); ++ ++ if (slow_locked > 0) ++ submit_unlock_object(submit, slow_locked); ++ ++ if (ret == -EDEADLK) { ++ struct etnaviv_gem_object *etnaviv_obj; ++ ++ etnaviv_obj = submit->bos[contended].obj; ++ ++ /* we lost out in a seqno race, lock and retry.. */ ++ ret = ww_mutex_lock_slow_interruptible(&etnaviv_obj->resv->lock, ++ &submit->ticket); ++ if (!ret) { ++ submit->bos[contended].flags |= BO_LOCKED; ++ slow_locked = contended; ++ goto retry; ++ } ++ } ++ ++ return ret; ++} ++ ++static int submit_fence_sync(const struct etnaviv_gem_submit *submit) ++{ ++ unsigned int context = submit->gpu->fence_context; ++ int i, ret = 0; ++ ++ for (i = 0; i < submit->nr_bos; i++) { ++ struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj; ++ bool write = submit->bos[i].flags & ETNA_SUBMIT_BO_WRITE; ++ ++ ret = etnaviv_gpu_fence_sync_obj(etnaviv_obj, context, write); ++ if (ret) ++ break; ++ } ++ ++ return ret; ++} ++ ++static void submit_unpin_objects(struct etnaviv_gem_submit *submit) ++{ ++ int i; ++ ++ for (i = 0; i < submit->nr_bos; i++) { ++ struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj; ++ ++ if (submit->bos[i].flags & BO_PINNED) ++ etnaviv_gem_put_iova(submit->gpu, &etnaviv_obj->base); ++ ++ submit->bos[i].iova = 0; ++ submit->bos[i].flags &= ~BO_PINNED; ++ } ++} ++ ++static int submit_pin_objects(struct etnaviv_gem_submit *submit) ++{ ++ int i, ret = 0; ++ ++ for (i = 0; i < submit->nr_bos; i++) { ++ struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj; ++ u32 iova; ++ ++ ret = etnaviv_gem_get_iova(submit->gpu, &etnaviv_obj->base, ++ &iova); ++ if (ret) ++ break; ++ ++ submit->bos[i].flags |= BO_PINNED; ++ submit->bos[i].iova = iova; ++ } ++ ++ return ret; ++} ++ ++static int submit_bo(struct etnaviv_gem_submit *submit, u32 idx, ++ struct etnaviv_gem_object **obj, u32 *iova) ++{ ++ if (idx >= submit->nr_bos) { ++ DRM_ERROR("invalid buffer index: %u (out of %u)\n", ++ idx, submit->nr_bos); ++ return -EINVAL; ++ } ++ ++ if (obj) ++ *obj = submit->bos[idx].obj; ++ if (iova) ++ *iova = submit->bos[idx].iova; ++ ++ return 0; ++} ++ ++/* process the reloc's and patch up the cmdstream as needed: */ ++static int submit_reloc(struct etnaviv_gem_submit *submit, void *stream, ++ u32 size, const struct drm_etnaviv_gem_submit_reloc *relocs, ++ u32 nr_relocs) ++{ ++ u32 i, last_offset = 0; ++ u32 *ptr = stream; ++ int ret; ++ ++ for (i = 0; i < nr_relocs; i++) { ++ const struct drm_etnaviv_gem_submit_reloc *r = relocs + i; ++ struct etnaviv_gem_object *bobj; ++ u32 iova, off; ++ ++ if (unlikely(r->flags)) { ++ DRM_ERROR("invalid reloc flags\n"); ++ return -EINVAL; ++ } ++ ++ if (r->submit_offset % 4) { ++ DRM_ERROR("non-aligned reloc offset: %u\n", ++ r->submit_offset); ++ return -EINVAL; ++ } ++ ++ /* offset in dwords: */ ++ off = r->submit_offset / 4; ++ ++ if ((off >= size ) || ++ (off < last_offset)) { ++ DRM_ERROR("invalid offset %u at reloc %u\n", off, i); ++ return -EINVAL; ++ } ++ ++ ret = submit_bo(submit, r->reloc_idx, &bobj, &iova); ++ if (ret) ++ return ret; ++ ++ if (r->reloc_offset >= ++ bobj->base.size - sizeof(*ptr)) { ++ DRM_ERROR("relocation %u outside object", i); ++ return -EINVAL; ++ } ++ ++ ptr[off] = iova + r->reloc_offset; ++ ++ last_offset = off; ++ } ++ ++ return 0; ++} ++ ++static void submit_cleanup(struct etnaviv_gem_submit *submit) ++{ ++ unsigned i; ++ ++ for (i = 0; i < submit->nr_bos; i++) { ++ struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj; ++ ++ submit_unlock_object(submit, i); ++ drm_gem_object_unreference_unlocked(&etnaviv_obj->base); ++ } ++ ++ ww_acquire_fini(&submit->ticket); ++ kfree(submit); ++} ++ ++int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data, ++ struct drm_file *file) ++{ ++ struct etnaviv_drm_private *priv = dev->dev_private; ++ struct drm_etnaviv_gem_submit *args = data; ++ struct drm_etnaviv_gem_submit_reloc *relocs; ++ struct drm_etnaviv_gem_submit_bo *bos; ++ struct etnaviv_gem_submit *submit; ++ struct etnaviv_cmdbuf *cmdbuf; ++ struct etnaviv_gpu *gpu; ++ void *stream; ++ int ret; ++ ++ if (args->pipe >= ETNA_MAX_PIPES) ++ return -EINVAL; ++ ++ gpu = priv->gpu[args->pipe]; ++ if (!gpu) ++ return -ENXIO; ++ ++ if (args->stream_size % 4) { ++ DRM_ERROR("non-aligned cmdstream buffer size: %u\n", ++ args->stream_size); ++ return -EINVAL; ++ } ++ ++ if (args->exec_state != ETNA_PIPE_3D && ++ args->exec_state != ETNA_PIPE_2D && ++ args->exec_state != ETNA_PIPE_VG) { ++ DRM_ERROR("invalid exec_state: 0x%x\n", args->exec_state); ++ return -EINVAL; ++ } ++ ++ /* ++ * Copy the command submission and bo array to kernel space in ++ * one go, and do this outside of any locks. ++ */ ++ bos = drm_malloc_ab(args->nr_bos, sizeof(*bos)); ++ relocs = drm_malloc_ab(args->nr_relocs, sizeof(*relocs)); ++ stream = drm_malloc_ab(1, args->stream_size); ++ cmdbuf = etnaviv_gpu_cmdbuf_new(gpu, ALIGN(args->stream_size, 8) + 8, ++ args->nr_bos); ++ if (!bos || !relocs || !stream || !cmdbuf) { ++ ret = -ENOMEM; ++ goto err_submit_cmds; ++ } ++ ++ cmdbuf->exec_state = args->exec_state; ++ cmdbuf->ctx = file->driver_priv; ++ ++ ret = copy_from_user(bos, to_user_ptr(args->bos), ++ args->nr_bos * sizeof(*bos)); ++ if (ret) { ++ ret = -EFAULT; ++ goto err_submit_cmds; ++ } ++ ++ ret = copy_from_user(relocs, to_user_ptr(args->relocs), ++ args->nr_relocs * sizeof(*relocs)); ++ if (ret) { ++ ret = -EFAULT; ++ goto err_submit_cmds; ++ } ++ ++ ret = copy_from_user(stream, to_user_ptr(args->stream), ++ args->stream_size); ++ if (ret) { ++ ret = -EFAULT; ++ goto err_submit_cmds; ++ } ++ ++ submit = submit_create(dev, gpu, args->nr_bos); ++ if (!submit) { ++ ret = -ENOMEM; ++ goto err_submit_cmds; ++ } ++ ++ ret = submit_lookup_objects(submit, file, bos, args->nr_bos); ++ if (ret) ++ goto err_submit_objects; ++ ++ ret = submit_lock_objects(submit); ++ if (ret) ++ goto err_submit_objects; ++ ++ if (!etnaviv_cmd_validate_one(gpu, stream, args->stream_size / 4, ++ relocs, args->nr_relocs)) { ++ ret = -EINVAL; ++ goto err_submit_objects; ++ } ++ ++ ret = submit_fence_sync(submit); ++ if (ret) ++ goto err_submit_objects; ++ ++ ret = submit_pin_objects(submit); ++ if (ret) ++ goto out; ++ ++ ret = submit_reloc(submit, stream, args->stream_size / 4, ++ relocs, args->nr_relocs); ++ if (ret) ++ goto out; ++ ++ memcpy(cmdbuf->vaddr, stream, args->stream_size); ++ cmdbuf->user_size = ALIGN(args->stream_size, 8); ++ ++ ret = etnaviv_gpu_submit(gpu, submit, cmdbuf); ++ if (ret == 0) ++ cmdbuf = NULL; ++ ++ args->fence = submit->fence; ++ ++out: ++ submit_unpin_objects(submit); ++ ++ /* ++ * If we're returning -EAGAIN, it may be due to the userptr code ++ * wanting to run its workqueue outside of any locks. Flush our ++ * workqueue to ensure that it is run in a timely manner. ++ */ ++ if (ret == -EAGAIN) ++ flush_workqueue(priv->wq); ++ ++err_submit_objects: ++ submit_cleanup(submit); ++ ++err_submit_cmds: ++ /* if we still own the cmdbuf */ ++ if (cmdbuf) ++ etnaviv_gpu_cmdbuf_free(cmdbuf); ++ if (stream) ++ drm_free_large(stream); ++ if (bos) ++ drm_free_large(bos); ++ if (relocs) ++ drm_free_large(relocs); ++ ++ return ret; ++} +diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +new file mode 100644 +index 0000000..d39093d +--- /dev/null ++++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +@@ -0,0 +1,1644 @@ ++/* ++ * Copyright (C) 2015 Etnaviv Project ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include "etnaviv_dump.h" ++#include "etnaviv_gpu.h" ++#include "etnaviv_gem.h" ++#include "etnaviv_mmu.h" ++#include "etnaviv_iommu.h" ++#include "etnaviv_iommu_v2.h" ++#include "common.xml.h" ++#include "state.xml.h" ++#include "state_hi.xml.h" ++#include "cmdstream.xml.h" ++ ++static const struct platform_device_id gpu_ids[] = { ++ { .name = "etnaviv-gpu,2d" }, ++ { }, ++}; ++ ++static bool etnaviv_dump_core = true; ++module_param_named(dump_core, etnaviv_dump_core, bool, 0600); ++ ++/* ++ * Driver functions: ++ */ ++ ++int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, u32 param, u64 *value) ++{ ++ switch (param) { ++ case ETNAVIV_PARAM_GPU_MODEL: ++ *value = gpu->identity.model; ++ break; ++ ++ case ETNAVIV_PARAM_GPU_REVISION: ++ *value = gpu->identity.revision; ++ break; ++ ++ case ETNAVIV_PARAM_GPU_FEATURES_0: ++ *value = gpu->identity.features; ++ break; ++ ++ case ETNAVIV_PARAM_GPU_FEATURES_1: ++ *value = gpu->identity.minor_features0; ++ break; ++ ++ case ETNAVIV_PARAM_GPU_FEATURES_2: ++ *value = gpu->identity.minor_features1; ++ break; ++ ++ case ETNAVIV_PARAM_GPU_FEATURES_3: ++ *value = gpu->identity.minor_features2; ++ break; ++ ++ case ETNAVIV_PARAM_GPU_FEATURES_4: ++ *value = gpu->identity.minor_features3; ++ break; ++ ++ case ETNAVIV_PARAM_GPU_STREAM_COUNT: ++ *value = gpu->identity.stream_count; ++ break; ++ ++ case ETNAVIV_PARAM_GPU_REGISTER_MAX: ++ *value = gpu->identity.register_max; ++ break; ++ ++ case ETNAVIV_PARAM_GPU_THREAD_COUNT: ++ *value = gpu->identity.thread_count; ++ break; ++ ++ case ETNAVIV_PARAM_GPU_VERTEX_CACHE_SIZE: ++ *value = gpu->identity.vertex_cache_size; ++ break; ++ ++ case ETNAVIV_PARAM_GPU_SHADER_CORE_COUNT: ++ *value = gpu->identity.shader_core_count; ++ break; ++ ++ case ETNAVIV_PARAM_GPU_PIXEL_PIPES: ++ *value = gpu->identity.pixel_pipes; ++ break; ++ ++ case ETNAVIV_PARAM_GPU_VERTEX_OUTPUT_BUFFER_SIZE: ++ *value = gpu->identity.vertex_output_buffer_size; ++ break; ++ ++ case ETNAVIV_PARAM_GPU_BUFFER_SIZE: ++ *value = gpu->identity.buffer_size; ++ break; ++ ++ case ETNAVIV_PARAM_GPU_INSTRUCTION_COUNT: ++ *value = gpu->identity.instruction_count; ++ break; ++ ++ case ETNAVIV_PARAM_GPU_NUM_CONSTANTS: ++ *value = gpu->identity.num_constants; ++ break; ++ ++ default: ++ DBG("%s: invalid param: %u", dev_name(gpu->dev), param); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static void etnaviv_hw_specs(struct etnaviv_gpu *gpu) ++{ ++ if (gpu->identity.minor_features0 & ++ chipMinorFeatures0_MORE_MINOR_FEATURES) { ++ u32 specs[2]; ++ ++ specs[0] = gpu_read(gpu, VIVS_HI_CHIP_SPECS); ++ specs[1] = gpu_read(gpu, VIVS_HI_CHIP_SPECS_2); ++ ++ gpu->identity.stream_count = ++ (specs[0] & VIVS_HI_CHIP_SPECS_STREAM_COUNT__MASK) ++ >> VIVS_HI_CHIP_SPECS_STREAM_COUNT__SHIFT; ++ gpu->identity.register_max = ++ (specs[0] & VIVS_HI_CHIP_SPECS_REGISTER_MAX__MASK) ++ >> VIVS_HI_CHIP_SPECS_REGISTER_MAX__SHIFT; ++ gpu->identity.thread_count = ++ (specs[0] & VIVS_HI_CHIP_SPECS_THREAD_COUNT__MASK) ++ >> VIVS_HI_CHIP_SPECS_THREAD_COUNT__SHIFT; ++ gpu->identity.vertex_cache_size = ++ (specs[0] & VIVS_HI_CHIP_SPECS_VERTEX_CACHE_SIZE__MASK) ++ >> VIVS_HI_CHIP_SPECS_VERTEX_CACHE_SIZE__SHIFT; ++ gpu->identity.shader_core_count = ++ (specs[0] & VIVS_HI_CHIP_SPECS_SHADER_CORE_COUNT__MASK) ++ >> VIVS_HI_CHIP_SPECS_SHADER_CORE_COUNT__SHIFT; ++ gpu->identity.pixel_pipes = ++ (specs[0] & VIVS_HI_CHIP_SPECS_PIXEL_PIPES__MASK) ++ >> VIVS_HI_CHIP_SPECS_PIXEL_PIPES__SHIFT; ++ gpu->identity.vertex_output_buffer_size = ++ (specs[0] & VIVS_HI_CHIP_SPECS_VERTEX_OUTPUT_BUFFER_SIZE__MASK) ++ >> VIVS_HI_CHIP_SPECS_VERTEX_OUTPUT_BUFFER_SIZE__SHIFT; ++ ++ gpu->identity.buffer_size = ++ (specs[1] & VIVS_HI_CHIP_SPECS_2_BUFFER_SIZE__MASK) ++ >> VIVS_HI_CHIP_SPECS_2_BUFFER_SIZE__SHIFT; ++ gpu->identity.instruction_count = ++ (specs[1] & VIVS_HI_CHIP_SPECS_2_INSTRUCTION_COUNT__MASK) ++ >> VIVS_HI_CHIP_SPECS_2_INSTRUCTION_COUNT__SHIFT; ++ gpu->identity.num_constants = ++ (specs[1] & VIVS_HI_CHIP_SPECS_2_NUM_CONSTANTS__MASK) ++ >> VIVS_HI_CHIP_SPECS_2_NUM_CONSTANTS__SHIFT; ++ } ++ ++ /* Fill in the stream count if not specified */ ++ if (gpu->identity.stream_count == 0) { ++ if (gpu->identity.model >= 0x1000) ++ gpu->identity.stream_count = 4; ++ else ++ gpu->identity.stream_count = 1; ++ } ++ ++ /* Convert the register max value */ ++ if (gpu->identity.register_max) ++ gpu->identity.register_max = 1 << gpu->identity.register_max; ++ else if (gpu->identity.model == 0x0400) ++ gpu->identity.register_max = 32; ++ else ++ gpu->identity.register_max = 64; ++ ++ /* Convert thread count */ ++ if (gpu->identity.thread_count) ++ gpu->identity.thread_count = 1 << gpu->identity.thread_count; ++ else if (gpu->identity.model == 0x0400) ++ gpu->identity.thread_count = 64; ++ else if (gpu->identity.model == 0x0500 || ++ gpu->identity.model == 0x0530) ++ gpu->identity.thread_count = 128; ++ else ++ gpu->identity.thread_count = 256; ++ ++ if (gpu->identity.vertex_cache_size == 0) ++ gpu->identity.vertex_cache_size = 8; ++ ++ if (gpu->identity.shader_core_count == 0) { ++ if (gpu->identity.model >= 0x1000) ++ gpu->identity.shader_core_count = 2; ++ else ++ gpu->identity.shader_core_count = 1; ++ } ++ ++ if (gpu->identity.pixel_pipes == 0) ++ gpu->identity.pixel_pipes = 1; ++ ++ /* Convert virtex buffer size */ ++ if (gpu->identity.vertex_output_buffer_size) { ++ gpu->identity.vertex_output_buffer_size = ++ 1 << gpu->identity.vertex_output_buffer_size; ++ } else if (gpu->identity.model == 0x0400) { ++ if (gpu->identity.revision < 0x4000) ++ gpu->identity.vertex_output_buffer_size = 512; ++ else if (gpu->identity.revision < 0x4200) ++ gpu->identity.vertex_output_buffer_size = 256; ++ else ++ gpu->identity.vertex_output_buffer_size = 128; ++ } else { ++ gpu->identity.vertex_output_buffer_size = 512; ++ } ++ ++ switch (gpu->identity.instruction_count) { ++ case 0: ++ if ((gpu->identity.model == 0x2000 && ++ gpu->identity.revision == 0x5108) || ++ gpu->identity.model == 0x880) ++ gpu->identity.instruction_count = 512; ++ else ++ gpu->identity.instruction_count = 256; ++ break; ++ ++ case 1: ++ gpu->identity.instruction_count = 1024; ++ break; ++ ++ case 2: ++ gpu->identity.instruction_count = 2048; ++ break; ++ ++ default: ++ gpu->identity.instruction_count = 256; ++ break; ++ } ++ ++ if (gpu->identity.num_constants == 0) ++ gpu->identity.num_constants = 168; ++} ++ ++static void etnaviv_hw_identify(struct etnaviv_gpu *gpu) ++{ ++ u32 chipIdentity; ++ ++ chipIdentity = gpu_read(gpu, VIVS_HI_CHIP_IDENTITY); ++ ++ /* Special case for older graphic cores. */ ++ if (VIVS_HI_CHIP_IDENTITY_FAMILY(chipIdentity) == 0x01) { ++ gpu->identity.model = 0x500; /* gc500 */ ++ gpu->identity.revision = VIVS_HI_CHIP_IDENTITY_REVISION(chipIdentity); ++ } else { ++ ++ gpu->identity.model = gpu_read(gpu, VIVS_HI_CHIP_MODEL); ++ gpu->identity.revision = gpu_read(gpu, VIVS_HI_CHIP_REV); ++ ++ /* ++ * !!!! HACK ALERT !!!! ++ * Because people change device IDs without letting software ++ * know about it - here is the hack to make it all look the ++ * same. Only for GC400 family. ++ */ ++ if ((gpu->identity.model & 0xff00) == 0x0400 && ++ gpu->identity.model != 0x0420) { ++ gpu->identity.model = gpu->identity.model & 0x0400; ++ } ++ ++ /* Another special case */ ++ if (gpu->identity.model == 0x300 && ++ gpu->identity.revision == 0x2201) { ++ u32 chipDate = gpu_read(gpu, VIVS_HI_CHIP_DATE); ++ u32 chipTime = gpu_read(gpu, VIVS_HI_CHIP_TIME); ++ ++ if (chipDate == 0x20080814 && chipTime == 0x12051100) { ++ /* ++ * This IP has an ECO; put the correct ++ * revision in it. ++ */ ++ gpu->identity.revision = 0x1051; ++ } ++ } ++ } ++ ++ dev_info(gpu->dev, "model: GC%x, revision: %x\n", ++ gpu->identity.model, gpu->identity.revision); ++ ++ gpu->identity.features = gpu_read(gpu, VIVS_HI_CHIP_FEATURE); ++ ++ /* Disable fast clear on GC700. */ ++ if (gpu->identity.model == 0x700) ++ gpu->identity.features &= ~chipFeatures_FAST_CLEAR; ++ ++ if ((gpu->identity.model == 0x500 && gpu->identity.revision < 2) || ++ (gpu->identity.model == 0x300 && gpu->identity.revision < 0x2000)) { ++ ++ /* ++ * GC500 rev 1.x and GC300 rev < 2.0 doesn't have these ++ * registers. ++ */ ++ gpu->identity.minor_features0 = 0; ++ gpu->identity.minor_features1 = 0; ++ gpu->identity.minor_features2 = 0; ++ gpu->identity.minor_features3 = 0; ++ } else ++ gpu->identity.minor_features0 = ++ gpu_read(gpu, VIVS_HI_CHIP_MINOR_FEATURE_0); ++ ++ if (gpu->identity.minor_features0 & ++ chipMinorFeatures0_MORE_MINOR_FEATURES) { ++ gpu->identity.minor_features1 = ++ gpu_read(gpu, VIVS_HI_CHIP_MINOR_FEATURE_1); ++ gpu->identity.minor_features2 = ++ gpu_read(gpu, VIVS_HI_CHIP_MINOR_FEATURE_2); ++ gpu->identity.minor_features3 = ++ gpu_read(gpu, VIVS_HI_CHIP_MINOR_FEATURE_3); ++ } ++ ++ /* GC600 idle register reports zero bits where modules aren't present */ ++ if (gpu->identity.model == chipModel_GC600) { ++ gpu->idle_mask = VIVS_HI_IDLE_STATE_TX | ++ VIVS_HI_IDLE_STATE_RA | ++ VIVS_HI_IDLE_STATE_SE | ++ VIVS_HI_IDLE_STATE_PA | ++ VIVS_HI_IDLE_STATE_SH | ++ VIVS_HI_IDLE_STATE_PE | ++ VIVS_HI_IDLE_STATE_DE | ++ VIVS_HI_IDLE_STATE_FE; ++ } else { ++ gpu->idle_mask = ~VIVS_HI_IDLE_STATE_AXI_LP; ++ } ++ ++ etnaviv_hw_specs(gpu); ++} ++ ++static void etnaviv_gpu_load_clock(struct etnaviv_gpu *gpu, u32 clock) ++{ ++ gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, clock | ++ VIVS_HI_CLOCK_CONTROL_FSCALE_CMD_LOAD); ++ gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, clock); ++} ++ ++static int etnaviv_hw_reset(struct etnaviv_gpu *gpu) ++{ ++ u32 control, idle; ++ unsigned long timeout; ++ bool failed = true; ++ ++ /* TODO ++ * ++ * - clock gating ++ * - puls eater ++ * - what about VG? ++ */ ++ ++ /* We hope that the GPU resets in under one second */ ++ timeout = jiffies + msecs_to_jiffies(1000); ++ ++ while (time_is_after_jiffies(timeout)) { ++ control = VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS | ++ VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(0x40); ++ ++ /* enable clock */ ++ etnaviv_gpu_load_clock(gpu, control); ++ ++ /* Wait for stable clock. Vivante's code waited for 1ms */ ++ usleep_range(1000, 10000); ++ ++ /* isolate the GPU. */ ++ control |= VIVS_HI_CLOCK_CONTROL_ISOLATE_GPU; ++ gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control); ++ ++ /* set soft reset. */ ++ control |= VIVS_HI_CLOCK_CONTROL_SOFT_RESET; ++ gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control); ++ ++ /* wait for reset. */ ++ msleep(1); ++ ++ /* reset soft reset bit. */ ++ control &= ~VIVS_HI_CLOCK_CONTROL_SOFT_RESET; ++ gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control); ++ ++ /* reset GPU isolation. */ ++ control &= ~VIVS_HI_CLOCK_CONTROL_ISOLATE_GPU; ++ gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control); ++ ++ /* read idle register. */ ++ idle = gpu_read(gpu, VIVS_HI_IDLE_STATE); ++ ++ /* try reseting again if FE it not idle */ ++ if ((idle & VIVS_HI_IDLE_STATE_FE) == 0) { ++ dev_dbg(gpu->dev, "FE is not idle\n"); ++ continue; ++ } ++ ++ /* read reset register. */ ++ control = gpu_read(gpu, VIVS_HI_CLOCK_CONTROL); ++ ++ /* is the GPU idle? */ ++ if (((control & VIVS_HI_CLOCK_CONTROL_IDLE_3D) == 0) || ++ ((control & VIVS_HI_CLOCK_CONTROL_IDLE_2D) == 0)) { ++ dev_dbg(gpu->dev, "GPU is not idle\n"); ++ continue; ++ } ++ ++ failed = false; ++ break; ++ } ++ ++ if (failed) { ++ idle = gpu_read(gpu, VIVS_HI_IDLE_STATE); ++ control = gpu_read(gpu, VIVS_HI_CLOCK_CONTROL); ++ ++ dev_err(gpu->dev, "GPU failed to reset: FE %sidle, 3D %sidle, 2D %sidle\n", ++ idle & VIVS_HI_IDLE_STATE_FE ? "" : "not ", ++ control & VIVS_HI_CLOCK_CONTROL_IDLE_3D ? "" : "not ", ++ control & VIVS_HI_CLOCK_CONTROL_IDLE_2D ? "" : "not "); ++ ++ return -EBUSY; ++ } ++ ++ /* We rely on the GPU running, so program the clock */ ++ control = VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS | ++ VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(0x40); ++ ++ /* enable clock */ ++ etnaviv_gpu_load_clock(gpu, control); ++ ++ return 0; ++} ++ ++static void etnaviv_gpu_hw_init(struct etnaviv_gpu *gpu) ++{ ++ u16 prefetch; ++ ++ if (gpu->identity.model == chipModel_GC320 && ++ gpu_read(gpu, VIVS_HI_CHIP_TIME) != 0x2062400 && ++ (gpu->identity.revision == 0x5007 || ++ gpu->identity.revision == 0x5220)) { ++ u32 mc_memory_debug; ++ ++ mc_memory_debug = gpu_read(gpu, VIVS_MC_DEBUG_MEMORY) & ~0xff; ++ ++ if (gpu->identity.revision == 0x5007) ++ mc_memory_debug |= 0x0c; ++ else ++ mc_memory_debug |= 0x08; ++ ++ gpu_write(gpu, VIVS_MC_DEBUG_MEMORY, mc_memory_debug); ++ } ++ ++ /* ++ * Update GPU AXI cache atttribute to "cacheable, no allocate". ++ * This is necessary to prevent the iMX6 SoC locking up. ++ */ ++ gpu_write(gpu, VIVS_HI_AXI_CONFIG, ++ VIVS_HI_AXI_CONFIG_AWCACHE(2) | ++ VIVS_HI_AXI_CONFIG_ARCACHE(2)); ++ ++ /* GC2000 rev 5108 needs a special bus config */ ++ if (gpu->identity.model == 0x2000 && gpu->identity.revision == 0x5108) { ++ u32 bus_config = gpu_read(gpu, VIVS_MC_BUS_CONFIG); ++ bus_config &= ~(VIVS_MC_BUS_CONFIG_FE_BUS_CONFIG__MASK | ++ VIVS_MC_BUS_CONFIG_TX_BUS_CONFIG__MASK); ++ bus_config |= VIVS_MC_BUS_CONFIG_FE_BUS_CONFIG(1) | ++ VIVS_MC_BUS_CONFIG_TX_BUS_CONFIG(0); ++ gpu_write(gpu, VIVS_MC_BUS_CONFIG, bus_config); ++ } ++ ++ /* set base addresses */ ++ gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_RA, gpu->memory_base); ++ gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_FE, gpu->memory_base); ++ gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_TX, gpu->memory_base); ++ gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PEZ, gpu->memory_base); ++ gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PE, gpu->memory_base); ++ ++ /* setup the MMU page table pointers */ ++ etnaviv_iommu_domain_restore(gpu, gpu->mmu->domain); ++ ++ /* Start command processor */ ++ prefetch = etnaviv_buffer_init(gpu); ++ ++ gpu_write(gpu, VIVS_HI_INTR_ENBL, ~0U); ++ gpu_write(gpu, VIVS_FE_COMMAND_ADDRESS, ++ gpu->buffer->paddr - gpu->memory_base); ++ gpu_write(gpu, VIVS_FE_COMMAND_CONTROL, ++ VIVS_FE_COMMAND_CONTROL_ENABLE | ++ VIVS_FE_COMMAND_CONTROL_PREFETCH(prefetch)); ++} ++ ++int etnaviv_gpu_init(struct etnaviv_gpu *gpu) ++{ ++ int ret, i; ++ struct iommu_domain *iommu; ++ enum etnaviv_iommu_version version; ++ bool mmuv2; ++ ++ ret = pm_runtime_get_sync(gpu->dev); ++ if (ret < 0) ++ return ret; ++ ++ etnaviv_hw_identify(gpu); ++ ++ if (gpu->identity.model == 0) { ++ dev_err(gpu->dev, "Unknown GPU model\n"); ++ pm_runtime_put_autosuspend(gpu->dev); ++ return -ENXIO; ++ } ++ ++ ret = etnaviv_hw_reset(gpu); ++ if (ret) ++ goto fail; ++ ++ /* Setup IOMMU.. eventually we will (I think) do this once per context ++ * and have separate page tables per context. For now, to keep things ++ * simple and to get something working, just use a single address space: ++ */ ++ mmuv2 = gpu->identity.minor_features1 & chipMinorFeatures1_MMU_VERSION; ++ dev_dbg(gpu->dev, "mmuv2: %d\n", mmuv2); ++ ++ if (!mmuv2) { ++ iommu = etnaviv_iommu_domain_alloc(gpu); ++ version = ETNAVIV_IOMMU_V1; ++ } else { ++ iommu = etnaviv_iommu_v2_domain_alloc(gpu); ++ version = ETNAVIV_IOMMU_V2; ++ } ++ ++ if (!iommu) { ++ ret = -ENOMEM; ++ goto fail; ++ } ++ ++ /* TODO: we will leak here memory - fix it! */ ++ ++ gpu->mmu = etnaviv_iommu_new(gpu, iommu, version); ++ if (!gpu->mmu) { ++ ret = -ENOMEM; ++ goto fail; ++ } ++ ++ /* Create buffer: */ ++ gpu->buffer = etnaviv_gpu_cmdbuf_new(gpu, PAGE_SIZE, 0); ++ if (!gpu->buffer) { ++ ret = -ENOMEM; ++ dev_err(gpu->dev, "could not create command buffer\n"); ++ goto fail; ++ } ++ if (gpu->buffer->paddr - gpu->memory_base > 0x80000000) { ++ ret = -EINVAL; ++ dev_err(gpu->dev, ++ "command buffer outside valid memory window\n"); ++ goto free_buffer; ++ } ++ ++ /* Setup event management */ ++ spin_lock_init(&gpu->event_spinlock); ++ init_completion(&gpu->event_free); ++ for (i = 0; i < ARRAY_SIZE(gpu->event); i++) { ++ gpu->event[i].used = false; ++ complete(&gpu->event_free); ++ } ++ ++ /* Now program the hardware */ ++ mutex_lock(&gpu->lock); ++ etnaviv_gpu_hw_init(gpu); ++ mutex_unlock(&gpu->lock); ++ ++ pm_runtime_mark_last_busy(gpu->dev); ++ pm_runtime_put_autosuspend(gpu->dev); ++ ++ return 0; ++ ++free_buffer: ++ etnaviv_gpu_cmdbuf_free(gpu->buffer); ++ gpu->buffer = NULL; ++fail: ++ pm_runtime_mark_last_busy(gpu->dev); ++ pm_runtime_put_autosuspend(gpu->dev); ++ ++ return ret; ++} ++ ++#ifdef CONFIG_DEBUG_FS ++struct dma_debug { ++ u32 address[2]; ++ u32 state[2]; ++}; ++ ++static void verify_dma(struct etnaviv_gpu *gpu, struct dma_debug *debug) ++{ ++ u32 i; ++ ++ debug->address[0] = gpu_read(gpu, VIVS_FE_DMA_ADDRESS); ++ debug->state[0] = gpu_read(gpu, VIVS_FE_DMA_DEBUG_STATE); ++ ++ for (i = 0; i < 500; i++) { ++ debug->address[1] = gpu_read(gpu, VIVS_FE_DMA_ADDRESS); ++ debug->state[1] = gpu_read(gpu, VIVS_FE_DMA_DEBUG_STATE); ++ ++ if (debug->address[0] != debug->address[1]) ++ break; ++ ++ if (debug->state[0] != debug->state[1]) ++ break; ++ } ++} ++ ++int etnaviv_gpu_debugfs(struct etnaviv_gpu *gpu, struct seq_file *m) ++{ ++ struct dma_debug debug; ++ u32 dma_lo, dma_hi, axi, idle; ++ int ret; ++ ++ seq_printf(m, "%s Status:\n", dev_name(gpu->dev)); ++ ++ ret = pm_runtime_get_sync(gpu->dev); ++ if (ret < 0) ++ return ret; ++ ++ dma_lo = gpu_read(gpu, VIVS_FE_DMA_LOW); ++ dma_hi = gpu_read(gpu, VIVS_FE_DMA_HIGH); ++ axi = gpu_read(gpu, VIVS_HI_AXI_STATUS); ++ idle = gpu_read(gpu, VIVS_HI_IDLE_STATE); ++ ++ verify_dma(gpu, &debug); ++ ++ seq_puts(m, "\tfeatures\n"); ++ seq_printf(m, "\t minor_features0: 0x%08x\n", ++ gpu->identity.minor_features0); ++ seq_printf(m, "\t minor_features1: 0x%08x\n", ++ gpu->identity.minor_features1); ++ seq_printf(m, "\t minor_features2: 0x%08x\n", ++ gpu->identity.minor_features2); ++ seq_printf(m, "\t minor_features3: 0x%08x\n", ++ gpu->identity.minor_features3); ++ ++ seq_puts(m, "\tspecs\n"); ++ seq_printf(m, "\t stream_count: %d\n", ++ gpu->identity.stream_count); ++ seq_printf(m, "\t register_max: %d\n", ++ gpu->identity.register_max); ++ seq_printf(m, "\t thread_count: %d\n", ++ gpu->identity.thread_count); ++ seq_printf(m, "\t vertex_cache_size: %d\n", ++ gpu->identity.vertex_cache_size); ++ seq_printf(m, "\t shader_core_count: %d\n", ++ gpu->identity.shader_core_count); ++ seq_printf(m, "\t pixel_pipes: %d\n", ++ gpu->identity.pixel_pipes); ++ seq_printf(m, "\t vertex_output_buffer_size: %d\n", ++ gpu->identity.vertex_output_buffer_size); ++ seq_printf(m, "\t buffer_size: %d\n", ++ gpu->identity.buffer_size); ++ seq_printf(m, "\t instruction_count: %d\n", ++ gpu->identity.instruction_count); ++ seq_printf(m, "\t num_constants: %d\n", ++ gpu->identity.num_constants); ++ ++ seq_printf(m, "\taxi: 0x%08x\n", axi); ++ seq_printf(m, "\tidle: 0x%08x\n", idle); ++ idle |= ~gpu->idle_mask & ~VIVS_HI_IDLE_STATE_AXI_LP; ++ if ((idle & VIVS_HI_IDLE_STATE_FE) == 0) ++ seq_puts(m, "\t FE is not idle\n"); ++ if ((idle & VIVS_HI_IDLE_STATE_DE) == 0) ++ seq_puts(m, "\t DE is not idle\n"); ++ if ((idle & VIVS_HI_IDLE_STATE_PE) == 0) ++ seq_puts(m, "\t PE is not idle\n"); ++ if ((idle & VIVS_HI_IDLE_STATE_SH) == 0) ++ seq_puts(m, "\t SH is not idle\n"); ++ if ((idle & VIVS_HI_IDLE_STATE_PA) == 0) ++ seq_puts(m, "\t PA is not idle\n"); ++ if ((idle & VIVS_HI_IDLE_STATE_SE) == 0) ++ seq_puts(m, "\t SE is not idle\n"); ++ if ((idle & VIVS_HI_IDLE_STATE_RA) == 0) ++ seq_puts(m, "\t RA is not idle\n"); ++ if ((idle & VIVS_HI_IDLE_STATE_TX) == 0) ++ seq_puts(m, "\t TX is not idle\n"); ++ if ((idle & VIVS_HI_IDLE_STATE_VG) == 0) ++ seq_puts(m, "\t VG is not idle\n"); ++ if ((idle & VIVS_HI_IDLE_STATE_IM) == 0) ++ seq_puts(m, "\t IM is not idle\n"); ++ if ((idle & VIVS_HI_IDLE_STATE_FP) == 0) ++ seq_puts(m, "\t FP is not idle\n"); ++ if ((idle & VIVS_HI_IDLE_STATE_TS) == 0) ++ seq_puts(m, "\t TS is not idle\n"); ++ if (idle & VIVS_HI_IDLE_STATE_AXI_LP) ++ seq_puts(m, "\t AXI low power mode\n"); ++ ++ if (gpu->identity.features & chipFeatures_DEBUG_MODE) { ++ u32 read0 = gpu_read(gpu, VIVS_MC_DEBUG_READ0); ++ u32 read1 = gpu_read(gpu, VIVS_MC_DEBUG_READ1); ++ u32 write = gpu_read(gpu, VIVS_MC_DEBUG_WRITE); ++ ++ seq_puts(m, "\tMC\n"); ++ seq_printf(m, "\t read0: 0x%08x\n", read0); ++ seq_printf(m, "\t read1: 0x%08x\n", read1); ++ seq_printf(m, "\t write: 0x%08x\n", write); ++ } ++ ++ seq_puts(m, "\tDMA "); ++ ++ if (debug.address[0] == debug.address[1] && ++ debug.state[0] == debug.state[1]) { ++ seq_puts(m, "seems to be stuck\n"); ++ } else if (debug.address[0] == debug.address[1]) { ++ seq_puts(m, "adress is constant\n"); ++ } else { ++ seq_puts(m, "is runing\n"); ++ } ++ ++ seq_printf(m, "\t address 0: 0x%08x\n", debug.address[0]); ++ seq_printf(m, "\t address 1: 0x%08x\n", debug.address[1]); ++ seq_printf(m, "\t state 0: 0x%08x\n", debug.state[0]); ++ seq_printf(m, "\t state 1: 0x%08x\n", debug.state[1]); ++ seq_printf(m, "\t last fetch 64 bit word: 0x%08x 0x%08x\n", ++ dma_lo, dma_hi); ++ ++ ret = 0; ++ ++ pm_runtime_mark_last_busy(gpu->dev); ++ pm_runtime_put_autosuspend(gpu->dev); ++ ++ return ret; ++} ++#endif ++ ++/* ++ * Power Management: ++ */ ++static int enable_clk(struct etnaviv_gpu *gpu) ++{ ++ if (gpu->clk_core) ++ clk_prepare_enable(gpu->clk_core); ++ if (gpu->clk_shader) ++ clk_prepare_enable(gpu->clk_shader); ++ ++ return 0; ++} ++ ++static int disable_clk(struct etnaviv_gpu *gpu) ++{ ++ if (gpu->clk_core) ++ clk_disable_unprepare(gpu->clk_core); ++ if (gpu->clk_shader) ++ clk_disable_unprepare(gpu->clk_shader); ++ ++ return 0; ++} ++ ++static int enable_axi(struct etnaviv_gpu *gpu) ++{ ++ if (gpu->clk_bus) ++ clk_prepare_enable(gpu->clk_bus); ++ ++ return 0; ++} ++ ++static int disable_axi(struct etnaviv_gpu *gpu) ++{ ++ if (gpu->clk_bus) ++ clk_disable_unprepare(gpu->clk_bus); ++ ++ return 0; ++} ++ ++/* ++ * Hangcheck detection for locked gpu: ++ */ ++static void recover_worker(struct work_struct *work) ++{ ++ struct etnaviv_gpu *gpu = container_of(work, struct etnaviv_gpu, ++ recover_work); ++ unsigned long flags; ++ unsigned int i; ++ ++ dev_err(gpu->dev, "hangcheck recover!\n"); ++ ++ if (pm_runtime_get_sync(gpu->dev) < 0) ++ return; ++ ++ mutex_lock(&gpu->lock); ++ ++ /* Only catch the first event, or when manually re-armed */ ++ if (etnaviv_dump_core) { ++ etnaviv_core_dump(gpu); ++ etnaviv_dump_core = false; ++ } ++ ++ etnaviv_hw_reset(gpu); ++ ++ /* complete all events, the GPU won't do it after the reset */ ++ spin_lock_irqsave(&gpu->event_spinlock, flags); ++ for (i = 0; i < ARRAY_SIZE(gpu->event); i++) { ++ if (!gpu->event[i].used) ++ continue; ++ fence_signal(gpu->event[i].fence); ++ gpu->event[i].fence = NULL; ++ gpu->event[i].used = false; ++ complete(&gpu->event_free); ++ /* ++ * Decrement the PM count for each stuck event. This is safe ++ * even in atomic context as we use ASYNC RPM here. ++ */ ++ pm_runtime_put_autosuspend(gpu->dev); ++ } ++ spin_unlock_irqrestore(&gpu->event_spinlock, flags); ++ gpu->completed_fence = gpu->active_fence; ++ ++ etnaviv_gpu_hw_init(gpu); ++ gpu->switch_context = true; ++ ++ mutex_unlock(&gpu->lock); ++ pm_runtime_mark_last_busy(gpu->dev); ++ pm_runtime_put_autosuspend(gpu->dev); ++ ++ /* Retire the buffer objects in a work */ ++ etnaviv_queue_work(gpu->drm, &gpu->retire_work); ++} ++ ++static void hangcheck_timer_reset(struct etnaviv_gpu *gpu) ++{ ++ DBG("%s", dev_name(gpu->dev)); ++ mod_timer(&gpu->hangcheck_timer, ++ round_jiffies_up(jiffies + DRM_ETNAVIV_HANGCHECK_JIFFIES)); ++} ++ ++static void hangcheck_handler(unsigned long data) ++{ ++ struct etnaviv_gpu *gpu = (struct etnaviv_gpu *)data; ++ u32 fence = gpu->completed_fence; ++ bool progress = false; ++ ++ if (fence != gpu->hangcheck_fence) { ++ gpu->hangcheck_fence = fence; ++ progress = true; ++ } ++ ++ if (!progress) { ++ u32 dma_addr = gpu_read(gpu, VIVS_FE_DMA_ADDRESS); ++ int change = dma_addr - gpu->hangcheck_dma_addr; ++ ++ if (change < 0 || change > 16) { ++ gpu->hangcheck_dma_addr = dma_addr; ++ progress = true; ++ } ++ } ++ ++ if (!progress && fence_after(gpu->active_fence, fence)) { ++ dev_err(gpu->dev, "hangcheck detected gpu lockup!\n"); ++ dev_err(gpu->dev, " completed fence: %u\n", fence); ++ dev_err(gpu->dev, " active fence: %u\n", ++ gpu->active_fence); ++ etnaviv_queue_work(gpu->drm, &gpu->recover_work); ++ } ++ ++ /* if still more pending work, reset the hangcheck timer: */ ++ if (fence_after(gpu->active_fence, gpu->hangcheck_fence)) ++ hangcheck_timer_reset(gpu); ++} ++ ++static void hangcheck_disable(struct etnaviv_gpu *gpu) ++{ ++ del_timer_sync(&gpu->hangcheck_timer); ++ cancel_work_sync(&gpu->recover_work); ++} ++ ++/* fence object management */ ++struct etnaviv_fence { ++ struct etnaviv_gpu *gpu; ++ struct fence base; ++}; ++ ++static inline struct etnaviv_fence *to_etnaviv_fence(struct fence *fence) ++{ ++ return container_of(fence, struct etnaviv_fence, base); ++} ++ ++static const char *etnaviv_fence_get_driver_name(struct fence *fence) ++{ ++ return "etnaviv"; ++} ++ ++static const char *etnaviv_fence_get_timeline_name(struct fence *fence) ++{ ++ struct etnaviv_fence *f = to_etnaviv_fence(fence); ++ ++ return dev_name(f->gpu->dev); ++} ++ ++static bool etnaviv_fence_enable_signaling(struct fence *fence) ++{ ++ return true; ++} ++ ++static bool etnaviv_fence_signaled(struct fence *fence) ++{ ++ struct etnaviv_fence *f = to_etnaviv_fence(fence); ++ ++ return fence_completed(f->gpu, f->base.seqno); ++} ++ ++static void etnaviv_fence_release(struct fence *fence) ++{ ++ struct etnaviv_fence *f = to_etnaviv_fence(fence); ++ ++ kfree_rcu(f, base.rcu); ++} ++ ++static const struct fence_ops etnaviv_fence_ops = { ++ .get_driver_name = etnaviv_fence_get_driver_name, ++ .get_timeline_name = etnaviv_fence_get_timeline_name, ++ .enable_signaling = etnaviv_fence_enable_signaling, ++ .signaled = etnaviv_fence_signaled, ++ .wait = fence_default_wait, ++ .release = etnaviv_fence_release, ++}; ++ ++static struct fence *etnaviv_gpu_fence_alloc(struct etnaviv_gpu *gpu) ++{ ++ struct etnaviv_fence *f; ++ ++ f = kzalloc(sizeof(*f), GFP_KERNEL); ++ if (!f) ++ return NULL; ++ ++ f->gpu = gpu; ++ ++ fence_init(&f->base, &etnaviv_fence_ops, &gpu->fence_spinlock, ++ gpu->fence_context, ++gpu->next_fence); ++ ++ return &f->base; ++} ++ ++int etnaviv_gpu_fence_sync_obj(struct etnaviv_gem_object *etnaviv_obj, ++ unsigned int context, bool exclusive) ++{ ++ struct reservation_object *robj = etnaviv_obj->resv; ++ struct reservation_object_list *fobj; ++ struct fence *fence; ++ int i, ret; ++ ++ if (!exclusive) { ++ ret = reservation_object_reserve_shared(robj); ++ if (ret) ++ return ret; ++ } ++ ++ /* ++ * If we have any shared fences, then the exclusive fence ++ * should be ignored as it will already have been signalled. ++ */ ++ fobj = reservation_object_get_list(robj); ++ if (!fobj || fobj->shared_count == 0) { ++ /* Wait on any existing exclusive fence which isn't our own */ ++ fence = reservation_object_get_excl(robj); ++ if (fence && fence->context != context) { ++ ret = fence_wait(fence, true); ++ if (ret) ++ return ret; ++ } ++ } ++ ++ if (!exclusive || !fobj) ++ return 0; ++ ++ for (i = 0; i < fobj->shared_count; i++) { ++ fence = rcu_dereference_protected(fobj->shared[i], ++ reservation_object_held(robj)); ++ if (fence->context != context) { ++ ret = fence_wait(fence, true); ++ if (ret) ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++/* ++ * event management: ++ */ ++ ++static unsigned int event_alloc(struct etnaviv_gpu *gpu) ++{ ++ unsigned long ret, flags; ++ unsigned int i, event = ~0U; ++ ++ ret = wait_for_completion_timeout(&gpu->event_free, ++ msecs_to_jiffies(10 * 10000)); ++ if (!ret) ++ dev_err(gpu->dev, "wait_for_completion_timeout failed"); ++ ++ spin_lock_irqsave(&gpu->event_spinlock, flags); ++ ++ /* find first free event */ ++ for (i = 0; i < ARRAY_SIZE(gpu->event); i++) { ++ if (gpu->event[i].used == false) { ++ gpu->event[i].used = true; ++ event = i; ++ break; ++ } ++ } ++ ++ spin_unlock_irqrestore(&gpu->event_spinlock, flags); ++ ++ return event; ++} ++ ++static void event_free(struct etnaviv_gpu *gpu, unsigned int event) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&gpu->event_spinlock, flags); ++ ++ if (gpu->event[event].used == false) { ++ dev_warn(gpu->dev, "event %u is already marked as free", ++ event); ++ spin_unlock_irqrestore(&gpu->event_spinlock, flags); ++ } else { ++ gpu->event[event].used = false; ++ spin_unlock_irqrestore(&gpu->event_spinlock, flags); ++ ++ complete(&gpu->event_free); ++ } ++} ++ ++/* ++ * Cmdstream submission/retirement: ++ */ ++ ++struct etnaviv_cmdbuf *etnaviv_gpu_cmdbuf_new(struct etnaviv_gpu *gpu, u32 size, ++ size_t nr_bos) ++{ ++ struct etnaviv_cmdbuf *cmdbuf; ++ size_t sz = size_vstruct(nr_bos, sizeof(cmdbuf->bo[0]), ++ sizeof(*cmdbuf)); ++ ++ cmdbuf = kzalloc(sz, GFP_KERNEL); ++ if (!cmdbuf) ++ return NULL; ++ ++ cmdbuf->vaddr = dma_alloc_writecombine(gpu->dev, size, &cmdbuf->paddr, ++ GFP_KERNEL); ++ if (!cmdbuf->vaddr) { ++ kfree(cmdbuf); ++ return NULL; ++ } ++ ++ cmdbuf->gpu = gpu; ++ cmdbuf->size = size; ++ ++ return cmdbuf; ++} ++ ++void etnaviv_gpu_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf) ++{ ++ dma_free_writecombine(cmdbuf->gpu->dev, cmdbuf->size, ++ cmdbuf->vaddr, cmdbuf->paddr); ++ kfree(cmdbuf); ++} ++ ++static void retire_worker(struct work_struct *work) ++{ ++ struct etnaviv_gpu *gpu = container_of(work, struct etnaviv_gpu, ++ retire_work); ++ u32 fence = gpu->completed_fence; ++ struct etnaviv_cmdbuf *cmdbuf, *tmp; ++ unsigned int i; ++ ++ mutex_lock(&gpu->lock); ++ list_for_each_entry_safe(cmdbuf, tmp, &gpu->active_cmd_list, node) { ++ if (!fence_is_signaled(cmdbuf->fence)) ++ break; ++ ++ list_del(&cmdbuf->node); ++ fence_put(cmdbuf->fence); ++ ++ for (i = 0; i < cmdbuf->nr_bos; i++) { ++ struct etnaviv_gem_object *etnaviv_obj = cmdbuf->bo[i]; ++ ++ atomic_dec(&etnaviv_obj->gpu_active); ++ /* drop the refcount taken in etnaviv_gpu_submit */ ++ etnaviv_gem_put_iova(gpu, &etnaviv_obj->base); ++ } ++ ++ etnaviv_gpu_cmdbuf_free(cmdbuf); ++ } ++ ++ gpu->retired_fence = fence; ++ ++ mutex_unlock(&gpu->lock); ++ ++ wake_up_all(&gpu->fence_event); ++} ++ ++int etnaviv_gpu_wait_fence_interruptible(struct etnaviv_gpu *gpu, ++ u32 fence, struct timespec *timeout) ++{ ++ int ret; ++ ++ if (fence_after(fence, gpu->next_fence)) { ++ DRM_ERROR("waiting on invalid fence: %u (of %u)\n", ++ fence, gpu->next_fence); ++ return -EINVAL; ++ } ++ ++ if (!timeout) { ++ /* No timeout was requested: just test for completion */ ++ ret = fence_completed(gpu, fence) ? 0 : -EBUSY; ++ } else { ++ unsigned long remaining = etnaviv_timeout_to_jiffies(timeout); ++ ++ ret = wait_event_interruptible_timeout(gpu->fence_event, ++ fence_completed(gpu, fence), ++ remaining); ++ if (ret == 0) { ++ DBG("timeout waiting for fence: %u (retired: %u completed: %u)", ++ fence, gpu->retired_fence, ++ gpu->completed_fence); ++ ret = -ETIMEDOUT; ++ } else if (ret != -ERESTARTSYS) { ++ ret = 0; ++ } ++ } ++ ++ return ret; ++} ++ ++/* ++ * Wait for an object to become inactive. This, on it's own, is not race ++ * free: the object is moved by the retire worker off the active list, and ++ * then the iova is put. Moreover, the object could be re-submitted just ++ * after we notice that it's become inactive. ++ * ++ * Although the retirement happens under the gpu lock, we don't want to hold ++ * that lock in this function while waiting. ++ */ ++int etnaviv_gpu_wait_obj_inactive(struct etnaviv_gpu *gpu, ++ struct etnaviv_gem_object *etnaviv_obj, struct timespec *timeout) ++{ ++ unsigned long remaining; ++ long ret; ++ ++ if (!timeout) ++ return !is_active(etnaviv_obj) ? 0 : -EBUSY; ++ ++ remaining = etnaviv_timeout_to_jiffies(timeout); ++ ++ ret = wait_event_interruptible_timeout(gpu->fence_event, ++ !is_active(etnaviv_obj), ++ remaining); ++ if (ret > 0) { ++ struct etnaviv_drm_private *priv = gpu->drm->dev_private; ++ ++ /* Synchronise with the retire worker */ ++ flush_workqueue(priv->wq); ++ return 0; ++ } else if (ret == -ERESTARTSYS) { ++ return -ERESTARTSYS; ++ } else { ++ return -ETIMEDOUT; ++ } ++} ++ ++int etnaviv_gpu_pm_get_sync(struct etnaviv_gpu *gpu) ++{ ++ return pm_runtime_get_sync(gpu->dev); ++} ++ ++void etnaviv_gpu_pm_put(struct etnaviv_gpu *gpu) ++{ ++ pm_runtime_mark_last_busy(gpu->dev); ++ pm_runtime_put_autosuspend(gpu->dev); ++} ++ ++/* add bo's to gpu's ring, and kick gpu: */ ++int etnaviv_gpu_submit(struct etnaviv_gpu *gpu, ++ struct etnaviv_gem_submit *submit, struct etnaviv_cmdbuf *cmdbuf) ++{ ++ struct fence *fence; ++ unsigned int event, i; ++ int ret; ++ ++ ret = etnaviv_gpu_pm_get_sync(gpu); ++ if (ret < 0) ++ return ret; ++ ++ mutex_lock(&gpu->lock); ++ ++ /* ++ * TODO ++ * ++ * - flush ++ * - data endian ++ * - prefetch ++ * ++ */ ++ ++ event = event_alloc(gpu); ++ if (unlikely(event == ~0U)) { ++ DRM_ERROR("no free event\n"); ++ ret = -EBUSY; ++ goto out_unlock; ++ } ++ ++ fence = etnaviv_gpu_fence_alloc(gpu); ++ if (!fence) { ++ event_free(gpu, event); ++ ret = -ENOMEM; ++ goto out_unlock; ++ } ++ ++ gpu->event[event].fence = fence; ++ submit->fence = fence->seqno; ++ gpu->active_fence = submit->fence; ++ ++ if (gpu->lastctx != cmdbuf->ctx) { ++ gpu->mmu->need_flush = true; ++ gpu->switch_context = true; ++ gpu->lastctx = cmdbuf->ctx; ++ } ++ ++ etnaviv_buffer_queue(gpu, event, cmdbuf); ++ ++ cmdbuf->fence = fence; ++ list_add_tail(&cmdbuf->node, &gpu->active_cmd_list); ++ ++ /* We're committed to adding this command buffer, hold a PM reference */ ++ pm_runtime_get_noresume(gpu->dev); ++ ++ for (i = 0; i < submit->nr_bos; i++) { ++ struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj; ++ u32 iova; ++ ++ /* Each cmdbuf takes a refcount on the iova */ ++ etnaviv_gem_get_iova(gpu, &etnaviv_obj->base, &iova); ++ cmdbuf->bo[i] = etnaviv_obj; ++ atomic_inc(&etnaviv_obj->gpu_active); ++ ++ if (submit->bos[i].flags & ETNA_SUBMIT_BO_WRITE) ++ reservation_object_add_excl_fence(etnaviv_obj->resv, ++ fence); ++ else ++ reservation_object_add_shared_fence(etnaviv_obj->resv, ++ fence); ++ } ++ cmdbuf->nr_bos = submit->nr_bos; ++ hangcheck_timer_reset(gpu); ++ ret = 0; ++ ++out_unlock: ++ mutex_unlock(&gpu->lock); ++ ++ etnaviv_gpu_pm_put(gpu); ++ ++ return ret; ++} ++ ++/* ++ * Init/Cleanup: ++ */ ++static irqreturn_t irq_handler(int irq, void *data) ++{ ++ struct etnaviv_gpu *gpu = data; ++ irqreturn_t ret = IRQ_NONE; ++ ++ u32 intr = gpu_read(gpu, VIVS_HI_INTR_ACKNOWLEDGE); ++ ++ if (intr != 0) { ++ int event; ++ ++ pm_runtime_mark_last_busy(gpu->dev); ++ ++ dev_dbg(gpu->dev, "intr 0x%08x\n", intr); ++ ++ if (intr & VIVS_HI_INTR_ACKNOWLEDGE_AXI_BUS_ERROR) { ++ dev_err(gpu->dev, "AXI bus error\n"); ++ intr &= ~VIVS_HI_INTR_ACKNOWLEDGE_AXI_BUS_ERROR; ++ } ++ ++ while ((event = ffs(intr)) != 0) { ++ struct fence *fence; ++ ++ event -= 1; ++ ++ intr &= ~(1 << event); ++ ++ dev_dbg(gpu->dev, "event %u\n", event); ++ ++ fence = gpu->event[event].fence; ++ gpu->event[event].fence = NULL; ++ fence_signal(fence); ++ ++ /* ++ * Events can be processed out of order. Eg, ++ * - allocate and queue event 0 ++ * - allocate event 1 ++ * - event 0 completes, we process it ++ * - allocate and queue event 0 ++ * - event 1 and event 0 complete ++ * we can end up processing event 0 first, then 1. ++ */ ++ if (fence_after(fence->seqno, gpu->completed_fence)) ++ gpu->completed_fence = fence->seqno; ++ ++ event_free(gpu, event); ++ ++ /* ++ * We need to balance the runtime PM count caused by ++ * each submission. Upon submission, we increment ++ * the runtime PM counter, and allocate one event. ++ * So here, we put the runtime PM count for each ++ * completed event. ++ */ ++ pm_runtime_put_autosuspend(gpu->dev); ++ } ++ ++ /* Retire the buffer objects in a work */ ++ etnaviv_queue_work(gpu->drm, &gpu->retire_work); ++ ++ ret = IRQ_HANDLED; ++ } ++ ++ return ret; ++} ++ ++static int etnaviv_gpu_clk_enable(struct etnaviv_gpu *gpu) ++{ ++ int ret; ++ ++ ret = enable_clk(gpu); ++ if (ret) ++ return ret; ++ ++ ret = enable_axi(gpu); ++ if (ret) { ++ disable_clk(gpu); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int etnaviv_gpu_clk_disable(struct etnaviv_gpu *gpu) ++{ ++ int ret; ++ ++ ret = disable_axi(gpu); ++ if (ret) ++ return ret; ++ ++ ret = disable_clk(gpu); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int etnaviv_gpu_hw_suspend(struct etnaviv_gpu *gpu) ++{ ++ if (gpu->buffer) { ++ unsigned long timeout; ++ ++ /* Replace the last WAIT with END */ ++ etnaviv_buffer_end(gpu); ++ ++ /* ++ * We know that only the FE is busy here, this should ++ * happen quickly (as the WAIT is only 200 cycles). If ++ * we fail, just warn and continue. ++ */ ++ timeout = jiffies + msecs_to_jiffies(100); ++ do { ++ u32 idle = gpu_read(gpu, VIVS_HI_IDLE_STATE); ++ ++ if ((idle & gpu->idle_mask) == gpu->idle_mask) ++ break; ++ ++ if (time_is_before_jiffies(timeout)) { ++ dev_warn(gpu->dev, ++ "timed out waiting for idle: idle=0x%x\n", ++ idle); ++ break; ++ } ++ ++ udelay(5); ++ } while (1); ++ } ++ ++ return etnaviv_gpu_clk_disable(gpu); ++} ++ ++#ifdef CONFIG_PM ++static int etnaviv_gpu_hw_resume(struct etnaviv_gpu *gpu) ++{ ++ u32 clock; ++ int ret; ++ ++ ret = mutex_lock_killable(&gpu->lock); ++ if (ret) ++ return ret; ++ ++ clock = VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS | ++ VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(0x40); ++ ++ etnaviv_gpu_load_clock(gpu, clock); ++ etnaviv_gpu_hw_init(gpu); ++ ++ gpu->switch_context = true; ++ ++ mutex_unlock(&gpu->lock); ++ ++ return 0; ++} ++#endif ++ ++static int etnaviv_gpu_bind(struct device *dev, struct device *master, ++ void *data) ++{ ++ struct drm_device *drm = data; ++ struct etnaviv_drm_private *priv = drm->dev_private; ++ struct etnaviv_gpu *gpu = dev_get_drvdata(dev); ++ int ret; ++ ++#ifdef CONFIG_PM ++ ret = pm_runtime_get_sync(gpu->dev); ++#else ++ ret = etnaviv_gpu_clk_enable(gpu); ++#endif ++ if (ret < 0) ++ return ret; ++ ++ gpu->drm = drm; ++ gpu->fence_context = fence_context_alloc(1); ++ spin_lock_init(&gpu->fence_spinlock); ++ ++ INIT_LIST_HEAD(&gpu->active_cmd_list); ++ INIT_WORK(&gpu->retire_work, retire_worker); ++ INIT_WORK(&gpu->recover_work, recover_worker); ++ init_waitqueue_head(&gpu->fence_event); ++ ++ setup_timer(&gpu->hangcheck_timer, hangcheck_handler, ++ (unsigned long)gpu); ++ ++ priv->gpu[priv->num_gpus++] = gpu; ++ ++ pm_runtime_mark_last_busy(gpu->dev); ++ pm_runtime_put_autosuspend(gpu->dev); ++ ++ return 0; ++} ++ ++static void etnaviv_gpu_unbind(struct device *dev, struct device *master, ++ void *data) ++{ ++ struct etnaviv_gpu *gpu = dev_get_drvdata(dev); ++ ++ DBG("%s", dev_name(gpu->dev)); ++ ++ hangcheck_disable(gpu); ++ ++#ifdef CONFIG_PM ++ pm_runtime_get_sync(gpu->dev); ++ pm_runtime_put_sync_suspend(gpu->dev); ++#else ++ etnaviv_gpu_hw_suspend(gpu); ++#endif ++ ++ if (gpu->buffer) { ++ etnaviv_gpu_cmdbuf_free(gpu->buffer); ++ gpu->buffer = NULL; ++ } ++ ++ if (gpu->mmu) { ++ etnaviv_iommu_destroy(gpu->mmu); ++ gpu->mmu = NULL; ++ } ++ ++ gpu->drm = NULL; ++} ++ ++static const struct component_ops gpu_ops = { ++ .bind = etnaviv_gpu_bind, ++ .unbind = etnaviv_gpu_unbind, ++}; ++ ++static const struct of_device_id etnaviv_gpu_match[] = { ++ { ++ .compatible = "vivante,gc" ++ }, ++ { /* sentinel */ } ++}; ++ ++static int etnaviv_gpu_platform_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct etnaviv_gpu *gpu; ++ int err = 0; ++ ++ gpu = devm_kzalloc(dev, sizeof(*gpu), GFP_KERNEL); ++ if (!gpu) ++ return -ENOMEM; ++ ++ gpu->dev = &pdev->dev; ++ mutex_init(&gpu->lock); ++ ++ /* ++ * Set the GPU base address to the start of physical memory. This ++ * ensures that if we have up to 2GB, the v1 MMU can address the ++ * highest memory. This is important as command buffers may be ++ * allocated outside of this limit. ++ */ ++ gpu->memory_base = PHYS_OFFSET; ++ ++ /* Map registers: */ ++ gpu->mmio = etnaviv_ioremap(pdev, NULL, dev_name(gpu->dev)); ++ if (IS_ERR(gpu->mmio)) ++ return PTR_ERR(gpu->mmio); ++ ++ /* Get Interrupt: */ ++ gpu->irq = platform_get_irq(pdev, 0); ++ if (gpu->irq < 0) { ++ err = gpu->irq; ++ dev_err(dev, "failed to get irq: %d\n", err); ++ goto fail; ++ } ++ ++ err = devm_request_irq(&pdev->dev, gpu->irq, irq_handler, 0, ++ dev_name(gpu->dev), gpu); ++ if (err) { ++ dev_err(dev, "failed to request IRQ%u: %d\n", gpu->irq, err); ++ goto fail; ++ } ++ ++ /* Get Clocks: */ ++ gpu->clk_bus = devm_clk_get(&pdev->dev, "bus"); ++ DBG("clk_bus: %p", gpu->clk_bus); ++ if (IS_ERR(gpu->clk_bus)) ++ gpu->clk_bus = NULL; ++ ++ gpu->clk_core = devm_clk_get(&pdev->dev, "core"); ++ DBG("clk_core: %p", gpu->clk_core); ++ if (IS_ERR(gpu->clk_core)) ++ gpu->clk_core = NULL; ++ ++ gpu->clk_shader = devm_clk_get(&pdev->dev, "shader"); ++ DBG("clk_shader: %p", gpu->clk_shader); ++ if (IS_ERR(gpu->clk_shader)) ++ gpu->clk_shader = NULL; ++ ++ /* TODO: figure out max mapped size */ ++ dev_set_drvdata(dev, gpu); ++ ++ /* ++ * We treat the device as initially suspended. The runtime PM ++ * autosuspend delay is rather arbitary: no measurements have ++ * yet been performed to determine an appropriate value. ++ */ ++ pm_runtime_use_autosuspend(gpu->dev); ++ pm_runtime_set_autosuspend_delay(gpu->dev, 200); ++ pm_runtime_enable(gpu->dev); ++ ++ err = component_add(&pdev->dev, &gpu_ops); ++ if (err < 0) { ++ dev_err(&pdev->dev, "failed to register component: %d\n", err); ++ goto fail; ++ } ++ ++ return 0; ++ ++fail: ++ return err; ++} ++ ++static int etnaviv_gpu_platform_remove(struct platform_device *pdev) ++{ ++ component_del(&pdev->dev, &gpu_ops); ++ pm_runtime_disable(&pdev->dev); ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int etnaviv_gpu_rpm_suspend(struct device *dev) ++{ ++ struct etnaviv_gpu *gpu = dev_get_drvdata(dev); ++ u32 idle, mask; ++ ++ /* If we have outstanding fences, we're not idle */ ++ if (gpu->completed_fence != gpu->active_fence) ++ return -EBUSY; ++ ++ /* Check whether the hardware (except FE) is idle */ ++ mask = gpu->idle_mask & ~VIVS_HI_IDLE_STATE_FE; ++ idle = gpu_read(gpu, VIVS_HI_IDLE_STATE) & mask; ++ if (idle != mask) ++ return -EBUSY; ++ ++ return etnaviv_gpu_hw_suspend(gpu); ++} ++ ++static int etnaviv_gpu_rpm_resume(struct device *dev) ++{ ++ struct etnaviv_gpu *gpu = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = etnaviv_gpu_clk_enable(gpu); ++ if (ret) ++ return ret; ++ ++ /* Re-initialise the basic hardware state */ ++ if (gpu->drm && gpu->buffer) { ++ ret = etnaviv_gpu_hw_resume(gpu); ++ if (ret) { ++ etnaviv_gpu_clk_disable(gpu); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++#endif ++ ++static const struct dev_pm_ops etnaviv_gpu_pm_ops = { ++ SET_RUNTIME_PM_OPS(etnaviv_gpu_rpm_suspend, etnaviv_gpu_rpm_resume, ++ NULL) ++}; ++ ++struct platform_driver etnaviv_gpu_driver = { ++ .driver = { ++ .name = "etnaviv-gpu", ++ .owner = THIS_MODULE, ++ .pm = &etnaviv_gpu_pm_ops, ++ .of_match_table = etnaviv_gpu_match, ++ }, ++ .probe = etnaviv_gpu_platform_probe, ++ .remove = etnaviv_gpu_platform_remove, ++ .id_table = gpu_ids, ++}; +diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.h b/drivers/gpu/drm/etnaviv/etnaviv_gpu.h +new file mode 100644 +index 0000000..c75d503 +--- /dev/null ++++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.h +@@ -0,0 +1,209 @@ ++/* ++ * Copyright (C) 2015 Etnaviv Project ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ */ ++ ++#ifndef __ETNAVIV_GPU_H__ ++#define __ETNAVIV_GPU_H__ ++ ++#include ++#include ++ ++#include "etnaviv_drv.h" ++ ++struct etnaviv_gem_submit; ++ ++struct etnaviv_chip_identity { ++ /* Chip model. */ ++ u32 model; ++ ++ /* Revision value.*/ ++ u32 revision; ++ ++ /* Supported feature fields. */ ++ u32 features; ++ ++ /* Supported minor feature fields. */ ++ u32 minor_features0; ++ ++ /* Supported minor feature 1 fields. */ ++ u32 minor_features1; ++ ++ /* Supported minor feature 2 fields. */ ++ u32 minor_features2; ++ ++ /* Supported minor feature 3 fields. */ ++ u32 minor_features3; ++ ++ /* Number of streams supported. */ ++ u32 stream_count; ++ ++ /* Total number of temporary registers per thread. */ ++ u32 register_max; ++ ++ /* Maximum number of threads. */ ++ u32 thread_count; ++ ++ /* Number of shader cores. */ ++ u32 shader_core_count; ++ ++ /* Size of the vertex cache. */ ++ u32 vertex_cache_size; ++ ++ /* Number of entries in the vertex output buffer. */ ++ u32 vertex_output_buffer_size; ++ ++ /* Number of pixel pipes. */ ++ u32 pixel_pipes; ++ ++ /* Number of instructions. */ ++ u32 instruction_count; ++ ++ /* Number of constants. */ ++ u32 num_constants; ++ ++ /* Buffer size */ ++ u32 buffer_size; ++}; ++ ++struct etnaviv_event { ++ bool used; ++ struct fence *fence; ++}; ++ ++struct etnaviv_cmdbuf; ++ ++struct etnaviv_gpu { ++ struct drm_device *drm; ++ struct device *dev; ++ struct mutex lock; ++ struct etnaviv_chip_identity identity; ++ struct etnaviv_file_private *lastctx; ++ bool switch_context; ++ ++ /* 'ring'-buffer: */ ++ struct etnaviv_cmdbuf *buffer; ++ ++ /* bus base address of memory */ ++ u32 memory_base; ++ ++ /* event management: */ ++ struct etnaviv_event event[30]; ++ struct completion event_free; ++ spinlock_t event_spinlock; ++ ++ /* list of currently in-flight command buffers */ ++ struct list_head active_cmd_list; ++ ++ u32 idle_mask; ++ ++ /* Fencing support */ ++ u32 next_fence; ++ u32 active_fence; ++ u32 completed_fence; ++ u32 retired_fence; ++ wait_queue_head_t fence_event; ++ unsigned int fence_context; ++ spinlock_t fence_spinlock; ++ ++ /* worker for handling active-list retiring: */ ++ struct work_struct retire_work; ++ ++ void __iomem *mmio; ++ int irq; ++ ++ struct etnaviv_iommu *mmu; ++ ++ /* Power Control: */ ++ struct clk *clk_bus; ++ struct clk *clk_core; ++ struct clk *clk_shader; ++ ++ /* Hang Detction: */ ++#define DRM_ETNAVIV_HANGCHECK_PERIOD 500 /* in ms */ ++#define DRM_ETNAVIV_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_ETNAVIV_HANGCHECK_PERIOD) ++ struct timer_list hangcheck_timer; ++ u32 hangcheck_fence; ++ u32 hangcheck_dma_addr; ++ struct work_struct recover_work; ++}; ++ ++struct etnaviv_cmdbuf { ++ /* device this cmdbuf is allocated for */ ++ struct etnaviv_gpu *gpu; ++ /* user context key, must be unique between all active users */ ++ struct etnaviv_file_private *ctx; ++ /* cmdbuf properties */ ++ void *vaddr; ++ dma_addr_t paddr; ++ u32 size; ++ u32 user_size; ++ /* fence after which this buffer is to be disposed */ ++ struct fence *fence; ++ /* target exec state */ ++ u32 exec_state; ++ /* per GPU in-flight list */ ++ struct list_head node; ++ /* BOs attached to this command buffer */ ++ unsigned int nr_bos; ++ struct etnaviv_gem_object *bo[0]; ++}; ++ ++static inline void gpu_write(struct etnaviv_gpu *gpu, u32 reg, u32 data) ++{ ++ etnaviv_writel(data, gpu->mmio + reg); ++} ++ ++static inline u32 gpu_read(struct etnaviv_gpu *gpu, u32 reg) ++{ ++ return etnaviv_readl(gpu->mmio + reg); ++} ++ ++static inline bool fence_completed(struct etnaviv_gpu *gpu, u32 fence) ++{ ++ return fence_after_eq(gpu->completed_fence, fence); ++} ++ ++static inline bool fence_retired(struct etnaviv_gpu *gpu, u32 fence) ++{ ++ return fence_after_eq(gpu->retired_fence, fence); ++} ++ ++int etnaviv_gpu_get_param(struct etnaviv_gpu *gpu, u32 param, u64 *value); ++ ++int etnaviv_gpu_init(struct etnaviv_gpu *gpu); ++ ++#ifdef CONFIG_DEBUG_FS ++int etnaviv_gpu_debugfs(struct etnaviv_gpu *gpu, struct seq_file *m); ++#endif ++ ++int etnaviv_gpu_fence_sync_obj(struct etnaviv_gem_object *etnaviv_obj, ++ unsigned int context, bool exclusive); ++ ++void etnaviv_gpu_retire(struct etnaviv_gpu *gpu); ++int etnaviv_gpu_wait_fence_interruptible(struct etnaviv_gpu *gpu, ++ u32 fence, struct timespec *timeout); ++int etnaviv_gpu_wait_obj_inactive(struct etnaviv_gpu *gpu, ++ struct etnaviv_gem_object *etnaviv_obj, struct timespec *timeout); ++int etnaviv_gpu_submit(struct etnaviv_gpu *gpu, ++ struct etnaviv_gem_submit *submit, struct etnaviv_cmdbuf *cmdbuf); ++struct etnaviv_cmdbuf *etnaviv_gpu_cmdbuf_new(struct etnaviv_gpu *gpu, ++ u32 size, size_t nr_bos); ++void etnaviv_gpu_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf); ++int etnaviv_gpu_pm_get_sync(struct etnaviv_gpu *gpu); ++void etnaviv_gpu_pm_put(struct etnaviv_gpu *gpu); ++ ++extern struct platform_driver etnaviv_gpu_driver; ++ ++#endif /* __ETNAVIV_GPU_H__ */ +diff --git a/drivers/gpu/drm/etnaviv/etnaviv_iommu.c b/drivers/gpu/drm/etnaviv/etnaviv_iommu.c +new file mode 100644 +index 0000000..522cfd4 +--- /dev/null ++++ b/drivers/gpu/drm/etnaviv/etnaviv_iommu.c +@@ -0,0 +1,240 @@ ++/* ++ * Copyright (C) 2014 Christian Gmeiner ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "etnaviv_gpu.h" ++#include "etnaviv_mmu.h" ++#include "etnaviv_iommu.h" ++#include "state_hi.xml.h" ++ ++#define PT_SIZE SZ_2M ++#define PT_ENTRIES (PT_SIZE / sizeof(u32)) ++ ++#define GPU_MEM_START 0x80000000 ++ ++struct etnaviv_iommu_domain_pgtable { ++ u32 *pgtable; ++ dma_addr_t paddr; ++}; ++ ++struct etnaviv_iommu_domain { ++ struct iommu_domain domain; ++ struct device *dev; ++ void *bad_page_cpu; ++ dma_addr_t bad_page_dma; ++ struct etnaviv_iommu_domain_pgtable pgtable; ++ spinlock_t map_lock; ++}; ++ ++static struct etnaviv_iommu_domain *to_etnaviv_domain(struct iommu_domain *domain) ++{ ++ return container_of(domain, struct etnaviv_iommu_domain, domain); ++} ++ ++static int pgtable_alloc(struct etnaviv_iommu_domain_pgtable *pgtable, ++ size_t size) ++{ ++ pgtable->pgtable = dma_alloc_coherent(NULL, size, &pgtable->paddr, GFP_KERNEL); ++ if (!pgtable->pgtable) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++static void pgtable_free(struct etnaviv_iommu_domain_pgtable *pgtable, ++ size_t size) ++{ ++ dma_free_coherent(NULL, size, pgtable->pgtable, pgtable->paddr); ++} ++ ++static u32 pgtable_read(struct etnaviv_iommu_domain_pgtable *pgtable, ++ unsigned long iova) ++{ ++ /* calcuate index into page table */ ++ unsigned int index = (iova - GPU_MEM_START) / SZ_4K; ++ phys_addr_t paddr; ++ ++ paddr = pgtable->pgtable[index]; ++ ++ return paddr; ++} ++ ++static void pgtable_write(struct etnaviv_iommu_domain_pgtable *pgtable, ++ unsigned long iova, phys_addr_t paddr) ++{ ++ /* calcuate index into page table */ ++ unsigned int index = (iova - GPU_MEM_START) / SZ_4K; ++ ++ pgtable->pgtable[index] = paddr; ++} ++ ++static int __etnaviv_iommu_init(struct etnaviv_iommu_domain *etnaviv_domain) ++{ ++ u32 *p; ++ int ret, i; ++ ++ etnaviv_domain->bad_page_cpu = dma_alloc_coherent(etnaviv_domain->dev, ++ SZ_4K, ++ &etnaviv_domain->bad_page_dma, ++ GFP_KERNEL); ++ if (!etnaviv_domain->bad_page_cpu) ++ return -ENOMEM; ++ ++ p = etnaviv_domain->bad_page_cpu; ++ for (i = 0; i < SZ_4K / 4; i++) ++ *p++ = 0xdead55aa; ++ ++ ret = pgtable_alloc(&etnaviv_domain->pgtable, PT_SIZE); ++ if (ret < 0) { ++ dma_free_coherent(etnaviv_domain->dev, SZ_4K, ++ etnaviv_domain->bad_page_cpu, ++ etnaviv_domain->bad_page_dma); ++ return ret; ++ } ++ ++ for (i = 0; i < PT_ENTRIES; i++) ++ etnaviv_domain->pgtable.pgtable[i] = ++ etnaviv_domain->bad_page_dma; ++ ++ spin_lock_init(&etnaviv_domain->map_lock); ++ ++ return 0; ++} ++ ++static void etnaviv_domain_free(struct iommu_domain *domain) ++{ ++ struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain); ++ ++ pgtable_free(&etnaviv_domain->pgtable, PT_SIZE); ++ ++ dma_free_coherent(etnaviv_domain->dev, SZ_4K, ++ etnaviv_domain->bad_page_cpu, ++ etnaviv_domain->bad_page_dma); ++ ++ kfree(etnaviv_domain); ++} ++ ++static int etnaviv_iommuv1_map(struct iommu_domain *domain, unsigned long iova, ++ phys_addr_t paddr, size_t size, int prot) ++{ ++ struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain); ++ ++ if (size != SZ_4K) ++ return -EINVAL; ++ ++ spin_lock(&etnaviv_domain->map_lock); ++ pgtable_write(&etnaviv_domain->pgtable, iova, paddr); ++ spin_unlock(&etnaviv_domain->map_lock); ++ ++ return 0; ++} ++ ++static size_t etnaviv_iommuv1_unmap(struct iommu_domain *domain, ++ unsigned long iova, size_t size) ++{ ++ struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain); ++ ++ if (size != SZ_4K) ++ return -EINVAL; ++ ++ spin_lock(&etnaviv_domain->map_lock); ++ pgtable_write(&etnaviv_domain->pgtable, iova, ++ etnaviv_domain->bad_page_dma); ++ spin_unlock(&etnaviv_domain->map_lock); ++ ++ return SZ_4K; ++} ++ ++static phys_addr_t etnaviv_iommu_iova_to_phys(struct iommu_domain *domain, ++ dma_addr_t iova) ++{ ++ struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain); ++ ++ return pgtable_read(&etnaviv_domain->pgtable, iova); ++} ++ ++static size_t etnaviv_iommuv1_dump_size(struct iommu_domain *domain) ++{ ++ return PT_SIZE; ++} ++ ++static void etnaviv_iommuv1_dump(struct iommu_domain *domain, void *buf) ++{ ++ struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain); ++ ++ memcpy(buf, etnaviv_domain->pgtable.pgtable, PT_SIZE); ++} ++ ++static struct etnaviv_iommu_ops etnaviv_iommu_ops = { ++ .ops = { ++ .domain_free = etnaviv_domain_free, ++ .map = etnaviv_iommuv1_map, ++ .unmap = etnaviv_iommuv1_unmap, ++ .iova_to_phys = etnaviv_iommu_iova_to_phys, ++ .pgsize_bitmap = SZ_4K, ++ }, ++ .dump_size = etnaviv_iommuv1_dump_size, ++ .dump = etnaviv_iommuv1_dump, ++}; ++ ++void etnaviv_iommu_domain_restore(struct etnaviv_gpu *gpu, ++ struct iommu_domain *domain) ++{ ++ struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain); ++ u32 pgtable; ++ ++ /* set page table address in MC */ ++ pgtable = (u32)etnaviv_domain->pgtable.paddr; ++ ++ gpu_write(gpu, VIVS_MC_MMU_FE_PAGE_TABLE, pgtable); ++ gpu_write(gpu, VIVS_MC_MMU_TX_PAGE_TABLE, pgtable); ++ gpu_write(gpu, VIVS_MC_MMU_PE_PAGE_TABLE, pgtable); ++ gpu_write(gpu, VIVS_MC_MMU_PEZ_PAGE_TABLE, pgtable); ++ gpu_write(gpu, VIVS_MC_MMU_RA_PAGE_TABLE, pgtable); ++} ++ ++struct iommu_domain *etnaviv_iommu_domain_alloc(struct etnaviv_gpu *gpu) ++{ ++ struct etnaviv_iommu_domain *etnaviv_domain; ++ int ret; ++ ++ etnaviv_domain = kzalloc(sizeof(*etnaviv_domain), GFP_KERNEL); ++ if (!etnaviv_domain) ++ return NULL; ++ ++ etnaviv_domain->dev = gpu->dev; ++ ++ etnaviv_domain->domain.type = __IOMMU_DOMAIN_PAGING; ++ etnaviv_domain->domain.ops = &etnaviv_iommu_ops.ops; ++ etnaviv_domain->domain.geometry.aperture_start = GPU_MEM_START; ++ etnaviv_domain->domain.geometry.aperture_end = GPU_MEM_START + PT_ENTRIES * SZ_4K - 1; ++ ++ ret = __etnaviv_iommu_init(etnaviv_domain); ++ if (ret) ++ goto out_free; ++ ++ return &etnaviv_domain->domain; ++ ++out_free: ++ kfree(etnaviv_domain); ++ return NULL; ++} +diff --git a/drivers/gpu/drm/etnaviv/etnaviv_iommu.h b/drivers/gpu/drm/etnaviv/etnaviv_iommu.h +new file mode 100644 +index 0000000..cf45503 +--- /dev/null ++++ b/drivers/gpu/drm/etnaviv/etnaviv_iommu.h +@@ -0,0 +1,28 @@ ++/* ++ * Copyright (C) 2014 Christian Gmeiner ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ */ ++ ++#ifndef __ETNAVIV_IOMMU_H__ ++#define __ETNAVIV_IOMMU_H__ ++ ++#include ++struct etnaviv_gpu; ++ ++struct iommu_domain *etnaviv_iommu_domain_alloc(struct etnaviv_gpu *gpu); ++void etnaviv_iommu_domain_restore(struct etnaviv_gpu *gpu, ++ struct iommu_domain *domain); ++struct iommu_domain *etnaviv_iommu_v2_domain_alloc(struct etnaviv_gpu *gpu); ++ ++#endif /* __ETNAVIV_IOMMU_H__ */ +diff --git a/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c b/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c +new file mode 100644 +index 0000000..fbb4aed +--- /dev/null ++++ b/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c +@@ -0,0 +1,33 @@ ++/* ++ * Copyright (C) 2014 Christian Gmeiner ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "etnaviv_gpu.h" ++#include "etnaviv_iommu.h" ++#include "state_hi.xml.h" ++ ++ ++struct iommu_domain *etnaviv_iommu_v2_domain_alloc(struct etnaviv_gpu *gpu) ++{ ++ /* TODO */ ++ return NULL; ++} +diff --git a/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.h b/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.h +new file mode 100644 +index 0000000..603ea41 +--- /dev/null ++++ b/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.h +@@ -0,0 +1,25 @@ ++/* ++ * Copyright (C) 2014 Christian Gmeiner ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ */ ++ ++#ifndef __ETNAVIV_IOMMU_V2_H__ ++#define __ETNAVIV_IOMMU_V2_H__ ++ ++#include ++struct etnaviv_gpu; ++ ++struct iommu_domain *etnaviv_iommu_v2_domain_alloc(struct etnaviv_gpu *gpu); ++ ++#endif /* __ETNAVIV_IOMMU_V2_H__ */ +diff --git a/drivers/gpu/drm/etnaviv/etnaviv_mmu.c b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c +new file mode 100644 +index 0000000..6743bc6 +--- /dev/null ++++ b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c +@@ -0,0 +1,299 @@ ++/* ++ * Copyright (C) 2015 Etnaviv Project ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ */ ++ ++#include "etnaviv_drv.h" ++#include "etnaviv_gem.h" ++#include "etnaviv_gpu.h" ++#include "etnaviv_mmu.h" ++ ++static int etnaviv_fault_handler(struct iommu_domain *iommu, struct device *dev, ++ unsigned long iova, int flags, void *arg) ++{ ++ DBG("*** fault: iova=%08lx, flags=%d", iova, flags); ++ return 0; ++} ++ ++int etnaviv_iommu_map(struct etnaviv_iommu *iommu, u32 iova, ++ struct sg_table *sgt, unsigned len, int prot) ++{ ++ struct iommu_domain *domain = iommu->domain; ++ struct scatterlist *sg; ++ unsigned int da = iova; ++ unsigned int i, j; ++ int ret; ++ ++ if (!domain || !sgt) ++ return -EINVAL; ++ ++ for_each_sg(sgt->sgl, sg, sgt->nents, i) { ++ u32 pa = sg_dma_address(sg) - sg->offset; ++ size_t bytes = sg_dma_len(sg) + sg->offset; ++ ++ VERB("map[%d]: %08x %08x(%zx)", i, iova, pa, bytes); ++ ++ ret = iommu_map(domain, da, pa, bytes, prot); ++ if (ret) ++ goto fail; ++ ++ da += bytes; ++ } ++ ++ return 0; ++ ++fail: ++ da = iova; ++ ++ for_each_sg(sgt->sgl, sg, i, j) { ++ size_t bytes = sg_dma_len(sg) + sg->offset; ++ ++ iommu_unmap(domain, da, bytes); ++ da += bytes; ++ } ++ return ret; ++} ++ ++int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, u32 iova, ++ struct sg_table *sgt, unsigned len) ++{ ++ struct iommu_domain *domain = iommu->domain; ++ struct scatterlist *sg; ++ unsigned int da = iova; ++ int i; ++ ++ for_each_sg(sgt->sgl, sg, sgt->nents, i) { ++ size_t bytes = sg_dma_len(sg) + sg->offset; ++ size_t unmapped; ++ ++ unmapped = iommu_unmap(domain, da, bytes); ++ if (unmapped < bytes) ++ return unmapped; ++ ++ VERB("unmap[%d]: %08x(%zx)", i, iova, bytes); ++ ++ BUG_ON(!PAGE_ALIGNED(bytes)); ++ ++ da += bytes; ++ } ++ ++ return 0; ++} ++ ++static void etnaviv_iommu_remove_mapping(struct etnaviv_iommu *mmu, ++ struct etnaviv_vram_mapping *mapping) ++{ ++ struct etnaviv_gem_object *etnaviv_obj = mapping->object; ++ ++ etnaviv_iommu_unmap(mmu, mapping->vram_node.start, ++ etnaviv_obj->sgt, etnaviv_obj->base.size); ++ drm_mm_remove_node(&mapping->vram_node); ++} ++ ++int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu, ++ struct etnaviv_gem_object *etnaviv_obj, u32 memory_base, ++ struct etnaviv_vram_mapping *mapping) ++{ ++ struct etnaviv_vram_mapping *free = NULL; ++ struct sg_table *sgt = etnaviv_obj->sgt; ++ struct drm_mm_node *node; ++ int ret; ++ ++ lockdep_assert_held(&etnaviv_obj->lock); ++ ++ mutex_lock(&mmu->lock); ++ ++ /* v1 MMU can optimize single entry (contiguous) scatterlists */ ++ if (sgt->nents == 1 && !(etnaviv_obj->flags & ETNA_BO_FORCE_MMU)) { ++ u32 iova; ++ ++ iova = sg_dma_address(sgt->sgl) - memory_base; ++ if (iova < 0x80000000 - sg_dma_len(sgt->sgl)) { ++ mapping->iova = iova; ++ list_add_tail(&mapping->mmu_node, &mmu->mappings); ++ mutex_unlock(&mmu->lock); ++ return 0; ++ } ++ } ++ ++ node = &mapping->vram_node; ++ while (1) { ++ struct etnaviv_vram_mapping *m, *n; ++ struct list_head list; ++ bool found; ++ ++ ret = drm_mm_insert_node_in_range(&mmu->mm, node, ++ etnaviv_obj->base.size, 0, mmu->last_iova, ~0UL, ++ DRM_MM_SEARCH_DEFAULT); ++ ++ if (ret != -ENOSPC) ++ break; ++ ++ /* ++ * If we did not search from the start of the MMU region, ++ * try again in case there are free slots. ++ */ ++ if (mmu->last_iova) { ++ mmu->last_iova = 0; ++ mmu->need_flush = true; ++ continue; ++ } ++ ++ /* Try to retire some entries */ ++ drm_mm_init_scan(&mmu->mm, etnaviv_obj->base.size, 0, 0); ++ ++ found = 0; ++ INIT_LIST_HEAD(&list); ++ list_for_each_entry(free, &mmu->mappings, mmu_node) { ++ /* If this vram node has not been used, skip this. */ ++ if (!free->vram_node.mm) ++ continue; ++ ++ /* ++ * If the iova is pinned, then it's in-use, ++ * so we must keep its mapping. ++ */ ++ if (free->use) ++ continue; ++ ++ list_add(&free->scan_node, &list); ++ if (drm_mm_scan_add_block(&free->vram_node)) { ++ found = true; ++ break; ++ } ++ } ++ ++ if (!found) { ++ /* Nothing found, clean up and fail */ ++ list_for_each_entry_safe(m, n, &list, scan_node) ++ BUG_ON(drm_mm_scan_remove_block(&m->vram_node)); ++ break; ++ } ++ ++ /* ++ * drm_mm does not allow any other operations while ++ * scanning, so we have to remove all blocks first. ++ * If drm_mm_scan_remove_block() returns false, we ++ * can leave the block pinned. ++ */ ++ list_for_each_entry_safe(m, n, &list, scan_node) ++ if (!drm_mm_scan_remove_block(&m->vram_node)) ++ list_del_init(&m->scan_node); ++ ++ /* ++ * Unmap the blocks which need to be reaped from the MMU. ++ * Clear the mmu pointer to prevent the get_iova finding ++ * this mapping. ++ */ ++ list_for_each_entry_safe(m, n, &list, scan_node) { ++ etnaviv_iommu_remove_mapping(mmu, m); ++ m->mmu = NULL; ++ list_del_init(&m->mmu_node); ++ list_del_init(&m->scan_node); ++ } ++ ++ /* ++ * We removed enough mappings so that the new allocation will ++ * succeed. Ensure that the MMU will be flushed before the ++ * associated commit requesting this mapping, and retry the ++ * allocation one more time. ++ */ ++ mmu->need_flush = true; ++ } ++ ++ if (ret < 0) { ++ mutex_unlock(&mmu->lock); ++ return ret; ++ } ++ ++ mmu->last_iova = node->start + etnaviv_obj->base.size; ++ mapping->iova = node->start; ++ ret = etnaviv_iommu_map(mmu, node->start, sgt, etnaviv_obj->base.size, ++ IOMMU_READ | IOMMU_WRITE); ++ ++ if (ret < 0) { ++ drm_mm_remove_node(node); ++ mutex_unlock(&mmu->lock); ++ return ret; ++ } ++ ++ list_add_tail(&mapping->mmu_node, &mmu->mappings); ++ mutex_unlock(&mmu->lock); ++ ++ return ret; ++} ++ ++void etnaviv_iommu_unmap_gem(struct etnaviv_iommu *mmu, ++ struct etnaviv_vram_mapping *mapping) ++{ ++ WARN_ON(mapping->use); ++ ++ mutex_lock(&mmu->lock); ++ ++ /* If the vram node is on the mm, unmap and remove the node */ ++ if (mapping->vram_node.mm == &mmu->mm) ++ etnaviv_iommu_remove_mapping(mmu, mapping); ++ ++ list_del(&mapping->mmu_node); ++ mutex_unlock(&mmu->lock); ++} ++ ++void etnaviv_iommu_destroy(struct etnaviv_iommu *mmu) ++{ ++ drm_mm_takedown(&mmu->mm); ++ iommu_domain_free(mmu->domain); ++ kfree(mmu); ++} ++ ++struct etnaviv_iommu *etnaviv_iommu_new(struct etnaviv_gpu *gpu, ++ struct iommu_domain *domain, enum etnaviv_iommu_version version) ++{ ++ struct etnaviv_iommu *mmu; ++ ++ mmu = kzalloc(sizeof(*mmu), GFP_KERNEL); ++ if (!mmu) ++ return ERR_PTR(-ENOMEM); ++ ++ mmu->domain = domain; ++ mmu->gpu = gpu; ++ mmu->version = version; ++ mutex_init(&mmu->lock); ++ INIT_LIST_HEAD(&mmu->mappings); ++ ++ drm_mm_init(&mmu->mm, domain->geometry.aperture_start, ++ domain->geometry.aperture_end - ++ domain->geometry.aperture_start + 1); ++ ++ iommu_set_fault_handler(domain, etnaviv_fault_handler, gpu->dev); ++ ++ return mmu; ++} ++ ++size_t etnaviv_iommu_dump_size(struct etnaviv_iommu *iommu) ++{ ++ struct etnaviv_iommu_ops *ops; ++ ++ ops = container_of(iommu->domain->ops, struct etnaviv_iommu_ops, ops); ++ ++ return ops->dump_size(iommu->domain); ++} ++ ++void etnaviv_iommu_dump(struct etnaviv_iommu *iommu, void *buf) ++{ ++ struct etnaviv_iommu_ops *ops; ++ ++ ops = container_of(iommu->domain->ops, struct etnaviv_iommu_ops, ops); ++ ++ ops->dump(iommu->domain, buf); ++} +diff --git a/drivers/gpu/drm/etnaviv/etnaviv_mmu.h b/drivers/gpu/drm/etnaviv/etnaviv_mmu.h +new file mode 100644 +index 0000000..fff215a +--- /dev/null ++++ b/drivers/gpu/drm/etnaviv/etnaviv_mmu.h +@@ -0,0 +1,71 @@ ++/* ++ * Copyright (C) 2015 Etnaviv Project ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ */ ++ ++#ifndef __ETNAVIV_MMU_H__ ++#define __ETNAVIV_MMU_H__ ++ ++#include ++ ++enum etnaviv_iommu_version { ++ ETNAVIV_IOMMU_V1 = 0, ++ ETNAVIV_IOMMU_V2, ++}; ++ ++struct etnaviv_gpu; ++struct etnaviv_vram_mapping; ++ ++struct etnaviv_iommu_ops { ++ struct iommu_ops ops; ++ size_t (*dump_size)(struct iommu_domain *); ++ void (*dump)(struct iommu_domain *, void *); ++}; ++ ++struct etnaviv_iommu { ++ struct etnaviv_gpu *gpu; ++ struct iommu_domain *domain; ++ ++ enum etnaviv_iommu_version version; ++ ++ /* memory manager for GPU address area */ ++ struct mutex lock; ++ struct list_head mappings; ++ struct drm_mm mm; ++ u32 last_iova; ++ bool need_flush; ++}; ++ ++struct etnaviv_gem_object; ++ ++int etnaviv_iommu_attach(struct etnaviv_iommu *iommu, const char **names, ++ int cnt); ++int etnaviv_iommu_map(struct etnaviv_iommu *iommu, u32 iova, ++ struct sg_table *sgt, unsigned len, int prot); ++int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, u32 iova, ++ struct sg_table *sgt, unsigned len); ++int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu, ++ struct etnaviv_gem_object *etnaviv_obj, u32 memory_base, ++ struct etnaviv_vram_mapping *mapping); ++void etnaviv_iommu_unmap_gem(struct etnaviv_iommu *mmu, ++ struct etnaviv_vram_mapping *mapping); ++void etnaviv_iommu_destroy(struct etnaviv_iommu *iommu); ++ ++size_t etnaviv_iommu_dump_size(struct etnaviv_iommu *iommu); ++void etnaviv_iommu_dump(struct etnaviv_iommu *iommu, void *buf); ++ ++struct etnaviv_iommu *etnaviv_iommu_new(struct etnaviv_gpu *gpu, ++ struct iommu_domain *domain, enum etnaviv_iommu_version version); ++ ++#endif /* __ETNAVIV_MMU_H__ */ +diff --git a/drivers/gpu/drm/etnaviv/state.xml.h b/drivers/gpu/drm/etnaviv/state.xml.h +new file mode 100644 +index 0000000..3682183 +--- /dev/null ++++ b/drivers/gpu/drm/etnaviv/state.xml.h +@@ -0,0 +1,351 @@ ++#ifndef STATE_XML ++#define STATE_XML ++ ++/* Autogenerated file, DO NOT EDIT manually! ++ ++This file was generated by the rules-ng-ng headergen tool in this git repository: ++http://0x04.net/cgit/index.cgi/rules-ng-ng ++git clone git://0x04.net/rules-ng-ng ++ ++The rules-ng-ng source files this header was generated from are: ++- state.xml ( 18882 bytes, from 2015-03-25 11:42:32) ++- common.xml ( 18437 bytes, from 2015-03-25 11:27:41) ++- state_hi.xml ( 23420 bytes, from 2015-03-25 11:47:21) ++- state_2d.xml ( 51549 bytes, from 2015-03-25 11:25:06) ++- state_3d.xml ( 54600 bytes, from 2015-03-25 11:25:19) ++- state_vg.xml ( 5973 bytes, from 2015-03-25 11:26:01) ++ ++Copyright (C) 2015 ++*/ ++ ++ ++#define VARYING_COMPONENT_USE_UNUSED 0x00000000 ++#define VARYING_COMPONENT_USE_USED 0x00000001 ++#define VARYING_COMPONENT_USE_POINTCOORD_X 0x00000002 ++#define VARYING_COMPONENT_USE_POINTCOORD_Y 0x00000003 ++#define FE_VERTEX_STREAM_CONTROL_VERTEX_STRIDE__MASK 0x000000ff ++#define FE_VERTEX_STREAM_CONTROL_VERTEX_STRIDE__SHIFT 0 ++#define FE_VERTEX_STREAM_CONTROL_VERTEX_STRIDE(x) (((x) << FE_VERTEX_STREAM_CONTROL_VERTEX_STRIDE__SHIFT) & FE_VERTEX_STREAM_CONTROL_VERTEX_STRIDE__MASK) ++#define VIVS_FE 0x00000000 ++ ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG(i0) (0x00000600 + 0x4*(i0)) ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG__ESIZE 0x00000004 ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG__LEN 0x00000010 ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE__MASK 0x0000000f ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE__SHIFT 0 ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_BYTE 0x00000000 ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_UNSIGNED_BYTE 0x00000001 ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_SHORT 0x00000002 ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_UNSIGNED_SHORT 0x00000003 ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_INT 0x00000004 ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_UNSIGNED_INT 0x00000005 ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_FLOAT 0x00000008 ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_HALF_FLOAT 0x00000009 ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_FIXED 0x0000000b ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_INT_10_10_10_2 0x0000000c ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_TYPE_UNSIGNED_INT_10_10_10_2 0x0000000d ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_ENDIAN__MASK 0x00000030 ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_ENDIAN__SHIFT 4 ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_ENDIAN(x) (((x) << VIVS_FE_VERTEX_ELEMENT_CONFIG_ENDIAN__SHIFT) & VIVS_FE_VERTEX_ELEMENT_CONFIG_ENDIAN__MASK) ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_NONCONSECUTIVE 0x00000080 ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_STREAM__MASK 0x00000700 ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_STREAM__SHIFT 8 ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_STREAM(x) (((x) << VIVS_FE_VERTEX_ELEMENT_CONFIG_STREAM__SHIFT) & VIVS_FE_VERTEX_ELEMENT_CONFIG_STREAM__MASK) ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_NUM__MASK 0x00003000 ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_NUM__SHIFT 12 ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_NUM(x) (((x) << VIVS_FE_VERTEX_ELEMENT_CONFIG_NUM__SHIFT) & VIVS_FE_VERTEX_ELEMENT_CONFIG_NUM__MASK) ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_NORMALIZE__MASK 0x0000c000 ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_NORMALIZE__SHIFT 14 ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_NORMALIZE_OFF 0x00000000 ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_NORMALIZE_ON 0x00008000 ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_START__MASK 0x00ff0000 ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_START__SHIFT 16 ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_START(x) (((x) << VIVS_FE_VERTEX_ELEMENT_CONFIG_START__SHIFT) & VIVS_FE_VERTEX_ELEMENT_CONFIG_START__MASK) ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_END__MASK 0xff000000 ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_END__SHIFT 24 ++#define VIVS_FE_VERTEX_ELEMENT_CONFIG_END(x) (((x) << VIVS_FE_VERTEX_ELEMENT_CONFIG_END__SHIFT) & VIVS_FE_VERTEX_ELEMENT_CONFIG_END__MASK) ++ ++#define VIVS_FE_CMD_STREAM_BASE_ADDR 0x00000640 ++ ++#define VIVS_FE_INDEX_STREAM_BASE_ADDR 0x00000644 ++ ++#define VIVS_FE_INDEX_STREAM_CONTROL 0x00000648 ++#define VIVS_FE_INDEX_STREAM_CONTROL_TYPE__MASK 0x00000003 ++#define VIVS_FE_INDEX_STREAM_CONTROL_TYPE__SHIFT 0 ++#define VIVS_FE_INDEX_STREAM_CONTROL_TYPE_UNSIGNED_CHAR 0x00000000 ++#define VIVS_FE_INDEX_STREAM_CONTROL_TYPE_UNSIGNED_SHORT 0x00000001 ++#define VIVS_FE_INDEX_STREAM_CONTROL_TYPE_UNSIGNED_INT 0x00000002 ++ ++#define VIVS_FE_VERTEX_STREAM_BASE_ADDR 0x0000064c ++ ++#define VIVS_FE_VERTEX_STREAM_CONTROL 0x00000650 ++ ++#define VIVS_FE_COMMAND_ADDRESS 0x00000654 ++ ++#define VIVS_FE_COMMAND_CONTROL 0x00000658 ++#define VIVS_FE_COMMAND_CONTROL_PREFETCH__MASK 0x0000ffff ++#define VIVS_FE_COMMAND_CONTROL_PREFETCH__SHIFT 0 ++#define VIVS_FE_COMMAND_CONTROL_PREFETCH(x) (((x) << VIVS_FE_COMMAND_CONTROL_PREFETCH__SHIFT) & VIVS_FE_COMMAND_CONTROL_PREFETCH__MASK) ++#define VIVS_FE_COMMAND_CONTROL_ENABLE 0x00010000 ++ ++#define VIVS_FE_DMA_STATUS 0x0000065c ++ ++#define VIVS_FE_DMA_DEBUG_STATE 0x00000660 ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE__MASK 0x0000001f ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE__SHIFT 0 ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_IDLE 0x00000000 ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_DEC 0x00000001 ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_ADR0 0x00000002 ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_LOAD0 0x00000003 ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_ADR1 0x00000004 ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_LOAD1 0x00000005 ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_3DADR 0x00000006 ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_3DCMD 0x00000007 ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_3DCNTL 0x00000008 ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_3DIDXCNTL 0x00000009 ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_INITREQDMA 0x0000000a ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_DRAWIDX 0x0000000b ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_DRAW 0x0000000c ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_2DRECT0 0x0000000d ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_2DRECT1 0x0000000e ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_2DDATA0 0x0000000f ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_2DDATA1 0x00000010 ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_WAITFIFO 0x00000011 ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_WAIT 0x00000012 ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_LINK 0x00000013 ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_END 0x00000014 ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_STATE_STALL 0x00000015 ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_DMA_STATE__MASK 0x00000300 ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_DMA_STATE__SHIFT 8 ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_DMA_STATE_IDLE 0x00000000 ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_DMA_STATE_START 0x00000100 ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_DMA_STATE_REQ 0x00000200 ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_DMA_STATE_END 0x00000300 ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_FETCH_STATE__MASK 0x00000c00 ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_FETCH_STATE__SHIFT 10 ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_FETCH_STATE_IDLE 0x00000000 ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_FETCH_STATE_RAMVALID 0x00000400 ++#define VIVS_FE_DMA_DEBUG_STATE_CMD_FETCH_STATE_VALID 0x00000800 ++#define VIVS_FE_DMA_DEBUG_STATE_REQ_DMA_STATE__MASK 0x00003000 ++#define VIVS_FE_DMA_DEBUG_STATE_REQ_DMA_STATE__SHIFT 12 ++#define VIVS_FE_DMA_DEBUG_STATE_REQ_DMA_STATE_IDLE 0x00000000 ++#define VIVS_FE_DMA_DEBUG_STATE_REQ_DMA_STATE_WAITIDX 0x00001000 ++#define VIVS_FE_DMA_DEBUG_STATE_REQ_DMA_STATE_CAL 0x00002000 ++#define VIVS_FE_DMA_DEBUG_STATE_CAL_STATE__MASK 0x0000c000 ++#define VIVS_FE_DMA_DEBUG_STATE_CAL_STATE__SHIFT 14 ++#define VIVS_FE_DMA_DEBUG_STATE_CAL_STATE_IDLE 0x00000000 ++#define VIVS_FE_DMA_DEBUG_STATE_CAL_STATE_LDADR 0x00004000 ++#define VIVS_FE_DMA_DEBUG_STATE_CAL_STATE_IDXCALC 0x00008000 ++#define VIVS_FE_DMA_DEBUG_STATE_VE_REQ_STATE__MASK 0x00030000 ++#define VIVS_FE_DMA_DEBUG_STATE_VE_REQ_STATE__SHIFT 16 ++#define VIVS_FE_DMA_DEBUG_STATE_VE_REQ_STATE_IDLE 0x00000000 ++#define VIVS_FE_DMA_DEBUG_STATE_VE_REQ_STATE_CKCACHE 0x00010000 ++#define VIVS_FE_DMA_DEBUG_STATE_VE_REQ_STATE_MISS 0x00020000 ++ ++#define VIVS_FE_DMA_ADDRESS 0x00000664 ++ ++#define VIVS_FE_DMA_LOW 0x00000668 ++ ++#define VIVS_FE_DMA_HIGH 0x0000066c ++ ++#define VIVS_FE_AUTO_FLUSH 0x00000670 ++ ++#define VIVS_FE_UNK00678 0x00000678 ++ ++#define VIVS_FE_UNK0067C 0x0000067c ++ ++#define VIVS_FE_VERTEX_STREAMS(i0) (0x00000000 + 0x4*(i0)) ++#define VIVS_FE_VERTEX_STREAMS__ESIZE 0x00000004 ++#define VIVS_FE_VERTEX_STREAMS__LEN 0x00000008 ++ ++#define VIVS_FE_VERTEX_STREAMS_BASE_ADDR(i0) (0x00000680 + 0x4*(i0)) ++ ++#define VIVS_FE_VERTEX_STREAMS_CONTROL(i0) (0x000006a0 + 0x4*(i0)) ++ ++#define VIVS_FE_UNK00700(i0) (0x00000700 + 0x4*(i0)) ++#define VIVS_FE_UNK00700__ESIZE 0x00000004 ++#define VIVS_FE_UNK00700__LEN 0x00000010 ++ ++#define VIVS_FE_UNK00740(i0) (0x00000740 + 0x4*(i0)) ++#define VIVS_FE_UNK00740__ESIZE 0x00000004 ++#define VIVS_FE_UNK00740__LEN 0x00000010 ++ ++#define VIVS_FE_UNK00780(i0) (0x00000780 + 0x4*(i0)) ++#define VIVS_FE_UNK00780__ESIZE 0x00000004 ++#define VIVS_FE_UNK00780__LEN 0x00000010 ++ ++#define VIVS_GL 0x00000000 ++ ++#define VIVS_GL_PIPE_SELECT 0x00003800 ++#define VIVS_GL_PIPE_SELECT_PIPE__MASK 0x00000001 ++#define VIVS_GL_PIPE_SELECT_PIPE__SHIFT 0 ++#define VIVS_GL_PIPE_SELECT_PIPE(x) (((x) << VIVS_GL_PIPE_SELECT_PIPE__SHIFT) & VIVS_GL_PIPE_SELECT_PIPE__MASK) ++ ++#define VIVS_GL_EVENT 0x00003804 ++#define VIVS_GL_EVENT_EVENT_ID__MASK 0x0000001f ++#define VIVS_GL_EVENT_EVENT_ID__SHIFT 0 ++#define VIVS_GL_EVENT_EVENT_ID(x) (((x) << VIVS_GL_EVENT_EVENT_ID__SHIFT) & VIVS_GL_EVENT_EVENT_ID__MASK) ++#define VIVS_GL_EVENT_FROM_FE 0x00000020 ++#define VIVS_GL_EVENT_FROM_PE 0x00000040 ++#define VIVS_GL_EVENT_SOURCE__MASK 0x00001f00 ++#define VIVS_GL_EVENT_SOURCE__SHIFT 8 ++#define VIVS_GL_EVENT_SOURCE(x) (((x) << VIVS_GL_EVENT_SOURCE__SHIFT) & VIVS_GL_EVENT_SOURCE__MASK) ++ ++#define VIVS_GL_SEMAPHORE_TOKEN 0x00003808 ++#define VIVS_GL_SEMAPHORE_TOKEN_FROM__MASK 0x0000001f ++#define VIVS_GL_SEMAPHORE_TOKEN_FROM__SHIFT 0 ++#define VIVS_GL_SEMAPHORE_TOKEN_FROM(x) (((x) << VIVS_GL_SEMAPHORE_TOKEN_FROM__SHIFT) & VIVS_GL_SEMAPHORE_TOKEN_FROM__MASK) ++#define VIVS_GL_SEMAPHORE_TOKEN_TO__MASK 0x00001f00 ++#define VIVS_GL_SEMAPHORE_TOKEN_TO__SHIFT 8 ++#define VIVS_GL_SEMAPHORE_TOKEN_TO(x) (((x) << VIVS_GL_SEMAPHORE_TOKEN_TO__SHIFT) & VIVS_GL_SEMAPHORE_TOKEN_TO__MASK) ++ ++#define VIVS_GL_FLUSH_CACHE 0x0000380c ++#define VIVS_GL_FLUSH_CACHE_DEPTH 0x00000001 ++#define VIVS_GL_FLUSH_CACHE_COLOR 0x00000002 ++#define VIVS_GL_FLUSH_CACHE_TEXTURE 0x00000004 ++#define VIVS_GL_FLUSH_CACHE_PE2D 0x00000008 ++#define VIVS_GL_FLUSH_CACHE_TEXTUREVS 0x00000010 ++#define VIVS_GL_FLUSH_CACHE_SHADER_L1 0x00000020 ++#define VIVS_GL_FLUSH_CACHE_SHADER_L2 0x00000040 ++ ++#define VIVS_GL_FLUSH_MMU 0x00003810 ++#define VIVS_GL_FLUSH_MMU_FLUSH_FEMMU 0x00000001 ++#define VIVS_GL_FLUSH_MMU_FLUSH_UNK1 0x00000002 ++#define VIVS_GL_FLUSH_MMU_FLUSH_UNK2 0x00000004 ++#define VIVS_GL_FLUSH_MMU_FLUSH_PEMMU 0x00000008 ++#define VIVS_GL_FLUSH_MMU_FLUSH_UNK4 0x00000010 ++ ++#define VIVS_GL_VERTEX_ELEMENT_CONFIG 0x00003814 ++ ++#define VIVS_GL_MULTI_SAMPLE_CONFIG 0x00003818 ++#define VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_SAMPLES__MASK 0x00000003 ++#define VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_SAMPLES__SHIFT 0 ++#define VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_SAMPLES_NONE 0x00000000 ++#define VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_SAMPLES_2X 0x00000001 ++#define VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_SAMPLES_4X 0x00000002 ++#define VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_SAMPLES_MASK 0x00000008 ++#define VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_ENABLES__MASK 0x000000f0 ++#define VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_ENABLES__SHIFT 4 ++#define VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_ENABLES(x) (((x) << VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_ENABLES__SHIFT) & VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_ENABLES__MASK) ++#define VIVS_GL_MULTI_SAMPLE_CONFIG_MSAA_ENABLES_MASK 0x00000100 ++#define VIVS_GL_MULTI_SAMPLE_CONFIG_UNK12__MASK 0x00007000 ++#define VIVS_GL_MULTI_SAMPLE_CONFIG_UNK12__SHIFT 12 ++#define VIVS_GL_MULTI_SAMPLE_CONFIG_UNK12(x) (((x) << VIVS_GL_MULTI_SAMPLE_CONFIG_UNK12__SHIFT) & VIVS_GL_MULTI_SAMPLE_CONFIG_UNK12__MASK) ++#define VIVS_GL_MULTI_SAMPLE_CONFIG_UNK12_MASK 0x00008000 ++#define VIVS_GL_MULTI_SAMPLE_CONFIG_UNK16__MASK 0x00030000 ++#define VIVS_GL_MULTI_SAMPLE_CONFIG_UNK16__SHIFT 16 ++#define VIVS_GL_MULTI_SAMPLE_CONFIG_UNK16(x) (((x) << VIVS_GL_MULTI_SAMPLE_CONFIG_UNK16__SHIFT) & VIVS_GL_MULTI_SAMPLE_CONFIG_UNK16__MASK) ++#define VIVS_GL_MULTI_SAMPLE_CONFIG_UNK16_MASK 0x00080000 ++ ++#define VIVS_GL_VARYING_TOTAL_COMPONENTS 0x0000381c ++#define VIVS_GL_VARYING_TOTAL_COMPONENTS_NUM__MASK 0x000000ff ++#define VIVS_GL_VARYING_TOTAL_COMPONENTS_NUM__SHIFT 0 ++#define VIVS_GL_VARYING_TOTAL_COMPONENTS_NUM(x) (((x) << VIVS_GL_VARYING_TOTAL_COMPONENTS_NUM__SHIFT) & VIVS_GL_VARYING_TOTAL_COMPONENTS_NUM__MASK) ++ ++#define VIVS_GL_VARYING_NUM_COMPONENTS 0x00003820 ++#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR0__MASK 0x00000007 ++#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR0__SHIFT 0 ++#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR0(x) (((x) << VIVS_GL_VARYING_NUM_COMPONENTS_VAR0__SHIFT) & VIVS_GL_VARYING_NUM_COMPONENTS_VAR0__MASK) ++#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR1__MASK 0x00000070 ++#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR1__SHIFT 4 ++#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR1(x) (((x) << VIVS_GL_VARYING_NUM_COMPONENTS_VAR1__SHIFT) & VIVS_GL_VARYING_NUM_COMPONENTS_VAR1__MASK) ++#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR2__MASK 0x00000700 ++#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR2__SHIFT 8 ++#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR2(x) (((x) << VIVS_GL_VARYING_NUM_COMPONENTS_VAR2__SHIFT) & VIVS_GL_VARYING_NUM_COMPONENTS_VAR2__MASK) ++#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR3__MASK 0x00007000 ++#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR3__SHIFT 12 ++#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR3(x) (((x) << VIVS_GL_VARYING_NUM_COMPONENTS_VAR3__SHIFT) & VIVS_GL_VARYING_NUM_COMPONENTS_VAR3__MASK) ++#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR4__MASK 0x00070000 ++#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR4__SHIFT 16 ++#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR4(x) (((x) << VIVS_GL_VARYING_NUM_COMPONENTS_VAR4__SHIFT) & VIVS_GL_VARYING_NUM_COMPONENTS_VAR4__MASK) ++#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR5__MASK 0x00700000 ++#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR5__SHIFT 20 ++#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR5(x) (((x) << VIVS_GL_VARYING_NUM_COMPONENTS_VAR5__SHIFT) & VIVS_GL_VARYING_NUM_COMPONENTS_VAR5__MASK) ++#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR6__MASK 0x07000000 ++#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR6__SHIFT 24 ++#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR6(x) (((x) << VIVS_GL_VARYING_NUM_COMPONENTS_VAR6__SHIFT) & VIVS_GL_VARYING_NUM_COMPONENTS_VAR6__MASK) ++#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR7__MASK 0x70000000 ++#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR7__SHIFT 28 ++#define VIVS_GL_VARYING_NUM_COMPONENTS_VAR7(x) (((x) << VIVS_GL_VARYING_NUM_COMPONENTS_VAR7__SHIFT) & VIVS_GL_VARYING_NUM_COMPONENTS_VAR7__MASK) ++ ++#define VIVS_GL_VARYING_COMPONENT_USE(i0) (0x00003828 + 0x4*(i0)) ++#define VIVS_GL_VARYING_COMPONENT_USE__ESIZE 0x00000004 ++#define VIVS_GL_VARYING_COMPONENT_USE__LEN 0x00000002 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP0__MASK 0x00000003 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP0__SHIFT 0 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP0(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP0__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP0__MASK) ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP1__MASK 0x0000000c ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP1__SHIFT 2 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP1(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP1__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP1__MASK) ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP2__MASK 0x00000030 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP2__SHIFT 4 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP2(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP2__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP2__MASK) ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP3__MASK 0x000000c0 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP3__SHIFT 6 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP3(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP3__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP3__MASK) ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP4__MASK 0x00000300 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP4__SHIFT 8 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP4(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP4__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP4__MASK) ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP5__MASK 0x00000c00 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP5__SHIFT 10 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP5(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP5__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP5__MASK) ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP6__MASK 0x00003000 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP6__SHIFT 12 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP6(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP6__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP6__MASK) ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP7__MASK 0x0000c000 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP7__SHIFT 14 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP7(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP7__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP7__MASK) ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP8__MASK 0x00030000 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP8__SHIFT 16 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP8(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP8__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP8__MASK) ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP9__MASK 0x000c0000 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP9__SHIFT 18 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP9(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP9__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP9__MASK) ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP10__MASK 0x00300000 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP10__SHIFT 20 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP10(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP10__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP10__MASK) ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP11__MASK 0x00c00000 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP11__SHIFT 22 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP11(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP11__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP11__MASK) ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP12__MASK 0x03000000 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP12__SHIFT 24 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP12(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP12__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP12__MASK) ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP13__MASK 0x0c000000 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP13__SHIFT 26 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP13(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP13__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP13__MASK) ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP14__MASK 0x30000000 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP14__SHIFT 28 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP14(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP14__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP14__MASK) ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP15__MASK 0xc0000000 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP15__SHIFT 30 ++#define VIVS_GL_VARYING_COMPONENT_USE_COMP15(x) (((x) << VIVS_GL_VARYING_COMPONENT_USE_COMP15__SHIFT) & VIVS_GL_VARYING_COMPONENT_USE_COMP15__MASK) ++ ++#define VIVS_GL_UNK03834 0x00003834 ++ ++#define VIVS_GL_UNK03838 0x00003838 ++ ++#define VIVS_GL_API_MODE 0x0000384c ++#define VIVS_GL_API_MODE_OPENGL 0x00000000 ++#define VIVS_GL_API_MODE_OPENVG 0x00000001 ++#define VIVS_GL_API_MODE_OPENCL 0x00000002 ++ ++#define VIVS_GL_CONTEXT_POINTER 0x00003850 ++ ++#define VIVS_GL_UNK03A00 0x00003a00 ++ ++#define VIVS_GL_STALL_TOKEN 0x00003c00 ++#define VIVS_GL_STALL_TOKEN_FROM__MASK 0x0000001f ++#define VIVS_GL_STALL_TOKEN_FROM__SHIFT 0 ++#define VIVS_GL_STALL_TOKEN_FROM(x) (((x) << VIVS_GL_STALL_TOKEN_FROM__SHIFT) & VIVS_GL_STALL_TOKEN_FROM__MASK) ++#define VIVS_GL_STALL_TOKEN_TO__MASK 0x00001f00 ++#define VIVS_GL_STALL_TOKEN_TO__SHIFT 8 ++#define VIVS_GL_STALL_TOKEN_TO(x) (((x) << VIVS_GL_STALL_TOKEN_TO__SHIFT) & VIVS_GL_STALL_TOKEN_TO__MASK) ++#define VIVS_GL_STALL_TOKEN_FLIP0 0x40000000 ++#define VIVS_GL_STALL_TOKEN_FLIP1 0x80000000 ++ ++#define VIVS_DUMMY 0x00000000 ++ ++#define VIVS_DUMMY_DUMMY 0x0003fffc ++ ++ ++#endif /* STATE_XML */ +diff --git a/drivers/gpu/drm/etnaviv/state_hi.xml.h b/drivers/gpu/drm/etnaviv/state_hi.xml.h +new file mode 100644 +index 0000000..0064f26 +--- /dev/null ++++ b/drivers/gpu/drm/etnaviv/state_hi.xml.h +@@ -0,0 +1,407 @@ ++#ifndef STATE_HI_XML ++#define STATE_HI_XML ++ ++/* Autogenerated file, DO NOT EDIT manually! ++ ++This file was generated by the rules-ng-ng headergen tool in this git repository: ++http://0x04.net/cgit/index.cgi/rules-ng-ng ++git clone git://0x04.net/rules-ng-ng ++ ++The rules-ng-ng source files this header was generated from are: ++- state_hi.xml ( 23420 bytes, from 2015-03-25 11:47:21) ++- common.xml ( 18437 bytes, from 2015-03-25 11:27:41) ++ ++Copyright (C) 2015 ++*/ ++ ++ ++#define MMU_EXCEPTION_SLAVE_NOT_PRESENT 0x00000001 ++#define MMU_EXCEPTION_PAGE_NOT_PRESENT 0x00000002 ++#define MMU_EXCEPTION_WRITE_VIOLATION 0x00000003 ++#define VIVS_HI 0x00000000 ++ ++#define VIVS_HI_CLOCK_CONTROL 0x00000000 ++#define VIVS_HI_CLOCK_CONTROL_CLK3D_DIS 0x00000001 ++#define VIVS_HI_CLOCK_CONTROL_CLK2D_DIS 0x00000002 ++#define VIVS_HI_CLOCK_CONTROL_FSCALE_VAL__MASK 0x000001fc ++#define VIVS_HI_CLOCK_CONTROL_FSCALE_VAL__SHIFT 2 ++#define VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(x) (((x) << VIVS_HI_CLOCK_CONTROL_FSCALE_VAL__SHIFT) & VIVS_HI_CLOCK_CONTROL_FSCALE_VAL__MASK) ++#define VIVS_HI_CLOCK_CONTROL_FSCALE_CMD_LOAD 0x00000200 ++#define VIVS_HI_CLOCK_CONTROL_DISABLE_RAM_CLK_GATING 0x00000400 ++#define VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS 0x00000800 ++#define VIVS_HI_CLOCK_CONTROL_SOFT_RESET 0x00001000 ++#define VIVS_HI_CLOCK_CONTROL_IDLE_3D 0x00010000 ++#define VIVS_HI_CLOCK_CONTROL_IDLE_2D 0x00020000 ++#define VIVS_HI_CLOCK_CONTROL_IDLE_VG 0x00040000 ++#define VIVS_HI_CLOCK_CONTROL_ISOLATE_GPU 0x00080000 ++#define VIVS_HI_CLOCK_CONTROL_DEBUG_PIXEL_PIPE__MASK 0x00f00000 ++#define VIVS_HI_CLOCK_CONTROL_DEBUG_PIXEL_PIPE__SHIFT 20 ++#define VIVS_HI_CLOCK_CONTROL_DEBUG_PIXEL_PIPE(x) (((x) << VIVS_HI_CLOCK_CONTROL_DEBUG_PIXEL_PIPE__SHIFT) & VIVS_HI_CLOCK_CONTROL_DEBUG_PIXEL_PIPE__MASK) ++ ++#define VIVS_HI_IDLE_STATE 0x00000004 ++#define VIVS_HI_IDLE_STATE_FE 0x00000001 ++#define VIVS_HI_IDLE_STATE_DE 0x00000002 ++#define VIVS_HI_IDLE_STATE_PE 0x00000004 ++#define VIVS_HI_IDLE_STATE_SH 0x00000008 ++#define VIVS_HI_IDLE_STATE_PA 0x00000010 ++#define VIVS_HI_IDLE_STATE_SE 0x00000020 ++#define VIVS_HI_IDLE_STATE_RA 0x00000040 ++#define VIVS_HI_IDLE_STATE_TX 0x00000080 ++#define VIVS_HI_IDLE_STATE_VG 0x00000100 ++#define VIVS_HI_IDLE_STATE_IM 0x00000200 ++#define VIVS_HI_IDLE_STATE_FP 0x00000400 ++#define VIVS_HI_IDLE_STATE_TS 0x00000800 ++#define VIVS_HI_IDLE_STATE_AXI_LP 0x80000000 ++ ++#define VIVS_HI_AXI_CONFIG 0x00000008 ++#define VIVS_HI_AXI_CONFIG_AWID__MASK 0x0000000f ++#define VIVS_HI_AXI_CONFIG_AWID__SHIFT 0 ++#define VIVS_HI_AXI_CONFIG_AWID(x) (((x) << VIVS_HI_AXI_CONFIG_AWID__SHIFT) & VIVS_HI_AXI_CONFIG_AWID__MASK) ++#define VIVS_HI_AXI_CONFIG_ARID__MASK 0x000000f0 ++#define VIVS_HI_AXI_CONFIG_ARID__SHIFT 4 ++#define VIVS_HI_AXI_CONFIG_ARID(x) (((x) << VIVS_HI_AXI_CONFIG_ARID__SHIFT) & VIVS_HI_AXI_CONFIG_ARID__MASK) ++#define VIVS_HI_AXI_CONFIG_AWCACHE__MASK 0x00000f00 ++#define VIVS_HI_AXI_CONFIG_AWCACHE__SHIFT 8 ++#define VIVS_HI_AXI_CONFIG_AWCACHE(x) (((x) << VIVS_HI_AXI_CONFIG_AWCACHE__SHIFT) & VIVS_HI_AXI_CONFIG_AWCACHE__MASK) ++#define VIVS_HI_AXI_CONFIG_ARCACHE__MASK 0x0000f000 ++#define VIVS_HI_AXI_CONFIG_ARCACHE__SHIFT 12 ++#define VIVS_HI_AXI_CONFIG_ARCACHE(x) (((x) << VIVS_HI_AXI_CONFIG_ARCACHE__SHIFT) & VIVS_HI_AXI_CONFIG_ARCACHE__MASK) ++ ++#define VIVS_HI_AXI_STATUS 0x0000000c ++#define VIVS_HI_AXI_STATUS_WR_ERR_ID__MASK 0x0000000f ++#define VIVS_HI_AXI_STATUS_WR_ERR_ID__SHIFT 0 ++#define VIVS_HI_AXI_STATUS_WR_ERR_ID(x) (((x) << VIVS_HI_AXI_STATUS_WR_ERR_ID__SHIFT) & VIVS_HI_AXI_STATUS_WR_ERR_ID__MASK) ++#define VIVS_HI_AXI_STATUS_RD_ERR_ID__MASK 0x000000f0 ++#define VIVS_HI_AXI_STATUS_RD_ERR_ID__SHIFT 4 ++#define VIVS_HI_AXI_STATUS_RD_ERR_ID(x) (((x) << VIVS_HI_AXI_STATUS_RD_ERR_ID__SHIFT) & VIVS_HI_AXI_STATUS_RD_ERR_ID__MASK) ++#define VIVS_HI_AXI_STATUS_DET_WR_ERR 0x00000100 ++#define VIVS_HI_AXI_STATUS_DET_RD_ERR 0x00000200 ++ ++#define VIVS_HI_INTR_ACKNOWLEDGE 0x00000010 ++#define VIVS_HI_INTR_ACKNOWLEDGE_INTR_VEC__MASK 0x7fffffff ++#define VIVS_HI_INTR_ACKNOWLEDGE_INTR_VEC__SHIFT 0 ++#define VIVS_HI_INTR_ACKNOWLEDGE_INTR_VEC(x) (((x) << VIVS_HI_INTR_ACKNOWLEDGE_INTR_VEC__SHIFT) & VIVS_HI_INTR_ACKNOWLEDGE_INTR_VEC__MASK) ++#define VIVS_HI_INTR_ACKNOWLEDGE_AXI_BUS_ERROR 0x80000000 ++ ++#define VIVS_HI_INTR_ENBL 0x00000014 ++#define VIVS_HI_INTR_ENBL_INTR_ENBL_VEC__MASK 0xffffffff ++#define VIVS_HI_INTR_ENBL_INTR_ENBL_VEC__SHIFT 0 ++#define VIVS_HI_INTR_ENBL_INTR_ENBL_VEC(x) (((x) << VIVS_HI_INTR_ENBL_INTR_ENBL_VEC__SHIFT) & VIVS_HI_INTR_ENBL_INTR_ENBL_VEC__MASK) ++ ++#define VIVS_HI_CHIP_IDENTITY 0x00000018 ++#define VIVS_HI_CHIP_IDENTITY_FAMILY__MASK 0xff000000 ++#define VIVS_HI_CHIP_IDENTITY_FAMILY__SHIFT 24 ++#define VIVS_HI_CHIP_IDENTITY_FAMILY(x) (((x) << VIVS_HI_CHIP_IDENTITY_FAMILY__SHIFT) & VIVS_HI_CHIP_IDENTITY_FAMILY__MASK) ++#define VIVS_HI_CHIP_IDENTITY_PRODUCT__MASK 0x00ff0000 ++#define VIVS_HI_CHIP_IDENTITY_PRODUCT__SHIFT 16 ++#define VIVS_HI_CHIP_IDENTITY_PRODUCT(x) (((x) << VIVS_HI_CHIP_IDENTITY_PRODUCT__SHIFT) & VIVS_HI_CHIP_IDENTITY_PRODUCT__MASK) ++#define VIVS_HI_CHIP_IDENTITY_REVISION__MASK 0x0000f000 ++#define VIVS_HI_CHIP_IDENTITY_REVISION__SHIFT 12 ++#define VIVS_HI_CHIP_IDENTITY_REVISION(x) (((x) << VIVS_HI_CHIP_IDENTITY_REVISION__SHIFT) & VIVS_HI_CHIP_IDENTITY_REVISION__MASK) ++ ++#define VIVS_HI_CHIP_FEATURE 0x0000001c ++ ++#define VIVS_HI_CHIP_MODEL 0x00000020 ++ ++#define VIVS_HI_CHIP_REV 0x00000024 ++ ++#define VIVS_HI_CHIP_DATE 0x00000028 ++ ++#define VIVS_HI_CHIP_TIME 0x0000002c ++ ++#define VIVS_HI_CHIP_MINOR_FEATURE_0 0x00000034 ++ ++#define VIVS_HI_CACHE_CONTROL 0x00000038 ++ ++#define VIVS_HI_MEMORY_COUNTER_RESET 0x0000003c ++ ++#define VIVS_HI_PROFILE_READ_BYTES8 0x00000040 ++ ++#define VIVS_HI_PROFILE_WRITE_BYTES8 0x00000044 ++ ++#define VIVS_HI_CHIP_SPECS 0x00000048 ++#define VIVS_HI_CHIP_SPECS_STREAM_COUNT__MASK 0x0000000f ++#define VIVS_HI_CHIP_SPECS_STREAM_COUNT__SHIFT 0 ++#define VIVS_HI_CHIP_SPECS_STREAM_COUNT(x) (((x) << VIVS_HI_CHIP_SPECS_STREAM_COUNT__SHIFT) & VIVS_HI_CHIP_SPECS_STREAM_COUNT__MASK) ++#define VIVS_HI_CHIP_SPECS_REGISTER_MAX__MASK 0x000000f0 ++#define VIVS_HI_CHIP_SPECS_REGISTER_MAX__SHIFT 4 ++#define VIVS_HI_CHIP_SPECS_REGISTER_MAX(x) (((x) << VIVS_HI_CHIP_SPECS_REGISTER_MAX__SHIFT) & VIVS_HI_CHIP_SPECS_REGISTER_MAX__MASK) ++#define VIVS_HI_CHIP_SPECS_THREAD_COUNT__MASK 0x00000f00 ++#define VIVS_HI_CHIP_SPECS_THREAD_COUNT__SHIFT 8 ++#define VIVS_HI_CHIP_SPECS_THREAD_COUNT(x) (((x) << VIVS_HI_CHIP_SPECS_THREAD_COUNT__SHIFT) & VIVS_HI_CHIP_SPECS_THREAD_COUNT__MASK) ++#define VIVS_HI_CHIP_SPECS_VERTEX_CACHE_SIZE__MASK 0x0001f000 ++#define VIVS_HI_CHIP_SPECS_VERTEX_CACHE_SIZE__SHIFT 12 ++#define VIVS_HI_CHIP_SPECS_VERTEX_CACHE_SIZE(x) (((x) << VIVS_HI_CHIP_SPECS_VERTEX_CACHE_SIZE__SHIFT) & VIVS_HI_CHIP_SPECS_VERTEX_CACHE_SIZE__MASK) ++#define VIVS_HI_CHIP_SPECS_SHADER_CORE_COUNT__MASK 0x01f00000 ++#define VIVS_HI_CHIP_SPECS_SHADER_CORE_COUNT__SHIFT 20 ++#define VIVS_HI_CHIP_SPECS_SHADER_CORE_COUNT(x) (((x) << VIVS_HI_CHIP_SPECS_SHADER_CORE_COUNT__SHIFT) & VIVS_HI_CHIP_SPECS_SHADER_CORE_COUNT__MASK) ++#define VIVS_HI_CHIP_SPECS_PIXEL_PIPES__MASK 0x0e000000 ++#define VIVS_HI_CHIP_SPECS_PIXEL_PIPES__SHIFT 25 ++#define VIVS_HI_CHIP_SPECS_PIXEL_PIPES(x) (((x) << VIVS_HI_CHIP_SPECS_PIXEL_PIPES__SHIFT) & VIVS_HI_CHIP_SPECS_PIXEL_PIPES__MASK) ++#define VIVS_HI_CHIP_SPECS_VERTEX_OUTPUT_BUFFER_SIZE__MASK 0xf0000000 ++#define VIVS_HI_CHIP_SPECS_VERTEX_OUTPUT_BUFFER_SIZE__SHIFT 28 ++#define VIVS_HI_CHIP_SPECS_VERTEX_OUTPUT_BUFFER_SIZE(x) (((x) << VIVS_HI_CHIP_SPECS_VERTEX_OUTPUT_BUFFER_SIZE__SHIFT) & VIVS_HI_CHIP_SPECS_VERTEX_OUTPUT_BUFFER_SIZE__MASK) ++ ++#define VIVS_HI_PROFILE_WRITE_BURSTS 0x0000004c ++ ++#define VIVS_HI_PROFILE_WRITE_REQUESTS 0x00000050 ++ ++#define VIVS_HI_PROFILE_READ_BURSTS 0x00000058 ++ ++#define VIVS_HI_PROFILE_READ_REQUESTS 0x0000005c ++ ++#define VIVS_HI_PROFILE_READ_LASTS 0x00000060 ++ ++#define VIVS_HI_GP_OUT0 0x00000064 ++ ++#define VIVS_HI_GP_OUT1 0x00000068 ++ ++#define VIVS_HI_GP_OUT2 0x0000006c ++ ++#define VIVS_HI_AXI_CONTROL 0x00000070 ++#define VIVS_HI_AXI_CONTROL_WR_FULL_BURST_MODE 0x00000001 ++ ++#define VIVS_HI_CHIP_MINOR_FEATURE_1 0x00000074 ++ ++#define VIVS_HI_PROFILE_TOTAL_CYCLES 0x00000078 ++ ++#define VIVS_HI_PROFILE_IDLE_CYCLES 0x0000007c ++ ++#define VIVS_HI_CHIP_SPECS_2 0x00000080 ++#define VIVS_HI_CHIP_SPECS_2_BUFFER_SIZE__MASK 0x000000ff ++#define VIVS_HI_CHIP_SPECS_2_BUFFER_SIZE__SHIFT 0 ++#define VIVS_HI_CHIP_SPECS_2_BUFFER_SIZE(x) (((x) << VIVS_HI_CHIP_SPECS_2_BUFFER_SIZE__SHIFT) & VIVS_HI_CHIP_SPECS_2_BUFFER_SIZE__MASK) ++#define VIVS_HI_CHIP_SPECS_2_INSTRUCTION_COUNT__MASK 0x0000ff00 ++#define VIVS_HI_CHIP_SPECS_2_INSTRUCTION_COUNT__SHIFT 8 ++#define VIVS_HI_CHIP_SPECS_2_INSTRUCTION_COUNT(x) (((x) << VIVS_HI_CHIP_SPECS_2_INSTRUCTION_COUNT__SHIFT) & VIVS_HI_CHIP_SPECS_2_INSTRUCTION_COUNT__MASK) ++#define VIVS_HI_CHIP_SPECS_2_NUM_CONSTANTS__MASK 0xffff0000 ++#define VIVS_HI_CHIP_SPECS_2_NUM_CONSTANTS__SHIFT 16 ++#define VIVS_HI_CHIP_SPECS_2_NUM_CONSTANTS(x) (((x) << VIVS_HI_CHIP_SPECS_2_NUM_CONSTANTS__SHIFT) & VIVS_HI_CHIP_SPECS_2_NUM_CONSTANTS__MASK) ++ ++#define VIVS_HI_CHIP_MINOR_FEATURE_2 0x00000084 ++ ++#define VIVS_HI_CHIP_MINOR_FEATURE_3 0x00000088 ++ ++#define VIVS_HI_CHIP_MINOR_FEATURE_4 0x00000094 ++ ++#define VIVS_PM 0x00000000 ++ ++#define VIVS_PM_POWER_CONTROLS 0x00000100 ++#define VIVS_PM_POWER_CONTROLS_ENABLE_MODULE_CLOCK_GATING 0x00000001 ++#define VIVS_PM_POWER_CONTROLS_DISABLE_STALL_MODULE_CLOCK_GATING 0x00000002 ++#define VIVS_PM_POWER_CONTROLS_DISABLE_STARVE_MODULE_CLOCK_GATING 0x00000004 ++#define VIVS_PM_POWER_CONTROLS_TURN_ON_COUNTER__MASK 0x000000f0 ++#define VIVS_PM_POWER_CONTROLS_TURN_ON_COUNTER__SHIFT 4 ++#define VIVS_PM_POWER_CONTROLS_TURN_ON_COUNTER(x) (((x) << VIVS_PM_POWER_CONTROLS_TURN_ON_COUNTER__SHIFT) & VIVS_PM_POWER_CONTROLS_TURN_ON_COUNTER__MASK) ++#define VIVS_PM_POWER_CONTROLS_TURN_OFF_COUNTER__MASK 0xffff0000 ++#define VIVS_PM_POWER_CONTROLS_TURN_OFF_COUNTER__SHIFT 16 ++#define VIVS_PM_POWER_CONTROLS_TURN_OFF_COUNTER(x) (((x) << VIVS_PM_POWER_CONTROLS_TURN_OFF_COUNTER__SHIFT) & VIVS_PM_POWER_CONTROLS_TURN_OFF_COUNTER__MASK) ++ ++#define VIVS_PM_MODULE_CONTROLS 0x00000104 ++#define VIVS_PM_MODULE_CONTROLS_DISABLE_MODULE_CLOCK_GATING_FE 0x00000001 ++#define VIVS_PM_MODULE_CONTROLS_DISABLE_MODULE_CLOCK_GATING_DE 0x00000002 ++#define VIVS_PM_MODULE_CONTROLS_DISABLE_MODULE_CLOCK_GATING_PE 0x00000004 ++ ++#define VIVS_PM_MODULE_STATUS 0x00000108 ++#define VIVS_PM_MODULE_STATUS_MODULE_CLOCK_GATED_FE 0x00000001 ++#define VIVS_PM_MODULE_STATUS_MODULE_CLOCK_GATED_DE 0x00000002 ++#define VIVS_PM_MODULE_STATUS_MODULE_CLOCK_GATED_PE 0x00000004 ++ ++#define VIVS_PM_PULSE_EATER 0x0000010c ++ ++#define VIVS_MMUv2 0x00000000 ++ ++#define VIVS_MMUv2_SAFE_ADDRESS 0x00000180 ++ ++#define VIVS_MMUv2_CONFIGURATION 0x00000184 ++#define VIVS_MMUv2_CONFIGURATION_MODE__MASK 0x00000001 ++#define VIVS_MMUv2_CONFIGURATION_MODE__SHIFT 0 ++#define VIVS_MMUv2_CONFIGURATION_MODE_MODE4_K 0x00000000 ++#define VIVS_MMUv2_CONFIGURATION_MODE_MODE1_K 0x00000001 ++#define VIVS_MMUv2_CONFIGURATION_MODE_MASK 0x00000008 ++#define VIVS_MMUv2_CONFIGURATION_FLUSH__MASK 0x00000010 ++#define VIVS_MMUv2_CONFIGURATION_FLUSH__SHIFT 4 ++#define VIVS_MMUv2_CONFIGURATION_FLUSH_FLUSH 0x00000010 ++#define VIVS_MMUv2_CONFIGURATION_FLUSH_MASK 0x00000080 ++#define VIVS_MMUv2_CONFIGURATION_ADDRESS_MASK 0x00000100 ++#define VIVS_MMUv2_CONFIGURATION_ADDRESS__MASK 0xfffffc00 ++#define VIVS_MMUv2_CONFIGURATION_ADDRESS__SHIFT 10 ++#define VIVS_MMUv2_CONFIGURATION_ADDRESS(x) (((x) << VIVS_MMUv2_CONFIGURATION_ADDRESS__SHIFT) & VIVS_MMUv2_CONFIGURATION_ADDRESS__MASK) ++ ++#define VIVS_MMUv2_STATUS 0x00000188 ++#define VIVS_MMUv2_STATUS_EXCEPTION0__MASK 0x00000003 ++#define VIVS_MMUv2_STATUS_EXCEPTION0__SHIFT 0 ++#define VIVS_MMUv2_STATUS_EXCEPTION0(x) (((x) << VIVS_MMUv2_STATUS_EXCEPTION0__SHIFT) & VIVS_MMUv2_STATUS_EXCEPTION0__MASK) ++#define VIVS_MMUv2_STATUS_EXCEPTION1__MASK 0x00000030 ++#define VIVS_MMUv2_STATUS_EXCEPTION1__SHIFT 4 ++#define VIVS_MMUv2_STATUS_EXCEPTION1(x) (((x) << VIVS_MMUv2_STATUS_EXCEPTION1__SHIFT) & VIVS_MMUv2_STATUS_EXCEPTION1__MASK) ++#define VIVS_MMUv2_STATUS_EXCEPTION2__MASK 0x00000300 ++#define VIVS_MMUv2_STATUS_EXCEPTION2__SHIFT 8 ++#define VIVS_MMUv2_STATUS_EXCEPTION2(x) (((x) << VIVS_MMUv2_STATUS_EXCEPTION2__SHIFT) & VIVS_MMUv2_STATUS_EXCEPTION2__MASK) ++#define VIVS_MMUv2_STATUS_EXCEPTION3__MASK 0x00003000 ++#define VIVS_MMUv2_STATUS_EXCEPTION3__SHIFT 12 ++#define VIVS_MMUv2_STATUS_EXCEPTION3(x) (((x) << VIVS_MMUv2_STATUS_EXCEPTION3__SHIFT) & VIVS_MMUv2_STATUS_EXCEPTION3__MASK) ++ ++#define VIVS_MMUv2_CONTROL 0x0000018c ++#define VIVS_MMUv2_CONTROL_ENABLE 0x00000001 ++ ++#define VIVS_MMUv2_EXCEPTION_ADDR(i0) (0x00000190 + 0x4*(i0)) ++#define VIVS_MMUv2_EXCEPTION_ADDR__ESIZE 0x00000004 ++#define VIVS_MMUv2_EXCEPTION_ADDR__LEN 0x00000004 ++ ++#define VIVS_MC 0x00000000 ++ ++#define VIVS_MC_MMU_FE_PAGE_TABLE 0x00000400 ++ ++#define VIVS_MC_MMU_TX_PAGE_TABLE 0x00000404 ++ ++#define VIVS_MC_MMU_PE_PAGE_TABLE 0x00000408 ++ ++#define VIVS_MC_MMU_PEZ_PAGE_TABLE 0x0000040c ++ ++#define VIVS_MC_MMU_RA_PAGE_TABLE 0x00000410 ++ ++#define VIVS_MC_DEBUG_MEMORY 0x00000414 ++#define VIVS_MC_DEBUG_MEMORY_SPECIAL_PATCH_GC320 0x00000008 ++#define VIVS_MC_DEBUG_MEMORY_FAST_CLEAR_BYPASS 0x00100000 ++#define VIVS_MC_DEBUG_MEMORY_COMPRESSION_BYPASS 0x00200000 ++ ++#define VIVS_MC_MEMORY_BASE_ADDR_RA 0x00000418 ++ ++#define VIVS_MC_MEMORY_BASE_ADDR_FE 0x0000041c ++ ++#define VIVS_MC_MEMORY_BASE_ADDR_TX 0x00000420 ++ ++#define VIVS_MC_MEMORY_BASE_ADDR_PEZ 0x00000424 ++ ++#define VIVS_MC_MEMORY_BASE_ADDR_PE 0x00000428 ++ ++#define VIVS_MC_MEMORY_TIMING_CONTROL 0x0000042c ++ ++#define VIVS_MC_MEMORY_FLUSH 0x00000430 ++ ++#define VIVS_MC_PROFILE_CYCLE_COUNTER 0x00000438 ++ ++#define VIVS_MC_DEBUG_READ0 0x0000043c ++ ++#define VIVS_MC_DEBUG_READ1 0x00000440 ++ ++#define VIVS_MC_DEBUG_WRITE 0x00000444 ++ ++#define VIVS_MC_PROFILE_RA_READ 0x00000448 ++ ++#define VIVS_MC_PROFILE_TX_READ 0x0000044c ++ ++#define VIVS_MC_PROFILE_FE_READ 0x00000450 ++ ++#define VIVS_MC_PROFILE_PE_READ 0x00000454 ++ ++#define VIVS_MC_PROFILE_DE_READ 0x00000458 ++ ++#define VIVS_MC_PROFILE_SH_READ 0x0000045c ++ ++#define VIVS_MC_PROFILE_PA_READ 0x00000460 ++ ++#define VIVS_MC_PROFILE_SE_READ 0x00000464 ++ ++#define VIVS_MC_PROFILE_MC_READ 0x00000468 ++ ++#define VIVS_MC_PROFILE_HI_READ 0x0000046c ++ ++#define VIVS_MC_PROFILE_CONFIG0 0x00000470 ++#define VIVS_MC_PROFILE_CONFIG0_FE__MASK 0x0000000f ++#define VIVS_MC_PROFILE_CONFIG0_FE__SHIFT 0 ++#define VIVS_MC_PROFILE_CONFIG0_FE_RESET 0x0000000f ++#define VIVS_MC_PROFILE_CONFIG0_DE__MASK 0x00000f00 ++#define VIVS_MC_PROFILE_CONFIG0_DE__SHIFT 8 ++#define VIVS_MC_PROFILE_CONFIG0_DE_RESET 0x00000f00 ++#define VIVS_MC_PROFILE_CONFIG0_PE__MASK 0x000f0000 ++#define VIVS_MC_PROFILE_CONFIG0_PE__SHIFT 16 ++#define VIVS_MC_PROFILE_CONFIG0_PE_PIXEL_COUNT_KILLED_BY_COLOR_PIPE 0x00000000 ++#define VIVS_MC_PROFILE_CONFIG0_PE_PIXEL_COUNT_KILLED_BY_DEPTH_PIPE 0x00010000 ++#define VIVS_MC_PROFILE_CONFIG0_PE_PIXEL_COUNT_DRAWN_BY_COLOR_PIPE 0x00020000 ++#define VIVS_MC_PROFILE_CONFIG0_PE_PIXEL_COUNT_DRAWN_BY_DEPTH_PIPE 0x00030000 ++#define VIVS_MC_PROFILE_CONFIG0_PE_PIXELS_RENDERED_2D 0x000b0000 ++#define VIVS_MC_PROFILE_CONFIG0_PE_RESET 0x000f0000 ++#define VIVS_MC_PROFILE_CONFIG0_SH__MASK 0x0f000000 ++#define VIVS_MC_PROFILE_CONFIG0_SH__SHIFT 24 ++#define VIVS_MC_PROFILE_CONFIG0_SH_SHADER_CYCLES 0x04000000 ++#define VIVS_MC_PROFILE_CONFIG0_SH_PS_INST_COUNTER 0x07000000 ++#define VIVS_MC_PROFILE_CONFIG0_SH_RENDERED_PIXEL_COUNTER 0x08000000 ++#define VIVS_MC_PROFILE_CONFIG0_SH_VS_INST_COUNTER 0x09000000 ++#define VIVS_MC_PROFILE_CONFIG0_SH_RENDERED_VERTICE_COUNTER 0x0a000000 ++#define VIVS_MC_PROFILE_CONFIG0_SH_VTX_BRANCH_INST_COUNTER 0x0b000000 ++#define VIVS_MC_PROFILE_CONFIG0_SH_VTX_TEXLD_INST_COUNTER 0x0c000000 ++#define VIVS_MC_PROFILE_CONFIG0_SH_PXL_BRANCH_INST_COUNTER 0x0d000000 ++#define VIVS_MC_PROFILE_CONFIG0_SH_PXL_TEXLD_INST_COUNTER 0x0e000000 ++#define VIVS_MC_PROFILE_CONFIG0_SH_RESET 0x0f000000 ++ ++#define VIVS_MC_PROFILE_CONFIG1 0x00000474 ++#define VIVS_MC_PROFILE_CONFIG1_PA__MASK 0x0000000f ++#define VIVS_MC_PROFILE_CONFIG1_PA__SHIFT 0 ++#define VIVS_MC_PROFILE_CONFIG1_PA_INPUT_VTX_COUNTER 0x00000003 ++#define VIVS_MC_PROFILE_CONFIG1_PA_INPUT_PRIM_COUNTER 0x00000004 ++#define VIVS_MC_PROFILE_CONFIG1_PA_OUTPUT_PRIM_COUNTER 0x00000005 ++#define VIVS_MC_PROFILE_CONFIG1_PA_DEPTH_CLIPPED_COUNTER 0x00000006 ++#define VIVS_MC_PROFILE_CONFIG1_PA_TRIVIAL_REJECTED_COUNTER 0x00000007 ++#define VIVS_MC_PROFILE_CONFIG1_PA_CULLED_COUNTER 0x00000008 ++#define VIVS_MC_PROFILE_CONFIG1_PA_RESET 0x0000000f ++#define VIVS_MC_PROFILE_CONFIG1_SE__MASK 0x00000f00 ++#define VIVS_MC_PROFILE_CONFIG1_SE__SHIFT 8 ++#define VIVS_MC_PROFILE_CONFIG1_SE_CULLED_TRIANGLE_COUNT 0x00000000 ++#define VIVS_MC_PROFILE_CONFIG1_SE_CULLED_LINES_COUNT 0x00000100 ++#define VIVS_MC_PROFILE_CONFIG1_SE_RESET 0x00000f00 ++#define VIVS_MC_PROFILE_CONFIG1_RA__MASK 0x000f0000 ++#define VIVS_MC_PROFILE_CONFIG1_RA__SHIFT 16 ++#define VIVS_MC_PROFILE_CONFIG1_RA_VALID_PIXEL_COUNT 0x00000000 ++#define VIVS_MC_PROFILE_CONFIG1_RA_TOTAL_QUAD_COUNT 0x00010000 ++#define VIVS_MC_PROFILE_CONFIG1_RA_VALID_QUAD_COUNT_AFTER_EARLY_Z 0x00020000 ++#define VIVS_MC_PROFILE_CONFIG1_RA_TOTAL_PRIMITIVE_COUNT 0x00030000 ++#define VIVS_MC_PROFILE_CONFIG1_RA_PIPE_CACHE_MISS_COUNTER 0x00090000 ++#define VIVS_MC_PROFILE_CONFIG1_RA_PREFETCH_CACHE_MISS_COUNTER 0x000a0000 ++#define VIVS_MC_PROFILE_CONFIG1_RA_CULLED_QUAD_COUNT 0x000b0000 ++#define VIVS_MC_PROFILE_CONFIG1_RA_RESET 0x000f0000 ++#define VIVS_MC_PROFILE_CONFIG1_TX__MASK 0x0f000000 ++#define VIVS_MC_PROFILE_CONFIG1_TX__SHIFT 24 ++#define VIVS_MC_PROFILE_CONFIG1_TX_TOTAL_BILINEAR_REQUESTS 0x00000000 ++#define VIVS_MC_PROFILE_CONFIG1_TX_TOTAL_TRILINEAR_REQUESTS 0x01000000 ++#define VIVS_MC_PROFILE_CONFIG1_TX_TOTAL_DISCARDED_TEXTURE_REQUESTS 0x02000000 ++#define VIVS_MC_PROFILE_CONFIG1_TX_TOTAL_TEXTURE_REQUESTS 0x03000000 ++#define VIVS_MC_PROFILE_CONFIG1_TX_UNKNOWN 0x04000000 ++#define VIVS_MC_PROFILE_CONFIG1_TX_MEM_READ_COUNT 0x05000000 ++#define VIVS_MC_PROFILE_CONFIG1_TX_MEM_READ_IN_8B_COUNT 0x06000000 ++#define VIVS_MC_PROFILE_CONFIG1_TX_CACHE_MISS_COUNT 0x07000000 ++#define VIVS_MC_PROFILE_CONFIG1_TX_CACHE_HIT_TEXEL_COUNT 0x08000000 ++#define VIVS_MC_PROFILE_CONFIG1_TX_CACHE_MISS_TEXEL_COUNT 0x09000000 ++#define VIVS_MC_PROFILE_CONFIG1_TX_RESET 0x0f000000 ++ ++#define VIVS_MC_PROFILE_CONFIG2 0x00000478 ++#define VIVS_MC_PROFILE_CONFIG2_MC__MASK 0x0000000f ++#define VIVS_MC_PROFILE_CONFIG2_MC__SHIFT 0 ++#define VIVS_MC_PROFILE_CONFIG2_MC_TOTAL_READ_REQ_8B_FROM_PIPELINE 0x00000001 ++#define VIVS_MC_PROFILE_CONFIG2_MC_TOTAL_READ_REQ_8B_FROM_IP 0x00000002 ++#define VIVS_MC_PROFILE_CONFIG2_MC_TOTAL_WRITE_REQ_8B_FROM_PIPELINE 0x00000003 ++#define VIVS_MC_PROFILE_CONFIG2_MC_RESET 0x0000000f ++#define VIVS_MC_PROFILE_CONFIG2_HI__MASK 0x00000f00 ++#define VIVS_MC_PROFILE_CONFIG2_HI__SHIFT 8 ++#define VIVS_MC_PROFILE_CONFIG2_HI_AXI_CYCLES_READ_REQUEST_STALLED 0x00000000 ++#define VIVS_MC_PROFILE_CONFIG2_HI_AXI_CYCLES_WRITE_REQUEST_STALLED 0x00000100 ++#define VIVS_MC_PROFILE_CONFIG2_HI_AXI_CYCLES_WRITE_DATA_STALLED 0x00000200 ++#define VIVS_MC_PROFILE_CONFIG2_HI_RESET 0x00000f00 ++ ++#define VIVS_MC_PROFILE_CONFIG3 0x0000047c ++ ++#define VIVS_MC_BUS_CONFIG 0x00000480 ++#define VIVS_MC_BUS_CONFIG_FE_BUS_CONFIG__MASK 0x0000000f ++#define VIVS_MC_BUS_CONFIG_FE_BUS_CONFIG__SHIFT 0 ++#define VIVS_MC_BUS_CONFIG_FE_BUS_CONFIG(x) (((x) << VIVS_MC_BUS_CONFIG_FE_BUS_CONFIG__SHIFT) & VIVS_MC_BUS_CONFIG_FE_BUS_CONFIG__MASK) ++#define VIVS_MC_BUS_CONFIG_TX_BUS_CONFIG__MASK 0x000000f0 ++#define VIVS_MC_BUS_CONFIG_TX_BUS_CONFIG__SHIFT 4 ++#define VIVS_MC_BUS_CONFIG_TX_BUS_CONFIG(x) (((x) << VIVS_MC_BUS_CONFIG_TX_BUS_CONFIG__SHIFT) & VIVS_MC_BUS_CONFIG_TX_BUS_CONFIG__MASK) ++ ++#define VIVS_MC_START_COMPOSITION 0x00000554 ++ ++#define VIVS_MC_128B_MERGE 0x00000558 ++ ++ ++#endif /* STATE_HI_XML */ +diff --git a/include/uapi/drm/etnaviv_drm.h b/include/uapi/drm/etnaviv_drm.h +new file mode 100644 +index 0000000..4cc989a +--- /dev/null ++++ b/include/uapi/drm/etnaviv_drm.h +@@ -0,0 +1,222 @@ ++/* ++ * Copyright (C) 2015 Etnaviv Project ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ */ ++ ++#ifndef __ETNAVIV_DRM_H__ ++#define __ETNAVIV_DRM_H__ ++ ++#include "drm.h" ++ ++/* Please note that modifications to all structs defined here are ++ * subject to backwards-compatibility constraints: ++ * 1) Do not use pointers, use __u64 instead for 32 bit / 64 bit ++ * user/kernel compatibility ++ * 2) Keep fields aligned to their size ++ * 3) Because of how drm_ioctl() works, we can add new fields at ++ * the end of an ioctl if some care is taken: drm_ioctl() will ++ * zero out the new fields at the tail of the ioctl, so a zero ++ * value should have a backwards compatible meaning. And for ++ * output params, userspace won't see the newly added output ++ * fields.. so that has to be somehow ok. ++ */ ++ ++/* timeouts are specified in clock-monotonic absolute times (to simplify ++ * restarting interrupted ioctls). The following struct is logically the ++ * same as 'struct timespec' but 32/64b ABI safe. ++ */ ++struct drm_etnaviv_timespec { ++ __s64 tv_sec; /* seconds */ ++ __s64 tv_nsec; /* nanoseconds */ ++}; ++ ++#define ETNAVIV_PARAM_GPU_MODEL 0x01 ++#define ETNAVIV_PARAM_GPU_REVISION 0x02 ++#define ETNAVIV_PARAM_GPU_FEATURES_0 0x03 ++#define ETNAVIV_PARAM_GPU_FEATURES_1 0x04 ++#define ETNAVIV_PARAM_GPU_FEATURES_2 0x05 ++#define ETNAVIV_PARAM_GPU_FEATURES_3 0x06 ++#define ETNAVIV_PARAM_GPU_FEATURES_4 0x07 ++ ++#define ETNAVIV_PARAM_GPU_STREAM_COUNT 0x10 ++#define ETNAVIV_PARAM_GPU_REGISTER_MAX 0x11 ++#define ETNAVIV_PARAM_GPU_THREAD_COUNT 0x12 ++#define ETNAVIV_PARAM_GPU_VERTEX_CACHE_SIZE 0x13 ++#define ETNAVIV_PARAM_GPU_SHADER_CORE_COUNT 0x14 ++#define ETNAVIV_PARAM_GPU_PIXEL_PIPES 0x15 ++#define ETNAVIV_PARAM_GPU_VERTEX_OUTPUT_BUFFER_SIZE 0x16 ++#define ETNAVIV_PARAM_GPU_BUFFER_SIZE 0x17 ++#define ETNAVIV_PARAM_GPU_INSTRUCTION_COUNT 0x18 ++#define ETNAVIV_PARAM_GPU_NUM_CONSTANTS 0x19 ++ ++#define ETNA_MAX_PIPES 4 ++ ++struct drm_etnaviv_param { ++ __u32 pipe; /* in */ ++ __u32 param; /* in, ETNAVIV_PARAM_x */ ++ __u64 value; /* out (get_param) or in (set_param) */ ++}; ++ ++/* ++ * GEM buffers: ++ */ ++ ++#define ETNA_BO_CACHE_MASK 0x000f0000 ++/* cache modes */ ++#define ETNA_BO_CACHED 0x00010000 ++#define ETNA_BO_WC 0x00020000 ++#define ETNA_BO_UNCACHED 0x00040000 ++/* map flags */ ++#define ETNA_BO_FORCE_MMU 0x00100000 ++ ++struct drm_etnaviv_gem_new { ++ __u64 size; /* in */ ++ __u32 flags; /* in, mask of ETNA_BO_x */ ++ __u32 handle; /* out */ ++}; ++ ++struct drm_etnaviv_gem_info { ++ __u32 handle; /* in */ ++ __u32 pad; ++ __u64 offset; /* out, offset to pass to mmap() */ ++}; ++ ++#define ETNA_PREP_READ 0x01 ++#define ETNA_PREP_WRITE 0x02 ++#define ETNA_PREP_NOSYNC 0x04 ++ ++struct drm_etnaviv_gem_cpu_prep { ++ __u32 handle; /* in */ ++ __u32 op; /* in, mask of ETNA_PREP_x */ ++ struct drm_etnaviv_timespec timeout; /* in */ ++}; ++ ++struct drm_etnaviv_gem_cpu_fini { ++ __u32 handle; /* in */ ++ __u32 flags; /* in, placeholder for now, no defined values */ ++}; ++ ++/* ++ * Cmdstream Submission: ++ */ ++ ++/* The value written into the cmdstream is logically: ++ * relocbuf->gpuaddr + reloc_offset ++ * ++ * NOTE that reloc's must be sorted by order of increasing submit_offset, ++ * otherwise EINVAL. ++ */ ++struct drm_etnaviv_gem_submit_reloc { ++ __u32 submit_offset; /* in, offset from submit_bo */ ++ __u32 reloc_idx; /* in, index of reloc_bo buffer */ ++ __u64 reloc_offset; /* in, offset from start of reloc_bo */ ++ __u32 flags; /* in, placeholder for now, no defined values */ ++}; ++ ++/* Each buffer referenced elsewhere in the cmdstream submit (ie. the ++ * cmdstream buffer(s) themselves or reloc entries) has one (and only ++ * one) entry in the submit->bos[] table. ++ * ++ * As a optimization, the current buffer (gpu virtual address) can be ++ * passed back through the 'presumed' field. If on a subsequent reloc, ++ * userspace passes back a 'presumed' address that is still valid, ++ * then patching the cmdstream for this entry is skipped. This can ++ * avoid kernel needing to map/access the cmdstream bo in the common ++ * case. ++ */ ++#define ETNA_SUBMIT_BO_READ 0x0001 ++#define ETNA_SUBMIT_BO_WRITE 0x0002 ++struct drm_etnaviv_gem_submit_bo { ++ __u32 flags; /* in, mask of ETNA_SUBMIT_BO_x */ ++ __u32 handle; /* in, GEM handle */ ++ __u64 presumed; /* in/out, presumed buffer address */ ++}; ++ ++/* Each cmdstream submit consists of a table of buffers involved, and ++ * one or more cmdstream buffers. This allows for conditional execution ++ * (context-restore), and IB buffers needed for per tile/bin draw cmds. ++ */ ++#define ETNA_PIPE_3D 0x00 ++#define ETNA_PIPE_2D 0x01 ++#define ETNA_PIPE_VG 0x02 ++struct drm_etnaviv_gem_submit { ++ __u32 fence; /* out */ ++ __u32 pipe; /* in */ ++ __u32 exec_state; /* in, initial execution state (ETNA_PIPE_x) */ ++ __u32 nr_bos; /* in, number of submit_bo's */ ++ __u32 nr_relocs; /* in, number of submit_reloc's */ ++ __u32 stream_size; /* in, cmdstream size */ ++ __u64 bos; /* in, ptr to array of submit_bo's */ ++ __u64 relocs; /* in, ptr to array of submit_reloc's */ ++ __u64 stream; /* in, ptr to cmdstream */ ++}; ++ ++/* The normal way to synchronize with the GPU is just to CPU_PREP on ++ * a buffer if you need to access it from the CPU (other cmdstream ++ * submission from same or other contexts, PAGE_FLIP ioctl, etc, all ++ * handle the required synchronization under the hood). This ioctl ++ * mainly just exists as a way to implement the gallium pipe_fence ++ * APIs without requiring a dummy bo to synchronize on. ++ */ ++#define ETNA_WAIT_NONBLOCK 0x01 ++struct drm_etnaviv_wait_fence { ++ __u32 pipe; /* in */ ++ __u32 fence; /* in */ ++ __u32 flags; /* in, mask of ETNA_WAIT_x */ ++ __u32 pad; ++ struct drm_etnaviv_timespec timeout; /* in */ ++}; ++ ++#define ETNA_USERPTR_READ 0x01 ++#define ETNA_USERPTR_WRITE 0x02 ++struct drm_etnaviv_gem_userptr { ++ __u64 user_ptr; /* in, page aligned user pointer */ ++ __u64 user_size; /* in, page aligned user size */ ++ __u32 flags; /* in, flags */ ++ __u32 handle; /* out, non-zero handle */ ++}; ++ ++struct drm_etnaviv_gem_wait { ++ __u32 pipe; /* in */ ++ __u32 handle; /* in, bo to be waited for */ ++ __u32 flags; /* in, mask of ETNA_WAIT_x */ ++ __u32 pad; ++ struct drm_etnaviv_timespec timeout; /* in */ ++}; ++ ++#define DRM_ETNAVIV_GET_PARAM 0x00 ++/* placeholder: ++#define DRM_ETNAVIV_SET_PARAM 0x01 ++ */ ++#define DRM_ETNAVIV_GEM_NEW 0x02 ++#define DRM_ETNAVIV_GEM_INFO 0x03 ++#define DRM_ETNAVIV_GEM_CPU_PREP 0x04 ++#define DRM_ETNAVIV_GEM_CPU_FINI 0x05 ++#define DRM_ETNAVIV_GEM_SUBMIT 0x06 ++#define DRM_ETNAVIV_WAIT_FENCE 0x07 ++#define DRM_ETNAVIV_GEM_USERPTR 0x08 ++#define DRM_ETNAVIV_GEM_WAIT 0x09 ++#define DRM_ETNAVIV_NUM_IOCTLS 0x0a ++ ++#define DRM_IOCTL_ETNAVIV_GET_PARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GET_PARAM, struct drm_etnaviv_param) ++#define DRM_IOCTL_ETNAVIV_GEM_NEW DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_NEW, struct drm_etnaviv_gem_new) ++#define DRM_IOCTL_ETNAVIV_GEM_INFO DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_INFO, struct drm_etnaviv_gem_info) ++#define DRM_IOCTL_ETNAVIV_GEM_CPU_PREP DRM_IOW(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_CPU_PREP, struct drm_etnaviv_gem_cpu_prep) ++#define DRM_IOCTL_ETNAVIV_GEM_CPU_FINI DRM_IOW(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_CPU_FINI, struct drm_etnaviv_gem_cpu_fini) ++#define DRM_IOCTL_ETNAVIV_GEM_SUBMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_SUBMIT, struct drm_etnaviv_gem_submit) ++#define DRM_IOCTL_ETNAVIV_WAIT_FENCE DRM_IOW(DRM_COMMAND_BASE + DRM_ETNAVIV_WAIT_FENCE, struct drm_etnaviv_wait_fence) ++#define DRM_IOCTL_ETNAVIV_GEM_USERPTR DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_USERPTR, struct drm_etnaviv_gem_userptr) ++#define DRM_IOCTL_ETNAVIV_GEM_WAIT DRM_IOW(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_WAIT, struct drm_etnaviv_gem_wait) ++ ++#endif /* __ETNAVIV_DRM_H__ */ +-- +2.6.4 + diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-0004-MAINTAINERS-add-maintainer-and-reviewers-for-the-etn.patch b/kernel/kernel/files/patches/mageia/gpu-drm-0004-MAINTAINERS-add-maintainer-and-reviewers-for-the-etn.patch new file mode 100644 index 00000000..b16230df --- /dev/null +++ b/kernel/kernel/files/patches/mageia/gpu-drm-0004-MAINTAINERS-add-maintainer-and-reviewers-for-the-etn.patch @@ -0,0 +1,36 @@ +From 8bb0bce92ec9330b0ea931df90f719fb5c4a5224 Mon Sep 17 00:00:00 2001 +From: Lucas Stach +Date: Thu, 3 Dec 2015 17:12:07 +0100 +Subject: [PATCH 4/4] MAINTAINERS: add maintainer and reviewers for the etnaviv + DRM driver + +Signed-off-by: Lucas Stach +Acked-by: Russell King +Acked-by: Christian Gmeiner +--- + MAINTAINERS | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/MAINTAINERS b/MAINTAINERS +index e9caa4b..9990a3b 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -3741,6 +3741,15 @@ S: Maintained + F: drivers/gpu/drm/sti + F: Documentation/devicetree/bindings/display/st,stih4xx.txt + ++DRM DRIVERS FOR VIVANTE GPU IP ++M: Lucas Stach ++R: Russell King ++R: Christian Gmeiner ++L: dri-devel@lists.freedesktop.org ++S: Maintained ++F: drivers/gpu/drm/etnaviv ++F: Documentation/devicetree/bindings/display/etnaviv ++ + DSBR100 USB FM RADIO DRIVER + M: Alexey Klimov + L: linux-media@vger.kernel.org +-- +2.6.4 + diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-0005-drm-etnaviv-unlock-on-error-in-etnaviv_gem_get_iova.patch b/kernel/kernel/files/patches/mageia/gpu-drm-0005-drm-etnaviv-unlock-on-error-in-etnaviv_gem_get_iova.patch new file mode 100644 index 00000000..de12f1fd --- /dev/null +++ b/kernel/kernel/files/patches/mageia/gpu-drm-0005-drm-etnaviv-unlock-on-error-in-etnaviv_gem_get_iova.patch @@ -0,0 +1,34 @@ +From ed94add00e290e675c36cef6767d7d1f51a02f28 Mon Sep 17 00:00:00 2001 +From: Dan Carpenter +Date: Mon, 4 Jan 2016 16:10:24 +0300 +Subject: [PATCH 1/2] drm/etnaviv: unlock on error in etnaviv_gem_get_iova() + +We have to drop a lock before returning -ENOMEM here. + +Fixes: a8c21a5451d8 ('drm/etnaviv: add initial etnaviv DRM driver') +Signed-off-by: Dan Carpenter +Acked-by: Russell King +--- + drivers/gpu/drm/etnaviv/etnaviv_gem.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c +index 8d6f859..9f77c3b 100644 +--- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c ++++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c +@@ -305,8 +305,10 @@ int etnaviv_gem_get_iova(struct etnaviv_gpu *gpu, + mapping = etnaviv_gem_get_vram_mapping(etnaviv_obj, NULL); + if (!mapping) { + mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); +- if (!mapping) +- return -ENOMEM; ++ if (!mapping) { ++ ret = -ENOMEM; ++ goto out; ++ } + + INIT_LIST_HEAD(&mapping->scan_node); + mapping->object = etnaviv_obj; +-- +2.7.0 + diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-0006-drm-etnaviv-fix-workaround-for-GC500.patch b/kernel/kernel/files/patches/mageia/gpu-drm-0006-drm-etnaviv-fix-workaround-for-GC500.patch new file mode 100644 index 00000000..ae189b54 --- /dev/null +++ b/kernel/kernel/files/patches/mageia/gpu-drm-0006-drm-etnaviv-fix-workaround-for-GC500.patch @@ -0,0 +1,38 @@ +From c33246d793b5bb9d8be7c67918136c310185c23d Mon Sep 17 00:00:00 2001 +From: Lucas Stach +Date: Wed, 6 Jan 2016 14:36:40 +0100 +Subject: [PATCH 2/2] drm/etnaviv: fix workaround for GC500 + +The hardware description macros define the mask and shifts the wrong +way around for the intended use, leading to the condition never being +true and the chip revision ending up with the wrong value. + +Reported-by: Dan Carpenter +Signed-off-by: Lucas Stach +Acked-by: Christian Gmeiner +--- + drivers/gpu/drm/etnaviv/etnaviv_gpu.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +index d39093d..056a72e 100644 +--- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c ++++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +@@ -251,9 +251,12 @@ static void etnaviv_hw_identify(struct etnaviv_gpu *gpu) + chipIdentity = gpu_read(gpu, VIVS_HI_CHIP_IDENTITY); + + /* Special case for older graphic cores. */ +- if (VIVS_HI_CHIP_IDENTITY_FAMILY(chipIdentity) == 0x01) { ++ if (((chipIdentity & VIVS_HI_CHIP_IDENTITY_FAMILY__MASK) ++ >> VIVS_HI_CHIP_IDENTITY_FAMILY__SHIFT) == 0x01) { + gpu->identity.model = 0x500; /* gc500 */ +- gpu->identity.revision = VIVS_HI_CHIP_IDENTITY_REVISION(chipIdentity); ++ gpu->identity.revision = ++ (chipIdentity & VIVS_HI_CHIP_IDENTITY_REVISION__MASK) ++ >> VIVS_HI_CHIP_IDENTITY_REVISION__SHIFT; + } else { + + gpu->identity.model = gpu_read(gpu, VIVS_HI_CHIP_MODEL); +-- +2.7.0 + diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-0101-drm-Create-a-driver-hook-for-allocating-GEM-object-s.patch b/kernel/kernel/files/patches/mageia/gpu-drm-0101-drm-Create-a-driver-hook-for-allocating-GEM-object-s.patch new file mode 100644 index 00000000..2ccda340 --- /dev/null +++ b/kernel/kernel/files/patches/mageia/gpu-drm-0101-drm-Create-a-driver-hook-for-allocating-GEM-object-s.patch @@ -0,0 +1,62 @@ +From 10028c5ab107d3765c7fc282b6c45324d1602155 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Mon, 30 Nov 2015 10:55:13 -0800 +Subject: [PATCH 1/9] drm: Create a driver hook for allocating GEM object + structs. + +The CMA helpers had no way for a driver to extend the struct with its +own fields. Since the CMA helpers are mostly "Allocate a +drm_gem_cma_object, then fill in a few fields", it's hard to write as +pure helpers without passing in a driver callback for the allocate +step. + +Signed-off-by: Eric Anholt +Reviewed-by: Daniel Vetter +--- + drivers/gpu/drm/drm_gem_cma_helper.c | 10 ++++++---- + include/drm/drmP.h | 7 +++++++ + 2 files changed, 13 insertions(+), 4 deletions(-) + +diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c +index e109b49..0f7b00b 100644 +--- a/drivers/gpu/drm/drm_gem_cma_helper.c ++++ b/drivers/gpu/drm/drm_gem_cma_helper.c +@@ -59,11 +59,13 @@ __drm_gem_cma_create(struct drm_device *drm, size_t size) + struct drm_gem_object *gem_obj; + int ret; + +- cma_obj = kzalloc(sizeof(*cma_obj), GFP_KERNEL); +- if (!cma_obj) ++ if (drm->driver->gem_create_object) ++ gem_obj = drm->driver->gem_create_object(drm, size); ++ else ++ gem_obj = kzalloc(sizeof(*cma_obj), GFP_KERNEL); ++ if (!gem_obj) + return ERR_PTR(-ENOMEM); +- +- gem_obj = &cma_obj->base; ++ cma_obj = container_of(gem_obj, struct drm_gem_cma_object, base); + + ret = drm_gem_object_init(drm, gem_obj, size); + if (ret) +diff --git a/include/drm/drmP.h b/include/drm/drmP.h +index 0b921ae..22ff162 100644 +--- a/include/drm/drmP.h ++++ b/include/drm/drmP.h +@@ -580,6 +580,13 @@ struct drm_driver { + int (*gem_open_object) (struct drm_gem_object *, struct drm_file *); + void (*gem_close_object) (struct drm_gem_object *, struct drm_file *); + ++ /** ++ * Hook for allocating the GEM object struct, for use by core ++ * helpers. ++ */ ++ struct drm_gem_object *(*gem_create_object)(struct drm_device *dev, ++ size_t size); ++ + /* prime: */ + /* export handle -> fd (see drm_gem_prime_handle_to_fd() helper) */ + int (*prime_handle_to_fd)(struct drm_device *dev, struct drm_file *file_priv, +-- +2.6.4 + diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-0102-drm-vc4-Add-a-BO-cache.patch b/kernel/kernel/files/patches/mageia/gpu-drm-0102-drm-vc4-Add-a-BO-cache.patch new file mode 100644 index 00000000..8b8ecc8d --- /dev/null +++ b/kernel/kernel/files/patches/mageia/gpu-drm-0102-drm-vc4-Add-a-BO-cache.patch @@ -0,0 +1,512 @@ +From c826a6e1064419f78855463cf29ce9e8b9d25bf4 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Fri, 9 Oct 2015 20:25:07 -0700 +Subject: [PATCH 2/9] drm/vc4: Add a BO cache. + +We need to allocate new BOs in the kernel as part of each frame, but +the CMA allocator is way too slow for that. As an optimization, keep +track of recently-freed BOs and reuse them, with a 1 second timeout to +fully free them back to the system. + +This improves 3D performance by about 15%. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_bo.c | 336 +++++++++++++++++++++++++++++++++++++- + drivers/gpu/drm/vc4/vc4_debugfs.c | 1 + + drivers/gpu/drm/vc4/vc4_drv.c | 6 +- + drivers/gpu/drm/vc4/vc4_drv.h | 49 +++++- + 4 files changed, 384 insertions(+), 8 deletions(-) + +diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c +index ab9f510..18faa5b 100644 +--- a/drivers/gpu/drm/vc4/vc4_bo.c ++++ b/drivers/gpu/drm/vc4/vc4_bo.c +@@ -12,19 +12,229 @@ + * access to system memory with no MMU in between. To support it, we + * use the GEM CMA helper functions to allocate contiguous ranges of + * physical memory for our BOs. ++ * ++ * Since the CMA allocator is very slow, we keep a cache of recently ++ * freed BOs around so that the kernel's allocation of objects for 3D ++ * rendering can return quickly. + */ + + #include "vc4_drv.h" + +-struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size) ++static void vc4_bo_stats_dump(struct vc4_dev *vc4) ++{ ++ DRM_INFO("num bos allocated: %d\n", ++ vc4->bo_stats.num_allocated); ++ DRM_INFO("size bos allocated: %dkb\n", ++ vc4->bo_stats.size_allocated / 1024); ++ DRM_INFO("num bos used: %d\n", ++ vc4->bo_stats.num_allocated - vc4->bo_stats.num_cached); ++ DRM_INFO("size bos used: %dkb\n", ++ (vc4->bo_stats.size_allocated - ++ vc4->bo_stats.size_cached) / 1024); ++ DRM_INFO("num bos cached: %d\n", ++ vc4->bo_stats.num_cached); ++ DRM_INFO("size bos cached: %dkb\n", ++ vc4->bo_stats.size_cached / 1024); ++} ++ ++#ifdef CONFIG_DEBUG_FS ++int vc4_bo_stats_debugfs(struct seq_file *m, void *unused) ++{ ++ struct drm_info_node *node = (struct drm_info_node *)m->private; ++ struct drm_device *dev = node->minor->dev; ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ struct vc4_bo_stats stats; ++ ++ /* Take a snapshot of the current stats with the lock held. */ ++ mutex_lock(&vc4->bo_lock); ++ stats = vc4->bo_stats; ++ mutex_unlock(&vc4->bo_lock); ++ ++ seq_printf(m, "num bos allocated: %d\n", ++ stats.num_allocated); ++ seq_printf(m, "size bos allocated: %dkb\n", ++ stats.size_allocated / 1024); ++ seq_printf(m, "num bos used: %d\n", ++ stats.num_allocated - stats.num_cached); ++ seq_printf(m, "size bos used: %dkb\n", ++ (stats.size_allocated - stats.size_cached) / 1024); ++ seq_printf(m, "num bos cached: %d\n", ++ stats.num_cached); ++ seq_printf(m, "size bos cached: %dkb\n", ++ stats.size_cached / 1024); ++ ++ return 0; ++} ++#endif ++ ++static uint32_t bo_page_index(size_t size) ++{ ++ return (size / PAGE_SIZE) - 1; ++} ++ ++/* Must be called with bo_lock held. */ ++static void vc4_bo_destroy(struct vc4_bo *bo) + { ++ struct drm_gem_object *obj = &bo->base.base; ++ struct vc4_dev *vc4 = to_vc4_dev(obj->dev); ++ ++ vc4->bo_stats.num_allocated--; ++ vc4->bo_stats.size_allocated -= obj->size; ++ drm_gem_cma_free_object(obj); ++} ++ ++/* Must be called with bo_lock held. */ ++static void vc4_bo_remove_from_cache(struct vc4_bo *bo) ++{ ++ struct drm_gem_object *obj = &bo->base.base; ++ struct vc4_dev *vc4 = to_vc4_dev(obj->dev); ++ ++ vc4->bo_stats.num_cached--; ++ vc4->bo_stats.size_cached -= obj->size; ++ ++ list_del(&bo->unref_head); ++ list_del(&bo->size_head); ++} ++ ++static struct list_head *vc4_get_cache_list_for_size(struct drm_device *dev, ++ size_t size) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ uint32_t page_index = bo_page_index(size); ++ ++ if (vc4->bo_cache.size_list_size <= page_index) { ++ uint32_t new_size = max(vc4->bo_cache.size_list_size * 2, ++ page_index + 1); ++ struct list_head *new_list; ++ uint32_t i; ++ ++ new_list = kmalloc_array(new_size, sizeof(struct list_head), ++ GFP_KERNEL); ++ if (!new_list) ++ return NULL; ++ ++ /* Rebase the old cached BO lists to their new list ++ * head locations. ++ */ ++ for (i = 0; i < vc4->bo_cache.size_list_size; i++) { ++ struct list_head *old_list = ++ &vc4->bo_cache.size_list[i]; ++ ++ if (list_empty(old_list)) ++ INIT_LIST_HEAD(&new_list[i]); ++ else ++ list_replace(old_list, &new_list[i]); ++ } ++ /* And initialize the brand new BO list heads. */ ++ for (i = vc4->bo_cache.size_list_size; i < new_size; i++) ++ INIT_LIST_HEAD(&new_list[i]); ++ ++ kfree(vc4->bo_cache.size_list); ++ vc4->bo_cache.size_list = new_list; ++ vc4->bo_cache.size_list_size = new_size; ++ } ++ ++ return &vc4->bo_cache.size_list[page_index]; ++} ++ ++void vc4_bo_cache_purge(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ mutex_lock(&vc4->bo_lock); ++ while (!list_empty(&vc4->bo_cache.time_list)) { ++ struct vc4_bo *bo = list_last_entry(&vc4->bo_cache.time_list, ++ struct vc4_bo, unref_head); ++ vc4_bo_remove_from_cache(bo); ++ vc4_bo_destroy(bo); ++ } ++ mutex_unlock(&vc4->bo_lock); ++} ++ ++static struct vc4_bo *vc4_bo_get_from_cache(struct drm_device *dev, ++ uint32_t size) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ uint32_t page_index = bo_page_index(size); ++ struct vc4_bo *bo = NULL; ++ ++ size = roundup(size, PAGE_SIZE); ++ ++ mutex_lock(&vc4->bo_lock); ++ if (page_index >= vc4->bo_cache.size_list_size) ++ goto out; ++ ++ if (list_empty(&vc4->bo_cache.size_list[page_index])) ++ goto out; ++ ++ bo = list_first_entry(&vc4->bo_cache.size_list[page_index], ++ struct vc4_bo, size_head); ++ vc4_bo_remove_from_cache(bo); ++ kref_init(&bo->base.base.refcount); ++ ++out: ++ mutex_unlock(&vc4->bo_lock); ++ return bo; ++} ++ ++/** ++ * vc4_gem_create_object - Implementation of driver->gem_create_object. ++ * ++ * This lets the CMA helpers allocate object structs for us, and keep ++ * our BO stats correct. ++ */ ++struct drm_gem_object *vc4_create_object(struct drm_device *dev, size_t size) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ struct vc4_bo *bo; ++ ++ bo = kzalloc(sizeof(*bo), GFP_KERNEL); ++ if (!bo) ++ return ERR_PTR(-ENOMEM); ++ ++ mutex_lock(&vc4->bo_lock); ++ vc4->bo_stats.num_allocated++; ++ vc4->bo_stats.size_allocated += size; ++ mutex_unlock(&vc4->bo_lock); ++ ++ return &bo->base.base; ++} ++ ++struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t unaligned_size, ++ bool from_cache) ++{ ++ size_t size = roundup(unaligned_size, PAGE_SIZE); ++ struct vc4_dev *vc4 = to_vc4_dev(dev); + struct drm_gem_cma_object *cma_obj; + +- cma_obj = drm_gem_cma_create(dev, size); +- if (IS_ERR(cma_obj)) ++ if (size == 0) + return NULL; +- else +- return to_vc4_bo(&cma_obj->base); ++ ++ /* First, try to get a vc4_bo from the kernel BO cache. */ ++ if (from_cache) { ++ struct vc4_bo *bo = vc4_bo_get_from_cache(dev, size); ++ ++ if (bo) ++ return bo; ++ } ++ ++ cma_obj = drm_gem_cma_create(dev, size); ++ if (IS_ERR(cma_obj)) { ++ /* ++ * If we've run out of CMA memory, kill the cache of ++ * CMA allocations we've got laying around and try again. ++ */ ++ vc4_bo_cache_purge(dev); ++ ++ cma_obj = drm_gem_cma_create(dev, size); ++ if (IS_ERR(cma_obj)) { ++ DRM_ERROR("Failed to allocate from CMA:\n"); ++ vc4_bo_stats_dump(vc4); ++ return NULL; ++ } ++ } ++ ++ return to_vc4_bo(&cma_obj->base); + } + + int vc4_dumb_create(struct drm_file *file_priv, +@@ -41,7 +251,7 @@ int vc4_dumb_create(struct drm_file *file_priv, + if (args->size < args->pitch * args->height) + args->size = args->pitch * args->height; + +- bo = vc4_bo_create(dev, roundup(args->size, PAGE_SIZE)); ++ bo = vc4_bo_create(dev, args->size, false); + if (!bo) + return -ENOMEM; + +@@ -50,3 +260,117 @@ int vc4_dumb_create(struct drm_file *file_priv, + + return ret; + } ++ ++/* Must be called with bo_lock held. */ ++static void vc4_bo_cache_free_old(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ unsigned long expire_time = jiffies - msecs_to_jiffies(1000); ++ ++ while (!list_empty(&vc4->bo_cache.time_list)) { ++ struct vc4_bo *bo = list_last_entry(&vc4->bo_cache.time_list, ++ struct vc4_bo, unref_head); ++ if (time_before(expire_time, bo->free_time)) { ++ mod_timer(&vc4->bo_cache.time_timer, ++ round_jiffies_up(jiffies + ++ msecs_to_jiffies(1000))); ++ return; ++ } ++ ++ vc4_bo_remove_from_cache(bo); ++ vc4_bo_destroy(bo); ++ } ++} ++ ++/* Called on the last userspace/kernel unreference of the BO. Returns ++ * it to the BO cache if possible, otherwise frees it. ++ * ++ * Note that this is called with the struct_mutex held. ++ */ ++void vc4_free_object(struct drm_gem_object *gem_bo) ++{ ++ struct drm_device *dev = gem_bo->dev; ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ struct vc4_bo *bo = to_vc4_bo(gem_bo); ++ struct list_head *cache_list; ++ ++ mutex_lock(&vc4->bo_lock); ++ /* If the object references someone else's memory, we can't cache it. ++ */ ++ if (gem_bo->import_attach) { ++ vc4_bo_destroy(bo); ++ goto out; ++ } ++ ++ /* Don't cache if it was publicly named. */ ++ if (gem_bo->name) { ++ vc4_bo_destroy(bo); ++ goto out; ++ } ++ ++ cache_list = vc4_get_cache_list_for_size(dev, gem_bo->size); ++ if (!cache_list) { ++ vc4_bo_destroy(bo); ++ goto out; ++ } ++ ++ bo->free_time = jiffies; ++ list_add(&bo->size_head, cache_list); ++ list_add(&bo->unref_head, &vc4->bo_cache.time_list); ++ ++ vc4->bo_stats.num_cached++; ++ vc4->bo_stats.size_cached += gem_bo->size; ++ ++ vc4_bo_cache_free_old(dev); ++ ++out: ++ mutex_unlock(&vc4->bo_lock); ++} ++ ++static void vc4_bo_cache_time_work(struct work_struct *work) ++{ ++ struct vc4_dev *vc4 = ++ container_of(work, struct vc4_dev, bo_cache.time_work); ++ struct drm_device *dev = vc4->dev; ++ ++ mutex_lock(&vc4->bo_lock); ++ vc4_bo_cache_free_old(dev); ++ mutex_unlock(&vc4->bo_lock); ++} ++ ++static void vc4_bo_cache_time_timer(unsigned long data) ++{ ++ struct drm_device *dev = (struct drm_device *)data; ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ schedule_work(&vc4->bo_cache.time_work); ++} ++ ++void vc4_bo_cache_init(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ mutex_init(&vc4->bo_lock); ++ ++ INIT_LIST_HEAD(&vc4->bo_cache.time_list); ++ ++ INIT_WORK(&vc4->bo_cache.time_work, vc4_bo_cache_time_work); ++ setup_timer(&vc4->bo_cache.time_timer, ++ vc4_bo_cache_time_timer, ++ (unsigned long)dev); ++} ++ ++void vc4_bo_cache_destroy(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ del_timer(&vc4->bo_cache.time_timer); ++ cancel_work_sync(&vc4->bo_cache.time_work); ++ ++ vc4_bo_cache_purge(dev); ++ ++ if (vc4->bo_stats.num_allocated) { ++ DRM_ERROR("Destroying BO cache while BOs still allocated:\n"); ++ vc4_bo_stats_dump(vc4); ++ } ++} +diff --git a/drivers/gpu/drm/vc4/vc4_debugfs.c b/drivers/gpu/drm/vc4/vc4_debugfs.c +index 4297b0a5..6bcf96e 100644 +--- a/drivers/gpu/drm/vc4/vc4_debugfs.c ++++ b/drivers/gpu/drm/vc4/vc4_debugfs.c +@@ -16,6 +16,7 @@ + #include "vc4_regs.h" + + static const struct drm_info_list vc4_debugfs_list[] = { ++ {"bo_stats", vc4_bo_stats_debugfs, 0}, + {"hdmi_regs", vc4_hdmi_debugfs_regs, 0}, + {"hvs_regs", vc4_hvs_debugfs_regs, 0}, + {"crtc0_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)0}, +diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c +index 6e73060..da041fa 100644 +--- a/drivers/gpu/drm/vc4/vc4_drv.c ++++ b/drivers/gpu/drm/vc4/vc4_drv.c +@@ -92,7 +92,8 @@ static struct drm_driver vc4_drm_driver = { + .debugfs_cleanup = vc4_debugfs_cleanup, + #endif + +- .gem_free_object = drm_gem_cma_free_object, ++ .gem_create_object = vc4_create_object, ++ .gem_free_object = vc4_free_object, + .gem_vm_ops = &drm_gem_cma_vm_ops, + + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, +@@ -170,6 +171,8 @@ static int vc4_drm_bind(struct device *dev) + + drm_dev_set_unique(drm, dev_name(dev)); + ++ vc4_bo_cache_init(drm); ++ + drm_mode_config_init(drm); + if (ret) + goto unref; +@@ -202,6 +205,7 @@ unbind_all: + component_unbind_all(dev, drm); + unref: + drm_dev_unref(drm); ++ vc4_bo_cache_destroy(drm); + return ret; + } + +diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h +index fd8319f..39a1ff5 100644 +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -17,6 +17,37 @@ struct vc4_dev { + struct vc4_crtc *crtc[3]; + + struct drm_fbdev_cma *fbdev; ++ ++ /* The kernel-space BO cache. Tracks buffers that have been ++ * unreferenced by all other users (refcounts of 0!) but not ++ * yet freed, so we can do cheap allocations. ++ */ ++ struct vc4_bo_cache { ++ /* Array of list heads for entries in the BO cache, ++ * based on number of pages, so we can do O(1) lookups ++ * in the cache when allocating. ++ */ ++ struct list_head *size_list; ++ uint32_t size_list_size; ++ ++ /* List of all BOs in the cache, ordered by age, so we ++ * can do O(1) lookups when trying to free old ++ * buffers. ++ */ ++ struct list_head time_list; ++ struct work_struct time_work; ++ struct timer_list time_timer; ++ } bo_cache; ++ ++ struct vc4_bo_stats { ++ u32 num_allocated; ++ u32 size_allocated; ++ u32 num_cached; ++ u32 size_cached; ++ } bo_stats; ++ ++ /* Protects bo_cache and the BO stats. */ ++ struct mutex bo_lock; + }; + + static inline struct vc4_dev * +@@ -27,6 +58,17 @@ to_vc4_dev(struct drm_device *dev) + + struct vc4_bo { + struct drm_gem_cma_object base; ++ ++ /* List entry for the BO's position in either ++ * vc4_exec_info->unref_list or vc4_dev->bo_cache.time_list ++ */ ++ struct list_head unref_head; ++ ++ /* Time in jiffies when the BO was put in vc4->bo_cache. */ ++ unsigned long free_time; ++ ++ /* List entry for the BO's position in vc4_dev->bo_cache.size_list */ ++ struct list_head size_head; + }; + + static inline struct vc4_bo * +@@ -104,13 +146,18 @@ to_vc4_encoder(struct drm_encoder *encoder) + #define wait_for(COND, MS) _wait_for(COND, MS, 1) + + /* vc4_bo.c */ ++struct drm_gem_object *vc4_create_object(struct drm_device *dev, size_t size); + void vc4_free_object(struct drm_gem_object *gem_obj); +-struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size); ++struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size, ++ bool from_cache); + int vc4_dumb_create(struct drm_file *file_priv, + struct drm_device *dev, + struct drm_mode_create_dumb *args); + struct dma_buf *vc4_prime_export(struct drm_device *dev, + struct drm_gem_object *obj, int flags); ++void vc4_bo_cache_init(struct drm_device *dev); ++void vc4_bo_cache_destroy(struct drm_device *dev); ++int vc4_bo_stats_debugfs(struct seq_file *m, void *arg); + + /* vc4_crtc.c */ + extern struct platform_driver vc4_crtc_driver; +-- +2.6.4 + diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-0103-drm-vc4-Add-create-and-map-BO-ioctls.patch b/kernel/kernel/files/patches/mageia/gpu-drm-0103-drm-vc4-Add-create-and-map-BO-ioctls.patch new file mode 100644 index 00000000..53df377e --- /dev/null +++ b/kernel/kernel/files/patches/mageia/gpu-drm-0103-drm-vc4-Add-create-and-map-BO-ioctls.patch @@ -0,0 +1,204 @@ +From d5bc60f6ad05b3c676b057bec662cfafc3ee24dd Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Sun, 18 Jan 2015 09:33:17 +1300 +Subject: [PATCH 3/9] drm/vc4: Add create and map BO ioctls. + +While there exist dumb APIs for creating and mapping BOs, one of the +rules is that drivers doing 3D acceleration have to provide their own +APIs for buffer allocation (besides, the pitch/height parameters of +the dumb alloc don't really make sense for a lot of 3D allocations). + +v2: Use __u32-style types, use "drm.h" instead of . + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_bo.c | 41 ++++++++++++++++++++++++++ + drivers/gpu/drm/vc4/vc4_drv.c | 3 ++ + drivers/gpu/drm/vc4/vc4_drv.h | 4 +++ + include/uapi/drm/Kbuild | 1 + + include/uapi/drm/vc4_drm.h | 68 +++++++++++++++++++++++++++++++++++++++++++ + 5 files changed, 117 insertions(+) + create mode 100644 include/uapi/drm/vc4_drm.h + +diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c +index 18faa5b..06cba26 100644 +--- a/drivers/gpu/drm/vc4/vc4_bo.c ++++ b/drivers/gpu/drm/vc4/vc4_bo.c +@@ -19,6 +19,7 @@ + */ + + #include "vc4_drv.h" ++#include "uapi/drm/vc4_drm.h" + + static void vc4_bo_stats_dump(struct vc4_dev *vc4) + { +@@ -346,6 +347,46 @@ static void vc4_bo_cache_time_timer(unsigned long data) + schedule_work(&vc4->bo_cache.time_work); + } + ++int vc4_create_bo_ioctl(struct drm_device *dev, void *data, ++ struct drm_file *file_priv) ++{ ++ struct drm_vc4_create_bo *args = data; ++ struct vc4_bo *bo = NULL; ++ int ret; ++ ++ /* ++ * We can't allocate from the BO cache, because the BOs don't ++ * get zeroed, and that might leak data between users. ++ */ ++ bo = vc4_bo_create(dev, args->size, false); ++ if (!bo) ++ return -ENOMEM; ++ ++ ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); ++ drm_gem_object_unreference_unlocked(&bo->base.base); ++ ++ return ret; ++} ++ ++int vc4_mmap_bo_ioctl(struct drm_device *dev, void *data, ++ struct drm_file *file_priv) ++{ ++ struct drm_vc4_mmap_bo *args = data; ++ struct drm_gem_object *gem_obj; ++ ++ gem_obj = drm_gem_object_lookup(dev, file_priv, args->handle); ++ if (!gem_obj) { ++ DRM_ERROR("Failed to look up GEM BO %d\n", args->handle); ++ return -EINVAL; ++ } ++ ++ /* The mmap offset was set up at BO allocation time. */ ++ args->offset = drm_vma_node_offset_addr(&gem_obj->vma_node); ++ ++ drm_gem_object_unreference_unlocked(gem_obj); ++ return 0; ++} ++ + void vc4_bo_cache_init(struct drm_device *dev) + { + struct vc4_dev *vc4 = to_vc4_dev(dev); +diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c +index da041fa..5fa4688 100644 +--- a/drivers/gpu/drm/vc4/vc4_drv.c ++++ b/drivers/gpu/drm/vc4/vc4_drv.c +@@ -16,6 +16,7 @@ + #include + #include "drm_fb_cma_helper.h" + ++#include "uapi/drm/vc4_drm.h" + #include "vc4_drv.h" + #include "vc4_regs.h" + +@@ -73,6 +74,8 @@ static const struct file_operations vc4_drm_fops = { + }; + + static const struct drm_ioctl_desc vc4_drm_ioctls[] = { ++ DRM_IOCTL_DEF_DRV(VC4_CREATE_BO, vc4_create_bo_ioctl, 0), ++ DRM_IOCTL_DEF_DRV(VC4_MMAP_BO, vc4_mmap_bo_ioctl, 0), + }; + + static struct drm_driver vc4_drm_driver = { +diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h +index 39a1ff5..fddb0a0 100644 +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -155,6 +155,10 @@ int vc4_dumb_create(struct drm_file *file_priv, + struct drm_mode_create_dumb *args); + struct dma_buf *vc4_prime_export(struct drm_device *dev, + struct drm_gem_object *obj, int flags); ++int vc4_create_bo_ioctl(struct drm_device *dev, void *data, ++ struct drm_file *file_priv); ++int vc4_mmap_bo_ioctl(struct drm_device *dev, void *data, ++ struct drm_file *file_priv); + void vc4_bo_cache_init(struct drm_device *dev); + void vc4_bo_cache_destroy(struct drm_device *dev); + int vc4_bo_stats_debugfs(struct seq_file *m, void *arg); +diff --git a/include/uapi/drm/Kbuild b/include/uapi/drm/Kbuild +index 38d4370..974fcd5 100644 +--- a/include/uapi/drm/Kbuild ++++ b/include/uapi/drm/Kbuild +@@ -17,4 +17,5 @@ header-y += tegra_drm.h + header-y += via_drm.h + header-y += vmwgfx_drm.h + header-y += msm_drm.h ++header-y += vc4_drm.h + header-y += virtgpu_drm.h +diff --git a/include/uapi/drm/vc4_drm.h b/include/uapi/drm/vc4_drm.h +new file mode 100644 +index 0000000..219d34c +--- /dev/null ++++ b/include/uapi/drm/vc4_drm.h +@@ -0,0 +1,68 @@ ++/* ++ * Copyright © 2014-2015 Broadcom ++ * ++ * 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 (including the next ++ * paragraph) 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 _UAPI_VC4_DRM_H_ ++#define _UAPI_VC4_DRM_H_ ++ ++#include "drm.h" ++ ++#define DRM_VC4_CREATE_BO 0x03 ++#define DRM_VC4_MMAP_BO 0x04 ++ ++#define DRM_IOCTL_VC4_CREATE_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_CREATE_BO, struct drm_vc4_create_bo) ++#define DRM_IOCTL_VC4_MMAP_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_MMAP_BO, struct drm_vc4_mmap_bo) ++ ++/** ++ * struct drm_vc4_create_bo - ioctl argument for creating VC4 BOs. ++ * ++ * There are currently no values for the flags argument, but it may be ++ * used in a future extension. ++ */ ++struct drm_vc4_create_bo { ++ __u32 size; ++ __u32 flags; ++ /** Returned GEM handle for the BO. */ ++ __u32 handle; ++ __u32 pad; ++}; ++ ++/** ++ * struct drm_vc4_mmap_bo - ioctl argument for mapping VC4 BOs. ++ * ++ * This doesn't actually perform an mmap. Instead, it returns the ++ * offset you need to use in an mmap on the DRM device node. This ++ * means that tools like valgrind end up knowing about the mapped ++ * memory. ++ * ++ * There are currently no values for the flags argument, but it may be ++ * used in a future extension. ++ */ ++struct drm_vc4_mmap_bo { ++ /** Handle for the object being mapped. */ ++ __u32 handle; ++ __u32 flags; ++ /** offset into the drm node to use for subsequent mmap call. */ ++ __u64 offset; ++}; ++ ++#endif /* _UAPI_VC4_DRM_H_ */ +-- +2.6.4 + diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-0104-drm-vc4-Add-an-API-for-creating-GPU-shaders-in-GEM-B.patch b/kernel/kernel/files/patches/mageia/gpu-drm-0104-drm-vc4-Add-an-API-for-creating-GPU-shaders-in-GEM-B.patch new file mode 100644 index 00000000..f06c0a10 --- /dev/null +++ b/kernel/kernel/files/patches/mageia/gpu-drm-0104-drm-vc4-Add-an-API-for-creating-GPU-shaders-in-GEM-B.patch @@ -0,0 +1,1168 @@ +From 463873d5701427f2964a0b4b72c45f1f14b6df87 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Mon, 30 Nov 2015 11:41:40 -0800 +Subject: [PATCH 4/9] drm/vc4: Add an API for creating GPU shaders in GEM BOs. + +Since we have no MMU, the kernel needs to validate that the submitted +shader code won't make any accesses to memory that the user doesn't +control, which involves banning some operations (general purpose DMA +writes), and tracking where we need to write out pointers for other +operations (texture sampling). Once it's validated, we return a GEM +BO containing the shader, which doesn't allow mapping for write or +exporting to other subsystems. + +v2: Use __u32-style types. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/Makefile | 3 +- + drivers/gpu/drm/vc4/vc4_bo.c | 140 ++++++++ + drivers/gpu/drm/vc4/vc4_drv.c | 9 +- + drivers/gpu/drm/vc4/vc4_drv.h | 50 +++ + drivers/gpu/drm/vc4/vc4_qpu_defines.h | 264 +++++++++++++++ + drivers/gpu/drm/vc4/vc4_validate_shaders.c | 513 +++++++++++++++++++++++++++++ + include/uapi/drm/vc4_drm.h | 25 ++ + 7 files changed, 999 insertions(+), 5 deletions(-) + create mode 100644 drivers/gpu/drm/vc4/vc4_qpu_defines.h + create mode 100644 drivers/gpu/drm/vc4/vc4_validate_shaders.c + +diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile +index 32b4f9c..eb776a6 100644 +--- a/drivers/gpu/drm/vc4/Makefile ++++ b/drivers/gpu/drm/vc4/Makefile +@@ -10,7 +10,8 @@ vc4-y := \ + vc4_kms.o \ + vc4_hdmi.o \ + vc4_hvs.o \ +- vc4_plane.o ++ vc4_plane.o \ ++ vc4_validate_shaders.o + + vc4-$(CONFIG_DEBUG_FS) += vc4_debugfs.o + +diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c +index 06cba26..18dfe3e 100644 +--- a/drivers/gpu/drm/vc4/vc4_bo.c ++++ b/drivers/gpu/drm/vc4/vc4_bo.c +@@ -79,6 +79,12 @@ static void vc4_bo_destroy(struct vc4_bo *bo) + struct drm_gem_object *obj = &bo->base.base; + struct vc4_dev *vc4 = to_vc4_dev(obj->dev); + ++ if (bo->validated_shader) { ++ kfree(bo->validated_shader->texture_samples); ++ kfree(bo->validated_shader); ++ bo->validated_shader = NULL; ++ } ++ + vc4->bo_stats.num_allocated--; + vc4->bo_stats.size_allocated -= obj->size; + drm_gem_cma_free_object(obj); +@@ -315,6 +321,12 @@ void vc4_free_object(struct drm_gem_object *gem_bo) + goto out; + } + ++ if (bo->validated_shader) { ++ kfree(bo->validated_shader->texture_samples); ++ kfree(bo->validated_shader); ++ bo->validated_shader = NULL; ++ } ++ + bo->free_time = jiffies; + list_add(&bo->size_head, cache_list); + list_add(&bo->unref_head, &vc4->bo_cache.time_list); +@@ -347,6 +359,78 @@ static void vc4_bo_cache_time_timer(unsigned long data) + schedule_work(&vc4->bo_cache.time_work); + } + ++struct dma_buf * ++vc4_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags) ++{ ++ struct vc4_bo *bo = to_vc4_bo(obj); ++ ++ if (bo->validated_shader) { ++ DRM_ERROR("Attempting to export shader BO\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ return drm_gem_prime_export(dev, obj, flags); ++} ++ ++int vc4_mmap(struct file *filp, struct vm_area_struct *vma) ++{ ++ struct drm_gem_object *gem_obj; ++ struct vc4_bo *bo; ++ int ret; ++ ++ ret = drm_gem_mmap(filp, vma); ++ if (ret) ++ return ret; ++ ++ gem_obj = vma->vm_private_data; ++ bo = to_vc4_bo(gem_obj); ++ ++ if (bo->validated_shader && (vma->vm_flags & VM_WRITE)) { ++ DRM_ERROR("mmaping of shader BOs for writing not allowed.\n"); ++ return -EINVAL; ++ } ++ ++ /* ++ * Clear the VM_PFNMAP flag that was set by drm_gem_mmap(), and set the ++ * vm_pgoff (used as a fake buffer offset by DRM) to 0 as we want to map ++ * the whole buffer. ++ */ ++ vma->vm_flags &= ~VM_PFNMAP; ++ vma->vm_pgoff = 0; ++ ++ ret = dma_mmap_writecombine(bo->base.base.dev->dev, vma, ++ bo->base.vaddr, bo->base.paddr, ++ vma->vm_end - vma->vm_start); ++ if (ret) ++ drm_gem_vm_close(vma); ++ ++ return ret; ++} ++ ++int vc4_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) ++{ ++ struct vc4_bo *bo = to_vc4_bo(obj); ++ ++ if (bo->validated_shader && (vma->vm_flags & VM_WRITE)) { ++ DRM_ERROR("mmaping of shader BOs for writing not allowed.\n"); ++ return -EINVAL; ++ } ++ ++ return drm_gem_cma_prime_mmap(obj, vma); ++} ++ ++void *vc4_prime_vmap(struct drm_gem_object *obj) ++{ ++ struct vc4_bo *bo = to_vc4_bo(obj); ++ ++ if (bo->validated_shader) { ++ DRM_ERROR("mmaping of shader BOs not allowed.\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ return drm_gem_cma_prime_vmap(obj); ++} ++ + int vc4_create_bo_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) + { +@@ -387,6 +471,62 @@ int vc4_mmap_bo_ioctl(struct drm_device *dev, void *data, + return 0; + } + ++int ++vc4_create_shader_bo_ioctl(struct drm_device *dev, void *data, ++ struct drm_file *file_priv) ++{ ++ struct drm_vc4_create_shader_bo *args = data; ++ struct vc4_bo *bo = NULL; ++ int ret; ++ ++ if (args->size == 0) ++ return -EINVAL; ++ ++ if (args->size % sizeof(u64) != 0) ++ return -EINVAL; ++ ++ if (args->flags != 0) { ++ DRM_INFO("Unknown flags set: 0x%08x\n", args->flags); ++ return -EINVAL; ++ } ++ ++ if (args->pad != 0) { ++ DRM_INFO("Pad set: 0x%08x\n", args->pad); ++ return -EINVAL; ++ } ++ ++ bo = vc4_bo_create(dev, args->size, true); ++ if (!bo) ++ return -ENOMEM; ++ ++ ret = copy_from_user(bo->base.vaddr, ++ (void __user *)(uintptr_t)args->data, ++ args->size); ++ if (ret != 0) ++ goto fail; ++ /* Clear the rest of the memory from allocating from the BO ++ * cache. ++ */ ++ memset(bo->base.vaddr + args->size, 0, ++ bo->base.base.size - args->size); ++ ++ bo->validated_shader = vc4_validate_shader(&bo->base); ++ if (!bo->validated_shader) { ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ /* We have to create the handle after validation, to avoid ++ * races for users to do doing things like mmap the shader BO. ++ */ ++ ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); ++ ++ fail: ++ drm_gem_object_unreference_unlocked(&bo->base.base); ++ ++ return ret; ++} ++ + void vc4_bo_cache_init(struct drm_device *dev) + { + struct vc4_dev *vc4 = to_vc4_dev(dev); +diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c +index 5fa4688..da4be9c 100644 +--- a/drivers/gpu/drm/vc4/vc4_drv.c ++++ b/drivers/gpu/drm/vc4/vc4_drv.c +@@ -64,7 +64,7 @@ static const struct file_operations vc4_drm_fops = { + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, +- .mmap = drm_gem_cma_mmap, ++ .mmap = vc4_mmap, + .poll = drm_poll, + .read = drm_read, + #ifdef CONFIG_COMPAT +@@ -76,6 +76,7 @@ static const struct file_operations vc4_drm_fops = { + static const struct drm_ioctl_desc vc4_drm_ioctls[] = { + DRM_IOCTL_DEF_DRV(VC4_CREATE_BO, vc4_create_bo_ioctl, 0), + DRM_IOCTL_DEF_DRV(VC4_MMAP_BO, vc4_mmap_bo_ioctl, 0), ++ DRM_IOCTL_DEF_DRV(VC4_CREATE_SHADER_BO, vc4_create_shader_bo_ioctl, 0), + }; + + static struct drm_driver vc4_drm_driver = { +@@ -102,12 +103,12 @@ static struct drm_driver vc4_drm_driver = { + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_import = drm_gem_prime_import, +- .gem_prime_export = drm_gem_prime_export, ++ .gem_prime_export = vc4_prime_export, + .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, + .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, +- .gem_prime_vmap = drm_gem_cma_prime_vmap, ++ .gem_prime_vmap = vc4_prime_vmap, + .gem_prime_vunmap = drm_gem_cma_prime_vunmap, +- .gem_prime_mmap = drm_gem_cma_prime_mmap, ++ .gem_prime_mmap = vc4_prime_mmap, + + .dumb_create = vc4_dumb_create, + .dumb_map_offset = drm_gem_cma_dumb_map_offset, +diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h +index fddb0a0..bd77d55 100644 +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -69,6 +69,11 @@ struct vc4_bo { + + /* List entry for the BO's position in vc4_dev->bo_cache.size_list */ + struct list_head size_head; ++ ++ /* Struct for shader validation state, if created by ++ * DRM_IOCTL_VC4_CREATE_SHADER_BO. ++ */ ++ struct vc4_validated_shader_info *validated_shader; + }; + + static inline struct vc4_bo * +@@ -118,6 +123,42 @@ to_vc4_encoder(struct drm_encoder *encoder) + #define HVS_WRITE(offset, val) writel(val, vc4->hvs->regs + offset) + + /** ++ * struct vc4_texture_sample_info - saves the offsets into the UBO for texture ++ * setup parameters. ++ * ++ * This will be used at draw time to relocate the reference to the texture ++ * contents in p0, and validate that the offset combined with ++ * width/height/stride/etc. from p1 and p2/p3 doesn't sample outside the BO. ++ * Note that the hardware treats unprovided config parameters as 0, so not all ++ * of them need to be set up for every texure sample, and we'll store ~0 as ++ * the offset to mark the unused ones. ++ * ++ * See the VC4 3D architecture guide page 41 ("Texture and Memory Lookup Unit ++ * Setup") for definitions of the texture parameters. ++ */ ++struct vc4_texture_sample_info { ++ bool is_direct; ++ uint32_t p_offset[4]; ++}; ++ ++/** ++ * struct vc4_validated_shader_info - information about validated shaders that ++ * needs to be used from command list validation. ++ * ++ * For a given shader, each time a shader state record references it, we need ++ * to verify that the shader doesn't read more uniforms than the shader state ++ * record's uniform BO pointer can provide, and we need to apply relocations ++ * and validate the shader state record's uniforms that define the texture ++ * samples. ++ */ ++struct vc4_validated_shader_info { ++ uint32_t uniforms_size; ++ uint32_t uniforms_src_size; ++ uint32_t num_texture_samples; ++ struct vc4_texture_sample_info *texture_samples; ++}; ++ ++/** + * _wait_for - magic (register) wait macro + * + * Does the right thing for modeset paths when run under kdgb or similar atomic +@@ -157,8 +198,13 @@ struct dma_buf *vc4_prime_export(struct drm_device *dev, + struct drm_gem_object *obj, int flags); + int vc4_create_bo_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); ++int vc4_create_shader_bo_ioctl(struct drm_device *dev, void *data, ++ struct drm_file *file_priv); + int vc4_mmap_bo_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); ++int vc4_mmap(struct file *filp, struct vm_area_struct *vma); ++int vc4_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma); ++void *vc4_prime_vmap(struct drm_gem_object *obj); + void vc4_bo_cache_init(struct drm_device *dev); + void vc4_bo_cache_destroy(struct drm_device *dev); + int vc4_bo_stats_debugfs(struct seq_file *m, void *arg); +@@ -194,3 +240,7 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev, + enum drm_plane_type type); + u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist); + u32 vc4_plane_dlist_size(struct drm_plane_state *state); ++ ++/* vc4_validate_shader.c */ ++struct vc4_validated_shader_info * ++vc4_validate_shader(struct drm_gem_cma_object *shader_obj); +diff --git a/drivers/gpu/drm/vc4/vc4_qpu_defines.h b/drivers/gpu/drm/vc4/vc4_qpu_defines.h +new file mode 100644 +index 0000000..d5c2f3c +--- /dev/null ++++ b/drivers/gpu/drm/vc4/vc4_qpu_defines.h +@@ -0,0 +1,264 @@ ++/* ++ * Copyright © 2014 Broadcom ++ * ++ * 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 (including the next ++ * paragraph) 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 VC4_QPU_DEFINES_H ++#define VC4_QPU_DEFINES_H ++ ++enum qpu_op_add { ++ QPU_A_NOP, ++ QPU_A_FADD, ++ QPU_A_FSUB, ++ QPU_A_FMIN, ++ QPU_A_FMAX, ++ QPU_A_FMINABS, ++ QPU_A_FMAXABS, ++ QPU_A_FTOI, ++ QPU_A_ITOF, ++ QPU_A_ADD = 12, ++ QPU_A_SUB, ++ QPU_A_SHR, ++ QPU_A_ASR, ++ QPU_A_ROR, ++ QPU_A_SHL, ++ QPU_A_MIN, ++ QPU_A_MAX, ++ QPU_A_AND, ++ QPU_A_OR, ++ QPU_A_XOR, ++ QPU_A_NOT, ++ QPU_A_CLZ, ++ QPU_A_V8ADDS = 30, ++ QPU_A_V8SUBS = 31, ++}; ++ ++enum qpu_op_mul { ++ QPU_M_NOP, ++ QPU_M_FMUL, ++ QPU_M_MUL24, ++ QPU_M_V8MULD, ++ QPU_M_V8MIN, ++ QPU_M_V8MAX, ++ QPU_M_V8ADDS, ++ QPU_M_V8SUBS, ++}; ++ ++enum qpu_raddr { ++ QPU_R_FRAG_PAYLOAD_ZW = 15, /* W for A file, Z for B file */ ++ /* 0-31 are the plain regfile a or b fields */ ++ QPU_R_UNIF = 32, ++ QPU_R_VARY = 35, ++ QPU_R_ELEM_QPU = 38, ++ QPU_R_NOP, ++ QPU_R_XY_PIXEL_COORD = 41, ++ QPU_R_MS_REV_FLAGS = 41, ++ QPU_R_VPM = 48, ++ QPU_R_VPM_LD_BUSY, ++ QPU_R_VPM_LD_WAIT, ++ QPU_R_MUTEX_ACQUIRE, ++}; ++ ++enum qpu_waddr { ++ /* 0-31 are the plain regfile a or b fields */ ++ QPU_W_ACC0 = 32, /* aka r0 */ ++ QPU_W_ACC1, ++ QPU_W_ACC2, ++ QPU_W_ACC3, ++ QPU_W_TMU_NOSWAP, ++ QPU_W_ACC5, ++ QPU_W_HOST_INT, ++ QPU_W_NOP, ++ QPU_W_UNIFORMS_ADDRESS, ++ QPU_W_QUAD_XY, /* X for regfile a, Y for regfile b */ ++ QPU_W_MS_FLAGS = 42, ++ QPU_W_REV_FLAG = 42, ++ QPU_W_TLB_STENCIL_SETUP = 43, ++ QPU_W_TLB_Z, ++ QPU_W_TLB_COLOR_MS, ++ QPU_W_TLB_COLOR_ALL, ++ QPU_W_TLB_ALPHA_MASK, ++ QPU_W_VPM, ++ QPU_W_VPMVCD_SETUP, /* LD for regfile a, ST for regfile b */ ++ QPU_W_VPM_ADDR, /* LD for regfile a, ST for regfile b */ ++ QPU_W_MUTEX_RELEASE, ++ QPU_W_SFU_RECIP, ++ QPU_W_SFU_RECIPSQRT, ++ QPU_W_SFU_EXP, ++ QPU_W_SFU_LOG, ++ QPU_W_TMU0_S, ++ QPU_W_TMU0_T, ++ QPU_W_TMU0_R, ++ QPU_W_TMU0_B, ++ QPU_W_TMU1_S, ++ QPU_W_TMU1_T, ++ QPU_W_TMU1_R, ++ QPU_W_TMU1_B, ++}; ++ ++enum qpu_sig_bits { ++ QPU_SIG_SW_BREAKPOINT, ++ QPU_SIG_NONE, ++ QPU_SIG_THREAD_SWITCH, ++ QPU_SIG_PROG_END, ++ QPU_SIG_WAIT_FOR_SCOREBOARD, ++ QPU_SIG_SCOREBOARD_UNLOCK, ++ QPU_SIG_LAST_THREAD_SWITCH, ++ QPU_SIG_COVERAGE_LOAD, ++ QPU_SIG_COLOR_LOAD, ++ QPU_SIG_COLOR_LOAD_END, ++ QPU_SIG_LOAD_TMU0, ++ QPU_SIG_LOAD_TMU1, ++ QPU_SIG_ALPHA_MASK_LOAD, ++ QPU_SIG_SMALL_IMM, ++ QPU_SIG_LOAD_IMM, ++ QPU_SIG_BRANCH ++}; ++ ++enum qpu_mux { ++ /* hardware mux values */ ++ QPU_MUX_R0, ++ QPU_MUX_R1, ++ QPU_MUX_R2, ++ QPU_MUX_R3, ++ QPU_MUX_R4, ++ QPU_MUX_R5, ++ QPU_MUX_A, ++ QPU_MUX_B, ++ ++ /* non-hardware mux values */ ++ QPU_MUX_IMM, ++}; ++ ++enum qpu_cond { ++ QPU_COND_NEVER, ++ QPU_COND_ALWAYS, ++ QPU_COND_ZS, ++ QPU_COND_ZC, ++ QPU_COND_NS, ++ QPU_COND_NC, ++ QPU_COND_CS, ++ QPU_COND_CC, ++}; ++ ++enum qpu_pack_mul { ++ QPU_PACK_MUL_NOP, ++ /* replicated to each 8 bits of the 32-bit dst. */ ++ QPU_PACK_MUL_8888 = 3, ++ QPU_PACK_MUL_8A, ++ QPU_PACK_MUL_8B, ++ QPU_PACK_MUL_8C, ++ QPU_PACK_MUL_8D, ++}; ++ ++enum qpu_pack_a { ++ QPU_PACK_A_NOP, ++ /* convert to 16 bit float if float input, or to int16. */ ++ QPU_PACK_A_16A, ++ QPU_PACK_A_16B, ++ /* replicated to each 8 bits of the 32-bit dst. */ ++ QPU_PACK_A_8888, ++ /* Convert to 8-bit unsigned int. */ ++ QPU_PACK_A_8A, ++ QPU_PACK_A_8B, ++ QPU_PACK_A_8C, ++ QPU_PACK_A_8D, ++ ++ /* Saturating variants of the previous instructions. */ ++ QPU_PACK_A_32_SAT, /* int-only */ ++ QPU_PACK_A_16A_SAT, /* int or float */ ++ QPU_PACK_A_16B_SAT, ++ QPU_PACK_A_8888_SAT, ++ QPU_PACK_A_8A_SAT, ++ QPU_PACK_A_8B_SAT, ++ QPU_PACK_A_8C_SAT, ++ QPU_PACK_A_8D_SAT, ++}; ++ ++enum qpu_unpack_r4 { ++ QPU_UNPACK_R4_NOP, ++ QPU_UNPACK_R4_F16A_TO_F32, ++ QPU_UNPACK_R4_F16B_TO_F32, ++ QPU_UNPACK_R4_8D_REP, ++ QPU_UNPACK_R4_8A, ++ QPU_UNPACK_R4_8B, ++ QPU_UNPACK_R4_8C, ++ QPU_UNPACK_R4_8D, ++}; ++ ++#define QPU_MASK(high, low) \ ++ ((((uint64_t)1 << ((high) - (low) + 1)) - 1) << (low)) ++ ++#define QPU_GET_FIELD(word, field) \ ++ ((uint32_t)(((word) & field ## _MASK) >> field ## _SHIFT)) ++ ++#define QPU_SIG_SHIFT 60 ++#define QPU_SIG_MASK QPU_MASK(63, 60) ++ ++#define QPU_UNPACK_SHIFT 57 ++#define QPU_UNPACK_MASK QPU_MASK(59, 57) ++ ++/** ++ * If set, the pack field means PACK_MUL or R4 packing, instead of normal ++ * regfile a packing. ++ */ ++#define QPU_PM ((uint64_t)1 << 56) ++ ++#define QPU_PACK_SHIFT 52 ++#define QPU_PACK_MASK QPU_MASK(55, 52) ++ ++#define QPU_COND_ADD_SHIFT 49 ++#define QPU_COND_ADD_MASK QPU_MASK(51, 49) ++#define QPU_COND_MUL_SHIFT 46 ++#define QPU_COND_MUL_MASK QPU_MASK(48, 46) ++ ++#define QPU_SF ((uint64_t)1 << 45) ++ ++#define QPU_WADDR_ADD_SHIFT 38 ++#define QPU_WADDR_ADD_MASK QPU_MASK(43, 38) ++#define QPU_WADDR_MUL_SHIFT 32 ++#define QPU_WADDR_MUL_MASK QPU_MASK(37, 32) ++ ++#define QPU_OP_MUL_SHIFT 29 ++#define QPU_OP_MUL_MASK QPU_MASK(31, 29) ++ ++#define QPU_RADDR_A_SHIFT 18 ++#define QPU_RADDR_A_MASK QPU_MASK(23, 18) ++#define QPU_RADDR_B_SHIFT 12 ++#define QPU_RADDR_B_MASK QPU_MASK(17, 12) ++#define QPU_SMALL_IMM_SHIFT 12 ++#define QPU_SMALL_IMM_MASK QPU_MASK(17, 12) ++ ++#define QPU_ADD_A_SHIFT 9 ++#define QPU_ADD_A_MASK QPU_MASK(11, 9) ++#define QPU_ADD_B_SHIFT 6 ++#define QPU_ADD_B_MASK QPU_MASK(8, 6) ++#define QPU_MUL_A_SHIFT 3 ++#define QPU_MUL_A_MASK QPU_MASK(5, 3) ++#define QPU_MUL_B_SHIFT 0 ++#define QPU_MUL_B_MASK QPU_MASK(2, 0) ++ ++#define QPU_WS ((uint64_t)1 << 44) ++ ++#define QPU_OP_ADD_SHIFT 24 ++#define QPU_OP_ADD_MASK QPU_MASK(28, 24) ++ ++#endif /* VC4_QPU_DEFINES_H */ +diff --git a/drivers/gpu/drm/vc4/vc4_validate_shaders.c b/drivers/gpu/drm/vc4/vc4_validate_shaders.c +new file mode 100644 +index 0000000..f67124b +--- /dev/null ++++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c +@@ -0,0 +1,513 @@ ++/* ++ * Copyright © 2014 Broadcom ++ * ++ * 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 (including the next ++ * paragraph) 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. ++ */ ++ ++/** ++ * DOC: Shader validator for VC4. ++ * ++ * The VC4 has no IOMMU between it and system memory, so a user with ++ * access to execute shaders could escalate privilege by overwriting ++ * system memory (using the VPM write address register in the ++ * general-purpose DMA mode) or reading system memory it shouldn't ++ * (reading it as a texture, or uniform data, or vertex data). ++ * ++ * This walks over a shader BO, ensuring that its accesses are ++ * appropriately bounded, and recording how many texture accesses are ++ * made and where so that we can do relocations for them in the ++ * uniform stream. ++ */ ++ ++#include "vc4_drv.h" ++#include "vc4_qpu_defines.h" ++ ++struct vc4_shader_validation_state { ++ struct vc4_texture_sample_info tmu_setup[2]; ++ int tmu_write_count[2]; ++ ++ /* For registers that were last written to by a MIN instruction with ++ * one argument being a uniform, the address of the uniform. ++ * Otherwise, ~0. ++ * ++ * This is used for the validation of direct address memory reads. ++ */ ++ uint32_t live_min_clamp_offsets[32 + 32 + 4]; ++ bool live_max_clamp_regs[32 + 32 + 4]; ++}; ++ ++static uint32_t ++waddr_to_live_reg_index(uint32_t waddr, bool is_b) ++{ ++ if (waddr < 32) { ++ if (is_b) ++ return 32 + waddr; ++ else ++ return waddr; ++ } else if (waddr <= QPU_W_ACC3) { ++ return 64 + waddr - QPU_W_ACC0; ++ } else { ++ return ~0; ++ } ++} ++ ++static uint32_t ++raddr_add_a_to_live_reg_index(uint64_t inst) ++{ ++ uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG); ++ uint32_t add_a = QPU_GET_FIELD(inst, QPU_ADD_A); ++ uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A); ++ uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B); ++ ++ if (add_a == QPU_MUX_A) ++ return raddr_a; ++ else if (add_a == QPU_MUX_B && sig != QPU_SIG_SMALL_IMM) ++ return 32 + raddr_b; ++ else if (add_a <= QPU_MUX_R3) ++ return 64 + add_a; ++ else ++ return ~0; ++} ++ ++static bool ++is_tmu_submit(uint32_t waddr) ++{ ++ return (waddr == QPU_W_TMU0_S || ++ waddr == QPU_W_TMU1_S); ++} ++ ++static bool ++is_tmu_write(uint32_t waddr) ++{ ++ return (waddr >= QPU_W_TMU0_S && ++ waddr <= QPU_W_TMU1_B); ++} ++ ++static bool ++record_texture_sample(struct vc4_validated_shader_info *validated_shader, ++ struct vc4_shader_validation_state *validation_state, ++ int tmu) ++{ ++ uint32_t s = validated_shader->num_texture_samples; ++ int i; ++ struct vc4_texture_sample_info *temp_samples; ++ ++ temp_samples = krealloc(validated_shader->texture_samples, ++ (s + 1) * sizeof(*temp_samples), ++ GFP_KERNEL); ++ if (!temp_samples) ++ return false; ++ ++ memcpy(&temp_samples[s], ++ &validation_state->tmu_setup[tmu], ++ sizeof(*temp_samples)); ++ ++ validated_shader->num_texture_samples = s + 1; ++ validated_shader->texture_samples = temp_samples; ++ ++ for (i = 0; i < 4; i++) ++ validation_state->tmu_setup[tmu].p_offset[i] = ~0; ++ ++ return true; ++} ++ ++static bool ++check_tmu_write(uint64_t inst, ++ struct vc4_validated_shader_info *validated_shader, ++ struct vc4_shader_validation_state *validation_state, ++ bool is_mul) ++{ ++ uint32_t waddr = (is_mul ? ++ QPU_GET_FIELD(inst, QPU_WADDR_MUL) : ++ QPU_GET_FIELD(inst, QPU_WADDR_ADD)); ++ uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A); ++ uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B); ++ int tmu = waddr > QPU_W_TMU0_B; ++ bool submit = is_tmu_submit(waddr); ++ bool is_direct = submit && validation_state->tmu_write_count[tmu] == 0; ++ uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG); ++ ++ if (is_direct) { ++ uint32_t add_b = QPU_GET_FIELD(inst, QPU_ADD_B); ++ uint32_t clamp_reg, clamp_offset; ++ ++ if (sig == QPU_SIG_SMALL_IMM) { ++ DRM_ERROR("direct TMU read used small immediate\n"); ++ return false; ++ } ++ ++ /* Make sure that this texture load is an add of the base ++ * address of the UBO to a clamped offset within the UBO. ++ */ ++ if (is_mul || ++ QPU_GET_FIELD(inst, QPU_OP_ADD) != QPU_A_ADD) { ++ DRM_ERROR("direct TMU load wasn't an add\n"); ++ return false; ++ } ++ ++ /* We assert that the the clamped address is the first ++ * argument, and the UBO base address is the second argument. ++ * This is arbitrary, but simpler than supporting flipping the ++ * two either way. ++ */ ++ clamp_reg = raddr_add_a_to_live_reg_index(inst); ++ if (clamp_reg == ~0) { ++ DRM_ERROR("direct TMU load wasn't clamped\n"); ++ return false; ++ } ++ ++ clamp_offset = validation_state->live_min_clamp_offsets[clamp_reg]; ++ if (clamp_offset == ~0) { ++ DRM_ERROR("direct TMU load wasn't clamped\n"); ++ return false; ++ } ++ ++ /* Store the clamp value's offset in p1 (see reloc_tex() in ++ * vc4_validate.c). ++ */ ++ validation_state->tmu_setup[tmu].p_offset[1] = ++ clamp_offset; ++ ++ if (!(add_b == QPU_MUX_A && raddr_a == QPU_R_UNIF) && ++ !(add_b == QPU_MUX_B && raddr_b == QPU_R_UNIF)) { ++ DRM_ERROR("direct TMU load didn't add to a uniform\n"); ++ return false; ++ } ++ ++ validation_state->tmu_setup[tmu].is_direct = true; ++ } else { ++ if (raddr_a == QPU_R_UNIF || (sig != QPU_SIG_SMALL_IMM && ++ raddr_b == QPU_R_UNIF)) { ++ DRM_ERROR("uniform read in the same instruction as " ++ "texture setup.\n"); ++ return false; ++ } ++ } ++ ++ if (validation_state->tmu_write_count[tmu] >= 4) { ++ DRM_ERROR("TMU%d got too many parameters before dispatch\n", ++ tmu); ++ return false; ++ } ++ validation_state->tmu_setup[tmu].p_offset[validation_state->tmu_write_count[tmu]] = ++ validated_shader->uniforms_size; ++ validation_state->tmu_write_count[tmu]++; ++ /* Since direct uses a RADDR uniform reference, it will get counted in ++ * check_instruction_reads() ++ */ ++ if (!is_direct) ++ validated_shader->uniforms_size += 4; ++ ++ if (submit) { ++ if (!record_texture_sample(validated_shader, ++ validation_state, tmu)) { ++ return false; ++ } ++ ++ validation_state->tmu_write_count[tmu] = 0; ++ } ++ ++ return true; ++} ++ ++static bool ++check_reg_write(uint64_t inst, ++ struct vc4_validated_shader_info *validated_shader, ++ struct vc4_shader_validation_state *validation_state, ++ bool is_mul) ++{ ++ uint32_t waddr = (is_mul ? ++ QPU_GET_FIELD(inst, QPU_WADDR_MUL) : ++ QPU_GET_FIELD(inst, QPU_WADDR_ADD)); ++ ++ switch (waddr) { ++ case QPU_W_UNIFORMS_ADDRESS: ++ /* XXX: We'll probably need to support this for reladdr, but ++ * it's definitely a security-related one. ++ */ ++ DRM_ERROR("uniforms address load unsupported\n"); ++ return false; ++ ++ case QPU_W_TLB_COLOR_MS: ++ case QPU_W_TLB_COLOR_ALL: ++ case QPU_W_TLB_Z: ++ /* These only interact with the tile buffer, not main memory, ++ * so they're safe. ++ */ ++ return true; ++ ++ case QPU_W_TMU0_S: ++ case QPU_W_TMU0_T: ++ case QPU_W_TMU0_R: ++ case QPU_W_TMU0_B: ++ case QPU_W_TMU1_S: ++ case QPU_W_TMU1_T: ++ case QPU_W_TMU1_R: ++ case QPU_W_TMU1_B: ++ return check_tmu_write(inst, validated_shader, validation_state, ++ is_mul); ++ ++ case QPU_W_HOST_INT: ++ case QPU_W_TMU_NOSWAP: ++ case QPU_W_TLB_ALPHA_MASK: ++ case QPU_W_MUTEX_RELEASE: ++ /* XXX: I haven't thought about these, so don't support them ++ * for now. ++ */ ++ DRM_ERROR("Unsupported waddr %d\n", waddr); ++ return false; ++ ++ case QPU_W_VPM_ADDR: ++ DRM_ERROR("General VPM DMA unsupported\n"); ++ return false; ++ ++ case QPU_W_VPM: ++ case QPU_W_VPMVCD_SETUP: ++ /* We allow VPM setup in general, even including VPM DMA ++ * configuration setup, because the (unsafe) DMA can only be ++ * triggered by QPU_W_VPM_ADDR writes. ++ */ ++ return true; ++ ++ case QPU_W_TLB_STENCIL_SETUP: ++ return true; ++ } ++ ++ return true; ++} ++ ++static void ++track_live_clamps(uint64_t inst, ++ struct vc4_validated_shader_info *validated_shader, ++ struct vc4_shader_validation_state *validation_state) ++{ ++ uint32_t op_add = QPU_GET_FIELD(inst, QPU_OP_ADD); ++ uint32_t waddr_add = QPU_GET_FIELD(inst, QPU_WADDR_ADD); ++ uint32_t waddr_mul = QPU_GET_FIELD(inst, QPU_WADDR_MUL); ++ uint32_t cond_add = QPU_GET_FIELD(inst, QPU_COND_ADD); ++ uint32_t add_a = QPU_GET_FIELD(inst, QPU_ADD_A); ++ uint32_t add_b = QPU_GET_FIELD(inst, QPU_ADD_B); ++ uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A); ++ uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B); ++ uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG); ++ bool ws = inst & QPU_WS; ++ uint32_t lri_add_a, lri_add, lri_mul; ++ bool add_a_is_min_0; ++ ++ /* Check whether OP_ADD's A argumennt comes from a live MAX(x, 0), ++ * before we clear previous live state. ++ */ ++ lri_add_a = raddr_add_a_to_live_reg_index(inst); ++ add_a_is_min_0 = (lri_add_a != ~0 && ++ validation_state->live_max_clamp_regs[lri_add_a]); ++ ++ /* Clear live state for registers written by our instruction. */ ++ lri_add = waddr_to_live_reg_index(waddr_add, ws); ++ lri_mul = waddr_to_live_reg_index(waddr_mul, !ws); ++ if (lri_mul != ~0) { ++ validation_state->live_max_clamp_regs[lri_mul] = false; ++ validation_state->live_min_clamp_offsets[lri_mul] = ~0; ++ } ++ if (lri_add != ~0) { ++ validation_state->live_max_clamp_regs[lri_add] = false; ++ validation_state->live_min_clamp_offsets[lri_add] = ~0; ++ } else { ++ /* Nothing further to do for live tracking, since only ADDs ++ * generate new live clamp registers. ++ */ ++ return; ++ } ++ ++ /* Now, handle remaining live clamp tracking for the ADD operation. */ ++ ++ if (cond_add != QPU_COND_ALWAYS) ++ return; ++ ++ if (op_add == QPU_A_MAX) { ++ /* Track live clamps of a value to a minimum of 0 (in either ++ * arg). ++ */ ++ if (sig != QPU_SIG_SMALL_IMM || raddr_b != 0 || ++ (add_a != QPU_MUX_B && add_b != QPU_MUX_B)) { ++ return; ++ } ++ ++ validation_state->live_max_clamp_regs[lri_add] = true; ++ } else if (op_add == QPU_A_MIN) { ++ /* Track live clamps of a value clamped to a minimum of 0 and ++ * a maximum of some uniform's offset. ++ */ ++ if (!add_a_is_min_0) ++ return; ++ ++ if (!(add_b == QPU_MUX_A && raddr_a == QPU_R_UNIF) && ++ !(add_b == QPU_MUX_B && raddr_b == QPU_R_UNIF && ++ sig != QPU_SIG_SMALL_IMM)) { ++ return; ++ } ++ ++ validation_state->live_min_clamp_offsets[lri_add] = ++ validated_shader->uniforms_size; ++ } ++} ++ ++static bool ++check_instruction_writes(uint64_t inst, ++ struct vc4_validated_shader_info *validated_shader, ++ struct vc4_shader_validation_state *validation_state) ++{ ++ uint32_t waddr_add = QPU_GET_FIELD(inst, QPU_WADDR_ADD); ++ uint32_t waddr_mul = QPU_GET_FIELD(inst, QPU_WADDR_MUL); ++ bool ok; ++ ++ if (is_tmu_write(waddr_add) && is_tmu_write(waddr_mul)) { ++ DRM_ERROR("ADD and MUL both set up textures\n"); ++ return false; ++ } ++ ++ ok = (check_reg_write(inst, validated_shader, validation_state, ++ false) && ++ check_reg_write(inst, validated_shader, validation_state, ++ true)); ++ ++ track_live_clamps(inst, validated_shader, validation_state); ++ ++ return ok; ++} ++ ++static bool ++check_instruction_reads(uint64_t inst, ++ struct vc4_validated_shader_info *validated_shader) ++{ ++ uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A); ++ uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B); ++ uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG); ++ ++ if (raddr_a == QPU_R_UNIF || ++ (raddr_b == QPU_R_UNIF && sig != QPU_SIG_SMALL_IMM)) { ++ /* This can't overflow the uint32_t, because we're reading 8 ++ * bytes of instruction to increment by 4 here, so we'd ++ * already be OOM. ++ */ ++ validated_shader->uniforms_size += 4; ++ } ++ ++ return true; ++} ++ ++struct vc4_validated_shader_info * ++vc4_validate_shader(struct drm_gem_cma_object *shader_obj) ++{ ++ bool found_shader_end = false; ++ int shader_end_ip = 0; ++ uint32_t ip, max_ip; ++ uint64_t *shader; ++ struct vc4_validated_shader_info *validated_shader; ++ struct vc4_shader_validation_state validation_state; ++ int i; ++ ++ memset(&validation_state, 0, sizeof(validation_state)); ++ ++ for (i = 0; i < 8; i++) ++ validation_state.tmu_setup[i / 4].p_offset[i % 4] = ~0; ++ for (i = 0; i < ARRAY_SIZE(validation_state.live_min_clamp_offsets); i++) ++ validation_state.live_min_clamp_offsets[i] = ~0; ++ ++ shader = shader_obj->vaddr; ++ max_ip = shader_obj->base.size / sizeof(uint64_t); ++ ++ validated_shader = kcalloc(1, sizeof(*validated_shader), GFP_KERNEL); ++ if (!validated_shader) ++ return NULL; ++ ++ for (ip = 0; ip < max_ip; ip++) { ++ uint64_t inst = shader[ip]; ++ uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG); ++ ++ switch (sig) { ++ case QPU_SIG_NONE: ++ case QPU_SIG_WAIT_FOR_SCOREBOARD: ++ case QPU_SIG_SCOREBOARD_UNLOCK: ++ case QPU_SIG_COLOR_LOAD: ++ case QPU_SIG_LOAD_TMU0: ++ case QPU_SIG_LOAD_TMU1: ++ case QPU_SIG_PROG_END: ++ case QPU_SIG_SMALL_IMM: ++ if (!check_instruction_writes(inst, validated_shader, ++ &validation_state)) { ++ DRM_ERROR("Bad write at ip %d\n", ip); ++ goto fail; ++ } ++ ++ if (!check_instruction_reads(inst, validated_shader)) ++ goto fail; ++ ++ if (sig == QPU_SIG_PROG_END) { ++ found_shader_end = true; ++ shader_end_ip = ip; ++ } ++ ++ break; ++ ++ case QPU_SIG_LOAD_IMM: ++ if (!check_instruction_writes(inst, validated_shader, ++ &validation_state)) { ++ DRM_ERROR("Bad LOAD_IMM write at ip %d\n", ip); ++ goto fail; ++ } ++ break; ++ ++ default: ++ DRM_ERROR("Unsupported QPU signal %d at " ++ "instruction %d\n", sig, ip); ++ goto fail; ++ } ++ ++ /* There are two delay slots after program end is signaled ++ * that are still executed, then we're finished. ++ */ ++ if (found_shader_end && ip == shader_end_ip + 2) ++ break; ++ } ++ ++ if (ip == max_ip) { ++ DRM_ERROR("shader failed to terminate before " ++ "shader BO end at %zd\n", ++ shader_obj->base.size); ++ goto fail; ++ } ++ ++ /* Again, no chance of integer overflow here because the worst case ++ * scenario is 8 bytes of uniforms plus handles per 8-byte ++ * instruction. ++ */ ++ validated_shader->uniforms_src_size = ++ (validated_shader->uniforms_size + ++ 4 * validated_shader->num_texture_samples); ++ ++ return validated_shader; ++ ++fail: ++ if (validated_shader) { ++ kfree(validated_shader->texture_samples); ++ kfree(validated_shader); ++ } ++ return NULL; ++} +diff --git a/include/uapi/drm/vc4_drm.h b/include/uapi/drm/vc4_drm.h +index 219d34c..74de184 100644 +--- a/include/uapi/drm/vc4_drm.h ++++ b/include/uapi/drm/vc4_drm.h +@@ -28,9 +28,11 @@ + + #define DRM_VC4_CREATE_BO 0x03 + #define DRM_VC4_MMAP_BO 0x04 ++#define DRM_VC4_CREATE_SHADER_BO 0x05 + + #define DRM_IOCTL_VC4_CREATE_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_CREATE_BO, struct drm_vc4_create_bo) + #define DRM_IOCTL_VC4_MMAP_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_MMAP_BO, struct drm_vc4_mmap_bo) ++#define DRM_IOCTL_VC4_CREATE_SHADER_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_CREATE_SHADER_BO, struct drm_vc4_create_shader_bo) + + /** + * struct drm_vc4_create_bo - ioctl argument for creating VC4 BOs. +@@ -65,4 +67,27 @@ struct drm_vc4_mmap_bo { + __u64 offset; + }; + ++/** ++ * struct drm_vc4_create_shader_bo - ioctl argument for creating VC4 ++ * shader BOs. ++ * ++ * Since allowing a shader to be overwritten while it's also being ++ * executed from would allow privlege escalation, shaders must be ++ * created using this ioctl, and they can't be mmapped later. ++ */ ++struct drm_vc4_create_shader_bo { ++ /* Size of the data argument. */ ++ __u32 size; ++ /* Flags, currently must be 0. */ ++ __u32 flags; ++ ++ /* Pointer to the data. */ ++ __u64 data; ++ ++ /** Returned GEM handle for the BO. */ ++ __u32 handle; ++ /* Pad, must be 0. */ ++ __u32 pad; ++}; ++ + #endif /* _UAPI_VC4_DRM_H_ */ +-- +2.6.4 + diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-0105-drm-vc4-Fix-a-typo-in-a-V3D-debug-register.patch b/kernel/kernel/files/patches/mageia/gpu-drm-0105-drm-vc4-Fix-a-typo-in-a-V3D-debug-register.patch new file mode 100644 index 00000000..e084f368 --- /dev/null +++ b/kernel/kernel/files/patches/mageia/gpu-drm-0105-drm-vc4-Fix-a-typo-in-a-V3D-debug-register.patch @@ -0,0 +1,26 @@ +From 1fa81589bbac16af6baf153ccc9b3f38fb16a498 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Fri, 23 Oct 2015 14:57:22 +0100 +Subject: [PATCH 5/9] drm/vc4: Fix a typo in a V3D debug register. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_regs.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h +index 9e4e904..4e52a0a 100644 +--- a/drivers/gpu/drm/vc4/vc4_regs.h ++++ b/drivers/gpu/drm/vc4/vc4_regs.h +@@ -154,7 +154,7 @@ + #define V3D_PCTRS14 0x006f4 + #define V3D_PCTR15 0x006f8 + #define V3D_PCTRS15 0x006fc +-#define V3D_BGE 0x00f00 ++#define V3D_DBGE 0x00f00 + #define V3D_FDBGO 0x00f04 + #define V3D_FDBGB 0x00f08 + #define V3D_FDBGR 0x00f0c +-- +2.6.4 + diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-0106-drm-vc4-Bind-and-initialize-the-V3D-engine.patch b/kernel/kernel/files/patches/mageia/gpu-drm-0106-drm-vc4-Bind-and-initialize-the-V3D-engine.patch new file mode 100644 index 00000000..c24a21a6 --- /dev/null +++ b/kernel/kernel/files/patches/mageia/gpu-drm-0106-drm-vc4-Bind-and-initialize-the-V3D-engine.patch @@ -0,0 +1,333 @@ +From d3f5168a0810005920e7a3d5ba83e249bd9a750c Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Mon, 2 Mar 2015 13:01:12 -0800 +Subject: [PATCH 6/9] drm/vc4: Bind and initialize the V3D engine. + +This is the component of the GPU that does 3D rendering. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/Makefile | 1 + + drivers/gpu/drm/vc4/vc4_debugfs.c | 2 + + drivers/gpu/drm/vc4/vc4_drv.c | 1 + + drivers/gpu/drm/vc4/vc4_drv.h | 13 +++ + drivers/gpu/drm/vc4/vc4_v3d.c | 225 ++++++++++++++++++++++++++++++++++++++ + 5 files changed, 242 insertions(+) + create mode 100644 drivers/gpu/drm/vc4/vc4_v3d.c + +diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile +index eb776a6..e87a6f2 100644 +--- a/drivers/gpu/drm/vc4/Makefile ++++ b/drivers/gpu/drm/vc4/Makefile +@@ -11,6 +11,7 @@ vc4-y := \ + vc4_hdmi.o \ + vc4_hvs.o \ + vc4_plane.o \ ++ vc4_v3d.o \ + vc4_validate_shaders.o + + vc4-$(CONFIG_DEBUG_FS) += vc4_debugfs.o +diff --git a/drivers/gpu/drm/vc4/vc4_debugfs.c b/drivers/gpu/drm/vc4/vc4_debugfs.c +index 6bcf96e..d76ad10 100644 +--- a/drivers/gpu/drm/vc4/vc4_debugfs.c ++++ b/drivers/gpu/drm/vc4/vc4_debugfs.c +@@ -22,6 +22,8 @@ static const struct drm_info_list vc4_debugfs_list[] = { + {"crtc0_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)0}, + {"crtc1_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)1}, + {"crtc2_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)2}, ++ {"v3d_ident", vc4_v3d_debugfs_ident, 0}, ++ {"v3d_regs", vc4_v3d_debugfs_regs, 0}, + }; + + #define VC4_DEBUGFS_ENTRIES ARRAY_SIZE(vc4_debugfs_list) +diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c +index da4be9c..db58d74 100644 +--- a/drivers/gpu/drm/vc4/vc4_drv.c ++++ b/drivers/gpu/drm/vc4/vc4_drv.c +@@ -236,6 +236,7 @@ static struct platform_driver *const component_drivers[] = { + &vc4_hdmi_driver, + &vc4_crtc_driver, + &vc4_hvs_driver, ++ &vc4_v3d_driver, + }; + + static int vc4_platform_drm_probe(struct platform_device *pdev) +diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h +index bd77d55..8945463 100644 +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -15,6 +15,7 @@ struct vc4_dev { + struct vc4_hdmi *hdmi; + struct vc4_hvs *hvs; + struct vc4_crtc *crtc[3]; ++ struct vc4_v3d *v3d; + + struct drm_fbdev_cma *fbdev; + +@@ -82,6 +83,11 @@ to_vc4_bo(struct drm_gem_object *bo) + return (struct vc4_bo *)bo; + } + ++struct vc4_v3d { ++ struct platform_device *pdev; ++ void __iomem *regs; ++}; ++ + struct vc4_hvs { + struct platform_device *pdev; + void __iomem *regs; +@@ -119,6 +125,8 @@ to_vc4_encoder(struct drm_encoder *encoder) + return container_of(encoder, struct vc4_encoder, base); + } + ++#define V3D_READ(offset) readl(vc4->v3d->regs + offset) ++#define V3D_WRITE(offset, val) writel(val, vc4->v3d->regs + offset) + #define HVS_READ(offset) readl(vc4->hvs->regs + offset) + #define HVS_WRITE(offset, val) writel(val, vc4->hvs->regs + offset) + +@@ -241,6 +249,11 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev, + u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist); + u32 vc4_plane_dlist_size(struct drm_plane_state *state); + ++/* vc4_v3d.c */ ++extern struct platform_driver vc4_v3d_driver; ++int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused); ++int vc4_v3d_debugfs_regs(struct seq_file *m, void *unused); ++ + /* vc4_validate_shader.c */ + struct vc4_validated_shader_info * + vc4_validate_shader(struct drm_gem_cma_object *shader_obj); +diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c +new file mode 100644 +index 0000000..040ad0d +--- /dev/null ++++ b/drivers/gpu/drm/vc4/vc4_v3d.c +@@ -0,0 +1,225 @@ ++/* ++ * Copyright (c) 2014 The Linux Foundation. All rights reserved. ++ * Copyright (C) 2013 Red Hat ++ * Author: Rob Clark ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ */ ++ ++#include "linux/component.h" ++#include "vc4_drv.h" ++#include "vc4_regs.h" ++ ++#ifdef CONFIG_DEBUG_FS ++#define REGDEF(reg) { reg, #reg } ++static const struct { ++ uint32_t reg; ++ const char *name; ++} vc4_reg_defs[] = { ++ REGDEF(V3D_IDENT0), ++ REGDEF(V3D_IDENT1), ++ REGDEF(V3D_IDENT2), ++ REGDEF(V3D_SCRATCH), ++ REGDEF(V3D_L2CACTL), ++ REGDEF(V3D_SLCACTL), ++ REGDEF(V3D_INTCTL), ++ REGDEF(V3D_INTENA), ++ REGDEF(V3D_INTDIS), ++ REGDEF(V3D_CT0CS), ++ REGDEF(V3D_CT1CS), ++ REGDEF(V3D_CT0EA), ++ REGDEF(V3D_CT1EA), ++ REGDEF(V3D_CT0CA), ++ REGDEF(V3D_CT1CA), ++ REGDEF(V3D_CT00RA0), ++ REGDEF(V3D_CT01RA0), ++ REGDEF(V3D_CT0LC), ++ REGDEF(V3D_CT1LC), ++ REGDEF(V3D_CT0PC), ++ REGDEF(V3D_CT1PC), ++ REGDEF(V3D_PCS), ++ REGDEF(V3D_BFC), ++ REGDEF(V3D_RFC), ++ REGDEF(V3D_BPCA), ++ REGDEF(V3D_BPCS), ++ REGDEF(V3D_BPOA), ++ REGDEF(V3D_BPOS), ++ REGDEF(V3D_BXCF), ++ REGDEF(V3D_SQRSV0), ++ REGDEF(V3D_SQRSV1), ++ REGDEF(V3D_SQCNTL), ++ REGDEF(V3D_SRQPC), ++ REGDEF(V3D_SRQUA), ++ REGDEF(V3D_SRQUL), ++ REGDEF(V3D_SRQCS), ++ REGDEF(V3D_VPACNTL), ++ REGDEF(V3D_VPMBASE), ++ REGDEF(V3D_PCTRC), ++ REGDEF(V3D_PCTRE), ++ REGDEF(V3D_PCTR0), ++ REGDEF(V3D_PCTRS0), ++ REGDEF(V3D_PCTR1), ++ REGDEF(V3D_PCTRS1), ++ REGDEF(V3D_PCTR2), ++ REGDEF(V3D_PCTRS2), ++ REGDEF(V3D_PCTR3), ++ REGDEF(V3D_PCTRS3), ++ REGDEF(V3D_PCTR4), ++ REGDEF(V3D_PCTRS4), ++ REGDEF(V3D_PCTR5), ++ REGDEF(V3D_PCTRS5), ++ REGDEF(V3D_PCTR6), ++ REGDEF(V3D_PCTRS6), ++ REGDEF(V3D_PCTR7), ++ REGDEF(V3D_PCTRS7), ++ REGDEF(V3D_PCTR8), ++ REGDEF(V3D_PCTRS8), ++ REGDEF(V3D_PCTR9), ++ REGDEF(V3D_PCTRS9), ++ REGDEF(V3D_PCTR10), ++ REGDEF(V3D_PCTRS10), ++ REGDEF(V3D_PCTR11), ++ REGDEF(V3D_PCTRS11), ++ REGDEF(V3D_PCTR12), ++ REGDEF(V3D_PCTRS12), ++ REGDEF(V3D_PCTR13), ++ REGDEF(V3D_PCTRS13), ++ REGDEF(V3D_PCTR14), ++ REGDEF(V3D_PCTRS14), ++ REGDEF(V3D_PCTR15), ++ REGDEF(V3D_PCTRS15), ++ REGDEF(V3D_DBGE), ++ REGDEF(V3D_FDBGO), ++ REGDEF(V3D_FDBGB), ++ REGDEF(V3D_FDBGR), ++ REGDEF(V3D_FDBGS), ++ REGDEF(V3D_ERRSTAT), ++}; ++ ++int vc4_v3d_debugfs_regs(struct seq_file *m, void *unused) ++{ ++ struct drm_info_node *node = (struct drm_info_node *)m->private; ++ struct drm_device *dev = node->minor->dev; ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(vc4_reg_defs); i++) { ++ seq_printf(m, "%s (0x%04x): 0x%08x\n", ++ vc4_reg_defs[i].name, vc4_reg_defs[i].reg, ++ V3D_READ(vc4_reg_defs[i].reg)); ++ } ++ ++ return 0; ++} ++ ++int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused) ++{ ++ struct drm_info_node *node = (struct drm_info_node *)m->private; ++ struct drm_device *dev = node->minor->dev; ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ uint32_t ident1 = V3D_READ(V3D_IDENT1); ++ uint32_t nslc = VC4_GET_FIELD(ident1, V3D_IDENT1_NSLC); ++ uint32_t tups = VC4_GET_FIELD(ident1, V3D_IDENT1_TUPS); ++ uint32_t qups = VC4_GET_FIELD(ident1, V3D_IDENT1_QUPS); ++ ++ seq_printf(m, "Revision: %d\n", ++ VC4_GET_FIELD(ident1, V3D_IDENT1_REV)); ++ seq_printf(m, "Slices: %d\n", nslc); ++ seq_printf(m, "TMUs: %d\n", nslc * tups); ++ seq_printf(m, "QPUs: %d\n", nslc * qups); ++ seq_printf(m, "Semaphores: %d\n", ++ VC4_GET_FIELD(ident1, V3D_IDENT1_NSEM)); ++ ++ return 0; ++} ++#endif /* CONFIG_DEBUG_FS */ ++ ++static void vc4_v3d_init_hw(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ /* Take all the memory that would have been reserved for user ++ * QPU programs, since we don't have an interface for running ++ * them, anyway. ++ */ ++ V3D_WRITE(V3D_VPMBASE, 0); ++} ++ ++static int vc4_v3d_bind(struct device *dev, struct device *master, void *data) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct drm_device *drm = dev_get_drvdata(master); ++ struct vc4_dev *vc4 = to_vc4_dev(drm); ++ struct vc4_v3d *v3d = NULL; ++ ++ v3d = devm_kzalloc(&pdev->dev, sizeof(*v3d), GFP_KERNEL); ++ if (!v3d) ++ return -ENOMEM; ++ ++ v3d->pdev = pdev; ++ ++ v3d->regs = vc4_ioremap_regs(pdev, 0); ++ if (IS_ERR(v3d->regs)) ++ return PTR_ERR(v3d->regs); ++ ++ vc4->v3d = v3d; ++ ++ if (V3D_READ(V3D_IDENT0) != V3D_EXPECTED_IDENT0) { ++ DRM_ERROR("V3D_IDENT0 read 0x%08x instead of 0x%08x\n", ++ V3D_READ(V3D_IDENT0), V3D_EXPECTED_IDENT0); ++ return -EINVAL; ++ } ++ ++ vc4_v3d_init_hw(drm); ++ ++ return 0; ++} ++ ++static void vc4_v3d_unbind(struct device *dev, struct device *master, ++ void *data) ++{ ++ struct drm_device *drm = dev_get_drvdata(master); ++ struct vc4_dev *vc4 = to_vc4_dev(drm); ++ ++ vc4->v3d = NULL; ++} ++ ++static const struct component_ops vc4_v3d_ops = { ++ .bind = vc4_v3d_bind, ++ .unbind = vc4_v3d_unbind, ++}; ++ ++static int vc4_v3d_dev_probe(struct platform_device *pdev) ++{ ++ return component_add(&pdev->dev, &vc4_v3d_ops); ++} ++ ++static int vc4_v3d_dev_remove(struct platform_device *pdev) ++{ ++ component_del(&pdev->dev, &vc4_v3d_ops); ++ return 0; ++} ++ ++static const struct of_device_id vc4_v3d_dt_match[] = { ++ { .compatible = "brcm,vc4-v3d" }, ++ {} ++}; ++ ++struct platform_driver vc4_v3d_driver = { ++ .probe = vc4_v3d_dev_probe, ++ .remove = vc4_v3d_dev_remove, ++ .driver = { ++ .name = "vc4_v3d", ++ .of_match_table = vc4_v3d_dt_match, ++ }, ++}; +-- +2.6.4 + diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-0107-drm-vc4-Add-support-for-drawing-3D-frames.patch b/kernel/kernel/files/patches/mageia/gpu-drm-0107-drm-vc4-Add-support-for-drawing-3D-frames.patch new file mode 100644 index 00000000..565f2c1e --- /dev/null +++ b/kernel/kernel/files/patches/mageia/gpu-drm-0107-drm-vc4-Add-support-for-drawing-3D-frames.patch @@ -0,0 +1,3477 @@ +From d5b1a78a772f1e31a94f8babfa964152ec5e9aa5 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Mon, 30 Nov 2015 12:13:37 -0800 +Subject: [PATCH 7/9] drm/vc4: Add support for drawing 3D frames. + +The user submission is basically a pointer to a command list and a +pointer to uniforms. We copy those in to the kernel, validate and +relocate them, and store the result in a GPU BO which we queue for +execution. + +v2: Drop support for NV shader recs (not necessary for GL), simplify + vc4_use_bo(), improve bin flush/semaphore checks, use __u32 style + types. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/Makefile | 7 + + drivers/gpu/drm/vc4/vc4_drv.c | 15 +- + drivers/gpu/drm/vc4/vc4_drv.h | 182 +++++++ + drivers/gpu/drm/vc4/vc4_gem.c | 642 +++++++++++++++++++++++ + drivers/gpu/drm/vc4/vc4_irq.c | 210 ++++++++ + drivers/gpu/drm/vc4/vc4_packet.h | 399 +++++++++++++++ + drivers/gpu/drm/vc4/vc4_render_cl.c | 634 +++++++++++++++++++++++ + drivers/gpu/drm/vc4/vc4_trace.h | 63 +++ + drivers/gpu/drm/vc4/vc4_trace_points.c | 14 + + drivers/gpu/drm/vc4/vc4_v3d.c | 37 ++ + drivers/gpu/drm/vc4/vc4_validate.c | 900 +++++++++++++++++++++++++++++++++ + include/uapi/drm/vc4_drm.h | 141 ++++++ + 12 files changed, 3243 insertions(+), 1 deletion(-) + create mode 100644 drivers/gpu/drm/vc4/vc4_gem.c + create mode 100644 drivers/gpu/drm/vc4/vc4_irq.c + create mode 100644 drivers/gpu/drm/vc4/vc4_packet.h + create mode 100644 drivers/gpu/drm/vc4/vc4_render_cl.c + create mode 100644 drivers/gpu/drm/vc4/vc4_trace.h + create mode 100644 drivers/gpu/drm/vc4/vc4_trace_points.c + create mode 100644 drivers/gpu/drm/vc4/vc4_validate.c + +diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile +index e87a6f2..4c6a99f 100644 +--- a/drivers/gpu/drm/vc4/Makefile ++++ b/drivers/gpu/drm/vc4/Makefile +@@ -8,12 +8,19 @@ vc4-y := \ + vc4_crtc.o \ + vc4_drv.o \ + vc4_kms.o \ ++ vc4_gem.o \ + vc4_hdmi.o \ + vc4_hvs.o \ ++ vc4_irq.o \ + vc4_plane.o \ ++ vc4_render_cl.o \ ++ vc4_trace_points.o \ + vc4_v3d.o \ ++ vc4_validate.o \ + vc4_validate_shaders.o + + vc4-$(CONFIG_DEBUG_FS) += vc4_debugfs.o + + obj-$(CONFIG_DRM_VC4) += vc4.o ++ ++CFLAGS_vc4_trace_points.o := -I$(src) +diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c +index db58d74..2cfee59 100644 +--- a/drivers/gpu/drm/vc4/vc4_drv.c ++++ b/drivers/gpu/drm/vc4/vc4_drv.c +@@ -74,6 +74,9 @@ static const struct file_operations vc4_drm_fops = { + }; + + static const struct drm_ioctl_desc vc4_drm_ioctls[] = { ++ DRM_IOCTL_DEF_DRV(VC4_SUBMIT_CL, vc4_submit_cl_ioctl, 0), ++ DRM_IOCTL_DEF_DRV(VC4_WAIT_SEQNO, vc4_wait_seqno_ioctl, 0), ++ DRM_IOCTL_DEF_DRV(VC4_WAIT_BO, vc4_wait_bo_ioctl, 0), + DRM_IOCTL_DEF_DRV(VC4_CREATE_BO, vc4_create_bo_ioctl, 0), + DRM_IOCTL_DEF_DRV(VC4_MMAP_BO, vc4_mmap_bo_ioctl, 0), + DRM_IOCTL_DEF_DRV(VC4_CREATE_SHADER_BO, vc4_create_shader_bo_ioctl, 0), +@@ -83,10 +86,16 @@ static struct drm_driver vc4_drm_driver = { + .driver_features = (DRIVER_MODESET | + DRIVER_ATOMIC | + DRIVER_GEM | ++ DRIVER_HAVE_IRQ | + DRIVER_PRIME), + .lastclose = vc4_lastclose, + .preclose = vc4_drm_preclose, + ++ .irq_handler = vc4_irq, ++ .irq_preinstall = vc4_irq_preinstall, ++ .irq_postinstall = vc4_irq_postinstall, ++ .irq_uninstall = vc4_irq_uninstall, ++ + .enable_vblank = vc4_enable_vblank, + .disable_vblank = vc4_disable_vblank, + .get_vblank_counter = drm_vblank_count, +@@ -181,9 +190,11 @@ static int vc4_drm_bind(struct device *dev) + if (ret) + goto unref; + ++ vc4_gem_init(drm); ++ + ret = component_bind_all(dev, drm); + if (ret) +- goto unref; ++ goto gem_destroy; + + ret = drm_dev_register(drm, 0); + if (ret < 0) +@@ -207,6 +218,8 @@ unregister: + drm_dev_unregister(drm); + unbind_all: + component_unbind_all(dev, drm); ++gem_destroy: ++ vc4_gem_destroy(drm); + unref: + drm_dev_unref(drm); + vc4_bo_cache_destroy(drm); +diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h +index 8945463..0bc8c57 100644 +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -49,6 +49,48 @@ struct vc4_dev { + + /* Protects bo_cache and the BO stats. */ + struct mutex bo_lock; ++ ++ /* Sequence number for the last job queued in job_list. ++ * Starts at 0 (no jobs emitted). ++ */ ++ uint64_t emit_seqno; ++ ++ /* Sequence number for the last completed job on the GPU. ++ * Starts at 0 (no jobs completed). ++ */ ++ uint64_t finished_seqno; ++ ++ /* List of all struct vc4_exec_info for jobs to be executed. ++ * The first job in the list is the one currently programmed ++ * into ct0ca/ct1ca for execution. ++ */ ++ struct list_head job_list; ++ /* List of the finished vc4_exec_infos waiting to be freed by ++ * job_done_work. ++ */ ++ struct list_head job_done_list; ++ /* Spinlock used to synchronize the job_list and seqno ++ * accesses between the IRQ handler and GEM ioctls. ++ */ ++ spinlock_t job_lock; ++ wait_queue_head_t job_wait_queue; ++ struct work_struct job_done_work; ++ ++ /* The binner overflow memory that's currently set up in ++ * BPOA/BPOS registers. When overflow occurs and a new one is ++ * allocated, the previous one will be moved to ++ * vc4->current_exec's free list. ++ */ ++ struct vc4_bo *overflow_mem; ++ struct work_struct overflow_mem_work; ++ ++ struct { ++ uint32_t last_ct0ca, last_ct1ca; ++ struct timer_list timer; ++ struct work_struct reset_work; ++ } hangcheck; ++ ++ struct semaphore async_modeset; + }; + + static inline struct vc4_dev * +@@ -60,6 +102,9 @@ to_vc4_dev(struct drm_device *dev) + struct vc4_bo { + struct drm_gem_cma_object base; + ++ /* seqno of the last job to render to this BO. */ ++ uint64_t seqno; ++ + /* List entry for the BO's position in either + * vc4_exec_info->unref_list or vc4_dev->bo_cache.time_list + */ +@@ -130,6 +175,101 @@ to_vc4_encoder(struct drm_encoder *encoder) + #define HVS_READ(offset) readl(vc4->hvs->regs + offset) + #define HVS_WRITE(offset, val) writel(val, vc4->hvs->regs + offset) + ++struct vc4_exec_info { ++ /* Sequence number for this bin/render job. */ ++ uint64_t seqno; ++ ++ /* Kernel-space copy of the ioctl arguments */ ++ struct drm_vc4_submit_cl *args; ++ ++ /* This is the array of BOs that were looked up at the start of exec. ++ * Command validation will use indices into this array. ++ */ ++ struct drm_gem_cma_object **bo; ++ uint32_t bo_count; ++ ++ /* Pointers for our position in vc4->job_list */ ++ struct list_head head; ++ ++ /* List of other BOs used in the job that need to be released ++ * once the job is complete. ++ */ ++ struct list_head unref_list; ++ ++ /* Current unvalidated indices into @bo loaded by the non-hardware ++ * VC4_PACKET_GEM_HANDLES. ++ */ ++ uint32_t bo_index[2]; ++ ++ /* This is the BO where we store the validated command lists, shader ++ * records, and uniforms. ++ */ ++ struct drm_gem_cma_object *exec_bo; ++ ++ /** ++ * This tracks the per-shader-record state (packet 64) that ++ * determines the length of the shader record and the offset ++ * it's expected to be found at. It gets read in from the ++ * command lists. ++ */ ++ struct vc4_shader_state { ++ uint32_t addr; ++ /* Maximum vertex index referenced by any primitive using this ++ * shader state. ++ */ ++ uint32_t max_index; ++ } *shader_state; ++ ++ /** How many shader states the user declared they were using. */ ++ uint32_t shader_state_size; ++ /** How many shader state records the validator has seen. */ ++ uint32_t shader_state_count; ++ ++ bool found_tile_binning_mode_config_packet; ++ bool found_start_tile_binning_packet; ++ bool found_increment_semaphore_packet; ++ bool found_flush; ++ uint8_t bin_tiles_x, bin_tiles_y; ++ struct drm_gem_cma_object *tile_bo; ++ uint32_t tile_alloc_offset; ++ ++ /** ++ * Computed addresses pointing into exec_bo where we start the ++ * bin thread (ct0) and render thread (ct1). ++ */ ++ uint32_t ct0ca, ct0ea; ++ uint32_t ct1ca, ct1ea; ++ ++ /* Pointer to the unvalidated bin CL (if present). */ ++ void *bin_u; ++ ++ /* Pointers to the shader recs. These paddr gets incremented as CL ++ * packets are relocated in validate_gl_shader_state, and the vaddrs ++ * (u and v) get incremented and size decremented as the shader recs ++ * themselves are validated. ++ */ ++ void *shader_rec_u; ++ void *shader_rec_v; ++ uint32_t shader_rec_p; ++ uint32_t shader_rec_size; ++ ++ /* Pointers to the uniform data. These pointers are incremented, and ++ * size decremented, as each batch of uniforms is uploaded. ++ */ ++ void *uniforms_u; ++ void *uniforms_v; ++ uint32_t uniforms_p; ++ uint32_t uniforms_size; ++}; ++ ++static inline struct vc4_exec_info * ++vc4_first_job(struct vc4_dev *vc4) ++{ ++ if (list_empty(&vc4->job_list)) ++ return NULL; ++ return list_first_entry(&vc4->job_list, struct vc4_exec_info, head); ++} ++ + /** + * struct vc4_texture_sample_info - saves the offsets into the UBO for texture + * setup parameters. +@@ -231,10 +371,31 @@ void vc4_debugfs_cleanup(struct drm_minor *minor); + /* vc4_drv.c */ + void __iomem *vc4_ioremap_regs(struct platform_device *dev, int index); + ++/* vc4_gem.c */ ++void vc4_gem_init(struct drm_device *dev); ++void vc4_gem_destroy(struct drm_device *dev); ++int vc4_submit_cl_ioctl(struct drm_device *dev, void *data, ++ struct drm_file *file_priv); ++int vc4_wait_seqno_ioctl(struct drm_device *dev, void *data, ++ struct drm_file *file_priv); ++int vc4_wait_bo_ioctl(struct drm_device *dev, void *data, ++ struct drm_file *file_priv); ++void vc4_submit_next_job(struct drm_device *dev); ++int vc4_wait_for_seqno(struct drm_device *dev, uint64_t seqno, ++ uint64_t timeout_ns, bool interruptible); ++void vc4_job_handle_completed(struct vc4_dev *vc4); ++ + /* vc4_hdmi.c */ + extern struct platform_driver vc4_hdmi_driver; + int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused); + ++/* vc4_irq.c */ ++irqreturn_t vc4_irq(int irq, void *arg); ++void vc4_irq_preinstall(struct drm_device *dev); ++int vc4_irq_postinstall(struct drm_device *dev); ++void vc4_irq_uninstall(struct drm_device *dev); ++void vc4_irq_reset(struct drm_device *dev); ++ + /* vc4_hvs.c */ + extern struct platform_driver vc4_hvs_driver; + void vc4_hvs_dump_state(struct drm_device *dev); +@@ -253,6 +414,27 @@ u32 vc4_plane_dlist_size(struct drm_plane_state *state); + extern struct platform_driver vc4_v3d_driver; + int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused); + int vc4_v3d_debugfs_regs(struct seq_file *m, void *unused); ++int vc4_v3d_set_power(struct vc4_dev *vc4, bool on); ++ ++/* vc4_validate.c */ ++int ++vc4_validate_bin_cl(struct drm_device *dev, ++ void *validated, ++ void *unvalidated, ++ struct vc4_exec_info *exec); ++ ++int ++vc4_validate_shader_recs(struct drm_device *dev, struct vc4_exec_info *exec); ++ ++struct drm_gem_cma_object *vc4_use_bo(struct vc4_exec_info *exec, ++ uint32_t hindex); ++ ++int vc4_get_rcl(struct drm_device *dev, struct vc4_exec_info *exec); ++ ++bool vc4_check_tex_size(struct vc4_exec_info *exec, ++ struct drm_gem_cma_object *fbo, ++ uint32_t offset, uint8_t tiling_format, ++ uint32_t width, uint32_t height, uint8_t cpp); + + /* vc4_validate_shader.c */ + struct vc4_validated_shader_info * +diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c +new file mode 100644 +index 0000000..936dddf +--- /dev/null ++++ b/drivers/gpu/drm/vc4/vc4_gem.c +@@ -0,0 +1,642 @@ ++/* ++ * Copyright © 2014 Broadcom ++ * ++ * 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 (including the next ++ * paragraph) 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. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "uapi/drm/vc4_drm.h" ++#include "vc4_drv.h" ++#include "vc4_regs.h" ++#include "vc4_trace.h" ++ ++static void ++vc4_queue_hangcheck(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ mod_timer(&vc4->hangcheck.timer, ++ round_jiffies_up(jiffies + msecs_to_jiffies(100))); ++} ++ ++static void ++vc4_reset(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ DRM_INFO("Resetting GPU.\n"); ++ vc4_v3d_set_power(vc4, false); ++ vc4_v3d_set_power(vc4, true); ++ ++ vc4_irq_reset(dev); ++ ++ /* Rearm the hangcheck -- another job might have been waiting ++ * for our hung one to get kicked off, and vc4_irq_reset() ++ * would have started it. ++ */ ++ vc4_queue_hangcheck(dev); ++} ++ ++static void ++vc4_reset_work(struct work_struct *work) ++{ ++ struct vc4_dev *vc4 = ++ container_of(work, struct vc4_dev, hangcheck.reset_work); ++ ++ vc4_reset(vc4->dev); ++} ++ ++static void ++vc4_hangcheck_elapsed(unsigned long data) ++{ ++ struct drm_device *dev = (struct drm_device *)data; ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ uint32_t ct0ca, ct1ca; ++ ++ /* If idle, we can stop watching for hangs. */ ++ if (list_empty(&vc4->job_list)) ++ return; ++ ++ ct0ca = V3D_READ(V3D_CTNCA(0)); ++ ct1ca = V3D_READ(V3D_CTNCA(1)); ++ ++ /* If we've made any progress in execution, rearm the timer ++ * and wait. ++ */ ++ if (ct0ca != vc4->hangcheck.last_ct0ca || ++ ct1ca != vc4->hangcheck.last_ct1ca) { ++ vc4->hangcheck.last_ct0ca = ct0ca; ++ vc4->hangcheck.last_ct1ca = ct1ca; ++ vc4_queue_hangcheck(dev); ++ return; ++ } ++ ++ /* We've gone too long with no progress, reset. This has to ++ * be done from a work struct, since resetting can sleep and ++ * this timer hook isn't allowed to. ++ */ ++ schedule_work(&vc4->hangcheck.reset_work); ++} ++ ++static void ++submit_cl(struct drm_device *dev, uint32_t thread, uint32_t start, uint32_t end) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ /* Set the current and end address of the control list. ++ * Writing the end register is what starts the job. ++ */ ++ V3D_WRITE(V3D_CTNCA(thread), start); ++ V3D_WRITE(V3D_CTNEA(thread), end); ++} ++ ++int ++vc4_wait_for_seqno(struct drm_device *dev, uint64_t seqno, uint64_t timeout_ns, ++ bool interruptible) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ int ret = 0; ++ unsigned long timeout_expire; ++ DEFINE_WAIT(wait); ++ ++ if (vc4->finished_seqno >= seqno) ++ return 0; ++ ++ if (timeout_ns == 0) ++ return -ETIME; ++ ++ timeout_expire = jiffies + nsecs_to_jiffies(timeout_ns); ++ ++ trace_vc4_wait_for_seqno_begin(dev, seqno, timeout_ns); ++ for (;;) { ++ prepare_to_wait(&vc4->job_wait_queue, &wait, ++ interruptible ? TASK_INTERRUPTIBLE : ++ TASK_UNINTERRUPTIBLE); ++ ++ if (interruptible && signal_pending(current)) { ++ ret = -ERESTARTSYS; ++ break; ++ } ++ ++ if (vc4->finished_seqno >= seqno) ++ break; ++ ++ if (timeout_ns != ~0ull) { ++ if (time_after_eq(jiffies, timeout_expire)) { ++ ret = -ETIME; ++ break; ++ } ++ schedule_timeout(timeout_expire - jiffies); ++ } else { ++ schedule(); ++ } ++ } ++ ++ finish_wait(&vc4->job_wait_queue, &wait); ++ trace_vc4_wait_for_seqno_end(dev, seqno); ++ ++ if (ret && ret != -ERESTARTSYS) { ++ DRM_ERROR("timeout waiting for render thread idle\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void ++vc4_flush_caches(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ /* Flush the GPU L2 caches. These caches sit on top of system ++ * L3 (the 128kb or so shared with the CPU), and are ++ * non-allocating in the L3. ++ */ ++ V3D_WRITE(V3D_L2CACTL, ++ V3D_L2CACTL_L2CCLR); ++ ++ V3D_WRITE(V3D_SLCACTL, ++ VC4_SET_FIELD(0xf, V3D_SLCACTL_T1CC) | ++ VC4_SET_FIELD(0xf, V3D_SLCACTL_T0CC) | ++ VC4_SET_FIELD(0xf, V3D_SLCACTL_UCC) | ++ VC4_SET_FIELD(0xf, V3D_SLCACTL_ICC)); ++} ++ ++/* Sets the registers for the next job to be actually be executed in ++ * the hardware. ++ * ++ * The job_lock should be held during this. ++ */ ++void ++vc4_submit_next_job(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ struct vc4_exec_info *exec = vc4_first_job(vc4); ++ ++ if (!exec) ++ return; ++ ++ vc4_flush_caches(dev); ++ ++ /* Disable the binner's pre-loaded overflow memory address */ ++ V3D_WRITE(V3D_BPOA, 0); ++ V3D_WRITE(V3D_BPOS, 0); ++ ++ if (exec->ct0ca != exec->ct0ea) ++ submit_cl(dev, 0, exec->ct0ca, exec->ct0ea); ++ submit_cl(dev, 1, exec->ct1ca, exec->ct1ea); ++} ++ ++static void ++vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno) ++{ ++ struct vc4_bo *bo; ++ unsigned i; ++ ++ for (i = 0; i < exec->bo_count; i++) { ++ bo = to_vc4_bo(&exec->bo[i]->base); ++ bo->seqno = seqno; ++ } ++ ++ list_for_each_entry(bo, &exec->unref_list, unref_head) { ++ bo->seqno = seqno; ++ } ++} ++ ++/* Queues a struct vc4_exec_info for execution. If no job is ++ * currently executing, then submits it. ++ * ++ * Unlike most GPUs, our hardware only handles one command list at a ++ * time. To queue multiple jobs at once, we'd need to edit the ++ * previous command list to have a jump to the new one at the end, and ++ * then bump the end address. That's a change for a later date, ++ * though. ++ */ ++static void ++vc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ uint64_t seqno; ++ unsigned long irqflags; ++ ++ spin_lock_irqsave(&vc4->job_lock, irqflags); ++ ++ seqno = ++vc4->emit_seqno; ++ exec->seqno = seqno; ++ vc4_update_bo_seqnos(exec, seqno); ++ ++ list_add_tail(&exec->head, &vc4->job_list); ++ ++ /* If no job was executing, kick ours off. Otherwise, it'll ++ * get started when the previous job's frame done interrupt ++ * occurs. ++ */ ++ if (vc4_first_job(vc4) == exec) { ++ vc4_submit_next_job(dev); ++ vc4_queue_hangcheck(dev); ++ } ++ ++ spin_unlock_irqrestore(&vc4->job_lock, irqflags); ++} ++ ++/** ++ * Looks up a bunch of GEM handles for BOs and stores the array for ++ * use in the command validator that actually writes relocated ++ * addresses pointing to them. ++ */ ++static int ++vc4_cl_lookup_bos(struct drm_device *dev, ++ struct drm_file *file_priv, ++ struct vc4_exec_info *exec) ++{ ++ struct drm_vc4_submit_cl *args = exec->args; ++ uint32_t *handles; ++ int ret = 0; ++ int i; ++ ++ exec->bo_count = args->bo_handle_count; ++ ++ if (!exec->bo_count) { ++ /* See comment on bo_index for why we have to check ++ * this. ++ */ ++ DRM_ERROR("Rendering requires BOs to validate\n"); ++ return -EINVAL; ++ } ++ ++ exec->bo = kcalloc(exec->bo_count, sizeof(struct drm_gem_cma_object *), ++ GFP_KERNEL); ++ if (!exec->bo) { ++ DRM_ERROR("Failed to allocate validated BO pointers\n"); ++ return -ENOMEM; ++ } ++ ++ handles = drm_malloc_ab(exec->bo_count, sizeof(uint32_t)); ++ if (!handles) { ++ DRM_ERROR("Failed to allocate incoming GEM handles\n"); ++ goto fail; ++ } ++ ++ ret = copy_from_user(handles, ++ (void __user *)(uintptr_t)args->bo_handles, ++ exec->bo_count * sizeof(uint32_t)); ++ if (ret) { ++ DRM_ERROR("Failed to copy in GEM handles\n"); ++ goto fail; ++ } ++ ++ spin_lock(&file_priv->table_lock); ++ for (i = 0; i < exec->bo_count; i++) { ++ struct drm_gem_object *bo = idr_find(&file_priv->object_idr, ++ handles[i]); ++ if (!bo) { ++ DRM_ERROR("Failed to look up GEM BO %d: %d\n", ++ i, handles[i]); ++ ret = -EINVAL; ++ spin_unlock(&file_priv->table_lock); ++ goto fail; ++ } ++ drm_gem_object_reference(bo); ++ exec->bo[i] = (struct drm_gem_cma_object *)bo; ++ } ++ spin_unlock(&file_priv->table_lock); ++ ++fail: ++ kfree(handles); ++ return 0; ++} ++ ++static int ++vc4_get_bcl(struct drm_device *dev, struct vc4_exec_info *exec) ++{ ++ struct drm_vc4_submit_cl *args = exec->args; ++ void *temp = NULL; ++ void *bin; ++ int ret = 0; ++ uint32_t bin_offset = 0; ++ uint32_t shader_rec_offset = roundup(bin_offset + args->bin_cl_size, ++ 16); ++ uint32_t uniforms_offset = shader_rec_offset + args->shader_rec_size; ++ uint32_t exec_size = uniforms_offset + args->uniforms_size; ++ uint32_t temp_size = exec_size + (sizeof(struct vc4_shader_state) * ++ args->shader_rec_count); ++ struct vc4_bo *bo; ++ ++ if (uniforms_offset < shader_rec_offset || ++ exec_size < uniforms_offset || ++ args->shader_rec_count >= (UINT_MAX / ++ sizeof(struct vc4_shader_state)) || ++ temp_size < exec_size) { ++ DRM_ERROR("overflow in exec arguments\n"); ++ goto fail; ++ } ++ ++ /* Allocate space where we'll store the copied in user command lists ++ * and shader records. ++ * ++ * We don't just copy directly into the BOs because we need to ++ * read the contents back for validation, and I think the ++ * bo->vaddr is uncached access. ++ */ ++ temp = kmalloc(temp_size, GFP_KERNEL); ++ if (!temp) { ++ DRM_ERROR("Failed to allocate storage for copying " ++ "in bin/render CLs.\n"); ++ ret = -ENOMEM; ++ goto fail; ++ } ++ bin = temp + bin_offset; ++ exec->shader_rec_u = temp + shader_rec_offset; ++ exec->uniforms_u = temp + uniforms_offset; ++ exec->shader_state = temp + exec_size; ++ exec->shader_state_size = args->shader_rec_count; ++ ++ ret = copy_from_user(bin, ++ (void __user *)(uintptr_t)args->bin_cl, ++ args->bin_cl_size); ++ if (ret) { ++ DRM_ERROR("Failed to copy in bin cl\n"); ++ goto fail; ++ } ++ ++ ret = copy_from_user(exec->shader_rec_u, ++ (void __user *)(uintptr_t)args->shader_rec, ++ args->shader_rec_size); ++ if (ret) { ++ DRM_ERROR("Failed to copy in shader recs\n"); ++ goto fail; ++ } ++ ++ ret = copy_from_user(exec->uniforms_u, ++ (void __user *)(uintptr_t)args->uniforms, ++ args->uniforms_size); ++ if (ret) { ++ DRM_ERROR("Failed to copy in uniforms cl\n"); ++ goto fail; ++ } ++ ++ bo = vc4_bo_create(dev, exec_size, true); ++ if (!bo) { ++ DRM_ERROR("Couldn't allocate BO for binning\n"); ++ ret = PTR_ERR(exec->exec_bo); ++ goto fail; ++ } ++ exec->exec_bo = &bo->base; ++ ++ list_add_tail(&to_vc4_bo(&exec->exec_bo->base)->unref_head, ++ &exec->unref_list); ++ ++ exec->ct0ca = exec->exec_bo->paddr + bin_offset; ++ ++ exec->bin_u = bin; ++ ++ exec->shader_rec_v = exec->exec_bo->vaddr + shader_rec_offset; ++ exec->shader_rec_p = exec->exec_bo->paddr + shader_rec_offset; ++ exec->shader_rec_size = args->shader_rec_size; ++ ++ exec->uniforms_v = exec->exec_bo->vaddr + uniforms_offset; ++ exec->uniforms_p = exec->exec_bo->paddr + uniforms_offset; ++ exec->uniforms_size = args->uniforms_size; ++ ++ ret = vc4_validate_bin_cl(dev, ++ exec->exec_bo->vaddr + bin_offset, ++ bin, ++ exec); ++ if (ret) ++ goto fail; ++ ++ ret = vc4_validate_shader_recs(dev, exec); ++ ++fail: ++ kfree(temp); ++ return ret; ++} ++ ++static void ++vc4_complete_exec(struct drm_device *dev, struct vc4_exec_info *exec) ++{ ++ unsigned i; ++ ++ /* Need the struct lock for drm_gem_object_unreference(). */ ++ mutex_lock(&dev->struct_mutex); ++ if (exec->bo) { ++ for (i = 0; i < exec->bo_count; i++) ++ drm_gem_object_unreference(&exec->bo[i]->base); ++ kfree(exec->bo); ++ } ++ ++ while (!list_empty(&exec->unref_list)) { ++ struct vc4_bo *bo = list_first_entry(&exec->unref_list, ++ struct vc4_bo, unref_head); ++ list_del(&bo->unref_head); ++ drm_gem_object_unreference(&bo->base.base); ++ } ++ mutex_unlock(&dev->struct_mutex); ++ ++ kfree(exec); ++} ++ ++void ++vc4_job_handle_completed(struct vc4_dev *vc4) ++{ ++ unsigned long irqflags; ++ ++ spin_lock_irqsave(&vc4->job_lock, irqflags); ++ while (!list_empty(&vc4->job_done_list)) { ++ struct vc4_exec_info *exec = ++ list_first_entry(&vc4->job_done_list, ++ struct vc4_exec_info, head); ++ list_del(&exec->head); ++ ++ spin_unlock_irqrestore(&vc4->job_lock, irqflags); ++ vc4_complete_exec(vc4->dev, exec); ++ spin_lock_irqsave(&vc4->job_lock, irqflags); ++ } ++ spin_unlock_irqrestore(&vc4->job_lock, irqflags); ++} ++ ++/* Scheduled when any job has been completed, this walks the list of ++ * jobs that had completed and unrefs their BOs and frees their exec ++ * structs. ++ */ ++static void ++vc4_job_done_work(struct work_struct *work) ++{ ++ struct vc4_dev *vc4 = ++ container_of(work, struct vc4_dev, job_done_work); ++ ++ vc4_job_handle_completed(vc4); ++} ++ ++static int ++vc4_wait_for_seqno_ioctl_helper(struct drm_device *dev, ++ uint64_t seqno, ++ uint64_t *timeout_ns) ++{ ++ unsigned long start = jiffies; ++ int ret = vc4_wait_for_seqno(dev, seqno, *timeout_ns, true); ++ ++ if ((ret == -EINTR || ret == -ERESTARTSYS) && *timeout_ns != ~0ull) { ++ uint64_t delta = jiffies_to_nsecs(jiffies - start); ++ ++ if (*timeout_ns >= delta) ++ *timeout_ns -= delta; ++ } ++ ++ return ret; ++} ++ ++int ++vc4_wait_seqno_ioctl(struct drm_device *dev, void *data, ++ struct drm_file *file_priv) ++{ ++ struct drm_vc4_wait_seqno *args = data; ++ ++ return vc4_wait_for_seqno_ioctl_helper(dev, args->seqno, ++ &args->timeout_ns); ++} ++ ++int ++vc4_wait_bo_ioctl(struct drm_device *dev, void *data, ++ struct drm_file *file_priv) ++{ ++ int ret; ++ struct drm_vc4_wait_bo *args = data; ++ struct drm_gem_object *gem_obj; ++ struct vc4_bo *bo; ++ ++ gem_obj = drm_gem_object_lookup(dev, file_priv, args->handle); ++ if (!gem_obj) { ++ DRM_ERROR("Failed to look up GEM BO %d\n", args->handle); ++ return -EINVAL; ++ } ++ bo = to_vc4_bo(gem_obj); ++ ++ ret = vc4_wait_for_seqno_ioctl_helper(dev, bo->seqno, ++ &args->timeout_ns); ++ ++ drm_gem_object_unreference_unlocked(gem_obj); ++ return ret; ++} ++ ++/** ++ * Submits a command list to the VC4. ++ * ++ * This is what is called batchbuffer emitting on other hardware. ++ */ ++int ++vc4_submit_cl_ioctl(struct drm_device *dev, void *data, ++ struct drm_file *file_priv) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ struct drm_vc4_submit_cl *args = data; ++ struct vc4_exec_info *exec; ++ int ret; ++ ++ if ((args->flags & ~VC4_SUBMIT_CL_USE_CLEAR_COLOR) != 0) { ++ DRM_ERROR("Unknown flags: 0x%02x\n", args->flags); ++ return -EINVAL; ++ } ++ ++ exec = kcalloc(1, sizeof(*exec), GFP_KERNEL); ++ if (!exec) { ++ DRM_ERROR("malloc failure on exec struct\n"); ++ return -ENOMEM; ++ } ++ ++ exec->args = args; ++ INIT_LIST_HEAD(&exec->unref_list); ++ ++ ret = vc4_cl_lookup_bos(dev, file_priv, exec); ++ if (ret) ++ goto fail; ++ ++ if (exec->args->bin_cl_size != 0) { ++ ret = vc4_get_bcl(dev, exec); ++ if (ret) ++ goto fail; ++ } else { ++ exec->ct0ca = 0; ++ exec->ct0ea = 0; ++ } ++ ++ ret = vc4_get_rcl(dev, exec); ++ if (ret) ++ goto fail; ++ ++ /* Clear this out of the struct we'll be putting in the queue, ++ * since it's part of our stack. ++ */ ++ exec->args = NULL; ++ ++ vc4_queue_submit(dev, exec); ++ ++ /* Return the seqno for our job. */ ++ args->seqno = vc4->emit_seqno; ++ ++ return 0; ++ ++fail: ++ vc4_complete_exec(vc4->dev, exec); ++ ++ return ret; ++} ++ ++void ++vc4_gem_init(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ INIT_LIST_HEAD(&vc4->job_list); ++ INIT_LIST_HEAD(&vc4->job_done_list); ++ spin_lock_init(&vc4->job_lock); ++ ++ INIT_WORK(&vc4->hangcheck.reset_work, vc4_reset_work); ++ setup_timer(&vc4->hangcheck.timer, ++ vc4_hangcheck_elapsed, ++ (unsigned long)dev); ++ ++ INIT_WORK(&vc4->job_done_work, vc4_job_done_work); ++} ++ ++void ++vc4_gem_destroy(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ /* Waiting for exec to finish would need to be done before ++ * unregistering V3D. ++ */ ++ WARN_ON(vc4->emit_seqno != vc4->finished_seqno); ++ ++ /* V3D should already have disabled its interrupt and cleared ++ * the overflow allocation registers. Now free the object. ++ */ ++ if (vc4->overflow_mem) { ++ drm_gem_object_unreference_unlocked(&vc4->overflow_mem->base.base); ++ vc4->overflow_mem = NULL; ++ } ++ ++ vc4_bo_cache_destroy(dev); ++} +diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c +new file mode 100644 +index 0000000..b68060e +--- /dev/null ++++ b/drivers/gpu/drm/vc4/vc4_irq.c +@@ -0,0 +1,210 @@ ++/* ++ * Copyright © 2014 Broadcom ++ * ++ * 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 (including the next ++ * paragraph) 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. ++ */ ++ ++/** DOC: Interrupt management for the V3D engine. ++ * ++ * We have an interrupt status register (V3D_INTCTL) which reports ++ * interrupts, and where writing 1 bits clears those interrupts. ++ * There are also a pair of interrupt registers ++ * (V3D_INTENA/V3D_INTDIS) where writing a 1 to their bits enables or ++ * disables that specific interrupt, and 0s written are ignored ++ * (reading either one returns the set of enabled interrupts). ++ * ++ * When we take a render frame interrupt, we need to wake the ++ * processes waiting for some frame to be done, and get the next frame ++ * submitted ASAP (so the hardware doesn't sit idle when there's work ++ * to do). ++ * ++ * When we take the binner out of memory interrupt, we need to ++ * allocate some new memory and pass it to the binner so that the ++ * current job can make progress. ++ */ ++ ++#include "vc4_drv.h" ++#include "vc4_regs.h" ++ ++#define V3D_DRIVER_IRQS (V3D_INT_OUTOMEM | \ ++ V3D_INT_FRDONE) ++ ++DECLARE_WAIT_QUEUE_HEAD(render_wait); ++ ++static void ++vc4_overflow_mem_work(struct work_struct *work) ++{ ++ struct vc4_dev *vc4 = ++ container_of(work, struct vc4_dev, overflow_mem_work); ++ struct drm_device *dev = vc4->dev; ++ struct vc4_bo *bo; ++ ++ bo = vc4_bo_create(dev, 256 * 1024, true); ++ if (!bo) { ++ DRM_ERROR("Couldn't allocate binner overflow mem\n"); ++ return; ++ } ++ ++ /* If there's a job executing currently, then our previous ++ * overflow allocation is getting used in that job and we need ++ * to queue it to be released when the job is done. But if no ++ * job is executing at all, then we can free the old overflow ++ * object direcctly. ++ * ++ * No lock necessary for this pointer since we're the only ++ * ones that update the pointer, and our workqueue won't ++ * reenter. ++ */ ++ if (vc4->overflow_mem) { ++ struct vc4_exec_info *current_exec; ++ unsigned long irqflags; ++ ++ spin_lock_irqsave(&vc4->job_lock, irqflags); ++ current_exec = vc4_first_job(vc4); ++ if (current_exec) { ++ vc4->overflow_mem->seqno = vc4->finished_seqno + 1; ++ list_add_tail(&vc4->overflow_mem->unref_head, ++ ¤t_exec->unref_list); ++ vc4->overflow_mem = NULL; ++ } ++ spin_unlock_irqrestore(&vc4->job_lock, irqflags); ++ } ++ ++ if (vc4->overflow_mem) ++ drm_gem_object_unreference_unlocked(&vc4->overflow_mem->base.base); ++ vc4->overflow_mem = bo; ++ ++ V3D_WRITE(V3D_BPOA, bo->base.paddr); ++ V3D_WRITE(V3D_BPOS, bo->base.base.size); ++ V3D_WRITE(V3D_INTCTL, V3D_INT_OUTOMEM); ++ V3D_WRITE(V3D_INTENA, V3D_INT_OUTOMEM); ++} ++ ++static void ++vc4_irq_finish_job(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ struct vc4_exec_info *exec = vc4_first_job(vc4); ++ ++ if (!exec) ++ return; ++ ++ vc4->finished_seqno++; ++ list_move_tail(&exec->head, &vc4->job_done_list); ++ vc4_submit_next_job(dev); ++ ++ wake_up_all(&vc4->job_wait_queue); ++ schedule_work(&vc4->job_done_work); ++} ++ ++irqreturn_t ++vc4_irq(int irq, void *arg) ++{ ++ struct drm_device *dev = arg; ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ uint32_t intctl; ++ irqreturn_t status = IRQ_NONE; ++ ++ barrier(); ++ intctl = V3D_READ(V3D_INTCTL); ++ ++ /* Acknowledge the interrupts we're handling here. The render ++ * frame done interrupt will be cleared, while OUTOMEM will ++ * stay high until the underlying cause is cleared. ++ */ ++ V3D_WRITE(V3D_INTCTL, intctl); ++ ++ if (intctl & V3D_INT_OUTOMEM) { ++ /* Disable OUTOMEM until the work is done. */ ++ V3D_WRITE(V3D_INTDIS, V3D_INT_OUTOMEM); ++ schedule_work(&vc4->overflow_mem_work); ++ status = IRQ_HANDLED; ++ } ++ ++ if (intctl & V3D_INT_FRDONE) { ++ spin_lock(&vc4->job_lock); ++ vc4_irq_finish_job(dev); ++ spin_unlock(&vc4->job_lock); ++ status = IRQ_HANDLED; ++ } ++ ++ return status; ++} ++ ++void ++vc4_irq_preinstall(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ init_waitqueue_head(&vc4->job_wait_queue); ++ INIT_WORK(&vc4->overflow_mem_work, vc4_overflow_mem_work); ++ ++ /* Clear any pending interrupts someone might have left around ++ * for us. ++ */ ++ V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS); ++} ++ ++int ++vc4_irq_postinstall(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ /* Enable both the render done and out of memory interrupts. */ ++ V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS); ++ ++ return 0; ++} ++ ++void ++vc4_irq_uninstall(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ /* Disable sending interrupts for our driver's IRQs. */ ++ V3D_WRITE(V3D_INTDIS, V3D_DRIVER_IRQS); ++ ++ /* Clear any pending interrupts we might have left. */ ++ V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS); ++ ++ cancel_work_sync(&vc4->overflow_mem_work); ++} ++ ++/** Reinitializes interrupt registers when a GPU reset is performed. */ ++void vc4_irq_reset(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ unsigned long irqflags; ++ ++ /* Acknowledge any stale IRQs. */ ++ V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS); ++ ++ /* ++ * Turn all our interrupts on. Binner out of memory is the ++ * only one we expect to trigger at this point, since we've ++ * just come from poweron and haven't supplied any overflow ++ * memory yet. ++ */ ++ V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS); ++ ++ spin_lock_irqsave(&vc4->job_lock, irqflags); ++ vc4_irq_finish_job(dev); ++ spin_unlock_irqrestore(&vc4->job_lock, irqflags); ++} +diff --git a/drivers/gpu/drm/vc4/vc4_packet.h b/drivers/gpu/drm/vc4/vc4_packet.h +new file mode 100644 +index 0000000..0f31cc0 +--- /dev/null ++++ b/drivers/gpu/drm/vc4/vc4_packet.h +@@ -0,0 +1,399 @@ ++/* ++ * Copyright © 2014 Broadcom ++ * ++ * 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 (including the next ++ * paragraph) 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 VC4_PACKET_H ++#define VC4_PACKET_H ++ ++#include "vc4_regs.h" /* for VC4_MASK, VC4_GET_FIELD, VC4_SET_FIELD */ ++ ++enum vc4_packet { ++ VC4_PACKET_HALT = 0, ++ VC4_PACKET_NOP = 1, ++ ++ VC4_PACKET_FLUSH = 4, ++ VC4_PACKET_FLUSH_ALL = 5, ++ VC4_PACKET_START_TILE_BINNING = 6, ++ VC4_PACKET_INCREMENT_SEMAPHORE = 7, ++ VC4_PACKET_WAIT_ON_SEMAPHORE = 8, ++ ++ VC4_PACKET_BRANCH = 16, ++ VC4_PACKET_BRANCH_TO_SUB_LIST = 17, ++ ++ VC4_PACKET_STORE_MS_TILE_BUFFER = 24, ++ VC4_PACKET_STORE_MS_TILE_BUFFER_AND_EOF = 25, ++ VC4_PACKET_STORE_FULL_RES_TILE_BUFFER = 26, ++ VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER = 27, ++ VC4_PACKET_STORE_TILE_BUFFER_GENERAL = 28, ++ VC4_PACKET_LOAD_TILE_BUFFER_GENERAL = 29, ++ ++ VC4_PACKET_GL_INDEXED_PRIMITIVE = 32, ++ VC4_PACKET_GL_ARRAY_PRIMITIVE = 33, ++ ++ VC4_PACKET_COMPRESSED_PRIMITIVE = 48, ++ VC4_PACKET_CLIPPED_COMPRESSED_PRIMITIVE = 49, ++ ++ VC4_PACKET_PRIMITIVE_LIST_FORMAT = 56, ++ ++ VC4_PACKET_GL_SHADER_STATE = 64, ++ VC4_PACKET_NV_SHADER_STATE = 65, ++ VC4_PACKET_VG_SHADER_STATE = 66, ++ ++ VC4_PACKET_CONFIGURATION_BITS = 96, ++ VC4_PACKET_FLAT_SHADE_FLAGS = 97, ++ VC4_PACKET_POINT_SIZE = 98, ++ VC4_PACKET_LINE_WIDTH = 99, ++ VC4_PACKET_RHT_X_BOUNDARY = 100, ++ VC4_PACKET_DEPTH_OFFSET = 101, ++ VC4_PACKET_CLIP_WINDOW = 102, ++ VC4_PACKET_VIEWPORT_OFFSET = 103, ++ VC4_PACKET_Z_CLIPPING = 104, ++ VC4_PACKET_CLIPPER_XY_SCALING = 105, ++ VC4_PACKET_CLIPPER_Z_SCALING = 106, ++ ++ VC4_PACKET_TILE_BINNING_MODE_CONFIG = 112, ++ VC4_PACKET_TILE_RENDERING_MODE_CONFIG = 113, ++ VC4_PACKET_CLEAR_COLORS = 114, ++ VC4_PACKET_TILE_COORDINATES = 115, ++ ++ /* Not an actual hardware packet -- this is what we use to put ++ * references to GEM bos in the command stream, since we need the u32 ++ * int the actual address packet in order to store the offset from the ++ * start of the BO. ++ */ ++ VC4_PACKET_GEM_HANDLES = 254, ++} __attribute__ ((__packed__)); ++ ++#define VC4_PACKET_HALT_SIZE 1 ++#define VC4_PACKET_NOP_SIZE 1 ++#define VC4_PACKET_FLUSH_SIZE 1 ++#define VC4_PACKET_FLUSH_ALL_SIZE 1 ++#define VC4_PACKET_START_TILE_BINNING_SIZE 1 ++#define VC4_PACKET_INCREMENT_SEMAPHORE_SIZE 1 ++#define VC4_PACKET_WAIT_ON_SEMAPHORE_SIZE 1 ++#define VC4_PACKET_BRANCH_SIZE 5 ++#define VC4_PACKET_BRANCH_TO_SUB_LIST_SIZE 5 ++#define VC4_PACKET_STORE_MS_TILE_BUFFER_SIZE 1 ++#define VC4_PACKET_STORE_MS_TILE_BUFFER_AND_EOF_SIZE 1 ++#define VC4_PACKET_STORE_FULL_RES_TILE_BUFFER_SIZE 5 ++#define VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER_SIZE 5 ++#define VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE 7 ++#define VC4_PACKET_LOAD_TILE_BUFFER_GENERAL_SIZE 7 ++#define VC4_PACKET_GL_INDEXED_PRIMITIVE_SIZE 14 ++#define VC4_PACKET_GL_ARRAY_PRIMITIVE_SIZE 10 ++#define VC4_PACKET_COMPRESSED_PRIMITIVE_SIZE 1 ++#define VC4_PACKET_CLIPPED_COMPRESSED_PRIMITIVE_SIZE 1 ++#define VC4_PACKET_PRIMITIVE_LIST_FORMAT_SIZE 2 ++#define VC4_PACKET_GL_SHADER_STATE_SIZE 5 ++#define VC4_PACKET_NV_SHADER_STATE_SIZE 5 ++#define VC4_PACKET_VG_SHADER_STATE_SIZE 5 ++#define VC4_PACKET_CONFIGURATION_BITS_SIZE 4 ++#define VC4_PACKET_FLAT_SHADE_FLAGS_SIZE 5 ++#define VC4_PACKET_POINT_SIZE_SIZE 5 ++#define VC4_PACKET_LINE_WIDTH_SIZE 5 ++#define VC4_PACKET_RHT_X_BOUNDARY_SIZE 3 ++#define VC4_PACKET_DEPTH_OFFSET_SIZE 5 ++#define VC4_PACKET_CLIP_WINDOW_SIZE 9 ++#define VC4_PACKET_VIEWPORT_OFFSET_SIZE 5 ++#define VC4_PACKET_Z_CLIPPING_SIZE 9 ++#define VC4_PACKET_CLIPPER_XY_SCALING_SIZE 9 ++#define VC4_PACKET_CLIPPER_Z_SCALING_SIZE 9 ++#define VC4_PACKET_TILE_BINNING_MODE_CONFIG_SIZE 16 ++#define VC4_PACKET_TILE_RENDERING_MODE_CONFIG_SIZE 11 ++#define VC4_PACKET_CLEAR_COLORS_SIZE 14 ++#define VC4_PACKET_TILE_COORDINATES_SIZE 3 ++#define VC4_PACKET_GEM_HANDLES_SIZE 9 ++ ++/* Number of multisamples supported. */ ++#define VC4_MAX_SAMPLES 4 ++/* Size of a full resolution color or Z tile buffer load/store. */ ++#define VC4_TILE_BUFFER_SIZE (64 * 64 * 4) ++ ++/** @{ ++ * Bits used by packets like VC4_PACKET_STORE_TILE_BUFFER_GENERAL and ++ * VC4_PACKET_TILE_RENDERING_MODE_CONFIG. ++*/ ++#define VC4_TILING_FORMAT_LINEAR 0 ++#define VC4_TILING_FORMAT_T 1 ++#define VC4_TILING_FORMAT_LT 2 ++/** @} */ ++ ++/** @{ ++ * ++ * low bits of VC4_PACKET_STORE_FULL_RES_TILE_BUFFER and ++ * VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER. ++ */ ++#define VC4_LOADSTORE_FULL_RES_EOF BIT(3) ++#define VC4_LOADSTORE_FULL_RES_DISABLE_CLEAR_ALL BIT(2) ++#define VC4_LOADSTORE_FULL_RES_DISABLE_ZS BIT(1) ++#define VC4_LOADSTORE_FULL_RES_DISABLE_COLOR BIT(0) ++ ++/** @{ ++ * ++ * low bits of VC4_PACKET_STORE_FULL_RES_TILE_BUFFER and ++ * VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER. ++ */ ++#define VC4_LOADSTORE_FULL_RES_EOF BIT(3) ++#define VC4_LOADSTORE_FULL_RES_DISABLE_CLEAR_ALL BIT(2) ++#define VC4_LOADSTORE_FULL_RES_DISABLE_ZS BIT(1) ++#define VC4_LOADSTORE_FULL_RES_DISABLE_COLOR BIT(0) ++ ++/** @{ ++ * ++ * byte 2 of VC4_PACKET_STORE_TILE_BUFFER_GENERAL and ++ * VC4_PACKET_LOAD_TILE_BUFFER_GENERAL (low bits of the address) ++ */ ++ ++#define VC4_LOADSTORE_TILE_BUFFER_EOF BIT(3) ++#define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_VG_MASK BIT(2) ++#define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_ZS BIT(1) ++#define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_COLOR BIT(0) ++ ++/** @} */ ++ ++/** @{ ++ * ++ * byte 0-1 of VC4_PACKET_STORE_TILE_BUFFER_GENERAL and ++ * VC4_PACKET_LOAD_TILE_BUFFER_GENERAL ++ */ ++#define VC4_STORE_TILE_BUFFER_DISABLE_VG_MASK_CLEAR BIT(15) ++#define VC4_STORE_TILE_BUFFER_DISABLE_ZS_CLEAR BIT(14) ++#define VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR BIT(13) ++#define VC4_STORE_TILE_BUFFER_DISABLE_SWAP BIT(12) ++ ++#define VC4_LOADSTORE_TILE_BUFFER_FORMAT_MASK VC4_MASK(9, 8) ++#define VC4_LOADSTORE_TILE_BUFFER_FORMAT_SHIFT 8 ++#define VC4_LOADSTORE_TILE_BUFFER_RGBA8888 0 ++#define VC4_LOADSTORE_TILE_BUFFER_BGR565_DITHER 1 ++#define VC4_LOADSTORE_TILE_BUFFER_BGR565 2 ++/** @} */ ++ ++/** @{ ++ * ++ * byte 0 of VC4_PACKET_STORE_TILE_BUFFER_GENERAL and ++ * VC4_PACKET_LOAD_TILE_BUFFER_GENERAL ++ */ ++#define VC4_STORE_TILE_BUFFER_MODE_MASK VC4_MASK(7, 6) ++#define VC4_STORE_TILE_BUFFER_MODE_SHIFT 6 ++#define VC4_STORE_TILE_BUFFER_MODE_SAMPLE0 (0 << 6) ++#define VC4_STORE_TILE_BUFFER_MODE_DECIMATE_X4 (1 << 6) ++#define VC4_STORE_TILE_BUFFER_MODE_DECIMATE_X16 (2 << 6) ++ ++/** The values of the field are VC4_TILING_FORMAT_* */ ++#define VC4_LOADSTORE_TILE_BUFFER_TILING_MASK VC4_MASK(5, 4) ++#define VC4_LOADSTORE_TILE_BUFFER_TILING_SHIFT 4 ++ ++#define VC4_LOADSTORE_TILE_BUFFER_BUFFER_MASK VC4_MASK(2, 0) ++#define VC4_LOADSTORE_TILE_BUFFER_BUFFER_SHIFT 0 ++#define VC4_LOADSTORE_TILE_BUFFER_NONE 0 ++#define VC4_LOADSTORE_TILE_BUFFER_COLOR 1 ++#define VC4_LOADSTORE_TILE_BUFFER_ZS 2 ++#define VC4_LOADSTORE_TILE_BUFFER_Z 3 ++#define VC4_LOADSTORE_TILE_BUFFER_VG_MASK 4 ++#define VC4_LOADSTORE_TILE_BUFFER_FULL 5 ++/** @} */ ++ ++#define VC4_INDEX_BUFFER_U8 (0 << 4) ++#define VC4_INDEX_BUFFER_U16 (1 << 4) ++ ++/* This flag is only present in NV shader state. */ ++#define VC4_SHADER_FLAG_SHADED_CLIP_COORDS BIT(3) ++#define VC4_SHADER_FLAG_ENABLE_CLIPPING BIT(2) ++#define VC4_SHADER_FLAG_VS_POINT_SIZE BIT(1) ++#define VC4_SHADER_FLAG_FS_SINGLE_THREAD BIT(0) ++ ++/** @{ byte 2 of config bits. */ ++#define VC4_CONFIG_BITS_EARLY_Z_UPDATE BIT(1) ++#define VC4_CONFIG_BITS_EARLY_Z BIT(0) ++/** @} */ ++ ++/** @{ byte 1 of config bits. */ ++#define VC4_CONFIG_BITS_Z_UPDATE BIT(7) ++/** same values in this 3-bit field as PIPE_FUNC_* */ ++#define VC4_CONFIG_BITS_DEPTH_FUNC_SHIFT 4 ++#define VC4_CONFIG_BITS_COVERAGE_READ_LEAVE BIT(3) ++ ++#define VC4_CONFIG_BITS_COVERAGE_UPDATE_NONZERO (0 << 1) ++#define VC4_CONFIG_BITS_COVERAGE_UPDATE_ODD (1 << 1) ++#define VC4_CONFIG_BITS_COVERAGE_UPDATE_OR (2 << 1) ++#define VC4_CONFIG_BITS_COVERAGE_UPDATE_ZERO (3 << 1) ++ ++#define VC4_CONFIG_BITS_COVERAGE_PIPE_SELECT BIT(0) ++/** @} */ ++ ++/** @{ byte 0 of config bits. */ ++#define VC4_CONFIG_BITS_RASTERIZER_OVERSAMPLE_NONE (0 << 6) ++#define VC4_CONFIG_BITS_RASTERIZER_OVERSAMPLE_4X (1 << 6) ++#define VC4_CONFIG_BITS_RASTERIZER_OVERSAMPLE_16X (2 << 6) ++ ++#define VC4_CONFIG_BITS_AA_POINTS_AND_LINES BIT(4) ++#define VC4_CONFIG_BITS_ENABLE_DEPTH_OFFSET BIT(3) ++#define VC4_CONFIG_BITS_CW_PRIMITIVES BIT(2) ++#define VC4_CONFIG_BITS_ENABLE_PRIM_BACK BIT(1) ++#define VC4_CONFIG_BITS_ENABLE_PRIM_FRONT BIT(0) ++/** @} */ ++ ++/** @{ bits in the last u8 of VC4_PACKET_TILE_BINNING_MODE_CONFIG */ ++#define VC4_BIN_CONFIG_DB_NON_MS BIT(7) ++ ++#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_MASK VC4_MASK(6, 5) ++#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_SHIFT 5 ++#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_32 0 ++#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_64 1 ++#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_128 2 ++#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_256 3 ++ ++#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_MASK VC4_MASK(4, 3) ++#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_SHIFT 3 ++#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_32 0 ++#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_64 1 ++#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_128 2 ++#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_256 3 ++ ++#define VC4_BIN_CONFIG_AUTO_INIT_TSDA BIT(2) ++#define VC4_BIN_CONFIG_TILE_BUFFER_64BIT BIT(1) ++#define VC4_BIN_CONFIG_MS_MODE_4X BIT(0) ++/** @} */ ++ ++/** @{ bits in the last u16 of VC4_PACKET_TILE_RENDERING_MODE_CONFIG */ ++#define VC4_RENDER_CONFIG_DB_NON_MS BIT(12) ++#define VC4_RENDER_CONFIG_EARLY_Z_COVERAGE_DISABLE BIT(11) ++#define VC4_RENDER_CONFIG_EARLY_Z_DIRECTION_G BIT(10) ++#define VC4_RENDER_CONFIG_COVERAGE_MODE BIT(9) ++#define VC4_RENDER_CONFIG_ENABLE_VG_MASK BIT(8) ++ ++/** The values of the field are VC4_TILING_FORMAT_* */ ++#define VC4_RENDER_CONFIG_MEMORY_FORMAT_MASK VC4_MASK(7, 6) ++#define VC4_RENDER_CONFIG_MEMORY_FORMAT_SHIFT 6 ++ ++#define VC4_RENDER_CONFIG_DECIMATE_MODE_1X (0 << 4) ++#define VC4_RENDER_CONFIG_DECIMATE_MODE_4X (1 << 4) ++#define VC4_RENDER_CONFIG_DECIMATE_MODE_16X (2 << 4) ++ ++#define VC4_RENDER_CONFIG_FORMAT_MASK VC4_MASK(3, 2) ++#define VC4_RENDER_CONFIG_FORMAT_SHIFT 2 ++#define VC4_RENDER_CONFIG_FORMAT_BGR565_DITHERED 0 ++#define VC4_RENDER_CONFIG_FORMAT_RGBA8888 1 ++#define VC4_RENDER_CONFIG_FORMAT_BGR565 2 ++ ++#define VC4_RENDER_CONFIG_TILE_BUFFER_64BIT BIT(1) ++#define VC4_RENDER_CONFIG_MS_MODE_4X BIT(0) ++ ++#define VC4_PRIMITIVE_LIST_FORMAT_16_INDEX (1 << 4) ++#define VC4_PRIMITIVE_LIST_FORMAT_32_XY (3 << 4) ++#define VC4_PRIMITIVE_LIST_FORMAT_TYPE_POINTS (0 << 0) ++#define VC4_PRIMITIVE_LIST_FORMAT_TYPE_LINES (1 << 0) ++#define VC4_PRIMITIVE_LIST_FORMAT_TYPE_TRIANGLES (2 << 0) ++#define VC4_PRIMITIVE_LIST_FORMAT_TYPE_RHT (3 << 0) ++ ++enum vc4_texture_data_type { ++ VC4_TEXTURE_TYPE_RGBA8888 = 0, ++ VC4_TEXTURE_TYPE_RGBX8888 = 1, ++ VC4_TEXTURE_TYPE_RGBA4444 = 2, ++ VC4_TEXTURE_TYPE_RGBA5551 = 3, ++ VC4_TEXTURE_TYPE_RGB565 = 4, ++ VC4_TEXTURE_TYPE_LUMINANCE = 5, ++ VC4_TEXTURE_TYPE_ALPHA = 6, ++ VC4_TEXTURE_TYPE_LUMALPHA = 7, ++ VC4_TEXTURE_TYPE_ETC1 = 8, ++ VC4_TEXTURE_TYPE_S16F = 9, ++ VC4_TEXTURE_TYPE_S8 = 10, ++ VC4_TEXTURE_TYPE_S16 = 11, ++ VC4_TEXTURE_TYPE_BW1 = 12, ++ VC4_TEXTURE_TYPE_A4 = 13, ++ VC4_TEXTURE_TYPE_A1 = 14, ++ VC4_TEXTURE_TYPE_RGBA64 = 15, ++ VC4_TEXTURE_TYPE_RGBA32R = 16, ++ VC4_TEXTURE_TYPE_YUV422R = 17, ++}; ++ ++#define VC4_TEX_P0_OFFSET_MASK VC4_MASK(31, 12) ++#define VC4_TEX_P0_OFFSET_SHIFT 12 ++#define VC4_TEX_P0_CSWIZ_MASK VC4_MASK(11, 10) ++#define VC4_TEX_P0_CSWIZ_SHIFT 10 ++#define VC4_TEX_P0_CMMODE_MASK VC4_MASK(9, 9) ++#define VC4_TEX_P0_CMMODE_SHIFT 9 ++#define VC4_TEX_P0_FLIPY_MASK VC4_MASK(8, 8) ++#define VC4_TEX_P0_FLIPY_SHIFT 8 ++#define VC4_TEX_P0_TYPE_MASK VC4_MASK(7, 4) ++#define VC4_TEX_P0_TYPE_SHIFT 4 ++#define VC4_TEX_P0_MIPLVLS_MASK VC4_MASK(3, 0) ++#define VC4_TEX_P0_MIPLVLS_SHIFT 0 ++ ++#define VC4_TEX_P1_TYPE4_MASK VC4_MASK(31, 31) ++#define VC4_TEX_P1_TYPE4_SHIFT 31 ++#define VC4_TEX_P1_HEIGHT_MASK VC4_MASK(30, 20) ++#define VC4_TEX_P1_HEIGHT_SHIFT 20 ++#define VC4_TEX_P1_ETCFLIP_MASK VC4_MASK(19, 19) ++#define VC4_TEX_P1_ETCFLIP_SHIFT 19 ++#define VC4_TEX_P1_WIDTH_MASK VC4_MASK(18, 8) ++#define VC4_TEX_P1_WIDTH_SHIFT 8 ++ ++#define VC4_TEX_P1_MAGFILT_MASK VC4_MASK(7, 7) ++#define VC4_TEX_P1_MAGFILT_SHIFT 7 ++# define VC4_TEX_P1_MAGFILT_LINEAR 0 ++# define VC4_TEX_P1_MAGFILT_NEAREST 1 ++ ++#define VC4_TEX_P1_MINFILT_MASK VC4_MASK(6, 4) ++#define VC4_TEX_P1_MINFILT_SHIFT 4 ++# define VC4_TEX_P1_MINFILT_LINEAR 0 ++# define VC4_TEX_P1_MINFILT_NEAREST 1 ++# define VC4_TEX_P1_MINFILT_NEAR_MIP_NEAR 2 ++# define VC4_TEX_P1_MINFILT_NEAR_MIP_LIN 3 ++# define VC4_TEX_P1_MINFILT_LIN_MIP_NEAR 4 ++# define VC4_TEX_P1_MINFILT_LIN_MIP_LIN 5 ++ ++#define VC4_TEX_P1_WRAP_T_MASK VC4_MASK(3, 2) ++#define VC4_TEX_P1_WRAP_T_SHIFT 2 ++#define VC4_TEX_P1_WRAP_S_MASK VC4_MASK(1, 0) ++#define VC4_TEX_P1_WRAP_S_SHIFT 0 ++# define VC4_TEX_P1_WRAP_REPEAT 0 ++# define VC4_TEX_P1_WRAP_CLAMP 1 ++# define VC4_TEX_P1_WRAP_MIRROR 2 ++# define VC4_TEX_P1_WRAP_BORDER 3 ++ ++#define VC4_TEX_P2_PTYPE_MASK VC4_MASK(31, 30) ++#define VC4_TEX_P2_PTYPE_SHIFT 30 ++# define VC4_TEX_P2_PTYPE_IGNORED 0 ++# define VC4_TEX_P2_PTYPE_CUBE_MAP_STRIDE 1 ++# define VC4_TEX_P2_PTYPE_CHILD_IMAGE_DIMENSIONS 2 ++# define VC4_TEX_P2_PTYPE_CHILD_IMAGE_OFFSETS 3 ++ ++/* VC4_TEX_P2_PTYPE_CUBE_MAP_STRIDE bits */ ++#define VC4_TEX_P2_CMST_MASK VC4_MASK(29, 12) ++#define VC4_TEX_P2_CMST_SHIFT 12 ++#define VC4_TEX_P2_BSLOD_MASK VC4_MASK(0, 0) ++#define VC4_TEX_P2_BSLOD_SHIFT 0 ++ ++/* VC4_TEX_P2_PTYPE_CHILD_IMAGE_DIMENSIONS */ ++#define VC4_TEX_P2_CHEIGHT_MASK VC4_MASK(22, 12) ++#define VC4_TEX_P2_CHEIGHT_SHIFT 12 ++#define VC4_TEX_P2_CWIDTH_MASK VC4_MASK(10, 0) ++#define VC4_TEX_P2_CWIDTH_SHIFT 0 ++ ++/* VC4_TEX_P2_PTYPE_CHILD_IMAGE_OFFSETS */ ++#define VC4_TEX_P2_CYOFF_MASK VC4_MASK(22, 12) ++#define VC4_TEX_P2_CYOFF_SHIFT 12 ++#define VC4_TEX_P2_CXOFF_MASK VC4_MASK(10, 0) ++#define VC4_TEX_P2_CXOFF_SHIFT 0 ++ ++#endif /* VC4_PACKET_H */ +diff --git a/drivers/gpu/drm/vc4/vc4_render_cl.c b/drivers/gpu/drm/vc4/vc4_render_cl.c +new file mode 100644 +index 0000000..8a2a312 +--- /dev/null ++++ b/drivers/gpu/drm/vc4/vc4_render_cl.c +@@ -0,0 +1,634 @@ ++/* ++ * Copyright © 2014-2015 Broadcom ++ * ++ * 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 (including the next ++ * paragraph) 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. ++ */ ++ ++/** ++ * DOC: Render command list generation ++ * ++ * In the VC4 driver, render command list generation is performed by the ++ * kernel instead of userspace. We do this because validating a ++ * user-submitted command list is hard to get right and has high CPU overhead, ++ * while the number of valid configurations for render command lists is ++ * actually fairly low. ++ */ ++ ++#include "uapi/drm/vc4_drm.h" ++#include "vc4_drv.h" ++#include "vc4_packet.h" ++ ++struct vc4_rcl_setup { ++ struct drm_gem_cma_object *color_read; ++ struct drm_gem_cma_object *color_write; ++ struct drm_gem_cma_object *zs_read; ++ struct drm_gem_cma_object *zs_write; ++ struct drm_gem_cma_object *msaa_color_write; ++ struct drm_gem_cma_object *msaa_zs_write; ++ ++ struct drm_gem_cma_object *rcl; ++ u32 next_offset; ++}; ++ ++static inline void rcl_u8(struct vc4_rcl_setup *setup, u8 val) ++{ ++ *(u8 *)(setup->rcl->vaddr + setup->next_offset) = val; ++ setup->next_offset += 1; ++} ++ ++static inline void rcl_u16(struct vc4_rcl_setup *setup, u16 val) ++{ ++ *(u16 *)(setup->rcl->vaddr + setup->next_offset) = val; ++ setup->next_offset += 2; ++} ++ ++static inline void rcl_u32(struct vc4_rcl_setup *setup, u32 val) ++{ ++ *(u32 *)(setup->rcl->vaddr + setup->next_offset) = val; ++ setup->next_offset += 4; ++} ++ ++/* ++ * Emits a no-op STORE_TILE_BUFFER_GENERAL. ++ * ++ * If we emit a PACKET_TILE_COORDINATES, it must be followed by a store of ++ * some sort before another load is triggered. ++ */ ++static void vc4_store_before_load(struct vc4_rcl_setup *setup) ++{ ++ rcl_u8(setup, VC4_PACKET_STORE_TILE_BUFFER_GENERAL); ++ rcl_u16(setup, ++ VC4_SET_FIELD(VC4_LOADSTORE_TILE_BUFFER_NONE, ++ VC4_LOADSTORE_TILE_BUFFER_BUFFER) | ++ VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR | ++ VC4_STORE_TILE_BUFFER_DISABLE_ZS_CLEAR | ++ VC4_STORE_TILE_BUFFER_DISABLE_VG_MASK_CLEAR); ++ rcl_u32(setup, 0); /* no address, since we're in None mode */ ++} ++ ++/* ++ * Calculates the physical address of the start of a tile in a RCL surface. ++ * ++ * Unlike the other load/store packets, ++ * VC4_PACKET_LOAD/STORE_FULL_RES_TILE_BUFFER don't look at the tile ++ * coordinates packet, and instead just store to the address given. ++ */ ++static uint32_t vc4_full_res_offset(struct vc4_exec_info *exec, ++ struct drm_gem_cma_object *bo, ++ struct drm_vc4_submit_rcl_surface *surf, ++ uint8_t x, uint8_t y) ++{ ++ return bo->paddr + surf->offset + VC4_TILE_BUFFER_SIZE * ++ (DIV_ROUND_UP(exec->args->width, 32) * y + x); ++} ++ ++/* ++ * Emits a PACKET_TILE_COORDINATES if one isn't already pending. ++ * ++ * The tile coordinates packet triggers a pending load if there is one, are ++ * used for clipping during rendering, and determine where loads/stores happen ++ * relative to their base address. ++ */ ++static void vc4_tile_coordinates(struct vc4_rcl_setup *setup, ++ uint32_t x, uint32_t y) ++{ ++ rcl_u8(setup, VC4_PACKET_TILE_COORDINATES); ++ rcl_u8(setup, x); ++ rcl_u8(setup, y); ++} ++ ++static void emit_tile(struct vc4_exec_info *exec, ++ struct vc4_rcl_setup *setup, ++ uint8_t x, uint8_t y, bool first, bool last) ++{ ++ struct drm_vc4_submit_cl *args = exec->args; ++ bool has_bin = args->bin_cl_size != 0; ++ ++ /* Note that the load doesn't actually occur until the ++ * tile coords packet is processed, and only one load ++ * may be outstanding at a time. ++ */ ++ if (setup->color_read) { ++ if (args->color_read.flags & ++ VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { ++ rcl_u8(setup, VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER); ++ rcl_u32(setup, ++ vc4_full_res_offset(exec, setup->color_read, ++ &args->color_read, x, y) | ++ VC4_LOADSTORE_FULL_RES_DISABLE_ZS); ++ } else { ++ rcl_u8(setup, VC4_PACKET_LOAD_TILE_BUFFER_GENERAL); ++ rcl_u16(setup, args->color_read.bits); ++ rcl_u32(setup, setup->color_read->paddr + ++ args->color_read.offset); ++ } ++ } ++ ++ if (setup->zs_read) { ++ if (args->zs_read.flags & ++ VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { ++ rcl_u8(setup, VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER); ++ rcl_u32(setup, ++ vc4_full_res_offset(exec, setup->zs_read, ++ &args->zs_read, x, y) | ++ VC4_LOADSTORE_FULL_RES_DISABLE_COLOR); ++ } else { ++ if (setup->color_read) { ++ /* Exec previous load. */ ++ vc4_tile_coordinates(setup, x, y); ++ vc4_store_before_load(setup); ++ } ++ ++ rcl_u8(setup, VC4_PACKET_LOAD_TILE_BUFFER_GENERAL); ++ rcl_u16(setup, args->zs_read.bits); ++ rcl_u32(setup, setup->zs_read->paddr + ++ args->zs_read.offset); ++ } ++ } ++ ++ /* Clipping depends on tile coordinates having been ++ * emitted, so we always need one here. ++ */ ++ vc4_tile_coordinates(setup, x, y); ++ ++ /* Wait for the binner before jumping to the first ++ * tile's lists. ++ */ ++ if (first && has_bin) ++ rcl_u8(setup, VC4_PACKET_WAIT_ON_SEMAPHORE); ++ ++ if (has_bin) { ++ rcl_u8(setup, VC4_PACKET_BRANCH_TO_SUB_LIST); ++ rcl_u32(setup, (exec->tile_bo->paddr + ++ exec->tile_alloc_offset + ++ (y * exec->bin_tiles_x + x) * 32)); ++ } ++ ++ if (setup->msaa_color_write) { ++ bool last_tile_write = (!setup->msaa_zs_write && ++ !setup->zs_write && ++ !setup->color_write); ++ uint32_t bits = VC4_LOADSTORE_FULL_RES_DISABLE_ZS; ++ ++ if (!last_tile_write) ++ bits |= VC4_LOADSTORE_FULL_RES_DISABLE_CLEAR_ALL; ++ else if (last) ++ bits |= VC4_LOADSTORE_FULL_RES_EOF; ++ rcl_u8(setup, VC4_PACKET_STORE_FULL_RES_TILE_BUFFER); ++ rcl_u32(setup, ++ vc4_full_res_offset(exec, setup->msaa_color_write, ++ &args->msaa_color_write, x, y) | ++ bits); ++ } ++ ++ if (setup->msaa_zs_write) { ++ bool last_tile_write = (!setup->zs_write && ++ !setup->color_write); ++ uint32_t bits = VC4_LOADSTORE_FULL_RES_DISABLE_COLOR; ++ ++ if (setup->msaa_color_write) ++ vc4_tile_coordinates(setup, x, y); ++ if (!last_tile_write) ++ bits |= VC4_LOADSTORE_FULL_RES_DISABLE_CLEAR_ALL; ++ else if (last) ++ bits |= VC4_LOADSTORE_FULL_RES_EOF; ++ rcl_u8(setup, VC4_PACKET_STORE_FULL_RES_TILE_BUFFER); ++ rcl_u32(setup, ++ vc4_full_res_offset(exec, setup->msaa_zs_write, ++ &args->msaa_zs_write, x, y) | ++ bits); ++ } ++ ++ if (setup->zs_write) { ++ bool last_tile_write = !setup->color_write; ++ ++ if (setup->msaa_color_write || setup->msaa_zs_write) ++ vc4_tile_coordinates(setup, x, y); ++ ++ rcl_u8(setup, VC4_PACKET_STORE_TILE_BUFFER_GENERAL); ++ rcl_u16(setup, args->zs_write.bits | ++ (last_tile_write ? ++ 0 : VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR)); ++ rcl_u32(setup, ++ (setup->zs_write->paddr + args->zs_write.offset) | ++ ((last && last_tile_write) ? ++ VC4_LOADSTORE_TILE_BUFFER_EOF : 0)); ++ } ++ ++ if (setup->color_write) { ++ if (setup->msaa_color_write || setup->msaa_zs_write || ++ setup->zs_write) { ++ vc4_tile_coordinates(setup, x, y); ++ } ++ ++ if (last) ++ rcl_u8(setup, VC4_PACKET_STORE_MS_TILE_BUFFER_AND_EOF); ++ else ++ rcl_u8(setup, VC4_PACKET_STORE_MS_TILE_BUFFER); ++ } ++} ++ ++static int vc4_create_rcl_bo(struct drm_device *dev, struct vc4_exec_info *exec, ++ struct vc4_rcl_setup *setup) ++{ ++ struct drm_vc4_submit_cl *args = exec->args; ++ bool has_bin = args->bin_cl_size != 0; ++ uint8_t min_x_tile = args->min_x_tile; ++ uint8_t min_y_tile = args->min_y_tile; ++ uint8_t max_x_tile = args->max_x_tile; ++ uint8_t max_y_tile = args->max_y_tile; ++ uint8_t xtiles = max_x_tile - min_x_tile + 1; ++ uint8_t ytiles = max_y_tile - min_y_tile + 1; ++ uint8_t x, y; ++ uint32_t size, loop_body_size; ++ ++ size = VC4_PACKET_TILE_RENDERING_MODE_CONFIG_SIZE; ++ loop_body_size = VC4_PACKET_TILE_COORDINATES_SIZE; ++ ++ if (args->flags & VC4_SUBMIT_CL_USE_CLEAR_COLOR) { ++ size += VC4_PACKET_CLEAR_COLORS_SIZE + ++ VC4_PACKET_TILE_COORDINATES_SIZE + ++ VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE; ++ } ++ ++ if (setup->color_read) { ++ if (args->color_read.flags & ++ VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { ++ loop_body_size += VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER_SIZE; ++ } else { ++ loop_body_size += VC4_PACKET_LOAD_TILE_BUFFER_GENERAL_SIZE; ++ } ++ } ++ if (setup->zs_read) { ++ if (args->zs_read.flags & ++ VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { ++ loop_body_size += VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER_SIZE; ++ } else { ++ if (setup->color_read && ++ !(args->color_read.flags & ++ VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES)) { ++ loop_body_size += VC4_PACKET_TILE_COORDINATES_SIZE; ++ loop_body_size += VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE; ++ } ++ loop_body_size += VC4_PACKET_LOAD_TILE_BUFFER_GENERAL_SIZE; ++ } ++ } ++ ++ if (has_bin) { ++ size += VC4_PACKET_WAIT_ON_SEMAPHORE_SIZE; ++ loop_body_size += VC4_PACKET_BRANCH_TO_SUB_LIST_SIZE; ++ } ++ ++ if (setup->msaa_color_write) ++ loop_body_size += VC4_PACKET_STORE_FULL_RES_TILE_BUFFER_SIZE; ++ if (setup->msaa_zs_write) ++ loop_body_size += VC4_PACKET_STORE_FULL_RES_TILE_BUFFER_SIZE; ++ ++ if (setup->zs_write) ++ loop_body_size += VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE; ++ if (setup->color_write) ++ loop_body_size += VC4_PACKET_STORE_MS_TILE_BUFFER_SIZE; ++ ++ /* We need a VC4_PACKET_TILE_COORDINATES in between each store. */ ++ loop_body_size += VC4_PACKET_TILE_COORDINATES_SIZE * ++ ((setup->msaa_color_write != NULL) + ++ (setup->msaa_zs_write != NULL) + ++ (setup->color_write != NULL) + ++ (setup->zs_write != NULL) - 1); ++ ++ size += xtiles * ytiles * loop_body_size; ++ ++ setup->rcl = &vc4_bo_create(dev, size, true)->base; ++ if (!setup->rcl) ++ return -ENOMEM; ++ list_add_tail(&to_vc4_bo(&setup->rcl->base)->unref_head, ++ &exec->unref_list); ++ ++ rcl_u8(setup, VC4_PACKET_TILE_RENDERING_MODE_CONFIG); ++ rcl_u32(setup, ++ (setup->color_write ? (setup->color_write->paddr + ++ args->color_write.offset) : ++ 0)); ++ rcl_u16(setup, args->width); ++ rcl_u16(setup, args->height); ++ rcl_u16(setup, args->color_write.bits); ++ ++ /* The tile buffer gets cleared when the previous tile is stored. If ++ * the clear values changed between frames, then the tile buffer has ++ * stale clear values in it, so we have to do a store in None mode (no ++ * writes) so that we trigger the tile buffer clear. ++ */ ++ if (args->flags & VC4_SUBMIT_CL_USE_CLEAR_COLOR) { ++ rcl_u8(setup, VC4_PACKET_CLEAR_COLORS); ++ rcl_u32(setup, args->clear_color[0]); ++ rcl_u32(setup, args->clear_color[1]); ++ rcl_u32(setup, args->clear_z); ++ rcl_u8(setup, args->clear_s); ++ ++ vc4_tile_coordinates(setup, 0, 0); ++ ++ rcl_u8(setup, VC4_PACKET_STORE_TILE_BUFFER_GENERAL); ++ rcl_u16(setup, VC4_LOADSTORE_TILE_BUFFER_NONE); ++ rcl_u32(setup, 0); /* no address, since we're in None mode */ ++ } ++ ++ for (y = min_y_tile; y <= max_y_tile; y++) { ++ for (x = min_x_tile; x <= max_x_tile; x++) { ++ bool first = (x == min_x_tile && y == min_y_tile); ++ bool last = (x == max_x_tile && y == max_y_tile); ++ ++ emit_tile(exec, setup, x, y, first, last); ++ } ++ } ++ ++ BUG_ON(setup->next_offset != size); ++ exec->ct1ca = setup->rcl->paddr; ++ exec->ct1ea = setup->rcl->paddr + setup->next_offset; ++ ++ return 0; ++} ++ ++static int vc4_full_res_bounds_check(struct vc4_exec_info *exec, ++ struct drm_gem_cma_object *obj, ++ struct drm_vc4_submit_rcl_surface *surf) ++{ ++ struct drm_vc4_submit_cl *args = exec->args; ++ u32 render_tiles_stride = DIV_ROUND_UP(exec->args->width, 32); ++ ++ if (surf->offset > obj->base.size) { ++ DRM_ERROR("surface offset %d > BO size %zd\n", ++ surf->offset, obj->base.size); ++ return -EINVAL; ++ } ++ ++ if ((obj->base.size - surf->offset) / VC4_TILE_BUFFER_SIZE < ++ render_tiles_stride * args->max_y_tile + args->max_x_tile) { ++ DRM_ERROR("MSAA tile %d, %d out of bounds " ++ "(bo size %zd, offset %d).\n", ++ args->max_x_tile, args->max_y_tile, ++ obj->base.size, ++ surf->offset); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int vc4_rcl_msaa_surface_setup(struct vc4_exec_info *exec, ++ struct drm_gem_cma_object **obj, ++ struct drm_vc4_submit_rcl_surface *surf) ++{ ++ if (surf->flags != 0 || surf->bits != 0) { ++ DRM_ERROR("MSAA surface had nonzero flags/bits\n"); ++ return -EINVAL; ++ } ++ ++ if (surf->hindex == ~0) ++ return 0; ++ ++ *obj = vc4_use_bo(exec, surf->hindex); ++ if (!*obj) ++ return -EINVAL; ++ ++ if (surf->offset & 0xf) { ++ DRM_ERROR("MSAA write must be 16b aligned.\n"); ++ return -EINVAL; ++ } ++ ++ return vc4_full_res_bounds_check(exec, *obj, surf); ++} ++ ++static int vc4_rcl_surface_setup(struct vc4_exec_info *exec, ++ struct drm_gem_cma_object **obj, ++ struct drm_vc4_submit_rcl_surface *surf) ++{ ++ uint8_t tiling = VC4_GET_FIELD(surf->bits, ++ VC4_LOADSTORE_TILE_BUFFER_TILING); ++ uint8_t buffer = VC4_GET_FIELD(surf->bits, ++ VC4_LOADSTORE_TILE_BUFFER_BUFFER); ++ uint8_t format = VC4_GET_FIELD(surf->bits, ++ VC4_LOADSTORE_TILE_BUFFER_FORMAT); ++ int cpp; ++ int ret; ++ ++ if (surf->flags & ~VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { ++ DRM_ERROR("Extra flags set\n"); ++ return -EINVAL; ++ } ++ ++ if (surf->hindex == ~0) ++ return 0; ++ ++ *obj = vc4_use_bo(exec, surf->hindex); ++ if (!*obj) ++ return -EINVAL; ++ ++ if (surf->flags & VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) { ++ if (surf == &exec->args->zs_write) { ++ DRM_ERROR("general zs write may not be a full-res.\n"); ++ return -EINVAL; ++ } ++ ++ if (surf->bits != 0) { ++ DRM_ERROR("load/store general bits set with " ++ "full res load/store.\n"); ++ return -EINVAL; ++ } ++ ++ ret = vc4_full_res_bounds_check(exec, *obj, surf); ++ if (!ret) ++ return ret; ++ ++ return 0; ++ } ++ ++ if (surf->bits & ~(VC4_LOADSTORE_TILE_BUFFER_TILING_MASK | ++ VC4_LOADSTORE_TILE_BUFFER_BUFFER_MASK | ++ VC4_LOADSTORE_TILE_BUFFER_FORMAT_MASK)) { ++ DRM_ERROR("Unknown bits in load/store: 0x%04x\n", ++ surf->bits); ++ return -EINVAL; ++ } ++ ++ if (tiling > VC4_TILING_FORMAT_LT) { ++ DRM_ERROR("Bad tiling format\n"); ++ return -EINVAL; ++ } ++ ++ if (buffer == VC4_LOADSTORE_TILE_BUFFER_ZS) { ++ if (format != 0) { ++ DRM_ERROR("No color format should be set for ZS\n"); ++ return -EINVAL; ++ } ++ cpp = 4; ++ } else if (buffer == VC4_LOADSTORE_TILE_BUFFER_COLOR) { ++ switch (format) { ++ case VC4_LOADSTORE_TILE_BUFFER_BGR565: ++ case VC4_LOADSTORE_TILE_BUFFER_BGR565_DITHER: ++ cpp = 2; ++ break; ++ case VC4_LOADSTORE_TILE_BUFFER_RGBA8888: ++ cpp = 4; ++ break; ++ default: ++ DRM_ERROR("Bad tile buffer format\n"); ++ return -EINVAL; ++ } ++ } else { ++ DRM_ERROR("Bad load/store buffer %d.\n", buffer); ++ return -EINVAL; ++ } ++ ++ if (surf->offset & 0xf) { ++ DRM_ERROR("load/store buffer must be 16b aligned.\n"); ++ return -EINVAL; ++ } ++ ++ if (!vc4_check_tex_size(exec, *obj, surf->offset, tiling, ++ exec->args->width, exec->args->height, cpp)) { ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int ++vc4_rcl_render_config_surface_setup(struct vc4_exec_info *exec, ++ struct vc4_rcl_setup *setup, ++ struct drm_gem_cma_object **obj, ++ struct drm_vc4_submit_rcl_surface *surf) ++{ ++ uint8_t tiling = VC4_GET_FIELD(surf->bits, ++ VC4_RENDER_CONFIG_MEMORY_FORMAT); ++ uint8_t format = VC4_GET_FIELD(surf->bits, ++ VC4_RENDER_CONFIG_FORMAT); ++ int cpp; ++ ++ if (surf->flags != 0) { ++ DRM_ERROR("No flags supported on render config.\n"); ++ return -EINVAL; ++ } ++ ++ if (surf->bits & ~(VC4_RENDER_CONFIG_MEMORY_FORMAT_MASK | ++ VC4_RENDER_CONFIG_FORMAT_MASK | ++ VC4_RENDER_CONFIG_MS_MODE_4X | ++ VC4_RENDER_CONFIG_DECIMATE_MODE_4X)) { ++ DRM_ERROR("Unknown bits in render config: 0x%04x\n", ++ surf->bits); ++ return -EINVAL; ++ } ++ ++ if (surf->hindex == ~0) ++ return 0; ++ ++ *obj = vc4_use_bo(exec, surf->hindex); ++ if (!*obj) ++ return -EINVAL; ++ ++ if (tiling > VC4_TILING_FORMAT_LT) { ++ DRM_ERROR("Bad tiling format\n"); ++ return -EINVAL; ++ } ++ ++ switch (format) { ++ case VC4_RENDER_CONFIG_FORMAT_BGR565_DITHERED: ++ case VC4_RENDER_CONFIG_FORMAT_BGR565: ++ cpp = 2; ++ break; ++ case VC4_RENDER_CONFIG_FORMAT_RGBA8888: ++ cpp = 4; ++ break; ++ default: ++ DRM_ERROR("Bad tile buffer format\n"); ++ return -EINVAL; ++ } ++ ++ if (!vc4_check_tex_size(exec, *obj, surf->offset, tiling, ++ exec->args->width, exec->args->height, cpp)) { ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++int vc4_get_rcl(struct drm_device *dev, struct vc4_exec_info *exec) ++{ ++ struct vc4_rcl_setup setup = {0}; ++ struct drm_vc4_submit_cl *args = exec->args; ++ bool has_bin = args->bin_cl_size != 0; ++ int ret; ++ ++ if (args->min_x_tile > args->max_x_tile || ++ args->min_y_tile > args->max_y_tile) { ++ DRM_ERROR("Bad render tile set (%d,%d)-(%d,%d)\n", ++ args->min_x_tile, args->min_y_tile, ++ args->max_x_tile, args->max_y_tile); ++ return -EINVAL; ++ } ++ ++ if (has_bin && ++ (args->max_x_tile > exec->bin_tiles_x || ++ args->max_y_tile > exec->bin_tiles_y)) { ++ DRM_ERROR("Render tiles (%d,%d) outside of bin config " ++ "(%d,%d)\n", ++ args->max_x_tile, args->max_y_tile, ++ exec->bin_tiles_x, exec->bin_tiles_y); ++ return -EINVAL; ++ } ++ ++ ret = vc4_rcl_render_config_surface_setup(exec, &setup, ++ &setup.color_write, ++ &args->color_write); ++ if (ret) ++ return ret; ++ ++ ret = vc4_rcl_surface_setup(exec, &setup.color_read, &args->color_read); ++ if (ret) ++ return ret; ++ ++ ret = vc4_rcl_surface_setup(exec, &setup.zs_read, &args->zs_read); ++ if (ret) ++ return ret; ++ ++ ret = vc4_rcl_surface_setup(exec, &setup.zs_write, &args->zs_write); ++ if (ret) ++ return ret; ++ ++ ret = vc4_rcl_msaa_surface_setup(exec, &setup.msaa_color_write, ++ &args->msaa_color_write); ++ if (ret) ++ return ret; ++ ++ ret = vc4_rcl_msaa_surface_setup(exec, &setup.msaa_zs_write, ++ &args->msaa_zs_write); ++ if (ret) ++ return ret; ++ ++ /* We shouldn't even have the job submitted to us if there's no ++ * surface to write out. ++ */ ++ if (!setup.color_write && !setup.zs_write && ++ !setup.msaa_color_write && !setup.msaa_zs_write) { ++ DRM_ERROR("RCL requires color or Z/S write\n"); ++ return -EINVAL; ++ } ++ ++ return vc4_create_rcl_bo(dev, exec, &setup); ++} +diff --git a/drivers/gpu/drm/vc4/vc4_trace.h b/drivers/gpu/drm/vc4/vc4_trace.h +new file mode 100644 +index 0000000..ad7b1ea +--- /dev/null ++++ b/drivers/gpu/drm/vc4/vc4_trace.h +@@ -0,0 +1,63 @@ ++/* ++ * Copyright (C) 2015 Broadcom ++ * ++ * 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. ++ */ ++ ++#if !defined(_VC4_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) ++#define _VC4_TRACE_H_ ++ ++#include ++#include ++#include ++ ++#undef TRACE_SYSTEM ++#define TRACE_SYSTEM vc4 ++#define TRACE_INCLUDE_FILE vc4_trace ++ ++TRACE_EVENT(vc4_wait_for_seqno_begin, ++ TP_PROTO(struct drm_device *dev, uint64_t seqno, uint64_t timeout), ++ TP_ARGS(dev, seqno, timeout), ++ ++ TP_STRUCT__entry( ++ __field(u32, dev) ++ __field(u64, seqno) ++ __field(u64, timeout) ++ ), ++ ++ TP_fast_assign( ++ __entry->dev = dev->primary->index; ++ __entry->seqno = seqno; ++ __entry->timeout = timeout; ++ ), ++ ++ TP_printk("dev=%u, seqno=%llu, timeout=%llu", ++ __entry->dev, __entry->seqno, __entry->timeout) ++); ++ ++TRACE_EVENT(vc4_wait_for_seqno_end, ++ TP_PROTO(struct drm_device *dev, uint64_t seqno), ++ TP_ARGS(dev, seqno), ++ ++ TP_STRUCT__entry( ++ __field(u32, dev) ++ __field(u64, seqno) ++ ), ++ ++ TP_fast_assign( ++ __entry->dev = dev->primary->index; ++ __entry->seqno = seqno; ++ ), ++ ++ TP_printk("dev=%u, seqno=%llu", ++ __entry->dev, __entry->seqno) ++); ++ ++#endif /* _VC4_TRACE_H_ */ ++ ++/* This part must be outside protection */ ++#undef TRACE_INCLUDE_PATH ++#define TRACE_INCLUDE_PATH . ++#include +diff --git a/drivers/gpu/drm/vc4/vc4_trace_points.c b/drivers/gpu/drm/vc4/vc4_trace_points.c +new file mode 100644 +index 0000000..e6278f2 +--- /dev/null ++++ b/drivers/gpu/drm/vc4/vc4_trace_points.c +@@ -0,0 +1,14 @@ ++/* ++ * Copyright (C) 2015 Broadcom ++ * ++ * 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 "vc4_drv.h" ++ ++#ifndef __CHECKER__ ++#define CREATE_TRACE_POINTS ++#include "vc4_trace.h" ++#endif +diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c +index 040ad0d..424d515 100644 +--- a/drivers/gpu/drm/vc4/vc4_v3d.c ++++ b/drivers/gpu/drm/vc4/vc4_v3d.c +@@ -144,6 +144,21 @@ int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused) + } + #endif /* CONFIG_DEBUG_FS */ + ++/* ++ * Asks the firmware to turn on power to the V3D engine. ++ * ++ * This may be doable with just the clocks interface, though this ++ * packet does some other register setup from the firmware, too. ++ */ ++int ++vc4_v3d_set_power(struct vc4_dev *vc4, bool on) ++{ ++ if (on) ++ return pm_generic_poweroff(&vc4->v3d->pdev->dev); ++ else ++ return pm_generic_resume(&vc4->v3d->pdev->dev); ++} ++ + static void vc4_v3d_init_hw(struct drm_device *dev) + { + struct vc4_dev *vc4 = to_vc4_dev(dev); +@@ -161,6 +176,7 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data) + struct drm_device *drm = dev_get_drvdata(master); + struct vc4_dev *vc4 = to_vc4_dev(drm); + struct vc4_v3d *v3d = NULL; ++ int ret; + + v3d = devm_kzalloc(&pdev->dev, sizeof(*v3d), GFP_KERNEL); + if (!v3d) +@@ -180,8 +196,20 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data) + return -EINVAL; + } + ++ /* Reset the binner overflow address/size at setup, to be sure ++ * we don't reuse an old one. ++ */ ++ V3D_WRITE(V3D_BPOA, 0); ++ V3D_WRITE(V3D_BPOS, 0); ++ + vc4_v3d_init_hw(drm); + ++ ret = drm_irq_install(drm, platform_get_irq(pdev, 0)); ++ if (ret) { ++ DRM_ERROR("Failed to install IRQ handler\n"); ++ return ret; ++ } ++ + return 0; + } + +@@ -191,6 +219,15 @@ static void vc4_v3d_unbind(struct device *dev, struct device *master, + struct drm_device *drm = dev_get_drvdata(master); + struct vc4_dev *vc4 = to_vc4_dev(drm); + ++ drm_irq_uninstall(drm); ++ ++ /* Disable the binner's overflow memory address, so the next ++ * driver probe (if any) doesn't try to reuse our old ++ * allocation. ++ */ ++ V3D_WRITE(V3D_BPOA, 0); ++ V3D_WRITE(V3D_BPOS, 0); ++ + vc4->v3d = NULL; + } + +diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c +new file mode 100644 +index 0000000..0fb5b99 +--- /dev/null ++++ b/drivers/gpu/drm/vc4/vc4_validate.c +@@ -0,0 +1,900 @@ ++/* ++ * Copyright © 2014 Broadcom ++ * ++ * 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 (including the next ++ * paragraph) 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. ++ */ ++ ++/** ++ * Command list validator for VC4. ++ * ++ * The VC4 has no IOMMU between it and system memory. So, a user with ++ * access to execute command lists could escalate privilege by ++ * overwriting system memory (drawing to it as a framebuffer) or ++ * reading system memory it shouldn't (reading it as a texture, or ++ * uniform data, or vertex data). ++ * ++ * This validates command lists to ensure that all accesses are within ++ * the bounds of the GEM objects referenced. It explicitly whitelists ++ * packets, and looks at the offsets in any address fields to make ++ * sure they're constrained within the BOs they reference. ++ * ++ * Note that because of the validation that's happening anyway, this ++ * is where GEM relocation processing happens. ++ */ ++ ++#include "uapi/drm/vc4_drm.h" ++#include "vc4_drv.h" ++#include "vc4_packet.h" ++ ++#define VALIDATE_ARGS \ ++ struct vc4_exec_info *exec, \ ++ void *validated, \ ++ void *untrusted ++ ++/** Return the width in pixels of a 64-byte microtile. */ ++static uint32_t ++utile_width(int cpp) ++{ ++ switch (cpp) { ++ case 1: ++ case 2: ++ return 8; ++ case 4: ++ return 4; ++ case 8: ++ return 2; ++ default: ++ DRM_ERROR("unknown cpp: %d\n", cpp); ++ return 1; ++ } ++} ++ ++/** Return the height in pixels of a 64-byte microtile. */ ++static uint32_t ++utile_height(int cpp) ++{ ++ switch (cpp) { ++ case 1: ++ return 8; ++ case 2: ++ case 4: ++ case 8: ++ return 4; ++ default: ++ DRM_ERROR("unknown cpp: %d\n", cpp); ++ return 1; ++ } ++} ++ ++/** ++ * The texture unit decides what tiling format a particular miplevel is using ++ * this function, so we lay out our miptrees accordingly. ++ */ ++static bool ++size_is_lt(uint32_t width, uint32_t height, int cpp) ++{ ++ return (width <= 4 * utile_width(cpp) || ++ height <= 4 * utile_height(cpp)); ++} ++ ++struct drm_gem_cma_object * ++vc4_use_bo(struct vc4_exec_info *exec, uint32_t hindex) ++{ ++ struct drm_gem_cma_object *obj; ++ struct vc4_bo *bo; ++ ++ if (hindex >= exec->bo_count) { ++ DRM_ERROR("BO index %d greater than BO count %d\n", ++ hindex, exec->bo_count); ++ return NULL; ++ } ++ obj = exec->bo[hindex]; ++ bo = to_vc4_bo(&obj->base); ++ ++ if (bo->validated_shader) { ++ DRM_ERROR("Trying to use shader BO as something other than " ++ "a shader\n"); ++ return NULL; ++ } ++ ++ return obj; ++} ++ ++static struct drm_gem_cma_object * ++vc4_use_handle(struct vc4_exec_info *exec, uint32_t gem_handles_packet_index) ++{ ++ return vc4_use_bo(exec, exec->bo_index[gem_handles_packet_index]); ++} ++ ++static bool ++validate_bin_pos(struct vc4_exec_info *exec, void *untrusted, uint32_t pos) ++{ ++ /* Note that the untrusted pointer passed to these functions is ++ * incremented past the packet byte. ++ */ ++ return (untrusted - 1 == exec->bin_u + pos); ++} ++ ++static uint32_t ++gl_shader_rec_size(uint32_t pointer_bits) ++{ ++ uint32_t attribute_count = pointer_bits & 7; ++ bool extended = pointer_bits & 8; ++ ++ if (attribute_count == 0) ++ attribute_count = 8; ++ ++ if (extended) ++ return 100 + attribute_count * 4; ++ else ++ return 36 + attribute_count * 8; ++} ++ ++bool ++vc4_check_tex_size(struct vc4_exec_info *exec, struct drm_gem_cma_object *fbo, ++ uint32_t offset, uint8_t tiling_format, ++ uint32_t width, uint32_t height, uint8_t cpp) ++{ ++ uint32_t aligned_width, aligned_height, stride, size; ++ uint32_t utile_w = utile_width(cpp); ++ uint32_t utile_h = utile_height(cpp); ++ ++ /* The shaded vertex format stores signed 12.4 fixed point ++ * (-2048,2047) offsets from the viewport center, so we should ++ * never have a render target larger than 4096. The texture ++ * unit can only sample from 2048x2048, so it's even more ++ * restricted. This lets us avoid worrying about overflow in ++ * our math. ++ */ ++ if (width > 4096 || height > 4096) { ++ DRM_ERROR("Surface dimesions (%d,%d) too large", width, height); ++ return false; ++ } ++ ++ switch (tiling_format) { ++ case VC4_TILING_FORMAT_LINEAR: ++ aligned_width = round_up(width, utile_w); ++ aligned_height = height; ++ break; ++ case VC4_TILING_FORMAT_T: ++ aligned_width = round_up(width, utile_w * 8); ++ aligned_height = round_up(height, utile_h * 8); ++ break; ++ case VC4_TILING_FORMAT_LT: ++ aligned_width = round_up(width, utile_w); ++ aligned_height = round_up(height, utile_h); ++ break; ++ default: ++ DRM_ERROR("buffer tiling %d unsupported\n", tiling_format); ++ return false; ++ } ++ ++ stride = aligned_width * cpp; ++ size = stride * aligned_height; ++ ++ if (size + offset < size || ++ size + offset > fbo->base.size) { ++ DRM_ERROR("Overflow in %dx%d (%dx%d) fbo size (%d + %d > %zd)\n", ++ width, height, ++ aligned_width, aligned_height, ++ size, offset, fbo->base.size); ++ return false; ++ } ++ ++ return true; ++} ++ ++static int ++validate_flush(VALIDATE_ARGS) ++{ ++ if (!validate_bin_pos(exec, untrusted, exec->args->bin_cl_size - 1)) { ++ DRM_ERROR("Bin CL must end with VC4_PACKET_FLUSH\n"); ++ return -EINVAL; ++ } ++ exec->found_flush = true; ++ ++ return 0; ++} ++ ++static int ++validate_start_tile_binning(VALIDATE_ARGS) ++{ ++ if (exec->found_start_tile_binning_packet) { ++ DRM_ERROR("Duplicate VC4_PACKET_START_TILE_BINNING\n"); ++ return -EINVAL; ++ } ++ exec->found_start_tile_binning_packet = true; ++ ++ if (!exec->found_tile_binning_mode_config_packet) { ++ DRM_ERROR("missing VC4_PACKET_TILE_BINNING_MODE_CONFIG\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int ++validate_increment_semaphore(VALIDATE_ARGS) ++{ ++ if (!validate_bin_pos(exec, untrusted, exec->args->bin_cl_size - 2)) { ++ DRM_ERROR("Bin CL must end with " ++ "VC4_PACKET_INCREMENT_SEMAPHORE\n"); ++ return -EINVAL; ++ } ++ exec->found_increment_semaphore_packet = true; ++ ++ return 0; ++} ++ ++static int ++validate_indexed_prim_list(VALIDATE_ARGS) ++{ ++ struct drm_gem_cma_object *ib; ++ uint32_t length = *(uint32_t *)(untrusted + 1); ++ uint32_t offset = *(uint32_t *)(untrusted + 5); ++ uint32_t max_index = *(uint32_t *)(untrusted + 9); ++ uint32_t index_size = (*(uint8_t *)(untrusted + 0) >> 4) ? 2 : 1; ++ struct vc4_shader_state *shader_state; ++ ++ /* Check overflow condition */ ++ if (exec->shader_state_count == 0) { ++ DRM_ERROR("shader state must precede primitives\n"); ++ return -EINVAL; ++ } ++ shader_state = &exec->shader_state[exec->shader_state_count - 1]; ++ ++ if (max_index > shader_state->max_index) ++ shader_state->max_index = max_index; ++ ++ ib = vc4_use_handle(exec, 0); ++ if (!ib) ++ return -EINVAL; ++ ++ if (offset > ib->base.size || ++ (ib->base.size - offset) / index_size < length) { ++ DRM_ERROR("IB access overflow (%d + %d*%d > %zd)\n", ++ offset, length, index_size, ib->base.size); ++ return -EINVAL; ++ } ++ ++ *(uint32_t *)(validated + 5) = ib->paddr + offset; ++ ++ return 0; ++} ++ ++static int ++validate_gl_array_primitive(VALIDATE_ARGS) ++{ ++ uint32_t length = *(uint32_t *)(untrusted + 1); ++ uint32_t base_index = *(uint32_t *)(untrusted + 5); ++ uint32_t max_index; ++ struct vc4_shader_state *shader_state; ++ ++ /* Check overflow condition */ ++ if (exec->shader_state_count == 0) { ++ DRM_ERROR("shader state must precede primitives\n"); ++ return -EINVAL; ++ } ++ shader_state = &exec->shader_state[exec->shader_state_count - 1]; ++ ++ if (length + base_index < length) { ++ DRM_ERROR("primitive vertex count overflow\n"); ++ return -EINVAL; ++ } ++ max_index = length + base_index - 1; ++ ++ if (max_index > shader_state->max_index) ++ shader_state->max_index = max_index; ++ ++ return 0; ++} ++ ++static int ++validate_gl_shader_state(VALIDATE_ARGS) ++{ ++ uint32_t i = exec->shader_state_count++; ++ ++ if (i >= exec->shader_state_size) { ++ DRM_ERROR("More requests for shader states than declared\n"); ++ return -EINVAL; ++ } ++ ++ exec->shader_state[i].addr = *(uint32_t *)untrusted; ++ exec->shader_state[i].max_index = 0; ++ ++ if (exec->shader_state[i].addr & ~0xf) { ++ DRM_ERROR("high bits set in GL shader rec reference\n"); ++ return -EINVAL; ++ } ++ ++ *(uint32_t *)validated = (exec->shader_rec_p + ++ exec->shader_state[i].addr); ++ ++ exec->shader_rec_p += ++ roundup(gl_shader_rec_size(exec->shader_state[i].addr), 16); ++ ++ return 0; ++} ++ ++static int ++validate_tile_binning_config(VALIDATE_ARGS) ++{ ++ struct drm_device *dev = exec->exec_bo->base.dev; ++ struct vc4_bo *tile_bo; ++ uint8_t flags; ++ uint32_t tile_state_size, tile_alloc_size; ++ uint32_t tile_count; ++ ++ if (exec->found_tile_binning_mode_config_packet) { ++ DRM_ERROR("Duplicate VC4_PACKET_TILE_BINNING_MODE_CONFIG\n"); ++ return -EINVAL; ++ } ++ exec->found_tile_binning_mode_config_packet = true; ++ ++ exec->bin_tiles_x = *(uint8_t *)(untrusted + 12); ++ exec->bin_tiles_y = *(uint8_t *)(untrusted + 13); ++ tile_count = exec->bin_tiles_x * exec->bin_tiles_y; ++ flags = *(uint8_t *)(untrusted + 14); ++ ++ if (exec->bin_tiles_x == 0 || ++ exec->bin_tiles_y == 0) { ++ DRM_ERROR("Tile binning config of %dx%d too small\n", ++ exec->bin_tiles_x, exec->bin_tiles_y); ++ return -EINVAL; ++ } ++ ++ if (flags & (VC4_BIN_CONFIG_DB_NON_MS | ++ VC4_BIN_CONFIG_TILE_BUFFER_64BIT)) { ++ DRM_ERROR("unsupported binning config flags 0x%02x\n", flags); ++ return -EINVAL; ++ } ++ ++ /* The tile state data array is 48 bytes per tile, and we put it at ++ * the start of a BO containing both it and the tile alloc. ++ */ ++ tile_state_size = 48 * tile_count; ++ ++ /* Since the tile alloc array will follow us, align. */ ++ exec->tile_alloc_offset = roundup(tile_state_size, 4096); ++ ++ *(uint8_t *)(validated + 14) = ++ ((flags & ~(VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_MASK | ++ VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_MASK)) | ++ VC4_BIN_CONFIG_AUTO_INIT_TSDA | ++ VC4_SET_FIELD(VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_32, ++ VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE) | ++ VC4_SET_FIELD(VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_128, ++ VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE)); ++ ++ /* Initial block size. */ ++ tile_alloc_size = 32 * tile_count; ++ ++ /* ++ * The initial allocation gets rounded to the next 256 bytes before ++ * the hardware starts fulfilling further allocations. ++ */ ++ tile_alloc_size = roundup(tile_alloc_size, 256); ++ ++ /* Add space for the extra allocations. This is what gets used first, ++ * before overflow memory. It must have at least 4096 bytes, but we ++ * want to avoid overflow memory usage if possible. ++ */ ++ tile_alloc_size += 1024 * 1024; ++ ++ tile_bo = vc4_bo_create(dev, exec->tile_alloc_offset + tile_alloc_size, ++ true); ++ exec->tile_bo = &tile_bo->base; ++ if (!exec->tile_bo) ++ return -ENOMEM; ++ list_add_tail(&tile_bo->unref_head, &exec->unref_list); ++ ++ /* tile alloc address. */ ++ *(uint32_t *)(validated + 0) = (exec->tile_bo->paddr + ++ exec->tile_alloc_offset); ++ /* tile alloc size. */ ++ *(uint32_t *)(validated + 4) = tile_alloc_size; ++ /* tile state address. */ ++ *(uint32_t *)(validated + 8) = exec->tile_bo->paddr; ++ ++ return 0; ++} ++ ++static int ++validate_gem_handles(VALIDATE_ARGS) ++{ ++ memcpy(exec->bo_index, untrusted, sizeof(exec->bo_index)); ++ return 0; ++} ++ ++#define VC4_DEFINE_PACKET(packet, func) \ ++ [packet] = { packet ## _SIZE, #packet, func } ++ ++static const struct cmd_info { ++ uint16_t len; ++ const char *name; ++ int (*func)(struct vc4_exec_info *exec, void *validated, ++ void *untrusted); ++} cmd_info[] = { ++ VC4_DEFINE_PACKET(VC4_PACKET_HALT, NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_NOP, NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_FLUSH, validate_flush), ++ VC4_DEFINE_PACKET(VC4_PACKET_FLUSH_ALL, NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_START_TILE_BINNING, ++ validate_start_tile_binning), ++ VC4_DEFINE_PACKET(VC4_PACKET_INCREMENT_SEMAPHORE, ++ validate_increment_semaphore), ++ ++ VC4_DEFINE_PACKET(VC4_PACKET_GL_INDEXED_PRIMITIVE, ++ validate_indexed_prim_list), ++ VC4_DEFINE_PACKET(VC4_PACKET_GL_ARRAY_PRIMITIVE, ++ validate_gl_array_primitive), ++ ++ VC4_DEFINE_PACKET(VC4_PACKET_PRIMITIVE_LIST_FORMAT, NULL), ++ ++ VC4_DEFINE_PACKET(VC4_PACKET_GL_SHADER_STATE, validate_gl_shader_state), ++ ++ VC4_DEFINE_PACKET(VC4_PACKET_CONFIGURATION_BITS, NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_FLAT_SHADE_FLAGS, NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_POINT_SIZE, NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_LINE_WIDTH, NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_RHT_X_BOUNDARY, NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_DEPTH_OFFSET, NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_CLIP_WINDOW, NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_VIEWPORT_OFFSET, NULL), ++ VC4_DEFINE_PACKET(VC4_PACKET_CLIPPER_XY_SCALING, NULL), ++ /* Note: The docs say this was also 105, but it was 106 in the ++ * initial userland code drop. ++ */ ++ VC4_DEFINE_PACKET(VC4_PACKET_CLIPPER_Z_SCALING, NULL), ++ ++ VC4_DEFINE_PACKET(VC4_PACKET_TILE_BINNING_MODE_CONFIG, ++ validate_tile_binning_config), ++ ++ VC4_DEFINE_PACKET(VC4_PACKET_GEM_HANDLES, validate_gem_handles), ++}; ++ ++int ++vc4_validate_bin_cl(struct drm_device *dev, ++ void *validated, ++ void *unvalidated, ++ struct vc4_exec_info *exec) ++{ ++ uint32_t len = exec->args->bin_cl_size; ++ uint32_t dst_offset = 0; ++ uint32_t src_offset = 0; ++ ++ while (src_offset < len) { ++ void *dst_pkt = validated + dst_offset; ++ void *src_pkt = unvalidated + src_offset; ++ u8 cmd = *(uint8_t *)src_pkt; ++ const struct cmd_info *info; ++ ++ if (cmd >= ARRAY_SIZE(cmd_info)) { ++ DRM_ERROR("0x%08x: packet %d out of bounds\n", ++ src_offset, cmd); ++ return -EINVAL; ++ } ++ ++ info = &cmd_info[cmd]; ++ if (!info->name) { ++ DRM_ERROR("0x%08x: packet %d invalid\n", ++ src_offset, cmd); ++ return -EINVAL; ++ } ++ ++ if (src_offset + info->len > len) { ++ DRM_ERROR("0x%08x: packet %d (%s) length 0x%08x " ++ "exceeds bounds (0x%08x)\n", ++ src_offset, cmd, info->name, info->len, ++ src_offset + len); ++ return -EINVAL; ++ } ++ ++ if (cmd != VC4_PACKET_GEM_HANDLES) ++ memcpy(dst_pkt, src_pkt, info->len); ++ ++ if (info->func && info->func(exec, ++ dst_pkt + 1, ++ src_pkt + 1)) { ++ DRM_ERROR("0x%08x: packet %d (%s) failed to validate\n", ++ src_offset, cmd, info->name); ++ return -EINVAL; ++ } ++ ++ src_offset += info->len; ++ /* GEM handle loading doesn't produce HW packets. */ ++ if (cmd != VC4_PACKET_GEM_HANDLES) ++ dst_offset += info->len; ++ ++ /* When the CL hits halt, it'll stop reading anything else. */ ++ if (cmd == VC4_PACKET_HALT) ++ break; ++ } ++ ++ exec->ct0ea = exec->ct0ca + dst_offset; ++ ++ if (!exec->found_start_tile_binning_packet) { ++ DRM_ERROR("Bin CL missing VC4_PACKET_START_TILE_BINNING\n"); ++ return -EINVAL; ++ } ++ ++ /* The bin CL must be ended with INCREMENT_SEMAPHORE and FLUSH. The ++ * semaphore is used to trigger the render CL to start up, and the ++ * FLUSH is what caps the bin lists with ++ * VC4_PACKET_RETURN_FROM_SUB_LIST (so they jump back to the main ++ * render CL when they get called to) and actually triggers the queued ++ * semaphore increment. ++ */ ++ if (!exec->found_increment_semaphore_packet || !exec->found_flush) { ++ DRM_ERROR("Bin CL missing VC4_PACKET_INCREMENT_SEMAPHORE + " ++ "VC4_PACKET_FLUSH\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static bool ++reloc_tex(struct vc4_exec_info *exec, ++ void *uniform_data_u, ++ struct vc4_texture_sample_info *sample, ++ uint32_t texture_handle_index) ++ ++{ ++ struct drm_gem_cma_object *tex; ++ uint32_t p0 = *(uint32_t *)(uniform_data_u + sample->p_offset[0]); ++ uint32_t p1 = *(uint32_t *)(uniform_data_u + sample->p_offset[1]); ++ uint32_t p2 = (sample->p_offset[2] != ~0 ? ++ *(uint32_t *)(uniform_data_u + sample->p_offset[2]) : 0); ++ uint32_t p3 = (sample->p_offset[3] != ~0 ? ++ *(uint32_t *)(uniform_data_u + sample->p_offset[3]) : 0); ++ uint32_t *validated_p0 = exec->uniforms_v + sample->p_offset[0]; ++ uint32_t offset = p0 & VC4_TEX_P0_OFFSET_MASK; ++ uint32_t miplevels = VC4_GET_FIELD(p0, VC4_TEX_P0_MIPLVLS); ++ uint32_t width = VC4_GET_FIELD(p1, VC4_TEX_P1_WIDTH); ++ uint32_t height = VC4_GET_FIELD(p1, VC4_TEX_P1_HEIGHT); ++ uint32_t cpp, tiling_format, utile_w, utile_h; ++ uint32_t i; ++ uint32_t cube_map_stride = 0; ++ enum vc4_texture_data_type type; ++ ++ tex = vc4_use_bo(exec, texture_handle_index); ++ if (!tex) ++ return false; ++ ++ if (sample->is_direct) { ++ uint32_t remaining_size = tex->base.size - p0; ++ ++ if (p0 > tex->base.size - 4) { ++ DRM_ERROR("UBO offset greater than UBO size\n"); ++ goto fail; ++ } ++ if (p1 > remaining_size - 4) { ++ DRM_ERROR("UBO clamp would allow reads " ++ "outside of UBO\n"); ++ goto fail; ++ } ++ *validated_p0 = tex->paddr + p0; ++ return true; ++ } ++ ++ if (width == 0) ++ width = 2048; ++ if (height == 0) ++ height = 2048; ++ ++ if (p0 & VC4_TEX_P0_CMMODE_MASK) { ++ if (VC4_GET_FIELD(p2, VC4_TEX_P2_PTYPE) == ++ VC4_TEX_P2_PTYPE_CUBE_MAP_STRIDE) ++ cube_map_stride = p2 & VC4_TEX_P2_CMST_MASK; ++ if (VC4_GET_FIELD(p3, VC4_TEX_P2_PTYPE) == ++ VC4_TEX_P2_PTYPE_CUBE_MAP_STRIDE) { ++ if (cube_map_stride) { ++ DRM_ERROR("Cube map stride set twice\n"); ++ goto fail; ++ } ++ ++ cube_map_stride = p3 & VC4_TEX_P2_CMST_MASK; ++ } ++ if (!cube_map_stride) { ++ DRM_ERROR("Cube map stride not set\n"); ++ goto fail; ++ } ++ } ++ ++ type = (VC4_GET_FIELD(p0, VC4_TEX_P0_TYPE) | ++ (VC4_GET_FIELD(p1, VC4_TEX_P1_TYPE4) << 4)); ++ ++ switch (type) { ++ case VC4_TEXTURE_TYPE_RGBA8888: ++ case VC4_TEXTURE_TYPE_RGBX8888: ++ case VC4_TEXTURE_TYPE_RGBA32R: ++ cpp = 4; ++ break; ++ case VC4_TEXTURE_TYPE_RGBA4444: ++ case VC4_TEXTURE_TYPE_RGBA5551: ++ case VC4_TEXTURE_TYPE_RGB565: ++ case VC4_TEXTURE_TYPE_LUMALPHA: ++ case VC4_TEXTURE_TYPE_S16F: ++ case VC4_TEXTURE_TYPE_S16: ++ cpp = 2; ++ break; ++ case VC4_TEXTURE_TYPE_LUMINANCE: ++ case VC4_TEXTURE_TYPE_ALPHA: ++ case VC4_TEXTURE_TYPE_S8: ++ cpp = 1; ++ break; ++ case VC4_TEXTURE_TYPE_ETC1: ++ case VC4_TEXTURE_TYPE_BW1: ++ case VC4_TEXTURE_TYPE_A4: ++ case VC4_TEXTURE_TYPE_A1: ++ case VC4_TEXTURE_TYPE_RGBA64: ++ case VC4_TEXTURE_TYPE_YUV422R: ++ default: ++ DRM_ERROR("Texture format %d unsupported\n", type); ++ goto fail; ++ } ++ utile_w = utile_width(cpp); ++ utile_h = utile_height(cpp); ++ ++ if (type == VC4_TEXTURE_TYPE_RGBA32R) { ++ tiling_format = VC4_TILING_FORMAT_LINEAR; ++ } else { ++ if (size_is_lt(width, height, cpp)) ++ tiling_format = VC4_TILING_FORMAT_LT; ++ else ++ tiling_format = VC4_TILING_FORMAT_T; ++ } ++ ++ if (!vc4_check_tex_size(exec, tex, offset + cube_map_stride * 5, ++ tiling_format, width, height, cpp)) { ++ goto fail; ++ } ++ ++ /* The mipmap levels are stored before the base of the texture. Make ++ * sure there is actually space in the BO. ++ */ ++ for (i = 1; i <= miplevels; i++) { ++ uint32_t level_width = max(width >> i, 1u); ++ uint32_t level_height = max(height >> i, 1u); ++ uint32_t aligned_width, aligned_height; ++ uint32_t level_size; ++ ++ /* Once the levels get small enough, they drop from T to LT. */ ++ if (tiling_format == VC4_TILING_FORMAT_T && ++ size_is_lt(level_width, level_height, cpp)) { ++ tiling_format = VC4_TILING_FORMAT_LT; ++ } ++ ++ switch (tiling_format) { ++ case VC4_TILING_FORMAT_T: ++ aligned_width = round_up(level_width, utile_w * 8); ++ aligned_height = round_up(level_height, utile_h * 8); ++ break; ++ case VC4_TILING_FORMAT_LT: ++ aligned_width = round_up(level_width, utile_w); ++ aligned_height = round_up(level_height, utile_h); ++ break; ++ default: ++ aligned_width = round_up(level_width, utile_w); ++ aligned_height = level_height; ++ break; ++ } ++ ++ level_size = aligned_width * cpp * aligned_height; ++ ++ if (offset < level_size) { ++ DRM_ERROR("Level %d (%dx%d -> %dx%d) size %db " ++ "overflowed buffer bounds (offset %d)\n", ++ i, level_width, level_height, ++ aligned_width, aligned_height, ++ level_size, offset); ++ goto fail; ++ } ++ ++ offset -= level_size; ++ } ++ ++ *validated_p0 = tex->paddr + p0; ++ ++ return true; ++ fail: ++ DRM_INFO("Texture p0 at %d: 0x%08x\n", sample->p_offset[0], p0); ++ DRM_INFO("Texture p1 at %d: 0x%08x\n", sample->p_offset[1], p1); ++ DRM_INFO("Texture p2 at %d: 0x%08x\n", sample->p_offset[2], p2); ++ DRM_INFO("Texture p3 at %d: 0x%08x\n", sample->p_offset[3], p3); ++ return false; ++} ++ ++static int ++validate_gl_shader_rec(struct drm_device *dev, ++ struct vc4_exec_info *exec, ++ struct vc4_shader_state *state) ++{ ++ uint32_t *src_handles; ++ void *pkt_u, *pkt_v; ++ static const uint32_t shader_reloc_offsets[] = { ++ 4, /* fs */ ++ 16, /* vs */ ++ 28, /* cs */ ++ }; ++ uint32_t shader_reloc_count = ARRAY_SIZE(shader_reloc_offsets); ++ struct drm_gem_cma_object *bo[shader_reloc_count + 8]; ++ uint32_t nr_attributes, nr_relocs, packet_size; ++ int i; ++ ++ nr_attributes = state->addr & 0x7; ++ if (nr_attributes == 0) ++ nr_attributes = 8; ++ packet_size = gl_shader_rec_size(state->addr); ++ ++ nr_relocs = ARRAY_SIZE(shader_reloc_offsets) + nr_attributes; ++ if (nr_relocs * 4 > exec->shader_rec_size) { ++ DRM_ERROR("overflowed shader recs reading %d handles " ++ "from %d bytes left\n", ++ nr_relocs, exec->shader_rec_size); ++ return -EINVAL; ++ } ++ src_handles = exec->shader_rec_u; ++ exec->shader_rec_u += nr_relocs * 4; ++ exec->shader_rec_size -= nr_relocs * 4; ++ ++ if (packet_size > exec->shader_rec_size) { ++ DRM_ERROR("overflowed shader recs copying %db packet " ++ "from %d bytes left\n", ++ packet_size, exec->shader_rec_size); ++ return -EINVAL; ++ } ++ pkt_u = exec->shader_rec_u; ++ pkt_v = exec->shader_rec_v; ++ memcpy(pkt_v, pkt_u, packet_size); ++ exec->shader_rec_u += packet_size; ++ /* Shader recs have to be aligned to 16 bytes (due to the attribute ++ * flags being in the low bytes), so round the next validated shader ++ * rec address up. This should be safe, since we've got so many ++ * relocations in a shader rec packet. ++ */ ++ BUG_ON(roundup(packet_size, 16) - packet_size > nr_relocs * 4); ++ exec->shader_rec_v += roundup(packet_size, 16); ++ exec->shader_rec_size -= packet_size; ++ ++ if (!(*(uint16_t *)pkt_u & VC4_SHADER_FLAG_FS_SINGLE_THREAD)) { ++ DRM_ERROR("Multi-threaded fragment shaders not supported.\n"); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < shader_reloc_count; i++) { ++ if (src_handles[i] > exec->bo_count) { ++ DRM_ERROR("Shader handle %d too big\n", src_handles[i]); ++ return -EINVAL; ++ } ++ ++ bo[i] = exec->bo[src_handles[i]]; ++ if (!bo[i]) ++ return -EINVAL; ++ } ++ for (i = shader_reloc_count; i < nr_relocs; i++) { ++ bo[i] = vc4_use_bo(exec, src_handles[i]); ++ if (!bo[i]) ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < shader_reloc_count; i++) { ++ struct vc4_validated_shader_info *validated_shader; ++ uint32_t o = shader_reloc_offsets[i]; ++ uint32_t src_offset = *(uint32_t *)(pkt_u + o); ++ uint32_t *texture_handles_u; ++ void *uniform_data_u; ++ uint32_t tex; ++ ++ *(uint32_t *)(pkt_v + o) = bo[i]->paddr + src_offset; ++ ++ if (src_offset != 0) { ++ DRM_ERROR("Shaders must be at offset 0 of " ++ "the BO.\n"); ++ return -EINVAL; ++ } ++ ++ validated_shader = to_vc4_bo(&bo[i]->base)->validated_shader; ++ if (!validated_shader) ++ return -EINVAL; ++ ++ if (validated_shader->uniforms_src_size > ++ exec->uniforms_size) { ++ DRM_ERROR("Uniforms src buffer overflow\n"); ++ return -EINVAL; ++ } ++ ++ texture_handles_u = exec->uniforms_u; ++ uniform_data_u = (texture_handles_u + ++ validated_shader->num_texture_samples); ++ ++ memcpy(exec->uniforms_v, uniform_data_u, ++ validated_shader->uniforms_size); ++ ++ for (tex = 0; ++ tex < validated_shader->num_texture_samples; ++ tex++) { ++ if (!reloc_tex(exec, ++ uniform_data_u, ++ &validated_shader->texture_samples[tex], ++ texture_handles_u[tex])) { ++ return -EINVAL; ++ } ++ } ++ ++ *(uint32_t *)(pkt_v + o + 4) = exec->uniforms_p; ++ ++ exec->uniforms_u += validated_shader->uniforms_src_size; ++ exec->uniforms_v += validated_shader->uniforms_size; ++ exec->uniforms_p += validated_shader->uniforms_size; ++ } ++ ++ for (i = 0; i < nr_attributes; i++) { ++ struct drm_gem_cma_object *vbo = ++ bo[ARRAY_SIZE(shader_reloc_offsets) + i]; ++ uint32_t o = 36 + i * 8; ++ uint32_t offset = *(uint32_t *)(pkt_u + o + 0); ++ uint32_t attr_size = *(uint8_t *)(pkt_u + o + 4) + 1; ++ uint32_t stride = *(uint8_t *)(pkt_u + o + 5); ++ uint32_t max_index; ++ ++ if (state->addr & 0x8) ++ stride |= (*(uint32_t *)(pkt_u + 100 + i * 4)) & ~0xff; ++ ++ if (vbo->base.size < offset || ++ vbo->base.size - offset < attr_size) { ++ DRM_ERROR("BO offset overflow (%d + %d > %d)\n", ++ offset, attr_size, vbo->base.size); ++ return -EINVAL; ++ } ++ ++ if (stride != 0) { ++ max_index = ((vbo->base.size - offset - attr_size) / ++ stride); ++ if (state->max_index > max_index) { ++ DRM_ERROR("primitives use index %d out of " ++ "supplied %d\n", ++ state->max_index, max_index); ++ return -EINVAL; ++ } ++ } ++ ++ *(uint32_t *)(pkt_v + o) = vbo->paddr + offset; ++ } ++ ++ return 0; ++} ++ ++int ++vc4_validate_shader_recs(struct drm_device *dev, ++ struct vc4_exec_info *exec) ++{ ++ uint32_t i; ++ int ret = 0; ++ ++ for (i = 0; i < exec->shader_state_count; i++) { ++ ret = validate_gl_shader_rec(dev, exec, &exec->shader_state[i]); ++ if (ret) ++ return ret; ++ } ++ ++ return ret; ++} +diff --git a/include/uapi/drm/vc4_drm.h b/include/uapi/drm/vc4_drm.h +index 74de184..fe4161b 100644 +--- a/include/uapi/drm/vc4_drm.h ++++ b/include/uapi/drm/vc4_drm.h +@@ -26,14 +26,155 @@ + + #include "drm.h" + ++#define DRM_VC4_SUBMIT_CL 0x00 ++#define DRM_VC4_WAIT_SEQNO 0x01 ++#define DRM_VC4_WAIT_BO 0x02 + #define DRM_VC4_CREATE_BO 0x03 + #define DRM_VC4_MMAP_BO 0x04 + #define DRM_VC4_CREATE_SHADER_BO 0x05 + ++#define DRM_IOCTL_VC4_SUBMIT_CL DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_SUBMIT_CL, struct drm_vc4_submit_cl) ++#define DRM_IOCTL_VC4_WAIT_SEQNO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_WAIT_SEQNO, struct drm_vc4_wait_seqno) ++#define DRM_IOCTL_VC4_WAIT_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_WAIT_BO, struct drm_vc4_wait_bo) + #define DRM_IOCTL_VC4_CREATE_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_CREATE_BO, struct drm_vc4_create_bo) + #define DRM_IOCTL_VC4_MMAP_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_MMAP_BO, struct drm_vc4_mmap_bo) + #define DRM_IOCTL_VC4_CREATE_SHADER_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_CREATE_SHADER_BO, struct drm_vc4_create_shader_bo) + ++struct drm_vc4_submit_rcl_surface { ++ __u32 hindex; /* Handle index, or ~0 if not present. */ ++ __u32 offset; /* Offset to start of buffer. */ ++ /* ++ * Bits for either render config (color_write) or load/store packet. ++ * Bits should all be 0 for MSAA load/stores. ++ */ ++ __u16 bits; ++ ++#define VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES (1 << 0) ++ __u16 flags; ++}; ++ ++/** ++ * struct drm_vc4_submit_cl - ioctl argument for submitting commands to the 3D ++ * engine. ++ * ++ * Drivers typically use GPU BOs to store batchbuffers / command lists and ++ * their associated state. However, because the VC4 lacks an MMU, we have to ++ * do validation of memory accesses by the GPU commands. If we were to store ++ * our commands in BOs, we'd need to do uncached readback from them to do the ++ * validation process, which is too expensive. Instead, userspace accumulates ++ * commands and associated state in plain memory, then the kernel copies the ++ * data to its own address space, and then validates and stores it in a GPU ++ * BO. ++ */ ++struct drm_vc4_submit_cl { ++ /* Pointer to the binner command list. ++ * ++ * This is the first set of commands executed, which runs the ++ * coordinate shader to determine where primitives land on the screen, ++ * then writes out the state updates and draw calls necessary per tile ++ * to the tile allocation BO. ++ */ ++ __u64 bin_cl; ++ ++ /* Pointer to the shader records. ++ * ++ * Shader records are the structures read by the hardware that contain ++ * pointers to uniforms, shaders, and vertex attributes. The ++ * reference to the shader record has enough information to determine ++ * how many pointers are necessary (fixed number for shaders/uniforms, ++ * and an attribute count), so those BO indices into bo_handles are ++ * just stored as __u32s before each shader record passed in. ++ */ ++ __u64 shader_rec; ++ ++ /* Pointer to uniform data and texture handles for the textures ++ * referenced by the shader. ++ * ++ * For each shader state record, there is a set of uniform data in the ++ * order referenced by the record (FS, VS, then CS). Each set of ++ * uniform data has a __u32 index into bo_handles per texture ++ * sample operation, in the order the QPU_W_TMUn_S writes appear in ++ * the program. Following the texture BO handle indices is the actual ++ * uniform data. ++ * ++ * The individual uniform state blocks don't have sizes passed in, ++ * because the kernel has to determine the sizes anyway during shader ++ * code validation. ++ */ ++ __u64 uniforms; ++ __u64 bo_handles; ++ ++ /* Size in bytes of the binner command list. */ ++ __u32 bin_cl_size; ++ /* Size in bytes of the set of shader records. */ ++ __u32 shader_rec_size; ++ /* Number of shader records. ++ * ++ * This could just be computed from the contents of shader_records and ++ * the address bits of references to them from the bin CL, but it ++ * keeps the kernel from having to resize some allocations it makes. ++ */ ++ __u32 shader_rec_count; ++ /* Size in bytes of the uniform state. */ ++ __u32 uniforms_size; ++ ++ /* Number of BO handles passed in (size is that times 4). */ ++ __u32 bo_handle_count; ++ ++ /* RCL setup: */ ++ __u16 width; ++ __u16 height; ++ __u8 min_x_tile; ++ __u8 min_y_tile; ++ __u8 max_x_tile; ++ __u8 max_y_tile; ++ struct drm_vc4_submit_rcl_surface color_read; ++ struct drm_vc4_submit_rcl_surface color_write; ++ struct drm_vc4_submit_rcl_surface zs_read; ++ struct drm_vc4_submit_rcl_surface zs_write; ++ struct drm_vc4_submit_rcl_surface msaa_color_write; ++ struct drm_vc4_submit_rcl_surface msaa_zs_write; ++ __u32 clear_color[2]; ++ __u32 clear_z; ++ __u8 clear_s; ++ ++ __u32 pad:24; ++ ++#define VC4_SUBMIT_CL_USE_CLEAR_COLOR (1 << 0) ++ __u32 flags; ++ ++ /* Returned value of the seqno of this render job (for the ++ * wait ioctl). ++ */ ++ __u64 seqno; ++}; ++ ++/** ++ * struct drm_vc4_wait_seqno - ioctl argument for waiting for ++ * DRM_VC4_SUBMIT_CL completion using its returned seqno. ++ * ++ * timeout_ns is the timeout in nanoseconds, where "0" means "don't ++ * block, just return the status." ++ */ ++struct drm_vc4_wait_seqno { ++ __u64 seqno; ++ __u64 timeout_ns; ++}; ++ ++/** ++ * struct drm_vc4_wait_bo - ioctl argument for waiting for ++ * completion of the last DRM_VC4_SUBMIT_CL on a BO. ++ * ++ * This is useful for cases where multiple processes might be ++ * rendering to a BO and you want to wait for all rendering to be ++ * completed. ++ */ ++struct drm_vc4_wait_bo { ++ __u32 handle; ++ __u32 pad; ++ __u64 timeout_ns; ++}; ++ + /** + * struct drm_vc4_create_bo - ioctl argument for creating VC4 BOs. + * +-- +2.6.4 + diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-0108-drm-vc4-Add-support-for-async-pageflips.patch b/kernel/kernel/files/patches/mageia/gpu-drm-0108-drm-vc4-Add-support-for-async-pageflips.patch new file mode 100644 index 00000000..ff86f47c --- /dev/null +++ b/kernel/kernel/files/patches/mageia/gpu-drm-0108-drm-vc4-Add-support-for-async-pageflips.patch @@ -0,0 +1,511 @@ +From b501bacc6060fd62654b756469cc3091eb53de3a Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Mon, 30 Nov 2015 12:34:01 -0800 +Subject: [PATCH 8/9] drm/vc4: Add support for async pageflips. + +An async pageflip stores the modeset to be done and executes it once +the BOs are ready to be displayed. This gets us about 3x performance +in full screen rendering with pageflipping. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_crtc.c | 99 +++++++++++++++++++++++++- + drivers/gpu/drm/vc4/vc4_drv.h | 16 +++++ + drivers/gpu/drm/vc4/vc4_gem.c | 40 +++++++++++ + drivers/gpu/drm/vc4/vc4_kms.c | 149 +++++++++++++++++++++++++++++++++++++++- + drivers/gpu/drm/vc4/vc4_plane.c | 40 +++++++++++ + 5 files changed, 342 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c +index 7a9f476..a319332 100644 +--- a/drivers/gpu/drm/vc4/vc4_crtc.c ++++ b/drivers/gpu/drm/vc4/vc4_crtc.c +@@ -35,6 +35,7 @@ + #include "drm_atomic_helper.h" + #include "drm_crtc_helper.h" + #include "linux/clk.h" ++#include "drm_fb_cma_helper.h" + #include "linux/component.h" + #include "linux/of_device.h" + #include "vc4_drv.h" +@@ -475,10 +476,106 @@ static irqreturn_t vc4_crtc_irq_handler(int irq, void *data) + return ret; + } + ++struct vc4_async_flip_state { ++ struct drm_crtc *crtc; ++ struct drm_framebuffer *fb; ++ struct drm_pending_vblank_event *event; ++ ++ struct vc4_seqno_cb cb; ++}; ++ ++/* Called when the V3D execution for the BO being flipped to is done, so that ++ * we can actually update the plane's address to point to it. ++ */ ++static void ++vc4_async_page_flip_complete(struct vc4_seqno_cb *cb) ++{ ++ struct vc4_async_flip_state *flip_state = ++ container_of(cb, struct vc4_async_flip_state, cb); ++ struct drm_crtc *crtc = flip_state->crtc; ++ struct drm_device *dev = crtc->dev; ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ struct drm_plane *plane = crtc->primary; ++ ++ vc4_plane_async_set_fb(plane, flip_state->fb); ++ if (flip_state->event) { ++ unsigned long flags; ++ ++ spin_lock_irqsave(&dev->event_lock, flags); ++ drm_crtc_send_vblank_event(crtc, flip_state->event); ++ spin_unlock_irqrestore(&dev->event_lock, flags); ++ } ++ ++ drm_framebuffer_unreference(flip_state->fb); ++ kfree(flip_state); ++ ++ up(&vc4->async_modeset); ++} ++ ++/* Implements async (non-vblank-synced) page flips. ++ * ++ * The page flip ioctl needs to return immediately, so we grab the ++ * modeset semaphore on the pipe, and queue the address update for ++ * when V3D is done with the BO being flipped to. ++ */ ++static int vc4_async_page_flip(struct drm_crtc *crtc, ++ struct drm_framebuffer *fb, ++ struct drm_pending_vblank_event *event, ++ uint32_t flags) ++{ ++ struct drm_device *dev = crtc->dev; ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ struct drm_plane *plane = crtc->primary; ++ int ret = 0; ++ struct vc4_async_flip_state *flip_state; ++ struct drm_gem_cma_object *cma_bo = drm_fb_cma_get_gem_obj(fb, 0); ++ struct vc4_bo *bo = to_vc4_bo(&cma_bo->base); ++ ++ flip_state = kzalloc(sizeof(*flip_state), GFP_KERNEL); ++ if (!flip_state) ++ return -ENOMEM; ++ ++ drm_framebuffer_reference(fb); ++ flip_state->fb = fb; ++ flip_state->crtc = crtc; ++ flip_state->event = event; ++ ++ /* Make sure all other async modesetes have landed. */ ++ ret = down_interruptible(&vc4->async_modeset); ++ if (ret) { ++ kfree(flip_state); ++ return ret; ++ } ++ ++ /* Immediately update the plane's legacy fb pointer, so that later ++ * modeset prep sees the state that will be present when the semaphore ++ * is released. ++ */ ++ drm_atomic_set_fb_for_plane(plane->state, fb); ++ plane->fb = fb; ++ ++ vc4_queue_seqno_cb(dev, &flip_state->cb, bo->seqno, ++ vc4_async_page_flip_complete); ++ ++ /* Driver takes ownership of state on successful async commit. */ ++ return 0; ++} ++ ++static int vc4_page_flip(struct drm_crtc *crtc, ++ struct drm_framebuffer *fb, ++ struct drm_pending_vblank_event *event, ++ uint32_t flags) ++{ ++ if (flags & DRM_MODE_PAGE_FLIP_ASYNC) ++ return vc4_async_page_flip(crtc, fb, event, flags); ++ else ++ return drm_atomic_helper_page_flip(crtc, fb, event, flags); ++} ++ + static const struct drm_crtc_funcs vc4_crtc_funcs = { + .set_config = drm_atomic_helper_set_config, + .destroy = vc4_crtc_destroy, +- .page_flip = drm_atomic_helper_page_flip, ++ .page_flip = vc4_page_flip, + .set_property = NULL, + .cursor_set = NULL, /* handled by drm_mode_cursor_universal */ + .cursor_move = NULL, /* handled by drm_mode_cursor_universal */ +diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h +index 0bc8c57..f9927d8 100644 +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -76,6 +76,11 @@ struct vc4_dev { + wait_queue_head_t job_wait_queue; + struct work_struct job_done_work; + ++ /* List of struct vc4_seqno_cb for callbacks to be made from a ++ * workqueue when the given seqno is passed. ++ */ ++ struct list_head seqno_cb_list; ++ + /* The binner overflow memory that's currently set up in + * BPOA/BPOS registers. When overflow occurs and a new one is + * allocated, the previous one will be moved to +@@ -128,6 +133,12 @@ to_vc4_bo(struct drm_gem_object *bo) + return (struct vc4_bo *)bo; + } + ++struct vc4_seqno_cb { ++ struct work_struct work; ++ uint64_t seqno; ++ void (*func)(struct vc4_seqno_cb *cb); ++}; ++ + struct vc4_v3d { + struct platform_device *pdev; + void __iomem *regs; +@@ -384,6 +395,9 @@ void vc4_submit_next_job(struct drm_device *dev); + int vc4_wait_for_seqno(struct drm_device *dev, uint64_t seqno, + uint64_t timeout_ns, bool interruptible); + void vc4_job_handle_completed(struct vc4_dev *vc4); ++int vc4_queue_seqno_cb(struct drm_device *dev, ++ struct vc4_seqno_cb *cb, uint64_t seqno, ++ void (*func)(struct vc4_seqno_cb *cb)); + + /* vc4_hdmi.c */ + extern struct platform_driver vc4_hdmi_driver; +@@ -409,6 +423,8 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev, + enum drm_plane_type type); + u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist); + u32 vc4_plane_dlist_size(struct drm_plane_state *state); ++void vc4_plane_async_set_fb(struct drm_plane *plane, ++ struct drm_framebuffer *fb); + + /* vc4_v3d.c */ + extern struct platform_driver vc4_v3d_driver; +diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c +index 936dddf..5fb0556 100644 +--- a/drivers/gpu/drm/vc4/vc4_gem.c ++++ b/drivers/gpu/drm/vc4/vc4_gem.c +@@ -461,6 +461,7 @@ void + vc4_job_handle_completed(struct vc4_dev *vc4) + { + unsigned long irqflags; ++ struct vc4_seqno_cb *cb, *cb_temp; + + spin_lock_irqsave(&vc4->job_lock, irqflags); + while (!list_empty(&vc4->job_done_list)) { +@@ -473,7 +474,45 @@ vc4_job_handle_completed(struct vc4_dev *vc4) + vc4_complete_exec(vc4->dev, exec); + spin_lock_irqsave(&vc4->job_lock, irqflags); + } ++ ++ list_for_each_entry_safe(cb, cb_temp, &vc4->seqno_cb_list, work.entry) { ++ if (cb->seqno <= vc4->finished_seqno) { ++ list_del_init(&cb->work.entry); ++ schedule_work(&cb->work); ++ } ++ } ++ ++ spin_unlock_irqrestore(&vc4->job_lock, irqflags); ++} ++ ++static void vc4_seqno_cb_work(struct work_struct *work) ++{ ++ struct vc4_seqno_cb *cb = container_of(work, struct vc4_seqno_cb, work); ++ ++ cb->func(cb); ++} ++ ++int vc4_queue_seqno_cb(struct drm_device *dev, ++ struct vc4_seqno_cb *cb, uint64_t seqno, ++ void (*func)(struct vc4_seqno_cb *cb)) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ int ret = 0; ++ unsigned long irqflags; ++ ++ cb->func = func; ++ INIT_WORK(&cb->work, vc4_seqno_cb_work); ++ ++ spin_lock_irqsave(&vc4->job_lock, irqflags); ++ if (seqno > vc4->finished_seqno) { ++ cb->seqno = seqno; ++ list_add_tail(&cb->work.entry, &vc4->seqno_cb_list); ++ } else { ++ schedule_work(&cb->work); ++ } + spin_unlock_irqrestore(&vc4->job_lock, irqflags); ++ ++ return ret; + } + + /* Scheduled when any job has been completed, this walks the list of +@@ -610,6 +649,7 @@ vc4_gem_init(struct drm_device *dev) + + INIT_LIST_HEAD(&vc4->job_list); + INIT_LIST_HEAD(&vc4->job_done_list); ++ INIT_LIST_HEAD(&vc4->seqno_cb_list); + spin_lock_init(&vc4->job_lock); + + INIT_WORK(&vc4->hangcheck.reset_work, vc4_reset_work); +diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c +index 2e5597d..f95f2df 100644 +--- a/drivers/gpu/drm/vc4/vc4_kms.c ++++ b/drivers/gpu/drm/vc4/vc4_kms.c +@@ -15,6 +15,7 @@ + */ + + #include "drm_crtc.h" ++#include "drm_atomic.h" + #include "drm_atomic_helper.h" + #include "drm_crtc_helper.h" + #include "drm_plane_helper.h" +@@ -29,10 +30,152 @@ static void vc4_output_poll_changed(struct drm_device *dev) + drm_fbdev_cma_hotplug_event(vc4->fbdev); + } + ++struct vc4_commit { ++ struct drm_device *dev; ++ struct drm_atomic_state *state; ++ struct vc4_seqno_cb cb; ++}; ++ ++static void ++vc4_atomic_complete_commit(struct vc4_commit *c) ++{ ++ struct drm_atomic_state *state = c->state; ++ struct drm_device *dev = state->dev; ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ ++ drm_atomic_helper_commit_modeset_disables(dev, state); ++ ++ drm_atomic_helper_commit_planes(dev, state, false); ++ ++ drm_atomic_helper_commit_modeset_enables(dev, state); ++ ++ drm_atomic_helper_wait_for_vblanks(dev, state); ++ ++ drm_atomic_helper_cleanup_planes(dev, state); ++ ++ drm_atomic_state_free(state); ++ ++ up(&vc4->async_modeset); ++ ++ kfree(c); ++} ++ ++static void ++vc4_atomic_complete_commit_seqno_cb(struct vc4_seqno_cb *cb) ++{ ++ struct vc4_commit *c = container_of(cb, struct vc4_commit, cb); ++ ++ vc4_atomic_complete_commit(c); ++} ++ ++static struct vc4_commit *commit_init(struct drm_atomic_state *state) ++{ ++ struct vc4_commit *c = kzalloc(sizeof(*c), GFP_KERNEL); ++ ++ if (!c) ++ return NULL; ++ c->dev = state->dev; ++ c->state = state; ++ ++ return c; ++} ++ ++/** ++ * vc4_atomic_commit - commit validated state object ++ * @dev: DRM device ++ * @state: the driver state object ++ * @async: asynchronous commit ++ * ++ * This function commits a with drm_atomic_helper_check() pre-validated state ++ * object. This can still fail when e.g. the framebuffer reservation fails. For ++ * now this doesn't implement asynchronous commits. ++ * ++ * RETURNS ++ * Zero for success or -errno. ++ */ ++static int vc4_atomic_commit(struct drm_device *dev, ++ struct drm_atomic_state *state, ++ bool async) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ int ret; ++ int i; ++ uint64_t wait_seqno = 0; ++ struct vc4_commit *c; ++ ++ c = commit_init(state); ++ if (!c) ++ return -ENOMEM; ++ ++ /* Make sure that any outstanding modesets have finished. */ ++ ret = down_interruptible(&vc4->async_modeset); ++ if (ret) { ++ kfree(c); ++ return ret; ++ } ++ ++ ret = drm_atomic_helper_prepare_planes(dev, state); ++ if (ret) { ++ kfree(c); ++ up(&vc4->async_modeset); ++ return ret; ++ } ++ ++ for (i = 0; i < dev->mode_config.num_total_plane; i++) { ++ struct drm_plane *plane = state->planes[i]; ++ struct drm_plane_state *new_state = state->plane_states[i]; ++ ++ if (!plane) ++ continue; ++ ++ if ((plane->state->fb != new_state->fb) && new_state->fb) { ++ struct drm_gem_cma_object *cma_bo = ++ drm_fb_cma_get_gem_obj(new_state->fb, 0); ++ struct vc4_bo *bo = to_vc4_bo(&cma_bo->base); ++ ++ wait_seqno = max(bo->seqno, wait_seqno); ++ } ++ } ++ ++ /* ++ * This is the point of no return - everything below never fails except ++ * when the hw goes bonghits. Which means we can commit the new state on ++ * the software side now. ++ */ ++ ++ drm_atomic_helper_swap_state(dev, state); ++ ++ /* ++ * Everything below can be run asynchronously without the need to grab ++ * any modeset locks at all under one condition: It must be guaranteed ++ * that the asynchronous work has either been cancelled (if the driver ++ * supports it, which at least requires that the framebuffers get ++ * cleaned up with drm_atomic_helper_cleanup_planes()) or completed ++ * before the new state gets committed on the software side with ++ * drm_atomic_helper_swap_state(). ++ * ++ * This scheme allows new atomic state updates to be prepared and ++ * checked in parallel to the asynchronous completion of the previous ++ * update. Which is important since compositors need to figure out the ++ * composition of the next frame right after having submitted the ++ * current layout. ++ */ ++ ++ if (async) { ++ vc4_queue_seqno_cb(dev, &c->cb, wait_seqno, ++ vc4_atomic_complete_commit_seqno_cb); ++ } else { ++ vc4_wait_for_seqno(dev, wait_seqno, ~0ull, false); ++ vc4_atomic_complete_commit(c); ++ } ++ ++ return 0; ++} ++ + static const struct drm_mode_config_funcs vc4_mode_funcs = { + .output_poll_changed = vc4_output_poll_changed, + .atomic_check = drm_atomic_helper_check, +- .atomic_commit = drm_atomic_helper_commit, ++ .atomic_commit = vc4_atomic_commit, + .fb_create = drm_fb_cma_create, + }; + +@@ -41,6 +184,8 @@ int vc4_kms_load(struct drm_device *dev) + struct vc4_dev *vc4 = to_vc4_dev(dev); + int ret; + ++ sema_init(&vc4->async_modeset, 1); ++ + ret = drm_vblank_init(dev, dev->mode_config.num_crtc); + if (ret < 0) { + dev_err(dev->dev, "failed to initialize vblank\n"); +@@ -51,6 +196,8 @@ int vc4_kms_load(struct drm_device *dev) + dev->mode_config.max_height = 2048; + dev->mode_config.funcs = &vc4_mode_funcs; + dev->mode_config.preferred_depth = 24; ++ dev->mode_config.async_page_flip = true; ++ + dev->vblank_disable_allowed = true; + + drm_mode_config_reset(dev); +diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c +index cdd8b10..db32c373 100644 +--- a/drivers/gpu/drm/vc4/vc4_plane.c ++++ b/drivers/gpu/drm/vc4/vc4_plane.c +@@ -29,6 +29,14 @@ struct vc4_plane_state { + u32 *dlist; + u32 dlist_size; /* Number of dwords in allocated for the display list */ + u32 dlist_count; /* Number of used dwords in the display list. */ ++ ++ /* Offset in the dlist to pointer word 0. */ ++ u32 pw0_offset; ++ ++ /* Offset where the plane's dlist was last stored in the ++ hardware at vc4_crtc_atomic_flush() time. ++ */ ++ u32 *hw_dlist; + }; + + static inline struct vc4_plane_state * +@@ -197,6 +205,8 @@ static int vc4_plane_mode_set(struct drm_plane *plane, + /* Position Word 3: Context. Written by the HVS. */ + vc4_dlist_write(vc4_state, 0xc0c0c0c0); + ++ vc4_state->pw0_offset = vc4_state->dlist_count; ++ + /* Pointer Word 0: RGB / Y Pointer */ + vc4_dlist_write(vc4_state, bo->paddr + offset); + +@@ -248,6 +258,8 @@ u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist) + struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state); + int i; + ++ vc4_state->hw_dlist = dlist; ++ + /* Can't memcpy_toio() because it needs to be 32-bit writes. */ + for (i = 0; i < vc4_state->dlist_count; i++) + writel(vc4_state->dlist[i], &dlist[i]); +@@ -262,6 +274,34 @@ u32 vc4_plane_dlist_size(struct drm_plane_state *state) + return vc4_state->dlist_count; + } + ++/* Updates the plane to immediately (well, once the FIFO needs ++ * refilling) scan out from at a new framebuffer. ++ */ ++void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb) ++{ ++ struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state); ++ struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0); ++ uint32_t addr; ++ ++ /* We're skipping the address adjustment for negative origin, ++ * because this is only called on the primary plane. ++ */ ++ WARN_ON_ONCE(plane->state->crtc_x < 0 || plane->state->crtc_y < 0); ++ addr = bo->paddr + fb->offsets[0]; ++ ++ /* Write the new address into the hardware immediately. The ++ * scanout will start from this address as soon as the FIFO ++ * needs to refill with pixels. ++ */ ++ writel(addr, &vc4_state->hw_dlist[vc4_state->pw0_offset]); ++ ++ /* Also update the CPU-side dlist copy, so that any later ++ * atomic updates that don't do a new modeset on our plane ++ * also use our updated address. ++ */ ++ vc4_state->dlist[vc4_state->pw0_offset] = addr; ++} ++ + static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = { + .prepare_fb = NULL, + .cleanup_fb = NULL, +-- +2.6.4 + diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-0109-drm-vc4-Add-an-interface-for-capturing-the-GPU-state.patch b/kernel/kernel/files/patches/mageia/gpu-drm-0109-drm-vc4-Add-an-interface-for-capturing-the-GPU-state.patch new file mode 100644 index 00000000..1b6c6514 --- /dev/null +++ b/kernel/kernel/files/patches/mageia/gpu-drm-0109-drm-vc4-Add-an-interface-for-capturing-the-GPU-state.patch @@ -0,0 +1,333 @@ +From 214613656b5179f0daab6e0a080814b5100d45f0 Mon Sep 17 00:00:00 2001 +From: Eric Anholt +Date: Fri, 30 Oct 2015 10:09:02 -0700 +Subject: [PATCH 9/9] drm/vc4: Add an interface for capturing the GPU state + after a hang. + +This can be parsed with vc4-gpu-tools tools for trying to figure out +what was going on. + +v2: Use __u32-style types. + +Signed-off-by: Eric Anholt +--- + drivers/gpu/drm/vc4/vc4_drv.c | 2 + + drivers/gpu/drm/vc4/vc4_drv.h | 4 + + drivers/gpu/drm/vc4/vc4_gem.c | 185 ++++++++++++++++++++++++++++++++++++++++++ + include/uapi/drm/vc4_drm.h | 45 ++++++++++ + 4 files changed, 236 insertions(+) + +diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c +index 2cfee59..97226b6 100644 +--- a/drivers/gpu/drm/vc4/vc4_drv.c ++++ b/drivers/gpu/drm/vc4/vc4_drv.c +@@ -80,6 +80,8 @@ static const struct drm_ioctl_desc vc4_drm_ioctls[] = { + DRM_IOCTL_DEF_DRV(VC4_CREATE_BO, vc4_create_bo_ioctl, 0), + DRM_IOCTL_DEF_DRV(VC4_MMAP_BO, vc4_mmap_bo_ioctl, 0), + DRM_IOCTL_DEF_DRV(VC4_CREATE_SHADER_BO, vc4_create_shader_bo_ioctl, 0), ++ DRM_IOCTL_DEF_DRV(VC4_GET_HANG_STATE, vc4_get_hang_state_ioctl, ++ DRM_ROOT_ONLY), + }; + + static struct drm_driver vc4_drm_driver = { +diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h +index f9927d8..080865e 100644 +--- a/drivers/gpu/drm/vc4/vc4_drv.h ++++ b/drivers/gpu/drm/vc4/vc4_drv.h +@@ -19,6 +19,8 @@ struct vc4_dev { + + struct drm_fbdev_cma *fbdev; + ++ struct vc4_hang_state *hang_state; ++ + /* The kernel-space BO cache. Tracks buffers that have been + * unreferenced by all other users (refcounts of 0!) but not + * yet freed, so we can do cheap allocations. +@@ -361,6 +363,8 @@ int vc4_create_shader_bo_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); + int vc4_mmap_bo_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); ++int vc4_get_hang_state_ioctl(struct drm_device *dev, void *data, ++ struct drm_file *file_priv); + int vc4_mmap(struct file *filp, struct vm_area_struct *vma); + int vc4_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma); + void *vc4_prime_vmap(struct drm_gem_object *obj); +diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c +index 5fb0556..39f29e7 100644 +--- a/drivers/gpu/drm/vc4/vc4_gem.c ++++ b/drivers/gpu/drm/vc4/vc4_gem.c +@@ -40,6 +40,186 @@ vc4_queue_hangcheck(struct drm_device *dev) + round_jiffies_up(jiffies + msecs_to_jiffies(100))); + } + ++struct vc4_hang_state { ++ struct drm_vc4_get_hang_state user_state; ++ ++ u32 bo_count; ++ struct drm_gem_object **bo; ++}; ++ ++static void ++vc4_free_hang_state(struct drm_device *dev, struct vc4_hang_state *state) ++{ ++ unsigned int i; ++ ++ mutex_lock(&dev->struct_mutex); ++ for (i = 0; i < state->user_state.bo_count; i++) ++ drm_gem_object_unreference(state->bo[i]); ++ mutex_unlock(&dev->struct_mutex); ++ ++ kfree(state); ++} ++ ++int ++vc4_get_hang_state_ioctl(struct drm_device *dev, void *data, ++ struct drm_file *file_priv) ++{ ++ struct drm_vc4_get_hang_state *get_state = data; ++ struct drm_vc4_get_hang_state_bo *bo_state; ++ struct vc4_hang_state *kernel_state; ++ struct drm_vc4_get_hang_state *state; ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ unsigned long irqflags; ++ u32 i; ++ int ret; ++ ++ spin_lock_irqsave(&vc4->job_lock, irqflags); ++ kernel_state = vc4->hang_state; ++ if (!kernel_state) { ++ spin_unlock_irqrestore(&vc4->job_lock, irqflags); ++ return -ENOENT; ++ } ++ state = &kernel_state->user_state; ++ ++ /* If the user's array isn't big enough, just return the ++ * required array size. ++ */ ++ if (get_state->bo_count < state->bo_count) { ++ get_state->bo_count = state->bo_count; ++ spin_unlock_irqrestore(&vc4->job_lock, irqflags); ++ return 0; ++ } ++ ++ vc4->hang_state = NULL; ++ spin_unlock_irqrestore(&vc4->job_lock, irqflags); ++ ++ /* Save the user's BO pointer, so we don't stomp it with the memcpy. */ ++ state->bo = get_state->bo; ++ memcpy(get_state, state, sizeof(*state)); ++ ++ bo_state = kcalloc(state->bo_count, sizeof(*bo_state), GFP_KERNEL); ++ if (!bo_state) { ++ ret = -ENOMEM; ++ goto err_free; ++ } ++ ++ for (i = 0; i < state->bo_count; i++) { ++ struct vc4_bo *vc4_bo = to_vc4_bo(kernel_state->bo[i]); ++ u32 handle; ++ ++ ret = drm_gem_handle_create(file_priv, kernel_state->bo[i], ++ &handle); ++ ++ if (ret) { ++ state->bo_count = i - 1; ++ goto err; ++ } ++ bo_state[i].handle = handle; ++ bo_state[i].paddr = vc4_bo->base.paddr; ++ bo_state[i].size = vc4_bo->base.base.size; ++ } ++ ++ ret = copy_to_user((void __user *)(uintptr_t)get_state->bo, ++ bo_state, ++ state->bo_count * sizeof(*bo_state)); ++ kfree(bo_state); ++ ++err_free: ++ ++ vc4_free_hang_state(dev, kernel_state); ++ ++err: ++ return ret; ++} ++ ++static void ++vc4_save_hang_state(struct drm_device *dev) ++{ ++ struct vc4_dev *vc4 = to_vc4_dev(dev); ++ struct drm_vc4_get_hang_state *state; ++ struct vc4_hang_state *kernel_state; ++ struct vc4_exec_info *exec; ++ struct vc4_bo *bo; ++ unsigned long irqflags; ++ unsigned int i, unref_list_count; ++ ++ kernel_state = kcalloc(1, sizeof(*state), GFP_KERNEL); ++ if (!kernel_state) ++ return; ++ ++ state = &kernel_state->user_state; ++ ++ spin_lock_irqsave(&vc4->job_lock, irqflags); ++ exec = vc4_first_job(vc4); ++ if (!exec) { ++ spin_unlock_irqrestore(&vc4->job_lock, irqflags); ++ return; ++ } ++ ++ unref_list_count = 0; ++ list_for_each_entry(bo, &exec->unref_list, unref_head) ++ unref_list_count++; ++ ++ state->bo_count = exec->bo_count + unref_list_count; ++ kernel_state->bo = kcalloc(state->bo_count, sizeof(*kernel_state->bo), ++ GFP_ATOMIC); ++ if (!kernel_state->bo) { ++ spin_unlock_irqrestore(&vc4->job_lock, irqflags); ++ return; ++ } ++ ++ for (i = 0; i < exec->bo_count; i++) { ++ drm_gem_object_reference(&exec->bo[i]->base); ++ kernel_state->bo[i] = &exec->bo[i]->base; ++ } ++ ++ list_for_each_entry(bo, &exec->unref_list, unref_head) { ++ drm_gem_object_reference(&bo->base.base); ++ kernel_state->bo[i] = &bo->base.base; ++ i++; ++ } ++ ++ state->start_bin = exec->ct0ca; ++ state->start_render = exec->ct1ca; ++ ++ spin_unlock_irqrestore(&vc4->job_lock, irqflags); ++ ++ state->ct0ca = V3D_READ(V3D_CTNCA(0)); ++ state->ct0ea = V3D_READ(V3D_CTNEA(0)); ++ ++ state->ct1ca = V3D_READ(V3D_CTNCA(1)); ++ state->ct1ea = V3D_READ(V3D_CTNEA(1)); ++ ++ state->ct0cs = V3D_READ(V3D_CTNCS(0)); ++ state->ct1cs = V3D_READ(V3D_CTNCS(1)); ++ ++ state->ct0ra0 = V3D_READ(V3D_CT00RA0); ++ state->ct1ra0 = V3D_READ(V3D_CT01RA0); ++ ++ state->bpca = V3D_READ(V3D_BPCA); ++ state->bpcs = V3D_READ(V3D_BPCS); ++ state->bpoa = V3D_READ(V3D_BPOA); ++ state->bpos = V3D_READ(V3D_BPOS); ++ ++ state->vpmbase = V3D_READ(V3D_VPMBASE); ++ ++ state->dbge = V3D_READ(V3D_DBGE); ++ state->fdbgo = V3D_READ(V3D_FDBGO); ++ state->fdbgb = V3D_READ(V3D_FDBGB); ++ state->fdbgr = V3D_READ(V3D_FDBGR); ++ state->fdbgs = V3D_READ(V3D_FDBGS); ++ state->errstat = V3D_READ(V3D_ERRSTAT); ++ ++ spin_lock_irqsave(&vc4->job_lock, irqflags); ++ if (vc4->hang_state) { ++ spin_unlock_irqrestore(&vc4->job_lock, irqflags); ++ vc4_free_hang_state(dev, kernel_state); ++ } else { ++ vc4->hang_state = kernel_state; ++ spin_unlock_irqrestore(&vc4->job_lock, irqflags); ++ } ++} ++ + static void + vc4_reset(struct drm_device *dev) + { +@@ -64,6 +244,8 @@ vc4_reset_work(struct work_struct *work) + struct vc4_dev *vc4 = + container_of(work, struct vc4_dev, hangcheck.reset_work); + ++ vc4_save_hang_state(vc4->dev); ++ + vc4_reset(vc4->dev); + } + +@@ -679,4 +861,7 @@ vc4_gem_destroy(struct drm_device *dev) + } + + vc4_bo_cache_destroy(dev); ++ ++ if (vc4->hang_state) ++ vc4_free_hang_state(dev, vc4->hang_state); + } +diff --git a/include/uapi/drm/vc4_drm.h b/include/uapi/drm/vc4_drm.h +index fe4161b..eeb37e3 100644 +--- a/include/uapi/drm/vc4_drm.h ++++ b/include/uapi/drm/vc4_drm.h +@@ -32,6 +32,7 @@ + #define DRM_VC4_CREATE_BO 0x03 + #define DRM_VC4_MMAP_BO 0x04 + #define DRM_VC4_CREATE_SHADER_BO 0x05 ++#define DRM_VC4_GET_HANG_STATE 0x06 + + #define DRM_IOCTL_VC4_SUBMIT_CL DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_SUBMIT_CL, struct drm_vc4_submit_cl) + #define DRM_IOCTL_VC4_WAIT_SEQNO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_WAIT_SEQNO, struct drm_vc4_wait_seqno) +@@ -39,6 +40,7 @@ + #define DRM_IOCTL_VC4_CREATE_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_CREATE_BO, struct drm_vc4_create_bo) + #define DRM_IOCTL_VC4_MMAP_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_MMAP_BO, struct drm_vc4_mmap_bo) + #define DRM_IOCTL_VC4_CREATE_SHADER_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_CREATE_SHADER_BO, struct drm_vc4_create_shader_bo) ++#define DRM_IOCTL_VC4_GET_HANG_STATE DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_GET_HANG_STATE, struct drm_vc4_get_hang_state) + + struct drm_vc4_submit_rcl_surface { + __u32 hindex; /* Handle index, or ~0 if not present. */ +@@ -231,4 +233,47 @@ struct drm_vc4_create_shader_bo { + __u32 pad; + }; + ++struct drm_vc4_get_hang_state_bo { ++ __u32 handle; ++ __u32 paddr; ++ __u32 size; ++ __u32 pad; ++}; ++ ++/** ++ * struct drm_vc4_hang_state - ioctl argument for collecting state ++ * from a GPU hang for analysis. ++*/ ++struct drm_vc4_get_hang_state { ++ /** Pointer to array of struct drm_vc4_get_hang_state_bo. */ ++ __u64 bo; ++ /** ++ * On input, the size of the bo array. Output is the number ++ * of bos to be returned. ++ */ ++ __u32 bo_count; ++ ++ __u32 start_bin, start_render; ++ ++ __u32 ct0ca, ct0ea; ++ __u32 ct1ca, ct1ea; ++ __u32 ct0cs, ct1cs; ++ __u32 ct0ra0, ct1ra0; ++ ++ __u32 bpca, bpcs; ++ __u32 bpoa, bpos; ++ ++ __u32 vpmbase; ++ ++ __u32 dbge; ++ __u32 fdbgo; ++ __u32 fdbgb; ++ __u32 fdbgr; ++ __u32 fdbgs; ++ __u32 errstat; ++ ++ /* Pad that we may save more registers into in the future. */ ++ __u32 pad[16]; ++}; ++ + #endif /* _UAPI_VC4_DRM_H_ */ +-- +2.6.4 + diff --git a/kernel/kernel/files/patches/mageia/gpu-drm-Fix-an-unwanted-master-inheritance-v2.patch b/kernel/kernel/files/patches/mageia/gpu-drm-Fix-an-unwanted-master-inheritance-v2.patch deleted file mode 100644 index a4036782..00000000 --- a/kernel/kernel/files/patches/mageia/gpu-drm-Fix-an-unwanted-master-inheritance-v2.patch +++ /dev/null @@ -1,163 +0,0 @@ -From a0af2e538c80f3e47f1d6ddf120a153ad909e8ad Mon Sep 17 00:00:00 2001 -From: Thomas Hellstrom -Date: Wed, 2 Dec 2015 09:24:46 -0800 -Subject: drm: Fix an unwanted master inheritance v2 - -A client calling drmSetMaster() using a file descriptor that was opened -when another client was master would inherit the latter client's master -object and all its authenticated clients. - -This is unwanted behaviour, and when this happens, instead allocate a -brand new master object for the client calling drmSetMaster(). - -Fixes a BUG() throw in vmw_master_set(). - -Cc: -Signed-off-by: Thomas Hellstrom -Signed-off-by: Dave Airlie - -diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c -index 9362609..7dd6728 100644 ---- a/drivers/gpu/drm/drm_drv.c -+++ b/drivers/gpu/drm/drm_drv.c -@@ -160,6 +160,11 @@ int drm_setmaster_ioctl(struct drm_device *dev, void *data, - goto out_unlock; - } - -+ if (!file_priv->allowed_master) { -+ ret = drm_new_set_master(dev, file_priv); -+ goto out_unlock; -+ } -+ - file_priv->minor->master = drm_master_get(file_priv->master); - file_priv->is_master = 1; - if (dev->driver->master_set) { -diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c -index c59ce4d..6b5625e 100644 ---- a/drivers/gpu/drm/drm_fops.c -+++ b/drivers/gpu/drm/drm_fops.c -@@ -126,6 +126,60 @@ static int drm_cpu_valid(void) - } - - /** -+ * drm_new_set_master - Allocate a new master object and become master for the -+ * associated master realm. -+ * -+ * @dev: The associated device. -+ * @fpriv: File private identifying the client. -+ * -+ * This function must be called with dev::struct_mutex held. -+ * Returns negative error code on failure. Zero on success. -+ */ -+int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv) -+{ -+ struct drm_master *old_master; -+ int ret; -+ -+ lockdep_assert_held_once(&dev->master_mutex); -+ -+ /* create a new master */ -+ fpriv->minor->master = drm_master_create(fpriv->minor); -+ if (!fpriv->minor->master) -+ return -ENOMEM; -+ -+ /* take another reference for the copy in the local file priv */ -+ old_master = fpriv->master; -+ fpriv->master = drm_master_get(fpriv->minor->master); -+ -+ if (dev->driver->master_create) { -+ ret = dev->driver->master_create(dev, fpriv->master); -+ if (ret) -+ goto out_err; -+ } -+ if (dev->driver->master_set) { -+ ret = dev->driver->master_set(dev, fpriv, true); -+ if (ret) -+ goto out_err; -+ } -+ -+ fpriv->is_master = 1; -+ fpriv->allowed_master = 1; -+ fpriv->authenticated = 1; -+ if (old_master) -+ drm_master_put(&old_master); -+ -+ return 0; -+ -+out_err: -+ /* drop both references and restore old master on failure */ -+ drm_master_put(&fpriv->minor->master); -+ drm_master_put(&fpriv->master); -+ fpriv->master = old_master; -+ -+ return ret; -+} -+ -+/** - * Called whenever a process opens /dev/drm. - * - * \param filp file pointer. -@@ -189,35 +243,9 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor) - mutex_lock(&dev->master_mutex); - if (drm_is_primary_client(priv) && !priv->minor->master) { - /* create a new master */ -- priv->minor->master = drm_master_create(priv->minor); -- if (!priv->minor->master) { -- ret = -ENOMEM; -+ ret = drm_new_set_master(dev, priv); -+ if (ret) - goto out_close; -- } -- -- priv->is_master = 1; -- /* take another reference for the copy in the local file priv */ -- priv->master = drm_master_get(priv->minor->master); -- priv->authenticated = 1; -- -- if (dev->driver->master_create) { -- ret = dev->driver->master_create(dev, priv->master); -- if (ret) { -- /* drop both references if this fails */ -- drm_master_put(&priv->minor->master); -- drm_master_put(&priv->master); -- goto out_close; -- } -- } -- if (dev->driver->master_set) { -- ret = dev->driver->master_set(dev, priv, true); -- if (ret) { -- /* drop both references if this fails */ -- drm_master_put(&priv->minor->master); -- drm_master_put(&priv->master); -- goto out_close; -- } -- } - } else if (drm_is_primary_client(priv)) { - /* get a reference to the master */ - priv->master = drm_master_get(priv->minor->master); -diff --git a/include/drm/drmP.h b/include/drm/drmP.h -index 0b921ae..441b26e 100644 ---- a/include/drm/drmP.h -+++ b/include/drm/drmP.h -@@ -309,6 +309,11 @@ struct drm_file { - unsigned universal_planes:1; - /* true if client understands atomic properties */ - unsigned atomic:1; -+ /* -+ * This client is allowed to gain master privileges for @master. -+ * Protected by struct drm_device::master_mutex. -+ */ -+ unsigned allowed_master:1; - - struct pid *pid; - kuid_t uid; -@@ -910,6 +915,7 @@ extern int drm_open(struct inode *inode, struct file *filp); - extern ssize_t drm_read(struct file *filp, char __user *buffer, - size_t count, loff_t *offset); - extern int drm_release(struct inode *inode, struct file *filp); -+extern int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv); - - /* Mapping support (drm_vm.h) */ - extern unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait); --- -cgit v0.10.2 diff --git a/kernel/kernel/files/patches/mageia/net-netfilter-Fix-removal-of-GRE-expectation-entries-created-by-PPTP.patch b/kernel/kernel/files/patches/mageia/net-netfilter-Fix-removal-of-GRE-expectation-entries-created-by-PPTP.patch deleted file mode 100644 index 8f0b8b28..00000000 --- a/kernel/kernel/files/patches/mageia/net-netfilter-Fix-removal-of-GRE-expectation-entries-created-by-PPTP.patch +++ /dev/null @@ -1,31 +0,0 @@ -From c255cb2ed3c7960b2c68f45de1dc0ac2197c8f78 Mon Sep 17 00:00:00 2001 -From: Anthony Lineham -Date: Thu, 22 Oct 2015 11:17:03 +1300 -Subject: netfilter: Fix removal of GRE expectation entries created by PPTP - -The uninitialized tuple structure caused incorrect hash calculation -and the lookup failed. - -Link: https://bugzilla.kernel.org/show_bug.cgi?id=106441 -Signed-off-by: Anthony Lineham -Signed-off-by: Pablo Neira Ayuso ---- - net/ipv4/netfilter/nf_nat_pptp.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/net/ipv4/netfilter/nf_nat_pptp.c b/net/ipv4/netfilter/nf_nat_pptp.c -index 657d230..b3ca21b 100644 ---- a/net/ipv4/netfilter/nf_nat_pptp.c -+++ b/net/ipv4/netfilter/nf_nat_pptp.c -@@ -45,7 +45,7 @@ static void pptp_nat_expected(struct nf_conn *ct, - struct net *net = nf_ct_net(ct); - const struct nf_conn *master = ct->master; - struct nf_conntrack_expect *other_exp; -- struct nf_conntrack_tuple t; -+ struct nf_conntrack_tuple t = {}; - const struct nf_ct_pptp_master *ct_pptp_info; - const struct nf_nat_pptp *nat_pptp_info; - struct nf_nat_range range; --- -cgit v0.11.2 - diff --git a/kernel/kernel/files/patches/mageia/net-netfilter-ipset-Fix-extension-alignment.patch b/kernel/kernel/files/patches/mageia/net-netfilter-ipset-Fix-extension-alignment.patch deleted file mode 100644 index 08f0e078..00000000 --- a/kernel/kernel/files/patches/mageia/net-netfilter-ipset-Fix-extension-alignment.patch +++ /dev/null @@ -1,481 +0,0 @@ -From 95ad1f4a9358dff1dcf84bf5c9cc84caa9215f7f Mon Sep 17 00:00:00 2001 -From: Jozsef Kadlecsik -Date: Sat, 7 Nov 2015 11:21:47 +0100 -Subject: netfilter: ipset: Fix extension alignment - -The data extensions in ipset lacked the proper memory alignment and -thus could lead to kernel crash on several architectures. Therefore -the structures have been reorganized and alignment attributes added -where needed. The patch was tested on armv7h by Gerhard Wiesinger and -on x86_64, sparc64 by Jozsef Kadlecsik. - -Reported-by: Gerhard Wiesinger -Tested-by: Gerhard Wiesinger -Tested-by: Jozsef Kadlecsik -Signed-off-by: Jozsef Kadlecsik ---- - include/linux/netfilter/ipset/ip_set.h | 2 +- - net/netfilter/ipset/ip_set_bitmap_gen.h | 17 +++----- - net/netfilter/ipset/ip_set_bitmap_ip.c | 14 ++----- - net/netfilter/ipset/ip_set_bitmap_ipmac.c | 64 ++++++++++++++----------------- - net/netfilter/ipset/ip_set_bitmap_port.c | 18 ++++----- - net/netfilter/ipset/ip_set_core.c | 14 ++++--- - net/netfilter/ipset/ip_set_hash_gen.h | 11 ++++-- - net/netfilter/ipset/ip_set_list_set.c | 5 ++- - 8 files changed, 65 insertions(+), 80 deletions(-) - -diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h -index 48bb01e..0e1f433 100644 ---- a/include/linux/netfilter/ipset/ip_set.h -+++ b/include/linux/netfilter/ipset/ip_set.h -@@ -421,7 +421,7 @@ extern void ip_set_free(void *members); - extern int ip_set_get_ipaddr4(struct nlattr *nla, __be32 *ipaddr); - extern int ip_set_get_ipaddr6(struct nlattr *nla, union nf_inet_addr *ipaddr); - extern size_t ip_set_elem_len(struct ip_set *set, struct nlattr *tb[], -- size_t len); -+ size_t len, size_t align); - extern int ip_set_get_extensions(struct ip_set *set, struct nlattr *tb[], - struct ip_set_ext *ext); - -diff --git a/net/netfilter/ipset/ip_set_bitmap_gen.h b/net/netfilter/ipset/ip_set_bitmap_gen.h -index d05e759..b0bc475 100644 ---- a/net/netfilter/ipset/ip_set_bitmap_gen.h -+++ b/net/netfilter/ipset/ip_set_bitmap_gen.h -@@ -33,7 +33,7 @@ - #define mtype_gc IPSET_TOKEN(MTYPE, _gc) - #define mtype MTYPE - --#define get_ext(set, map, id) ((map)->extensions + (set)->dsize * (id)) -+#define get_ext(set, map, id) ((map)->extensions + ((set)->dsize * (id))) - - static void - mtype_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set)) -@@ -67,12 +67,9 @@ mtype_destroy(struct ip_set *set) - del_timer_sync(&map->gc); - - ip_set_free(map->members); -- if (set->dsize) { -- if (set->extensions & IPSET_EXT_DESTROY) -- mtype_ext_cleanup(set); -- ip_set_free(map->extensions); -- } -- kfree(map); -+ if (set->dsize && set->extensions & IPSET_EXT_DESTROY) -+ mtype_ext_cleanup(set); -+ ip_set_free(map); - - set->data = NULL; - } -@@ -92,16 +89,14 @@ mtype_head(struct ip_set *set, struct sk_buff *skb) - { - const struct mtype *map = set->data; - struct nlattr *nested; -+ size_t memsize = sizeof(*map) + map->memsize; - - nested = ipset_nest_start(skb, IPSET_ATTR_DATA); - if (!nested) - goto nla_put_failure; - if (mtype_do_head(skb, map) || - nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) || -- nla_put_net32(skb, IPSET_ATTR_MEMSIZE, -- htonl(sizeof(*map) + -- map->memsize + -- set->dsize * map->elements))) -+ nla_put_net32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize))) - goto nla_put_failure; - if (unlikely(ip_set_put_flags(skb, set))) - goto nla_put_failure; -diff --git a/net/netfilter/ipset/ip_set_bitmap_ip.c b/net/netfilter/ipset/ip_set_bitmap_ip.c -index 64a5643..4783eff 100644 ---- a/net/netfilter/ipset/ip_set_bitmap_ip.c -+++ b/net/netfilter/ipset/ip_set_bitmap_ip.c -@@ -41,7 +41,6 @@ MODULE_ALIAS("ip_set_bitmap:ip"); - /* Type structure */ - struct bitmap_ip { - void *members; /* the set members */ -- void *extensions; /* data extensions */ - u32 first_ip; /* host byte order, included in range */ - u32 last_ip; /* host byte order, included in range */ - u32 elements; /* number of max elements in the set */ -@@ -49,6 +48,8 @@ struct bitmap_ip { - size_t memsize; /* members size */ - u8 netmask; /* subnet netmask */ - struct timer_list gc; /* garbage collection */ -+ unsigned char extensions[0] /* data extensions */ -+ __aligned(__alignof__(u64)); - }; - - /* ADT structure for generic function args */ -@@ -224,13 +225,6 @@ init_map_ip(struct ip_set *set, struct bitmap_ip *map, - map->members = ip_set_alloc(map->memsize); - if (!map->members) - return false; -- if (set->dsize) { -- map->extensions = ip_set_alloc(set->dsize * elements); -- if (!map->extensions) { -- kfree(map->members); -- return false; -- } -- } - map->first_ip = first_ip; - map->last_ip = last_ip; - map->elements = elements; -@@ -316,13 +310,13 @@ bitmap_ip_create(struct net *net, struct ip_set *set, struct nlattr *tb[], - pr_debug("hosts %u, elements %llu\n", - hosts, (unsigned long long)elements); - -- map = kzalloc(sizeof(*map), GFP_KERNEL); -+ set->dsize = ip_set_elem_len(set, tb, 0, 0); -+ map = ip_set_alloc(sizeof(*map) + elements * set->dsize); - if (!map) - return -ENOMEM; - - map->memsize = bitmap_bytes(0, elements - 1); - set->variant = &bitmap_ip; -- set->dsize = ip_set_elem_len(set, tb, 0); - if (!init_map_ip(set, map, first_ip, last_ip, - elements, hosts, netmask)) { - kfree(map); -diff --git a/net/netfilter/ipset/ip_set_bitmap_ipmac.c b/net/netfilter/ipset/ip_set_bitmap_ipmac.c -index 1430535..29dde20 100644 ---- a/net/netfilter/ipset/ip_set_bitmap_ipmac.c -+++ b/net/netfilter/ipset/ip_set_bitmap_ipmac.c -@@ -47,24 +47,26 @@ enum { - /* Type structure */ - struct bitmap_ipmac { - void *members; /* the set members */ -- void *extensions; /* MAC + data extensions */ - u32 first_ip; /* host byte order, included in range */ - u32 last_ip; /* host byte order, included in range */ - u32 elements; /* number of max elements in the set */ - size_t memsize; /* members size */ - struct timer_list gc; /* garbage collector */ -+ unsigned char extensions[0] /* MAC + data extensions */ -+ __aligned(__alignof__(u64)); - }; - - /* ADT structure for generic function args */ - struct bitmap_ipmac_adt_elem { -+ unsigned char ether[ETH_ALEN] __aligned(2); - u16 id; -- unsigned char *ether; -+ u16 add_mac; - }; - - struct bitmap_ipmac_elem { - unsigned char ether[ETH_ALEN]; - unsigned char filled; --} __attribute__ ((aligned)); -+} __aligned(__alignof__(u64)); - - static inline u32 - ip_to_id(const struct bitmap_ipmac *m, u32 ip) -@@ -72,11 +74,11 @@ ip_to_id(const struct bitmap_ipmac *m, u32 ip) - return ip - m->first_ip; - } - --static inline struct bitmap_ipmac_elem * --get_elem(void *extensions, u16 id, size_t dsize) --{ -- return (struct bitmap_ipmac_elem *)(extensions + id * dsize); --} -+#define get_elem(extensions, id, dsize) \ -+ (struct bitmap_ipmac_elem *)(extensions + (id) * (dsize)) -+ -+#define get_const_elem(extensions, id, dsize) \ -+ (const struct bitmap_ipmac_elem *)(extensions + (id) * (dsize)) - - /* Common functions */ - -@@ -88,10 +90,9 @@ bitmap_ipmac_do_test(const struct bitmap_ipmac_adt_elem *e, - - if (!test_bit(e->id, map->members)) - return 0; -- elem = get_elem(map->extensions, e->id, dsize); -- if (elem->filled == MAC_FILLED) -- return !e->ether || -- ether_addr_equal(e->ether, elem->ether); -+ elem = get_const_elem(map->extensions, e->id, dsize); -+ if (e->add_mac && elem->filled == MAC_FILLED) -+ return ether_addr_equal(e->ether, elem->ether); - /* Trigger kernel to fill out the ethernet address */ - return -EAGAIN; - } -@@ -103,7 +104,7 @@ bitmap_ipmac_gc_test(u16 id, const struct bitmap_ipmac *map, size_t dsize) - - if (!test_bit(id, map->members)) - return 0; -- elem = get_elem(map->extensions, id, dsize); -+ elem = get_const_elem(map->extensions, id, dsize); - /* Timer not started for the incomplete elements */ - return elem->filled == MAC_FILLED; - } -@@ -133,7 +134,7 @@ bitmap_ipmac_add_timeout(unsigned long *timeout, - * and we can reuse it later when MAC is filled out, - * possibly by the kernel - */ -- if (e->ether) -+ if (e->add_mac) - ip_set_timeout_set(timeout, t); - else - *timeout = t; -@@ -150,7 +151,7 @@ bitmap_ipmac_do_add(const struct bitmap_ipmac_adt_elem *e, - elem = get_elem(map->extensions, e->id, dsize); - if (test_bit(e->id, map->members)) { - if (elem->filled == MAC_FILLED) { -- if (e->ether && -+ if (e->add_mac && - (flags & IPSET_FLAG_EXIST) && - !ether_addr_equal(e->ether, elem->ether)) { - /* memcpy isn't atomic */ -@@ -159,7 +160,7 @@ bitmap_ipmac_do_add(const struct bitmap_ipmac_adt_elem *e, - ether_addr_copy(elem->ether, e->ether); - } - return IPSET_ADD_FAILED; -- } else if (!e->ether) -+ } else if (!e->add_mac) - /* Already added without ethernet address */ - return IPSET_ADD_FAILED; - /* Fill the MAC address and trigger the timer activation */ -@@ -168,7 +169,7 @@ bitmap_ipmac_do_add(const struct bitmap_ipmac_adt_elem *e, - ether_addr_copy(elem->ether, e->ether); - elem->filled = MAC_FILLED; - return IPSET_ADD_START_STORED_TIMEOUT; -- } else if (e->ether) { -+ } else if (e->add_mac) { - /* We can store MAC too */ - ether_addr_copy(elem->ether, e->ether); - elem->filled = MAC_FILLED; -@@ -191,7 +192,7 @@ bitmap_ipmac_do_list(struct sk_buff *skb, const struct bitmap_ipmac *map, - u32 id, size_t dsize) - { - const struct bitmap_ipmac_elem *elem = -- get_elem(map->extensions, id, dsize); -+ get_const_elem(map->extensions, id, dsize); - - return nla_put_ipaddr4(skb, IPSET_ATTR_IP, - htonl(map->first_ip + id)) || -@@ -213,7 +214,7 @@ bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb, - { - struct bitmap_ipmac *map = set->data; - ipset_adtfn adtfn = set->variant->adt[adt]; -- struct bitmap_ipmac_adt_elem e = { .id = 0 }; -+ struct bitmap_ipmac_adt_elem e = { .id = 0, .add_mac = 1 }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); - u32 ip; - -@@ -231,7 +232,7 @@ bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb, - return -EINVAL; - - e.id = ip_to_id(map, ip); -- e.ether = eth_hdr(skb)->h_source; -+ memcpy(e.ether, eth_hdr(skb)->h_source, ETH_ALEN); - - return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); - } -@@ -265,11 +266,10 @@ bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *tb[], - return -IPSET_ERR_BITMAP_RANGE; - - e.id = ip_to_id(map, ip); -- if (tb[IPSET_ATTR_ETHER]) -- e.ether = nla_data(tb[IPSET_ATTR_ETHER]); -- else -- e.ether = NULL; -- -+ if (tb[IPSET_ATTR_ETHER]) { -+ memcpy(e.ether, nla_data(tb[IPSET_ATTR_ETHER]), ETH_ALEN); -+ e.add_mac = 1; -+ } - ret = adtfn(set, &e, &ext, &ext, flags); - - return ip_set_eexist(ret, flags) ? 0 : ret; -@@ -300,13 +300,6 @@ init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map, - map->members = ip_set_alloc(map->memsize); - if (!map->members) - return false; -- if (set->dsize) { -- map->extensions = ip_set_alloc(set->dsize * elements); -- if (!map->extensions) { -- kfree(map->members); -- return false; -- } -- } - map->first_ip = first_ip; - map->last_ip = last_ip; - map->elements = elements; -@@ -361,14 +354,15 @@ bitmap_ipmac_create(struct net *net, struct ip_set *set, struct nlattr *tb[], - if (elements > IPSET_BITMAP_MAX_RANGE + 1) - return -IPSET_ERR_BITMAP_RANGE_SIZE; - -- map = kzalloc(sizeof(*map), GFP_KERNEL); -+ set->dsize = ip_set_elem_len(set, tb, -+ sizeof(struct bitmap_ipmac_elem), -+ __alignof__(struct bitmap_ipmac_elem)); -+ map = ip_set_alloc(sizeof(*map) + elements * set->dsize); - if (!map) - return -ENOMEM; - - map->memsize = bitmap_bytes(0, elements - 1); - set->variant = &bitmap_ipmac; -- set->dsize = ip_set_elem_len(set, tb, -- sizeof(struct bitmap_ipmac_elem)); - if (!init_map_ipmac(set, map, first_ip, last_ip, elements)) { - kfree(map); - return -ENOMEM; -diff --git a/net/netfilter/ipset/ip_set_bitmap_port.c b/net/netfilter/ipset/ip_set_bitmap_port.c -index 5338ccd..7f0c733 100644 ---- a/net/netfilter/ipset/ip_set_bitmap_port.c -+++ b/net/netfilter/ipset/ip_set_bitmap_port.c -@@ -35,12 +35,13 @@ MODULE_ALIAS("ip_set_bitmap:port"); - /* Type structure */ - struct bitmap_port { - void *members; /* the set members */ -- void *extensions; /* data extensions */ - u16 first_port; /* host byte order, included in range */ - u16 last_port; /* host byte order, included in range */ - u32 elements; /* number of max elements in the set */ - size_t memsize; /* members size */ - struct timer_list gc; /* garbage collection */ -+ unsigned char extensions[0] /* data extensions */ -+ __aligned(__alignof__(u64)); - }; - - /* ADT structure for generic function args */ -@@ -209,13 +210,6 @@ init_map_port(struct ip_set *set, struct bitmap_port *map, - map->members = ip_set_alloc(map->memsize); - if (!map->members) - return false; -- if (set->dsize) { -- map->extensions = ip_set_alloc(set->dsize * map->elements); -- if (!map->extensions) { -- kfree(map->members); -- return false; -- } -- } - map->first_port = first_port; - map->last_port = last_port; - set->timeout = IPSET_NO_TIMEOUT; -@@ -232,6 +226,7 @@ bitmap_port_create(struct net *net, struct ip_set *set, struct nlattr *tb[], - { - struct bitmap_port *map; - u16 first_port, last_port; -+ u32 elements; - - if (unlikely(!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || - !ip_set_attr_netorder(tb, IPSET_ATTR_PORT_TO) || -@@ -248,14 +243,15 @@ bitmap_port_create(struct net *net, struct ip_set *set, struct nlattr *tb[], - last_port = tmp; - } - -- map = kzalloc(sizeof(*map), GFP_KERNEL); -+ elements = last_port - first_port + 1; -+ set->dsize = ip_set_elem_len(set, tb, 0, 0); -+ map = ip_set_alloc(sizeof(*map) + elements * set->dsize); - if (!map) - return -ENOMEM; - -- map->elements = last_port - first_port + 1; -+ map->elements = elements; - map->memsize = bitmap_bytes(0, map->elements); - set->variant = &bitmap_port; -- set->dsize = ip_set_elem_len(set, tb, 0); - if (!init_map_port(set, map, first_port, last_port)) { - kfree(map); - return -ENOMEM; -diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c -index 69ab9c26..54f3d7c 100644 ---- a/net/netfilter/ipset/ip_set_core.c -+++ b/net/netfilter/ipset/ip_set_core.c -@@ -364,25 +364,27 @@ add_extension(enum ip_set_ext_id id, u32 flags, struct nlattr *tb[]) - } - - size_t --ip_set_elem_len(struct ip_set *set, struct nlattr *tb[], size_t len) -+ip_set_elem_len(struct ip_set *set, struct nlattr *tb[], size_t len, -+ size_t align) - { - enum ip_set_ext_id id; -- size_t offset = len; - u32 cadt_flags = 0; - - if (tb[IPSET_ATTR_CADT_FLAGS]) - cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); - if (cadt_flags & IPSET_FLAG_WITH_FORCEADD) - set->flags |= IPSET_CREATE_FLAG_FORCEADD; -+ if (!align) -+ align = 1; - for (id = 0; id < IPSET_EXT_ID_MAX; id++) { - if (!add_extension(id, cadt_flags, tb)) - continue; -- offset = ALIGN(offset, ip_set_extensions[id].align); -- set->offset[id] = offset; -+ len = ALIGN(len, ip_set_extensions[id].align); -+ set->offset[id] = len; - set->extensions |= ip_set_extensions[id].type; -- offset += ip_set_extensions[id].len; -+ len += ip_set_extensions[id].len; - } -- return offset; -+ return ALIGN(len, align); - } - EXPORT_SYMBOL_GPL(ip_set_elem_len); - -diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h -index 691b54f..4ff2219 100644 ---- a/net/netfilter/ipset/ip_set_hash_gen.h -+++ b/net/netfilter/ipset/ip_set_hash_gen.h -@@ -72,8 +72,9 @@ struct hbucket { - DECLARE_BITMAP(used, AHASH_MAX_TUNED); - u8 size; /* size of the array */ - u8 pos; /* position of the first free entry */ -- unsigned char value[0]; /* the array of the values */ --} __attribute__ ((aligned)); -+ unsigned char value[0] /* the array of the values */ -+ __aligned(__alignof__(u64)); -+}; - - /* The hash table: the table size stored here in order to make resizing easy */ - struct htable { -@@ -1323,12 +1324,14 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, - #endif - set->variant = &IPSET_TOKEN(HTYPE, 4_variant); - set->dsize = ip_set_elem_len(set, tb, -- sizeof(struct IPSET_TOKEN(HTYPE, 4_elem))); -+ sizeof(struct IPSET_TOKEN(HTYPE, 4_elem)), -+ __alignof__(struct IPSET_TOKEN(HTYPE, 4_elem))); - #ifndef IP_SET_PROTO_UNDEF - } else { - set->variant = &IPSET_TOKEN(HTYPE, 6_variant); - set->dsize = ip_set_elem_len(set, tb, -- sizeof(struct IPSET_TOKEN(HTYPE, 6_elem))); -+ sizeof(struct IPSET_TOKEN(HTYPE, 6_elem)), -+ __alignof__(struct IPSET_TOKEN(HTYPE, 6_elem))); - } - #endif - if (tb[IPSET_ATTR_TIMEOUT]) { -diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c -index 5a30ce6..bbede95 100644 ---- a/net/netfilter/ipset/ip_set_list_set.c -+++ b/net/netfilter/ipset/ip_set_list_set.c -@@ -31,7 +31,7 @@ struct set_elem { - struct rcu_head rcu; - struct list_head list; - ip_set_id_t id; --}; -+} __aligned(__alignof__(u64)); - - struct set_adt_elem { - ip_set_id_t id; -@@ -618,7 +618,8 @@ list_set_create(struct net *net, struct ip_set *set, struct nlattr *tb[], - size = IP_SET_LIST_MIN_SIZE; - - set->variant = &set_variant; -- set->dsize = ip_set_elem_len(set, tb, sizeof(struct set_elem)); -+ set->dsize = ip_set_elem_len(set, tb, sizeof(struct set_elem), -+ __alignof__(struct set_elem)); - if (!init_list_set(net, set, size)) - return -ENOMEM; - if (tb[IPSET_ATTR_TIMEOUT]) { --- -cgit v0.11.2 - diff --git a/kernel/kernel/files/patches/mageia/net-netfilter-ipset-Fix-hash-type-expiration.patch b/kernel/kernel/files/patches/mageia/net-netfilter-ipset-Fix-hash-type-expiration.patch deleted file mode 100644 index 54a1c72d..00000000 --- a/kernel/kernel/files/patches/mageia/net-netfilter-ipset-Fix-hash-type-expiration.patch +++ /dev/null @@ -1,30 +0,0 @@ -From e9dfdc052d018268926ab769d5b7598226713d5a Mon Sep 17 00:00:00 2001 -From: Jozsef Kadlecsik -Date: Sat, 7 Nov 2015 11:23:34 +0100 -Subject: netfilter: ipset: Fix hash:* type expiration - -Incorrect index was used when the data blob was shrinked at expiration, -which could lead to falsely expired entries and memory leak when -the comment extension was used too. - -Signed-off-by: Jozsef Kadlecsik ---- - net/netfilter/ipset/ip_set_hash_gen.h | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h -index 4ff2219..fa4f637 100644 ---- a/net/netfilter/ipset/ip_set_hash_gen.h -+++ b/net/netfilter/ipset/ip_set_hash_gen.h -@@ -523,7 +523,7 @@ mtype_expire(struct ip_set *set, struct htype *h, u8 nets_length, size_t dsize) - continue; - data = ahash_data(n, j, dsize); - memcpy(tmp->value + d * dsize, data, dsize); -- set_bit(j, tmp->used); -+ set_bit(d, tmp->used); - d++; - } - tmp->pos = d; --- -cgit v0.11.2 - diff --git a/kernel/kernel/files/patches/mageia/net-netfilter-ipset-Fix-hash-type-expire-release-empty-hash-bucket-block.patch b/kernel/kernel/files/patches/mageia/net-netfilter-ipset-Fix-hash-type-expire-release-empty-hash-bucket-block.patch deleted file mode 100644 index 92a4c27d..00000000 --- a/kernel/kernel/files/patches/mageia/net-netfilter-ipset-Fix-hash-type-expire-release-empty-hash-bucket-block.patch +++ /dev/null @@ -1,47 +0,0 @@ -From 0aae24eb409fc429f54ca3809f904f1b91e295e0 Mon Sep 17 00:00:00 2001 -From: Jozsef Kadlecsik -Date: Sat, 7 Nov 2015 11:24:51 +0100 -Subject: netfilter: ipset: Fix hash type expire: release empty hash bucket - block - -When all entries are expired/all slots are empty, release the bucket. - -Signed-off-by: Jozsef Kadlecsik ---- - net/netfilter/ipset/ip_set_hash_gen.h | 13 +++++++++---- - 1 file changed, 9 insertions(+), 4 deletions(-) - -diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h -index fa4f637..e5336ab 100644 ---- a/net/netfilter/ipset/ip_set_hash_gen.h -+++ b/net/netfilter/ipset/ip_set_hash_gen.h -@@ -476,7 +476,7 @@ static void - mtype_expire(struct ip_set *set, struct htype *h, u8 nets_length, size_t dsize) - { - struct htable *t; -- struct hbucket *n; -+ struct hbucket *n, *tmp; - struct mtype_elem *data; - u32 i, j, d; - #ifdef IP_SET_HASH_WITH_NETS -@@ -511,9 +511,14 @@ mtype_expire(struct ip_set *set, struct htype *h, u8 nets_length, size_t dsize) - } - } - if (d >= AHASH_INIT_SIZE) { -- struct hbucket *tmp = kzalloc(sizeof(*tmp) + -- (n->size - AHASH_INIT_SIZE) * dsize, -- GFP_ATOMIC); -+ if (d >= n->size) { -+ rcu_assign_pointer(hbucket(t, i), NULL); -+ kfree_rcu(n, rcu); -+ continue; -+ } -+ tmp = kzalloc(sizeof(*tmp) + -+ (n->size - AHASH_INIT_SIZE) * dsize, -+ GFP_ATOMIC); - if (!tmp) - /* Still try to delete expired elements */ - continue; --- -cgit v0.11.2 - diff --git a/kernel/kernel/files/patches/mageia/net-wireless-rtlwifi-rtl8821ae-Fix-lockups-on-boot.patch b/kernel/kernel/files/patches/mageia/net-wireless-rtlwifi-rtl8821ae-Fix-lockups-on-boot.patch deleted file mode 100644 index 33490eda..00000000 --- a/kernel/kernel/files/patches/mageia/net-wireless-rtlwifi-rtl8821ae-Fix-lockups-on-boot.patch +++ /dev/null @@ -1,58 +0,0 @@ -From: Larry Finger -Subject: [PATCH] realtek: rtlwifi: rtl8821ae: Fix lockups on boot -Date: Tue, 10 Nov 2015 10:46:11 -0600 - -In commit 54328e64047a5 ("rtlwifi: rtl8821ae: Fix system lockups on boot"), -an attempt was made to fix a regression introduced in commit 1277fa2ab2f9 -("rtlwifi: Remove the clear interrupt routine from all drivers"). -Unfortunately, there were logic errors in that patch that prevented -affected boxes from booting even after that patch was applied. - -The actual cause of the original problem is unknown as none of the -developers have systems that are affected. - -Signed-off-by: Larry Finger -Cc: Stable [V4.1+] ---- - -Kalle, - -I hope this patch can be applied to 4.4. - -Thanks, - -Larry - - drivers/net/wireless/rtlwifi/rtl8821ae/hw.c | 2 +- - drivers/net/wireless/rtlwifi/rtl8821ae/sw.c | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) - -diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c b/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c -index 6e9418e..bbb789f 100644 ---- a/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c -+++ b/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c -@@ -2272,7 +2272,7 @@ void rtl8821ae_enable_interrupt(struct ieee80211_hw *hw) - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); - -- if (!rtlpci->int_clear) -+ if (rtlpci->int_clear) - rtl8821ae_clear_interrupt(hw);/*clear it here first*/ - - rtl_write_dword(rtlpriv, REG_HIMR, rtlpci->irq_mask[0] & 0xFFFFFFFF); -diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/sw.c b/drivers/net/wireless/rtlwifi/rtl8821ae/sw.c -index 8ee141a..142bdff 100644 ---- a/drivers/net/wireless/rtlwifi/rtl8821ae/sw.c -+++ b/drivers/net/wireless/rtlwifi/rtl8821ae/sw.c -@@ -448,7 +448,7 @@ MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n"); - MODULE_PARM_DESC(msi, "Set to 1 to use MSI interrupts mode (default 1)\n"); - MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)"); - MODULE_PARM_DESC(disable_watchdog, "Set to 1 to disable the watchdog (default 0)\n"); --MODULE_PARM_DESC(int_clear, "Set to 1 to disable interrupt clear before set (default 0)\n"); -+MODULE_PARM_DESC(int_clear, "Set to 0 to disable interrupt clear before set (default 1)\n"); - - static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume); - --- -2.1.4 - diff --git a/kernel/kernel/files/patches/mageia/net_43.mbox.patch b/kernel/kernel/files/patches/mageia/net_43.mbox.patch deleted file mode 100644 index 2233f933..00000000 --- a/kernel/kernel/files/patches/mageia/net_43.mbox.patch +++ /dev/null @@ -1,4174 +0,0 @@ -From 19900d0bd94181ffa4d2130b5d6afcc6aef805e1 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?fran=C3=A7ois=20romieu?= -Date: Wed, 11 Nov 2015 23:35:18 +0100 -Subject: [PATCH 01/43] r8169: fix kasan reported skb use-after-free. - -[ Upstream commit 39174291d8e8acfd1113214a943263aaa03c57c8 ] - -Signed-off-by: Francois Romieu -Reported-by: Dave Jones -Fixes: d7d2d89d4b0af ("r8169: Add software counter for multicast packages") -Acked-by: Eric Dumazet -Acked-by: Corinna Vinschen -Signed-off-by: David S. Miller ---- - drivers/net/ethernet/realtek/r8169.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c -index b4f2123..79ef799 100644 ---- a/drivers/net/ethernet/realtek/r8169.c -+++ b/drivers/net/ethernet/realtek/r8169.c -@@ -7429,15 +7429,15 @@ process_pkt: - - rtl8169_rx_vlan_tag(desc, skb); - -+ if (skb->pkt_type == PACKET_MULTICAST) -+ dev->stats.multicast++; -+ - napi_gro_receive(&tp->napi, skb); - - u64_stats_update_begin(&tp->rx_stats.syncp); - tp->rx_stats.packets++; - tp->rx_stats.bytes += pkt_size; - u64_stats_update_end(&tp->rx_stats.syncp); -- -- if (skb->pkt_type == PACKET_MULTICAST) -- dev->stats.multicast++; - } - release_descriptor: - desc->opts2 = 0; --- -2.1.0 - - -From 16e46199ccdb834330ed801019d6b96077291d7c Mon Sep 17 00:00:00 2001 -From: Hannes Frederic Sowa -Date: Tue, 10 Nov 2015 16:23:15 +0100 -Subject: [PATCH 02/43] af-unix: fix use-after-free with concurrent readers - while splicing - -[ Upstream commit 73ed5d25dce0354ea381d6dc93005c3085fae03d ] - -During splicing an af-unix socket to a pipe we have to drop all -af-unix socket locks. While doing so we allow another reader to enter -unix_stream_read_generic which can read, copy and finally free another -skb. If exactly this skb is just in process of being spliced we get a -use-after-free report by kasan. - -First, we must make sure to not have a free while the skb is used during -the splice operation. We simply increment its use counter before unlocking -the reader lock. - -Stream sockets have the nice characteristic that we don't care about -zero length writes and they never reach the peer socket's queue. That -said, we can take the UNIXCB.consumed field as the indicator if the -skb was already freed from the socket's receive queue. If the skb was -fully consumed after we locked the reader side again we know it has been -dropped by a second reader. We indicate a short read to user space and -abort the current splice operation. - -This bug has been found with syzkaller -(http://github.com/google/syzkaller) by Dmitry Vyukov. - -Fixes: 2b514574f7e8 ("net: af_unix: implement splice for stream af_unix sockets") -Reported-by: Dmitry Vyukov -Cc: Dmitry Vyukov -Cc: Eric Dumazet -Acked-by: Eric Dumazet -Signed-off-by: Hannes Frederic Sowa -Signed-off-by: David S. Miller ---- - net/unix/af_unix.c | 18 ++++++++++++++++++ - 1 file changed, 18 insertions(+) - -diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c -index 94f6582..a5afe41 100644 ---- a/net/unix/af_unix.c -+++ b/net/unix/af_unix.c -@@ -440,6 +440,7 @@ static void unix_release_sock(struct sock *sk, int embrion) - if (state == TCP_LISTEN) - unix_release_sock(skb->sk, 1); - /* passed fds are erased in the kfree_skb hook */ -+ UNIXCB(skb).consumed = skb->len; - kfree_skb(skb); - } - -@@ -2071,6 +2072,7 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state) - - do { - int chunk; -+ bool drop_skb; - struct sk_buff *skb, *last; - - unix_state_lock(sk); -@@ -2151,7 +2153,11 @@ unlock: - } - - chunk = min_t(unsigned int, unix_skb_len(skb) - skip, size); -+ skb_get(skb); - chunk = state->recv_actor(skb, skip, chunk, state); -+ drop_skb = !unix_skb_len(skb); -+ /* skb is only safe to use if !drop_skb */ -+ consume_skb(skb); - if (chunk < 0) { - if (copied == 0) - copied = -EFAULT; -@@ -2160,6 +2166,18 @@ unlock: - copied += chunk; - size -= chunk; - -+ if (drop_skb) { -+ /* the skb was touched by a concurrent reader; -+ * we should not expect anything from this skb -+ * anymore and assume it invalid - we can be -+ * sure it was dropped from the socket queue -+ * -+ * let's report a short read -+ */ -+ err = 0; -+ break; -+ } -+ - /* Mark read part of skb as used */ - if (!(flags & MSG_PEEK)) { - UNIXCB(skb).consumed += chunk; --- -2.1.0 - - -From 75262dbd214a2a628aa333d51531b9e64fd7dc7e Mon Sep 17 00:00:00 2001 -From: Hannes Frederic Sowa -Date: Mon, 16 Nov 2015 16:25:56 +0100 -Subject: [PATCH 03/43] af_unix: don't append consumed skbs to sk_receive_queue - -[ Upstream commit 8844f97238ca6c1ca92a5d6c69f53efd361a266f ] - -In case multiple writes to a unix stream socket race we could end up in a -situation where we pre-allocate a new skb for use in unix_stream_sendpage -but have to free it again in the locked section because another skb -has been appended meanwhile, which we must use. Accidentally we didn't -clear the pointer after consuming it and so we touched freed memory -while appending it to the sk_receive_queue. So, clear the pointer after -consuming the skb. - -This bug has been found with syzkaller -(http://github.com/google/syzkaller) by Dmitry Vyukov. - -Fixes: 869e7c62486e ("net: af_unix: implement stream sendpage support") -Reported-by: Dmitry Vyukov -Cc: Dmitry Vyukov -Cc: Eric Dumazet -Signed-off-by: Hannes Frederic Sowa -Acked-by: Eric Dumazet -Signed-off-by: David S. Miller ---- - net/unix/af_unix.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c -index a5afe41..3e2ca39 100644 ---- a/net/unix/af_unix.c -+++ b/net/unix/af_unix.c -@@ -1799,6 +1799,7 @@ alloc_skb: - * this - does no harm - */ - consume_skb(newskb); -+ newskb = NULL; - } - - if (skb_append_pagefrags(skb, page, offset, size)) { --- -2.1.0 - - -From faa83c6e01f8604c385c4e0ccc7ca1da591100a5 Mon Sep 17 00:00:00 2001 -From: Hannes Frederic Sowa -Date: Tue, 17 Nov 2015 15:10:59 +0100 -Subject: [PATCH 04/43] af_unix: take receive queue lock while appending new - skb - -[ Upstream commit a3a116e04cc6a94d595ead4e956ab1bc1d2f4746 ] - -While possibly in future we don't necessarily need to use -sk_buff_head.lock this is a rather larger change, as it affects the -af_unix fd garbage collector, diag and socket cleanups. This is too much -for a stable patch. - -For the time being grab sk_buff_head.lock without disabling bh and irqs, -so don't use locked skb_queue_tail. - -Fixes: 869e7c62486e ("net: af_unix: implement stream sendpage support") -Cc: Eric Dumazet -Signed-off-by: Hannes Frederic Sowa -Reported-by: Eric Dumazet -Acked-by: Eric Dumazet -Signed-off-by: David S. Miller ---- - net/unix/af_unix.c | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) - -diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c -index 3e2ca39..42ab2cc 100644 ---- a/net/unix/af_unix.c -+++ b/net/unix/af_unix.c -@@ -1812,8 +1812,11 @@ alloc_skb: - skb->truesize += size; - atomic_add(size, &sk->sk_wmem_alloc); - -- if (newskb) -+ if (newskb) { -+ spin_lock(&other->sk_receive_queue.lock); - __skb_queue_tail(&other->sk_receive_queue, newskb); -+ spin_unlock(&other->sk_receive_queue.lock); -+ } - - unix_state_unlock(other); - mutex_unlock(&unix_sk(other)->readlock); --- -2.1.0 - - -From a46b9d2bac864f3ef6b21eb96864ddd88794222d Mon Sep 17 00:00:00 2001 -From: Rainer Weikusat -Date: Fri, 20 Nov 2015 22:07:23 +0000 -Subject: [PATCH 05/43] unix: avoid use-after-free in ep_remove_wait_queue - -[ Upstream commit 7d267278a9ece963d77eefec61630223fce08c6c ] - -Rainer Weikusat writes: -An AF_UNIX datagram socket being the client in an n:1 association with -some server socket is only allowed to send messages to the server if the -receive queue of this socket contains at most sk_max_ack_backlog -datagrams. This implies that prospective writers might be forced to go -to sleep despite none of the message presently enqueued on the server -receive queue were sent by them. In order to ensure that these will be -woken up once space becomes again available, the present unix_dgram_poll -routine does a second sock_poll_wait call with the peer_wait wait queue -of the server socket as queue argument (unix_dgram_recvmsg does a wake -up on this queue after a datagram was received). This is inherently -problematic because the server socket is only guaranteed to remain alive -for as long as the client still holds a reference to it. In case the -connection is dissolved via connect or by the dead peer detection logic -in unix_dgram_sendmsg, the server socket may be freed despite "the -polling mechanism" (in particular, epoll) still has a pointer to the -corresponding peer_wait queue. There's no way to forcibly deregister a -wait queue with epoll. - -Based on an idea by Jason Baron, the patch below changes the code such -that a wait_queue_t belonging to the client socket is enqueued on the -peer_wait queue of the server whenever the peer receive queue full -condition is detected by either a sendmsg or a poll. A wake up on the -peer queue is then relayed to the ordinary wait queue of the client -socket via wake function. The connection to the peer wait queue is again -dissolved if either a wake up is about to be relayed or the client -socket reconnects or a dead peer is detected or the client socket is -itself closed. This enables removing the second sock_poll_wait from -unix_dgram_poll, thus avoiding the use-after-free, while still ensuring -that no blocked writer sleeps forever. - -Signed-off-by: Rainer Weikusat -Fixes: ec0d215f9420 ("af_unix: fix 'poll for write'/connected DGRAM sockets") -Reviewed-by: Jason Baron -Signed-off-by: David S. Miller ---- - include/net/af_unix.h | 1 + - net/unix/af_unix.c | 183 ++++++++++++++++++++++++++++++++++++++++++++------ - 2 files changed, 165 insertions(+), 19 deletions(-) - -diff --git a/include/net/af_unix.h b/include/net/af_unix.h -index b36d837..2a91a05 100644 ---- a/include/net/af_unix.h -+++ b/include/net/af_unix.h -@@ -62,6 +62,7 @@ struct unix_sock { - #define UNIX_GC_CANDIDATE 0 - #define UNIX_GC_MAYBE_CYCLE 1 - struct socket_wq peer_wq; -+ wait_queue_t peer_wake; - }; - - static inline struct unix_sock *unix_sk(const struct sock *sk) -diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c -index 42ab2cc..153b2f2 100644 ---- a/net/unix/af_unix.c -+++ b/net/unix/af_unix.c -@@ -326,6 +326,118 @@ found: - return s; - } - -+/* Support code for asymmetrically connected dgram sockets -+ * -+ * If a datagram socket is connected to a socket not itself connected -+ * to the first socket (eg, /dev/log), clients may only enqueue more -+ * messages if the present receive queue of the server socket is not -+ * "too large". This means there's a second writeability condition -+ * poll and sendmsg need to test. The dgram recv code will do a wake -+ * up on the peer_wait wait queue of a socket upon reception of a -+ * datagram which needs to be propagated to sleeping would-be writers -+ * since these might not have sent anything so far. This can't be -+ * accomplished via poll_wait because the lifetime of the server -+ * socket might be less than that of its clients if these break their -+ * association with it or if the server socket is closed while clients -+ * are still connected to it and there's no way to inform "a polling -+ * implementation" that it should let go of a certain wait queue -+ * -+ * In order to propagate a wake up, a wait_queue_t of the client -+ * socket is enqueued on the peer_wait queue of the server socket -+ * whose wake function does a wake_up on the ordinary client socket -+ * wait queue. This connection is established whenever a write (or -+ * poll for write) hit the flow control condition and broken when the -+ * association to the server socket is dissolved or after a wake up -+ * was relayed. -+ */ -+ -+static int unix_dgram_peer_wake_relay(wait_queue_t *q, unsigned mode, int flags, -+ void *key) -+{ -+ struct unix_sock *u; -+ wait_queue_head_t *u_sleep; -+ -+ u = container_of(q, struct unix_sock, peer_wake); -+ -+ __remove_wait_queue(&unix_sk(u->peer_wake.private)->peer_wait, -+ q); -+ u->peer_wake.private = NULL; -+ -+ /* relaying can only happen while the wq still exists */ -+ u_sleep = sk_sleep(&u->sk); -+ if (u_sleep) -+ wake_up_interruptible_poll(u_sleep, key); -+ -+ return 0; -+} -+ -+static int unix_dgram_peer_wake_connect(struct sock *sk, struct sock *other) -+{ -+ struct unix_sock *u, *u_other; -+ int rc; -+ -+ u = unix_sk(sk); -+ u_other = unix_sk(other); -+ rc = 0; -+ spin_lock(&u_other->peer_wait.lock); -+ -+ if (!u->peer_wake.private) { -+ u->peer_wake.private = other; -+ __add_wait_queue(&u_other->peer_wait, &u->peer_wake); -+ -+ rc = 1; -+ } -+ -+ spin_unlock(&u_other->peer_wait.lock); -+ return rc; -+} -+ -+static void unix_dgram_peer_wake_disconnect(struct sock *sk, -+ struct sock *other) -+{ -+ struct unix_sock *u, *u_other; -+ -+ u = unix_sk(sk); -+ u_other = unix_sk(other); -+ spin_lock(&u_other->peer_wait.lock); -+ -+ if (u->peer_wake.private == other) { -+ __remove_wait_queue(&u_other->peer_wait, &u->peer_wake); -+ u->peer_wake.private = NULL; -+ } -+ -+ spin_unlock(&u_other->peer_wait.lock); -+} -+ -+static void unix_dgram_peer_wake_disconnect_wakeup(struct sock *sk, -+ struct sock *other) -+{ -+ unix_dgram_peer_wake_disconnect(sk, other); -+ wake_up_interruptible_poll(sk_sleep(sk), -+ POLLOUT | -+ POLLWRNORM | -+ POLLWRBAND); -+} -+ -+/* preconditions: -+ * - unix_peer(sk) == other -+ * - association is stable -+ */ -+static int unix_dgram_peer_wake_me(struct sock *sk, struct sock *other) -+{ -+ int connected; -+ -+ connected = unix_dgram_peer_wake_connect(sk, other); -+ -+ if (unix_recvq_full(other)) -+ return 1; -+ -+ if (connected) -+ unix_dgram_peer_wake_disconnect(sk, other); -+ -+ return 0; -+} -+ - static inline int unix_writable(struct sock *sk) - { - return (atomic_read(&sk->sk_wmem_alloc) << 2) <= sk->sk_sndbuf; -@@ -430,6 +542,8 @@ static void unix_release_sock(struct sock *sk, int embrion) - skpair->sk_state_change(skpair); - sk_wake_async(skpair, SOCK_WAKE_WAITD, POLL_HUP); - } -+ -+ unix_dgram_peer_wake_disconnect(sk, skpair); - sock_put(skpair); /* It may now die */ - unix_peer(sk) = NULL; - } -@@ -665,6 +779,7 @@ static struct sock *unix_create1(struct net *net, struct socket *sock, int kern) - INIT_LIST_HEAD(&u->link); - mutex_init(&u->readlock); /* single task reading lock */ - init_waitqueue_head(&u->peer_wait); -+ init_waitqueue_func_entry(&u->peer_wake, unix_dgram_peer_wake_relay); - unix_insert_socket(unix_sockets_unbound(sk), sk); - out: - if (sk == NULL) -@@ -1032,6 +1147,8 @@ restart: - if (unix_peer(sk)) { - struct sock *old_peer = unix_peer(sk); - unix_peer(sk) = other; -+ unix_dgram_peer_wake_disconnect_wakeup(sk, old_peer); -+ - unix_state_double_unlock(sk, other); - - if (other != old_peer) -@@ -1471,6 +1588,7 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg, - struct scm_cookie scm; - int max_level; - int data_len = 0; -+ int sk_locked; - - wait_for_unix_gc(); - err = scm_send(sock, msg, &scm, false); -@@ -1549,12 +1667,14 @@ restart: - goto out_free; - } - -+ sk_locked = 0; - unix_state_lock(other); -+restart_locked: - err = -EPERM; - if (!unix_may_send(sk, other)) - goto out_unlock; - -- if (sock_flag(other, SOCK_DEAD)) { -+ if (unlikely(sock_flag(other, SOCK_DEAD))) { - /* - * Check with 1003.1g - what should - * datagram error -@@ -1562,10 +1682,14 @@ restart: - unix_state_unlock(other); - sock_put(other); - -+ if (!sk_locked) -+ unix_state_lock(sk); -+ - err = 0; -- unix_state_lock(sk); - if (unix_peer(sk) == other) { - unix_peer(sk) = NULL; -+ unix_dgram_peer_wake_disconnect_wakeup(sk, other); -+ - unix_state_unlock(sk); - - unix_dgram_disconnected(sk, other); -@@ -1591,21 +1715,38 @@ restart: - goto out_unlock; - } - -- if (unix_peer(other) != sk && unix_recvq_full(other)) { -- if (!timeo) { -- err = -EAGAIN; -- goto out_unlock; -+ if (unlikely(unix_peer(other) != sk && unix_recvq_full(other))) { -+ if (timeo) { -+ timeo = unix_wait_for_peer(other, timeo); -+ -+ err = sock_intr_errno(timeo); -+ if (signal_pending(current)) -+ goto out_free; -+ -+ goto restart; - } - -- timeo = unix_wait_for_peer(other, timeo); -+ if (!sk_locked) { -+ unix_state_unlock(other); -+ unix_state_double_lock(sk, other); -+ } - -- err = sock_intr_errno(timeo); -- if (signal_pending(current)) -- goto out_free; -+ if (unix_peer(sk) != other || -+ unix_dgram_peer_wake_me(sk, other)) { -+ err = -EAGAIN; -+ sk_locked = 1; -+ goto out_unlock; -+ } - -- goto restart; -+ if (!sk_locked) { -+ sk_locked = 1; -+ goto restart_locked; -+ } - } - -+ if (unlikely(sk_locked)) -+ unix_state_unlock(sk); -+ - if (sock_flag(other, SOCK_RCVTSTAMP)) - __net_timestamp(skb); - maybe_add_creds(skb, sock, other); -@@ -1619,6 +1760,8 @@ restart: - return len; - - out_unlock: -+ if (sk_locked) -+ unix_state_unlock(sk); - unix_state_unlock(other); - out_free: - kfree_skb(skb); -@@ -2475,14 +2618,16 @@ static unsigned int unix_dgram_poll(struct file *file, struct socket *sock, - return mask; - - writable = unix_writable(sk); -- other = unix_peer_get(sk); -- if (other) { -- if (unix_peer(other) != sk) { -- sock_poll_wait(file, &unix_sk(other)->peer_wait, wait); -- if (unix_recvq_full(other)) -- writable = 0; -- } -- sock_put(other); -+ if (writable) { -+ unix_state_lock(sk); -+ -+ other = unix_peer(sk); -+ if (other && unix_peer(other) != sk && -+ unix_recvq_full(other) && -+ unix_dgram_peer_wake_me(sk, other)) -+ writable = 0; -+ -+ unix_state_unlock(sk); - } - - if (writable) --- -2.1.0 - - -From 8a029a6b8df3a955bd24253ce77cfc93834ce71f Mon Sep 17 00:00:00 2001 -From: Hannes Frederic Sowa -Date: Thu, 26 Nov 2015 12:08:18 +0100 -Subject: [PATCH 06/43] af-unix: passcred support for sendpage - -[ Upstream commit 9490f886b192964796285907d777ff00fba1fa0f ] - -sendpage did not care about credentials at all. This could lead to -situations in which because of fd passing between processes we could -append data to skbs with different scm data. It is illegal to splice those -skbs together. Instead we have to allocate a new skb and if requested -fill out the scm details. - -Fixes: 869e7c62486ec ("net: af_unix: implement stream sendpage support") -Reported-by: Al Viro -Cc: Al Viro -Cc: Eric Dumazet -Signed-off-by: Hannes Frederic Sowa -Signed-off-by: David S. Miller ---- - net/unix/af_unix.c | 79 +++++++++++++++++++++++++++++++++++++++++++----------- - 1 file changed, 64 insertions(+), 15 deletions(-) - -diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c -index 153b2f2..128b098 100644 ---- a/net/unix/af_unix.c -+++ b/net/unix/af_unix.c -@@ -1550,6 +1550,14 @@ static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb, bool sen - return err; - } - -+static bool unix_passcred_enabled(const struct socket *sock, -+ const struct sock *other) -+{ -+ return test_bit(SOCK_PASSCRED, &sock->flags) || -+ !other->sk_socket || -+ test_bit(SOCK_PASSCRED, &other->sk_socket->flags); -+} -+ - /* - * Some apps rely on write() giving SCM_CREDENTIALS - * We include credentials if source or destination socket -@@ -1560,14 +1568,41 @@ static void maybe_add_creds(struct sk_buff *skb, const struct socket *sock, - { - if (UNIXCB(skb).pid) - return; -- if (test_bit(SOCK_PASSCRED, &sock->flags) || -- !other->sk_socket || -- test_bit(SOCK_PASSCRED, &other->sk_socket->flags)) { -+ if (unix_passcred_enabled(sock, other)) { - UNIXCB(skb).pid = get_pid(task_tgid(current)); - current_uid_gid(&UNIXCB(skb).uid, &UNIXCB(skb).gid); - } - } - -+static int maybe_init_creds(struct scm_cookie *scm, -+ struct socket *socket, -+ const struct sock *other) -+{ -+ int err; -+ struct msghdr msg = { .msg_controllen = 0 }; -+ -+ err = scm_send(socket, &msg, scm, false); -+ if (err) -+ return err; -+ -+ if (unix_passcred_enabled(socket, other)) { -+ scm->pid = get_pid(task_tgid(current)); -+ current_uid_gid(&scm->creds.uid, &scm->creds.gid); -+ } -+ return err; -+} -+ -+static bool unix_skb_scm_eq(struct sk_buff *skb, -+ struct scm_cookie *scm) -+{ -+ const struct unix_skb_parms *u = &UNIXCB(skb); -+ -+ return u->pid == scm->pid && -+ uid_eq(u->uid, scm->creds.uid) && -+ gid_eq(u->gid, scm->creds.gid) && -+ unix_secdata_eq(scm, skb); -+} -+ - /* - * Send AF_UNIX data. - */ -@@ -1883,8 +1918,10 @@ out_err: - static ssize_t unix_stream_sendpage(struct socket *socket, struct page *page, - int offset, size_t size, int flags) - { -- int err = 0; -- bool send_sigpipe = true; -+ int err; -+ bool send_sigpipe = false; -+ bool init_scm = true; -+ struct scm_cookie scm; - struct sock *other, *sk = socket->sk; - struct sk_buff *skb, *newskb = NULL, *tail = NULL; - -@@ -1902,7 +1939,7 @@ alloc_skb: - newskb = sock_alloc_send_pskb(sk, 0, 0, flags & MSG_DONTWAIT, - &err, 0); - if (!newskb) -- return err; -+ goto err; - } - - /* we must acquire readlock as we modify already present -@@ -1911,12 +1948,12 @@ alloc_skb: - err = mutex_lock_interruptible(&unix_sk(other)->readlock); - if (err) { - err = flags & MSG_DONTWAIT ? -EAGAIN : -ERESTARTSYS; -- send_sigpipe = false; - goto err; - } - - if (sk->sk_shutdown & SEND_SHUTDOWN) { - err = -EPIPE; -+ send_sigpipe = true; - goto err_unlock; - } - -@@ -1925,17 +1962,27 @@ alloc_skb: - if (sock_flag(other, SOCK_DEAD) || - other->sk_shutdown & RCV_SHUTDOWN) { - err = -EPIPE; -+ send_sigpipe = true; - goto err_state_unlock; - } - -+ if (init_scm) { -+ err = maybe_init_creds(&scm, socket, other); -+ if (err) -+ goto err_state_unlock; -+ init_scm = false; -+ } -+ - skb = skb_peek_tail(&other->sk_receive_queue); - if (tail && tail == skb) { - skb = newskb; -- } else if (!skb) { -- if (newskb) -+ } else if (!skb || !unix_skb_scm_eq(skb, &scm)) { -+ if (newskb) { - skb = newskb; -- else -+ } else { -+ tail = skb; - goto alloc_skb; -+ } - } else if (newskb) { - /* this is fast path, we don't necessarily need to - * call to kfree_skb even though with newskb == NULL -@@ -1956,6 +2003,9 @@ alloc_skb: - atomic_add(size, &sk->sk_wmem_alloc); - - if (newskb) { -+ err = unix_scm_to_skb(&scm, skb, false); -+ if (err) -+ goto err_state_unlock; - spin_lock(&other->sk_receive_queue.lock); - __skb_queue_tail(&other->sk_receive_queue, newskb); - spin_unlock(&other->sk_receive_queue.lock); -@@ -1965,7 +2015,7 @@ alloc_skb: - mutex_unlock(&unix_sk(other)->readlock); - - other->sk_data_ready(other); -- -+ scm_destroy(&scm); - return size; - - err_state_unlock: -@@ -1976,6 +2026,8 @@ err: - kfree_skb(newskb); - if (send_sigpipe && !(flags & MSG_NOSIGNAL)) - send_sig(SIGPIPE, current, 0); -+ if (!init_scm) -+ scm_destroy(&scm); - return err; - } - -@@ -2279,10 +2331,7 @@ unlock: - - if (check_creds) { - /* Never glue messages from different writers */ -- if ((UNIXCB(skb).pid != scm.pid) || -- !uid_eq(UNIXCB(skb).uid, scm.creds.uid) || -- !gid_eq(UNIXCB(skb).gid, scm.creds.gid) || -- !unix_secdata_eq(&scm, skb)) -+ if (!unix_skb_scm_eq(skb, &scm)) - break; - } else if (test_bit(SOCK_PASSCRED, &sock->flags)) { - /* Copy credentials */ --- -2.1.0 - - -From 6f61ebb821b04690a54ae998160cb758dbaa7f9b Mon Sep 17 00:00:00 2001 -From: Martin KaFai Lau -Date: Wed, 11 Nov 2015 11:51:06 -0800 -Subject: [PATCH 07/43] ipv6: Avoid creating RTF_CACHE from a rt that is not - managed by fib6 tree - -[ Upstream commit 0d3f6d297bfb7af24d0508460fdb3d1ec4903fa3 ] - -The original bug report: -https://bugzilla.redhat.com/show_bug.cgi?id=1272571 - -The setup has a IPv4 GRE tunnel running in a IPSec. The bug -happens when ndisc starts sending router solicitation at the gre -interface. The simplified oops stack is like: - -__lock_acquire+0x1b2/0x1c30 -lock_acquire+0xb9/0x140 -_raw_write_lock_bh+0x3f/0x50 -__ip6_ins_rt+0x2e/0x60 -ip6_ins_rt+0x49/0x50 -~~~~~~~~ -__ip6_rt_update_pmtu.part.54+0x145/0x250 -ip6_rt_update_pmtu+0x2e/0x40 -~~~~~~~~ -ip_tunnel_xmit+0x1f1/0xf40 -__gre_xmit+0x7a/0x90 -ipgre_xmit+0x15a/0x220 -dev_hard_start_xmit+0x2bd/0x480 -__dev_queue_xmit+0x696/0x730 -dev_queue_xmit+0x10/0x20 -neigh_direct_output+0x11/0x20 -ip6_finish_output2+0x21f/0x770 -ip6_finish_output+0xa7/0x1d0 -ip6_output+0x56/0x190 -~~~~~~~~ -ndisc_send_skb+0x1d9/0x400 -ndisc_send_rs+0x88/0xc0 -~~~~~~~~ - -The rt passed to ip6_rt_update_pmtu() is created by -icmp6_dst_alloc() and it is not managed by the fib6 tree, -so its rt6i_table == NULL. When __ip6_rt_update_pmtu() creates -a RTF_CACHE clone, the newly created clone also has rt6i_table == NULL -and it causes the ip6_ins_rt() oops. - -During pmtu update, we only want to create a RTF_CACHE clone -from a rt which is currently managed (or owned) by the -fib6 tree. It means either rt->rt6i_node != NULL or -rt is a RTF_PCPU clone. - -It is worth to note that rt6i_table may not be NULL even it is -not (yet) managed by the fib6 tree (e.g. addrconf_dst_alloc()). -Hence, rt6i_node is a better check instead of rt6i_table. - -Fixes: 45e4fd26683c ("ipv6: Only create RTF_CACHE routes after encountering pmtu") -Signed-off-by: Martin KaFai Lau -Reported-by: Chris Siebenmann -Cc: Chris Siebenmann -Cc: Hannes Frederic Sowa -Signed-off-by: David S. Miller ---- - net/ipv6/route.c | 8 +++++++- - 1 file changed, 7 insertions(+), 1 deletion(-) - -diff --git a/net/ipv6/route.c b/net/ipv6/route.c -index 946880a..711ec7a 100644 ---- a/net/ipv6/route.c -+++ b/net/ipv6/route.c -@@ -1340,6 +1340,12 @@ static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu) - rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires); - } - -+static bool rt6_cache_allowed_for_pmtu(const struct rt6_info *rt) -+{ -+ return !(rt->rt6i_flags & RTF_CACHE) && -+ (rt->rt6i_flags & RTF_PCPU || rt->rt6i_node); -+} -+ - static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk, - const struct ipv6hdr *iph, u32 mtu) - { -@@ -1353,7 +1359,7 @@ static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk, - if (mtu >= dst_mtu(dst)) - return; - -- if (rt6->rt6i_flags & RTF_CACHE) { -+ if (!rt6_cache_allowed_for_pmtu(rt6)) { - rt6_do_update_pmtu(rt6, mtu); - } else { - const struct in6_addr *daddr, *saddr; --- -2.1.0 - - -From 36e5325023d53e603ce46bfcd55587f164850102 Mon Sep 17 00:00:00 2001 -From: Martin KaFai Lau -Date: Wed, 11 Nov 2015 11:51:07 -0800 -Subject: [PATCH 08/43] ipv6: Check expire on DST_NOCACHE route - -[ Upstream commit 5973fb1e245086071bf71994c8b54d99526ded03 ] - -Since the expires of the DST_NOCACHE rt can be set during -the ip6_rt_update_pmtu(), we also need to consider the expires -value when doing ip6_dst_check(). - -This patches creates __rt6_check_expired() to only -check the expire value (if one exists) of the current rt. - -In rt6_dst_from_check(), it adds __rt6_check_expired() as -one of the condition check. - -Signed-off-by: Martin KaFai Lau -Cc: Hannes Frederic Sowa -Signed-off-by: David S. Miller ---- - net/ipv6/route.c | 11 ++++++++++- - 1 file changed, 10 insertions(+), 1 deletion(-) - -diff --git a/net/ipv6/route.c b/net/ipv6/route.c -index 711ec7a..ea892c1 100644 ---- a/net/ipv6/route.c -+++ b/net/ipv6/route.c -@@ -403,6 +403,14 @@ static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, - } - } - -+static bool __rt6_check_expired(const struct rt6_info *rt) -+{ -+ if (rt->rt6i_flags & RTF_EXPIRES) -+ return time_after(jiffies, rt->dst.expires); -+ else -+ return false; -+} -+ - static bool rt6_check_expired(const struct rt6_info *rt) - { - if (rt->rt6i_flags & RTF_EXPIRES) { -@@ -1270,7 +1278,8 @@ static struct dst_entry *rt6_check(struct rt6_info *rt, u32 cookie) - - static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt, u32 cookie) - { -- if (rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK && -+ if (!__rt6_check_expired(rt) && -+ rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK && - rt6_check((struct rt6_info *)(rt->dst.from), cookie)) - return &rt->dst; - else --- -2.1.0 - - -From 037104a8ac4a3509ee800cfa124897600c9483e7 Mon Sep 17 00:00:00 2001 -From: Martin KaFai Lau -Date: Wed, 11 Nov 2015 11:51:08 -0800 -Subject: [PATCH 09/43] ipv6: Check rt->dst.from for the DST_NOCACHE route - -[ Upstrem commit 02bcf4e082e4dc634409a6a6cb7def8806d6e5e6 ] - -All DST_NOCACHE rt6_info used to have rt->dst.from set to -its parent. - -After commit 8e3d5be73681 ("ipv6: Avoid double dst_free"), -DST_NOCACHE is also set to rt6_info which does not have -a parent (i.e. rt->dst.from is NULL). - -This patch catches the rt->dst.from == NULL case. - -Fixes: 8e3d5be73681 ("ipv6: Avoid double dst_free") -Signed-off-by: Martin KaFai Lau -Cc: Hannes Frederic Sowa -Signed-off-by: David S. Miller ---- - include/net/ip6_fib.h | 3 ++- - net/ipv6/route.c | 3 ++- - 2 files changed, 4 insertions(+), 2 deletions(-) - -diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h -index aaf9700..fb961a5 100644 ---- a/include/net/ip6_fib.h -+++ b/include/net/ip6_fib.h -@@ -167,7 +167,8 @@ static inline void rt6_update_expires(struct rt6_info *rt0, int timeout) - - static inline u32 rt6_get_cookie(const struct rt6_info *rt) - { -- if (rt->rt6i_flags & RTF_PCPU || unlikely(rt->dst.flags & DST_NOCACHE)) -+ if (rt->rt6i_flags & RTF_PCPU || -+ (unlikely(rt->dst.flags & DST_NOCACHE) && rt->dst.from)) - rt = (struct rt6_info *)(rt->dst.from); - - return rt->rt6i_node ? rt->rt6i_node->fn_sernum : 0; -diff --git a/net/ipv6/route.c b/net/ipv6/route.c -index ea892c1..d377326 100644 ---- a/net/ipv6/route.c -+++ b/net/ipv6/route.c -@@ -1299,7 +1299,8 @@ static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie) - - rt6_dst_from_metrics_check(rt); - -- if ((rt->rt6i_flags & RTF_PCPU) || unlikely(dst->flags & DST_NOCACHE)) -+ if (rt->rt6i_flags & RTF_PCPU || -+ (unlikely(dst->flags & DST_NOCACHE) && rt->dst.from)) - return rt6_dst_from_check(rt, cookie); - else - return rt6_check(rt, cookie); --- -2.1.0 - - -From bbecfdcd08494929d0cde9176346d51007fb77ed Mon Sep 17 00:00:00 2001 -From: Nicolas Dichtel -Date: Fri, 27 Nov 2015 18:17:05 +0100 -Subject: [PATCH 10/43] Revert "ipv6: ndisc: inherit metadata dst when creating - ndisc requests" - -[ Upstream commit 304d888b29cf96f1dd53511ee686499cd8cdf249 ] - -This reverts commit ab450605b35caa768ca33e86db9403229bf42be4. - -In IPv6, we cannot inherit the dst of the original dst. ndisc packets -are IPv6 packets and may take another route than the original packet. - -This patch breaks the following scenario: a packet comes from eth0 and -is forwarded through vxlan1. The encapsulated packet triggers an NS -which cannot be sent because of the wrong route. - -CC: Jiri Benc -CC: Thomas Graf -Signed-off-by: Nicolas Dichtel -Signed-off-by: David S. Miller ---- - include/net/ndisc.h | 3 +-- - net/ipv6/addrconf.c | 2 +- - net/ipv6/ndisc.c | 10 +++------- - net/ipv6/route.c | 2 +- - 4 files changed, 6 insertions(+), 11 deletions(-) - -diff --git a/include/net/ndisc.h b/include/net/ndisc.h -index aba5695..b3a7751 100644 ---- a/include/net/ndisc.h -+++ b/include/net/ndisc.h -@@ -182,8 +182,7 @@ int ndisc_rcv(struct sk_buff *skb); - - void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh, - const struct in6_addr *solicit, -- const struct in6_addr *daddr, const struct in6_addr *saddr, -- struct sk_buff *oskb); -+ const struct in6_addr *daddr, const struct in6_addr *saddr); - - void ndisc_send_rs(struct net_device *dev, - const struct in6_addr *saddr, const struct in6_addr *daddr); -diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c -index dd00828..3939dd2 100644 ---- a/net/ipv6/addrconf.c -+++ b/net/ipv6/addrconf.c -@@ -3628,7 +3628,7 @@ static void addrconf_dad_work(struct work_struct *w) - - /* send a neighbour solicitation for our addr */ - addrconf_addr_solict_mult(&ifp->addr, &mcaddr); -- ndisc_send_ns(ifp->idev->dev, NULL, &ifp->addr, &mcaddr, &in6addr_any, NULL); -+ ndisc_send_ns(ifp->idev->dev, NULL, &ifp->addr, &mcaddr, &in6addr_any); - out: - in6_ifa_put(ifp); - rtnl_unlock(); -diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c -index 64a7135..9ad46cd 100644 ---- a/net/ipv6/ndisc.c -+++ b/net/ipv6/ndisc.c -@@ -553,8 +553,7 @@ static void ndisc_send_unsol_na(struct net_device *dev) - - void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh, - const struct in6_addr *solicit, -- const struct in6_addr *daddr, const struct in6_addr *saddr, -- struct sk_buff *oskb) -+ const struct in6_addr *daddr, const struct in6_addr *saddr) - { - struct sk_buff *skb; - struct in6_addr addr_buf; -@@ -590,9 +589,6 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh, - ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR, - dev->dev_addr); - -- if (!(dev->priv_flags & IFF_XMIT_DST_RELEASE) && oskb) -- skb_dst_copy(skb, oskb); -- - ndisc_send_skb(skb, daddr, saddr); - } - -@@ -679,12 +675,12 @@ static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb) - "%s: trying to ucast probe in NUD_INVALID: %pI6\n", - __func__, target); - } -- ndisc_send_ns(dev, neigh, target, target, saddr, skb); -+ ndisc_send_ns(dev, neigh, target, target, saddr); - } else if ((probes -= NEIGH_VAR(neigh->parms, APP_PROBES)) < 0) { - neigh_app_ns(neigh); - } else { - addrconf_addr_solict_mult(target, &mcaddr); -- ndisc_send_ns(dev, NULL, target, &mcaddr, saddr, skb); -+ ndisc_send_ns(dev, NULL, target, &mcaddr, saddr); - } - } - -diff --git a/net/ipv6/route.c b/net/ipv6/route.c -index d377326..fd0e674 100644 ---- a/net/ipv6/route.c -+++ b/net/ipv6/route.c -@@ -546,7 +546,7 @@ static void rt6_probe_deferred(struct work_struct *w) - container_of(w, struct __rt6_probe_work, work); - - addrconf_addr_solict_mult(&work->target, &mcaddr); -- ndisc_send_ns(work->dev, NULL, &work->target, &mcaddr, NULL, NULL); -+ ndisc_send_ns(work->dev, NULL, &work->target, &mcaddr, NULL); - dev_put(work->dev); - kfree(work); - } --- -2.1.0 - - -From 3855e07a82c0180d9110ac03d84405f9ecc79866 Mon Sep 17 00:00:00 2001 -From: Kamal Mostafa -Date: Wed, 11 Nov 2015 14:24:27 -0800 -Subject: [PATCH 11/43] tools/net: Use include/uapi with __EXPORTED_HEADERS__ - -[ Upstream commit d7475de58575c904818efa369c82e88c6648ce2e ] - -Use the local uapi headers to keep in sync with "recently" added #define's -(e.g. SKF_AD_VLAN_TPID). Refactored CFLAGS, and bpf_asm doesn't need -I. - -Fixes: 3f356385e8a4 ("filter: bpf_asm: add minimal bpf asm tool") -Signed-off-by: Kamal Mostafa -Acked-by: Daniel Borkmann -Signed-off-by: David S. Miller ---- - tools/net/Makefile | 7 ++++--- - 1 file changed, 4 insertions(+), 3 deletions(-) - -diff --git a/tools/net/Makefile b/tools/net/Makefile -index ee577ea..ddf8880 100644 ---- a/tools/net/Makefile -+++ b/tools/net/Makefile -@@ -4,6 +4,9 @@ CC = gcc - LEX = flex - YACC = bison - -+CFLAGS += -Wall -O2 -+CFLAGS += -D__EXPORTED_HEADERS__ -I../../include/uapi -I../../include -+ - %.yacc.c: %.y - $(YACC) -o $@ -d $< - -@@ -12,15 +15,13 @@ YACC = bison - - all : bpf_jit_disasm bpf_dbg bpf_asm - --bpf_jit_disasm : CFLAGS = -Wall -O2 -DPACKAGE='bpf_jit_disasm' -+bpf_jit_disasm : CFLAGS += -DPACKAGE='bpf_jit_disasm' - bpf_jit_disasm : LDLIBS = -lopcodes -lbfd -ldl - bpf_jit_disasm : bpf_jit_disasm.o - --bpf_dbg : CFLAGS = -Wall -O2 - bpf_dbg : LDLIBS = -lreadline - bpf_dbg : bpf_dbg.o - --bpf_asm : CFLAGS = -Wall -O2 -I. - bpf_asm : LDLIBS = - bpf_asm : bpf_asm.o bpf_exp.yacc.o bpf_exp.lex.o - bpf_exp.lex.o : bpf_exp.yacc.c --- -2.1.0 - - -From 00e7c45712e9d826a2908cdfb4d15581f825e47d Mon Sep 17 00:00:00 2001 -From: Daniel Borkmann -Date: Wed, 11 Nov 2015 23:25:40 +0100 -Subject: [PATCH 12/43] packet: do skb_probe_transport_header when we actually - have data - -[ Upstream commit efdfa2f7848f64517008136fb41f53c4a1faf93a ] - -In tpacket_fill_skb() commit c1aad275b029 ("packet: set transport -header before doing xmit") and later on 40893fd0fd4e ("net: switch -to use skb_probe_transport_header()") was probing for a transport -header on the skb from a ring buffer slot, but at a time, where -the skb has _not even_ been filled with data yet. So that call into -the flow dissector is pretty useless. Lets do it after we've set -up the skb frags. - -Fixes: c1aad275b029 ("packet: set transport header before doing xmit") -Reported-by: Eric Dumazet -Signed-off-by: Daniel Borkmann -Acked-by: Jason Wang -Signed-off-by: David S. Miller ---- - net/packet/af_packet.c | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - -diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c -index 27b2898..be038c9 100644 ---- a/net/packet/af_packet.c -+++ b/net/packet/af_packet.c -@@ -2368,8 +2368,6 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, - skb_reserve(skb, hlen); - skb_reset_network_header(skb); - -- if (!packet_use_direct_xmit(po)) -- skb_probe_transport_header(skb, 0); - if (unlikely(po->tp_tx_has_off)) { - int off_min, off_max, off; - off_min = po->tp_hdrlen - sizeof(struct sockaddr_ll); -@@ -2449,6 +2447,9 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, - len = ((to_write > len_max) ? len_max : to_write); - } - -+ if (!packet_use_direct_xmit(po)) -+ skb_probe_transport_header(skb, 0); -+ - return tp_len; - } - --- -2.1.0 - - -From 995afa88f27c706e0daf329f6fb07398414f2334 Mon Sep 17 00:00:00 2001 -From: Daniel Borkmann -Date: Wed, 11 Nov 2015 23:25:41 +0100 -Subject: [PATCH 13/43] packet: always probe for transport header - -[ Upstream commit 8fd6c80d9dd938ca338c70698533a7e304752846 ] - -We concluded that the skb_probe_transport_header() should better be -called unconditionally. Avoiding the call into the flow dissector has -also not really much to do with the direct xmit mode. - -While it seems that only virtio_net code makes use of GSO from non -RX/TX ring packet socket paths, we should probe for a transport header -nevertheless before they hit devices. - -Reference: http://thread.gmane.org/gmane.linux.network/386173/ -Signed-off-by: Daniel Borkmann -Acked-by: Jason Wang -Signed-off-by: David S. Miller ---- - net/packet/af_packet.c | 7 +++---- - 1 file changed, 3 insertions(+), 4 deletions(-) - -diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c -index be038c9..3059f51 100644 ---- a/net/packet/af_packet.c -+++ b/net/packet/af_packet.c -@@ -2447,8 +2447,7 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, - len = ((to_write > len_max) ? len_max : to_write); - } - -- if (!packet_use_direct_xmit(po)) -- skb_probe_transport_header(skb, 0); -+ skb_probe_transport_header(skb, 0); - - return tp_len; - } -@@ -2800,8 +2799,8 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) - len += vnet_hdr_len; - } - -- if (!packet_use_direct_xmit(po)) -- skb_probe_transport_header(skb, reserve); -+ skb_probe_transport_header(skb, reserve); -+ - if (unlikely(extra_len == 4)) - skb->no_fcs = 1; - --- -2.1.0 - - -From 85df0fab1bfabd5a88b9d53a09d34df51672282a Mon Sep 17 00:00:00 2001 -From: Daniel Borkmann -Date: Wed, 11 Nov 2015 23:25:42 +0100 -Subject: [PATCH 14/43] packet: only allow extra vlan len on ethernet devices - -[ Upstream commit 3c70c132488794e2489ab045559b0ce0afcf17de ] - -Packet sockets can be used by various net devices and are not -really restricted to ARPHRD_ETHER device types. However, when -currently checking for the extra 4 bytes that can be transmitted -in VLAN case, our assumption is that we generally probe on -ARPHRD_ETHER devices. Therefore, before looking into Ethernet -header, check the device type first. - -This also fixes the issue where non-ARPHRD_ETHER devices could -have no dev->hard_header_len in TX_RING SOCK_RAW case, and thus -the check would test unfilled linear part of the skb (instead -of non-linear). - -Fixes: 57f89bfa2140 ("network: Allow af_packet to transmit +4 bytes for VLAN packets.") -Fixes: 52f1454f629f ("packet: allow to transmit +4 byte in TX_RING slot for VLAN case") -Signed-off-by: Daniel Borkmann -Acked-by: Willem de Bruijn -Signed-off-by: David S. Miller ---- - net/packet/af_packet.c | 60 +++++++++++++++++++++----------------------------- - 1 file changed, 25 insertions(+), 35 deletions(-) - -diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c -index 3059f51..6e3cd2f 100644 ---- a/net/packet/af_packet.c -+++ b/net/packet/af_packet.c -@@ -1741,6 +1741,20 @@ static void fanout_release(struct sock *sk) - kfree_rcu(po->rollover, rcu); - } - -+static bool packet_extra_vlan_len_allowed(const struct net_device *dev, -+ struct sk_buff *skb) -+{ -+ /* Earlier code assumed this would be a VLAN pkt, double-check -+ * this now that we have the actual packet in hand. We can only -+ * do this check on Ethernet devices. -+ */ -+ if (unlikely(dev->type != ARPHRD_ETHER)) -+ return false; -+ -+ skb_reset_mac_header(skb); -+ return likely(eth_hdr(skb)->h_proto == htons(ETH_P_8021Q)); -+} -+ - static const struct proto_ops packet_ops; - - static const struct proto_ops packet_ops_spkt; -@@ -1902,18 +1916,10 @@ retry: - goto retry; - } - -- if (len > (dev->mtu + dev->hard_header_len + extra_len)) { -- /* Earlier code assumed this would be a VLAN pkt, -- * double-check this now that we have the actual -- * packet in hand. -- */ -- struct ethhdr *ehdr; -- skb_reset_mac_header(skb); -- ehdr = eth_hdr(skb); -- if (ehdr->h_proto != htons(ETH_P_8021Q)) { -- err = -EMSGSIZE; -- goto out_unlock; -- } -+ if (len > (dev->mtu + dev->hard_header_len + extra_len) && -+ !packet_extra_vlan_len_allowed(dev, skb)) { -+ err = -EMSGSIZE; -+ goto out_unlock; - } - - skb->protocol = proto; -@@ -2525,18 +2531,10 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) - tp_len = tpacket_fill_skb(po, skb, ph, dev, size_max, proto, - addr, hlen); - if (likely(tp_len >= 0) && -- tp_len > dev->mtu + dev->hard_header_len) { -- struct ethhdr *ehdr; -- /* Earlier code assumed this would be a VLAN pkt, -- * double-check this now that we have the actual -- * packet in hand. -- */ -+ tp_len > dev->mtu + dev->hard_header_len && -+ !packet_extra_vlan_len_allowed(dev, skb)) -+ tp_len = -EMSGSIZE; - -- skb_reset_mac_header(skb); -- ehdr = eth_hdr(skb); -- if (ehdr->h_proto != htons(ETH_P_8021Q)) -- tp_len = -EMSGSIZE; -- } - if (unlikely(tp_len < 0)) { - if (po->tp_loss) { - __packet_set_status(po, ph, -@@ -2757,18 +2755,10 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) - - sock_tx_timestamp(sk, &skb_shinfo(skb)->tx_flags); - -- if (!gso_type && (len > dev->mtu + reserve + extra_len)) { -- /* Earlier code assumed this would be a VLAN pkt, -- * double-check this now that we have the actual -- * packet in hand. -- */ -- struct ethhdr *ehdr; -- skb_reset_mac_header(skb); -- ehdr = eth_hdr(skb); -- if (ehdr->h_proto != htons(ETH_P_8021Q)) { -- err = -EMSGSIZE; -- goto out_free; -- } -+ if (!gso_type && (len > dev->mtu + reserve + extra_len) && -+ !packet_extra_vlan_len_allowed(dev, skb)) { -+ err = -EMSGSIZE; -+ goto out_free; - } - - skb->protocol = proto; --- -2.1.0 - - -From 3862137bdee867b99ade815a3c4d966ab223ac6d Mon Sep 17 00:00:00 2001 -From: Daniel Borkmann -Date: Wed, 11 Nov 2015 23:25:43 +0100 -Subject: [PATCH 15/43] packet: infer protocol from ethernet header if unset - -[ Upstream commit c72219b75fde768efccf7666342282fab7f9e4e7 ] - -In case no struct sockaddr_ll has been passed to packet -socket's sendmsg() when doing a TX_RING flush run, then -skb->protocol is set to po->num instead, which is the protocol -passed via socket(2)/bind(2). - -Applications only xmitting can go the path of allocating the -socket as socket(PF_PACKET, , 0) and do a bind(2) on the -TX_RING with sll_protocol of 0. That way, register_prot_hook() -is neither called on creation nor on bind time, which saves -cycles when there's no interest in capturing anyway. - -That leaves us however with po->num 0 instead and therefore -the TX_RING flush run sets skb->protocol to 0 as well. Eric -reported that this leads to problems when using tools like -trafgen over bonding device. I.e. the bonding's hash function -could invoke the kernel's flow dissector, which depends on -skb->protocol being properly set. In the current situation, all -the traffic is then directed to a single slave. - -Fix it up by inferring skb->protocol from the Ethernet header -when not set and we have ARPHRD_ETHER device type. This is only -done in case of SOCK_RAW and where we have a dev->hard_header_len -length. In case of ARPHRD_ETHER devices, this is guaranteed to -cover ETH_HLEN, and therefore being accessed on the skb after -the skb_store_bits(). - -Reported-by: Eric Dumazet -Signed-off-by: Daniel Borkmann -Acked-by: Willem de Bruijn -Signed-off-by: David S. Miller ---- - net/packet/af_packet.c | 11 +++++++++++ - 1 file changed, 11 insertions(+) - -diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c -index 6e3cd2f..45d4196 100644 ---- a/net/packet/af_packet.c -+++ b/net/packet/af_packet.c -@@ -2338,6 +2338,15 @@ static bool ll_header_truncated(const struct net_device *dev, int len) - return false; - } - -+static void tpacket_set_protocol(const struct net_device *dev, -+ struct sk_buff *skb) -+{ -+ if (dev->type == ARPHRD_ETHER) { -+ skb_reset_mac_header(skb); -+ skb->protocol = eth_hdr(skb)->h_proto; -+ } -+} -+ - static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, - void *frame, struct net_device *dev, int size_max, - __be16 proto, unsigned char *addr, int hlen) -@@ -2419,6 +2428,8 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, - dev->hard_header_len); - if (unlikely(err)) - return err; -+ if (!skb->protocol) -+ tpacket_set_protocol(dev, skb); - - data += dev->hard_header_len; - to_write -= dev->hard_header_len; --- -2.1.0 - - -From c5cde0ccf91927c87fd06b86d39d580a9244fb57 Mon Sep 17 00:00:00 2001 -From: Daniel Borkmann -Date: Wed, 11 Nov 2015 23:25:44 +0100 -Subject: [PATCH 16/43] packet: fix tpacket_snd max frame len - -[ Upstream commit 5cfb4c8d05b4409c4044cb9c05b19705c1d9818b ] - -Since it's introduction in commit 69e3c75f4d54 ("net: TX_RING and -packet mmap"), TX_RING could be used from SOCK_DGRAM and SOCK_RAW -side. When used with SOCK_DGRAM only, the size_max > dev->mtu + -reserve check should have reserve as 0, but currently, this is -unconditionally set (in it's original form as dev->hard_header_len). - -I think this is not correct since tpacket_fill_skb() would then -take dev->mtu and dev->hard_header_len into account for SOCK_DGRAM, -the extra VLAN_HLEN could be possible in both cases. Presumably, the -reserve code was copied from packet_snd(), but later on missed the -check. Make it similar as we have it in packet_snd(). - -Fixes: 69e3c75f4d54 ("net: TX_RING and packet mmap") -Signed-off-by: Daniel Borkmann -Acked-by: Willem de Bruijn -Signed-off-by: David S. Miller ---- - net/packet/af_packet.c | 9 +++++---- - 1 file changed, 5 insertions(+), 4 deletions(-) - -diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c -index 45d4196..4695a36 100644 ---- a/net/packet/af_packet.c -+++ b/net/packet/af_packet.c -@@ -2510,12 +2510,13 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) - if (unlikely(!(dev->flags & IFF_UP))) - goto out_put; - -- reserve = dev->hard_header_len + VLAN_HLEN; -+ if (po->sk.sk_socket->type == SOCK_RAW) -+ reserve = dev->hard_header_len; - size_max = po->tx_ring.frame_size - - (po->tp_hdrlen - sizeof(struct sockaddr_ll)); - -- if (size_max > dev->mtu + reserve) -- size_max = dev->mtu + reserve; -+ if (size_max > dev->mtu + reserve + VLAN_HLEN) -+ size_max = dev->mtu + reserve + VLAN_HLEN; - - do { - ph = packet_current_frame(po, &po->tx_ring, -@@ -2542,7 +2543,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) - tp_len = tpacket_fill_skb(po, skb, ph, dev, size_max, proto, - addr, hlen); - if (likely(tp_len >= 0) && -- tp_len > dev->mtu + dev->hard_header_len && -+ tp_len > dev->mtu + reserve && - !packet_extra_vlan_len_allowed(dev, skb)) - tp_len = -EMSGSIZE; - --- -2.1.0 - - -From 93d8395a99eefdd3b470cf9c04e947c6cb8bb85a Mon Sep 17 00:00:00 2001 -From: lucien -Date: Thu, 12 Nov 2015 13:07:07 +0800 -Subject: [PATCH 17/43] sctp: translate host order to network order when - setting a hmacid - -[ Upstream commit ed5a377d87dc4c87fb3e1f7f698cba38cd893103 ] - -now sctp auth cannot work well when setting a hmacid manually, which -is caused by that we didn't use the network order for hmacid, so fix -it by adding the transformation in sctp_auth_ep_set_hmacs. - -even we set hmacid with the network order in userspace, it still -can't work, because of this condition in sctp_auth_ep_set_hmacs(): - - if (id > SCTP_AUTH_HMAC_ID_MAX) - return -EOPNOTSUPP; - -so this wasn't working before and thus it won't break compatibility. - -Fixes: 65b07e5d0d09 ("[SCTP]: API updates to suport SCTP-AUTH extensions.") -Signed-off-by: Xin Long -Signed-off-by: Marcelo Ricardo Leitner -Acked-by: Neil Horman -Acked-by: Vlad Yasevich -Signed-off-by: David S. Miller ---- - net/sctp/auth.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/net/sctp/auth.c b/net/sctp/auth.c -index 4f15b7d..1543e39 100644 ---- a/net/sctp/auth.c -+++ b/net/sctp/auth.c -@@ -809,8 +809,8 @@ int sctp_auth_ep_set_hmacs(struct sctp_endpoint *ep, - if (!has_sha1) - return -EINVAL; - -- memcpy(ep->auth_hmacs_list->hmac_ids, &hmacs->shmac_idents[0], -- hmacs->shmac_num_idents * sizeof(__u16)); -+ for (i = 0; i < hmacs->shmac_num_idents; i++) -+ ep->auth_hmacs_list->hmac_ids[i] = htons(hmacs->shmac_idents[i]); - ep->auth_hmacs_list->param_hdr.length = htons(sizeof(sctp_paramhdr_t) + - hmacs->shmac_num_idents * sizeof(__u16)); - return 0; --- -2.1.0 - - -From d53e3dbdedfaa2e1e0fd8b37a5e34b0df8a295c5 Mon Sep 17 00:00:00 2001 -From: Tariq Toukan -Date: Thu, 12 Nov 2015 19:35:26 +0200 -Subject: [PATCH 18/43] net/mlx5e: Added self loopback prevention - -[ Upstream commit 66189961e986e53ae39822898fc2ce88f44c61bb ] - -Prevent outgoing multicast frames from looping back to the RX queue. - -By introducing new HW capability self_lb_en_modifiable, which indicates -the support to modify self_lb_en bit in modify_tir command. - -When this capability is set we can prevent TIRs from sending back -loopback multicast traffic to their own RQs, by "refreshing TIRs" with -modify_tir command, on every time new channels (SQs/RQs) are created at -device open. -This is needed since TIRs are static and only allocated once on driver -load, and the loopback decision is under their responsibility. - -Fixes issues of the kind: -"IPv6: eth2: IPv6 duplicate address fe80::e61d:2dff:fe5c:f2e9 detected!" -The issue is seen since the IPv6 solicitations multicast messages are -loopedback and the network stack thinks they are coming from another host. - -Fixes: 5c50368f3831 ("net/mlx5e: Light-weight netdev open/stop") -Signed-off-by: Tariq Toukan -Signed-off-by: Saeed Mahameed -Signed-off-by: Or Gerlitz -Signed-off-by: David S. Miller ---- - drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 56 ++++++++++++++++++++++- - include/linux/mlx5/mlx5_ifc.h | 24 ++++++---- - 2 files changed, 68 insertions(+), 12 deletions(-) - -diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c -index 59874d6..443632d 100644 ---- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c -+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c -@@ -1332,6 +1332,42 @@ static int mlx5e_modify_tir_lro(struct mlx5e_priv *priv, int tt) - return err; - } - -+static int mlx5e_refresh_tir_self_loopback_enable(struct mlx5_core_dev *mdev, -+ u32 tirn) -+{ -+ void *in; -+ int inlen; -+ int err; -+ -+ inlen = MLX5_ST_SZ_BYTES(modify_tir_in); -+ in = mlx5_vzalloc(inlen); -+ if (!in) -+ return -ENOMEM; -+ -+ MLX5_SET(modify_tir_in, in, bitmask.self_lb_en, 1); -+ -+ err = mlx5_core_modify_tir(mdev, tirn, in, inlen); -+ -+ kvfree(in); -+ -+ return err; -+} -+ -+static int mlx5e_refresh_tirs_self_loopback_enable(struct mlx5e_priv *priv) -+{ -+ int err; -+ int i; -+ -+ for (i = 0; i < MLX5E_NUM_TT; i++) { -+ err = mlx5e_refresh_tir_self_loopback_enable(priv->mdev, -+ priv->tirn[i]); -+ if (err) -+ return err; -+ } -+ -+ return 0; -+} -+ - static int mlx5e_set_dev_port_mtu(struct net_device *netdev) - { - struct mlx5e_priv *priv = netdev_priv(netdev); -@@ -1367,13 +1403,20 @@ int mlx5e_open_locked(struct net_device *netdev) - - err = mlx5e_set_dev_port_mtu(netdev); - if (err) -- return err; -+ goto err_clear_state_opened_flag; - - err = mlx5e_open_channels(priv); - if (err) { - netdev_err(netdev, "%s: mlx5e_open_channels failed, %d\n", - __func__, err); -- return err; -+ goto err_clear_state_opened_flag; -+ } -+ -+ err = mlx5e_refresh_tirs_self_loopback_enable(priv); -+ if (err) { -+ netdev_err(netdev, "%s: mlx5e_refresh_tirs_self_loopback_enable failed, %d\n", -+ __func__, err); -+ goto err_close_channels; - } - - mlx5e_update_carrier(priv); -@@ -1382,6 +1425,12 @@ int mlx5e_open_locked(struct net_device *netdev) - schedule_delayed_work(&priv->update_stats_work, 0); - - return 0; -+ -+err_close_channels: -+ mlx5e_close_channels(priv); -+err_clear_state_opened_flag: -+ clear_bit(MLX5E_STATE_OPENED, &priv->state); -+ return err; - } - - static int mlx5e_open(struct net_device *netdev) -@@ -1899,6 +1948,9 @@ static int mlx5e_check_required_hca_cap(struct mlx5_core_dev *mdev) - "Not creating net device, some required device capabilities are missing\n"); - return -ENOTSUPP; - } -+ if (!MLX5_CAP_ETH(mdev, self_lb_en_modifiable)) -+ mlx5_core_warn(mdev, "Self loop back prevention is not supported\n"); -+ - return 0; - } - -diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h -index dd20974..1565324 100644 ---- a/include/linux/mlx5/mlx5_ifc.h -+++ b/include/linux/mlx5/mlx5_ifc.h -@@ -453,26 +453,28 @@ struct mlx5_ifc_per_protocol_networking_offload_caps_bits { - u8 lro_cap[0x1]; - u8 lro_psh_flag[0x1]; - u8 lro_time_stamp[0x1]; -- u8 reserved_0[0x6]; -+ u8 reserved_0[0x3]; -+ u8 self_lb_en_modifiable[0x1]; -+ u8 reserved_1[0x2]; - u8 max_lso_cap[0x5]; -- u8 reserved_1[0x4]; -+ u8 reserved_2[0x4]; - u8 rss_ind_tbl_cap[0x4]; -- u8 reserved_2[0x3]; -+ u8 reserved_3[0x3]; - u8 tunnel_lso_const_out_ip_id[0x1]; -- u8 reserved_3[0x2]; -+ u8 reserved_4[0x2]; - u8 tunnel_statless_gre[0x1]; - u8 tunnel_stateless_vxlan[0x1]; - -- u8 reserved_4[0x20]; -+ u8 reserved_5[0x20]; - -- u8 reserved_5[0x10]; -+ u8 reserved_6[0x10]; - u8 lro_min_mss_size[0x10]; - -- u8 reserved_6[0x120]; -+ u8 reserved_7[0x120]; - - u8 lro_timer_supported_periods[4][0x20]; - -- u8 reserved_7[0x600]; -+ u8 reserved_8[0x600]; - }; - - struct mlx5_ifc_roce_cap_bits { -@@ -4051,9 +4053,11 @@ struct mlx5_ifc_modify_tis_in_bits { - }; - - struct mlx5_ifc_modify_tir_bitmask_bits { -- u8 reserved[0x20]; -+ u8 reserved_0[0x20]; - -- u8 reserved1[0x1f]; -+ u8 reserved_1[0x1b]; -+ u8 self_lb_en[0x1]; -+ u8 reserved_2[0x3]; - u8 lro[0x1]; - }; - --- -2.1.0 - - -From 752f833f18bc5af0d0f5172cbeb554cf9b836311 Mon Sep 17 00:00:00 2001 -From: Eran Ben Elisha -Date: Thu, 12 Nov 2015 19:35:29 +0200 -Subject: [PATCH 19/43] net/mlx4_core: Fix sleeping while holding spinlock at - rem_slave_counters - -[ Upstream commit f5adbfee72282bb1f456d52b04adacd4fe6ac502 ] - -When cleaning slave's counter resources, we hold a spinlock that -protects the slave's counters list. As part of the clean, we call -__mlx4_clear_if_stat which calls mlx4_alloc_cmd_mailbox which is a -sleepable function. - -In order to fix this issue, hold the spinlock, and copy all counter -indices into a temporary array, and release the spinlock. Afterwards, -iterate over this array and free every counter. Repeat this scenario -until the original list is empty (a new counter might have been added -while releasing the counters from the temporary array). - -Fixes: b72ca7e96acf ("net/mlx4_core: Reset counters data when freed") -Reported-by: Moni Shoua -Tested-by: Moni Shoua -Signed-off-by: Jack Morgenstein -Signed-off-by: Eran Ben Elisha -Signed-off-by: Or Gerlitz -Signed-off-by: David S. Miller ---- - .../net/ethernet/mellanox/mlx4/resource_tracker.c | 39 +++++++++++++++------- - 1 file changed, 27 insertions(+), 12 deletions(-) - -diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c -index 731423c..8bead97 100644 ---- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c -+++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c -@@ -4934,26 +4934,41 @@ static void rem_slave_counters(struct mlx4_dev *dev, int slave) - struct res_counter *counter; - struct res_counter *tmp; - int err; -- int index; -+ int *counters_arr = NULL; -+ int i, j; - - err = move_all_busy(dev, slave, RES_COUNTER); - if (err) - mlx4_warn(dev, "rem_slave_counters: Could not move all counters - too busy for slave %d\n", - slave); - -- spin_lock_irq(mlx4_tlock(dev)); -- list_for_each_entry_safe(counter, tmp, counter_list, com.list) { -- if (counter->com.owner == slave) { -- index = counter->com.res_id; -- rb_erase(&counter->com.node, -- &tracker->res_tree[RES_COUNTER]); -- list_del(&counter->com.list); -- kfree(counter); -- __mlx4_counter_free(dev, index); -+ counters_arr = kmalloc_array(dev->caps.max_counters, -+ sizeof(*counters_arr), GFP_KERNEL); -+ if (!counters_arr) -+ return; -+ -+ do { -+ i = 0; -+ j = 0; -+ spin_lock_irq(mlx4_tlock(dev)); -+ list_for_each_entry_safe(counter, tmp, counter_list, com.list) { -+ if (counter->com.owner == slave) { -+ counters_arr[i++] = counter->com.res_id; -+ rb_erase(&counter->com.node, -+ &tracker->res_tree[RES_COUNTER]); -+ list_del(&counter->com.list); -+ kfree(counter); -+ } -+ } -+ spin_unlock_irq(mlx4_tlock(dev)); -+ -+ while (j < i) { -+ __mlx4_counter_free(dev, counters_arr[j++]); - mlx4_release_resource(dev, slave, RES_COUNTER, 1, 0); - } -- } -- spin_unlock_irq(mlx4_tlock(dev)); -+ } while (i); -+ -+ kfree(counters_arr); - } - - static void rem_slave_xrcdns(struct mlx4_dev *dev, int slave) --- -2.1.0 - - -From 30de3861d01de70551c41529a9933cc2d3221c7b Mon Sep 17 00:00:00 2001 -From: "Jason A. Donenfeld" -Date: Thu, 12 Nov 2015 17:35:58 +0100 -Subject: [PATCH 20/43] ip_tunnel: disable preemption when updating per-cpu - tstats - -[ Upstream commit b4fe85f9c9146f60457e9512fb6055e69e6a7a65 ] - -Drivers like vxlan use the recently introduced -udp_tunnel_xmit_skb/udp_tunnel6_xmit_skb APIs. udp_tunnel6_xmit_skb -makes use of ip6tunnel_xmit, and ip6tunnel_xmit, after sending the -packet, updates the struct stats using the usual -u64_stats_update_begin/end calls on this_cpu_ptr(dev->tstats). -udp_tunnel_xmit_skb makes use of iptunnel_xmit, which doesn't touch -tstats, so drivers like vxlan, immediately after, call -iptunnel_xmit_stats, which does the same thing - calls -u64_stats_update_begin/end on this_cpu_ptr(dev->tstats). - -While vxlan is probably fine (I don't know?), calling a similar function -from, say, an unbound workqueue, on a fully preemptable kernel causes -real issues: - -[ 188.434537] BUG: using smp_processor_id() in preemptible [00000000] code: kworker/u8:0/6 -[ 188.435579] caller is debug_smp_processor_id+0x17/0x20 -[ 188.435583] CPU: 0 PID: 6 Comm: kworker/u8:0 Not tainted 4.2.6 #2 -[ 188.435607] Call Trace: -[ 188.435611] [] dump_stack+0x4f/0x7b -[ 188.435615] [] check_preemption_disabled+0x19d/0x1c0 -[ 188.435619] [] debug_smp_processor_id+0x17/0x20 - -The solution would be to protect the whole -this_cpu_ptr(dev->tstats)/u64_stats_update_begin/end blocks with -disabling preemption and then reenabling it. - -Signed-off-by: Jason A. Donenfeld -Acked-by: Hannes Frederic Sowa -Signed-off-by: David S. Miller ---- - include/net/ip6_tunnel.h | 3 ++- - include/net/ip_tunnels.h | 3 ++- - 2 files changed, 4 insertions(+), 2 deletions(-) - -diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h -index fa915fa..d49a8f8 100644 ---- a/include/net/ip6_tunnel.h -+++ b/include/net/ip6_tunnel.h -@@ -90,11 +90,12 @@ static inline void ip6tunnel_xmit(struct sock *sk, struct sk_buff *skb, - err = ip6_local_out_sk(sk, skb); - - if (net_xmit_eval(err) == 0) { -- struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats); -+ struct pcpu_sw_netstats *tstats = get_cpu_ptr(dev->tstats); - u64_stats_update_begin(&tstats->syncp); - tstats->tx_bytes += pkt_len; - tstats->tx_packets++; - u64_stats_update_end(&tstats->syncp); -+ put_cpu_ptr(tstats); - } else { - stats->tx_errors++; - stats->tx_aborted_errors++; -diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h -index f6dafec..62a750a 100644 ---- a/include/net/ip_tunnels.h -+++ b/include/net/ip_tunnels.h -@@ -287,12 +287,13 @@ static inline void iptunnel_xmit_stats(int err, - struct pcpu_sw_netstats __percpu *stats) - { - if (err > 0) { -- struct pcpu_sw_netstats *tstats = this_cpu_ptr(stats); -+ struct pcpu_sw_netstats *tstats = get_cpu_ptr(stats); - - u64_stats_update_begin(&tstats->syncp); - tstats->tx_bytes += err; - tstats->tx_packets++; - u64_stats_update_end(&tstats->syncp); -+ put_cpu_ptr(tstats); - } else if (err < 0) { - err_stats->tx_errors++; - err_stats->tx_aborted_errors++; --- -2.1.0 - - -From b532a633740a46806a53d618634ff1169c5e9a4b Mon Sep 17 00:00:00 2001 -From: Dragos Tatulea -Date: Mon, 16 Nov 2015 10:52:48 +0100 -Subject: [PATCH 21/43] net: switchdev: fix return code of fdb_dump stub - -[ Upstream commit 24cb7055a3066634a0f3fa0cd6a4780652905d35 ] - -rtnl_fdb_dump always expects an index to be returned by the ndo_fdb_dump op, -but when CONFIG_NET_SWITCHDEV is off, it returns an error. - -Fix that by returning the given unmodified idx. - -A similar fix was 0890cf6cb6ab ("switchdev: fix return value of -switchdev_port_fdb_dump in case of error") but for the CONFIG_NET_SWITCHDEV=y -case. - -Fixes: 45d4122ca7cd ("switchdev: add support for fdb add/del/dump via switchdev_port_obj ops.") -Signed-off-by: Dragos Tatulea -Acked-by: Jiri Pirko -Signed-off-by: David S. Miller ---- - include/net/switchdev.h | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/include/net/switchdev.h b/include/net/switchdev.h -index 319baab..731c40e 100644 ---- a/include/net/switchdev.h -+++ b/include/net/switchdev.h -@@ -272,7 +272,7 @@ static inline int switchdev_port_fdb_dump(struct sk_buff *skb, - struct net_device *filter_dev, - int idx) - { -- return -EOPNOTSUPP; -+ return idx; - } - - static inline void switchdev_port_fwd_mark_set(struct net_device *dev, --- -2.1.0 - - -From 06416634e5b57f33854a5905bdf87ddc68f99ff4 Mon Sep 17 00:00:00 2001 -From: Pavel Fedin -Date: Mon, 16 Nov 2015 17:51:34 +0300 -Subject: [PATCH 22/43] net: thunder: Check for driver data in nicvf_remove() - -[ Upstream commit 7750130d93decff06120df0d8ea024ff8a038a21 ] - -In some cases the crash is caused by nicvf_remove() being called from -outside. For example, if we try to feed the device to vfio after the -probe has failed for some reason. So, move the check to better place. - -Signed-off-by: Pavel Fedin -Signed-off-by: David S. Miller ---- - drivers/net/ethernet/cavium/thunder/nicvf_main.c | 10 ++++++++-- - 1 file changed, 8 insertions(+), 2 deletions(-) - -diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c -index a937772..7f709cb 100644 ---- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c -+++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c -@@ -1583,8 +1583,14 @@ err_disable_device: - static void nicvf_remove(struct pci_dev *pdev) - { - struct net_device *netdev = pci_get_drvdata(pdev); -- struct nicvf *nic = netdev_priv(netdev); -- struct net_device *pnetdev = nic->pnicvf->netdev; -+ struct nicvf *nic; -+ struct net_device *pnetdev; -+ -+ if (!netdev) -+ return; -+ -+ nic = netdev_priv(netdev); -+ pnetdev = nic->pnicvf->netdev; - - /* Check if this Qset is assigned to different VF. - * If yes, clean primary and all secondary Qsets. --- -2.1.0 - - -From fba5174af82c781b154aa5ffcc0fad2229767ec5 Mon Sep 17 00:00:00 2001 -From: Neil Horman -Date: Mon, 16 Nov 2015 13:09:10 -0500 -Subject: [PATCH 23/43] snmp: Remove duplicate OUTMCAST stat increment - -[ Upstream commit 41033f029e393a64e81966cbe34d66c6cf8a2e7e ] - -the OUTMCAST stat is double incremented, getting bumped once in the mcast code -itself, and again in the common ip output path. Remove the mcast bump, as its -not needed - -Validated by the reporter, with good results - -Signed-off-by: Neil Horman -Reported-by: Claus Jensen -CC: Claus Jensen -CC: David Miller -Signed-off-by: David S. Miller ---- - net/ipv6/mcast.c | 2 -- - 1 file changed, 2 deletions(-) - -diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c -index 083b292..41e3b5e 100644 ---- a/net/ipv6/mcast.c -+++ b/net/ipv6/mcast.c -@@ -1651,7 +1651,6 @@ out: - if (!err) { - ICMP6MSGOUT_INC_STATS(net, idev, ICMPV6_MLD2_REPORT); - ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS); -- IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUTMCAST, payload_len); - } else { - IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS); - } -@@ -2014,7 +2013,6 @@ out: - if (!err) { - ICMP6MSGOUT_INC_STATS(net, idev, type); - ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS); -- IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUTMCAST, full_len); - } else - IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS); - --- -2.1.0 - - -From 677298fd534e16274c8859e7658e57864bddb2a9 Mon Sep 17 00:00:00 2001 -From: Paolo Abeni -Date: Wed, 18 Nov 2015 16:40:19 +0100 -Subject: [PATCH 24/43] net/ip6_tunnel: fix dst leak - -[ Upstream commit 206b49500df558dbc15d8836b09f6397ec5ed8bb ] - -the commit cdf3464e6c6b ("ipv6: Fix dst_entry refcnt bugs in ip6_tunnel") -introduced percpu storage for ip6_tunnel dst cache, but while clearing -such cache it used raw_cpu_ptr to walk the per cpu entries, so cached -dst on non current cpu are not actually reset. - -This patch replaces raw_cpu_ptr with per_cpu_ptr, properly cleaning -such storage. - -Fixes: cdf3464e6c6b ("ipv6: Fix dst_entry refcnt bugs in ip6_tunnel") -Signed-off-by: Paolo Abeni -Acked-by: Martin KaFai Lau -Signed-off-by: David S. Miller ---- - net/ipv6/ip6_tunnel.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c -index eabffbb..137fca4 100644 ---- a/net/ipv6/ip6_tunnel.c -+++ b/net/ipv6/ip6_tunnel.c -@@ -177,7 +177,7 @@ void ip6_tnl_dst_reset(struct ip6_tnl *t) - int i; - - for_each_possible_cpu(i) -- ip6_tnl_per_cpu_dst_set(raw_cpu_ptr(t->dst_cache), NULL); -+ ip6_tnl_per_cpu_dst_set(per_cpu_ptr(t->dst_cache, i), NULL); - } - EXPORT_SYMBOL_GPL(ip6_tnl_dst_reset); - --- -2.1.0 - - -From 1451e186dddbd12331cbbd82cd1ec4df6207d598 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= -Date: Wed, 18 Nov 2015 21:13:07 +0100 -Subject: [PATCH 25/43] net: qmi_wwan: add XS Stick W100-2 from 4G Systems -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -[ Upstream commit 68242a5a1e2edce39b069385cbafb82304eac0f1 ] - -Thomas reports -" -4gsystems sells two total different LTE-surfsticks under the same name. -.. -The newer version of XS Stick W100 is from "omega" -.. -Under windows the driver switches to the same ID, and uses MI03\6 for -network and MI01\6 for modem. -.. -echo "1c9e 9b01" > /sys/bus/usb/drivers/qmi_wwan/new_id -echo "1c9e 9b01" > /sys/bus/usb-serial/drivers/option1/new_id - -T: Bus=01 Lev=01 Prnt=01 Port=03 Cnt=01 Dev#= 4 Spd=480 MxCh= 0 -D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 -P: Vendor=1c9e ProdID=9b01 Rev=02.32 -S: Manufacturer=USB Modem -S: Product=USB Modem -S: SerialNumber= -C: #Ifs= 5 Cfg#= 1 Atr=80 MxPwr=500mA -I: If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=option -I: If#= 1 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=option -I: If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=option -I: If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=qmi_wwan -I: If#= 4 Alt= 0 #EPs= 2 Cls=08(stor.) Sub=06 Prot=50 Driver=usb-storage - -Now all important things are there: - -wwp0s29f7u2i3 (net), ttyUSB2 (at), cdc-wdm0 (qmi), ttyUSB1 (at) - -There is also ttyUSB0, but it is not usable, at least not for at. - -The device works well with qmi and ModemManager-NetworkManager. -" - -Reported-by: Thomas Schäfer -Signed-off-by: Bjørn Mork -Signed-off-by: David S. Miller ---- - drivers/net/usb/qmi_wwan.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c -index 2a7c1be..66e0853 100644 ---- a/drivers/net/usb/qmi_wwan.c -+++ b/drivers/net/usb/qmi_wwan.c -@@ -775,6 +775,7 @@ static const struct usb_device_id products[] = { - {QMI_FIXED_INTF(0x2357, 0x9000, 4)}, /* TP-LINK MA260 */ - {QMI_FIXED_INTF(0x1bc7, 0x1200, 5)}, /* Telit LE920 */ - {QMI_FIXED_INTF(0x1bc7, 0x1201, 2)}, /* Telit LE920 */ -+ {QMI_FIXED_INTF(0x1c9e, 0x9b01, 3)}, /* XS Stick W100-2 from 4G Systems */ - {QMI_FIXED_INTF(0x0b3c, 0xc000, 4)}, /* Olivetti Olicard 100 */ - {QMI_FIXED_INTF(0x0b3c, 0xc001, 4)}, /* Olivetti Olicard 120 */ - {QMI_FIXED_INTF(0x0b3c, 0xc002, 4)}, /* Olivetti Olicard 140 */ --- -2.1.0 - - -From c137397256ef9502cfba4de0fa77f850ed0ad7a6 Mon Sep 17 00:00:00 2001 -From: Eric Dumazet -Date: Wed, 18 Nov 2015 12:40:13 -0800 -Subject: [PATCH 26/43] tcp: md5: fix lockdep annotation - -[ Upstream commit 1b8e6a01e19f001e9f93b39c32387961c91ed3cc ] - -When a passive TCP is created, we eventually call tcp_md5_do_add() -with sk pointing to the child. It is not owner by the user yet (we -will add this socket into listener accept queue a bit later anyway) - -But we do own the spinlock, so amend the lockdep annotation to avoid -following splat : - -[ 8451.090932] net/ipv4/tcp_ipv4.c:923 suspicious rcu_dereference_protected() usage! -[ 8451.090932] -[ 8451.090932] other info that might help us debug this: -[ 8451.090932] -[ 8451.090934] -[ 8451.090934] rcu_scheduler_active = 1, debug_locks = 1 -[ 8451.090936] 3 locks held by socket_sockopt_/214795: -[ 8451.090936] #0: (rcu_read_lock){.+.+..}, at: [] __netif_receive_skb_core+0x151/0xe90 -[ 8451.090947] #1: (rcu_read_lock){.+.+..}, at: [] ip_local_deliver_finish+0x43/0x2b0 -[ 8451.090952] #2: (slock-AF_INET){+.-...}, at: [] sk_clone_lock+0x1c5/0x500 -[ 8451.090958] -[ 8451.090958] stack backtrace: -[ 8451.090960] CPU: 7 PID: 214795 Comm: socket_sockopt_ - -[ 8451.091215] Call Trace: -[ 8451.091216] [] dump_stack+0x55/0x76 -[ 8451.091229] [] lockdep_rcu_suspicious+0xeb/0x110 -[ 8451.091235] [] tcp_md5_do_add+0x1bf/0x1e0 -[ 8451.091239] [] tcp_v4_syn_recv_sock+0x1f1/0x4c0 -[ 8451.091242] [] ? tcp_v4_md5_hash_skb+0x167/0x190 -[ 8451.091246] [] tcp_check_req+0x3c8/0x500 -[ 8451.091249] [] ? tcp_v4_inbound_md5_hash+0x11e/0x190 -[ 8451.091253] [] tcp_v4_rcv+0x3c0/0x9f0 -[ 8451.091256] [] ? ip_local_deliver_finish+0x43/0x2b0 -[ 8451.091260] [] ip_local_deliver_finish+0xb6/0x2b0 -[ 8451.091263] [] ? ip_local_deliver_finish+0x43/0x2b0 -[ 8451.091267] [] ip_local_deliver+0x48/0x80 -[ 8451.091270] [] ip_rcv_finish+0x160/0x700 -[ 8451.091273] [] ip_rcv+0x29e/0x3d0 -[ 8451.091277] [] __netif_receive_skb_core+0xb47/0xe90 - -Fixes: a8afca0329988 ("tcp: md5: protects md5sig_info with RCU") -Signed-off-by: Eric Dumazet -Reported-by: Willem de Bruijn -Signed-off-by: David S. Miller ---- - net/ipv4/tcp_ipv4.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c -index 93898e0..a7739c8 100644 ---- a/net/ipv4/tcp_ipv4.c -+++ b/net/ipv4/tcp_ipv4.c -@@ -922,7 +922,8 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, - } - - md5sig = rcu_dereference_protected(tp->md5sig_info, -- sock_owned_by_user(sk)); -+ sock_owned_by_user(sk) || -+ lockdep_is_held(&sk->sk_lock.slock)); - if (!md5sig) { - md5sig = kmalloc(sizeof(*md5sig), gfp); - if (!md5sig) --- -2.1.0 - - -From 9a9201e594f1e328a927ea12f672a3541b03d797 Mon Sep 17 00:00:00 2001 -From: Yuchung Cheng -Date: Wed, 18 Nov 2015 18:17:30 -0800 -Subject: [PATCH 27/43] tcp: disable Fast Open on timeouts after handshake - -[ Upstream commit 0e45f4da5981895e885dd72fe912a3f8e32bae73 ] - -Some middle-boxes black-hole the data after the Fast Open handshake -(https://www.ietf.org/proceedings/94/slides/slides-94-tcpm-13.pdf). -The exact reason is unknown. The work-around is to disable Fast Open -temporarily after multiple recurring timeouts with few or no data -delivered in the established state. - -Signed-off-by: Yuchung Cheng -Signed-off-by: Eric Dumazet -Reported-by: Christoph Paasch -Signed-off-by: David S. Miller ---- - net/ipv4/tcp_timer.c | 12 ++++++++++++ - 1 file changed, 12 insertions(+) - -diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c -index 7149ebc..04f0a05 100644 ---- a/net/ipv4/tcp_timer.c -+++ b/net/ipv4/tcp_timer.c -@@ -176,6 +176,18 @@ static int tcp_write_timeout(struct sock *sk) - syn_set = true; - } else { - if (retransmits_timed_out(sk, sysctl_tcp_retries1, 0, 0)) { -+ /* Some middle-boxes may black-hole Fast Open _after_ -+ * the handshake. Therefore we conservatively disable -+ * Fast Open on this path on recurring timeouts with -+ * few or zero bytes acked after Fast Open. -+ */ -+ if (tp->syn_data_acked && -+ tp->bytes_acked <= tp->rx_opt.mss_clamp) { -+ tcp_fastopen_cache_set(sk, 0, NULL, true, 0); -+ if (icsk->icsk_retransmits == sysctl_tcp_retries1) -+ NET_INC_STATS_BH(sock_net(sk), -+ LINUX_MIB_TCPFASTOPENACTIVEFAIL); -+ } - /* Black hole detection */ - tcp_mtu_probing(icsk, sk); - --- -2.1.0 - - -From fc6c61249dd9e249b96291b83c0751b441cda8de Mon Sep 17 00:00:00 2001 -From: Eric Dumazet -Date: Wed, 18 Nov 2015 21:03:33 -0800 -Subject: [PATCH 28/43] tcp: fix potential huge kmalloc() calls in TCP_REPAIR - -[ Upstream commit 5d4c9bfbabdb1d497f21afd81501e5c54b0c85d9 ] - -tcp_send_rcvq() is used for re-injecting data into tcp receive queue. - -Problems : - -- No check against size is performed, allowed user to fool kernel in - attempting very large memory allocations, eventually triggering - OOM when memory is fragmented. - -- In case of fault during the copy we do not return correct errno. - -Lets use alloc_skb_with_frags() to cook optimal skbs. - -Fixes: 292e8d8c8538 ("tcp: Move rcvq sending to tcp_input.c") -Fixes: c0e88ff0f256 ("tcp: Repair socket queues") -Signed-off-by: Eric Dumazet -Cc: Pavel Emelyanov -Acked-by: Pavel Emelyanov -Signed-off-by: David S. Miller ---- - net/ipv4/tcp_input.c | 22 +++++++++++++++++++--- - 1 file changed, 19 insertions(+), 3 deletions(-) - -diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c -index a8f515b..cc6bd43 100644 ---- a/net/ipv4/tcp_input.c -+++ b/net/ipv4/tcp_input.c -@@ -4457,19 +4457,34 @@ static int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int - int tcp_send_rcvq(struct sock *sk, struct msghdr *msg, size_t size) - { - struct sk_buff *skb; -+ int err = -ENOMEM; -+ int data_len = 0; - bool fragstolen; - - if (size == 0) - return 0; - -- skb = alloc_skb(size, sk->sk_allocation); -+ if (size > PAGE_SIZE) { -+ int npages = min_t(size_t, size >> PAGE_SHIFT, MAX_SKB_FRAGS); -+ -+ data_len = npages << PAGE_SHIFT; -+ size = data_len + (size & ~PAGE_MASK); -+ } -+ skb = alloc_skb_with_frags(size - data_len, data_len, -+ PAGE_ALLOC_COSTLY_ORDER, -+ &err, sk->sk_allocation); - if (!skb) - goto err; - -+ skb_put(skb, size - data_len); -+ skb->data_len = data_len; -+ skb->len = size; -+ - if (tcp_try_rmem_schedule(sk, skb, skb->truesize)) - goto err_free; - -- if (memcpy_from_msg(skb_put(skb, size), msg, size)) -+ err = skb_copy_datagram_from_iter(skb, 0, &msg->msg_iter, size); -+ if (err) - goto err_free; - - TCP_SKB_CB(skb)->seq = tcp_sk(sk)->rcv_nxt; -@@ -4485,7 +4500,8 @@ int tcp_send_rcvq(struct sock *sk, struct msghdr *msg, size_t size) - err_free: - kfree_skb(skb); - err: -- return -ENOMEM; -+ return err; -+ - } - - static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) --- -2.1.0 - - -From 419ee3eb1d6dea668f00ee440c670f8f479f8216 Mon Sep 17 00:00:00 2001 -From: Eric Dumazet -Date: Thu, 26 Nov 2015 08:18:14 -0800 -Subject: [PATCH 29/43] tcp: initialize tp->copied_seq in case of cross SYN - connection - -[ Upstream commit 142a2e7ece8d8ac0e818eb2c91f99ca894730e2a ] - -Dmitry provided a syzkaller (http://github.com/google/syzkaller) -generated program that triggers the WARNING at -net/ipv4/tcp.c:1729 in tcp_recvmsg() : - -WARN_ON(tp->copied_seq != tp->rcv_nxt && - !(flags & (MSG_PEEK | MSG_TRUNC))); - -His program is specifically attempting a Cross SYN TCP exchange, -that we support (for the pleasure of hackers ?), but it looks we -lack proper tcp->copied_seq initialization. - -Thanks again Dmitry for your report and testings. - -Signed-off-by: Eric Dumazet -Reported-by: Dmitry Vyukov -Tested-by: Dmitry Vyukov -Signed-off-by: David S. Miller ---- - net/ipv4/tcp_input.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c -index cc6bd43..0a2b61d 100644 ---- a/net/ipv4/tcp_input.c -+++ b/net/ipv4/tcp_input.c -@@ -5659,6 +5659,7 @@ discard: - } - - tp->rcv_nxt = TCP_SKB_CB(skb)->seq + 1; -+ tp->copied_seq = tp->rcv_nxt; - tp->rcv_wup = TCP_SKB_CB(skb)->seq + 1; - - /* RFC1323: The window in SYN & SYN/ACK segments is --- -2.1.0 - - -From 4d8baf23458211113b2b4aaac26d715a1cfa9a4f Mon Sep 17 00:00:00 2001 -From: Daniel Borkmann -Date: Fri, 20 Nov 2015 00:11:56 +0100 -Subject: [PATCH 30/43] net, scm: fix PaX detected msg_controllen overflow in - scm_detach_fds - -[ Upstream commit 6900317f5eff0a7070c5936e5383f589e0de7a09 ] - -David and HacKurx reported a following/similar size overflow triggered -in a grsecurity kernel, thanks to PaX's gcc size overflow plugin: - -(Already fixed in later grsecurity versions by Brad and PaX Team.) - -[ 1002.296137] PAX: size overflow detected in function scm_detach_fds net/core/scm.c:314 - cicus.202_127 min, count: 4, decl: msg_controllen; num: 0; context: msghdr; -[ 1002.296145] CPU: 0 PID: 3685 Comm: scm_rights_recv Not tainted 4.2.3-grsec+ #7 -[ 1002.296149] Hardware name: Apple Inc. MacBookAir5,1/Mac-66F35F19FE2A0D05, [...] -[ 1002.296153] ffffffff81c27366 0000000000000000 ffffffff81c27375 ffffc90007843aa8 -[ 1002.296162] ffffffff818129ba 0000000000000000 ffffffff81c27366 ffffc90007843ad8 -[ 1002.296169] ffffffff8121f838 fffffffffffffffc fffffffffffffffc ffffc90007843e60 -[ 1002.296176] Call Trace: -[ 1002.296190] [] dump_stack+0x45/0x57 -[ 1002.296200] [] report_size_overflow+0x38/0x60 -[ 1002.296209] [] scm_detach_fds+0x2ce/0x300 -[ 1002.296220] [] unix_stream_read_generic+0x609/0x930 -[ 1002.296228] [] unix_stream_recvmsg+0x4f/0x60 -[ 1002.296236] [] ? unix_set_peek_off+0x50/0x50 -[ 1002.296243] [] sock_recvmsg+0x47/0x60 -[ 1002.296248] [] ___sys_recvmsg+0xe2/0x1e0 -[ 1002.296257] [] __sys_recvmsg+0x46/0x80 -[ 1002.296263] [] SyS_recvmsg+0x2c/0x40 -[ 1002.296271] [] entry_SYSCALL_64_fastpath+0x12/0x85 - -Further investigation showed that this can happen when an *odd* number of -fds are being passed over AF_UNIX sockets. - -In these cases CMSG_LEN(i * sizeof(int)) and CMSG_SPACE(i * sizeof(int)), -where i is the number of successfully passed fds, differ by 4 bytes due -to the extra CMSG_ALIGN() padding in CMSG_SPACE() to an 8 byte boundary -on 64 bit. The padding is used to align subsequent cmsg headers in the -control buffer. - -When the control buffer passed in from the receiver side *lacks* these 4 -bytes (e.g. due to buggy/wrong API usage), then msg->msg_controllen will -overflow in scm_detach_fds(): - - int cmlen = CMSG_LEN(i * sizeof(int)); <--- cmlen w/o tail-padding - err = put_user(SOL_SOCKET, &cm->cmsg_level); - if (!err) - err = put_user(SCM_RIGHTS, &cm->cmsg_type); - if (!err) - err = put_user(cmlen, &cm->cmsg_len); - if (!err) { - cmlen = CMSG_SPACE(i * sizeof(int)); <--- cmlen w/ 4 byte extra tail-padding - msg->msg_control += cmlen; - msg->msg_controllen -= cmlen; <--- iff no tail-padding space here ... - } ... wrap-around - -F.e. it will wrap to a length of 18446744073709551612 bytes in case the -receiver passed in msg->msg_controllen of 20 bytes, and the sender -properly transferred 1 fd to the receiver, so that its CMSG_LEN results -in 20 bytes and CMSG_SPACE in 24 bytes. - -In case of MSG_CMSG_COMPAT (scm_detach_fds_compat()), I haven't seen an -issue in my tests as alignment seems always on 4 byte boundary. Same -should be in case of native 32 bit, where we end up with 4 byte boundaries -as well. - -In practice, passing msg->msg_controllen of 20 to recvmsg() while receiving -a single fd would mean that on successful return, msg->msg_controllen is -being set by the kernel to 24 bytes instead, thus more than the input -buffer advertised. It could f.e. become an issue if such application later -on zeroes or copies the control buffer based on the returned msg->msg_controllen -elsewhere. - -Maximum number of fds we can send is a hard upper limit SCM_MAX_FD (253). - -Going over the code, it seems like msg->msg_controllen is not being read -after scm_detach_fds() in scm_recv() anymore by the kernel, good! - -Relevant recvmsg() handler are unix_dgram_recvmsg() (unix_seqpacket_recvmsg()) -and unix_stream_recvmsg(). Both return back to their recvmsg() caller, -and ___sys_recvmsg() places the updated length, that is, new msg_control - -old msg_control pointer into msg->msg_controllen (hence the 24 bytes seen -in the example). - -Long time ago, Wei Yongjun fixed something related in commit 1ac70e7ad24a -("[NET]: Fix function put_cmsg() which may cause usr application memory -overflow"). - -RFC3542, section 20.2. says: - - The fields shown as "XX" are possible padding, between the cmsghdr - structure and the data, and between the data and the next cmsghdr - structure, if required by the implementation. While sending an - application may or may not include padding at the end of last - ancillary data in msg_controllen and implementations must accept both - as valid. On receiving a portable application must provide space for - padding at the end of the last ancillary data as implementations may - copy out the padding at the end of the control message buffer and - include it in the received msg_controllen. When recvmsg() is called - if msg_controllen is too small for all the ancillary data items - including any trailing padding after the last item an implementation - may set MSG_CTRUNC. - -Since we didn't place MSG_CTRUNC for already quite a long time, just do -the same as in 1ac70e7ad24a to avoid an overflow. - -Btw, even man-page author got this wrong :/ See db939c9b26e9 ("cmsg.3: Fix -error in SCM_RIGHTS code sample"). Some people must have copied this (?), -thus it got triggered in the wild (reported several times during boot by -David and HacKurx). - -No Fixes tag this time as pre 2002 (that is, pre history tree). - -Reported-by: David Sterba -Reported-by: HacKurx -Cc: PaX Team -Cc: Emese Revfy -Cc: Brad Spengler -Cc: Wei Yongjun -Cc: Eric Dumazet -Reviewed-by: Hannes Frederic Sowa -Signed-off-by: Daniel Borkmann -Signed-off-by: David S. Miller ---- - net/core/scm.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/net/core/scm.c b/net/core/scm.c -index 3b6899b..8a1741b 100644 ---- a/net/core/scm.c -+++ b/net/core/scm.c -@@ -305,6 +305,8 @@ void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm) - err = put_user(cmlen, &cm->cmsg_len); - if (!err) { - cmlen = CMSG_SPACE(i*sizeof(int)); -+ if (msg->msg_controllen < cmlen) -+ cmlen = msg->msg_controllen; - msg->msg_control += cmlen; - msg->msg_controllen -= cmlen; - } --- -2.1.0 - - -From fd10116065e9a7bde6eb70f3f69c5e9ec2722e2e Mon Sep 17 00:00:00 2001 -From: Nikolay Aleksandrov -Date: Fri, 20 Nov 2015 13:54:19 +0100 -Subject: [PATCH 31/43] net: ipmr: fix static mfc/dev leaks on table - destruction - -[ Upstream commit 0e615e9601a15efeeb8942cf7cd4dadba0c8c5a7 ] - -When destroying an mrt table the static mfc entries and the static -devices are kept, which leads to devices that can never be destroyed -(because of refcnt taken) and leaked memory, for example: -unreferenced object 0xffff880034c144c0 (size 192): - comm "mfc-broken", pid 4777, jiffies 4320349055 (age 46001.964s) - hex dump (first 32 bytes): - 98 53 f0 34 00 88 ff ff 98 53 f0 34 00 88 ff ff .S.4.....S.4.... - ef 0a 0a 14 01 02 03 04 00 00 00 00 01 00 00 00 ................ - backtrace: - [] kmemleak_alloc+0x4e/0xb0 - [] kmem_cache_alloc+0x190/0x300 - [] ip_mroute_setsockopt+0x5cb/0x910 - [] do_ip_setsockopt.isra.11+0x105/0xff0 - [] ip_setsockopt+0x30/0xa0 - [] raw_setsockopt+0x33/0x90 - [] sock_common_setsockopt+0x14/0x20 - [] SyS_setsockopt+0x71/0xc0 - [] entry_SYSCALL_64_fastpath+0x16/0x7a - [] 0xffffffffffffffff - -Make sure that everything is cleaned on netns destruction. - -Signed-off-by: Nikolay Aleksandrov -Reviewed-by: Cong Wang -Signed-off-by: David S. Miller ---- - net/ipv4/ipmr.c | 15 ++++++++------- - 1 file changed, 8 insertions(+), 7 deletions(-) - -diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c -index 8e8203d..ef7e2c4 100644 ---- a/net/ipv4/ipmr.c -+++ b/net/ipv4/ipmr.c -@@ -134,7 +134,7 @@ static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb, - struct mfc_cache *c, struct rtmsg *rtm); - static void mroute_netlink_event(struct mr_table *mrt, struct mfc_cache *mfc, - int cmd); --static void mroute_clean_tables(struct mr_table *mrt); -+static void mroute_clean_tables(struct mr_table *mrt, bool all); - static void ipmr_expire_process(unsigned long arg); - - #ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES -@@ -350,7 +350,7 @@ static struct mr_table *ipmr_new_table(struct net *net, u32 id) - static void ipmr_free_table(struct mr_table *mrt) - { - del_timer_sync(&mrt->ipmr_expire_timer); -- mroute_clean_tables(mrt); -+ mroute_clean_tables(mrt, true); - kfree(mrt); - } - -@@ -1208,7 +1208,7 @@ static int ipmr_mfc_add(struct net *net, struct mr_table *mrt, - * Close the multicast socket, and clear the vif tables etc - */ - --static void mroute_clean_tables(struct mr_table *mrt) -+static void mroute_clean_tables(struct mr_table *mrt, bool all) - { - int i; - LIST_HEAD(list); -@@ -1217,8 +1217,9 @@ static void mroute_clean_tables(struct mr_table *mrt) - /* Shut down all active vif entries */ - - for (i = 0; i < mrt->maxvif; i++) { -- if (!(mrt->vif_table[i].flags & VIFF_STATIC)) -- vif_delete(mrt, i, 0, &list); -+ if (!all && (mrt->vif_table[i].flags & VIFF_STATIC)) -+ continue; -+ vif_delete(mrt, i, 0, &list); - } - unregister_netdevice_many(&list); - -@@ -1226,7 +1227,7 @@ static void mroute_clean_tables(struct mr_table *mrt) - - for (i = 0; i < MFC_LINES; i++) { - list_for_each_entry_safe(c, next, &mrt->mfc_cache_array[i], list) { -- if (c->mfc_flags & MFC_STATIC) -+ if (!all && (c->mfc_flags & MFC_STATIC)) - continue; - list_del_rcu(&c->list); - mroute_netlink_event(mrt, c, RTM_DELROUTE); -@@ -1261,7 +1262,7 @@ static void mrtsock_destruct(struct sock *sk) - NETCONFA_IFINDEX_ALL, - net->ipv4.devconf_all); - RCU_INIT_POINTER(mrt->mroute_sk, NULL); -- mroute_clean_tables(mrt); -+ mroute_clean_tables(mrt, false); - } - } - rtnl_unlock(); --- -2.1.0 - - -From 6fea7a6bf9a832ea306a816204984e2244000784 Mon Sep 17 00:00:00 2001 -From: Nikolay Aleksandrov -Date: Fri, 20 Nov 2015 13:54:20 +0100 -Subject: [PATCH 32/43] net: ip6mr: fix static mfc/dev leaks on table - destruction - -[ Upstream commit 4c6980462f32b4f282c5d8e5f7ea8070e2937725 ] - -Similar to ipv4, when destroying an mrt table the static mfc entries and -the static devices are kept, which leads to devices that can never be -destroyed (because of refcnt taken) and leaked memory. Make sure that -everything is cleaned up on netns destruction. - -Fixes: 8229efdaef1e ("netns: ip6mr: enable namespace support in ipv6 multicast forwarding code") -CC: Benjamin Thery -Signed-off-by: Nikolay Aleksandrov -Reviewed-by: Cong Wang -Signed-off-by: David S. Miller ---- - net/ipv6/ip6mr.c | 15 ++++++++------- - 1 file changed, 8 insertions(+), 7 deletions(-) - -diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c -index 0e004cc..35eee72 100644 ---- a/net/ipv6/ip6mr.c -+++ b/net/ipv6/ip6mr.c -@@ -118,7 +118,7 @@ static void mr6_netlink_event(struct mr6_table *mrt, struct mfc6_cache *mfc, - int cmd); - static int ip6mr_rtm_dumproute(struct sk_buff *skb, - struct netlink_callback *cb); --static void mroute_clean_tables(struct mr6_table *mrt); -+static void mroute_clean_tables(struct mr6_table *mrt, bool all); - static void ipmr_expire_process(unsigned long arg); - - #ifdef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES -@@ -334,7 +334,7 @@ static struct mr6_table *ip6mr_new_table(struct net *net, u32 id) - static void ip6mr_free_table(struct mr6_table *mrt) - { - del_timer_sync(&mrt->ipmr_expire_timer); -- mroute_clean_tables(mrt); -+ mroute_clean_tables(mrt, true); - kfree(mrt); - } - -@@ -1542,7 +1542,7 @@ static int ip6mr_mfc_add(struct net *net, struct mr6_table *mrt, - * Close the multicast socket, and clear the vif tables etc - */ - --static void mroute_clean_tables(struct mr6_table *mrt) -+static void mroute_clean_tables(struct mr6_table *mrt, bool all) - { - int i; - LIST_HEAD(list); -@@ -1552,8 +1552,9 @@ static void mroute_clean_tables(struct mr6_table *mrt) - * Shut down all active vif entries - */ - for (i = 0; i < mrt->maxvif; i++) { -- if (!(mrt->vif6_table[i].flags & VIFF_STATIC)) -- mif6_delete(mrt, i, &list); -+ if (!all && (mrt->vif6_table[i].flags & VIFF_STATIC)) -+ continue; -+ mif6_delete(mrt, i, &list); - } - unregister_netdevice_many(&list); - -@@ -1562,7 +1563,7 @@ static void mroute_clean_tables(struct mr6_table *mrt) - */ - for (i = 0; i < MFC6_LINES; i++) { - list_for_each_entry_safe(c, next, &mrt->mfc6_cache_array[i], list) { -- if (c->mfc_flags & MFC_STATIC) -+ if (!all && (c->mfc_flags & MFC_STATIC)) - continue; - write_lock_bh(&mrt_lock); - list_del(&c->list); -@@ -1625,7 +1626,7 @@ int ip6mr_sk_done(struct sock *sk) - net->ipv6.devconf_all); - write_unlock_bh(&mrt_lock); - -- mroute_clean_tables(mrt); -+ mroute_clean_tables(mrt, false); - err = 0; - break; - } --- -2.1.0 - - -From 46b15261453a821e3d33889189715d61c4fb0af2 Mon Sep 17 00:00:00 2001 -From: Nikolay Aleksandrov -Date: Sat, 21 Nov 2015 19:46:19 +0100 -Subject: [PATCH 33/43] vrf: fix double free and memory corruption on - register_netdevice failure - -[ Upstream commit 7f109f7cc37108cba7243bc832988525b0d85909 ] - -When vrf's ->newlink is called, if register_netdevice() fails then it -does free_netdev(), but that's also done by rtnl_newlink() so a second -free happens and memory gets corrupted, to reproduce execute the -following line a couple of times (1 - 5 usually is enough): -$ for i in `seq 1 5`; do ip link add vrf: type vrf table 1; done; -This works because we fail in register_netdevice() because of the wrong -name "vrf:". - -And here's a trace of one crash: -[ 28.792157] ------------[ cut here ]------------ -[ 28.792407] kernel BUG at fs/namei.c:246! -[ 28.792608] invalid opcode: 0000 [#1] SMP -[ 28.793240] Modules linked in: vrf nfsd auth_rpcgss oid_registry -nfs_acl nfs lockd grace sunrpc crct10dif_pclmul crc32_pclmul -crc32c_intel qxl drm_kms_helper ttm drm aesni_intel aes_x86_64 psmouse -glue_helper lrw evdev gf128mul i2c_piix4 ablk_helper cryptd ppdev -parport_pc parport serio_raw pcspkr virtio_balloon virtio_console -i2c_core acpi_cpufreq button 9pnet_virtio 9p 9pnet fscache ipv6 autofs4 -ext4 crc16 mbcache jbd2 virtio_blk virtio_net sg sr_mod cdrom -ata_generic ehci_pci uhci_hcd ehci_hcd e1000 usbcore usb_common ata_piix -libata virtio_pci virtio_ring virtio scsi_mod floppy -[ 28.796016] CPU: 0 PID: 1148 Comm: ld-linux-x86-64 Not tainted -4.4.0-rc1+ #24 -[ 28.796016] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), -BIOS 1.8.1-20150318_183358- 04/01/2014 -[ 28.796016] task: ffff8800352561c0 ti: ffff88003592c000 task.ti: -ffff88003592c000 -[ 28.796016] RIP: 0010:[] [] -putname+0x43/0x60 -[ 28.796016] RSP: 0018:ffff88003592fe88 EFLAGS: 00010246 -[ 28.796016] RAX: 0000000000000000 RBX: ffff8800352561c0 RCX: -0000000000000001 -[ 28.796016] RDX: 0000000000000000 RSI: 0000000000000000 RDI: -ffff88003784f000 -[ 28.796016] RBP: ffff88003592ff08 R08: 0000000000000001 R09: -0000000000000000 -[ 28.796016] R10: 0000000000000000 R11: 0000000000000001 R12: -0000000000000000 -[ 28.796016] R13: 000000000000047c R14: ffff88003784f000 R15: -ffff8800358c4a00 -[ 28.796016] FS: 0000000000000000(0000) GS:ffff88003fc00000(0000) -knlGS:0000000000000000 -[ 28.796016] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 -[ 28.796016] CR2: 00007ffd583bc2d9 CR3: 0000000035a99000 CR4: -00000000000406f0 -[ 28.796016] Stack: -[ 28.796016] ffffffff8121045d ffffffff812102d3 ffff8800352561c0 -ffff880035a91660 -[ 28.796016] ffff8800008a9880 0000000000000000 ffffffff81a49940 -00ffffff81218684 -[ 28.796016] ffff8800352561c0 000000000000047c 0000000000000000 -ffff880035b36d80 -[ 28.796016] Call Trace: -[ 28.796016] [] ? -do_execveat_common.isra.34+0x74d/0x930 -[ 28.796016] [] ? -do_execveat_common.isra.34+0x5c3/0x930 -[ 28.796016] [] do_execve+0x2c/0x30 -[ 28.796016] [] -call_usermodehelper_exec_async+0xf0/0x140 -[ 28.796016] [] ? umh_complete+0x40/0x40 -[ 28.796016] [] ret_from_fork+0x3f/0x70 -[ 28.796016] Code: 48 8d 47 1c 48 89 e5 53 48 8b 37 48 89 fb 48 39 c6 -74 1a 48 8b 3d 7e e9 8f 00 e8 49 fa fc ff 48 89 df e8 f1 01 fd ff 5b 5d -f3 c3 <0f> 0b 48 89 fe 48 8b 3d 61 e9 8f 00 e8 2c fa fc ff 5b 5d eb e9 -[ 28.796016] RIP [] putname+0x43/0x60 -[ 28.796016] RSP - -Fixes: 193125dbd8eb ("net: Introduce VRF device driver") -Signed-off-by: Nikolay Aleksandrov -Acked-by: David Ahern -Signed-off-by: David S. Miller ---- - drivers/net/vrf.c | 15 ++------------- - 1 file changed, 2 insertions(+), 13 deletions(-) - -diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c -index 488c6f5..c9e309c 100644 ---- a/drivers/net/vrf.c -+++ b/drivers/net/vrf.c -@@ -581,7 +581,6 @@ static int vrf_newlink(struct net *src_net, struct net_device *dev, - { - struct net_vrf *vrf = netdev_priv(dev); - struct net_vrf_dev *vrf_ptr; -- int err; - - if (!data || !data[IFLA_VRF_TABLE]) - return -EINVAL; -@@ -590,26 +589,16 @@ static int vrf_newlink(struct net *src_net, struct net_device *dev, - - dev->priv_flags |= IFF_VRF_MASTER; - -- err = -ENOMEM; - vrf_ptr = kmalloc(sizeof(*dev->vrf_ptr), GFP_KERNEL); - if (!vrf_ptr) -- goto out_fail; -+ return -ENOMEM; - - vrf_ptr->ifindex = dev->ifindex; - vrf_ptr->tb_id = vrf->tb_id; - -- err = register_netdevice(dev); -- if (err < 0) -- goto out_fail; -- - rcu_assign_pointer(dev->vrf_ptr, vrf_ptr); - -- return 0; -- --out_fail: -- kfree(vrf_ptr); -- free_netdev(dev); -- return err; -+ return register_netdev(dev); - } - - static size_t vrf_nl_getsize(const struct net_device *dev) --- -2.1.0 - - -From 3ac4063f990a9e106c8971d60e1b430518fb5cec Mon Sep 17 00:00:00 2001 -From: Aaro Koskinen -Date: Sun, 22 Nov 2015 01:08:54 +0200 -Subject: [PATCH 34/43] broadcom: fix PHY_ID_BCM5481 entry in the id table - -[ Upstream commit 3c25a860d17b7378822f35d8c9141db9507e3beb ] - -Commit fcb26ec5b18d ("broadcom: move all PHY_ID's to header") -updated broadcom_tbl to use PHY_IDs, but incorrectly replaced 0x0143bca0 -with PHY_ID_BCM5482 (making a duplicate entry, and completely omitting -the original). Fix that. - -Fixes: fcb26ec5b18d ("broadcom: move all PHY_ID's to header") -Signed-off-by: Aaro Koskinen -Signed-off-by: David S. Miller ---- - drivers/net/phy/broadcom.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c -index 9c71295..85e64044 100644 ---- a/drivers/net/phy/broadcom.c -+++ b/drivers/net/phy/broadcom.c -@@ -675,7 +675,7 @@ static struct mdio_device_id __maybe_unused broadcom_tbl[] = { - { PHY_ID_BCM5461, 0xfffffff0 }, - { PHY_ID_BCM54616S, 0xfffffff0 }, - { PHY_ID_BCM5464, 0xfffffff0 }, -- { PHY_ID_BCM5482, 0xfffffff0 }, -+ { PHY_ID_BCM5481, 0xfffffff0 }, - { PHY_ID_BCM5482, 0xfffffff0 }, - { PHY_ID_BCM50610, 0xfffffff0 }, - { PHY_ID_BCM50610M, 0xfffffff0 }, --- -2.1.0 - - -From d71b7cd7146378b31252e33a23ad693b6d461ed8 Mon Sep 17 00:00:00 2001 -From: Ying Xue -Date: Tue, 24 Nov 2015 13:57:57 +0800 -Subject: [PATCH 35/43] tipc: fix error handling of expanding buffer headroom - -[ Upstream commit 7098356baca723513e97ca0020df4e18bc353be3 ] - -Coverity says: - -*** CID 1338065: Error handling issues (CHECKED_RETURN) -/net/tipc/udp_media.c: 162 in tipc_udp_send_msg() -156 struct udp_media_addr *dst = (struct udp_media_addr *)&dest->value; -157 struct udp_media_addr *src = (struct udp_media_addr *)&b->addr.value; -158 struct sk_buff *clone; -159 struct rtable *rt; -160 -161 if (skb_headroom(skb) < UDP_MIN_HEADROOM) ->>> CID 1338065: Error handling issues (CHECKED_RETURN) ->>> Calling "pskb_expand_head" without checking return value (as is done elsewhere 51 out of 56 times). -162 pskb_expand_head(skb, UDP_MIN_HEADROOM, 0, GFP_ATOMIC); -163 -164 clone = skb_clone(skb, GFP_ATOMIC); -165 skb_set_inner_protocol(clone, htons(ETH_P_TIPC)); -166 ub = rcu_dereference_rtnl(b->media_ptr); -167 if (!ub) { - -When expanding buffer headroom over udp tunnel with pskb_expand_head(), -it's unfortunate that we don't check its return value. As a result, if -the function returns an error code due to the lack of memory, it may -cause unpredictable consequence as we unconditionally consider that -it's always successful. - -Fixes: e53567948f82 ("tipc: conditionally expand buffer headroom over udp tunnel") -Reported-by: -Cc: Stephen Hemminger -Signed-off-by: Ying Xue -Signed-off-by: David S. Miller ---- - net/tipc/udp_media.c | 7 +++++-- - 1 file changed, 5 insertions(+), 2 deletions(-) - -diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c -index cd7c5f1..86f2e7c 100644 ---- a/net/tipc/udp_media.c -+++ b/net/tipc/udp_media.c -@@ -159,8 +159,11 @@ static int tipc_udp_send_msg(struct net *net, struct sk_buff *skb, - struct sk_buff *clone; - struct rtable *rt; - -- if (skb_headroom(skb) < UDP_MIN_HEADROOM) -- pskb_expand_head(skb, UDP_MIN_HEADROOM, 0, GFP_ATOMIC); -+ if (skb_headroom(skb) < UDP_MIN_HEADROOM) { -+ err = pskb_expand_head(skb, UDP_MIN_HEADROOM, 0, GFP_ATOMIC); -+ if (err) -+ goto tx_error; -+ } - - clone = skb_clone(skb, GFP_ATOMIC); - skb_set_inner_protocol(clone, htons(ETH_P_TIPC)); --- -2.1.0 - - -From d26834684ff963259ed7fd9e19c816ed71d7bf61 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Michal=20Kube=C4=8Dek?= -Date: Tue, 24 Nov 2015 15:07:11 +0100 -Subject: [PATCH 36/43] ipv6: distinguish frag queues by device for multicast - and link-local packets - -[ Upstream commit 264640fc2c5f4f913db5c73fa3eb1ead2c45e9d7 ] - -If a fragmented multicast packet is received on an ethernet device which -has an active macvlan on top of it, each fragment is duplicated and -received both on the underlying device and the macvlan. If some -fragments for macvlan are processed before the whole packet for the -underlying device is reassembled, the "overlapping fragments" test in -ip6_frag_queue() discards the whole fragment queue. - -To resolve this, add device ifindex to the search key and require it to -match reassembling multicast packets and packets to link-local -addresses. - -Note: similar patch has been already submitted by Yoshifuji Hideaki in - - http://patchwork.ozlabs.org/patch/220979/ - -but got lost and forgotten for some reason. - -Signed-off-by: Michal Kubecek -Signed-off-by: David S. Miller ---- - include/net/ipv6.h | 1 + - net/ipv6/netfilter/nf_conntrack_reasm.c | 5 +++-- - net/ipv6/reassembly.c | 10 +++++++--- - 3 files changed, 11 insertions(+), 5 deletions(-) - -diff --git a/include/net/ipv6.h b/include/net/ipv6.h -index 711cca4..1b63717 100644 ---- a/include/net/ipv6.h -+++ b/include/net/ipv6.h -@@ -490,6 +490,7 @@ struct ip6_create_arg { - u32 user; - const struct in6_addr *src; - const struct in6_addr *dst; -+ int iif; - u8 ecn; - }; - -diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c -index c7196ad..dc50143 100644 ---- a/net/ipv6/netfilter/nf_conntrack_reasm.c -+++ b/net/ipv6/netfilter/nf_conntrack_reasm.c -@@ -190,7 +190,7 @@ static void nf_ct_frag6_expire(unsigned long data) - /* Creation primitives. */ - static inline struct frag_queue *fq_find(struct net *net, __be32 id, - u32 user, struct in6_addr *src, -- struct in6_addr *dst, u8 ecn) -+ struct in6_addr *dst, int iif, u8 ecn) - { - struct inet_frag_queue *q; - struct ip6_create_arg arg; -@@ -200,6 +200,7 @@ static inline struct frag_queue *fq_find(struct net *net, __be32 id, - arg.user = user; - arg.src = src; - arg.dst = dst; -+ arg.iif = iif; - arg.ecn = ecn; - - local_bh_disable(); -@@ -603,7 +604,7 @@ struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb, u32 user) - fhdr = (struct frag_hdr *)skb_transport_header(clone); - - fq = fq_find(net, fhdr->identification, user, &hdr->saddr, &hdr->daddr, -- ip6_frag_ecn(hdr)); -+ skb->dev ? skb->dev->ifindex : 0, ip6_frag_ecn(hdr)); - if (fq == NULL) { - pr_debug("Can't find and can't create new queue\n"); - goto ret_orig; -diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c -index f1159bb..04013a9 100644 ---- a/net/ipv6/reassembly.c -+++ b/net/ipv6/reassembly.c -@@ -108,7 +108,10 @@ bool ip6_frag_match(const struct inet_frag_queue *q, const void *a) - return fq->id == arg->id && - fq->user == arg->user && - ipv6_addr_equal(&fq->saddr, arg->src) && -- ipv6_addr_equal(&fq->daddr, arg->dst); -+ ipv6_addr_equal(&fq->daddr, arg->dst) && -+ (arg->iif == fq->iif || -+ !(ipv6_addr_type(arg->dst) & (IPV6_ADDR_MULTICAST | -+ IPV6_ADDR_LINKLOCAL))); - } - EXPORT_SYMBOL(ip6_frag_match); - -@@ -180,7 +183,7 @@ static void ip6_frag_expire(unsigned long data) - - static struct frag_queue * - fq_find(struct net *net, __be32 id, const struct in6_addr *src, -- const struct in6_addr *dst, u8 ecn) -+ const struct in6_addr *dst, int iif, u8 ecn) - { - struct inet_frag_queue *q; - struct ip6_create_arg arg; -@@ -190,6 +193,7 @@ fq_find(struct net *net, __be32 id, const struct in6_addr *src, - arg.user = IP6_DEFRAG_LOCAL_DELIVER; - arg.src = src; - arg.dst = dst; -+ arg.iif = iif; - arg.ecn = ecn; - - hash = inet6_hash_frag(id, src, dst); -@@ -551,7 +555,7 @@ static int ipv6_frag_rcv(struct sk_buff *skb) - } - - fq = fq_find(net, fhdr->identification, &hdr->saddr, &hdr->daddr, -- ip6_frag_ecn(hdr)); -+ skb->dev ? skb->dev->ifindex : 0, ip6_frag_ecn(hdr)); - if (fq) { - int ret; - --- -2.1.0 - - -From a9bb7b65ac7cd16bafc5cbd4ebb182ce5bbcb488 Mon Sep 17 00:00:00 2001 -From: Quentin Casasnovas -Date: Tue, 24 Nov 2015 17:13:21 -0500 -Subject: [PATCH 37/43] RDS: fix race condition when sending a message on - unbound socket - -[ Upstream commit 8c7188b23474cca017b3ef354c4a58456f68303a ] - -Sasha's found a NULL pointer dereference in the RDS connection code when -sending a message to an apparently unbound socket. The problem is caused -by the code checking if the socket is bound in rds_sendmsg(), which checks -the rs_bound_addr field without taking a lock on the socket. This opens a -race where rs_bound_addr is temporarily set but where the transport is not -in rds_bind(), leading to a NULL pointer dereference when trying to -dereference 'trans' in __rds_conn_create(). - -Vegard wrote a reproducer for this issue, so kindly ask him to share if -you're interested. - -I cannot reproduce the NULL pointer dereference using Vegard's reproducer -with this patch, whereas I could without. - -Complete earlier incomplete fix to CVE-2015-6937: - - 74e98eb08588 ("RDS: verify the underlying transport exists before creating a connection") - -Cc: David S. Miller -Cc: stable@vger.kernel.org - -Reviewed-by: Vegard Nossum -Reviewed-by: Sasha Levin -Acked-by: Santosh Shilimkar -Signed-off-by: Quentin Casasnovas -Signed-off-by: David S. Miller ---- - net/rds/connection.c | 6 ------ - net/rds/send.c | 4 +++- - 2 files changed, 3 insertions(+), 7 deletions(-) - -diff --git a/net/rds/connection.c b/net/rds/connection.c -index 49adeef..9b2de5e 100644 ---- a/net/rds/connection.c -+++ b/net/rds/connection.c -@@ -190,12 +190,6 @@ new_conn: - } - } - -- if (trans == NULL) { -- kmem_cache_free(rds_conn_slab, conn); -- conn = ERR_PTR(-ENODEV); -- goto out; -- } -- - conn->c_trans = trans; - - ret = trans->conn_alloc(conn, gfp); -diff --git a/net/rds/send.c b/net/rds/send.c -index 4df61a5..859de6f 100644 ---- a/net/rds/send.c -+++ b/net/rds/send.c -@@ -1009,11 +1009,13 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len) - release_sock(sk); - } - -- /* racing with another thread binding seems ok here */ -+ lock_sock(sk); - if (daddr == 0 || rs->rs_bound_addr == 0) { -+ release_sock(sk); - ret = -ENOTCONN; /* XXX not a great errno */ - goto out; - } -+ release_sock(sk); - - if (payload_len > rds_sk_sndbuf(rs)) { - ret = -EMSGSIZE; --- -2.1.0 - - -From f6c7967f25e8220913bedf596c260c379dab5341 Mon Sep 17 00:00:00 2001 -From: Daniel Borkmann -Date: Mon, 30 Nov 2015 13:02:56 +0100 -Subject: [PATCH 38/43] bpf, array: fix heap out-of-bounds access when updating - elements - -[ Upstream commit fbca9d2d35c6ef1b323fae75cc9545005ba25097 ] - -During own review but also reported by Dmitry's syzkaller [1] it has been -noticed that we trigger a heap out-of-bounds access on eBPF array maps -when updating elements. This happens with each map whose map->value_size -(specified during map creation time) is not multiple of 8 bytes. - -In array_map_alloc(), elem_size is round_up(attr->value_size, 8) and -used to align array map slots for faster access. However, in function -array_map_update_elem(), we update the element as ... - -memcpy(array->value + array->elem_size * index, value, array->elem_size); - -... where we access 'value' out-of-bounds, since it was allocated from -map_update_elem() from syscall side as kmalloc(map->value_size, GFP_USER) -and later on copied through copy_from_user(value, uvalue, map->value_size). -Thus, up to 7 bytes, we can access out-of-bounds. - -Same could happen from within an eBPF program, where in worst case we -access beyond an eBPF program's designated stack. - -Since 1be7f75d1668 ("bpf: enable non-root eBPF programs") didn't hit an -official release yet, it only affects priviledged users. - -In case of array_map_lookup_elem(), the verifier prevents eBPF programs -from accessing beyond map->value_size through check_map_access(). Also -from syscall side map_lookup_elem() only copies map->value_size back to -user, so nothing could leak. - - [1] http://github.com/google/syzkaller - -Fixes: 28fbcfa08d8e ("bpf: add array type of eBPF maps") -Reported-by: Dmitry Vyukov -Signed-off-by: Daniel Borkmann -Acked-by: Alexei Starovoitov -Signed-off-by: David S. Miller ---- - kernel/bpf/arraymap.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c -index 29ace10..7a0decf 100644 ---- a/kernel/bpf/arraymap.c -+++ b/kernel/bpf/arraymap.c -@@ -104,7 +104,7 @@ static int array_map_update_elem(struct bpf_map *map, void *key, void *value, - /* all elements already exist */ - return -EEXIST; - -- memcpy(array->value + array->elem_size * index, value, array->elem_size); -+ memcpy(array->value + array->elem_size * index, value, map->value_size); - return 0; - } - --- -2.1.0 - - -From 5c3c67ba34c640fa3d93ff4e0506cb5826619de2 Mon Sep 17 00:00:00 2001 -From: Eric Dumazet -Date: Sun, 29 Nov 2015 19:37:57 -0800 -Subject: [PATCH 39/43] ipv6: add complete rcu protection around np->opt - -[ Upstream commit 45f6fad84cc305103b28d73482b344d7f5b76f39 ] - -This patch addresses multiple problems : - -UDP/RAW sendmsg() need to get a stable struct ipv6_txoptions -while socket is not locked : Other threads can change np->opt -concurrently. Dmitry posted a syzkaller -(http://github.com/google/syzkaller) program desmonstrating -use-after-free. - -Starting with TCP/DCCP lockless listeners, tcp_v6_syn_recv_sock() -and dccp_v6_request_recv_sock() also need to use RCU protection -to dereference np->opt once (before calling ipv6_dup_options()) - -This patch adds full RCU protection to np->opt - -Reported-by: Dmitry Vyukov -Signed-off-by: Eric Dumazet -Acked-by: Hannes Frederic Sowa -Signed-off-by: David S. Miller ---- - include/linux/ipv6.h | 2 +- - include/net/ipv6.h | 21 ++++++++++++++++++++- - net/dccp/ipv6.c | 33 +++++++++++++++++++++------------ - net/ipv6/af_inet6.c | 13 +++++++++---- - net/ipv6/datagram.c | 4 +++- - net/ipv6/exthdrs.c | 3 ++- - net/ipv6/inet6_connection_sock.c | 11 ++++++++--- - net/ipv6/ipv6_sockglue.c | 33 ++++++++++++++++++++++----------- - net/ipv6/raw.c | 8 ++++++-- - net/ipv6/syncookies.c | 2 +- - net/ipv6/tcp_ipv6.c | 28 +++++++++++++++++----------- - net/ipv6/udp.c | 8 ++++++-- - net/l2tp/l2tp_ip6.c | 8 ++++++-- - 13 files changed, 122 insertions(+), 52 deletions(-) - -diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h -index f1f32af..3e4ff3f 100644 ---- a/include/linux/ipv6.h -+++ b/include/linux/ipv6.h -@@ -227,7 +227,7 @@ struct ipv6_pinfo { - struct ipv6_ac_socklist *ipv6_ac_list; - struct ipv6_fl_socklist __rcu *ipv6_fl_list; - -- struct ipv6_txoptions *opt; -+ struct ipv6_txoptions __rcu *opt; - struct sk_buff *pktoptions; - struct sk_buff *rxpmtu; - struct inet6_cork cork; -diff --git a/include/net/ipv6.h b/include/net/ipv6.h -index 1b63717..b14e158 100644 ---- a/include/net/ipv6.h -+++ b/include/net/ipv6.h -@@ -205,6 +205,7 @@ extern rwlock_t ip6_ra_lock; - */ - - struct ipv6_txoptions { -+ atomic_t refcnt; - /* Length of this structure */ - int tot_len; - -@@ -217,7 +218,7 @@ struct ipv6_txoptions { - struct ipv6_opt_hdr *dst0opt; - struct ipv6_rt_hdr *srcrt; /* Routing Header */ - struct ipv6_opt_hdr *dst1opt; -- -+ struct rcu_head rcu; - /* Option buffer, as read by IPV6_PKTOPTIONS, starts here. */ - }; - -@@ -252,6 +253,24 @@ struct ipv6_fl_socklist { - struct rcu_head rcu; - }; - -+static inline struct ipv6_txoptions *txopt_get(const struct ipv6_pinfo *np) -+{ -+ struct ipv6_txoptions *opt; -+ -+ rcu_read_lock(); -+ opt = rcu_dereference(np->opt); -+ if (opt && !atomic_inc_not_zero(&opt->refcnt)) -+ opt = NULL; -+ rcu_read_unlock(); -+ return opt; -+} -+ -+static inline void txopt_put(struct ipv6_txoptions *opt) -+{ -+ if (opt && atomic_dec_and_test(&opt->refcnt)) -+ kfree_rcu(opt, rcu); -+} -+ - struct ip6_flowlabel *fl6_sock_lookup(struct sock *sk, __be32 label); - struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions *opt_space, - struct ip6_flowlabel *fl, -diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c -index 5165571..a049050 100644 ---- a/net/dccp/ipv6.c -+++ b/net/dccp/ipv6.c -@@ -202,7 +202,9 @@ static int dccp_v6_send_response(struct sock *sk, struct request_sock *req) - security_req_classify_flow(req, flowi6_to_flowi(&fl6)); - - -- final_p = fl6_update_dst(&fl6, np->opt, &final); -+ rcu_read_lock(); -+ final_p = fl6_update_dst(&fl6, rcu_dereference(np->opt), &final); -+ rcu_read_unlock(); - - dst = ip6_dst_lookup_flow(sk, &fl6, final_p); - if (IS_ERR(dst)) { -@@ -219,7 +221,10 @@ static int dccp_v6_send_response(struct sock *sk, struct request_sock *req) - &ireq->ir_v6_loc_addr, - &ireq->ir_v6_rmt_addr); - fl6.daddr = ireq->ir_v6_rmt_addr; -- err = ip6_xmit(sk, skb, &fl6, np->opt, np->tclass); -+ rcu_read_lock(); -+ err = ip6_xmit(sk, skb, &fl6, rcu_dereference(np->opt), -+ np->tclass); -+ rcu_read_unlock(); - err = net_xmit_eval(err); - } - -@@ -415,6 +420,7 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk, - { - struct inet_request_sock *ireq = inet_rsk(req); - struct ipv6_pinfo *newnp, *np = inet6_sk(sk); -+ struct ipv6_txoptions *opt; - struct inet_sock *newinet; - struct dccp6_sock *newdp6; - struct sock *newsk; -@@ -534,13 +540,15 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk, - * Yes, keeping reference count would be much more clever, but we make - * one more one thing there: reattach optmem to newsk. - */ -- if (np->opt != NULL) -- newnp->opt = ipv6_dup_options(newsk, np->opt); -- -+ opt = rcu_dereference(np->opt); -+ if (opt) { -+ opt = ipv6_dup_options(newsk, opt); -+ RCU_INIT_POINTER(newnp->opt, opt); -+ } - inet_csk(newsk)->icsk_ext_hdr_len = 0; -- if (newnp->opt != NULL) -- inet_csk(newsk)->icsk_ext_hdr_len = (newnp->opt->opt_nflen + -- newnp->opt->opt_flen); -+ if (opt) -+ inet_csk(newsk)->icsk_ext_hdr_len = opt->opt_nflen + -+ opt->opt_flen; - - dccp_sync_mss(newsk, dst_mtu(dst)); - -@@ -793,6 +801,7 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr, - struct ipv6_pinfo *np = inet6_sk(sk); - struct dccp_sock *dp = dccp_sk(sk); - struct in6_addr *saddr = NULL, *final_p, final; -+ struct ipv6_txoptions *opt; - struct flowi6 fl6; - struct dst_entry *dst; - int addr_type; -@@ -892,7 +901,8 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr, - fl6.fl6_sport = inet->inet_sport; - security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); - -- final_p = fl6_update_dst(&fl6, np->opt, &final); -+ opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk)); -+ final_p = fl6_update_dst(&fl6, opt, &final); - - dst = ip6_dst_lookup_flow(sk, &fl6, final_p); - if (IS_ERR(dst)) { -@@ -912,9 +922,8 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr, - __ip6_dst_store(sk, dst, NULL, NULL); - - icsk->icsk_ext_hdr_len = 0; -- if (np->opt != NULL) -- icsk->icsk_ext_hdr_len = (np->opt->opt_flen + -- np->opt->opt_nflen); -+ if (opt) -+ icsk->icsk_ext_hdr_len = opt->opt_flen + opt->opt_nflen; - - inet->inet_dport = usin->sin6_port; - -diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c -index 44bb66b..38d66dd 100644 ---- a/net/ipv6/af_inet6.c -+++ b/net/ipv6/af_inet6.c -@@ -428,9 +428,11 @@ void inet6_destroy_sock(struct sock *sk) - - /* Free tx options */ - -- opt = xchg(&np->opt, NULL); -- if (opt) -- sock_kfree_s(sk, opt, opt->tot_len); -+ opt = xchg((__force struct ipv6_txoptions **)&np->opt, NULL); -+ if (opt) { -+ atomic_sub(opt->tot_len, &sk->sk_omem_alloc); -+ txopt_put(opt); -+ } - } - EXPORT_SYMBOL_GPL(inet6_destroy_sock); - -@@ -659,7 +661,10 @@ int inet6_sk_rebuild_header(struct sock *sk) - fl6.fl6_sport = inet->inet_sport; - security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); - -- final_p = fl6_update_dst(&fl6, np->opt, &final); -+ rcu_read_lock(); -+ final_p = fl6_update_dst(&fl6, rcu_dereference(np->opt), -+ &final); -+ rcu_read_unlock(); - - dst = ip6_dst_lookup_flow(sk, &fl6, final_p); - if (IS_ERR(dst)) { -diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c -index 9aadd57..a42a673 100644 ---- a/net/ipv6/datagram.c -+++ b/net/ipv6/datagram.c -@@ -167,8 +167,10 @@ ipv4_connected: - - security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); - -- opt = flowlabel ? flowlabel->opt : np->opt; -+ rcu_read_lock(); -+ opt = flowlabel ? flowlabel->opt : rcu_dereference(np->opt); - final_p = fl6_update_dst(&fl6, opt, &final); -+ rcu_read_unlock(); - - dst = ip6_dst_lookup_flow(sk, &fl6, final_p); - err = 0; -diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c -index ce203b0..ea7c4d6 100644 ---- a/net/ipv6/exthdrs.c -+++ b/net/ipv6/exthdrs.c -@@ -727,6 +727,7 @@ ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt) - *((char **)&opt2->dst1opt) += dif; - if (opt2->srcrt) - *((char **)&opt2->srcrt) += dif; -+ atomic_set(&opt2->refcnt, 1); - } - return opt2; - } -@@ -790,7 +791,7 @@ ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt, - return ERR_PTR(-ENOBUFS); - - memset(opt2, 0, tot_len); -- -+ atomic_set(&opt2->refcnt, 1); - opt2->tot_len = tot_len; - p = (char *)(opt2 + 1); - -diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c -index 6927f3f..9beed30 100644 ---- a/net/ipv6/inet6_connection_sock.c -+++ b/net/ipv6/inet6_connection_sock.c -@@ -77,7 +77,9 @@ struct dst_entry *inet6_csk_route_req(struct sock *sk, - memset(fl6, 0, sizeof(*fl6)); - fl6->flowi6_proto = IPPROTO_TCP; - fl6->daddr = ireq->ir_v6_rmt_addr; -- final_p = fl6_update_dst(fl6, np->opt, &final); -+ rcu_read_lock(); -+ final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &final); -+ rcu_read_unlock(); - fl6->saddr = ireq->ir_v6_loc_addr; - fl6->flowi6_oif = ireq->ir_iif; - fl6->flowi6_mark = ireq->ir_mark; -@@ -207,7 +209,9 @@ static struct dst_entry *inet6_csk_route_socket(struct sock *sk, - fl6->fl6_dport = inet->inet_dport; - security_sk_classify_flow(sk, flowi6_to_flowi(fl6)); - -- final_p = fl6_update_dst(fl6, np->opt, &final); -+ rcu_read_lock(); -+ final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &final); -+ rcu_read_unlock(); - - dst = __inet6_csk_dst_check(sk, np->dst_cookie); - if (!dst) { -@@ -240,7 +244,8 @@ int inet6_csk_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl_unused - /* Restore final destination back after routing done */ - fl6.daddr = sk->sk_v6_daddr; - -- res = ip6_xmit(sk, skb, &fl6, np->opt, np->tclass); -+ res = ip6_xmit(sk, skb, &fl6, rcu_dereference(np->opt), -+ np->tclass); - rcu_read_unlock(); - return res; - } -diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c -index 63e6956..4449ad1 100644 ---- a/net/ipv6/ipv6_sockglue.c -+++ b/net/ipv6/ipv6_sockglue.c -@@ -111,7 +111,8 @@ struct ipv6_txoptions *ipv6_update_options(struct sock *sk, - icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie); - } - } -- opt = xchg(&inet6_sk(sk)->opt, opt); -+ opt = xchg((__force struct ipv6_txoptions **)&inet6_sk(sk)->opt, -+ opt); - sk_dst_reset(sk); - - return opt; -@@ -231,9 +232,12 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, - sk->sk_socket->ops = &inet_dgram_ops; - sk->sk_family = PF_INET; - } -- opt = xchg(&np->opt, NULL); -- if (opt) -- sock_kfree_s(sk, opt, opt->tot_len); -+ opt = xchg((__force struct ipv6_txoptions **)&np->opt, -+ NULL); -+ if (opt) { -+ atomic_sub(opt->tot_len, &sk->sk_omem_alloc); -+ txopt_put(opt); -+ } - pktopt = xchg(&np->pktoptions, NULL); - kfree_skb(pktopt); - -@@ -403,7 +407,8 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, - if (optname != IPV6_RTHDR && !ns_capable(net->user_ns, CAP_NET_RAW)) - break; - -- opt = ipv6_renew_options(sk, np->opt, optname, -+ opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk)); -+ opt = ipv6_renew_options(sk, opt, optname, - (struct ipv6_opt_hdr __user *)optval, - optlen); - if (IS_ERR(opt)) { -@@ -432,8 +437,10 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, - retv = 0; - opt = ipv6_update_options(sk, opt); - sticky_done: -- if (opt) -- sock_kfree_s(sk, opt, opt->tot_len); -+ if (opt) { -+ atomic_sub(opt->tot_len, &sk->sk_omem_alloc); -+ txopt_put(opt); -+ } - break; - } - -@@ -486,6 +493,7 @@ sticky_done: - break; - - memset(opt, 0, sizeof(*opt)); -+ atomic_set(&opt->refcnt, 1); - opt->tot_len = sizeof(*opt) + optlen; - retv = -EFAULT; - if (copy_from_user(opt+1, optval, optlen)) -@@ -502,8 +510,10 @@ update: - retv = 0; - opt = ipv6_update_options(sk, opt); - done: -- if (opt) -- sock_kfree_s(sk, opt, opt->tot_len); -+ if (opt) { -+ atomic_sub(opt->tot_len, &sk->sk_omem_alloc); -+ txopt_put(opt); -+ } - break; - } - case IPV6_UNICAST_HOPS: -@@ -1110,10 +1120,11 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, - case IPV6_RTHDR: - case IPV6_DSTOPTS: - { -+ struct ipv6_txoptions *opt; - - lock_sock(sk); -- len = ipv6_getsockopt_sticky(sk, np->opt, -- optname, optval, len); -+ opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk)); -+ len = ipv6_getsockopt_sticky(sk, opt, optname, optval, len); - release_sock(sk); - /* check if ipv6_getsockopt_sticky() returns err code */ - if (len < 0) -diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c -index fdbada156..fe97729 100644 ---- a/net/ipv6/raw.c -+++ b/net/ipv6/raw.c -@@ -732,6 +732,7 @@ static int raw6_getfrag(void *from, char *to, int offset, int len, int odd, - - static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) - { -+ struct ipv6_txoptions *opt_to_free = NULL; - struct ipv6_txoptions opt_space; - DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name); - struct in6_addr *daddr, *final_p, final; -@@ -838,8 +839,10 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) - if (!(opt->opt_nflen|opt->opt_flen)) - opt = NULL; - } -- if (!opt) -- opt = np->opt; -+ if (!opt) { -+ opt = txopt_get(np); -+ opt_to_free = opt; -+ } - if (flowlabel) - opt = fl6_merge_options(&opt_space, flowlabel, opt); - opt = ipv6_fixup_options(&opt_space, opt); -@@ -905,6 +908,7 @@ done: - dst_release(dst); - out: - fl6_sock_release(flowlabel); -+ txopt_put(opt_to_free); - return err < 0 ? err : len; - do_confirm: - dst_confirm(dst); -diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c -index 0909f4e..f30bfdc 100644 ---- a/net/ipv6/syncookies.c -+++ b/net/ipv6/syncookies.c -@@ -225,7 +225,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) - memset(&fl6, 0, sizeof(fl6)); - fl6.flowi6_proto = IPPROTO_TCP; - fl6.daddr = ireq->ir_v6_rmt_addr; -- final_p = fl6_update_dst(&fl6, np->opt, &final); -+ final_p = fl6_update_dst(&fl6, rcu_dereference(np->opt), &final); - fl6.saddr = ireq->ir_v6_loc_addr; - fl6.flowi6_oif = sk->sk_bound_dev_if; - fl6.flowi6_mark = ireq->ir_mark; -diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c -index 97d9314..9e9b77b 100644 ---- a/net/ipv6/tcp_ipv6.c -+++ b/net/ipv6/tcp_ipv6.c -@@ -120,6 +120,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, - struct ipv6_pinfo *np = inet6_sk(sk); - struct tcp_sock *tp = tcp_sk(sk); - struct in6_addr *saddr = NULL, *final_p, final; -+ struct ipv6_txoptions *opt; - struct flowi6 fl6; - struct dst_entry *dst; - int addr_type; -@@ -235,7 +236,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, - fl6.fl6_dport = usin->sin6_port; - fl6.fl6_sport = inet->inet_sport; - -- final_p = fl6_update_dst(&fl6, np->opt, &final); -+ opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk)); -+ final_p = fl6_update_dst(&fl6, opt, &final); - - security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); - -@@ -263,9 +265,9 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, - tcp_fetch_timewait_stamp(sk, dst); - - icsk->icsk_ext_hdr_len = 0; -- if (np->opt) -- icsk->icsk_ext_hdr_len = (np->opt->opt_flen + -- np->opt->opt_nflen); -+ if (opt) -+ icsk->icsk_ext_hdr_len = opt->opt_flen + -+ opt->opt_nflen; - - tp->rx_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr); - -@@ -461,7 +463,8 @@ static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst, - fl6->flowlabel = ip6_flowlabel(ipv6_hdr(ireq->pktopts)); - - skb_set_queue_mapping(skb, queue_mapping); -- err = ip6_xmit(sk, skb, fl6, np->opt, np->tclass); -+ err = ip6_xmit(sk, skb, fl6, rcu_dereference(np->opt), -+ np->tclass); - err = net_xmit_eval(err); - } - -@@ -991,6 +994,7 @@ static struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, - struct inet_request_sock *ireq; - struct ipv6_pinfo *newnp, *np = inet6_sk(sk); - struct tcp6_sock *newtcp6sk; -+ struct ipv6_txoptions *opt; - struct inet_sock *newinet; - struct tcp_sock *newtp; - struct sock *newsk; -@@ -1126,13 +1130,15 @@ static struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, - but we make one more one thing there: reattach optmem - to newsk. - */ -- if (np->opt) -- newnp->opt = ipv6_dup_options(newsk, np->opt); -- -+ opt = rcu_dereference(np->opt); -+ if (opt) { -+ opt = ipv6_dup_options(newsk, opt); -+ RCU_INIT_POINTER(newnp->opt, opt); -+ } - inet_csk(newsk)->icsk_ext_hdr_len = 0; -- if (newnp->opt) -- inet_csk(newsk)->icsk_ext_hdr_len = (newnp->opt->opt_nflen + -- newnp->opt->opt_flen); -+ if (opt) -+ inet_csk(newsk)->icsk_ext_hdr_len = opt->opt_nflen + -+ opt->opt_flen; - - tcp_ca_openreq_child(newsk, dst); - -diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c -index 0aba654..8379fc2 100644 ---- a/net/ipv6/udp.c -+++ b/net/ipv6/udp.c -@@ -1107,6 +1107,7 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) - DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name); - struct in6_addr *daddr, *final_p, final; - struct ipv6_txoptions *opt = NULL; -+ struct ipv6_txoptions *opt_to_free = NULL; - struct ip6_flowlabel *flowlabel = NULL; - struct flowi6 fl6; - struct dst_entry *dst; -@@ -1260,8 +1261,10 @@ do_udp_sendmsg: - opt = NULL; - connected = 0; - } -- if (!opt) -- opt = np->opt; -+ if (!opt) { -+ opt = txopt_get(np); -+ opt_to_free = opt; -+ } - if (flowlabel) - opt = fl6_merge_options(&opt_space, flowlabel, opt); - opt = ipv6_fixup_options(&opt_space, opt); -@@ -1370,6 +1373,7 @@ release_dst: - out: - dst_release(dst); - fl6_sock_release(flowlabel); -+ txopt_put(opt_to_free); - if (!err) - return len; - /* -diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c -index d1ded37..0ce9da9 100644 ---- a/net/l2tp/l2tp_ip6.c -+++ b/net/l2tp/l2tp_ip6.c -@@ -486,6 +486,7 @@ static int l2tp_ip6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) - DECLARE_SOCKADDR(struct sockaddr_l2tpip6 *, lsa, msg->msg_name); - struct in6_addr *daddr, *final_p, final; - struct ipv6_pinfo *np = inet6_sk(sk); -+ struct ipv6_txoptions *opt_to_free = NULL; - struct ipv6_txoptions *opt = NULL; - struct ip6_flowlabel *flowlabel = NULL; - struct dst_entry *dst = NULL; -@@ -575,8 +576,10 @@ static int l2tp_ip6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) - opt = NULL; - } - -- if (opt == NULL) -- opt = np->opt; -+ if (!opt) { -+ opt = txopt_get(np); -+ opt_to_free = opt; -+ } - if (flowlabel) - opt = fl6_merge_options(&opt_space, flowlabel, opt); - opt = ipv6_fixup_options(&opt_space, opt); -@@ -631,6 +634,7 @@ done: - dst_release(dst); - out: - fl6_sock_release(flowlabel); -+ txopt_put(opt_to_free); - - return err < 0 ? err : len; - --- -2.1.0 - - -From 42b4973eefcdcfc62b49c61f50f9f1e81ae6a615 Mon Sep 17 00:00:00 2001 -From: Konstantin Khlebnikov -Date: Tue, 1 Dec 2015 01:14:48 +0300 -Subject: [PATCH 40/43] net/neighbour: fix crash at dumping device-agnostic - proxy entries - -[ Upstream commit 6adc5fd6a142c6e2c80574c1db0c7c17dedaa42e ] - -Proxy entries could have null pointer to net-device. - -Signed-off-by: Konstantin Khlebnikov -Fixes: 84920c1420e2 ("net: Allow ipv6 proxies and arp proxies be shown with iproute2") -Signed-off-by: David S. Miller ---- - net/core/neighbour.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/net/core/neighbour.c b/net/core/neighbour.c -index 2b515ba..c169bba 100644 ---- a/net/core/neighbour.c -+++ b/net/core/neighbour.c -@@ -2215,7 +2215,7 @@ static int pneigh_fill_info(struct sk_buff *skb, struct pneigh_entry *pn, - ndm->ndm_pad2 = 0; - ndm->ndm_flags = pn->flags | NTF_PROXY; - ndm->ndm_type = RTN_UNICAST; -- ndm->ndm_ifindex = pn->dev->ifindex; -+ ndm->ndm_ifindex = pn->dev ? pn->dev->ifindex : 0; - ndm->ndm_state = NUD_NONE; - - if (nla_put(skb, NDA_DST, tbl->key_len, pn->key)) -@@ -2290,7 +2290,7 @@ static int pneigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, - if (h > s_h) - s_idx = 0; - for (n = tbl->phash_buckets[h], idx = 0; n; n = n->next) { -- if (dev_net(n->dev) != net) -+ if (pneigh_net(n) != net) - continue; - if (idx < s_idx) - goto next; --- -2.1.0 - - -From f7391ce93c846664fb548ff4054edc165e7a5442 Mon Sep 17 00:00:00 2001 -From: Eric Dumazet -Date: Tue, 1 Dec 2015 07:20:07 -0800 -Subject: [PATCH 41/43] ipv6: sctp: implement sctp_v6_destroy_sock() - -[ Upstream commit 602dd62dfbda3e63a2d6a3cbde953ebe82bf5087 ] - -Dmitry Vyukov reported a memory leak using IPV6 SCTP sockets. - -We need to call inet6_destroy_sock() to properly release -inet6 specific fields. - -Reported-by: Dmitry Vyukov -Signed-off-by: Eric Dumazet -Acked-by: Daniel Borkmann -Signed-off-by: David S. Miller ---- - net/sctp/socket.c | 9 ++++++++- - 1 file changed, 8 insertions(+), 1 deletion(-) - -diff --git a/net/sctp/socket.c b/net/sctp/socket.c -index 17bef01..3ec88be 100644 ---- a/net/sctp/socket.c -+++ b/net/sctp/socket.c -@@ -7375,6 +7375,13 @@ struct proto sctp_prot = { - - #if IS_ENABLED(CONFIG_IPV6) - -+#include -+static void sctp_v6_destroy_sock(struct sock *sk) -+{ -+ sctp_destroy_sock(sk); -+ inet6_destroy_sock(sk); -+} -+ - struct proto sctpv6_prot = { - .name = "SCTPv6", - .owner = THIS_MODULE, -@@ -7384,7 +7391,7 @@ struct proto sctpv6_prot = { - .accept = sctp_accept, - .ioctl = sctp_ioctl, - .init = sctp_init_sock, -- .destroy = sctp_destroy_sock, -+ .destroy = sctp_v6_destroy_sock, - .shutdown = sctp_shutdown, - .setsockopt = sctp_setsockopt, - .getsockopt = sctp_getsockopt, --- -2.1.0 - - -From c391cc510c3cf6b5a8dd57fbc1de26b4b28bd7e0 Mon Sep 17 00:00:00 2001 -From: Paolo Abeni -Date: Tue, 1 Dec 2015 18:33:36 +0100 -Subject: [PATCH 42/43] openvswitch: fix hangup on vxlan/gre/geneve device - deletion - -[ Upstream commit 13175303024c8f4cd09e51079a8fcbbe572111ec ] - -Each openvswitch tunnel vport (vxlan,gre,geneve) holds a reference -to the underlying tunnel device, but never released it when such -device is deleted. -Deleting the underlying device via the ip tool cause the kernel to -hangup in the netdev_wait_allrefs() loop. -This commit ensure that on device unregistration dp_detach_port_notify() -is called for all vports that hold the device reference, properly -releasing it. - -Fixes: 614732eaa12d ("openvswitch: Use regular VXLAN net_device device") -Fixes: b2acd1dc3949 ("openvswitch: Use regular GRE net_device instead of vport") -Fixes: 6b001e682e90 ("openvswitch: Use Geneve device.") -Signed-off-by: Paolo Abeni -Acked-by: Flavio Leitner -Acked-by: Pravin B Shelar -Signed-off-by: David S. Miller ---- - net/openvswitch/dp_notify.c | 2 +- - net/openvswitch/vport-netdev.c | 8 ++++++-- - 2 files changed, 7 insertions(+), 3 deletions(-) - -diff --git a/net/openvswitch/dp_notify.c b/net/openvswitch/dp_notify.c -index a7a80a6..653d073 100644 ---- a/net/openvswitch/dp_notify.c -+++ b/net/openvswitch/dp_notify.c -@@ -58,7 +58,7 @@ void ovs_dp_notify_wq(struct work_struct *work) - struct hlist_node *n; - - hlist_for_each_entry_safe(vport, n, &dp->ports[i], dp_hash_node) { -- if (vport->ops->type != OVS_VPORT_TYPE_NETDEV) -+ if (vport->ops->type == OVS_VPORT_TYPE_INTERNAL) - continue; - - if (!(vport->dev->priv_flags & IFF_OVS_DATAPATH)) -diff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c -index f7e8dcc..ac14c48 100644 ---- a/net/openvswitch/vport-netdev.c -+++ b/net/openvswitch/vport-netdev.c -@@ -180,9 +180,13 @@ void ovs_netdev_tunnel_destroy(struct vport *vport) - if (vport->dev->priv_flags & IFF_OVS_DATAPATH) - ovs_netdev_detach_dev(vport); - -- /* Early release so we can unregister the device */ -+ /* We can be invoked by both explicit vport deletion and -+ * underlying netdev deregistration; delete the link only -+ * if it's not already shutting down. -+ */ -+ if (vport->dev->reg_state == NETREG_REGISTERED) -+ rtnl_delete_link(vport->dev); - dev_put(vport->dev); -- rtnl_delete_link(vport->dev); - vport->dev = NULL; - rtnl_unlock(); - --- -2.1.0 - - -From d6c39cbd9479fcce790b2381042c9405fc170384 Mon Sep 17 00:00:00 2001 -From: Eric Dumazet -Date: Tue, 1 Dec 2015 20:08:51 -0800 -Subject: [PATCH 43/43] net_sched: fix qdisc_tree_decrease_qlen() races - -[ Upstream commit 4eaf3b84f2881c9c028f1d5e76c52ab575fe3a66 ] - -qdisc_tree_decrease_qlen() suffers from two problems on multiqueue -devices. - -One problem is that it updates sch->q.qlen and sch->qstats.drops -on the mq/mqprio root qdisc, while it should not : Daniele -reported underflows errors : -[ 681.774821] PAX: sch->q.qlen: 0 n: 1 -[ 681.774825] PAX: size overflow detected in function qdisc_tree_decrease_qlen net/sched/sch_api.c:769 cicus.693_49 min, count: 72, decl: qlen; num: 0; context: sk_buff_head; -[ 681.774954] CPU: 2 PID: 19 Comm: ksoftirqd/2 Tainted: G O 4.2.6.201511282239-1-grsec #1 -[ 681.774955] Hardware name: ASUSTeK COMPUTER INC. X302LJ/X302LJ, BIOS X302LJ.202 03/05/2015 -[ 681.774956] ffffffffa9a04863 0000000000000000 0000000000000000 ffffffffa990ff7c -[ 681.774959] ffffc90000d3bc38 ffffffffa95d2810 0000000000000007 ffffffffa991002b -[ 681.774960] ffffc90000d3bc68 ffffffffa91a44f4 0000000000000001 0000000000000001 -[ 681.774962] Call Trace: -[ 681.774967] [] dump_stack+0x4c/0x7f -[ 681.774970] [] report_size_overflow+0x34/0x50 -[ 681.774972] [] qdisc_tree_decrease_qlen+0x152/0x160 -[ 681.774976] [] fq_codel_dequeue+0x7b1/0x820 [sch_fq_codel] -[ 681.774978] [] ? qdisc_peek_dequeued+0xa0/0xa0 [sch_fq_codel] -[ 681.774980] [] __qdisc_run+0x4d/0x1d0 -[ 681.774983] [] net_tx_action+0xc2/0x160 -[ 681.774985] [] __do_softirq+0xf1/0x200 -[ 681.774987] [] run_ksoftirqd+0x1e/0x30 -[ 681.774989] [] smpboot_thread_fn+0x150/0x260 -[ 681.774991] [] ? sort_range+0x40/0x40 -[ 681.774992] [] kthread+0xe4/0x100 -[ 681.774994] [] ? kthread_worker_fn+0x170/0x170 -[ 681.774995] [] ret_from_fork+0x3e/0x70 - -mq/mqprio have their own ways to report qlen/drops by folding stats on -all their queues, with appropriate locking. - -A second problem is that qdisc_tree_decrease_qlen() calls qdisc_lookup() -without proper locking : concurrent qdisc updates could corrupt the list -that qdisc_match_from_root() parses to find a qdisc given its handle. - -Fix first problem adding a TCQ_F_NOPARENT qdisc flag that -qdisc_tree_decrease_qlen() can use to abort its tree traversal, -as soon as it meets a mq/mqprio qdisc children. - -Second problem can be fixed by RCU protection. -Qdisc are already freed after RCU grace period, so qdisc_list_add() and -qdisc_list_del() simply have to use appropriate rcu list variants. - -A future patch will add a per struct netdev_queue list anchor, so that -qdisc_tree_decrease_qlen() can have more efficient lookups. - -Reported-by: Daniele Fucini -Signed-off-by: Eric Dumazet -Cc: Cong Wang -Cc: Jamal Hadi Salim -Signed-off-by: David S. Miller ---- - include/net/sch_generic.h | 3 +++ - net/sched/sch_api.c | 27 ++++++++++++++++++--------- - net/sched/sch_generic.c | 2 +- - net/sched/sch_mq.c | 4 ++-- - net/sched/sch_mqprio.c | 4 ++-- - 5 files changed, 26 insertions(+), 14 deletions(-) - -diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h -index 444faa8..f1ad8f8 100644 ---- a/include/net/sch_generic.h -+++ b/include/net/sch_generic.h -@@ -61,6 +61,9 @@ struct Qdisc { - */ - #define TCQ_F_WARN_NONWC (1 << 16) - #define TCQ_F_CPUSTATS 0x20 /* run using percpu statistics */ -+#define TCQ_F_NOPARENT 0x40 /* root of its hierarchy : -+ * qdisc_tree_decrease_qlen() should stop. -+ */ - u32 limit; - const struct Qdisc_ops *ops; - struct qdisc_size_table __rcu *stab; -diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c -index f43c8f3..7ec667d 100644 ---- a/net/sched/sch_api.c -+++ b/net/sched/sch_api.c -@@ -253,7 +253,8 @@ int qdisc_set_default(const char *name) - } - - /* We know handle. Find qdisc among all qdisc's attached to device -- (root qdisc, all its children, children of children etc.) -+ * (root qdisc, all its children, children of children etc.) -+ * Note: caller either uses rtnl or rcu_read_lock() - */ - - static struct Qdisc *qdisc_match_from_root(struct Qdisc *root, u32 handle) -@@ -264,7 +265,7 @@ static struct Qdisc *qdisc_match_from_root(struct Qdisc *root, u32 handle) - root->handle == handle) - return root; - -- list_for_each_entry(q, &root->list, list) { -+ list_for_each_entry_rcu(q, &root->list, list) { - if (q->handle == handle) - return q; - } -@@ -277,15 +278,18 @@ void qdisc_list_add(struct Qdisc *q) - struct Qdisc *root = qdisc_dev(q)->qdisc; - - WARN_ON_ONCE(root == &noop_qdisc); -- list_add_tail(&q->list, &root->list); -+ ASSERT_RTNL(); -+ list_add_tail_rcu(&q->list, &root->list); - } - } - EXPORT_SYMBOL(qdisc_list_add); - - void qdisc_list_del(struct Qdisc *q) - { -- if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS)) -- list_del(&q->list); -+ if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS)) { -+ ASSERT_RTNL(); -+ list_del_rcu(&q->list); -+ } - } - EXPORT_SYMBOL(qdisc_list_del); - -@@ -750,14 +754,18 @@ void qdisc_tree_decrease_qlen(struct Qdisc *sch, unsigned int n) - if (n == 0) - return; - drops = max_t(int, n, 0); -+ rcu_read_lock(); - while ((parentid = sch->parent)) { - if (TC_H_MAJ(parentid) == TC_H_MAJ(TC_H_INGRESS)) -- return; -+ break; - -+ if (sch->flags & TCQ_F_NOPARENT) -+ break; -+ /* TODO: perform the search on a per txq basis */ - sch = qdisc_lookup(qdisc_dev(sch), TC_H_MAJ(parentid)); - if (sch == NULL) { -- WARN_ON(parentid != TC_H_ROOT); -- return; -+ WARN_ON_ONCE(parentid != TC_H_ROOT); -+ break; - } - cops = sch->ops->cl_ops; - if (cops->qlen_notify) { -@@ -768,6 +776,7 @@ void qdisc_tree_decrease_qlen(struct Qdisc *sch, unsigned int n) - sch->q.qlen -= n; - __qdisc_qstats_drop(sch, drops); - } -+ rcu_read_unlock(); - } - EXPORT_SYMBOL(qdisc_tree_decrease_qlen); - -@@ -941,7 +950,7 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue, - } - lockdep_set_class(qdisc_lock(sch), &qdisc_tx_lock); - if (!netif_is_multiqueue(dev)) -- sch->flags |= TCQ_F_ONETXQUEUE; -+ sch->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT; - } - - sch->handle = handle; -diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c -index cb5d4ad..e82a1ad 100644 ---- a/net/sched/sch_generic.c -+++ b/net/sched/sch_generic.c -@@ -737,7 +737,7 @@ static void attach_one_default_qdisc(struct net_device *dev, - return; - } - if (!netif_is_multiqueue(dev)) -- qdisc->flags |= TCQ_F_ONETXQUEUE; -+ qdisc->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT; - dev_queue->qdisc_sleeping = qdisc; - } - -diff --git a/net/sched/sch_mq.c b/net/sched/sch_mq.c -index f3cbaec..3e82f04 100644 ---- a/net/sched/sch_mq.c -+++ b/net/sched/sch_mq.c -@@ -63,7 +63,7 @@ static int mq_init(struct Qdisc *sch, struct nlattr *opt) - if (qdisc == NULL) - goto err; - priv->qdiscs[ntx] = qdisc; -- qdisc->flags |= TCQ_F_ONETXQUEUE; -+ qdisc->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT; - } - - sch->flags |= TCQ_F_MQROOT; -@@ -156,7 +156,7 @@ static int mq_graft(struct Qdisc *sch, unsigned long cl, struct Qdisc *new, - - *old = dev_graft_qdisc(dev_queue, new); - if (new) -- new->flags |= TCQ_F_ONETXQUEUE; -+ new->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT; - if (dev->flags & IFF_UP) - dev_activate(dev); - return 0; -diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c -index 3811a74..ad70ecf 100644 ---- a/net/sched/sch_mqprio.c -+++ b/net/sched/sch_mqprio.c -@@ -132,7 +132,7 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt) - goto err; - } - priv->qdiscs[i] = qdisc; -- qdisc->flags |= TCQ_F_ONETXQUEUE; -+ qdisc->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT; - } - - /* If the mqprio options indicate that hardware should own -@@ -209,7 +209,7 @@ static int mqprio_graft(struct Qdisc *sch, unsigned long cl, struct Qdisc *new, - *old = dev_graft_qdisc(dev_queue, new); - - if (new) -- new->flags |= TCQ_F_ONETXQUEUE; -+ new->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT; - - if (dev->flags & IFF_UP) - dev_activate(dev); --- -2.1.0 - diff --git a/kernel/kernel/files/patches/mageia/scsi-Fix-NULL-pointer-dereference-in-RTPM-of-block-layer.patch b/kernel/kernel/files/patches/mageia/scsi-Fix-NULL-pointer-dereference-in-RTPM-of-block-layer.patch deleted file mode 100644 index 49928d17..00000000 --- a/kernel/kernel/files/patches/mageia/scsi-Fix-NULL-pointer-dereference-in-RTPM-of-block-layer.patch +++ /dev/null @@ -1,74 +0,0 @@ -SCSI: Fix NULL pointer dereference in RTPM of block layer - -The routines in scsi_pm.c assume that if a runtime-PM callback is -invoked for a SCSI device, it can only mean that the device's driver -has asked the block layer to handle the runtime power management (by -calling blk_pm_runtime_init(), which among other things sets q->dev). - -However, this assumption turns out to be wrong for things like the ses -driver. Normally ses devices are not allowed to do runtime PM, but -userspace can override this setting. If this happens, the kernel gets -a NULL pointer dereference when blk_post_runtime_resume() tries to use -the uninitialized q->dev pointer. - -This patch fixes the problem by checking q->dev in block layer before -handle runtime PM. Since ses doesn't define any PM callbacks and call -blk_pm_runtime_init(), the crash won't occur. - -This fixes Bugzilla #101371. -https://bugzilla.kernel.org/show_bug.cgi?id=101371 - -Signed-off-by: Ken Xue -Acked-by: Alan Stern -Cc: stable@vger.kernel.org - ---- - block/blk-core.c | 12 ++++++++++++ - 1 file changed, 12 insertions(+) - -diff --git a/block/blk-core.c b/block/blk-core.c -index 60912e9..a07ab18 100644 ---- a/block/blk-core.c -+++ b/block/blk-core.c -@@ -3280,6 +3280,9 @@ int blk_pre_runtime_suspend(struct request_queue *q) - { - int ret = 0; - -+ if (!q->dev) -+ return ret; -+ - spin_lock_irq(q->queue_lock); - if (q->nr_pending) { - ret = -EBUSY; -@@ -3307,6 +3310,9 @@ EXPORT_SYMBOL(blk_pre_runtime_suspend); - */ - void blk_post_runtime_suspend(struct request_queue *q, int err) - { -+ if (!q->dev) -+ return; -+ - spin_lock_irq(q->queue_lock); - if (!err) { - q->rpm_status = RPM_SUSPENDED; -@@ -3331,6 +3337,9 @@ EXPORT_SYMBOL(blk_post_runtime_suspend); - */ - void blk_pre_runtime_resume(struct request_queue *q) - { -+ if (!q->dev) -+ return; -+ - spin_lock_irq(q->queue_lock); - q->rpm_status = RPM_RESUMING; - spin_unlock_irq(q->queue_lock); -@@ -3353,6 +3362,9 @@ EXPORT_SYMBOL(blk_pre_runtime_resume); - */ - void blk_post_runtime_resume(struct request_queue *q, int err) - { -+ if (!q->dev) -+ return; -+ - spin_lock_irq(q->queue_lock); - if (!err) { - q->rpm_status = RPM_ACTIVE; --- -1.9.1 diff --git a/kernel/kernel/files/patches/mageia/scsi-Revert-SCSI-Fix-NULL-pointer-dereference-in-runtime-PM.patch b/kernel/kernel/files/patches/mageia/scsi-Revert-SCSI-Fix-NULL-pointer-dereference-in-runtime-PM.patch deleted file mode 100644 index dc20be03..00000000 --- a/kernel/kernel/files/patches/mageia/scsi-Revert-SCSI-Fix-NULL-pointer-dereference-in-runtime-PM.patch +++ /dev/null @@ -1,64 +0,0 @@ -Revert "SCSI: Fix NULL pointer dereference in runtime PM" - -This reverts commit 49718f0fb8c9 ("SCSI: Fix NULL pointer dereference in -runtime PM") - -The old commit may lead to a issue that blk_{pre|post}_runtime_suspend and -blk_{pre|post}_runtime_resume can not be called in pairs. - -Take sr device as example, when sr device goes to runtime suspend, -blk_{pre|post}_runtime_suspend will be called since sr device defined -pm->runtime_suspend. But blk_{pre|post}_runtime_resume will not be called -since sr device doesn't have pm->runtime_resume. Then sr device can not -resume correctly anymore. - -Signed-off-by: Ken Xue -Acked-by: Alan Stern -Cc: stable@vger.kernel.org - ---- - drivers/scsi/scsi_pm.c | 20 ++++++++++---------- - 1 file changed, 10 insertions(+), 10 deletions(-) - -diff --git a/drivers/scsi/scsi_pm.c b/drivers/scsi/scsi_pm.c -index e4b7998..459abe1 100644 ---- a/drivers/scsi/scsi_pm.c -+++ b/drivers/scsi/scsi_pm.c -@@ -219,13 +219,13 @@ static int sdev_runtime_suspend(struct device *dev) - struct scsi_device *sdev = to_scsi_device(dev); - int err = 0; - -- if (pm && pm->runtime_suspend) { -- err = blk_pre_runtime_suspend(sdev->request_queue); -- if (err) -- return err; -+ err = blk_pre_runtime_suspend(sdev->request_queue); -+ if (err) -+ return err; -+ if (pm && pm->runtime_suspend) - err = pm->runtime_suspend(dev); -- blk_post_runtime_suspend(sdev->request_queue, err); -- } -+ blk_post_runtime_suspend(sdev->request_queue, err); -+ - return err; - } - -@@ -248,11 +248,11 @@ static int sdev_runtime_resume(struct device *dev) - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - int err = 0; - -- if (pm && pm->runtime_resume) { -- blk_pre_runtime_resume(sdev->request_queue); -+ blk_pre_runtime_resume(sdev->request_queue); -+ if (pm && pm->runtime_resume) - err = pm->runtime_resume(dev); -- blk_post_runtime_resume(sdev->request_queue, err); -- } -+ blk_post_runtime_resume(sdev->request_queue, err); -+ - return err; - } - --- -1.9.1 diff --git a/kernel/kernel/files/patches/mageia/series b/kernel/kernel/files/patches/mageia/series index beb0de3c..aab0e49d 100644 --- a/kernel/kernel/files/patches/mageia/series +++ b/kernel/kernel/files/patches/mageia/series @@ -38,12 +38,6 @@ x86-increase-default-minimum-vmalloc-area-by-64MB-to-192MB.patch # slows down boot Revert-cpufreq-pcc-Enable-autoload-of-pcc-cpufreq-fo.patch -# (CVE-2015-8104) -x86-KVM-svm-unconditionally-intercept-DB.patch - -# efi borkage -Revert-x86-efi-Request-desired-alignment-via-the-PE-.patch - # breaks nvidia304 Revert-x86-mm-mtrr-Remove-kernel-internal-MTRR-inter.patch @@ -52,9 +46,6 @@ Revert-x86-mm-mtrr-Remove-kernel-internal-MTRR-inter.patch ### base-cacheinfo-silence-DT-warnings.patch -# git fix -certs-add-.gitignore-to-stop-git-nagging-about-x509_.patch - ### ### i2c ### @@ -85,7 +76,6 @@ acpi-processor-M720SR-limit-to-C2.patch # backlight fixes ACPI-video-Add-a-quirk-to-force-acpi-video-backlight.patch -ACPI-video-Add-a-quirk-to-force-native-backlight-on-.patch ### ### Block @@ -129,12 +119,7 @@ ata-prefer-ata-drivers-over-ide-drivers-when-both-are-built.patch # Nice SSD speedup block-Make-CFQ-default-to-IOPS-mode-on-SSDs.patch -# properly fix null pointer deref introduced in 4.1.7 -scsi-Revert-SCSI-Fix-NULL-pointer-dereference-in-runtime-PM.patch -scsi-Fix-NULL-pointer-dereference-in-RTPM-of-block-layer.patch - # ahci ids -ahci-Add-Marvell-88se91a2-device-id.patch ahci-add-new-Intel-device-IDs.patch ### @@ -142,8 +127,8 @@ ahci-add-new-Intel-device-IDs.patch ### # aufs from: http://aufs.sourceforge.net/ (mga#8314) -fs-aufs-4.3.patch -fs-aufs-4.3-modular.patch +fs-aufs-4.4.patch +fs-aufs-4.4-modular.patch ### ### FireWire @@ -179,9 +164,27 @@ gpu-drm-mach64-3.17-buildfix.patch gpu-drm-mach64-3.18-buildfix.patch # drm fixes -gpu-drm-Fix-an-unwanted-master-inheritance-v2.patch gpu-drm-i915-Fix-RPS-pointer-passed-from-wait_ioctl-to-i915_wait_request.patch +# etnaviv / vivante gpu +gpu-drm-0001-devicetree-add-vendor-prefix-for-Vivante-Corporation.patch +gpu-drm-0002-drm-etnaviv-add-devicetree-bindings.patch +gpu-drm-0003-drm-etnaviv-add-initial-etnaviv-DRM-driver.patch +gpu-drm-0004-MAINTAINERS-add-maintainer-and-reviewers-for-the-etn.patch +gpu-drm-0005-drm-etnaviv-unlock-on-error-in-etnaviv_gem_get_iova.patch +gpu-drm-0006-drm-etnaviv-fix-workaround-for-GC500.patch + +# vc4 3d accel support +gpu-drm-0101-drm-Create-a-driver-hook-for-allocating-GEM-object-s.patch +gpu-drm-0102-drm-vc4-Add-a-BO-cache.patch +gpu-drm-0103-drm-vc4-Add-create-and-map-BO-ioctls.patch +gpu-drm-0104-drm-vc4-Add-an-API-for-creating-GPU-shaders-in-GEM-B.patch +gpu-drm-0105-drm-vc4-Fix-a-typo-in-a-V3D-debug-register.patch +gpu-drm-0106-drm-vc4-Bind-and-initialize-the-V3D-engine.patch +gpu-drm-0107-drm-vc4-Add-support-for-drawing-3D-frames.patch +gpu-drm-0108-drm-vc4-Add-support-for-async-pageflips.patch +gpu-drm-0109-drm-vc4-Add-an-interface-for-capturing-the-GPU-state.patch + ### ### Hardware Monitoring ### @@ -232,18 +235,6 @@ net-netfilter-psd.patch net-netfilter-psd-mdv.patch net-netfilter-psd-2.6.35-buildfix.patch -# netfilter fixes -net-netfilter-ipset-Fix-extension-alignment.patch -net-netfilter-ipset-Fix-hash-type-expiration.patch -net-netfilter-ipset-Fix-hash-type-expire-release-empty-hash-bucket-block.patch -net-netfilter-Fix-removal-of-GRE-expectation-entries-created-by-PPTP.patch - -# rtlwifi hang fix -net-wireless-rtlwifi-rtl8821ae-Fix-lockups-on-boot.patch - -# net stable fixes from DaveM -net_43.mbox.patch - ### ### Platform drivers ### @@ -367,6 +358,20 @@ video-mageia-logo.patch ### ARM ### +# RPi2 support +arm-0001-dt-bindings-Add-root-properties-for-Raspberry-Pi-2.patch +arm-0002-ARM-bcm2835-Add-a-compat-string-for-bcm2836-machine-.patch +arm-0003-ARM-bcm2835-Add-Kconfig-support-for-bcm2836.patch +arm-0011-ARM-bcm2835-Define-two-new-packets-from-the-latest-f.patch +arm-0012-dt-bindings-add-rpi-power-domain-driver-bindings.patch +arm-0013-ARM-bcm2835-add-rpi-power-domain-driver.patch +arm-0021-ARM-bcm2835-Split-the-DT-for-peripherals-from-the-DT.patch +arm-0022-ARM-bcm2835-Move-the-CPU-peripheral-include-out-of-c.patch +arm-0023-ARM-bcm2835-Add-devicetree-for-bcm2836-and-Raspberry.patch +arm-0024-ARM-bcm2835-Add-the-auxiliary-clocks-to-the-device-t.patch +arm-0031-ARM-bcm2835-enable-all-bcm2835-relevant-in-defconfig.patch +arm-0032-ARM-bcm2835-enable-auxiliary-spi-driver-in-defconfig.patch + ### ### IA64 ### diff --git a/kernel/kernel/files/patches/mageia/x86-KVM-svm-unconditionally-intercept-DB.patch b/kernel/kernel/files/patches/mageia/x86-KVM-svm-unconditionally-intercept-DB.patch deleted file mode 100644 index ca67ef99..00000000 --- a/kernel/kernel/files/patches/mageia/x86-KVM-svm-unconditionally-intercept-DB.patch +++ /dev/null @@ -1,80 +0,0 @@ -From: Paolo Bonzini -Subject: [PATCH 2/3] KVM: svm: unconditionally intercept #DB -Date: Tue, 10 Nov 2015 13:22:53 +0100 - -This is needed to avoid the possibility that the guest triggers -an infinite stream of #DB exceptions (CVE-2015-8104). - -VMX is not affected: because it does not save DR6 in the VMCS, -it already intercepts #DB unconditionally. - -Reported-by: Jan Beulich -Cc: stable@vger.kernel.org -Signed-off-by: Paolo Bonzini ---- - arch/x86/kvm/svm.c | 14 +++----------- - 1 file changed, 3 insertions(+), 11 deletions(-) - -diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c -index 183926483c3a..1cc1ffca0d8c 100644 ---- a/arch/x86/kvm/svm.c -+++ b/arch/x86/kvm/svm.c -@@ -1020,6 +1020,7 @@ static void init_vmcb(struct vcpu_svm *svm) - set_exception_intercept(svm, UD_VECTOR); - set_exception_intercept(svm, MC_VECTOR); - set_exception_intercept(svm, AC_VECTOR); -+ set_exception_intercept(svm, DB_VECTOR); - - set_intercept(svm, INTERCEPT_INTR); - set_intercept(svm, INTERCEPT_NMI); -@@ -1554,20 +1555,13 @@ static void svm_set_segment(struct kvm_vcpu *vcpu, - mark_dirty(svm->vmcb, VMCB_SEG); - } - --static void update_db_bp_intercept(struct kvm_vcpu *vcpu) -+static void update_bp_intercept(struct kvm_vcpu *vcpu) - { - struct vcpu_svm *svm = to_svm(vcpu); - -- clr_exception_intercept(svm, DB_VECTOR); - clr_exception_intercept(svm, BP_VECTOR); - -- if (svm->nmi_singlestep) -- set_exception_intercept(svm, DB_VECTOR); -- - if (vcpu->guest_debug & KVM_GUESTDBG_ENABLE) { -- if (vcpu->guest_debug & -- (KVM_GUESTDBG_SINGLESTEP | KVM_GUESTDBG_USE_HW_BP)) -- set_exception_intercept(svm, DB_VECTOR); - if (vcpu->guest_debug & KVM_GUESTDBG_USE_SW_BP) - set_exception_intercept(svm, BP_VECTOR); - } else -@@ -1673,7 +1667,6 @@ static int db_interception(struct vcpu_svm *svm) - if (!(svm->vcpu.guest_debug & KVM_GUESTDBG_SINGLESTEP)) - svm->vmcb->save.rflags &= - ~(X86_EFLAGS_TF | X86_EFLAGS_RF); -- update_db_bp_intercept(&svm->vcpu); - } - - if (svm->vcpu.guest_debug & -@@ -3661,7 +3654,6 @@ static void enable_nmi_window(struct kvm_vcpu *vcpu) - */ - svm->nmi_singlestep = true; - svm->vmcb->save.rflags |= (X86_EFLAGS_TF | X86_EFLAGS_RF); -- update_db_bp_intercept(vcpu); - } - - static int svm_set_tss_addr(struct kvm *kvm, unsigned int addr) -@@ -4287,7 +4279,7 @@ static struct kvm_x86_ops svm_x86_ops = { - .vcpu_load = svm_vcpu_load, - .vcpu_put = svm_vcpu_put, - -- .update_db_bp_intercept = update_db_bp_intercept, -+ .update_db_bp_intercept = update_bp_intercept, - .get_msr = svm_get_msr, - .set_msr = svm_set_msr, - .get_segment_base = svm_get_segment_base, --- -1.8.3.1 - - diff --git a/kernel/kernel/pspec.xml b/kernel/kernel/pspec.xml index 93227a6f..fc3136c6 100644 --- a/kernel/kernel/pspec.xml +++ b/kernel/kernel/pspec.xml @@ -12,7 +12,7 @@ kernel The Linux kernel (the core of the Linux operating system) for Pisi Linux kernel contains the Linux kernel, the core of any Linux operating system. The kernel handles the basic functions of the operating system: memory allocation, process allocation, device input and output, etc. - https://www.kernel.org/pub/linux/kernel/v4.x/linux-4.3.tar.gz + https://www.kernel.org/pub/linux/kernel/v4.x/linux-4.4.tar.gz configs/kernel-i686-config configs/kernel-x86_64-config @@ -28,8 +28,8 @@ - patches/linux/patch-4.3.2.xz - + + patches/mageia/x86-pci-toshiba-equium-a60-assign-busses.patch @@ -37,17 +37,13 @@ patches/mageia/x86-default_poweroff_up_machines.patch patches/mageia/x86-increase-default-minimum-vmalloc-area-by-64MB-to-192MB.patch patches/mageia/Revert-cpufreq-pcc-Enable-autoload-of-pcc-cpufreq-fo.patch - patches/mageia/x86-KVM-svm-unconditionally-intercept-DB.patch - patches/mageia/Revert-x86-efi-Request-desired-alignment-via-the-PE-.patch patches/mageia/Revert-x86-mm-mtrr-Remove-kernel-internal-MTRR-inter.patch patches/mageia/base-cacheinfo-silence-DT-warnings.patch - patches/mageia/certs-add-.gitignore-to-stop-git-nagging-about-x509_.patch patches/mageia/pci-add-ALI-M5229-ide-compatibility-mode-quirk.patch patches/mageia/pci-quirks-drop-devinit-exit.patch patches/mageia/acpi-CLEVO-M360S-disable_acpi_irq.patch patches/mageia/acpi-processor-M720SR-limit-to-C2.patch patches/mageia/ACPI-video-Add-a-quirk-to-force-acpi-video-backlight.patch - patches/mageia/ACPI-video-Add-a-quirk-to-force-native-backlight-on-.patch config.cache @@ -21,7 +21,7 @@ glibc-devel libkmod-devel - glib2-devel + libpcre-devel gperf @@ -33,7 +33,7 @@ libkmod - glib2 + udev @@ -78,7 +78,7 @@ Development files for eudev eudev - glib2-devel + udev-devel @@ -108,11 +108,11 @@ glibc-32bit libkmod-32bit - glib2-32bit + glibc-32bit - glib2-32bit + /lib32 @@ -121,6 +121,14 @@ + + 2016-01-03 + 3.1.5 + Version bump. + Add rules for mount storage. + Ertuğrul Erata + ertugrulerata@gmail.com + 2016-01-04 3.1.2