From 61466c651fc2faffe89000e9d42661f6ab81d8e4 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Sun, 9 Nov 2014 12:42:22 +0200 Subject: drm/amd: Add get_fw_version to kfd-->kgd interface This patch adds a new interface to the kfd-->kgd interface. The new interface function retrieves the firmware version that is currently in use by the MEC engine. The firmware was uploaded to the MEC engine by the kgd (radeon). v2: Added parameter of engine type to interface function Signed-off-by: Oded Gabbay Reviewed-by: Alex Deucher diff --git a/drivers/gpu/drm/amd/include/kgd_kfd_interface.h b/drivers/gpu/drm/amd/include/kgd_kfd_interface.h index 9c729dd..47b5519 100644 --- a/drivers/gpu/drm/amd/include/kgd_kfd_interface.h +++ b/drivers/gpu/drm/amd/include/kgd_kfd_interface.h @@ -45,6 +45,17 @@ enum kgd_memory_pool { KGD_POOL_FRAMEBUFFER = 3, }; +enum kgd_engine_type { + KGD_ENGINE_PFP = 1, + KGD_ENGINE_ME, + KGD_ENGINE_CE, + KGD_ENGINE_MEC1, + KGD_ENGINE_MEC2, + KGD_ENGINE_RLC, + KGD_ENGINE_SDMA, + KGD_ENGINE_MAX +}; + struct kgd2kfd_shared_resources { /* Bit n == 1 means VMID n is available for KFD. */ unsigned int compute_vmid_bitmap; @@ -137,6 +148,8 @@ struct kgd2kfd_calls { * * @hqd_destroy: Destructs and preempts the queue assigned to that hqd slot. * + * @get_fw_version: Returns FW versions from the header + * * This structure contains function pointers to services that the kgd driver * provides to amdkfd driver. * @@ -176,6 +189,8 @@ struct kfd2kgd_calls { int (*hqd_destroy)(struct kgd_dev *kgd, uint32_t reset_type, unsigned int timeout, uint32_t pipe_id, uint32_t queue_id); + uint16_t (*get_fw_version)(struct kgd_dev *kgd, + enum kgd_engine_type type); }; bool kgd2kfd_init(unsigned interface_version, -- cgit v0.10.2 From f7694323b44edb6ef690cffd97fcb9055f60677e Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Sun, 9 Nov 2014 12:45:11 +0200 Subject: drm/radeon: Add implementation of get_fw_version This patch implements a new interface that was added to the kfd-->kgd interface. The new interface function retrieves the firmware version that is currently in use by a specific engine. The firmware was uploaded to the engine by the radeon driver. v2: Returns the fw version of the specific engine, as passed into the function by a new parameter Signed-off-by: Oded Gabbay Reviewed-by: Alex Deucher diff --git a/drivers/gpu/drm/radeon/radeon_kfd.c b/drivers/gpu/drm/radeon/radeon_kfd.c index 065d020..242fd8b 100644 --- a/drivers/gpu/drm/radeon/radeon_kfd.c +++ b/drivers/gpu/drm/radeon/radeon_kfd.c @@ -28,6 +28,8 @@ #include "cikd.h" #include "cik_reg.h" #include "radeon_kfd.h" +#include "radeon_ucode.h" +#include #define CIK_PIPE_PER_MEC (4) @@ -49,6 +51,7 @@ static uint64_t get_vmem_size(struct kgd_dev *kgd); static uint64_t get_gpu_clock_counter(struct kgd_dev *kgd); static uint32_t get_max_engine_clock_in_mhz(struct kgd_dev *kgd); +static uint16_t get_fw_version(struct kgd_dev *kgd, enum kgd_engine_type type); /* * Register access functions @@ -91,6 +94,7 @@ static const struct kfd2kgd_calls kfd2kgd = { .hqd_load = kgd_hqd_load, .hqd_is_occupies = kgd_hqd_is_occupies, .hqd_destroy = kgd_hqd_destroy, + .get_fw_version = get_fw_version }; static const struct kgd2kfd_calls *kgd2kfd; @@ -561,3 +565,52 @@ static int kgd_hqd_destroy(struct kgd_dev *kgd, uint32_t reset_type, release_queue(kgd); return 0; } + +static uint16_t get_fw_version(struct kgd_dev *kgd, enum kgd_engine_type type) +{ + struct radeon_device *rdev = (struct radeon_device *) kgd; + const union radeon_firmware_header *hdr; + + BUG_ON(kgd == NULL || rdev->mec_fw == NULL); + + switch (type) { + case KGD_ENGINE_PFP: + hdr = (const union radeon_firmware_header *) rdev->pfp_fw->data; + break; + + case KGD_ENGINE_ME: + hdr = (const union radeon_firmware_header *) rdev->me_fw->data; + break; + + case KGD_ENGINE_CE: + hdr = (const union radeon_firmware_header *) rdev->ce_fw->data; + break; + + case KGD_ENGINE_MEC1: + hdr = (const union radeon_firmware_header *) rdev->mec_fw->data; + break; + + case KGD_ENGINE_MEC2: + hdr = (const union radeon_firmware_header *) + rdev->mec2_fw->data; + break; + + case KGD_ENGINE_RLC: + hdr = (const union radeon_firmware_header *) rdev->rlc_fw->data; + break; + + case KGD_ENGINE_SDMA: + hdr = (const union radeon_firmware_header *) + rdev->sdma_fw->data; + break; + + default: + return 0; + } + + if (hdr == NULL) + return 0; + + /* Only 12 bit in use*/ + return hdr->common.ucode_version; +} -- cgit v0.10.2 From f1386fbc2bc9e316c3f58ef9463f03002eb41346 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Sun, 9 Nov 2014 12:46:56 +0200 Subject: amdkfd: Display MEC fw version in topology node This patch displays the firmware version of the microcode that is currently running in the MEC. This is needed for the HSA RT, so it could differentiate its behavior based on fw version. e.g. workarounds for bugs in fw v2: Send the KGD_ENGINE_MEC1 as a parameter to the get_fw_version() Signed-off-by: Oded Gabbay Reviewed-by: Alex Deucher diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c index 5733e28..b11792d 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c @@ -700,8 +700,6 @@ static ssize_t node_show(struct kobject *kobj, struct attribute *attr, dev->node_props.simd_per_cu); sysfs_show_32bit_prop(buffer, "max_slots_scratch_cu", dev->node_props.max_slots_scratch_cu); - sysfs_show_32bit_prop(buffer, "engine_id", - dev->node_props.engine_id); sysfs_show_32bit_prop(buffer, "vendor_id", dev->node_props.vendor_id); sysfs_show_32bit_prop(buffer, "device_id", @@ -715,6 +713,12 @@ static ssize_t node_show(struct kobject *kobj, struct attribute *attr, dev->gpu->kgd)); sysfs_show_64bit_prop(buffer, "local_mem_size", kfd2kgd->get_vmem_size(dev->gpu->kgd)); + + sysfs_show_32bit_prop(buffer, "fw_version", + kfd2kgd->get_fw_version( + dev->gpu->kgd, + KGD_ENGINE_MEC1)); + } ret = sysfs_show_32bit_prop(buffer, "max_engine_clk_ccompute", -- cgit v0.10.2 From dd59239a9862a42e4b8d47e4aaa8d595d08c29ab Mon Sep 17 00:00:00 2001 From: Alexey Skidanov Date: Tue, 18 Nov 2014 13:56:23 +0200 Subject: amdkfd: init aperture once per process Since the user space may call open() more that once from the same process, the aperture initialization should be moved from kfd_open() Signed-off-by: Alexey Skidanov Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index 4f7b275..7d4974b 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -121,13 +121,9 @@ static int kfd_open(struct inode *inode, struct file *filep) if (IS_ERR(process)) return PTR_ERR(process); - process->is_32bit_user_mode = is_32bit_user_mode; - dev_dbg(kfd_device, "process %d opened, compat mode (32 bit) - %d\n", process->pasid, process->is_32bit_user_mode); - kfd_init_apertures(process); - return 0; } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_flat_memory.c b/drivers/gpu/drm/amd/amdkfd/kfd_flat_memory.c index 66df4da..e64aa99 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_flat_memory.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_flat_memory.c @@ -299,13 +299,13 @@ int kfd_init_apertures(struct kfd_process *process) struct kfd_dev *dev; struct kfd_process_device *pdd; - mutex_lock(&process->mutex); - /*Iterating over all devices*/ while ((dev = kfd_topology_enum_kfd_devices(id)) != NULL && id < NUM_OF_SUPPORTED_GPUS) { pdd = kfd_get_process_device_data(dev, process, 1); + if (!pdd) + return -1; /* * For 64 bit process aperture will be statically reserved in @@ -348,8 +348,6 @@ int kfd_init_apertures(struct kfd_process *process) id++; } - mutex_unlock(&process->mutex); - return 0; } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c b/drivers/gpu/drm/amd/amdkfd/kfd_process.c index b85eb0b..3c76ef0 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c @@ -26,6 +26,8 @@ #include #include #include +#include + struct mm_struct; #include "kfd_priv.h" @@ -285,8 +287,15 @@ static struct kfd_process *create_process(const struct task_struct *thread) if (err != 0) goto err_process_pqm_init; + /* init process apertures*/ + process->is_32bit_user_mode = is_compat_task(); + if (kfd_init_apertures(process) != 0) + goto err_init_apretures; + return process; +err_init_apretures: + pqm_uninit(&process->pqm); err_process_pqm_init: hash_del_rcu(&process->kfd_processes); synchronize_rcu(); -- cgit v0.10.2 From 0f1be51c358f740fe5183bd0bcd60076fdfb53d0 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Thu, 4 Dec 2014 09:41:43 +0530 Subject: thermal: cpu_cooling: check for the readiness of cpufreq layer In this patch, the cpu_cooling code checks for the usability of cpufreq layer before proceeding with the CPU cooling device registration. The main reason is: CPU cooling device is not usable if cpufreq cannot switch frequencies. Similar checks are spread in thermal drivers. Thus, the advantage now is to have the check in a single place: cpu cooling device registration. For this reason, this patch also updates the existing drivers that depend on CPU cooling to simply propagate the error code of the cpu cooling registration call. Therefore, in case cpufreq is not ready, the thermal drivers will still return -EPROBE_DEFER, in an attempt to try again when cpufreq layer gets ready. Cc: devicetree@vger.kernel.org Cc: Grant Likely Cc: Kukjin Kim Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Cc: linux-pm@vger.kernel.org Cc: linux-samsung-soc@vger.kernel.org Cc: Naveen Krishna Chatradhi Cc: Rob Herring Cc: Zhang Rui Acked-by: Viresh Kumar Signed-off-by: Viresh Kumar Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index ad09e51..f98a763 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -443,6 +443,11 @@ __cpufreq_cooling_register(struct device_node *np, int ret = 0, i; struct cpufreq_policy policy; + if (!cpufreq_frequency_get_table(cpumask_first(clip_cpus))) { + pr_debug("%s: CPUFreq table not found\n", __func__); + return ERR_PTR(-EPROBE_DEFER); + } + /* Verify that all the clip cpus have same freq_min, freq_max limit */ for_each_cpu(i, clip_cpus) { /* continue if cpufreq policy not found and not return error */ diff --git a/drivers/thermal/db8500_cpufreq_cooling.c b/drivers/thermal/db8500_cpufreq_cooling.c index 786d192..1ac7ec6 100644 --- a/drivers/thermal/db8500_cpufreq_cooling.c +++ b/drivers/thermal/db8500_cpufreq_cooling.c @@ -18,7 +18,6 @@ */ #include -#include #include #include #include @@ -30,10 +29,6 @@ static int db8500_cpufreq_cooling_probe(struct platform_device *pdev) struct thermal_cooling_device *cdev; struct cpumask mask_val; - /* make sure cpufreq driver has been initialized */ - if (!cpufreq_frequency_get_table(0)) - return -EPROBE_DEFER; - cpumask_set_cpu(0, &mask_val); cdev = cpufreq_cooling_register(&mask_val); diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c index 5a1f107..16405b4 100644 --- a/drivers/thermal/imx_thermal.c +++ b/drivers/thermal/imx_thermal.c @@ -9,7 +9,6 @@ #include #include -#include #include #include #include @@ -459,10 +458,6 @@ static int imx_thermal_probe(struct platform_device *pdev) int measure_freq; int ret; - if (!cpufreq_get_current_driver()) { - dev_dbg(&pdev->dev, "no cpufreq driver!"); - return -EPROBE_DEFER; - } data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; diff --git a/drivers/thermal/samsung/exynos_thermal_common.c b/drivers/thermal/samsung/exynos_thermal_common.c index b6be572..50a1f17 100644 --- a/drivers/thermal/samsung/exynos_thermal_common.c +++ b/drivers/thermal/samsung/exynos_thermal_common.c @@ -371,9 +371,11 @@ int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf) th_zone->cool_dev[th_zone->cool_dev_size] = cpufreq_cooling_register(&mask_val); if (IS_ERR(th_zone->cool_dev[th_zone->cool_dev_size])) { - dev_err(sensor_conf->dev, - "Failed to register cpufreq cooling device\n"); - ret = -EINVAL; + ret = PTR_ERR(th_zone->cool_dev[th_zone->cool_dev_size]); + if (ret != -EPROBE_DEFER) + dev_err(sensor_conf->dev, + "Failed to register cpufreq cooling device: %d\n", + ret); goto err_unregister; } th_zone->cool_dev_size++; diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c index 49c0924..2afca9b 100644 --- a/drivers/thermal/samsung/exynos_tmu.c +++ b/drivers/thermal/samsung/exynos_tmu.c @@ -683,7 +683,10 @@ static int exynos_tmu_probe(struct platform_device *pdev) /* Register the sensor with thermal management interface */ ret = exynos_register_thermal(sensor_conf); if (ret) { - dev_err(&pdev->dev, "Failed to register thermal interface\n"); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "Failed to register thermal interface: %d\n", + ret); goto err_clk; } data->reg_conf = sensor_conf; diff --git a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c index 9eec26d..5f07d7e 100644 --- a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c +++ b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -403,11 +402,6 @@ int ti_thermal_register_cpu_cooling(struct ti_bandgap *bgp, int id) if (!data) return -EINVAL; - if (!cpufreq_get_current_driver()) { - dev_dbg(bgp->dev, "no cpufreq driver yet\n"); - return -EPROBE_DEFER; - } - /* Register cooling device */ data->cool_dev = cpufreq_cooling_register(cpu_present_mask); if (IS_ERR(data->cool_dev)) { -- cgit v0.10.2 From b45257b10d7cc21ed0393f31e1f8ac6cdde9fa18 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Dec 2014 09:41:44 +0530 Subject: thermal: db8500: pass cpu_present_mask to cpufreq_cooling_register() cpufreq_cooling_register() expects mask of all the CPUs where frequency constraint is applicable. This platform has more than one CPU to which these constraints will apply and so passing mask of only CPU0 wouldn't be sufficient. Also, this platform has a single cluster of CPUs and the constraint applies to all CPUs. If CPU0 is hoplugged out then we may face strange BUGs as cpu_cooling framework isn't aware of any siblings sharing clock line. Fix it by passing cpu_present_mask to cpufreq_cooling_register(). Cc: Hongbo Zhang Cc: Linus Walleij Signed-off-by: Viresh Kumar Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/db8500_cpufreq_cooling.c b/drivers/thermal/db8500_cpufreq_cooling.c index 1ac7ec6..3cc3dd9 100644 --- a/drivers/thermal/db8500_cpufreq_cooling.c +++ b/drivers/thermal/db8500_cpufreq_cooling.c @@ -27,11 +27,8 @@ static int db8500_cpufreq_cooling_probe(struct platform_device *pdev) { struct thermal_cooling_device *cdev; - struct cpumask mask_val; - - cpumask_set_cpu(0, &mask_val); - cdev = cpufreq_cooling_register(&mask_val); + cdev = cpufreq_cooling_register(cpu_present_mask); if (IS_ERR(cdev)) { dev_err(&pdev->dev, "Failed to register cooling device\n"); return PTR_ERR(cdev); -- cgit v0.10.2 From bec85d2e0c9f71cab57614ab05057840bb7c422f Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Dec 2014 09:41:45 +0530 Subject: thermal: imx: pass cpu_present_mask to cpufreq_cooling_register() cpufreq_cooling_register() expects mask of all the CPUs where frequency constraint is applicable. This platform has more than one CPU to which these constraints will apply and so passing mask of only CPU0 wouldn't be sufficient. Also, this platform has a single cluster of CPUs and the constraint applies to all CPUs. If CPU0 is hoplugged out then we may face strange BUGs as cpu_cooling framework isn't aware of any siblings sharing clock line. Fix it by passing cpu_present_mask to cpufreq_cooling_register(). Cc: Shawn Guo Signed-off-by: Viresh Kumar Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c index 16405b4..d80e36eb 100644 --- a/drivers/thermal/imx_thermal.c +++ b/drivers/thermal/imx_thermal.c @@ -453,7 +453,6 @@ static int imx_thermal_probe(struct platform_device *pdev) const struct of_device_id *of_id = of_match_device(of_imx_thermal_match, &pdev->dev); struct imx_thermal_data *data; - struct cpumask clip_cpus; struct regmap *map; int measure_freq; int ret; @@ -511,8 +510,7 @@ static int imx_thermal_probe(struct platform_device *pdev) regmap_write(map, MISC0 + REG_SET, MISC0_REFTOP_SELBIASOFF); regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN); - cpumask_set_cpu(0, &clip_cpus); - data->cdev = cpufreq_cooling_register(&clip_cpus); + data->cdev = cpufreq_cooling_register(cpu_present_mask); if (IS_ERR(data->cdev)) { ret = PTR_ERR(data->cdev); dev_err(&pdev->dev, -- cgit v0.10.2 From f3764e6c187b2841971d6347f81acc1a5264a92f Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Dec 2014 09:41:46 +0530 Subject: thermal: exynos: pass cpu_present_mask to cpufreq_cooling_register() cpufreq_cooling_register() expects mask of all the CPUs where frequency constraint is applicable. This platform has more than one CPU to which these constraints will apply and so passing mask of only CPU0 wouldn't be sufficient. Also, this platform has a single cluster of CPUs and the constraint applies to all CPUs. If CPU0 is hoplugged out then we may face strange BUGs as cpu_cooling framework isn't aware of any siblings sharing clock line. Fix it by passing cpu_present_mask to cpufreq_cooling_register(). Cc: Chanwoo Choi Cc: Kyungmin Park Cc: Amit Daniel Kachhap Cc: Lukasz Majewski Signed-off-by: Viresh Kumar Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/samsung/exynos_thermal_common.c b/drivers/thermal/samsung/exynos_thermal_common.c index 50a1f17..6dc3815 100644 --- a/drivers/thermal/samsung/exynos_thermal_common.c +++ b/drivers/thermal/samsung/exynos_thermal_common.c @@ -347,7 +347,6 @@ void exynos_report_trigger(struct thermal_sensor_conf *conf) int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf) { int ret; - struct cpumask mask_val; struct exynos_thermal_zone *th_zone; if (!sensor_conf || !sensor_conf->read_temperature) { @@ -367,9 +366,8 @@ int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf) * sensor */ if (sensor_conf->cooling_data.freq_clip_count > 0) { - cpumask_set_cpu(0, &mask_val); th_zone->cool_dev[th_zone->cool_dev_size] = - cpufreq_cooling_register(&mask_val); + cpufreq_cooling_register(cpu_present_mask); if (IS_ERR(th_zone->cool_dev[th_zone->cool_dev_size])) { ret = PTR_ERR(th_zone->cool_dev[th_zone->cool_dev_size]); if (ret != -EPROBE_DEFER) -- cgit v0.10.2 From 728c03c9592198717fed3e9fbae7260cff300175 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Dec 2014 09:41:47 +0530 Subject: thermal: cpu_cooling: random comment fixups s/give/given Acked-by: Eduardo Valentin Signed-off-by: Viresh Kumar Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index f98a763..6f2d41e 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -121,7 +121,7 @@ enum cpufreq_cooling_property { }; /** - * get_property - fetch a property of interest for a give cpu. + * get_property - fetch a property of interest for a given cpu. * @cpu: cpu for which the property is required * @input: query parameter * @output: query return @@ -131,6 +131,7 @@ enum cpufreq_cooling_property { * 1. get maximum cpu cooling states * 2. translate frequency to cooling state * 3. translate cooling state to frequency + * * Note that the code may be not in good shape * but it is written in this way in order to: * a) reduce duplicate code as most of the code can be shared. @@ -211,7 +212,7 @@ static int get_property(unsigned int cpu, unsigned long input, } /** - * cpufreq_cooling_get_level - for a give cpu, return the cooling level. + * cpufreq_cooling_get_level - for a given cpu, return the cooling level. * @cpu: cpu for which the level is required * @freq: the frequency of interest * -- cgit v0.10.2 From beca6053fc21bbe0ed0242a3f79c0cca5749a90f Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Dec 2014 09:41:48 +0530 Subject: thermal: cpu_cooling: fix doc comment over struct cpufreq_cooling_device cooling_cpufreq_lock isn't used to protect this structure and so the comment over it is outdated. Fix it. Signed-off-by: Viresh Kumar Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 6f2d41e..cc10641 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -40,9 +40,8 @@ * frequency. * @allowed_cpus: all the cpus involved for this cpufreq_cooling_device. * - * This structure is required for keeping information of each - * cpufreq_cooling_device registered. In order to prevent corruption of this a - * mutex lock cooling_cpufreq_lock is used. + * This structure is required for keeping information of each registered + * cpufreq_cooling_device. */ struct cpufreq_cooling_device { int id; -- cgit v0.10.2 From 07d888d831b038c01c5415f8945f41c743f49fb2 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Dec 2014 09:41:49 +0530 Subject: thermal: cpu_cooling: Add comment to clarify relation between cooling state and frequency This wasn't explained well anywhere and should be clearly specified. Lets add a top level comment for this. Signed-off-by: Viresh Kumar Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index cc10641..a5a9317 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -28,6 +28,20 @@ #include #include +/* + * Cooling state <-> CPUFreq frequency + * + * Cooling states are translated to frequencies throughout this driver and this + * is the relation between them. + * + * Highest cooling state corresponds to lowest possible frequency. + * + * i.e. + * level 0 --> 1st Max Freq + * level 1 --> 2nd Max Freq + * ... + */ + /** * struct cpufreq_cooling_device - data for cooling device with cpufreq * @id: unique integer value corresponding to each cpufreq_cooling_device -- cgit v0.10.2 From 98d522f056568007557867d53833770dee9d8fe8 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Dec 2014 09:41:50 +0530 Subject: thermal: cpu_cooling: Pass variable instead of its type to sizeof() Just following coding guidelines here. Signed-off-by: Viresh Kumar Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index a5a9317..5378561 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -476,8 +476,7 @@ __cpufreq_cooling_register(struct device_node *np, return ERR_PTR(-EINVAL); } } - cpufreq_dev = kzalloc(sizeof(struct cpufreq_cooling_device), - GFP_KERNEL); + cpufreq_dev = kzalloc(sizeof(*cpufreq_dev), GFP_KERNEL); if (!cpufreq_dev) return ERR_PTR(-ENOMEM); -- cgit v0.10.2 From 92e615ec82c0314fb480eeb19396f4ac15bf97ef Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Dec 2014 09:41:51 +0530 Subject: thermal: cpu_cooling: no need to set cpufreq_state to zero Its already zero, we allocated cpufreq_dev with kzalloc. Signed-off-by: Viresh Kumar Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 5378561..30e2ecb 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -499,7 +499,7 @@ __cpufreq_cooling_register(struct device_node *np, return cool_dev; } cpufreq_dev->cool_dev = cool_dev; - cpufreq_dev->cpufreq_state = 0; + mutex_lock(&cooling_cpufreq_lock); /* Register the notifier for first cpufreq cooling device */ -- cgit v0.10.2 From 5d3bdb8998e066fe270f2f71db7163d5ac40d989 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Dec 2014 09:41:52 +0530 Subject: thermal: cpu_cooling: no need to set cpufreq_dev to NULL It will be overwritten soon with return value of kzalloc(). Signed-off-by: Viresh Kumar Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 30e2ecb..c144493 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -451,7 +451,7 @@ __cpufreq_cooling_register(struct device_node *np, const struct cpumask *clip_cpus) { struct thermal_cooling_device *cool_dev; - struct cpufreq_cooling_device *cpufreq_dev = NULL; + struct cpufreq_cooling_device *cpufreq_dev; unsigned int min = 0, max = 0; char dev_name[THERMAL_NAME_LENGTH]; int ret = 0, i; -- cgit v0.10.2 From 8e54d442fe3cdd1ffe5f563ee843b4d48e14ef6e Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Dec 2014 09:41:53 +0530 Subject: thermal: cpu_cooling: no need to initialze 'ret' ret is initialized before it is used, so no need to set it to 0 in its declaration. Signed-off-by: Viresh Kumar Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index c144493..d57b8bb 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -454,7 +454,7 @@ __cpufreq_cooling_register(struct device_node *np, struct cpufreq_cooling_device *cpufreq_dev; unsigned int min = 0, max = 0; char dev_name[THERMAL_NAME_LENGTH]; - int ret = 0, i; + int ret, i; struct cpufreq_policy policy; if (!cpufreq_frequency_get_table(cpumask_first(clip_cpus))) { -- cgit v0.10.2 From 268ac445ee1b2b7c2806e7a21076e6d94aca1ca3 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Dec 2014 09:41:54 +0530 Subject: thermal: cpu_cooling: propagate error returned by idr_alloc() We aren't supposed to return our own error type here. Return what we got. Signed-off-by: Viresh Kumar Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index d57b8bb..5c9a2ef 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -485,7 +485,7 @@ __cpufreq_cooling_register(struct device_node *np, ret = get_idr(&cpufreq_idr, &cpufreq_dev->id); if (ret) { kfree(cpufreq_dev); - return ERR_PTR(-EINVAL); + return ERR_PTR(ret); } snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d", -- cgit v0.10.2 From 405fb8256226ad68cf6ba5172d289a70cb447c81 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Dec 2014 09:41:55 +0530 Subject: thermal: cpu_cooling: Don't match min/max frequencies for all CPUs on cooling register In __cpufreq_cooling_register() we try to match min/max frequencies for all CPUs passed in 'clip_cpus' mask. This mask is the cpumask of cpus where the frequency constraints will be applied. Same frequency constraint can be applied only to the CPUs belonging to the same cluster (i.e. CPUs sharing clock line). For all such CPUs we have a single 'struct cpufreq_policy' structure managing them and so getting policies for all CPUs wouldn't make any sense as they will all return the same pointer. So, remove this useless check of checking min/max for all CPUs. Also update doc comment to make this more obvious that clip_cpus should be same as policy->related_cpus. Signed-off-by: Viresh Kumar Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 5c9a2ef..f325738 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -437,6 +437,7 @@ static struct notifier_block thermal_cpufreq_notifier_block = { * __cpufreq_cooling_register - helper function to create cpufreq cooling device * @np: a valid struct device_node to the cooling device device tree node * @clip_cpus: cpumask of cpus where the frequency constraints will happen. + * Normally this should be same as cpufreq policy->related_cpus. * * This interface function registers the cpufreq cooling device with the name * "thermal-cpufreq-%x". This api can support multiple instances of cpufreq @@ -452,30 +453,14 @@ __cpufreq_cooling_register(struct device_node *np, { struct thermal_cooling_device *cool_dev; struct cpufreq_cooling_device *cpufreq_dev; - unsigned int min = 0, max = 0; char dev_name[THERMAL_NAME_LENGTH]; - int ret, i; - struct cpufreq_policy policy; + int ret; if (!cpufreq_frequency_get_table(cpumask_first(clip_cpus))) { pr_debug("%s: CPUFreq table not found\n", __func__); return ERR_PTR(-EPROBE_DEFER); } - /* Verify that all the clip cpus have same freq_min, freq_max limit */ - for_each_cpu(i, clip_cpus) { - /* continue if cpufreq policy not found and not return error */ - if (!cpufreq_get_policy(&policy, i)) - continue; - if (min == 0 && max == 0) { - min = policy.cpuinfo.min_freq; - max = policy.cpuinfo.max_freq; - } else { - if (min != policy.cpuinfo.min_freq || - max != policy.cpuinfo.max_freq) - return ERR_PTR(-EINVAL); - } - } cpufreq_dev = kzalloc(sizeof(*cpufreq_dev), GFP_KERNEL); if (!cpufreq_dev) return ERR_PTR(-ENOMEM); -- cgit v0.10.2 From e1fae554fb69b8869acbea9397d15758a93d1204 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Dec 2014 09:41:56 +0530 Subject: thermal: cpu_cooling: don't iterate over all allowed_cpus to update cpufreq policy All CPUs present in 'allowed_cpus' share the same 'struct cpufreq_policy' structure and so calling cpufreq_update_policy() for each of them doesn't make sense. Signed-off-by: Viresh Kumar Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index f325738..7f27f1b 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -285,11 +285,10 @@ static unsigned int get_cpu_frequency(unsigned int cpu, unsigned long level) static int cpufreq_apply_cooling(struct cpufreq_cooling_device *cpufreq_device, unsigned long cooling_state) { - unsigned int cpuid, clip_freq; + unsigned int clip_freq; struct cpumask *mask = &cpufreq_device->allowed_cpus; unsigned int cpu = cpumask_any(mask); - /* Check if the old cooling action is same as new cooling action */ if (cpufreq_device->cpufreq_state == cooling_state) return 0; @@ -301,10 +300,8 @@ static int cpufreq_apply_cooling(struct cpufreq_cooling_device *cpufreq_device, cpufreq_device->cpufreq_state = cooling_state; cpufreq_device->cpufreq_val = clip_freq; - for_each_cpu(cpuid, mask) { - if (is_cpufreq_valid(cpuid)) - cpufreq_update_policy(cpuid); - } + if (is_cpufreq_valid(cpu)) + cpufreq_update_policy(cpu); return 0; } -- cgit v0.10.2 From c9ca319f0579cd51b07a666683157233c2cf720d Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Dec 2014 09:41:57 +0530 Subject: thermal: cpu_cooling: Don't check is_cpufreq_valid() Because get_cpu_frequency() has returned a valid frequency, it means that the cpufreq policy is surely valid and so no point checking that again with is_cpufreq_valid(). Get rid of the routine as well as there are no more users. Signed-off-by: Viresh Kumar Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 7f27f1b..1dd4cc4 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -110,23 +110,6 @@ static void release_idr(struct idr *idr, int id) /* Below code defines functions to be used for cpufreq as cooling device */ -/** - * is_cpufreq_valid - function to check frequency transitioning capability. - * @cpu: cpu for which check is needed. - * - * This function will check the current state of the system if - * it is capable of changing the frequency for a given @cpu. - * - * Return: 0 if the system is not currently capable of changing - * the frequency of given cpu. !0 in case the frequency is changeable. - */ -static int is_cpufreq_valid(int cpu) -{ - struct cpufreq_policy policy; - - return !cpufreq_get_policy(&policy, cpu); -} - enum cpufreq_cooling_property { GET_LEVEL, GET_FREQ, @@ -300,8 +283,7 @@ static int cpufreq_apply_cooling(struct cpufreq_cooling_device *cpufreq_device, cpufreq_device->cpufreq_state = cooling_state; cpufreq_device->cpufreq_val = clip_freq; - if (is_cpufreq_valid(cpu)) - cpufreq_update_policy(cpu); + cpufreq_update_policy(cpu); return 0; } -- cgit v0.10.2 From 730abe064b6f8860302b75a689ceed059c08e0b1 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Dec 2014 09:41:58 +0530 Subject: thermal: cpu_cooling: do error handling at the bottom in __cpufreq_cooling_register() This makes life easy and bug free. And is scalable for future resource allocations. Acked-by: Javi Merino Signed-off-by: Viresh Kumar Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 1dd4cc4..491d90a 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -448,8 +448,8 @@ __cpufreq_cooling_register(struct device_node *np, ret = get_idr(&cpufreq_idr, &cpufreq_dev->id); if (ret) { - kfree(cpufreq_dev); - return ERR_PTR(ret); + cool_dev = ERR_PTR(ret); + goto free_cdev; } snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d", @@ -457,11 +457,9 @@ __cpufreq_cooling_register(struct device_node *np, cool_dev = thermal_of_cooling_device_register(np, dev_name, cpufreq_dev, &cpufreq_cooling_ops); - if (IS_ERR(cool_dev)) { - release_idr(&cpufreq_idr, cpufreq_dev->id); - kfree(cpufreq_dev); - return cool_dev; - } + if (IS_ERR(cool_dev)) + goto remove_idr; + cpufreq_dev->cool_dev = cool_dev; mutex_lock(&cooling_cpufreq_lock); @@ -476,6 +474,13 @@ __cpufreq_cooling_register(struct device_node *np, mutex_unlock(&cooling_cpufreq_lock); return cool_dev; + +remove_idr: + release_idr(&cpufreq_idr, cpufreq_dev->id); +free_cdev: + kfree(cpufreq_dev); + + return cool_dev; } /** -- cgit v0.10.2 From 7adb635b3cd790e4e0d7e9d0b3dd30574ae36596 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Dec 2014 09:41:59 +0530 Subject: thermal: cpu_cooling: initialize 'cpufreq_val' on registration There is no point checking for validity of 'cpufreq_val' from cpufreq_thermal_notifier() every time the routine is called. Its guaranteed to be 0 on the first call but will be valid otherwise. Lets update it once while the device registers. Signed-off-by: Viresh Kumar Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 491d90a..86bcf8d 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -316,11 +316,6 @@ static int cpufreq_thermal_notifier(struct notifier_block *nb, &cpufreq_dev->allowed_cpus)) continue; - if (!cpufreq_dev->cpufreq_val) - cpufreq_dev->cpufreq_val = get_cpu_frequency( - cpumask_any(&cpufreq_dev->allowed_cpus), - cpufreq_dev->cpufreq_state); - max_freq = cpufreq_dev->cpufreq_val; if (policy->max != max_freq) @@ -444,6 +439,13 @@ __cpufreq_cooling_register(struct device_node *np, if (!cpufreq_dev) return ERR_PTR(-ENOMEM); + cpufreq_dev->cpufreq_val = get_cpu_frequency(cpumask_any(clip_cpus), 0); + if (!cpufreq_dev->cpufreq_val) { + pr_err("%s: Failed to get frequency", __func__); + cool_dev = ERR_PTR(-EINVAL); + goto free_cdev; + } + cpumask_copy(&cpufreq_dev->allowed_cpus, clip_cpus); ret = get_idr(&cpufreq_idr, &cpufreq_dev->id); -- cgit v0.10.2 From 5194fe469927e50367b35e556812c7fc6ce130d1 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Dec 2014 09:42:00 +0530 Subject: thermal: cpu_cooling: Merge cpufreq_apply_cooling() into cpufreq_set_cur_state() cpufreq_apply_cooling() has a single caller, cpufreq_set_cur_state() and cpufreq_set_cur_state() is an unnecessary wrapper over cpufreq_apply_cooling(). Get rid of it by merging both routines. Signed-off-by: Viresh Kumar Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 86bcf8d..a3dd74f 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -254,41 +254,6 @@ static unsigned int get_cpu_frequency(unsigned int cpu, unsigned long level) } /** - * cpufreq_apply_cooling - function to apply frequency clipping. - * @cpufreq_device: cpufreq_cooling_device pointer containing frequency - * clipping data. - * @cooling_state: value of the cooling state. - * - * Function used to make sure the cpufreq layer is aware of current thermal - * limits. The limits are applied by updating the cpufreq policy. - * - * Return: 0 on success, an error code otherwise (-EINVAL in case wrong - * cooling state). - */ -static int cpufreq_apply_cooling(struct cpufreq_cooling_device *cpufreq_device, - unsigned long cooling_state) -{ - unsigned int clip_freq; - struct cpumask *mask = &cpufreq_device->allowed_cpus; - unsigned int cpu = cpumask_any(mask); - - /* Check if the old cooling action is same as new cooling action */ - if (cpufreq_device->cpufreq_state == cooling_state) - return 0; - - clip_freq = get_cpu_frequency(cpu, cooling_state); - if (!clip_freq) - return -EINVAL; - - cpufreq_device->cpufreq_state = cooling_state; - cpufreq_device->cpufreq_val = clip_freq; - - cpufreq_update_policy(cpu); - - return 0; -} - -/** * cpufreq_thermal_notifier - notifier callback for cpufreq policy change. * @nb: struct notifier_block * with callback info. * @event: value showing cpufreq event for which this function invoked. @@ -391,8 +356,23 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) { struct cpufreq_cooling_device *cpufreq_device = cdev->devdata; + unsigned int cpu = cpumask_any(&cpufreq_device->allowed_cpus); + unsigned int clip_freq; + + /* Check if the old cooling action is same as new cooling action */ + if (cpufreq_device->cpufreq_state == state) + return 0; - return cpufreq_apply_cooling(cpufreq_device, state); + clip_freq = get_cpu_frequency(cpu, state); + if (!clip_freq) + return -EINVAL; + + cpufreq_device->cpufreq_state = state; + cpufreq_device->cpufreq_val = clip_freq; + + cpufreq_update_policy(cpu); + + return 0; } /* Bind cpufreq callbacks to thermal cooling device ops */ -- cgit v0.10.2 From 521a2e5831704efef8aa826d6b22abef55650d59 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Dec 2014 09:42:01 +0530 Subject: thermal: cpu_cooling: remove unnecessary wrapper get_cpu_frequency() get_cpu_frequency() isn't doing much by itself, just calling get_property(). And so this wrapper isn't required at all. Get rid of it. Signed-off-by: Viresh Kumar Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index a3dd74f..2c4c485 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -230,30 +230,6 @@ unsigned long cpufreq_cooling_get_level(unsigned int cpu, unsigned int freq) EXPORT_SYMBOL_GPL(cpufreq_cooling_get_level); /** - * get_cpu_frequency - get the absolute value of frequency from level. - * @cpu: cpu for which frequency is fetched. - * @level: cooling level - * - * This function matches cooling level with frequency. Based on a cooling level - * of frequency, equals cooling state of cpu cooling device, it will return - * the corresponding frequency. - * e.g level=0 --> 1st MAX FREQ, level=1 ---> 2nd MAX FREQ, .... etc - * - * Return: 0 on error, the corresponding frequency otherwise. - */ -static unsigned int get_cpu_frequency(unsigned int cpu, unsigned long level) -{ - int ret = 0; - unsigned int freq; - - ret = get_property(cpu, level, &freq, GET_FREQ); - if (ret) - return 0; - - return freq; -} - -/** * cpufreq_thermal_notifier - notifier callback for cpufreq policy change. * @nb: struct notifier_block * with callback info. * @event: value showing cpufreq event for which this function invoked. @@ -358,14 +334,15 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev, struct cpufreq_cooling_device *cpufreq_device = cdev->devdata; unsigned int cpu = cpumask_any(&cpufreq_device->allowed_cpus); unsigned int clip_freq; + int ret; /* Check if the old cooling action is same as new cooling action */ if (cpufreq_device->cpufreq_state == state) return 0; - clip_freq = get_cpu_frequency(cpu, state); - if (!clip_freq) - return -EINVAL; + ret = get_property(cpu, state, &clip_freq, GET_FREQ); + if (ret) + return ret; cpufreq_device->cpufreq_state = state; cpufreq_device->cpufreq_val = clip_freq; @@ -419,10 +396,11 @@ __cpufreq_cooling_register(struct device_node *np, if (!cpufreq_dev) return ERR_PTR(-ENOMEM); - cpufreq_dev->cpufreq_val = get_cpu_frequency(cpumask_any(clip_cpus), 0); - if (!cpufreq_dev->cpufreq_val) { - pr_err("%s: Failed to get frequency", __func__); - cool_dev = ERR_PTR(-EINVAL); + ret = get_property(cpumask_any(clip_cpus), 0, &cpufreq_dev->cpufreq_val, + GET_FREQ); + if (ret) { + pr_err("%s: Failed to get frequency: %d", __func__, ret); + cool_dev = ERR_PTR(ret); goto free_cdev; } -- cgit v0.10.2 From dcc6c7fdef9e705b1300be22213fb23e3fd1994d Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Dec 2014 09:42:02 +0530 Subject: thermal: cpu_cooling: find max level during device registration CPU frequency tables don't update after the driver is registered and so we don't need to iterate over them to find total number of states every time cpufreq_get_max_state() is called. Do it once at boot time. Signed-off-by: Viresh Kumar Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 2c4c485..d34cc5b 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -52,6 +52,8 @@ * cooling devices. * @cpufreq_val: integer value representing the absolute value of the clipped * frequency. + * @max_level: maximum cooling level. One less than total number of valid + * cpufreq frequencies. * @allowed_cpus: all the cpus involved for this cpufreq_cooling_device. * * This structure is required for keeping information of each registered @@ -62,6 +64,7 @@ struct cpufreq_cooling_device { struct thermal_cooling_device *cool_dev; unsigned int cpufreq_state; unsigned int cpufreq_val; + unsigned int max_level; struct cpumask allowed_cpus; struct list_head node; }; @@ -283,19 +286,9 @@ static int cpufreq_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state) { struct cpufreq_cooling_device *cpufreq_device = cdev->devdata; - struct cpumask *mask = &cpufreq_device->allowed_cpus; - unsigned int cpu; - unsigned int count = 0; - int ret; - - cpu = cpumask_any(mask); - - ret = get_property(cpu, 0, &count, GET_MAXL); - - if (count > 0) - *state = count; - return ret; + *state = cpufreq_device->max_level; + return 0; } /** @@ -385,9 +378,11 @@ __cpufreq_cooling_register(struct device_node *np, struct thermal_cooling_device *cool_dev; struct cpufreq_cooling_device *cpufreq_dev; char dev_name[THERMAL_NAME_LENGTH]; + struct cpufreq_frequency_table *pos, *table; int ret; - if (!cpufreq_frequency_get_table(cpumask_first(clip_cpus))) { + table = cpufreq_frequency_get_table(cpumask_first(clip_cpus)); + if (!table) { pr_debug("%s: CPUFreq table not found\n", __func__); return ERR_PTR(-EPROBE_DEFER); } @@ -404,6 +399,13 @@ __cpufreq_cooling_register(struct device_node *np, goto free_cdev; } + /* Find max levels */ + cpufreq_for_each_valid_entry(pos, table) + cpufreq_dev->max_level++; + + /* max_level is an index, not a counter */ + cpufreq_dev->max_level--; + cpumask_copy(&cpufreq_dev->allowed_cpus, clip_cpus); ret = get_idr(&cpufreq_idr, &cpufreq_dev->id); -- cgit v0.10.2 From 97afa4aafb821eca197f678b6552488c46f8c48e Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Dec 2014 09:42:03 +0530 Subject: thermal: cpu_cooling: get_property() doesn't need to support GET_MAXL anymore We don't use get_property() to find max levels anymore as it is done at boot now. So, don't support GET_MAXL in get_property(). Signed-off-by: Viresh Kumar Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index d34cc5b..d2e6f84 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -116,7 +116,6 @@ static void release_idr(struct idr *idr, int id) enum cpufreq_cooling_property { GET_LEVEL, GET_FREQ, - GET_MAXL, }; /** @@ -124,12 +123,11 @@ enum cpufreq_cooling_property { * @cpu: cpu for which the property is required * @input: query parameter * @output: query return - * @property: type of query (frequency, level, max level) + * @property: type of query (frequency, level) * * This is the common function to - * 1. get maximum cpu cooling states - * 2. translate frequency to cooling state - * 3. translate cooling state to frequency + * 1. translate frequency to cooling state + * 2. translate cooling state to frequency * * Note that the code may be not in good shape * but it is written in this way in order to: @@ -176,12 +174,6 @@ static int get_property(unsigned int cpu, unsigned long input, /* max_level is an index, not a counter */ max_level--; - /* get max level */ - if (property == GET_MAXL) { - *output = (unsigned int)max_level; - return 0; - } - if (property == GET_FREQ) level = descend ? input : (max_level - input); -- cgit v0.10.2 From 2479bb6443d6a793f896219a34bfab0cc410f0b4 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Dec 2014 09:42:04 +0530 Subject: thermal: cpu_cooling: use cpufreq_dev_list instead of cpufreq_dev_count As we already have a list of cpufreq_cooling_devices now, lets use it instead of a local counter. Signed-off-by: Viresh Kumar Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index d2e6f84..32ff6dc 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -71,8 +71,6 @@ struct cpufreq_cooling_device { static DEFINE_IDR(cpufreq_idr); static DEFINE_MUTEX(cooling_cpufreq_lock); -static unsigned int cpufreq_dev_count; - static LIST_HEAD(cpufreq_dev_list); /** @@ -419,10 +417,9 @@ __cpufreq_cooling_register(struct device_node *np, mutex_lock(&cooling_cpufreq_lock); /* Register the notifier for first cpufreq cooling device */ - if (cpufreq_dev_count == 0) + if (list_empty(&cpufreq_dev_list)) cpufreq_register_notifier(&thermal_cpufreq_notifier_block, CPUFREQ_POLICY_NOTIFIER); - cpufreq_dev_count++; list_add(&cpufreq_dev->node, &cpufreq_dev_list); mutex_unlock(&cooling_cpufreq_lock); @@ -495,10 +492,9 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) cpufreq_dev = cdev->devdata; mutex_lock(&cooling_cpufreq_lock); list_del(&cpufreq_dev->node); - cpufreq_dev_count--; /* Unregister the notifier for the last cpufreq cooling device */ - if (cpufreq_dev_count == 0) + if (list_empty(&cpufreq_dev_list)) cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block, CPUFREQ_POLICY_NOTIFIER); mutex_unlock(&cooling_cpufreq_lock); -- cgit v0.10.2 From b9f8b4160310e4459c08b54b918cd83da141f7f0 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Dec 2014 09:42:05 +0530 Subject: thermal: cpu_cooling: Pass 'cpufreq_dev' to get_property() We already know the value of 'cpufreq_dev->max_level' and so there is no need calculating that once again. For this, we need to send 'cpufreq_dev' to get_property(). Make all necessary changes for this change. Because cpufreq_cooling_get_level() doesn't have access to 'cpufreq_dev', it is updated to iterate over the list of cpufreq_cooling_devices to get cooling device for the cpu number passed to it. This also makes it robust to return levels only for the CPU registered via a cooling device. We don't have to support anything that isn't registered yet. Signed-off-by: Viresh Kumar Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 32ff6dc..7687922 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -118,7 +118,7 @@ enum cpufreq_cooling_property { /** * get_property - fetch a property of interest for a given cpu. - * @cpu: cpu for which the property is required + * @cpufreq_dev: cpufreq_dev for which the property is required * @input: query parameter * @output: query return * @property: type of query (frequency, level) @@ -135,20 +135,20 @@ enum cpufreq_cooling_property { * * Return: 0 on success, -EINVAL when invalid parameters are passed. */ -static int get_property(unsigned int cpu, unsigned long input, - unsigned int *output, +static int get_property(struct cpufreq_cooling_device *cpufreq_dev, + unsigned long input, unsigned int *output, enum cpufreq_cooling_property property) { int i; - unsigned long max_level = 0, level = 0; + unsigned long level = 0; unsigned int freq = CPUFREQ_ENTRY_INVALID; int descend = -1; - struct cpufreq_frequency_table *pos, *table = - cpufreq_frequency_get_table(cpu); + struct cpufreq_frequency_table *pos, *table; if (!output) return -EINVAL; + table = cpufreq_frequency_get_table(cpumask_first(&cpufreq_dev->allowed_cpus)); if (!table) return -EINVAL; @@ -162,18 +162,10 @@ static int get_property(unsigned int cpu, unsigned long input, descend = freq > pos->frequency; freq = pos->frequency; - max_level++; } - /* No valid cpu frequency entry */ - if (max_level == 0) - return -EINVAL; - - /* max_level is an index, not a counter */ - max_level--; - if (property == GET_FREQ) - level = descend ? input : (max_level - input); + level = descend ? input : (cpufreq_dev->max_level - input); i = 0; cpufreq_for_each_valid_entry(pos, table) { @@ -186,7 +178,7 @@ static int get_property(unsigned int cpu, unsigned long input, if (property == GET_LEVEL && (unsigned int)input == freq) { /* get level by frequency */ - *output = descend ? i : (max_level - i); + *output = descend ? i : (cpufreq_dev->max_level - i); return 0; } if (property == GET_FREQ && level == i) { @@ -213,12 +205,25 @@ static int get_property(unsigned int cpu, unsigned long input, */ unsigned long cpufreq_cooling_get_level(unsigned int cpu, unsigned int freq) { - unsigned int val; + struct cpufreq_cooling_device *cpufreq_dev; - if (get_property(cpu, (unsigned long)freq, &val, GET_LEVEL)) - return THERMAL_CSTATE_INVALID; + mutex_lock(&cooling_cpufreq_lock); + list_for_each_entry(cpufreq_dev, &cpufreq_dev_list, node) { + if (cpumask_test_cpu(cpu, &cpufreq_dev->allowed_cpus)) { + unsigned int val; + + mutex_unlock(&cooling_cpufreq_lock); + if (get_property(cpufreq_dev, (unsigned long)freq, &val, + GET_LEVEL)) + return THERMAL_CSTATE_INVALID; + + return (unsigned long)val; + } + } + mutex_unlock(&cooling_cpufreq_lock); - return (unsigned long)val; + pr_err("%s: cpu:%d not part of any cooling device\n", __func__, cpu); + return THERMAL_CSTATE_INVALID; } EXPORT_SYMBOL_GPL(cpufreq_cooling_get_level); @@ -323,7 +328,7 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev, if (cpufreq_device->cpufreq_state == state) return 0; - ret = get_property(cpu, state, &clip_freq, GET_FREQ); + ret = get_property(cpufreq_device, state, &clip_freq, GET_FREQ); if (ret) return ret; @@ -381,8 +386,7 @@ __cpufreq_cooling_register(struct device_node *np, if (!cpufreq_dev) return ERR_PTR(-ENOMEM); - ret = get_property(cpumask_any(clip_cpus), 0, &cpufreq_dev->cpufreq_val, - GET_FREQ); + ret = get_property(cpufreq_dev, 0, &cpufreq_dev->cpufreq_val, GET_FREQ); if (ret) { pr_err("%s: Failed to get frequency: %d", __func__, ret); cool_dev = ERR_PTR(ret); -- cgit v0.10.2 From f6859014c7e7cc0e7688525741fc3a0e7aee63be Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Dec 2014 09:42:06 +0530 Subject: thermal: cpu_cooling: Store frequencies in descending order CPUFreq framework *doesn't* guarantee that frequencies present in cpufreq table will be in ascending or descending order. But cpu_cooling somehow assumes that. Probably because most of current users are creating this list from DT, which is done with the help of OPP layer. And OPP layer creates the list in ascending order of frequencies. But cpu_cooling can be used for other platforms too, which don't have frequencies arranged in any order. This patch tries to fix this issue by creating another list of valid frequencies in descending order. Care is also taken to throw warnings for duplicate entries. Later patches would use it to simplify code. Signed-off-by: Viresh Kumar Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 7687922..cb5a4b9 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -65,6 +65,7 @@ struct cpufreq_cooling_device { unsigned int cpufreq_state; unsigned int cpufreq_val; unsigned int max_level; + unsigned int *freq_table; /* In descending order */ struct cpumask allowed_cpus; struct list_head node; }; @@ -352,6 +353,20 @@ static struct notifier_block thermal_cpufreq_notifier_block = { .notifier_call = cpufreq_thermal_notifier, }; +static unsigned int find_next_max(struct cpufreq_frequency_table *table, + unsigned int prev_max) +{ + struct cpufreq_frequency_table *pos; + unsigned int max = 0; + + cpufreq_for_each_valid_entry(pos, table) { + if (pos->frequency > max && pos->frequency < prev_max) + max = pos->frequency; + } + + return max; +} + /** * __cpufreq_cooling_register - helper function to create cpufreq cooling device * @np: a valid struct device_node to the cooling device device tree node @@ -374,6 +389,7 @@ __cpufreq_cooling_register(struct device_node *np, struct cpufreq_cooling_device *cpufreq_dev; char dev_name[THERMAL_NAME_LENGTH]; struct cpufreq_frequency_table *pos, *table; + unsigned int freq, i; int ret; table = cpufreq_frequency_get_table(cpumask_first(clip_cpus)); @@ -397,6 +413,14 @@ __cpufreq_cooling_register(struct device_node *np, cpufreq_for_each_valid_entry(pos, table) cpufreq_dev->max_level++; + cpufreq_dev->freq_table = kmalloc(sizeof(*cpufreq_dev->freq_table) * + cpufreq_dev->max_level, GFP_KERNEL); + if (!cpufreq_dev->freq_table) { + return ERR_PTR(-ENOMEM); + cool_dev = ERR_PTR(-ENOMEM); + goto free_cdev; + } + /* max_level is an index, not a counter */ cpufreq_dev->max_level--; @@ -405,7 +429,7 @@ __cpufreq_cooling_register(struct device_node *np, ret = get_idr(&cpufreq_idr, &cpufreq_dev->id); if (ret) { cool_dev = ERR_PTR(ret); - goto free_cdev; + goto free_table; } snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d", @@ -416,6 +440,18 @@ __cpufreq_cooling_register(struct device_node *np, if (IS_ERR(cool_dev)) goto remove_idr; + /* Fill freq-table in descending order of frequencies */ + for (i = 0, freq = -1; i <= cpufreq_dev->max_level; i++) { + freq = find_next_max(table, freq); + cpufreq_dev->freq_table[i] = freq; + + /* Warn for duplicate entries */ + if (!freq) + pr_warn("%s: table has duplicate entries\n", __func__); + else + pr_debug("%s: freq:%u KHz\n", __func__, freq); + } + cpufreq_dev->cool_dev = cool_dev; mutex_lock(&cooling_cpufreq_lock); @@ -432,6 +468,8 @@ __cpufreq_cooling_register(struct device_node *np, remove_idr: release_idr(&cpufreq_idr, cpufreq_dev->id); +free_table: + kfree(cpufreq_dev->freq_table); free_cdev: kfree(cpufreq_dev); @@ -505,6 +543,7 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) thermal_cooling_device_unregister(cpufreq_dev->cool_dev); release_idr(&cpufreq_idr, cpufreq_dev->id); + kfree(cpufreq_dev->freq_table); kfree(cpufreq_dev); } EXPORT_SYMBOL_GPL(cpufreq_cooling_unregister); -- cgit v0.10.2 From 4843c4a190495aec41c8a87365697e933dc88bc9 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Dec 2014 09:42:07 +0530 Subject: thermal: cpu_cooling: Use cpufreq_dev->freq_table for finding level/freq get_property() was an over complicated beast with BUGs. It used to believe that cpufreq table is present in ascending or descending order, which might not always be true. Previous patch has created another freq table in descending order for us and we better use it now. With that get_property() simply goes away and another helper get_level() comes in. Signed-off-by: Viresh Kumar Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index cb5a4b9..cd6f642 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -112,85 +112,27 @@ static void release_idr(struct idr *idr, int id) /* Below code defines functions to be used for cpufreq as cooling device */ -enum cpufreq_cooling_property { - GET_LEVEL, - GET_FREQ, -}; - /** - * get_property - fetch a property of interest for a given cpu. + * get_level: Find the level for a particular frequency * @cpufreq_dev: cpufreq_dev for which the property is required - * @input: query parameter - * @output: query return - * @property: type of query (frequency, level) - * - * This is the common function to - * 1. translate frequency to cooling state - * 2. translate cooling state to frequency + * @freq: Frequency * - * Note that the code may be not in good shape - * but it is written in this way in order to: - * a) reduce duplicate code as most of the code can be shared. - * b) make sure the logic is consistent when translating between - * cooling states and frequencies. - * - * Return: 0 on success, -EINVAL when invalid parameters are passed. + * Return: level on success, THERMAL_CSTATE_INVALID on error. */ -static int get_property(struct cpufreq_cooling_device *cpufreq_dev, - unsigned long input, unsigned int *output, - enum cpufreq_cooling_property property) +static unsigned long get_level(struct cpufreq_cooling_device *cpufreq_dev, + unsigned int freq) { - int i; - unsigned long level = 0; - unsigned int freq = CPUFREQ_ENTRY_INVALID; - int descend = -1; - struct cpufreq_frequency_table *pos, *table; - - if (!output) - return -EINVAL; - - table = cpufreq_frequency_get_table(cpumask_first(&cpufreq_dev->allowed_cpus)); - if (!table) - return -EINVAL; - - cpufreq_for_each_valid_entry(pos, table) { - /* ignore duplicate entry */ - if (freq == pos->frequency) - continue; - - /* get the frequency order */ - if (freq != CPUFREQ_ENTRY_INVALID && descend == -1) - descend = freq > pos->frequency; - - freq = pos->frequency; - } - - if (property == GET_FREQ) - level = descend ? input : (cpufreq_dev->max_level - input); - - i = 0; - cpufreq_for_each_valid_entry(pos, table) { - /* ignore duplicate entry */ - if (freq == pos->frequency) - continue; + unsigned long level; - /* now we have a valid frequency entry */ - freq = pos->frequency; + for (level = 0; level <= cpufreq_dev->max_level; level++) { + if (freq == cpufreq_dev->freq_table[level]) + return level; - if (property == GET_LEVEL && (unsigned int)input == freq) { - /* get level by frequency */ - *output = descend ? i : (cpufreq_dev->max_level - i); - return 0; - } - if (property == GET_FREQ && level == i) { - /* get frequency by level */ - *output = freq; - return 0; - } - i++; + if (freq > cpufreq_dev->freq_table[level]) + break; } - return -EINVAL; + return THERMAL_CSTATE_INVALID; } /** @@ -211,14 +153,8 @@ unsigned long cpufreq_cooling_get_level(unsigned int cpu, unsigned int freq) mutex_lock(&cooling_cpufreq_lock); list_for_each_entry(cpufreq_dev, &cpufreq_dev_list, node) { if (cpumask_test_cpu(cpu, &cpufreq_dev->allowed_cpus)) { - unsigned int val; - mutex_unlock(&cooling_cpufreq_lock); - if (get_property(cpufreq_dev, (unsigned long)freq, &val, - GET_LEVEL)) - return THERMAL_CSTATE_INVALID; - - return (unsigned long)val; + return get_level(cpufreq_dev, freq); } } mutex_unlock(&cooling_cpufreq_lock); @@ -323,16 +259,16 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev, struct cpufreq_cooling_device *cpufreq_device = cdev->devdata; unsigned int cpu = cpumask_any(&cpufreq_device->allowed_cpus); unsigned int clip_freq; - int ret; + + /* Request state should be less than max_level */ + if (WARN_ON(state > cpufreq_device->max_level)) + return -EINVAL; /* Check if the old cooling action is same as new cooling action */ if (cpufreq_device->cpufreq_state == state) return 0; - ret = get_property(cpufreq_device, state, &clip_freq, GET_FREQ); - if (ret) - return ret; - + clip_freq = cpufreq_device->freq_table[state]; cpufreq_device->cpufreq_state = state; cpufreq_device->cpufreq_val = clip_freq; @@ -402,13 +338,6 @@ __cpufreq_cooling_register(struct device_node *np, if (!cpufreq_dev) return ERR_PTR(-ENOMEM); - ret = get_property(cpufreq_dev, 0, &cpufreq_dev->cpufreq_val, GET_FREQ); - if (ret) { - pr_err("%s: Failed to get frequency: %d", __func__, ret); - cool_dev = ERR_PTR(ret); - goto free_cdev; - } - /* Find max levels */ cpufreq_for_each_valid_entry(pos, table) cpufreq_dev->max_level++; @@ -452,6 +381,7 @@ __cpufreq_cooling_register(struct device_node *np, pr_debug("%s: freq:%u KHz\n", __func__, freq); } + cpufreq_dev->cpufreq_val = cpufreq_dev->freq_table[0]; cpufreq_dev->cool_dev = cool_dev; mutex_lock(&cooling_cpufreq_lock); -- cgit v0.10.2 From 73904cbc1a5a5143323743209257d4668fadc7f3 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Dec 2014 09:42:08 +0530 Subject: thermal: cpu_cooling: update copyright tags Adding my copyright information for two purposes: - To get cc'd for future patches to review (Only if people read this header while sending mail) - Have done enough changes to earn a place here? Cc: Amit Daniel Kachhap Signed-off-by: Viresh Kumar Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index cd6f642..051eb48 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -4,6 +4,8 @@ * Copyright (C) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com) * Copyright (C) 2012 Amit Daniel * + * Copyright (C) 2014 Viresh Kumar + * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by -- cgit v0.10.2 From 9a3031dc3e7a5edfeb52ae8951f8bcd927351854 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Tue, 18 Nov 2014 11:16:30 +0100 Subject: thermal:core:fix: Check return code of the ->get_max_state() callback The return code from ->get_max_state() callback was not checked during binding cooling device to thermal zone device. Signed-off-by: Lukasz Majewski Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 43b9070..8567929 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -928,7 +928,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, struct thermal_zone_device *pos1; struct thermal_cooling_device *pos2; unsigned long max_state; - int result; + int result, ret; if (trip >= tz->trips || (trip < 0 && trip != THERMAL_TRIPS_NONE)) return -EINVAL; @@ -945,7 +945,9 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, if (tz != pos1 || cdev != pos2) return -EINVAL; - cdev->ops->get_max_state(cdev, &max_state); + ret = cdev->ops->get_max_state(cdev, &max_state); + if (ret) + return ret; /* lower default 0, upper default max_state */ lower = lower == THERMAL_NO_LIMIT ? 0 : lower; -- cgit v0.10.2 From fcbb1e02ee540e1875137d36259017f91b95c30c Mon Sep 17 00:00:00 2001 From: Abhilash Kesavan Date: Tue, 2 Dec 2014 12:04:32 +0530 Subject: drivers: thermal: Remove ARCH_HAS_BANDGAP dependency for samsung As samsung thermal support is enabled only for ARCH_EXYNOS, there is no need to select ARCH_HAS_BANDGAP from the arch-specific code. Removing this dependency will also allow the driver to be enabled on 64-bit SoCs. Reviewed-by: Bartlomiej Zolnierkiewicz Reviewed-by: Lukasz Majewski Acked-by: Lukasz Majewski Signed-off-by: Abhilash Kesavan Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/samsung/Kconfig b/drivers/thermal/samsung/Kconfig index f760389..c43306e 100644 --- a/drivers/thermal/samsung/Kconfig +++ b/drivers/thermal/samsung/Kconfig @@ -1,6 +1,6 @@ config EXYNOS_THERMAL tristate "Exynos thermal management unit driver" - depends on ARCH_HAS_BANDGAP && OF + depends on OF help If you say yes here you get support for the TMU (Thermal Management Unit) driver for SAMSUNG EXYNOS series of SoCs. This driver initialises -- cgit v0.10.2 From a940cb34fed73b2d4809a4575f2981d5927e2c21 Mon Sep 17 00:00:00 2001 From: Punit Agrawal Date: Tue, 9 Dec 2014 12:22:01 +0000 Subject: thermal: Fix cdev registration with THERMAL_NO_LIMIT on 64bit The size of unsigned long varies between 32 and 64 bit systems while the size of phandle arguments is always 32 bits per parameter. On 64-bit systems, cooling devices registered via of-thermal apis fail to bind when the min/max cooling state is specified as THERMAL_NO_LIMIT (-1UL) as there is a mis-match between the value read from the device tree (32bit) and the pre-processor define (64bit). As we're unlikely to need cooling states larger than 32 bits, and for consistency with the size of phandle arguments, explicitly limit THERMAL_NO_LIMIT to 32 bits. Reported-by: Hyungwoo Yang Acked-by: Zhang Rui Signed-off-by: Punit Agrawal Signed-off-by: Eduardo Valentin diff --git a/include/dt-bindings/thermal/thermal.h b/include/dt-bindings/thermal/thermal.h index 59822a9..b5e6b00 100644 --- a/include/dt-bindings/thermal/thermal.h +++ b/include/dt-bindings/thermal/thermal.h @@ -11,7 +11,7 @@ #define _DT_BINDINGS_THERMAL_THERMAL_H /* On cooling devices upper and lower limits */ -#define THERMAL_NO_LIMIT (-1UL) +#define THERMAL_NO_LIMIT (~0) #endif diff --git a/include/linux/thermal.h b/include/linux/thermal.h index ef90838..005586f 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -38,7 +38,7 @@ #define THERMAL_CSTATE_INVALID -1UL /* No upper/lower limit requirement */ -#define THERMAL_NO_LIMIT THERMAL_CSTATE_INVALID +#define THERMAL_NO_LIMIT ((u32)~0) /* Unit conversion macros */ #define KELVIN_TO_CELSIUS(t) (long)(((long)t-2732 >= 0) ? \ -- cgit v0.10.2 From c352d1ba1e1e2c8a96af660944a58e86b12ac4af Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Thu, 20 Nov 2014 16:05:55 +0200 Subject: drm/i915: vlv: fix IRQ masking when uninstalling interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit irq_mask should include all IRQ bits that we want to mask, but atm we set it incorrectly to the inverse of this. If the mask is used subsequently to enable/disable some IRQ bits, we may unintentionally unmask unrelated IRQs. I can't see any way that this can lead to a real problem in the current -nightly code, since the first place the mask will be used next (after a suspend/resume cycle) is in valleyview_irq_postinstall(), but the mask is reset there to its proper value. This causes a problem in the upstream kernel though, where - due to another issue - the mask is used in the above way to disable only the display IRQs. This other issue is fixed by: commit 950eabaf5a87257040e0c207be09487954113f54 Author: Imre Deak Date: Mon Sep 8 15:21:09 2014 +0300 drm/i915: vlv: fix display IRQ enable/disable Interestingly, even with the above two bugs, we shouldn't in theory have any real problems (arguably a famous last sentence:). That's because even if we unmask something unintentionally via the VLV_IMR/VLV_IER register the master IRQ masking bit in VLV_MASTER_IER is still set and should prevent all i915 interrupts. According to my testing on an ASUS T100 with DSI output this isn't the case at least with the MIPIA_INTERRUPT. Leaving this one unmasked in IMR/IER, while having VLV_MASTER_IER set to 0 may lead to a lockup during system suspend as shown in the bugzilla ticket below. This fix should get rid of the problem reported there in upstream and older kernels. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=85920 Cc: stable@vger.kernel.org (v3.15+) Signed-off-by: Imre Deak Reviewed-by: Ville Syrjälä Signed-off-by: Jani Nikula diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 981834b..8bab2ab 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -3609,7 +3609,7 @@ static void vlv_display_irq_uninstall(struct drm_i915_private *dev_priv) vlv_display_irq_reset(dev_priv); - dev_priv->irq_mask = 0; + dev_priv->irq_mask = ~0; } static void valleyview_irq_uninstall(struct drm_device *dev) -- cgit v0.10.2 From 1222d8fe578cd28a6c7f5e4e6c6b664c56abfdc0 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 11 Dec 2014 14:40:21 +0100 Subject: regulator: s2mps11: Fix dw_mmc failure on Gear 2 Invalid buck4 configuration for linear mapping of voltage in S2MPS14 regulators caused boot failure on Gear 2 (dw_mmc-exynos): [ 3.569137] EXT4-fs (mmcblk0p15): mounted filesystem with ordered data mode. Opts: (null) [ 3.571716] VFS: Mounted root (ext4 filesystem) readonly on device 179:15. [ 3.629842] mmcblk0: error -110 sending status command, retrying [ 3.630244] mmcblk0: error -110 sending status command, retrying [ 3.636292] mmcblk0: error -110 sending status command, aborting Buck4 voltage regulator has different minimal voltage value than other bucks. Commit merging multiple regulator description macros caused to use linear_min_sel from buck[1235] regulators as value for buck4. This lead to lower voltage of buck4 than required. Output of the buck4 is used internally as power source for LDO{3,4,7,11,19,20,21,23}. On Gear 2 board LDO11 is used as MMC regulator (V_EMMC_1.8V). Fixes: 5a867cf28893 ("regulator: s2mps11: Optimize the regulator description macro") Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mark Brown Cc: diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index adab82d..697be11 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -479,7 +479,7 @@ static struct regulator_ops s2mps14_reg_ops = { .enable_mask = S2MPS14_ENABLE_MASK \ } -#define regulator_desc_s2mps14_buck(num, min, step) { \ +#define regulator_desc_s2mps14_buck(num, min, step, min_sel) { \ .name = "BUCK"#num, \ .id = S2MPS14_BUCK##num, \ .ops = &s2mps14_reg_ops, \ @@ -488,7 +488,7 @@ static struct regulator_ops s2mps14_reg_ops = { .min_uV = min, \ .uV_step = step, \ .n_voltages = S2MPS14_BUCK_N_VOLTAGES, \ - .linear_min_sel = S2MPS14_BUCK1235_START_SEL, \ + .linear_min_sel = min_sel, \ .ramp_delay = S2MPS14_BUCK_RAMP_DELAY, \ .vsel_reg = S2MPS14_REG_B1CTRL2 + (num - 1) * 2, \ .vsel_mask = S2MPS14_BUCK_VSEL_MASK, \ @@ -522,11 +522,16 @@ static const struct regulator_desc s2mps14_regulators[] = { regulator_desc_s2mps14_ldo(23, MIN_800_MV, STEP_25_MV), regulator_desc_s2mps14_ldo(24, MIN_1800_MV, STEP_25_MV), regulator_desc_s2mps14_ldo(25, MIN_1800_MV, STEP_25_MV), - regulator_desc_s2mps14_buck(1, MIN_600_MV, STEP_6_25_MV), - regulator_desc_s2mps14_buck(2, MIN_600_MV, STEP_6_25_MV), - regulator_desc_s2mps14_buck(3, MIN_600_MV, STEP_6_25_MV), - regulator_desc_s2mps14_buck(4, MIN_1400_MV, STEP_12_5_MV), - regulator_desc_s2mps14_buck(5, MIN_600_MV, STEP_6_25_MV), + regulator_desc_s2mps14_buck(1, MIN_600_MV, STEP_6_25_MV, + S2MPS14_BUCK1235_START_SEL), + regulator_desc_s2mps14_buck(2, MIN_600_MV, STEP_6_25_MV, + S2MPS14_BUCK1235_START_SEL), + regulator_desc_s2mps14_buck(3, MIN_600_MV, STEP_6_25_MV, + S2MPS14_BUCK1235_START_SEL), + regulator_desc_s2mps14_buck(4, MIN_1400_MV, STEP_12_5_MV, + S2MPS14_BUCK4_START_SEL), + regulator_desc_s2mps14_buck(5, MIN_600_MV, STEP_6_25_MV, + S2MPS14_BUCK1235_START_SEL), }; static int s2mps14_pmic_enable_ext_control(struct s2mps11_info *s2mps11, -- cgit v0.10.2 From 412aff9497ea55f30b1ae54df918d0aa4d7d8a4b Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 11 Dec 2014 19:11:40 -0200 Subject: thermal: imx: Do not print error message in the EPROBE_DEFER case During imx_thermal probe we have the following log: [ 1.514819] imx_thermal 2000000.aips-bus:tempmon: failed to register cpufreq cooling device: -517 [ 1.515064] platform 2000000.aips-bus:tempmon: Driver imx_thermal requests probe deferral Avoid printing the error message in the EPROBE_DEFER case. Signed-off-by: Fabio Estevam Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c index d80e36eb..f94062b 100644 --- a/drivers/thermal/imx_thermal.c +++ b/drivers/thermal/imx_thermal.c @@ -513,8 +513,10 @@ static int imx_thermal_probe(struct platform_device *pdev) data->cdev = cpufreq_cooling_register(cpu_present_mask); if (IS_ERR(data->cdev)) { ret = PTR_ERR(data->cdev); - dev_err(&pdev->dev, - "failed to register cpufreq cooling device: %d\n", ret); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "failed to register cpufreq cooling device: %d\n", + ret); return ret; } -- cgit v0.10.2 From 38cbf0414ce9f3403fd0a8f508117cb637d693db Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Fri, 12 Dec 2014 09:58:29 -0400 Subject: thermal: db8500: Do not print error message in the EPROBE_DEFER case Avoid printing the error message in the EPROBE_DEFER case where registering cpu cooling at db8500 thermal driver. Cc: Zhang Rui Cc: Grant Likely Cc: Rob Herring Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Cc: devicetree@vger.kernel.org Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/db8500_cpufreq_cooling.c b/drivers/thermal/db8500_cpufreq_cooling.c index 3cc3dd9..28bbff9 100644 --- a/drivers/thermal/db8500_cpufreq_cooling.c +++ b/drivers/thermal/db8500_cpufreq_cooling.c @@ -30,8 +30,14 @@ static int db8500_cpufreq_cooling_probe(struct platform_device *pdev) cdev = cpufreq_cooling_register(cpu_present_mask); if (IS_ERR(cdev)) { - dev_err(&pdev->dev, "Failed to register cooling device\n"); - return PTR_ERR(cdev); + int ret = PTR_ERR(cdev); + + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "Failed to register cooling device %d\n", + ret); + + return ret; } platform_set_drvdata(pdev, cdev); -- cgit v0.10.2 From cffafc3247356088babff01201478c3c7adaef3f Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Fri, 12 Dec 2014 10:05:39 -0400 Subject: thermal: ti-soc-thermal: Do not print error message in the EPROBE_DEFER case Avoid printing the error message in the EPROBE_DEFER case where registering cpu cooling at ti-soc-thermal thermal driver. Cc: Zhang Rui Cc: linux-pm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c index 5f07d7e..096fb21 100644 --- a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c +++ b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c @@ -405,9 +405,14 @@ int ti_thermal_register_cpu_cooling(struct ti_bandgap *bgp, int id) /* Register cooling device */ data->cool_dev = cpufreq_cooling_register(cpu_present_mask); if (IS_ERR(data->cool_dev)) { - dev_err(bgp->dev, - "Failed to register cpufreq cooling device\n"); - return PTR_ERR(data->cool_dev); + int ret = PTR_ERR(data->cool_dev); + + if (ret != -EPROBE_DEFER) + dev_err(bgp->dev, + "Failed to register cpu cooling device %d\n", + ret); + + return ret; } ti_bandgap_set_sensor_data(bgp, id, data); -- cgit v0.10.2 From 0a79a0c011cb291675e3b80760a452fcba5c59d9 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 4 Dec 2014 10:27:20 +0200 Subject: iwlwifi: mvm: clear IN_HW_RESTART flag on stop() On stop(), we already cleared our internal state, and the restart_complete() callback won't be called, so simply clear the IN_HW_RESTART flag. Keeping the flag might result in invalid state on the next start(), preventing the driver starting properly. Additionally, don't take IWL_MVM_REF_UCODE_DOWN on stop() if hw restart was requested, as the ref was already taken in this case. Signed-off-by: Eliad Peller Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 31a5b3f..e880f9d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -1004,8 +1004,13 @@ void __iwl_mvm_mac_stop(struct iwl_mvm *mvm) { lockdep_assert_held(&mvm->mutex); - /* disallow low power states when the FW is down */ - iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); + /* + * Disallow low power states when the FW is down by taking + * the UCODE_DOWN ref. in case of ongoing hw restart the + * ref is already taken, so don't take it again. + */ + if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) + iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); /* async_handlers_wk is now blocked */ @@ -1023,6 +1028,12 @@ void __iwl_mvm_mac_stop(struct iwl_mvm *mvm) /* the fw is stopped, the aux sta is dead: clean up driver state */ iwl_mvm_del_aux_sta(mvm); + /* + * Clear IN_HW_RESTART flag when stopping the hw (as restart_complete() + * won't be called in this case). + */ + clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); + mvm->ucode_loaded = false; } -- cgit v0.10.2 From 03d6c3b0fa4f5f0379cede079ec828a6c999fe43 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 3 Dec 2014 10:39:07 +0200 Subject: iwlwifi: pcie: re-ACK all interrupts after device reset When we reset the device, the CSR_INT gets cleared as well as CSR_INT_MASK. Meaning that we shouldn't get any interrupt but, due to a hardware bug, recent devices will keep sending interrupts. This leads to an interrupt storm while stopping the device. The way to fix this is to ACK all the interrupts after the device is reset so that the value of CSR_INT will stay 0xffffffff. Fixes: 522713c81e4e ("iwlwifi: pcie: properly reset the device") Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 5d79a1f..d151af3 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -1012,16 +1012,21 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) /* Stop the device, and put it in low power state */ iwl_pcie_apm_stop(trans); - /* Upon stop, the APM issues an interrupt if HW RF kill is set. - * Clean again the interrupt here + /* stop and reset the on-board processor */ + iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); + udelay(20); + + /* + * Upon stop, the APM issues an interrupt if HW RF kill is set. + * This is a bug in certain verions of the hardware. + * Certain devices also keep sending HW RF kill interrupt all + * the time, unless the interrupt is ACKed even if the interrupt + * should be masked. Re-ACK all the interrupts here. */ spin_lock(&trans_pcie->irq_lock); iwl_disable_interrupts(trans); spin_unlock(&trans_pcie->irq_lock); - /* stop and reset the on-board processor */ - iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); - udelay(20); /* clear all status bits */ clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); -- cgit v0.10.2 From 31a5a09c1c5c888181e86a951a9a6c3ec27f7642 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 3 Dec 2014 08:25:44 +0200 Subject: iwlwifi: don't double free a pointer if no FW was found In the very unlikely case in which no firmware could be, found. the same pointer was freed twice. Fix that. Fixes: 490fefebb6db ("iwlwifi: define the .ucode file format for debug") Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index 38de151..850b85a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -1323,10 +1323,10 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) try_again: /* try next, if any */ - kfree(pieces); release_firmware(ucode_raw); if (iwl_request_firmware(drv, false)) goto out_unbind; + kfree(pieces); return; out_free_fw: -- cgit v0.10.2 From 55fd1ce820f461b77919a1997ba8285652219024 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 2 Dec 2014 22:09:55 +0200 Subject: iwlwifi: add new device IDs for 3165 A few device IDs were added, reflect this change in the driver. Cc; [3.13+] Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c index 3ee8e38..2f0c4b1 100644 --- a/drivers/net/wireless/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/iwlwifi/pcie/drv.c @@ -367,7 +367,11 @@ static const struct pci_device_id iwl_hw_card_ids[] = { /* 3165 Series */ {IWL_PCI_DEVICE(0x3165, 0x4010, iwl3165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x3165, 0x4012, iwl3165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x3165, 0x4110, iwl3165_2ac_cfg)}, {IWL_PCI_DEVICE(0x3165, 0x4210, iwl3165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x3165, 0x4410, iwl3165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x3165, 0x4510, iwl3165_2ac_cfg)}, /* 7265 Series */ {IWL_PCI_DEVICE(0x095A, 0x5010, iwl7265_2ac_cfg)}, -- cgit v0.10.2 From baa21e834941ee5fbe4bd421c871f7c0c5f9a086 Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Tue, 2 Dec 2014 14:28:45 +0200 Subject: iwlwifi: pcie: limit fw chunk sizes given to fh New FW has chunks that are larger than the size limit of the FH's DMA. To make sure we don't crash it - actively limit the max size of each chunk. Signed-off-by: Liad Kaufman Reviewed-by: Johannes Berg Signed-off-by: Emmanuel Grumbach diff --git a/drivers/net/wireless/iwlwifi/iwl-fh.h b/drivers/net/wireless/iwlwifi/iwl-fh.h index 9564ae1..1f7f15e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fh.h +++ b/drivers/net/wireless/iwlwifi/iwl-fh.h @@ -310,6 +310,7 @@ static inline unsigned int FH_MEM_CBBC_QUEUE(unsigned int chnl) #define FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE (0x01000000) #define FH_MEM_TFDIB_REG1_ADDR_BITSHIFT 28 +#define FH_MEM_TB_MAX_LENGTH (0x00020000) /* TFDB Area - TFDs buffer table */ #define FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK (0xFFFFFFFF) diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index d151af3..523fe0c 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -614,7 +614,7 @@ static int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num, { u8 *v_addr; dma_addr_t p_addr; - u32 offset, chunk_sz = section->len; + u32 offset, chunk_sz = min_t(u32, FH_MEM_TB_MAX_LENGTH, section->len); int ret = 0; IWL_DEBUG_FW(trans, "[%d] uCode section being loaded...\n", -- cgit v0.10.2 From 9235d09873316d602937b5d45c431fb653f3aed8 Mon Sep 17 00:00:00 2001 From: Sachin Prabhu Date: Tue, 9 Dec 2014 17:37:00 +0000 Subject: Convert MessageID in smb2_hdr to LE We have encountered failures when When testing smb2 mounts on ppc64 machines when using both Samba as well as Windows 2012. On poking around, the problem was determined to be caused by the high endian MessageID passed in the header for smb2. On checking the corresponding MID for smb1 is converted to LE before being sent on the wire. We have tested this patch successfully on a ppc64 machine. Signed-off-by: Sachin Prabhu diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 6e13911..22b289a 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -661,16 +661,16 @@ set_credits(struct TCP_Server_Info *server, const int val) server->ops->set_credits(server, val); } -static inline __u64 +static inline __le64 get_next_mid64(struct TCP_Server_Info *server) { - return server->ops->get_next_mid(server); + return cpu_to_le64(server->ops->get_next_mid(server)); } static inline __le16 get_next_mid(struct TCP_Server_Info *server) { - __u16 mid = get_next_mid64(server); + __u16 mid = server->ops->get_next_mid(server); /* * The value in the SMB header should be little endian for easy * on-the-wire decoding. diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c index f1cefc9..689f035 100644 --- a/fs/cifs/smb2misc.c +++ b/fs/cifs/smb2misc.c @@ -32,12 +32,14 @@ static int check_smb2_hdr(struct smb2_hdr *hdr, __u64 mid) { + __u64 wire_mid = le64_to_cpu(hdr->MessageId); + /* * Make sure that this really is an SMB, that it is a response, * and that the message ids match. */ if ((*(__le32 *)hdr->ProtocolId == SMB2_PROTO_NUMBER) && - (mid == hdr->MessageId)) { + (mid == wire_mid)) { if (hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) return 0; else { @@ -51,11 +53,11 @@ check_smb2_hdr(struct smb2_hdr *hdr, __u64 mid) if (*(__le32 *)hdr->ProtocolId != SMB2_PROTO_NUMBER) cifs_dbg(VFS, "Bad protocol string signature header %x\n", *(unsigned int *) hdr->ProtocolId); - if (mid != hdr->MessageId) + if (mid != wire_mid) cifs_dbg(VFS, "Mids do not match: %llu and %llu\n", - mid, hdr->MessageId); + mid, wire_mid); } - cifs_dbg(VFS, "Bad SMB detected. The Mid=%llu\n", hdr->MessageId); + cifs_dbg(VFS, "Bad SMB detected. The Mid=%llu\n", wire_mid); return 1; } @@ -95,7 +97,7 @@ smb2_check_message(char *buf, unsigned int length) { struct smb2_hdr *hdr = (struct smb2_hdr *)buf; struct smb2_pdu *pdu = (struct smb2_pdu *)hdr; - __u64 mid = hdr->MessageId; + __u64 mid = le64_to_cpu(hdr->MessageId); __u32 len = get_rfc1002_length(buf); __u32 clc_len; /* calculated length */ int command; diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 93fd058..96b5d40 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -176,10 +176,11 @@ smb2_find_mid(struct TCP_Server_Info *server, char *buf) { struct mid_q_entry *mid; struct smb2_hdr *hdr = (struct smb2_hdr *)buf; + __u64 wire_mid = le64_to_cpu(hdr->MessageId); spin_lock(&GlobalMid_Lock); list_for_each_entry(mid, &server->pending_mid_q, qhead) { - if ((mid->mid == hdr->MessageId) && + if ((mid->mid == wire_mid) && (mid->mid_state == MID_REQUEST_SUBMITTED) && (mid->command == hdr->Command)) { spin_unlock(&GlobalMid_Lock); diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index ce85847..70867d5 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h @@ -110,7 +110,7 @@ struct smb2_hdr { __le16 CreditRequest; /* CreditResponse */ __le32 Flags; __le32 NextCommand; - __u64 MessageId; /* opaque - so can stay little endian */ + __le64 MessageId; __le32 ProcessId; __u32 TreeId; /* opaque - so do not make little endian */ __u64 SessionId; /* opaque - so do not make little endian */ diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c index 5111e72..d4c5b6f 100644 --- a/fs/cifs/smb2transport.c +++ b/fs/cifs/smb2transport.c @@ -490,7 +490,7 @@ smb2_mid_entry_alloc(const struct smb2_hdr *smb_buffer, return temp; else { memset(temp, 0, sizeof(struct mid_q_entry)); - temp->mid = smb_buffer->MessageId; /* always LE */ + temp->mid = le64_to_cpu(smb_buffer->MessageId); temp->pid = current->pid; temp->command = smb_buffer->Command; /* Always LE */ temp->when_alloc = jiffies; -- cgit v0.10.2 From 97c7134ae22fbd2b8730211f9d4d4517264a8efe Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Mon, 10 Nov 2014 13:09:23 -0800 Subject: Fix signed/unsigned pointer warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 2ae83bf93882d1 ("[CIFS] Fix setting time before epoch (negative time values)") changed "u64 t" to "s64 t", which makes do_div() complain about a pointer signedness mismatch: CC fs/cifs/netmisc.o In file included from ./arch/mips/include/asm/div64.h:12:0, from include/linux/kernel.h:124, from include/linux/list.h:8, from include/linux/wait.h:6, from include/linux/net.h:23, from fs/cifs/netmisc.c:25: fs/cifs/netmisc.c: In function ‘cifs_NTtimeToUnix’: include/asm-generic/div64.h:43:28: warning: comparison of distinct pointer types lacks a cast [enabled by default] (void)(((typeof((n)) *)0) == ((uint64_t *)0)); \ ^ fs/cifs/netmisc.c:941:22: note: in expansion of macro ‘do_div’ ts.tv_nsec = (long)do_div(t, 10000000) * 100; Introduce a temporary "u64 abs_t" variable to fix this. Signed-off-by: Kevin Cernekee Signed-off-by: Steve French diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c index b333ff6..abae6dd 100644 --- a/fs/cifs/netmisc.c +++ b/fs/cifs/netmisc.c @@ -926,6 +926,7 @@ cifs_NTtimeToUnix(__le64 ntutc) /* Subtract the NTFS time offset, then convert to 1s intervals. */ s64 t = le64_to_cpu(ntutc) - NTFS_TIME_OFFSET; + u64 abs_t; /* * Unfortunately can not use normal 64 bit division on 32 bit arch, but @@ -933,13 +934,14 @@ cifs_NTtimeToUnix(__le64 ntutc) * to special case them */ if (t < 0) { - t = -t; - ts.tv_nsec = (long)(do_div(t, 10000000) * 100); + abs_t = -t; + ts.tv_nsec = (long)(do_div(abs_t, 10000000) * 100); ts.tv_nsec = -ts.tv_nsec; - ts.tv_sec = -t; + ts.tv_sec = -abs_t; } else { - ts.tv_nsec = (long)do_div(t, 10000000) * 100; - ts.tv_sec = t; + abs_t = t; + ts.tv_nsec = (long)do_div(abs_t, 10000000) * 100; + ts.tv_sec = abs_t; } return ts; -- cgit v0.10.2 From 78e68d36dab31c1f41885f757195fdfb29fc3075 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 15 Dec 2014 18:59:27 +0200 Subject: drm/i915: move RPS PM_IER enabling to gen6_enable_rps_interrupts Paulo noticed that we don't enable RPS interrupts via PM_IER in gen6_enable_rps_interrupts(). This wasn't a problem so far, since the only place we disabled RPS interrupts was during system/runtime suspend and after that we reenable all interrupts in the IRQ pre/postinstall hooks. In the next patch we'll disable/reenable RPS interrupts during GPU reset too, but not call IRQ uninstall, pre/postinstall hooks, so there the above wouldn't work. The logical place for programming PM_IER is gen6_enable_rps_interrupts() and this also makes the function more symmetric with gen6_disable_rps_interrupts(), so move the programming there from the postinstall hooks. Note that these changes don't affect the ILK RPS interrupt code, which could be sanitized in a similar way. But that can be done as a follow-up. Credits-to: Paulo Zanoni Signed-off-by: Imre Deak Reviewed-by: Paulo Zanoni Signed-off-by: Jani Nikula diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 8bab2ab..996c293 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -281,10 +281,14 @@ void gen6_enable_rps_interrupts(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; spin_lock_irq(&dev_priv->irq_lock); + WARN_ON(dev_priv->rps.pm_iir); WARN_ON(I915_READ(gen6_pm_iir(dev_priv)) & dev_priv->pm_rps_events); dev_priv->rps.interrupts_enabled = true; + I915_WRITE(gen6_pm_ier(dev_priv), I915_READ(gen6_pm_ier(dev_priv)) | + dev_priv->pm_rps_events); gen6_enable_pm_irq(dev_priv, dev_priv->pm_rps_events); + spin_unlock_irq(&dev_priv->irq_lock); } @@ -3307,8 +3311,10 @@ static void gen5_gt_irq_postinstall(struct drm_device *dev) GEN5_IRQ_INIT(GT, dev_priv->gt_irq_mask, gt_irqs); if (INTEL_INFO(dev)->gen >= 6) { - pm_irqs |= dev_priv->pm_rps_events; - + /* + * RPS interrupts will get enabled/disabled on demand when RPS + * itself is enabled/disabled. + */ if (HAS_VEBOX(dev)) pm_irqs |= PM_VEBOX_USER_INTERRUPT; @@ -3520,7 +3526,11 @@ static void gen8_gt_irq_postinstall(struct drm_i915_private *dev_priv) dev_priv->pm_irq_mask = 0xffffffff; GEN8_IRQ_INIT_NDX(GT, 0, ~gt_interrupts[0], gt_interrupts[0]); GEN8_IRQ_INIT_NDX(GT, 1, ~gt_interrupts[1], gt_interrupts[1]); - GEN8_IRQ_INIT_NDX(GT, 2, dev_priv->pm_irq_mask, dev_priv->pm_rps_events); + /* + * RPS interrupts will get enabled/disabled on demand when RPS itself + * is enabled/disabled. + */ + GEN8_IRQ_INIT_NDX(GT, 2, dev_priv->pm_irq_mask, 0); GEN8_IRQ_INIT_NDX(GT, 3, ~gt_interrupts[3], gt_interrupts[3]); } -- cgit v0.10.2 From dbea3cea69508e9d548ed4a6be13de35492e5d15 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 15 Dec 2014 18:59:28 +0200 Subject: drm/i915: sanitize RPS resetting during GPU reset Atm, we don't disable RPS interrupts and related work items before resetting the GPU. This may interfere with the following GPU initialization and cause RPS interrupts to show up in PM_IIR too early before calling gen6_enable_rps_interrupts() (triggering a WARN there). Solve this by disabling RPS interrupts and flushing any related work items before resetting the GPU. v2: - split out the common parts of the gt suspend and the new gt reset functions (Paulo) v3: - remove the check for UMS, it's a NOP nowadays (Daniel) Reported-by: He, Shuang Testcase: igt/gem_reset_stats/ban-render Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=86644 Signed-off-by: Imre Deak Reviewed-by: Paulo Zanoni Signed-off-by: Jani Nikula diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index f990ab4..fc8cfdd 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -811,6 +811,8 @@ int i915_reset(struct drm_device *dev) if (!i915.reset) return 0; + intel_reset_gt_powersave(dev); + mutex_lock(&dev->struct_mutex); i915_gem_reset(dev); @@ -880,7 +882,7 @@ int i915_reset(struct drm_device *dev) * of re-init after reset. */ if (INTEL_INFO(dev)->gen > 5) - intel_reset_gt_powersave(dev); + intel_enable_gt_powersave(dev); } else { mutex_unlock(&dev->struct_mutex); } diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 1f4b56e..964b28e 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -6191,6 +6191,20 @@ void intel_cleanup_gt_powersave(struct drm_device *dev) valleyview_cleanup_gt_powersave(dev); } +static void gen6_suspend_rps(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + flush_delayed_work(&dev_priv->rps.delayed_resume_work); + + /* + * TODO: disable RPS interrupts on GEN9+ too once RPS support + * is added for it. + */ + if (INTEL_INFO(dev)->gen < 9) + gen6_disable_rps_interrupts(dev); +} + /** * intel_suspend_gt_powersave - suspend PM work and helper threads * @dev: drm device @@ -6206,14 +6220,7 @@ void intel_suspend_gt_powersave(struct drm_device *dev) if (INTEL_INFO(dev)->gen < 6) return; - flush_delayed_work(&dev_priv->rps.delayed_resume_work); - - /* - * TODO: disable RPS interrupts on GEN9+ too once RPS support - * is added for it. - */ - if (INTEL_INFO(dev)->gen < 9) - gen6_disable_rps_interrupts(dev); + gen6_suspend_rps(dev); /* Force GPU to min freq during suspend */ gen6_rps_idle(dev_priv); @@ -6316,8 +6323,11 @@ void intel_reset_gt_powersave(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + if (INTEL_INFO(dev)->gen < 6) + return; + + gen6_suspend_rps(dev); dev_priv->rps.enabled = false; - intel_enable_gt_powersave(dev); } static void ibx_init_clock_gating(struct drm_device *dev) -- cgit v0.10.2 From 015760563ec77bf17cec712fa94afdf53b285287 Mon Sep 17 00:00:00 2001 From: Hisashi Nakamura Date: Mon, 15 Dec 2014 23:01:11 +0900 Subject: spi: sh-msiof: Add runtime PM lock in initializing SH-MSIOF driver is enabled autosuspend API of spi framework. But autosuspend framework doesn't work during initializing. So runtime PM lock is added in SH-MSIOF driver initializing. Fixes: e2a0ba547ba31c (spi: sh-msiof: Convert to spi core auto_runtime_pm framework) Signed-off-by: Hisashi Nakamura Signed-off-by: Yoshihiro Kaneko Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 3f36540..1405293 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -480,6 +480,8 @@ static int sh_msiof_spi_setup(struct spi_device *spi) struct device_node *np = spi->master->dev.of_node; struct sh_msiof_spi_priv *p = spi_master_get_devdata(spi->master); + pm_runtime_get_sync(&p->pdev->dev); + if (!np) { /* * Use spi->controller_data for CS (same strategy as spi_gpio), @@ -498,6 +500,9 @@ static int sh_msiof_spi_setup(struct spi_device *spi) if (spi->cs_gpio >= 0) gpio_set_value(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH)); + + pm_runtime_put_sync(&p->pdev->dev); + return 0; } -- cgit v0.10.2 From 97d86e07b71643086a6d22a60efae2fb095fa82a Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 14 Nov 2014 15:57:09 -0800 Subject: Input: gpio_keys - allow separating gpio and irq in device tree This change allows specify interrupt for buttons separately form gpio, potentially allowing to form several "clusters" of buttons on different interrupts. Button defined without both gpio and irq in device tree is a hared error instead of a warning now. Tested-by: Andy Shevchenko Reviewed-by: Linus Walleij Signed-off-by: Dmitry Torokhov diff --git a/Documentation/devicetree/bindings/input/gpio-keys.txt b/Documentation/devicetree/bindings/input/gpio-keys.txt index a4a38fc..44b7057 100644 --- a/Documentation/devicetree/bindings/input/gpio-keys.txt +++ b/Documentation/devicetree/bindings/input/gpio-keys.txt @@ -10,12 +10,13 @@ Optional properties: Each button (key) is represented as a sub-node of "gpio-keys": Subnode properties: + - gpios: OF device-tree gpio specification. + - interrupts: the interrupt line for that input. - label: Descriptive name of the key. - linux,code: Keycode to emit. -Required mutual exclusive subnode-properties: - - gpios: OF device-tree gpio specification. - - interrupts: the interrupt line for that input +Note that either "interrupts" or "gpios" properties can be omitted, but not +both at the same time. Specifying both properties is allowed. Optional subnode-properties: - linux,input-type: Specify event type this button/key generates. @@ -23,6 +24,9 @@ Optional subnode-properties: - debounce-interval: Debouncing interval time in milliseconds. If not specified defaults to 5. - gpio-key,wakeup: Boolean, button can wake-up the system. + - linux,can-disable: Boolean, indicates that button is connected + to dedicated (not shared) interrupt which can be disabled to + suppress events from the button. Example nodes: diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index f44f05b..a5ece3f 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -470,15 +470,19 @@ static int gpio_keys_setup_key(struct platform_device *pdev, button->debounce_interval; } - irq = gpio_to_irq(button->gpio); - if (irq < 0) { - error = irq; - dev_err(dev, - "Unable to get irq number for GPIO %d, error %d\n", - button->gpio, error); - return error; + if (button->irq) { + bdata->irq = button->irq; + } else { + irq = gpio_to_irq(button->gpio); + if (irq < 0) { + error = irq; + dev_err(dev, + "Unable to get irq number for GPIO %d, error %d\n", + button->gpio, error); + return error; + } + bdata->irq = irq; } - bdata->irq = irq; INIT_WORK(&bdata->work, gpio_keys_gpio_work_func); setup_timer(&bdata->timer, @@ -618,33 +622,30 @@ gpio_keys_get_devtree_pdata(struct device *dev) i = 0; for_each_child_of_node(node, pp) { - int gpio = -1; enum of_gpio_flags flags; button = &pdata->buttons[i++]; - if (!of_find_property(pp, "gpios", NULL)) { - button->irq = irq_of_parse_and_map(pp, 0); - if (button->irq == 0) { - i--; - pdata->nbuttons--; - dev_warn(dev, "Found button without gpios or irqs\n"); - continue; - } - } else { - gpio = of_get_gpio_flags(pp, 0, &flags); - if (gpio < 0) { - error = gpio; + button->gpio = of_get_gpio_flags(pp, 0, &flags); + if (button->gpio < 0) { + error = button->gpio; + if (error != -ENOENT) { if (error != -EPROBE_DEFER) dev_err(dev, "Failed to get gpio flags, error: %d\n", error); return ERR_PTR(error); } + } else { + button->active_low = flags & OF_GPIO_ACTIVE_LOW; } - button->gpio = gpio; - button->active_low = flags & OF_GPIO_ACTIVE_LOW; + button->irq = irq_of_parse_and_map(pp, 0); + + if (!gpio_is_valid(button->gpio) && !button->irq) { + dev_err(dev, "Found button without gpios or irqs\n"); + return ERR_PTR(-EINVAL); + } if (of_property_read_u32(pp, "linux,code", &button->code)) { dev_err(dev, "Button without keycode: 0x%x\n", @@ -659,6 +660,8 @@ gpio_keys_get_devtree_pdata(struct device *dev) button->wakeup = !!of_get_property(pp, "gpio-key,wakeup", NULL); + button->can_disable = !!of_get_property(pp, "linux,can-disable", NULL); + if (of_property_read_u32(pp, "debounce-interval", &button->debounce_interval)) button->debounce_interval = 5; -- cgit v0.10.2 From 8ed92556761e1f383d28215d6de92fe4ada35001 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 14 Nov 2014 17:32:01 -0800 Subject: Input: gpio_keys - replace timer and workqueue with delayed workqueue We do not need to roll our own implementation of delayed work now that we have proper implementation of mod_delayed_work. For interrupt-only driven buttons we retain the timer, but we rename it to release_timer to better reflect its purpose. Tested-by: Andy Shevchenko Reviewed-by: Linus Walleij Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index a5ece3f..eefd976 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -35,9 +35,13 @@ struct gpio_button_data { const struct gpio_keys_button *button; struct input_dev *input; - struct timer_list timer; - struct work_struct work; - unsigned int timer_debounce; /* in msecs */ + + struct timer_list release_timer; + unsigned int release_delay; /* in msecs, for IRQ-only buttons */ + + struct delayed_work work; + unsigned int software_debounce; /* in msecs, for GPIO-driven buttons */ + unsigned int irq; spinlock_t lock; bool disabled; @@ -116,11 +120,14 @@ static void gpio_keys_disable_button(struct gpio_button_data *bdata) { if (!bdata->disabled) { /* - * Disable IRQ and possible debouncing timer. + * Disable IRQ and associated timer/work structure. */ disable_irq(bdata->irq); - if (bdata->timer_debounce) - del_timer_sync(&bdata->timer); + + if (gpio_is_valid(bdata->button->gpio)) + cancel_delayed_work_sync(&bdata->work); + else + del_timer_sync(&bdata->release_timer); bdata->disabled = true; } @@ -343,7 +350,7 @@ static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata) static void gpio_keys_gpio_work_func(struct work_struct *work) { struct gpio_button_data *bdata = - container_of(work, struct gpio_button_data, work); + container_of(work, struct gpio_button_data, work.work); gpio_keys_gpio_report_event(bdata); @@ -351,13 +358,6 @@ static void gpio_keys_gpio_work_func(struct work_struct *work) pm_relax(bdata->input->dev.parent); } -static void gpio_keys_gpio_timer(unsigned long _data) -{ - struct gpio_button_data *bdata = (struct gpio_button_data *)_data; - - schedule_work(&bdata->work); -} - static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id) { struct gpio_button_data *bdata = dev_id; @@ -366,11 +366,10 @@ static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id) if (bdata->button->wakeup) pm_stay_awake(bdata->input->dev.parent); - if (bdata->timer_debounce) - mod_timer(&bdata->timer, - jiffies + msecs_to_jiffies(bdata->timer_debounce)); - else - schedule_work(&bdata->work); + + mod_delayed_work(system_wq, + &bdata->work, + msecs_to_jiffies(bdata->software_debounce)); return IRQ_HANDLED; } @@ -408,7 +407,7 @@ static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id) input_event(input, EV_KEY, button->code, 1); input_sync(input); - if (!bdata->timer_debounce) { + if (!bdata->release_delay) { input_event(input, EV_KEY, button->code, 0); input_sync(input); goto out; @@ -417,9 +416,9 @@ static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id) bdata->key_pressed = true; } - if (bdata->timer_debounce) - mod_timer(&bdata->timer, - jiffies + msecs_to_jiffies(bdata->timer_debounce)); + if (bdata->release_delay) + mod_timer(&bdata->release_timer, + jiffies + msecs_to_jiffies(bdata->release_delay)); out: spin_unlock_irqrestore(&bdata->lock, flags); return IRQ_HANDLED; @@ -429,10 +428,10 @@ static void gpio_keys_quiesce_key(void *data) { struct gpio_button_data *bdata = data; - if (bdata->timer_debounce) - del_timer_sync(&bdata->timer); - - cancel_work_sync(&bdata->work); + if (gpio_is_valid(bdata->button->gpio)) + cancel_delayed_work_sync(&bdata->work); + else + del_timer_sync(&bdata->release_timer); } static int gpio_keys_setup_key(struct platform_device *pdev, @@ -466,7 +465,7 @@ static int gpio_keys_setup_key(struct platform_device *pdev, button->debounce_interval * 1000); /* use timer if gpiolib doesn't provide debounce */ if (error < 0) - bdata->timer_debounce = + bdata->software_debounce = button->debounce_interval; } @@ -484,9 +483,7 @@ static int gpio_keys_setup_key(struct platform_device *pdev, bdata->irq = irq; } - INIT_WORK(&bdata->work, gpio_keys_gpio_work_func); - setup_timer(&bdata->timer, - gpio_keys_gpio_timer, (unsigned long)bdata); + INIT_DELAYED_WORK(&bdata->work, gpio_keys_gpio_work_func); isr = gpio_keys_gpio_isr; irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; @@ -503,8 +500,8 @@ static int gpio_keys_setup_key(struct platform_device *pdev, return -EINVAL; } - bdata->timer_debounce = button->debounce_interval; - setup_timer(&bdata->timer, + bdata->release_delay = button->debounce_interval; + setup_timer(&bdata->release_timer, gpio_keys_irq_timer, (unsigned long)bdata); isr = gpio_keys_irq_isr; @@ -514,7 +511,7 @@ static int gpio_keys_setup_key(struct platform_device *pdev, input_set_capability(input, button->type ?: EV_KEY, button->code); /* - * Install custom action to cancel debounce timer and + * Install custom action to cancel release timer and * workqueue item. */ error = devm_add_action(&pdev->dev, gpio_keys_quiesce_key, bdata); -- cgit v0.10.2 From 189387f9e0affb491b8c8833b6afd9623ab7f26a Mon Sep 17 00:00:00 2001 From: Asaf Vertz Date: Sat, 13 Dec 2014 10:59:04 -0800 Subject: Input: edt-ft5x06 - fixed a macro coding style issue Fixed a coding style error, macros with complex values should be enclosed in parentheses. Signed-off-by: Asaf Vertz Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 2e4d909..dcc68ef 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -850,9 +850,11 @@ static int edt_ft5x06_ts_identify(struct i2c_client *client, } #define EDT_ATTR_CHECKSET(name, reg) \ +do { \ if (pdata->name >= edt_ft5x06_attr_##name.limit_low && \ pdata->name <= edt_ft5x06_attr_##name.limit_high) \ - edt_ft5x06_register_write(tsdata, reg, pdata->name) + edt_ft5x06_register_write(tsdata, reg, pdata->name); \ +} while (0) #define EDT_GET_PROP(name, reg) { \ u32 val; \ -- cgit v0.10.2 From baf332c0f1cede26e9c2af6276b36b4c3a36e34a Mon Sep 17 00:00:00 2001 From: Anshul Garg Date: Sat, 13 Dec 2014 11:58:23 -0800 Subject: Input: optimize events_per_packet count calculation This patch avoids unnecessary operations while estimating events per packet for an input device when event type is not set. Signed-off-by: Anshul Garg Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/input.c b/drivers/input/input.c index 04217c2..213e3a1 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -1974,18 +1974,22 @@ static unsigned int input_estimate_events_per_packet(struct input_dev *dev) events = mt_slots + 1; /* count SYN_MT_REPORT and SYN_REPORT */ - for (i = 0; i < ABS_CNT; i++) { - if (test_bit(i, dev->absbit)) { - if (input_is_mt_axis(i)) - events += mt_slots; - else - events++; + if (test_bit(EV_ABS, dev->evbit)) { + for (i = 0; i < ABS_CNT; i++) { + if (test_bit(i, dev->absbit)) { + if (input_is_mt_axis(i)) + events += mt_slots; + else + events++; + } } } - for (i = 0; i < REL_CNT; i++) - if (test_bit(i, dev->relbit)) - events++; + if (test_bit(EV_REL, dev->evbit)) { + for (i = 0; i < REL_CNT; i++) + if (test_bit(i, dev->relbit)) + events++; + } /* Make room for KEY and MSC events */ events += 7; -- cgit v0.10.2 From 80e1dd82be59d247e899d8ce29389f84ed828994 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 3 Nov 2014 16:48:47 -0800 Subject: mfd: stmpe: add pull up/down register offsets for STMPE This adds the register offsets for pull up/down for the STMPE 1601, 1801 and 24xx expanders. This is used to bias GPIO lines and keypad lines. Signed-off-by: Linus Walleij Acked-by: Lee Jones Signed-off-by: Dmitry Torokhov diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index 02a17c3..2d29d17 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -519,6 +519,7 @@ static const u8 stmpe1601_regs[] = { [STMPE_IDX_GPDR_LSB] = STMPE1601_REG_GPIO_SET_DIR_LSB, [STMPE_IDX_GPRER_LSB] = STMPE1601_REG_GPIO_RE_LSB, [STMPE_IDX_GPFER_LSB] = STMPE1601_REG_GPIO_FE_LSB, + [STMPE_IDX_GPPUR_LSB] = STMPE1601_REG_GPIO_PU_LSB, [STMPE_IDX_GPAFR_U_MSB] = STMPE1601_REG_GPIO_AF_U_MSB, [STMPE_IDX_IEGPIOR_LSB] = STMPE1601_REG_INT_EN_GPIO_MASK_LSB, [STMPE_IDX_ISGPIOR_MSB] = STMPE1601_REG_INT_STA_GPIO_MSB, @@ -667,6 +668,7 @@ static const u8 stmpe1801_regs[] = { [STMPE_IDX_GPDR_LSB] = STMPE1801_REG_GPIO_SET_DIR_LOW, [STMPE_IDX_GPRER_LSB] = STMPE1801_REG_GPIO_RE_LOW, [STMPE_IDX_GPFER_LSB] = STMPE1801_REG_GPIO_FE_LOW, + [STMPE_IDX_GPPUR_LSB] = STMPE1801_REG_GPIO_PULL_UP_LOW, [STMPE_IDX_IEGPIOR_LSB] = STMPE1801_REG_INT_EN_GPIO_MASK_LOW, [STMPE_IDX_ISGPIOR_LSB] = STMPE1801_REG_INT_STA_GPIO_LOW, }; @@ -750,6 +752,8 @@ static const u8 stmpe24xx_regs[] = { [STMPE_IDX_GPDR_LSB] = STMPE24XX_REG_GPDR_LSB, [STMPE_IDX_GPRER_LSB] = STMPE24XX_REG_GPRER_LSB, [STMPE_IDX_GPFER_LSB] = STMPE24XX_REG_GPFER_LSB, + [STMPE_IDX_GPPUR_LSB] = STMPE24XX_REG_GPPUR_LSB, + [STMPE_IDX_GPPDR_LSB] = STMPE24XX_REG_GPPDR_LSB, [STMPE_IDX_GPAFR_U_MSB] = STMPE24XX_REG_GPAFR_U_MSB, [STMPE_IDX_IEGPIOR_LSB] = STMPE24XX_REG_IEGPIOR_LSB, [STMPE_IDX_ISGPIOR_MSB] = STMPE24XX_REG_ISGPIOR_MSB, diff --git a/drivers/mfd/stmpe.h b/drivers/mfd/stmpe.h index 2d045f2..e00710b 100644 --- a/drivers/mfd/stmpe.h +++ b/drivers/mfd/stmpe.h @@ -188,6 +188,7 @@ int stmpe_remove(struct stmpe *stmpe); #define STMPE1601_REG_GPIO_ED_MSB 0x8A #define STMPE1601_REG_GPIO_RE_LSB 0x8D #define STMPE1601_REG_GPIO_FE_LSB 0x8F +#define STMPE1601_REG_GPIO_PU_LSB 0x91 #define STMPE1601_REG_GPIO_AF_U_MSB 0x92 #define STMPE1601_SYS_CTRL_ENABLE_GPIO (1 << 3) @@ -276,6 +277,8 @@ int stmpe_remove(struct stmpe *stmpe); #define STMPE24XX_REG_GPEDR_MSB 0x8C #define STMPE24XX_REG_GPRER_LSB 0x91 #define STMPE24XX_REG_GPFER_LSB 0x94 +#define STMPE24XX_REG_GPPUR_LSB 0x97 +#define STMPE24XX_REG_GPPDR_LSB 0x9a #define STMPE24XX_REG_GPAFR_U_MSB 0x9B #define STMPE24XX_SYS_CTRL_ENABLE_GPIO (1 << 3) diff --git a/include/linux/mfd/stmpe.h b/include/linux/mfd/stmpe.h index 575a86c..cc0deb7 100644 --- a/include/linux/mfd/stmpe.h +++ b/include/linux/mfd/stmpe.h @@ -50,6 +50,8 @@ enum { STMPE_IDX_GPEDR_MSB, STMPE_IDX_GPRER_LSB, STMPE_IDX_GPFER_LSB, + STMPE_IDX_GPPUR_LSB, + STMPE_IDX_GPPDR_LSB, STMPE_IDX_GPAFR_U_MSB, STMPE_IDX_IEGPIOR_LSB, STMPE_IDX_ISGPIOR_LSB, -- cgit v0.10.2 From a4164863e150c4991d2ac965e3fc52f9d8df3d7e Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 3 Nov 2014 16:51:26 -0800 Subject: Input: stmpe - enforce device tree only mode The STMPE keypad controller is only used with device tree configured systems, so force the configuration to come from device tree only, and now actually get the rows and cols from the device tree too. Signed-off-by: Linus Walleij Acked-by: Lee Jones Signed-off-by: Dmitry Torokhov diff --git a/Documentation/devicetree/bindings/input/stmpe-keypad.txt b/Documentation/devicetree/bindings/input/stmpe-keypad.txt index 1b97222..12bb771 100644 --- a/Documentation/devicetree/bindings/input/stmpe-keypad.txt +++ b/Documentation/devicetree/bindings/input/stmpe-keypad.txt @@ -8,6 +8,8 @@ Optional properties: - debounce-interval : Debouncing interval time in milliseconds - st,scan-count : Scanning cycles elapsed before key data is updated - st,no-autorepeat : If specified device will not autorepeat + - keypad,num-rows : See ./matrix-keymap.txt + - keypad,num-columns : See ./matrix-keymap.txt Example: diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 96ee26c..a5d9b3f 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -559,6 +559,7 @@ config KEYBOARD_SH_KEYSC config KEYBOARD_STMPE tristate "STMPE keypad support" depends on MFD_STMPE + depends on OF select INPUT_MATRIXKMAP help Say Y here if you want to use the keypad controller on STMPE I/O diff --git a/drivers/input/keyboard/stmpe-keypad.c b/drivers/input/keyboard/stmpe-keypad.c index c6727dd..8d1e7af 100644 --- a/drivers/input/keyboard/stmpe-keypad.c +++ b/drivers/input/keyboard/stmpe-keypad.c @@ -45,7 +45,7 @@ #define STMPE_KEYPAD_MAX_ROWS 8 #define STMPE_KEYPAD_MAX_COLS 8 #define STMPE_KEYPAD_ROW_SHIFT 3 -#define STMPE_KEYPAD_KEYMAP_SIZE \ +#define STMPE_KEYPAD_KEYMAP_MAX_SIZE \ (STMPE_KEYPAD_MAX_ROWS * STMPE_KEYPAD_MAX_COLS) /** @@ -99,16 +99,30 @@ static const struct stmpe_keypad_variant stmpe_keypad_variants[] = { }, }; +/** + * struct stmpe_keypad - STMPE keypad state container + * @stmpe: pointer to parent STMPE device + * @input: spawned input device + * @variant: STMPE variant + * @debounce_ms: debounce interval, in ms. Maximum is + * %STMPE_KEYPAD_MAX_DEBOUNCE. + * @scan_count: number of key scanning cycles to confirm key data. + * Maximum is %STMPE_KEYPAD_MAX_SCAN_COUNT. + * @no_autorepeat: disable key autorepeat + * @rows: bitmask for the rows + * @cols: bitmask for the columns + * @keymap: the keymap + */ struct stmpe_keypad { struct stmpe *stmpe; struct input_dev *input; const struct stmpe_keypad_variant *variant; - const struct stmpe_keypad_platform_data *plat; - + unsigned int debounce_ms; + unsigned int scan_count; + bool no_autorepeat; unsigned int rows; unsigned int cols; - - unsigned short keymap[STMPE_KEYPAD_KEYMAP_SIZE]; + unsigned short keymap[STMPE_KEYPAD_KEYMAP_MAX_SIZE]; }; static int stmpe_keypad_read_data(struct stmpe_keypad *keypad, u8 *data) @@ -208,15 +222,14 @@ static int stmpe_keypad_altfunc_init(struct stmpe_keypad *keypad) static int stmpe_keypad_chip_init(struct stmpe_keypad *keypad) { - const struct stmpe_keypad_platform_data *plat = keypad->plat; const struct stmpe_keypad_variant *variant = keypad->variant; struct stmpe *stmpe = keypad->stmpe; int ret; - if (plat->debounce_ms > STMPE_KEYPAD_MAX_DEBOUNCE) + if (keypad->debounce_ms > STMPE_KEYPAD_MAX_DEBOUNCE) return -EINVAL; - if (plat->scan_count > STMPE_KEYPAD_MAX_SCAN_COUNT) + if (keypad->scan_count > STMPE_KEYPAD_MAX_SCAN_COUNT) return -EINVAL; ret = stmpe_enable(stmpe, STMPE_BLOCK_KEYPAD); @@ -245,7 +258,7 @@ static int stmpe_keypad_chip_init(struct stmpe_keypad *keypad) ret = stmpe_set_bits(stmpe, STMPE_KPC_CTRL_MSB, STMPE_KPC_CTRL_MSB_SCAN_COUNT, - plat->scan_count << 4); + keypad->scan_count << 4); if (ret < 0) return ret; @@ -253,17 +266,18 @@ static int stmpe_keypad_chip_init(struct stmpe_keypad *keypad) STMPE_KPC_CTRL_LSB_SCAN | STMPE_KPC_CTRL_LSB_DEBOUNCE, STMPE_KPC_CTRL_LSB_SCAN | - (plat->debounce_ms << 1)); + (keypad->debounce_ms << 1)); } -static void stmpe_keypad_fill_used_pins(struct stmpe_keypad *keypad) +static void stmpe_keypad_fill_used_pins(struct stmpe_keypad *keypad, + u32 used_rows, u32 used_cols) { int row, col; - for (row = 0; row < STMPE_KEYPAD_MAX_ROWS; row++) { - for (col = 0; col < STMPE_KEYPAD_MAX_COLS; col++) { + for (row = 0; row < used_rows; row++) { + for (col = 0; col < used_cols; col++) { int code = MATRIX_SCAN_CODE(row, col, - STMPE_KEYPAD_ROW_SHIFT); + STMPE_KEYPAD_ROW_SHIFT); if (keypad->keymap[code] != KEY_RESERVED) { keypad->rows |= 1 << row; keypad->cols |= 1 << col; @@ -272,51 +286,17 @@ static void stmpe_keypad_fill_used_pins(struct stmpe_keypad *keypad) } } -#ifdef CONFIG_OF -static const struct stmpe_keypad_platform_data * -stmpe_keypad_of_probe(struct device *dev) -{ - struct device_node *np = dev->of_node; - struct stmpe_keypad_platform_data *plat; - - if (!np) - return ERR_PTR(-ENODEV); - - plat = devm_kzalloc(dev, sizeof(*plat), GFP_KERNEL); - if (!plat) - return ERR_PTR(-ENOMEM); - - of_property_read_u32(np, "debounce-interval", &plat->debounce_ms); - of_property_read_u32(np, "st,scan-count", &plat->scan_count); - - plat->no_autorepeat = of_property_read_bool(np, "st,no-autorepeat"); - - return plat; -} -#else -static inline const struct stmpe_keypad_platform_data * -stmpe_keypad_of_probe(struct device *dev) -{ - return ERR_PTR(-EINVAL); -} -#endif - static int stmpe_keypad_probe(struct platform_device *pdev) { struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent); - const struct stmpe_keypad_platform_data *plat; + struct device_node *np = pdev->dev.of_node; struct stmpe_keypad *keypad; struct input_dev *input; + u32 rows; + u32 cols; int error; int irq; - plat = stmpe->pdata->keypad; - if (!plat) { - plat = stmpe_keypad_of_probe(&pdev->dev); - if (IS_ERR(plat)) - return PTR_ERR(plat); - } - irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; @@ -326,6 +306,13 @@ static int stmpe_keypad_probe(struct platform_device *pdev) if (!keypad) return -ENOMEM; + keypad->stmpe = stmpe; + keypad->variant = &stmpe_keypad_variants[stmpe->partnum]; + + of_property_read_u32(np, "debounce-interval", &keypad->debounce_ms); + of_property_read_u32(np, "st,scan-count", &keypad->scan_count); + keypad->no_autorepeat = of_property_read_bool(np, "st,no-autorepeat"); + input = devm_input_allocate_device(&pdev->dev); if (!input) return -ENOMEM; @@ -334,23 +321,22 @@ static int stmpe_keypad_probe(struct platform_device *pdev) input->id.bustype = BUS_I2C; input->dev.parent = &pdev->dev; - error = matrix_keypad_build_keymap(plat->keymap_data, NULL, - STMPE_KEYPAD_MAX_ROWS, - STMPE_KEYPAD_MAX_COLS, + error = matrix_keypad_parse_of_params(&pdev->dev, &rows, &cols); + if (error) + return error; + + error = matrix_keypad_build_keymap(NULL, NULL, rows, cols, keypad->keymap, input); if (error) return error; input_set_capability(input, EV_MSC, MSC_SCAN); - if (!plat->no_autorepeat) + if (!keypad->no_autorepeat) __set_bit(EV_REP, input->evbit); - stmpe_keypad_fill_used_pins(keypad); + stmpe_keypad_fill_used_pins(keypad, rows, cols); - keypad->stmpe = stmpe; - keypad->plat = plat; keypad->input = input; - keypad->variant = &stmpe_keypad_variants[stmpe->partnum]; error = stmpe_keypad_chip_init(keypad); if (error < 0) diff --git a/include/linux/mfd/stmpe.h b/include/linux/mfd/stmpe.h index cc0deb7..f742b67 100644 --- a/include/linux/mfd/stmpe.h +++ b/include/linux/mfd/stmpe.h @@ -115,24 +115,6 @@ extern int stmpe_set_altfunc(struct stmpe *stmpe, u32 pins, extern int stmpe_enable(struct stmpe *stmpe, unsigned int blocks); extern int stmpe_disable(struct stmpe *stmpe, unsigned int blocks); -struct matrix_keymap_data; - -/** - * struct stmpe_keypad_platform_data - STMPE keypad platform data - * @keymap_data: key map table and size - * @debounce_ms: debounce interval, in ms. Maximum is - * %STMPE_KEYPAD_MAX_DEBOUNCE. - * @scan_count: number of key scanning cycles to confirm key data. - * Maximum is %STMPE_KEYPAD_MAX_SCAN_COUNT. - * @no_autorepeat: disable key autorepeat - */ -struct stmpe_keypad_platform_data { - const struct matrix_keymap_data *keymap_data; - unsigned int debounce_ms; - unsigned int scan_count; - bool no_autorepeat; -}; - #define STMPE_GPIO_NOREQ_811_TOUCH (0xf0) /** @@ -201,7 +183,6 @@ struct stmpe_ts_platform_data { * @irq_gpio: gpio number over which irq will be requested (significant only if * irq_over_gpio is true) * @gpio: GPIO-specific platform data - * @keypad: keypad-specific platform data * @ts: touchscreen-specific platform data */ struct stmpe_platform_data { @@ -214,7 +195,6 @@ struct stmpe_platform_data { int autosleep_timeout; struct stmpe_gpio_platform_data *gpio; - struct stmpe_keypad_platform_data *keypad; struct stmpe_ts_platform_data *ts; }; -- cgit v0.10.2 From 7c12a5b19e13ee78c3acb759f264df87ad984ffa Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 15 Dec 2014 22:23:40 -0800 Subject: Input: stmpe - bias keypad columns properly All keypad column pins used as inputs should be pulled up on the STMPE24xx, but this is not done by the current driver. Add some logic that will do this properly. The STMPE1601 also has a keypad controller, but explicitly does *NOT* require you to set up any pull-ups. Signed-off-by: Linus Walleij Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/keyboard/stmpe-keypad.c b/drivers/input/keyboard/stmpe-keypad.c index 8d1e7af..64514e64 100644 --- a/drivers/input/keyboard/stmpe-keypad.c +++ b/drivers/input/keyboard/stmpe-keypad.c @@ -52,6 +52,7 @@ * struct stmpe_keypad_variant - model-specific attributes * @auto_increment: whether the KPC_DATA_BYTE register address * auto-increments on multiple read + * @set_pullup: whether the pins need to have their pull-ups set * @num_data: number of data bytes * @num_normal_data: number of normal keys' data bytes * @max_cols: maximum number of columns supported @@ -61,6 +62,7 @@ */ struct stmpe_keypad_variant { bool auto_increment; + bool set_pullup; int num_data; int num_normal_data; int max_cols; @@ -81,6 +83,7 @@ static const struct stmpe_keypad_variant stmpe_keypad_variants[] = { }, [STMPE2401] = { .auto_increment = false, + .set_pullup = true, .num_data = 3, .num_normal_data = 2, .max_cols = 8, @@ -90,6 +93,7 @@ static const struct stmpe_keypad_variant stmpe_keypad_variants[] = { }, [STMPE2403] = { .auto_increment = true, + .set_pullup = true, .num_data = 5, .num_normal_data = 3, .max_cols = 8, @@ -185,7 +189,10 @@ static int stmpe_keypad_altfunc_init(struct stmpe_keypad *keypad) unsigned int col_gpios = variant->col_gpios; unsigned int row_gpios = variant->row_gpios; struct stmpe *stmpe = keypad->stmpe; + u8 pureg = stmpe->regs[STMPE_IDX_GPPUR_LSB]; unsigned int pins = 0; + unsigned int pu_pins = 0; + int ret; int i; /* @@ -202,8 +209,10 @@ static int stmpe_keypad_altfunc_init(struct stmpe_keypad *keypad) for (i = 0; i < variant->max_cols; i++) { int num = __ffs(col_gpios); - if (keypad->cols & (1 << i)) + if (keypad->cols & (1 << i)) { pins |= 1 << num; + pu_pins |= 1 << num; + } col_gpios &= ~(1 << num); } @@ -217,7 +226,31 @@ static int stmpe_keypad_altfunc_init(struct stmpe_keypad *keypad) row_gpios &= ~(1 << num); } - return stmpe_set_altfunc(stmpe, pins, STMPE_BLOCK_KEYPAD); + ret = stmpe_set_altfunc(stmpe, pins, STMPE_BLOCK_KEYPAD); + if (ret) + return ret; + + /* + * On STMPE24xx, set pin bias to pull-up on all keypad input + * pins (columns), this incidentally happen to be maximum 8 pins + * and placed at GPIO0-7 so only the LSB of the pull up register + * ever needs to be written. + */ + if (variant->set_pullup) { + u8 val; + + ret = stmpe_reg_read(stmpe, pureg); + if (ret) + return ret; + + /* Do not touch unused pins, may be used for GPIO */ + val = ret & ~pu_pins; + val |= pu_pins; + + ret = stmpe_reg_write(stmpe, pureg, val); + } + + return 0; } static int stmpe_keypad_chip_init(struct stmpe_keypad *keypad) -- cgit v0.10.2 From 148b83d0815a3778c8949e6a97cb798cbaa0efb3 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 16 Dec 2014 08:44:31 +0000 Subject: drm/i915: Invalidate media caches on gen7 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the gen7 pipe control there is an extra bit to flush the media caches, so let's set it during cache invalidation flushes. v2: Rename to MEDIA_STATE_CLEAR to be more inline with spec. Cc: Simon Farnsworth Cc: Ville Syrjälä Cc: Daniel Vetter Signed-off-by: Chris Wilson Reviewed-by: Daniel Vetter Cc: stable@vger.kernel.org Signed-off-by: Jani Nikula diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index eefdc23..5800955 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -395,6 +395,7 @@ #define PIPE_CONTROL_STORE_DATA_INDEX (1<<21) #define PIPE_CONTROL_CS_STALL (1<<20) #define PIPE_CONTROL_TLB_INVALIDATE (1<<18) +#define PIPE_CONTROL_MEDIA_STATE_CLEAR (1<<16) #define PIPE_CONTROL_QW_WRITE (1<<14) #define PIPE_CONTROL_POST_SYNC_OP_MASK (3<<14) #define PIPE_CONTROL_DEPTH_STALL (1<<13) diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 9f445e9..3d6bc8d 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -362,6 +362,7 @@ gen7_render_ring_flush(struct intel_engine_cs *ring, flags |= PIPE_CONTROL_VF_CACHE_INVALIDATE; flags |= PIPE_CONTROL_CONST_CACHE_INVALIDATE; flags |= PIPE_CONTROL_STATE_CACHE_INVALIDATE; + flags |= PIPE_CONTROL_MEDIA_STATE_CLEAR; /* * TLB invalidate requires a post-sync write. */ -- cgit v0.10.2 From add284a3a2481e759d6bec35f6444c32c8ddc383 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 16 Dec 2014 08:44:32 +0000 Subject: drm/i915: Force the CS stall for invalidate flushes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to act as a full command barrier by itself, we need to tell the pipecontrol to actually stall the command streamer while the flush runs. We require the full command barrier before operations like MI_SET_CONTEXT, which currently rely on a prior invalidate flush. References: https://bugs.freedesktop.org/show_bug.cgi?id=83677 Cc: Simon Farnsworth Cc: Daniel Vetter Cc: Ville Syrjälä Signed-off-by: Chris Wilson Cc: stable@vger.kernel.org Signed-off-by: Jani Nikula diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 3d6bc8d..c7bc93d 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -369,6 +369,8 @@ gen7_render_ring_flush(struct intel_engine_cs *ring, flags |= PIPE_CONTROL_QW_WRITE; flags |= PIPE_CONTROL_GLOBAL_GTT_IVB; + flags |= PIPE_CONTROL_STALL_AT_SCOREBOARD; + /* Workaround: we must issue a pipe_control with CS-stall bit * set before a pipe_control command that has the state cache * invalidate bit set. */ -- cgit v0.10.2 From 2c550183476dfa25641309ae9a28d30feed14379 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 16 Dec 2014 10:02:27 +0000 Subject: drm/i915: Disable PSMI sleep messages on all rings around context switches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There exists a current workaround to prevent a hang on context switch should the ring go to sleep in the middle of the restore, WaProgramMiArbOnOffAroundMiSetContext (applicable to all gen7+). In spite of disabling arbitration (which prevents the ring from powering down during the critical section) we were still hitting hangs that had the hallmarks of the known erratum. That is we are still seeing hangs "on the last instruction in the context restore". By comparing -nightly (broken) with requests (working), we were able to deduce that it was the semaphore LRI cross-talk that reproduced the original failure. The key was that requests implemented deferred semaphore signalling, and disabling that, i.e. emitting the semaphore signal to every other ring after every batch restored the frequent hang. Explicitly disabling PSMI sleep on the RCS ring was insufficient, all the rings had to be awake to prevent the hangs. Fortunately, we can reduce the wakelock to the MI_SET_CONTEXT operation itself, and so should be able to limit the extra power implications. Since the MI_ARB_ON_OFF workaround is listed for all gen7 and above products, we should apply this extra hammer for all of the same platforms despite so far that we have only been able to reproduce the hang on certain ivb and hsw models. The last question is whether we want to always use the extra hammer or only when we know semaphores are in operation. At the moment, we only use LRI on non-RCS rings for semaphores, but that may change in the future with the possibility of reintroducing this bug under subtle conditions. v2: Make it explicit that the PSMI LRI are an extension to the original workaround for the other rings. v3: Bikeshedding variable names and whitespacing Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=80660 Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=83677 Cc: Simon Farnsworth Cc: Daniel Vetter Cc: Ville Syrjälä Signed-off-by: Chris Wilson Tested-by: Peter Frühberger Reviewed-by: Daniel Vetter Cc: stable@vger.kernel.org Signed-off-by: Jani Nikula diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index d17ff43..d011ec8 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -473,7 +473,12 @@ mi_set_context(struct intel_engine_cs *ring, u32 hw_flags) { u32 flags = hw_flags | MI_MM_SPACE_GTT; - int ret; + const int num_rings = + /* Use an extended w/a on ivb+ if signalling from other rings */ + i915_semaphore_is_enabled(ring->dev) ? + hweight32(INTEL_INFO(ring->dev)->ring_mask) - 1 : + 0; + int len, i, ret; /* w/a: If Flush TLB Invalidation Mode is enabled, driver must do a TLB * invalidation prior to MI_SET_CONTEXT. On GEN6 we don't set the value @@ -490,15 +495,31 @@ mi_set_context(struct intel_engine_cs *ring, if (!IS_HASWELL(ring->dev) && INTEL_INFO(ring->dev)->gen < 8) flags |= (MI_SAVE_EXT_STATE_EN | MI_RESTORE_EXT_STATE_EN); - ret = intel_ring_begin(ring, 6); + + len = 4; + if (INTEL_INFO(ring->dev)->gen >= 7) + len += 2 + (num_rings ? 4*num_rings + 2 : 0); + + ret = intel_ring_begin(ring, len); if (ret) return ret; /* WaProgramMiArbOnOffAroundMiSetContext:ivb,vlv,hsw,bdw,chv */ - if (INTEL_INFO(ring->dev)->gen >= 7) + if (INTEL_INFO(ring->dev)->gen >= 7) { intel_ring_emit(ring, MI_ARB_ON_OFF | MI_ARB_DISABLE); - else - intel_ring_emit(ring, MI_NOOP); + if (num_rings) { + struct intel_engine_cs *signaller; + + intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(num_rings)); + for_each_ring(signaller, to_i915(ring->dev), i) { + if (signaller == ring) + continue; + + intel_ring_emit(ring, RING_PSMI_CTL(signaller->mmio_base)); + intel_ring_emit(ring, _MASKED_BIT_ENABLE(GEN6_PSMI_SLEEP_MSG_DISABLE)); + } + } + } intel_ring_emit(ring, MI_NOOP); intel_ring_emit(ring, MI_SET_CONTEXT); @@ -510,10 +531,21 @@ mi_set_context(struct intel_engine_cs *ring, */ intel_ring_emit(ring, MI_NOOP); - if (INTEL_INFO(ring->dev)->gen >= 7) + if (INTEL_INFO(ring->dev)->gen >= 7) { + if (num_rings) { + struct intel_engine_cs *signaller; + + intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(num_rings)); + for_each_ring(signaller, to_i915(ring->dev), i) { + if (signaller == ring) + continue; + + intel_ring_emit(ring, RING_PSMI_CTL(signaller->mmio_base)); + intel_ring_emit(ring, _MASKED_BIT_DISABLE(GEN6_PSMI_SLEEP_MSG_DISABLE)); + } + } intel_ring_emit(ring, MI_ARB_ON_OFF | MI_ARB_ENABLE); - else - intel_ring_emit(ring, MI_NOOP); + } intel_ring_advance(ring); diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 5800955..172de3b 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1129,6 +1129,7 @@ enum punit_power_well { #define GEN6_VERSYNC (RING_SYNC_1(VEBOX_RING_BASE)) #define GEN6_VEVSYNC (RING_SYNC_2(VEBOX_RING_BASE)) #define GEN6_NOSYNC 0 +#define RING_PSMI_CTL(base) ((base)+0x50) #define RING_MAX_IDLE(base) ((base)+0x54) #define RING_HWS_PGA(base) ((base)+0x80) #define RING_HWS_PGA_GEN6(base) ((base)+0x2080) @@ -1459,6 +1460,7 @@ enum punit_power_well { #define GEN6_BLITTER_FBC_NOTIFY (1<<3) #define GEN6_RC_SLEEP_PSMI_CONTROL 0x2050 +#define GEN6_PSMI_SLEEP_MSG_DISABLE (1 << 0) #define GEN8_RC_SEMA_IDLE_MSG_DISABLE (1 << 12) #define GEN8_FF_DOP_CLOCK_GATE_DISABLE (1<<10) -- cgit v0.10.2 From d6ad36913083d683aad4e02e53580c995f1a6ede Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Wed, 10 Dec 2014 11:02:09 +0000 Subject: clocksource: arch_timer: Only use the virtual counter (CNTVCT) on arm64 Commit 0b46b8a718c6 (clocksource: arch_timer: Fix code to use physical timers when requested) introduces the use of physical counters in the ARM architected timer driver. However, he arm64 kernel uses CNTVCT in VDSO. When booting in EL2, the kernel switches to the physical timers to make things easier for KVM but it continues to use the virtual counter both in user and kernel. While in such scenario CNTVCT == CNTPCT (since CNTVOFF is initialised by the kernel to 0), we want to spot firmware bugs corrupting CNTVOFF early (which would affect CNTVCT). Signed-off-by: Catalin Marinas Tested-by: Yingjoe Chen Cc: Daniel Lezcano Signed-off-by: Arnd Bergmann diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 6a79fc4..095c177 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -462,7 +462,7 @@ static void __init arch_counter_register(unsigned type) /* Register the CP15 based counter if we have one */ if (type & ARCH_CP15_TIMER) { - if (arch_timer_use_virtual) + if (IS_ENABLED(CONFIG_ARM64) || arch_timer_use_virtual) arch_timer_read_counter = arch_counter_get_cntvct; else arch_timer_read_counter = arch_counter_get_cntpct; -- cgit v0.10.2 From 4108b3d96273784f697dd6d8e59ef9203a10a02d Mon Sep 17 00:00:00 2001 From: Len Brown Date: Tue, 16 Dec 2014 01:52:06 -0500 Subject: cpuidle: menu: Better idle duration measurement without using CPUIDLE_FLAG_TIME_INVALID When menu sees CPUIDLE_FLAG_TIME_INVALID, it ignores its timestamps, and assumes that idle lasted as long as the time till next predicted timer expiration. But if an interrupt was seen and serviced before that duration, it would actually be more accurate to use the measured time rather than rounding up to the next predicted timer expiration. And if an interrupt is seen and serviced such that the mesured time exceeds the time till next predicted timer expiration, then truncating to that expiration is the right thing to do -- since we can never stay idle past that timer expiration. So the code can do a better job without checking for CPUIDLE_FLAG_TIME_INVALID. Signed-off-by: Len Brown Acked-by: Daniel Lezcano Reviewed-by: Tuukka Tikkanen Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index 659d7b0..4058079 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -396,8 +396,8 @@ static void menu_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) * power state and occurrence of the wakeup event. * * If the entered idle state didn't support residency measurements, - * we are basically lost in the dark how much time passed. - * As a compromise, assume we slept for the whole expected time. + * we use them anyway if they are short, and if long, + * truncate to the whole expected time. * * Any measured amount of time will include the exit latency. * Since we are interested in when the wakeup begun, not when it @@ -405,22 +405,17 @@ static void menu_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) * the measured amount of time is less than the exit latency, * assume the state was never reached and the exit latency is 0. */ - if (unlikely(target->flags & CPUIDLE_FLAG_TIME_INVALID)) { - /* Use timer value as is */ - measured_us = data->next_timer_us; - } else { - /* Use measured value */ - measured_us = cpuidle_get_last_residency(dev); + /* measured value */ + measured_us = cpuidle_get_last_residency(dev); - /* Deduct exit latency */ - if (measured_us > target->exit_latency) - measured_us -= target->exit_latency; + /* Deduct exit latency */ + if (measured_us > target->exit_latency) + measured_us -= target->exit_latency; - /* Make sure our coefficients do not exceed unity */ - if (measured_us > data->next_timer_us) - measured_us = data->next_timer_us; - } + /* Make sure our coefficients do not exceed unity */ + if (measured_us > data->next_timer_us) + measured_us = data->next_timer_us; /* Update our correction ratio */ new_factor = data->correction_factor[data->bucket]; -- cgit v0.10.2 From b73026b9c959600bcd65eeae7a5f7ac00ded886f Mon Sep 17 00:00:00 2001 From: Len Brown Date: Tue, 16 Dec 2014 01:52:07 -0500 Subject: cpuidle: ladder: Better idle duration measurement without using CPUIDLE_FLAG_TIME_INVALID When the ladder governor sees the CPUIDLE_FLAG_TIME_INVALID flag, it unconditionally causes a state promotion by setting last_residency to a number higher than the state's promotion_time: last_residency = last_state->threshold.promotion_time + 1 It does this for fear that cpuidle_get_last_residency() will be in-accurate, because cpuidle_enter_state() invoked a state with CPUIDLE_FLAG_TIME_INVALID. But the only state with CPUIDLE_FLAG_TIME_INVALID is acpi_safe_halt(), which may return well after its actual idle duration because it enables interrupts, so cpuidle_enter_state() also measures interrupt service time. So what? In ladder, a huge invalid last_residency has exactly the same effect as the current code -- it unconditionally causes a state promotion. In the case where the idle residency plus measured interrupt handling time is less than the state's demotion_time -- we should use that timestamp to give ladder a chance to demote, rather than unconditionally promoting. This can be done by simply ignoring the CPUIDLE_FLAG_TIME_INVALID, and using the "invalid" time, as it is either equal to what we are doing today, or better. Signed-off-by: Len Brown Acked-by: Daniel Lezcano Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpuidle/governors/ladder.c b/drivers/cpuidle/governors/ladder.c index 37263d9..401c010 100644 --- a/drivers/cpuidle/governors/ladder.c +++ b/drivers/cpuidle/governors/ladder.c @@ -79,12 +79,7 @@ static int ladder_select_state(struct cpuidle_driver *drv, last_state = &ldev->states[last_idx]; - if (!(drv->states[last_idx].flags & CPUIDLE_FLAG_TIME_INVALID)) { - last_residency = cpuidle_get_last_residency(dev) - \ - drv->states[last_idx].exit_latency; - } - else - last_residency = last_state->threshold.promotion_time + 1; + last_residency = cpuidle_get_last_residency(dev) - drv->states[last_idx].exit_latency; /* consider promotion */ if (last_idx < drv->state_count - 1 && -- cgit v0.10.2 From 62c4cf97e82cf79446642e599d155884f600cf17 Mon Sep 17 00:00:00 2001 From: Len Brown Date: Tue, 16 Dec 2014 01:52:08 -0500 Subject: cpuidle / ACPI: remove unused CPUIDLE_FLAG_TIME_INVALID CPUIDLE_FLAG_TIME_INVALID is no longer checked by menu or ladder cpuidle governors, so don't bother setting or defining it. It was originally invented to account for the fact that acpi_safe_halt() enables interrupts to invoke HLT. That would allow interrupt service routines to be included in the last_idle duration measurements made in cpuidle_enter_state(), potentially returning a duration much larger than reality. But menu and ladder can gracefully handle erroneously large duration intervals without checking for CPUIDLE_FLAG_TIME_INVALID. Further, if they don't check CPUIDLE_FLAG_TIME_INVALID, they can also benefit from the instances when the duration interval is not erroneously large. Signed-off-by: Len Brown Acked-by: Daniel Lezcano Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 380b4b4..7afba40 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -985,8 +985,6 @@ static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr) state->flags = 0; switch (cx->type) { case ACPI_STATE_C1: - if (cx->entry_method != ACPI_CSTATE_FFH) - state->flags |= CPUIDLE_FLAG_TIME_INVALID; state->enter = acpi_idle_enter_c1; state->enter_dead = acpi_idle_play_dead; diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index a07e087..ab70f3b 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -53,7 +53,6 @@ struct cpuidle_state { }; /* Idle State Flags */ -#define CPUIDLE_FLAG_TIME_INVALID (0x01) /* is residency time measurable? */ #define CPUIDLE_FLAG_COUPLED (0x02) /* state applies to multiple cpus */ #define CPUIDLE_FLAG_TIMER_STOP (0x04) /* timer is stopped on this state */ @@ -89,8 +88,6 @@ DECLARE_PER_CPU(struct cpuidle_device, cpuidle_dev); /** * cpuidle_get_last_residency - retrieves the last state's residency time * @dev: the target CPU - * - * NOTE: this value is invalid if CPUIDLE_FLAG_TIME_INVALID is set */ static inline int cpuidle_get_last_residency(struct cpuidle_device *dev) { -- cgit v0.10.2 From 7496fcbe8a643097efc061160e1c3b65ee2fa350 Mon Sep 17 00:00:00 2001 From: Amit Daniel Kachhap Date: Mon, 15 Dec 2014 09:08:59 +0530 Subject: PM / Domains: Export of_genpd_get_from_provider function This function looks up a PM domain form the provider. This will be useful to add parent/child domain relationship from the SoC specific code. The caller of the function must make sure that PM domain provider is already registered. Reviewed-by: Ulf Hansson Signed-off-by: Amit Daniel Kachhap Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 5d7b754..1bd119e 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -2108,7 +2108,7 @@ EXPORT_SYMBOL_GPL(of_genpd_del_provider); * Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR() * on failure. */ -static struct generic_pm_domain *of_genpd_get_from_provider( +struct generic_pm_domain *of_genpd_get_from_provider( struct of_phandle_args *genpdspec) { struct generic_pm_domain *genpd = ERR_PTR(-ENOENT); @@ -2128,6 +2128,7 @@ static struct generic_pm_domain *of_genpd_get_from_provider( return genpd; } +EXPORT_SYMBOL_GPL(of_genpd_get_from_provider); /** * genpd_dev_pm_detach - Detach a device from its PM domain. diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 1dd6c7f..ba7ca54 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -275,6 +275,8 @@ typedef struct generic_pm_domain *(*genpd_xlate_t)(struct of_phandle_args *args, int __of_genpd_add_provider(struct device_node *np, genpd_xlate_t xlate, void *data); void of_genpd_del_provider(struct device_node *np); +struct generic_pm_domain *of_genpd_get_from_provider( + struct of_phandle_args *genpdspec); struct generic_pm_domain *__of_genpd_xlate_simple( struct of_phandle_args *genpdspec, @@ -292,6 +294,12 @@ static inline int __of_genpd_add_provider(struct device_node *np, } static inline void of_genpd_del_provider(struct device_node *np) {} +static inline struct generic_pm_domain *of_genpd_get_from_provider( + struct of_phandle_args *genpdspec) +{ + return NULL; +} + #define __of_genpd_xlate_simple NULL #define __of_genpd_xlate_onecell NULL -- cgit v0.10.2 From d72be771c5dbabaf3058ef382f4934aa31c88df2 Mon Sep 17 00:00:00 2001 From: Jacob Pan Date: Tue, 16 Dec 2014 09:52:47 -0800 Subject: powercap / RAPL: add IDs for future Xeon CPUs Enable RAPL driver on Xeon cpu id 0x56. Signed-off-by: Jacob Pan Signed-off-by: Rafael J. Wysocki diff --git a/drivers/powercap/intel_rapl.c b/drivers/powercap/intel_rapl.c index c71443c..97b5e4e 100644 --- a/drivers/powercap/intel_rapl.c +++ b/drivers/powercap/intel_rapl.c @@ -1041,6 +1041,7 @@ static const struct x86_cpu_id rapl_ids[] = { RAPL_CPU(0x45, rapl_defaults_core),/* Haswell ULT */ RAPL_CPU(0x4C, rapl_defaults_atom),/* Braswell */ RAPL_CPU(0x4A, rapl_defaults_atom),/* Tangier */ + RAPL_CPU(0x56, rapl_defaults_core),/* Future Xeon */ RAPL_CPU(0x5A, rapl_defaults_atom),/* Annidale */ {} }; -- cgit v0.10.2 From 2d2e95ea8f124869b96ad929d1701bd64844a06a Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 17 Dec 2014 02:55:53 +0300 Subject: thermal: cpu_cooling: small memory leak on error There was a left over return here so the error handling isn't run. It leads to a small memory leak and a static checker warning. drivers/thermal/cpu_cooling.c:351 __cpufreq_cooling_register() info: ignoring unreachable code. Fixes: f6859014c7e7 ("thermal: cpu_cooling: Store frequencies in descending order") Acked-by: Viresh Kumar Signed-off-by: Dan Carpenter Signed-off-by: Eduardo Valentin diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 051eb48..9b45f64 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -347,7 +347,6 @@ __cpufreq_cooling_register(struct device_node *np, cpufreq_dev->freq_table = kmalloc(sizeof(*cpufreq_dev->freq_table) * cpufreq_dev->max_level, GFP_KERNEL); if (!cpufreq_dev->freq_table) { - return ERR_PTR(-ENOMEM); cool_dev = ERR_PTR(-ENOMEM); goto free_cdev; } -- cgit v0.10.2 From 503ccc3fec4a56cdcfedc507cd1ea0d85e1fbfa2 Mon Sep 17 00:00:00 2001 From: Javi Merino Date: Wed, 17 Dec 2014 11:11:24 +0000 Subject: thermal: cpu_cooling: return ERR_PTR() for !CPU_THERMAL or !THERMAL_OF The documentation of of_cpufreq_cooling_register() and cpufreq_cooling_register() say that they return ERR_PTR() on error. Accordingly, callers only check for IS_ERR(). Therefore, make them return ERR_PTR(-ENOSYS) as is customary in the kernel when config options are missing. Cc: Zhang Rui Cc: Eduardo Valentin Signed-off-by: Javi Merino Signed-off-by: Eduardo Valentin diff --git a/include/linux/cpu_cooling.h b/include/linux/cpu_cooling.h index c303d38..bd95527 100644 --- a/include/linux/cpu_cooling.h +++ b/include/linux/cpu_cooling.h @@ -50,7 +50,7 @@ static inline struct thermal_cooling_device * of_cpufreq_cooling_register(struct device_node *np, const struct cpumask *clip_cpus) { - return NULL; + return ERR_PTR(-ENOSYS); } #endif @@ -65,13 +65,13 @@ unsigned long cpufreq_cooling_get_level(unsigned int cpu, unsigned int freq); static inline struct thermal_cooling_device * cpufreq_cooling_register(const struct cpumask *clip_cpus) { - return NULL; + return ERR_PTR(-ENOSYS); } static inline struct thermal_cooling_device * of_cpufreq_cooling_register(struct device_node *np, const struct cpumask *clip_cpus) { - return NULL; + return ERR_PTR(-ENOSYS); } static inline void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) -- cgit v0.10.2 From a4d7b30df5d10bca92f8f359fc56fb8db0410f89 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 15 Dec 2014 14:47:18 +0100 Subject: drm/irq: Add drm_crtc_send_vblank_event() This function is the KMS native variant of drm_send_vblank_event(). It takes a struct drm_crtc * instead of a struct drm_device * and an index of the CRTC. Eventually the goal is to access vblank data through the CRTC only so that the per-CRTC data can be moved to struct drm_crtc. Reviewed-by: Daniel Vetter Signed-off-by: Thierry Reding diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index f5a5f18..2763010 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -904,6 +904,8 @@ static void send_vblank_event(struct drm_device *dev, * * Updates sequence # and timestamp on event, and sends it to userspace. * Caller must hold event lock. + * + * This is the legacy version of drm_crtc_send_vblank_event(). */ void drm_send_vblank_event(struct drm_device *dev, int crtc, struct drm_pending_vblank_event *e) @@ -923,6 +925,23 @@ void drm_send_vblank_event(struct drm_device *dev, int crtc, EXPORT_SYMBOL(drm_send_vblank_event); /** + * drm_crtc_send_vblank_event - helper to send vblank event after pageflip + * @crtc: the source CRTC of the vblank event + * @e: the event to send + * + * Updates sequence # and timestamp on event, and sends it to userspace. + * Caller must hold event lock. + * + * This is the native KMS version of drm_send_vblank_event(). + */ +void drm_crtc_send_vblank_event(struct drm_crtc *crtc, + struct drm_pending_vblank_event *e) +{ + drm_send_vblank_event(crtc->dev, drm_crtc_index(crtc), e); +} +EXPORT_SYMBOL(drm_crtc_send_vblank_event); + +/** * drm_vblank_enable - enable the vblank interrupt on a CRTC * @dev: DRM device * @crtc: CRTC in question diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 8ba35c6..b78601b 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -905,6 +905,8 @@ extern u32 drm_vblank_count_and_time(struct drm_device *dev, int crtc, struct timeval *vblanktime); extern void drm_send_vblank_event(struct drm_device *dev, int crtc, struct drm_pending_vblank_event *e); +extern void drm_crtc_send_vblank_event(struct drm_crtc *crtc, + struct drm_pending_vblank_event *e); extern bool drm_handle_vblank(struct drm_device *dev, int crtc); extern int drm_vblank_get(struct drm_device *dev, int crtc); extern void drm_vblank_put(struct drm_device *dev, int crtc); -- cgit v0.10.2 From 115ebcd4fa90df8cfc80d2302b7afa8d39edb7e2 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 15 Dec 2014 14:47:18 +0100 Subject: drm/irq: Add drm_crtc_handle_vblank() This function is the KMS native variant of drm_handle_vblank(). It takes a struct drm_crtc * instead of a struct drm_device * and an index of the CRTC. Eventually the goal is to access vblank data through the CRTC only so that the per-CRTC data can be moved to struct drm_crtc. Reviewed-by: Daniel Vetter Signed-off-by: Thierry Reding diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 2763010..ece5a93 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -1613,6 +1613,8 @@ static void drm_handle_vblank_events(struct drm_device *dev, int crtc) * * Drivers should call this routine in their vblank interrupt handlers to * update the vblank counter and send any signals that may be pending. + * + * This is the legacy version of drm_crtc_handle_vblank(). */ bool drm_handle_vblank(struct drm_device *dev, int crtc) { @@ -1689,3 +1691,21 @@ bool drm_handle_vblank(struct drm_device *dev, int crtc) return true; } EXPORT_SYMBOL(drm_handle_vblank); + +/** + * drm_crtc_handle_vblank - handle a vblank event + * @crtc: where this event occurred + * + * Drivers should call this routine in their vblank interrupt handlers to + * update the vblank counter and send any signals that may be pending. + * + * This is the native KMS version of drm_handle_vblank(). + * + * Returns: + * True if the event was successfully handled, false on failure. + */ +bool drm_crtc_handle_vblank(struct drm_crtc *crtc) +{ + return drm_handle_vblank(crtc->dev, drm_crtc_index(crtc)); +} +EXPORT_SYMBOL(drm_crtc_handle_vblank); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index b78601b..f1f7f15 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -908,6 +908,7 @@ extern void drm_send_vblank_event(struct drm_device *dev, int crtc, extern void drm_crtc_send_vblank_event(struct drm_crtc *crtc, struct drm_pending_vblank_event *e); extern bool drm_handle_vblank(struct drm_device *dev, int crtc); +extern bool drm_crtc_handle_vblank(struct drm_crtc *crtc); extern int drm_vblank_get(struct drm_device *dev, int crtc); extern void drm_vblank_put(struct drm_device *dev, int crtc); extern int drm_crtc_vblank_get(struct drm_crtc *crtc); -- cgit v0.10.2 From 96d3f91eb263d478777bb9bff1b1300e0c08c29e Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 16 Dec 2014 13:08:47 +0100 Subject: drm/irq: Add drm_crtc_vblank_count() This function is the KMS native variant of drm_vblank_count(). It takes a struct drm_crtc * instead of a struct drm_device * and an index of the CRTC. Eventually the goal is to access vblank data through the CRTC only so that the per-CRTC data can be moved to struct drm_crtc. Reviewed-by: Daniel Vetter Signed-off-by: Thierry Reding diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index ece5a93..4d79dad 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -830,6 +830,8 @@ drm_get_last_vbltimestamp(struct drm_device *dev, int crtc, * vblank events since the system was booted, including lost events due to * modesetting activity. * + * This is the legacy version of drm_crtc_vblank_count(). + * * Returns: * The software vblank counter. */ @@ -844,6 +846,25 @@ u32 drm_vblank_count(struct drm_device *dev, int crtc) EXPORT_SYMBOL(drm_vblank_count); /** + * drm_crtc_vblank_count - retrieve "cooked" vblank counter value + * @crtc: which counter to retrieve + * + * Fetches the "cooked" vblank count value that represents the number of + * vblank events since the system was booted, including lost events due to + * modesetting activity. + * + * This is the native KMS version of drm_vblank_count(). + * + * Returns: + * The software vblank counter. + */ +u32 drm_crtc_vblank_count(struct drm_crtc *crtc) +{ + return drm_vblank_count(crtc->dev, drm_crtc_index(crtc)); +} +EXPORT_SYMBOL(drm_crtc_vblank_count); + +/** * drm_vblank_count_and_time - retrieve "cooked" vblank counter value * and the system timestamp corresponding to that vblank counter value. * diff --git a/include/drm/drmP.h b/include/drm/drmP.h index f1f7f15..e1b2e8b 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -901,6 +901,7 @@ extern int drm_vblank_init(struct drm_device *dev, int num_crtcs); extern int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *filp); extern u32 drm_vblank_count(struct drm_device *dev, int crtc); +extern u32 drm_crtc_vblank_count(struct drm_crtc *crtc); extern u32 drm_vblank_count_and_time(struct drm_device *dev, int crtc, struct timeval *vblanktime); extern void drm_send_vblank_event(struct drm_device *dev, int crtc, -- cgit v0.10.2 From ed7dae58de246790f394caea5ef7eecad0e83387 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 16 Dec 2014 16:03:13 +0100 Subject: drm/tegra: dc: Consistently use the same pipe The hardware pipe numbers don't always match the DRM CRTC indices. This can happen for example if the first display controller defers probe, causing it to be registered with DRM after the second display controller. When that happens the hardware pipe numbers and DRM CRTC indices become different. Make sure that the CRTC index is always used when accessing per-CRTC VBLANK data. This can be ensured by using the drm_crtc_vblank_*() API, which will do the right thing automatically given a struct drm_crtc *. Signed-off-by: Thierry Reding diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 3367960..5a6d43d 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -826,8 +826,8 @@ static void tegra_dc_finish_page_flip(struct tegra_dc *dc) if (base == bo->paddr + crtc->primary->fb->offsets[0]) { spin_lock_irqsave(&drm->event_lock, flags); - drm_send_vblank_event(drm, dc->pipe, dc->event); - drm_vblank_put(drm, dc->pipe); + drm_crtc_send_vblank_event(crtc, dc->event); + drm_crtc_vblank_put(crtc); dc->event = NULL; spin_unlock_irqrestore(&drm->event_lock, flags); } @@ -843,7 +843,7 @@ void tegra_dc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file) if (dc->event && dc->event->base.file_priv == file) { dc->event->base.destroy(&dc->event->base); - drm_vblank_put(drm, dc->pipe); + drm_crtc_vblank_put(crtc); dc->event = NULL; } @@ -853,16 +853,16 @@ void tegra_dc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file) static int tegra_dc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event, uint32_t page_flip_flags) { + unsigned int pipe = drm_crtc_index(crtc); struct tegra_dc *dc = to_tegra_dc(crtc); - struct drm_device *drm = crtc->dev; if (dc->event) return -EBUSY; if (event) { - event->pipe = dc->pipe; + event->pipe = pipe; dc->event = event; - drm_vblank_get(drm, dc->pipe); + drm_crtc_vblank_get(crtc); } tegra_dc_set_base(dc, 0, 0, fb); @@ -1127,7 +1127,7 @@ static irqreturn_t tegra_dc_irq(int irq, void *data) /* dev_dbg(dc->dev, "%s(): vertical blank\n", __func__); */ - drm_handle_vblank(dc->base.dev, dc->pipe); + drm_crtc_handle_vblank(&dc->base); tegra_dc_finish_page_flip(dc); } diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index e549afe..d4f8275 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -694,24 +694,28 @@ static const struct file_operations tegra_drm_fops = { .llseek = noop_llseek, }; -static struct drm_crtc *tegra_crtc_from_pipe(struct drm_device *drm, int pipe) +static struct drm_crtc *tegra_crtc_from_pipe(struct drm_device *drm, + unsigned int pipe) { struct drm_crtc *crtc; list_for_each_entry(crtc, &drm->mode_config.crtc_list, head) { - struct tegra_dc *dc = to_tegra_dc(crtc); - - if (dc->pipe == pipe) + if (pipe == drm_crtc_index(crtc)) return crtc; } return NULL; } -static u32 tegra_drm_get_vblank_counter(struct drm_device *dev, int crtc) +static u32 tegra_drm_get_vblank_counter(struct drm_device *drm, int pipe) { + struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe); + + if (!crtc) + return 0; + /* TODO: implement real hardware counter using syncpoints */ - return drm_vblank_count(dev, crtc); + return drm_crtc_vblank_count(crtc); } static int tegra_drm_enable_vblank(struct drm_device *drm, int pipe) -- cgit v0.10.2 From 6b59cc1c86e90cccf8fb0b4dee5fbc226bb82d3e Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 16 Dec 2014 16:33:27 +0100 Subject: drm/tegra: dc: Fix a potential race on page-flip completion Page-flip completion could race with page-flip submission, so extend the critical section to include all accesses to page-flip related data. Reported-by: Alexandre Courbot Signed-off-by: Thierry Reding diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 5a6d43d..ef36f9d 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -814,8 +814,12 @@ static void tegra_dc_finish_page_flip(struct tegra_dc *dc) unsigned long flags, base; struct tegra_bo *bo; - if (!dc->event) + spin_lock_irqsave(&drm->event_lock, flags); + + if (!dc->event) { + spin_unlock_irqrestore(&drm->event_lock, flags); return; + } bo = tegra_fb_get_plane(crtc->primary->fb, 0); @@ -825,12 +829,12 @@ static void tegra_dc_finish_page_flip(struct tegra_dc *dc) tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS); if (base == bo->paddr + crtc->primary->fb->offsets[0]) { - spin_lock_irqsave(&drm->event_lock, flags); drm_crtc_send_vblank_event(crtc, dc->event); drm_crtc_vblank_put(crtc); dc->event = NULL; - spin_unlock_irqrestore(&drm->event_lock, flags); } + + spin_unlock_irqrestore(&drm->event_lock, flags); } void tegra_dc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file) -- cgit v0.10.2 From a04251fc94b58ec25476e57986dfec727b812c22 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 16 Dec 2014 16:35:26 +0100 Subject: drm/tegra: gem: Flush buffer objects upon allocation Buffers obtained via shmem may still have associated cachelines. If they aren't properly flushed they may cause framebuffer corruption if the cache gets flushed after the application has drawn to it. Signed-off-by: Thierry Reding diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index da32086..c888bed 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -219,19 +219,47 @@ static void tegra_bo_free(struct drm_device *drm, struct tegra_bo *bo) static int tegra_bo_get_pages(struct drm_device *drm, struct tegra_bo *bo, size_t size) { + struct scatterlist *s; + struct sg_table *sgt; + unsigned int i; + bo->pages = drm_gem_get_pages(&bo->gem); if (IS_ERR(bo->pages)) return PTR_ERR(bo->pages); bo->num_pages = size >> PAGE_SHIFT; - bo->sgt = drm_prime_pages_to_sg(bo->pages, bo->num_pages); - if (IS_ERR(bo->sgt)) { - drm_gem_put_pages(&bo->gem, bo->pages, false, false); - return PTR_ERR(bo->sgt); + sgt = drm_prime_pages_to_sg(bo->pages, bo->num_pages); + if (IS_ERR(sgt)) + goto put_pages; + + /* + * Fake up the SG table so that dma_map_sg() can be used to flush the + * pages associated with it. Note that this relies on the fact that + * the DMA API doesn't hook into IOMMU on Tegra, therefore mapping is + * only cache maintenance. + * + * TODO: Replace this by drm_clflash_sg() once it can be implemented + * without relying on symbols that are not exported. + */ + for_each_sg(sgt->sgl, s, sgt->nents, i) + sg_dma_address(s) = sg_phys(s); + + if (dma_map_sg(drm->dev, sgt->sgl, sgt->nents, DMA_TO_DEVICE) == 0) { + sgt = ERR_PTR(-ENOMEM); + goto release_sgt; } + bo->sgt = sgt; + return 0; + +release_sgt: + sg_free_table(sgt); + kfree(sgt); +put_pages: + drm_gem_put_pages(&bo->gem, bo->pages, false, false); + return PTR_ERR(sgt); } static int tegra_bo_alloc(struct drm_device *drm, struct tegra_bo *bo, -- cgit v0.10.2 From 73c42c79767a03ae64d11457e3ce80e80e09e514 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 16 Dec 2014 16:41:47 +0100 Subject: drm/tegra: gem: Use the proper size for GEM objects If the requested buffer size wasn't a multiple of the page size, the IOMMU code would round down the size to the next multiple of the page size, thereby causing translation errors. To fix this we no longer pass around the requested size but reuse the computed size of the GEM object. This is already rounded to the next page boundary, so mapping that size works out fine. Signed-off-by: Thierry Reding diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index c888bed..8777b7f 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -216,8 +216,7 @@ static void tegra_bo_free(struct drm_device *drm, struct tegra_bo *bo) } } -static int tegra_bo_get_pages(struct drm_device *drm, struct tegra_bo *bo, - size_t size) +static int tegra_bo_get_pages(struct drm_device *drm, struct tegra_bo *bo) { struct scatterlist *s; struct sg_table *sgt; @@ -227,7 +226,7 @@ static int tegra_bo_get_pages(struct drm_device *drm, struct tegra_bo *bo, if (IS_ERR(bo->pages)) return PTR_ERR(bo->pages); - bo->num_pages = size >> PAGE_SHIFT; + bo->num_pages = bo->gem.size >> PAGE_SHIFT; sgt = drm_prime_pages_to_sg(bo->pages, bo->num_pages); if (IS_ERR(sgt)) @@ -262,14 +261,13 @@ put_pages: return PTR_ERR(sgt); } -static int tegra_bo_alloc(struct drm_device *drm, struct tegra_bo *bo, - size_t size) +static int tegra_bo_alloc(struct drm_device *drm, struct tegra_bo *bo) { struct tegra_drm *tegra = drm->dev_private; int err; if (tegra->domain) { - err = tegra_bo_get_pages(drm, bo, size); + err = tegra_bo_get_pages(drm, bo); if (err < 0) return err; @@ -279,6 +277,8 @@ static int tegra_bo_alloc(struct drm_device *drm, struct tegra_bo *bo, return err; } } else { + size_t size = bo->gem.size; + bo->vaddr = dma_alloc_writecombine(drm->dev, size, &bo->paddr, GFP_KERNEL | __GFP_NOWARN); if (!bo->vaddr) { @@ -302,7 +302,7 @@ struct tegra_bo *tegra_bo_create(struct drm_device *drm, size_t size, if (IS_ERR(bo)) return bo; - err = tegra_bo_alloc(drm, bo, size); + err = tegra_bo_alloc(drm, bo); if (err < 0) goto release; -- cgit v0.10.2 From 93396d0f9c027654eb09151d2e22fe78a39feedb Mon Sep 17 00:00:00 2001 From: Sean Paul Date: Wed, 19 Nov 2014 13:04:49 -0500 Subject: drm/tegra: dc: Select root window for event dispatch In finish pageflip, the driver was not selecting the root window when dispatching events. This exposed a race where a plane update would change the window selection and cause tegra_dc_finish_page_flip to check the wrong base address. This patch also protects access to the window selection register as well as the registers affected by it. Signed-off-by: Sean Paul Signed-off-by: Thierry Reding diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index ef36f9d..978993f 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -168,7 +168,7 @@ static int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, const struct tegra_dc_window *window) { unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp; - unsigned long value; + unsigned long value, flags; bool yuv, planar; /* @@ -181,6 +181,8 @@ static int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, else bpp = planar ? 1 : 2; + spin_lock_irqsave(&dc->lock, flags); + value = WINDOW_A_SELECT << index; tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER); @@ -273,6 +275,7 @@ static int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, case TEGRA_BO_TILING_MODE_BLOCK: DRM_ERROR("hardware doesn't support block linear mode\n"); + spin_unlock_irqrestore(&dc->lock, flags); return -EINVAL; } @@ -331,6 +334,8 @@ static int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, tegra_dc_window_commit(dc, index); + spin_unlock_irqrestore(&dc->lock, flags); + return 0; } @@ -338,11 +343,14 @@ static int tegra_window_plane_disable(struct drm_plane *plane) { struct tegra_dc *dc = to_tegra_dc(plane->crtc); struct tegra_plane *p = to_tegra_plane(plane); + unsigned long flags; u32 value; if (!plane->crtc) return 0; + spin_lock_irqsave(&dc->lock, flags); + value = WINDOW_A_SELECT << p->index; tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER); @@ -352,6 +360,8 @@ static int tegra_window_plane_disable(struct drm_plane *plane) tegra_dc_window_commit(dc, p->index); + spin_unlock_irqrestore(&dc->lock, flags); + return 0; } @@ -699,14 +709,16 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y, struct tegra_bo *bo = tegra_fb_get_plane(fb, 0); unsigned int h_offset = 0, v_offset = 0; struct tegra_bo_tiling tiling; + unsigned long value, flags; unsigned int format, swap; - unsigned long value; int err; err = tegra_fb_get_tiling(fb, &tiling); if (err < 0) return err; + spin_lock_irqsave(&dc->lock, flags); + tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER); value = fb->offsets[0] + y * fb->pitches[0] + @@ -752,6 +764,7 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y, case TEGRA_BO_TILING_MODE_BLOCK: DRM_ERROR("hardware doesn't support block linear mode\n"); + spin_unlock_irqrestore(&dc->lock, flags); return -EINVAL; } @@ -778,6 +791,8 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y, tegra_dc_writel(dc, value << 8, DC_CMD_STATE_CONTROL); tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); + spin_unlock_irqrestore(&dc->lock, flags); + return 0; } @@ -823,11 +838,16 @@ static void tegra_dc_finish_page_flip(struct tegra_dc *dc) bo = tegra_fb_get_plane(crtc->primary->fb, 0); + spin_lock_irqsave(&dc->lock, flags); + /* check if new start address has been latched */ + tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER); tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); base = tegra_dc_readl(dc, DC_WINBUF_START_ADDR); tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS); + spin_unlock_irqrestore(&dc->lock, flags); + if (base == bo->paddr + crtc->primary->fb->offsets[0]) { drm_crtc_send_vblank_event(crtc, dc->event); drm_crtc_vblank_put(crtc); -- cgit v0.10.2 From 264f7d673fad394d8ed6b17fa9c16e2e62fa0c4e Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Tue, 25 Nov 2014 14:30:28 +0100 Subject: drm/msm: Deletion of unnecessary checks before two function calls The functions framebuffer_release() and vunmap() perform also input parameter validation. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Reviewed-by: Thierry Reding Signed-off-by: Rob Clark diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c index 94d55e5..1f3af13 100644 --- a/drivers/gpu/drm/msm/msm_fbdev.c +++ b/drivers/gpu/drm/msm/msm_fbdev.c @@ -190,8 +190,7 @@ fail_unlock: fail: if (ret) { - if (fbi) - framebuffer_release(fbi); + framebuffer_release(fbi); if (fb) { drm_framebuffer_unregister_private(fb); drm_framebuffer_remove(fb); diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index 4a6f0e4..49dea4f 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -535,8 +535,7 @@ void msm_gem_free_object(struct drm_gem_object *obj) drm_free_large(msm_obj->pages); } else { - if (msm_obj->vaddr) - vunmap(msm_obj->vaddr); + vunmap(msm_obj->vaddr); put_pages(obj); } -- cgit v0.10.2 From 5acb07ea802c3a06bbe22cba32fbb8eb97b6b3ae Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Tue, 25 Nov 2014 13:44:20 +0100 Subject: drm/msm: Deletion of unnecessary checks before the function call "release_firmware" The release_firmware() function tests whether its argument is NULL and then returns immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Reviewed-by: Thierry Reding Signed-off-by: Rob Clark diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index aa87304..94a5bee 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -386,9 +386,7 @@ void adreno_gpu_cleanup(struct adreno_gpu *gpu) msm_gem_put_iova(gpu->memptrs_bo, gpu->base.id); drm_gem_object_unreference(gpu->memptrs_bo); } - if (gpu->pm4) - release_firmware(gpu->pm4); - if (gpu->pfp) - release_firmware(gpu->pfp); + release_firmware(gpu->pm4); + release_firmware(gpu->pfp); msm_gpu_cleanup(&gpu->base); } -- cgit v0.10.2 From 45ec9bd0fd7abf8705e7cf12205ff69fe9d51181 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 10 Dec 2014 17:06:57 +0000 Subject: dm thin: fix inability to discard blocks when in out-of-data-space mode When the pool was in PM_OUT_OF_SPACE mode its process_prepared_discard function pointer was incorrectly being set to process_prepared_discard_passdown rather than process_prepared_discard. This incorrect function pointer meant the discard was being passed down, but not effecting the mapping. As such any discard that was issued, in an attempt to reclaim blocks, would not successfully free data space. Reported-by: Eric Sandeen Signed-off-by: Joe Thornber Signed-off-by: Mike Snitzer Cc: stable@vger.kernel.org diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c index 8735543..14b51a4 100644 --- a/drivers/md/dm-thin.c +++ b/drivers/md/dm-thin.c @@ -2155,7 +2155,7 @@ static void set_pool_mode(struct pool *pool, enum pool_mode new_mode) pool->process_cell = process_cell_read_only; pool->process_discard_cell = process_discard_cell; pool->process_prepared_mapping = process_prepared_mapping; - pool->process_prepared_discard = process_prepared_discard_passdown; + pool->process_prepared_discard = process_prepared_discard; if (!pool->pf.error_if_no_space && no_space_timeout) queue_delayed_work(pool->wq, &pool->no_space_timeout, no_space_timeout); -- cgit v0.10.2 From 2c43fd26e46734430122b8d2ad3024bb532df3ef Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Thu, 11 Dec 2014 11:12:19 +0000 Subject: dm thin: fix missing out-of-data-space to write mode transition if blocks are released Discard bios and thin device deletion have the potential to release data blocks. If the thin-pool is in out-of-data-space mode, and blocks were released, transition the thin-pool back to full write mode. The correct time to do this is just after the thin-pool metadata commit. It cannot be done before the commit because the space maps will not allow immediate reuse of the data blocks in case there's a rollback following power failure. Signed-off-by: Joe Thornber Signed-off-by: Mike Snitzer Cc: stable@vger.kernel.org diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c index 14b51a4..922aa55 100644 --- a/drivers/md/dm-thin.c +++ b/drivers/md/dm-thin.c @@ -1127,6 +1127,24 @@ static void schedule_external_copy(struct thin_c *tc, dm_block_t virt_block, schedule_zero(tc, virt_block, data_dest, cell, bio); } +static void set_pool_mode(struct pool *pool, enum pool_mode new_mode); + +static void check_for_space(struct pool *pool) +{ + int r; + dm_block_t nr_free; + + if (get_pool_mode(pool) != PM_OUT_OF_DATA_SPACE) + return; + + r = dm_pool_get_free_block_count(pool->pmd, &nr_free); + if (r) + return; + + if (nr_free) + set_pool_mode(pool, PM_WRITE); +} + /* * A non-zero return indicates read_only or fail_io mode. * Many callers don't care about the return value. @@ -1141,6 +1159,8 @@ static int commit(struct pool *pool) r = dm_pool_commit_metadata(pool->pmd); if (r) metadata_operation_failed(pool, "dm_pool_commit_metadata", r); + else + check_for_space(pool); return r; } @@ -1159,8 +1179,6 @@ static void check_low_water_mark(struct pool *pool, dm_block_t free_blocks) } } -static void set_pool_mode(struct pool *pool, enum pool_mode new_mode); - static int alloc_data_block(struct thin_c *tc, dm_block_t *result) { int r; -- cgit v0.10.2 From 2b94e8960cc3f225dec058f27570505351f4bc13 Mon Sep 17 00:00:00 2001 From: Marc Dionne Date: Wed, 17 Dec 2014 07:59:59 -0500 Subject: dm thin: fix crash by initializing thin device's refcount and completion earlier Commit 80e96c5484be ("dm thin: do not allow thin device activation while pool is suspended") delayed the initialization of a new thin device's refcount and completion until after this new thin was added to the pool's active_thins list and the pool lock is released. This opens a race with a worker thread that walks the list and calls thin_get/put, noticing that the refcount goes to 0 and calling complete, freezing up the system and giving the oops below: kernel: BUG: unable to handle kernel NULL pointer dereference at (null) kernel: IP: [] __wake_up_common+0x2b/0x90 kernel: Call Trace: kernel: [] __wake_up_locked+0x13/0x20 kernel: [] complete+0x37/0x50 kernel: [] thin_put+0x20/0x30 [dm_thin_pool] kernel: [] do_worker+0x667/0x870 [dm_thin_pool] kernel: [] ? __schedule+0x3ac/0x9a0 kernel: [] process_one_work+0x14f/0x400 kernel: [] worker_thread+0x6b/0x490 kernel: [] ? rescuer_thread+0x260/0x260 kernel: [] kthread+0xdb/0x100 kernel: [] ? kthread_create_on_node+0x170/0x170 kernel: [] ret_from_fork+0x7c/0xb0 kernel: [] ? kthread_create_on_node+0x170/0x170 Set the thin device's initial refcount and initialize the completion before adding it to the pool's active_thins list in thin_ctr(). Signed-off-by: Marc Dionne Signed-off-by: Mike Snitzer diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c index 922aa55..4934789 100644 --- a/drivers/md/dm-thin.c +++ b/drivers/md/dm-thin.c @@ -3832,6 +3832,8 @@ static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv) r = -EINVAL; goto bad; } + atomic_set(&tc->refcount, 1); + init_completion(&tc->can_destroy); list_add_tail_rcu(&tc->list, &tc->pool->active_thins); spin_unlock_irqrestore(&tc->pool->lock, flags); /* @@ -3844,9 +3846,6 @@ static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv) dm_put(pool_md); - atomic_set(&tc->refcount, 1); - init_completion(&tc->can_destroy); - return 0; bad: -- cgit v0.10.2 From 5164bece1673cdf04782f8ed3fba70743700f5da Mon Sep 17 00:00:00 2001 From: zhendong chen Date: Wed, 17 Dec 2014 14:37:04 +0800 Subject: dm: fix missed error code if .end_io isn't implemented by target_type In bio-based DM's clone_endio(), when target_type doesn't implement .end_io (e.g. linear) r will be always be initialized 0. So if a WRITE SAME bio fails WRITE SAME will not be disabled as intended. Fix this by initializing r to error, rather than 0, in clone_endio(). Signed-off-by: Alex Chen Signed-off-by: Mike Snitzer Fixes: 7eee4ae2db ("dm: disable WRITE SAME if it fails") Cc: stable@vger.kernel.org diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 4c06585..b98cd9d 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -899,7 +899,7 @@ static void disable_write_same(struct mapped_device *md) static void clone_endio(struct bio *bio, int error) { - int r = 0; + int r = error; struct dm_target_io *tio = container_of(bio, struct dm_target_io, clone); struct dm_io *io = tio->io; struct mapped_device *md = tio->io->md; -- cgit v0.10.2 From 4b08eae52f2f73723dbc4dd4d251eb60a7d8c0e1 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Mon, 8 Dec 2014 10:45:43 -0500 Subject: drm/atomic: fix potential null ptr on plane enable When a plane is being enabled, plane->crtc has not been set yet. Use plane->state->crtc. Signed-off-by: Rob Clark Reviewed-by: Sean Paul Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 4a78a77..bbdbe47 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -61,7 +61,7 @@ drm_atomic_helper_plane_changed(struct drm_atomic_state *state, struct drm_crtc_state *crtc_state; if (plane->state->crtc) { - crtc_state = state->crtc_states[drm_crtc_index(plane->crtc)]; + crtc_state = state->crtc_states[drm_crtc_index(plane->state->crtc)]; if (WARN_ON(!crtc_state)) return; -- cgit v0.10.2 From 2ba353204779c81d09bb03051d8a7a4b842f9ad3 Mon Sep 17 00:00:00 2001 From: haarp Date: Wed, 17 Dec 2014 15:22:08 -0800 Subject: Input: psmouse - expose drift duration for IBM trackpoints IBM Trackpoints have a feature to compensate for drift by recalibrating themselves periodically. By default, if for 0.5 seconds there is no change in position, it's used as the new zero. This duration is too low. Often, the calibration happens when the trackpoint is in fact being used. IBM's Trackpoint Engineering Specifications show a configuration register that allows changing this duration, rstdft1. Expose it via sysfs among the other settings. Signed-off-by: Mike Murdoch Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/mouse/trackpoint.c b/drivers/input/mouse/trackpoint.c index 30c8b69..354d47e 100644 --- a/drivers/input/mouse/trackpoint.c +++ b/drivers/input/mouse/trackpoint.c @@ -227,6 +227,7 @@ TRACKPOINT_INT_ATTR(thresh, TP_THRESH, TP_DEF_THRESH); TRACKPOINT_INT_ATTR(upthresh, TP_UP_THRESH, TP_DEF_UP_THRESH); TRACKPOINT_INT_ATTR(ztime, TP_Z_TIME, TP_DEF_Z_TIME); TRACKPOINT_INT_ATTR(jenks, TP_JENKS_CURV, TP_DEF_JENKS_CURV); +TRACKPOINT_INT_ATTR(drift_time, TP_DRIFT_TIME, TP_DEF_DRIFT_TIME); TRACKPOINT_BIT_ATTR(press_to_select, TP_TOGGLE_PTSON, TP_MASK_PTSON, 0, TP_DEF_PTSON); @@ -246,6 +247,7 @@ static struct attribute *trackpoint_attrs[] = { &psmouse_attr_upthresh.dattr.attr, &psmouse_attr_ztime.dattr.attr, &psmouse_attr_jenks.dattr.attr, + &psmouse_attr_drift_time.dattr.attr, &psmouse_attr_press_to_select.dattr.attr, &psmouse_attr_skipback.dattr.attr, &psmouse_attr_ext_dev.dattr.attr, @@ -312,6 +314,7 @@ static int trackpoint_sync(struct psmouse *psmouse, bool in_power_on_state) TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, upthresh); TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, ztime); TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, jenks); + TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, drift_time); /* toggles */ TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, press_to_select); @@ -332,6 +335,7 @@ static void trackpoint_defaults(struct trackpoint_data *tp) TRACKPOINT_SET_POWER_ON_DEFAULT(tp, upthresh); TRACKPOINT_SET_POWER_ON_DEFAULT(tp, ztime); TRACKPOINT_SET_POWER_ON_DEFAULT(tp, jenks); + TRACKPOINT_SET_POWER_ON_DEFAULT(tp, drift_time); TRACKPOINT_SET_POWER_ON_DEFAULT(tp, inertia); /* toggles */ diff --git a/drivers/input/mouse/trackpoint.h b/drivers/input/mouse/trackpoint.h index ecd0547..5617ed3 100644 --- a/drivers/input/mouse/trackpoint.h +++ b/drivers/input/mouse/trackpoint.h @@ -70,6 +70,9 @@ #define TP_UP_THRESH 0x5A /* Used to generate a 'click' on Z-axis */ #define TP_Z_TIME 0x5E /* How sharp of a press */ #define TP_JENKS_CURV 0x5D /* Minimum curvature for double click */ +#define TP_DRIFT_TIME 0x5F /* How long a 'hands off' condition */ + /* must last (x*107ms) for drift */ + /* correction to occur */ /* * Toggling Flag bits @@ -120,6 +123,7 @@ #define TP_DEF_UP_THRESH 0xFF #define TP_DEF_Z_TIME 0x26 #define TP_DEF_JENKS_CURV 0x87 +#define TP_DEF_DRIFT_TIME 0x05 /* Toggles */ #define TP_DEF_MB 0x00 @@ -137,6 +141,7 @@ struct trackpoint_data unsigned char draghys, mindrag; unsigned char thresh, upthresh; unsigned char ztime, jenks; + unsigned char drift_time; /* toggles */ unsigned char press_to_select; -- cgit v0.10.2 From aac8bcf1ed3e2e97da0ec7e859d20fe3fa76bd97 Mon Sep 17 00:00:00 2001 From: Aniroop Mathur Date: Wed, 17 Dec 2014 15:33:06 -0800 Subject: Input: evdev - add CLOCK_BOOTTIME support This patch adds support for CLOCK_BOOTTIME for input event timestamp. CLOCK_BOOTTIME includes suspend time, so it would allow aplications to get correct time difference between two events even when system resumes from suspend state. Signed-off-by: Aniroop Mathur Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index bc20348..b1a52ab 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -28,6 +28,13 @@ #include #include "input-compat.h" +enum evdev_clock_type { + EV_CLK_REAL = 0, + EV_CLK_MONO, + EV_CLK_BOOT, + EV_CLK_MAX +}; + struct evdev { int open; struct input_handle handle; @@ -49,12 +56,32 @@ struct evdev_client { struct fasync_struct *fasync; struct evdev *evdev; struct list_head node; - int clkid; + int clk_type; bool revoked; unsigned int bufsize; struct input_event buffer[]; }; +static int evdev_set_clk_type(struct evdev_client *client, unsigned int clkid) +{ + switch (clkid) { + + case CLOCK_REALTIME: + client->clk_type = EV_CLK_REAL; + break; + case CLOCK_MONOTONIC: + client->clk_type = EV_CLK_MONO; + break; + case CLOCK_BOOTTIME: + client->clk_type = EV_CLK_BOOT; + break; + default: + return -EINVAL; + } + + return 0; +} + /* flush queued events of type @type, caller must hold client->buffer_lock */ static void __evdev_flush_queue(struct evdev_client *client, unsigned int type) { @@ -108,8 +135,11 @@ static void evdev_queue_syn_dropped(struct evdev_client *client) struct input_event ev; ktime_t time; - time = (client->clkid == CLOCK_MONOTONIC) ? - ktime_get() : ktime_get_real(); + time = client->clk_type == EV_CLK_REAL ? + ktime_get_real() : + client->clk_type == EV_CLK_MONO ? + ktime_get() : + ktime_get_boottime(); ev.time = ktime_to_timeval(time); ev.type = EV_SYN; @@ -159,7 +189,7 @@ static void __pass_event(struct evdev_client *client, static void evdev_pass_values(struct evdev_client *client, const struct input_value *vals, unsigned int count, - ktime_t mono, ktime_t real) + ktime_t *ev_time) { struct evdev *evdev = client->evdev; const struct input_value *v; @@ -169,8 +199,7 @@ static void evdev_pass_values(struct evdev_client *client, if (client->revoked) return; - event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ? - mono : real); + event.time = ktime_to_timeval(ev_time[client->clk_type]); /* Interrupts are disabled, just acquire the lock. */ spin_lock(&client->buffer_lock); @@ -198,21 +227,22 @@ static void evdev_events(struct input_handle *handle, { struct evdev *evdev = handle->private; struct evdev_client *client; - ktime_t time_mono, time_real; + ktime_t ev_time[EV_CLK_MAX]; - time_mono = ktime_get(); - time_real = ktime_mono_to_real(time_mono); + ev_time[EV_CLK_MONO] = ktime_get(); + ev_time[EV_CLK_REAL] = ktime_mono_to_real(ev_time[EV_CLK_MONO]); + ev_time[EV_CLK_BOOT] = ktime_mono_to_any(ev_time[EV_CLK_MONO], + TK_OFFS_BOOT); rcu_read_lock(); client = rcu_dereference(evdev->grab); if (client) - evdev_pass_values(client, vals, count, time_mono, time_real); + evdev_pass_values(client, vals, count, ev_time); else list_for_each_entry_rcu(client, &evdev->client_list, node) - evdev_pass_values(client, vals, count, - time_mono, time_real); + evdev_pass_values(client, vals, count, ev_time); rcu_read_unlock(); } @@ -877,10 +907,8 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, case EVIOCSCLOCKID: if (copy_from_user(&i, p, sizeof(unsigned int))) return -EFAULT; - if (i != CLOCK_MONOTONIC && i != CLOCK_REALTIME) - return -EINVAL; - client->clkid = i; - return 0; + + return evdev_set_clk_type(client, i); case EVIOCGKEYCODE: return evdev_handle_get_keycode(dev, p); -- cgit v0.10.2 From b02ded246d011d0eb22efc178ee711b636214083 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 16 Dec 2014 15:09:36 -0800 Subject: PM / OPP: add some lockdep annotations Certain OPP APIs need to be called under RCU lock; let's add a few rcu_lockdep_assert() calls to warn about potential misuse. Signed-off-by: Dmitry Torokhov Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index d24dd614a..b78c14d 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -108,6 +108,14 @@ static LIST_HEAD(dev_opp_list); /* Lock to allow exclusive modification to the device and opp lists */ static DEFINE_MUTEX(dev_opp_list_lock); +#define opp_rcu_lockdep_assert() \ +do { \ + rcu_lockdep_assert(rcu_read_lock_held() || \ + lockdep_is_held(&dev_opp_list_lock), \ + "Missing rcu_read_lock() or " \ + "dev_opp_list_lock protection"); \ +} while (0) + /** * find_device_opp() - find device_opp struct using device pointer * @dev: device pointer used to lookup device OPPs @@ -218,6 +226,8 @@ int dev_pm_opp_get_opp_count(struct device *dev) struct dev_pm_opp *temp_opp; int count = 0; + opp_rcu_lockdep_assert(); + dev_opp = find_device_opp(dev); if (IS_ERR(dev_opp)) { int r = PTR_ERR(dev_opp); @@ -267,6 +277,8 @@ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, struct device_opp *dev_opp; struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); + opp_rcu_lockdep_assert(); + dev_opp = find_device_opp(dev); if (IS_ERR(dev_opp)) { int r = PTR_ERR(dev_opp); @@ -313,6 +325,8 @@ struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev, struct device_opp *dev_opp; struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); + opp_rcu_lockdep_assert(); + if (!dev || !freq) { dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq); return ERR_PTR(-EINVAL); @@ -361,6 +375,8 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, struct device_opp *dev_opp; struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); + opp_rcu_lockdep_assert(); + if (!dev || !freq) { dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq); return ERR_PTR(-EINVAL); -- cgit v0.10.2 From 0fe30da2cb43782ee62d30c00a273d6934e5370e Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 16 Dec 2014 15:09:37 -0800 Subject: PM / OPP: fix warning in of_free_opp_table() Not having OPP defined for a device is not a crime, we should not splat warning in this case. Also, it seems that we are ready to accept invalid dev (find_device_opp will return ERR_PTR(-EINVAL) then) so let's not crash in dev_name() in such case. Signed-off-by: Dmitry Torokhov Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index b78c14d..aac7abc 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -799,9 +799,15 @@ void of_free_opp_table(struct device *dev) /* Check for existing list for 'dev' */ dev_opp = find_device_opp(dev); - if (WARN(IS_ERR(dev_opp), "%s: dev_opp: %ld\n", dev_name(dev), - PTR_ERR(dev_opp))) + if (IS_ERR(dev_opp)) { + int error = PTR_ERR(dev_opp); + if (error != -ENODEV) + WARN(1, "%s: dev_opp: %d\n", + IS_ERR_OR_NULL(dev) ? + "Invalid device" : dev_name(dev), + error); return; + } /* Hold our list modification lock here */ mutex_lock(&dev_opp_list_lock); -- cgit v0.10.2 From b4718c02f49ab5e1452353f0fae78beabe81467c Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 16 Dec 2014 15:09:38 -0800 Subject: PM / OPP: take RCU lock in dev_pm_opp_get_opp_count A lot of callers are missing the fact that dev_pm_opp_get_opp_count needs to be called under RCU lock. Given that RCU locks can safely be nested, instead of providing *_locked() API, let's take RCU lock inside dev_pm_opp_get_opp_count() and leave callers as is. Signed-off-by: Dmitry Torokhov Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index aac7abc..106c693 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -216,9 +216,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq); * This function returns the number of available opps if there are any, * else returns 0 if none or the corresponding error value. * - * Locking: This function must be called under rcu_read_lock(). This function - * internally references two RCU protected structures: device_opp and opp which - * are safe as long as we are under a common RCU locked section. + * Locking: This function takes rcu_read_lock(). */ int dev_pm_opp_get_opp_count(struct device *dev) { @@ -226,13 +224,14 @@ int dev_pm_opp_get_opp_count(struct device *dev) struct dev_pm_opp *temp_opp; int count = 0; - opp_rcu_lockdep_assert(); + rcu_read_lock(); dev_opp = find_device_opp(dev); if (IS_ERR(dev_opp)) { - int r = PTR_ERR(dev_opp); - dev_err(dev, "%s: device OPP not found (%d)\n", __func__, r); - return r; + count = PTR_ERR(dev_opp); + dev_err(dev, "%s: device OPP not found (%d)\n", + __func__, count); + goto out_unlock; } list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) { @@ -240,6 +239,8 @@ int dev_pm_opp_get_opp_count(struct device *dev) count++; } +out_unlock: + rcu_read_unlock(); return count; } EXPORT_SYMBOL_GPL(dev_pm_opp_get_opp_count); -- cgit v0.10.2 From 62a041a4f58f32989460e37cb4f9aed5183f357f Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 16 Dec 2014 15:09:39 -0800 Subject: cpufreq-dt: defer probing if OPP table is not ready cpufreq-dt driver supports mode when OPP table is provided by platform code and not device tree. However on certain platforms code that fills OPP table may run after cpufreq driver tries to initialize, so let's report -EPROBE_DEFER if we do not find any entires in OPP table for the CPU. Signed-off-by: Dmitry Torokhov Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c index 9bc2720..538abd5 100644 --- a/drivers/cpufreq/cpufreq-dt.c +++ b/drivers/cpufreq/cpufreq-dt.c @@ -211,6 +211,17 @@ static int cpufreq_init(struct cpufreq_policy *policy) /* OPPs might be populated at runtime, don't check for error here */ of_init_opp_table(cpu_dev); + /* + * But we need OPP table to function so if it is not there let's + * give platform code chance to provide it for us. + */ + ret = dev_pm_opp_get_opp_count(cpu_dev); + if (ret <= 0) { + pr_debug("OPP table is not ready, deferring probe\n"); + ret = -EPROBE_DEFER; + goto out_free_opp; + } + priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { ret = -ENOMEM; -- cgit v0.10.2 From 8b23811535d2e1dd6abbe4ce6ea1edfd50ce72de Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 18 Dec 2014 09:52:59 -0800 Subject: Input: alps - v7: ignore new packets NEW packets are send to indicate a discontinuity in the finger coordinate reporting. Specifically a finger may have moved from slot 0 to 1 or vice versa. INPUT_MT_TRACK takes care of this for us. NEW packets have 3 problems: 1) They do not contain middle / right button info (on non clickpads) this can be worked around by preserving the old button state 2) They do not contain an accurate fingercount, and they are typically send when the number of fingers changes. We cannot use the old finger count as that may mismatch with the amount of touch coordinates we've available in the NEW packet 3) Their x data for the second touch is inaccurate leading to a possible jump of the x coordinate by 16 units when the first non NEW packet comes in Since problems 2 & 3 cannot be worked around, just ignore them. BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=86338 Cc: stable@vger.kernel.org # 3.17 Signed-off-by: Hans de Goede Tested-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 35a49bf..49e6220 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -938,18 +938,36 @@ static int alps_decode_packet_v7(struct alps_fields *f, return 0; if (pkt_id == V7_PACKET_ID_UNKNOWN) return -1; + /* + * NEW packets are send to indicate a discontinuity in the finger + * coordinate reporting. Specifically a finger may have moved from + * slot 0 to 1 or vice versa. INPUT_MT_TRACK takes care of this for + * us. + * + * NEW packets have 3 problems: + * 1) They do not contain middle / right button info (on non clickpads) + * this can be worked around by preserving the old button state + * 2) They do not contain an accurate fingercount, and they are + * typically send when the number of fingers changes. We cannot use + * the old finger count as that may mismatch with the amount of + * touch coordinates we've available in the NEW packet + * 3) Their x data for the second touch is inaccurate leading to + * a possible jump of the x coordinate by 16 units when the first + * non NEW packet comes in + * Since problems 2 & 3 cannot be worked around, just ignore them. + */ + if (pkt_id == V7_PACKET_ID_NEW) + return 1; alps_get_finger_coordinate_v7(f->mt, p, pkt_id); - if (pkt_id == V7_PACKET_ID_TWO || pkt_id == V7_PACKET_ID_MULTI) { - f->left = (p[0] & 0x80) >> 7; - f->right = (p[0] & 0x20) >> 5; - f->middle = (p[0] & 0x10) >> 4; - } + f->left = (p[0] & 0x80) >> 7; + f->right = (p[0] & 0x20) >> 5; + f->middle = (p[0] & 0x10) >> 4; if (pkt_id == V7_PACKET_ID_TWO) f->fingers = alps_get_mt_count(f->mt); - else if (pkt_id == V7_PACKET_ID_MULTI) + else /* pkt_id == V7_PACKET_ID_MULTI */ f->fingers = 3 + (p[5] & 0x03); return 0; -- cgit v0.10.2 From 7091c443dda8c6c6d8e70e33452252f9ad3e7814 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 18 Dec 2014 09:53:34 -0800 Subject: Input: alps - v7: sometimes a single touch is reported in mt[1] The v7 proto differentiates between a primary touch (with high precision) and a secondary touch (with lower precision). Normally when 2 fingers are down and one is lifted the still present touch becomes the primary touch, but some traces have shown that this does not happen always. This commit deals with this by making alps_get_mt_count() not stop at the first empty mt slot, and if a touch is present in mt[1] and not mt[0] moving the data to mt[0] (for input_mt_assign_slots). BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=86338 Cc: stable@vger.kernel.org # 3.17 Signed-off-by: Hans de Goede Tested-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 49e6220..bfa62a6 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -919,12 +919,14 @@ static void alps_get_finger_coordinate_v7(struct input_mt_pos *mt, static int alps_get_mt_count(struct input_mt_pos *mt) { - int i; + int i, fingers = 0; - for (i = 0; i < MAX_TOUCHES && mt[i].x != 0 && mt[i].y != 0; i++) - /* empty */; + for (i = 0; i < MAX_TOUCHES; i++) { + if (mt[i].x != 0 || mt[i].y != 0) + fingers++; + } - return i; + return fingers; } static int alps_decode_packet_v7(struct alps_fields *f, @@ -970,6 +972,14 @@ static int alps_decode_packet_v7(struct alps_fields *f, else /* pkt_id == V7_PACKET_ID_MULTI */ f->fingers = 3 + (p[5] & 0x03); + /* Sometimes a single touch is reported in mt[1] rather then mt[0] */ + if (f->fingers == 1 && f->mt[0].x == 0 && f->mt[0].y == 0) { + f->mt[0].x = f->mt[1].x; + f->mt[0].y = f->mt[1].y; + f->mt[1].x = 0; + f->mt[1].y = 0; + } + return 0; } -- cgit v0.10.2 From d27eb7931c98a1ebfc9b2fcc48939846bcbfc804 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 18 Dec 2014 09:55:14 -0800 Subject: Input: alps - v7: fix finger counting for > 2 fingers on clickpads Protocol v7 uses the middle / right button bits on clickpads to communicate "location" information of a 3th touch (and possible 4th) touch on clickpads. Specifically when 3 touches are down, if one of the 3 touches is in the left / right button area, this will get reported in the middle / right button bits and the touchpad will still send a TWO type packet rather then a MULTI type packet, so when this happens we must add the finger reported in the button area to the finger count. Likewise we must also add fingers reported this way to the finger count when we get MULTI packets. BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=86338 Cc: stable@vger.kernel.org # 3.17 Signed-off-by: Hans de Goede Tested-by: Benjamin Tissoires Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index bfa62a6..b48c6fb 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -933,6 +933,7 @@ static int alps_decode_packet_v7(struct alps_fields *f, unsigned char *p, struct psmouse *psmouse) { + struct alps_data *priv = psmouse->private; unsigned char pkt_id; pkt_id = alps_get_packet_id_v7(p); @@ -963,15 +964,22 @@ static int alps_decode_packet_v7(struct alps_fields *f, alps_get_finger_coordinate_v7(f->mt, p, pkt_id); - f->left = (p[0] & 0x80) >> 7; - f->right = (p[0] & 0x20) >> 5; - f->middle = (p[0] & 0x10) >> 4; - if (pkt_id == V7_PACKET_ID_TWO) f->fingers = alps_get_mt_count(f->mt); else /* pkt_id == V7_PACKET_ID_MULTI */ f->fingers = 3 + (p[5] & 0x03); + f->left = (p[0] & 0x80) >> 7; + if (priv->flags & ALPS_BUTTONPAD) { + if (p[0] & 0x20) + f->fingers++; + if (p[0] & 0x10) + f->fingers++; + } else { + f->right = (p[0] & 0x20) >> 5; + f->middle = (p[0] & 0x10) >> 4; + } + /* Sometimes a single touch is reported in mt[1] rather then mt[0] */ if (f->fingers == 1 && f->mt[0].x == 0 && f->mt[0].y == 0) { f->mt[0].x = f->mt[1].x; -- cgit v0.10.2 From 27a560ba1d4f0a07a36e1de2cae839abe776e8f3 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 18 Dec 2014 09:54:50 -0800 Subject: Input: alps - v7: document the v7 touchpad packet protocol Add a table documenting where all the bits are in the v7 touchpad packets. Signed-off-by: Hans de Goede Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index b48c6fb..0faea6d 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -881,6 +881,34 @@ static void alps_get_finger_coordinate_v7(struct input_mt_pos *mt, unsigned char *pkt, unsigned char pkt_id) { + /* + * packet-fmt b7 b6 b5 b4 b3 b2 b1 b0 + * Byte0 TWO & MULTI L 1 R M 1 Y0-2 Y0-1 Y0-0 + * Byte0 NEW L 1 X1-5 1 1 Y0-2 Y0-1 Y0-0 + * Byte1 Y0-10 Y0-9 Y0-8 Y0-7 Y0-6 Y0-5 Y0-4 Y0-3 + * Byte2 X0-11 1 X0-10 X0-9 X0-8 X0-7 X0-6 X0-5 + * Byte3 X1-11 1 X0-4 X0-3 1 X0-2 X0-1 X0-0 + * Byte4 TWO X1-10 TWO X1-9 X1-8 X1-7 X1-6 X1-5 X1-4 + * Byte4 MULTI X1-10 TWO X1-9 X1-8 X1-7 X1-6 Y1-5 1 + * Byte4 NEW X1-10 TWO X1-9 X1-8 X1-7 X1-6 0 0 + * Byte5 TWO & NEW Y1-10 0 Y1-9 Y1-8 Y1-7 Y1-6 Y1-5 Y1-4 + * Byte5 MULTI Y1-10 0 Y1-9 Y1-8 Y1-7 Y1-6 F-1 F-0 + * L: Left button + * R / M: Non-clickpads: Right / Middle button + * Clickpads: When > 2 fingers are down, and some fingers + * are in the button area, then the 2 coordinates reported + * are for fingers outside the button area and these report + * extra fingers being present in the right / left button + * area. Note these fingers are not added to the F field! + * so if a TWO packet is received and R = 1 then there are + * 3 fingers down, etc. + * TWO: 1: Two touches present, byte 0/4/5 are in TWO fmt + * 0: If byte 4 bit 0 is 1, then byte 0/4/5 are in MULTI fmt + * otherwise byte 0 bit 4 must be set and byte 0/4/5 are + * in NEW fmt + * F: Number of fingers - 3, 0 means 3 fingers, 1 means 4 ... + */ + mt[0].x = ((pkt[2] & 0x80) << 4); mt[0].x |= ((pkt[2] & 0x3F) << 5); mt[0].x |= ((pkt[3] & 0x30) >> 1); -- cgit v0.10.2 From f86afecf0defbc8d046bc7a7c5fc19a8c9ba1364 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 25 Nov 2014 12:41:18 -0500 Subject: drm/msm: block incoming update on pending updates We can't have multiple updates pending on a given CRTC, and we don't want a sync update to race w/ an async update that preceeded it. So keep track of which CRTCs have updates in flight, and block later updates that would conflict. Signed-off-by: Rob Clark diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c index a7672e1..3449213 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c @@ -331,17 +331,8 @@ static int mdp4_crtc_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state) { struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); - struct drm_device *dev = crtc->dev; - DBG("%s: check", mdp4_crtc->name); - - if (mdp4_crtc->event) { - dev_err(dev->dev, "already pending flip!\n"); - return -EBUSY; - } - // TODO anything else to check? - return 0; } @@ -357,7 +348,7 @@ static void mdp4_crtc_atomic_flush(struct drm_crtc *crtc) struct drm_device *dev = crtc->dev; unsigned long flags; - DBG("%s: flush", mdp4_crtc->name); + DBG("%s: event: %p", mdp4_crtc->name, crtc->state->event); WARN_ON(mdp4_crtc->event); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index 0e9a2e3..930bcec 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -303,11 +303,6 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc, DBG("%s: check", mdp5_crtc->name); - if (mdp5_crtc->event) { - dev_err(dev->dev, "already pending flip!\n"); - return -EBUSY; - } - /* request a free CTL, if none is already allocated for this CRTC */ if (state->enable && !mdp5_crtc->ctl) { mdp5_crtc->ctl = mdp5_ctlm_request(mdp5_kms->ctlm, crtc); @@ -364,7 +359,7 @@ static void mdp5_crtc_atomic_flush(struct drm_crtc *crtc) struct drm_device *dev = crtc->dev; unsigned long flags; - DBG("%s: flush", mdp5_crtc->name); + DBG("%s: event: %p", mdp5_crtc->name, crtc->state->event); WARN_ON(mdp5_crtc->event); diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c index f0de412..1919682 100644 --- a/drivers/gpu/drm/msm/msm_atomic.c +++ b/drivers/gpu/drm/msm/msm_atomic.c @@ -23,10 +23,41 @@ struct msm_commit { struct drm_atomic_state *state; uint32_t fence; struct msm_fence_cb fence_cb; + uint32_t crtc_mask; }; static void fence_cb(struct msm_fence_cb *cb); +/* block until specified crtcs are no longer pending update, and + * atomically mark them as pending update + */ +static int start_atomic(struct msm_drm_private *priv, uint32_t crtc_mask) +{ + int ret; + + spin_lock(&priv->pending_crtcs_event.lock); + ret = wait_event_interruptible_locked(priv->pending_crtcs_event, + !(priv->pending_crtcs & crtc_mask)); + if (ret == 0) { + DBG("start: %08x", crtc_mask); + priv->pending_crtcs |= crtc_mask; + } + spin_unlock(&priv->pending_crtcs_event.lock); + + return ret; +} + +/* clear specified crtcs (no longer pending update) + */ +static void end_atomic(struct msm_drm_private *priv, uint32_t crtc_mask) +{ + spin_lock(&priv->pending_crtcs_event.lock); + DBG("end: %08x", crtc_mask); + priv->pending_crtcs &= ~crtc_mask; + wake_up_all_locked(&priv->pending_crtcs_event); + spin_unlock(&priv->pending_crtcs_event.lock); +} + static struct msm_commit *new_commit(struct drm_atomic_state *state) { struct msm_commit *c = kzalloc(sizeof(*c), GFP_KERNEL); @@ -58,12 +89,27 @@ static void complete_commit(struct msm_commit *c) drm_atomic_helper_commit_post_planes(dev, state); + /* NOTE: _wait_for_vblanks() only waits for vblank on + * enabled CRTCs. So we end up faulting when disabling + * due to (potentially) unref'ing the outgoing fb's + * before the vblank when the disable has latched. + * + * But if it did wait on disabled (or newly disabled) + * CRTCs, that would be racy (ie. we could have missed + * the irq. We need some way to poll for pipe shut + * down. Or just live with occasionally hitting the + * timeout in the CRTC disable path (which really should + * not be critical path) + */ + drm_atomic_helper_wait_for_vblanks(dev, state); drm_atomic_helper_cleanup_planes(dev, state); drm_atomic_state_free(state); + end_atomic(dev->dev_private, c->crtc_mask); + kfree(c); } @@ -97,8 +143,9 @@ static void add_fb(struct msm_commit *c, struct drm_framebuffer *fb) int msm_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state, bool async) { - struct msm_commit *c; int nplanes = dev->mode_config.num_total_plane; + int ncrtcs = dev->mode_config.num_crtc; + struct msm_commit *c; int i, ret; ret = drm_atomic_helper_prepare_planes(dev, state); @@ -106,6 +153,18 @@ int msm_atomic_commit(struct drm_device *dev, return ret; c = new_commit(state); + if (!c) + return -ENOMEM; + + /* + * Figure out what crtcs we have: + */ + for (i = 0; i < ncrtcs; i++) { + struct drm_crtc *crtc = state->crtcs[i]; + if (!crtc) + continue; + c->crtc_mask |= (1 << drm_crtc_index(crtc)); + } /* * Figure out what fence to wait for: @@ -122,6 +181,14 @@ int msm_atomic_commit(struct drm_device *dev, } /* + * Wait for pending updates on any of the same crtc's and then + * mark our set of crtc's as busy: + */ + ret = start_atomic(dev->dev_private, c->crtc_mask); + if (ret) + return ret; + + /* * 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. diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index d3b791b..7e1c71e 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -193,6 +193,7 @@ static int msm_load(struct drm_device *dev, unsigned long flags) priv->wq = alloc_ordered_workqueue("msm", 0); init_waitqueue_head(&priv->fence_event); + init_waitqueue_head(&priv->pending_crtcs_event); INIT_LIST_HEAD(&priv->inactive_list); INIT_LIST_HEAD(&priv->fence_cbs); diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index 1363038..b69ef2d 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -96,6 +96,10 @@ struct msm_drm_private { /* callbacks deferred until bo is inactive: */ struct list_head fence_cbs; + /* crtcs pending async atomic updates: */ + uint32_t pending_crtcs; + wait_queue_head_t pending_crtcs_event; + /* registered MMUs: */ unsigned int num_mmus; struct msm_mmu *mmus[NUM_DOMAINS]; -- cgit v0.10.2 From 8bc1fe92e15742985fcbd543df6954dae5f6b33e Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 2 Dec 2014 11:50:06 -0500 Subject: drm/msm/mdp5: update irqs on crtc<->encoder link change If crtc <-> encoder linkage changes, we could end up with the CRTC listening for the wrong error or vsync irqs. Generally this problem would correct itself relatively quickly, since we update the global irqmask after dispatching irqs, but to be sure let the CRTC trigger update_irq(). Signed-off-by: Rob Clark diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index 930bcec..f021f96 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -455,10 +455,7 @@ void mdp5_crtc_set_intf(struct drm_crtc *crtc, int intf, /* now that we know what irq's we want: */ mdp5_crtc->err.irqmask = intf2err(intf); mdp5_crtc->vblank.irqmask = intf2vblank(intf); - - /* when called from modeset_init(), skip the rest until later: */ - if (!mdp5_kms) - return; + mdp_irq_update(&mdp5_kms->base); spin_lock_irqsave(&mdp5_kms->resource_lock, flags); intf_sel = mdp5_read(mdp5_kms, REG_MDP5_DISP_INTF_SEL); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c index a11f1b8..9f01a4f 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c @@ -216,17 +216,7 @@ static int modeset_init(struct mdp5_kms *mdp5_kms) goto fail; } - /* NOTE: the vsync and error irq's are actually associated with - * the INTF/encoder.. the easiest way to deal with this (ie. what - * we do now) is assume a fixed relationship between crtc's and - * encoders. I'm not sure if there is ever a need to more freely - * assign crtcs to encoders, but if there is then we need to take - * care of error and vblank irq's that the crtc has registered, - * and also update user-requested vblank_mask. - */ - encoder->possible_crtcs = BIT(0); - mdp5_crtc_set_intf(priv->crtcs[0], 3, INTF_HDMI); - + encoder->possible_crtcs = (1 << priv->num_crtcs) - 1;; priv->encoders[priv->num_encoders++] = encoder; /* Construct bridge/connector for HDMI: */ diff --git a/drivers/gpu/drm/msm/mdp/mdp_kms.c b/drivers/gpu/drm/msm/mdp/mdp_kms.c index 03455b6..2a73172 100644 --- a/drivers/gpu/drm/msm/mdp/mdp_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp_kms.c @@ -42,7 +42,10 @@ static void update_irq(struct mdp_kms *mdp_kms) mdp_kms->funcs->set_irqmask(mdp_kms, irqmask); } -static void update_irq_unlocked(struct mdp_kms *mdp_kms) +/* if an mdp_irq's irqmask has changed, such as when mdp5 crtc<->encoder + * link changes, this must be called to figure out the new global irqmask + */ +void mdp_irq_update(struct mdp_kms *mdp_kms) { unsigned long flags; spin_lock_irqsave(&list_lock, flags); @@ -122,7 +125,7 @@ void mdp_irq_register(struct mdp_kms *mdp_kms, struct mdp_irq *irq) spin_unlock_irqrestore(&list_lock, flags); if (needs_update) - update_irq_unlocked(mdp_kms); + mdp_irq_update(mdp_kms); } void mdp_irq_unregister(struct mdp_kms *mdp_kms, struct mdp_irq *irq) @@ -141,5 +144,5 @@ void mdp_irq_unregister(struct mdp_kms *mdp_kms, struct mdp_irq *irq) spin_unlock_irqrestore(&list_lock, flags); if (needs_update) - update_irq_unlocked(mdp_kms); + mdp_irq_update(mdp_kms); } diff --git a/drivers/gpu/drm/msm/mdp/mdp_kms.h b/drivers/gpu/drm/msm/mdp/mdp_kms.h index 99557b5..b268ce9 100644 --- a/drivers/gpu/drm/msm/mdp/mdp_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp_kms.h @@ -75,7 +75,7 @@ void mdp_update_vblank_mask(struct mdp_kms *mdp_kms, uint32_t mask, bool enable) void mdp_irq_wait(struct mdp_kms *mdp_kms, uint32_t irqmask); void mdp_irq_register(struct mdp_kms *mdp_kms, struct mdp_irq *irq); void mdp_irq_unregister(struct mdp_kms *mdp_kms, struct mdp_irq *irq); - +void mdp_irq_update(struct mdp_kms *mdp_kms); /* * pixel format helpers: -- cgit v0.10.2 From e6d7a16f5ff7d048fbf4b4791825f5b91ebe21ab Mon Sep 17 00:00:00 2001 From: Jilai Wang Date: Mon, 1 Dec 2014 15:10:37 -0500 Subject: drm/msm/hdmi: enable regulators before clocks to avoid warnings HPD regulators need to be enabled before clocks, otherwise clock driver will report warning. Signed-off-by: Jilai Wang diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c index fbebb04..08eaee3 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c @@ -141,6 +141,15 @@ static int hpd_enable(struct hdmi_connector *hdmi_connector) uint32_t hpd_ctrl; int i, ret; + for (i = 0; i < config->hpd_reg_cnt; i++) { + ret = regulator_enable(hdmi->hpd_regs[i]); + if (ret) { + dev_err(dev->dev, "failed to enable hpd regulator: %s (%d)\n", + config->hpd_reg_names[i], ret); + goto fail; + } + } + ret = gpio_config(hdmi, true); if (ret) { dev_err(dev->dev, "failed to configure GPIOs: %d\n", ret); @@ -164,15 +173,6 @@ static int hpd_enable(struct hdmi_connector *hdmi_connector) } } - for (i = 0; i < config->hpd_reg_cnt; i++) { - ret = regulator_enable(hdmi->hpd_regs[i]); - if (ret) { - dev_err(dev->dev, "failed to enable hpd regulator: %s (%d)\n", - config->hpd_reg_names[i], ret); - goto fail; - } - } - hdmi_set_mode(hdmi, false); phy->funcs->reset(phy); hdmi_set_mode(hdmi, true); @@ -200,7 +200,7 @@ fail: return ret; } -static int hdp_disable(struct hdmi_connector *hdmi_connector) +static void hdp_disable(struct hdmi_connector *hdmi_connector) { struct hdmi *hdmi = hdmi_connector->hdmi; const struct hdmi_platform_config *config = hdmi->config; @@ -212,28 +212,19 @@ static int hdp_disable(struct hdmi_connector *hdmi_connector) hdmi_set_mode(hdmi, false); - for (i = 0; i < config->hpd_reg_cnt; i++) { - ret = regulator_disable(hdmi->hpd_regs[i]); - if (ret) { - dev_err(dev->dev, "failed to disable hpd regulator: %s (%d)\n", - config->hpd_reg_names[i], ret); - goto fail; - } - } - for (i = 0; i < config->hpd_clk_cnt; i++) clk_disable_unprepare(hdmi->hpd_clks[i]); ret = gpio_config(hdmi, false); - if (ret) { - dev_err(dev->dev, "failed to unconfigure GPIOs: %d\n", ret); - goto fail; - } - - return 0; + if (ret) + dev_warn(dev->dev, "failed to unconfigure GPIOs: %d\n", ret); -fail: - return ret; + for (i = 0; i < config->hpd_reg_cnt; i++) { + ret = regulator_disable(hdmi->hpd_regs[i]); + if (ret) + dev_warn(dev->dev, "failed to disable hpd regulator: %s (%d)\n", + config->hpd_reg_names[i], ret); + } } static void -- cgit v0.10.2 From ff2f974e5c9d6b67444364605c758a9707edf1ca Mon Sep 17 00:00:00 2001 From: Jilai Wang Date: Mon, 1 Dec 2014 15:12:23 -0500 Subject: drm/msm/hdmi: rework HDMI IRQ handler Disable the HPD interrupt when acking it, to avoid spurious interrupt. Signed-off-by: Jilai Wang diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c index 08eaee3..b4e70e0 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c @@ -251,11 +251,11 @@ void hdmi_connector_irq(struct drm_connector *connector) (hpd_int_status & HDMI_HPD_INT_STATUS_INT)) { bool detected = !!(hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED); - DBG("status=%04x, ctrl=%04x", hpd_int_status, hpd_int_ctrl); - - /* ack the irq: */ + /* ack & disable (temporarily) HPD events: */ hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, - hpd_int_ctrl | HDMI_HPD_INT_CTRL_INT_ACK); + HDMI_HPD_INT_CTRL_INT_ACK); + + DBG("status=%04x, ctrl=%04x", hpd_int_status, hpd_int_ctrl); /* detect disconnect if we are connected or visa versa: */ hpd_int_ctrl = HDMI_HPD_INT_CTRL_INT_EN; -- cgit v0.10.2 From 4e2024624e678f0ebb916e6192bd23c1f9fdf696 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 18 Dec 2014 17:26:10 +0100 Subject: isofs: Fix unchecked printing of ER records We didn't check length of rock ridge ER records before printing them. Thus corrupted isofs image can cause us to access and print some memory behind the buffer with obvious consequences. Reported-and-tested-by: Carl Henrik Lunde CC: stable@vger.kernel.org Signed-off-by: Jan Kara diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c index bb63254..735d752 100644 --- a/fs/isofs/rock.c +++ b/fs/isofs/rock.c @@ -362,6 +362,9 @@ repeat: rs.cont_size = isonum_733(rr->u.CE.size); break; case SIG('E', 'R'): + /* Invalid length of ER tag id? */ + if (rr->u.ER.len_id + offsetof(struct rock_ridge, u.ER.data) > rr->len) + goto out; ISOFS_SB(inode->i_sb)->s_rock = 1; printk(KERN_DEBUG "ISO 9660 Extensions: "); { -- cgit v0.10.2 From e3b1e6a19e09877b91517dfe304a2b3f6b2138fc Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 18 Dec 2014 11:46:38 +0000 Subject: ASoC: dapm: Remove snd_soc_of_parse_audio_routing() due to deferred probe This reverts commit f8781db8aeb18d (ASoC: dapm: Augment existing card DAPM routes in snd_soc_of_parse_audio_routing) since it is broken for deferred probing as it ends up storing data allocated with devm_ over multiple instantiations of the device. Reported-by: Russell King Tested-by: Russell King Signed-off-by: Mark Brown diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 9357210..d759546 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3231,7 +3231,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, const char *propname) { struct device_node *np = card->dev->of_node; - int num_routes, old_routes; + int num_routes; struct snd_soc_dapm_route *routes; int i, ret; @@ -3249,9 +3249,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, return -EINVAL; } - old_routes = card->num_dapm_routes; - routes = devm_kzalloc(card->dev, - (old_routes + num_routes) * sizeof(*routes), + routes = devm_kzalloc(card->dev, num_routes * sizeof(*routes), GFP_KERNEL); if (!routes) { dev_err(card->dev, @@ -3259,11 +3257,9 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, return -EINVAL; } - memcpy(routes, card->dapm_routes, old_routes * sizeof(*routes)); - for (i = 0; i < num_routes; i++) { ret = of_property_read_string_index(np, propname, - 2 * i, &routes[old_routes + i].sink); + 2 * i, &routes[i].sink); if (ret) { dev_err(card->dev, "ASoC: Property '%s' index %d could not be read: %d\n", @@ -3271,7 +3267,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, return -EINVAL; } ret = of_property_read_string_index(np, propname, - (2 * i) + 1, &routes[old_routes + i].source); + (2 * i) + 1, &routes[i].source); if (ret) { dev_err(card->dev, "ASoC: Property '%s' index %d could not be read: %d\n", @@ -3280,7 +3276,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, } } - card->num_dapm_routes += num_routes; + card->num_dapm_routes = num_routes; card->dapm_routes = routes; return 0; -- cgit v0.10.2 From e159332b9af4b04d882dbcfe1bb0117f0a6d4b58 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 19 Dec 2014 12:03:53 +0100 Subject: udf: Verify i_size when loading inode Verify that inode size is sane when loading inode with data stored in ICB. Otherwise we may get confused later when working with the inode and inode size is too big. CC: stable@vger.kernel.org Reported-by: Carl Henrik Lunde Signed-off-by: Jan Kara diff --git a/fs/udf/inode.c b/fs/udf/inode.c index c9b4df5..5bc71d9 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -1489,6 +1489,20 @@ reread: } inode->i_generation = iinfo->i_unique; + /* Sanity checks for files in ICB so that we don't get confused later */ + if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { + /* + * For file in ICB data is stored in allocation descriptor + * so sizes should match + */ + if (iinfo->i_lenAlloc != inode->i_size) + goto out; + /* File in ICB has to fit in there... */ + if (inode->i_size > inode->i_sb->s_blocksize - + udf_file_entry_alloc_offset(inode)) + goto out; + } + switch (fe->icbTag.fileType) { case ICBTAG_FILE_TYPE_DIRECTORY: inode->i_op = &udf_dir_inode_operations; -- cgit v0.10.2 From a1d47b262952a45aae62bd49cfaf33dd76c11a2c Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 19 Dec 2014 12:21:47 +0100 Subject: udf: Verify symlink size before loading it UDF specification allows arbitrarily large symlinks. However we support only symlinks at most one block large. Check the length of the symlink so that we don't access memory beyond end of the symlink block. CC: stable@vger.kernel.org Reported-by: Carl Henrik Lunde Signed-off-by: Jan Kara diff --git a/fs/udf/symlink.c b/fs/udf/symlink.c index 6fb7945..c3aa6fa 100644 --- a/fs/udf/symlink.c +++ b/fs/udf/symlink.c @@ -80,11 +80,17 @@ static int udf_symlink_filler(struct file *file, struct page *page) struct inode *inode = page->mapping->host; struct buffer_head *bh = NULL; unsigned char *symlink; - int err = -EIO; + int err; unsigned char *p = kmap(page); struct udf_inode_info *iinfo; uint32_t pos; + /* We don't support symlinks longer than one block */ + if (inode->i_size > inode->i_sb->s_blocksize) { + err = -ENAMETOOLONG; + goto out_unmap; + } + iinfo = UDF_I(inode); pos = udf_block_map(inode, 0); @@ -94,8 +100,10 @@ static int udf_symlink_filler(struct file *file, struct page *page) } else { bh = sb_bread(inode->i_sb, pos); - if (!bh) - goto out; + if (!bh) { + err = -EIO; + goto out_unlock_inode; + } symlink = bh->b_data; } @@ -109,9 +117,10 @@ static int udf_symlink_filler(struct file *file, struct page *page) unlock_page(page); return 0; -out: +out_unlock_inode: up_read(&iinfo->i_data_sem); SetPageError(page); +out_unmap: kunmap(page); unlock_page(page); return err; -- cgit v0.10.2 From 004fa5ed08cc5d3188db42c05d6b80feaae004c2 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 10 Dec 2014 14:19:53 +0200 Subject: Bluetooth: 6lowpan: Do not free skb when packet is dropped If we need to drop the message because of some error in the compression etc, then do not free the skb as that is done automatically in other part of networking stack. Signed-off-by: Jukka Rissanen Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 76617be..c989253 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -390,7 +390,6 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev, drop: dev->stats.rx_dropped++; - kfree_skb(skb); return NET_RX_DROP; } -- cgit v0.10.2 From 51bda2bca53b265715ca1852528f38dc67429d9a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 19 Dec 2014 06:20:57 +0000 Subject: Bluetooth: hidp_connection_add() unsafe use of l2cap_pi() it's OK after we'd verified the sockets, but not before that. Signed-off-by: Al Viro Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index cc25d0b..07348e1 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -1314,13 +1314,14 @@ int hidp_connection_add(struct hidp_connadd_req *req, { struct hidp_session *session; struct l2cap_conn *conn; - struct l2cap_chan *chan = l2cap_pi(ctrl_sock->sk)->chan; + struct l2cap_chan *chan; int ret; ret = hidp_verify_sockets(ctrl_sock, intr_sock); if (ret) return ret; + chan = l2cap_pi(ctrl_sock->sk)->chan; conn = NULL; l2cap_chan_lock(chan); if (chan->conn) -- cgit v0.10.2 From 96c26653ce65bf84f3212f8b00d4316c1efcbf4c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 19 Dec 2014 06:20:58 +0000 Subject: Bluetooth: cmtp: cmtp_add_connection() should verify that it's dealing with l2cap socket ... rather than relying on ciptool(8) never passing it anything else. Give it e.g. an AF_UNIX connected socket (from socketpair(2)) and it'll oops, trying to evaluate &l2cap_pi(sock->sk)->chan->dst... Signed-off-by: Al Viro Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c index 67fe5e8..278a194 100644 --- a/net/bluetooth/cmtp/core.c +++ b/net/bluetooth/cmtp/core.c @@ -334,6 +334,9 @@ int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock) BT_DBG(""); + if (!l2cap_is_socket(sock)) + return -EBADFD; + session = kzalloc(sizeof(struct cmtp_session), GFP_KERNEL); if (!session) return -ENOMEM; -- cgit v0.10.2 From 71bb99a02b32b4cc4265118e85f6035ca72923f0 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 19 Dec 2014 06:20:59 +0000 Subject: Bluetooth: bnep: bnep_add_connection() should verify that it's dealing with l2cap socket same story as cmtp Signed-off-by: Al Viro Signed-off-by: Marcel Holtmann diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index 85bcc21..ce82722d 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -533,6 +533,9 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock) BT_DBG(""); + if (!l2cap_is_socket(sock)) + return -EBADFD; + baswap((void *) dst, &l2cap_pi(sock->sk)->chan->dst); baswap((void *) src, &l2cap_pi(sock->sk)->chan->src); -- cgit v0.10.2 From 0e5cc9a40ada6046e6bc3bdfcd0c0d7e4b706b14 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 18 Dec 2014 22:37:50 +0100 Subject: udf: Check path length when reading symlink Symlink reading code does not check whether the resulting path fits into the page provided by the generic code. This isn't as easy as just checking the symlink size because of various encoding conversions we perform on path. So we have to check whether there is still enough space in the buffer on the fly. CC: stable@vger.kernel.org Reported-by: Carl Henrik Lunde Signed-off-by: Jan Kara diff --git a/fs/udf/dir.c b/fs/udf/dir.c index a012c51..a7690b4 100644 --- a/fs/udf/dir.c +++ b/fs/udf/dir.c @@ -167,7 +167,8 @@ static int udf_readdir(struct file *file, struct dir_context *ctx) continue; } - flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi); + flen = udf_get_filename(dir->i_sb, nameptr, lfi, fname, + UDF_NAME_LEN); if (!flen) continue; diff --git a/fs/udf/namei.c b/fs/udf/namei.c index c12e260..6ff19b5 100644 --- a/fs/udf/namei.c +++ b/fs/udf/namei.c @@ -233,7 +233,8 @@ static struct fileIdentDesc *udf_find_entry(struct inode *dir, if (!lfi) continue; - flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi); + flen = udf_get_filename(dir->i_sb, nameptr, lfi, fname, + UDF_NAME_LEN); if (flen && udf_match(flen, fname, child->len, child->name)) goto out_ok; } diff --git a/fs/udf/symlink.c b/fs/udf/symlink.c index c3aa6fa..0f1b3a2 100644 --- a/fs/udf/symlink.c +++ b/fs/udf/symlink.c @@ -30,13 +30,16 @@ #include #include "udf_i.h" -static void udf_pc_to_char(struct super_block *sb, unsigned char *from, - int fromlen, unsigned char *to) +static int udf_pc_to_char(struct super_block *sb, unsigned char *from, + int fromlen, unsigned char *to, int tolen) { struct pathComponent *pc; int elen = 0; + int comp_len; unsigned char *p = to; + /* Reserve one byte for terminating \0 */ + tolen--; while (elen < fromlen) { pc = (struct pathComponent *)(from + elen); switch (pc->componentType) { @@ -49,22 +52,37 @@ static void udf_pc_to_char(struct super_block *sb, unsigned char *from, break; /* Fall through */ case 2: + if (tolen == 0) + return -ENAMETOOLONG; p = to; *p++ = '/'; + tolen--; break; case 3: + if (tolen < 3) + return -ENAMETOOLONG; memcpy(p, "../", 3); p += 3; + tolen -= 3; break; case 4: + if (tolen < 2) + return -ENAMETOOLONG; memcpy(p, "./", 2); p += 2; + tolen -= 2; /* that would be . - just ignore */ break; case 5: - p += udf_get_filename(sb, pc->componentIdent, p, - pc->lengthComponentIdent); + comp_len = udf_get_filename(sb, pc->componentIdent, + pc->lengthComponentIdent, + p, tolen); + p += comp_len; + tolen -= comp_len; + if (tolen == 0) + return -ENAMETOOLONG; *p++ = '/'; + tolen--; break; } elen += sizeof(struct pathComponent) + pc->lengthComponentIdent; @@ -73,6 +91,7 @@ static void udf_pc_to_char(struct super_block *sb, unsigned char *from, p[-1] = '\0'; else p[0] = '\0'; + return 0; } static int udf_symlink_filler(struct file *file, struct page *page) @@ -108,8 +127,10 @@ static int udf_symlink_filler(struct file *file, struct page *page) symlink = bh->b_data; } - udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p); + err = udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p, PAGE_SIZE); brelse(bh); + if (err) + goto out_unlock_inode; up_read(&iinfo->i_data_sem); SetPageUptodate(page); diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h index 1cc3c99..47bb3f5 100644 --- a/fs/udf/udfdecl.h +++ b/fs/udf/udfdecl.h @@ -211,7 +211,8 @@ udf_get_lb_pblock(struct super_block *sb, struct kernel_lb_addr *loc, } /* unicode.c */ -extern int udf_get_filename(struct super_block *, uint8_t *, uint8_t *, int); +extern int udf_get_filename(struct super_block *, uint8_t *, int, uint8_t *, + int); extern int udf_put_filename(struct super_block *, const uint8_t *, uint8_t *, int); extern int udf_build_ustr(struct ustr *, dstring *, int); diff --git a/fs/udf/unicode.c b/fs/udf/unicode.c index afd470e..b84fee3 100644 --- a/fs/udf/unicode.c +++ b/fs/udf/unicode.c @@ -28,7 +28,8 @@ #include "udf_sb.h" -static int udf_translate_to_linux(uint8_t *, uint8_t *, int, uint8_t *, int); +static int udf_translate_to_linux(uint8_t *, int, uint8_t *, int, uint8_t *, + int); static int udf_char_to_ustr(struct ustr *dest, const uint8_t *src, int strlen) { @@ -333,8 +334,8 @@ try_again: return u_len + 1; } -int udf_get_filename(struct super_block *sb, uint8_t *sname, uint8_t *dname, - int flen) +int udf_get_filename(struct super_block *sb, uint8_t *sname, int slen, + uint8_t *dname, int dlen) { struct ustr *filename, *unifilename; int len = 0; @@ -347,7 +348,7 @@ int udf_get_filename(struct super_block *sb, uint8_t *sname, uint8_t *dname, if (!unifilename) goto out1; - if (udf_build_ustr_exact(unifilename, sname, flen)) + if (udf_build_ustr_exact(unifilename, sname, slen)) goto out2; if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8)) { @@ -366,7 +367,8 @@ int udf_get_filename(struct super_block *sb, uint8_t *sname, uint8_t *dname, } else goto out2; - len = udf_translate_to_linux(dname, filename->u_name, filename->u_len, + len = udf_translate_to_linux(dname, dlen, + filename->u_name, filename->u_len, unifilename->u_name, unifilename->u_len); out2: kfree(unifilename); @@ -403,10 +405,12 @@ int udf_put_filename(struct super_block *sb, const uint8_t *sname, #define EXT_MARK '.' #define CRC_MARK '#' #define EXT_SIZE 5 +/* Number of chars we need to store generated CRC to make filename unique */ +#define CRC_LEN 5 -static int udf_translate_to_linux(uint8_t *newName, uint8_t *udfName, - int udfLen, uint8_t *fidName, - int fidNameLen) +static int udf_translate_to_linux(uint8_t *newName, int newLen, + uint8_t *udfName, int udfLen, + uint8_t *fidName, int fidNameLen) { int index, newIndex = 0, needsCRC = 0; int extIndex = 0, newExtIndex = 0, hasExt = 0; @@ -439,7 +443,7 @@ static int udf_translate_to_linux(uint8_t *newName, uint8_t *udfName, newExtIndex = newIndex; } } - if (newIndex < 256) + if (newIndex < newLen) newName[newIndex++] = curr; else needsCRC = 1; @@ -467,13 +471,13 @@ static int udf_translate_to_linux(uint8_t *newName, uint8_t *udfName, } ext[localExtIndex++] = curr; } - maxFilenameLen = 250 - localExtIndex; + maxFilenameLen = newLen - CRC_LEN - localExtIndex; if (newIndex > maxFilenameLen) newIndex = maxFilenameLen; else newIndex = newExtIndex; - } else if (newIndex > 250) - newIndex = 250; + } else if (newIndex > newLen - CRC_LEN) + newIndex = newLen - CRC_LEN; newName[newIndex++] = CRC_MARK; valueCRC = crc_itu_t(0, fidName, fidNameLen); newName[newIndex++] = hex_asc_upper_hi(valueCRC >> 8); -- cgit v0.10.2 From 4f2ff8ef9eab32e6bc01c71bb539fc68d0917f59 Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Fri, 19 Dec 2014 13:19:35 +0800 Subject: sunvnet: fix a memory leak in vnet_handle_offloads when skb_gso_segment returns error, the original skb should be freed Signed-off-by: Li RongQing Acked-by: David L Stevens Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c index 45c408e..d2835bf 100644 --- a/drivers/net/ethernet/sun/sunvnet.c +++ b/drivers/net/ethernet/sun/sunvnet.c @@ -1201,6 +1201,7 @@ static int vnet_handle_offloads(struct vnet_port *port, struct sk_buff *skb) segs = skb_gso_segment(skb, dev->features & ~NETIF_F_TSO); if (IS_ERR(segs)) { dev->stats.tx_dropped++; + dev_kfree_skb_any(skb); return NETDEV_TX_OK; } -- cgit v0.10.2 From 17e96834fd35997ca7cdfbf15413bcd5a36ad448 Mon Sep 17 00:00:00 2001 From: Govindarajulu Varadarajan <_govind@gmx.com> Date: Thu, 18 Dec 2014 15:58:42 +0530 Subject: enic: fix rx skb checksum Hardware always provides compliment of IP pseudo checksum. Stack expects whole packet checksum without pseudo checksum if CHECKSUM_COMPLETE is set. This causes checksum error in nf & ovs. kernel: qg-19546f09-f2: hw csum failure kernel: CPU: 9 PID: 0 Comm: swapper/9 Tainted: GF O-------------- 3.10.0-123.8.1.el7.x86_64 #1 kernel: Hardware name: Cisco Systems Inc UCSB-B200-M3/UCSB-B200-M3, BIOS B200M3.2.2.3.0.080820141339 08/08/2014 kernel: ffff881218f40000 df68243feb35e3a8 ffff881237a43ab8 ffffffff815e237b kernel: ffff881237a43ad0 ffffffff814cd4ca ffff8829ec71eb00 ffff881237a43af0 kernel: ffffffff814c6232 0000000000000286 ffff8829ec71eb00 ffff881237a43b00 kernel: Call Trace: kernel: [] dump_stack+0x19/0x1b kernel: [] netdev_rx_csum_fault+0x3a/0x40 kernel: [] __skb_checksum_complete_head+0x62/0x70 kernel: [] __skb_checksum_complete+0x11/0x20 kernel: [] nf_ip_checksum+0xcc/0x100 kernel: [] icmp_error+0x1f7/0x35c [nf_conntrack_ipv4] kernel: [] ? netif_rx+0xb9/0x1d0 kernel: [] ? internal_dev_recv+0xdb/0x130 [openvswitch] kernel: [] nf_conntrack_in+0xf0/0xa80 [nf_conntrack] kernel: [] ? inet_del_offload+0x40/0x40 kernel: [] ipv4_conntrack_in+0x22/0x30 [nf_conntrack_ipv4] kernel: [] nf_iterate+0xaa/0xc0 kernel: [] ? inet_del_offload+0x40/0x40 kernel: [] nf_hook_slow+0x84/0x140 kernel: [] ? inet_del_offload+0x40/0x40 kernel: [] ip_rcv+0x344/0x380 Hardware verifies IP & tcp/udp header checksum but does not provide payload checksum, use CHECKSUM_UNNECESSARY. Set it only if its valid IP tcp/udp packet. Cc: Jiri Benc Cc: Stefan Assmann Reported-by: Sunil Choudhary Signed-off-by: Govindarajulu Varadarajan <_govind@gmx.com> Reviewed-by: Jiri Benc Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index 868d0f6..705f334 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -1060,10 +1060,14 @@ static void enic_rq_indicate_buf(struct vnic_rq *rq, PKT_HASH_TYPE_L4 : PKT_HASH_TYPE_L3); } - if ((netdev->features & NETIF_F_RXCSUM) && !csum_not_calc) { - skb->csum = htons(checksum); - skb->ip_summed = CHECKSUM_COMPLETE; - } + /* Hardware does not provide whole packet checksum. It only + * provides pseudo checksum. Since hw validates the packet + * checksum but not provide us the checksum value. use + * CHECSUM_UNNECESSARY. + */ + if ((netdev->features & NETIF_F_RXCSUM) && tcp_udp_csum_ok && + ipv4_csum_ok) + skb->ip_summed = CHECKSUM_UNNECESSARY; if (vlan_stripped) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tci); -- cgit v0.10.2 From cb57720bf79688d64854a0a43565aa52303c1f3f Mon Sep 17 00:00:00 2001 From: Ethan Zhao Date: Thu, 18 Dec 2014 15:28:19 +0900 Subject: cpufreq: fix a NULL pointer dereference in __cpufreq_governor() If ACPI _PPC changed notification happens before governor was initiated while kernel is booting, a NULL pointer dereference will be triggered: BUG: unable to handle kernel NULL pointer dereference at 0000000000000030 IP: [] __cpufreq_governor+0x23/0x1e0 PGD 0 Oops: 0000 [#1] SMP ... ... RIP: 0010:[] [] __cpufreq_governor+0x23/0x1e0 RSP: 0018:ffff881fcfbcfbb8 EFLAGS: 00010286 RAX: 0000000000000000 RBX: ffff881fd11b3980 RCX: ffff88407fc20000 RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff881fd11b3980 RBP: ffff881fcfbcfbd8 R08: 0000000000000000 R09: 000000000000000f R10: ffffffff818068d0 R11: 0000000000000043 R12: 0000000000000004 R13: 0000000000000000 R14: ffffffff8196cae0 R15: 0000000000000000 FS: 0000000000000000(0000) GS:ffff881fffc00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000030 CR3: 00000000018ae000 CR4: 00000000000407f0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Process kworker/0:3 (pid: 750, threadinfo ffff881fcfbce000, task ffff881fcf556400) Stack: ffff881fffc17d00 ffff881fcfbcfc18 ffff881fd11b3980 0000000000000000 ffff881fcfbcfc08 ffffffff81470d08 ffff881fd11b3980 0000000000000007 ffff881fcfbcfc18 ffff881fffc17d00 ffff881fcfbcfd28 ffffffff81472e9a Call Trace: [] __cpufreq_set_policy+0x1b8/0x2e0 [] cpufreq_update_policy+0xca/0x150 [] ? cpufreq_update_policy+0x150/0x150 [] acpi_processor_ppc_has_changed+0x71/0x7b [] acpi_processor_notify+0x55/0x115 [] acpi_device_notify+0x19/0x1b [] acpi_ev_notify_dispatch+0x41/0x5f [] acpi_os_execute_deferred+0x27/0x34 The root cause is a race conditon -- cpufreq core and acpi-cpufreq driver were initiated, but cpufreq_governor wasn't and _PPC changed notification happened, __cpufreq_governor() was called within acpi_os_execute_deferred kernel thread context. To fix this panic issue, add pointer checking code in __cpufreq_governor() before pointer policy->governor is to be dereferenced. Signed-off-by: Ethan Zhao Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index a09a29c..46bed4f 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -2028,6 +2028,12 @@ static int __cpufreq_governor(struct cpufreq_policy *policy, /* Don't start any governor operations if we are entering suspend */ if (cpufreq_suspended) return 0; + /* + * Governor might not be initiated here if ACPI _PPC changed + * notification happened, so check it. + */ + if (!policy->governor) + return -EINVAL; if (policy->governor->max_transition_latency && policy->cpuinfo.transition_latency > -- cgit v0.10.2 From ad1d8313cdeec8bc10cecb2143d047eb565b9f65 Mon Sep 17 00:00:00 2001 From: Michal Privoznik Date: Sun, 14 Dec 2014 13:36:52 +0100 Subject: tools / cpupower: Correctly detect if running as root Some operations, like frequency-set, need root privileges. However, the way that this is detected is not correct. The getuid() is called, while in fact geteuid() should be. This way we can allow distributions or users to set SETUID flags on the cpupower binary if they want to and let regular users change the cpu frequency governor. Signed-off-by: Michal Privoznik Acked-by: Thomas Renninger Signed-off-by: Rafael J. Wysocki diff --git a/tools/power/cpupower/utils/cpupower.c b/tools/power/cpupower/utils/cpupower.c index 7cdcf88..9ea9143 100644 --- a/tools/power/cpupower/utils/cpupower.c +++ b/tools/power/cpupower/utils/cpupower.c @@ -199,7 +199,7 @@ int main(int argc, const char *argv[]) } get_cpu_info(0, &cpupower_cpu_info); - run_as_root = !getuid(); + run_as_root = !geteuid(); if (run_as_root) { ret = uname(&uts); if (!ret && !strcmp(uts.machine, "x86_64") && -- cgit v0.10.2 From a59e5109f648f703d877d3c33cc5a7ef283dba83 Mon Sep 17 00:00:00 2001 From: Prarit Bhargava Date: Sun, 14 Dec 2014 09:06:38 -0500 Subject: tools / cpupower: Fix no idle state information return value sysfs_get_idlestate_count() returns an unsigned int. Returning -ENODEV is not the right thing to do here, and in any case is handled the same way as if there are no states found. Signed-off-by: Prarit Bhargava Acked-by: Thomas Renninger Signed-off-by: Rafael J. Wysocki diff --git a/tools/power/cpupower/utils/helpers/sysfs.c b/tools/power/cpupower/utils/helpers/sysfs.c index 09afe5d..4e8fe2c 100644 --- a/tools/power/cpupower/utils/helpers/sysfs.c +++ b/tools/power/cpupower/utils/helpers/sysfs.c @@ -361,7 +361,7 @@ unsigned int sysfs_get_idlestate_count(unsigned int cpu) snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpuidle"); if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) - return -ENODEV; + return 0; snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu); if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) -- cgit v0.10.2 From 3640dcfa4fd00cd91d88bb86250bdb496f7070c0 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Fri, 19 Dec 2014 18:35:53 -0500 Subject: audit: don't attempt to lookup PIDs when changing PID filtering audit rules Commit f1dc4867 ("audit: anchor all pid references in the initial pid namespace") introduced a find_vpid() call when adding/removing audit rules with PID/PPID filters; unfortunately this is problematic as find_vpid() only works if there is a task with the associated PID alive on the system. The following commands demonstrate a simple reproducer. # auditctl -D # auditctl -l # autrace /bin/true # auditctl -l This patch resolves the problem by simply using the PID provided by the user without any additional validation, e.g. no calls to check to see if the task/PID exists. Cc: stable@vger.kernel.org # 3.15 Cc: Richard Guy Briggs Signed-off-by: Paul Moore Acked-by: Eric Paris Reviewed-by: Richard Guy Briggs diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index d214cd0..c0d148b 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -444,19 +444,6 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, f->val = 0; } - if ((f->type == AUDIT_PID) || (f->type == AUDIT_PPID)) { - struct pid *pid; - rcu_read_lock(); - pid = find_vpid(f->val); - if (!pid) { - rcu_read_unlock(); - err = -ESRCH; - goto exit_free; - } - f->val = pid_nr(pid); - rcu_read_unlock(); - } - err = audit_field_valid(entry, f); if (err) goto exit_free; -- cgit v0.10.2 From 54dc77d974a50147d6639dac6f59cb2c29207161 Mon Sep 17 00:00:00 2001 From: Richard Guy Briggs Date: Thu, 18 Dec 2014 23:09:27 -0500 Subject: audit: use supplied gfp_mask from audit_buffer in kauditd_send_multicast_skb Eric Paris explains: Since kauditd_send_multicast_skb() gets called in audit_log_end(), which can come from any context (aka even a sleeping context) GFP_KERNEL can't be used. Since the audit_buffer knows what context it should use, pass that down and use that. See: https://lkml.org/lkml/2014/12/16/542 BUG: sleeping function called from invalid context at mm/slab.c:2849 in_atomic(): 1, irqs_disabled(): 0, pid: 885, name: sulogin 2 locks held by sulogin/885: #0: (&sig->cred_guard_mutex){+.+.+.}, at: [] prepare_bprm_creds+0x28/0x8b #1: (tty_files_lock){+.+.+.}, at: [] selinux_bprm_committing_creds+0x55/0x22b CPU: 1 PID: 885 Comm: sulogin Not tainted 3.18.0-next-20141216 #30 Hardware name: Dell Inc. Latitude E6530/07Y85M, BIOS A15 06/20/2014 ffff880223744f10 ffff88022410f9b8 ffffffff916ba529 0000000000000375 ffff880223744f10 ffff88022410f9e8 ffffffff91063185 0000000000000006 0000000000000000 0000000000000000 0000000000000000 ffff88022410fa38 Call Trace: [] dump_stack+0x50/0xa8 [] ___might_sleep+0x1b6/0x1be [] __might_sleep+0x119/0x128 [] cache_alloc_debugcheck_before.isra.45+0x1d/0x1f [] kmem_cache_alloc+0x43/0x1c9 [] __alloc_skb+0x42/0x1a3 [] skb_copy+0x3e/0xa3 [] audit_log_end+0x83/0x100 [] ? avc_audit_pre_callback+0x103/0x103 [] common_lsm_audit+0x441/0x450 [] slow_avc_audit+0x63/0x67 [] avc_has_perm+0xca/0xe3 [] inode_has_perm+0x5a/0x65 [] selinux_bprm_committing_creds+0x98/0x22b [] security_bprm_committing_creds+0xe/0x10 [] install_exec_creds+0xe/0x79 [] load_elf_binary+0xe36/0x10d7 [] search_binary_handler+0x81/0x18c [] do_execveat_common.isra.31+0x4e3/0x7b7 [] do_execve+0x1f/0x21 [] SyS_execve+0x25/0x29 [] stub_execve+0x69/0xa0 Cc: stable@vger.kernel.org #v3.16-rc1 Reported-by: Valdis Kletnieks Signed-off-by: Richard Guy Briggs Tested-by: Valdis Kletnieks Signed-off-by: Paul Moore diff --git a/kernel/audit.c b/kernel/audit.c index f3a981d..c7e097a 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -429,7 +429,7 @@ static void kauditd_send_skb(struct sk_buff *skb) * This function doesn't consume an skb as might be expected since it has to * copy it anyways. */ -static void kauditd_send_multicast_skb(struct sk_buff *skb) +static void kauditd_send_multicast_skb(struct sk_buff *skb, gfp_t gfp_mask) { struct sk_buff *copy; struct audit_net *aunet = net_generic(&init_net, audit_net_id); @@ -448,11 +448,11 @@ static void kauditd_send_multicast_skb(struct sk_buff *skb) * no reason for new multicast clients to continue with this * non-compliance. */ - copy = skb_copy(skb, GFP_KERNEL); + copy = skb_copy(skb, gfp_mask); if (!copy) return; - nlmsg_multicast(sock, copy, 0, AUDIT_NLGRP_READLOG, GFP_KERNEL); + nlmsg_multicast(sock, copy, 0, AUDIT_NLGRP_READLOG, gfp_mask); } /* @@ -1949,7 +1949,7 @@ void audit_log_end(struct audit_buffer *ab) struct nlmsghdr *nlh = nlmsg_hdr(ab->skb); nlh->nlmsg_len = ab->skb->len; - kauditd_send_multicast_skb(ab->skb); + kauditd_send_multicast_skb(ab->skb, ab->gfp_mask); /* * The original kaudit unicast socket sends up messages with -- cgit v0.10.2 From d4b0833a65e8b9fc58d992ba0cc89cad1580db31 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Wed, 17 Dec 2014 18:07:23 +0100 Subject: ARM: mvebu: Fix pinctrl configuration for Armada 370 DB The commit b4607572ef86 (ARM: mvebu: remove conflicting muxing on Armada 370 DB) removes the hog pins muxing. As it is explained in the commit log it solves a warning a boot time, but more important it also allows using the Giga port 0 of the board. Unfortunately in the same time the commit 4904a82a9399 (arm: mvebu: move Armada 370/XP pinctrl node definition armada-370-xp.dtsi) was merged and it introduced again the hog pins muxing. Because of it, the Giga port 0 of the board is no more usable. This commit remove again the conflicting muxing (hopefully for the last time). Signed-off-by: Gregory CLEMENT [andrew@lunn.ch: Correct commit IDs] Signed-off-by: Andrew Lunn Fixes: 4904a82a9399 ("arm: mvebu: move Armada 370/XP pinctrl node definition armada-370-xp.dtsi") diff --git a/arch/arm/boot/dts/armada-370-db.dts b/arch/arm/boot/dts/armada-370-db.dts index 1466580..70b1943 100644 --- a/arch/arm/boot/dts/armada-370-db.dts +++ b/arch/arm/boot/dts/armada-370-db.dts @@ -203,27 +203,3 @@ compatible = "linux,spdif-dir"; }; }; - -&pinctrl { - /* - * These pins might be muxed as I2S by - * the bootloader, but it conflicts - * with the real I2S pins that are - * muxed using i2s_pins. We must mux - * those pins to a function other than - * I2S. - */ - pinctrl-0 = <&hog_pins1 &hog_pins2>; - pinctrl-names = "default"; - - hog_pins1: hog-pins1 { - marvell,pins = "mpp6", "mpp8", "mpp10", - "mpp12", "mpp13"; - marvell,function = "gpio"; - }; - - hog_pins2: hog-pins2 { - marvell,pins = "mpp5", "mpp7", "mpp9"; - marvell,function = "gpo"; - }; -}; -- cgit v0.10.2 From 7b09406390e76df97c9f5f29c23a4f56d982f22c Mon Sep 17 00:00:00 2001 From: Ilkka Koskinen Date: Tue, 9 Dec 2014 12:34:03 -0800 Subject: Thermal/int340x: Handle properly the case when _trt or _art acpi entry is missing If either of the entries was missing, the driver tried to free memory using uninitialized pointer. In addition, it was dereferencing null pointer. Signed-off-by: Ilkka Koskinen Acked-by: Jacob Pan Signed-off-by: Zhang Rui diff --git a/drivers/thermal/int340x_thermal/acpi_thermal_rel.c b/drivers/thermal/int340x_thermal/acpi_thermal_rel.c index e4e61b3..231cabc 100644 --- a/drivers/thermal/int340x_thermal/acpi_thermal_rel.c +++ b/drivers/thermal/int340x_thermal/acpi_thermal_rel.c @@ -82,7 +82,7 @@ int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trtp, struct acpi_buffer trt_format = { sizeof("RRNNNNNN"), "RRNNNNNN" }; if (!acpi_has_method(handle, "_TRT")) - return 0; + return -ENODEV; status = acpi_evaluate_object(handle, "_TRT", NULL, &buffer); if (ACPI_FAILURE(status)) @@ -167,7 +167,7 @@ int acpi_parse_art(acpi_handle handle, int *art_count, struct art **artp, sizeof("RRNNNNNNNNNNN"), "RRNNNNNNNNNNN" }; if (!acpi_has_method(handle, "_ART")) - return 0; + return -ENODEV; status = acpi_evaluate_object(handle, "_ART", NULL, &buffer); if (ACPI_FAILURE(status)) @@ -321,8 +321,8 @@ static long acpi_thermal_rel_ioctl(struct file *f, unsigned int cmd, unsigned long length = 0; int count = 0; char __user *arg = (void __user *)__arg; - struct trt *trts; - struct art *arts; + struct trt *trts = NULL; + struct art *arts = NULL; switch (cmd) { case ACPI_THERMAL_GET_TRT_COUNT: -- cgit v0.10.2 From 59c56eb6db0c14fe569b1c9625cb850e52d29d88 Mon Sep 17 00:00:00 2001 From: Jacob Pan Date: Mon, 15 Dec 2014 10:15:41 -0800 Subject: thermal/powerclamp: add ids for future xeon cpus Enable Intel Powerclamp driver on Xeon cpu id 0x56, package C-state is available on this CPU for idle injection. Signed-off-by: Jacob Pan Signed-off-by: Zhang Rui diff --git a/drivers/thermal/intel_powerclamp.c b/drivers/thermal/intel_powerclamp.c index e98b424..6ceebd6 100644 --- a/drivers/thermal/intel_powerclamp.c +++ b/drivers/thermal/intel_powerclamp.c @@ -688,6 +688,7 @@ static const struct x86_cpu_id intel_powerclamp_ids[] = { { X86_VENDOR_INTEL, 6, 0x45}, { X86_VENDOR_INTEL, 6, 0x46}, { X86_VENDOR_INTEL, 6, 0x4c}, + { X86_VENDOR_INTEL, 6, 0x56}, {} }; MODULE_DEVICE_TABLE(x86cpu, intel_powerclamp_ids); -- cgit v0.10.2 From fc4de356e033a46a08caf78027df871cc20780c1 Mon Sep 17 00:00:00 2001 From: Javi Merino Date: Mon, 15 Dec 2014 16:55:52 +0000 Subject: thermal: cpu_cooling: document node in struct cpufreq_cooling_device The node field of struct cpufreq_cooling_device was reintroduced in 2dcd851fe4b4 (thermal: cpu_cooling: Update always cpufreq policy with thermal constraints) but without the documentation that it once had. Add it back so that all the fields of struct cpufreq_cooling_device are documented. Cc: Yadwinder Singh Brar Cc: Eduardo Valentin Cc: Zhang Rui Signed-off-by: Javi Merino Signed-off-by: Zhang Rui diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index ad09e51..588185a 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -39,6 +39,7 @@ * @cpufreq_val: integer value representing the absolute value of the clipped * frequency. * @allowed_cpus: all the cpus involved for this cpufreq_cooling_device. + * @node: list_head to link all cpufreq_cooling_device together. * * This structure is required for keeping information of each * cpufreq_cooling_device registered. In order to prevent corruption of this a -- cgit v0.10.2 From e237ec37ec154564f8690c5bd1795339955eeef9 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 19 Dec 2014 14:27:55 +0100 Subject: udf: Check component length before reading it Check that length specified in a component of a symlink fits in the input buffer we are reading. Also properly ignore component length for component types that do not use it. Otherwise we read memory after end of buffer for corrupted udf image. Reported-by: Carl Henrik Lunde CC: stable@vger.kernel.org Signed-off-by: Jan Kara diff --git a/fs/udf/symlink.c b/fs/udf/symlink.c index 0f1b3a2..ac10ca9 100644 --- a/fs/udf/symlink.c +++ b/fs/udf/symlink.c @@ -42,14 +42,17 @@ static int udf_pc_to_char(struct super_block *sb, unsigned char *from, tolen--; while (elen < fromlen) { pc = (struct pathComponent *)(from + elen); + elen += sizeof(struct pathComponent); switch (pc->componentType) { case 1: /* * Symlink points to some place which should be agreed * upon between originator and receiver of the media. Ignore. */ - if (pc->lengthComponentIdent > 0) + if (pc->lengthComponentIdent > 0) { + elen += pc->lengthComponentIdent; break; + } /* Fall through */ case 2: if (tolen == 0) @@ -74,6 +77,9 @@ static int udf_pc_to_char(struct super_block *sb, unsigned char *from, /* that would be . - just ignore */ break; case 5: + elen += pc->lengthComponentIdent; + if (elen > fromlen) + return -EIO; comp_len = udf_get_filename(sb, pc->componentIdent, pc->lengthComponentIdent, p, tolen); @@ -85,7 +91,6 @@ static int udf_pc_to_char(struct super_block *sb, unsigned char *from, tolen--; break; } - elen += sizeof(struct pathComponent) + pc->lengthComponentIdent; } if (p > to + 1) p[-1] = '\0'; -- cgit v0.10.2 From 3ee3039c5b4d121d56dc6b7deeeee3ba4150a260 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 18 Dec 2014 22:49:12 +0100 Subject: udf: Reduce repeated dereferences Replace repeated dereferences like dir->i_sb by storing superblock pointer in a variable and using that. Signed-off-by: Jan Kara diff --git a/fs/udf/dir.c b/fs/udf/dir.c index a7690b4..05e90ed 100644 --- a/fs/udf/dir.c +++ b/fs/udf/dir.c @@ -57,6 +57,7 @@ static int udf_readdir(struct file *file, struct dir_context *ctx) sector_t offset; int i, num, ret = 0; struct extent_position epos = { NULL, 0, {0, 0} }; + struct super_block *sb = dir->i_sb; if (ctx->pos == 0) { if (!dir_emit_dot(file, ctx)) @@ -76,16 +77,16 @@ static int udf_readdir(struct file *file, struct dir_context *ctx) if (nf_pos == 0) nf_pos = udf_ext0_offset(dir); - fibh.soffset = fibh.eoffset = nf_pos & (dir->i_sb->s_blocksize - 1); + fibh.soffset = fibh.eoffset = nf_pos & (sb->s_blocksize - 1); if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) { - if (inode_bmap(dir, nf_pos >> dir->i_sb->s_blocksize_bits, + if (inode_bmap(dir, nf_pos >> sb->s_blocksize_bits, &epos, &eloc, &elen, &offset) != (EXT_RECORDED_ALLOCATED >> 30)) { ret = -ENOENT; goto out; } - block = udf_get_lb_pblock(dir->i_sb, &eloc, offset); - if ((++offset << dir->i_sb->s_blocksize_bits) < elen) { + block = udf_get_lb_pblock(sb, &eloc, offset); + if ((++offset << sb->s_blocksize_bits) < elen) { if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) epos.offset -= sizeof(struct short_ad); else if (iinfo->i_alloc_type == @@ -95,18 +96,18 @@ static int udf_readdir(struct file *file, struct dir_context *ctx) offset = 0; } - if (!(fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block))) { + if (!(fibh.sbh = fibh.ebh = udf_tread(sb, block))) { ret = -EIO; goto out; } - if (!(offset & ((16 >> (dir->i_sb->s_blocksize_bits - 9)) - 1))) { - i = 16 >> (dir->i_sb->s_blocksize_bits - 9); - if (i + offset > (elen >> dir->i_sb->s_blocksize_bits)) - i = (elen >> dir->i_sb->s_blocksize_bits) - offset; + if (!(offset & ((16 >> (sb->s_blocksize_bits - 9)) - 1))) { + i = 16 >> (sb->s_blocksize_bits - 9); + if (i + offset > (elen >> sb->s_blocksize_bits)) + i = (elen >> sb->s_blocksize_bits) - offset; for (num = 0; i > 0; i--) { - block = udf_get_lb_pblock(dir->i_sb, &eloc, offset + i); - tmp = udf_tgetblk(dir->i_sb, block); + block = udf_get_lb_pblock(sb, &eloc, offset + i); + tmp = udf_tgetblk(sb, block); if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp)) bha[num++] = tmp; else @@ -152,12 +153,12 @@ static int udf_readdir(struct file *file, struct dir_context *ctx) } if ((cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) != 0) { - if (!UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE)) + if (!UDF_QUERY_FLAG(sb, UDF_FLAG_UNDELETE)) continue; } if ((cfi.fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0) { - if (!UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNHIDE)) + if (!UDF_QUERY_FLAG(sb, UDF_FLAG_UNHIDE)) continue; } @@ -167,13 +168,12 @@ static int udf_readdir(struct file *file, struct dir_context *ctx) continue; } - flen = udf_get_filename(dir->i_sb, nameptr, lfi, fname, - UDF_NAME_LEN); + flen = udf_get_filename(sb, nameptr, lfi, fname, UDF_NAME_LEN); if (!flen) continue; tloc = lelb_to_cpu(cfi.icb.extLocation); - iblock = udf_get_lb_pblock(dir->i_sb, &tloc, 0); + iblock = udf_get_lb_pblock(sb, &tloc, 0); if (!dir_emit(ctx, fname, flen, iblock, DT_UNKNOWN)) goto out; } /* end while */ diff --git a/fs/udf/namei.c b/fs/udf/namei.c index 6ff19b5..33b246b 100644 --- a/fs/udf/namei.c +++ b/fs/udf/namei.c @@ -159,18 +159,19 @@ static struct fileIdentDesc *udf_find_entry(struct inode *dir, struct udf_inode_info *dinfo = UDF_I(dir); int isdotdot = child->len == 2 && child->name[0] == '.' && child->name[1] == '.'; + struct super_block *sb = dir->i_sb; size = udf_ext0_offset(dir) + dir->i_size; f_pos = udf_ext0_offset(dir); fibh->sbh = fibh->ebh = NULL; - fibh->soffset = fibh->eoffset = f_pos & (dir->i_sb->s_blocksize - 1); + fibh->soffset = fibh->eoffset = f_pos & (sb->s_blocksize - 1); if (dinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) { - if (inode_bmap(dir, f_pos >> dir->i_sb->s_blocksize_bits, &epos, + if (inode_bmap(dir, f_pos >> sb->s_blocksize_bits, &epos, &eloc, &elen, &offset) != (EXT_RECORDED_ALLOCATED >> 30)) goto out_err; - block = udf_get_lb_pblock(dir->i_sb, &eloc, offset); - if ((++offset << dir->i_sb->s_blocksize_bits) < elen) { + block = udf_get_lb_pblock(sb, &eloc, offset); + if ((++offset << sb->s_blocksize_bits) < elen) { if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) epos.offset -= sizeof(struct short_ad); else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG) @@ -178,7 +179,7 @@ static struct fileIdentDesc *udf_find_entry(struct inode *dir, } else offset = 0; - fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block); + fibh->sbh = fibh->ebh = udf_tread(sb, block); if (!fibh->sbh) goto out_err; } @@ -217,12 +218,12 @@ static struct fileIdentDesc *udf_find_entry(struct inode *dir, } if ((cfi->fileCharacteristics & FID_FILE_CHAR_DELETED) != 0) { - if (!UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE)) + if (!UDF_QUERY_FLAG(sb, UDF_FLAG_UNDELETE)) continue; } if ((cfi->fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0) { - if (!UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNHIDE)) + if (!UDF_QUERY_FLAG(sb, UDF_FLAG_UNHIDE)) continue; } @@ -233,8 +234,7 @@ static struct fileIdentDesc *udf_find_entry(struct inode *dir, if (!lfi) continue; - flen = udf_get_filename(dir->i_sb, nameptr, lfi, fname, - UDF_NAME_LEN); + flen = udf_get_filename(sb, nameptr, lfi, fname, UDF_NAME_LEN); if (flen && udf_match(flen, fname, child->len, child->name)) goto out_ok; } -- cgit v0.10.2 From 9c633317a589754ff3c6e22dcb5c63f149303ec9 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Fri, 12 Dec 2014 19:06:07 -0600 Subject: ipmi: Finish cleanup of BMC attributes The previous cleanup of BMC attributes left a few holes, and if you run with lockdep debugging with a BMC with the proper attributes, you could get a warning. This patch removes all the unused attributes from the BMC structure, since they are all declared in the .data section now. It makes the attributes all static. It fixes the referencing of the attributes in a couple of cases that dynamically added the files depending on BMC information. Signed-off-by: Corey Minyard Cc: Huang Ying Tested-by: Alexei Starovoitov diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 5fa83f7..6b65fa4 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -199,18 +199,6 @@ struct bmc_device { int guid_set; char name[16]; struct kref usecount; - - /* bmc device attributes */ - struct device_attribute device_id_attr; - struct device_attribute provides_dev_sdrs_attr; - struct device_attribute revision_attr; - struct device_attribute firmware_rev_attr; - struct device_attribute version_attr; - struct device_attribute add_dev_support_attr; - struct device_attribute manufacturer_id_attr; - struct device_attribute product_id_attr; - struct device_attribute guid_attr; - struct device_attribute aux_firmware_rev_attr; }; #define to_bmc_device(x) container_of((x), struct bmc_device, pdev.dev) @@ -2252,7 +2240,7 @@ static ssize_t device_id_show(struct device *dev, return snprintf(buf, 10, "%u\n", bmc->id.device_id); } -DEVICE_ATTR(device_id, S_IRUGO, device_id_show, NULL); +static DEVICE_ATTR(device_id, S_IRUGO, device_id_show, NULL); static ssize_t provides_device_sdrs_show(struct device *dev, struct device_attribute *attr, @@ -2263,7 +2251,8 @@ static ssize_t provides_device_sdrs_show(struct device *dev, return snprintf(buf, 10, "%u\n", (bmc->id.device_revision & 0x80) >> 7); } -DEVICE_ATTR(provides_device_sdrs, S_IRUGO, provides_device_sdrs_show, NULL); +static DEVICE_ATTR(provides_device_sdrs, S_IRUGO, provides_device_sdrs_show, + NULL); static ssize_t revision_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -2273,7 +2262,7 @@ static ssize_t revision_show(struct device *dev, struct device_attribute *attr, return snprintf(buf, 20, "%u\n", bmc->id.device_revision & 0x0F); } -DEVICE_ATTR(revision, S_IRUGO, revision_show, NULL); +static DEVICE_ATTR(revision, S_IRUGO, revision_show, NULL); static ssize_t firmware_revision_show(struct device *dev, struct device_attribute *attr, @@ -2284,7 +2273,7 @@ static ssize_t firmware_revision_show(struct device *dev, return snprintf(buf, 20, "%u.%x\n", bmc->id.firmware_revision_1, bmc->id.firmware_revision_2); } -DEVICE_ATTR(firmware_revision, S_IRUGO, firmware_revision_show, NULL); +static DEVICE_ATTR(firmware_revision, S_IRUGO, firmware_revision_show, NULL); static ssize_t ipmi_version_show(struct device *dev, struct device_attribute *attr, @@ -2296,7 +2285,7 @@ static ssize_t ipmi_version_show(struct device *dev, ipmi_version_major(&bmc->id), ipmi_version_minor(&bmc->id)); } -DEVICE_ATTR(ipmi_version, S_IRUGO, ipmi_version_show, NULL); +static DEVICE_ATTR(ipmi_version, S_IRUGO, ipmi_version_show, NULL); static ssize_t add_dev_support_show(struct device *dev, struct device_attribute *attr, @@ -2307,7 +2296,8 @@ static ssize_t add_dev_support_show(struct device *dev, return snprintf(buf, 10, "0x%02x\n", bmc->id.additional_device_support); } -DEVICE_ATTR(additional_device_support, S_IRUGO, add_dev_support_show, NULL); +static DEVICE_ATTR(additional_device_support, S_IRUGO, add_dev_support_show, + NULL); static ssize_t manufacturer_id_show(struct device *dev, struct device_attribute *attr, @@ -2317,7 +2307,7 @@ static ssize_t manufacturer_id_show(struct device *dev, return snprintf(buf, 20, "0x%6.6x\n", bmc->id.manufacturer_id); } -DEVICE_ATTR(manufacturer_id, S_IRUGO, manufacturer_id_show, NULL); +static DEVICE_ATTR(manufacturer_id, S_IRUGO, manufacturer_id_show, NULL); static ssize_t product_id_show(struct device *dev, struct device_attribute *attr, @@ -2327,7 +2317,7 @@ static ssize_t product_id_show(struct device *dev, return snprintf(buf, 10, "0x%4.4x\n", bmc->id.product_id); } -DEVICE_ATTR(product_id, S_IRUGO, product_id_show, NULL); +static DEVICE_ATTR(product_id, S_IRUGO, product_id_show, NULL); static ssize_t aux_firmware_rev_show(struct device *dev, struct device_attribute *attr, @@ -2341,7 +2331,7 @@ static ssize_t aux_firmware_rev_show(struct device *dev, bmc->id.aux_firmware_revision[1], bmc->id.aux_firmware_revision[0]); } -DEVICE_ATTR(aux_firmware_revision, S_IRUGO, aux_firmware_rev_show, NULL); +static DEVICE_ATTR(aux_firmware_revision, S_IRUGO, aux_firmware_rev_show, NULL); static ssize_t guid_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -2352,7 +2342,7 @@ static ssize_t guid_show(struct device *dev, struct device_attribute *attr, (long long) bmc->guid[0], (long long) bmc->guid[8]); } -DEVICE_ATTR(guid, S_IRUGO, guid_show, NULL); +static DEVICE_ATTR(guid, S_IRUGO, guid_show, NULL); static struct attribute *bmc_dev_attrs[] = { &dev_attr_device_id.attr, @@ -2392,10 +2382,10 @@ cleanup_bmc_device(struct kref *ref) if (bmc->id.aux_firmware_revision_set) device_remove_file(&bmc->pdev.dev, - &bmc->aux_firmware_rev_attr); + &dev_attr_aux_firmware_revision); if (bmc->guid_set) device_remove_file(&bmc->pdev.dev, - &bmc->guid_attr); + &dev_attr_guid); platform_device_unregister(&bmc->pdev); } @@ -2422,16 +2412,14 @@ static int create_bmc_files(struct bmc_device *bmc) int err; if (bmc->id.aux_firmware_revision_set) { - bmc->aux_firmware_rev_attr.attr.name = "aux_firmware_revision"; err = device_create_file(&bmc->pdev.dev, - &bmc->aux_firmware_rev_attr); + &dev_attr_aux_firmware_revision); if (err) goto out; } if (bmc->guid_set) { - bmc->guid_attr.attr.name = "guid"; err = device_create_file(&bmc->pdev.dev, - &bmc->guid_attr); + &dev_attr_guid); if (err) goto out_aux_firm; } @@ -2441,7 +2429,7 @@ static int create_bmc_files(struct bmc_device *bmc) out_aux_firm: if (bmc->id.aux_firmware_revision_set) device_remove_file(&bmc->pdev.dev, - &bmc->aux_firmware_rev_attr); + &dev_attr_aux_firmware_revision); out: return err; } -- cgit v0.10.2 From e3fe142704f8fe855990de834eab11af45fe2788 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Tue, 16 Dec 2014 08:36:32 -0600 Subject: ipmi: Fix compile issue with isspace() Some arches don't get ctypes.h included from these includes, so add it explicitly. Signed-off-by: Corey Minyard diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index e178ac2..fd5a5e8 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -52,6 +52,7 @@ #include #include #include +#include #define PFX "ipmi_ssif: " #define DEVICE_NAME "ipmi_ssif" -- cgit v0.10.2 From 0733d1387ed8b845a6b60121f06ddbe9e24f12ea Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 21 Dec 2014 22:14:39 +0100 Subject: thermal: drop owner assignment from platform_drivers This platform_driver does not need to set an owner, it will be populated by the driver core. Signed-off-by: Wolfram Sang diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c index 1bcddfc..9c6ce54 100644 --- a/drivers/thermal/rockchip_thermal.c +++ b/drivers/thermal/rockchip_thermal.c @@ -677,7 +677,6 @@ static SIMPLE_DEV_PM_OPS(rockchip_thermal_pm_ops, static struct platform_driver rockchip_thermal_driver = { .driver = { .name = "rockchip-thermal", - .owner = THIS_MODULE, .pm = &rockchip_thermal_pm_ops, .of_match_table = of_rockchip_thermal_match, }, -- cgit v0.10.2 From 0716b0ff0a11ca96e925bfac43a82eb88f75c928 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 21 Dec 2014 22:14:40 +0100 Subject: thermal: int340x_thermal: drop owner assignment from platform_drivers This platform_driver does not need to set an owner, it will be populated by the driver core. Signed-off-by: Wolfram Sang Signed-off-by: Zhang Rui diff --git a/drivers/thermal/int340x_thermal/int3400_thermal.c b/drivers/thermal/int340x_thermal/int3400_thermal.c index dcb306e..65a98a9 100644 --- a/drivers/thermal/int340x_thermal/int3400_thermal.c +++ b/drivers/thermal/int340x_thermal/int3400_thermal.c @@ -335,7 +335,6 @@ static struct platform_driver int3400_thermal_driver = { .remove = int3400_thermal_remove, .driver = { .name = "int3400 thermal", - .owner = THIS_MODULE, .acpi_match_table = ACPI_PTR(int3400_thermal_match), }, }; diff --git a/drivers/thermal/int340x_thermal/int3402_thermal.c b/drivers/thermal/int340x_thermal/int3402_thermal.c index a5d08c1..c5cbc3a 100644 --- a/drivers/thermal/int340x_thermal/int3402_thermal.c +++ b/drivers/thermal/int340x_thermal/int3402_thermal.c @@ -231,7 +231,6 @@ static struct platform_driver int3402_thermal_driver = { .remove = int3402_thermal_remove, .driver = { .name = "int3402 thermal", - .owner = THIS_MODULE, .acpi_match_table = int3402_thermal_match, }, }; -- cgit v0.10.2 From 31dde116cb084bc0042cafe48975ffb7d1a4ae5d Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Thu, 18 Dec 2014 17:13:49 +0000 Subject: arm64: Replace set_arch_dma_coherent_ops with arch_setup_dma_ops Commit a3a60f81ee6f (dma-mapping: replace set_arch_dma_coherent_ops with arch_setup_dma_ops) changes the of_dma_configure() arch dma_ops callback to arch_setup_dma_ops but only the arch/arm code is updated. Subsequent commit 97890ba9289c (dma-mapping: detect and configure IOMMU in of_dma_configure) changes the arch_setup_dma_ops() prototype further to handle iommu. The patch makes the corresponding arm64 changes. Signed-off-by: Catalin Marinas Reported-by: Arnd Bergmann Acked-by: Will Deacon diff --git a/arch/arm64/include/asm/dma-mapping.h b/arch/arm64/include/asm/dma-mapping.h index d34189b..9ce3e68 100644 --- a/arch/arm64/include/asm/dma-mapping.h +++ b/arch/arm64/include/asm/dma-mapping.h @@ -52,13 +52,14 @@ static inline void set_dma_ops(struct device *dev, struct dma_map_ops *ops) dev->archdata.dma_ops = ops; } -static inline int set_arch_dma_coherent_ops(struct device *dev) +static inline void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size, + struct iommu_ops *iommu, bool coherent) { - dev->archdata.dma_coherent = true; - set_dma_ops(dev, &coherent_swiotlb_dma_ops); - return 0; + dev->archdata.dma_coherent = coherent; + if (coherent) + set_dma_ops(dev, &coherent_swiotlb_dma_ops); } -#define set_arch_dma_coherent_ops set_arch_dma_coherent_ops +#define arch_setup_dma_ops arch_setup_dma_ops /* do not use this function in a driver */ static inline bool is_device_dma_coherent(struct device *dev) -- cgit v0.10.2 From 7e77bdebff5cb1e9876c561f69710b9ab8fa1f7e Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Fri, 19 Dec 2014 13:36:08 +0100 Subject: crypto: af_alg - fix backlog handling If a request is backlogged, it's complete() handler will get called twice: once with -EINPROGRESS, and once with the final error code. af_alg's complete handler, unlike other users, does not handle the -EINPROGRESS but instead always completes the completion that recvmsg() is waiting on. This can lead to a return to user space while the request is still pending in the driver. If userspace closes the sockets before the requests are handled by the driver, this will lead to use-after-frees (and potential crashes) in the kernel due to the tfm having been freed. The crashes can be easily reproduced (for example) by reducing the max queue length in cryptod.c and running the following (from http://www.chronox.de/libkcapi.html) on AES-NI capable hardware: $ while true; do kcapi -x 1 -e -c '__ecb-aes-aesni' \ -k 00000000000000000000000000000000 \ -p 00000000000000000000000000000000 >/dev/null & done Cc: stable@vger.kernel.org Signed-off-by: Rabin Vincent Signed-off-by: Herbert Xu diff --git a/crypto/af_alg.c b/crypto/af_alg.c index 1fa7bc3..4665b79 100644 --- a/crypto/af_alg.c +++ b/crypto/af_alg.c @@ -455,6 +455,9 @@ void af_alg_complete(struct crypto_async_request *req, int err) { struct af_alg_completion *completion = req->data; + if (err == -EINPROGRESS) + return; + completion->err = err; complete(&completion->completion); } -- cgit v0.10.2 From da042e3a6261a444868b99bece98e4e5b77a0fce Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Thu, 18 Dec 2014 16:40:35 +0200 Subject: ASoC: Intel: Add I2C dependency to two new machines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix following build error when CONFIG_I2C is not enabled: sound/soc/codecs/rt5640.c:2252:1: warning: data definition has no type or storage class module_i2c_driver(rt5640_i2c_driver); ^ sound/soc/codecs/rt5640.c:2252:1: error: type defaults to ‘int’ in declaration of ‘module_i2c_driver’ [-Werror=implicit-int] sound/soc/codecs/rt5640.c:2252:1: warning: parameter names (without types) in function declaration sound/soc/codecs/rt5640.c:2241:26: warning: ‘rt5640_i2c_driver’ defined but not used [-Wunused-variable] static struct i2c_driver rt5640_i2c_driver = { ^ cc1: some warnings being treated as errors Signed-off-by: Jarkko Nikula Reported-by: Randy Dunlap Acked-by: Randy Dunlap Signed-off-by: Mark Brown diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index e989ecf..f86de12 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -89,7 +89,7 @@ config SND_SOC_INTEL_BROADWELL_MACH config SND_SOC_INTEL_BYTCR_RT5640_MACH tristate "ASoC Audio DSP Support for MID BYT Platform" - depends on X86 + depends on X86 && I2C select SND_SOC_RT5640 select SND_SST_MFLD_PLATFORM select SND_SST_IPC_ACPI @@ -101,7 +101,7 @@ config SND_SOC_INTEL_BYTCR_RT5640_MACH config SND_SOC_INTEL_CHT_BSW_RT5672_MACH tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5672 codec" - depends on X86_INTEL_LPSS + depends on X86_INTEL_LPSS && I2C select SND_SOC_RT5670 select SND_SST_MFLD_PLATFORM select SND_SST_IPC_ACPI -- cgit v0.10.2 From 3475c3d034d7f276a474c8bd53f44b48c8bf669d Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Fri, 19 Dec 2014 16:18:05 +0000 Subject: ASoC: dwc: Ensure FIFOs are flushed to prevent channel swap Flush the FIFOs when the stream is prepared for use. This avoids an inadvertent swapping of the left/right channels if the FIFOs are not empty at startup. Signed-off-by: Andrew Jackson Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index b93168d..4df19b5 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -263,6 +263,19 @@ static void dw_i2s_shutdown(struct snd_pcm_substream *substream, snd_soc_dai_set_dma_data(dai, substream, NULL); } +static int dw_i2s_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + i2s_write_reg(dev->i2s_base, TXFFR, 1); + else + i2s_write_reg(dev->i2s_base, RXFFR, 1); + + return 0; +} + static int dw_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { @@ -294,6 +307,7 @@ static struct snd_soc_dai_ops dw_i2s_dai_ops = { .startup = dw_i2s_startup, .shutdown = dw_i2s_shutdown, .hw_params = dw_i2s_hw_params, + .prepare = dw_i2s_prepare, .trigger = dw_i2s_trigger, }; -- cgit v0.10.2 From db2c1f9e38a38e0f9294b5a8a83d744f68fbe726 Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Fri, 19 Dec 2014 16:18:06 +0000 Subject: ASoC: dwc: Iterate over all channels The Designware core can be configured with up to four stereo channels. Each stereo channel is individually configured so, when the driver's hw_params call is made, each requested stereo channel has to be programmed. Signed-off-by: Andrew Jackson Signed-off-by: Mark Brown diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index 4df19b5..8d18bbd 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -209,16 +209,9 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, switch (config->chan_nr) { case EIGHT_CHANNEL_SUPPORT: - ch_reg = 3; - break; case SIX_CHANNEL_SUPPORT: - ch_reg = 2; - break; case FOUR_CHANNEL_SUPPORT: - ch_reg = 1; - break; case TWO_CHANNEL_SUPPORT: - ch_reg = 0; break; default: dev_err(dev->dev, "channel not supported\n"); @@ -227,18 +220,22 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, i2s_disable_channels(dev, substream->stream); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - i2s_write_reg(dev->i2s_base, TCR(ch_reg), xfer_resolution); - i2s_write_reg(dev->i2s_base, TFCR(ch_reg), 0x02); - irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg)); - i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30); - i2s_write_reg(dev->i2s_base, TER(ch_reg), 1); - } else { - i2s_write_reg(dev->i2s_base, RCR(ch_reg), xfer_resolution); - i2s_write_reg(dev->i2s_base, RFCR(ch_reg), 0x07); - irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg)); - i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03); - i2s_write_reg(dev->i2s_base, RER(ch_reg), 1); + for (ch_reg = 0; ch_reg < (config->chan_nr / 2); ch_reg++) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + i2s_write_reg(dev->i2s_base, TCR(ch_reg), + xfer_resolution); + i2s_write_reg(dev->i2s_base, TFCR(ch_reg), 0x02); + irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg)); + i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30); + i2s_write_reg(dev->i2s_base, TER(ch_reg), 1); + } else { + i2s_write_reg(dev->i2s_base, RCR(ch_reg), + xfer_resolution); + i2s_write_reg(dev->i2s_base, RFCR(ch_reg), 0x07); + irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg)); + i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03); + i2s_write_reg(dev->i2s_base, RER(ch_reg), 1); + } } i2s_write_reg(dev->i2s_base, CCR, ccr); -- cgit v0.10.2 From 64b9c90b86002fb33ddc5583c5be165128835913 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Mon, 22 Dec 2014 16:49:19 +0200 Subject: ASoC: Intel: Fix BYTCR firmware name BYTCR DSP firmware is in intel/ subdirectory. See linux-firmware.git commit d562a3b63632 ("linux-firmware: add sst audio firmware for baytrail platforms"). Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst/sst_acpi.c b/sound/soc/intel/sst/sst_acpi.c index 3abc29e..2ac72eb 100644 --- a/sound/soc/intel/sst/sst_acpi.c +++ b/sound/soc/intel/sst/sst_acpi.c @@ -343,7 +343,7 @@ int sst_acpi_remove(struct platform_device *pdev) } static struct sst_machines sst_acpi_bytcr[] = { - {"10EC5640", "T100", "bytt100_rt5640", NULL, "fw_sst_0f28.bin", + {"10EC5640", "T100", "bytt100_rt5640", NULL, "intel/fw_sst_0f28.bin", &byt_rvp_platform_data }, {}, }; -- cgit v0.10.2 From c6905d62269cdf66e179c579c1bd4b1b8baa02fe Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Mon, 22 Dec 2014 16:49:20 +0200 Subject: ASoC: Intel: Fix BYTCR machine driver MODULE_ALIAS snd_soc_sst_bytcr_dpcm_rt5640 doesn't autoload because MODULE_ALIAS doesn't match with "bytt100_rt5640" platform device. Signed-off-by: Jarkko Nikula Cc: Subhransu S. Prusty Signed-off-by: Mark Brown diff --git a/sound/soc/intel/bytcr_dpcm_rt5640.c b/sound/soc/intel/bytcr_dpcm_rt5640.c index f5d0fc1..eef0c56 100644 --- a/sound/soc/intel/bytcr_dpcm_rt5640.c +++ b/sound/soc/intel/bytcr_dpcm_rt5640.c @@ -227,4 +227,4 @@ module_platform_driver(snd_byt_mc_driver); MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver"); MODULE_AUTHOR("Subhransu S. Prusty "); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:bytrt5640-audio"); +MODULE_ALIAS("platform:bytt100_rt5640"); -- cgit v0.10.2 From 4a92843601ad0f5067f441d2f0dca55bbe18c076 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Mon, 22 Dec 2014 12:27:39 -0500 Subject: audit: correctly record file names with different path name types There is a problem with the audit system when multiple audit records are created for the same path, each with a different path name type. The root cause of the problem is in __audit_inode() when an exact match (both the path name and path name type) is not found for a path name record; the existing code creates a new path name record, but it never sets the path name in this record, leaving it NULL. This patch corrects this problem by assigning the path name to these newly created records. There are many ways to reproduce this problem, but one of the easiest is the following (assuming auditd is running): # mkdir /root/tmp/test # touch /root/tmp/test/567 # auditctl -a always,exit -F dir=/root/tmp/test # touch /root/tmp/test/567 Afterwards, or while the commands above are running, check the audit log and pay special attention to the PATH records. A faulty kernel will display something like the following for the file creation: type=SYSCALL msg=audit(1416957442.025:93): arch=c000003e syscall=2 success=yes exit=3 ... comm="touch" exe="/usr/bin/touch" type=CWD msg=audit(1416957442.025:93): cwd="/root/tmp" type=PATH msg=audit(1416957442.025:93): item=0 name="test/" inode=401409 ... nametype=PARENT type=PATH msg=audit(1416957442.025:93): item=1 name=(null) inode=393804 ... nametype=NORMAL type=PATH msg=audit(1416957442.025:93): item=2 name=(null) inode=393804 ... nametype=NORMAL While a patched kernel will show the following: type=SYSCALL msg=audit(1416955786.566:89): arch=c000003e syscall=2 success=yes exit=3 ... comm="touch" exe="/usr/bin/touch" type=CWD msg=audit(1416955786.566:89): cwd="/root/tmp" type=PATH msg=audit(1416955786.566:89): item=0 name="test/" inode=401409 ... nametype=PARENT type=PATH msg=audit(1416955786.566:89): item=1 name="test/567" inode=393804 ... nametype=NORMAL This issue was brought up by a number of people, but special credit should go to hujianyang@huawei.com for reporting the problem along with an explanation of the problem and a patch. While the original patch did have some problems (see the archive link below), it did demonstrate the problem and helped kickstart the fix presented here. * https://lkml.org/lkml/2014/9/5/66 Reported-by: hujianyang Signed-off-by: Paul Moore Acked-by: Richard Guy Briggs diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 8933572..287b3d3 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1877,12 +1877,18 @@ void __audit_inode(struct filename *name, const struct dentry *dentry, } out_alloc: - /* unable to find the name from a previous getname(). Allocate a new - * anonymous entry. - */ - n = audit_alloc_name(context, AUDIT_TYPE_NORMAL); + /* unable to find an entry with both a matching name and type */ + n = audit_alloc_name(context, AUDIT_TYPE_UNKNOWN); if (!n) return; + if (name) + /* since name is not NULL we know there is already a matching + * name record, see audit_getname(), so there must be a type + * mismatch; reuse the string path since the original name + * record will keep the string valid until we free it in + * audit_free_names() */ + n->name = name; + out: if (parent) { n->name_len = n->name ? parent_len(n->name->name) : AUDIT_NAME_FULL; -- cgit v0.10.2 From c0e7dc21d33caa31296d1e4af1e99008359f1b64 Mon Sep 17 00:00:00 2001 From: Andrew Bresticker Date: Tue, 16 Dec 2014 13:05:44 -0800 Subject: spi: img-spfi: Enable controller before starting TX DMA It is recommended that the SPFI controller be enabled (i.e. setting SPFI_EN in SPFI_CONTROL) before TX DMA begins. Signed-off-by: Andrew Bresticker Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-img-spfi.c b/drivers/spi/spi-img-spfi.c index b410499..cd14556 100644 --- a/drivers/spi/spi-img-spfi.c +++ b/drivers/spi/spi-img-spfi.c @@ -390,14 +390,14 @@ static int img_spfi_start_dma(struct spi_master *master, dma_async_issue_pending(spfi->rx_ch); } + spfi_start(spfi); + if (xfer->tx_buf) { spfi->tx_dma_busy = true; dmaengine_submit(txdesc); dma_async_issue_pending(spfi->tx_ch); } - spfi_start(spfi); - return 1; stop_dma: -- cgit v0.10.2 From 6898b627aab6ba553e6d8b40a0b1ddc43c48d42f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 21 Dec 2014 11:58:16 +0100 Subject: selftests/exec: Use %zu to format size_t On 32-bit: execveat.c: In function 'check_execveat_pathmax': execveat.c:183: warning: format '%lu' expects type 'long unsigned int', but argument 3 has type 'size_t' execveat.c:187: warning: format '%lu' expects type 'long unsigned int', but argument 2 has type 'size_t' Signed-off-by: Geert Uytterhoeven Signed-off-by: Shuah Khan diff --git a/tools/testing/selftests/exec/execveat.c b/tools/testing/selftests/exec/execveat.c index 33a5c06..d273624 100644 --- a/tools/testing/selftests/exec/execveat.c +++ b/tools/testing/selftests/exec/execveat.c @@ -179,11 +179,11 @@ static int check_execveat_pathmax(int dot_dfd, const char *src, int is_script) */ fd = open(longpath, O_RDONLY); if (fd > 0) { - printf("Invoke copy of '%s' via filename of length %lu:\n", + printf("Invoke copy of '%s' via filename of length %zu:\n", src, strlen(longpath)); fail += check_execveat(fd, "", AT_EMPTY_PATH); } else { - printf("Failed to open length %lu filename, errno=%d (%s)\n", + printf("Failed to open length %zu filename, errno=%d (%s)\n", strlen(longpath), errno, strerror(errno)); fail++; } -- cgit v0.10.2 From 9e6d722f3d91c94f2a303d67ddd8fb1ca4c0d375 Mon Sep 17 00:00:00 2001 From: Nakajima Akira Date: Fri, 19 Dec 2014 15:38:14 +0900 Subject: cifs: make new inode cache when file type is different In spite of different file type, if file is same name and same inode number, old inode cache is used. This causes that you can not cd directory, can not cat SymbolicLink. So this patch is that if file type is different, return error. Reproducible sample : 1. create file 'a' at cifs client. 2. repeat rm and mkdir 'a' 4 times at server, then direcotry 'a' having same inode number is created. (Repeat 4 times, then same inode number is recycled.) (When server is under RHEL 6.6, 1 time is O.K. Always same inode number is recycled.) 3. ls -li at client, then you can not cd directory, can not remove directory. SymbolicLink has same problem. Bug link: https://bugzilla.kernel.org/show_bug.cgi?id=90011 Signed-off-by: Nakajima Akira Acked-by: Jeff Layton Signed-off-by: Steve French diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 8eaf20a..c295338 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -69,7 +69,8 @@ static inline void dump_cifs_file_struct(struct file *file, char *label) * Attempt to preload the dcache with the results from the FIND_FIRST/NEXT * * Find the dentry that matches "name". If there isn't one, create one. If it's - * a negative dentry or the uniqueid changed, then drop it and recreate it. + * a negative dentry or the uniqueid or filetype(mode) changed, + * then drop it and recreate it. */ static void cifs_prime_dcache(struct dentry *parent, struct qstr *name, @@ -97,8 +98,11 @@ cifs_prime_dcache(struct dentry *parent, struct qstr *name, if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) fattr->cf_uniqueid = CIFS_I(inode)->uniqueid; - /* update inode in place if i_ino didn't change */ - if (CIFS_I(inode)->uniqueid == fattr->cf_uniqueid) { + /* update inode in place + * if both i_ino and i_mode didn't change */ + if (CIFS_I(inode)->uniqueid == fattr->cf_uniqueid && + (inode->i_mode & S_IFMT) == + (fattr->cf_mode & S_IFMT)) { cifs_fattr_to_inode(inode, fattr); goto out; } -- cgit v0.10.2 From da413eec729dae5dcb150e2eb34c5e7e5e4e1b49 Mon Sep 17 00:00:00 2001 From: Dan Collins Date: Fri, 19 Dec 2014 16:49:25 +1300 Subject: packet: Fixed TPACKET V3 to signal poll when block is closed rather than every packet Make TPACKET_V3 signal poll when block is closed rather than for every packet. Side effect is that poll will be signaled when block retire timer expires which didn't previously happen. Issue was visible when sending packets at a very low frequency such that all blocks are retired before packets are received by TPACKET_V3. This caused avoidable packet loss. The fix ensures that the signal is sent when blocks are closed which covers the normal path where the block is filled as well as the path where the timer expires. The case where a block is filled without moving to the next block (ie. all blocks are full) will still cause poll to be signaled. Signed-off-by: Dan Collins Signed-off-by: David S. Miller diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index e52a447..6880f34 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -785,6 +785,7 @@ static void prb_close_block(struct tpacket_kbdq_core *pkc1, struct tpacket3_hdr *last_pkt; struct tpacket_hdr_v1 *h1 = &pbd1->hdr.bh1; + struct sock *sk = &po->sk; if (po->stats.stats3.tp_drops) status |= TP_STATUS_LOSING; @@ -809,6 +810,8 @@ static void prb_close_block(struct tpacket_kbdq_core *pkc1, /* Flush the block */ prb_flush_block(pkc1, pbd1, status); + sk->sk_data_ready(sk); + pkc1->kactive_blk_num = GET_NEXT_PRB_BLK_NUM(pkc1); } @@ -2052,12 +2055,12 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, smp_wmb(); #endif - if (po->tp_version <= TPACKET_V2) + if (po->tp_version <= TPACKET_V2) { __packet_set_status(po, h.raw, status); - else + sk->sk_data_ready(sk); + } else { prb_clear_blk_fill_status(&po->rx_ring); - - sk->sk_data_ready(sk); + } drop_n_restore: if (skb_head != skb->data && skb_shared(skb)) { -- cgit v0.10.2 From fe13192911507c49002fc4882ef11f75f529a010 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Fri, 19 Dec 2014 22:38:18 +0800 Subject: stmmac: Don't init ptp again when resume from suspend/hibernation Both stmmac_open() and stmmac_resume() call stmmac_hw_setup(), and stmmac_hw_setup() call stmmac_init_ptp() unconditionally. However, only stmmac_release() calls stmmac_release_ptp(). Since stmmac_suspend() doesn't call stmmac_release_ptp(), stmmac_resume() also needn't call stmmac_init_ptp(). This patch also fix a "scheduling while atomic" problem when resume from suspend/hibernation. Because stmmac_init_ptp() will trigger scheduling while stmmac_resume() hold a spinlock. Callgraph of "scheduling while atomic": stmmac_resume() --> stmmac_hw_setup() --> stmmac_init_ptp() --> stmmac_ptp_register() --> ptp_clock_register() --> device_create() --> device_create_groups_vargs() --> device_add() --> devtmpfs_create_node() --> wait_for_common() --> schedule_timeout() --> __schedule() Signed-off-by: Huacai Chen Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 118a427..8c6b7c1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1671,7 +1671,7 @@ static void stmmac_init_tx_coalesce(struct stmmac_priv *priv) * 0 on success and an appropriate (-)ve integer as defined in errno.h * file on failure. */ -static int stmmac_hw_setup(struct net_device *dev) +static int stmmac_hw_setup(struct net_device *dev, bool init_ptp) { struct stmmac_priv *priv = netdev_priv(dev); int ret; @@ -1708,9 +1708,11 @@ static int stmmac_hw_setup(struct net_device *dev) stmmac_mmc_setup(priv); - ret = stmmac_init_ptp(priv); - if (ret && ret != -EOPNOTSUPP) - pr_warn("%s: failed PTP initialisation\n", __func__); + if (init_ptp) { + ret = stmmac_init_ptp(priv); + if (ret && ret != -EOPNOTSUPP) + pr_warn("%s: failed PTP initialisation\n", __func__); + } #ifdef CONFIG_DEBUG_FS ret = stmmac_init_fs(dev); @@ -1787,7 +1789,7 @@ static int stmmac_open(struct net_device *dev) goto init_error; } - ret = stmmac_hw_setup(dev); + ret = stmmac_hw_setup(dev, true); if (ret < 0) { pr_err("%s: Hw setup failed\n", __func__); goto init_error; @@ -3036,7 +3038,7 @@ int stmmac_resume(struct net_device *ndev) netif_device_attach(ndev); init_dma_desc_rings(ndev, GFP_ATOMIC); - stmmac_hw_setup(ndev); + stmmac_hw_setup(ndev, false); stmmac_init_tx_coalesce(priv); napi_enable(&priv->napi); -- cgit v0.10.2 From 8acdf999accfd95093db17f33a58429a38782060 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Sat, 20 Dec 2014 11:23:27 +1100 Subject: virtio_net: Fix napi poll list corruption The commit d75b1ade567ffab085e8adbbdacf0092d10cd09c (net: less interrupt masking in NAPI) breaks virtio_net in an insidious way. It is now required that if the entire budget is consumed when poll returns, the napi poll_list must remain empty. However, like some other drivers virtio_net tries to do a last-ditch check and if there is more work it will call napi_schedule and then immediately process some of this new work. Should the entire budget be consumed while processing such new work then we will violate the new caller contract. This patch fixes this by not touching any work when we reschedule in virtio_net. The worst part of this bug is that the list corruption causes other napi users to be moved off-list. In my case I was chasing a stall in IPsec (IPsec uses netif_rx) and I only belatedly realised that it was virtio_net which caused the stall even though the virtio_net poll was still functioning perfectly after IPsec stalled. Signed-off-by: Herbert Xu Acked-by: Jason Wang Signed-off-by: David S. Miller diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index b8bd719..5ca9771 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -760,7 +760,6 @@ static int virtnet_poll(struct napi_struct *napi, int budget) container_of(napi, struct receive_queue, napi); unsigned int r, received = 0; -again: received += virtnet_receive(rq, budget - received); /* Out of packets? */ @@ -771,7 +770,6 @@ again: napi_schedule_prep(napi)) { virtqueue_disable_cb(rq->vq); __napi_schedule(napi); - goto again; } } -- cgit v0.10.2 From c51ed18257e731e15541324cd58bd3761d9d3b1c Mon Sep 17 00:00:00 2001 From: Haiyang Zhang Date: Fri, 19 Dec 2014 18:25:18 -0800 Subject: hyperv: Fix some variable name typos in send-buffer init/revoke The changed names are union fields with the same size, so the existing code still works. But, we now update these variables to the correct names. Signed-off-by: Haiyang Zhang Reviewed-by: K. Y. Srinivasan Signed-off-by: David S. Miller diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 2f48f79..384ca4f 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -590,6 +590,7 @@ struct nvsp_message { #define NETVSC_RECEIVE_BUFFER_ID 0xcafe +#define NETVSC_SEND_BUFFER_ID 0 #define NETVSC_PACKET_SIZE 4096 diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index dd867e6..9f49c01 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -161,8 +161,8 @@ static int netvsc_destroy_buf(struct netvsc_device *net_device) /* Deal with the send buffer we may have setup. * If we got a send section size, it means we received a - * SendsendBufferComplete msg (ie sent - * NvspMessage1TypeSendReceiveBuffer msg) therefore, we need + * NVSP_MSG1_TYPE_SEND_SEND_BUF_COMPLETE msg (ie sent + * NVSP_MSG1_TYPE_SEND_SEND_BUF msg) therefore, we need * to send a revoke msg here */ if (net_device->send_section_size) { @@ -172,7 +172,8 @@ static int netvsc_destroy_buf(struct netvsc_device *net_device) revoke_packet->hdr.msg_type = NVSP_MSG1_TYPE_REVOKE_SEND_BUF; - revoke_packet->msg.v1_msg.revoke_recv_buf.id = 0; + revoke_packet->msg.v1_msg.revoke_send_buf.id = + NETVSC_SEND_BUFFER_ID; ret = vmbus_sendpacket(net_device->dev->channel, revoke_packet, @@ -204,7 +205,7 @@ static int netvsc_destroy_buf(struct netvsc_device *net_device) net_device->send_buf_gpadl_handle = 0; } if (net_device->send_buf) { - /* Free up the receive buffer */ + /* Free up the send buffer */ vfree(net_device->send_buf); net_device->send_buf = NULL; } @@ -339,9 +340,9 @@ static int netvsc_init_buf(struct hv_device *device) init_packet = &net_device->channel_init_pkt; memset(init_packet, 0, sizeof(struct nvsp_message)); init_packet->hdr.msg_type = NVSP_MSG1_TYPE_SEND_SEND_BUF; - init_packet->msg.v1_msg.send_recv_buf.gpadl_handle = + init_packet->msg.v1_msg.send_send_buf.gpadl_handle = net_device->send_buf_gpadl_handle; - init_packet->msg.v1_msg.send_recv_buf.id = 0; + init_packet->msg.v1_msg.send_send_buf.id = NETVSC_SEND_BUFFER_ID; /* Send the gpadl notification request */ ret = vmbus_sendpacket(device->channel, init_packet, @@ -364,7 +365,7 @@ static int netvsc_init_buf(struct hv_device *device) netdev_err(ndev, "Unable to complete send buffer " "initialization with NetVsp - status %d\n", init_packet->msg.v1_msg. - send_recv_buf_complete.status); + send_send_buf_complete.status); ret = -EINVAL; goto cleanup; } -- cgit v0.10.2 From 6d08acd2d32e3e877579315dc3202d7a5f336d98 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Sat, 20 Dec 2014 12:15:49 -0800 Subject: in6: fix conflict with glibc Resolve conflicts between glibc definition of IPV6 socket options and those defined in Linux headers. Looks like earlier efforts to solve this did not cover all the definitions. It resolves warnings during iproute2 build. Please consider for stable as well. Signed-off-by: Stephen Hemminger Acked-by: Hannes Frederic Sowa Signed-off-by: David S. Miller diff --git a/include/uapi/linux/in6.h b/include/uapi/linux/in6.h index 74a2a17..79b12b0 100644 --- a/include/uapi/linux/in6.h +++ b/include/uapi/linux/in6.h @@ -149,7 +149,7 @@ struct in6_flowlabel_req { /* * IPV6 socket options */ - +#if __UAPI_DEF_IPV6_OPTIONS #define IPV6_ADDRFORM 1 #define IPV6_2292PKTINFO 2 #define IPV6_2292HOPOPTS 3 @@ -196,6 +196,7 @@ struct in6_flowlabel_req { #define IPV6_IPSEC_POLICY 34 #define IPV6_XFRM_POLICY 35 +#endif /* * Multicast: diff --git a/include/uapi/linux/libc-compat.h b/include/uapi/linux/libc-compat.h index c140620..e28807a 100644 --- a/include/uapi/linux/libc-compat.h +++ b/include/uapi/linux/libc-compat.h @@ -69,6 +69,7 @@ #define __UAPI_DEF_SOCKADDR_IN6 0 #define __UAPI_DEF_IPV6_MREQ 0 #define __UAPI_DEF_IPPROTO_V6 0 +#define __UAPI_DEF_IPV6_OPTIONS 0 #else @@ -82,6 +83,7 @@ #define __UAPI_DEF_SOCKADDR_IN6 1 #define __UAPI_DEF_IPV6_MREQ 1 #define __UAPI_DEF_IPPROTO_V6 1 +#define __UAPI_DEF_IPV6_OPTIONS 1 #endif /* _NETINET_IN_H */ @@ -103,6 +105,7 @@ #define __UAPI_DEF_SOCKADDR_IN6 1 #define __UAPI_DEF_IPV6_MREQ 1 #define __UAPI_DEF_IPPROTO_V6 1 +#define __UAPI_DEF_IPV6_OPTIONS 1 /* Definitions for xattr.h */ #define __UAPI_DEF_XATTR 1 -- cgit v0.10.2 From 05b0aa579397b734f127af58e401a30784a1e315 Mon Sep 17 00:00:00 2001 From: Prashant Sreedharan Date: Sat, 20 Dec 2014 12:16:17 -0800 Subject: tg3: tg3_disable_ints using uninitialized mailbox value to disable interrupts During driver load in tg3_init_one, if the driver detects DMA activity before intializing the chip tg3_halt is called. As part of tg3_halt interrupts are disabled using routine tg3_disable_ints. This routine was using mailbox value which was not initialized (default value is 0). As a result driver was writing 0x00000001 to pci config space register 0, which is the vendor id / device id. This driver bug was exposed because of the commit a7877b17a667 (PCI: Check only the Vendor ID to identify Configuration Request Retry). Also this issue is only seen in older generation chipsets like 5722 because config space write to offset 0 from driver is possible. The newer generation chips ignore writes to offset 0. Also without commit a7877b17a667, for these older chips when a GRC reset is issued the Bootcode would reprogram the vendor id/device id, which is the reason this bug was masked earlier. Fixed by initializing the interrupt mailbox registers before calling tg3_halt. Please queue for -stable. Reported-by: Nils Holland Reported-by: Marcelo Ricardo Leitner Signed-off-by: Prashant Sreedharan Signed-off-by: Michael Chan Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index bb48a61..553dcd8 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -17800,23 +17800,6 @@ static int tg3_init_one(struct pci_dev *pdev, goto err_out_apeunmap; } - /* - * Reset chip in case UNDI or EFI driver did not shutdown - * DMA self test will enable WDMAC and we'll see (spurious) - * pending DMA on the PCI bus at that point. - */ - if ((tr32(HOSTCC_MODE) & HOSTCC_MODE_ENABLE) || - (tr32(WDMAC_MODE) & WDMAC_MODE_ENABLE)) { - tw32(MEMARB_MODE, MEMARB_MODE_ENABLE); - tg3_halt(tp, RESET_KIND_SHUTDOWN, 1); - } - - err = tg3_test_dma(tp); - if (err) { - dev_err(&pdev->dev, "DMA engine test failed, aborting\n"); - goto err_out_apeunmap; - } - intmbx = MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW; rcvmbx = MAILBOX_RCVRET_CON_IDX_0 + TG3_64BIT_REG_LOW; sndmbx = MAILBOX_SNDHOST_PROD_IDX_0 + TG3_64BIT_REG_LOW; @@ -17861,6 +17844,23 @@ static int tg3_init_one(struct pci_dev *pdev, sndmbx += 0xc; } + /* + * Reset chip in case UNDI or EFI driver did not shutdown + * DMA self test will enable WDMAC and we'll see (spurious) + * pending DMA on the PCI bus at that point. + */ + if ((tr32(HOSTCC_MODE) & HOSTCC_MODE_ENABLE) || + (tr32(WDMAC_MODE) & WDMAC_MODE_ENABLE)) { + tw32(MEMARB_MODE, MEMARB_MODE_ENABLE); + tg3_halt(tp, RESET_KIND_SHUTDOWN, 1); + } + + err = tg3_test_dma(tp); + if (err) { + dev_err(&pdev->dev, "DMA engine test failed, aborting\n"); + goto err_out_apeunmap; + } + tg3_init_coal(tp); pci_set_drvdata(pdev, dev); -- cgit v0.10.2 From 62633b8adb1b4c675b6f1bedc44eaa9062b02301 Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Sun, 21 Dec 2014 18:41:38 +0100 Subject: net: ethernet: micrel: ksz884x.c: Remove unused function Remove the function port_cfg_dis_learn() that is not used anywhere. This was partially found by using a static code analysis program called cppcheck. Signed-off-by: Rickard Strandqvist Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/micrel/ksz884x.c b/drivers/net/ethernet/micrel/ksz884x.c index f1ebed6c..2fa6ae0 100644 --- a/drivers/net/ethernet/micrel/ksz884x.c +++ b/drivers/net/ethernet/micrel/ksz884x.c @@ -2303,12 +2303,6 @@ static inline int port_chk_force_flow_ctrl(struct ksz_hw *hw, int p) /* Spanning Tree */ -static inline void port_cfg_dis_learn(struct ksz_hw *hw, int p, int set) -{ - port_cfg(hw, p, - KS8842_PORT_CTRL_2_OFFSET, PORT_LEARN_DISABLE, set); -} - static inline void port_cfg_rx(struct ksz_hw *hw, int p, int set) { port_cfg(hw, p, -- cgit v0.10.2 From f3ba9d490d6e9371bbda5f45a22b6b3313125f97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gl=C3=B6ckner?= Date: Sun, 21 Dec 2014 20:27:39 +0100 Subject: net: s6gmac: remove driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The s6000 Xtensa support has been removed from the kernel in 4006e565e1500db4. There are no other chips using this driver. While the Mentor/Alcatel PE-MCXMAC IP core is also used in other designs (Freescale Gianfar/UCC, QLogic NetXen, Solarflare, Agere ET-1310, Netlogic XLR/XLS), none of these use this driver as it heavily depends on the s6000 DMA engine. In fact, there is no code sharing across any of the aforementioned devices. Signed-off-by: Daniel Glöckner Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index df76050..eadcb05 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -156,18 +156,6 @@ source "drivers/net/ethernet/realtek/Kconfig" source "drivers/net/ethernet/renesas/Kconfig" source "drivers/net/ethernet/rdc/Kconfig" source "drivers/net/ethernet/rocker/Kconfig" - -config S6GMAC - tristate "S6105 GMAC ethernet support" - depends on XTENSA_VARIANT_S6000 - select PHYLIB - ---help--- - This driver supports the on chip ethernet device on the - S6105 xtensa processor. - - To compile this driver as a module, choose M here. The module - will be called s6gmac. - source "drivers/net/ethernet/samsung/Kconfig" source "drivers/net/ethernet/seeq/Kconfig" source "drivers/net/ethernet/silan/Kconfig" diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index bf56f8b..1367afc 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -66,7 +66,6 @@ obj-$(CONFIG_NET_VENDOR_REALTEK) += realtek/ obj-$(CONFIG_SH_ETH) += renesas/ obj-$(CONFIG_NET_VENDOR_RDC) += rdc/ obj-$(CONFIG_NET_VENDOR_ROCKER) += rocker/ -obj-$(CONFIG_S6GMAC) += s6gmac.o obj-$(CONFIG_NET_VENDOR_SAMSUNG) += samsung/ obj-$(CONFIG_NET_VENDOR_SEEQ) += seeq/ obj-$(CONFIG_NET_VENDOR_SILAN) += silan/ diff --git a/drivers/net/ethernet/s6gmac.c b/drivers/net/ethernet/s6gmac.c deleted file mode 100644 index f537cbe..0000000 --- a/drivers/net/ethernet/s6gmac.c +++ /dev/null @@ -1,1058 +0,0 @@ -/* - * Ethernet driver for S6105 on chip network device - * (c)2008 emlix GmbH http://www.emlix.com - * Authors: Oskar Schirmer - * Daniel Gloeckner - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRV_NAME "s6gmac" -#define DRV_PRMT DRV_NAME ": " - - -/* register declarations */ - -#define S6_GMAC_MACCONF1 0x000 -#define S6_GMAC_MACCONF1_TXENA 0 -#define S6_GMAC_MACCONF1_SYNCTX 1 -#define S6_GMAC_MACCONF1_RXENA 2 -#define S6_GMAC_MACCONF1_SYNCRX 3 -#define S6_GMAC_MACCONF1_TXFLOWCTRL 4 -#define S6_GMAC_MACCONF1_RXFLOWCTRL 5 -#define S6_GMAC_MACCONF1_LOOPBACK 8 -#define S6_GMAC_MACCONF1_RESTXFUNC 16 -#define S6_GMAC_MACCONF1_RESRXFUNC 17 -#define S6_GMAC_MACCONF1_RESTXMACCTRL 18 -#define S6_GMAC_MACCONF1_RESRXMACCTRL 19 -#define S6_GMAC_MACCONF1_SIMULRES 30 -#define S6_GMAC_MACCONF1_SOFTRES 31 -#define S6_GMAC_MACCONF2 0x004 -#define S6_GMAC_MACCONF2_FULL 0 -#define S6_GMAC_MACCONF2_CRCENA 1 -#define S6_GMAC_MACCONF2_PADCRCENA 2 -#define S6_GMAC_MACCONF2_LENGTHFCHK 4 -#define S6_GMAC_MACCONF2_HUGEFRAMENA 5 -#define S6_GMAC_MACCONF2_IFMODE 8 -#define S6_GMAC_MACCONF2_IFMODE_NIBBLE 1 -#define S6_GMAC_MACCONF2_IFMODE_BYTE 2 -#define S6_GMAC_MACCONF2_IFMODE_MASK 3 -#define S6_GMAC_MACCONF2_PREAMBLELEN 12 -#define S6_GMAC_MACCONF2_PREAMBLELEN_MASK 0x0F -#define S6_GMAC_MACIPGIFG 0x008 -#define S6_GMAC_MACIPGIFG_B2BINTERPGAP 0 -#define S6_GMAC_MACIPGIFG_B2BINTERPGAP_MASK 0x7F -#define S6_GMAC_MACIPGIFG_MINIFGENFORCE 8 -#define S6_GMAC_MACIPGIFG_B2BINTERPGAP2 16 -#define S6_GMAC_MACIPGIFG_B2BINTERPGAP1 24 -#define S6_GMAC_MACHALFDUPLEX 0x00C -#define S6_GMAC_MACHALFDUPLEX_COLLISWIN 0 -#define S6_GMAC_MACHALFDUPLEX_COLLISWIN_MASK 0x3F -#define S6_GMAC_MACHALFDUPLEX_RETXMAX 12 -#define S6_GMAC_MACHALFDUPLEX_RETXMAX_MASK 0x0F -#define S6_GMAC_MACHALFDUPLEX_EXCESSDEF 16 -#define S6_GMAC_MACHALFDUPLEX_NOBACKOFF 17 -#define S6_GMAC_MACHALFDUPLEX_BPNOBCKOF 18 -#define S6_GMAC_MACHALFDUPLEX_ALTBEBENA 19 -#define S6_GMAC_MACHALFDUPLEX_ALTBEBTRN 20 -#define S6_GMAC_MACHALFDUPLEX_ALTBEBTR_MASK 0x0F -#define S6_GMAC_MACMAXFRAMELEN 0x010 -#define S6_GMAC_MACMIICONF 0x020 -#define S6_GMAC_MACMIICONF_CSEL 0 -#define S6_GMAC_MACMIICONF_CSEL_DIV10 0 -#define S6_GMAC_MACMIICONF_CSEL_DIV12 1 -#define S6_GMAC_MACMIICONF_CSEL_DIV14 2 -#define S6_GMAC_MACMIICONF_CSEL_DIV18 3 -#define S6_GMAC_MACMIICONF_CSEL_DIV24 4 -#define S6_GMAC_MACMIICONF_CSEL_DIV34 5 -#define S6_GMAC_MACMIICONF_CSEL_DIV68 6 -#define S6_GMAC_MACMIICONF_CSEL_DIV168 7 -#define S6_GMAC_MACMIICONF_CSEL_MASK 7 -#define S6_GMAC_MACMIICONF_PREAMBLESUPR 4 -#define S6_GMAC_MACMIICONF_SCANAUTOINCR 5 -#define S6_GMAC_MACMIICMD 0x024 -#define S6_GMAC_MACMIICMD_READ 0 -#define S6_GMAC_MACMIICMD_SCAN 1 -#define S6_GMAC_MACMIIADDR 0x028 -#define S6_GMAC_MACMIIADDR_REG 0 -#define S6_GMAC_MACMIIADDR_REG_MASK 0x1F -#define S6_GMAC_MACMIIADDR_PHY 8 -#define S6_GMAC_MACMIIADDR_PHY_MASK 0x1F -#define S6_GMAC_MACMIICTRL 0x02C -#define S6_GMAC_MACMIISTAT 0x030 -#define S6_GMAC_MACMIIINDI 0x034 -#define S6_GMAC_MACMIIINDI_BUSY 0 -#define S6_GMAC_MACMIIINDI_SCAN 1 -#define S6_GMAC_MACMIIINDI_INVAL 2 -#define S6_GMAC_MACINTERFSTAT 0x03C -#define S6_GMAC_MACINTERFSTAT_LINKFAIL 3 -#define S6_GMAC_MACINTERFSTAT_EXCESSDEF 9 -#define S6_GMAC_MACSTATADDR1 0x040 -#define S6_GMAC_MACSTATADDR2 0x044 - -#define S6_GMAC_FIFOCONF0 0x048 -#define S6_GMAC_FIFOCONF0_HSTRSTWT 0 -#define S6_GMAC_FIFOCONF0_HSTRSTSR 1 -#define S6_GMAC_FIFOCONF0_HSTRSTFR 2 -#define S6_GMAC_FIFOCONF0_HSTRSTST 3 -#define S6_GMAC_FIFOCONF0_HSTRSTFT 4 -#define S6_GMAC_FIFOCONF0_WTMENREQ 8 -#define S6_GMAC_FIFOCONF0_SRFENREQ 9 -#define S6_GMAC_FIFOCONF0_FRFENREQ 10 -#define S6_GMAC_FIFOCONF0_STFENREQ 11 -#define S6_GMAC_FIFOCONF0_FTFENREQ 12 -#define S6_GMAC_FIFOCONF0_WTMENRPLY 16 -#define S6_GMAC_FIFOCONF0_SRFENRPLY 17 -#define S6_GMAC_FIFOCONF0_FRFENRPLY 18 -#define S6_GMAC_FIFOCONF0_STFENRPLY 19 -#define S6_GMAC_FIFOCONF0_FTFENRPLY 20 -#define S6_GMAC_FIFOCONF1 0x04C -#define S6_GMAC_FIFOCONF2 0x050 -#define S6_GMAC_FIFOCONF2_CFGLWM 0 -#define S6_GMAC_FIFOCONF2_CFGHWM 16 -#define S6_GMAC_FIFOCONF3 0x054 -#define S6_GMAC_FIFOCONF3_CFGFTTH 0 -#define S6_GMAC_FIFOCONF3_CFGHWMFT 16 -#define S6_GMAC_FIFOCONF4 0x058 -#define S6_GMAC_FIFOCONF_RSV_PREVDROP 0 -#define S6_GMAC_FIFOCONF_RSV_RUNT 1 -#define S6_GMAC_FIFOCONF_RSV_FALSECAR 2 -#define S6_GMAC_FIFOCONF_RSV_CODEERR 3 -#define S6_GMAC_FIFOCONF_RSV_CRCERR 4 -#define S6_GMAC_FIFOCONF_RSV_LENGTHERR 5 -#define S6_GMAC_FIFOCONF_RSV_LENRANGE 6 -#define S6_GMAC_FIFOCONF_RSV_OK 7 -#define S6_GMAC_FIFOCONF_RSV_MULTICAST 8 -#define S6_GMAC_FIFOCONF_RSV_BROADCAST 9 -#define S6_GMAC_FIFOCONF_RSV_DRIBBLE 10 -#define S6_GMAC_FIFOCONF_RSV_CTRLFRAME 11 -#define S6_GMAC_FIFOCONF_RSV_PAUSECTRL 12 -#define S6_GMAC_FIFOCONF_RSV_UNOPCODE 13 -#define S6_GMAC_FIFOCONF_RSV_VLANTAG 14 -#define S6_GMAC_FIFOCONF_RSV_LONGEVENT 15 -#define S6_GMAC_FIFOCONF_RSV_TRUNCATED 16 -#define S6_GMAC_FIFOCONF_RSV_MASK 0x3FFFF -#define S6_GMAC_FIFOCONF5 0x05C -#define S6_GMAC_FIFOCONF5_DROPLT64 18 -#define S6_GMAC_FIFOCONF5_CFGBYTM 19 -#define S6_GMAC_FIFOCONF5_RXDROPSIZE 20 -#define S6_GMAC_FIFOCONF5_RXDROPSIZE_MASK 0xF - -#define S6_GMAC_STAT_REGS 0x080 -#define S6_GMAC_STAT_SIZE_MIN 12 -#define S6_GMAC_STATTR64 0x080 -#define S6_GMAC_STATTR64_SIZE 18 -#define S6_GMAC_STATTR127 0x084 -#define S6_GMAC_STATTR127_SIZE 18 -#define S6_GMAC_STATTR255 0x088 -#define S6_GMAC_STATTR255_SIZE 18 -#define S6_GMAC_STATTR511 0x08C -#define S6_GMAC_STATTR511_SIZE 18 -#define S6_GMAC_STATTR1K 0x090 -#define S6_GMAC_STATTR1K_SIZE 18 -#define S6_GMAC_STATTRMAX 0x094 -#define S6_GMAC_STATTRMAX_SIZE 18 -#define S6_GMAC_STATTRMGV 0x098 -#define S6_GMAC_STATTRMGV_SIZE 18 -#define S6_GMAC_STATRBYT 0x09C -#define S6_GMAC_STATRBYT_SIZE 24 -#define S6_GMAC_STATRPKT 0x0A0 -#define S6_GMAC_STATRPKT_SIZE 18 -#define S6_GMAC_STATRFCS 0x0A4 -#define S6_GMAC_STATRFCS_SIZE 12 -#define S6_GMAC_STATRMCA 0x0A8 -#define S6_GMAC_STATRMCA_SIZE 18 -#define S6_GMAC_STATRBCA 0x0AC -#define S6_GMAC_STATRBCA_SIZE 22 -#define S6_GMAC_STATRXCF 0x0B0 -#define S6_GMAC_STATRXCF_SIZE 18 -#define S6_GMAC_STATRXPF 0x0B4 -#define S6_GMAC_STATRXPF_SIZE 12 -#define S6_GMAC_STATRXUO 0x0B8 -#define S6_GMAC_STATRXUO_SIZE 12 -#define S6_GMAC_STATRALN 0x0BC -#define S6_GMAC_STATRALN_SIZE 12 -#define S6_GMAC_STATRFLR 0x0C0 -#define S6_GMAC_STATRFLR_SIZE 16 -#define S6_GMAC_STATRCDE 0x0C4 -#define S6_GMAC_STATRCDE_SIZE 12 -#define S6_GMAC_STATRCSE 0x0C8 -#define S6_GMAC_STATRCSE_SIZE 12 -#define S6_GMAC_STATRUND 0x0CC -#define S6_GMAC_STATRUND_SIZE 12 -#define S6_GMAC_STATROVR 0x0D0 -#define S6_GMAC_STATROVR_SIZE 12 -#define S6_GMAC_STATRFRG 0x0D4 -#define S6_GMAC_STATRFRG_SIZE 12 -#define S6_GMAC_STATRJBR 0x0D8 -#define S6_GMAC_STATRJBR_SIZE 12 -#define S6_GMAC_STATRDRP 0x0DC -#define S6_GMAC_STATRDRP_SIZE 12 -#define S6_GMAC_STATTBYT 0x0E0 -#define S6_GMAC_STATTBYT_SIZE 24 -#define S6_GMAC_STATTPKT 0x0E4 -#define S6_GMAC_STATTPKT_SIZE 18 -#define S6_GMAC_STATTMCA 0x0E8 -#define S6_GMAC_STATTMCA_SIZE 18 -#define S6_GMAC_STATTBCA 0x0EC -#define S6_GMAC_STATTBCA_SIZE 18 -#define S6_GMAC_STATTXPF 0x0F0 -#define S6_GMAC_STATTXPF_SIZE 12 -#define S6_GMAC_STATTDFR 0x0F4 -#define S6_GMAC_STATTDFR_SIZE 12 -#define S6_GMAC_STATTEDF 0x0F8 -#define S6_GMAC_STATTEDF_SIZE 12 -#define S6_GMAC_STATTSCL 0x0FC -#define S6_GMAC_STATTSCL_SIZE 12 -#define S6_GMAC_STATTMCL 0x100 -#define S6_GMAC_STATTMCL_SIZE 12 -#define S6_GMAC_STATTLCL 0x104 -#define S6_GMAC_STATTLCL_SIZE 12 -#define S6_GMAC_STATTXCL 0x108 -#define S6_GMAC_STATTXCL_SIZE 12 -#define S6_GMAC_STATTNCL 0x10C -#define S6_GMAC_STATTNCL_SIZE 13 -#define S6_GMAC_STATTPFH 0x110 -#define S6_GMAC_STATTPFH_SIZE 12 -#define S6_GMAC_STATTDRP 0x114 -#define S6_GMAC_STATTDRP_SIZE 12 -#define S6_GMAC_STATTJBR 0x118 -#define S6_GMAC_STATTJBR_SIZE 12 -#define S6_GMAC_STATTFCS 0x11C -#define S6_GMAC_STATTFCS_SIZE 12 -#define S6_GMAC_STATTXCF 0x120 -#define S6_GMAC_STATTXCF_SIZE 12 -#define S6_GMAC_STATTOVR 0x124 -#define S6_GMAC_STATTOVR_SIZE 12 -#define S6_GMAC_STATTUND 0x128 -#define S6_GMAC_STATTUND_SIZE 12 -#define S6_GMAC_STATTFRG 0x12C -#define S6_GMAC_STATTFRG_SIZE 12 -#define S6_GMAC_STATCARRY(n) (0x130 + 4*(n)) -#define S6_GMAC_STATCARRYMSK(n) (0x138 + 4*(n)) -#define S6_GMAC_STATCARRY1_RDRP 0 -#define S6_GMAC_STATCARRY1_RJBR 1 -#define S6_GMAC_STATCARRY1_RFRG 2 -#define S6_GMAC_STATCARRY1_ROVR 3 -#define S6_GMAC_STATCARRY1_RUND 4 -#define S6_GMAC_STATCARRY1_RCSE 5 -#define S6_GMAC_STATCARRY1_RCDE 6 -#define S6_GMAC_STATCARRY1_RFLR 7 -#define S6_GMAC_STATCARRY1_RALN 8 -#define S6_GMAC_STATCARRY1_RXUO 9 -#define S6_GMAC_STATCARRY1_RXPF 10 -#define S6_GMAC_STATCARRY1_RXCF 11 -#define S6_GMAC_STATCARRY1_RBCA 12 -#define S6_GMAC_STATCARRY1_RMCA 13 -#define S6_GMAC_STATCARRY1_RFCS 14 -#define S6_GMAC_STATCARRY1_RPKT 15 -#define S6_GMAC_STATCARRY1_RBYT 16 -#define S6_GMAC_STATCARRY1_TRMGV 25 -#define S6_GMAC_STATCARRY1_TRMAX 26 -#define S6_GMAC_STATCARRY1_TR1K 27 -#define S6_GMAC_STATCARRY1_TR511 28 -#define S6_GMAC_STATCARRY1_TR255 29 -#define S6_GMAC_STATCARRY1_TR127 30 -#define S6_GMAC_STATCARRY1_TR64 31 -#define S6_GMAC_STATCARRY2_TDRP 0 -#define S6_GMAC_STATCARRY2_TPFH 1 -#define S6_GMAC_STATCARRY2_TNCL 2 -#define S6_GMAC_STATCARRY2_TXCL 3 -#define S6_GMAC_STATCARRY2_TLCL 4 -#define S6_GMAC_STATCARRY2_TMCL 5 -#define S6_GMAC_STATCARRY2_TSCL 6 -#define S6_GMAC_STATCARRY2_TEDF 7 -#define S6_GMAC_STATCARRY2_TDFR 8 -#define S6_GMAC_STATCARRY2_TXPF 9 -#define S6_GMAC_STATCARRY2_TBCA 10 -#define S6_GMAC_STATCARRY2_TMCA 11 -#define S6_GMAC_STATCARRY2_TPKT 12 -#define S6_GMAC_STATCARRY2_TBYT 13 -#define S6_GMAC_STATCARRY2_TFRG 14 -#define S6_GMAC_STATCARRY2_TUND 15 -#define S6_GMAC_STATCARRY2_TOVR 16 -#define S6_GMAC_STATCARRY2_TXCF 17 -#define S6_GMAC_STATCARRY2_TFCS 18 -#define S6_GMAC_STATCARRY2_TJBR 19 - -#define S6_GMAC_HOST_PBLKCTRL 0x140 -#define S6_GMAC_HOST_PBLKCTRL_TXENA 0 -#define S6_GMAC_HOST_PBLKCTRL_RXENA 1 -#define S6_GMAC_HOST_PBLKCTRL_TXSRES 2 -#define S6_GMAC_HOST_PBLKCTRL_RXSRES 3 -#define S6_GMAC_HOST_PBLKCTRL_TXBSIZ 8 -#define S6_GMAC_HOST_PBLKCTRL_RXBSIZ 12 -#define S6_GMAC_HOST_PBLKCTRL_SIZ_16 4 -#define S6_GMAC_HOST_PBLKCTRL_SIZ_32 5 -#define S6_GMAC_HOST_PBLKCTRL_SIZ_64 6 -#define S6_GMAC_HOST_PBLKCTRL_SIZ_128 7 -#define S6_GMAC_HOST_PBLKCTRL_SIZ_MASK 0xF -#define S6_GMAC_HOST_PBLKCTRL_STATENA 16 -#define S6_GMAC_HOST_PBLKCTRL_STATAUTOZ 17 -#define S6_GMAC_HOST_PBLKCTRL_STATCLEAR 18 -#define S6_GMAC_HOST_PBLKCTRL_RGMII 19 -#define S6_GMAC_HOST_INTMASK 0x144 -#define S6_GMAC_HOST_INTSTAT 0x148 -#define S6_GMAC_HOST_INT_TXBURSTOVER 3 -#define S6_GMAC_HOST_INT_TXPREWOVER 4 -#define S6_GMAC_HOST_INT_RXBURSTUNDER 5 -#define S6_GMAC_HOST_INT_RXPOSTRFULL 6 -#define S6_GMAC_HOST_INT_RXPOSTRUNDER 7 -#define S6_GMAC_HOST_RXFIFOHWM 0x14C -#define S6_GMAC_HOST_CTRLFRAMXP 0x150 -#define S6_GMAC_HOST_DSTADDRLO(n) (0x160 + 8*(n)) -#define S6_GMAC_HOST_DSTADDRHI(n) (0x164 + 8*(n)) -#define S6_GMAC_HOST_DSTMASKLO(n) (0x180 + 8*(n)) -#define S6_GMAC_HOST_DSTMASKHI(n) (0x184 + 8*(n)) - -#define S6_GMAC_BURST_PREWR 0x1B0 -#define S6_GMAC_BURST_PREWR_LEN 0 -#define S6_GMAC_BURST_PREWR_LEN_MASK ((1 << 20) - 1) -#define S6_GMAC_BURST_PREWR_CFE 20 -#define S6_GMAC_BURST_PREWR_PPE 21 -#define S6_GMAC_BURST_PREWR_FCS 22 -#define S6_GMAC_BURST_PREWR_PAD 23 -#define S6_GMAC_BURST_POSTRD 0x1D0 -#define S6_GMAC_BURST_POSTRD_LEN 0 -#define S6_GMAC_BURST_POSTRD_LEN_MASK ((1 << 20) - 1) -#define S6_GMAC_BURST_POSTRD_DROP 20 - - -/* data handling */ - -#define S6_NUM_TX_SKB 8 /* must be larger than TX fifo size */ -#define S6_NUM_RX_SKB 16 -#define S6_MAX_FRLEN 1536 - -struct s6gmac { - u32 reg; - u32 tx_dma; - u32 rx_dma; - u32 io; - u8 tx_chan; - u8 rx_chan; - spinlock_t lock; - u8 tx_skb_i, tx_skb_o; - u8 rx_skb_i, rx_skb_o; - struct sk_buff *tx_skb[S6_NUM_TX_SKB]; - struct sk_buff *rx_skb[S6_NUM_RX_SKB]; - unsigned long carry[sizeof(struct net_device_stats) / sizeof(long)]; - unsigned long stats[sizeof(struct net_device_stats) / sizeof(long)]; - struct phy_device *phydev; - struct { - struct mii_bus *bus; - int irq[PHY_MAX_ADDR]; - } mii; - struct { - unsigned int mbit; - u8 giga; - u8 isup; - u8 full; - } link; -}; - -static void s6gmac_rx_fillfifo(struct net_device *dev) -{ - struct s6gmac *pd = netdev_priv(dev); - struct sk_buff *skb; - while ((((u8)(pd->rx_skb_i - pd->rx_skb_o)) < S6_NUM_RX_SKB) && - (!s6dmac_fifo_full(pd->rx_dma, pd->rx_chan)) && - (skb = netdev_alloc_skb(dev, S6_MAX_FRLEN + 2))) { - pd->rx_skb[(pd->rx_skb_i++) % S6_NUM_RX_SKB] = skb; - s6dmac_put_fifo_cache(pd->rx_dma, pd->rx_chan, - pd->io, (u32)skb->data, S6_MAX_FRLEN); - } -} - -static void s6gmac_rx_interrupt(struct net_device *dev) -{ - struct s6gmac *pd = netdev_priv(dev); - u32 pfx; - struct sk_buff *skb; - while (((u8)(pd->rx_skb_i - pd->rx_skb_o)) > - s6dmac_pending_count(pd->rx_dma, pd->rx_chan)) { - skb = pd->rx_skb[(pd->rx_skb_o++) % S6_NUM_RX_SKB]; - pfx = readl(pd->reg + S6_GMAC_BURST_POSTRD); - if (pfx & (1 << S6_GMAC_BURST_POSTRD_DROP)) { - dev_kfree_skb_irq(skb); - } else { - skb_put(skb, (pfx >> S6_GMAC_BURST_POSTRD_LEN) - & S6_GMAC_BURST_POSTRD_LEN_MASK); - skb->protocol = eth_type_trans(skb, dev); - skb->ip_summed = CHECKSUM_UNNECESSARY; - netif_rx(skb); - } - } -} - -static void s6gmac_tx_interrupt(struct net_device *dev) -{ - struct s6gmac *pd = netdev_priv(dev); - while (((u8)(pd->tx_skb_i - pd->tx_skb_o)) > - s6dmac_pending_count(pd->tx_dma, pd->tx_chan)) { - dev_kfree_skb_irq(pd->tx_skb[(pd->tx_skb_o++) % S6_NUM_TX_SKB]); - } - if (!s6dmac_fifo_full(pd->tx_dma, pd->tx_chan)) - netif_wake_queue(dev); -} - -struct s6gmac_statinf { - unsigned reg_size : 4; /* 0: unused */ - unsigned reg_off : 6; - unsigned net_index : 6; -}; - -#define S6_STATS_B (8 * sizeof(u32)) -#define S6_STATS_C(b, r, f) [b] = { \ - BUILD_BUG_ON_ZERO(r##_SIZE < S6_GMAC_STAT_SIZE_MIN) + \ - BUILD_BUG_ON_ZERO((r##_SIZE - (S6_GMAC_STAT_SIZE_MIN - 1)) \ - >= (1<<4)) + \ - r##_SIZE - (S6_GMAC_STAT_SIZE_MIN - 1), \ - BUILD_BUG_ON_ZERO(((unsigned)((r - S6_GMAC_STAT_REGS) / sizeof(u32))) \ - >= ((1<<6)-1)) + \ - (r - S6_GMAC_STAT_REGS) / sizeof(u32), \ - BUILD_BUG_ON_ZERO((offsetof(struct net_device_stats, f)) \ - % sizeof(unsigned long)) + \ - BUILD_BUG_ON_ZERO((((unsigned)(offsetof(struct net_device_stats, f)) \ - / sizeof(unsigned long)) >= (1<<6))) + \ - BUILD_BUG_ON_ZERO((sizeof(((struct net_device_stats *)0)->f) \ - != sizeof(unsigned long))) + \ - (offsetof(struct net_device_stats, f)) / sizeof(unsigned long)}, - -static const struct s6gmac_statinf statinf[2][S6_STATS_B] = { { - S6_STATS_C(S6_GMAC_STATCARRY1_RBYT, S6_GMAC_STATRBYT, rx_bytes) - S6_STATS_C(S6_GMAC_STATCARRY1_RPKT, S6_GMAC_STATRPKT, rx_packets) - S6_STATS_C(S6_GMAC_STATCARRY1_RFCS, S6_GMAC_STATRFCS, rx_crc_errors) - S6_STATS_C(S6_GMAC_STATCARRY1_RMCA, S6_GMAC_STATRMCA, multicast) - S6_STATS_C(S6_GMAC_STATCARRY1_RALN, S6_GMAC_STATRALN, rx_frame_errors) - S6_STATS_C(S6_GMAC_STATCARRY1_RFLR, S6_GMAC_STATRFLR, rx_length_errors) - S6_STATS_C(S6_GMAC_STATCARRY1_RCDE, S6_GMAC_STATRCDE, rx_missed_errors) - S6_STATS_C(S6_GMAC_STATCARRY1_RUND, S6_GMAC_STATRUND, rx_length_errors) - S6_STATS_C(S6_GMAC_STATCARRY1_ROVR, S6_GMAC_STATROVR, rx_length_errors) - S6_STATS_C(S6_GMAC_STATCARRY1_RFRG, S6_GMAC_STATRFRG, rx_crc_errors) - S6_STATS_C(S6_GMAC_STATCARRY1_RJBR, S6_GMAC_STATRJBR, rx_crc_errors) - S6_STATS_C(S6_GMAC_STATCARRY1_RDRP, S6_GMAC_STATRDRP, rx_dropped) -}, { - S6_STATS_C(S6_GMAC_STATCARRY2_TBYT, S6_GMAC_STATTBYT, tx_bytes) - S6_STATS_C(S6_GMAC_STATCARRY2_TPKT, S6_GMAC_STATTPKT, tx_packets) - S6_STATS_C(S6_GMAC_STATCARRY2_TEDF, S6_GMAC_STATTEDF, tx_aborted_errors) - S6_STATS_C(S6_GMAC_STATCARRY2_TXCL, S6_GMAC_STATTXCL, tx_aborted_errors) - S6_STATS_C(S6_GMAC_STATCARRY2_TNCL, S6_GMAC_STATTNCL, collisions) - S6_STATS_C(S6_GMAC_STATCARRY2_TDRP, S6_GMAC_STATTDRP, tx_dropped) - S6_STATS_C(S6_GMAC_STATCARRY2_TJBR, S6_GMAC_STATTJBR, tx_errors) - S6_STATS_C(S6_GMAC_STATCARRY2_TFCS, S6_GMAC_STATTFCS, tx_errors) - S6_STATS_C(S6_GMAC_STATCARRY2_TOVR, S6_GMAC_STATTOVR, tx_errors) - S6_STATS_C(S6_GMAC_STATCARRY2_TUND, S6_GMAC_STATTUND, tx_errors) - S6_STATS_C(S6_GMAC_STATCARRY2_TFRG, S6_GMAC_STATTFRG, tx_errors) -} }; - -static void s6gmac_stats_collect(struct s6gmac *pd, - const struct s6gmac_statinf *inf) -{ - int b; - for (b = 0; b < S6_STATS_B; b++) { - if (inf[b].reg_size) { - pd->stats[inf[b].net_index] += - readl(pd->reg + S6_GMAC_STAT_REGS - + sizeof(u32) * inf[b].reg_off); - } - } -} - -static void s6gmac_stats_carry(struct s6gmac *pd, - const struct s6gmac_statinf *inf, u32 mask) -{ - int b; - while (mask) { - b = fls(mask) - 1; - mask &= ~(1 << b); - pd->carry[inf[b].net_index] += (1 << inf[b].reg_size); - } -} - -static inline u32 s6gmac_stats_pending(struct s6gmac *pd, int carry) -{ - int r = readl(pd->reg + S6_GMAC_STATCARRY(carry)) & - ~readl(pd->reg + S6_GMAC_STATCARRYMSK(carry)); - return r; -} - -static inline void s6gmac_stats_interrupt(struct s6gmac *pd, int carry) -{ - u32 mask; - mask = s6gmac_stats_pending(pd, carry); - if (mask) { - writel(mask, pd->reg + S6_GMAC_STATCARRY(carry)); - s6gmac_stats_carry(pd, &statinf[carry][0], mask); - } -} - -static irqreturn_t s6gmac_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = (struct net_device *)dev_id; - struct s6gmac *pd = netdev_priv(dev); - if (!dev) - return IRQ_NONE; - spin_lock(&pd->lock); - if (s6dmac_termcnt_irq(pd->rx_dma, pd->rx_chan)) - s6gmac_rx_interrupt(dev); - s6gmac_rx_fillfifo(dev); - if (s6dmac_termcnt_irq(pd->tx_dma, pd->tx_chan)) - s6gmac_tx_interrupt(dev); - s6gmac_stats_interrupt(pd, 0); - s6gmac_stats_interrupt(pd, 1); - spin_unlock(&pd->lock); - return IRQ_HANDLED; -} - -static inline void s6gmac_set_dstaddr(struct s6gmac *pd, int n, - u32 addrlo, u32 addrhi, u32 masklo, u32 maskhi) -{ - writel(addrlo, pd->reg + S6_GMAC_HOST_DSTADDRLO(n)); - writel(addrhi, pd->reg + S6_GMAC_HOST_DSTADDRHI(n)); - writel(masklo, pd->reg + S6_GMAC_HOST_DSTMASKLO(n)); - writel(maskhi, pd->reg + S6_GMAC_HOST_DSTMASKHI(n)); -} - -static inline void s6gmac_stop_device(struct net_device *dev) -{ - struct s6gmac *pd = netdev_priv(dev); - writel(0, pd->reg + S6_GMAC_MACCONF1); -} - -static inline void s6gmac_init_device(struct net_device *dev) -{ - struct s6gmac *pd = netdev_priv(dev); - int is_rgmii = !!(pd->phydev->supported - & (SUPPORTED_1000baseT_Full | SUPPORTED_1000baseT_Half)); -#if 0 - writel(1 << S6_GMAC_MACCONF1_SYNCTX | - 1 << S6_GMAC_MACCONF1_SYNCRX | - 1 << S6_GMAC_MACCONF1_TXFLOWCTRL | - 1 << S6_GMAC_MACCONF1_RXFLOWCTRL | - 1 << S6_GMAC_MACCONF1_RESTXFUNC | - 1 << S6_GMAC_MACCONF1_RESRXFUNC | - 1 << S6_GMAC_MACCONF1_RESTXMACCTRL | - 1 << S6_GMAC_MACCONF1_RESRXMACCTRL, - pd->reg + S6_GMAC_MACCONF1); -#endif - writel(1 << S6_GMAC_MACCONF1_SOFTRES, pd->reg + S6_GMAC_MACCONF1); - udelay(1000); - writel(1 << S6_GMAC_MACCONF1_TXENA | 1 << S6_GMAC_MACCONF1_RXENA, - pd->reg + S6_GMAC_MACCONF1); - writel(1 << S6_GMAC_HOST_PBLKCTRL_TXSRES | - 1 << S6_GMAC_HOST_PBLKCTRL_RXSRES, - pd->reg + S6_GMAC_HOST_PBLKCTRL); - writel(S6_GMAC_HOST_PBLKCTRL_SIZ_128 << S6_GMAC_HOST_PBLKCTRL_TXBSIZ | - S6_GMAC_HOST_PBLKCTRL_SIZ_128 << S6_GMAC_HOST_PBLKCTRL_RXBSIZ | - 1 << S6_GMAC_HOST_PBLKCTRL_STATENA | - 1 << S6_GMAC_HOST_PBLKCTRL_STATCLEAR | - is_rgmii << S6_GMAC_HOST_PBLKCTRL_RGMII, - pd->reg + S6_GMAC_HOST_PBLKCTRL); - writel(1 << S6_GMAC_MACCONF1_TXENA | - 1 << S6_GMAC_MACCONF1_RXENA | - (dev->flags & IFF_LOOPBACK ? 1 : 0) - << S6_GMAC_MACCONF1_LOOPBACK, - pd->reg + S6_GMAC_MACCONF1); - writel(dev->mtu && (dev->mtu < (S6_MAX_FRLEN - ETH_HLEN-ETH_FCS_LEN)) ? - dev->mtu+ETH_HLEN+ETH_FCS_LEN : S6_MAX_FRLEN, - pd->reg + S6_GMAC_MACMAXFRAMELEN); - writel((pd->link.full ? 1 : 0) << S6_GMAC_MACCONF2_FULL | - 1 << S6_GMAC_MACCONF2_PADCRCENA | - 1 << S6_GMAC_MACCONF2_LENGTHFCHK | - (pd->link.giga ? - S6_GMAC_MACCONF2_IFMODE_BYTE : - S6_GMAC_MACCONF2_IFMODE_NIBBLE) - << S6_GMAC_MACCONF2_IFMODE | - 7 << S6_GMAC_MACCONF2_PREAMBLELEN, - pd->reg + S6_GMAC_MACCONF2); - writel(0, pd->reg + S6_GMAC_MACSTATADDR1); - writel(0, pd->reg + S6_GMAC_MACSTATADDR2); - writel(1 << S6_GMAC_FIFOCONF0_WTMENREQ | - 1 << S6_GMAC_FIFOCONF0_SRFENREQ | - 1 << S6_GMAC_FIFOCONF0_FRFENREQ | - 1 << S6_GMAC_FIFOCONF0_STFENREQ | - 1 << S6_GMAC_FIFOCONF0_FTFENREQ, - pd->reg + S6_GMAC_FIFOCONF0); - writel(128 << S6_GMAC_FIFOCONF3_CFGFTTH | - 128 << S6_GMAC_FIFOCONF3_CFGHWMFT, - pd->reg + S6_GMAC_FIFOCONF3); - writel((S6_GMAC_FIFOCONF_RSV_MASK & ~( - 1 << S6_GMAC_FIFOCONF_RSV_RUNT | - 1 << S6_GMAC_FIFOCONF_RSV_CRCERR | - 1 << S6_GMAC_FIFOCONF_RSV_OK | - 1 << S6_GMAC_FIFOCONF_RSV_DRIBBLE | - 1 << S6_GMAC_FIFOCONF_RSV_CTRLFRAME | - 1 << S6_GMAC_FIFOCONF_RSV_PAUSECTRL | - 1 << S6_GMAC_FIFOCONF_RSV_UNOPCODE | - 1 << S6_GMAC_FIFOCONF_RSV_TRUNCATED)) | - 1 << S6_GMAC_FIFOCONF5_DROPLT64 | - pd->link.giga << S6_GMAC_FIFOCONF5_CFGBYTM | - 1 << S6_GMAC_FIFOCONF5_RXDROPSIZE, - pd->reg + S6_GMAC_FIFOCONF5); - writel(1 << S6_GMAC_FIFOCONF_RSV_RUNT | - 1 << S6_GMAC_FIFOCONF_RSV_CRCERR | - 1 << S6_GMAC_FIFOCONF_RSV_DRIBBLE | - 1 << S6_GMAC_FIFOCONF_RSV_CTRLFRAME | - 1 << S6_GMAC_FIFOCONF_RSV_PAUSECTRL | - 1 << S6_GMAC_FIFOCONF_RSV_UNOPCODE | - 1 << S6_GMAC_FIFOCONF_RSV_TRUNCATED, - pd->reg + S6_GMAC_FIFOCONF4); - s6gmac_set_dstaddr(pd, 0, - 0xFFFFFFFF, 0x0000FFFF, 0xFFFFFFFF, 0x0000FFFF); - s6gmac_set_dstaddr(pd, 1, - dev->dev_addr[5] | - dev->dev_addr[4] << 8 | - dev->dev_addr[3] << 16 | - dev->dev_addr[2] << 24, - dev->dev_addr[1] | - dev->dev_addr[0] << 8, - 0xFFFFFFFF, 0x0000FFFF); - s6gmac_set_dstaddr(pd, 2, - 0x00000000, 0x00000100, 0x00000000, 0x00000100); - s6gmac_set_dstaddr(pd, 3, - 0x00000000, 0x00000000, 0x00000000, 0x00000000); - writel(1 << S6_GMAC_HOST_PBLKCTRL_TXENA | - 1 << S6_GMAC_HOST_PBLKCTRL_RXENA | - S6_GMAC_HOST_PBLKCTRL_SIZ_128 << S6_GMAC_HOST_PBLKCTRL_TXBSIZ | - S6_GMAC_HOST_PBLKCTRL_SIZ_128 << S6_GMAC_HOST_PBLKCTRL_RXBSIZ | - 1 << S6_GMAC_HOST_PBLKCTRL_STATENA | - 1 << S6_GMAC_HOST_PBLKCTRL_STATCLEAR | - is_rgmii << S6_GMAC_HOST_PBLKCTRL_RGMII, - pd->reg + S6_GMAC_HOST_PBLKCTRL); -} - -static void s6mii_enable(struct s6gmac *pd) -{ - writel(readl(pd->reg + S6_GMAC_MACCONF1) & - ~(1 << S6_GMAC_MACCONF1_SOFTRES), - pd->reg + S6_GMAC_MACCONF1); - writel((readl(pd->reg + S6_GMAC_MACMIICONF) - & ~(S6_GMAC_MACMIICONF_CSEL_MASK << S6_GMAC_MACMIICONF_CSEL)) - | (S6_GMAC_MACMIICONF_CSEL_DIV168 << S6_GMAC_MACMIICONF_CSEL), - pd->reg + S6_GMAC_MACMIICONF); -} - -static int s6mii_busy(struct s6gmac *pd, int tmo) -{ - while (readl(pd->reg + S6_GMAC_MACMIIINDI)) { - if (--tmo == 0) - return -ETIME; - udelay(64); - } - return 0; -} - -static int s6mii_read(struct mii_bus *bus, int phy_addr, int regnum) -{ - struct s6gmac *pd = bus->priv; - s6mii_enable(pd); - if (s6mii_busy(pd, 256)) - return -ETIME; - writel(phy_addr << S6_GMAC_MACMIIADDR_PHY | - regnum << S6_GMAC_MACMIIADDR_REG, - pd->reg + S6_GMAC_MACMIIADDR); - writel(1 << S6_GMAC_MACMIICMD_READ, pd->reg + S6_GMAC_MACMIICMD); - writel(0, pd->reg + S6_GMAC_MACMIICMD); - if (s6mii_busy(pd, 256)) - return -ETIME; - return (u16)readl(pd->reg + S6_GMAC_MACMIISTAT); -} - -static int s6mii_write(struct mii_bus *bus, int phy_addr, int regnum, u16 value) -{ - struct s6gmac *pd = bus->priv; - s6mii_enable(pd); - if (s6mii_busy(pd, 256)) - return -ETIME; - writel(phy_addr << S6_GMAC_MACMIIADDR_PHY | - regnum << S6_GMAC_MACMIIADDR_REG, - pd->reg + S6_GMAC_MACMIIADDR); - writel(value, pd->reg + S6_GMAC_MACMIICTRL); - if (s6mii_busy(pd, 256)) - return -ETIME; - return 0; -} - -static int s6mii_reset(struct mii_bus *bus) -{ - struct s6gmac *pd = bus->priv; - s6mii_enable(pd); - if (s6mii_busy(pd, PHY_INIT_TIMEOUT)) - return -ETIME; - return 0; -} - -static void s6gmac_set_rgmii_txclock(struct s6gmac *pd) -{ - u32 pllsel = readl(S6_REG_GREG1 + S6_GREG1_PLLSEL); - pllsel &= ~(S6_GREG1_PLLSEL_GMAC_MASK << S6_GREG1_PLLSEL_GMAC); - switch (pd->link.mbit) { - case 10: - pllsel |= S6_GREG1_PLLSEL_GMAC_2500KHZ << S6_GREG1_PLLSEL_GMAC; - break; - case 100: - pllsel |= S6_GREG1_PLLSEL_GMAC_25MHZ << S6_GREG1_PLLSEL_GMAC; - break; - case 1000: - pllsel |= S6_GREG1_PLLSEL_GMAC_125MHZ << S6_GREG1_PLLSEL_GMAC; - break; - default: - return; - } - writel(pllsel, S6_REG_GREG1 + S6_GREG1_PLLSEL); -} - -static inline void s6gmac_linkisup(struct net_device *dev, int isup) -{ - struct s6gmac *pd = netdev_priv(dev); - struct phy_device *phydev = pd->phydev; - - pd->link.full = phydev->duplex; - pd->link.giga = (phydev->speed == 1000); - if (pd->link.mbit != phydev->speed) { - pd->link.mbit = phydev->speed; - s6gmac_set_rgmii_txclock(pd); - } - pd->link.isup = isup; - if (isup) - netif_carrier_on(dev); - phy_print_status(phydev); -} - -static void s6gmac_adjust_link(struct net_device *dev) -{ - struct s6gmac *pd = netdev_priv(dev); - struct phy_device *phydev = pd->phydev; - if (pd->link.isup && - (!phydev->link || - (pd->link.mbit != phydev->speed) || - (pd->link.full != phydev->duplex))) { - pd->link.isup = 0; - netif_tx_disable(dev); - if (!phydev->link) { - netif_carrier_off(dev); - phy_print_status(phydev); - } - } - if (!pd->link.isup && phydev->link) { - if (pd->link.full != phydev->duplex) { - u32 maccfg = readl(pd->reg + S6_GMAC_MACCONF2); - if (phydev->duplex) - maccfg |= 1 << S6_GMAC_MACCONF2_FULL; - else - maccfg &= ~(1 << S6_GMAC_MACCONF2_FULL); - writel(maccfg, pd->reg + S6_GMAC_MACCONF2); - } - - if (pd->link.giga != (phydev->speed == 1000)) { - u32 fifocfg = readl(pd->reg + S6_GMAC_FIFOCONF5); - u32 maccfg = readl(pd->reg + S6_GMAC_MACCONF2); - maccfg &= ~(S6_GMAC_MACCONF2_IFMODE_MASK - << S6_GMAC_MACCONF2_IFMODE); - if (phydev->speed == 1000) { - fifocfg |= 1 << S6_GMAC_FIFOCONF5_CFGBYTM; - maccfg |= S6_GMAC_MACCONF2_IFMODE_BYTE - << S6_GMAC_MACCONF2_IFMODE; - } else { - fifocfg &= ~(1 << S6_GMAC_FIFOCONF5_CFGBYTM); - maccfg |= S6_GMAC_MACCONF2_IFMODE_NIBBLE - << S6_GMAC_MACCONF2_IFMODE; - } - writel(fifocfg, pd->reg + S6_GMAC_FIFOCONF5); - writel(maccfg, pd->reg + S6_GMAC_MACCONF2); - } - - if (!s6dmac_fifo_full(pd->tx_dma, pd->tx_chan)) - netif_wake_queue(dev); - s6gmac_linkisup(dev, 1); - } -} - -static inline int s6gmac_phy_start(struct net_device *dev) -{ - struct s6gmac *pd = netdev_priv(dev); - int i = 0; - struct phy_device *p = NULL; - while ((i < PHY_MAX_ADDR) && (!(p = pd->mii.bus->phy_map[i]))) - i++; - p = phy_connect(dev, dev_name(&p->dev), &s6gmac_adjust_link, - PHY_INTERFACE_MODE_RGMII); - if (IS_ERR(p)) { - printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name); - return PTR_ERR(p); - } - p->supported &= PHY_GBIT_FEATURES; - p->advertising = p->supported; - pd->phydev = p; - return 0; -} - -static inline void s6gmac_init_stats(struct net_device *dev) -{ - struct s6gmac *pd = netdev_priv(dev); - u32 mask; - mask = 1 << S6_GMAC_STATCARRY1_RDRP | - 1 << S6_GMAC_STATCARRY1_RJBR | - 1 << S6_GMAC_STATCARRY1_RFRG | - 1 << S6_GMAC_STATCARRY1_ROVR | - 1 << S6_GMAC_STATCARRY1_RUND | - 1 << S6_GMAC_STATCARRY1_RCDE | - 1 << S6_GMAC_STATCARRY1_RFLR | - 1 << S6_GMAC_STATCARRY1_RALN | - 1 << S6_GMAC_STATCARRY1_RMCA | - 1 << S6_GMAC_STATCARRY1_RFCS | - 1 << S6_GMAC_STATCARRY1_RPKT | - 1 << S6_GMAC_STATCARRY1_RBYT; - writel(mask, pd->reg + S6_GMAC_STATCARRY(0)); - writel(~mask, pd->reg + S6_GMAC_STATCARRYMSK(0)); - mask = 1 << S6_GMAC_STATCARRY2_TDRP | - 1 << S6_GMAC_STATCARRY2_TNCL | - 1 << S6_GMAC_STATCARRY2_TXCL | - 1 << S6_GMAC_STATCARRY2_TEDF | - 1 << S6_GMAC_STATCARRY2_TPKT | - 1 << S6_GMAC_STATCARRY2_TBYT | - 1 << S6_GMAC_STATCARRY2_TFRG | - 1 << S6_GMAC_STATCARRY2_TUND | - 1 << S6_GMAC_STATCARRY2_TOVR | - 1 << S6_GMAC_STATCARRY2_TFCS | - 1 << S6_GMAC_STATCARRY2_TJBR; - writel(mask, pd->reg + S6_GMAC_STATCARRY(1)); - writel(~mask, pd->reg + S6_GMAC_STATCARRYMSK(1)); -} - -static inline void s6gmac_init_dmac(struct net_device *dev) -{ - struct s6gmac *pd = netdev_priv(dev); - s6dmac_disable_chan(pd->tx_dma, pd->tx_chan); - s6dmac_disable_chan(pd->rx_dma, pd->rx_chan); - s6dmac_disable_error_irqs(pd->tx_dma, 1 << S6_HIFDMA_GMACTX); - s6dmac_disable_error_irqs(pd->rx_dma, 1 << S6_HIFDMA_GMACRX); -} - -static int s6gmac_tx(struct sk_buff *skb, struct net_device *dev) -{ - struct s6gmac *pd = netdev_priv(dev); - unsigned long flags; - - spin_lock_irqsave(&pd->lock, flags); - writel(skb->len << S6_GMAC_BURST_PREWR_LEN | - 0 << S6_GMAC_BURST_PREWR_CFE | - 1 << S6_GMAC_BURST_PREWR_PPE | - 1 << S6_GMAC_BURST_PREWR_FCS | - ((skb->len < ETH_ZLEN) ? 1 : 0) << S6_GMAC_BURST_PREWR_PAD, - pd->reg + S6_GMAC_BURST_PREWR); - s6dmac_put_fifo_cache(pd->tx_dma, pd->tx_chan, - (u32)skb->data, pd->io, skb->len); - if (s6dmac_fifo_full(pd->tx_dma, pd->tx_chan)) - netif_stop_queue(dev); - if (((u8)(pd->tx_skb_i - pd->tx_skb_o)) >= S6_NUM_TX_SKB) { - printk(KERN_ERR "GMAC BUG: skb tx ring overflow [%x, %x]\n", - pd->tx_skb_o, pd->tx_skb_i); - BUG(); - } - pd->tx_skb[(pd->tx_skb_i++) % S6_NUM_TX_SKB] = skb; - spin_unlock_irqrestore(&pd->lock, flags); - return 0; -} - -static void s6gmac_tx_timeout(struct net_device *dev) -{ - struct s6gmac *pd = netdev_priv(dev); - unsigned long flags; - spin_lock_irqsave(&pd->lock, flags); - s6gmac_tx_interrupt(dev); - spin_unlock_irqrestore(&pd->lock, flags); -} - -static int s6gmac_open(struct net_device *dev) -{ - struct s6gmac *pd = netdev_priv(dev); - unsigned long flags; - phy_read_status(pd->phydev); - spin_lock_irqsave(&pd->lock, flags); - pd->link.mbit = 0; - s6gmac_linkisup(dev, pd->phydev->link); - s6gmac_init_device(dev); - s6gmac_init_stats(dev); - s6gmac_init_dmac(dev); - s6gmac_rx_fillfifo(dev); - s6dmac_enable_chan(pd->rx_dma, pd->rx_chan, - 2, 1, 0, 1, 0, 0, 0, 7, -1, 2, 0, 1); - s6dmac_enable_chan(pd->tx_dma, pd->tx_chan, - 2, 0, 1, 0, 0, 0, 0, 7, -1, 2, 0, 1); - writel(0 << S6_GMAC_HOST_INT_TXBURSTOVER | - 0 << S6_GMAC_HOST_INT_TXPREWOVER | - 0 << S6_GMAC_HOST_INT_RXBURSTUNDER | - 0 << S6_GMAC_HOST_INT_RXPOSTRFULL | - 0 << S6_GMAC_HOST_INT_RXPOSTRUNDER, - pd->reg + S6_GMAC_HOST_INTMASK); - spin_unlock_irqrestore(&pd->lock, flags); - phy_start(pd->phydev); - netif_start_queue(dev); - return 0; -} - -static int s6gmac_stop(struct net_device *dev) -{ - struct s6gmac *pd = netdev_priv(dev); - unsigned long flags; - netif_stop_queue(dev); - phy_stop(pd->phydev); - spin_lock_irqsave(&pd->lock, flags); - s6gmac_init_dmac(dev); - s6gmac_stop_device(dev); - while (pd->tx_skb_i != pd->tx_skb_o) - dev_kfree_skb(pd->tx_skb[(pd->tx_skb_o++) % S6_NUM_TX_SKB]); - while (pd->rx_skb_i != pd->rx_skb_o) - dev_kfree_skb(pd->rx_skb[(pd->rx_skb_o++) % S6_NUM_RX_SKB]); - spin_unlock_irqrestore(&pd->lock, flags); - return 0; -} - -static struct net_device_stats *s6gmac_stats(struct net_device *dev) -{ - struct s6gmac *pd = netdev_priv(dev); - struct net_device_stats *st = (struct net_device_stats *)&pd->stats; - int i; - do { - unsigned long flags; - spin_lock_irqsave(&pd->lock, flags); - for (i = 0; i < ARRAY_SIZE(pd->stats); i++) - pd->stats[i] = - pd->carry[i] << (S6_GMAC_STAT_SIZE_MIN - 1); - s6gmac_stats_collect(pd, &statinf[0][0]); - s6gmac_stats_collect(pd, &statinf[1][0]); - i = s6gmac_stats_pending(pd, 0) | - s6gmac_stats_pending(pd, 1); - spin_unlock_irqrestore(&pd->lock, flags); - } while (i); - st->rx_errors = st->rx_crc_errors + - st->rx_frame_errors + - st->rx_length_errors + - st->rx_missed_errors; - st->tx_errors += st->tx_aborted_errors; - return st; -} - -static int s6gmac_probe(struct platform_device *pdev) -{ - struct net_device *dev; - struct s6gmac *pd; - int res; - unsigned long i; - struct mii_bus *mb; - - dev = alloc_etherdev(sizeof(*pd)); - if (!dev) - return -ENOMEM; - - dev->open = s6gmac_open; - dev->stop = s6gmac_stop; - dev->hard_start_xmit = s6gmac_tx; - dev->tx_timeout = s6gmac_tx_timeout; - dev->watchdog_timeo = HZ; - dev->get_stats = s6gmac_stats; - dev->irq = platform_get_irq(pdev, 0); - pd = netdev_priv(dev); - memset(pd, 0, sizeof(*pd)); - spin_lock_init(&pd->lock); - pd->reg = platform_get_resource(pdev, IORESOURCE_MEM, 0)->start; - i = platform_get_resource(pdev, IORESOURCE_DMA, 0)->start; - pd->tx_dma = DMA_MASK_DMAC(i); - pd->tx_chan = DMA_INDEX_CHNL(i); - i = platform_get_resource(pdev, IORESOURCE_DMA, 1)->start; - pd->rx_dma = DMA_MASK_DMAC(i); - pd->rx_chan = DMA_INDEX_CHNL(i); - pd->io = platform_get_resource(pdev, IORESOURCE_IO, 0)->start; - res = request_irq(dev->irq, s6gmac_interrupt, 0, dev->name, dev); - if (res) { - printk(KERN_ERR DRV_PRMT "irq request failed: %d\n", dev->irq); - goto errirq; - } - res = register_netdev(dev); - if (res) { - printk(KERN_ERR DRV_PRMT "error registering device %s\n", - dev->name); - goto errdev; - } - mb = mdiobus_alloc(); - if (!mb) { - printk(KERN_ERR DRV_PRMT "error allocating mii bus\n"); - res = -ENOMEM; - goto errmii; - } - mb->name = "s6gmac_mii"; - mb->read = s6mii_read; - mb->write = s6mii_write; - mb->reset = s6mii_reset; - mb->priv = pd; - snprintf(mb->id, MII_BUS_ID_SIZE, "%s-%x", pdev->name, pdev->id); - mb->phy_mask = ~(1 << 0); - mb->irq = &pd->mii.irq[0]; - for (i = 0; i < PHY_MAX_ADDR; i++) { - int n = platform_get_irq(pdev, i + 1); - if (n < 0) - n = PHY_POLL; - pd->mii.irq[i] = n; - } - mdiobus_register(mb); - pd->mii.bus = mb; - res = s6gmac_phy_start(dev); - if (res) - return res; - platform_set_drvdata(pdev, dev); - return 0; -errmii: - unregister_netdev(dev); -errdev: - free_irq(dev->irq, dev); -errirq: - free_netdev(dev); - return res; -} - -static int s6gmac_remove(struct platform_device *pdev) -{ - struct net_device *dev = platform_get_drvdata(pdev); - if (dev) { - struct s6gmac *pd = netdev_priv(dev); - mdiobus_unregister(pd->mii.bus); - unregister_netdev(dev); - free_irq(dev->irq, dev); - free_netdev(dev); - } - return 0; -} - -static struct platform_driver s6gmac_driver = { - .probe = s6gmac_probe, - .remove = s6gmac_remove, - .driver = { - .name = "s6gmac", - }, -}; - -module_platform_driver(s6gmac_driver); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("S6105 on chip Ethernet driver"); -MODULE_AUTHOR("Oskar Schirmer "); -- cgit v0.10.2 From f620e4fe161f4d488414d7893a690e76b53f0c5e Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 21 Dec 2014 22:14:31 +0100 Subject: net: ethernet: stmicro: stmmac: drop owner assignment from platform_drivers This platform_driver does not need to set an owner, it will be populated by the driver core. Signed-off-by: Wolfram Sang Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 4032b17..3039de2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -430,7 +430,6 @@ static struct platform_driver stmmac_pltfr_driver = { .remove = stmmac_pltfr_remove, .driver = { .name = STMMAC_RESOURCE_NAME, - .owner = THIS_MODULE, .pm = &stmmac_pltfr_pm_ops, .of_match_table = of_match_ptr(stmmac_dt_ids), }, -- cgit v0.10.2 From 6402a577f4688053e98c1ffda1f4a6a59684ba81 Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Mon, 22 Dec 2014 08:28:52 +0800 Subject: 8139too: Fix the lack of pci_disable_device For linux-3.18.0 When pci_request_regions is failed in rtl8139_init_board, pci_disable_device is not called to disable the device which are enabled by pci_enable_device, because of disable_dev_on_err is not assigned 1. This patch fix this problem. Signed-off-by: Jia-Ju Bai Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/realtek/8139too.c b/drivers/net/ethernet/realtek/8139too.c index 6d0b9df..d4b0a29 100644 --- a/drivers/net/ethernet/realtek/8139too.c +++ b/drivers/net/ethernet/realtek/8139too.c @@ -787,10 +787,10 @@ static struct net_device *rtl8139_init_board(struct pci_dev *pdev) if (rc) goto err_out; + disable_dev_on_err = 1; rc = pci_request_regions (pdev, DRV_NAME); if (rc) goto err_out; - disable_dev_on_err = 1; pci_set_master (pdev); -- cgit v0.10.2 From ea3c9e13afe739f1ff31130cea57084cfe8c5dcd Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Mon, 22 Dec 2014 08:43:14 +0800 Subject: 8139too: Add netif_napi_del in the driver For linux-3.18.0 The driver lacks netif_napi_del in the normal path and error path to match the call of netif_napi_add in rtl8139_init_one. This patch fixes this problem. Signed-off-by: Jia-Ju Bai Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/realtek/8139too.c b/drivers/net/ethernet/realtek/8139too.c index d4b0a29..78bb4ce 100644 --- a/drivers/net/ethernet/realtek/8139too.c +++ b/drivers/net/ethernet/realtek/8139too.c @@ -1110,6 +1110,7 @@ static int rtl8139_init_one(struct pci_dev *pdev, return 0; err_out: + netif_napi_del(&tp->napi); __rtl8139_cleanup_dev (dev); pci_disable_device (pdev); return i; @@ -1124,6 +1125,7 @@ static void rtl8139_remove_one(struct pci_dev *pdev) assert (dev != NULL); cancel_delayed_work_sync(&tp->thread); + netif_napi_del(&tp->napi); unregister_netdev (dev); -- cgit v0.10.2 From 492f5add4be84652bbe13da8a250d60c6856a5c5 Mon Sep 17 00:00:00 2001 From: Amir Vadai Date: Mon, 22 Dec 2014 10:21:57 +0200 Subject: net/mlx4_en: Doorbell is byteswapped in Little Endian archs iowrite32() will byteswap it's argument on big endian archs. iowrite32be() will byteswap on little endian archs. Since we don't want to do this unnecessary byteswap on the fast path, doorbell is stored in the NIC's native endianness. Using the right iowrite() according to the arch endianness. CC: Wei Yang CC: David Laight Fixes: 6a4e812 ("net/mlx4_en: Avoid calling bswap in tx fast path") Signed-off-by: Amir Vadai Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c index a308d41..e3357bf 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c @@ -962,7 +962,17 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) tx_desc->ctrl.owner_opcode = op_own; if (send_doorbell) { wmb(); - iowrite32(ring->doorbell_qpn, + /* Since there is no iowrite*_native() that writes the + * value as is, without byteswapping - using the one + * the doesn't do byteswapping in the relevant arch + * endianness. + */ +#if defined(__LITTLE_ENDIAN) + iowrite32( +#else + iowrite32be( +#endif + ring->doorbell_qpn, ring->bf.uar->map + MLX4_SEND_DOORBELL); } else { ring->xmit_more++; -- cgit v0.10.2 From 3079c652141f9d6377417a7e8fd650c9948df65e Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Mon, 22 Dec 2014 20:35:25 +1100 Subject: caif: Fix napi poll list corruption The commit d75b1ade567ffab085e8adbbdacf0092d10cd09c (net: less interrupt masking in NAPI) breaks caif. It is now required that if the entire budget is consumed when poll returns, the napi poll_list must remain empty. However, like some other drivers caif tries to do a last-ditch check and if there is more work it will call napi_schedule and then immediately process some of this new work. Should the entire budget be consumed while processing such new work then we will violate the new caller contract. This patch fixes this by not touching any work when we reschedule in caif. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller diff --git a/drivers/net/caif/caif_virtio.c b/drivers/net/caif/caif_virtio.c index a5fefb9..b306210 100644 --- a/drivers/net/caif/caif_virtio.c +++ b/drivers/net/caif/caif_virtio.c @@ -257,7 +257,6 @@ static int cfv_rx_poll(struct napi_struct *napi, int quota) struct vringh_kiov *riov = &cfv->ctx.riov; unsigned int skb_len; -again: do { skb = NULL; @@ -322,7 +321,6 @@ exit: napi_schedule_prep(napi)) { vringh_notify_disable_kern(cfv->vr_rx); __napi_schedule(napi); - goto again; } break; -- cgit v0.10.2 From 5ad24def21b205a8e91925cd276b0a794b5ace82 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Mon, 22 Dec 2014 15:14:37 +0530 Subject: cxgb4vf: Fix ethtool get_settings for VF driver Decode and display Port Type and Module Type for ethtool get_settings() call Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/adapter.h b/drivers/net/ethernet/chelsio/cxgb4vf/adapter.h index d00a751..6049f70 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/adapter.h +++ b/drivers/net/ethernet/chelsio/cxgb4vf/adapter.h @@ -96,6 +96,9 @@ struct port_info { s16 xact_addr_filt; /* index of our MAC address filter */ u16 rss_size; /* size of VI's RSS table slice */ u8 pidx; /* index into adapter port[] */ + s8 mdio_addr; + u8 port_type; /* firmware port type */ + u8 mod_type; /* firmware module type */ u8 port_id; /* physical port ID */ u8 nqsets; /* # of "Queue Sets" */ u8 first_qset; /* index of first "Queue Set" */ @@ -522,6 +525,7 @@ static inline struct adapter *netdev2adap(const struct net_device *dev) * is "contracted" to provide for the common code. */ void t4vf_os_link_changed(struct adapter *, int, int); +void t4vf_os_portmod_changed(struct adapter *, int); /* * SGE function prototype declarations. diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c index aa74ec3..2215d43 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c @@ -44,6 +44,7 @@ #include #include #include +#include #include "t4vf_common.h" #include "t4vf_defs.h" @@ -210,6 +211,38 @@ void t4vf_os_link_changed(struct adapter *adapter, int pidx, int link_ok) } /* + * THe port module type has changed on the indicated "port" (Virtual + * Interface). + */ +void t4vf_os_portmod_changed(struct adapter *adapter, int pidx) +{ + static const char * const mod_str[] = { + NULL, "LR", "SR", "ER", "passive DA", "active DA", "LRM" + }; + const struct net_device *dev = adapter->port[pidx]; + const struct port_info *pi = netdev_priv(dev); + + if (pi->mod_type == FW_PORT_MOD_TYPE_NONE) + dev_info(adapter->pdev_dev, "%s: port module unplugged\n", + dev->name); + else if (pi->mod_type < ARRAY_SIZE(mod_str)) + dev_info(adapter->pdev_dev, "%s: %s port module inserted\n", + dev->name, mod_str[pi->mod_type]); + else if (pi->mod_type == FW_PORT_MOD_TYPE_NOTSUPPORTED) + dev_info(adapter->pdev_dev, "%s: unsupported optical port " + "module inserted\n", dev->name); + else if (pi->mod_type == FW_PORT_MOD_TYPE_UNKNOWN) + dev_info(adapter->pdev_dev, "%s: unknown port module inserted," + "forcing TWINAX\n", dev->name); + else if (pi->mod_type == FW_PORT_MOD_TYPE_ERROR) + dev_info(adapter->pdev_dev, "%s: transceiver module error\n", + dev->name); + else + dev_info(adapter->pdev_dev, "%s: unknown module type %d " + "inserted\n", dev->name, pi->mod_type); +} + +/* * Net device operations. * ====================== */ @@ -1193,24 +1226,103 @@ static void cxgb4vf_poll_controller(struct net_device *dev) * state of the port to which we're linked. */ -/* - * Return current port link settings. - */ -static int cxgb4vf_get_settings(struct net_device *dev, - struct ethtool_cmd *cmd) -{ - const struct port_info *pi = netdev_priv(dev); +static unsigned int t4vf_from_fw_linkcaps(enum fw_port_type type, + unsigned int caps) +{ + unsigned int v = 0; + + if (type == FW_PORT_TYPE_BT_SGMII || type == FW_PORT_TYPE_BT_XFI || + type == FW_PORT_TYPE_BT_XAUI) { + v |= SUPPORTED_TP; + if (caps & FW_PORT_CAP_SPEED_100M) + v |= SUPPORTED_100baseT_Full; + if (caps & FW_PORT_CAP_SPEED_1G) + v |= SUPPORTED_1000baseT_Full; + if (caps & FW_PORT_CAP_SPEED_10G) + v |= SUPPORTED_10000baseT_Full; + } else if (type == FW_PORT_TYPE_KX4 || type == FW_PORT_TYPE_KX) { + v |= SUPPORTED_Backplane; + if (caps & FW_PORT_CAP_SPEED_1G) + v |= SUPPORTED_1000baseKX_Full; + if (caps & FW_PORT_CAP_SPEED_10G) + v |= SUPPORTED_10000baseKX4_Full; + } else if (type == FW_PORT_TYPE_KR) + v |= SUPPORTED_Backplane | SUPPORTED_10000baseKR_Full; + else if (type == FW_PORT_TYPE_BP_AP) + v |= SUPPORTED_Backplane | SUPPORTED_10000baseR_FEC | + SUPPORTED_10000baseKR_Full | SUPPORTED_1000baseKX_Full; + else if (type == FW_PORT_TYPE_BP4_AP) + v |= SUPPORTED_Backplane | SUPPORTED_10000baseR_FEC | + SUPPORTED_10000baseKR_Full | SUPPORTED_1000baseKX_Full | + SUPPORTED_10000baseKX4_Full; + else if (type == FW_PORT_TYPE_FIBER_XFI || + type == FW_PORT_TYPE_FIBER_XAUI || + type == FW_PORT_TYPE_SFP || + type == FW_PORT_TYPE_QSFP_10G || + type == FW_PORT_TYPE_QSA) { + v |= SUPPORTED_FIBRE; + if (caps & FW_PORT_CAP_SPEED_1G) + v |= SUPPORTED_1000baseT_Full; + if (caps & FW_PORT_CAP_SPEED_10G) + v |= SUPPORTED_10000baseT_Full; + } else if (type == FW_PORT_TYPE_BP40_BA || + type == FW_PORT_TYPE_QSFP) { + v |= SUPPORTED_40000baseSR4_Full; + v |= SUPPORTED_FIBRE; + } + + if (caps & FW_PORT_CAP_ANEG) + v |= SUPPORTED_Autoneg; + return v; +} + +static int cxgb4vf_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + const struct port_info *p = netdev_priv(dev); + + if (p->port_type == FW_PORT_TYPE_BT_SGMII || + p->port_type == FW_PORT_TYPE_BT_XFI || + p->port_type == FW_PORT_TYPE_BT_XAUI) + cmd->port = PORT_TP; + else if (p->port_type == FW_PORT_TYPE_FIBER_XFI || + p->port_type == FW_PORT_TYPE_FIBER_XAUI) + cmd->port = PORT_FIBRE; + else if (p->port_type == FW_PORT_TYPE_SFP || + p->port_type == FW_PORT_TYPE_QSFP_10G || + p->port_type == FW_PORT_TYPE_QSA || + p->port_type == FW_PORT_TYPE_QSFP) { + if (p->mod_type == FW_PORT_MOD_TYPE_LR || + p->mod_type == FW_PORT_MOD_TYPE_SR || + p->mod_type == FW_PORT_MOD_TYPE_ER || + p->mod_type == FW_PORT_MOD_TYPE_LRM) + cmd->port = PORT_FIBRE; + else if (p->mod_type == FW_PORT_MOD_TYPE_TWINAX_PASSIVE || + p->mod_type == FW_PORT_MOD_TYPE_TWINAX_ACTIVE) + cmd->port = PORT_DA; + else + cmd->port = PORT_OTHER; + } else + cmd->port = PORT_OTHER; - cmd->supported = pi->link_cfg.supported; - cmd->advertising = pi->link_cfg.advertising; + if (p->mdio_addr >= 0) { + cmd->phy_address = p->mdio_addr; + cmd->transceiver = XCVR_EXTERNAL; + cmd->mdio_support = p->port_type == FW_PORT_TYPE_BT_SGMII ? + MDIO_SUPPORTS_C22 : MDIO_SUPPORTS_C45; + } else { + cmd->phy_address = 0; /* not really, but no better option */ + cmd->transceiver = XCVR_INTERNAL; + cmd->mdio_support = 0; + } + + cmd->supported = t4vf_from_fw_linkcaps(p->port_type, + p->link_cfg.supported); + cmd->advertising = t4vf_from_fw_linkcaps(p->port_type, + p->link_cfg.advertising); ethtool_cmd_speed_set(cmd, - netif_carrier_ok(dev) ? pi->link_cfg.speed : -1); + netif_carrier_ok(dev) ? p->link_cfg.speed : 0); cmd->duplex = DUPLEX_FULL; - - cmd->port = (cmd->supported & SUPPORTED_TP) ? PORT_TP : PORT_FIBRE; - cmd->phy_address = pi->port_id; - cmd->transceiver = XCVR_EXTERNAL; - cmd->autoneg = pi->link_cfg.autoneg; + cmd->autoneg = p->link_cfg.autoneg; cmd->maxtxpkt = 0; cmd->maxrxpkt = 0; return 0; diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_common.h b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_common.h index 8d3237f..b9debb4 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_common.h +++ b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_common.h @@ -230,7 +230,7 @@ struct adapter_params { static inline bool is_10g_port(const struct link_config *lc) { - return (lc->supported & SUPPORTED_10000baseT_Full) != 0; + return (lc->supported & FW_PORT_CAP_SPEED_10G) != 0; } static inline bool is_x_10g_port(const struct link_config *lc) diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c index 02e8833..21dc9a2 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c @@ -245,6 +245,10 @@ static int hash_mac_addr(const u8 *addr) return a & 0x3f; } +#define ADVERT_MASK (FW_PORT_CAP_SPEED_100M | FW_PORT_CAP_SPEED_1G |\ + FW_PORT_CAP_SPEED_10G | FW_PORT_CAP_SPEED_40G | \ + FW_PORT_CAP_SPEED_100G | FW_PORT_CAP_ANEG) + /** * init_link_config - initialize a link's SW state * @lc: structure holding the link state @@ -259,8 +263,8 @@ static void init_link_config(struct link_config *lc, unsigned int caps) lc->requested_speed = 0; lc->speed = 0; lc->requested_fc = lc->fc = PAUSE_RX | PAUSE_TX; - if (lc->supported & SUPPORTED_Autoneg) { - lc->advertising = lc->supported; + if (lc->supported & FW_PORT_CAP_ANEG) { + lc->advertising = lc->supported & ADVERT_MASK; lc->autoneg = AUTONEG_ENABLE; lc->requested_fc |= PAUSE_AUTONEG; } else { @@ -280,7 +284,6 @@ int t4vf_port_init(struct adapter *adapter, int pidx) struct fw_vi_cmd vi_cmd, vi_rpl; struct fw_port_cmd port_cmd, port_rpl; int v; - u32 word; /* * Execute a VI Read command to get our Virtual Interface information @@ -319,19 +322,11 @@ int t4vf_port_init(struct adapter *adapter, int pidx) if (v) return v; - v = 0; - word = be16_to_cpu(port_rpl.u.info.pcap); - if (word & FW_PORT_CAP_SPEED_100M) - v |= SUPPORTED_100baseT_Full; - if (word & FW_PORT_CAP_SPEED_1G) - v |= SUPPORTED_1000baseT_Full; - if (word & FW_PORT_CAP_SPEED_10G) - v |= SUPPORTED_10000baseT_Full; - if (word & FW_PORT_CAP_SPEED_40G) - v |= SUPPORTED_40000baseSR4_Full; - if (word & FW_PORT_CAP_ANEG) - v |= SUPPORTED_Autoneg; - init_link_config(&pi->link_cfg, v); + v = be32_to_cpu(port_rpl.u.info.lstatus_to_modtype); + pi->port_type = FW_PORT_CMD_PTYPE_G(v); + pi->mod_type = FW_PORT_MOD_TYPE_NA; + + init_link_config(&pi->link_cfg, be16_to_cpu(port_rpl.u.info.pcap)); return 0; } @@ -1491,7 +1486,7 @@ int t4vf_handle_fw_rpl(struct adapter *adapter, const __be64 *rpl) */ const struct fw_port_cmd *port_cmd = (const struct fw_port_cmd *)rpl; - u32 word; + u32 stat, mod; int action, port_id, link_ok, speed, fc, pidx; /* @@ -1509,21 +1504,21 @@ int t4vf_handle_fw_rpl(struct adapter *adapter, const __be64 *rpl) port_id = FW_PORT_CMD_PORTID_G( be32_to_cpu(port_cmd->op_to_portid)); - word = be32_to_cpu(port_cmd->u.info.lstatus_to_modtype); - link_ok = (word & FW_PORT_CMD_LSTATUS_F) != 0; + stat = be32_to_cpu(port_cmd->u.info.lstatus_to_modtype); + link_ok = (stat & FW_PORT_CMD_LSTATUS_F) != 0; speed = 0; fc = 0; - if (word & FW_PORT_CMD_RXPAUSE_F) + if (stat & FW_PORT_CMD_RXPAUSE_F) fc |= PAUSE_RX; - if (word & FW_PORT_CMD_TXPAUSE_F) + if (stat & FW_PORT_CMD_TXPAUSE_F) fc |= PAUSE_TX; - if (word & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100M)) + if (stat & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100M)) speed = 100; - else if (word & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_1G)) + else if (stat & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_1G)) speed = 1000; - else if (word & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_10G)) + else if (stat & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_10G)) speed = 10000; - else if (word & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_40G)) + else if (stat & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_40G)) speed = 40000; /* @@ -1540,12 +1535,21 @@ int t4vf_handle_fw_rpl(struct adapter *adapter, const __be64 *rpl) continue; lc = &pi->link_cfg; + + mod = FW_PORT_CMD_MODTYPE_G(stat); + if (mod != pi->mod_type) { + pi->mod_type = mod; + t4vf_os_portmod_changed(adapter, pidx); + } + if (link_ok != lc->link_ok || speed != lc->speed || fc != lc->fc) { /* something changed */ lc->link_ok = link_ok; lc->speed = speed; lc->fc = fc; + lc->supported = + be16_to_cpu(port_cmd->u.info.pcap); t4vf_os_link_changed(adapter, pidx, link_ok); } } -- cgit v0.10.2 From 2dc49d1680b534877fd20cce52557ea542bb06b6 Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Mon, 22 Dec 2014 18:22:48 +0100 Subject: tcp6: don't move IP6CB before xfrm6_policy_check() When xfrm6_policy_check() is used, _decode_session6() is called after some intermediate functions. This function uses IP6CB(), thus TCP_SKB_CB() must be prepared after the call of xfrm6_policy_check(). Before this patch, scenarii with IPv6 + TCP + IPsec Transport are broken. Fixes: 971f10eca186 ("tcp: better TCP_SKB_CB layout to reduce cache line misses") Reported-by: Huaibin Wang Suggested-by: Eric Dumazet Signed-off-by: Nicolas Dichtel Signed-off-by: David S. Miller diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 5ff8780..9c0b54e 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1387,6 +1387,28 @@ ipv6_pktoptions: return 0; } +static void tcp_v6_fill_cb(struct sk_buff *skb, const struct ipv6hdr *hdr, + const struct tcphdr *th) +{ + /* This is tricky: we move IP6CB at its correct location into + * TCP_SKB_CB(). It must be done after xfrm6_policy_check(), because + * _decode_session6() uses IP6CB(). + * barrier() makes sure compiler won't play aliasing games. + */ + memmove(&TCP_SKB_CB(skb)->header.h6, IP6CB(skb), + sizeof(struct inet6_skb_parm)); + barrier(); + + TCP_SKB_CB(skb)->seq = ntohl(th->seq); + TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin + + skb->len - th->doff*4); + TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq); + TCP_SKB_CB(skb)->tcp_flags = tcp_flag_byte(th); + TCP_SKB_CB(skb)->tcp_tw_isn = 0; + TCP_SKB_CB(skb)->ip_dsfield = ipv6_get_dsfield(hdr); + TCP_SKB_CB(skb)->sacked = 0; +} + static int tcp_v6_rcv(struct sk_buff *skb) { const struct tcphdr *th; @@ -1418,24 +1440,9 @@ static int tcp_v6_rcv(struct sk_buff *skb) th = tcp_hdr(skb); hdr = ipv6_hdr(skb); - /* This is tricky : We move IPCB at its correct location into TCP_SKB_CB() - * barrier() makes sure compiler wont play fool^Waliasing games. - */ - memmove(&TCP_SKB_CB(skb)->header.h6, IP6CB(skb), - sizeof(struct inet6_skb_parm)); - barrier(); - - TCP_SKB_CB(skb)->seq = ntohl(th->seq); - TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin + - skb->len - th->doff*4); - TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq); - TCP_SKB_CB(skb)->tcp_flags = tcp_flag_byte(th); - TCP_SKB_CB(skb)->tcp_tw_isn = 0; - TCP_SKB_CB(skb)->ip_dsfield = ipv6_get_dsfield(hdr); - TCP_SKB_CB(skb)->sacked = 0; sk = __inet6_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest, - tcp_v6_iif(skb)); + inet6_iif(skb)); if (!sk) goto no_tcp_socket; @@ -1451,6 +1458,8 @@ process: if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) goto discard_and_relse; + tcp_v6_fill_cb(skb, hdr, th); + #ifdef CONFIG_TCP_MD5SIG if (tcp_v6_inbound_md5_hash(sk, skb)) goto discard_and_relse; @@ -1482,6 +1491,8 @@ no_tcp_socket: if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) goto discard_it; + tcp_v6_fill_cb(skb, hdr, th); + if (skb->len < (th->doff<<2) || tcp_checksum_complete(skb)) { csum_error: TCP_INC_STATS_BH(net, TCP_MIB_CSUMERRORS); @@ -1505,6 +1516,8 @@ do_time_wait: goto discard_it; } + tcp_v6_fill_cb(skb, hdr, th); + if (skb->len < (th->doff<<2)) { inet_twsk_put(inet_twsk(sk)); goto bad_packet; -- cgit v0.10.2 From 7d0b93499f4879ddbc75d594f4ea216ba964f78e Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Mon, 22 Dec 2014 15:18:05 +0800 Subject: ACPI / video: Add some Samsung models to disable_native_backlight list Several Samsung laptop models (SAMSUNG 870Z5E/880Z5E/680Z5E and SAMSUNG 370R4E/370R4V/370R5E/3570RE/370R5V) do not have a working native backlight control interface so restore their acpi_videoX interface. Link: https://bugzilla.kernel.org/show_bug.cgi?id=84221 Link: https://bugzilla.kernel.org/show_bug.cgi?id=84651 For SAMSUNG 870Z5E/880Z5E/680Z5E: Reported-and-tested-by: Brent Saner Reported-by: Vitaliy Filippov Reported-by: Laszlo KREKACS For SAMSUNG 370R4E/370R4V/370R5E/3570RE/370R5V: Reported-by: Vladimir Perepechin Cc: 3.17+ # 3.17+ Signed-off-by: Aaron Lu Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 1eaadff..c72e79d2c5 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -505,6 +505,23 @@ static struct dmi_system_id video_dmi_table[] __initdata = { DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY 15 Notebook PC"), }, }, + + { + .callback = video_disable_native_backlight, + .ident = "SAMSUNG 870Z5E/880Z5E/680Z5E", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "870Z5E/880Z5E/680Z5E"), + }, + }, + { + .callback = video_disable_native_backlight, + .ident = "SAMSUNG 370R4E/370R4V/370R5E/3570RE/370R5V", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "370R4E/370R4V/370R5E/3570RE/370R5V"), + }, + }, {} }; -- cgit v0.10.2 From 2036eaa74031b11028ee8fc1f44f128fdc871dda Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Tue, 16 Dec 2014 16:33:09 +1000 Subject: nouveau: bring back legacy mmap handler nouveau userspace back at 1.0.1 used to call the X server DRIOpenDRMMaster interface even for DRI2 (doh!), this attempts to map the sarea and fails if it can't. Since 884c6dabb0eafe7227f099c9e78e514191efaf13 from Daniel, this fails, but only ancient drivers would see it. Revert the nouveau bits of that fix. Acked-by: Daniel Vetter Cc: # 3.18 Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/nouveau/nouveau_ttm.c b/drivers/gpu/drm/nouveau/nouveau_ttm.c index 753a6de..3d1cfcb 100644 --- a/drivers/gpu/drm/nouveau/nouveau_ttm.c +++ b/drivers/gpu/drm/nouveau/nouveau_ttm.c @@ -28,6 +28,7 @@ #include "nouveau_ttm.h" #include "nouveau_gem.h" +#include "drm_legacy.h" static int nouveau_vram_manager_init(struct ttm_mem_type_manager *man, unsigned long psize) { @@ -281,7 +282,7 @@ nouveau_ttm_mmap(struct file *filp, struct vm_area_struct *vma) struct nouveau_drm *drm = nouveau_drm(file_priv->minor->dev); if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET)) - return -EINVAL; + return drm_legacy_mmap(filp, vma); return ttm_bo_mmap(filp, vma, &drm->ttm.bdev); } -- cgit v0.10.2 From 48ec833b7851438f02164ea846852ce4696f09ad Mon Sep 17 00:00:00 2001 From: "Kirill A. Shutemov" Date: Mon, 22 Dec 2014 21:01:54 +0200 Subject: Revert "mm/memory.c: share the i_mmap_rwsem" This reverts commit c8475d144abb1e62958cc5ec281d2a9e161c1946. There are several[1][2] of bug reports which points to this commit as potential cause[3]. Let's revert it until we figure out what's going on. [1] https://lkml.org/lkml/2014/11/14/342 [2] https://lkml.org/lkml/2014/12/22/213 [3] https://lkml.org/lkml/2014/12/9/741 Signed-off-by: Kirill A. Shutemov Reported-by: Sasha Levin Acked-by: Davidlohr Bueso Cc: Hugh Dickins Cc: Oleg Nesterov Cc: Peter Zijlstra (Intel) Cc: Rik van Riel Cc: Srikar Dronamraju Cc: Mel Gorman Signed-off-by: Linus Torvalds diff --git a/mm/memory.c b/mm/memory.c index 649e7d44..ca920d1 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -2378,12 +2378,12 @@ void unmap_mapping_range(struct address_space *mapping, details.last_index = ULONG_MAX; - i_mmap_lock_read(mapping); + i_mmap_lock_write(mapping); if (unlikely(!RB_EMPTY_ROOT(&mapping->i_mmap))) unmap_mapping_range_tree(&mapping->i_mmap, &details); if (unlikely(!list_empty(&mapping->i_mmap_nonlinear))) unmap_mapping_range_list(&mapping->i_mmap_nonlinear, &details); - i_mmap_unlock_read(mapping); + i_mmap_unlock_write(mapping); } EXPORT_SYMBOL(unmap_mapping_range); -- cgit v0.10.2 From bd8136d397c72e16d61810bb71d92656acfc66e6 Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Fri, 19 Dec 2014 11:23:50 -0500 Subject: agp: Fix up email address & attributions in AGP MODULE_AUTHOR tags - Remove soon-to-be-dead @redhat address. - Jeff Hartmann wrote the bulk of the original backend code, and should at least get a mention in the MODULE_AUTHOR for backend.o - Various people at Intel have done a lot more work than myself on the intel-* drivers, so again, mention that. Signed-off-by: Dave Jones Signed-off-by: Dave Airlie diff --git a/drivers/char/agp/ali-agp.c b/drivers/char/agp/ali-agp.c index 19db036..dcbbb4e 100644 --- a/drivers/char/agp/ali-agp.c +++ b/drivers/char/agp/ali-agp.c @@ -417,6 +417,6 @@ static void __exit agp_ali_cleanup(void) module_init(agp_ali_init); module_exit(agp_ali_cleanup); -MODULE_AUTHOR("Dave Jones "); +MODULE_AUTHOR("Dave Jones"); MODULE_LICENSE("GPL and additional rights"); diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c index 3b47ed0..0ef3500 100644 --- a/drivers/char/agp/amd64-agp.c +++ b/drivers/char/agp/amd64-agp.c @@ -813,6 +813,6 @@ static void __exit agp_amd64_cleanup(void) module_init(agp_amd64_mod_init); module_exit(agp_amd64_cleanup); -MODULE_AUTHOR("Dave Jones , Andi Kleen"); +MODULE_AUTHOR("Dave Jones, Andi Kleen"); module_param(agp_try_unsupported, bool, 0); MODULE_LICENSE("GPL"); diff --git a/drivers/char/agp/ati-agp.c b/drivers/char/agp/ati-agp.c index 18a7a6b..75a9786 100644 --- a/drivers/char/agp/ati-agp.c +++ b/drivers/char/agp/ati-agp.c @@ -579,6 +579,6 @@ static void __exit agp_ati_cleanup(void) module_init(agp_ati_init); module_exit(agp_ati_cleanup); -MODULE_AUTHOR("Dave Jones "); +MODULE_AUTHOR("Dave Jones"); MODULE_LICENSE("GPL and additional rights"); diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c index 317c28c..38ffb28 100644 --- a/drivers/char/agp/backend.c +++ b/drivers/char/agp/backend.c @@ -356,7 +356,7 @@ static __init int agp_setup(char *s) __setup("agp=", agp_setup); #endif -MODULE_AUTHOR("Dave Jones "); +MODULE_AUTHOR("Dave Jones, Jeff Hartmann"); MODULE_DESCRIPTION("AGP GART driver"); MODULE_LICENSE("GPL and additional rights"); MODULE_ALIAS_MISCDEV(AGPGART_MINOR); diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index f9b9ca5..0a21dae 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -920,5 +920,5 @@ static void __exit agp_intel_cleanup(void) module_init(agp_intel_init); module_exit(agp_intel_cleanup); -MODULE_AUTHOR("Dave Jones "); +MODULE_AUTHOR("Dave Jones, Various @Intel"); MODULE_LICENSE("GPL and additional rights"); diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index f333482..92aa43f 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -1438,5 +1438,5 @@ void intel_gmch_remove(void) } EXPORT_SYMBOL(intel_gmch_remove); -MODULE_AUTHOR("Dave Jones "); +MODULE_AUTHOR("Dave Jones, Various @Intel"); MODULE_LICENSE("GPL and additional rights"); diff --git a/drivers/char/agp/nvidia-agp.c b/drivers/char/agp/nvidia-agp.c index a1861b7..6c8d39c 100644 --- a/drivers/char/agp/nvidia-agp.c +++ b/drivers/char/agp/nvidia-agp.c @@ -1,7 +1,7 @@ /* * Nvidia AGPGART routines. * Based upon a 2.4 agpgart diff by the folks from NVIDIA, and hacked up - * to work in 2.5 by Dave Jones + * to work in 2.5 by Dave Jones. */ #include diff --git a/drivers/char/agp/via-agp.c b/drivers/char/agp/via-agp.c index 228f20c..a4961d3 100644 --- a/drivers/char/agp/via-agp.c +++ b/drivers/char/agp/via-agp.c @@ -595,4 +595,4 @@ module_init(agp_via_init); module_exit(agp_via_cleanup); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Dave Jones "); +MODULE_AUTHOR("Dave Jones"); -- cgit v0.10.2 From f43c27188a49111b58e9611afa2f0365b0b55625 Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Fri, 19 Dec 2014 17:03:47 +0000 Subject: arm64: kernel: fix __cpu_suspend mm switch on warm-boot On arm64 the TTBR0_EL1 register is set to either the reserved TTBR0 page tables on boot or to the active_mm mappings belonging to user space processes, it must never be set to swapper_pg_dir page tables mappings. When a CPU is booted its active_mm is set to init_mm even though its TTBR0_EL1 points at the reserved TTBR0 page mappings. This implies that when __cpu_suspend is triggered the active_mm can point at init_mm even if the current TTBR0_EL1 register contains the reserved TTBR0_EL1 mappings. Therefore, the mm save and restore executed in __cpu_suspend might turn out to be erroneous in that, if the current->active_mm corresponds to init_mm, on resume from low power it ends up restoring in the TTBR0_EL1 the init_mm mappings that are global and can cause speculation of TLB entries which end up being propagated to user space. This patch fixes the issue by checking the active_mm pointer before restoring the TTBR0 mappings. If the current active_mm == &init_mm, the code sets the TTBR0_EL1 to the reserved TTBR0 mapping instead of switching back to the active_mm, which is the expected behaviour corresponding to the TTBR0_EL1 settings when __cpu_suspend was entered. Fixes: 95322526ef62 ("arm64: kernel: cpu_{suspend/resume} implementation") Cc: # 3.14+: 18ab7db Cc: # 3.14+: 714f599 Cc: # 3.14+: c3684fb Cc: # 3.14+ Cc: Will Deacon Signed-off-by: Lorenzo Pieralisi Signed-off-by: Catalin Marinas diff --git a/arch/arm64/kernel/suspend.c b/arch/arm64/kernel/suspend.c index 3771b72..2d6b606 100644 --- a/arch/arm64/kernel/suspend.c +++ b/arch/arm64/kernel/suspend.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -98,7 +99,18 @@ int __cpu_suspend(unsigned long arg, int (*fn)(unsigned long)) */ ret = __cpu_suspend_enter(arg, fn); if (ret == 0) { - cpu_switch_mm(mm->pgd, mm); + /* + * We are resuming from reset with TTBR0_EL1 set to the + * idmap to enable the MMU; restore the active_mm mappings in + * TTBR0_EL1 unless the active_mm == &init_mm, in which case + * the thread entered __cpu_suspend with TTBR0_EL1 set to + * reserved TTBR0 page tables and should be restored as such. + */ + if (mm == &init_mm) + cpu_set_reserved_ttbr0(); + else + cpu_switch_mm(mm->pgd, mm); + flush_tlb_all(); /* -- cgit v0.10.2 From f7bf130ecd01f6c9d95c02c7a83d5d9dc263b0c9 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Sun, 21 Dec 2014 11:13:12 +0000 Subject: arm64: defconfig: defconfig update for 3.19 The usual defconfig tweaks, this time: - FHANDLE and AUTOFS4_FS to keep systemd happy - PID_NS, QUOTA and KEYS to keep LTP happy - Disable DEBUG_PREEMPT, as this *really* hurts performance Signed-off-by: Will Deacon Signed-off-by: Catalin Marinas diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index dd301be..5376d90 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -1,6 +1,7 @@ # CONFIG_LOCALVERSION_AUTO is not set CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y +CONFIG_FHANDLE=y CONFIG_AUDIT=y CONFIG_NO_HZ_IDLE=y CONFIG_HIGH_RES_TIMERS=y @@ -13,14 +14,12 @@ CONFIG_TASK_IO_ACCOUNTING=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 -CONFIG_RESOURCE_COUNTERS=y CONFIG_MEMCG=y CONFIG_MEMCG_SWAP=y CONFIG_MEMCG_KMEM=y CONFIG_CGROUP_HUGETLB=y # CONFIG_UTS_NS is not set # CONFIG_IPC_NS is not set -# CONFIG_PID_NS is not set # CONFIG_NET_NS is not set CONFIG_SCHED_AUTOGROUP=y CONFIG_BLK_DEV_INITRD=y @@ -92,7 +91,6 @@ CONFIG_SERIAL_AMBA_PL011_CONSOLE=y CONFIG_SERIAL_OF_PLATFORM=y CONFIG_VIRTIO_CONSOLE=y # CONFIG_HW_RANDOM is not set -# CONFIG_HMC_DRV is not set CONFIG_SPI=y CONFIG_SPI_PL022=y CONFIG_GPIO_PL061=y @@ -133,6 +131,8 @@ CONFIG_EXT3_FS=y CONFIG_EXT4_FS=y CONFIG_FANOTIFY=y CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y +CONFIG_QUOTA=y +CONFIG_AUTOFS4_FS=y CONFIG_FUSE_FS=y CONFIG_CUSE=y CONFIG_VFAT_FS=y @@ -152,14 +152,15 @@ CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_KERNEL=y CONFIG_LOCKUP_DETECTOR=y # CONFIG_SCHED_DEBUG is not set +# CONFIG_DEBUG_PREEMPT is not set # CONFIG_FTRACE is not set +CONFIG_KEYS=y CONFIG_SECURITY=y CONFIG_CRYPTO_ANSI_CPRNG=y CONFIG_ARM64_CRYPTO=y CONFIG_CRYPTO_SHA1_ARM64_CE=y CONFIG_CRYPTO_SHA2_ARM64_CE=y CONFIG_CRYPTO_GHASH_ARM64_CE=y -CONFIG_CRYPTO_AES_ARM64_CE=y CONFIG_CRYPTO_AES_ARM64_CE_CCM=y CONFIG_CRYPTO_AES_ARM64_CE_BLK=y CONFIG_CRYPTO_AES_ARM64_NEON_BLK=y -- cgit v0.10.2 From 5d96e0cba26323c3daeb9f7cdfd4efe70985e2c6 Mon Sep 17 00:00:00 2001 From: Jungseok Lee Date: Sat, 20 Dec 2014 00:49:40 +0000 Subject: arm64: mm: Add pgd_page to support RCU fast_gup This patch adds pgd_page definition in order to keep supporting HAVE_GENERIC_RCU_GUP configuration. In addition, it changes pud_page expression to align with pmd_page for readability. An introduction of pgd_page resolves the following build breakage under 4KB + 4Level memory management combo. mm/gup.c: In function 'gup_huge_pgd': mm/gup.c:889:2: error: implicit declaration of function 'pgd_page' [-Werror=implicit-function-declaration] head = pgd_page(orig); ^ mm/gup.c:889:7: warning: assignment makes pointer from integer without a cast head = pgd_page(orig); Cc: Will Deacon Cc: Steve Capper Signed-off-by: Jungseok Lee [catalin.marinas@arm.com: remove duplicate pmd_page definition] Signed-off-by: Catalin Marinas diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h index df22314..210d632 100644 --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h @@ -298,7 +298,6 @@ void pmdp_splitting_flush(struct vm_area_struct *vma, unsigned long address, #define pfn_pmd(pfn,prot) (__pmd(((phys_addr_t)(pfn) << PAGE_SHIFT) | pgprot_val(prot))) #define mk_pmd(page,prot) pfn_pmd(page_to_pfn(page),prot) -#define pmd_page(pmd) pfn_to_page(__phys_to_pfn(pmd_val(pmd) & PHYS_MASK)) #define pud_write(pud) pte_write(pud_pte(pud)) #define pud_pfn(pud) (((pud_val(pud) & PUD_MASK) & PHYS_MASK) >> PAGE_SHIFT) @@ -401,7 +400,7 @@ static inline pmd_t *pmd_offset(pud_t *pud, unsigned long addr) return (pmd_t *)pud_page_vaddr(*pud) + pmd_index(addr); } -#define pud_page(pud) pmd_page(pud_pmd(pud)) +#define pud_page(pud) pfn_to_page(__phys_to_pfn(pud_val(pud) & PHYS_MASK)) #endif /* CONFIG_ARM64_PGTABLE_LEVELS > 2 */ @@ -437,6 +436,8 @@ static inline pud_t *pud_offset(pgd_t *pgd, unsigned long addr) return (pud_t *)pgd_page_vaddr(*pgd) + pud_index(addr); } +#define pgd_page(pgd) pfn_to_page(__phys_to_pfn(pgd_val(pgd) & PHYS_MASK)) + #endif /* CONFIG_ARM64_PGTABLE_LEVELS > 3 */ #define pgd_ERROR(pgd) __pgd_error(__FILE__, __LINE__, pgd_val(pgd)) -- cgit v0.10.2 From 041d7b98ffe59c59fdd639931dea7d74f9aa9a59 Mon Sep 17 00:00:00 2001 From: Richard Guy Briggs Date: Tue, 23 Dec 2014 13:02:04 -0500 Subject: audit: restore AUDIT_LOGINUID unset ABI A regression was caused by commit 780a7654cee8: audit: Make testing for a valid loginuid explicit. (which in turn attempted to fix a regression caused by e1760bd) When audit_krule_to_data() fills in the rules to get a listing, there was a missing clause to convert back from AUDIT_LOGINUID_SET to AUDIT_LOGINUID. This broke userspace by not returning the same information that was sent and expected. The rule: auditctl -a exit,never -F auid=-1 gives: auditctl -l LIST_RULES: exit,never f24=0 syscall=all when it should give: LIST_RULES: exit,never auid=-1 (0xffffffff) syscall=all Tag it so that it is reported the same way it was set. Create a new private flags audit_krule field (pflags) to store it that won't interact with the public one from the API. Cc: stable@vger.kernel.org # v3.10-rc1+ Signed-off-by: Richard Guy Briggs Signed-off-by: Paul Moore diff --git a/include/linux/audit.h b/include/linux/audit.h index 36dffec..9333192 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -47,6 +47,7 @@ struct sk_buff; struct audit_krule { int vers_ops; + u32 pflags; u32 flags; u32 listnr; u32 action; @@ -64,6 +65,9 @@ struct audit_krule { u64 prio; }; +/* Flag to indicate legacy AUDIT_LOGINUID unset usage */ +#define AUDIT_LOGINUID_LEGACY 0x1 + struct audit_field { u32 type; union { diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index c0d148b..103586e 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -442,6 +442,7 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, if ((f->type == AUDIT_LOGINUID) && (f->val == AUDIT_UID_UNSET)) { f->type = AUDIT_LOGINUID_SET; f->val = 0; + entry->rule.pflags |= AUDIT_LOGINUID_LEGACY; } err = audit_field_valid(entry, f); @@ -617,6 +618,13 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule) data->buflen += data->values[i] = audit_pack_string(&bufp, krule->filterkey); break; + case AUDIT_LOGINUID_SET: + if (krule->pflags & AUDIT_LOGINUID_LEGACY && !f->val) { + data->fields[i] = AUDIT_LOGINUID; + data->values[i] = AUDIT_UID_UNSET; + break; + } + /* fallthrough if set */ default: data->values[i] = f->val; } @@ -633,6 +641,7 @@ static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b) int i; if (a->flags != b->flags || + a->pflags != b->pflags || a->listnr != b->listnr || a->action != b->action || a->field_count != b->field_count) @@ -751,6 +760,7 @@ struct audit_entry *audit_dupe_rule(struct audit_krule *old) new = &entry->rule; new->vers_ops = old->vers_ops; new->flags = old->flags; + new->pflags = old->pflags; new->listnr = old->listnr; new->action = old->action; for (i = 0; i < AUDIT_BITMASK_SIZE; i++) -- cgit v0.10.2 From 47c93e6b3f37bf2b709fb107f3db586e39b8fd56 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 9 Dec 2014 16:47:17 -0800 Subject: thermal: int340x: Introduce processor reporting device The Int340x thermal provides a processor thermal device, which is used to control processor thermal states. These devices are either reported as a PCI device or an ACPI device. This device provides power limits, control states and optional temperature. This change implements minimal requirements to expose processor power limits which can be used during thermal power limiting. Power limits are exposed via an attribute group called "power_limits" under the device. The exported attributes are: power_limit_0_max_uw power_limit_1_max_uw power_limit_0_min_uw power_limit_1_min_uw power_limit_0_tmin_us power_limit_1_tmin_us power_limit_0_tmax_us power_limit_1_tmax_us power_limit_0_step_uw power_limit_1_step_uw Signed-off-by: Srinivas Pandruvada Signed-off-by: Zhang Rui diff --git a/drivers/thermal/int340x_thermal/Makefile b/drivers/thermal/int340x_thermal/Makefile index ffe40bf..d441369 100644 --- a/drivers/thermal/int340x_thermal/Makefile +++ b/drivers/thermal/int340x_thermal/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_INT340X_THERMAL) += int3400_thermal.o obj-$(CONFIG_INT340X_THERMAL) += int3402_thermal.o obj-$(CONFIG_INT340X_THERMAL) += int3403_thermal.o +obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_device.o obj-$(CONFIG_ACPI_THERMAL_REL) += acpi_thermal_rel.o diff --git a/drivers/thermal/int340x_thermal/processor_thermal_device.c b/drivers/thermal/int340x_thermal/processor_thermal_device.c new file mode 100644 index 0000000..f83c55b --- /dev/null +++ b/drivers/thermal/int340x_thermal/processor_thermal_device.c @@ -0,0 +1,298 @@ +/* + * processor_thermal_device.c + * Copyright (c) 2014, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ +#include +#include +#include +#include +#include +#include + +/* Broadwell-U/HSB thermal reporting device */ +#define PCI_DEVICE_ID_PROC_BDW_THERMAL 0x1603 +#define PCI_DEVICE_ID_PROC_HSB_THERMAL 0x0A03 + +/* Braswell thermal reporting device */ +#define PCI_DEVICE_ID_PROC_BSW_THERMAL 0x22DC + +struct power_config { + u32 index; + u32 min_uw; + u32 max_uw; + u32 tmin_us; + u32 tmax_us; + u32 step_uw; +}; + +struct proc_thermal_device { + struct device *dev; + struct acpi_device *adev; + struct power_config power_limits[2]; +}; + +enum proc_thermal_emum_mode_type { + PROC_THERMAL_NONE, + PROC_THERMAL_PCI, + PROC_THERMAL_PLATFORM_DEV +}; + +/* + * We can have only one type of enumeration, PCI or Platform, + * not both. So we don't need instance specific data. + */ +static enum proc_thermal_emum_mode_type proc_thermal_emum_mode = + PROC_THERMAL_NONE; + +#define POWER_LIMIT_SHOW(index, suffix) \ +static ssize_t power_limit_##index##_##suffix##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct pci_dev *pci_dev; \ + struct platform_device *pdev; \ + struct proc_thermal_device *proc_dev; \ +\ + if (proc_thermal_emum_mode == PROC_THERMAL_PLATFORM_DEV) { \ + pdev = to_platform_device(dev); \ + proc_dev = platform_get_drvdata(pdev); \ + } else { \ + pci_dev = to_pci_dev(dev); \ + proc_dev = pci_get_drvdata(pci_dev); \ + } \ + return sprintf(buf, "%lu\n",\ + (unsigned long)proc_dev->power_limits[index].suffix * 1000); \ +} + +POWER_LIMIT_SHOW(0, min_uw) +POWER_LIMIT_SHOW(0, max_uw) +POWER_LIMIT_SHOW(0, step_uw) +POWER_LIMIT_SHOW(0, tmin_us) +POWER_LIMIT_SHOW(0, tmax_us) + +POWER_LIMIT_SHOW(1, min_uw) +POWER_LIMIT_SHOW(1, max_uw) +POWER_LIMIT_SHOW(1, step_uw) +POWER_LIMIT_SHOW(1, tmin_us) +POWER_LIMIT_SHOW(1, tmax_us) + +static DEVICE_ATTR_RO(power_limit_0_min_uw); +static DEVICE_ATTR_RO(power_limit_0_max_uw); +static DEVICE_ATTR_RO(power_limit_0_step_uw); +static DEVICE_ATTR_RO(power_limit_0_tmin_us); +static DEVICE_ATTR_RO(power_limit_0_tmax_us); + +static DEVICE_ATTR_RO(power_limit_1_min_uw); +static DEVICE_ATTR_RO(power_limit_1_max_uw); +static DEVICE_ATTR_RO(power_limit_1_step_uw); +static DEVICE_ATTR_RO(power_limit_1_tmin_us); +static DEVICE_ATTR_RO(power_limit_1_tmax_us); + +static struct attribute *power_limit_attrs[] = { + &dev_attr_power_limit_0_min_uw.attr, + &dev_attr_power_limit_1_min_uw.attr, + &dev_attr_power_limit_0_max_uw.attr, + &dev_attr_power_limit_1_max_uw.attr, + &dev_attr_power_limit_0_step_uw.attr, + &dev_attr_power_limit_1_step_uw.attr, + &dev_attr_power_limit_0_tmin_us.attr, + &dev_attr_power_limit_1_tmin_us.attr, + &dev_attr_power_limit_0_tmax_us.attr, + &dev_attr_power_limit_1_tmax_us.attr, + NULL +}; + +static struct attribute_group power_limit_attribute_group = { + .attrs = power_limit_attrs, + .name = "power_limits" +}; + +static int proc_thermal_add(struct device *dev, + struct proc_thermal_device **priv) +{ + struct proc_thermal_device *proc_priv; + struct acpi_device *adev; + acpi_status status; + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *elements, *ppcc; + union acpi_object *p; + int i; + + adev = ACPI_COMPANION(dev); + + status = acpi_evaluate_object(adev->handle, "PPCC", NULL, &buf); + if (ACPI_FAILURE(status)) + return -ENODEV; + + p = buf.pointer; + if (!p || (p->type != ACPI_TYPE_PACKAGE)) { + dev_err(dev, "Invalid PPCC data\n"); + return -EFAULT; + } + if (!p->package.count) { + dev_err(dev, "Invalid PPCC package size\n"); + return -EFAULT; + } + + proc_priv = devm_kzalloc(dev, sizeof(*proc_priv), GFP_KERNEL); + if (!proc_priv) + return -ENOMEM; + + proc_priv->dev = dev; + proc_priv->adev = adev; + + for (i = 0; i < min((int)p->package.count - 1, 2); ++i) { + elements = &(p->package.elements[i+1]); + if (elements->type != ACPI_TYPE_PACKAGE || + elements->package.count != 6) + return -EFAULT; + + ppcc = elements->package.elements; + proc_priv->power_limits[i].index = ppcc[0].integer.value; + proc_priv->power_limits[i].min_uw = ppcc[1].integer.value; + proc_priv->power_limits[i].max_uw = ppcc[2].integer.value; + proc_priv->power_limits[i].tmin_us = ppcc[3].integer.value; + proc_priv->power_limits[i].tmax_us = ppcc[4].integer.value; + proc_priv->power_limits[i].step_uw = ppcc[5].integer.value; + } + + *priv = proc_priv; + + return sysfs_create_group(&dev->kobj, + &power_limit_attribute_group); +} + +void proc_thermal_remove(struct proc_thermal_device *proc_priv) +{ + sysfs_remove_group(&proc_priv->dev->kobj, + &power_limit_attribute_group); +} + +static int int3401_add(struct platform_device *pdev) +{ + struct proc_thermal_device *proc_priv; + int ret; + + if (proc_thermal_emum_mode == PROC_THERMAL_PCI) { + dev_err(&pdev->dev, "error: enumerated as PCI dev\n"); + return -ENODEV; + } + + ret = proc_thermal_add(&pdev->dev, &proc_priv); + if (ret) + return ret; + + platform_set_drvdata(pdev, proc_priv); + proc_thermal_emum_mode = PROC_THERMAL_PLATFORM_DEV; + + return 0; +} + +static int int3401_remove(struct platform_device *pdev) +{ + proc_thermal_remove(platform_get_drvdata(pdev)); + + return 0; +} + +static int proc_thermal_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *unused) +{ + struct proc_thermal_device *proc_priv; + int ret; + + if (proc_thermal_emum_mode == PROC_THERMAL_PLATFORM_DEV) { + dev_err(&pdev->dev, "error: enumerated as platform dev\n"); + return -ENODEV; + } + + ret = pci_enable_device(pdev); + if (ret < 0) { + dev_err(&pdev->dev, "error: could not enable device\n"); + return ret; + } + + ret = proc_thermal_add(&pdev->dev, &proc_priv); + if (ret) { + pci_disable_device(pdev); + return ret; + } + + pci_set_drvdata(pdev, proc_priv); + proc_thermal_emum_mode = PROC_THERMAL_PCI; + + return 0; +} + +static void proc_thermal_pci_remove(struct pci_dev *pdev) +{ + proc_thermal_remove(pci_get_drvdata(pdev)); + pci_disable_device(pdev); +} + +static const struct pci_device_id proc_thermal_pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BDW_THERMAL)}, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_HSB_THERMAL)}, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BSW_THERMAL)}, + { 0, }, +}; + +MODULE_DEVICE_TABLE(pci, proc_thermal_pci_ids); + +static struct pci_driver proc_thermal_pci_driver = { + .name = "proc_thermal", + .probe = proc_thermal_pci_probe, + .remove = proc_thermal_pci_remove, + .id_table = proc_thermal_pci_ids, +}; + +static const struct acpi_device_id int3401_device_ids[] = { + {"INT3401", 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, int3401_device_ids); + +static struct platform_driver int3401_driver = { + .probe = int3401_add, + .remove = int3401_remove, + .driver = { + .name = "int3401 thermal", + .acpi_match_table = int3401_device_ids, + }, +}; + +static int __init proc_thermal_init(void) +{ + int ret; + + ret = platform_driver_register(&int3401_driver); + if (ret) + return ret; + + ret = pci_register_driver(&proc_thermal_pci_driver); + + return ret; +} + +static void __exit proc_thermal_exit(void) +{ + platform_driver_unregister(&int3401_driver); + pci_unregister_driver(&proc_thermal_pci_driver); +} + +module_init(proc_thermal_init); +module_exit(proc_thermal_exit); + +MODULE_AUTHOR("Srinivas Pandruvada "); +MODULE_DESCRIPTION("Processor Thermal Reporting Device Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From f01bc8f37ec940caae8bf8b6e7169f7cc2b6877a Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 23 Dec 2014 15:23:34 -0800 Subject: Thermal/int340x/int3403: Fix memory leak Address memory leak for buffer allocated with ACPI_ALLOCATE_BUFFER. Signed-off-by: Srinivas Pandruvada Signed-off-by: Zhang Rui diff --git a/drivers/thermal/int340x_thermal/int3403_thermal.c b/drivers/thermal/int340x_thermal/int3403_thermal.c index 1bfa6a6..479d754 100644 --- a/drivers/thermal/int340x_thermal/int3403_thermal.c +++ b/drivers/thermal/int340x_thermal/int3403_thermal.c @@ -369,6 +369,7 @@ static int int3403_cdev_add(struct int3403_priv *priv) p = buf.pointer; if (!p || (p->type != ACPI_TYPE_PACKAGE)) { printk(KERN_WARNING "Invalid PPSS data\n"); + kfree(buf.pointer); return -EFAULT; } @@ -381,6 +382,7 @@ static int int3403_cdev_add(struct int3403_priv *priv) priv->priv = obj; + kfree(buf.pointer); /* TODO: add ACPI notification support */ return result; -- cgit v0.10.2 From cc3f71a41576ba014ba3b80d5d68b7faa90bfeef Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 23 Dec 2014 15:23:35 -0800 Subject: Thermal/int340x/processor_thermal: Fix memory leak Address memory leak for buffer allocated with ACPI_ALLOCATE_BUFFER. Signed-off-by: Srinivas Pandruvada Signed-off-by: Zhang Rui diff --git a/drivers/thermal/int340x_thermal/processor_thermal_device.c b/drivers/thermal/int340x_thermal/processor_thermal_device.c index f83c55b..31bb553 100644 --- a/drivers/thermal/int340x_thermal/processor_thermal_device.c +++ b/drivers/thermal/int340x_thermal/processor_thermal_device.c @@ -127,6 +127,7 @@ static int proc_thermal_add(struct device *dev, union acpi_object *elements, *ppcc; union acpi_object *p; int i; + int ret; adev = ACPI_COMPANION(dev); @@ -137,16 +138,20 @@ static int proc_thermal_add(struct device *dev, p = buf.pointer; if (!p || (p->type != ACPI_TYPE_PACKAGE)) { dev_err(dev, "Invalid PPCC data\n"); - return -EFAULT; + ret = -EFAULT; + goto free_buffer; } if (!p->package.count) { dev_err(dev, "Invalid PPCC package size\n"); - return -EFAULT; + ret = -EFAULT; + goto free_buffer; } proc_priv = devm_kzalloc(dev, sizeof(*proc_priv), GFP_KERNEL); - if (!proc_priv) - return -ENOMEM; + if (!proc_priv) { + ret = -ENOMEM; + goto free_buffer; + } proc_priv->dev = dev; proc_priv->adev = adev; @@ -154,9 +159,10 @@ static int proc_thermal_add(struct device *dev, for (i = 0; i < min((int)p->package.count - 1, 2); ++i) { elements = &(p->package.elements[i+1]); if (elements->type != ACPI_TYPE_PACKAGE || - elements->package.count != 6) - return -EFAULT; - + elements->package.count != 6) { + ret = -EFAULT; + goto free_buffer; + } ppcc = elements->package.elements; proc_priv->power_limits[i].index = ppcc[0].integer.value; proc_priv->power_limits[i].min_uw = ppcc[1].integer.value; @@ -168,8 +174,13 @@ static int proc_thermal_add(struct device *dev, *priv = proc_priv; - return sysfs_create_group(&dev->kobj, - &power_limit_attribute_group); + ret = sysfs_create_group(&dev->kobj, + &power_limit_attribute_group); + +free_buffer: + kfree(buf.pointer); + + return ret; } void proc_thermal_remove(struct proc_thermal_device *proc_priv) -- cgit v0.10.2 From f8061d383b09ed933a49ac3fd301c2f991261851 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 23 Dec 2014 15:23:36 -0800 Subject: Thermal/int340x/int3403: Free acpi notification handler Remove acpi notification handler when zone is removed. Signed-off-by: Srinivas Pandruvada Signed-off-by: Zhang Rui diff --git a/drivers/thermal/int340x_thermal/int3403_thermal.c b/drivers/thermal/int340x_thermal/int3403_thermal.c index 479d754..0faf500 100644 --- a/drivers/thermal/int340x_thermal/int3403_thermal.c +++ b/drivers/thermal/int340x_thermal/int3403_thermal.c @@ -301,6 +301,8 @@ static int int3403_sensor_remove(struct int3403_priv *priv) { struct int3403_sensor *obj = priv->priv; + acpi_remove_notify_handler(priv->adev->handle, + ACPI_DEVICE_NOTIFY, int3403_notify); thermal_zone_device_unregister(obj->tzone); return 0; } -- cgit v0.10.2 From da6b51d007da17fd394405055e2f0109ec5f05f4 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Wed, 24 Dec 2014 13:11:17 +1000 Subject: Revert "drm/gem: Warn on illegal use of the dumb buffer interface v2" This reverts commit 355a70183848f21198e9f6296bd646df3478a26d. This had some bad side effects under normal operation, and should have been dropped earlier. Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index fc8cfdd..574057c 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -1586,7 +1586,7 @@ static struct drm_driver driver = { .gem_prime_import = i915_gem_prime_import, .dumb_create = i915_gem_dumb_create, - .dumb_map_offset = i915_gem_dumb_map_offset, + .dumb_map_offset = i915_gem_mmap_gtt, .dumb_destroy = drm_gem_dumb_destroy, .ioctls = i915_ioctls, .fops = &i915_driver_fops, diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 63bcda5..70d0f0f 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2501,9 +2501,8 @@ void i915_vma_move_to_active(struct i915_vma *vma, int i915_gem_dumb_create(struct drm_file *file_priv, struct drm_device *dev, struct drm_mode_create_dumb *args); -int i915_gem_dumb_map_offset(struct drm_file *file_priv, - struct drm_device *dev, uint32_t handle, - uint64_t *offset); +int i915_gem_mmap_gtt(struct drm_file *file_priv, struct drm_device *dev, + uint32_t handle, uint64_t *offset); /** * Returns true if seq1 is later than seq2. */ diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 4a9faea6..52adcb6 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -401,7 +401,6 @@ static int i915_gem_create(struct drm_file *file, struct drm_device *dev, uint64_t size, - bool dumb, uint32_t *handle_p) { struct drm_i915_gem_object *obj; @@ -417,7 +416,6 @@ i915_gem_create(struct drm_file *file, if (obj == NULL) return -ENOMEM; - obj->base.dumb = dumb; ret = drm_gem_handle_create(file, &obj->base, &handle); /* drop reference from allocate - handle holds it now */ drm_gem_object_unreference_unlocked(&obj->base); @@ -437,7 +435,7 @@ i915_gem_dumb_create(struct drm_file *file, args->pitch = ALIGN(args->width * DIV_ROUND_UP(args->bpp, 8), 64); args->size = args->pitch * args->height; return i915_gem_create(file, dev, - args->size, true, &args->handle); + args->size, &args->handle); } /** @@ -450,7 +448,7 @@ i915_gem_create_ioctl(struct drm_device *dev, void *data, struct drm_i915_gem_create *args = data; return i915_gem_create(file, dev, - args->size, false, &args->handle); + args->size, &args->handle); } static inline int @@ -1840,10 +1838,10 @@ static void i915_gem_object_free_mmap_offset(struct drm_i915_gem_object *obj) drm_gem_free_mmap_offset(&obj->base); } -static int +int i915_gem_mmap_gtt(struct drm_file *file, struct drm_device *dev, - uint32_t handle, bool dumb, + uint32_t handle, uint64_t *offset) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -1860,13 +1858,6 @@ i915_gem_mmap_gtt(struct drm_file *file, goto unlock; } - /* - * We don't allow dumb mmaps on objects created using another - * interface. - */ - WARN_ONCE(dumb && !(obj->base.dumb || obj->base.import_attach), - "Illegal dumb map of accelerated buffer.\n"); - if (obj->base.size > dev_priv->gtt.mappable_end) { ret = -E2BIG; goto out; @@ -1891,15 +1882,6 @@ unlock: return ret; } -int -i915_gem_dumb_map_offset(struct drm_file *file, - struct drm_device *dev, - uint32_t handle, - uint64_t *offset) -{ - return i915_gem_mmap_gtt(file, dev, handle, true, offset); -} - /** * i915_gem_mmap_gtt_ioctl - prepare an object for GTT mmap'ing * @dev: DRM device @@ -1921,7 +1903,7 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data, { struct drm_i915_gem_mmap_gtt *args = data; - return i915_gem_mmap_gtt(file, dev, args->handle, false, &args->offset); + return i915_gem_mmap_gtt(file, dev, args->handle, &args->offset); } static inline int diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index f06027b..1173831 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -121,9 +121,6 @@ eb_lookup_vmas(struct eb_vmas *eb, goto err; } - WARN_ONCE(obj->base.dumb, - "GPU use of dumb buffer is illegal.\n"); - drm_gem_object_reference(&obj->base); list_add_tail(&obj->obj_exec_link, &objects); } diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 5d93902..f804243 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -876,7 +876,6 @@ nouveau_display_dumb_create(struct drm_file *file_priv, struct drm_device *dev, if (ret) return ret; - bo->gem.dumb = true; ret = drm_gem_handle_create(file_priv, &bo->gem, &args->handle); drm_gem_object_unreference_unlocked(&bo->gem); return ret; @@ -892,14 +891,6 @@ nouveau_display_dumb_map_offset(struct drm_file *file_priv, gem = drm_gem_object_lookup(dev, file_priv, handle); if (gem) { struct nouveau_bo *bo = nouveau_gem_object(gem); - - /* - * We don't allow dumb mmaps on objects created using another - * interface. - */ - WARN_ONCE(!(gem->dumb || gem->import_attach), - "Illegal dumb map of accelerated buffer.\n"); - *poffset = drm_vma_node_offset_addr(&bo->bo.vma_node); drm_gem_object_unreference_unlocked(gem); return 0; diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index 28d51a2..42c34ba 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -444,9 +444,6 @@ validate_list(struct nouveau_channel *chan, struct nouveau_cli *cli, list_for_each_entry(nvbo, list, entry) { struct drm_nouveau_gem_pushbuf_bo *b = &pbbo[nvbo->pbbo_index]; - WARN_ONCE(nvbo->gem.dumb, - "GPU use of dumb buffer is illegal.\n"); - ret = nouveau_gem_set_domain(&nvbo->gem, b->read_domains, b->write_domains, b->valid_domains); diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c index fe48f22..a46f737 100644 --- a/drivers/gpu/drm/radeon/radeon_gem.c +++ b/drivers/gpu/drm/radeon/radeon_gem.c @@ -394,10 +394,9 @@ int radeon_gem_set_domain_ioctl(struct drm_device *dev, void *data, return r; } -static int radeon_mode_mmap(struct drm_file *filp, - struct drm_device *dev, - uint32_t handle, bool dumb, - uint64_t *offset_p) +int radeon_mode_dumb_mmap(struct drm_file *filp, + struct drm_device *dev, + uint32_t handle, uint64_t *offset_p) { struct drm_gem_object *gobj; struct radeon_bo *robj; @@ -406,14 +405,6 @@ static int radeon_mode_mmap(struct drm_file *filp, if (gobj == NULL) { return -ENOENT; } - - /* - * We don't allow dumb mmaps on objects created using another - * interface. - */ - WARN_ONCE(dumb && !(gobj->dumb || gobj->import_attach), - "Illegal dumb map of GPU buffer.\n"); - robj = gem_to_radeon_bo(gobj); if (radeon_ttm_tt_has_userptr(robj->tbo.ttm)) { drm_gem_object_unreference_unlocked(gobj); @@ -424,20 +415,12 @@ static int radeon_mode_mmap(struct drm_file *filp, return 0; } -int radeon_mode_dumb_mmap(struct drm_file *filp, - struct drm_device *dev, - uint32_t handle, uint64_t *offset_p) -{ - return radeon_mode_mmap(filp, dev, handle, true, offset_p); -} - int radeon_gem_mmap_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) { struct drm_radeon_gem_mmap *args = data; - return radeon_mode_mmap(filp, dev, args->handle, false, - &args->addr_ptr); + return radeon_mode_dumb_mmap(filp, dev, args->handle, &args->addr_ptr); } int radeon_gem_busy_ioctl(struct drm_device *dev, void *data, @@ -763,7 +746,6 @@ int radeon_mode_dumb_create(struct drm_file *file_priv, return -ENOMEM; r = drm_gem_handle_create(file_priv, gobj, &handle); - gobj->dumb = true; /* drop reference from allocate - handle holds it now */ drm_gem_object_unreference_unlocked(gobj); if (r) { diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c index 7d68223..86fc564 100644 --- a/drivers/gpu/drm/radeon/radeon_object.c +++ b/drivers/gpu/drm/radeon/radeon_object.c @@ -529,9 +529,6 @@ int radeon_bo_list_validate(struct radeon_device *rdev, u32 current_domain = radeon_mem_type_to_domain(bo->tbo.mem.mem_type); - WARN_ONCE(bo->gem_base.dumb, - "GPU use of dumb buffer is illegal.\n"); - /* Check if this buffer will be moved and don't move it * if we have moved too many buffers for this IB already. * diff --git a/include/drm/drm_gem.h b/include/drm/drm_gem.h index 780511a..1e6ae14 100644 --- a/include/drm/drm_gem.h +++ b/include/drm/drm_gem.h @@ -119,13 +119,6 @@ struct drm_gem_object { * simply leave it as NULL. */ struct dma_buf_attachment *import_attach; - - /** - * dumb - created as dumb buffer - * Whether the gem object was created using the dumb buffer interface - * as such it may not be used for GPU rendering. - */ - bool dumb; }; void drm_gem_object_release(struct drm_gem_object *obj); -- cgit v0.10.2 From af6dabc9c70ae3f307685b1f32f52d60b1bf0527 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Fri, 19 Dec 2014 11:09:13 +0800 Subject: net: drop the packet when fails to do software segmentation or header check Commit cecda693a969816bac5e470e1d9c9c0ef5567bca ("net: keep original skb which only needs header checking during software GSO") keeps the original skb for packets that only needs header check, but it doesn't drop the packet if software segmentation or header check were failed. Fixes cecda693a9 ("net: keep original skb which only needs header checking during software GSO") Cc: Eric Dumazet Signed-off-by: Jason Wang Signed-off-by: David S. Miller diff --git a/net/core/dev.c b/net/core/dev.c index f411c28..a989f85 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2673,7 +2673,7 @@ static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device segs = skb_gso_segment(skb, features); if (IS_ERR(segs)) { - segs = NULL; + goto out_kfree_skb; } else if (segs) { consume_skb(skb); skb = segs; -- cgit v0.10.2 From 5b6698b0e4a37053de35cc24ee695b98a7eb712b Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 20 Dec 2014 13:48:55 +0100 Subject: batman-adv: Calculate extra tail size based on queued fragments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fragmentation code was replaced in 610bfc6bc99bc83680d190ebc69359a05fc7f605 ("batman-adv: Receive fragmented packets and merge"). The new code provided a mostly unused parameter skb for the merging function. It is used inside the function to calculate the additionally needed skb tailroom. But instead of increasing its own tailroom, it is only increasing the tailroom of the first queued skb. This is not correct in some situations because the first queued entry can be a different one than the parameter. An observed problem was: 1. packet with size 104, total_size 1464, fragno 1 was received - packet is queued 2. packet with size 1400, total_size 1464, fragno 0 was received - packet is queued at the end of the list 3. enough data was received and can be given to the merge function (1464 == (1400 - 20) + (104 - 20)) - merge functions gets 1400 byte large packet as skb argument 4. merge function gets first entry in queue (104 byte) - stored as skb_out 5. merge function calculates the required extra tail as total_size - skb->len - pskb_expand_head tail of skb_out with 64 bytes 6. merge function tries to squeeze the extra 1380 bytes from the second queued skb (1400 byte aka skb parameter) in the 64 extra tail bytes of skb_out Instead calculate the extra required tail bytes for skb_out also using skb_out instead of using the parameter skb. The skb parameter is only used to get the total_size from the last received packet. This is also the total_size used to decide that all fragments were received. Reported-by: Philipp Psurek Signed-off-by: Sven Eckelmann Acked-by: Martin Hundebøll Signed-off-by: David S. Miller diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c index fc1835c..8af3461 100644 --- a/net/batman-adv/fragmentation.c +++ b/net/batman-adv/fragmentation.c @@ -251,7 +251,7 @@ batadv_frag_merge_packets(struct hlist_head *chain, struct sk_buff *skb) kfree(entry); /* Make room for the rest of the fragments. */ - if (pskb_expand_head(skb_out, 0, size - skb->len, GFP_ATOMIC) < 0) { + if (pskb_expand_head(skb_out, 0, size - skb_out->len, GFP_ATOMIC) < 0) { kfree_skb(skb_out); skb_out = NULL; goto free; -- cgit v0.10.2 From 0402e444cd199389b7fe47be68a67b817e09e097 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 20 Dec 2014 13:48:56 +0100 Subject: batman-adv: Unify fragment size calculation The fragmentation code was replaced in 610bfc6bc99bc83680d190ebc69359a05fc7f605 ("batman-adv: Receive fragmented packets and merge") by an implementation which can handle up to 16 fragments of a packet. The packet is prepared for the split in fragments by the function batadv_frag_send_packet and the actual split is done by batadv_frag_create. Both functions calculate the size of a fragment themself. But their calculation differs because batadv_frag_send_packet also subtracts ETH_HLEN. Therefore, the check in batadv_frag_send_packet "can a full fragment can be created?" may return true even when batadv_frag_create cannot create a full fragment. The function batadv_frag_create doesn't check the size of the skb before splitting it and therefore might try to create a larger fragment than the remaining buffer. This creates an integer underflow and an invalid len is given to skb_split. Signed-off-by: Sven Eckelmann Signed-off-by: David S. Miller diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c index 8af3461..00f9e14 100644 --- a/net/batman-adv/fragmentation.c +++ b/net/batman-adv/fragmentation.c @@ -434,7 +434,7 @@ bool batadv_frag_send_packet(struct sk_buff *skb, * fragments larger than BATADV_FRAG_MAX_FRAG_SIZE */ mtu = min_t(unsigned, mtu, BATADV_FRAG_MAX_FRAG_SIZE); - max_fragment_size = (mtu - header_size - ETH_HLEN); + max_fragment_size = mtu - header_size; max_packet_size = max_fragment_size * BATADV_FRAG_MAX_FRAGMENTS; /* Don't even try to fragment, if we need more than 16 fragments */ -- cgit v0.10.2 From 0d1644919578db525b9a7b6c8197ce02adbfce26 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Sat, 20 Dec 2014 13:48:57 +0100 Subject: batman-adv: avoid NULL dereferences and fix if check Gateway having bandwidth_down equal to zero are not accepted at all and so never added to the Gateway list. For this reason checking the bandwidth_down member in batadv_gw_out_of_range() is useless. This is probably a copy/paste error and this check was supposed to be "!gw_node" only. Moreover, the way the check is written now may also lead to a NULL dereference. Fix this by rewriting the if-condition properly. Introduced by 414254e342a0d58144de40c3da777521ebaeeb07 ("batman-adv: tvlv - gateway download/upload bandwidth container") Signed-off-by: Antonio Quartulli Reported-by: David Binderman Signed-off-by: Marek Lindner Signed-off-by: David S. Miller diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index 90cff58..e0bcf9e 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c @@ -810,7 +810,7 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, goto out; gw_node = batadv_gw_node_get(bat_priv, orig_dst_node); - if (!gw_node->bandwidth_down == 0) + if (!gw_node) goto out; switch (atomic_read(&bat_priv->gw_mode)) { -- cgit v0.10.2 From 726ce70e9e4050409243f3a1d735dc86bc6e6e57 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Sun, 21 Dec 2014 07:16:21 +1100 Subject: net: Move napi polling code out of net_rx_action This patch creates a new function napi_poll and moves the napi polling code from net_rx_action into it. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller diff --git a/net/core/dev.c b/net/core/dev.c index a989f85..493ae8e 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4557,6 +4557,59 @@ void netif_napi_del(struct napi_struct *napi) } EXPORT_SYMBOL(netif_napi_del); +static int napi_poll(struct napi_struct *n, struct list_head *repoll) +{ + void *have; + int work, weight; + + list_del_init(&n->poll_list); + + have = netpoll_poll_lock(n); + + weight = n->weight; + + /* This NAPI_STATE_SCHED test is for avoiding a race + * with netpoll's poll_napi(). Only the entity which + * obtains the lock and sees NAPI_STATE_SCHED set will + * actually make the ->poll() call. Therefore we avoid + * accidentally calling ->poll() when NAPI is not scheduled. + */ + work = 0; + if (test_bit(NAPI_STATE_SCHED, &n->state)) { + work = n->poll(n, weight); + trace_napi_poll(n); + } + + WARN_ON_ONCE(work > weight); + + if (likely(work < weight)) + goto out_unlock; + + /* Drivers must not modify the NAPI state if they + * consume the entire weight. In such cases this code + * still "owns" the NAPI instance and therefore can + * move the instance around on the list at-will. + */ + if (unlikely(napi_disable_pending(n))) { + napi_complete(n); + goto out_unlock; + } + + if (n->gro_list) { + /* flush too old packets + * If HZ < 1000, flush all packets. + */ + napi_gro_flush(n, HZ >= 1000); + } + + list_add_tail(&n->poll_list, repoll); + +out_unlock: + netpoll_poll_unlock(have); + + return work; +} + static void net_rx_action(struct softirq_action *h) { struct softnet_data *sd = this_cpu_ptr(&softnet_data); @@ -4564,7 +4617,6 @@ static void net_rx_action(struct softirq_action *h) int budget = netdev_budget; LIST_HEAD(list); LIST_HEAD(repoll); - void *have; local_irq_disable(); list_splice_init(&sd->poll_list, &list); @@ -4572,7 +4624,6 @@ static void net_rx_action(struct softirq_action *h) while (!list_empty(&list)) { struct napi_struct *n; - int work, weight; /* If softirq window is exhausted then punt. * Allow this to run for 2 jiffies since which will allow @@ -4583,48 +4634,7 @@ static void net_rx_action(struct softirq_action *h) n = list_first_entry(&list, struct napi_struct, poll_list); - list_del_init(&n->poll_list); - - have = netpoll_poll_lock(n); - - weight = n->weight; - - /* This NAPI_STATE_SCHED test is for avoiding a race - * with netpoll's poll_napi(). Only the entity which - * obtains the lock and sees NAPI_STATE_SCHED set will - * actually make the ->poll() call. Therefore we avoid - * accidentally calling ->poll() when NAPI is not scheduled. - */ - work = 0; - if (test_bit(NAPI_STATE_SCHED, &n->state)) { - work = n->poll(n, weight); - trace_napi_poll(n); - } - - WARN_ON_ONCE(work > weight); - - budget -= work; - - /* Drivers must not modify the NAPI state if they - * consume the entire weight. In such cases this code - * still "owns" the NAPI instance and therefore can - * move the instance around on the list at-will. - */ - if (unlikely(work == weight)) { - if (unlikely(napi_disable_pending(n))) { - napi_complete(n); - } else { - if (n->gro_list) { - /* flush too old packets - * If HZ < 1000, flush all packets. - */ - napi_gro_flush(n, HZ >= 1000); - } - list_add_tail(&n->poll_list, &repoll); - } - } - - netpoll_poll_unlock(have); + budget -= napi_poll(n, &repoll); } if (!sd_has_rps_ipi_waiting(sd) && -- cgit v0.10.2 From 001ce546bb537bb5b7821f05633556a0c9787e32 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Sun, 21 Dec 2014 07:16:22 +1100 Subject: net: Detect drivers that reschedule NAPI and exhaust budget The commit d75b1ade567ffab085e8adbbdacf0092d10cd09c (net: less interrupt masking in NAPI) required drivers to leave poll_list empty if the entire budget is consumed. We have already had two broken drivers so let's add a check for this. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller diff --git a/net/core/dev.c b/net/core/dev.c index 493ae8e..c0cf129 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4602,6 +4602,15 @@ static int napi_poll(struct napi_struct *n, struct list_head *repoll) napi_gro_flush(n, HZ >= 1000); } + /* Some drivers may have called napi_schedule + * prior to exhausting their budget. + */ + if (unlikely(!list_empty(&n->poll_list))) { + pr_warn_once("%s: Budget exhausted after napi rescheduled\n", + n->dev ? n->dev->name : "backlog"); + goto out_unlock; + } + list_add_tail(&n->poll_list, repoll); out_unlock: -- cgit v0.10.2 From 6bd373ebbac4b13ecd39ddc37a0dc5ad4c5e4585 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Sun, 21 Dec 2014 07:16:24 +1100 Subject: net: Always poll at least one device in net_rx_action We should only perform the softnet_break check after we have polled at least one device in net_rx_action. Otherwise a zero or negative setting of netdev_budget can lock up the whole system. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller diff --git a/net/core/dev.c b/net/core/dev.c index c0cf129..b85eba9 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4634,16 +4634,15 @@ static void net_rx_action(struct softirq_action *h) while (!list_empty(&list)) { struct napi_struct *n; + n = list_first_entry(&list, struct napi_struct, poll_list); + budget -= napi_poll(n, &repoll); + /* If softirq window is exhausted then punt. * Allow this to run for 2 jiffies since which will allow * an average latency of 1.5/HZ. */ if (unlikely(budget <= 0 || time_after_eq(jiffies, time_limit))) goto softnet_break; - - - n = list_first_entry(&list, struct napi_struct, poll_list); - budget -= napi_poll(n, &repoll); } if (!sd_has_rps_ipi_waiting(sd) && -- cgit v0.10.2 From ceb8d5bf17d366534f32d2f60f41d905a5bc864b Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Sun, 21 Dec 2014 07:16:25 +1100 Subject: net: Rearrange loop in net_rx_action This patch rearranges the loop in net_rx_action to reduce the amount of jumping back and forth when reading the code. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller diff --git a/net/core/dev.c b/net/core/dev.c index b85eba9..c97ae6f 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4631,9 +4631,15 @@ static void net_rx_action(struct softirq_action *h) list_splice_init(&sd->poll_list, &list); local_irq_enable(); - while (!list_empty(&list)) { + for (;;) { struct napi_struct *n; + if (list_empty(&list)) { + if (!sd_has_rps_ipi_waiting(sd) && list_empty(&repoll)) + return; + break; + } + n = list_first_entry(&list, struct napi_struct, poll_list); budget -= napi_poll(n, &repoll); @@ -4641,15 +4647,13 @@ static void net_rx_action(struct softirq_action *h) * Allow this to run for 2 jiffies since which will allow * an average latency of 1.5/HZ. */ - if (unlikely(budget <= 0 || time_after_eq(jiffies, time_limit))) - goto softnet_break; + if (unlikely(budget <= 0 || + time_after_eq(jiffies, time_limit))) { + sd->time_squeeze++; + break; + } } - if (!sd_has_rps_ipi_waiting(sd) && - list_empty(&list) && - list_empty(&repoll)) - return; -out: local_irq_disable(); list_splice_tail_init(&sd->poll_list, &list); @@ -4659,12 +4663,6 @@ out: __raise_softirq_irqoff(NET_RX_SOFTIRQ); net_rps_action_and_irq_enable(sd); - - return; - -softnet_break: - sd->time_squeeze++; - goto out; } struct netdev_adjacent { -- cgit v0.10.2 From d0edc7bf397a5e0f312bf8a1e87cfee0019dc07b Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Tue, 23 Dec 2014 16:20:11 -0800 Subject: mpls: Fix config check for mpls. Fixes MPLS GSO for case when mpls is compiled as kernel module. Fixes: 0d89d2035f ("MPLS: Add limited GSO support"). Signed-off-by: Pravin B Shelar Signed-off-by: David S. Miller diff --git a/net/core/dev.c b/net/core/dev.c index c97ae6f..67b6210 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2522,7 +2522,7 @@ static int illegal_highdma(struct net_device *dev, struct sk_buff *skb) /* If MPLS offload request, verify we are testing hardware MPLS features * instead of standard features for the netdev. */ -#ifdef CONFIG_NET_MPLS_GSO +#if IS_ENABLED(CONFIG_NET_MPLS_GSO) static netdev_features_t net_mpls_features(struct sk_buff *skb, netdev_features_t features, __be16 type) -- cgit v0.10.2 From 4cc1beca3096a6425543cb83a61665b9c8040f98 Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Tue, 23 Dec 2014 16:20:16 -0800 Subject: mpls: Fix allowed protocols for mpls gso MPLS and Tunnel GSO does not work together. Reject packet which request such GSO. Fixes: 0d89d2035f ("MPLS: Add limited GSO support"). Signed-off-by: Pravin B Shelar Signed-off-by: David S. Miller diff --git a/net/mpls/mpls_gso.c b/net/mpls/mpls_gso.c index ca27837..349295d 100644 --- a/net/mpls/mpls_gso.c +++ b/net/mpls/mpls_gso.c @@ -31,10 +31,7 @@ static struct sk_buff *mpls_gso_segment(struct sk_buff *skb, SKB_GSO_TCPV6 | SKB_GSO_UDP | SKB_GSO_DODGY | - SKB_GSO_TCP_ECN | - SKB_GSO_GRE | - SKB_GSO_GRE_CSUM | - SKB_GSO_IPIP))) + SKB_GSO_TCP_ECN))) goto out; /* Setup inner SKB. */ -- cgit v0.10.2 From ec449f40bb3e19c77f62ddabf7c1fe3ccefece6f Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Tue, 23 Dec 2014 16:20:20 -0800 Subject: openvswitch: Fix MPLS action validation. Linux stack does not implement GSO for packet with multiple encapsulations. Therefore there was check in MPLS action validation to detect such case, But this check introduced bug which deleted one or more actions from actions list. Following patch removes this check to fix the validation. Fixes: 25cd9ba0abc ("openvswitch: Add basic MPLS support to kernel"). Signed-off-by: Pravin B Shelar Reported-by: Srinivas Neginhal Acked-by: Jarno Rajahalme Signed-off-by: David S. Miller diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index 9645a21..d1eecf7 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -1753,7 +1753,6 @@ static int __ovs_nla_copy_actions(const struct nlattr *attr, __be16 eth_type, __be16 vlan_tci, bool log) { const struct nlattr *a; - bool out_tnl_port = false; int rem, err; if (depth >= SAMPLE_ACTION_DEPTH) @@ -1796,8 +1795,6 @@ static int __ovs_nla_copy_actions(const struct nlattr *attr, case OVS_ACTION_ATTR_OUTPUT: if (nla_get_u32(a) >= DP_MAX_PORTS) return -EINVAL; - out_tnl_port = false; - break; case OVS_ACTION_ATTR_HASH: { @@ -1832,12 +1829,6 @@ static int __ovs_nla_copy_actions(const struct nlattr *attr, case OVS_ACTION_ATTR_PUSH_MPLS: { const struct ovs_action_push_mpls *mpls = nla_data(a); - /* Networking stack do not allow simultaneous Tunnel - * and MPLS GSO. - */ - if (out_tnl_port) - return -EINVAL; - if (!eth_p_mpls(mpls->mpls_ethertype)) return -EINVAL; /* Prohibit push MPLS other than to a white list @@ -1873,11 +1864,9 @@ static int __ovs_nla_copy_actions(const struct nlattr *attr, case OVS_ACTION_ATTR_SET: err = validate_set(a, key, sfa, - &out_tnl_port, eth_type, log); + &skip_copy, eth_type, log); if (err) return err; - - skip_copy = out_tnl_port; break; case OVS_ACTION_ATTR_SAMPLE: -- cgit v0.10.2 From cbe7e76d94f59e89302bd514e4b685e03d1ebbe4 Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Tue, 23 Dec 2014 16:20:28 -0800 Subject: openvswitch: Fix GSO with multiple MPLS label. MPLS GSO needs to know inner most protocol to process GSO packets. Fixes: 25cd9ba0abc ("openvswitch: Add basic MPLS support to kernel"). Signed-off-by: Pravin B Shelar Signed-off-by: David S. Miller diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 764fdc3..770064c 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -147,7 +147,8 @@ static int push_mpls(struct sk_buff *skb, struct sw_flow_key *key, hdr = eth_hdr(skb); hdr->h_proto = mpls->mpls_ethertype; - skb_set_inner_protocol(skb, skb->protocol); + if (!skb->inner_protocol) + skb_set_inner_protocol(skb, skb->protocol); skb->protocol = mpls->mpls_ethertype; invalidate_flow_key(key); -- cgit v0.10.2 From 997e068ebc17d8d57e735578df44b6341cd5f2f3 Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Tue, 23 Dec 2014 16:20:32 -0800 Subject: openvswitch: Fix vport_send double free Today vport-send has complex error handling because it involves freeing skb and updating stats depending on return value from vport send implementation. This can be simplified by delegating responsibility of freeing skb to the vport implementation for all cases. So that vport-send needs just update stats. Fixes: 91b7514cdf ("openvswitch: Unify vport error stats handling") Signed-off-by: Pravin B Shelar Signed-off-by: David S. Miller diff --git a/net/ipv4/geneve.c b/net/ipv4/geneve.c index 95e47c9..394a200 100644 --- a/net/ipv4/geneve.c +++ b/net/ipv4/geneve.c @@ -122,14 +122,18 @@ int geneve_xmit_skb(struct geneve_sock *gs, struct rtable *rt, int err; skb = udp_tunnel_handle_offloads(skb, !gs->sock->sk->sk_no_check_tx); + if (IS_ERR(skb)) + return PTR_ERR(skb); min_headroom = LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len + GENEVE_BASE_HLEN + opt_len + sizeof(struct iphdr) + (vlan_tx_tag_present(skb) ? VLAN_HLEN : 0); err = skb_cow_head(skb, min_headroom); - if (unlikely(err)) + if (unlikely(err)) { + kfree_skb(skb); return err; + } skb = vlan_hwaccel_push_inside(skb); if (unlikely(!skb)) diff --git a/net/openvswitch/vport-geneve.c b/net/openvswitch/vport-geneve.c index 347fa23..484864d 100644 --- a/net/openvswitch/vport-geneve.c +++ b/net/openvswitch/vport-geneve.c @@ -219,7 +219,10 @@ static int geneve_tnl_send(struct vport *vport, struct sk_buff *skb) false); if (err < 0) ip_rt_put(rt); + return err; + error: + kfree_skb(skb); return err; } diff --git a/net/openvswitch/vport-gre.c b/net/openvswitch/vport-gre.c index 6b69df5..28f54e9 100644 --- a/net/openvswitch/vport-gre.c +++ b/net/openvswitch/vport-gre.c @@ -73,7 +73,7 @@ static struct sk_buff *__build_header(struct sk_buff *skb, skb = gre_handle_offloads(skb, !!(tun_key->tun_flags & TUNNEL_CSUM)); if (IS_ERR(skb)) - return NULL; + return skb; tpi.flags = filter_tnl_flags(tun_key->tun_flags); tpi.proto = htons(ETH_P_TEB); @@ -144,7 +144,7 @@ static int gre_tnl_send(struct vport *vport, struct sk_buff *skb) if (unlikely(!OVS_CB(skb)->egress_tun_info)) { err = -EINVAL; - goto error; + goto err_free_skb; } tun_key = &OVS_CB(skb)->egress_tun_info->tunnel; @@ -157,8 +157,10 @@ static int gre_tnl_send(struct vport *vport, struct sk_buff *skb) fl.flowi4_proto = IPPROTO_GRE; rt = ip_route_output_key(net, &fl); - if (IS_ERR(rt)) - return PTR_ERR(rt); + if (IS_ERR(rt)) { + err = PTR_ERR(rt); + goto err_free_skb; + } tunnel_hlen = ip_gre_calc_hlen(tun_key->tun_flags); @@ -183,8 +185,9 @@ static int gre_tnl_send(struct vport *vport, struct sk_buff *skb) /* Push Tunnel header. */ skb = __build_header(skb, tunnel_hlen); - if (unlikely(!skb)) { - err = 0; + if (IS_ERR(skb)) { + err = PTR_ERR(rt); + skb = NULL; goto err_free_rt; } @@ -198,7 +201,8 @@ static int gre_tnl_send(struct vport *vport, struct sk_buff *skb) tun_key->ipv4_tos, tun_key->ipv4_ttl, df, false); err_free_rt: ip_rt_put(rt); -error: +err_free_skb: + kfree_skb(skb); return err; } diff --git a/net/openvswitch/vport-vxlan.c b/net/openvswitch/vport-vxlan.c index 38f95a5..d7c46b3 100644 --- a/net/openvswitch/vport-vxlan.c +++ b/net/openvswitch/vport-vxlan.c @@ -187,7 +187,9 @@ static int vxlan_tnl_send(struct vport *vport, struct sk_buff *skb) false); if (err < 0) ip_rt_put(rt); + return err; error: + kfree_skb(skb); return err; } diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c index 9584526..53f3ebb 100644 --- a/net/openvswitch/vport.c +++ b/net/openvswitch/vport.c @@ -519,10 +519,9 @@ int ovs_vport_send(struct vport *vport, struct sk_buff *skb) u64_stats_update_end(&stats->syncp); } else if (sent < 0) { ovs_vport_record_error(vport, VPORT_E_TX_ERROR); - kfree_skb(skb); - } else + } else { ovs_vport_record_error(vport, VPORT_E_TX_DROPPED); - + } return sent; } -- cgit v0.10.2 From 74f47278cb056ffe1d261df3e094d608c3569829 Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Tue, 23 Dec 2014 16:20:36 -0800 Subject: vxlan: Fix double free of skb. In case of error vxlan_xmit_one() can free already freed skb. Also fixes memory leak of dst-entry. Fixes: acbf74a7630 ("vxlan: Refactor vxlan driver to make use of the common UDP tunnel functions"). Signed-off-by: Pravin B Shelar Signed-off-by: David S. Miller diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 49d9f22..7fbd89f 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -1579,8 +1579,10 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs, bool udp_sum = !udp_get_no_check6_tx(vs->sock->sk); skb = udp_tunnel_handle_offloads(skb, udp_sum); - if (IS_ERR(skb)) - return -EINVAL; + if (IS_ERR(skb)) { + err = -EINVAL; + goto err; + } skb_scrub_packet(skb, xnet); @@ -1590,12 +1592,16 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs, /* Need space for new headers (invalidates iph ptr) */ err = skb_cow_head(skb, min_headroom); - if (unlikely(err)) - return err; + if (unlikely(err)) { + kfree_skb(skb); + goto err; + } skb = vlan_hwaccel_push_inside(skb); - if (WARN_ON(!skb)) - return -ENOMEM; + if (WARN_ON(!skb)) { + err = -ENOMEM; + goto err; + } vxh = (struct vxlanhdr *) __skb_push(skb, sizeof(*vxh)); vxh->vx_flags = htonl(VXLAN_FLAGS); @@ -1606,6 +1612,9 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs, udp_tunnel6_xmit_skb(vs->sock, dst, skb, dev, saddr, daddr, prio, ttl, src_port, dst_port); return 0; +err: + dst_release(dst); + return err; } #endif @@ -1621,7 +1630,7 @@ int vxlan_xmit_skb(struct vxlan_sock *vs, skb = udp_tunnel_handle_offloads(skb, udp_sum); if (IS_ERR(skb)) - return -EINVAL; + return PTR_ERR(skb); min_headroom = LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len + VXLAN_HLEN + sizeof(struct iphdr) @@ -1629,8 +1638,10 @@ int vxlan_xmit_skb(struct vxlan_sock *vs, /* Need space for new headers (invalidates iph ptr) */ err = skb_cow_head(skb, min_headroom); - if (unlikely(err)) + if (unlikely(err)) { + kfree_skb(skb); return err; + } skb = vlan_hwaccel_push_inside(skb); if (WARN_ON(!skb)) @@ -1776,9 +1787,12 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, tos, ttl, df, src_port, dst_port, htonl(vni << 8), !net_eq(vxlan->net, dev_net(vxlan->dev))); - - if (err < 0) + if (err < 0) { + /* skb is already freed. */ + skb = NULL; goto rt_tx_error; + } + iptunnel_xmit_stats(err, &dev->stats, dev->tstats); #if IS_ENABLED(CONFIG_IPV6) } else { -- cgit v0.10.2 From 796f2da81bead71ffc91ef70912cd8d1827bf756 Mon Sep 17 00:00:00 2001 From: Toshiaki Makita Date: Mon, 22 Dec 2014 19:04:14 +0900 Subject: net: Fix stacked vlan offload features computation When vlan tags are stacked, it is very likely that the outer tag is stored in skb->vlan_tci and skb->protocol shows the inner tag's vlan_proto. Currently netif_skb_features() first looks at skb->protocol even if there is the outer tag in vlan_tci, thus it incorrectly retrieves the protocol encapsulated by the inner vlan instead of the inner vlan protocol. This allows GSO packets to be passed to HW and they end up being corrupted. Fixes: 58e998c6d239 ("offloading: Force software GSO for multiple vlan tags.") Signed-off-by: Toshiaki Makita Signed-off-by: David S. Miller diff --git a/net/core/dev.c b/net/core/dev.c index 67b6210..bd44e28 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2570,11 +2570,14 @@ netdev_features_t netif_skb_features(struct sk_buff *skb) if (gso_segs > dev->gso_max_segs || gso_segs < dev->gso_min_segs) features &= ~NETIF_F_GSO_MASK; - if (protocol == htons(ETH_P_8021Q) || protocol == htons(ETH_P_8021AD)) { - struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data; - protocol = veh->h_vlan_encapsulated_proto; - } else if (!vlan_tx_tag_present(skb)) { - return harmonize_features(skb, features); + if (!vlan_tx_tag_present(skb)) { + if (unlikely(protocol == htons(ETH_P_8021Q) || + protocol == htons(ETH_P_8021AD))) { + struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data; + protocol = veh->h_vlan_encapsulated_proto; + } else { + return harmonize_features(skb, features); + } } features = netdev_intersect_features(features, -- cgit v0.10.2 From b8fb4e0648a2ab3734140342002f68fb0c7d1602 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Tue, 23 Dec 2014 01:13:18 +0100 Subject: net: Reset secmark when scrubbing packet skb_scrub_packet() is called when a packet switches between a context such as between underlay and overlay, between namespaces, or between L3 subnets. While we already scrub the packet mark, connection tracking entry, and cached destination, the security mark/context is left intact. It seems wrong to inherit the security context of a packet when going from overlay to underlay or across forwarding paths. Signed-off-by: Thomas Graf Acked-by: Flavio Leitner Signed-off-by: David S. Miller diff --git a/net/core/skbuff.c b/net/core/skbuff.c index ae13ef6..395c15b 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -4148,6 +4148,7 @@ void skb_scrub_packet(struct sk_buff *skb, bool xnet) skb->ignore_df = 0; skb_dst_drop(skb); skb->mark = 0; + skb_init_secmark(skb); secpath_reset(skb); nf_reset(skb); nf_reset_trace(skb); -- cgit v0.10.2 From 6087fcab7bd5122e7264504854ec77d5be0286ff Mon Sep 17 00:00:00 2001 From: "Fang, Yang A" Date: Tue, 23 Dec 2014 23:49:05 -0400 Subject: ASoC: rt5677: fixed rt5677_dsp_vad_put rt5677_dsp_vad_get panic snd_kcontrol_chip should return snd_soc_component instead of snd_soc_codec Signed-off-by: Fang, Yang A Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 81fe146..c0fbe18 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -784,8 +784,8 @@ static unsigned int bst_tlv[] = { static int rt5677_dsp_vad_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); ucontrol->value.integer.value[0] = rt5677->dsp_vad_en; @@ -795,8 +795,9 @@ static int rt5677_dsp_vad_get(struct snd_kcontrol *kcontrol, static int rt5677_dsp_vad_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + struct snd_soc_codec *codec = snd_soc_component_to_codec(component); rt5677->dsp_vad_en = !!ucontrol->value.integer.value[0]; -- cgit v0.10.2 From 25f97549b5a19a373922e07c3e0f0b0b56a49148 Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Tue, 23 Dec 2014 09:12:45 +0800 Subject: ASoC: Intel: correct the fixed free block allocation For block span more than 1 section, when allocate it from a free block, we need allocate the remain buffers within the block, and then continue alloc the rest of needed size buffer. Here also make sure this free block is moved from free list to used list, and add it to block_list which may be used for power gating disabling later. Signed-off-by: Jie Yang Signed-off-by: Mark Brown diff --git a/sound/soc/intel/sst-firmware.c b/sound/soc/intel/sst-firmware.c index 4a5bde9..ef2e8b5 100644 --- a/sound/soc/intel/sst-firmware.c +++ b/sound/soc/intel/sst-firmware.c @@ -763,8 +763,12 @@ static int block_alloc_fixed(struct sst_dsp *dsp, struct sst_block_allocator *ba /* does block span more than 1 section */ if (ba->offset >= block->offset && ba->offset < block_end) { + /* add block */ + list_move(&block->list, &dsp->used_block_list); + list_add(&block->module_list, block_list); /* align ba to block boundary */ - ba->offset = block->offset; + ba->size -= block_end - ba->offset; + ba->offset = block_end; err = block_alloc_contiguous(dsp, ba, block_list); if (err < 0) -- cgit v0.10.2 From 76fe5e95fac3c93bdff9102480e5ba823ba656c3 Mon Sep 17 00:00:00 2001 From: Andrew Bresticker Date: Mon, 22 Dec 2014 11:35:15 -0800 Subject: spi: img-spfi: Increase DMA burst size A 1-byte burst size is rather inefficient and has been shown to cause TX issues during testing. Increase the DMA burst size to 4-bytes for both RX and TX DMA when using the 8-bit FIFO. Signed-off-by: Andrew Bresticker Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-img-spfi.c b/drivers/spi/spi-img-spfi.c index cd14556..aad6683 100644 --- a/drivers/spi/spi-img-spfi.c +++ b/drivers/spi/spi-img-spfi.c @@ -341,7 +341,7 @@ static int img_spfi_start_dma(struct spi_master *master, default: rxconf.src_addr = spfi->phys + SPFI_RX_8BIT_VALID_DATA; rxconf.src_addr_width = 1; - rxconf.src_maxburst = 1; + rxconf.src_maxburst = 4; } dmaengine_slave_config(spfi->rx_ch, &rxconf); @@ -368,7 +368,7 @@ static int img_spfi_start_dma(struct spi_master *master, default: txconf.dst_addr = spfi->phys + SPFI_TX_8BIT_VALID_DATA; txconf.dst_addr_width = 1; - txconf.dst_maxburst = 1; + txconf.dst_maxburst = 4; break; } dmaengine_slave_config(spfi->tx_ch, &txconf); -- cgit v0.10.2 From 4db9c4a9b27f2b3c7df0d75b16078322447dc87a Mon Sep 17 00:00:00 2001 From: Jianqun Xu Date: Wed, 24 Dec 2014 17:37:00 +0800 Subject: ASoC: rockchip: i2s: fix error defination of transmit data level According to description about "Transmit Data Level", This bit field controls the level at which a DMA request is made by the transmit logic. It is equal to the watermark level. That is, the dma_tx_req signal is generated when the number of valid data entries in the TXFIFO (TXFIFO0 if CSR=00 TXFIFO1 if CSR=01 TXFIFO2 if CSR=10 TXFIFO3 if CSR=11) is equal to or below this field value. Different to receive data level, transmit data level does not need to "-1". Signed-off-by: Jianqun Xu Signed-off-by: Mark Brown diff --git a/sound/soc/rockchip/rockchip_i2s.h b/sound/soc/rockchip/rockchip_i2s.h index 89a5d8b..93f456f 100644 --- a/sound/soc/rockchip/rockchip_i2s.h +++ b/sound/soc/rockchip/rockchip_i2s.h @@ -127,7 +127,7 @@ #define I2S_DMACR_TDE_DISABLE (0 << I2S_DMACR_TDE_SHIFT) #define I2S_DMACR_TDE_ENABLE (1 << I2S_DMACR_TDE_SHIFT) #define I2S_DMACR_TDL_SHIFT 0 -#define I2S_DMACR_TDL(x) ((x - 1) << I2S_DMACR_TDL_SHIFT) +#define I2S_DMACR_TDL(x) ((x) << I2S_DMACR_TDL_SHIFT) #define I2S_DMACR_TDL_MASK (0x1f << I2S_DMACR_TDL_SHIFT) /* -- cgit v0.10.2 From 27fd36ab135724ed9c648c57e5c0d0299bd7f67a Mon Sep 17 00:00:00 2001 From: Jianqun Xu Date: Wed, 24 Dec 2014 17:37:02 +0800 Subject: ASoC: rockchip: i2s: fix maxburst of dma data to 4 Since RK3288 DMAC's burst length only support max to 4, here set maxburst of playback and capture dma data to 4. Signed-off-by: Jianqun Xu Signed-off-by: Mark Brown diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index 26ec511..13d8507 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -454,11 +454,11 @@ static int rockchip_i2s_probe(struct platform_device *pdev) i2s->playback_dma_data.addr = res->start + I2S_TXDR; i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - i2s->playback_dma_data.maxburst = 16; + i2s->playback_dma_data.maxburst = 4; i2s->capture_dma_data.addr = res->start + I2S_RXDR; i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - i2s->capture_dma_data.maxburst = 16; + i2s->capture_dma_data.maxburst = 4; i2s->dev = &pdev->dev; dev_set_drvdata(&pdev->dev, i2s); -- cgit v0.10.2 From dddd60220f41775e634258efd1b54c6fa81ce706 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Mon, 22 Dec 2014 19:10:17 +0100 Subject: ipw2200: select CFG80211_WEXT Commit 24a0aa212ee2 ("cfg80211: make WEXT compatibility unselectable") made it impossible to depend on CFG80211_WEXT. It does still allow to select that symbol. (Yes, the commit summary is confusing.) So make IPW2200 select CFG80211_WEXT, so that the ipw2200 driver can be enabled in config again. Signed-off-by: Paul Bolle Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/ipw2x00/Kconfig b/drivers/net/wireless/ipw2x00/Kconfig index 91c0cb3..21de4fe 100644 --- a/drivers/net/wireless/ipw2x00/Kconfig +++ b/drivers/net/wireless/ipw2x00/Kconfig @@ -65,7 +65,8 @@ config IPW2100_DEBUG config IPW2200 tristate "Intel PRO/Wireless 2200BG and 2915ABG Network Connection" - depends on PCI && CFG80211 && CFG80211_WEXT + depends on PCI && CFG80211 + select CFG80211_WEXT select WIRELESS_EXT select WEXT_SPY select WEXT_PRIV -- cgit v0.10.2 From 8975842bed0840f314281c9fbf021a1d29537cf0 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 23 Dec 2014 16:48:32 +0200 Subject: brcmfmac: Do not crash if platform data is not populated The driver looks for pdata->oob_irq_supported to find out if wowl can be supported. However, not all platforms populate pdata in which case we crash the kernel because of NULL pointer dereference. Fixes: 330b4e4be937 ("brcmfmac: Add wowl support for SDIO devices.") Reported-by: Christophe Prigent Signed-off-by: Mika Westerberg Signed-off-by: Kalle Valo diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 3c06e93..9880dae 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -1070,7 +1070,7 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, */ if ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_KEEP_POWER) && ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_WAKE_SDIO_IRQ) || - (sdiodev->pdata->oob_irq_supported))) + (sdiodev->pdata && sdiodev->pdata->oob_irq_supported))) bus_if->wowl_supported = true; #endif @@ -1167,7 +1167,7 @@ static int brcmf_ops_sdio_resume(struct device *dev) struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; brcmf_dbg(SDIO, "Enter\n"); - if (sdiodev->pdata->oob_irq_supported) + if (sdiodev->pdata && sdiodev->pdata->oob_irq_supported) disable_irq_wake(sdiodev->pdata->oob_irq_nr); brcmf_sdio_wd_timer(sdiodev->bus, BRCMF_WD_POLL_MS); atomic_set(&sdiodev->suspend, false); -- cgit v0.10.2 From 8bfe8442ff20fdc2d965c197103d935a99bd3296 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 23 Dec 2014 23:10:48 +0100 Subject: Bluetooth: Fix controller configuration with HCI_QUIRK_INVALID_BDADDR When controllers set the HCI_QUIRK_INVALID_BDADDR flag, it is required by userspace to program a valid public Bluetooth device address into the controller before it can be used. After successful address configuration, the internal state changes and the controller runs the complete initialization procedure. However one small difference is that this is no longer the HCI_SETUP stage. The HCI_SETUP stage is only valid during initial controller setup. In this case the stack runs the initialization as part of the HCI_CONFIG stage. The controller version information, default name and supported commands are only stored during HCI_SETUP. While these information are static, they are not read initially when HCI_QUIRK_INVALID_BDADDR is set. So when running in HCI_CONFIG state, these information need to be updated as well. This especially impacts Bluetooth 4.1 and later controllers using extended feature pages and second event mask page. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg Cc: stable@vger.kernel.org # 3.17+ diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 39a5c8a..c03d4b0 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -242,7 +242,8 @@ static void hci_cc_read_local_name(struct hci_dev *hdev, struct sk_buff *skb) if (rp->status) return; - if (test_bit(HCI_SETUP, &hdev->dev_flags)) + if (test_bit(HCI_SETUP, &hdev->dev_flags) || + test_bit(HCI_CONFIG, &hdev->dev_flags)) memcpy(hdev->dev_name, rp->name, HCI_MAX_NAME_LENGTH); } @@ -509,7 +510,8 @@ static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb) if (rp->status) return; - if (test_bit(HCI_SETUP, &hdev->dev_flags)) { + if (test_bit(HCI_SETUP, &hdev->dev_flags) || + test_bit(HCI_CONFIG, &hdev->dev_flags)) { hdev->hci_ver = rp->hci_ver; hdev->hci_rev = __le16_to_cpu(rp->hci_rev); hdev->lmp_ver = rp->lmp_ver; @@ -528,7 +530,8 @@ static void hci_cc_read_local_commands(struct hci_dev *hdev, if (rp->status) return; - if (test_bit(HCI_SETUP, &hdev->dev_flags)) + if (test_bit(HCI_SETUP, &hdev->dev_flags) || + test_bit(HCI_CONFIG, &hdev->dev_flags)) memcpy(hdev->commands, rp->commands, sizeof(hdev->commands)); } -- cgit v0.10.2 From 6a8fc95c87110a466ee81675b41170b963f82bdb Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 24 Dec 2014 20:43:11 +0200 Subject: Bluetooth: Fix accepting connections when not using mgmt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When connectable mode is enabled (page scan on) through some non-mgmt method the HCI_CONNECTABLE flag will not be set. For backwards compatibility with user space versions not using mgmt we should not require HCI_CONNECTABLE to be set if HCI_MGMT is not set. Reported-by: Pali Rohár Tested-by: Pali Rohár Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann Cc: stable@vger.kernel.org # 3.17+ diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index c03d4b0..3f2e8b8 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2197,7 +2197,12 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) return; } - if (!test_bit(HCI_CONNECTABLE, &hdev->dev_flags) && + /* Require HCI_CONNECTABLE or a whitelist entry to accept the + * connection. These features are only touched through mgmt so + * only do the checks if HCI_MGMT is set. + */ + if (test_bit(HCI_MGMT, &hdev->dev_flags) && + !test_bit(HCI_CONNECTABLE, &hdev->dev_flags) && !hci_bdaddr_list_lookup(&hdev->whitelist, &ev->bdaddr, BDADDR_BREDR)) { hci_reject_conn(hdev, &ev->bdaddr); -- cgit v0.10.2 From 4aa6118811c09c75f508f3f070328c71292f1ce4 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 24 Dec 2014 14:41:39 +0800 Subject: openvswitch: fix odd_ptr_err.cocci warnings net/openvswitch/vport-gre.c:188:5-11: inconsistent IS_ERR and PTR_ERR, PTR_ERR on line 189 PTR_ERR should access the value just tested by IS_ERR Semantic patch information: There can be false positives in the patch case, where it is the call IS_ERR that is wrong. Generated by: scripts/coccinelle/tests/odd_ptr_err.cocci CC: Pravin B Shelar Signed-off-by: Fengguang Wu Acked-by: Pravin B Shelar Signed-off-by: David S. Miller diff --git a/net/openvswitch/vport-gre.c b/net/openvswitch/vport-gre.c index 28f54e9..d4168c4 100644 --- a/net/openvswitch/vport-gre.c +++ b/net/openvswitch/vport-gre.c @@ -186,7 +186,7 @@ static int gre_tnl_send(struct vport *vport, struct sk_buff *skb) /* Push Tunnel header. */ skb = __build_header(skb, tunnel_hlen); if (IS_ERR(skb)) { - err = PTR_ERR(rt); + err = PTR_ERR(skb); skb = NULL; goto err_free_rt; } -- cgit v0.10.2 From 93e3423e6ba4b0ddaf056ecbdf5bc46f18f41deb Mon Sep 17 00:00:00 2001 From: Rafal Redzimski Date: Fri, 19 Dec 2014 08:44:30 +0800 Subject: ALSA: hda_controller: Separate stream_tag for input and output streams. Implemented separate stream_tag assignment for input and output streams. According to hda specification stream tag must be unique throughout the input streams group, however an output stream might use a stream tag which is already in use by an input stream. This change is necessary to support HW which provides a total of more than 15 stream DMA engines which with legacy implementation causes an overflow on SDxCTL.STRM field (and the whole SDxCTL register) and as a result usage of Reserved value 0 in the SDxCTL.STRM field which confuses HDA controller. Signed-off-by: Rafal Redzimski Signed-off-by: Jayachandran B Signed-off-by: Libin Yang Reviewed-by: Vinod Koul Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 8276a74..0cfc9c8 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -1922,10 +1922,18 @@ int azx_mixer_create(struct azx *chip) EXPORT_SYMBOL_GPL(azx_mixer_create); +static bool is_input_stream(struct azx *chip, unsigned char index) +{ + return (index >= chip->capture_index_offset && + index < chip->capture_index_offset + chip->capture_streams); +} + /* initialize SD streams */ int azx_init_stream(struct azx *chip) { int i; + int in_stream_tag = 0; + int out_stream_tag = 0; /* initialize each stream (aka device) * assign the starting bdl address to each stream (device) @@ -1938,9 +1946,21 @@ int azx_init_stream(struct azx *chip) azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80); /* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */ azx_dev->sd_int_sta_mask = 1 << i; - /* stream tag: must be non-zero and unique */ azx_dev->index = i; - azx_dev->stream_tag = i + 1; + + /* stream tag must be unique throughout + * the stream direction group, + * valid values 1...15 + * use separate stream tag if the flag + * AZX_DCAPS_SEPARATE_STREAM_TAG is used + */ + if (chip->driver_caps & AZX_DCAPS_SEPARATE_STREAM_TAG) + azx_dev->stream_tag = + is_input_stream(chip, i) ? + ++in_stream_tag : + ++out_stream_tag; + else + azx_dev->stream_tag = i + 1; } return 0; diff --git a/sound/pci/hda/hda_priv.h b/sound/pci/hda/hda_priv.h index aa484fd..166e3e8 100644 --- a/sound/pci/hda/hda_priv.h +++ b/sound/pci/hda/hda_priv.h @@ -171,6 +171,7 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; #define AZX_DCAPS_I915_POWERWELL (1 << 27) /* HSW i915 powerwell support */ #define AZX_DCAPS_CORBRP_SELF_CLEAR (1 << 28) /* CORBRP clears itself after reset */ #define AZX_DCAPS_NO_MSI64 (1 << 29) /* Stick to 32-bit MSIs */ +#define AZX_DCAPS_SEPARATE_STREAM_TAG (1 << 30) /* capture and playback use separate stream tag */ enum { AZX_SNOOP_TYPE_NONE , -- cgit v0.10.2 From d6795827bd79b28fef1abdaf7e525fcca506b831 Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Fri, 19 Dec 2014 08:44:31 +0800 Subject: ALSA: hda_intel: apply the Seperate stream_tag for Skylake The total stream number of Skylake's input and output stream exceeds 15, which will cause some streams do not work because of the overflow on SDxCTL.STRM field if using the legacy stream tag allocation method. This patch uses the new stream tag allocation method by add the flag AZX_DCAPS_SEPARATE_STREAM_TAG for Skylake platform. Signed-off-by: Libin Yang Reviewed-by: Vinod Koul Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 2bf0b56..d426a0b 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -299,6 +299,9 @@ enum { AZX_DCAPS_PM_RUNTIME | AZX_DCAPS_I915_POWERWELL |\ AZX_DCAPS_SNOOP_TYPE(SCH)) +#define AZX_DCAPS_INTEL_SKYLAKE \ + (AZX_DCAPS_INTEL_PCH | AZX_DCAPS_SEPARATE_STREAM_TAG) + /* quirks for ATI SB / AMD Hudson */ #define AZX_DCAPS_PRESET_ATI_SB \ (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_SYNC_WRITE | AZX_DCAPS_POSFIX_LPIB |\ @@ -2027,7 +2030,7 @@ static const struct pci_device_id azx_ids[] = { .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, /* Sunrise Point-LP */ { PCI_DEVICE(0x8086, 0x9d70), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, + .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE }, /* Haswell */ { PCI_DEVICE(0x8086, 0x0a0c), .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL }, -- cgit v0.10.2 From 45db07382a5c78b0c43b3b0002b63757fb60e873 Mon Sep 17 00:00:00 2001 From: John David Anglin Date: Sun, 14 Dec 2014 10:49:11 -0500 Subject: parisc: fix out-of-register compiler error in ldcw inline assembler function The __ldcw macro has a problem when its argument needs to be reloaded from memory. The output memory operand and the input register operand both need to be reloaded using a register in class R1_REGS when generating 64-bit code. This fails because there's only a single register in the class. Instead, use a memory clobber. This also makes the __ldcw macro a compiler memory barrier. Signed-off-by: John David Anglin Cc: [3.13+] Signed-off-by: Helge Deller diff --git a/arch/parisc/include/asm/ldcw.h b/arch/parisc/include/asm/ldcw.h index d2d11b7..8121aa6 100644 --- a/arch/parisc/include/asm/ldcw.h +++ b/arch/parisc/include/asm/ldcw.h @@ -33,11 +33,18 @@ #endif /*!CONFIG_PA20*/ -/* LDCW, the only atomic read-write operation PA-RISC has. *sigh*. */ +/* LDCW, the only atomic read-write operation PA-RISC has. *sigh*. + We don't explicitly expose that "*a" may be written as reload + fails to find a register in class R1_REGS when "a" needs to be + reloaded when generating 64-bit PIC code. Instead, we clobber + memory to indicate to the compiler that the assembly code reads + or writes to items other than those listed in the input and output + operands. This may pessimize the code somewhat but __ldcw is + usually used within code blocks surrounded by memory barriors. */ #define __ldcw(a) ({ \ unsigned __ret; \ - __asm__ __volatile__(__LDCW " 0(%2),%0" \ - : "=r" (__ret), "+m" (*(a)) : "r" (a)); \ + __asm__ __volatile__(__LDCW " 0(%1),%0" \ + : "=r" (__ret) : "r" (a) : "memory"); \ __ret; \ }) -- cgit v0.10.2 From 2c26d34bbcc0b3f30385d5587aa232289e2eed8e Mon Sep 17 00:00:00 2001 From: Jay Vosburgh Date: Fri, 19 Dec 2014 15:32:00 -0800 Subject: net/core: Handle csum for CHECKSUM_COMPLETE VXLAN forwarding When using VXLAN tunnels and a sky2 device, I have experienced checksum failures of the following type: [ 4297.761899] eth0: hw csum failure [...] [ 4297.765223] Call Trace: [ 4297.765224] [] dump_stack+0x46/0x58 [ 4297.765235] [] netdev_rx_csum_fault+0x42/0x50 [ 4297.765238] [] ? skb_push+0x40/0x40 [ 4297.765240] [] __skb_checksum_complete+0xbc/0xd0 [ 4297.765243] [] tcp_v4_rcv+0x2e2/0x950 [ 4297.765246] [] ? ip_rcv_finish+0x360/0x360 These are reliably reproduced in a network topology of: container:eth0 == host(OVS VXLAN on VLAN) == bond0 == eth0 (sky2) -> switch When VXLAN encapsulated traffic is received from a similarly configured peer, the above warning is generated in the receive processing of the encapsulated packet. Note that the warning is associated with the container eth0. The skbs from sky2 have ip_summed set to CHECKSUM_COMPLETE, and because the packet is an encapsulated Ethernet frame, the checksum generated by the hardware includes the inner protocol and Ethernet headers. The receive code is careful to update the skb->csum, except in __dev_forward_skb, as called by dev_forward_skb. __dev_forward_skb calls eth_type_trans, which in turn calls skb_pull_inline(skb, ETH_HLEN) to skip over the Ethernet header, but does not update skb->csum when doing so. This patch resolves the problem by adding a call to skb_postpull_rcsum to update the skb->csum after the call to eth_type_trans. Signed-off-by: Jay Vosburgh Signed-off-by: David S. Miller diff --git a/net/core/dev.c b/net/core/dev.c index bd44e28..0094562 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1694,6 +1694,7 @@ int __dev_forward_skb(struct net_device *dev, struct sk_buff *skb) skb_scrub_packet(skb, true); skb->protocol = eth_type_trans(skb, dev); + skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN); return 0; } -- cgit v0.10.2 From d0e1df9cf4a98fac8508f1d73d0f3a147d6c2c85 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 23 Dec 2014 12:59:17 +0100 Subject: net: phy: micrel: use generic config_init for KSZ8021/KSZ8031 Use generic config_init callback also for KSZ8021 and KSZ8031. This has been avoided this far due to commit b838b4aced99 ("phy/micrel: KSZ8031RNL RMII clock reconfiguration bug"), which claims that the PHY becomes unresponsive if the broadcast-disable flag is set before configuring the clock mode. Turns out that the problem seemingly worked-around by the above mentioned commit was really due to a hardware-configuration issue, where the PHY was in fact strapped to address 3 rather than 0. Tested-by: Bruno Thomsen Signed-off-by: Johan Hovold Signed-off-by: David S. Miller diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index c530de1..3ad8ca7 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -88,6 +88,7 @@ struct kszphy_priv { static const struct kszphy_type ksz8021_type = { .led_mode_reg = MII_KSZPHY_CTRL_2, + .has_broadcast_disable = true, .has_rmii_ref_clk_sel = true, }; @@ -258,19 +259,6 @@ static int kszphy_config_init(struct phy_device *phydev) return 0; } -static int ksz8021_config_init(struct phy_device *phydev) -{ - int rc; - - rc = kszphy_config_init(phydev); - if (rc) - return rc; - - rc = kszphy_broadcast_disable(phydev); - - return rc < 0 ? rc : 0; -} - static int ksz9021_load_values_from_of(struct phy_device *phydev, struct device_node *of_node, u16 reg, char *field1, char *field2, @@ -584,7 +572,7 @@ static struct phy_driver ksphy_driver[] = { .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, .driver_data = &ksz8021_type, .probe = kszphy_probe, - .config_init = ksz8021_config_init, + .config_init = kszphy_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, .ack_interrupt = kszphy_ack_interrupt, @@ -601,7 +589,7 @@ static struct phy_driver ksphy_driver[] = { .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, .driver_data = &ksz8021_type, .probe = kszphy_probe, - .config_init = ksz8021_config_init, + .config_init = kszphy_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, .ack_interrupt = kszphy_ack_interrupt, -- cgit v0.10.2 From 16d8614b4f67fad7d12df34c53c9a1bab91fec49 Mon Sep 17 00:00:00 2001 From: Appana Durga Kedareswara Rao Date: Tue, 23 Dec 2014 18:07:55 +0530 Subject: net: xilinx: Remove unnecessary temac_property in the driver This property is no longer used in the code yet the code looks for it in the device tree. It does not cause an error if it's not in the tree. Signed-off-by: Kedareswara rao Appana Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h index 44b8d2b..4c9b4fa 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet.h +++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h @@ -388,7 +388,6 @@ struct axidma_bd { * @dma_err_tasklet: Tasklet structure to process Axi DMA errors * @tx_irq: Axidma TX IRQ number * @rx_irq: Axidma RX IRQ number - * @temac_type: axienet type to identify between soft and hard temac * @phy_type: Phy type to identify between MII/GMII/RGMII/SGMII/1000 Base-X * @options: AxiEthernet option word * @last_link: Phy link state in which the PHY was negotiated earlier @@ -431,7 +430,6 @@ struct axienet_local { int tx_irq; int rx_irq; - u32 temac_type; u32 phy_type; u32 options; /* Current options word */ diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index 4ea2d4e..c18a0c6 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -1555,10 +1555,6 @@ static int axienet_of_probe(struct platform_device *op) if ((be32_to_cpup(p)) >= 0x4000) lp->jumbo_support = 1; } - p = (__be32 *) of_get_property(op->dev.of_node, "xlnx,temac-type", - NULL); - if (p) - lp->temac_type = be32_to_cpup(p); p = (__be32 *) of_get_property(op->dev.of_node, "xlnx,phy-type", NULL); if (p) lp->phy_type = be32_to_cpup(p); -- cgit v0.10.2 From ef8f342b43c48035458306b5e5b3dcb949929b1d Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Tue, 23 Dec 2014 17:50:37 +0100 Subject: neigh: remove next ptr from struct neigh_table After commit d7480fd3b173 ("neigh: remove dynamic neigh table registration support"), this field is not used anymore. CC: Cong Wang Signed-off-by: Nicolas Dichtel Acked-by: Cong Wang Signed-off-by: David S. Miller diff --git a/include/net/neighbour.h b/include/net/neighbour.h index eb070b3..76f7084 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -190,7 +190,6 @@ struct neigh_hash_table { struct neigh_table { - struct neigh_table *next; int family; int entry_size; int key_len; -- cgit v0.10.2 From 6e4ab361b6f3eb41ffe63224a8b5770fc68ef710 Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Tue, 23 Dec 2014 18:47:50 +0100 Subject: net: incorrect use of init_completion fixup The second init_completion call should be a reinit_completion here. patch is against 3.18.0 linux-next Signed-off-by: Nicholas Mc Guire Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/brocade/bna/bnad_debugfs.c b/drivers/net/ethernet/brocade/bna/bnad_debugfs.c index 7d6aa8c..619083a 100644 --- a/drivers/net/ethernet/brocade/bna/bnad_debugfs.c +++ b/drivers/net/ethernet/brocade/bna/bnad_debugfs.c @@ -172,7 +172,7 @@ bnad_get_debug_drvinfo(struct bnad *bnad, void *buffer, u32 len) /* Retrieve flash partition info */ fcomp.comp_status = 0; - init_completion(&fcomp.comp); + reinit_completion(&fcomp.comp); spin_lock_irqsave(&bnad->bna_lock, flags); ret = bfa_nw_flash_get_attr(&bnad->bna.flash, &drvinfo->flash_attr, bnad_cb_completion, &fcomp); -- cgit v0.10.2 From 5f35227ea34bb616c436d9da47fc325866c428f3 Mon Sep 17 00:00:00 2001 From: Jesse Gross Date: Tue, 23 Dec 2014 22:37:26 -0800 Subject: net: Generalize ndo_gso_check to ndo_features_check GSO isn't the only offload feature with restrictions that potentially can't be expressed with the current features mechanism. Checksum is another although it's a general issue that could in theory apply to anything. Even if it may be possible to implement these restrictions in other ways, it can result in duplicate code or inefficient per-packet behavior. This generalizes ndo_gso_check so that drivers can remove any features that don't make sense for a given packet, similar to netif_skb_features(). It also converts existing driver restrictions to the new format, completing the work that was done to support tunnel protocols since the issues apply to checksums as well. By actually removing features from the set that are used to do offloading, it solves another problem with the existing interface. In these cases, GSO would run with the original set of features and not do anything because it appears that segmentation is not required. CC: Tom Herbert CC: Joe Stringer CC: Eric Dumazet CC: Hayes Wang Signed-off-by: Jesse Gross Acked-by: Tom Herbert Fixes: 04ffcb255f22 ("net: Add ndo_gso_check") Tested-by: Hayes Wang Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 9f5e387..72eef9f 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -12553,9 +12553,11 @@ static int bnx2x_get_phys_port_id(struct net_device *netdev, return 0; } -static bool bnx2x_gso_check(struct sk_buff *skb, struct net_device *dev) +static netdev_features_t bnx2x_features_check(struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features) { - return vxlan_gso_check(skb); + return vxlan_features_check(skb, features); } static const struct net_device_ops bnx2x_netdev_ops = { @@ -12589,7 +12591,7 @@ static const struct net_device_ops bnx2x_netdev_ops = { #endif .ndo_get_phys_port_id = bnx2x_get_phys_port_id, .ndo_set_vf_link_state = bnx2x_set_vf_link_state, - .ndo_gso_check = bnx2x_gso_check, + .ndo_features_check = bnx2x_features_check, }; static int bnx2x_set_coherency_mask(struct bnx2x *bp) diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 1960731..41a0a54 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -4459,9 +4459,11 @@ done: adapter->vxlan_port_count--; } -static bool be_gso_check(struct sk_buff *skb, struct net_device *dev) +static netdev_features_t be_features_check(struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features) { - return vxlan_gso_check(skb); + return vxlan_features_check(skb, features); } #endif @@ -4492,7 +4494,7 @@ static const struct net_device_ops be_netdev_ops = { #ifdef CONFIG_BE2NET_VXLAN .ndo_add_vxlan_port = be_add_vxlan_port, .ndo_del_vxlan_port = be_del_vxlan_port, - .ndo_gso_check = be_gso_check, + .ndo_features_check = be_features_check, #endif }; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 190cbd9..d0d6dc1 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -2365,9 +2365,11 @@ static void mlx4_en_del_vxlan_port(struct net_device *dev, queue_work(priv->mdev->workqueue, &priv->vxlan_del_task); } -static bool mlx4_en_gso_check(struct sk_buff *skb, struct net_device *dev) +static netdev_features_t mlx4_en_features_check(struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features) { - return vxlan_gso_check(skb); + return vxlan_features_check(skb, features); } #endif @@ -2400,7 +2402,7 @@ static const struct net_device_ops mlx4_netdev_ops = { #ifdef CONFIG_MLX4_EN_VXLAN .ndo_add_vxlan_port = mlx4_en_add_vxlan_port, .ndo_del_vxlan_port = mlx4_en_del_vxlan_port, - .ndo_gso_check = mlx4_en_gso_check, + .ndo_features_check = mlx4_en_features_check, #endif }; @@ -2434,7 +2436,7 @@ static const struct net_device_ops mlx4_netdev_ops_master = { #ifdef CONFIG_MLX4_EN_VXLAN .ndo_add_vxlan_port = mlx4_en_add_vxlan_port, .ndo_del_vxlan_port = mlx4_en_del_vxlan_port, - .ndo_gso_check = mlx4_en_gso_check, + .ndo_features_check = mlx4_en_features_check, #endif }; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index 1aa25b1..9929b97 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -505,9 +505,11 @@ static void qlcnic_del_vxlan_port(struct net_device *netdev, adapter->flags |= QLCNIC_DEL_VXLAN_PORT; } -static bool qlcnic_gso_check(struct sk_buff *skb, struct net_device *dev) +static netdev_features_t qlcnic_features_check(struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features) { - return vxlan_gso_check(skb); + return vxlan_features_check(skb, features); } #endif @@ -532,7 +534,7 @@ static const struct net_device_ops qlcnic_netdev_ops = { #ifdef CONFIG_QLCNIC_VXLAN .ndo_add_vxlan_port = qlcnic_add_vxlan_port, .ndo_del_vxlan_port = qlcnic_del_vxlan_port, - .ndo_gso_check = qlcnic_gso_check, + .ndo_features_check = qlcnic_features_check, #endif #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = qlcnic_poll_controller, diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index c31f74d..679e6e90 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1012,12 +1012,15 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev, * Callback to use for xmit over the accelerated station. This * is used in place of ndo_start_xmit on accelerated net * devices. - * bool (*ndo_gso_check) (struct sk_buff *skb, - * struct net_device *dev); + * netdev_features_t (*ndo_features_check) (struct sk_buff *skb, + * struct net_device *dev + * netdev_features_t features); * Called by core transmit path to determine if device is capable of - * performing GSO on a packet. The device returns true if it is - * able to GSO the packet, false otherwise. If the return value is - * false the stack will do software GSO. + * performing offload operations on a given packet. This is to give + * the device an opportunity to implement any restrictions that cannot + * be otherwise expressed by feature flags. The check is called with + * the set of features that the stack has calculated and it returns + * those the driver believes to be appropriate. * * int (*ndo_switch_parent_id_get)(struct net_device *dev, * struct netdev_phys_item_id *psid); @@ -1178,8 +1181,9 @@ struct net_device_ops { struct net_device *dev, void *priv); int (*ndo_get_lock_subclass)(struct net_device *dev); - bool (*ndo_gso_check) (struct sk_buff *skb, - struct net_device *dev); + netdev_features_t (*ndo_features_check) (struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features); #ifdef CONFIG_NET_SWITCHDEV int (*ndo_switch_parent_id_get)(struct net_device *dev, struct netdev_phys_item_id *psid); @@ -3611,8 +3615,6 @@ static inline bool netif_needs_gso(struct net_device *dev, struct sk_buff *skb, netdev_features_t features) { return skb_is_gso(skb) && (!skb_gso_ok(skb, features) || - (dev->netdev_ops->ndo_gso_check && - !dev->netdev_ops->ndo_gso_check(skb, dev)) || unlikely((skb->ip_summed != CHECKSUM_PARTIAL) && (skb->ip_summed != CHECKSUM_UNNECESSARY))); } diff --git a/include/net/vxlan.h b/include/net/vxlan.h index 57cccd0..903461a 100644 --- a/include/net/vxlan.h +++ b/include/net/vxlan.h @@ -1,6 +1,9 @@ #ifndef __NET_VXLAN_H #define __NET_VXLAN_H 1 +#include +#include +#include #include #include #include @@ -51,16 +54,33 @@ int vxlan_xmit_skb(struct vxlan_sock *vs, __be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df, __be16 src_port, __be16 dst_port, __be32 vni, bool xnet); -static inline bool vxlan_gso_check(struct sk_buff *skb) +static inline netdev_features_t vxlan_features_check(struct sk_buff *skb, + netdev_features_t features) { - if ((skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL) && + u8 l4_hdr = 0; + + if (!skb->encapsulation) + return features; + + switch (vlan_get_protocol(skb)) { + case htons(ETH_P_IP): + l4_hdr = ip_hdr(skb)->protocol; + break; + case htons(ETH_P_IPV6): + l4_hdr = ipv6_hdr(skb)->nexthdr; + break; + default: + return features;; + } + + if ((l4_hdr == IPPROTO_UDP) && (skb->inner_protocol_type != ENCAP_TYPE_ETHER || skb->inner_protocol != htons(ETH_P_TEB) || (skb_inner_mac_header(skb) - skb_transport_header(skb) != sizeof(struct udphdr) + sizeof(struct vxlanhdr)))) - return false; + return features & ~(NETIF_F_ALL_CSUM | NETIF_F_GSO_MASK); - return true; + return features; } /* IP header + UDP + VXLAN + Ethernet header */ diff --git a/net/core/dev.c b/net/core/dev.c index 0094562..683d493 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2563,7 +2563,7 @@ static netdev_features_t harmonize_features(struct sk_buff *skb, netdev_features_t netif_skb_features(struct sk_buff *skb) { - const struct net_device *dev = skb->dev; + struct net_device *dev = skb->dev; netdev_features_t features = dev->features; u16 gso_segs = skb_shinfo(skb)->gso_segs; __be16 protocol = skb->protocol; @@ -2571,13 +2571,20 @@ netdev_features_t netif_skb_features(struct sk_buff *skb) if (gso_segs > dev->gso_max_segs || gso_segs < dev->gso_min_segs) features &= ~NETIF_F_GSO_MASK; + /* If encapsulation offload request, verify we are testing + * hardware encapsulation features instead of standard + * features for the netdev + */ + if (skb->encapsulation) + features &= dev->hw_enc_features; + if (!vlan_tx_tag_present(skb)) { if (unlikely(protocol == htons(ETH_P_8021Q) || protocol == htons(ETH_P_8021AD))) { struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data; protocol = veh->h_vlan_encapsulated_proto; } else { - return harmonize_features(skb, features); + goto finalize; } } @@ -2595,6 +2602,11 @@ netdev_features_t netif_skb_features(struct sk_buff *skb) NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX); +finalize: + if (dev->netdev_ops->ndo_features_check) + features &= dev->netdev_ops->ndo_features_check(skb, dev, + features); + return harmonize_features(skb, features); } EXPORT_SYMBOL(netif_skb_features); @@ -2665,13 +2677,6 @@ static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device if (unlikely(!skb)) goto out_null; - /* If encapsulation offload request, verify we are testing - * hardware encapsulation features instead of standard - * features for the netdev - */ - if (skb->encapsulation) - features &= dev->hw_enc_features; - if (netif_needs_gso(dev, skb, features)) { struct sk_buff *segs; -- cgit v0.10.2 From 5242d422973d949c3b651efa049bcb1b5ef98ce9 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 26 Dec 2014 22:43:19 -0500 Subject: [regression] braino in "lustre: use is_root_inode()" In one of the places (ll_md_blocking_ast()) we had open-coded !is_root_inode(inode) and replaced it with is_root_inode(inode). See the last chunk of f76c23: - inode != inode->i_sb->s_root->d_inode) + is_root_inode(inode)) should've been + !is_root_inode(inode)) obviously... Signed-off-by: Al Viro diff --git a/drivers/staging/lustre/lustre/llite/namei.c b/drivers/staging/lustre/lustre/llite/namei.c index 1bf891b..4f361b7 100644 --- a/drivers/staging/lustre/lustre/llite/namei.c +++ b/drivers/staging/lustre/lustre/llite/namei.c @@ -264,7 +264,7 @@ int ll_md_blocking_ast(struct ldlm_lock *lock, struct ldlm_lock_desc *desc, if ((bits & (MDS_INODELOCK_LOOKUP | MDS_INODELOCK_PERM)) && inode->i_sb->s_root != NULL && - is_root_inode(inode)) + !is_root_inode(inode)) ll_invalidate_aliases(inode); iput(inode); -- cgit v0.10.2 From 02c81ab95d8718d75886d16227a10cc7774493ea Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 22 Dec 2014 18:56:35 +0100 Subject: netlink: rename netlink_unbind() to netlink_undo_bind() The new name is more expressive - this isn't a generic unbind function but rather only a little undo helper for use only in netlink_bind(). Signed-off-by: Johannes Berg Signed-off-by: David S. Miller diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 074cf3e..b4cf8ee 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1410,8 +1410,8 @@ static int netlink_realloc_groups(struct sock *sk) return err; } -static void netlink_unbind(int group, long unsigned int groups, - struct netlink_sock *nlk) +static void netlink_undo_bind(int group, long unsigned int groups, + struct netlink_sock *nlk) { int undo; @@ -1461,7 +1461,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, err = nlk->netlink_bind(group); if (!err) continue; - netlink_unbind(group, groups, nlk); + netlink_undo_bind(group, groups, nlk); return err; } } @@ -1471,7 +1471,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, netlink_insert(sk, net, nladdr->nl_pid) : netlink_autobind(sock); if (err) { - netlink_unbind(nlk->ngroups, groups, nlk); + netlink_undo_bind(nlk->ngroups, groups, nlk); return err; } } -- cgit v0.10.2 From f8403a2e47afb37bcd3b7e286996d138a116c39d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 22 Dec 2014 18:56:36 +0100 Subject: genetlink: pass only network namespace to genl_has_listeners() There's no point to force the caller to know about the internal genl_sock to use inside struct net, just have them pass the network namespace. This doesn't really change code generation since it's an inline, but makes the caller less magic - there's never any reason to pass another socket. Signed-off-by: Johannes Berg Signed-off-by: David S. Miller diff --git a/include/net/genetlink.h b/include/net/genetlink.h index af10c2c..38620da 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h @@ -395,11 +395,11 @@ static inline int genl_set_err(struct genl_family *family, struct net *net, } static inline int genl_has_listeners(struct genl_family *family, - struct sock *sk, unsigned int group) + struct net *net, unsigned int group) { if (WARN_ON_ONCE(group >= family->n_mcgrps)) return -EINVAL; group = family->mcgrp_offset + group; - return netlink_has_listeners(sk, group); + return netlink_has_listeners(net->genl_sock, group); } #endif /* __NET_GENERIC_NETLINK_H */ diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 332b5a0..4e9a5f0 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -83,8 +83,7 @@ static bool ovs_must_notify(struct genl_family *family, struct genl_info *info, unsigned int group) { return info->nlhdr->nlmsg_flags & NLM_F_ECHO || - genl_has_listeners(family, genl_info_net(info)->genl_sock, - group); + genl_has_listeners(family, genl_info_net(info), group); } static void ovs_notify(struct genl_family *family, -- cgit v0.10.2 From b10dcb3b94010e3ac3951f68789400b1665effb1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 22 Dec 2014 18:56:37 +0100 Subject: netlink: update listeners directly when removing socket The code is now confusing to read - first in one function down (netlink_remove) any group subscriptions are implicitly removed by calling __sk_del_bind_node(), but the subscriber database is only updated far later by calling netlink_update_listeners(). Move the latter call to just after removal from the list so it is easier to follow the code. This also enables moving the locking inside the kernel-socket conditional, which improves the normal socket destruction path. Signed-off-by: Johannes Berg Signed-off-by: David S. Miller diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index b4cf8ee..6a9fb7c 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1091,8 +1091,10 @@ static void netlink_remove(struct sock *sk) mutex_unlock(&nl_sk_hash_lock); netlink_table_grab(); - if (nlk_sk(sk)->subscriptions) + if (nlk_sk(sk)->subscriptions) { __sk_del_bind_node(sk); + netlink_update_listeners(sk); + } netlink_table_ungrab(); } @@ -1226,8 +1228,8 @@ static int netlink_release(struct socket *sock) module_put(nlk->module); - netlink_table_grab(); if (netlink_is_kernel(sk)) { + netlink_table_grab(); BUG_ON(nl_table[sk->sk_protocol].registered == 0); if (--nl_table[sk->sk_protocol].registered == 0) { struct listeners *old; @@ -1241,10 +1243,8 @@ static int netlink_release(struct socket *sock) nl_table[sk->sk_protocol].flags = 0; nl_table[sk->sk_protocol].registered = 0; } - } else if (nlk->subscriptions) { - netlink_update_listeners(sk); + netlink_table_ungrab(); } - netlink_table_ungrab(); kfree(nlk->groups); nlk->groups = NULL; -- cgit v0.10.2 From 7d68536bed72b09de03b07479dd707c5831b3b94 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 22 Dec 2014 18:56:38 +0100 Subject: netlink: call unbind when releasing socket Currently, netlink_unbind() is only called when the socket explicitly unbinds, which limits its usefulness (luckily there are no users of it yet anyway.) Call netlink_unbind() also when a socket is released, so it becomes possible to track listeners with this callback and without also implementing a netlink notifier (and checking netlink_has_listeners() in there.) Signed-off-by: Johannes Berg Signed-off-by: David S. Miller diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 6a9fb7c..f29b63f 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1246,6 +1246,13 @@ static int netlink_release(struct socket *sock) netlink_table_ungrab(); } + if (nlk->netlink_unbind) { + int i; + + for (i = 0; i < nlk->ngroups; i++) + if (test_bit(i, nlk->groups)) + nlk->netlink_unbind(i + 1); + } kfree(nlk->groups); nlk->groups = NULL; -- cgit v0.10.2 From c380d9a7afff0e4c2e5f3c1c2dc7d2f4214dd962 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 23 Dec 2014 20:54:40 +0100 Subject: genetlink: pass multicast bind/unbind to families In order to make the newly fixed multicast bind/unbind functionality in generic netlink, pass them down to the appropriate family. Signed-off-by: Johannes Berg Signed-off-by: David S. Miller diff --git a/include/net/genetlink.h b/include/net/genetlink.h index 38620da..3ed31e5 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h @@ -31,6 +31,9 @@ struct genl_info; * do additional, common, filtering and return an error * @post_doit: called after an operation's doit callback, it may * undo operations done by pre_doit, for example release locks + * @mcast_bind: a socket bound to the given multicast group (which + * is given as the offset into the groups array) + * @mcast_unbind: a socket was unbound from the given multicast group * @attrbuf: buffer to store parsed attributes * @family_list: family list * @mcgrps: multicast groups used by this family (private) @@ -53,6 +56,8 @@ struct genl_family { void (*post_doit)(const struct genl_ops *ops, struct sk_buff *skb, struct genl_info *info); + int (*mcast_bind)(int group); + void (*mcast_unbind)(int group); struct nlattr ** attrbuf; /* private */ const struct genl_ops * ops; /* private */ const struct genl_multicast_group *mcgrps; /* private */ diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 76393f2..05bf40b 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -983,11 +983,70 @@ static struct genl_multicast_group genl_ctrl_groups[] = { { .name = "notify", }, }; +static int genl_bind(int group) +{ + int i, err; + bool found = false; + + down_read(&cb_lock); + for (i = 0; i < GENL_FAM_TAB_SIZE; i++) { + struct genl_family *f; + + list_for_each_entry(f, genl_family_chain(i), family_list) { + if (group >= f->mcgrp_offset && + group < f->mcgrp_offset + f->n_mcgrps) { + int fam_grp = group - f->mcgrp_offset; + + if (f->mcast_bind) + err = f->mcast_bind(fam_grp); + else + err = 0; + found = true; + break; + } + } + } + up_read(&cb_lock); + + if (WARN_ON(!found)) + err = 0; + + return err; +} + +static void genl_unbind(int group) +{ + int i; + bool found = false; + + down_read(&cb_lock); + for (i = 0; i < GENL_FAM_TAB_SIZE; i++) { + struct genl_family *f; + + list_for_each_entry(f, genl_family_chain(i), family_list) { + if (group >= f->mcgrp_offset && + group < f->mcgrp_offset + f->n_mcgrps) { + int fam_grp = group - f->mcgrp_offset; + + if (f->mcast_unbind) + f->mcast_unbind(fam_grp); + found = true; + break; + } + } + } + up_read(&cb_lock); + + WARN_ON(!found); +} + static int __net_init genl_pernet_init(struct net *net) { struct netlink_kernel_cfg cfg = { .input = genl_rcv, .flags = NL_CFG_F_NONROOT_RECV, + .bind = genl_bind, + .unbind = genl_unbind, }; /* we'll bump the group number right afterwards */ -- cgit v0.10.2 From a22a9e4141474b9f314947f159817050a1db58d8 Mon Sep 17 00:00:00 2001 From: Wengang Wang Date: Tue, 23 Dec 2014 09:24:36 +0800 Subject: bonding: change error message to debug message in __bond_release_one() In __bond_release_one(), when the interface is not a slave or not a slave of "this" master, it log error message. The message actually should be a debug message matching what bond_enslave() does. Signed-off-by: Wengang Wang Acked-by: Ding Tianhong Signed-off-by: Andy Gospodarek Signed-off-by: David S. Miller diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 184c434..0dceba1 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1648,7 +1648,7 @@ static int __bond_release_one(struct net_device *bond_dev, /* slave is not a slave or master is not master of this slave */ if (!(slave_dev->flags & IFF_SLAVE) || !netdev_has_upper_dev(slave_dev, bond_dev)) { - netdev_err(bond_dev, "cannot release %s\n", + netdev_dbg(bond_dev, "cannot release %s\n", slave_dev->name); return -EINVAL; } -- cgit v0.10.2 From eb69c5bf8273edbe1c5c748fa299b5e5a08f35d6 Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Tue, 23 Dec 2014 11:29:03 +0800 Subject: ne2k-pci: Add pci_disable_device in error handling For linux-3.18.0 The driver lacks pci_disable_device in error handling code of ne2k_pci_init_one, so the device enabled by pci_enable_device is not disabled when errors occur. This patch fixes this problem. Signed-off-by: Jia-Ju Bai Signed-off-by: David S. Miller diff --git a/drivers/net/ethernet/8390/ne2k-pci.c b/drivers/net/ethernet/8390/ne2k-pci.c index 89c8d9f..57e9791 100644 --- a/drivers/net/ethernet/8390/ne2k-pci.c +++ b/drivers/net/ethernet/8390/ne2k-pci.c @@ -246,13 +246,13 @@ static int ne2k_pci_init_one(struct pci_dev *pdev, if (!ioaddr || ((pci_resource_flags (pdev, 0) & IORESOURCE_IO) == 0)) { dev_err(&pdev->dev, "no I/O resource at PCI BAR #0\n"); - return -ENODEV; + goto err_out; } if (request_region (ioaddr, NE_IO_EXTENT, DRV_NAME) == NULL) { dev_err(&pdev->dev, "I/O resource 0x%x @ 0x%lx busy\n", NE_IO_EXTENT, ioaddr); - return -EBUSY; + goto err_out; } reg0 = inb(ioaddr); @@ -392,6 +392,8 @@ err_out_free_netdev: free_netdev (dev); err_out_free_res: release_region (ioaddr, NE_IO_EXTENT); +err_out: + pci_disable_device(pdev); return -ENODEV; } -- cgit v0.10.2 From 023e2cfa36c31b0ad28c159a1bb0d61ff57334c8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 23 Dec 2014 21:00:06 +0100 Subject: netlink/genetlink: pass network namespace to bind/unbind Netlink families can exist in multiple namespaces, and for the most part multicast subscriptions are per network namespace. Thus it only makes sense to have bind/unbind notifications per network namespace. To achieve this, pass the network namespace of a given client socket to the bind/unbind functions. Also do this in generic netlink, and there also make sure that any bind for multicast groups that only exist in init_net is rejected. This isn't really a problem if it is accepted since a client in a different namespace will never receive any notifications from such a group, but it can confuse the family if not rejected (it's also possible to silently (without telling the family) accept it, but it would also have to be ignored on unbind so families that take any kind of action on bind/unbind won't do unnecessary work for invalid clients like that. Signed-off-by: Johannes Berg Signed-off-by: David S. Miller diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 9e572da..02fc86d 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -46,8 +46,8 @@ struct netlink_kernel_cfg { unsigned int flags; void (*input)(struct sk_buff *skb); struct mutex *cb_mutex; - int (*bind)(int group); - void (*unbind)(int group); + int (*bind)(struct net *net, int group); + void (*unbind)(struct net *net, int group); bool (*compare)(struct net *net, struct sock *sk); }; diff --git a/include/net/genetlink.h b/include/net/genetlink.h index 3ed31e5..8412508 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h @@ -56,8 +56,8 @@ struct genl_family { void (*post_doit)(const struct genl_ops *ops, struct sk_buff *skb, struct genl_info *info); - int (*mcast_bind)(int group); - void (*mcast_unbind)(int group); + int (*mcast_bind)(struct net *net, int group); + void (*mcast_unbind)(struct net *net, int group); struct nlattr ** attrbuf; /* private */ const struct genl_ops * ops; /* private */ const struct genl_multicast_group *mcgrps; /* private */ diff --git a/kernel/audit.c b/kernel/audit.c index f8f203e..aba9d9f 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -1100,7 +1100,7 @@ static void audit_receive(struct sk_buff *skb) } /* Run custom bind function on netlink socket group connect or bind requests. */ -static int audit_bind(int group) +static int audit_bind(struct net *net, int group) { if (!capable(CAP_AUDIT_READ)) return -EPERM; diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c index 13c2e17..cde4a67 100644 --- a/net/netfilter/nfnetlink.c +++ b/net/netfilter/nfnetlink.c @@ -463,7 +463,7 @@ static void nfnetlink_rcv(struct sk_buff *skb) } #ifdef CONFIG_MODULES -static int nfnetlink_bind(int group) +static int nfnetlink_bind(struct net *net, int group) { const struct nfnetlink_subsystem *ss; int type; diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index f29b63f..84ea76c 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1141,8 +1141,8 @@ static int netlink_create(struct net *net, struct socket *sock, int protocol, struct module *module = NULL; struct mutex *cb_mutex; struct netlink_sock *nlk; - int (*bind)(int group); - void (*unbind)(int group); + int (*bind)(struct net *net, int group); + void (*unbind)(struct net *net, int group); int err = 0; sock->state = SS_UNCONNECTED; @@ -1251,7 +1251,7 @@ static int netlink_release(struct socket *sock) for (i = 0; i < nlk->ngroups; i++) if (test_bit(i, nlk->groups)) - nlk->netlink_unbind(i + 1); + nlk->netlink_unbind(sock_net(sk), i + 1); } kfree(nlk->groups); nlk->groups = NULL; @@ -1418,8 +1418,9 @@ static int netlink_realloc_groups(struct sock *sk) } static void netlink_undo_bind(int group, long unsigned int groups, - struct netlink_sock *nlk) + struct sock *sk) { + struct netlink_sock *nlk = nlk_sk(sk); int undo; if (!nlk->netlink_unbind) @@ -1427,7 +1428,7 @@ static void netlink_undo_bind(int group, long unsigned int groups, for (undo = 0; undo < group; undo++) if (test_bit(undo, &groups)) - nlk->netlink_unbind(undo); + nlk->netlink_unbind(sock_net(sk), undo); } static int netlink_bind(struct socket *sock, struct sockaddr *addr, @@ -1465,10 +1466,10 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, for (group = 0; group < nlk->ngroups; group++) { if (!test_bit(group, &groups)) continue; - err = nlk->netlink_bind(group); + err = nlk->netlink_bind(net, group); if (!err) continue; - netlink_undo_bind(group, groups, nlk); + netlink_undo_bind(group, groups, sk); return err; } } @@ -1478,7 +1479,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, netlink_insert(sk, net, nladdr->nl_pid) : netlink_autobind(sock); if (err) { - netlink_undo_bind(nlk->ngroups, groups, nlk); + netlink_undo_bind(nlk->ngroups, groups, sk); return err; } } @@ -2129,7 +2130,7 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname, if (!val || val - 1 >= nlk->ngroups) return -EINVAL; if (optname == NETLINK_ADD_MEMBERSHIP && nlk->netlink_bind) { - err = nlk->netlink_bind(val); + err = nlk->netlink_bind(sock_net(sk), val); if (err) return err; } @@ -2138,7 +2139,7 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname, optname == NETLINK_ADD_MEMBERSHIP); netlink_table_ungrab(); if (optname == NETLINK_DROP_MEMBERSHIP && nlk->netlink_unbind) - nlk->netlink_unbind(val); + nlk->netlink_unbind(sock_net(sk), val); err = 0; break; diff --git a/net/netlink/af_netlink.h b/net/netlink/af_netlink.h index b20a173..f123a88 100644 --- a/net/netlink/af_netlink.h +++ b/net/netlink/af_netlink.h @@ -39,8 +39,8 @@ struct netlink_sock { struct mutex *cb_mutex; struct mutex cb_def_mutex; void (*netlink_rcv)(struct sk_buff *skb); - int (*netlink_bind)(int group); - void (*netlink_unbind)(int group); + int (*netlink_bind)(struct net *net, int group); + void (*netlink_unbind)(struct net *net, int group); struct module *module; #ifdef CONFIG_NETLINK_MMAP struct mutex pg_vec_lock; @@ -65,8 +65,8 @@ struct netlink_table { unsigned int groups; struct mutex *cb_mutex; struct module *module; - int (*bind)(int group); - void (*unbind)(int group); + int (*bind)(struct net *net, int group); + void (*unbind)(struct net *net, int group); bool (*compare)(struct net *net, struct sock *sock); int registered; }; diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 05bf40b..91566ed 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -983,7 +983,7 @@ static struct genl_multicast_group genl_ctrl_groups[] = { { .name = "notify", }, }; -static int genl_bind(int group) +static int genl_bind(struct net *net, int group) { int i, err; bool found = false; @@ -997,8 +997,10 @@ static int genl_bind(int group) group < f->mcgrp_offset + f->n_mcgrps) { int fam_grp = group - f->mcgrp_offset; - if (f->mcast_bind) - err = f->mcast_bind(fam_grp); + if (!f->netnsok && net != &init_net) + err = -ENOENT; + else if (f->mcast_bind) + err = f->mcast_bind(net, fam_grp); else err = 0; found = true; @@ -1014,7 +1016,7 @@ static int genl_bind(int group) return err; } -static void genl_unbind(int group) +static void genl_unbind(struct net *net, int group) { int i; bool found = false; @@ -1029,7 +1031,7 @@ static void genl_unbind(int group) int fam_grp = group - f->mcgrp_offset; if (f->mcast_unbind) - f->mcast_unbind(fam_grp); + f->mcast_unbind(net, fam_grp); found = true; break; } -- cgit v0.10.2 From baa035227b2e8b4bfba8f6176dc06c60477f1634 Mon Sep 17 00:00:00 2001 From: Tiejun Chen Date: Tue, 23 Dec 2014 16:21:11 +0800 Subject: kvm: x86: vmx: reorder some msr writing The commit 34a1cd60d17f, "x86: vmx: move some vmx setting from vmx_init() to hardware_setup()", tried to refactor some codes specific to vmx hardware setting into hardware_setup(), but some msr writing should depend on our previous setting condition like enable_apicv, enable_ept and so on. Reported-by: Jamie Heilman Tested-by: Jamie Heilman Signed-off-by: Tiejun Chen Signed-off-by: Paolo Bonzini diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index feb852b..d4c58d8 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -5840,53 +5840,10 @@ static __init int hardware_setup(void) memset(vmx_msr_bitmap_legacy, 0xff, PAGE_SIZE); memset(vmx_msr_bitmap_longmode, 0xff, PAGE_SIZE); - vmx_disable_intercept_for_msr(MSR_FS_BASE, false); - vmx_disable_intercept_for_msr(MSR_GS_BASE, false); - vmx_disable_intercept_for_msr(MSR_KERNEL_GS_BASE, true); - vmx_disable_intercept_for_msr(MSR_IA32_SYSENTER_CS, false); - vmx_disable_intercept_for_msr(MSR_IA32_SYSENTER_ESP, false); - vmx_disable_intercept_for_msr(MSR_IA32_SYSENTER_EIP, false); - vmx_disable_intercept_for_msr(MSR_IA32_BNDCFGS, true); - - memcpy(vmx_msr_bitmap_legacy_x2apic, - vmx_msr_bitmap_legacy, PAGE_SIZE); - memcpy(vmx_msr_bitmap_longmode_x2apic, - vmx_msr_bitmap_longmode, PAGE_SIZE); - - if (enable_apicv) { - for (msr = 0x800; msr <= 0x8ff; msr++) - vmx_disable_intercept_msr_read_x2apic(msr); - - /* According SDM, in x2apic mode, the whole id reg is used. - * But in KVM, it only use the highest eight bits. Need to - * intercept it */ - vmx_enable_intercept_msr_read_x2apic(0x802); - /* TMCCT */ - vmx_enable_intercept_msr_read_x2apic(0x839); - /* TPR */ - vmx_disable_intercept_msr_write_x2apic(0x808); - /* EOI */ - vmx_disable_intercept_msr_write_x2apic(0x80b); - /* SELF-IPI */ - vmx_disable_intercept_msr_write_x2apic(0x83f); - } - - if (enable_ept) { - kvm_mmu_set_mask_ptes(0ull, - (enable_ept_ad_bits) ? VMX_EPT_ACCESS_BIT : 0ull, - (enable_ept_ad_bits) ? VMX_EPT_DIRTY_BIT : 0ull, - 0ull, VMX_EPT_EXECUTABLE_MASK); - ept_set_mmio_spte_mask(); - kvm_enable_tdp(); - } else - kvm_disable_tdp(); - - update_ple_window_actual_max(); - if (setup_vmcs_config(&vmcs_config) < 0) { r = -EIO; goto out7; - } + } if (boot_cpu_has(X86_FEATURE_NX)) kvm_enable_efer_bits(EFER_NX); @@ -5945,6 +5902,49 @@ static __init int hardware_setup(void) if (nested) nested_vmx_setup_ctls_msrs(); + vmx_disable_intercept_for_msr(MSR_FS_BASE, false); + vmx_disable_intercept_for_msr(MSR_GS_BASE, false); + vmx_disable_intercept_for_msr(MSR_KERNEL_GS_BASE, true); + vmx_disable_intercept_for_msr(MSR_IA32_SYSENTER_CS, false); + vmx_disable_intercept_for_msr(MSR_IA32_SYSENTER_ESP, false); + vmx_disable_intercept_for_msr(MSR_IA32_SYSENTER_EIP, false); + vmx_disable_intercept_for_msr(MSR_IA32_BNDCFGS, true); + + memcpy(vmx_msr_bitmap_legacy_x2apic, + vmx_msr_bitmap_legacy, PAGE_SIZE); + memcpy(vmx_msr_bitmap_longmode_x2apic, + vmx_msr_bitmap_longmode, PAGE_SIZE); + + if (enable_apicv) { + for (msr = 0x800; msr <= 0x8ff; msr++) + vmx_disable_intercept_msr_read_x2apic(msr); + + /* According SDM, in x2apic mode, the whole id reg is used. + * But in KVM, it only use the highest eight bits. Need to + * intercept it */ + vmx_enable_intercept_msr_read_x2apic(0x802); + /* TMCCT */ + vmx_enable_intercept_msr_read_x2apic(0x839); + /* TPR */ + vmx_disable_intercept_msr_write_x2apic(0x808); + /* EOI */ + vmx_disable_intercept_msr_write_x2apic(0x80b); + /* SELF-IPI */ + vmx_disable_intercept_msr_write_x2apic(0x83f); + } + + if (enable_ept) { + kvm_mmu_set_mask_ptes(0ull, + (enable_ept_ad_bits) ? VMX_EPT_ACCESS_BIT : 0ull, + (enable_ept_ad_bits) ? VMX_EPT_DIRTY_BIT : 0ull, + 0ull, VMX_EPT_EXECUTABLE_MASK); + ept_set_mmio_spte_mask(); + kvm_enable_tdp(); + } else + kvm_disable_tdp(); + + update_ple_window_actual_max(); + return alloc_kvm_area(); out7: -- cgit v0.10.2 From a629df7eadffb03e6ce4a8616e62ea29fdf69b6b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 22 Dec 2014 10:43:39 +0100 Subject: kvm: x86: drop severity of "generation wraparound" message Since most virtual machines raise this message once, it is a bit annoying. Make it KERN_DEBUG severity. Cc: stable@vger.kernel.org Fixes: 7a2e8aaf0f6873b47bc2347f216ea5b0e4c258ab Signed-off-by: Paolo Bonzini diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index 10fbed1..f83fc6c 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -4448,7 +4448,7 @@ void kvm_mmu_invalidate_mmio_sptes(struct kvm *kvm) * zap all shadow pages. */ if (unlikely(kvm_current_mmio_generation(kvm) == 0)) { - printk_ratelimited(KERN_INFO "kvm: zapping shadow pages for mmio generation wraparound\n"); + printk_ratelimited(KERN_DEBUG "kvm: zapping shadow pages for mmio generation wraparound\n"); kvm_mmu_invalidate_zap_all_pages(kvm); } } -- cgit v0.10.2 From cceeb872d60f77f9305d9e138c7d0acee1d60038 Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Fri, 26 Dec 2014 15:41:42 -0800 Subject: Input: hil_kbd - fix incorrect use of init_completion The successive init_completion calls should be reinit_completion calls. Signed-off-by: Nicholas Mc Guire Acked-by: Helge Deller Tested-by: Helge Deller Signed-off-by: Dmitry Torokhov diff --git a/drivers/input/keyboard/hil_kbd.c b/drivers/input/keyboard/hil_kbd.c index 610a8af..5b152f2 100644 --- a/drivers/input/keyboard/hil_kbd.c +++ b/drivers/input/keyboard/hil_kbd.c @@ -473,7 +473,7 @@ static int hil_dev_connect(struct serio *serio, struct serio_driver *drv) if (error) goto bail1; - init_completion(&dev->cmd_done); + reinit_completion(&dev->cmd_done); serio_write(serio, 0); serio_write(serio, 0); serio_write(serio, HIL_PKT_CMD >> 8); @@ -482,7 +482,7 @@ static int hil_dev_connect(struct serio *serio, struct serio_driver *drv) if (error) goto bail1; - init_completion(&dev->cmd_done); + reinit_completion(&dev->cmd_done); serio_write(serio, 0); serio_write(serio, 0); serio_write(serio, HIL_PKT_CMD >> 8); @@ -491,7 +491,7 @@ static int hil_dev_connect(struct serio *serio, struct serio_driver *drv) if (error) goto bail1; - init_completion(&dev->cmd_done); + reinit_completion(&dev->cmd_done); serio_write(serio, 0); serio_write(serio, 0); serio_write(serio, HIL_PKT_CMD >> 8); -- cgit v0.10.2 From efbeec7098eee2b3d2359d0cc24bbba0436e7f21 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 27 Dec 2014 18:01:00 +0100 Subject: kvm: fix sorting of memslots with base_gfn == 0 Before commit 0e60b0799fed (kvm: change memslot sorting rule from size to GFN, 2014-12-01), the memslots' sorting key was npages, meaning that a valid memslot couldn't have its sorting key equal to zero. On the other hand, a valid memslot can have base_gfn == 0, and invalid memslots are identified by base_gfn == npages == 0. Because of this, commit 0e60b0799fed broke the invariant that invalid memslots are at the end of the mslots array. When a memslot with base_gfn == 0 was created, any invalid memslot before it were left in place. This can be fixed by changing the insertion to use a ">=" comparison instead of "<=", but some care is needed to avoid breaking the case of deleting a memslot; see the comment in update_memslots. Thanks to Tiejun Chen for posting an initial patch for this bug. Reported-by: Jamie Heilman Reported-by: Andy Lutomirski Tested-by: Jamie Heilman Signed-off-by: Paolo Bonzini diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index f528343..050974c 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -687,11 +687,23 @@ static void update_memslots(struct kvm_memslots *slots, slots->id_to_index[mslots[i].id] = i; i++; } - while (i > 0 && - new->base_gfn > mslots[i - 1].base_gfn) { - mslots[i] = mslots[i - 1]; - slots->id_to_index[mslots[i].id] = i; - i--; + + /* + * The ">=" is needed when creating a slot with base_gfn == 0, + * so that it moves before all those with base_gfn == npages == 0. + * + * On the other hand, if new->npages is zero, the above loop has + * already left i pointing to the beginning of the empty part of + * mslots, and the ">=" would move the hole backwards in this + * case---which is wrong. So skip the loop when deleting a slot. + */ + if (new->npages) { + while (i > 0 && + new->base_gfn >= mslots[i - 1].base_gfn) { + mslots[i] = mslots[i - 1]; + slots->id_to_index[mslots[i].id] = i; + i--; + } } mslots[i] = *new; -- cgit v0.10.2 From dbaff30940d6ef9bfa5f1f0c819cf3344ed3129f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 27 Dec 2014 21:08:16 +0100 Subject: kvm: warn on more invariant breakage Modifying a non-existent slot is not allowed. Also check that the first loop doesn't move a deleted slot beyond the used part of the mslots array. Signed-off-by: Paolo Bonzini diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 050974c..1cc6e2e 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -671,6 +671,7 @@ static void update_memslots(struct kvm_memslots *slots, WARN_ON(mslots[i].id != id); if (!new->npages) { + WARN_ON(!mslots[i].npages); new->base_gfn = 0; if (mslots[i].npages) slots->used_slots--; @@ -704,7 +705,8 @@ static void update_memslots(struct kvm_memslots *slots, slots->id_to_index[mslots[i].id] = i; i--; } - } + } else + WARN_ON_ONCE(i != slots->used_slots); mslots[i] = *new; slots->id_to_index[mslots[i].id] = i; -- cgit v0.10.2 From b7392d2247cfe6771f95d256374f1a8e6a6f48d6 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 28 Dec 2014 16:49:37 -0800 Subject: Linux 3.19-rc2 diff --git a/Makefile b/Makefile index b1c3254..ef748e1 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 3 PATCHLEVEL = 19 SUBLEVEL = 0 -EXTRAVERSION = -rc1 +EXTRAVERSION = -rc2 NAME = Diseased Newt # *DOCUMENTATION* -- cgit v0.10.2 From 1e5d0fdb5b30827141843d69eaddbb4c607fc679 Mon Sep 17 00:00:00 2001 From: Pranith Kumar Date: Sun, 21 Dec 2014 08:59:32 -0500 Subject: powerpc: Wire up sys_execveat() syscall Wire up sys_execveat(). This passes the selftests for the system call. Check success of execveat(3, '../execveat', 0)... [OK] Check success of execveat(5, 'execveat', 0)... [OK] Check success of execveat(6, 'execveat', 0)... [OK] Check success of execveat(-100, '/home/pranith/linux/...ftests/exec/execveat', 0)... [OK] Check success of execveat(99, '/home/pranith/linux/...ftests/exec/execveat', 0)... [OK] Check success of execveat(8, '', 4096)... [OK] Check success of execveat(17, '', 4096)... [OK] Check success of execveat(9, '', 4096)... [OK] Check success of execveat(14, '', 4096)... [OK] Check success of execveat(14, '', 4096)... [OK] Check success of execveat(15, '', 4096)... [OK] Check failure of execveat(8, '', 0) with ENOENT... [OK] Check failure of execveat(8, '(null)', 4096) with EFAULT... [OK] Check success of execveat(5, 'execveat.symlink', 0)... [OK] Check success of execveat(6, 'execveat.symlink', 0)... [OK] Check success of execveat(-100, '/home/pranith/linux/...xec/execveat.symlink', 0)... [OK] Check success of execveat(10, '', 4096)... [OK] Check success of execveat(10, '', 4352)... [OK] Check failure of execveat(5, 'execveat.symlink', 256) with ELOOP... [OK] Check failure of execveat(6, 'execveat.symlink', 256) with ELOOP... [OK] Check failure of execveat(-100, '/home/pranith/linux/tools/testing/selftests/exec/execveat.symlink', 256) with ELOOP... [OK] Check success of execveat(3, '../script', 0)... [OK] Check success of execveat(5, 'script', 0)... [OK] Check success of execveat(6, 'script', 0)... [OK] Check success of execveat(-100, '/home/pranith/linux/...elftests/exec/script', 0)... [OK] Check success of execveat(13, '', 4096)... [OK] Check success of execveat(13, '', 4352)... [OK] Check failure of execveat(18, '', 4096) with ENOENT... [OK] Check failure of execveat(7, 'script', 0) with ENOENT... [OK] Check success of execveat(16, '', 4096)... [OK] Check success of execveat(16, '', 4096)... [OK] Check success of execveat(4, '../script', 0)... [OK] Check success of execveat(4, 'script', 0)... [OK] Check success of execveat(4, '../script', 0)... [OK] Check failure of execveat(4, 'script', 0) with ENOENT... [OK] Check failure of execveat(5, 'execveat', 65535) with EINVAL... [OK] Check failure of execveat(5, 'no-such-file', 0) with ENOENT... [OK] Check failure of execveat(6, 'no-such-file', 0) with ENOENT... [OK] Check failure of execveat(-100, 'no-such-file', 0) with ENOENT... [OK] Check failure of execveat(5, '', 4096) with EACCES... [OK] Check failure of execveat(5, 'Makefile', 0) with EACCES... [OK] Check failure of execveat(11, '', 4096) with EACCES... [OK] Check failure of execveat(12, '', 4096) with EACCES... [OK] Check failure of execveat(99, '', 4096) with EBADF... [OK] Check failure of execveat(99, 'execveat', 0) with EBADF... [OK] Check failure of execveat(8, 'execveat', 0) with ENOTDIR... [OK] Invoke copy of 'execveat' via filename of length 4093: Check success of execveat(19, '', 4096)... [OK] Check success of execveat(5, 'xxxxxxxxxxxxxxxxxxxx...yyyyyyyyyyyyyyyyyyyy', 0)... [OK] Invoke copy of 'script' via filename of length 4093: Check success of execveat(20, '', 4096)... [OK] /bin/sh: 0: Can't open /dev/fd/5/xxxxxxx(... a long line of x's and y's, 0)... [OK] Check success of execveat(5, 'xxxxxxxxxxxxxxxxxxxx...yyyyyyyyyyyyyyyyyyyy', 0)... [OK] Tested on a 32-bit powerpc system. Signed-off-by: Pranith Kumar Signed-off-by: Michael Ellerman diff --git a/arch/powerpc/include/asm/systbl.h b/arch/powerpc/include/asm/systbl.h index ce9577d..91062ee 100644 --- a/arch/powerpc/include/asm/systbl.h +++ b/arch/powerpc/include/asm/systbl.h @@ -366,3 +366,4 @@ SYSCALL_SPU(seccomp) SYSCALL_SPU(getrandom) SYSCALL_SPU(memfd_create) SYSCALL_SPU(bpf) +COMPAT_SYS(execveat) diff --git a/arch/powerpc/include/asm/unistd.h b/arch/powerpc/include/asm/unistd.h index e0da021..36b79c3 100644 --- a/arch/powerpc/include/asm/unistd.h +++ b/arch/powerpc/include/asm/unistd.h @@ -12,7 +12,7 @@ #include -#define __NR_syscalls 362 +#define __NR_syscalls 363 #define __NR__exit __NR_exit #define NR_syscalls __NR_syscalls diff --git a/arch/powerpc/include/uapi/asm/unistd.h b/arch/powerpc/include/uapi/asm/unistd.h index f55351f..ef5b5b1 100644 --- a/arch/powerpc/include/uapi/asm/unistd.h +++ b/arch/powerpc/include/uapi/asm/unistd.h @@ -384,5 +384,6 @@ #define __NR_getrandom 359 #define __NR_memfd_create 360 #define __NR_bpf 361 +#define __NR_execveat 362 #endif /* _UAPI_ASM_POWERPC_UNISTD_H_ */ -- cgit v0.10.2 From c1caae3de46a072d0855729aed6e793e536a4a55 Mon Sep 17 00:00:00 2001 From: Hari Bathini Date: Thu, 18 Dec 2014 23:36:55 +0530 Subject: powerpc/kdump: Ignore failure in enabling big endian exception during crash In LE kernel, we currently have a hack for kexec that resets the exception endian before starting a new kernel as the kernel that is loaded could be a big endian or a little endian kernel. In kdump case, resetting exception endian fails when one or more cpus is disabled. But we can ignore the failure and still go ahead, as in most cases crashkernel will be of same endianess as primary kernel and reseting endianess is not even needed in those cases. This patch adds a new inline function to say if this is kdump path. This function is used at places where such a check is needed. Signed-off-by: Hari Bathini [mpe: Rename to kdump_in_progress(), use bool, and edit comment] Signed-off-by: Michael Ellerman diff --git a/arch/powerpc/include/asm/kexec.h b/arch/powerpc/include/asm/kexec.h index 19c36cb..a46f5f4 100644 --- a/arch/powerpc/include/asm/kexec.h +++ b/arch/powerpc/include/asm/kexec.h @@ -86,6 +86,11 @@ extern int overlaps_crashkernel(unsigned long start, unsigned long size); extern void reserve_crashkernel(void); extern void machine_kexec_mask_interrupts(void); +static inline bool kdump_in_progress(void) +{ + return crashing_cpu >= 0; +} + #else /* !CONFIG_KEXEC */ static inline void crash_kexec_secondary(struct pt_regs *regs) { } @@ -106,6 +111,11 @@ static inline int crash_shutdown_unregister(crash_shutdown_t handler) return 0; } +static inline bool kdump_in_progress(void) +{ + return false; +} + #endif /* CONFIG_KEXEC */ #endif /* ! __ASSEMBLY__ */ #endif /* __KERNEL__ */ diff --git a/arch/powerpc/kernel/machine_kexec_64.c b/arch/powerpc/kernel/machine_kexec_64.c index 879b3aa..f96d1ec 100644 --- a/arch/powerpc/kernel/machine_kexec_64.c +++ b/arch/powerpc/kernel/machine_kexec_64.c @@ -330,7 +330,7 @@ void default_machine_kexec(struct kimage *image) * using debugger IPI. */ - if (crashing_cpu == -1) + if (!kdump_in_progress()) kexec_prepare_cpus(); pr_debug("kexec: Starting switchover sequence.\n"); diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c index 469751d..b5682fd 100644 --- a/arch/powerpc/platforms/pseries/lpar.c +++ b/arch/powerpc/platforms/pseries/lpar.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include "pseries.h" @@ -267,8 +268,13 @@ static void pSeries_lpar_hptab_clear(void) * out to the user, but at least this will stop us from * continuing on further and creating an even more * difficult to debug situation. + * + * There is a known problem when kdump'ing, if cpus are offline + * the above call will fail. Rather than panicking again, keep + * going and hope the kdump kernel is also little endian, which + * it usually is. */ - if (rc) + if (rc && !kdump_in_progress()) panic("Could not enable big endian exceptions"); } #endif -- cgit v0.10.2 From 1be6f10f6f9caade3a053938cb80a2eed237e262 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Mon, 29 Dec 2014 15:47:05 +1100 Subject: Revert "powerpc: Secondary CPUs must set cpu_callin_map after setting active and online" This reverts commit 7c5c92ed56d932b2c19c3f8aea86369509407d33. Although this did fix the bug it was aimed at, it also broke secondary startup on platforms that use give/take_timebase(). Unfortunately we didn't detect that while it was in next. Signed-off-by: Michael Ellerman diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c index 8ec017c..8b2d2dc 100644 --- a/arch/powerpc/kernel/smp.c +++ b/arch/powerpc/kernel/smp.c @@ -700,6 +700,7 @@ void start_secondary(void *unused) smp_store_cpu_info(cpu); set_dec(tb_ticks_per_jiffy); preempt_disable(); + cpu_callin_map[cpu] = 1; if (smp_ops->setup_cpu) smp_ops->setup_cpu(cpu); @@ -738,14 +739,6 @@ void start_secondary(void *unused) notify_cpu_starting(cpu); set_cpu_online(cpu, true); - /* - * CPU must be marked active and online before we signal back to the - * master, because the scheduler needs to see the cpu_online and - * cpu_active bits set. - */ - smp_wmb(); - cpu_callin_map[cpu] = 1; - local_irq_enable(); cpu_startup_entry(CPUHP_ONLINE); -- cgit v0.10.2 From 1616cf01a4455501836fee506ed340baa571b366 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Thu, 11 Dec 2014 16:05:09 +0200 Subject: OMAPDSS: HDMI: remove double initializer entries HDMI hardware parameters structs for OMAP4 and OMAP5 contained two initializers for 'clkdco_max'. The first one was a remnant with wrong value. Remove the extra initializer entries. Signed-off-by: Tomi Valkeinen diff --git a/drivers/video/fbdev/omap2/dss/hdmi_pll.c b/drivers/video/fbdev/omap2/dss/hdmi_pll.c index 87accdb..ac83ef5 100644 --- a/drivers/video/fbdev/omap2/dss/hdmi_pll.c +++ b/drivers/video/fbdev/omap2/dss/hdmi_pll.c @@ -132,7 +132,6 @@ static const struct dss_pll_hw dss_omap4_hdmi_pll_hw = { .mX_max = 127, .fint_min = 500000, .fint_max = 2500000, - .clkdco_max = 1800000000, .clkdco_min = 500000000, .clkdco_low = 1000000000, @@ -156,7 +155,6 @@ static const struct dss_pll_hw dss_omap5_hdmi_pll_hw = { .mX_max = 127, .fint_min = 620000, .fint_max = 2500000, - .clkdco_max = 1800000000, .clkdco_min = 750000000, .clkdco_low = 1500000000, -- cgit v0.10.2 From 811174f45f5c586569f0574ef7ff8904d5b05420 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 17 Dec 2014 02:54:42 +0300 Subject: OMAPDSS: pll: NULL dereference in error handling The regulator_disable() doesn't accept NULL pointers. Signed-off-by: Dan Carpenter Signed-off-by: Tomi Valkeinen diff --git a/drivers/video/fbdev/omap2/dss/pll.c b/drivers/video/fbdev/omap2/dss/pll.c index 50bc62c5..335ffac 100644 --- a/drivers/video/fbdev/omap2/dss/pll.c +++ b/drivers/video/fbdev/omap2/dss/pll.c @@ -97,7 +97,8 @@ int dss_pll_enable(struct dss_pll *pll) return 0; err_enable: - regulator_disable(pll->regulator); + if (pll->regulator) + regulator_disable(pll->regulator); err_reg: clk_disable_unprepare(pll->clkin); return r; -- cgit v0.10.2 From 92b004d1aa9f367c372511ca0330f58216b25703 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Thu, 18 Dec 2014 13:40:06 +0200 Subject: video/logo: prevent use of logos after they have been freed If the probe of an fb driver has been deferred due to missing dependencies, and the probe is later ran when a module is loaded, the fbdev framework will try to find a logo to use. However, the logos are __initdata, and have already been freed. This causes sometimes page faults, if the logo memory is not mapped, sometimes other random crashes as the logo data is invalid, and sometimes nothing, if the fbdev decides to reject the logo (e.g. the random value depicting the logo's height is too big). This patch adds a late_initcall function to mark the logos as freed. In reality the logos are freed later, and fbdev probe may be ran between this late_initcall and the freeing of the logos. In that case we will miss drawing the logo, even if it would be possible. Signed-off-by: Tomi Valkeinen Cc: stable@vger.kernel.org diff --git a/drivers/video/logo/logo.c b/drivers/video/logo/logo.c index 940cd19..10fbfd8 100644 --- a/drivers/video/logo/logo.c +++ b/drivers/video/logo/logo.c @@ -21,6 +21,21 @@ static bool nologo; module_param(nologo, bool, 0); MODULE_PARM_DESC(nologo, "Disables startup logo"); +/* + * Logos are located in the initdata, and will be freed in kernel_init. + * Use late_init to mark the logos as freed to prevent any further use. + */ + +static bool logos_freed; + +static int __init fb_logo_late_init(void) +{ + logos_freed = true; + return 0; +} + +late_initcall(fb_logo_late_init); + /* logo's are marked __initdata. Use __init_refok to tell * modpost that it is intended that this function uses data * marked __initdata. @@ -29,7 +44,7 @@ const struct linux_logo * __init_refok fb_find_logo(int depth) { const struct linux_logo *logo = NULL; - if (nologo) + if (nologo || logos_freed) return NULL; if (depth >= 1) { -- cgit v0.10.2 From b28e0506fafd9c987bba7a6a71ea02a37fcabdea Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 25 Dec 2014 16:03:27 +0200 Subject: virtio_ring: document alignment requirements Host needs to know vring element alignment requirements: simply doing alignof on structures doesn't work reliably: on some platforms gcc has alignof(uint32_t) == 2. Add macros for alignment as specified in virtio 1.0 cs01, export them to userspace as well. Acked-by: Rusty Russell Signed-off-by: Michael S. Tsirkin diff --git a/include/uapi/linux/virtio_ring.h b/include/uapi/linux/virtio_ring.h index 61c818a..a3318f3 100644 --- a/include/uapi/linux/virtio_ring.h +++ b/include/uapi/linux/virtio_ring.h @@ -101,6 +101,13 @@ struct vring { struct vring_used *used; }; +/* Alignment requirements for vring elements. + * When using pre-virtio 1.0 layout, these fall out naturally. + */ +#define VRING_AVAIL_ALIGN_SIZE 2 +#define VRING_USED_ALIGN_SIZE 4 +#define VRING_DESC_ALIGN_SIZE 16 + /* The standard layout for the ring is a continuous chunk of memory which looks * like this. We assume num is a power of 2. * -- cgit v0.10.2 From 5d9a07b0de512b77bf28d2401e5fe3351f00a240 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Sun, 21 Dec 2014 01:00:23 +0200 Subject: vhost: relax used address alignment virtio 1.0 only requires used address to be 4 byte aligned, vhost required 8 bytes (size of vring_used_elem). Fix up vhost to match that. Additionally, while vhost correctly requires 8 byte alignment for log, it's unconnected to used ring: it's a consequence that log has u64 entries. Tweak code to make that clearer. Signed-off-by: Michael S. Tsirkin diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c index ed71b53..cb807d0 100644 --- a/drivers/vhost/vhost.c +++ b/drivers/vhost/vhost.c @@ -713,9 +713,13 @@ long vhost_vring_ioctl(struct vhost_dev *d, int ioctl, void __user *argp) r = -EFAULT; break; } - if ((a.avail_user_addr & (sizeof *vq->avail->ring - 1)) || - (a.used_user_addr & (sizeof *vq->used->ring - 1)) || - (a.log_guest_addr & (sizeof *vq->used->ring - 1))) { + + /* Make sure it's safe to cast pointers to vring types. */ + BUILD_BUG_ON(__alignof__ *vq->avail > VRING_AVAIL_ALIGN_SIZE); + BUILD_BUG_ON(__alignof__ *vq->used > VRING_USED_ALIGN_SIZE); + if ((a.avail_user_addr & (VRING_AVAIL_ALIGN_SIZE - 1)) || + (a.used_user_addr & (VRING_USED_ALIGN_SIZE - 1)) || + (a.log_guest_addr & (sizeof(u64) - 1))) { r = -EINVAL; break; } -- cgit v0.10.2 From 1c2d26e379fc8d37d55befd8589c4c252186ee58 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Wed, 17 Dec 2014 19:32:06 +0100 Subject: mmc: core: stop trying to switch width when only one bit is supported mmc_select_bus_width() will try to switch to MMC_BUS_WIDTH_4 even if MMC_CAP_4_BIT_DATA and MMC_CAP_8_BIT_DATA are not set in host->caps. Return as soon as possible when those flags are not set Fixes: 577fb13199b1 (mmc: rework selection of bus speed mode) Signed-off-by: Alexandre Belloni Cc: # 3.17 Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 02ad792..7466ce0 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -886,7 +886,7 @@ static int mmc_select_bus_width(struct mmc_card *card) unsigned idx, bus_width = 0; int err = 0; - if (!mmc_can_ext_csd(card) && + if (!mmc_can_ext_csd(card) || !(host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) return 0; -- cgit v0.10.2 From 45f87de57f8fad59302fd263dd81ffa4843b5b24 Mon Sep 17 00:00:00 2001 From: Michal Hocko Date: Mon, 29 Dec 2014 20:30:35 +0100 Subject: mm: get rid of radix tree gfp mask for pagecache_get_page Commit 2457aec63745 ("mm: non-atomically mark page accessed during page cache allocation where possible") has added a separate parameter for specifying gfp mask for radix tree allocations. Not only this is less than optimal from the API point of view because it is error prone, it is also buggy currently because grab_cache_page_write_begin is using GFP_KERNEL for radix tree and if fgp_flags doesn't contain FGP_NOFS (mostly controlled by fs by AOP_FLAG_NOFS flag) but the mapping_gfp_mask has __GFP_FS cleared then the radix tree allocation wouldn't obey the restriction and might recurse into filesystem and cause deadlocks. This is the case for most filesystems unfortunately because only ext4 and gfs2 are using AOP_FLAG_NOFS. Let's simply remove radix_gfp_mask parameter because the allocation context is same for both page cache and for the radix tree. Just make sure that the radix tree gets only the sane subset of the mask (e.g. do not pass __GFP_WRITE). Long term it is more preferable to convert remaining users of AOP_FLAG_NOFS to use mapping_gfp_mask instead and simplify this interface even further. Reported-by: Dave Chinner Signed-off-by: Michal Hocko Signed-off-by: Linus Torvalds diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index 7ea069c..4b3736f 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -251,7 +251,7 @@ pgoff_t page_cache_prev_hole(struct address_space *mapping, #define FGP_NOWAIT 0x00000020 struct page *pagecache_get_page(struct address_space *mapping, pgoff_t offset, - int fgp_flags, gfp_t cache_gfp_mask, gfp_t radix_gfp_mask); + int fgp_flags, gfp_t cache_gfp_mask); /** * find_get_page - find and get a page reference @@ -266,13 +266,13 @@ struct page *pagecache_get_page(struct address_space *mapping, pgoff_t offset, static inline struct page *find_get_page(struct address_space *mapping, pgoff_t offset) { - return pagecache_get_page(mapping, offset, 0, 0, 0); + return pagecache_get_page(mapping, offset, 0, 0); } static inline struct page *find_get_page_flags(struct address_space *mapping, pgoff_t offset, int fgp_flags) { - return pagecache_get_page(mapping, offset, fgp_flags, 0, 0); + return pagecache_get_page(mapping, offset, fgp_flags, 0); } /** @@ -292,7 +292,7 @@ static inline struct page *find_get_page_flags(struct address_space *mapping, static inline struct page *find_lock_page(struct address_space *mapping, pgoff_t offset) { - return pagecache_get_page(mapping, offset, FGP_LOCK, 0, 0); + return pagecache_get_page(mapping, offset, FGP_LOCK, 0); } /** @@ -319,7 +319,7 @@ static inline struct page *find_or_create_page(struct address_space *mapping, { return pagecache_get_page(mapping, offset, FGP_LOCK|FGP_ACCESSED|FGP_CREAT, - gfp_mask, gfp_mask & GFP_RECLAIM_MASK); + gfp_mask); } /** @@ -340,8 +340,7 @@ static inline struct page *grab_cache_page_nowait(struct address_space *mapping, { return pagecache_get_page(mapping, index, FGP_LOCK|FGP_CREAT|FGP_NOFS|FGP_NOWAIT, - mapping_gfp_mask(mapping), - GFP_NOFS); + mapping_gfp_mask(mapping)); } struct page *find_get_entry(struct address_space *mapping, pgoff_t offset); diff --git a/mm/filemap.c b/mm/filemap.c index bd8543c..673e458 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -1046,8 +1046,7 @@ EXPORT_SYMBOL(find_lock_entry); * @mapping: the address_space to search * @offset: the page index * @fgp_flags: PCG flags - * @cache_gfp_mask: gfp mask to use for the page cache data page allocation - * @radix_gfp_mask: gfp mask to use for radix tree node allocation + * @gfp_mask: gfp mask to use for the page cache data page allocation * * Looks up the page cache slot at @mapping & @offset. * @@ -1056,11 +1055,9 @@ EXPORT_SYMBOL(find_lock_entry); * FGP_ACCESSED: the page will be marked accessed * FGP_LOCK: Page is return locked * FGP_CREAT: If page is not present then a new page is allocated using - * @cache_gfp_mask and added to the page cache and the VM's LRU - * list. If radix tree nodes are allocated during page cache - * insertion then @radix_gfp_mask is used. The page is returned - * locked and with an increased refcount. Otherwise, %NULL is - * returned. + * @gfp_mask and added to the page cache and the VM's LRU + * list. The page is returned locked and with an increased + * refcount. Otherwise, %NULL is returned. * * If FGP_LOCK or FGP_CREAT are specified then the function may sleep even * if the GFP flags specified for FGP_CREAT are atomic. @@ -1068,7 +1065,7 @@ EXPORT_SYMBOL(find_lock_entry); * If there is a page cache page, it is returned with an increased refcount. */ struct page *pagecache_get_page(struct address_space *mapping, pgoff_t offset, - int fgp_flags, gfp_t cache_gfp_mask, gfp_t radix_gfp_mask) + int fgp_flags, gfp_t gfp_mask) { struct page *page; @@ -1105,13 +1102,11 @@ no_page: if (!page && (fgp_flags & FGP_CREAT)) { int err; if ((fgp_flags & FGP_WRITE) && mapping_cap_account_dirty(mapping)) - cache_gfp_mask |= __GFP_WRITE; - if (fgp_flags & FGP_NOFS) { - cache_gfp_mask &= ~__GFP_FS; - radix_gfp_mask &= ~__GFP_FS; - } + gfp_mask |= __GFP_WRITE; + if (fgp_flags & FGP_NOFS) + gfp_mask &= ~__GFP_FS; - page = __page_cache_alloc(cache_gfp_mask); + page = __page_cache_alloc(gfp_mask); if (!page) return NULL; @@ -1122,7 +1117,8 @@ no_page: if (fgp_flags & FGP_ACCESSED) __SetPageReferenced(page); - err = add_to_page_cache_lru(page, mapping, offset, radix_gfp_mask); + err = add_to_page_cache_lru(page, mapping, offset, + gfp_mask & GFP_RECLAIM_MASK); if (unlikely(err)) { page_cache_release(page); page = NULL; @@ -2443,8 +2439,7 @@ struct page *grab_cache_page_write_begin(struct address_space *mapping, fgp_flags |= FGP_NOFS; page = pagecache_get_page(mapping, index, fgp_flags, - mapping_gfp_mask(mapping), - GFP_KERNEL); + mapping_gfp_mask(mapping)); if (page) wait_for_stable_page(page); -- cgit v0.10.2 From dc97a1a9477f969e34b38ca9d9cd231cb93ebea2 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 29 Dec 2014 16:31:49 -0500 Subject: genetlink: A genl_bind() to an out-of-range multicast group should not WARN(). Users can request to bind to arbitrary multicast groups, so warning when the requested group number is out of range is not appropriate. And with the warning removed, and the 'err' variable properly given an initial value, we can remove 'found' altogether. Reported-by: Sedat Dilek Signed-off-by: David S. Miller diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 91566ed..2e11061 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -985,8 +985,7 @@ static struct genl_multicast_group genl_ctrl_groups[] = { static int genl_bind(struct net *net, int group) { - int i, err; - bool found = false; + int i, err = 0; down_read(&cb_lock); for (i = 0; i < GENL_FAM_TAB_SIZE; i++) { @@ -1003,16 +1002,12 @@ static int genl_bind(struct net *net, int group) err = f->mcast_bind(net, fam_grp); else err = 0; - found = true; break; } } } up_read(&cb_lock); - if (WARN_ON(!found)) - err = 0; - return err; } -- cgit v0.10.2 From 30ea9c5218651bc11cbdba7820be78f04e2d83bc Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Fri, 19 Dec 2014 13:55:41 +0200 Subject: video/fbdev: fix defio's fsync fb_deferred_io_fsync() returns the value of schedule_delayed_work() as an error code, but schedule_delayed_work() does not return an error. It returns true/false depending on whether the work was already queued. Fix this by ignoring the return value of schedule_delayed_work(). Signed-off-by: Tomi Valkeinen Cc: stable@vger.kernel.org diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c index 900aa4e..d6cab1f 100644 --- a/drivers/video/fbdev/core/fb_defio.c +++ b/drivers/video/fbdev/core/fb_defio.c @@ -83,9 +83,10 @@ int fb_deferred_io_fsync(struct file *file, loff_t start, loff_t end, int datasy cancel_delayed_work_sync(&info->deferred_work); /* Run it immediately */ - err = schedule_delayed_work(&info->deferred_work, 0); + schedule_delayed_work(&info->deferred_work, 0); mutex_unlock(&inode->i_mutex); - return err; + + return 0; } EXPORT_SYMBOL_GPL(fb_deferred_io_fsync); -- cgit v0.10.2 From a32442d4f8b712b701ea577c841d6a0a7c5993c1 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 29 Dec 2014 09:57:11 +0200 Subject: OMAPDSS: SDI: fix output port_num After the commit ef691ff48bc8 (OMAPDSS: DT: Get source endpoint by matching reg-id) we look for the SDI output using the port number. However, the SDI driver doesn't set the port number, which causes the SDI display to not initialize. Fix this by setting the SDI port number to 1. We use a hardcoded value, as SDI was used only on OMAP3 and it's always port number 1 there. Reported-by: Aaro Koskinen Reported-by: Pavel Machek Signed-off-by: Tomi Valkeinen diff --git a/drivers/video/fbdev/omap2/dss/sdi.c b/drivers/video/fbdev/omap2/dss/sdi.c index d51a9830..5c2ccab 100644 --- a/drivers/video/fbdev/omap2/dss/sdi.c +++ b/drivers/video/fbdev/omap2/dss/sdi.c @@ -342,6 +342,8 @@ static void sdi_init_output(struct platform_device *pdev) out->output_type = OMAP_DISPLAY_TYPE_SDI; out->name = "sdi.0"; out->dispc_channel = OMAP_DSS_CHANNEL_LCD; + /* We have SDI only on OMAP3, where it's on port 1 */ + out->port_num = 1; out->ops.sdi = &sdi_ops; out->owner = THIS_MODULE; -- cgit v0.10.2 From e461338b6cd4074e39a0d5fdd1dc5582fbca1520 Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Fri, 5 Dec 2014 08:58:03 -0500 Subject: sd: tweak discard heuristics to work around QEMU SCSI issue 7985090aa020 changed the discard heuristics to give preference to the WRITE SAME commands that (unlike UNMAP) guarantee deterministic results. Ming Lei discovered that QEMU SCSI's WRITE SAME implementation internally relied on limits that were only communicated for the UNMAP case. And therefore discard commands backed by WRITE SAME would fail. Tweak the heuristics so we still pick UNMAP in the LBPRZ=0 case and only prefer the WRITE SAME variants if the device has the LBPRZ flag set. Reported-by: Ming Lei Tested-by: Ming Lei Signed-off-by: Martin K. Petersen Acked-by: Paolo Bonzini Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index fedab3c..3995169 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -2623,8 +2623,9 @@ static void sd_read_block_limits(struct scsi_disk *sdkp) sd_config_discard(sdkp, SD_LBP_WS16); } else { /* LBP VPD page tells us what to use */ - - if (sdkp->lbpws) + if (sdkp->lbpu && sdkp->max_unmap_blocks && !sdkp->lbprz) + sd_config_discard(sdkp, SD_LBP_UNMAP); + else if (sdkp->lbpws) sd_config_discard(sdkp, SD_LBP_WS16); else if (sdkp->lbpws10) sd_config_discard(sdkp, SD_LBP_WS10); -- cgit v0.10.2 From efc7a288382cffc76d6cdb9678f643db37991906 Mon Sep 17 00:00:00 2001 From: "Anil Chintalapati (achintal)" Date: Tue, 23 Dec 2014 19:40:00 +0000 Subject: fnic: IOMMU Fault occurs when IO and abort IO is out of order When I/O is aborted by mid-layer, fnic FW will complete the I/O before completing the abort task. In some cases abort request is completed before the I/O, which could lead to inconsistent driver and firmware states. In this case firmware reset would clear the inconsistent state. Signed-off-by: Anil Chintalapati Signed-off-by: Sesidhar Baddela Signed-off-by: Hiral Shah Signed-off-by: Christoph Hellwig diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h index 3b73b96..26270c3 100644 --- a/drivers/scsi/fnic/fnic.h +++ b/drivers/scsi/fnic/fnic.h @@ -39,7 +39,7 @@ #define DRV_NAME "fnic" #define DRV_DESCRIPTION "Cisco FCoE HBA Driver" -#define DRV_VERSION "1.6.0.16" +#define DRV_VERSION "1.6.0.17" #define PFX DRV_NAME ": " #define DFX DRV_NAME "%d: " diff --git a/drivers/scsi/fnic/fnic_scsi.c b/drivers/scsi/fnic/fnic_scsi.c index 2097de4..155b286 100644 --- a/drivers/scsi/fnic/fnic_scsi.c +++ b/drivers/scsi/fnic/fnic_scsi.c @@ -1892,6 +1892,21 @@ int fnic_abort_cmd(struct scsi_cmnd *sc) goto fnic_abort_cmd_end; } + /* IO out of order */ + + if (!(CMD_FLAGS(sc) & (FNIC_IO_ABORTED | FNIC_IO_DONE))) { + spin_unlock_irqrestore(io_lock, flags); + FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host, + "Issuing Host reset due to out of order IO\n"); + + if (fnic_host_reset(sc) == FAILED) { + FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host, + "fnic_host_reset failed.\n"); + } + ret = FAILED; + goto fnic_abort_cmd_end; + } + CMD_STATE(sc) = FNIC_IOREQ_ABTS_COMPLETE; /* -- cgit v0.10.2 From fcf22d8267ad2601fe9b6c549d1be96401c23e0b Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 30 Dec 2014 09:26:21 -0500 Subject: audit: create private file name copies when auditing inodes Unfortunately, while commit 4a928436 ("audit: correctly record file names with different path name types") fixed a problem where we were not recording filenames, it created a new problem by attempting to use these file names after they had been freed. This patch resolves the issue by creating a copy of the filename which the audit subsystem frees after it is done with the string. At some point it would be nice to resolve this issue with refcounts, or something similar, instead of having to allocate/copy strings, but that is almost surely beyond the scope of a -rcX patch so we'll defer that for later. On the plus side, only audit users should be impacted by the string copying. Reported-by: Toralf Foerster Signed-off-by: Paul Moore diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 287b3d3..793e9e9 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -72,6 +72,8 @@ #include #include #include +#include +#include #include "audit.h" @@ -1861,8 +1863,7 @@ void __audit_inode(struct filename *name, const struct dentry *dentry, } list_for_each_entry_reverse(n, &context->names_list, list) { - /* does the name pointer match? */ - if (!n->name || n->name->name != name->name) + if (!n->name || strcmp(n->name->name, name->name)) continue; /* match the correct record type */ @@ -1881,14 +1882,44 @@ out_alloc: n = audit_alloc_name(context, AUDIT_TYPE_UNKNOWN); if (!n) return; - if (name) - /* since name is not NULL we know there is already a matching - * name record, see audit_getname(), so there must be a type - * mismatch; reuse the string path since the original name - * record will keep the string valid until we free it in - * audit_free_names() */ - n->name = name; + /* unfortunately, while we may have a path name to record with the + * inode, we can't always rely on the string lasting until the end of + * the syscall so we need to create our own copy, it may fail due to + * memory allocation issues, but we do our best */ + if (name) { + /* we can't use getname_kernel() due to size limits */ + size_t len = strlen(name->name) + 1; + struct filename *new = __getname(); + + if (unlikely(!new)) + goto out; + + if (len <= (PATH_MAX - sizeof(*new))) { + new->name = (char *)(new) + sizeof(*new); + new->separate = false; + } else if (len <= PATH_MAX) { + /* this looks odd, but is due to final_putname() */ + struct filename *new2; + new2 = kmalloc(sizeof(*new2), GFP_KERNEL); + if (unlikely(!new2)) { + __putname(new); + goto out; + } + new2->name = (char *)new; + new2->separate = true; + new = new2; + } else { + /* we should never get here, but let's be safe */ + __putname(new); + goto out; + } + strlcpy((char *)new->name, name->name, len); + new->uptr = NULL; + new->aname = n; + n->name = new; + n->name_put = true; + } out: if (parent) { n->name_len = n->name ? parent_len(n->name->name) : AUDIT_NAME_FULL; -- cgit v0.10.2 From 007487f1fd43d84f26cda926081ca219a24ecbc4 Mon Sep 17 00:00:00 2001 From: Steev Klimaszewski Date: Tue, 30 Dec 2014 00:55:48 -0600 Subject: Add USB_EHCI_EXYNOS to multi_v7_defconfig Currently we enable Exynos devices in the multi v7 defconfig, however, when testing on my ODROID-U3, I noticed that USB was not working. Enabling this option causes USB to work, which enables networking support as well since the ODROID-U3 has networking on the USB bus. [arnd] Support for odroid-u3 was added in 3.10, so it would be nice to backport this fix at least that far. Signed-off-by: Steev Klimaszewski Cc: stable@vger.kernel.org # 3.10 Signed-off-by: Arnd Bergmann diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index 2328fe7..bc393b7e 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig @@ -338,6 +338,7 @@ CONFIG_USB=y CONFIG_USB_XHCI_HCD=y CONFIG_USB_XHCI_MVEBU=y CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_EXYNOS=y CONFIG_USB_EHCI_TEGRA=y CONFIG_USB_EHCI_HCD_STI=y CONFIG_USB_EHCI_HCD_PLATFORM=y -- cgit v0.10.2 From 62f64a880af2e82d1b41cb02cb43b88d30413993 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Dec 2014 19:41:40 +0100 Subject: ALSA: pcm: Fix kerneldoc for params_*() functions Fix a copy and paste error in the kernel doc description for the params_*() functions. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 1e7f74a..b429b73 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -857,7 +857,7 @@ static inline unsigned int params_channels(const struct snd_pcm_hw_params *p) } /** - * params_channels - Get the sample rate from the hw params + * params_rate - Get the sample rate from the hw params * @p: hw params */ static inline unsigned int params_rate(const struct snd_pcm_hw_params *p) @@ -866,7 +866,7 @@ static inline unsigned int params_rate(const struct snd_pcm_hw_params *p) } /** - * params_channels - Get the period size (in frames) from the hw params + * params_period_size - Get the period size (in frames) from the hw params * @p: hw params */ static inline unsigned int params_period_size(const struct snd_pcm_hw_params *p) @@ -875,7 +875,7 @@ static inline unsigned int params_period_size(const struct snd_pcm_hw_params *p) } /** - * params_channels - Get the number of periods from the hw params + * params_periods - Get the number of periods from the hw params * @p: hw params */ static inline unsigned int params_periods(const struct snd_pcm_hw_params *p) @@ -884,7 +884,7 @@ static inline unsigned int params_periods(const struct snd_pcm_hw_params *p) } /** - * params_channels - Get the buffer size (in frames) from the hw params + * params_buffer_size - Get the buffer size (in frames) from the hw params * @p: hw params */ static inline unsigned int params_buffer_size(const struct snd_pcm_hw_params *p) @@ -893,7 +893,7 @@ static inline unsigned int params_buffer_size(const struct snd_pcm_hw_params *p) } /** - * params_channels - Get the buffer size (in bytes) from the hw params + * params_buffer_bytes - Get the buffer size (in bytes) from the hw params * @p: hw params */ static inline unsigned int params_buffer_bytes(const struct snd_pcm_hw_params *p) -- cgit v0.10.2 From 511833acfc06c013d453e288f483c682c60ffbff Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 21 Nov 2014 10:44:49 -0500 Subject: SCSI: fix regression in scsi_send_eh_cmnd() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit ac61d1955934 (scsi: set correct completion code in scsi_send_eh_cmnd()) introduced a bug. It changed the stored return value from a queuecommand call, but it didn't take into account that the return value was used again later on. This patch fixes the bug by changing the later usage. There is a big comment in the middle of scsi_send_eh_cmnd() which does a good job of explaining how the routine works. But it mentions a "rtn = FAILURE" value that doesn't exist in the code. This patch adjusts the code to match the comment (I assume the comment is right and the code is wrong). This fixes Bugzilla #88341. Signed-off-by: Alan Stern Reported-by: Андрей Аладьев Tested-by: Андрей Аладьев Fixes: ac61d19559349e205dad7b5122b281419aa74a82 Acked-by: Hannes Reinecke Cc: Signed-off-by: James Bottomley diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index e42fff6..8afb016 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -1041,7 +1041,7 @@ retry: } /* signal not to enter either branch of the if () below */ timeleft = 0; - rtn = NEEDS_RETRY; + rtn = FAILED; } else { timeleft = wait_for_completion_timeout(&done, timeout); rtn = SUCCESS; @@ -1081,7 +1081,7 @@ retry: rtn = FAILED; break; } - } else if (!rtn) { + } else if (rtn != FAILED) { scsi_abort_eh_cmnd(scmd); rtn = FAILED; } -- cgit v0.10.2 From 831a39c241e1254b6ddb8dea3144e77b9bbf44b3 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Tue, 30 Dec 2014 23:52:20 +0100 Subject: Revert "cfg80211: make WEXT compatibility unselectable" This reverts commit 24a0aa212ee2dbe44360288684478d76a8e20a0a. It's causing severe userspace breakage. Namely, all the utilities from wireless-utils which are relying on CONFIG_WEXT (which means tools like 'iwconfig', 'iwlist', etc) are not working anymore. There is a 'iw' utility in newer wireless-tools, which is supposed to be a replacement for all the "deprecated" binaries, but it's far away from being massively adopted. Please see [1] for example of the userspace breakage this is causing. In addition to that, Larry Finger reports [2] that this patch is also causing ipw2200 driver being impossible to build. To me this clearly shows that CONFIG_WEXT is far, far away from being "deprecated enough" to be removed. [1] http://thread.gmane.org/gmane.linux.kernel/1857010 [2] http://thread.gmane.org/gmane.linux.network/343688 Signed-off-by: Jiri Kosina Signed-off-by: Linus Torvalds diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index 22ba971..29c8675 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -175,7 +175,7 @@ config CFG80211_INTERNAL_REGDB Most distributions have a CRDA package. So if unsure, say N. config CFG80211_WEXT - bool + bool "cfg80211 wireless extensions compatibility" depends on CFG80211 select WEXT_CORE help -- cgit v0.10.2 From 6f3d2b0075f74ab4f4aeca1622cd71f212a24c46 Mon Sep 17 00:00:00 2001 From: Walter Goossens Date: Wed, 31 Dec 2014 09:29:07 +0800 Subject: nios2: Initialize cpuinfo.mmu This patch initializes the mmu field of the cpuinfo structure to the value supplied by the devicetree. Signed-off-by: Walter Goossens Acked-by: Ley Foon Tan diff --git a/arch/nios2/kernel/cpuinfo.c b/arch/nios2/kernel/cpuinfo.c index 51d5bb9..a223691d 100644 --- a/arch/nios2/kernel/cpuinfo.c +++ b/arch/nios2/kernel/cpuinfo.c @@ -72,6 +72,7 @@ void __init setup_cpuinfo(void) cpuinfo.has_div = fcpu_has(cpu, "altr,has-div"); cpuinfo.has_mul = fcpu_has(cpu, "altr,has-mul"); cpuinfo.has_mulx = fcpu_has(cpu, "altr,has-mulx"); + cpuinfo.mmu = fcpu_has(cpu, "altr,has-mmu"); if (IS_ENABLED(CONFIG_NIOS2_HW_DIV_SUPPORT) && !cpuinfo.has_div) err_cpu("DIV"); -- cgit v0.10.2 From 1b0f44923e186b2f9383b3260f6b5fbfc77b9e4a Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 31 Dec 2014 10:53:11 +0800 Subject: nios2: Use preempt_schedule_irq Follow aa0d53260596 ("ia64: Use preempt_schedule_irq") and use preempt_schedule_irq instead of enabling/disabling interrupts and messing around with PREEMPT_ACTIVE in the nios2 low-level preemption code ourselves. Also get rid of the now needless re-check for TIF_NEED_RESCHED, preempt_schedule_irq will already take care of rescheduling. This also fixes the following build error when building with CONFIG_PREEMPT: arch/nios2/kernel/built-in.o: In function `need_resched': arch/nios2/kernel/entry.S:374: undefined reference to `PREEMPT_ACTIVE' Cc: Thomas Gleixner Signed-off-by: Tobias Klauser Acked-by: Ley Foon Tan diff --git a/arch/nios2/kernel/entry.S b/arch/nios2/kernel/entry.S index 83bca17..0bdfd13 100644 --- a/arch/nios2/kernel/entry.S +++ b/arch/nios2/kernel/entry.S @@ -365,30 +365,14 @@ ENTRY(ret_from_interrupt) GET_THREAD_INFO r1 ldw r4, TI_PREEMPT_COUNT(r1) bne r4, r0, restore_all - -need_resched: ldw r4, TI_FLAGS(r1) /* ? Need resched set */ BTBZ r10, r4, TIF_NEED_RESCHED, restore_all ldw r4, PT_ESTATUS(sp) /* ? Interrupts off */ andi r10, r4, ESTATUS_EPIE beq r10, r0, restore_all - movia r4, PREEMPT_ACTIVE - stw r4, TI_PREEMPT_COUNT(r1) - rdctl r10, status /* enable intrs again */ - ori r10, r10 ,STATUS_PIE - wrctl status, r10 - PUSH r1 - call schedule - POP r1 - mov r4, r0 - stw r4, TI_PREEMPT_COUNT(r1) - rdctl r10, status /* disable intrs */ - andi r10, r10, %lo(~STATUS_PIE) - wrctl status, r10 - br need_resched -#else - br restore_all + call preempt_schedule_irq #endif + br restore_all /*********************************************************************** * A few syscall wrappers -- cgit v0.10.2 From 7f4054836d811c650c51f9c93088f8ebd61b0020 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 31 Dec 2014 12:59:34 -0800 Subject: Revert "Input: atmel_mxt_ts - use deep sleep mode when stopped" This reverts commit 9d469d033d135d80742a4e39e6bbb4519dd5eee1. It breaks the Chromebook Pixel touchpad (and touchscreen). Reported-by: Dirk Hohndel Bisected-by: Linus Torvalds Cc: Nick Dyer Cc: Benson Leung Cc: Yufeng Shen Cc: Dmitry Torokhov Cc: stable@vger.kernel.org # v3.16+ Signed-off-by: Linus Torvalds diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index bb07020..95ee92a 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -99,13 +99,9 @@ #define MXT_T6_STATUS_COMSERR (1 << 2) /* MXT_GEN_POWER_T7 field */ -struct t7_config { - u8 idle; - u8 active; -} __packed; - -#define MXT_POWER_CFG_RUN 0 -#define MXT_POWER_CFG_DEEPSLEEP 1 +#define MXT_POWER_IDLEACQINT 0 +#define MXT_POWER_ACTVACQINT 1 +#define MXT_POWER_ACTV2IDLETO 2 /* MXT_GEN_ACQUIRE_T8 field */ #define MXT_ACQUIRE_CHRGTIME 0 @@ -117,6 +113,7 @@ struct t7_config { #define MXT_ACQUIRE_ATCHCALSTHR 7 /* MXT_TOUCH_MULTI_T9 field */ +#define MXT_TOUCH_CTRL 0 #define MXT_T9_ORIENT 9 #define MXT_T9_RANGE 18 @@ -256,7 +253,6 @@ struct mxt_data { bool update_input; u8 last_message_count; u8 num_touchids; - struct t7_config t7_cfg; /* Cached parameters from object table */ u16 T5_address; @@ -672,6 +668,20 @@ static void mxt_proc_t6_messages(struct mxt_data *data, u8 *msg) data->t6_status = status; } +static int mxt_write_object(struct mxt_data *data, + u8 type, u8 offset, u8 val) +{ + struct mxt_object *object; + u16 reg; + + object = mxt_get_object(data, type); + if (!object || offset >= mxt_obj_size(object)) + return -EINVAL; + + reg = object->start_address; + return mxt_write_reg(data->client, reg + offset, val); +} + static void mxt_input_button(struct mxt_data *data, u8 *message) { struct input_dev *input = data->input_dev; @@ -1742,60 +1752,6 @@ err_free_object_table: return error; } -static int mxt_set_t7_power_cfg(struct mxt_data *data, u8 sleep) -{ - struct device *dev = &data->client->dev; - int error; - struct t7_config *new_config; - struct t7_config deepsleep = { .active = 0, .idle = 0 }; - - if (sleep == MXT_POWER_CFG_DEEPSLEEP) - new_config = &deepsleep; - else - new_config = &data->t7_cfg; - - error = __mxt_write_reg(data->client, data->T7_address, - sizeof(data->t7_cfg), new_config); - if (error) - return error; - - dev_dbg(dev, "Set T7 ACTV:%d IDLE:%d\n", - new_config->active, new_config->idle); - - return 0; -} - -static int mxt_init_t7_power_cfg(struct mxt_data *data) -{ - struct device *dev = &data->client->dev; - int error; - bool retry = false; - -recheck: - error = __mxt_read_reg(data->client, data->T7_address, - sizeof(data->t7_cfg), &data->t7_cfg); - if (error) - return error; - - if (data->t7_cfg.active == 0 || data->t7_cfg.idle == 0) { - if (!retry) { - dev_dbg(dev, "T7 cfg zero, resetting\n"); - mxt_soft_reset(data); - retry = true; - goto recheck; - } else { - dev_dbg(dev, "T7 cfg zero after reset, overriding\n"); - data->t7_cfg.active = 20; - data->t7_cfg.idle = 100; - return mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN); - } - } - - dev_dbg(dev, "Initialized power cfg: ACTV %d, IDLE %d\n", - data->t7_cfg.active, data->t7_cfg.idle); - return 0; -} - static int mxt_configure_objects(struct mxt_data *data, const struct firmware *cfg) { @@ -1809,12 +1765,6 @@ static int mxt_configure_objects(struct mxt_data *data, dev_warn(dev, "Error %d updating config\n", error); } - error = mxt_init_t7_power_cfg(data); - if (error) { - dev_err(dev, "Failed to initialize power cfg\n"); - return error; - } - error = mxt_initialize_t9_input_device(data); if (error) return error; @@ -2093,15 +2043,16 @@ static const struct attribute_group mxt_attr_group = { static void mxt_start(struct mxt_data *data) { - mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN); - - /* Recalibrate since chip has been in deep sleep */ - mxt_t6_command(data, MXT_COMMAND_CALIBRATE, 1, false); + /* Touch enable */ + mxt_write_object(data, + MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0x83); } static void mxt_stop(struct mxt_data *data) { - mxt_set_t7_power_cfg(data, MXT_POWER_CFG_DEEPSLEEP); + /* Touch disable */ + mxt_write_object(data, + MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0); } static int mxt_input_open(struct input_dev *dev) @@ -2266,6 +2217,8 @@ static int __maybe_unused mxt_resume(struct device *dev) struct mxt_data *data = i2c_get_clientdata(client); struct input_dev *input_dev = data->input_dev; + mxt_soft_reset(data); + mutex_lock(&input_dev->mutex); if (input_dev->users) -- cgit v0.10.2 From f911d731054ab3d82ee72a16b889e17ca3a2332a Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Wed, 10 Dec 2014 13:53:51 +0100 Subject: um: Skip futex_atomic_cmpxchg_inatomic() test futex_atomic_cmpxchg_inatomic() does not work on UML because it triggers a copy_from_user() in kernel context. On UML copy_from_user() can only be used if the kernel was called by a real user space process such that UML can use ptrace() to fetch the value. Reported-by: Miklos Szeredi Suggested-by: Geert Uytterhoeven Signed-off-by: Richard Weinberger Tested-by: Daniel Walter diff --git a/arch/um/Kconfig.common b/arch/um/Kconfig.common index 87bc868..d195a87 100644 --- a/arch/um/Kconfig.common +++ b/arch/um/Kconfig.common @@ -3,6 +3,7 @@ config UML default y select HAVE_ARCH_AUDITSYSCALL select HAVE_UID16 + select HAVE_FUTEX_CMPXCHG if FUTEX select GENERIC_IRQ_SHOW select GENERIC_CPU_DEVICES select GENERIC_IO -- cgit v0.10.2 From b485342bd79af363c77ef1a421c4a0aef2de9812 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sat, 3 Jan 2015 13:11:10 +0100 Subject: x86, um: actually mark system call tables readonly Commit a074335a370e ("x86, um: Mark system call tables readonly") was supposed to mark the sys_call_table in UML as RO by adding the const, but it doesn't have the desired effect as it's nevertheless being placed into the data section since __cacheline_aligned enforces sys_call_table being placed into .data..cacheline_aligned instead. We need to use the ____cacheline_aligned version instead to fix this issue. Before: $ nm -v arch/x86/um/sys_call_table_64.o | grep -1 "sys_call_table" U sys_writev 0000000000000000 D sys_call_table 0000000000000000 D syscall_table_size After: $ nm -v arch/x86/um/sys_call_table_64.o | grep -1 "sys_call_table" U sys_writev 0000000000000000 R sys_call_table 0000000000000000 D syscall_table_size Fixes: a074335a370e ("x86, um: Mark system call tables readonly") Cc: H. Peter Anvin Cc: Andrew Morton Signed-off-by: Daniel Borkmann Signed-off-by: Richard Weinberger diff --git a/arch/x86/um/sys_call_table_32.c b/arch/x86/um/sys_call_table_32.c index 531d426..bd16d6c 100644 --- a/arch/x86/um/sys_call_table_32.c +++ b/arch/x86/um/sys_call_table_32.c @@ -34,7 +34,7 @@ typedef asmlinkage void (*sys_call_ptr_t)(void); extern asmlinkage void sys_ni_syscall(void); -const sys_call_ptr_t sys_call_table[] __cacheline_aligned = { +const sys_call_ptr_t sys_call_table[] ____cacheline_aligned = { /* * Smells like a compiler bug -- it doesn't work * when the & below is removed. diff --git a/arch/x86/um/sys_call_table_64.c b/arch/x86/um/sys_call_table_64.c index 20c3649..5cdfa9d 100644 --- a/arch/x86/um/sys_call_table_64.c +++ b/arch/x86/um/sys_call_table_64.c @@ -47,7 +47,7 @@ typedef void (*sys_call_ptr_t)(void); extern void sys_ni_syscall(void); -const sys_call_ptr_t sys_call_table[] __cacheline_aligned = { +const sys_call_ptr_t sys_call_table[] ____cacheline_aligned = { /* * Smells like a compiler bug -- it doesn't work * when the & below is removed. -- cgit v0.10.2 From 4bf9636c39ac70da091d5a2e28d3448eaa7f115c Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Sun, 4 Jan 2015 20:01:23 +0100 Subject: Revert "ARM: 7830/1: delay: don't bother reporting bogomips in /proc/cpuinfo" Commit 9fc2105aeaaf ("ARM: 7830/1: delay: don't bother reporting bogomips in /proc/cpuinfo") breaks audio in python, and probably elsewhere, with message FATAL: cannot locate cpu MHz in /proc/cpuinfo I'm not the first one to hit it, see for example https://theredblacktree.wordpress.com/2014/08/10/fatal-cannot-locate-cpu-mhz-in-proccpuinfo/ https://devtalk.nvidia.com/default/topic/765800/workaround-for-fatal-cannot-locate-cpu-mhz-in-proc-cpuinf/?offset=1 Reading original changelog, I have to say "Stop breaking working setups. You know who you are!". Signed-off-by: Pavel Machek Signed-off-by: Linus Torvalds diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index f9c8639..715ae19 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -1046,6 +1046,15 @@ static int c_show(struct seq_file *m, void *v) seq_printf(m, "model name\t: %s rev %d (%s)\n", cpu_name, cpuid & 15, elf_platform); +#if defined(CONFIG_SMP) + seq_printf(m, "BogoMIPS\t: %lu.%02lu\n", + per_cpu(cpu_data, i).loops_per_jiffy / (500000UL/HZ), + (per_cpu(cpu_data, i).loops_per_jiffy / (5000UL/HZ)) % 100); +#else + seq_printf(m, "BogoMIPS\t: %lu.%02lu\n", + loops_per_jiffy / (500000/HZ), + (loops_per_jiffy / (5000/HZ)) % 100); +#endif /* dump out the processor features */ seq_puts(m, "Features\t: "); diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 5e6052e..86ef244 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -387,6 +387,18 @@ asmlinkage void secondary_start_kernel(void) void __init smp_cpus_done(unsigned int max_cpus) { + int cpu; + unsigned long bogosum = 0; + + for_each_online_cpu(cpu) + bogosum += per_cpu(cpu_data, cpu).loops_per_jiffy; + + printk(KERN_INFO "SMP: Total of %d processors activated " + "(%lu.%02lu BogoMIPS).\n", + num_online_cpus(), + bogosum / (500000/HZ), + (bogosum / (5000/HZ)) % 100); + hyp_mode_check(); } -- cgit v0.10.2 From b739896dd262ca523497913b0ea232da56d6ee69 Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Mon, 5 Jan 2015 11:25:19 -0800 Subject: [IA64] Enable execveat syscall for ia64 See commit 51f39a1f0cea1cacf8c787f652f26dfee9611874 syscalls: implement execveat() system call Signed-off-by: Tony Luck diff --git a/arch/ia64/include/asm/unistd.h b/arch/ia64/include/asm/unistd.h index f3b51b5..95c39b9 100644 --- a/arch/ia64/include/asm/unistd.h +++ b/arch/ia64/include/asm/unistd.h @@ -11,7 +11,7 @@ -#define NR_syscalls 318 /* length of syscall table */ +#define NR_syscalls 319 /* length of syscall table */ /* * The following defines stop scripts/checksyscalls.sh from complaining about diff --git a/arch/ia64/include/uapi/asm/unistd.h b/arch/ia64/include/uapi/asm/unistd.h index 4c2240c..4610795 100644 --- a/arch/ia64/include/uapi/asm/unistd.h +++ b/arch/ia64/include/uapi/asm/unistd.h @@ -331,5 +331,6 @@ #define __NR_getrandom 1339 #define __NR_memfd_create 1340 #define __NR_bpf 1341 +#define __NR_execveat 1342 #endif /* _UAPI_ASM_IA64_UNISTD_H */ diff --git a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S index f5e96df..fcf8b8c 100644 --- a/arch/ia64/kernel/entry.S +++ b/arch/ia64/kernel/entry.S @@ -1779,6 +1779,7 @@ sys_call_table: data8 sys_getrandom data8 sys_memfd_create // 1340 data8 sys_bpf + data8 sys_execveat .org sys_call_table + 8*NR_syscalls // guard against failures to increase NR_syscalls #endif /* __IA64_ASM_PARAVIRTUALIZED_NATIVE */ -- cgit v0.10.2 From b1940cd21c0f4abdce101253e860feff547291b0 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 5 Jan 2015 17:05:20 -0800 Subject: Linux 3.19-rc3 diff --git a/Makefile b/Makefile index ef748e1..eb4eca5 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 3 PATCHLEVEL = 19 SUBLEVEL = 0 -EXTRAVERSION = -rc2 +EXTRAVERSION = -rc3 NAME = Diseased Newt # *DOCUMENTATION* -- cgit v0.10.2 From fca08f326ae0423f03b097ff54de432fe77b95d0 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Fri, 9 Jan 2015 10:19:49 +0800 Subject: ARM: probes: move all probe code to dedicate directory In discussion on LKML (https://lkml.org/lkml/2014/11/28/158), Russell King suggests to move all probe related code to arch/arm/probes. This patch does the work. Due to dependency on 'arch/arm/kernel/patch.h', this patch also moves patch.h to 'arch/arm/include/asm/patch.h', and related '#include' directives are also midified to '#include '. Following is an overview of this patch: ./arch/arm/kernel/ ./arch/arm/probes/ |-- Makefile |-- Makefile |-- probes-arm.c ==> |-- decode-arm.c |-- probes-arm.h ==> |-- decode-arm.h |-- probes-thumb.c ==> |-- decode-thumb.c |-- probes-thumb.h ==> |-- decode-thumb.h |-- probes.c ==> |-- decode.c |-- probes.h ==> |-- decode.h | |-- kprobes | | |-- Makefile |-- kprobes-arm.c ==> | |-- actions-arm.c |-- kprobes-common.c ==> | |-- actions-common.c |-- kprobes-thumb.c ==> | |-- actions-thumb.c |-- kprobes.c ==> | |-- core.c |-- kprobes.h ==> | |-- core.h |-- kprobes-test-arm.c ==> | |-- test-arm.c |-- kprobes-test.c ==> | |-- test-core.c |-- kprobes-test.h ==> | |-- test-core.h |-- kprobes-test-thumb.c ==> | `-- test-thumb.c | `-- uprobes | |-- Makefile |-- uprobes-arm.c ==> |-- actions-arm.c |-- uprobes.c ==> |-- core.c |-- uprobes.h ==> `-- core.h | `-- patch.h ==> arch/arm/include/asm/patch.h Signed-off-by: Wang Nan Acked-by: Masami Hiramatsu Signed-off-by: Jon Medhurst diff --git a/arch/arm/Makefile b/arch/arm/Makefile index c1785ee..7f99cd6 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -266,6 +266,7 @@ core-$(CONFIG_KVM_ARM_HOST) += arch/arm/kvm/ # If we have a machine-specific directory, then include it in the build. core-y += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/ +core-y += arch/arm/probes/ core-y += arch/arm/net/ core-y += arch/arm/crypto/ core-y += arch/arm/firmware/ diff --git a/arch/arm/include/asm/patch.h b/arch/arm/include/asm/patch.h new file mode 100644 index 0000000..77e054c --- /dev/null +++ b/arch/arm/include/asm/patch.h @@ -0,0 +1,17 @@ +#ifndef _ARM_KERNEL_PATCH_H +#define _ARM_KERNEL_PATCH_H + +void patch_text(void *addr, unsigned int insn); +void __patch_text_real(void *addr, unsigned int insn, bool remap); + +static inline void __patch_text(void *addr, unsigned int insn) +{ + __patch_text_real(addr, insn, true); +} + +static inline void __patch_text_early(void *addr, unsigned int insn) +{ + __patch_text_real(addr, insn, false); +} + +#endif diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index fb2b71e..9c51a43 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -51,20 +51,8 @@ obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o insn.o obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o insn.o obj-$(CONFIG_JUMP_LABEL) += jump_label.o insn.o patch.o obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o -obj-$(CONFIG_UPROBES) += probes.o probes-arm.o uprobes.o uprobes-arm.o -obj-$(CONFIG_KPROBES) += probes.o kprobes.o kprobes-common.o patch.o -ifdef CONFIG_THUMB2_KERNEL -obj-$(CONFIG_KPROBES) += kprobes-thumb.o probes-thumb.o -else -obj-$(CONFIG_KPROBES) += kprobes-arm.o probes-arm.o -endif -obj-$(CONFIG_ARM_KPROBES_TEST) += test-kprobes.o -test-kprobes-objs := kprobes-test.o -ifdef CONFIG_THUMB2_KERNEL -test-kprobes-objs += kprobes-test-thumb.o -else -test-kprobes-objs += kprobes-test-arm.o -endif +# Main staffs in KPROBES are in arch/arm/probes/ . +obj-$(CONFIG_KPROBES) += patch.o obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o obj-$(CONFIG_ARM_THUMBEE) += thumbee.o obj-$(CONFIG_KGDB) += kgdb.o patch.o diff --git a/arch/arm/kernel/jump_label.c b/arch/arm/kernel/jump_label.c index afeeb9e..d8da0759 100644 --- a/arch/arm/kernel/jump_label.c +++ b/arch/arm/kernel/jump_label.c @@ -1,8 +1,8 @@ #include #include +#include #include "insn.h" -#include "patch.h" #ifdef HAVE_JUMP_LABEL diff --git a/arch/arm/kernel/kgdb.c b/arch/arm/kernel/kgdb.c index 07db2f8..a6ad93c 100644 --- a/arch/arm/kernel/kgdb.c +++ b/arch/arm/kernel/kgdb.c @@ -14,10 +14,9 @@ #include #include +#include #include -#include "patch.h" - struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = { { "r0", 4, offsetof(struct pt_regs, ARM_r0)}, diff --git a/arch/arm/kernel/kprobes-arm.c b/arch/arm/kernel/kprobes-arm.c deleted file mode 100644 index ac300c6..0000000 --- a/arch/arm/kernel/kprobes-arm.c +++ /dev/null @@ -1,343 +0,0 @@ -/* - * arch/arm/kernel/kprobes-decode.c - * - * Copyright (C) 2006, 2007 Motorola Inc. - * - * 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. - */ - -/* - * We do not have hardware single-stepping on ARM, This - * effort is further complicated by the ARM not having a - * "next PC" register. Instructions that change the PC - * can't be safely single-stepped in a MP environment, so - * we have a lot of work to do: - * - * In the prepare phase: - * *) If it is an instruction that does anything - * with the CPU mode, we reject it for a kprobe. - * (This is out of laziness rather than need. The - * instructions could be simulated.) - * - * *) Otherwise, decode the instruction rewriting its - * registers to take fixed, ordered registers and - * setting a handler for it to run the instruction. - * - * In the execution phase by an instruction's handler: - * - * *) If the PC is written to by the instruction, the - * instruction must be fully simulated in software. - * - * *) Otherwise, a modified form of the instruction is - * directly executed. Its handler calls the - * instruction in insn[0]. In insn[1] is a - * "mov pc, lr" to return. - * - * Before calling, load up the reordered registers - * from the original instruction's registers. If one - * of the original input registers is the PC, compute - * and adjust the appropriate input register. - * - * After call completes, copy the output registers to - * the original instruction's original registers. - * - * We don't use a real breakpoint instruction since that - * would have us in the kernel go from SVC mode to SVC - * mode losing the link register. Instead we use an - * undefined instruction. To simplify processing, the - * undefined instruction used for kprobes must be reserved - * exclusively for kprobes use. - * - * TODO: ifdef out some instruction decoding based on architecture. - */ - -#include -#include -#include - -#include "kprobes.h" -#include "probes-arm.h" - -#if __LINUX_ARM_ARCH__ >= 6 -#define BLX(reg) "blx "reg" \n\t" -#else -#define BLX(reg) "mov lr, pc \n\t" \ - "mov pc, "reg" \n\t" -#endif - -static void __kprobes -emulate_ldrdstrd(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long pc = regs->ARM_pc + 4; - int rt = (insn >> 12) & 0xf; - int rn = (insn >> 16) & 0xf; - int rm = insn & 0xf; - - register unsigned long rtv asm("r0") = regs->uregs[rt]; - register unsigned long rt2v asm("r1") = regs->uregs[rt+1]; - register unsigned long rnv asm("r2") = (rn == 15) ? pc - : regs->uregs[rn]; - register unsigned long rmv asm("r3") = regs->uregs[rm]; - - __asm__ __volatile__ ( - BLX("%[fn]") - : "=r" (rtv), "=r" (rt2v), "=r" (rnv) - : "0" (rtv), "1" (rt2v), "2" (rnv), "r" (rmv), - [fn] "r" (asi->insn_fn) - : "lr", "memory", "cc" - ); - - regs->uregs[rt] = rtv; - regs->uregs[rt+1] = rt2v; - if (is_writeback(insn)) - regs->uregs[rn] = rnv; -} - -static void __kprobes -emulate_ldr(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long pc = regs->ARM_pc + 4; - int rt = (insn >> 12) & 0xf; - int rn = (insn >> 16) & 0xf; - int rm = insn & 0xf; - - register unsigned long rtv asm("r0"); - register unsigned long rnv asm("r2") = (rn == 15) ? pc - : regs->uregs[rn]; - register unsigned long rmv asm("r3") = regs->uregs[rm]; - - __asm__ __volatile__ ( - BLX("%[fn]") - : "=r" (rtv), "=r" (rnv) - : "1" (rnv), "r" (rmv), [fn] "r" (asi->insn_fn) - : "lr", "memory", "cc" - ); - - if (rt == 15) - load_write_pc(rtv, regs); - else - regs->uregs[rt] = rtv; - - if (is_writeback(insn)) - regs->uregs[rn] = rnv; -} - -static void __kprobes -emulate_str(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long rtpc = regs->ARM_pc - 4 + str_pc_offset; - unsigned long rnpc = regs->ARM_pc + 4; - int rt = (insn >> 12) & 0xf; - int rn = (insn >> 16) & 0xf; - int rm = insn & 0xf; - - register unsigned long rtv asm("r0") = (rt == 15) ? rtpc - : regs->uregs[rt]; - register unsigned long rnv asm("r2") = (rn == 15) ? rnpc - : regs->uregs[rn]; - register unsigned long rmv asm("r3") = regs->uregs[rm]; - - __asm__ __volatile__ ( - BLX("%[fn]") - : "=r" (rnv) - : "r" (rtv), "0" (rnv), "r" (rmv), [fn] "r" (asi->insn_fn) - : "lr", "memory", "cc" - ); - - if (is_writeback(insn)) - regs->uregs[rn] = rnv; -} - -static void __kprobes -emulate_rd12rn16rm0rs8_rwflags(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long pc = regs->ARM_pc + 4; - int rd = (insn >> 12) & 0xf; - int rn = (insn >> 16) & 0xf; - int rm = insn & 0xf; - int rs = (insn >> 8) & 0xf; - - register unsigned long rdv asm("r0") = regs->uregs[rd]; - register unsigned long rnv asm("r2") = (rn == 15) ? pc - : regs->uregs[rn]; - register unsigned long rmv asm("r3") = (rm == 15) ? pc - : regs->uregs[rm]; - register unsigned long rsv asm("r1") = regs->uregs[rs]; - unsigned long cpsr = regs->ARM_cpsr; - - __asm__ __volatile__ ( - "msr cpsr_fs, %[cpsr] \n\t" - BLX("%[fn]") - "mrs %[cpsr], cpsr \n\t" - : "=r" (rdv), [cpsr] "=r" (cpsr) - : "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv), - "1" (cpsr), [fn] "r" (asi->insn_fn) - : "lr", "memory", "cc" - ); - - if (rd == 15) - alu_write_pc(rdv, regs); - else - regs->uregs[rd] = rdv; - regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); -} - -static void __kprobes -emulate_rd12rn16rm0_rwflags_nopc(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - int rd = (insn >> 12) & 0xf; - int rn = (insn >> 16) & 0xf; - int rm = insn & 0xf; - - register unsigned long rdv asm("r0") = regs->uregs[rd]; - register unsigned long rnv asm("r2") = regs->uregs[rn]; - register unsigned long rmv asm("r3") = regs->uregs[rm]; - unsigned long cpsr = regs->ARM_cpsr; - - __asm__ __volatile__ ( - "msr cpsr_fs, %[cpsr] \n\t" - BLX("%[fn]") - "mrs %[cpsr], cpsr \n\t" - : "=r" (rdv), [cpsr] "=r" (cpsr) - : "0" (rdv), "r" (rnv), "r" (rmv), - "1" (cpsr), [fn] "r" (asi->insn_fn) - : "lr", "memory", "cc" - ); - - regs->uregs[rd] = rdv; - regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); -} - -static void __kprobes -emulate_rd16rn12rm0rs8_rwflags_nopc(probes_opcode_t insn, - struct arch_probes_insn *asi, - struct pt_regs *regs) -{ - int rd = (insn >> 16) & 0xf; - int rn = (insn >> 12) & 0xf; - int rm = insn & 0xf; - int rs = (insn >> 8) & 0xf; - - register unsigned long rdv asm("r2") = regs->uregs[rd]; - register unsigned long rnv asm("r0") = regs->uregs[rn]; - register unsigned long rmv asm("r3") = regs->uregs[rm]; - register unsigned long rsv asm("r1") = regs->uregs[rs]; - unsigned long cpsr = regs->ARM_cpsr; - - __asm__ __volatile__ ( - "msr cpsr_fs, %[cpsr] \n\t" - BLX("%[fn]") - "mrs %[cpsr], cpsr \n\t" - : "=r" (rdv), [cpsr] "=r" (cpsr) - : "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv), - "1" (cpsr), [fn] "r" (asi->insn_fn) - : "lr", "memory", "cc" - ); - - regs->uregs[rd] = rdv; - regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); -} - -static void __kprobes -emulate_rd12rm0_noflags_nopc(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - int rd = (insn >> 12) & 0xf; - int rm = insn & 0xf; - - register unsigned long rdv asm("r0") = regs->uregs[rd]; - register unsigned long rmv asm("r3") = regs->uregs[rm]; - - __asm__ __volatile__ ( - BLX("%[fn]") - : "=r" (rdv) - : "0" (rdv), "r" (rmv), [fn] "r" (asi->insn_fn) - : "lr", "memory", "cc" - ); - - regs->uregs[rd] = rdv; -} - -static void __kprobes -emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(probes_opcode_t insn, - struct arch_probes_insn *asi, - struct pt_regs *regs) -{ - int rdlo = (insn >> 12) & 0xf; - int rdhi = (insn >> 16) & 0xf; - int rn = insn & 0xf; - int rm = (insn >> 8) & 0xf; - - register unsigned long rdlov asm("r0") = regs->uregs[rdlo]; - register unsigned long rdhiv asm("r2") = regs->uregs[rdhi]; - register unsigned long rnv asm("r3") = regs->uregs[rn]; - register unsigned long rmv asm("r1") = regs->uregs[rm]; - unsigned long cpsr = regs->ARM_cpsr; - - __asm__ __volatile__ ( - "msr cpsr_fs, %[cpsr] \n\t" - BLX("%[fn]") - "mrs %[cpsr], cpsr \n\t" - : "=r" (rdlov), "=r" (rdhiv), [cpsr] "=r" (cpsr) - : "0" (rdlov), "1" (rdhiv), "r" (rnv), "r" (rmv), - "2" (cpsr), [fn] "r" (asi->insn_fn) - : "lr", "memory", "cc" - ); - - regs->uregs[rdlo] = rdlov; - regs->uregs[rdhi] = rdhiv; - regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); -} - -const union decode_action kprobes_arm_actions[NUM_PROBES_ARM_ACTIONS] = { - [PROBES_EMULATE_NONE] = {.handler = probes_emulate_none}, - [PROBES_SIMULATE_NOP] = {.handler = probes_simulate_nop}, - [PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop}, - [PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop}, - [PROBES_BRANCH_IMM] = {.handler = simulate_blx1}, - [PROBES_MRS] = {.handler = simulate_mrs}, - [PROBES_BRANCH_REG] = {.handler = simulate_blx2bx}, - [PROBES_CLZ] = {.handler = emulate_rd12rm0_noflags_nopc}, - [PROBES_SATURATING_ARITHMETIC] = { - .handler = emulate_rd12rn16rm0_rwflags_nopc}, - [PROBES_MUL1] = {.handler = emulate_rdlo12rdhi16rn0rm8_rwflags_nopc}, - [PROBES_MUL2] = {.handler = emulate_rd16rn12rm0rs8_rwflags_nopc}, - [PROBES_SWP] = {.handler = emulate_rd12rn16rm0_rwflags_nopc}, - [PROBES_LDRSTRD] = {.handler = emulate_ldrdstrd}, - [PROBES_LOAD_EXTRA] = {.handler = emulate_ldr}, - [PROBES_LOAD] = {.handler = emulate_ldr}, - [PROBES_STORE_EXTRA] = {.handler = emulate_str}, - [PROBES_STORE] = {.handler = emulate_str}, - [PROBES_MOV_IP_SP] = {.handler = simulate_mov_ipsp}, - [PROBES_DATA_PROCESSING_REG] = { - .handler = emulate_rd12rn16rm0rs8_rwflags}, - [PROBES_DATA_PROCESSING_IMM] = { - .handler = emulate_rd12rn16rm0rs8_rwflags}, - [PROBES_MOV_HALFWORD] = {.handler = emulate_rd12rm0_noflags_nopc}, - [PROBES_SEV] = {.handler = probes_emulate_none}, - [PROBES_WFE] = {.handler = probes_simulate_nop}, - [PROBES_SATURATE] = {.handler = emulate_rd12rn16rm0_rwflags_nopc}, - [PROBES_REV] = {.handler = emulate_rd12rm0_noflags_nopc}, - [PROBES_MMI] = {.handler = emulate_rd12rn16rm0_rwflags_nopc}, - [PROBES_PACK] = {.handler = emulate_rd12rn16rm0_rwflags_nopc}, - [PROBES_EXTEND] = {.handler = emulate_rd12rm0_noflags_nopc}, - [PROBES_EXTEND_ADD] = {.handler = emulate_rd12rn16rm0_rwflags_nopc}, - [PROBES_MUL_ADD_LONG] = { - .handler = emulate_rdlo12rdhi16rn0rm8_rwflags_nopc}, - [PROBES_MUL_ADD] = {.handler = emulate_rd16rn12rm0rs8_rwflags_nopc}, - [PROBES_BITFIELD] = {.handler = emulate_rd12rm0_noflags_nopc}, - [PROBES_BRANCH] = {.handler = simulate_bbl}, - [PROBES_LDMSTM] = {.decoder = kprobe_decode_ldmstm} -}; diff --git a/arch/arm/kernel/kprobes-common.c b/arch/arm/kernel/kprobes-common.c deleted file mode 100644 index 0bf5d64..0000000 --- a/arch/arm/kernel/kprobes-common.c +++ /dev/null @@ -1,171 +0,0 @@ -/* - * arch/arm/kernel/kprobes-common.c - * - * Copyright (C) 2011 Jon Medhurst . - * - * Some contents moved here from arch/arm/include/asm/kprobes-arm.c which is - * Copyright (C) 2006, 2007 Motorola Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include - -#include "kprobes.h" - - -static void __kprobes simulate_ldm1stm1(probes_opcode_t insn, - struct arch_probes_insn *asi, - struct pt_regs *regs) -{ - int rn = (insn >> 16) & 0xf; - int lbit = insn & (1 << 20); - int wbit = insn & (1 << 21); - int ubit = insn & (1 << 23); - int pbit = insn & (1 << 24); - long *addr = (long *)regs->uregs[rn]; - int reg_bit_vector; - int reg_count; - - reg_count = 0; - reg_bit_vector = insn & 0xffff; - while (reg_bit_vector) { - reg_bit_vector &= (reg_bit_vector - 1); - ++reg_count; - } - - if (!ubit) - addr -= reg_count; - addr += (!pbit == !ubit); - - reg_bit_vector = insn & 0xffff; - while (reg_bit_vector) { - int reg = __ffs(reg_bit_vector); - reg_bit_vector &= (reg_bit_vector - 1); - if (lbit) - regs->uregs[reg] = *addr++; - else - *addr++ = regs->uregs[reg]; - } - - if (wbit) { - if (!ubit) - addr -= reg_count; - addr -= (!pbit == !ubit); - regs->uregs[rn] = (long)addr; - } -} - -static void __kprobes simulate_stm1_pc(probes_opcode_t insn, - struct arch_probes_insn *asi, - struct pt_regs *regs) -{ - unsigned long addr = regs->ARM_pc - 4; - - regs->ARM_pc = (long)addr + str_pc_offset; - simulate_ldm1stm1(insn, asi, regs); - regs->ARM_pc = (long)addr + 4; -} - -static void __kprobes simulate_ldm1_pc(probes_opcode_t insn, - struct arch_probes_insn *asi, - struct pt_regs *regs) -{ - simulate_ldm1stm1(insn, asi, regs); - load_write_pc(regs->ARM_pc, regs); -} - -static void __kprobes -emulate_generic_r0_12_noflags(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - register void *rregs asm("r1") = regs; - register void *rfn asm("lr") = asi->insn_fn; - - __asm__ __volatile__ ( - "stmdb sp!, {%[regs], r11} \n\t" - "ldmia %[regs], {r0-r12} \n\t" -#if __LINUX_ARM_ARCH__ >= 6 - "blx %[fn] \n\t" -#else - "str %[fn], [sp, #-4]! \n\t" - "adr lr, 1f \n\t" - "ldr pc, [sp], #4 \n\t" - "1: \n\t" -#endif - "ldr lr, [sp], #4 \n\t" /* lr = regs */ - "stmia lr, {r0-r12} \n\t" - "ldr r11, [sp], #4 \n\t" - : [regs] "=r" (rregs), [fn] "=r" (rfn) - : "0" (rregs), "1" (rfn) - : "r0", "r2", "r3", "r4", "r5", "r6", "r7", - "r8", "r9", "r10", "r12", "memory", "cc" - ); -} - -static void __kprobes -emulate_generic_r2_14_noflags(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - emulate_generic_r0_12_noflags(insn, asi, - (struct pt_regs *)(regs->uregs+2)); -} - -static void __kprobes -emulate_ldm_r3_15(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - emulate_generic_r0_12_noflags(insn, asi, - (struct pt_regs *)(regs->uregs+3)); - load_write_pc(regs->ARM_pc, regs); -} - -enum probes_insn __kprobes -kprobe_decode_ldmstm(probes_opcode_t insn, struct arch_probes_insn *asi, - const struct decode_header *h) -{ - probes_insn_handler_t *handler = 0; - unsigned reglist = insn & 0xffff; - int is_ldm = insn & 0x100000; - int rn = (insn >> 16) & 0xf; - - if (rn <= 12 && (reglist & 0xe000) == 0) { - /* Instruction only uses registers in the range R0..R12 */ - handler = emulate_generic_r0_12_noflags; - - } else if (rn >= 2 && (reglist & 0x8003) == 0) { - /* Instruction only uses registers in the range R2..R14 */ - rn -= 2; - reglist >>= 2; - handler = emulate_generic_r2_14_noflags; - - } else if (rn >= 3 && (reglist & 0x0007) == 0) { - /* Instruction only uses registers in the range R3..R15 */ - if (is_ldm && (reglist & 0x8000)) { - rn -= 3; - reglist >>= 3; - handler = emulate_ldm_r3_15; - } - } - - if (handler) { - /* We can emulate the instruction in (possibly) modified form */ - asi->insn[0] = __opcode_to_mem_arm((insn & 0xfff00000) | - (rn << 16) | reglist); - asi->insn_handler = handler; - return INSN_GOOD; - } - - /* Fallback to slower simulation... */ - if (reglist & 0x8000) - handler = is_ldm ? simulate_ldm1_pc : simulate_stm1_pc; - else - handler = simulate_ldm1stm1; - asi->insn_handler = handler; - return INSN_GOOD_NO_SLOT; -} - diff --git a/arch/arm/kernel/kprobes-test-arm.c b/arch/arm/kernel/kprobes-test-arm.c deleted file mode 100644 index cb14242..0000000 --- a/arch/arm/kernel/kprobes-test-arm.c +++ /dev/null @@ -1,1346 +0,0 @@ -/* - * arch/arm/kernel/kprobes-test-arm.c - * - * Copyright (C) 2011 Jon Medhurst . - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include - -#include "kprobes-test.h" - - -#define TEST_ISA "32" - -#define TEST_ARM_TO_THUMB_INTERWORK_R(code1, reg, val, code2) \ - TESTCASE_START(code1 #reg code2) \ - TEST_ARG_REG(reg, val) \ - TEST_ARG_REG(14, 99f) \ - TEST_ARG_END("") \ - "50: nop \n\t" \ - "1: "code1 #reg code2" \n\t" \ - " bx lr \n\t" \ - ".thumb \n\t" \ - "3: adr lr, 2f \n\t" \ - " bx lr \n\t" \ - ".arm \n\t" \ - "2: nop \n\t" \ - TESTCASE_END - -#define TEST_ARM_TO_THUMB_INTERWORK_P(code1, reg, val, code2) \ - TESTCASE_START(code1 #reg code2) \ - TEST_ARG_PTR(reg, val) \ - TEST_ARG_REG(14, 99f) \ - TEST_ARG_MEM(15, 3f+1) \ - TEST_ARG_END("") \ - "50: nop \n\t" \ - "1: "code1 #reg code2" \n\t" \ - " bx lr \n\t" \ - ".thumb \n\t" \ - "3: adr lr, 2f \n\t" \ - " bx lr \n\t" \ - ".arm \n\t" \ - "2: nop \n\t" \ - TESTCASE_END - - -void kprobe_arm_test_cases(void) -{ - kprobe_test_flags = 0; - - TEST_GROUP("Data-processing (register), (register-shifted register), (immediate)") - -#define _DATA_PROCESSING_DNM(op,s,val) \ - TEST_RR( op "eq" s " r0, r",1, VAL1,", r",2, val, "") \ - TEST_RR( op "ne" s " r1, r",1, VAL1,", r",2, val, ", lsl #3") \ - TEST_RR( op "cs" s " r2, r",3, VAL1,", r",2, val, ", lsr #4") \ - TEST_RR( op "cc" s " r3, r",3, VAL1,", r",2, val, ", asr #5") \ - TEST_RR( op "mi" s " r4, r",5, VAL1,", r",2, N(val),", asr #6") \ - TEST_RR( op "pl" s " r5, r",5, VAL1,", r",2, val, ", ror #7") \ - TEST_RR( op "vs" s " r6, r",7, VAL1,", r",2, val, ", rrx") \ - TEST_R( op "vc" s " r6, r",7, VAL1,", pc, lsl #3") \ - TEST_R( op "vc" s " r6, r",7, VAL1,", sp, lsr #4") \ - TEST_R( op "vc" s " r6, pc, r",7, VAL1,", asr #5") \ - TEST_R( op "vc" s " r6, sp, r",7, VAL1,", ror #6") \ - TEST_RRR( op "hi" s " r8, r",9, VAL1,", r",14,val, ", lsl r",0, 3,"")\ - TEST_RRR( op "ls" s " r9, r",9, VAL1,", r",14,val, ", lsr r",7, 4,"")\ - TEST_RRR( op "ge" s " r10, r",11,VAL1,", r",14,val, ", asr r",7, 5,"")\ - TEST_RRR( op "lt" s " r11, r",11,VAL1,", r",14,N(val),", asr r",7, 6,"")\ - TEST_RR( op "gt" s " r12, r13" ", r",14,val, ", ror r",14,7,"")\ - TEST_RR( op "le" s " r14, r",0, val, ", r13" ", lsl r",14,8,"")\ - TEST_R( op "eq" s " r0, r",11,VAL1,", #0xf5") \ - TEST_R( op "ne" s " r11, r",0, VAL1,", #0xf5000000") \ - TEST_R( op s " r7, r",8, VAL2,", #0x000af000") \ - TEST( op s " r4, pc" ", #0x00005a00") - -#define DATA_PROCESSING_DNM(op,val) \ - _DATA_PROCESSING_DNM(op,"",val) \ - _DATA_PROCESSING_DNM(op,"s",val) - -#define DATA_PROCESSING_NM(op,val) \ - TEST_RR( op "ne r",1, VAL1,", r",2, val, "") \ - TEST_RR( op "eq r",1, VAL1,", r",2, val, ", lsl #3") \ - TEST_RR( op "cc r",3, VAL1,", r",2, val, ", lsr #4") \ - TEST_RR( op "cs r",3, VAL1,", r",2, val, ", asr #5") \ - TEST_RR( op "pl r",5, VAL1,", r",2, N(val),", asr #6") \ - TEST_RR( op "mi r",5, VAL1,", r",2, val, ", ror #7") \ - TEST_RR( op "vc r",7, VAL1,", r",2, val, ", rrx") \ - TEST_R ( op "vs r",7, VAL1,", pc, lsl #3") \ - TEST_R ( op "vs r",7, VAL1,", sp, lsr #4") \ - TEST_R( op "vs pc, r",7, VAL1,", asr #5") \ - TEST_R( op "vs sp, r",7, VAL1,", ror #6") \ - TEST_RRR( op "ls r",9, VAL1,", r",14,val, ", lsl r",0, 3,"") \ - TEST_RRR( op "hi r",9, VAL1,", r",14,val, ", lsr r",7, 4,"") \ - TEST_RRR( op "lt r",11,VAL1,", r",14,val, ", asr r",7, 5,"") \ - TEST_RRR( op "ge r",11,VAL1,", r",14,N(val),", asr r",7, 6,"") \ - TEST_RR( op "le r13" ", r",14,val, ", ror r",14,7,"") \ - TEST_RR( op "gt r",0, val, ", r13" ", lsl r",14,8,"") \ - TEST_R( op "eq r",11,VAL1,", #0xf5") \ - TEST_R( op "ne r",0, VAL1,", #0xf5000000") \ - TEST_R( op " r",8, VAL2,", #0x000af000") - -#define _DATA_PROCESSING_DM(op,s,val) \ - TEST_R( op "eq" s " r0, r",1, val, "") \ - TEST_R( op "ne" s " r1, r",1, val, ", lsl #3") \ - TEST_R( op "cs" s " r2, r",3, val, ", lsr #4") \ - TEST_R( op "cc" s " r3, r",3, val, ", asr #5") \ - TEST_R( op "mi" s " r4, r",5, N(val),", asr #6") \ - TEST_R( op "pl" s " r5, r",5, val, ", ror #7") \ - TEST_R( op "vs" s " r6, r",10,val, ", rrx") \ - TEST( op "vs" s " r7, pc, lsl #3") \ - TEST( op "vs" s " r7, sp, lsr #4") \ - TEST_RR( op "vc" s " r8, r",7, val, ", lsl r",0, 3,"") \ - TEST_RR( op "hi" s " r9, r",9, val, ", lsr r",7, 4,"") \ - TEST_RR( op "ls" s " r10, r",9, val, ", asr r",7, 5,"") \ - TEST_RR( op "ge" s " r11, r",11,N(val),", asr r",7, 6,"") \ - TEST_RR( op "lt" s " r12, r",11,val, ", ror r",14,7,"") \ - TEST_R( op "gt" s " r14, r13" ", lsl r",14,8,"") \ - TEST( op "eq" s " r0, #0xf5") \ - TEST( op "ne" s " r11, #0xf5000000") \ - TEST( op s " r7, #0x000af000") \ - TEST( op s " r4, #0x00005a00") - -#define DATA_PROCESSING_DM(op,val) \ - _DATA_PROCESSING_DM(op,"",val) \ - _DATA_PROCESSING_DM(op,"s",val) - - DATA_PROCESSING_DNM("and",0xf00f00ff) - DATA_PROCESSING_DNM("eor",0xf00f00ff) - DATA_PROCESSING_DNM("sub",VAL2) - DATA_PROCESSING_DNM("rsb",VAL2) - DATA_PROCESSING_DNM("add",VAL2) - DATA_PROCESSING_DNM("adc",VAL2) - DATA_PROCESSING_DNM("sbc",VAL2) - DATA_PROCESSING_DNM("rsc",VAL2) - DATA_PROCESSING_NM("tst",0xf00f00ff) - DATA_PROCESSING_NM("teq",0xf00f00ff) - DATA_PROCESSING_NM("cmp",VAL2) - DATA_PROCESSING_NM("cmn",VAL2) - DATA_PROCESSING_DNM("orr",0xf00f00ff) - DATA_PROCESSING_DM("mov",VAL2) - DATA_PROCESSING_DNM("bic",0xf00f00ff) - DATA_PROCESSING_DM("mvn",VAL2) - - TEST("mov ip, sp") /* This has special case emulation code */ - - TEST_SUPPORTED("mov pc, #0x1000"); - TEST_SUPPORTED("mov sp, #0x1000"); - TEST_SUPPORTED("cmp pc, #0x1000"); - TEST_SUPPORTED("cmp sp, #0x1000"); - - /* Data-processing with PC and a shift count in a register */ - TEST_UNSUPPORTED(__inst_arm(0xe15c0f1e) " @ cmp r12, r14, asl pc") - TEST_UNSUPPORTED(__inst_arm(0xe1a0cf1e) " @ mov r12, r14, asl pc") - TEST_UNSUPPORTED(__inst_arm(0xe08caf1e) " @ add r10, r12, r14, asl pc") - TEST_UNSUPPORTED(__inst_arm(0xe151021f) " @ cmp r1, pc, lsl r2") - TEST_UNSUPPORTED(__inst_arm(0xe17f0211) " @ cmn pc, r1, lsl r2") - TEST_UNSUPPORTED(__inst_arm(0xe1a0121f) " @ mov r1, pc, lsl r2") - TEST_UNSUPPORTED(__inst_arm(0xe1a0f211) " @ mov pc, r1, lsl r2") - TEST_UNSUPPORTED(__inst_arm(0xe042131f) " @ sub r1, r2, pc, lsl r3") - TEST_UNSUPPORTED(__inst_arm(0xe1cf1312) " @ bic r1, pc, r2, lsl r3") - TEST_UNSUPPORTED(__inst_arm(0xe081f312) " @ add pc, r1, r2, lsl r3") - - /* Data-processing with PC as a target and status registers updated */ - TEST_UNSUPPORTED("movs pc, r1") - TEST_UNSUPPORTED("movs pc, r1, lsl r2") - TEST_UNSUPPORTED("movs pc, #0x10000") - TEST_UNSUPPORTED("adds pc, lr, r1") - TEST_UNSUPPORTED("adds pc, lr, r1, lsl r2") - TEST_UNSUPPORTED("adds pc, lr, #4") - - /* Data-processing with SP as target */ - TEST("add sp, sp, #16") - TEST("sub sp, sp, #8") - TEST("bic sp, sp, #0x20") - TEST("orr sp, sp, #0x20") - TEST_PR( "add sp, r",10,0,", r",11,4,"") - TEST_PRR("add sp, r",10,0,", r",11,4,", asl r",12,1,"") - TEST_P( "mov sp, r",10,0,"") - TEST_PR( "mov sp, r",10,0,", asl r",12,0,"") - - /* Data-processing with PC as target */ - TEST_BF( "add pc, pc, #2f-1b-8") - TEST_BF_R ("add pc, pc, r",14,2f-1f-8,"") - TEST_BF_R ("add pc, r",14,2f-1f-8,", pc") - TEST_BF_R ("mov pc, r",0,2f,"") - TEST_BF_R ("add pc, pc, r",14,(2f-1f-8)*2,", asr #1") - TEST_BB( "sub pc, pc, #1b-2b+8") -#if __LINUX_ARM_ARCH__ == 6 && !defined(CONFIG_CPU_V7) - TEST_BB( "sub pc, pc, #1b-2b+8-2") /* UNPREDICTABLE before and after ARMv6 */ -#endif - TEST_BB_R( "sub pc, pc, r",14, 1f-2f+8,"") - TEST_BB_R( "rsb pc, r",14,1f-2f+8,", pc") - TEST_R( "add pc, pc, r",10,-2,", asl #1") -#ifdef CONFIG_THUMB2_KERNEL - TEST_ARM_TO_THUMB_INTERWORK_R("add pc, pc, r",0,3f-1f-8+1,"") - TEST_ARM_TO_THUMB_INTERWORK_R("sub pc, r",0,3f+8+1,", #8") -#endif - TEST_GROUP("Miscellaneous instructions") - - TEST("mrs r0, cpsr") - TEST("mrspl r7, cpsr") - TEST("mrs r14, cpsr") - TEST_UNSUPPORTED(__inst_arm(0xe10ff000) " @ mrs r15, cpsr") - TEST_UNSUPPORTED("mrs r0, spsr") - TEST_UNSUPPORTED("mrs lr, spsr") - - TEST_UNSUPPORTED("msr cpsr, r0") - TEST_UNSUPPORTED("msr cpsr_f, lr") - TEST_UNSUPPORTED("msr spsr, r0") - - TEST_BF_R("bx r",0,2f,"") - TEST_BB_R("bx r",7,2f,"") - TEST_BF_R("bxeq r",14,2f,"") - -#if __LINUX_ARM_ARCH__ >= 5 - TEST_R("clz r0, r",0, 0x0,"") - TEST_R("clzeq r7, r",14,0x1,"") - TEST_R("clz lr, r",7, 0xffffffff,"") - TEST( "clz r4, sp") - TEST_UNSUPPORTED(__inst_arm(0x016fff10) " @ clz pc, r0") - TEST_UNSUPPORTED(__inst_arm(0x016f0f1f) " @ clz r0, pc") - -#if __LINUX_ARM_ARCH__ >= 6 - TEST_UNSUPPORTED("bxj r0") -#endif - - TEST_BF_R("blx r",0,2f,"") - TEST_BB_R("blx r",7,2f,"") - TEST_BF_R("blxeq r",14,2f,"") - TEST_UNSUPPORTED(__inst_arm(0x0120003f) " @ blx pc") - - TEST_RR( "qadd r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "qaddvs lr, r",9, VAL2,", r",8, VAL1,"") - TEST_R( "qadd lr, r",9, VAL2,", r13") - TEST_RR( "qsub r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "qsubvs lr, r",9, VAL2,", r",8, VAL1,"") - TEST_R( "qsub lr, r",9, VAL2,", r13") - TEST_RR( "qdadd r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "qdaddvs lr, r",9, VAL2,", r",8, VAL1,"") - TEST_R( "qdadd lr, r",9, VAL2,", r13") - TEST_RR( "qdsub r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "qdsubvs lr, r",9, VAL2,", r",8, VAL1,"") - TEST_R( "qdsub lr, r",9, VAL2,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe101f050) " @ qadd pc, r0, r1") - TEST_UNSUPPORTED(__inst_arm(0xe121f050) " @ qsub pc, r0, r1") - TEST_UNSUPPORTED(__inst_arm(0xe141f050) " @ qdadd pc, r0, r1") - TEST_UNSUPPORTED(__inst_arm(0xe161f050) " @ qdsub pc, r0, r1") - TEST_UNSUPPORTED(__inst_arm(0xe16f2050) " @ qdsub r2, r0, pc") - TEST_UNSUPPORTED(__inst_arm(0xe161205f) " @ qdsub r2, pc, r1") - - TEST_UNSUPPORTED("bkpt 0xffff") - TEST_UNSUPPORTED("bkpt 0x0000") - - TEST_UNSUPPORTED(__inst_arm(0xe1600070) " @ smc #0") - - TEST_GROUP("Halfword multiply and multiply-accumulate") - - TEST_RRR( "smlabb r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "smlabbge r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_RR( "smlabb lr, r",1, VAL2,", r",2, VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe10f3281) " @ smlabb pc, r1, r2, r3") - TEST_RRR( "smlatb r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "smlatbge r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_RR( "smlatb lr, r",1, VAL2,", r",2, VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe10f32a1) " @ smlatb pc, r1, r2, r3") - TEST_RRR( "smlabt r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "smlabtge r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_RR( "smlabt lr, r",1, VAL2,", r",2, VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe10f32c1) " @ smlabt pc, r1, r2, r3") - TEST_RRR( "smlatt r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "smlattge r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_RR( "smlatt lr, r",1, VAL2,", r",2, VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe10f32e1) " @ smlatt pc, r1, r2, r3") - - TEST_RRR( "smlawb r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "smlawbge r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_RR( "smlawb lr, r",1, VAL2,", r",2, VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe12f3281) " @ smlawb pc, r1, r2, r3") - TEST_RRR( "smlawt r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "smlawtge r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_RR( "smlawt lr, r",1, VAL2,", r",2, VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe12f32c1) " @ smlawt pc, r1, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe12032cf) " @ smlawt r0, pc, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe1203fc1) " @ smlawt r0, r1, pc, r3") - TEST_UNSUPPORTED(__inst_arm(0xe120f2c1) " @ smlawt r0, r1, r2, pc") - - TEST_RR( "smulwb r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "smulwbge r7, r",8, VAL3,", r",9, VAL1,"") - TEST_R( "smulwb lr, r",1, VAL2,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe12f02a1) " @ smulwb pc, r1, r2") - TEST_RR( "smulwt r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "smulwtge r7, r",8, VAL3,", r",9, VAL1,"") - TEST_R( "smulwt lr, r",1, VAL2,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe12f02e1) " @ smulwt pc, r1, r2") - - TEST_RRRR( "smlalbb r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) - TEST_RRRR( "smlalbble r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) - TEST_RRR( "smlalbb r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe14f1382) " @ smlalbb pc, r1, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe141f382) " @ smlalbb r1, pc, r2, r3") - TEST_RRRR( "smlaltb r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) - TEST_RRRR( "smlaltble r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) - TEST_RRR( "smlaltb r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe14f13a2) " @ smlaltb pc, r1, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe141f3a2) " @ smlaltb r1, pc, r2, r3") - TEST_RRRR( "smlalbt r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) - TEST_RRRR( "smlalbtle r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) - TEST_RRR( "smlalbt r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe14f13c2) " @ smlalbt pc, r1, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe141f3c2) " @ smlalbt r1, pc, r2, r3") - TEST_RRRR( "smlaltt r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) - TEST_RRRR( "smlalttle r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) - TEST_RRR( "smlaltt r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe14f13e2) " @ smlalbb pc, r1, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe140f3e2) " @ smlalbb r0, pc, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe14013ef) " @ smlalbb r0, r1, pc, r3") - TEST_UNSUPPORTED(__inst_arm(0xe1401fe2) " @ smlalbb r0, r1, r2, pc") - - TEST_RR( "smulbb r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "smulbbge r7, r",8, VAL3,", r",9, VAL1,"") - TEST_R( "smulbb lr, r",1, VAL2,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe16f0281) " @ smulbb pc, r1, r2") - TEST_RR( "smultb r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "smultbge r7, r",8, VAL3,", r",9, VAL1,"") - TEST_R( "smultb lr, r",1, VAL2,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe16f02a1) " @ smultb pc, r1, r2") - TEST_RR( "smulbt r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "smulbtge r7, r",8, VAL3,", r",9, VAL1,"") - TEST_R( "smulbt lr, r",1, VAL2,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe16f02c1) " @ smultb pc, r1, r2") - TEST_RR( "smultt r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "smulttge r7, r",8, VAL3,", r",9, VAL1,"") - TEST_R( "smultt lr, r",1, VAL2,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe16f02e1) " @ smultt pc, r1, r2") - TEST_UNSUPPORTED(__inst_arm(0xe16002ef) " @ smultt r0, pc, r2") - TEST_UNSUPPORTED(__inst_arm(0xe1600fe1) " @ smultt r0, r1, pc") -#endif - - TEST_GROUP("Multiply and multiply-accumulate") - - TEST_RR( "mul r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "mulls r7, r",8, VAL2,", r",9, VAL2,"") - TEST_R( "mul lr, r",4, VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe00f0291) " @ mul pc, r1, r2") - TEST_UNSUPPORTED(__inst_arm(0xe000029f) " @ mul r0, pc, r2") - TEST_UNSUPPORTED(__inst_arm(0xe0000f91) " @ mul r0, r1, pc") - TEST_RR( "muls r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "mullss r7, r",8, VAL2,", r",9, VAL2,"") - TEST_R( "muls lr, r",4, VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe01f0291) " @ muls pc, r1, r2") - - TEST_RRR( "mla r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "mlahi r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_RR( "mla lr, r",1, VAL2,", r",2, VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe02f3291) " @ mla pc, r1, r2, r3") - TEST_RRR( "mlas r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "mlahis r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_RR( "mlas lr, r",1, VAL2,", r",2, VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe03f3291) " @ mlas pc, r1, r2, r3") - -#if __LINUX_ARM_ARCH__ >= 6 - TEST_RR( "umaal r0, r1, r",2, VAL1,", r",3, VAL2,"") - TEST_RR( "umaalls r7, r8, r",9, VAL2,", r",10, VAL1,"") - TEST_R( "umaal lr, r12, r",11,VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe041f392) " @ umaal pc, r1, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe04f0392) " @ umaal r0, pc, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe0500090) " @ undef") - TEST_UNSUPPORTED(__inst_arm(0xe05fff9f) " @ undef") -#endif - -#if __LINUX_ARM_ARCH__ >= 7 - TEST_RRR( "mls r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "mlshi r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_RR( "mls lr, r",1, VAL2,", r",2, VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe06f3291) " @ mls pc, r1, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe060329f) " @ mls r0, pc, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe0603f91) " @ mls r0, r1, pc, r3") - TEST_UNSUPPORTED(__inst_arm(0xe060f291) " @ mls r0, r1, r2, pc") -#endif - - TEST_UNSUPPORTED(__inst_arm(0xe0700090) " @ undef") - TEST_UNSUPPORTED(__inst_arm(0xe07fff9f) " @ undef") - - TEST_RR( "umull r0, r1, r",2, VAL1,", r",3, VAL2,"") - TEST_RR( "umullls r7, r8, r",9, VAL2,", r",10, VAL1,"") - TEST_R( "umull lr, r12, r",11,VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe081f392) " @ umull pc, r1, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe08f1392) " @ umull r1, pc, r2, r3") - TEST_RR( "umulls r0, r1, r",2, VAL1,", r",3, VAL2,"") - TEST_RR( "umulllss r7, r8, r",9, VAL2,", r",10, VAL1,"") - TEST_R( "umulls lr, r12, r",11,VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe091f392) " @ umulls pc, r1, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe09f1392) " @ umulls r1, pc, r2, r3") - - TEST_RRRR( "umlal r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) - TEST_RRRR( "umlalle r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) - TEST_RRR( "umlal r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe0af1392) " @ umlal pc, r1, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe0a1f392) " @ umlal r1, pc, r2, r3") - TEST_RRRR( "umlals r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) - TEST_RRRR( "umlalles r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) - TEST_RRR( "umlals r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe0bf1392) " @ umlals pc, r1, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe0b1f392) " @ umlals r1, pc, r2, r3") - - TEST_RR( "smull r0, r1, r",2, VAL1,", r",3, VAL2,"") - TEST_RR( "smullls r7, r8, r",9, VAL2,", r",10, VAL1,"") - TEST_R( "smull lr, r12, r",11,VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe0c1f392) " @ smull pc, r1, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe0cf1392) " @ smull r1, pc, r2, r3") - TEST_RR( "smulls r0, r1, r",2, VAL1,", r",3, VAL2,"") - TEST_RR( "smulllss r7, r8, r",9, VAL2,", r",10, VAL1,"") - TEST_R( "smulls lr, r12, r",11,VAL3,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe0d1f392) " @ smulls pc, r1, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe0df1392) " @ smulls r1, pc, r2, r3") - - TEST_RRRR( "smlal r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) - TEST_RRRR( "smlalle r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) - TEST_RRR( "smlal r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe0ef1392) " @ smlal pc, r1, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe0e1f392) " @ smlal r1, pc, r2, r3") - TEST_RRRR( "smlals r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) - TEST_RRRR( "smlalles r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) - TEST_RRR( "smlals r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") - TEST_UNSUPPORTED(__inst_arm(0xe0ff1392) " @ smlals pc, r1, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe0f0f392) " @ smlals r0, pc, r2, r3") - TEST_UNSUPPORTED(__inst_arm(0xe0f0139f) " @ smlals r0, r1, pc, r3") - TEST_UNSUPPORTED(__inst_arm(0xe0f01f92) " @ smlals r0, r1, r2, pc") - - TEST_GROUP("Synchronization primitives") - -#if __LINUX_ARM_ARCH__ < 6 - TEST_RP("swp lr, r",7,VAL2,", [r",8,0,"]") - TEST_R( "swpvs r0, r",1,VAL1,", [sp]") - TEST_RP("swp sp, r",14,VAL2,", [r",12,13*4,"]") -#else - TEST_UNSUPPORTED(__inst_arm(0xe108e097) " @ swp lr, r7, [r8]") - TEST_UNSUPPORTED(__inst_arm(0x610d0091) " @ swpvs r0, r1, [sp]") - TEST_UNSUPPORTED(__inst_arm(0xe10cd09e) " @ swp sp, r14 [r12]") -#endif - TEST_UNSUPPORTED(__inst_arm(0xe102f091) " @ swp pc, r1, [r2]") - TEST_UNSUPPORTED(__inst_arm(0xe102009f) " @ swp r0, pc, [r2]") - TEST_UNSUPPORTED(__inst_arm(0xe10f0091) " @ swp r0, r1, [pc]") -#if __LINUX_ARM_ARCH__ < 6 - TEST_RP("swpb lr, r",7,VAL2,", [r",8,0,"]") - TEST_R( "swpvsb r0, r",1,VAL1,", [sp]") -#else - TEST_UNSUPPORTED(__inst_arm(0xe148e097) " @ swpb lr, r7, [r8]") - TEST_UNSUPPORTED(__inst_arm(0x614d0091) " @ swpvsb r0, r1, [sp]") -#endif - TEST_UNSUPPORTED(__inst_arm(0xe142f091) " @ swpb pc, r1, [r2]") - - TEST_UNSUPPORTED(__inst_arm(0xe1100090)) /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe1200090)) /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe1300090)) /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe1500090)) /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe1600090)) /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe1700090)) /* Unallocated space */ -#if __LINUX_ARM_ARCH__ >= 6 - TEST_UNSUPPORTED("ldrex r2, [sp]") -#endif -#if (__LINUX_ARM_ARCH__ >= 7) || defined(CONFIG_CPU_32v6K) - TEST_UNSUPPORTED("strexd r0, r2, r3, [sp]") - TEST_UNSUPPORTED("ldrexd r2, r3, [sp]") - TEST_UNSUPPORTED("strexb r0, r2, [sp]") - TEST_UNSUPPORTED("ldrexb r2, [sp]") - TEST_UNSUPPORTED("strexh r0, r2, [sp]") - TEST_UNSUPPORTED("ldrexh r2, [sp]") -#endif - TEST_GROUP("Extra load/store instructions") - - TEST_RPR( "strh r",0, VAL1,", [r",1, 48,", -r",2, 24,"]") - TEST_RPR( "streqh r",14,VAL2,", [r",13,0, ", r",12, 48,"]") - TEST_RPR( "strh r",1, VAL1,", [r",2, 24,", r",3, 48,"]!") - TEST_RPR( "strneh r",12,VAL2,", [r",11,48,", -r",10,24,"]!") - TEST_RPR( "strh r",2, VAL1,", [r",3, 24,"], r",4, 48,"") - TEST_RPR( "strh r",10,VAL2,", [r",9, 48,"], -r",11,24,"") - TEST_UNSUPPORTED(__inst_arm(0xe1afc0ba) " @ strh r12, [pc, r10]!") - TEST_UNSUPPORTED(__inst_arm(0xe089f0bb) " @ strh pc, [r9], r11") - TEST_UNSUPPORTED(__inst_arm(0xe089a0bf) " @ strh r10, [r9], pc") - - TEST_PR( "ldrh r0, [r",0, 48,", -r",2, 24,"]") - TEST_PR( "ldrcsh r14, [r",13,0, ", r",12, 48,"]") - TEST_PR( "ldrh r1, [r",2, 24,", r",3, 48,"]!") - TEST_PR( "ldrcch r12, [r",11,48,", -r",10,24,"]!") - TEST_PR( "ldrh r2, [r",3, 24,"], r",4, 48,"") - TEST_PR( "ldrh r10, [r",9, 48,"], -r",11,24,"") - TEST_UNSUPPORTED(__inst_arm(0xe1bfc0ba) " @ ldrh r12, [pc, r10]!") - TEST_UNSUPPORTED(__inst_arm(0xe099f0bb) " @ ldrh pc, [r9], r11") - TEST_UNSUPPORTED(__inst_arm(0xe099a0bf) " @ ldrh r10, [r9], pc") - - TEST_RP( "strh r",0, VAL1,", [r",1, 24,", #-2]") - TEST_RP( "strmih r",14,VAL2,", [r",13,0, ", #2]") - TEST_RP( "strh r",1, VAL1,", [r",2, 24,", #4]!") - TEST_RP( "strplh r",12,VAL2,", [r",11,24,", #-4]!") - TEST_RP( "strh r",2, VAL1,", [r",3, 24,"], #48") - TEST_RP( "strh r",10,VAL2,", [r",9, 64,"], #-48") - TEST_UNSUPPORTED(__inst_arm(0xe1efc3b0) " @ strh r12, [pc, #48]!") - TEST_UNSUPPORTED(__inst_arm(0xe0c9f3b0) " @ strh pc, [r9], #48") - - TEST_P( "ldrh r0, [r",0, 24,", #-2]") - TEST_P( "ldrvsh r14, [r",13,0, ", #2]") - TEST_P( "ldrh r1, [r",2, 24,", #4]!") - TEST_P( "ldrvch r12, [r",11,24,", #-4]!") - TEST_P( "ldrh r2, [r",3, 24,"], #48") - TEST_P( "ldrh r10, [r",9, 64,"], #-48") - TEST( "ldrh r0, [pc, #0]") - TEST_UNSUPPORTED(__inst_arm(0xe1ffc3b0) " @ ldrh r12, [pc, #48]!") - TEST_UNSUPPORTED(__inst_arm(0xe0d9f3b0) " @ ldrh pc, [r9], #48") - - TEST_PR( "ldrsb r0, [r",0, 48,", -r",2, 24,"]") - TEST_PR( "ldrhisb r14, [r",13,0,", r",12, 48,"]") - TEST_PR( "ldrsb r1, [r",2, 24,", r",3, 48,"]!") - TEST_PR( "ldrlssb r12, [r",11,48,", -r",10,24,"]!") - TEST_PR( "ldrsb r2, [r",3, 24,"], r",4, 48,"") - TEST_PR( "ldrsb r10, [r",9, 48,"], -r",11,24,"") - TEST_UNSUPPORTED(__inst_arm(0xe1bfc0da) " @ ldrsb r12, [pc, r10]!") - TEST_UNSUPPORTED(__inst_arm(0xe099f0db) " @ ldrsb pc, [r9], r11") - - TEST_P( "ldrsb r0, [r",0, 24,", #-1]") - TEST_P( "ldrgesb r14, [r",13,0, ", #1]") - TEST_P( "ldrsb r1, [r",2, 24,", #4]!") - TEST_P( "ldrltsb r12, [r",11,24,", #-4]!") - TEST_P( "ldrsb r2, [r",3, 24,"], #48") - TEST_P( "ldrsb r10, [r",9, 64,"], #-48") - TEST( "ldrsb r0, [pc, #0]") - TEST_UNSUPPORTED(__inst_arm(0xe1ffc3d0) " @ ldrsb r12, [pc, #48]!") - TEST_UNSUPPORTED(__inst_arm(0xe0d9f3d0) " @ ldrsb pc, [r9], #48") - - TEST_PR( "ldrsh r0, [r",0, 48,", -r",2, 24,"]") - TEST_PR( "ldrgtsh r14, [r",13,0, ", r",12, 48,"]") - TEST_PR( "ldrsh r1, [r",2, 24,", r",3, 48,"]!") - TEST_PR( "ldrlesh r12, [r",11,48,", -r",10,24,"]!") - TEST_PR( "ldrsh r2, [r",3, 24,"], r",4, 48,"") - TEST_PR( "ldrsh r10, [r",9, 48,"], -r",11,24,"") - TEST_UNSUPPORTED(__inst_arm(0xe1bfc0fa) " @ ldrsh r12, [pc, r10]!") - TEST_UNSUPPORTED(__inst_arm(0xe099f0fb) " @ ldrsh pc, [r9], r11") - - TEST_P( "ldrsh r0, [r",0, 24,", #-1]") - TEST_P( "ldreqsh r14, [r",13,0 ,", #1]") - TEST_P( "ldrsh r1, [r",2, 24,", #4]!") - TEST_P( "ldrnesh r12, [r",11,24,", #-4]!") - TEST_P( "ldrsh r2, [r",3, 24,"], #48") - TEST_P( "ldrsh r10, [r",9, 64,"], #-48") - TEST( "ldrsh r0, [pc, #0]") - TEST_UNSUPPORTED(__inst_arm(0xe1ffc3f0) " @ ldrsh r12, [pc, #48]!") - TEST_UNSUPPORTED(__inst_arm(0xe0d9f3f0) " @ ldrsh pc, [r9], #48") - -#if __LINUX_ARM_ARCH__ >= 7 - TEST_UNSUPPORTED("strht r1, [r2], r3") - TEST_UNSUPPORTED("ldrht r1, [r2], r3") - TEST_UNSUPPORTED("strht r1, [r2], #48") - TEST_UNSUPPORTED("ldrht r1, [r2], #48") - TEST_UNSUPPORTED("ldrsbt r1, [r2], r3") - TEST_UNSUPPORTED("ldrsbt r1, [r2], #48") - TEST_UNSUPPORTED("ldrsht r1, [r2], r3") - TEST_UNSUPPORTED("ldrsht r1, [r2], #48") -#endif - -#if __LINUX_ARM_ARCH__ >= 5 - TEST_RPR( "strd r",0, VAL1,", [r",1, 48,", -r",2,24,"]") - TEST_RPR( "strccd r",8, VAL2,", [r",13,0, ", r",12,48,"]") - TEST_RPR( "strd r",4, VAL1,", [r",2, 24,", r",3, 48,"]!") - TEST_RPR( "strcsd r",12,VAL2,", [r",11,48,", -r",10,24,"]!") - TEST_RPR( "strd r",2, VAL1,", [r",5, 24,"], r",4,48,"") - TEST_RPR( "strd r",10,VAL2,", [r",9, 48,"], -r",7,24,"") - TEST_UNSUPPORTED(__inst_arm(0xe1afc0fa) " @ strd r12, [pc, r10]!") - - TEST_PR( "ldrd r0, [r",0, 48,", -r",2,24,"]") - TEST_PR( "ldrmid r8, [r",13,0, ", r",12,48,"]") - TEST_PR( "ldrd r4, [r",2, 24,", r",3, 48,"]!") - TEST_PR( "ldrpld r6, [r",11,48,", -r",10,24,"]!") - TEST_PR( "ldrd r2, [r",5, 24,"], r",4,48,"") - TEST_PR( "ldrd r10, [r",9,48,"], -r",7,24,"") - TEST_UNSUPPORTED(__inst_arm(0xe1afc0da) " @ ldrd r12, [pc, r10]!") - TEST_UNSUPPORTED(__inst_arm(0xe089f0db) " @ ldrd pc, [r9], r11") - TEST_UNSUPPORTED(__inst_arm(0xe089e0db) " @ ldrd lr, [r9], r11") - TEST_UNSUPPORTED(__inst_arm(0xe089c0df) " @ ldrd r12, [r9], pc") - - TEST_RP( "strd r",0, VAL1,", [r",1, 24,", #-8]") - TEST_RP( "strvsd r",8, VAL2,", [r",13,0, ", #8]") - TEST_RP( "strd r",4, VAL1,", [r",2, 24,", #16]!") - TEST_RP( "strvcd r",12,VAL2,", [r",11,24,", #-16]!") - TEST_RP( "strd r",2, VAL1,", [r",4, 24,"], #48") - TEST_RP( "strd r",10,VAL2,", [r",9, 64,"], #-48") - TEST_UNSUPPORTED(__inst_arm(0xe1efc3f0) " @ strd r12, [pc, #48]!") - - TEST_P( "ldrd r0, [r",0, 24,", #-8]") - TEST_P( "ldrhid r8, [r",13,0, ", #8]") - TEST_P( "ldrd r4, [r",2, 24,", #16]!") - TEST_P( "ldrlsd r6, [r",11,24,", #-16]!") - TEST_P( "ldrd r2, [r",5, 24,"], #48") - TEST_P( "ldrd r10, [r",9,6,"], #-48") - TEST_UNSUPPORTED(__inst_arm(0xe1efc3d0) " @ ldrd r12, [pc, #48]!") - TEST_UNSUPPORTED(__inst_arm(0xe0c9f3d0) " @ ldrd pc, [r9], #48") - TEST_UNSUPPORTED(__inst_arm(0xe0c9e3d0) " @ ldrd lr, [r9], #48") -#endif - - TEST_GROUP("Miscellaneous") - -#if __LINUX_ARM_ARCH__ >= 7 - TEST("movw r0, #0") - TEST("movw r0, #0xffff") - TEST("movw lr, #0xffff") - TEST_UNSUPPORTED(__inst_arm(0xe300f000) " @ movw pc, #0") - TEST_R("movt r",0, VAL1,", #0") - TEST_R("movt r",0, VAL2,", #0xffff") - TEST_R("movt r",14,VAL1,", #0xffff") - TEST_UNSUPPORTED(__inst_arm(0xe340f000) " @ movt pc, #0") -#endif - - TEST_UNSUPPORTED("msr cpsr, 0x13") - TEST_UNSUPPORTED("msr cpsr_f, 0xf0000000") - TEST_UNSUPPORTED("msr spsr, 0x13") - -#if __LINUX_ARM_ARCH__ >= 7 - TEST_SUPPORTED("yield") - TEST("sev") - TEST("nop") - TEST("wfi") - TEST_SUPPORTED("wfe") - TEST_UNSUPPORTED("dbg #0") -#endif - - TEST_GROUP("Load/store word and unsigned byte") - -#define LOAD_STORE(byte) \ - TEST_RP( "str"byte" r",0, VAL1,", [r",1, 24,", #-2]") \ - TEST_RP( "str"byte" r",14,VAL2,", [r",13,0, ", #2]") \ - TEST_RP( "str"byte" r",1, VAL1,", [r",2, 24,", #4]!") \ - TEST_RP( "str"byte" r",12,VAL2,", [r",11,24,", #-4]!") \ - TEST_RP( "str"byte" r",2, VAL1,", [r",3, 24,"], #48") \ - TEST_RP( "str"byte" r",10,VAL2,", [r",9, 64,"], #-48") \ - TEST_RPR("str"byte" r",0, VAL1,", [r",1, 48,", -r",2, 24,"]") \ - TEST_RPR("str"byte" r",14,VAL2,", [r",13,0, ", r",12, 48,"]") \ - TEST_RPR("str"byte" r",1, VAL1,", [r",2, 24,", r",3, 48,"]!") \ - TEST_RPR("str"byte" r",12,VAL2,", [r",11,48,", -r",10,24,"]!") \ - TEST_RPR("str"byte" r",2, VAL1,", [r",3, 24,"], r",4, 48,"") \ - TEST_RPR("str"byte" r",10,VAL2,", [r",9, 48,"], -r",11,24,"") \ - TEST_RPR("str"byte" r",0, VAL1,", [r",1, 24,", r",2, 32,", asl #1]")\ - TEST_RPR("str"byte" r",14,VAL2,", [r",13,0, ", r",12, 32,", lsr #2]")\ - TEST_RPR("str"byte" r",1, VAL1,", [r",2, 24,", r",3, 32,", asr #3]!")\ - TEST_RPR("str"byte" r",12,VAL2,", [r",11,24,", r",10, 4,", ror #31]!")\ - TEST_P( "ldr"byte" r0, [r",0, 24,", #-2]") \ - TEST_P( "ldr"byte" r14, [r",13,0, ", #2]") \ - TEST_P( "ldr"byte" r1, [r",2, 24,", #4]!") \ - TEST_P( "ldr"byte" r12, [r",11,24,", #-4]!") \ - TEST_P( "ldr"byte" r2, [r",3, 24,"], #48") \ - TEST_P( "ldr"byte" r10, [r",9, 64,"], #-48") \ - TEST_PR( "ldr"byte" r0, [r",0, 48,", -r",2, 24,"]") \ - TEST_PR( "ldr"byte" r14, [r",13,0, ", r",12, 48,"]") \ - TEST_PR( "ldr"byte" r1, [r",2, 24,", r",3, 48,"]!") \ - TEST_PR( "ldr"byte" r12, [r",11,48,", -r",10,24,"]!") \ - TEST_PR( "ldr"byte" r2, [r",3, 24,"], r",4, 48,"") \ - TEST_PR( "ldr"byte" r10, [r",9, 48,"], -r",11,24,"") \ - TEST_PR( "ldr"byte" r0, [r",0, 24,", r",2, 32,", asl #1]") \ - TEST_PR( "ldr"byte" r14, [r",13,0, ", r",12, 32,", lsr #2]") \ - TEST_PR( "ldr"byte" r1, [r",2, 24,", r",3, 32,", asr #3]!") \ - TEST_PR( "ldr"byte" r12, [r",11,24,", r",10, 4,", ror #31]!") \ - TEST( "ldr"byte" r0, [pc, #0]") \ - TEST_R( "ldr"byte" r12, [pc, r",14,0,"]") - - LOAD_STORE("") - TEST_P( "str pc, [r",0,0,", #15*4]") - TEST_R( "str pc, [sp, r",2,15*4,"]") - TEST_BF( "ldr pc, [sp, #15*4]") - TEST_BF_R("ldr pc, [sp, r",2,15*4,"]") - - TEST_P( "str sp, [r",0,0,", #13*4]") - TEST_R( "str sp, [sp, r",2,13*4,"]") - TEST_BF( "ldr sp, [sp, #13*4]") - TEST_BF_R("ldr sp, [sp, r",2,13*4,"]") - -#ifdef CONFIG_THUMB2_KERNEL - TEST_ARM_TO_THUMB_INTERWORK_P("ldr pc, [r",0,0,", #15*4]") -#endif - TEST_UNSUPPORTED(__inst_arm(0xe5af6008) " @ str r6, [pc, #8]!") - TEST_UNSUPPORTED(__inst_arm(0xe7af6008) " @ str r6, [pc, r8]!") - TEST_UNSUPPORTED(__inst_arm(0xe5bf6008) " @ ldr r6, [pc, #8]!") - TEST_UNSUPPORTED(__inst_arm(0xe7bf6008) " @ ldr r6, [pc, r8]!") - TEST_UNSUPPORTED(__inst_arm(0xe788600f) " @ str r6, [r8, pc]") - TEST_UNSUPPORTED(__inst_arm(0xe798600f) " @ ldr r6, [r8, pc]") - - LOAD_STORE("b") - TEST_UNSUPPORTED(__inst_arm(0xe5f7f008) " @ ldrb pc, [r7, #8]!") - TEST_UNSUPPORTED(__inst_arm(0xe7f7f008) " @ ldrb pc, [r7, r8]!") - TEST_UNSUPPORTED(__inst_arm(0xe5ef6008) " @ strb r6, [pc, #8]!") - TEST_UNSUPPORTED(__inst_arm(0xe7ef6008) " @ strb r6, [pc, r3]!") - TEST_UNSUPPORTED(__inst_arm(0xe5ff6008) " @ ldrb r6, [pc, #8]!") - TEST_UNSUPPORTED(__inst_arm(0xe7ff6008) " @ ldrb r6, [pc, r3]!") - - TEST_UNSUPPORTED("ldrt r0, [r1], #4") - TEST_UNSUPPORTED("ldrt r1, [r2], r3") - TEST_UNSUPPORTED("strt r2, [r3], #4") - TEST_UNSUPPORTED("strt r3, [r4], r5") - TEST_UNSUPPORTED("ldrbt r4, [r5], #4") - TEST_UNSUPPORTED("ldrbt r5, [r6], r7") - TEST_UNSUPPORTED("strbt r6, [r7], #4") - TEST_UNSUPPORTED("strbt r7, [r8], r9") - -#if __LINUX_ARM_ARCH__ >= 7 - TEST_GROUP("Parallel addition and subtraction, signed") - - TEST_UNSUPPORTED(__inst_arm(0xe6000010) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe60fffff) "") /* Unallocated space */ - - TEST_RR( "sadd16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "sadd16 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe61cff1a) " @ sadd16 pc, r12, r10") - TEST_RR( "sasx r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "sasx r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe61cff3a) " @ sasx pc, r12, r10") - TEST_RR( "ssax r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "ssax r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe61cff5a) " @ ssax pc, r12, r10") - TEST_RR( "ssub16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "ssub16 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe61cff7a) " @ ssub16 pc, r12, r10") - TEST_RR( "sadd8 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "sadd8 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe61cff9a) " @ sadd8 pc, r12, r10") - TEST_UNSUPPORTED(__inst_arm(0xe61000b0) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe61fffbf) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe61000d0) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe61fffdf) "") /* Unallocated space */ - TEST_RR( "ssub8 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "ssub8 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe61cfffa) " @ ssub8 pc, r12, r10") - - TEST_RR( "qadd16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "qadd16 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe62cff1a) " @ qadd16 pc, r12, r10") - TEST_RR( "qasx r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "qasx r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe62cff3a) " @ qasx pc, r12, r10") - TEST_RR( "qsax r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "qsax r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe62cff5a) " @ qsax pc, r12, r10") - TEST_RR( "qsub16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "qsub16 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe62cff7a) " @ qsub16 pc, r12, r10") - TEST_RR( "qadd8 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "qadd8 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe62cff9a) " @ qadd8 pc, r12, r10") - TEST_UNSUPPORTED(__inst_arm(0xe62000b0) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe62fffbf) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe62000d0) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe62fffdf) "") /* Unallocated space */ - TEST_RR( "qsub8 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "qsub8 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe62cfffa) " @ qsub8 pc, r12, r10") - - TEST_RR( "shadd16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "shadd16 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe63cff1a) " @ shadd16 pc, r12, r10") - TEST_RR( "shasx r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "shasx r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe63cff3a) " @ shasx pc, r12, r10") - TEST_RR( "shsax r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "shsax r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe63cff5a) " @ shsax pc, r12, r10") - TEST_RR( "shsub16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "shsub16 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe63cff7a) " @ shsub16 pc, r12, r10") - TEST_RR( "shadd8 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "shadd8 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe63cff9a) " @ shadd8 pc, r12, r10") - TEST_UNSUPPORTED(__inst_arm(0xe63000b0) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe63fffbf) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe63000d0) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe63fffdf) "") /* Unallocated space */ - TEST_RR( "shsub8 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "shsub8 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe63cfffa) " @ shsub8 pc, r12, r10") - - TEST_GROUP("Parallel addition and subtraction, unsigned") - - TEST_UNSUPPORTED(__inst_arm(0xe6400010) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe64fffff) "") /* Unallocated space */ - - TEST_RR( "uadd16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uadd16 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe65cff1a) " @ uadd16 pc, r12, r10") - TEST_RR( "uasx r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uasx r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe65cff3a) " @ uasx pc, r12, r10") - TEST_RR( "usax r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "usax r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe65cff5a) " @ usax pc, r12, r10") - TEST_RR( "usub16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "usub16 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe65cff7a) " @ usub16 pc, r12, r10") - TEST_RR( "uadd8 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uadd8 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe65cff9a) " @ uadd8 pc, r12, r10") - TEST_UNSUPPORTED(__inst_arm(0xe65000b0) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe65fffbf) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe65000d0) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe65fffdf) "") /* Unallocated space */ - TEST_RR( "usub8 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "usub8 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe65cfffa) " @ usub8 pc, r12, r10") - - TEST_RR( "uqadd16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uqadd16 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe66cff1a) " @ uqadd16 pc, r12, r10") - TEST_RR( "uqasx r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uqasx r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe66cff3a) " @ uqasx pc, r12, r10") - TEST_RR( "uqsax r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uqsax r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe66cff5a) " @ uqsax pc, r12, r10") - TEST_RR( "uqsub16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uqsub16 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe66cff7a) " @ uqsub16 pc, r12, r10") - TEST_RR( "uqadd8 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uqadd8 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe66cff9a) " @ uqadd8 pc, r12, r10") - TEST_UNSUPPORTED(__inst_arm(0xe66000b0) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe66fffbf) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe66000d0) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe66fffdf) "") /* Unallocated space */ - TEST_RR( "uqsub8 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uqsub8 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe66cfffa) " @ uqsub8 pc, r12, r10") - - TEST_RR( "uhadd16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uhadd16 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe67cff1a) " @ uhadd16 pc, r12, r10") - TEST_RR( "uhasx r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uhasx r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe67cff3a) " @ uhasx pc, r12, r10") - TEST_RR( "uhsax r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uhsax r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe67cff5a) " @ uhsax pc, r12, r10") - TEST_RR( "uhsub16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uhsub16 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe67cff7a) " @ uhsub16 pc, r12, r10") - TEST_RR( "uhadd8 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uhadd8 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe67cff9a) " @ uhadd8 pc, r12, r10") - TEST_UNSUPPORTED(__inst_arm(0xe67000b0) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe67fffbf) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe67000d0) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe67fffdf) "") /* Unallocated space */ - TEST_RR( "uhsub8 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uhsub8 r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe67cfffa) " @ uhsub8 pc, r12, r10") - TEST_UNSUPPORTED(__inst_arm(0xe67feffa) " @ uhsub8 r14, pc, r10") - TEST_UNSUPPORTED(__inst_arm(0xe67cefff) " @ uhsub8 r14, r12, pc") -#endif /* __LINUX_ARM_ARCH__ >= 7 */ - -#if __LINUX_ARM_ARCH__ >= 6 - TEST_GROUP("Packing, unpacking, saturation, and reversal") - - TEST_RR( "pkhbt r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "pkhbt r14,r",12, HH1,", r",10,HH2,", lsl #2") - TEST_UNSUPPORTED(__inst_arm(0xe68cf11a) " @ pkhbt pc, r12, r10, lsl #2") - TEST_RR( "pkhtb r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "pkhtb r14,r",12, HH1,", r",10,HH2,", asr #2") - TEST_UNSUPPORTED(__inst_arm(0xe68cf15a) " @ pkhtb pc, r12, r10, asr #2") - TEST_UNSUPPORTED(__inst_arm(0xe68fe15a) " @ pkhtb r14, pc, r10, asr #2") - TEST_UNSUPPORTED(__inst_arm(0xe68ce15f) " @ pkhtb r14, r12, pc, asr #2") - TEST_UNSUPPORTED(__inst_arm(0xe6900010) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe69fffdf) "") /* Unallocated space */ - - TEST_R( "ssat r0, #24, r",0, VAL1,"") - TEST_R( "ssat r14, #24, r",12, VAL2,"") - TEST_R( "ssat r0, #24, r",0, VAL1,", lsl #8") - TEST_R( "ssat r14, #24, r",12, VAL2,", asr #8") - TEST_UNSUPPORTED(__inst_arm(0xe6b7f01c) " @ ssat pc, #24, r12") - - TEST_R( "usat r0, #24, r",0, VAL1,"") - TEST_R( "usat r14, #24, r",12, VAL2,"") - TEST_R( "usat r0, #24, r",0, VAL1,", lsl #8") - TEST_R( "usat r14, #24, r",12, VAL2,", asr #8") - TEST_UNSUPPORTED(__inst_arm(0xe6f7f01c) " @ usat pc, #24, r12") - - TEST_RR( "sxtab16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "sxtab16 r14,r",12, HH2,", r",10,HH1,", ror #8") - TEST_R( "sxtb16 r8, r",7, HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe68cf47a) " @ sxtab16 pc,r12, r10, ror #8") - - TEST_RR( "sel r0, r",0, VAL1,", r",1, VAL2,"") - TEST_RR( "sel r14, r",12,VAL1,", r",10, VAL2,"") - TEST_UNSUPPORTED(__inst_arm(0xe68cffba) " @ sel pc, r12, r10") - TEST_UNSUPPORTED(__inst_arm(0xe68fefba) " @ sel r14, pc, r10") - TEST_UNSUPPORTED(__inst_arm(0xe68cefbf) " @ sel r14, r12, pc") - - TEST_R( "ssat16 r0, #12, r",0, HH1,"") - TEST_R( "ssat16 r14, #12, r",12, HH2,"") - TEST_UNSUPPORTED(__inst_arm(0xe6abff3c) " @ ssat16 pc, #12, r12") - - TEST_RR( "sxtab r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "sxtab r14,r",12, HH2,", r",10,HH1,", ror #8") - TEST_R( "sxtb r8, r",7, HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe6acf47a) " @ sxtab pc,r12, r10, ror #8") - - TEST_R( "rev r0, r",0, VAL1,"") - TEST_R( "rev r14, r",12, VAL2,"") - TEST_UNSUPPORTED(__inst_arm(0xe6bfff3c) " @ rev pc, r12") - - TEST_RR( "sxtah r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "sxtah r14,r",12, HH2,", r",10,HH1,", ror #8") - TEST_R( "sxth r8, r",7, HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe6bcf47a) " @ sxtah pc,r12, r10, ror #8") - - TEST_R( "rev16 r0, r",0, VAL1,"") - TEST_R( "rev16 r14, r",12, VAL2,"") - TEST_UNSUPPORTED(__inst_arm(0xe6bfffbc) " @ rev16 pc, r12") - - TEST_RR( "uxtab16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uxtab16 r14,r",12, HH2,", r",10,HH1,", ror #8") - TEST_R( "uxtb16 r8, r",7, HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe6ccf47a) " @ uxtab16 pc,r12, r10, ror #8") - - TEST_R( "usat16 r0, #12, r",0, HH1,"") - TEST_R( "usat16 r14, #12, r",12, HH2,"") - TEST_UNSUPPORTED(__inst_arm(0xe6ecff3c) " @ usat16 pc, #12, r12") - TEST_UNSUPPORTED(__inst_arm(0xe6ecef3f) " @ usat16 r14, #12, pc") - - TEST_RR( "uxtab r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uxtab r14,r",12, HH2,", r",10,HH1,", ror #8") - TEST_R( "uxtb r8, r",7, HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe6ecf47a) " @ uxtab pc,r12, r10, ror #8") - -#if __LINUX_ARM_ARCH__ >= 7 - TEST_R( "rbit r0, r",0, VAL1,"") - TEST_R( "rbit r14, r",12, VAL2,"") - TEST_UNSUPPORTED(__inst_arm(0xe6ffff3c) " @ rbit pc, r12") -#endif - - TEST_RR( "uxtah r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uxtah r14,r",12, HH2,", r",10,HH1,", ror #8") - TEST_R( "uxth r8, r",7, HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe6fff077) " @ uxth pc, r7") - TEST_UNSUPPORTED(__inst_arm(0xe6ff807f) " @ uxth r8, pc") - TEST_UNSUPPORTED(__inst_arm(0xe6fcf47a) " @ uxtah pc, r12, r10, ror #8") - TEST_UNSUPPORTED(__inst_arm(0xe6fce47f) " @ uxtah r14, r12, pc, ror #8") - - TEST_R( "revsh r0, r",0, VAL1,"") - TEST_R( "revsh r14, r",12, VAL2,"") - TEST_UNSUPPORTED(__inst_arm(0xe6ffff3c) " @ revsh pc, r12") - TEST_UNSUPPORTED(__inst_arm(0xe6ffef3f) " @ revsh r14, pc") - - TEST_UNSUPPORTED(__inst_arm(0xe6900070) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe69fff7f) "") /* Unallocated space */ - - TEST_UNSUPPORTED(__inst_arm(0xe6d00070) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_arm(0xe6dfff7f) "") /* Unallocated space */ -#endif /* __LINUX_ARM_ARCH__ >= 6 */ - -#if __LINUX_ARM_ARCH__ >= 6 - TEST_GROUP("Signed multiplies") - - TEST_RRR( "smlad r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") - TEST_RRR( "smlad r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") - TEST_UNSUPPORTED(__inst_arm(0xe70f8a1c) " @ smlad pc, r12, r10, r8") - TEST_RRR( "smladx r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") - TEST_RRR( "smladx r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") - TEST_UNSUPPORTED(__inst_arm(0xe70f8a3c) " @ smladx pc, r12, r10, r8") - - TEST_RR( "smuad r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "smuad r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe70ffa1c) " @ smuad pc, r12, r10") - TEST_RR( "smuadx r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "smuadx r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe70ffa3c) " @ smuadx pc, r12, r10") - - TEST_RRR( "smlsd r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") - TEST_RRR( "smlsd r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") - TEST_UNSUPPORTED(__inst_arm(0xe70f8a5c) " @ smlsd pc, r12, r10, r8") - TEST_RRR( "smlsdx r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") - TEST_RRR( "smlsdx r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") - TEST_UNSUPPORTED(__inst_arm(0xe70f8a7c) " @ smlsdx pc, r12, r10, r8") - - TEST_RR( "smusd r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "smusd r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe70ffa5c) " @ smusd pc, r12, r10") - TEST_RR( "smusdx r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "smusdx r14, r",12,HH2,", r",10,HH1,"") - TEST_UNSUPPORTED(__inst_arm(0xe70ffa7c) " @ smusdx pc, r12, r10") - - TEST_RRRR( "smlald r",0, VAL1,", r",1, VAL2, ", r",0, HH1,", r",1, HH2) - TEST_RRRR( "smlald r",11,VAL2,", r",10,VAL1, ", r",9, HH2,", r",8, HH1) - TEST_UNSUPPORTED(__inst_arm(0xe74af819) " @ smlald pc, r10, r9, r8") - TEST_UNSUPPORTED(__inst_arm(0xe74fb819) " @ smlald r11, pc, r9, r8") - TEST_UNSUPPORTED(__inst_arm(0xe74ab81f) " @ smlald r11, r10, pc, r8") - TEST_UNSUPPORTED(__inst_arm(0xe74abf19) " @ smlald r11, r10, r9, pc") - - TEST_RRRR( "smlaldx r",0, VAL1,", r",1, VAL2, ", r",0, HH1,", r",1, HH2) - TEST_RRRR( "smlaldx r",11,VAL2,", r",10,VAL1, ", r",9, HH2,", r",8, HH1) - TEST_UNSUPPORTED(__inst_arm(0xe74af839) " @ smlaldx pc, r10, r9, r8") - TEST_UNSUPPORTED(__inst_arm(0xe74fb839) " @ smlaldx r11, pc, r9, r8") - - TEST_RRR( "smmla r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") - TEST_RRR( "smmla r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") - TEST_UNSUPPORTED(__inst_arm(0xe75f8a1c) " @ smmla pc, r12, r10, r8") - TEST_RRR( "smmlar r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") - TEST_RRR( "smmlar r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") - TEST_UNSUPPORTED(__inst_arm(0xe75f8a3c) " @ smmlar pc, r12, r10, r8") - - TEST_RR( "smmul r0, r",0, VAL1,", r",1, VAL2,"") - TEST_RR( "smmul r14, r",12,VAL2,", r",10,VAL1,"") - TEST_UNSUPPORTED(__inst_arm(0xe75ffa1c) " @ smmul pc, r12, r10") - TEST_RR( "smmulr r0, r",0, VAL1,", r",1, VAL2,"") - TEST_RR( "smmulr r14, r",12,VAL2,", r",10,VAL1,"") - TEST_UNSUPPORTED(__inst_arm(0xe75ffa3c) " @ smmulr pc, r12, r10") - - TEST_RRR( "smmls r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") - TEST_RRR( "smmls r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") - TEST_UNSUPPORTED(__inst_arm(0xe75f8adc) " @ smmls pc, r12, r10, r8") - TEST_RRR( "smmlsr r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") - TEST_RRR( "smmlsr r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") - TEST_UNSUPPORTED(__inst_arm(0xe75f8afc) " @ smmlsr pc, r12, r10, r8") - TEST_UNSUPPORTED(__inst_arm(0xe75e8aff) " @ smmlsr r14, pc, r10, r8") - TEST_UNSUPPORTED(__inst_arm(0xe75e8ffc) " @ smmlsr r14, r12, pc, r8") - TEST_UNSUPPORTED(__inst_arm(0xe75efafc) " @ smmlsr r14, r12, r10, pc") - - TEST_RR( "usad8 r0, r",0, VAL1,", r",1, VAL2,"") - TEST_RR( "usad8 r14, r",12,VAL2,", r",10,VAL1,"") - TEST_UNSUPPORTED(__inst_arm(0xe75ffa1c) " @ usad8 pc, r12, r10") - TEST_UNSUPPORTED(__inst_arm(0xe75efa1f) " @ usad8 r14, pc, r10") - TEST_UNSUPPORTED(__inst_arm(0xe75eff1c) " @ usad8 r14, r12, pc") - - TEST_RRR( "usada8 r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL3,"") - TEST_RRR( "usada8 r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL3,"") - TEST_UNSUPPORTED(__inst_arm(0xe78f8a1c) " @ usada8 pc, r12, r10, r8") - TEST_UNSUPPORTED(__inst_arm(0xe78e8a1f) " @ usada8 r14, pc, r10, r8") - TEST_UNSUPPORTED(__inst_arm(0xe78e8f1c) " @ usada8 r14, r12, pc, r8") -#endif /* __LINUX_ARM_ARCH__ >= 6 */ - -#if __LINUX_ARM_ARCH__ >= 7 - TEST_GROUP("Bit Field") - - TEST_R( "sbfx r0, r",0 , VAL1,", #0, #31") - TEST_R( "sbfxeq r14, r",12, VAL2,", #8, #16") - TEST_R( "sbfx r4, r",10, VAL1,", #16, #15") - TEST_UNSUPPORTED(__inst_arm(0xe7aff45c) " @ sbfx pc, r12, #8, #16") - - TEST_R( "ubfx r0, r",0 , VAL1,", #0, #31") - TEST_R( "ubfxcs r14, r",12, VAL2,", #8, #16") - TEST_R( "ubfx r4, r",10, VAL1,", #16, #15") - TEST_UNSUPPORTED(__inst_arm(0xe7eff45c) " @ ubfx pc, r12, #8, #16") - TEST_UNSUPPORTED(__inst_arm(0xe7efc45f) " @ ubfx r12, pc, #8, #16") - - TEST_R( "bfc r",0, VAL1,", #4, #20") - TEST_R( "bfcvs r",14,VAL2,", #4, #20") - TEST_R( "bfc r",7, VAL1,", #0, #31") - TEST_R( "bfc r",8, VAL2,", #0, #31") - TEST_UNSUPPORTED(__inst_arm(0xe7def01f) " @ bfc pc, #0, #31"); - - TEST_RR( "bfi r",0, VAL1,", r",0 , VAL2,", #0, #31") - TEST_RR( "bfipl r",12,VAL1,", r",14 , VAL2,", #4, #20") - TEST_UNSUPPORTED(__inst_arm(0xe7d7f21e) " @ bfi pc, r14, #4, #20") - - TEST_UNSUPPORTED(__inst_arm(0x07f000f0) "") /* Permanently UNDEFINED */ - TEST_UNSUPPORTED(__inst_arm(0x07ffffff) "") /* Permanently UNDEFINED */ -#endif /* __LINUX_ARM_ARCH__ >= 6 */ - - TEST_GROUP("Branch, branch with link, and block data transfer") - - TEST_P( "stmda r",0, 16*4,", {r0}") - TEST_P( "stmeqda r",4, 16*4,", {r0-r15}") - TEST_P( "stmneda r",8, 16*4,"!, {r8-r15}") - TEST_P( "stmda r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") - TEST_P( "stmda r",13,0, "!, {pc}") - - TEST_P( "ldmda r",0, 16*4,", {r0}") - TEST_BF_P("ldmcsda r",4, 15*4,", {r0-r15}") - TEST_BF_P("ldmccda r",7, 15*4,"!, {r8-r15}") - TEST_P( "ldmda r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") - TEST_BF_P("ldmda r",14,15*4,"!, {pc}") - - TEST_P( "stmia r",0, 16*4,", {r0}") - TEST_P( "stmmiia r",4, 16*4,", {r0-r15}") - TEST_P( "stmplia r",8, 16*4,"!, {r8-r15}") - TEST_P( "stmia r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") - TEST_P( "stmia r",14,0, "!, {pc}") - - TEST_P( "ldmia r",0, 16*4,", {r0}") - TEST_BF_P("ldmvsia r",4, 0, ", {r0-r15}") - TEST_BF_P("ldmvcia r",7, 8*4, "!, {r8-r15}") - TEST_P( "ldmia r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") - TEST_BF_P("ldmia r",14,15*4,"!, {pc}") - - TEST_P( "stmdb r",0, 16*4,", {r0}") - TEST_P( "stmhidb r",4, 16*4,", {r0-r15}") - TEST_P( "stmlsdb r",8, 16*4,"!, {r8-r15}") - TEST_P( "stmdb r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") - TEST_P( "stmdb r",13,4, "!, {pc}") - - TEST_P( "ldmdb r",0, 16*4,", {r0}") - TEST_BF_P("ldmgedb r",4, 16*4,", {r0-r15}") - TEST_BF_P("ldmltdb r",7, 16*4,"!, {r8-r15}") - TEST_P( "ldmdb r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") - TEST_BF_P("ldmdb r",14,16*4,"!, {pc}") - - TEST_P( "stmib r",0, 16*4,", {r0}") - TEST_P( "stmgtib r",4, 16*4,", {r0-r15}") - TEST_P( "stmleib r",8, 16*4,"!, {r8-r15}") - TEST_P( "stmib r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") - TEST_P( "stmib r",13,-4, "!, {pc}") - - TEST_P( "ldmib r",0, 16*4,", {r0}") - TEST_BF_P("ldmeqib r",4, -4,", {r0-r15}") - TEST_BF_P("ldmneib r",7, 7*4,"!, {r8-r15}") - TEST_P( "ldmib r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") - TEST_BF_P("ldmib r",14,14*4,"!, {pc}") - - TEST_P( "stmdb r",13,16*4,"!, {r3-r12,lr}") - TEST_P( "stmeqdb r",13,16*4,"!, {r3-r12}") - TEST_P( "stmnedb r",2, 16*4,", {r3-r12,lr}") - TEST_P( "stmdb r",13,16*4,"!, {r2-r12,lr}") - TEST_P( "stmdb r",0, 16*4,", {r0-r12}") - TEST_P( "stmdb r",0, 16*4,", {r0-r12,lr}") - - TEST_BF_P("ldmia r",13,5*4, "!, {r3-r12,pc}") - TEST_P( "ldmccia r",13,5*4, "!, {r3-r12}") - TEST_BF_P("ldmcsia r",2, 5*4, "!, {r3-r12,pc}") - TEST_BF_P("ldmia r",13,4*4, "!, {r2-r12,pc}") - TEST_P( "ldmia r",0, 16*4,", {r0-r12}") - TEST_P( "ldmia r",0, 16*4,", {r0-r12,lr}") - -#ifdef CONFIG_THUMB2_KERNEL - TEST_ARM_TO_THUMB_INTERWORK_P("ldmplia r",0,15*4,", {pc}") - TEST_ARM_TO_THUMB_INTERWORK_P("ldmmiia r",13,0,", {r0-r15}") -#endif - TEST_BF("b 2f") - TEST_BF("bl 2f") - TEST_BB("b 2b") - TEST_BB("bl 2b") - - TEST_BF("beq 2f") - TEST_BF("bleq 2f") - TEST_BB("bne 2b") - TEST_BB("blne 2b") - - TEST_BF("bgt 2f") - TEST_BF("blgt 2f") - TEST_BB("blt 2b") - TEST_BB("bllt 2b") - - TEST_GROUP("Supervisor Call, and coprocessor instructions") - - /* - * We can't really test these by executing them, so all - * we can do is check that probes are, or are not allowed. - * At the moment none are allowed... - */ -#define TEST_COPROCESSOR(code) TEST_UNSUPPORTED(code) - -#define COPROCESSOR_INSTRUCTIONS_ST_LD(two,cc) \ - TEST_COPROCESSOR("stc"two" 0, cr0, [r13, #4]") \ - TEST_COPROCESSOR("stc"two" 0, cr0, [r13, #-4]") \ - TEST_COPROCESSOR("stc"two" 0, cr0, [r13, #4]!") \ - TEST_COPROCESSOR("stc"two" 0, cr0, [r13, #-4]!") \ - TEST_COPROCESSOR("stc"two" 0, cr0, [r13], #4") \ - TEST_COPROCESSOR("stc"two" 0, cr0, [r13], #-4") \ - TEST_COPROCESSOR("stc"two" 0, cr0, [r13], {1}") \ - TEST_COPROCESSOR("stc"two"l 0, cr0, [r13, #4]") \ - TEST_COPROCESSOR("stc"two"l 0, cr0, [r13, #-4]") \ - TEST_COPROCESSOR("stc"two"l 0, cr0, [r13, #4]!") \ - TEST_COPROCESSOR("stc"two"l 0, cr0, [r13, #-4]!") \ - TEST_COPROCESSOR("stc"two"l 0, cr0, [r13], #4") \ - TEST_COPROCESSOR("stc"two"l 0, cr0, [r13], #-4") \ - TEST_COPROCESSOR("stc"two"l 0, cr0, [r13], {1}") \ - TEST_COPROCESSOR("ldc"two" 0, cr0, [r13, #4]") \ - TEST_COPROCESSOR("ldc"two" 0, cr0, [r13, #-4]") \ - TEST_COPROCESSOR("ldc"two" 0, cr0, [r13, #4]!") \ - TEST_COPROCESSOR("ldc"two" 0, cr0, [r13, #-4]!") \ - TEST_COPROCESSOR("ldc"two" 0, cr0, [r13], #4") \ - TEST_COPROCESSOR("ldc"two" 0, cr0, [r13], #-4") \ - TEST_COPROCESSOR("ldc"two" 0, cr0, [r13], {1}") \ - TEST_COPROCESSOR("ldc"two"l 0, cr0, [r13, #4]") \ - TEST_COPROCESSOR("ldc"two"l 0, cr0, [r13, #-4]") \ - TEST_COPROCESSOR("ldc"two"l 0, cr0, [r13, #4]!") \ - TEST_COPROCESSOR("ldc"two"l 0, cr0, [r13, #-4]!") \ - TEST_COPROCESSOR("ldc"two"l 0, cr0, [r13], #4") \ - TEST_COPROCESSOR("ldc"two"l 0, cr0, [r13], #-4") \ - TEST_COPROCESSOR("ldc"two"l 0, cr0, [r13], {1}") \ - \ - TEST_COPROCESSOR( "stc"two" 0, cr0, [r15, #4]") \ - TEST_COPROCESSOR( "stc"two" 0, cr0, [r15, #-4]") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##daf0001) " @ stc"two" 0, cr0, [r15, #4]!") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##d2f0001) " @ stc"two" 0, cr0, [r15, #-4]!") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##caf0001) " @ stc"two" 0, cr0, [r15], #4") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##c2f0001) " @ stc"two" 0, cr0, [r15], #-4") \ - TEST_COPROCESSOR( "stc"two" 0, cr0, [r15], {1}") \ - TEST_COPROCESSOR( "stc"two"l 0, cr0, [r15, #4]") \ - TEST_COPROCESSOR( "stc"two"l 0, cr0, [r15, #-4]") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##def0001) " @ stc"two"l 0, cr0, [r15, #4]!") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##d6f0001) " @ stc"two"l 0, cr0, [r15, #-4]!") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##cef0001) " @ stc"two"l 0, cr0, [r15], #4") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##c6f0001) " @ stc"two"l 0, cr0, [r15], #-4") \ - TEST_COPROCESSOR( "stc"two"l 0, cr0, [r15], {1}") \ - TEST_COPROCESSOR( "ldc"two" 0, cr0, [r15, #4]") \ - TEST_COPROCESSOR( "ldc"two" 0, cr0, [r15, #-4]") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##dbf0001) " @ ldc"two" 0, cr0, [r15, #4]!") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##d3f0001) " @ ldc"two" 0, cr0, [r15, #-4]!") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##cbf0001) " @ ldc"two" 0, cr0, [r15], #4") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##c3f0001) " @ ldc"two" 0, cr0, [r15], #-4") \ - TEST_COPROCESSOR( "ldc"two" 0, cr0, [r15], {1}") \ - TEST_COPROCESSOR( "ldc"two"l 0, cr0, [r15, #4]") \ - TEST_COPROCESSOR( "ldc"two"l 0, cr0, [r15, #-4]") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##dff0001) " @ ldc"two"l 0, cr0, [r15, #4]!") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##d7f0001) " @ ldc"two"l 0, cr0, [r15, #-4]!") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##cff0001) " @ ldc"two"l 0, cr0, [r15], #4") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##c7f0001) " @ ldc"two"l 0, cr0, [r15], #-4") \ - TEST_COPROCESSOR( "ldc"two"l 0, cr0, [r15], {1}") - -#define COPROCESSOR_INSTRUCTIONS_MC_MR(two,cc) \ - \ - TEST_COPROCESSOR( "mcrr"two" 0, 15, r0, r14, cr0") \ - TEST_COPROCESSOR( "mcrr"two" 15, 0, r14, r0, cr15") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##c4f00f0) " @ mcrr"two" 0, 15, r0, r15, cr0") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##c40ff0f) " @ mcrr"two" 15, 0, r15, r0, cr15") \ - TEST_COPROCESSOR( "mrrc"two" 0, 15, r0, r14, cr0") \ - TEST_COPROCESSOR( "mrrc"two" 15, 0, r14, r0, cr15") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##c5f00f0) " @ mrrc"two" 0, 15, r0, r15, cr0") \ - TEST_UNSUPPORTED(__inst_arm(0x##cc##c50ff0f) " @ mrrc"two" 15, 0, r15, r0, cr15") \ - TEST_COPROCESSOR( "cdp"two" 15, 15, cr15, cr15, cr15, 7") \ - TEST_COPROCESSOR( "cdp"two" 0, 0, cr0, cr0, cr0, 0") \ - TEST_COPROCESSOR( "mcr"two" 15, 7, r15, cr15, cr15, 7") \ - TEST_COPROCESSOR( "mcr"two" 0, 0, r0, cr0, cr0, 0") \ - TEST_COPROCESSOR( "mrc"two" 15, 7, r15, cr15, cr15, 7") \ - TEST_COPROCESSOR( "mrc"two" 0, 0, r0, cr0, cr0, 0") - - COPROCESSOR_INSTRUCTIONS_ST_LD("",e) -#if __LINUX_ARM_ARCH__ >= 5 - COPROCESSOR_INSTRUCTIONS_MC_MR("",e) -#endif - TEST_UNSUPPORTED("svc 0") - TEST_UNSUPPORTED("svc 0xffffff") - - TEST_UNSUPPORTED("svc 0") - - TEST_GROUP("Unconditional instruction") - -#if __LINUX_ARM_ARCH__ >= 6 - TEST_UNSUPPORTED("srsda sp, 0x13") - TEST_UNSUPPORTED("srsdb sp, 0x13") - TEST_UNSUPPORTED("srsia sp, 0x13") - TEST_UNSUPPORTED("srsib sp, 0x13") - TEST_UNSUPPORTED("srsda sp!, 0x13") - TEST_UNSUPPORTED("srsdb sp!, 0x13") - TEST_UNSUPPORTED("srsia sp!, 0x13") - TEST_UNSUPPORTED("srsib sp!, 0x13") - - TEST_UNSUPPORTED("rfeda sp") - TEST_UNSUPPORTED("rfedb sp") - TEST_UNSUPPORTED("rfeia sp") - TEST_UNSUPPORTED("rfeib sp") - TEST_UNSUPPORTED("rfeda sp!") - TEST_UNSUPPORTED("rfedb sp!") - TEST_UNSUPPORTED("rfeia sp!") - TEST_UNSUPPORTED("rfeib sp!") - TEST_UNSUPPORTED(__inst_arm(0xf81d0a00) " @ rfeda pc") - TEST_UNSUPPORTED(__inst_arm(0xf91d0a00) " @ rfedb pc") - TEST_UNSUPPORTED(__inst_arm(0xf89d0a00) " @ rfeia pc") - TEST_UNSUPPORTED(__inst_arm(0xf99d0a00) " @ rfeib pc") - TEST_UNSUPPORTED(__inst_arm(0xf83d0a00) " @ rfeda pc!") - TEST_UNSUPPORTED(__inst_arm(0xf93d0a00) " @ rfedb pc!") - TEST_UNSUPPORTED(__inst_arm(0xf8bd0a00) " @ rfeia pc!") - TEST_UNSUPPORTED(__inst_arm(0xf9bd0a00) " @ rfeib pc!") -#endif /* __LINUX_ARM_ARCH__ >= 6 */ - -#if __LINUX_ARM_ARCH__ >= 6 - TEST_X( "blx __dummy_thumb_subroutine_even", - ".thumb \n\t" - ".space 4 \n\t" - ".type __dummy_thumb_subroutine_even, %%function \n\t" - "__dummy_thumb_subroutine_even: \n\t" - "mov r0, pc \n\t" - "bx lr \n\t" - ".arm \n\t" - ) - TEST( "blx __dummy_thumb_subroutine_even") - - TEST_X( "blx __dummy_thumb_subroutine_odd", - ".thumb \n\t" - ".space 2 \n\t" - ".type __dummy_thumb_subroutine_odd, %%function \n\t" - "__dummy_thumb_subroutine_odd: \n\t" - "mov r0, pc \n\t" - "bx lr \n\t" - ".arm \n\t" - ) - TEST( "blx __dummy_thumb_subroutine_odd") -#endif /* __LINUX_ARM_ARCH__ >= 6 */ - -#if __LINUX_ARM_ARCH__ >= 5 - COPROCESSOR_INSTRUCTIONS_ST_LD("2",f) -#endif -#if __LINUX_ARM_ARCH__ >= 6 - COPROCESSOR_INSTRUCTIONS_MC_MR("2",f) -#endif - - TEST_GROUP("Miscellaneous instructions, memory hints, and Advanced SIMD instructions") - -#if __LINUX_ARM_ARCH__ >= 6 - TEST_UNSUPPORTED("cps 0x13") - TEST_UNSUPPORTED("cpsie i") - TEST_UNSUPPORTED("cpsid i") - TEST_UNSUPPORTED("cpsie i,0x13") - TEST_UNSUPPORTED("cpsid i,0x13") - TEST_UNSUPPORTED("setend le") - TEST_UNSUPPORTED("setend be") -#endif - -#if __LINUX_ARM_ARCH__ >= 7 - TEST_P("pli [r",0,0b,", #16]") - TEST( "pli [pc, #0]") - TEST_RR("pli [r",12,0b,", r",0, 16,"]") - TEST_RR("pli [r",0, 0b,", -r",12,16,", lsl #4]") -#endif - -#if __LINUX_ARM_ARCH__ >= 5 - TEST_P("pld [r",0,32,", #-16]") - TEST( "pld [pc, #0]") - TEST_PR("pld [r",7, 24, ", r",0, 16,"]") - TEST_PR("pld [r",8, 24, ", -r",12,16,", lsl #4]") -#endif - -#if __LINUX_ARM_ARCH__ >= 7 - TEST_SUPPORTED( __inst_arm(0xf590f000) " @ pldw [r0, #0]") - TEST_SUPPORTED( __inst_arm(0xf797f000) " @ pldw [r7, r0]") - TEST_SUPPORTED( __inst_arm(0xf798f18c) " @ pldw [r8, r12, lsl #3]"); -#endif - -#if __LINUX_ARM_ARCH__ >= 7 - TEST_UNSUPPORTED("clrex") - TEST_UNSUPPORTED("dsb") - TEST_UNSUPPORTED("dmb") - TEST_UNSUPPORTED("isb") -#endif - - verbose("\n"); -} - diff --git a/arch/arm/kernel/kprobes-test-thumb.c b/arch/arm/kernel/kprobes-test-thumb.c deleted file mode 100644 index 844dd10..0000000 --- a/arch/arm/kernel/kprobes-test-thumb.c +++ /dev/null @@ -1,1188 +0,0 @@ -/* - * arch/arm/kernel/kprobes-test-thumb.c - * - * Copyright (C) 2011 Jon Medhurst . - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include - -#include "kprobes-test.h" - - -#define TEST_ISA "16" - -#define DONT_TEST_IN_ITBLOCK(tests) \ - kprobe_test_flags |= TEST_FLAG_NO_ITBLOCK; \ - tests \ - kprobe_test_flags &= ~TEST_FLAG_NO_ITBLOCK; - -#define CONDITION_INSTRUCTIONS(cc_pos, tests) \ - kprobe_test_cc_position = cc_pos; \ - DONT_TEST_IN_ITBLOCK(tests) \ - kprobe_test_cc_position = 0; - -#define TEST_ITBLOCK(code) \ - kprobe_test_flags |= TEST_FLAG_FULL_ITBLOCK; \ - TESTCASE_START(code) \ - TEST_ARG_END("") \ - "50: nop \n\t" \ - "1: "code" \n\t" \ - " mov r1, #0x11 \n\t" \ - " mov r2, #0x22 \n\t" \ - " mov r3, #0x33 \n\t" \ - "2: nop \n\t" \ - TESTCASE_END \ - kprobe_test_flags &= ~TEST_FLAG_FULL_ITBLOCK; - -#define TEST_THUMB_TO_ARM_INTERWORK_P(code1, reg, val, code2) \ - TESTCASE_START(code1 #reg code2) \ - TEST_ARG_PTR(reg, val) \ - TEST_ARG_REG(14, 99f+1) \ - TEST_ARG_MEM(15, 3f) \ - TEST_ARG_END("") \ - " nop \n\t" /* To align 1f */ \ - "50: nop \n\t" \ - "1: "code1 #reg code2" \n\t" \ - " bx lr \n\t" \ - ".arm \n\t" \ - "3: adr lr, 2f+1 \n\t" \ - " bx lr \n\t" \ - ".thumb \n\t" \ - "2: nop \n\t" \ - TESTCASE_END - - -void kprobe_thumb16_test_cases(void) -{ - kprobe_test_flags = TEST_FLAG_NARROW_INSTR; - - TEST_GROUP("Shift (immediate), add, subtract, move, and compare") - - TEST_R( "lsls r7, r",0,VAL1,", #5") - TEST_R( "lsls r0, r",7,VAL2,", #11") - TEST_R( "lsrs r7, r",0,VAL1,", #5") - TEST_R( "lsrs r0, r",7,VAL2,", #11") - TEST_R( "asrs r7, r",0,VAL1,", #5") - TEST_R( "asrs r0, r",7,VAL2,", #11") - TEST_RR( "adds r2, r",0,VAL1,", r",7,VAL2,"") - TEST_RR( "adds r5, r",7,VAL2,", r",0,VAL2,"") - TEST_RR( "subs r2, r",0,VAL1,", r",7,VAL2,"") - TEST_RR( "subs r5, r",7,VAL2,", r",0,VAL2,"") - TEST_R( "adds r7, r",0,VAL1,", #5") - TEST_R( "adds r0, r",7,VAL2,", #2") - TEST_R( "subs r7, r",0,VAL1,", #5") - TEST_R( "subs r0, r",7,VAL2,", #2") - TEST( "movs.n r0, #0x5f") - TEST( "movs.n r7, #0xa0") - TEST_R( "cmp.n r",0,0x5e, ", #0x5f") - TEST_R( "cmp.n r",5,0x15f,", #0x5f") - TEST_R( "cmp.n r",7,0xa0, ", #0xa0") - TEST_R( "adds.n r",0,VAL1,", #0x5f") - TEST_R( "adds.n r",7,VAL2,", #0xa0") - TEST_R( "subs.n r",0,VAL1,", #0x5f") - TEST_R( "subs.n r",7,VAL2,", #0xa0") - - TEST_GROUP("16-bit Thumb data-processing instructions") - -#define DATA_PROCESSING16(op,val) \ - TEST_RR( op" r",0,VAL1,", r",7,val,"") \ - TEST_RR( op" r",7,VAL2,", r",0,val,"") - - DATA_PROCESSING16("ands",0xf00f00ff) - DATA_PROCESSING16("eors",0xf00f00ff) - DATA_PROCESSING16("lsls",11) - DATA_PROCESSING16("lsrs",11) - DATA_PROCESSING16("asrs",11) - DATA_PROCESSING16("adcs",VAL2) - DATA_PROCESSING16("sbcs",VAL2) - DATA_PROCESSING16("rors",11) - DATA_PROCESSING16("tst",0xf00f00ff) - TEST_R("rsbs r",0,VAL1,", #0") - TEST_R("rsbs r",7,VAL2,", #0") - DATA_PROCESSING16("cmp",0xf00f00ff) - DATA_PROCESSING16("cmn",0xf00f00ff) - DATA_PROCESSING16("orrs",0xf00f00ff) - DATA_PROCESSING16("muls",VAL2) - DATA_PROCESSING16("bics",0xf00f00ff) - DATA_PROCESSING16("mvns",VAL2) - - TEST_GROUP("Special data instructions and branch and exchange") - - TEST_RR( "add r",0, VAL1,", r",7,VAL2,"") - TEST_RR( "add r",3, VAL2,", r",8,VAL3,"") - TEST_RR( "add r",8, VAL3,", r",0,VAL1,"") - TEST_R( "add sp" ", r",8,-8, "") - TEST_R( "add r",14,VAL1,", pc") - TEST_BF_R("add pc" ", r",0,2f-1f-8,"") - TEST_UNSUPPORTED(__inst_thumb16(0x44ff) " @ add pc, pc") - - TEST_RR( "cmp r",3,VAL1,", r",8,VAL2,"") - TEST_RR( "cmp r",8,VAL2,", r",0,VAL1,"") - TEST_R( "cmp sp" ", r",8,-8, "") - - TEST_R( "mov r0, r",7,VAL2,"") - TEST_R( "mov r3, r",8,VAL3,"") - TEST_R( "mov r8, r",0,VAL1,"") - TEST_P( "mov sp, r",8,-8, "") - TEST( "mov lr, pc") - TEST_BF_R("mov pc, r",0,2f, "") - - TEST_BF_R("bx r",0, 2f+1,"") - TEST_BF_R("bx r",14,2f+1,"") - TESTCASE_START("bx pc") - TEST_ARG_REG(14, 99f+1) - TEST_ARG_END("") - " nop \n\t" /* To align the bx pc*/ - "50: nop \n\t" - "1: bx pc \n\t" - " bx lr \n\t" - ".arm \n\t" - " adr lr, 2f+1 \n\t" - " bx lr \n\t" - ".thumb \n\t" - "2: nop \n\t" - TESTCASE_END - - TEST_BF_R("blx r",0, 2f+1,"") - TEST_BB_R("blx r",14,2f+1,"") - TEST_UNSUPPORTED(__inst_thumb16(0x47f8) " @ blx pc") - - TEST_GROUP("Load from Literal Pool") - - TEST_X( "ldr r0, 3f", - ".align \n\t" - "3: .word "__stringify(VAL1)) - TEST_X( "ldr r7, 3f", - ".space 128 \n\t" - ".align \n\t" - "3: .word "__stringify(VAL2)) - - TEST_GROUP("16-bit Thumb Load/store instructions") - - TEST_RPR("str r",0, VAL1,", [r",1, 24,", r",2, 48,"]") - TEST_RPR("str r",7, VAL2,", [r",6, 24,", r",5, 48,"]") - TEST_RPR("strh r",0, VAL1,", [r",1, 24,", r",2, 48,"]") - TEST_RPR("strh r",7, VAL2,", [r",6, 24,", r",5, 48,"]") - TEST_RPR("strb r",0, VAL1,", [r",1, 24,", r",2, 48,"]") - TEST_RPR("strb r",7, VAL2,", [r",6, 24,", r",5, 48,"]") - TEST_PR( "ldrsb r0, [r",1, 24,", r",2, 48,"]") - TEST_PR( "ldrsb r7, [r",6, 24,", r",5, 50,"]") - TEST_PR( "ldr r0, [r",1, 24,", r",2, 48,"]") - TEST_PR( "ldr r7, [r",6, 24,", r",5, 48,"]") - TEST_PR( "ldrh r0, [r",1, 24,", r",2, 48,"]") - TEST_PR( "ldrh r7, [r",6, 24,", r",5, 50,"]") - TEST_PR( "ldrb r0, [r",1, 24,", r",2, 48,"]") - TEST_PR( "ldrb r7, [r",6, 24,", r",5, 50,"]") - TEST_PR( "ldrsh r0, [r",1, 24,", r",2, 48,"]") - TEST_PR( "ldrsh r7, [r",6, 24,", r",5, 50,"]") - - TEST_RP("str r",0, VAL1,", [r",1, 24,", #120]") - TEST_RP("str r",7, VAL2,", [r",6, 24,", #120]") - TEST_P( "ldr r0, [r",1, 24,", #120]") - TEST_P( "ldr r7, [r",6, 24,", #120]") - TEST_RP("strb r",0, VAL1,", [r",1, 24,", #30]") - TEST_RP("strb r",7, VAL2,", [r",6, 24,", #30]") - TEST_P( "ldrb r0, [r",1, 24,", #30]") - TEST_P( "ldrb r7, [r",6, 24,", #30]") - TEST_RP("strh r",0, VAL1,", [r",1, 24,", #60]") - TEST_RP("strh r",7, VAL2,", [r",6, 24,", #60]") - TEST_P( "ldrh r0, [r",1, 24,", #60]") - TEST_P( "ldrh r7, [r",6, 24,", #60]") - - TEST_R( "str r",0, VAL1,", [sp, #0]") - TEST_R( "str r",7, VAL2,", [sp, #160]") - TEST( "ldr r0, [sp, #0]") - TEST( "ldr r7, [sp, #160]") - - TEST_RP("str r",0, VAL1,", [r",0, 24,"]") - TEST_P( "ldr r0, [r",0, 24,"]") - - TEST_GROUP("Generate PC-/SP-relative address") - - TEST("add r0, pc, #4") - TEST("add r7, pc, #1020") - TEST("add r0, sp, #4") - TEST("add r7, sp, #1020") - - TEST_GROUP("Miscellaneous 16-bit instructions") - - TEST_UNSUPPORTED( "cpsie i") - TEST_UNSUPPORTED( "cpsid i") - TEST_UNSUPPORTED( "setend le") - TEST_UNSUPPORTED( "setend be") - - TEST("add sp, #"__stringify(TEST_MEMORY_SIZE)) /* Assumes TEST_MEMORY_SIZE < 0x400 */ - TEST("sub sp, #0x7f*4") - -DONT_TEST_IN_ITBLOCK( - TEST_BF_R( "cbnz r",0,0, ", 2f") - TEST_BF_R( "cbz r",2,-1,", 2f") - TEST_BF_RX( "cbnz r",4,1, ", 2f", SPACE_0x20) - TEST_BF_RX( "cbz r",7,0, ", 2f", SPACE_0x40) -) - TEST_R("sxth r0, r",7, HH1,"") - TEST_R("sxth r7, r",0, HH2,"") - TEST_R("sxtb r0, r",7, HH1,"") - TEST_R("sxtb r7, r",0, HH2,"") - TEST_R("uxth r0, r",7, HH1,"") - TEST_R("uxth r7, r",0, HH2,"") - TEST_R("uxtb r0, r",7, HH1,"") - TEST_R("uxtb r7, r",0, HH2,"") - TEST_R("rev r0, r",7, VAL1,"") - TEST_R("rev r7, r",0, VAL2,"") - TEST_R("rev16 r0, r",7, VAL1,"") - TEST_R("rev16 r7, r",0, VAL2,"") - TEST_UNSUPPORTED(__inst_thumb16(0xba80) "") - TEST_UNSUPPORTED(__inst_thumb16(0xbabf) "") - TEST_R("revsh r0, r",7, VAL1,"") - TEST_R("revsh r7, r",0, VAL2,"") - -#define TEST_POPPC(code, offset) \ - TESTCASE_START(code) \ - TEST_ARG_PTR(13, offset) \ - TEST_ARG_END("") \ - TEST_BRANCH_F(code) \ - TESTCASE_END - - TEST("push {r0}") - TEST("push {r7}") - TEST("push {r14}") - TEST("push {r0-r7,r14}") - TEST("push {r0,r2,r4,r6,r14}") - TEST("push {r1,r3,r5,r7}") - TEST("pop {r0}") - TEST("pop {r7}") - TEST("pop {r0,r2,r4,r6}") - TEST_POPPC("pop {pc}",15*4) - TEST_POPPC("pop {r0-r7,pc}",7*4) - TEST_POPPC("pop {r1,r3,r5,r7,pc}",11*4) - TEST_THUMB_TO_ARM_INTERWORK_P("pop {pc} @ ",13,15*4,"") - TEST_THUMB_TO_ARM_INTERWORK_P("pop {r0-r7,pc} @ ",13,7*4,"") - - TEST_UNSUPPORTED("bkpt.n 0") - TEST_UNSUPPORTED("bkpt.n 255") - - TEST_SUPPORTED("yield") - TEST("sev") - TEST("nop") - TEST("wfi") - TEST_SUPPORTED("wfe") - TEST_UNSUPPORTED(__inst_thumb16(0xbf50) "") /* Unassigned hints */ - TEST_UNSUPPORTED(__inst_thumb16(0xbff0) "") /* Unassigned hints */ - -#define TEST_IT(code, code2) \ - TESTCASE_START(code) \ - TEST_ARG_END("") \ - "50: nop \n\t" \ - "1: "code" \n\t" \ - " "code2" \n\t" \ - "2: nop \n\t" \ - TESTCASE_END - -DONT_TEST_IN_ITBLOCK( - TEST_IT("it eq","moveq r0,#0") - TEST_IT("it vc","movvc r0,#0") - TEST_IT("it le","movle r0,#0") - TEST_IT("ite eq","moveq r0,#0\n\t movne r1,#1") - TEST_IT("itet vc","movvc r0,#0\n\t movvs r1,#1\n\t movvc r2,#2") - TEST_IT("itete le","movle r0,#0\n\t movgt r1,#1\n\t movle r2,#2\n\t movgt r3,#3") - TEST_IT("itttt le","movle r0,#0\n\t movle r1,#1\n\t movle r2,#2\n\t movle r3,#3") - TEST_IT("iteee le","movle r0,#0\n\t movgt r1,#1\n\t movgt r2,#2\n\t movgt r3,#3") -) - - TEST_GROUP("Load and store multiple") - - TEST_P("ldmia r",4, 16*4,"!, {r0,r7}") - TEST_P("ldmia r",7, 16*4,"!, {r0-r6}") - TEST_P("stmia r",4, 16*4,"!, {r0,r7}") - TEST_P("stmia r",0, 16*4,"!, {r0-r7}") - - TEST_GROUP("Conditional branch and Supervisor Call instructions") - -CONDITION_INSTRUCTIONS(8, - TEST_BF("beq 2f") - TEST_BB("bne 2b") - TEST_BF("bgt 2f") - TEST_BB("blt 2b") -) - TEST_UNSUPPORTED(__inst_thumb16(0xde00) "") - TEST_UNSUPPORTED(__inst_thumb16(0xdeff) "") - TEST_UNSUPPORTED("svc #0x00") - TEST_UNSUPPORTED("svc #0xff") - - TEST_GROUP("Unconditional branch") - - TEST_BF( "b 2f") - TEST_BB( "b 2b") - TEST_BF_X("b 2f", SPACE_0x400) - TEST_BB_X("b 2b", SPACE_0x400) - - TEST_GROUP("Testing instructions in IT blocks") - - TEST_ITBLOCK("subs.n r0, r0") - - verbose("\n"); -} - - -void kprobe_thumb32_test_cases(void) -{ - kprobe_test_flags = 0; - - TEST_GROUP("Load/store multiple") - - TEST_UNSUPPORTED("rfedb sp") - TEST_UNSUPPORTED("rfeia sp") - TEST_UNSUPPORTED("rfedb sp!") - TEST_UNSUPPORTED("rfeia sp!") - - TEST_P( "stmia r",0, 16*4,", {r0,r8}") - TEST_P( "stmia r",4, 16*4,", {r0-r12,r14}") - TEST_P( "stmia r",7, 16*4,"!, {r8-r12,r14}") - TEST_P( "stmia r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") - - TEST_P( "ldmia r",0, 16*4,", {r0,r8}") - TEST_P( "ldmia r",4, 0, ", {r0-r12,r14}") - TEST_BF_P("ldmia r",5, 8*4, "!, {r6-r12,r15}") - TEST_P( "ldmia r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") - TEST_BF_P("ldmia r",14,14*4,"!, {r4,pc}") - - TEST_P( "stmdb r",0, 16*4,", {r0,r8}") - TEST_P( "stmdb r",4, 16*4,", {r0-r12,r14}") - TEST_P( "stmdb r",5, 16*4,"!, {r8-r12,r14}") - TEST_P( "stmdb r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") - - TEST_P( "ldmdb r",0, 16*4,", {r0,r8}") - TEST_P( "ldmdb r",4, 16*4,", {r0-r12,r14}") - TEST_BF_P("ldmdb r",5, 16*4,"!, {r6-r12,r15}") - TEST_P( "ldmdb r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") - TEST_BF_P("ldmdb r",14,16*4,"!, {r4,pc}") - - TEST_P( "stmdb r",13,16*4,"!, {r3-r12,lr}") - TEST_P( "stmdb r",13,16*4,"!, {r3-r12}") - TEST_P( "stmdb r",2, 16*4,", {r3-r12,lr}") - TEST_P( "stmdb r",13,16*4,"!, {r2-r12,lr}") - TEST_P( "stmdb r",0, 16*4,", {r0-r12}") - TEST_P( "stmdb r",0, 16*4,", {r0-r12,lr}") - - TEST_BF_P("ldmia r",13,5*4, "!, {r3-r12,pc}") - TEST_P( "ldmia r",13,5*4, "!, {r3-r12}") - TEST_BF_P("ldmia r",2, 5*4, "!, {r3-r12,pc}") - TEST_BF_P("ldmia r",13,4*4, "!, {r2-r12,pc}") - TEST_P( "ldmia r",0, 16*4,", {r0-r12}") - TEST_P( "ldmia r",0, 16*4,", {r0-r12,lr}") - - TEST_THUMB_TO_ARM_INTERWORK_P("ldmia r",0,14*4,", {r12,pc}") - TEST_THUMB_TO_ARM_INTERWORK_P("ldmia r",13,2*4,", {r0-r12,pc}") - - TEST_UNSUPPORTED(__inst_thumb32(0xe88f0101) " @ stmia pc, {r0,r8}") - TEST_UNSUPPORTED(__inst_thumb32(0xe92f5f00) " @ stmdb pc!, {r8-r12,r14}") - TEST_UNSUPPORTED(__inst_thumb32(0xe8bdc000) " @ ldmia r13!, {r14,pc}") - TEST_UNSUPPORTED(__inst_thumb32(0xe93ec000) " @ ldmdb r14!, {r14,pc}") - TEST_UNSUPPORTED(__inst_thumb32(0xe8a73f00) " @ stmia r7!, {r8-r12,sp}") - TEST_UNSUPPORTED(__inst_thumb32(0xe8a79f00) " @ stmia r7!, {r8-r12,pc}") - TEST_UNSUPPORTED(__inst_thumb32(0xe93e2010) " @ ldmdb r14!, {r4,sp}") - - TEST_GROUP("Load/store double or exclusive, table branch") - - TEST_P( "ldrd r0, r1, [r",1, 24,", #-16]") - TEST( "ldrd r12, r14, [sp, #16]") - TEST_P( "ldrd r1, r0, [r",7, 24,", #-16]!") - TEST( "ldrd r14, r12, [sp, #16]!") - TEST_P( "ldrd r1, r0, [r",7, 24,"], #16") - TEST( "ldrd r7, r8, [sp], #-16") - - TEST_X( "ldrd r12, r14, 3f", - ".align 3 \n\t" - "3: .word "__stringify(VAL1)" \n\t" - " .word "__stringify(VAL2)) - - TEST_UNSUPPORTED(__inst_thumb32(0xe9ffec04) " @ ldrd r14, r12, [pc, #16]!") - TEST_UNSUPPORTED(__inst_thumb32(0xe8ffec04) " @ ldrd r14, r12, [pc], #16") - TEST_UNSUPPORTED(__inst_thumb32(0xe9d4d800) " @ ldrd sp, r8, [r4]") - TEST_UNSUPPORTED(__inst_thumb32(0xe9d4f800) " @ ldrd pc, r8, [r4]") - TEST_UNSUPPORTED(__inst_thumb32(0xe9d47d00) " @ ldrd r7, sp, [r4]") - TEST_UNSUPPORTED(__inst_thumb32(0xe9d47f00) " @ ldrd r7, pc, [r4]") - - TEST_RRP("strd r",0, VAL1,", r",1, VAL2,", [r",1, 24,", #-16]") - TEST_RR( "strd r",12,VAL2,", r",14,VAL1,", [sp, #16]") - TEST_RRP("strd r",1, VAL1,", r",0, VAL2,", [r",7, 24,", #-16]!") - TEST_RR( "strd r",14,VAL2,", r",12,VAL1,", [sp, #16]!") - TEST_RRP("strd r",1, VAL1,", r",0, VAL2,", [r",7, 24,"], #16") - TEST_RR( "strd r",7, VAL2,", r",8, VAL1,", [sp], #-16") - TEST_UNSUPPORTED(__inst_thumb32(0xe9efec04) " @ strd r14, r12, [pc, #16]!") - TEST_UNSUPPORTED(__inst_thumb32(0xe8efec04) " @ strd r14, r12, [pc], #16") - - TEST_RX("tbb [pc, r",0, (9f-(1f+4)),"]", - "9: \n\t" - ".byte (2f-1b-4)>>1 \n\t" - ".byte (3f-1b-4)>>1 \n\t" - "3: mvn r0, r0 \n\t" - "2: nop \n\t") - - TEST_RX("tbb [pc, r",4, (9f-(1f+4)+1),"]", - "9: \n\t" - ".byte (2f-1b-4)>>1 \n\t" - ".byte (3f-1b-4)>>1 \n\t" - "3: mvn r0, r0 \n\t" - "2: nop \n\t") - - TEST_RRX("tbb [r",1,9f,", r",2,0,"]", - "9: \n\t" - ".byte (2f-1b-4)>>1 \n\t" - ".byte (3f-1b-4)>>1 \n\t" - "3: mvn r0, r0 \n\t" - "2: nop \n\t") - - TEST_RX("tbh [pc, r",7, (9f-(1f+4))>>1,"]", - "9: \n\t" - ".short (2f-1b-4)>>1 \n\t" - ".short (3f-1b-4)>>1 \n\t" - "3: mvn r0, r0 \n\t" - "2: nop \n\t") - - TEST_RX("tbh [pc, r",12, ((9f-(1f+4))>>1)+1,"]", - "9: \n\t" - ".short (2f-1b-4)>>1 \n\t" - ".short (3f-1b-4)>>1 \n\t" - "3: mvn r0, r0 \n\t" - "2: nop \n\t") - - TEST_RRX("tbh [r",1,9f, ", r",14,1,"]", - "9: \n\t" - ".short (2f-1b-4)>>1 \n\t" - ".short (3f-1b-4)>>1 \n\t" - "3: mvn r0, r0 \n\t" - "2: nop \n\t") - - TEST_UNSUPPORTED(__inst_thumb32(0xe8d1f01f) " @ tbh [r1, pc]") - TEST_UNSUPPORTED(__inst_thumb32(0xe8d1f01d) " @ tbh [r1, sp]") - TEST_UNSUPPORTED(__inst_thumb32(0xe8ddf012) " @ tbh [sp, r2]") - - TEST_UNSUPPORTED("strexb r0, r1, [r2]") - TEST_UNSUPPORTED("strexh r0, r1, [r2]") - TEST_UNSUPPORTED("strexd r0, r1, [r2]") - TEST_UNSUPPORTED("ldrexb r0, [r1]") - TEST_UNSUPPORTED("ldrexh r0, [r1]") - TEST_UNSUPPORTED("ldrexd r0, [r1]") - - TEST_GROUP("Data-processing (shifted register) and (modified immediate)") - -#define _DATA_PROCESSING32_DNM(op,s,val) \ - TEST_RR(op s".w r0, r",1, VAL1,", r",2, val, "") \ - TEST_RR(op s" r1, r",1, VAL1,", r",2, val, ", lsl #3") \ - TEST_RR(op s" r2, r",3, VAL1,", r",2, val, ", lsr #4") \ - TEST_RR(op s" r3, r",3, VAL1,", r",2, val, ", asr #5") \ - TEST_RR(op s" r4, r",5, VAL1,", r",2, N(val),", asr #6") \ - TEST_RR(op s" r5, r",5, VAL1,", r",2, val, ", ror #7") \ - TEST_RR(op s" r8, r",9, VAL1,", r",10,val, ", rrx") \ - TEST_R( op s" r0, r",11,VAL1,", #0x00010001") \ - TEST_R( op s" r11, r",0, VAL1,", #0xf5000000") \ - TEST_R( op s" r7, r",8, VAL2,", #0x000af000") - -#define DATA_PROCESSING32_DNM(op,val) \ - _DATA_PROCESSING32_DNM(op,"",val) \ - _DATA_PROCESSING32_DNM(op,"s",val) - -#define DATA_PROCESSING32_NM(op,val) \ - TEST_RR(op".w r",1, VAL1,", r",2, val, "") \ - TEST_RR(op" r",1, VAL1,", r",2, val, ", lsl #3") \ - TEST_RR(op" r",3, VAL1,", r",2, val, ", lsr #4") \ - TEST_RR(op" r",3, VAL1,", r",2, val, ", asr #5") \ - TEST_RR(op" r",5, VAL1,", r",2, N(val),", asr #6") \ - TEST_RR(op" r",5, VAL1,", r",2, val, ", ror #7") \ - TEST_RR(op" r",9, VAL1,", r",10,val, ", rrx") \ - TEST_R( op" r",11,VAL1,", #0x00010001") \ - TEST_R( op" r",0, VAL1,", #0xf5000000") \ - TEST_R( op" r",8, VAL2,", #0x000af000") - -#define _DATA_PROCESSING32_DM(op,s,val) \ - TEST_R( op s".w r0, r",14, val, "") \ - TEST_R( op s" r1, r",12, val, ", lsl #3") \ - TEST_R( op s" r2, r",11, val, ", lsr #4") \ - TEST_R( op s" r3, r",10, val, ", asr #5") \ - TEST_R( op s" r4, r",9, N(val),", asr #6") \ - TEST_R( op s" r5, r",8, val, ", ror #7") \ - TEST_R( op s" r8, r",7,val, ", rrx") \ - TEST( op s" r0, #0x00010001") \ - TEST( op s" r11, #0xf5000000") \ - TEST( op s" r7, #0x000af000") \ - TEST( op s" r4, #0x00005a00") - -#define DATA_PROCESSING32_DM(op,val) \ - _DATA_PROCESSING32_DM(op,"",val) \ - _DATA_PROCESSING32_DM(op,"s",val) - - DATA_PROCESSING32_DNM("and",0xf00f00ff) - DATA_PROCESSING32_NM("tst",0xf00f00ff) - DATA_PROCESSING32_DNM("bic",0xf00f00ff) - DATA_PROCESSING32_DNM("orr",0xf00f00ff) - DATA_PROCESSING32_DM("mov",VAL2) - DATA_PROCESSING32_DNM("orn",0xf00f00ff) - DATA_PROCESSING32_DM("mvn",VAL2) - DATA_PROCESSING32_DNM("eor",0xf00f00ff) - DATA_PROCESSING32_NM("teq",0xf00f00ff) - DATA_PROCESSING32_DNM("add",VAL2) - DATA_PROCESSING32_NM("cmn",VAL2) - DATA_PROCESSING32_DNM("adc",VAL2) - DATA_PROCESSING32_DNM("sbc",VAL2) - DATA_PROCESSING32_DNM("sub",VAL2) - DATA_PROCESSING32_NM("cmp",VAL2) - DATA_PROCESSING32_DNM("rsb",VAL2) - - TEST_RR("pkhbt r0, r",0, HH1,", r",1, HH2,"") - TEST_RR("pkhbt r14,r",12, HH1,", r",10,HH2,", lsl #2") - TEST_RR("pkhtb r0, r",0, HH1,", r",1, HH2,"") - TEST_RR("pkhtb r14,r",12, HH1,", r",10,HH2,", asr #2") - - TEST_UNSUPPORTED(__inst_thumb32(0xea170f0d) " @ tst.w r7, sp") - TEST_UNSUPPORTED(__inst_thumb32(0xea170f0f) " @ tst.w r7, pc") - TEST_UNSUPPORTED(__inst_thumb32(0xea1d0f07) " @ tst.w sp, r7") - TEST_UNSUPPORTED(__inst_thumb32(0xea1f0f07) " @ tst.w pc, r7") - TEST_UNSUPPORTED(__inst_thumb32(0xf01d1f08) " @ tst sp, #0x00080008") - TEST_UNSUPPORTED(__inst_thumb32(0xf01f1f08) " @ tst pc, #0x00080008") - - TEST_UNSUPPORTED(__inst_thumb32(0xea970f0d) " @ teq.w r7, sp") - TEST_UNSUPPORTED(__inst_thumb32(0xea970f0f) " @ teq.w r7, pc") - TEST_UNSUPPORTED(__inst_thumb32(0xea9d0f07) " @ teq.w sp, r7") - TEST_UNSUPPORTED(__inst_thumb32(0xea9f0f07) " @ teq.w pc, r7") - TEST_UNSUPPORTED(__inst_thumb32(0xf09d1f08) " @ tst sp, #0x00080008") - TEST_UNSUPPORTED(__inst_thumb32(0xf09f1f08) " @ tst pc, #0x00080008") - - TEST_UNSUPPORTED(__inst_thumb32(0xeb170f0d) " @ cmn.w r7, sp") - TEST_UNSUPPORTED(__inst_thumb32(0xeb170f0f) " @ cmn.w r7, pc") - TEST_P("cmn.w sp, r",7,0,"") - TEST_UNSUPPORTED(__inst_thumb32(0xeb1f0f07) " @ cmn.w pc, r7") - TEST( "cmn sp, #0x00080008") - TEST_UNSUPPORTED(__inst_thumb32(0xf11f1f08) " @ cmn pc, #0x00080008") - - TEST_UNSUPPORTED(__inst_thumb32(0xebb70f0d) " @ cmp.w r7, sp") - TEST_UNSUPPORTED(__inst_thumb32(0xebb70f0f) " @ cmp.w r7, pc") - TEST_P("cmp.w sp, r",7,0,"") - TEST_UNSUPPORTED(__inst_thumb32(0xebbf0f07) " @ cmp.w pc, r7") - TEST( "cmp sp, #0x00080008") - TEST_UNSUPPORTED(__inst_thumb32(0xf1bf1f08) " @ cmp pc, #0x00080008") - - TEST_UNSUPPORTED(__inst_thumb32(0xea5f070d) " @ movs.w r7, sp") - TEST_UNSUPPORTED(__inst_thumb32(0xea5f070f) " @ movs.w r7, pc") - TEST_UNSUPPORTED(__inst_thumb32(0xea5f0d07) " @ movs.w sp, r7") - TEST_UNSUPPORTED(__inst_thumb32(0xea4f0f07) " @ mov.w pc, r7") - TEST_UNSUPPORTED(__inst_thumb32(0xf04f1d08) " @ mov sp, #0x00080008") - TEST_UNSUPPORTED(__inst_thumb32(0xf04f1f08) " @ mov pc, #0x00080008") - - TEST_R("add.w r0, sp, r",1, 4,"") - TEST_R("adds r0, sp, r",1, 4,", asl #3") - TEST_R("add r0, sp, r",1, 4,", asl #4") - TEST_R("add r0, sp, r",1, 16,", ror #1") - TEST_R("add.w sp, sp, r",1, 4,"") - TEST_R("add sp, sp, r",1, 4,", asl #3") - TEST_UNSUPPORTED(__inst_thumb32(0xeb0d1d01) " @ add sp, sp, r1, asl #4") - TEST_UNSUPPORTED(__inst_thumb32(0xeb0d0d71) " @ add sp, sp, r1, ror #1") - TEST( "add.w r0, sp, #24") - TEST( "add.w sp, sp, #24") - TEST_UNSUPPORTED(__inst_thumb32(0xeb0d0f01) " @ add pc, sp, r1") - TEST_UNSUPPORTED(__inst_thumb32(0xeb0d000f) " @ add r0, sp, pc") - TEST_UNSUPPORTED(__inst_thumb32(0xeb0d000d) " @ add r0, sp, sp") - TEST_UNSUPPORTED(__inst_thumb32(0xeb0d0d0f) " @ add sp, sp, pc") - TEST_UNSUPPORTED(__inst_thumb32(0xeb0d0d0d) " @ add sp, sp, sp") - - TEST_R("sub.w r0, sp, r",1, 4,"") - TEST_R("subs r0, sp, r",1, 4,", asl #3") - TEST_R("sub r0, sp, r",1, 4,", asl #4") - TEST_R("sub r0, sp, r",1, 16,", ror #1") - TEST_R("sub.w sp, sp, r",1, 4,"") - TEST_R("sub sp, sp, r",1, 4,", asl #3") - TEST_UNSUPPORTED(__inst_thumb32(0xebad1d01) " @ sub sp, sp, r1, asl #4") - TEST_UNSUPPORTED(__inst_thumb32(0xebad0d71) " @ sub sp, sp, r1, ror #1") - TEST_UNSUPPORTED(__inst_thumb32(0xebad0f01) " @ sub pc, sp, r1") - TEST( "sub.w r0, sp, #24") - TEST( "sub.w sp, sp, #24") - - TEST_UNSUPPORTED(__inst_thumb32(0xea02010f) " @ and r1, r2, pc") - TEST_UNSUPPORTED(__inst_thumb32(0xea0f0103) " @ and r1, pc, r3") - TEST_UNSUPPORTED(__inst_thumb32(0xea020f03) " @ and pc, r2, r3") - TEST_UNSUPPORTED(__inst_thumb32(0xea02010d) " @ and r1, r2, sp") - TEST_UNSUPPORTED(__inst_thumb32(0xea0d0103) " @ and r1, sp, r3") - TEST_UNSUPPORTED(__inst_thumb32(0xea020d03) " @ and sp, r2, r3") - TEST_UNSUPPORTED(__inst_thumb32(0xf00d1108) " @ and r1, sp, #0x00080008") - TEST_UNSUPPORTED(__inst_thumb32(0xf00f1108) " @ and r1, pc, #0x00080008") - TEST_UNSUPPORTED(__inst_thumb32(0xf0021d08) " @ and sp, r8, #0x00080008") - TEST_UNSUPPORTED(__inst_thumb32(0xf0021f08) " @ and pc, r8, #0x00080008") - - TEST_UNSUPPORTED(__inst_thumb32(0xeb02010f) " @ add r1, r2, pc") - TEST_UNSUPPORTED(__inst_thumb32(0xeb0f0103) " @ add r1, pc, r3") - TEST_UNSUPPORTED(__inst_thumb32(0xeb020f03) " @ add pc, r2, r3") - TEST_UNSUPPORTED(__inst_thumb32(0xeb02010d) " @ add r1, r2, sp") - TEST_SUPPORTED( __inst_thumb32(0xeb0d0103) " @ add r1, sp, r3") - TEST_UNSUPPORTED(__inst_thumb32(0xeb020d03) " @ add sp, r2, r3") - TEST_SUPPORTED( __inst_thumb32(0xf10d1108) " @ add r1, sp, #0x00080008") - TEST_UNSUPPORTED(__inst_thumb32(0xf10d1f08) " @ add pc, sp, #0x00080008") - TEST_UNSUPPORTED(__inst_thumb32(0xf10f1108) " @ add r1, pc, #0x00080008") - TEST_UNSUPPORTED(__inst_thumb32(0xf1021d08) " @ add sp, r8, #0x00080008") - TEST_UNSUPPORTED(__inst_thumb32(0xf1021f08) " @ add pc, r8, #0x00080008") - - TEST_UNSUPPORTED(__inst_thumb32(0xeaa00000) "") - TEST_UNSUPPORTED(__inst_thumb32(0xeaf00000) "") - TEST_UNSUPPORTED(__inst_thumb32(0xeb200000) "") - TEST_UNSUPPORTED(__inst_thumb32(0xeb800000) "") - TEST_UNSUPPORTED(__inst_thumb32(0xebe00000) "") - - TEST_UNSUPPORTED(__inst_thumb32(0xf0a00000) "") - TEST_UNSUPPORTED(__inst_thumb32(0xf0c00000) "") - TEST_UNSUPPORTED(__inst_thumb32(0xf0f00000) "") - TEST_UNSUPPORTED(__inst_thumb32(0xf1200000) "") - TEST_UNSUPPORTED(__inst_thumb32(0xf1800000) "") - TEST_UNSUPPORTED(__inst_thumb32(0xf1e00000) "") - - TEST_GROUP("Coprocessor instructions") - - TEST_UNSUPPORTED(__inst_thumb32(0xec000000) "") - TEST_UNSUPPORTED(__inst_thumb32(0xeff00000) "") - TEST_UNSUPPORTED(__inst_thumb32(0xfc000000) "") - TEST_UNSUPPORTED(__inst_thumb32(0xfff00000) "") - - TEST_GROUP("Data-processing (plain binary immediate)") - - TEST_R("addw r0, r",1, VAL1,", #0x123") - TEST( "addw r14, sp, #0xf5a") - TEST( "addw sp, sp, #0x20") - TEST( "addw r7, pc, #0x888") - TEST_UNSUPPORTED(__inst_thumb32(0xf20f1f20) " @ addw pc, pc, #0x120") - TEST_UNSUPPORTED(__inst_thumb32(0xf20d1f20) " @ addw pc, sp, #0x120") - TEST_UNSUPPORTED(__inst_thumb32(0xf20f1d20) " @ addw sp, pc, #0x120") - TEST_UNSUPPORTED(__inst_thumb32(0xf2001d20) " @ addw sp, r0, #0x120") - - TEST_R("subw r0, r",1, VAL1,", #0x123") - TEST( "subw r14, sp, #0xf5a") - TEST( "subw sp, sp, #0x20") - TEST( "subw r7, pc, #0x888") - TEST_UNSUPPORTED(__inst_thumb32(0xf2af1f20) " @ subw pc, pc, #0x120") - TEST_UNSUPPORTED(__inst_thumb32(0xf2ad1f20) " @ subw pc, sp, #0x120") - TEST_UNSUPPORTED(__inst_thumb32(0xf2af1d20) " @ subw sp, pc, #0x120") - TEST_UNSUPPORTED(__inst_thumb32(0xf2a01d20) " @ subw sp, r0, #0x120") - - TEST("movw r0, #0") - TEST("movw r0, #0xffff") - TEST("movw lr, #0xffff") - TEST_UNSUPPORTED(__inst_thumb32(0xf2400d00) " @ movw sp, #0") - TEST_UNSUPPORTED(__inst_thumb32(0xf2400f00) " @ movw pc, #0") - - TEST_R("movt r",0, VAL1,", #0") - TEST_R("movt r",0, VAL2,", #0xffff") - TEST_R("movt r",14,VAL1,", #0xffff") - TEST_UNSUPPORTED(__inst_thumb32(0xf2c00d00) " @ movt sp, #0") - TEST_UNSUPPORTED(__inst_thumb32(0xf2c00f00) " @ movt pc, #0") - - TEST_R( "ssat r0, #24, r",0, VAL1,"") - TEST_R( "ssat r14, #24, r",12, VAL2,"") - TEST_R( "ssat r0, #24, r",0, VAL1,", lsl #8") - TEST_R( "ssat r14, #24, r",12, VAL2,", asr #8") - TEST_UNSUPPORTED(__inst_thumb32(0xf30c0d17) " @ ssat sp, #24, r12") - TEST_UNSUPPORTED(__inst_thumb32(0xf30c0f17) " @ ssat pc, #24, r12") - TEST_UNSUPPORTED(__inst_thumb32(0xf30d0c17) " @ ssat r12, #24, sp") - TEST_UNSUPPORTED(__inst_thumb32(0xf30f0c17) " @ ssat r12, #24, pc") - - TEST_R( "usat r0, #24, r",0, VAL1,"") - TEST_R( "usat r14, #24, r",12, VAL2,"") - TEST_R( "usat r0, #24, r",0, VAL1,", lsl #8") - TEST_R( "usat r14, #24, r",12, VAL2,", asr #8") - TEST_UNSUPPORTED(__inst_thumb32(0xf38c0d17) " @ usat sp, #24, r12") - TEST_UNSUPPORTED(__inst_thumb32(0xf38c0f17) " @ usat pc, #24, r12") - TEST_UNSUPPORTED(__inst_thumb32(0xf38d0c17) " @ usat r12, #24, sp") - TEST_UNSUPPORTED(__inst_thumb32(0xf38f0c17) " @ usat r12, #24, pc") - - TEST_R( "ssat16 r0, #12, r",0, HH1,"") - TEST_R( "ssat16 r14, #12, r",12, HH2,"") - TEST_UNSUPPORTED(__inst_thumb32(0xf32c0d0b) " @ ssat16 sp, #12, r12") - TEST_UNSUPPORTED(__inst_thumb32(0xf32c0f0b) " @ ssat16 pc, #12, r12") - TEST_UNSUPPORTED(__inst_thumb32(0xf32d0c0b) " @ ssat16 r12, #12, sp") - TEST_UNSUPPORTED(__inst_thumb32(0xf32f0c0b) " @ ssat16 r12, #12, pc") - - TEST_R( "usat16 r0, #12, r",0, HH1,"") - TEST_R( "usat16 r14, #12, r",12, HH2,"") - TEST_UNSUPPORTED(__inst_thumb32(0xf3ac0d0b) " @ usat16 sp, #12, r12") - TEST_UNSUPPORTED(__inst_thumb32(0xf3ac0f0b) " @ usat16 pc, #12, r12") - TEST_UNSUPPORTED(__inst_thumb32(0xf3ad0c0b) " @ usat16 r12, #12, sp") - TEST_UNSUPPORTED(__inst_thumb32(0xf3af0c0b) " @ usat16 r12, #12, pc") - - TEST_R( "sbfx r0, r",0 , VAL1,", #0, #31") - TEST_R( "sbfx r14, r",12, VAL2,", #8, #16") - TEST_R( "sbfx r4, r",10, VAL1,", #16, #15") - TEST_UNSUPPORTED(__inst_thumb32(0xf34c2d0f) " @ sbfx sp, r12, #8, #16") - TEST_UNSUPPORTED(__inst_thumb32(0xf34c2f0f) " @ sbfx pc, r12, #8, #16") - TEST_UNSUPPORTED(__inst_thumb32(0xf34d2c0f) " @ sbfx r12, sp, #8, #16") - TEST_UNSUPPORTED(__inst_thumb32(0xf34f2c0f) " @ sbfx r12, pc, #8, #16") - - TEST_R( "ubfx r0, r",0 , VAL1,", #0, #31") - TEST_R( "ubfx r14, r",12, VAL2,", #8, #16") - TEST_R( "ubfx r4, r",10, VAL1,", #16, #15") - TEST_UNSUPPORTED(__inst_thumb32(0xf3cc2d0f) " @ ubfx sp, r12, #8, #16") - TEST_UNSUPPORTED(__inst_thumb32(0xf3cc2f0f) " @ ubfx pc, r12, #8, #16") - TEST_UNSUPPORTED(__inst_thumb32(0xf3cd2c0f) " @ ubfx r12, sp, #8, #16") - TEST_UNSUPPORTED(__inst_thumb32(0xf3cf2c0f) " @ ubfx r12, pc, #8, #16") - - TEST_R( "bfc r",0, VAL1,", #4, #20") - TEST_R( "bfc r",14,VAL2,", #4, #20") - TEST_R( "bfc r",7, VAL1,", #0, #31") - TEST_R( "bfc r",8, VAL2,", #0, #31") - TEST_UNSUPPORTED(__inst_thumb32(0xf36f0d1e) " @ bfc sp, #0, #31") - TEST_UNSUPPORTED(__inst_thumb32(0xf36f0f1e) " @ bfc pc, #0, #31") - - TEST_RR( "bfi r",0, VAL1,", r",0 , VAL2,", #0, #31") - TEST_RR( "bfi r",12,VAL1,", r",14 , VAL2,", #4, #20") - TEST_UNSUPPORTED(__inst_thumb32(0xf36e1d17) " @ bfi sp, r14, #4, #20") - TEST_UNSUPPORTED(__inst_thumb32(0xf36e1f17) " @ bfi pc, r14, #4, #20") - TEST_UNSUPPORTED(__inst_thumb32(0xf36d1e17) " @ bfi r14, sp, #4, #20") - - TEST_GROUP("Branches and miscellaneous control") - -CONDITION_INSTRUCTIONS(22, - TEST_BF("beq.w 2f") - TEST_BB("bne.w 2b") - TEST_BF("bgt.w 2f") - TEST_BB("blt.w 2b") - TEST_BF_X("bpl.w 2f", SPACE_0x1000) -) - - TEST_UNSUPPORTED("msr cpsr, r0") - TEST_UNSUPPORTED("msr cpsr_f, r1") - TEST_UNSUPPORTED("msr spsr, r2") - - TEST_UNSUPPORTED("cpsie.w i") - TEST_UNSUPPORTED("cpsid.w i") - TEST_UNSUPPORTED("cps 0x13") - - TEST_SUPPORTED("yield.w") - TEST("sev.w") - TEST("nop.w") - TEST("wfi.w") - TEST_SUPPORTED("wfe.w") - TEST_UNSUPPORTED("dbg.w #0") - - TEST_UNSUPPORTED("clrex") - TEST_UNSUPPORTED("dsb") - TEST_UNSUPPORTED("dmb") - TEST_UNSUPPORTED("isb") - - TEST_UNSUPPORTED("bxj r0") - - TEST_UNSUPPORTED("subs pc, lr, #4") - - TEST("mrs r0, cpsr") - TEST("mrs r14, cpsr") - TEST_UNSUPPORTED(__inst_thumb32(0xf3ef8d00) " @ mrs sp, spsr") - TEST_UNSUPPORTED(__inst_thumb32(0xf3ef8f00) " @ mrs pc, spsr") - TEST_UNSUPPORTED("mrs r0, spsr") - TEST_UNSUPPORTED("mrs lr, spsr") - - TEST_UNSUPPORTED(__inst_thumb32(0xf7f08000) " @ smc #0") - - TEST_UNSUPPORTED(__inst_thumb32(0xf7f0a000) " @ undefeined") - - TEST_BF( "b.w 2f") - TEST_BB( "b.w 2b") - TEST_BF_X("b.w 2f", SPACE_0x1000) - - TEST_BF( "bl.w 2f") - TEST_BB( "bl.w 2b") - TEST_BB_X("bl.w 2b", SPACE_0x1000) - - TEST_X( "blx __dummy_arm_subroutine", - ".arm \n\t" - ".align \n\t" - ".type __dummy_arm_subroutine, %%function \n\t" - "__dummy_arm_subroutine: \n\t" - "mov r0, pc \n\t" - "bx lr \n\t" - ".thumb \n\t" - ) - TEST( "blx __dummy_arm_subroutine") - - TEST_GROUP("Store single data item") - -#define SINGLE_STORE(size) \ - TEST_RP( "str"size" r",0, VAL1,", [r",11,-1024,", #1024]") \ - TEST_RP( "str"size" r",14,VAL2,", [r",1, -1024,", #1080]") \ - TEST_RP( "str"size" r",0, VAL1,", [r",11,256, ", #-120]") \ - TEST_RP( "str"size" r",14,VAL2,", [r",1, 256, ", #-128]") \ - TEST_RP( "str"size" r",0, VAL1,", [r",11,24, "], #120") \ - TEST_RP( "str"size" r",14,VAL2,", [r",1, 24, "], #128") \ - TEST_RP( "str"size" r",0, VAL1,", [r",11,24, "], #-120") \ - TEST_RP( "str"size" r",14,VAL2,", [r",1, 24, "], #-128") \ - TEST_RP( "str"size" r",0, VAL1,", [r",11,24, ", #120]!") \ - TEST_RP( "str"size" r",14,VAL2,", [r",1, 24, ", #128]!") \ - TEST_RP( "str"size" r",0, VAL1,", [r",11,256, ", #-120]!") \ - TEST_RP( "str"size" r",14,VAL2,", [r",1, 256, ", #-128]!") \ - TEST_RPR("str"size".w r",0, VAL1,", [r",1, 0,", r",2, 4,"]") \ - TEST_RPR("str"size" r",14,VAL2,", [r",10,0,", r",11,4,", lsl #1]") \ - TEST_R( "str"size".w r",7, VAL1,", [sp, #24]") \ - TEST_RP( "str"size".w r",0, VAL2,", [r",0,0, "]") \ - TEST_UNSUPPORTED("str"size"t r0, [r1, #4]") - - SINGLE_STORE("b") - SINGLE_STORE("h") - SINGLE_STORE("") - - TEST("str sp, [sp]") - TEST_UNSUPPORTED(__inst_thumb32(0xf8cfe000) " @ str r14, [pc]") - TEST_UNSUPPORTED(__inst_thumb32(0xf8cef000) " @ str pc, [r14]") - - TEST_GROUP("Advanced SIMD element or structure load/store instructions") - - TEST_UNSUPPORTED(__inst_thumb32(0xf9000000) "") - TEST_UNSUPPORTED(__inst_thumb32(0xf92fffff) "") - TEST_UNSUPPORTED(__inst_thumb32(0xf9800000) "") - TEST_UNSUPPORTED(__inst_thumb32(0xf9efffff) "") - - TEST_GROUP("Load single data item and memory hints") - -#define SINGLE_LOAD(size) \ - TEST_P( "ldr"size" r0, [r",11,-1024, ", #1024]") \ - TEST_P( "ldr"size" r14, [r",1, -1024,", #1080]") \ - TEST_P( "ldr"size" r0, [r",11,256, ", #-120]") \ - TEST_P( "ldr"size" r14, [r",1, 256, ", #-128]") \ - TEST_P( "ldr"size" r0, [r",11,24, "], #120") \ - TEST_P( "ldr"size" r14, [r",1, 24, "], #128") \ - TEST_P( "ldr"size" r0, [r",11,24, "], #-120") \ - TEST_P( "ldr"size" r14, [r",1,24, "], #-128") \ - TEST_P( "ldr"size" r0, [r",11,24, ", #120]!") \ - TEST_P( "ldr"size" r14, [r",1, 24, ", #128]!") \ - TEST_P( "ldr"size" r0, [r",11,256, ", #-120]!") \ - TEST_P( "ldr"size" r14, [r",1, 256, ", #-128]!") \ - TEST_PR("ldr"size".w r0, [r",1, 0,", r",2, 4,"]") \ - TEST_PR("ldr"size" r14, [r",10,0,", r",11,4,", lsl #1]") \ - TEST_X( "ldr"size".w r0, 3f", \ - ".align 3 \n\t" \ - "3: .word "__stringify(VAL1)) \ - TEST_X( "ldr"size".w r14, 3f", \ - ".align 3 \n\t" \ - "3: .word "__stringify(VAL2)) \ - TEST( "ldr"size".w r7, 3b") \ - TEST( "ldr"size".w r7, [sp, #24]") \ - TEST_P( "ldr"size".w r0, [r",0,0, "]") \ - TEST_UNSUPPORTED("ldr"size"t r0, [r1, #4]") - - SINGLE_LOAD("b") - SINGLE_LOAD("sb") - SINGLE_LOAD("h") - SINGLE_LOAD("sh") - SINGLE_LOAD("") - - TEST_BF_P("ldr pc, [r",14, 15*4,"]") - TEST_P( "ldr sp, [r",14, 13*4,"]") - TEST_BF_R("ldr pc, [sp, r",14, 15*4,"]") - TEST_R( "ldr sp, [sp, r",14, 13*4,"]") - TEST_THUMB_TO_ARM_INTERWORK_P("ldr pc, [r",0,0,", #15*4]") - TEST_SUPPORTED("ldr sp, 99f") - TEST_SUPPORTED("ldr pc, 99f") - - TEST_UNSUPPORTED(__inst_thumb32(0xf854700d) " @ ldr r7, [r4, sp]") - TEST_UNSUPPORTED(__inst_thumb32(0xf854700f) " @ ldr r7, [r4, pc]") - TEST_UNSUPPORTED(__inst_thumb32(0xf814700d) " @ ldrb r7, [r4, sp]") - TEST_UNSUPPORTED(__inst_thumb32(0xf814700f) " @ ldrb r7, [r4, pc]") - TEST_UNSUPPORTED(__inst_thumb32(0xf89fd004) " @ ldrb sp, 99f") - TEST_UNSUPPORTED(__inst_thumb32(0xf814d008) " @ ldrb sp, [r4, r8]") - TEST_UNSUPPORTED(__inst_thumb32(0xf894d000) " @ ldrb sp, [r4]") - - TEST_UNSUPPORTED(__inst_thumb32(0xf8600000) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_thumb32(0xf9ffffff) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_thumb32(0xf9500000) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_thumb32(0xf95fffff) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_thumb32(0xf8000800) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_thumb32(0xf97ffaff) "") /* Unallocated space */ - - TEST( "pli [pc, #4]") - TEST( "pli [pc, #-4]") - TEST( "pld [pc, #4]") - TEST( "pld [pc, #-4]") - - TEST_P( "pld [r",0,-1024,", #1024]") - TEST( __inst_thumb32(0xf8b0f400) " @ pldw [r0, #1024]") - TEST_P( "pli [r",4, 0b,", #1024]") - TEST_P( "pld [r",7, 120,", #-120]") - TEST( __inst_thumb32(0xf837fc78) " @ pldw [r7, #-120]") - TEST_P( "pli [r",11,120,", #-120]") - TEST( "pld [sp, #0]") - - TEST_PR("pld [r",7, 24, ", r",0, 16,"]") - TEST_PR("pld [r",8, 24, ", r",12,16,", lsl #3]") - TEST_SUPPORTED(__inst_thumb32(0xf837f000) " @ pldw [r7, r0]") - TEST_SUPPORTED(__inst_thumb32(0xf838f03c) " @ pldw [r8, r12, lsl #3]"); - TEST_RR("pli [r",12,0b,", r",0, 16,"]") - TEST_RR("pli [r",0, 0b,", r",12,16,", lsl #3]") - TEST_R( "pld [sp, r",1, 16,"]") - TEST_UNSUPPORTED(__inst_thumb32(0xf817f00d) " @pld [r7, sp]") - TEST_UNSUPPORTED(__inst_thumb32(0xf817f00f) " @pld [r7, pc]") - - TEST_GROUP("Data-processing (register)") - -#define SHIFTS32(op) \ - TEST_RR(op" r0, r",1, VAL1,", r",2, 3, "") \ - TEST_RR(op" r14, r",12,VAL2,", r",11,10,"") - - SHIFTS32("lsl") - SHIFTS32("lsls") - SHIFTS32("lsr") - SHIFTS32("lsrs") - SHIFTS32("asr") - SHIFTS32("asrs") - SHIFTS32("ror") - SHIFTS32("rors") - - TEST_UNSUPPORTED(__inst_thumb32(0xfa01ff02) " @ lsl pc, r1, r2") - TEST_UNSUPPORTED(__inst_thumb32(0xfa01fd02) " @ lsl sp, r1, r2") - TEST_UNSUPPORTED(__inst_thumb32(0xfa0ff002) " @ lsl r0, pc, r2") - TEST_UNSUPPORTED(__inst_thumb32(0xfa0df002) " @ lsl r0, sp, r2") - TEST_UNSUPPORTED(__inst_thumb32(0xfa01f00f) " @ lsl r0, r1, pc") - TEST_UNSUPPORTED(__inst_thumb32(0xfa01f00d) " @ lsl r0, r1, sp") - - TEST_RR( "sxtah r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "sxtah r14,r",12, HH2,", r",10,HH1,", ror #8") - TEST_R( "sxth r8, r",7, HH1,"") - - TEST_UNSUPPORTED(__inst_thumb32(0xfa0fff87) " @ sxth pc, r7"); - TEST_UNSUPPORTED(__inst_thumb32(0xfa0ffd87) " @ sxth sp, r7"); - TEST_UNSUPPORTED(__inst_thumb32(0xfa0ff88f) " @ sxth r8, pc"); - TEST_UNSUPPORTED(__inst_thumb32(0xfa0ff88d) " @ sxth r8, sp"); - - TEST_RR( "uxtah r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uxtah r14,r",12, HH2,", r",10,HH1,", ror #8") - TEST_R( "uxth r8, r",7, HH1,"") - - TEST_RR( "sxtab16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "sxtab16 r14,r",12, HH2,", r",10,HH1,", ror #8") - TEST_R( "sxtb16 r8, r",7, HH1,"") - - TEST_RR( "uxtab16 r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uxtab16 r14,r",12, HH2,", r",10,HH1,", ror #8") - TEST_R( "uxtb16 r8, r",7, HH1,"") - - TEST_RR( "sxtab r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "sxtab r14,r",12, HH2,", r",10,HH1,", ror #8") - TEST_R( "sxtb r8, r",7, HH1,"") - - TEST_RR( "uxtab r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "uxtab r14,r",12, HH2,", r",10,HH1,", ror #8") - TEST_R( "uxtb r8, r",7, HH1,"") - - TEST_UNSUPPORTED(__inst_thumb32(0xfa6000f0) "") - TEST_UNSUPPORTED(__inst_thumb32(0xfa7fffff) "") - -#define PARALLEL_ADD_SUB(op) \ - TEST_RR( op"add16 r0, r",0, HH1,", r",1, HH2,"") \ - TEST_RR( op"add16 r14, r",12,HH2,", r",10,HH1,"") \ - TEST_RR( op"asx r0, r",0, HH1,", r",1, HH2,"") \ - TEST_RR( op"asx r14, r",12,HH2,", r",10,HH1,"") \ - TEST_RR( op"sax r0, r",0, HH1,", r",1, HH2,"") \ - TEST_RR( op"sax r14, r",12,HH2,", r",10,HH1,"") \ - TEST_RR( op"sub16 r0, r",0, HH1,", r",1, HH2,"") \ - TEST_RR( op"sub16 r14, r",12,HH2,", r",10,HH1,"") \ - TEST_RR( op"add8 r0, r",0, HH1,", r",1, HH2,"") \ - TEST_RR( op"add8 r14, r",12,HH2,", r",10,HH1,"") \ - TEST_RR( op"sub8 r0, r",0, HH1,", r",1, HH2,"") \ - TEST_RR( op"sub8 r14, r",12,HH2,", r",10,HH1,"") - - TEST_GROUP("Parallel addition and subtraction, signed") - - PARALLEL_ADD_SUB("s") - PARALLEL_ADD_SUB("q") - PARALLEL_ADD_SUB("sh") - - TEST_GROUP("Parallel addition and subtraction, unsigned") - - PARALLEL_ADD_SUB("u") - PARALLEL_ADD_SUB("uq") - PARALLEL_ADD_SUB("uh") - - TEST_GROUP("Miscellaneous operations") - - TEST_RR("qadd r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR("qadd lr, r",9, VAL2,", r",8, VAL1,"") - TEST_RR("qsub r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR("qsub lr, r",9, VAL2,", r",8, VAL1,"") - TEST_RR("qdadd r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR("qdadd lr, r",9, VAL2,", r",8, VAL1,"") - TEST_RR("qdsub r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR("qdsub lr, r",9, VAL2,", r",8, VAL1,"") - - TEST_R("rev.w r0, r",0, VAL1,"") - TEST_R("rev r14, r",12, VAL2,"") - TEST_R("rev16.w r0, r",0, VAL1,"") - TEST_R("rev16 r14, r",12, VAL2,"") - TEST_R("rbit r0, r",0, VAL1,"") - TEST_R("rbit r14, r",12, VAL2,"") - TEST_R("revsh.w r0, r",0, VAL1,"") - TEST_R("revsh r14, r",12, VAL2,"") - - TEST_UNSUPPORTED(__inst_thumb32(0xfa9cff8c) " @ rev pc, r12"); - TEST_UNSUPPORTED(__inst_thumb32(0xfa9cfd8c) " @ rev sp, r12"); - TEST_UNSUPPORTED(__inst_thumb32(0xfa9ffe8f) " @ rev r14, pc"); - TEST_UNSUPPORTED(__inst_thumb32(0xfa9dfe8d) " @ rev r14, sp"); - - TEST_RR("sel r0, r",0, VAL1,", r",1, VAL2,"") - TEST_RR("sel r14, r",12,VAL1,", r",10, VAL2,"") - - TEST_R("clz r0, r",0, 0x0,"") - TEST_R("clz r7, r",14,0x1,"") - TEST_R("clz lr, r",7, 0xffffffff,"") - - TEST_UNSUPPORTED(__inst_thumb32(0xfa80f030) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_thumb32(0xfaffff7f) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_thumb32(0xfab0f000) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_thumb32(0xfaffff7f) "") /* Unallocated space */ - - TEST_GROUP("Multiply, multiply accumulate, and absolute difference operations") - - TEST_RR( "mul r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "mul r7, r",8, VAL2,", r",9, VAL2,"") - TEST_UNSUPPORTED(__inst_thumb32(0xfb08ff09) " @ mul pc, r8, r9") - TEST_UNSUPPORTED(__inst_thumb32(0xfb08fd09) " @ mul sp, r8, r9") - TEST_UNSUPPORTED(__inst_thumb32(0xfb0ff709) " @ mul r7, pc, r9") - TEST_UNSUPPORTED(__inst_thumb32(0xfb0df709) " @ mul r7, sp, r9") - TEST_UNSUPPORTED(__inst_thumb32(0xfb08f70f) " @ mul r7, r8, pc") - TEST_UNSUPPORTED(__inst_thumb32(0xfb08f70d) " @ mul r7, r8, sp") - - TEST_RRR( "mla r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "mla r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_UNSUPPORTED(__inst_thumb32(0xfb08af09) " @ mla pc, r8, r9, r10"); - TEST_UNSUPPORTED(__inst_thumb32(0xfb08ad09) " @ mla sp, r8, r9, r10"); - TEST_UNSUPPORTED(__inst_thumb32(0xfb0fa709) " @ mla r7, pc, r9, r10"); - TEST_UNSUPPORTED(__inst_thumb32(0xfb0da709) " @ mla r7, sp, r9, r10"); - TEST_UNSUPPORTED(__inst_thumb32(0xfb08a70f) " @ mla r7, r8, pc, r10"); - TEST_UNSUPPORTED(__inst_thumb32(0xfb08a70d) " @ mla r7, r8, sp, r10"); - TEST_UNSUPPORTED(__inst_thumb32(0xfb08d709) " @ mla r7, r8, r9, sp"); - - TEST_RRR( "mls r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "mls r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - - TEST_RRR( "smlabb r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "smlabb r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_RRR( "smlatb r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "smlatb r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_RRR( "smlabt r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "smlabt r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_RRR( "smlatt r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "smlatt r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_RR( "smulbb r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "smulbb r7, r",8, VAL3,", r",9, VAL1,"") - TEST_RR( "smultb r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "smultb r7, r",8, VAL3,", r",9, VAL1,"") - TEST_RR( "smulbt r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "smulbt r7, r",8, VAL3,", r",9, VAL1,"") - TEST_RR( "smultt r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "smultt r7, r",8, VAL3,", r",9, VAL1,"") - - TEST_RRR( "smlad r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") - TEST_RRR( "smlad r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") - TEST_RRR( "smladx r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") - TEST_RRR( "smladx r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") - TEST_RR( "smuad r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "smuad r14, r",12,HH2,", r",10,HH1,"") - TEST_RR( "smuadx r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "smuadx r14, r",12,HH2,", r",10,HH1,"") - - TEST_RRR( "smlawb r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "smlawb r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_RRR( "smlawt r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") - TEST_RRR( "smlawt r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") - TEST_RR( "smulwb r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "smulwb r7, r",8, VAL3,", r",9, VAL1,"") - TEST_RR( "smulwt r0, r",1, VAL1,", r",2, VAL2,"") - TEST_RR( "smulwt r7, r",8, VAL3,", r",9, VAL1,"") - - TEST_RRR( "smlsd r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") - TEST_RRR( "smlsd r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") - TEST_RRR( "smlsdx r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") - TEST_RRR( "smlsdx r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") - TEST_RR( "smusd r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "smusd r14, r",12,HH2,", r",10,HH1,"") - TEST_RR( "smusdx r0, r",0, HH1,", r",1, HH2,"") - TEST_RR( "smusdx r14, r",12,HH2,", r",10,HH1,"") - - TEST_RRR( "smmla r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") - TEST_RRR( "smmla r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") - TEST_RRR( "smmlar r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") - TEST_RRR( "smmlar r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") - TEST_RR( "smmul r0, r",0, VAL1,", r",1, VAL2,"") - TEST_RR( "smmul r14, r",12,VAL2,", r",10,VAL1,"") - TEST_RR( "smmulr r0, r",0, VAL1,", r",1, VAL2,"") - TEST_RR( "smmulr r14, r",12,VAL2,", r",10,VAL1,"") - - TEST_RRR( "smmls r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") - TEST_RRR( "smmls r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") - TEST_RRR( "smmlsr r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") - TEST_RRR( "smmlsr r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") - - TEST_RRR( "usada8 r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL3,"") - TEST_RRR( "usada8 r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL3,"") - TEST_RR( "usad8 r0, r",0, VAL1,", r",1, VAL2,"") - TEST_RR( "usad8 r14, r",12,VAL2,", r",10,VAL1,"") - - TEST_UNSUPPORTED(__inst_thumb32(0xfb00f010) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_thumb32(0xfb0fff1f) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_thumb32(0xfb70f010) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_thumb32(0xfb7fff1f) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_thumb32(0xfb700010) "") /* Unallocated space */ - TEST_UNSUPPORTED(__inst_thumb32(0xfb7fff1f) "") /* Unallocated space */ - - TEST_GROUP("Long multiply, long multiply accumulate, and divide") - - TEST_RR( "smull r0, r1, r",2, VAL1,", r",3, VAL2,"") - TEST_RR( "smull r7, r8, r",9, VAL2,", r",10, VAL1,"") - TEST_UNSUPPORTED(__inst_thumb32(0xfb89f80a) " @ smull pc, r8, r9, r10"); - TEST_UNSUPPORTED(__inst_thumb32(0xfb89d80a) " @ smull sp, r8, r9, r10"); - TEST_UNSUPPORTED(__inst_thumb32(0xfb897f0a) " @ smull r7, pc, r9, r10"); - TEST_UNSUPPORTED(__inst_thumb32(0xfb897d0a) " @ smull r7, sp, r9, r10"); - TEST_UNSUPPORTED(__inst_thumb32(0xfb8f780a) " @ smull r7, r8, pc, r10"); - TEST_UNSUPPORTED(__inst_thumb32(0xfb8d780a) " @ smull r7, r8, sp, r10"); - TEST_UNSUPPORTED(__inst_thumb32(0xfb89780f) " @ smull r7, r8, r9, pc"); - TEST_UNSUPPORTED(__inst_thumb32(0xfb89780d) " @ smull r7, r8, r9, sp"); - - TEST_RR( "umull r0, r1, r",2, VAL1,", r",3, VAL2,"") - TEST_RR( "umull r7, r8, r",9, VAL2,", r",10, VAL1,"") - - TEST_RRRR( "smlal r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) - TEST_RRRR( "smlal r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) - - TEST_RRRR( "smlalbb r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) - TEST_RRRR( "smlalbb r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) - TEST_RRRR( "smlalbt r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) - TEST_RRRR( "smlalbt r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) - TEST_RRRR( "smlaltb r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) - TEST_RRRR( "smlaltb r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) - TEST_RRRR( "smlaltt r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) - TEST_RRRR( "smlaltt r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) - - TEST_RRRR( "smlald r",0, VAL1,", r",1, VAL2, ", r",0, HH1,", r",1, HH2) - TEST_RRRR( "smlald r",11,VAL2,", r",10,VAL1, ", r",9, HH2,", r",8, HH1) - TEST_RRRR( "smlaldx r",0, VAL1,", r",1, VAL2, ", r",0, HH1,", r",1, HH2) - TEST_RRRR( "smlaldx r",11,VAL2,", r",10,VAL1, ", r",9, HH2,", r",8, HH1) - - TEST_RRRR( "smlsld r",0, VAL1,", r",1, VAL2, ", r",0, HH1,", r",1, HH2) - TEST_RRRR( "smlsld r",11,VAL2,", r",10,VAL1, ", r",9, HH2,", r",8, HH1) - TEST_RRRR( "smlsldx r",0, VAL1,", r",1, VAL2, ", r",0, HH1,", r",1, HH2) - TEST_RRRR( "smlsldx r",11,VAL2,", r",10,VAL1, ", r",9, HH2,", r",8, HH1) - - TEST_RRRR( "umlal r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) - TEST_RRRR( "umlal r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) - TEST_RRRR( "umaal r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) - TEST_RRRR( "umaal r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) - - TEST_GROUP("Coprocessor instructions") - - TEST_UNSUPPORTED(__inst_thumb32(0xfc000000) "") - TEST_UNSUPPORTED(__inst_thumb32(0xffffffff) "") - - TEST_GROUP("Testing instructions in IT blocks") - - TEST_ITBLOCK("sub.w r0, r0") - - verbose("\n"); -} - diff --git a/arch/arm/kernel/kprobes-test.c b/arch/arm/kernel/kprobes-test.c deleted file mode 100644 index b206d77..0000000 --- a/arch/arm/kernel/kprobes-test.c +++ /dev/null @@ -1,1713 +0,0 @@ -/* - * arch/arm/kernel/kprobes-test.c - * - * Copyright (C) 2011 Jon Medhurst . - * - * 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 file contains test code for ARM kprobes. - * - * The top level function run_all_tests() executes tests for all of the - * supported instruction sets: ARM, 16-bit Thumb, and 32-bit Thumb. These tests - * fall into two categories; run_api_tests() checks basic functionality of the - * kprobes API, and run_test_cases() is a comprehensive test for kprobes - * instruction decoding and simulation. - * - * run_test_cases() first checks the kprobes decoding table for self consistency - * (using table_test()) then executes a series of test cases for each of the CPU - * instruction forms. coverage_start() and coverage_end() are used to verify - * that these test cases cover all of the possible combinations of instructions - * described by the kprobes decoding tables. - * - * The individual test cases are in kprobes-test-arm.c and kprobes-test-thumb.c - * which use the macros defined in kprobes-test.h. The rest of this - * documentation will describe the operation of the framework used by these - * test cases. - */ - -/* - * TESTING METHODOLOGY - * ------------------- - * - * The methodology used to test an ARM instruction 'test_insn' is to use - * inline assembler like: - * - * test_before: nop - * test_case: test_insn - * test_after: nop - * - * When the test case is run a kprobe is placed of each nop. The - * post-handler of the test_before probe is used to modify the saved CPU - * register context to that which we require for the test case. The - * pre-handler of the of the test_after probe saves a copy of the CPU - * register context. In this way we can execute test_insn with a specific - * register context and see the results afterwards. - * - * To actually test the kprobes instruction emulation we perform the above - * step a second time but with an additional kprobe on the test_case - * instruction itself. If the emulation is accurate then the results seen - * by the test_after probe will be identical to the first run which didn't - * have a probe on test_case. - * - * Each test case is run several times with a variety of variations in the - * flags value of stored in CPSR, and for Thumb code, different ITState. - * - * For instructions which can modify PC, a second test_after probe is used - * like this: - * - * test_before: nop - * test_case: test_insn - * test_after: nop - * b test_done - * test_after2: nop - * test_done: - * - * The test case is constructed such that test_insn branches to - * test_after2, or, if testing a conditional instruction, it may just - * continue to test_after. The probes inserted at both locations let us - * determine which happened. A similar approach is used for testing - * backwards branches... - * - * b test_before - * b test_done @ helps to cope with off by 1 branches - * test_after2: nop - * b test_done - * test_before: nop - * test_case: test_insn - * test_after: nop - * test_done: - * - * The macros used to generate the assembler instructions describe above - * are TEST_INSTRUCTION, TEST_BRANCH_F (branch forwards) and TEST_BRANCH_B - * (branch backwards). In these, the local variables numbered 1, 50, 2 and - * 99 represent: test_before, test_case, test_after2 and test_done. - * - * FRAMEWORK - * --------- - * - * Each test case is wrapped between the pair of macros TESTCASE_START and - * TESTCASE_END. As well as performing the inline assembler boilerplate, - * these call out to the kprobes_test_case_start() and - * kprobes_test_case_end() functions which drive the execution of the test - * case. The specific arguments to use for each test case are stored as - * inline data constructed using the various TEST_ARG_* macros. Putting - * this all together, a simple test case may look like: - * - * TESTCASE_START("Testing mov r0, r7") - * TEST_ARG_REG(7, 0x12345678) // Set r7=0x12345678 - * TEST_ARG_END("") - * TEST_INSTRUCTION("mov r0, r7") - * TESTCASE_END - * - * Note, in practice the single convenience macro TEST_R would be used for this - * instead. - * - * The above would expand to assembler looking something like: - * - * @ TESTCASE_START - * bl __kprobes_test_case_start - * .pushsection .rodata - * "10: - * .ascii "mov r0, r7" @ text title for test case - * .byte 0 - * .popsection - * @ start of inline data... - * .word 10b @ pointer to title in .rodata section - * - * @ TEST_ARG_REG - * .byte ARG_TYPE_REG - * .byte 7 - * .short 0 - * .word 0x1234567 - * - * @ TEST_ARG_END - * .byte ARG_TYPE_END - * .byte TEST_ISA @ flags, including ISA being tested - * .short 50f-0f @ offset of 'test_before' - * .short 2f-0f @ offset of 'test_after2' (if relevent) - * .short 99f-0f @ offset of 'test_done' - * @ start of test case code... - * 0: - * .code TEST_ISA @ switch to ISA being tested - * - * @ TEST_INSTRUCTION - * 50: nop @ location for 'test_before' probe - * 1: mov r0, r7 @ the test case instruction 'test_insn' - * nop @ location for 'test_after' probe - * - * // TESTCASE_END - * 2: - * 99: bl __kprobes_test_case_end_##TEST_ISA - * .code NONMAL_ISA - * - * When the above is execute the following happens... - * - * __kprobes_test_case_start() is an assembler wrapper which sets up space - * for a stack buffer and calls the C function kprobes_test_case_start(). - * This C function will do some initial processing of the inline data and - * setup some global state. It then inserts the test_before and test_after - * kprobes and returns a value which causes the assembler wrapper to jump - * to the start of the test case code, (local label '0'). - * - * When the test case code executes, the test_before probe will be hit and - * test_before_post_handler will call setup_test_context(). This fills the - * stack buffer and CPU registers with a test pattern and then processes - * the test case arguments. In our example there is one TEST_ARG_REG which - * indicates that R7 should be loaded with the value 0x12345678. - * - * When the test_before probe ends, the test case continues and executes - * the "mov r0, r7" instruction. It then hits the test_after probe and the - * pre-handler for this (test_after_pre_handler) will save a copy of the - * CPU register context. This should now have R0 holding the same value as - * R7. - * - * Finally we get to the call to __kprobes_test_case_end_{32,16}. This is - * an assembler wrapper which switches back to the ISA used by the test - * code and calls the C function kprobes_test_case_end(). - * - * For each run through the test case, test_case_run_count is incremented - * by one. For even runs, kprobes_test_case_end() saves a copy of the - * register and stack buffer contents from the test case just run. It then - * inserts a kprobe on the test case instruction 'test_insn' and returns a - * value to cause the test case code to be re-run. - * - * For odd numbered runs, kprobes_test_case_end() compares the register and - * stack buffer contents to those that were saved on the previous even - * numbered run (the one without the kprobe on test_insn). These should be - * the same if the kprobe instruction simulation routine is correct. - * - * The pair of test case runs is repeated with different combinations of - * flag values in CPSR and, for Thumb, different ITState. This is - * controlled by test_context_cpsr(). - * - * BUILDING TEST CASES - * ------------------- - * - * - * As an aid to building test cases, the stack buffer is initialised with - * some special values: - * - * [SP+13*4] Contains SP+120. This can be used to test instructions - * which load a value into SP. - * - * [SP+15*4] When testing branching instructions using TEST_BRANCH_{F,B}, - * this holds the target address of the branch, 'test_after2'. - * This can be used to test instructions which load a PC value - * from memory. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "kprobes.h" -#include "probes-arm.h" -#include "probes-thumb.h" -#include "kprobes-test.h" - - -#define BENCHMARKING 1 - - -/* - * Test basic API - */ - -static bool test_regs_ok; -static int test_func_instance; -static int pre_handler_called; -static int post_handler_called; -static int jprobe_func_called; -static int kretprobe_handler_called; -static int tests_failed; - -#define FUNC_ARG1 0x12345678 -#define FUNC_ARG2 0xabcdef - - -#ifndef CONFIG_THUMB2_KERNEL - -long arm_func(long r0, long r1); - -static void __used __naked __arm_kprobes_test_func(void) -{ - __asm__ __volatile__ ( - ".arm \n\t" - ".type arm_func, %%function \n\t" - "arm_func: \n\t" - "adds r0, r0, r1 \n\t" - "bx lr \n\t" - ".code "NORMAL_ISA /* Back to Thumb if necessary */ - : : : "r0", "r1", "cc" - ); -} - -#else /* CONFIG_THUMB2_KERNEL */ - -long thumb16_func(long r0, long r1); -long thumb32even_func(long r0, long r1); -long thumb32odd_func(long r0, long r1); - -static void __used __naked __thumb_kprobes_test_funcs(void) -{ - __asm__ __volatile__ ( - ".type thumb16_func, %%function \n\t" - "thumb16_func: \n\t" - "adds.n r0, r0, r1 \n\t" - "bx lr \n\t" - - ".align \n\t" - ".type thumb32even_func, %%function \n\t" - "thumb32even_func: \n\t" - "adds.w r0, r0, r1 \n\t" - "bx lr \n\t" - - ".align \n\t" - "nop.n \n\t" - ".type thumb32odd_func, %%function \n\t" - "thumb32odd_func: \n\t" - "adds.w r0, r0, r1 \n\t" - "bx lr \n\t" - - : : : "r0", "r1", "cc" - ); -} - -#endif /* CONFIG_THUMB2_KERNEL */ - - -static int call_test_func(long (*func)(long, long), bool check_test_regs) -{ - long ret; - - ++test_func_instance; - test_regs_ok = false; - - ret = (*func)(FUNC_ARG1, FUNC_ARG2); - if (ret != FUNC_ARG1 + FUNC_ARG2) { - pr_err("FAIL: call_test_func: func returned %lx\n", ret); - return false; - } - - if (check_test_regs && !test_regs_ok) { - pr_err("FAIL: test regs not OK\n"); - return false; - } - - return true; -} - -static int __kprobes pre_handler(struct kprobe *p, struct pt_regs *regs) -{ - pre_handler_called = test_func_instance; - if (regs->ARM_r0 == FUNC_ARG1 && regs->ARM_r1 == FUNC_ARG2) - test_regs_ok = true; - return 0; -} - -static void __kprobes post_handler(struct kprobe *p, struct pt_regs *regs, - unsigned long flags) -{ - post_handler_called = test_func_instance; - if (regs->ARM_r0 != FUNC_ARG1 + FUNC_ARG2 || regs->ARM_r1 != FUNC_ARG2) - test_regs_ok = false; -} - -static struct kprobe the_kprobe = { - .addr = 0, - .pre_handler = pre_handler, - .post_handler = post_handler -}; - -static int test_kprobe(long (*func)(long, long)) -{ - int ret; - - the_kprobe.addr = (kprobe_opcode_t *)func; - ret = register_kprobe(&the_kprobe); - if (ret < 0) { - pr_err("FAIL: register_kprobe failed with %d\n", ret); - return ret; - } - - ret = call_test_func(func, true); - - unregister_kprobe(&the_kprobe); - the_kprobe.flags = 0; /* Clear disable flag to allow reuse */ - - if (!ret) - return -EINVAL; - if (pre_handler_called != test_func_instance) { - pr_err("FAIL: kprobe pre_handler not called\n"); - return -EINVAL; - } - if (post_handler_called != test_func_instance) { - pr_err("FAIL: kprobe post_handler not called\n"); - return -EINVAL; - } - if (!call_test_func(func, false)) - return -EINVAL; - if (pre_handler_called == test_func_instance || - post_handler_called == test_func_instance) { - pr_err("FAIL: probe called after unregistering\n"); - return -EINVAL; - } - - return 0; -} - -static void __kprobes jprobe_func(long r0, long r1) -{ - jprobe_func_called = test_func_instance; - if (r0 == FUNC_ARG1 && r1 == FUNC_ARG2) - test_regs_ok = true; - jprobe_return(); -} - -static struct jprobe the_jprobe = { - .entry = jprobe_func, -}; - -static int test_jprobe(long (*func)(long, long)) -{ - int ret; - - the_jprobe.kp.addr = (kprobe_opcode_t *)func; - ret = register_jprobe(&the_jprobe); - if (ret < 0) { - pr_err("FAIL: register_jprobe failed with %d\n", ret); - return ret; - } - - ret = call_test_func(func, true); - - unregister_jprobe(&the_jprobe); - the_jprobe.kp.flags = 0; /* Clear disable flag to allow reuse */ - - if (!ret) - return -EINVAL; - if (jprobe_func_called != test_func_instance) { - pr_err("FAIL: jprobe handler function not called\n"); - return -EINVAL; - } - if (!call_test_func(func, false)) - return -EINVAL; - if (jprobe_func_called == test_func_instance) { - pr_err("FAIL: probe called after unregistering\n"); - return -EINVAL; - } - - return 0; -} - -static int __kprobes -kretprobe_handler(struct kretprobe_instance *ri, struct pt_regs *regs) -{ - kretprobe_handler_called = test_func_instance; - if (regs_return_value(regs) == FUNC_ARG1 + FUNC_ARG2) - test_regs_ok = true; - return 0; -} - -static struct kretprobe the_kretprobe = { - .handler = kretprobe_handler, -}; - -static int test_kretprobe(long (*func)(long, long)) -{ - int ret; - - the_kretprobe.kp.addr = (kprobe_opcode_t *)func; - ret = register_kretprobe(&the_kretprobe); - if (ret < 0) { - pr_err("FAIL: register_kretprobe failed with %d\n", ret); - return ret; - } - - ret = call_test_func(func, true); - - unregister_kretprobe(&the_kretprobe); - the_kretprobe.kp.flags = 0; /* Clear disable flag to allow reuse */ - - if (!ret) - return -EINVAL; - if (kretprobe_handler_called != test_func_instance) { - pr_err("FAIL: kretprobe handler not called\n"); - return -EINVAL; - } - if (!call_test_func(func, false)) - return -EINVAL; - if (jprobe_func_called == test_func_instance) { - pr_err("FAIL: kretprobe called after unregistering\n"); - return -EINVAL; - } - - return 0; -} - -static int run_api_tests(long (*func)(long, long)) -{ - int ret; - - pr_info(" kprobe\n"); - ret = test_kprobe(func); - if (ret < 0) - return ret; - - pr_info(" jprobe\n"); - ret = test_jprobe(func); -#if defined(CONFIG_THUMB2_KERNEL) && !defined(MODULE) - if (ret == -EINVAL) { - pr_err("FAIL: Known longtime bug with jprobe on Thumb kernels\n"); - tests_failed = ret; - ret = 0; - } -#endif - if (ret < 0) - return ret; - - pr_info(" kretprobe\n"); - ret = test_kretprobe(func); - if (ret < 0) - return ret; - - return 0; -} - - -/* - * Benchmarking - */ - -#if BENCHMARKING - -static void __naked benchmark_nop(void) -{ - __asm__ __volatile__ ( - "nop \n\t" - "bx lr" - ); -} - -#ifdef CONFIG_THUMB2_KERNEL -#define wide ".w" -#else -#define wide -#endif - -static void __naked benchmark_pushpop1(void) -{ - __asm__ __volatile__ ( - "stmdb"wide" sp!, {r3-r11,lr} \n\t" - "ldmia"wide" sp!, {r3-r11,pc}" - ); -} - -static void __naked benchmark_pushpop2(void) -{ - __asm__ __volatile__ ( - "stmdb"wide" sp!, {r0-r8,lr} \n\t" - "ldmia"wide" sp!, {r0-r8,pc}" - ); -} - -static void __naked benchmark_pushpop3(void) -{ - __asm__ __volatile__ ( - "stmdb"wide" sp!, {r4,lr} \n\t" - "ldmia"wide" sp!, {r4,pc}" - ); -} - -static void __naked benchmark_pushpop4(void) -{ - __asm__ __volatile__ ( - "stmdb"wide" sp!, {r0,lr} \n\t" - "ldmia"wide" sp!, {r0,pc}" - ); -} - - -#ifdef CONFIG_THUMB2_KERNEL - -static void __naked benchmark_pushpop_thumb(void) -{ - __asm__ __volatile__ ( - "push.n {r0-r7,lr} \n\t" - "pop.n {r0-r7,pc}" - ); -} - -#endif - -static int __kprobes -benchmark_pre_handler(struct kprobe *p, struct pt_regs *regs) -{ - return 0; -} - -static int benchmark(void(*fn)(void)) -{ - unsigned n, i, t, t0; - - for (n = 1000; ; n *= 2) { - t0 = sched_clock(); - for (i = n; i > 0; --i) - fn(); - t = sched_clock() - t0; - if (t >= 250000000) - break; /* Stop once we took more than 0.25 seconds */ - } - return t / n; /* Time for one iteration in nanoseconds */ -}; - -static int kprobe_benchmark(void(*fn)(void), unsigned offset) -{ - struct kprobe k = { - .addr = (kprobe_opcode_t *)((uintptr_t)fn + offset), - .pre_handler = benchmark_pre_handler, - }; - - int ret = register_kprobe(&k); - if (ret < 0) { - pr_err("FAIL: register_kprobe failed with %d\n", ret); - return ret; - } - - ret = benchmark(fn); - - unregister_kprobe(&k); - return ret; -}; - -struct benchmarks { - void (*fn)(void); - unsigned offset; - const char *title; -}; - -static int run_benchmarks(void) -{ - int ret; - struct benchmarks list[] = { - {&benchmark_nop, 0, "nop"}, - /* - * benchmark_pushpop{1,3} will have the optimised - * instruction emulation, whilst benchmark_pushpop{2,4} will - * be the equivalent unoptimised instructions. - */ - {&benchmark_pushpop1, 0, "stmdb sp!, {r3-r11,lr}"}, - {&benchmark_pushpop1, 4, "ldmia sp!, {r3-r11,pc}"}, - {&benchmark_pushpop2, 0, "stmdb sp!, {r0-r8,lr}"}, - {&benchmark_pushpop2, 4, "ldmia sp!, {r0-r8,pc}"}, - {&benchmark_pushpop3, 0, "stmdb sp!, {r4,lr}"}, - {&benchmark_pushpop3, 4, "ldmia sp!, {r4,pc}"}, - {&benchmark_pushpop4, 0, "stmdb sp!, {r0,lr}"}, - {&benchmark_pushpop4, 4, "ldmia sp!, {r0,pc}"}, -#ifdef CONFIG_THUMB2_KERNEL - {&benchmark_pushpop_thumb, 0, "push.n {r0-r7,lr}"}, - {&benchmark_pushpop_thumb, 2, "pop.n {r0-r7,pc}"}, -#endif - {0} - }; - - struct benchmarks *b; - for (b = list; b->fn; ++b) { - ret = kprobe_benchmark(b->fn, b->offset); - if (ret < 0) - return ret; - pr_info(" %dns for kprobe %s\n", ret, b->title); - } - - pr_info("\n"); - return 0; -} - -#endif /* BENCHMARKING */ - - -/* - * Decoding table self-consistency tests - */ - -static const int decode_struct_sizes[NUM_DECODE_TYPES] = { - [DECODE_TYPE_TABLE] = sizeof(struct decode_table), - [DECODE_TYPE_CUSTOM] = sizeof(struct decode_custom), - [DECODE_TYPE_SIMULATE] = sizeof(struct decode_simulate), - [DECODE_TYPE_EMULATE] = sizeof(struct decode_emulate), - [DECODE_TYPE_OR] = sizeof(struct decode_or), - [DECODE_TYPE_REJECT] = sizeof(struct decode_reject) -}; - -static int table_iter(const union decode_item *table, - int (*fn)(const struct decode_header *, void *), - void *args) -{ - const struct decode_header *h = (struct decode_header *)table; - int result; - - for (;;) { - enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK; - - if (type == DECODE_TYPE_END) - return 0; - - result = fn(h, args); - if (result) - return result; - - h = (struct decode_header *) - ((uintptr_t)h + decode_struct_sizes[type]); - - } -} - -static int table_test_fail(const struct decode_header *h, const char* message) -{ - - pr_err("FAIL: kprobes test failure \"%s\" (mask %08x, value %08x)\n", - message, h->mask.bits, h->value.bits); - return -EINVAL; -} - -struct table_test_args { - const union decode_item *root_table; - u32 parent_mask; - u32 parent_value; -}; - -static int table_test_fn(const struct decode_header *h, void *args) -{ - struct table_test_args *a = (struct table_test_args *)args; - enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK; - - if (h->value.bits & ~h->mask.bits) - return table_test_fail(h, "Match value has bits not in mask"); - - if ((h->mask.bits & a->parent_mask) != a->parent_mask) - return table_test_fail(h, "Mask has bits not in parent mask"); - - if ((h->value.bits ^ a->parent_value) & a->parent_mask) - return table_test_fail(h, "Value is inconsistent with parent"); - - if (type == DECODE_TYPE_TABLE) { - struct decode_table *d = (struct decode_table *)h; - struct table_test_args args2 = *a; - args2.parent_mask = h->mask.bits; - args2.parent_value = h->value.bits; - return table_iter(d->table.table, table_test_fn, &args2); - } - - return 0; -} - -static int table_test(const union decode_item *table) -{ - struct table_test_args args = { - .root_table = table, - .parent_mask = 0, - .parent_value = 0 - }; - return table_iter(args.root_table, table_test_fn, &args); -} - - -/* - * Decoding table test coverage analysis - * - * coverage_start() builds a coverage_table which contains a list of - * coverage_entry's to match each entry in the specified kprobes instruction - * decoding table. - * - * When test cases are run, coverage_add() is called to process each case. - * This looks up the corresponding entry in the coverage_table and sets it as - * being matched, as well as clearing the regs flag appropriate for the test. - * - * After all test cases have been run, coverage_end() is called to check that - * all entries in coverage_table have been matched and that all regs flags are - * cleared. I.e. that all possible combinations of instructions described by - * the kprobes decoding tables have had a test case executed for them. - */ - -bool coverage_fail; - -#define MAX_COVERAGE_ENTRIES 256 - -struct coverage_entry { - const struct decode_header *header; - unsigned regs; - unsigned nesting; - char matched; -}; - -struct coverage_table { - struct coverage_entry *base; - unsigned num_entries; - unsigned nesting; -}; - -struct coverage_table coverage; - -#define COVERAGE_ANY_REG (1<<0) -#define COVERAGE_SP (1<<1) -#define COVERAGE_PC (1<<2) -#define COVERAGE_PCWB (1<<3) - -static const char coverage_register_lookup[16] = { - [REG_TYPE_ANY] = COVERAGE_ANY_REG | COVERAGE_SP | COVERAGE_PC, - [REG_TYPE_SAMEAS16] = COVERAGE_ANY_REG, - [REG_TYPE_SP] = COVERAGE_SP, - [REG_TYPE_PC] = COVERAGE_PC, - [REG_TYPE_NOSP] = COVERAGE_ANY_REG | COVERAGE_SP, - [REG_TYPE_NOSPPC] = COVERAGE_ANY_REG | COVERAGE_SP | COVERAGE_PC, - [REG_TYPE_NOPC] = COVERAGE_ANY_REG | COVERAGE_PC, - [REG_TYPE_NOPCWB] = COVERAGE_ANY_REG | COVERAGE_PC | COVERAGE_PCWB, - [REG_TYPE_NOPCX] = COVERAGE_ANY_REG, - [REG_TYPE_NOSPPCX] = COVERAGE_ANY_REG | COVERAGE_SP, -}; - -unsigned coverage_start_registers(const struct decode_header *h) -{ - unsigned regs = 0; - int i; - for (i = 0; i < 20; i += 4) { - int r = (h->type_regs.bits >> (DECODE_TYPE_BITS + i)) & 0xf; - regs |= coverage_register_lookup[r] << i; - } - return regs; -} - -static int coverage_start_fn(const struct decode_header *h, void *args) -{ - struct coverage_table *coverage = (struct coverage_table *)args; - enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK; - struct coverage_entry *entry = coverage->base + coverage->num_entries; - - if (coverage->num_entries == MAX_COVERAGE_ENTRIES - 1) { - pr_err("FAIL: Out of space for test coverage data"); - return -ENOMEM; - } - - ++coverage->num_entries; - - entry->header = h; - entry->regs = coverage_start_registers(h); - entry->nesting = coverage->nesting; - entry->matched = false; - - if (type == DECODE_TYPE_TABLE) { - struct decode_table *d = (struct decode_table *)h; - int ret; - ++coverage->nesting; - ret = table_iter(d->table.table, coverage_start_fn, coverage); - --coverage->nesting; - return ret; - } - - return 0; -} - -static int coverage_start(const union decode_item *table) -{ - coverage.base = kmalloc(MAX_COVERAGE_ENTRIES * - sizeof(struct coverage_entry), GFP_KERNEL); - coverage.num_entries = 0; - coverage.nesting = 0; - return table_iter(table, coverage_start_fn, &coverage); -} - -static void -coverage_add_registers(struct coverage_entry *entry, kprobe_opcode_t insn) -{ - int regs = entry->header->type_regs.bits >> DECODE_TYPE_BITS; - int i; - for (i = 0; i < 20; i += 4) { - enum decode_reg_type reg_type = (regs >> i) & 0xf; - int reg = (insn >> i) & 0xf; - int flag; - - if (!reg_type) - continue; - - if (reg == 13) - flag = COVERAGE_SP; - else if (reg == 15) - flag = COVERAGE_PC; - else - flag = COVERAGE_ANY_REG; - entry->regs &= ~(flag << i); - - switch (reg_type) { - - case REG_TYPE_NONE: - case REG_TYPE_ANY: - case REG_TYPE_SAMEAS16: - break; - - case REG_TYPE_SP: - if (reg != 13) - return; - break; - - case REG_TYPE_PC: - if (reg != 15) - return; - break; - - case REG_TYPE_NOSP: - if (reg == 13) - return; - break; - - case REG_TYPE_NOSPPC: - case REG_TYPE_NOSPPCX: - if (reg == 13 || reg == 15) - return; - break; - - case REG_TYPE_NOPCWB: - if (!is_writeback(insn)) - break; - if (reg == 15) { - entry->regs &= ~(COVERAGE_PCWB << i); - return; - } - break; - - case REG_TYPE_NOPC: - case REG_TYPE_NOPCX: - if (reg == 15) - return; - break; - } - - } -} - -static void coverage_add(kprobe_opcode_t insn) -{ - struct coverage_entry *entry = coverage.base; - struct coverage_entry *end = coverage.base + coverage.num_entries; - bool matched = false; - unsigned nesting = 0; - - for (; entry < end; ++entry) { - const struct decode_header *h = entry->header; - enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK; - - if (entry->nesting > nesting) - continue; /* Skip sub-table we didn't match */ - - if (entry->nesting < nesting) - break; /* End of sub-table we were scanning */ - - if (!matched) { - if ((insn & h->mask.bits) != h->value.bits) - continue; - entry->matched = true; - } - - switch (type) { - - case DECODE_TYPE_TABLE: - ++nesting; - break; - - case DECODE_TYPE_CUSTOM: - case DECODE_TYPE_SIMULATE: - case DECODE_TYPE_EMULATE: - coverage_add_registers(entry, insn); - return; - - case DECODE_TYPE_OR: - matched = true; - break; - - case DECODE_TYPE_REJECT: - default: - return; - } - - } -} - -static void coverage_end(void) -{ - struct coverage_entry *entry = coverage.base; - struct coverage_entry *end = coverage.base + coverage.num_entries; - - for (; entry < end; ++entry) { - u32 mask = entry->header->mask.bits; - u32 value = entry->header->value.bits; - - if (entry->regs) { - pr_err("FAIL: Register test coverage missing for %08x %08x (%05x)\n", - mask, value, entry->regs); - coverage_fail = true; - } - if (!entry->matched) { - pr_err("FAIL: Test coverage entry missing for %08x %08x\n", - mask, value); - coverage_fail = true; - } - } - - kfree(coverage.base); -} - - -/* - * Framework for instruction set test cases - */ - -void __naked __kprobes_test_case_start(void) -{ - __asm__ __volatile__ ( - "stmdb sp!, {r4-r11} \n\t" - "sub sp, sp, #"__stringify(TEST_MEMORY_SIZE)"\n\t" - "bic r0, lr, #1 @ r0 = inline data \n\t" - "mov r1, sp \n\t" - "bl kprobes_test_case_start \n\t" - "bx r0 \n\t" - ); -} - -#ifndef CONFIG_THUMB2_KERNEL - -void __naked __kprobes_test_case_end_32(void) -{ - __asm__ __volatile__ ( - "mov r4, lr \n\t" - "bl kprobes_test_case_end \n\t" - "cmp r0, #0 \n\t" - "movne pc, r0 \n\t" - "mov r0, r4 \n\t" - "add sp, sp, #"__stringify(TEST_MEMORY_SIZE)"\n\t" - "ldmia sp!, {r4-r11} \n\t" - "mov pc, r0 \n\t" - ); -} - -#else /* CONFIG_THUMB2_KERNEL */ - -void __naked __kprobes_test_case_end_16(void) -{ - __asm__ __volatile__ ( - "mov r4, lr \n\t" - "bl kprobes_test_case_end \n\t" - "cmp r0, #0 \n\t" - "bxne r0 \n\t" - "mov r0, r4 \n\t" - "add sp, sp, #"__stringify(TEST_MEMORY_SIZE)"\n\t" - "ldmia sp!, {r4-r11} \n\t" - "bx r0 \n\t" - ); -} - -void __naked __kprobes_test_case_end_32(void) -{ - __asm__ __volatile__ ( - ".arm \n\t" - "orr lr, lr, #1 @ will return to Thumb code \n\t" - "ldr pc, 1f \n\t" - "1: \n\t" - ".word __kprobes_test_case_end_16 \n\t" - ); -} - -#endif - - -int kprobe_test_flags; -int kprobe_test_cc_position; - -static int test_try_count; -static int test_pass_count; -static int test_fail_count; - -static struct pt_regs initial_regs; -static struct pt_regs expected_regs; -static struct pt_regs result_regs; - -static u32 expected_memory[TEST_MEMORY_SIZE/sizeof(u32)]; - -static const char *current_title; -static struct test_arg *current_args; -static u32 *current_stack; -static uintptr_t current_branch_target; - -static uintptr_t current_code_start; -static kprobe_opcode_t current_instruction; - - -#define TEST_CASE_PASSED -1 -#define TEST_CASE_FAILED -2 - -static int test_case_run_count; -static bool test_case_is_thumb; -static int test_instance; - -/* - * We ignore the state of the imprecise abort disable flag (CPSR.A) because this - * can change randomly as the kernel doesn't take care to preserve or initialise - * this across context switches. Also, with Security Extentions, the flag may - * not be under control of the kernel; for this reason we ignore the state of - * the FIQ disable flag CPSR.F as well. - */ -#define PSR_IGNORE_BITS (PSR_A_BIT | PSR_F_BIT) - -static unsigned long test_check_cc(int cc, unsigned long cpsr) -{ - int ret = arm_check_condition(cc << 28, cpsr); - - return (ret != ARM_OPCODE_CONDTEST_FAIL); -} - -static int is_last_scenario; -static int probe_should_run; /* 0 = no, 1 = yes, -1 = unknown */ -static int memory_needs_checking; - -static unsigned long test_context_cpsr(int scenario) -{ - unsigned long cpsr; - - probe_should_run = 1; - - /* Default case is that we cycle through 16 combinations of flags */ - cpsr = (scenario & 0xf) << 28; /* N,Z,C,V flags */ - cpsr |= (scenario & 0xf) << 16; /* GE flags */ - cpsr |= (scenario & 0x1) << 27; /* Toggle Q flag */ - - if (!test_case_is_thumb) { - /* Testing ARM code */ - int cc = current_instruction >> 28; - - probe_should_run = test_check_cc(cc, cpsr) != 0; - if (scenario == 15) - is_last_scenario = true; - - } else if (kprobe_test_flags & TEST_FLAG_NO_ITBLOCK) { - /* Testing Thumb code without setting ITSTATE */ - if (kprobe_test_cc_position) { - int cc = (current_instruction >> kprobe_test_cc_position) & 0xf; - probe_should_run = test_check_cc(cc, cpsr) != 0; - } - - if (scenario == 15) - is_last_scenario = true; - - } else if (kprobe_test_flags & TEST_FLAG_FULL_ITBLOCK) { - /* Testing Thumb code with all combinations of ITSTATE */ - unsigned x = (scenario >> 4); - unsigned cond_base = x % 7; /* ITSTATE<7:5> */ - unsigned mask = x / 7 + 2; /* ITSTATE<4:0>, bits reversed */ - - if (mask > 0x1f) { - /* Finish by testing state from instruction 'itt al' */ - cond_base = 7; - mask = 0x4; - if ((scenario & 0xf) == 0xf) - is_last_scenario = true; - } - - cpsr |= cond_base << 13; /* ITSTATE<7:5> */ - cpsr |= (mask & 0x1) << 12; /* ITSTATE<4> */ - cpsr |= (mask & 0x2) << 10; /* ITSTATE<3> */ - cpsr |= (mask & 0x4) << 8; /* ITSTATE<2> */ - cpsr |= (mask & 0x8) << 23; /* ITSTATE<1> */ - cpsr |= (mask & 0x10) << 21; /* ITSTATE<0> */ - - probe_should_run = test_check_cc((cpsr >> 12) & 0xf, cpsr) != 0; - - } else { - /* Testing Thumb code with several combinations of ITSTATE */ - switch (scenario) { - case 16: /* Clear NZCV flags and 'it eq' state (false as Z=0) */ - cpsr = 0x00000800; - probe_should_run = 0; - break; - case 17: /* Set NZCV flags and 'it vc' state (false as V=1) */ - cpsr = 0xf0007800; - probe_should_run = 0; - break; - case 18: /* Clear NZCV flags and 'it ls' state (true as C=0) */ - cpsr = 0x00009800; - break; - case 19: /* Set NZCV flags and 'it cs' state (true as C=1) */ - cpsr = 0xf0002800; - is_last_scenario = true; - break; - } - } - - return cpsr; -} - -static void setup_test_context(struct pt_regs *regs) -{ - int scenario = test_case_run_count>>1; - unsigned long val; - struct test_arg *args; - int i; - - is_last_scenario = false; - memory_needs_checking = false; - - /* Initialise test memory on stack */ - val = (scenario & 1) ? VALM : ~VALM; - for (i = 0; i < TEST_MEMORY_SIZE / sizeof(current_stack[0]); ++i) - current_stack[i] = val + (i << 8); - /* Put target of branch on stack for tests which load PC from memory */ - if (current_branch_target) - current_stack[15] = current_branch_target; - /* Put a value for SP on stack for tests which load SP from memory */ - current_stack[13] = (u32)current_stack + 120; - - /* Initialise register values to their default state */ - val = (scenario & 2) ? VALR : ~VALR; - for (i = 0; i < 13; ++i) - regs->uregs[i] = val ^ (i << 8); - regs->ARM_lr = val ^ (14 << 8); - regs->ARM_cpsr &= ~(APSR_MASK | PSR_IT_MASK); - regs->ARM_cpsr |= test_context_cpsr(scenario); - - /* Perform testcase specific register setup */ - args = current_args; - for (; args[0].type != ARG_TYPE_END; ++args) - switch (args[0].type) { - case ARG_TYPE_REG: { - struct test_arg_regptr *arg = - (struct test_arg_regptr *)args; - regs->uregs[arg->reg] = arg->val; - break; - } - case ARG_TYPE_PTR: { - struct test_arg_regptr *arg = - (struct test_arg_regptr *)args; - regs->uregs[arg->reg] = - (unsigned long)current_stack + arg->val; - memory_needs_checking = true; - break; - } - case ARG_TYPE_MEM: { - struct test_arg_mem *arg = (struct test_arg_mem *)args; - current_stack[arg->index] = arg->val; - break; - } - default: - break; - } -} - -struct test_probe { - struct kprobe kprobe; - bool registered; - int hit; -}; - -static void unregister_test_probe(struct test_probe *probe) -{ - if (probe->registered) { - unregister_kprobe(&probe->kprobe); - probe->kprobe.flags = 0; /* Clear disable flag to allow reuse */ - } - probe->registered = false; -} - -static int register_test_probe(struct test_probe *probe) -{ - int ret; - - if (probe->registered) - BUG(); - - ret = register_kprobe(&probe->kprobe); - if (ret >= 0) { - probe->registered = true; - probe->hit = -1; - } - return ret; -} - -static int __kprobes -test_before_pre_handler(struct kprobe *p, struct pt_regs *regs) -{ - container_of(p, struct test_probe, kprobe)->hit = test_instance; - return 0; -} - -static void __kprobes -test_before_post_handler(struct kprobe *p, struct pt_regs *regs, - unsigned long flags) -{ - setup_test_context(regs); - initial_regs = *regs; - initial_regs.ARM_cpsr &= ~PSR_IGNORE_BITS; -} - -static int __kprobes -test_case_pre_handler(struct kprobe *p, struct pt_regs *regs) -{ - container_of(p, struct test_probe, kprobe)->hit = test_instance; - return 0; -} - -static int __kprobes -test_after_pre_handler(struct kprobe *p, struct pt_regs *regs) -{ - if (container_of(p, struct test_probe, kprobe)->hit == test_instance) - return 0; /* Already run for this test instance */ - - result_regs = *regs; - result_regs.ARM_cpsr &= ~PSR_IGNORE_BITS; - - /* Undo any changes done to SP by the test case */ - regs->ARM_sp = (unsigned long)current_stack; - - container_of(p, struct test_probe, kprobe)->hit = test_instance; - return 0; -} - -static struct test_probe test_before_probe = { - .kprobe.pre_handler = test_before_pre_handler, - .kprobe.post_handler = test_before_post_handler, -}; - -static struct test_probe test_case_probe = { - .kprobe.pre_handler = test_case_pre_handler, -}; - -static struct test_probe test_after_probe = { - .kprobe.pre_handler = test_after_pre_handler, -}; - -static struct test_probe test_after2_probe = { - .kprobe.pre_handler = test_after_pre_handler, -}; - -static void test_case_cleanup(void) -{ - unregister_test_probe(&test_before_probe); - unregister_test_probe(&test_case_probe); - unregister_test_probe(&test_after_probe); - unregister_test_probe(&test_after2_probe); -} - -static void print_registers(struct pt_regs *regs) -{ - pr_err("r0 %08lx | r1 %08lx | r2 %08lx | r3 %08lx\n", - regs->ARM_r0, regs->ARM_r1, regs->ARM_r2, regs->ARM_r3); - pr_err("r4 %08lx | r5 %08lx | r6 %08lx | r7 %08lx\n", - regs->ARM_r4, regs->ARM_r5, regs->ARM_r6, regs->ARM_r7); - pr_err("r8 %08lx | r9 %08lx | r10 %08lx | r11 %08lx\n", - regs->ARM_r8, regs->ARM_r9, regs->ARM_r10, regs->ARM_fp); - pr_err("r12 %08lx | sp %08lx | lr %08lx | pc %08lx\n", - regs->ARM_ip, regs->ARM_sp, regs->ARM_lr, regs->ARM_pc); - pr_err("cpsr %08lx\n", regs->ARM_cpsr); -} - -static void print_memory(u32 *mem, size_t size) -{ - int i; - for (i = 0; i < size / sizeof(u32); i += 4) - pr_err("%08x %08x %08x %08x\n", mem[i], mem[i+1], - mem[i+2], mem[i+3]); -} - -static size_t expected_memory_size(u32 *sp) -{ - size_t size = sizeof(expected_memory); - int offset = (uintptr_t)sp - (uintptr_t)current_stack; - if (offset > 0) - size -= offset; - return size; -} - -static void test_case_failed(const char *message) -{ - test_case_cleanup(); - - pr_err("FAIL: %s\n", message); - pr_err("FAIL: Test %s\n", current_title); - pr_err("FAIL: Scenario %d\n", test_case_run_count >> 1); -} - -static unsigned long next_instruction(unsigned long pc) -{ -#ifdef CONFIG_THUMB2_KERNEL - if ((pc & 1) && - !is_wide_instruction(__mem_to_opcode_thumb16(*(u16 *)(pc - 1)))) - return pc + 2; - else -#endif - return pc + 4; -} - -static uintptr_t __used kprobes_test_case_start(const char **title, void *stack) -{ - struct test_arg *args; - struct test_arg_end *end_arg; - unsigned long test_code; - - current_title = *title++; - args = (struct test_arg *)title; - current_args = args; - current_stack = stack; - - ++test_try_count; - - while (args->type != ARG_TYPE_END) - ++args; - end_arg = (struct test_arg_end *)args; - - test_code = (unsigned long)(args + 1); /* Code starts after args */ - - test_case_is_thumb = end_arg->flags & ARG_FLAG_THUMB; - if (test_case_is_thumb) - test_code |= 1; - - current_code_start = test_code; - - current_branch_target = 0; - if (end_arg->branch_offset != end_arg->end_offset) - current_branch_target = test_code + end_arg->branch_offset; - - test_code += end_arg->code_offset; - test_before_probe.kprobe.addr = (kprobe_opcode_t *)test_code; - - test_code = next_instruction(test_code); - test_case_probe.kprobe.addr = (kprobe_opcode_t *)test_code; - - if (test_case_is_thumb) { - u16 *p = (u16 *)(test_code & ~1); - current_instruction = __mem_to_opcode_thumb16(p[0]); - if (is_wide_instruction(current_instruction)) { - u16 instr2 = __mem_to_opcode_thumb16(p[1]); - current_instruction = __opcode_thumb32_compose(current_instruction, instr2); - } - } else { - current_instruction = __mem_to_opcode_arm(*(u32 *)test_code); - } - - if (current_title[0] == '.') - verbose("%s\n", current_title); - else - verbose("%s\t@ %0*x\n", current_title, - test_case_is_thumb ? 4 : 8, - current_instruction); - - test_code = next_instruction(test_code); - test_after_probe.kprobe.addr = (kprobe_opcode_t *)test_code; - - if (kprobe_test_flags & TEST_FLAG_NARROW_INSTR) { - if (!test_case_is_thumb || - is_wide_instruction(current_instruction)) { - test_case_failed("expected 16-bit instruction"); - goto fail; - } - } else { - if (test_case_is_thumb && - !is_wide_instruction(current_instruction)) { - test_case_failed("expected 32-bit instruction"); - goto fail; - } - } - - coverage_add(current_instruction); - - if (end_arg->flags & ARG_FLAG_UNSUPPORTED) { - if (register_test_probe(&test_case_probe) < 0) - goto pass; - test_case_failed("registered probe for unsupported instruction"); - goto fail; - } - - if (end_arg->flags & ARG_FLAG_SUPPORTED) { - if (register_test_probe(&test_case_probe) >= 0) - goto pass; - test_case_failed("couldn't register probe for supported instruction"); - goto fail; - } - - if (register_test_probe(&test_before_probe) < 0) { - test_case_failed("register test_before_probe failed"); - goto fail; - } - if (register_test_probe(&test_after_probe) < 0) { - test_case_failed("register test_after_probe failed"); - goto fail; - } - if (current_branch_target) { - test_after2_probe.kprobe.addr = - (kprobe_opcode_t *)current_branch_target; - if (register_test_probe(&test_after2_probe) < 0) { - test_case_failed("register test_after2_probe failed"); - goto fail; - } - } - - /* Start first run of test case */ - test_case_run_count = 0; - ++test_instance; - return current_code_start; -pass: - test_case_run_count = TEST_CASE_PASSED; - return (uintptr_t)test_after_probe.kprobe.addr; -fail: - test_case_run_count = TEST_CASE_FAILED; - return (uintptr_t)test_after_probe.kprobe.addr; -} - -static bool check_test_results(void) -{ - size_t mem_size = 0; - u32 *mem = 0; - - if (memcmp(&expected_regs, &result_regs, sizeof(expected_regs))) { - test_case_failed("registers differ"); - goto fail; - } - - if (memory_needs_checking) { - mem = (u32 *)result_regs.ARM_sp; - mem_size = expected_memory_size(mem); - if (memcmp(expected_memory, mem, mem_size)) { - test_case_failed("test memory differs"); - goto fail; - } - } - - return true; - -fail: - pr_err("initial_regs:\n"); - print_registers(&initial_regs); - pr_err("expected_regs:\n"); - print_registers(&expected_regs); - pr_err("result_regs:\n"); - print_registers(&result_regs); - - if (mem) { - pr_err("current_stack=%p\n", current_stack); - pr_err("expected_memory:\n"); - print_memory(expected_memory, mem_size); - pr_err("result_memory:\n"); - print_memory(mem, mem_size); - } - - return false; -} - -static uintptr_t __used kprobes_test_case_end(void) -{ - if (test_case_run_count < 0) { - if (test_case_run_count == TEST_CASE_PASSED) - /* kprobes_test_case_start did all the needed testing */ - goto pass; - else - /* kprobes_test_case_start failed */ - goto fail; - } - - if (test_before_probe.hit != test_instance) { - test_case_failed("test_before_handler not run"); - goto fail; - } - - if (test_after_probe.hit != test_instance && - test_after2_probe.hit != test_instance) { - test_case_failed("test_after_handler not run"); - goto fail; - } - - /* - * Even numbered test runs ran without a probe on the test case so - * we can gather reference results. The subsequent odd numbered run - * will have the probe inserted. - */ - if ((test_case_run_count & 1) == 0) { - /* Save results from run without probe */ - u32 *mem = (u32 *)result_regs.ARM_sp; - expected_regs = result_regs; - memcpy(expected_memory, mem, expected_memory_size(mem)); - - /* Insert probe onto test case instruction */ - if (register_test_probe(&test_case_probe) < 0) { - test_case_failed("register test_case_probe failed"); - goto fail; - } - } else { - /* Check probe ran as expected */ - if (probe_should_run == 1) { - if (test_case_probe.hit != test_instance) { - test_case_failed("test_case_handler not run"); - goto fail; - } - } else if (probe_should_run == 0) { - if (test_case_probe.hit == test_instance) { - test_case_failed("test_case_handler ran"); - goto fail; - } - } - - /* Remove probe for any subsequent reference run */ - unregister_test_probe(&test_case_probe); - - if (!check_test_results()) - goto fail; - - if (is_last_scenario) - goto pass; - } - - /* Do next test run */ - ++test_case_run_count; - ++test_instance; - return current_code_start; -fail: - ++test_fail_count; - goto end; -pass: - ++test_pass_count; -end: - test_case_cleanup(); - return 0; -} - - -/* - * Top level test functions - */ - -static int run_test_cases(void (*tests)(void), const union decode_item *table) -{ - int ret; - - pr_info(" Check decoding tables\n"); - ret = table_test(table); - if (ret) - return ret; - - pr_info(" Run test cases\n"); - ret = coverage_start(table); - if (ret) - return ret; - - tests(); - - coverage_end(); - return 0; -} - - -static int __init run_all_tests(void) -{ - int ret = 0; - - pr_info("Beginning kprobe tests...\n"); - -#ifndef CONFIG_THUMB2_KERNEL - - pr_info("Probe ARM code\n"); - ret = run_api_tests(arm_func); - if (ret) - goto out; - - pr_info("ARM instruction simulation\n"); - ret = run_test_cases(kprobe_arm_test_cases, probes_decode_arm_table); - if (ret) - goto out; - -#else /* CONFIG_THUMB2_KERNEL */ - - pr_info("Probe 16-bit Thumb code\n"); - ret = run_api_tests(thumb16_func); - if (ret) - goto out; - - pr_info("Probe 32-bit Thumb code, even halfword\n"); - ret = run_api_tests(thumb32even_func); - if (ret) - goto out; - - pr_info("Probe 32-bit Thumb code, odd halfword\n"); - ret = run_api_tests(thumb32odd_func); - if (ret) - goto out; - - pr_info("16-bit Thumb instruction simulation\n"); - ret = run_test_cases(kprobe_thumb16_test_cases, - probes_decode_thumb16_table); - if (ret) - goto out; - - pr_info("32-bit Thumb instruction simulation\n"); - ret = run_test_cases(kprobe_thumb32_test_cases, - probes_decode_thumb32_table); - if (ret) - goto out; -#endif - - pr_info("Total instruction simulation tests=%d, pass=%d fail=%d\n", - test_try_count, test_pass_count, test_fail_count); - if (test_fail_count) { - ret = -EINVAL; - goto out; - } - -#if BENCHMARKING - pr_info("Benchmarks\n"); - ret = run_benchmarks(); - if (ret) - goto out; -#endif - -#if __LINUX_ARM_ARCH__ >= 7 - /* We are able to run all test cases so coverage should be complete */ - if (coverage_fail) { - pr_err("FAIL: Test coverage checks failed\n"); - ret = -EINVAL; - goto out; - } -#endif - -out: - if (ret == 0) - ret = tests_failed; - if (ret == 0) - pr_info("Finished kprobe tests OK\n"); - else - pr_err("kprobe tests failed\n"); - - return ret; -} - - -/* - * Module setup - */ - -#ifdef MODULE - -static void __exit kprobe_test_exit(void) -{ -} - -module_init(run_all_tests) -module_exit(kprobe_test_exit) -MODULE_LICENSE("GPL"); - -#else /* !MODULE */ - -late_initcall(run_all_tests); - -#endif diff --git a/arch/arm/kernel/kprobes-test.h b/arch/arm/kernel/kprobes-test.h deleted file mode 100644 index 4430990..0000000 --- a/arch/arm/kernel/kprobes-test.h +++ /dev/null @@ -1,435 +0,0 @@ -/* - * arch/arm/kernel/kprobes-test.h - * - * Copyright (C) 2011 Jon Medhurst . - * - * 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. - */ - -#define VERBOSE 0 /* Set to '1' for more logging of test cases */ - -#ifdef CONFIG_THUMB2_KERNEL -#define NORMAL_ISA "16" -#else -#define NORMAL_ISA "32" -#endif - - -/* Flags used in kprobe_test_flags */ -#define TEST_FLAG_NO_ITBLOCK (1<<0) -#define TEST_FLAG_FULL_ITBLOCK (1<<1) -#define TEST_FLAG_NARROW_INSTR (1<<2) - -extern int kprobe_test_flags; -extern int kprobe_test_cc_position; - - -#define TEST_MEMORY_SIZE 256 - - -/* - * Test case structures. - * - * The arguments given to test cases can be one of three types. - * - * ARG_TYPE_REG - * Load a register with the given value. - * - * ARG_TYPE_PTR - * Load a register with a pointer into the stack buffer (SP + given value). - * - * ARG_TYPE_MEM - * Store the given value into the stack buffer at [SP+index]. - * - */ - -#define ARG_TYPE_END 0 -#define ARG_TYPE_REG 1 -#define ARG_TYPE_PTR 2 -#define ARG_TYPE_MEM 3 - -#define ARG_FLAG_UNSUPPORTED 0x01 -#define ARG_FLAG_SUPPORTED 0x02 -#define ARG_FLAG_THUMB 0x10 /* Must be 16 so TEST_ISA can be used */ -#define ARG_FLAG_ARM 0x20 /* Must be 32 so TEST_ISA can be used */ - -struct test_arg { - u8 type; /* ARG_TYPE_x */ - u8 _padding[7]; -}; - -struct test_arg_regptr { - u8 type; /* ARG_TYPE_REG or ARG_TYPE_PTR */ - u8 reg; - u8 _padding[2]; - u32 val; -}; - -struct test_arg_mem { - u8 type; /* ARG_TYPE_MEM */ - u8 index; - u8 _padding[2]; - u32 val; -}; - -struct test_arg_end { - u8 type; /* ARG_TYPE_END */ - u8 flags; /* ARG_FLAG_x */ - u16 code_offset; - u16 branch_offset; - u16 end_offset; -}; - - -/* - * Building blocks for test cases. - * - * Each test case is wrapped between TESTCASE_START and TESTCASE_END. - * - * To specify arguments for a test case the TEST_ARG_{REG,PTR,MEM} macros are - * used followed by a terminating TEST_ARG_END. - * - * After this, the instruction to be tested is defined with TEST_INSTRUCTION. - * Or for branches, TEST_BRANCH_B and TEST_BRANCH_F (branch forwards/backwards). - * - * Some specific test cases may make use of other custom constructs. - */ - -#if VERBOSE -#define verbose(fmt, ...) pr_info(fmt, ##__VA_ARGS__) -#else -#define verbose(fmt, ...) -#endif - -#define TEST_GROUP(title) \ - verbose("\n"); \ - verbose(title"\n"); \ - verbose("---------------------------------------------------------\n"); - -#define TESTCASE_START(title) \ - __asm__ __volatile__ ( \ - "bl __kprobes_test_case_start \n\t" \ - ".pushsection .rodata \n\t" \ - "10: \n\t" \ - /* don't use .asciz here as 'title' may be */ \ - /* multiple strings to be concatenated. */ \ - ".ascii "#title" \n\t" \ - ".byte 0 \n\t" \ - ".popsection \n\t" \ - ".word 10b \n\t" - -#define TEST_ARG_REG(reg, val) \ - ".byte "__stringify(ARG_TYPE_REG)" \n\t" \ - ".byte "#reg" \n\t" \ - ".short 0 \n\t" \ - ".word "#val" \n\t" - -#define TEST_ARG_PTR(reg, val) \ - ".byte "__stringify(ARG_TYPE_PTR)" \n\t" \ - ".byte "#reg" \n\t" \ - ".short 0 \n\t" \ - ".word "#val" \n\t" - -#define TEST_ARG_MEM(index, val) \ - ".byte "__stringify(ARG_TYPE_MEM)" \n\t" \ - ".byte "#index" \n\t" \ - ".short 0 \n\t" \ - ".word "#val" \n\t" - -#define TEST_ARG_END(flags) \ - ".byte "__stringify(ARG_TYPE_END)" \n\t" \ - ".byte "TEST_ISA flags" \n\t" \ - ".short 50f-0f \n\t" \ - ".short 2f-0f \n\t" \ - ".short 99f-0f \n\t" \ - ".code "TEST_ISA" \n\t" \ - "0: \n\t" - -#define TEST_INSTRUCTION(instruction) \ - "50: nop \n\t" \ - "1: "instruction" \n\t" \ - " nop \n\t" - -#define TEST_BRANCH_F(instruction) \ - TEST_INSTRUCTION(instruction) \ - " b 99f \n\t" \ - "2: nop \n\t" - -#define TEST_BRANCH_B(instruction) \ - " b 50f \n\t" \ - " b 99f \n\t" \ - "2: nop \n\t" \ - " b 99f \n\t" \ - TEST_INSTRUCTION(instruction) - -#define TEST_BRANCH_FX(instruction, codex) \ - TEST_INSTRUCTION(instruction) \ - " b 99f \n\t" \ - codex" \n\t" \ - " b 99f \n\t" \ - "2: nop \n\t" - -#define TEST_BRANCH_BX(instruction, codex) \ - " b 50f \n\t" \ - " b 99f \n\t" \ - "2: nop \n\t" \ - " b 99f \n\t" \ - codex" \n\t" \ - TEST_INSTRUCTION(instruction) - -#define TESTCASE_END \ - "2: \n\t" \ - "99: \n\t" \ - " bl __kprobes_test_case_end_"TEST_ISA" \n\t" \ - ".code "NORMAL_ISA" \n\t" \ - : : \ - : "r0", "r1", "r2", "r3", "ip", "lr", "memory", "cc" \ - ); - - -/* - * Macros to define test cases. - * - * Those of the form TEST_{R,P,M}* can be used to define test cases - * which take combinations of the three basic types of arguments. E.g. - * - * TEST_R One register argument - * TEST_RR Two register arguments - * TEST_RPR A register, a pointer, then a register argument - * - * For testing instructions which may branch, there are macros TEST_BF_* - * and TEST_BB_* for branching forwards and backwards. - * - * TEST_SUPPORTED and TEST_UNSUPPORTED don't cause the code to be executed, - * the just verify that a kprobe is or is not allowed on the given instruction. - */ - -#define TEST(code) \ - TESTCASE_START(code) \ - TEST_ARG_END("") \ - TEST_INSTRUCTION(code) \ - TESTCASE_END - -#define TEST_UNSUPPORTED(code) \ - TESTCASE_START(code) \ - TEST_ARG_END("|"__stringify(ARG_FLAG_UNSUPPORTED)) \ - TEST_INSTRUCTION(code) \ - TESTCASE_END - -#define TEST_SUPPORTED(code) \ - TESTCASE_START(code) \ - TEST_ARG_END("|"__stringify(ARG_FLAG_SUPPORTED)) \ - TEST_INSTRUCTION(code) \ - TESTCASE_END - -#define TEST_R(code1, reg, val, code2) \ - TESTCASE_START(code1 #reg code2) \ - TEST_ARG_REG(reg, val) \ - TEST_ARG_END("") \ - TEST_INSTRUCTION(code1 #reg code2) \ - TESTCASE_END - -#define TEST_RR(code1, reg1, val1, code2, reg2, val2, code3) \ - TESTCASE_START(code1 #reg1 code2 #reg2 code3) \ - TEST_ARG_REG(reg1, val1) \ - TEST_ARG_REG(reg2, val2) \ - TEST_ARG_END("") \ - TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3) \ - TESTCASE_END - -#define TEST_RRR(code1, reg1, val1, code2, reg2, val2, code3, reg3, val3, code4)\ - TESTCASE_START(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ - TEST_ARG_REG(reg1, val1) \ - TEST_ARG_REG(reg2, val2) \ - TEST_ARG_REG(reg3, val3) \ - TEST_ARG_END("") \ - TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ - TESTCASE_END - -#define TEST_RRRR(code1, reg1, val1, code2, reg2, val2, code3, reg3, val3, code4, reg4, val4) \ - TESTCASE_START(code1 #reg1 code2 #reg2 code3 #reg3 code4 #reg4) \ - TEST_ARG_REG(reg1, val1) \ - TEST_ARG_REG(reg2, val2) \ - TEST_ARG_REG(reg3, val3) \ - TEST_ARG_REG(reg4, val4) \ - TEST_ARG_END("") \ - TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3 #reg3 code4 #reg4) \ - TESTCASE_END - -#define TEST_P(code1, reg1, val1, code2) \ - TESTCASE_START(code1 #reg1 code2) \ - TEST_ARG_PTR(reg1, val1) \ - TEST_ARG_END("") \ - TEST_INSTRUCTION(code1 #reg1 code2) \ - TESTCASE_END - -#define TEST_PR(code1, reg1, val1, code2, reg2, val2, code3) \ - TESTCASE_START(code1 #reg1 code2 #reg2 code3) \ - TEST_ARG_PTR(reg1, val1) \ - TEST_ARG_REG(reg2, val2) \ - TEST_ARG_END("") \ - TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3) \ - TESTCASE_END - -#define TEST_RP(code1, reg1, val1, code2, reg2, val2, code3) \ - TESTCASE_START(code1 #reg1 code2 #reg2 code3) \ - TEST_ARG_REG(reg1, val1) \ - TEST_ARG_PTR(reg2, val2) \ - TEST_ARG_END("") \ - TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3) \ - TESTCASE_END - -#define TEST_PRR(code1, reg1, val1, code2, reg2, val2, code3, reg3, val3, code4)\ - TESTCASE_START(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ - TEST_ARG_PTR(reg1, val1) \ - TEST_ARG_REG(reg2, val2) \ - TEST_ARG_REG(reg3, val3) \ - TEST_ARG_END("") \ - TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ - TESTCASE_END - -#define TEST_RPR(code1, reg1, val1, code2, reg2, val2, code3, reg3, val3, code4)\ - TESTCASE_START(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ - TEST_ARG_REG(reg1, val1) \ - TEST_ARG_PTR(reg2, val2) \ - TEST_ARG_REG(reg3, val3) \ - TEST_ARG_END("") \ - TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ - TESTCASE_END - -#define TEST_RRP(code1, reg1, val1, code2, reg2, val2, code3, reg3, val3, code4)\ - TESTCASE_START(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ - TEST_ARG_REG(reg1, val1) \ - TEST_ARG_REG(reg2, val2) \ - TEST_ARG_PTR(reg3, val3) \ - TEST_ARG_END("") \ - TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ - TESTCASE_END - -#define TEST_BF_P(code1, reg1, val1, code2) \ - TESTCASE_START(code1 #reg1 code2) \ - TEST_ARG_PTR(reg1, val1) \ - TEST_ARG_END("") \ - TEST_BRANCH_F(code1 #reg1 code2) \ - TESTCASE_END - -#define TEST_BF(code) \ - TESTCASE_START(code) \ - TEST_ARG_END("") \ - TEST_BRANCH_F(code) \ - TESTCASE_END - -#define TEST_BB(code) \ - TESTCASE_START(code) \ - TEST_ARG_END("") \ - TEST_BRANCH_B(code) \ - TESTCASE_END - -#define TEST_BF_R(code1, reg, val, code2) \ - TESTCASE_START(code1 #reg code2) \ - TEST_ARG_REG(reg, val) \ - TEST_ARG_END("") \ - TEST_BRANCH_F(code1 #reg code2) \ - TESTCASE_END - -#define TEST_BB_R(code1, reg, val, code2) \ - TESTCASE_START(code1 #reg code2) \ - TEST_ARG_REG(reg, val) \ - TEST_ARG_END("") \ - TEST_BRANCH_B(code1 #reg code2) \ - TESTCASE_END - -#define TEST_BF_RR(code1, reg1, val1, code2, reg2, val2, code3) \ - TESTCASE_START(code1 #reg1 code2 #reg2 code3) \ - TEST_ARG_REG(reg1, val1) \ - TEST_ARG_REG(reg2, val2) \ - TEST_ARG_END("") \ - TEST_BRANCH_F(code1 #reg1 code2 #reg2 code3) \ - TESTCASE_END - -#define TEST_BF_X(code, codex) \ - TESTCASE_START(code) \ - TEST_ARG_END("") \ - TEST_BRANCH_FX(code, codex) \ - TESTCASE_END - -#define TEST_BB_X(code, codex) \ - TESTCASE_START(code) \ - TEST_ARG_END("") \ - TEST_BRANCH_BX(code, codex) \ - TESTCASE_END - -#define TEST_BF_RX(code1, reg, val, code2, codex) \ - TESTCASE_START(code1 #reg code2) \ - TEST_ARG_REG(reg, val) \ - TEST_ARG_END("") \ - TEST_BRANCH_FX(code1 #reg code2, codex) \ - TESTCASE_END - -#define TEST_X(code, codex) \ - TESTCASE_START(code) \ - TEST_ARG_END("") \ - TEST_INSTRUCTION(code) \ - " b 99f \n\t" \ - " "codex" \n\t" \ - TESTCASE_END - -#define TEST_RX(code1, reg, val, code2, codex) \ - TESTCASE_START(code1 #reg code2) \ - TEST_ARG_REG(reg, val) \ - TEST_ARG_END("") \ - TEST_INSTRUCTION(code1 __stringify(reg) code2) \ - " b 99f \n\t" \ - " "codex" \n\t" \ - TESTCASE_END - -#define TEST_RRX(code1, reg1, val1, code2, reg2, val2, code3, codex) \ - TESTCASE_START(code1 #reg1 code2 #reg2 code3) \ - TEST_ARG_REG(reg1, val1) \ - TEST_ARG_REG(reg2, val2) \ - TEST_ARG_END("") \ - TEST_INSTRUCTION(code1 __stringify(reg1) code2 __stringify(reg2) code3) \ - " b 99f \n\t" \ - " "codex" \n\t" \ - TESTCASE_END - - -/* - * Macros for defining space directives spread over multiple lines. - * These are required so the compiler guesses better the length of inline asm - * code and will spill the literal pool early enough to avoid generating PC - * relative loads with out of range offsets. - */ -#define TWICE(x) x x -#define SPACE_0x8 TWICE(".space 4\n\t") -#define SPACE_0x10 TWICE(SPACE_0x8) -#define SPACE_0x20 TWICE(SPACE_0x10) -#define SPACE_0x40 TWICE(SPACE_0x20) -#define SPACE_0x80 TWICE(SPACE_0x40) -#define SPACE_0x100 TWICE(SPACE_0x80) -#define SPACE_0x200 TWICE(SPACE_0x100) -#define SPACE_0x400 TWICE(SPACE_0x200) -#define SPACE_0x800 TWICE(SPACE_0x400) -#define SPACE_0x1000 TWICE(SPACE_0x800) - - -/* Various values used in test cases... */ -#define N(val) (val ^ 0xffffffff) -#define VAL1 0x12345678 -#define VAL2 N(VAL1) -#define VAL3 0xa5f801 -#define VAL4 N(VAL3) -#define VALM 0x456789ab -#define VALR 0xdeaddead -#define HH1 0x0123fecb -#define HH2 0xa9874567 - - -#ifdef CONFIG_THUMB2_KERNEL -void kprobe_thumb16_test_cases(void); -void kprobe_thumb32_test_cases(void); -#else -void kprobe_arm_test_cases(void); -#endif diff --git a/arch/arm/kernel/kprobes-thumb.c b/arch/arm/kernel/kprobes-thumb.c deleted file mode 100644 index 9495d7f..0000000 --- a/arch/arm/kernel/kprobes-thumb.c +++ /dev/null @@ -1,666 +0,0 @@ -/* - * arch/arm/kernel/kprobes-thumb.c - * - * Copyright (C) 2011 Jon Medhurst . - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include - -#include "kprobes.h" -#include "probes-thumb.h" - -/* These emulation encodings are functionally equivalent... */ -#define t32_emulate_rd8rn16rm0ra12_noflags \ - t32_emulate_rdlo12rdhi8rn16rm0_noflags - -/* t32 thumb actions */ - -static void __kprobes -t32_simulate_table_branch(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long pc = regs->ARM_pc; - int rn = (insn >> 16) & 0xf; - int rm = insn & 0xf; - - unsigned long rnv = (rn == 15) ? pc : regs->uregs[rn]; - unsigned long rmv = regs->uregs[rm]; - unsigned int halfwords; - - if (insn & 0x10) /* TBH */ - halfwords = ((u16 *)rnv)[rmv]; - else /* TBB */ - halfwords = ((u8 *)rnv)[rmv]; - - regs->ARM_pc = pc + 2 * halfwords; -} - -static void __kprobes -t32_simulate_mrs(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - int rd = (insn >> 8) & 0xf; - unsigned long mask = 0xf8ff03df; /* Mask out execution state */ - regs->uregs[rd] = regs->ARM_cpsr & mask; -} - -static void __kprobes -t32_simulate_cond_branch(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long pc = regs->ARM_pc; - - long offset = insn & 0x7ff; /* imm11 */ - offset += (insn & 0x003f0000) >> 5; /* imm6 */ - offset += (insn & 0x00002000) << 4; /* J1 */ - offset += (insn & 0x00000800) << 7; /* J2 */ - offset -= (insn & 0x04000000) >> 7; /* Apply sign bit */ - - regs->ARM_pc = pc + (offset * 2); -} - -static enum probes_insn __kprobes -t32_decode_cond_branch(probes_opcode_t insn, struct arch_probes_insn *asi, - const struct decode_header *d) -{ - int cc = (insn >> 22) & 0xf; - asi->insn_check_cc = probes_condition_checks[cc]; - asi->insn_handler = t32_simulate_cond_branch; - return INSN_GOOD_NO_SLOT; -} - -static void __kprobes -t32_simulate_branch(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long pc = regs->ARM_pc; - - long offset = insn & 0x7ff; /* imm11 */ - offset += (insn & 0x03ff0000) >> 5; /* imm10 */ - offset += (insn & 0x00002000) << 9; /* J1 */ - offset += (insn & 0x00000800) << 10; /* J2 */ - if (insn & 0x04000000) - offset -= 0x00800000; /* Apply sign bit */ - else - offset ^= 0x00600000; /* Invert J1 and J2 */ - - if (insn & (1 << 14)) { - /* BL or BLX */ - regs->ARM_lr = regs->ARM_pc | 1; - if (!(insn & (1 << 12))) { - /* BLX so switch to ARM mode */ - regs->ARM_cpsr &= ~PSR_T_BIT; - pc &= ~3; - } - } - - regs->ARM_pc = pc + (offset * 2); -} - -static void __kprobes -t32_simulate_ldr_literal(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long addr = regs->ARM_pc & ~3; - int rt = (insn >> 12) & 0xf; - unsigned long rtv; - - long offset = insn & 0xfff; - if (insn & 0x00800000) - addr += offset; - else - addr -= offset; - - if (insn & 0x00400000) { - /* LDR */ - rtv = *(unsigned long *)addr; - if (rt == 15) { - bx_write_pc(rtv, regs); - return; - } - } else if (insn & 0x00200000) { - /* LDRH */ - if (insn & 0x01000000) - rtv = *(s16 *)addr; - else - rtv = *(u16 *)addr; - } else { - /* LDRB */ - if (insn & 0x01000000) - rtv = *(s8 *)addr; - else - rtv = *(u8 *)addr; - } - - regs->uregs[rt] = rtv; -} - -static enum probes_insn __kprobes -t32_decode_ldmstm(probes_opcode_t insn, struct arch_probes_insn *asi, - const struct decode_header *d) -{ - enum probes_insn ret = kprobe_decode_ldmstm(insn, asi, d); - - /* Fixup modified instruction to have halfwords in correct order...*/ - insn = __mem_to_opcode_arm(asi->insn[0]); - ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(insn >> 16); - ((u16 *)asi->insn)[1] = __opcode_to_mem_thumb16(insn & 0xffff); - - return ret; -} - -static void __kprobes -t32_emulate_ldrdstrd(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long pc = regs->ARM_pc & ~3; - int rt1 = (insn >> 12) & 0xf; - int rt2 = (insn >> 8) & 0xf; - int rn = (insn >> 16) & 0xf; - - register unsigned long rt1v asm("r0") = regs->uregs[rt1]; - register unsigned long rt2v asm("r1") = regs->uregs[rt2]; - register unsigned long rnv asm("r2") = (rn == 15) ? pc - : regs->uregs[rn]; - - __asm__ __volatile__ ( - "blx %[fn]" - : "=r" (rt1v), "=r" (rt2v), "=r" (rnv) - : "0" (rt1v), "1" (rt2v), "2" (rnv), [fn] "r" (asi->insn_fn) - : "lr", "memory", "cc" - ); - - if (rn != 15) - regs->uregs[rn] = rnv; /* Writeback base register */ - regs->uregs[rt1] = rt1v; - regs->uregs[rt2] = rt2v; -} - -static void __kprobes -t32_emulate_ldrstr(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - int rt = (insn >> 12) & 0xf; - int rn = (insn >> 16) & 0xf; - int rm = insn & 0xf; - - register unsigned long rtv asm("r0") = regs->uregs[rt]; - register unsigned long rnv asm("r2") = regs->uregs[rn]; - register unsigned long rmv asm("r3") = regs->uregs[rm]; - - __asm__ __volatile__ ( - "blx %[fn]" - : "=r" (rtv), "=r" (rnv) - : "0" (rtv), "1" (rnv), "r" (rmv), [fn] "r" (asi->insn_fn) - : "lr", "memory", "cc" - ); - - regs->uregs[rn] = rnv; /* Writeback base register */ - if (rt == 15) /* Can't be true for a STR as they aren't allowed */ - bx_write_pc(rtv, regs); - else - regs->uregs[rt] = rtv; -} - -static void __kprobes -t32_emulate_rd8rn16rm0_rwflags(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - int rd = (insn >> 8) & 0xf; - int rn = (insn >> 16) & 0xf; - int rm = insn & 0xf; - - register unsigned long rdv asm("r1") = regs->uregs[rd]; - register unsigned long rnv asm("r2") = regs->uregs[rn]; - register unsigned long rmv asm("r3") = regs->uregs[rm]; - unsigned long cpsr = regs->ARM_cpsr; - - __asm__ __volatile__ ( - "msr cpsr_fs, %[cpsr] \n\t" - "blx %[fn] \n\t" - "mrs %[cpsr], cpsr \n\t" - : "=r" (rdv), [cpsr] "=r" (cpsr) - : "0" (rdv), "r" (rnv), "r" (rmv), - "1" (cpsr), [fn] "r" (asi->insn_fn) - : "lr", "memory", "cc" - ); - - regs->uregs[rd] = rdv; - regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); -} - -static void __kprobes -t32_emulate_rd8pc16_noflags(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long pc = regs->ARM_pc; - int rd = (insn >> 8) & 0xf; - - register unsigned long rdv asm("r1") = regs->uregs[rd]; - register unsigned long rnv asm("r2") = pc & ~3; - - __asm__ __volatile__ ( - "blx %[fn]" - : "=r" (rdv) - : "0" (rdv), "r" (rnv), [fn] "r" (asi->insn_fn) - : "lr", "memory", "cc" - ); - - regs->uregs[rd] = rdv; -} - -static void __kprobes -t32_emulate_rd8rn16_noflags(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - int rd = (insn >> 8) & 0xf; - int rn = (insn >> 16) & 0xf; - - register unsigned long rdv asm("r1") = regs->uregs[rd]; - register unsigned long rnv asm("r2") = regs->uregs[rn]; - - __asm__ __volatile__ ( - "blx %[fn]" - : "=r" (rdv) - : "0" (rdv), "r" (rnv), [fn] "r" (asi->insn_fn) - : "lr", "memory", "cc" - ); - - regs->uregs[rd] = rdv; -} - -static void __kprobes -t32_emulate_rdlo12rdhi8rn16rm0_noflags(probes_opcode_t insn, - struct arch_probes_insn *asi, - struct pt_regs *regs) -{ - int rdlo = (insn >> 12) & 0xf; - int rdhi = (insn >> 8) & 0xf; - int rn = (insn >> 16) & 0xf; - int rm = insn & 0xf; - - register unsigned long rdlov asm("r0") = regs->uregs[rdlo]; - register unsigned long rdhiv asm("r1") = regs->uregs[rdhi]; - register unsigned long rnv asm("r2") = regs->uregs[rn]; - register unsigned long rmv asm("r3") = regs->uregs[rm]; - - __asm__ __volatile__ ( - "blx %[fn]" - : "=r" (rdlov), "=r" (rdhiv) - : "0" (rdlov), "1" (rdhiv), "r" (rnv), "r" (rmv), - [fn] "r" (asi->insn_fn) - : "lr", "memory", "cc" - ); - - regs->uregs[rdlo] = rdlov; - regs->uregs[rdhi] = rdhiv; -} -/* t16 thumb actions */ - -static void __kprobes -t16_simulate_bxblx(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long pc = regs->ARM_pc + 2; - int rm = (insn >> 3) & 0xf; - unsigned long rmv = (rm == 15) ? pc : regs->uregs[rm]; - - if (insn & (1 << 7)) /* BLX ? */ - regs->ARM_lr = regs->ARM_pc | 1; - - bx_write_pc(rmv, regs); -} - -static void __kprobes -t16_simulate_ldr_literal(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long *base = (unsigned long *)((regs->ARM_pc + 2) & ~3); - long index = insn & 0xff; - int rt = (insn >> 8) & 0x7; - regs->uregs[rt] = base[index]; -} - -static void __kprobes -t16_simulate_ldrstr_sp_relative(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long* base = (unsigned long *)regs->ARM_sp; - long index = insn & 0xff; - int rt = (insn >> 8) & 0x7; - if (insn & 0x800) /* LDR */ - regs->uregs[rt] = base[index]; - else /* STR */ - base[index] = regs->uregs[rt]; -} - -static void __kprobes -t16_simulate_reladr(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long base = (insn & 0x800) ? regs->ARM_sp - : ((regs->ARM_pc + 2) & ~3); - long offset = insn & 0xff; - int rt = (insn >> 8) & 0x7; - regs->uregs[rt] = base + offset * 4; -} - -static void __kprobes -t16_simulate_add_sp_imm(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - long imm = insn & 0x7f; - if (insn & 0x80) /* SUB */ - regs->ARM_sp -= imm * 4; - else /* ADD */ - regs->ARM_sp += imm * 4; -} - -static void __kprobes -t16_simulate_cbz(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - int rn = insn & 0x7; - probes_opcode_t nonzero = regs->uregs[rn] ? insn : ~insn; - if (nonzero & 0x800) { - long i = insn & 0x200; - long imm5 = insn & 0xf8; - unsigned long pc = regs->ARM_pc + 2; - regs->ARM_pc = pc + (i >> 3) + (imm5 >> 2); - } -} - -static void __kprobes -t16_simulate_it(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - /* - * The 8 IT state bits are split into two parts in CPSR: - * ITSTATE<1:0> are in CPSR<26:25> - * ITSTATE<7:2> are in CPSR<15:10> - * The new IT state is in the lower byte of insn. - */ - unsigned long cpsr = regs->ARM_cpsr; - cpsr &= ~PSR_IT_MASK; - cpsr |= (insn & 0xfc) << 8; - cpsr |= (insn & 0x03) << 25; - regs->ARM_cpsr = cpsr; -} - -static void __kprobes -t16_singlestep_it(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - regs->ARM_pc += 2; - t16_simulate_it(insn, asi, regs); -} - -static enum probes_insn __kprobes -t16_decode_it(probes_opcode_t insn, struct arch_probes_insn *asi, - const struct decode_header *d) -{ - asi->insn_singlestep = t16_singlestep_it; - return INSN_GOOD_NO_SLOT; -} - -static void __kprobes -t16_simulate_cond_branch(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long pc = regs->ARM_pc + 2; - long offset = insn & 0x7f; - offset -= insn & 0x80; /* Apply sign bit */ - regs->ARM_pc = pc + (offset * 2); -} - -static enum probes_insn __kprobes -t16_decode_cond_branch(probes_opcode_t insn, struct arch_probes_insn *asi, - const struct decode_header *d) -{ - int cc = (insn >> 8) & 0xf; - asi->insn_check_cc = probes_condition_checks[cc]; - asi->insn_handler = t16_simulate_cond_branch; - return INSN_GOOD_NO_SLOT; -} - -static void __kprobes -t16_simulate_branch(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long pc = regs->ARM_pc + 2; - long offset = insn & 0x3ff; - offset -= insn & 0x400; /* Apply sign bit */ - regs->ARM_pc = pc + (offset * 2); -} - -static unsigned long __kprobes -t16_emulate_loregs(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long oldcpsr = regs->ARM_cpsr; - unsigned long newcpsr; - - __asm__ __volatile__ ( - "msr cpsr_fs, %[oldcpsr] \n\t" - "ldmia %[regs], {r0-r7} \n\t" - "blx %[fn] \n\t" - "stmia %[regs], {r0-r7} \n\t" - "mrs %[newcpsr], cpsr \n\t" - : [newcpsr] "=r" (newcpsr) - : [oldcpsr] "r" (oldcpsr), [regs] "r" (regs), - [fn] "r" (asi->insn_fn) - : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", - "lr", "memory", "cc" - ); - - return (oldcpsr & ~APSR_MASK) | (newcpsr & APSR_MASK); -} - -static void __kprobes -t16_emulate_loregs_rwflags(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - regs->ARM_cpsr = t16_emulate_loregs(insn, asi, regs); -} - -static void __kprobes -t16_emulate_loregs_noitrwflags(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long cpsr = t16_emulate_loregs(insn, asi, regs); - if (!in_it_block(cpsr)) - regs->ARM_cpsr = cpsr; -} - -static void __kprobes -t16_emulate_hiregs(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - unsigned long pc = regs->ARM_pc + 2; - int rdn = (insn & 0x7) | ((insn & 0x80) >> 4); - int rm = (insn >> 3) & 0xf; - - register unsigned long rdnv asm("r1"); - register unsigned long rmv asm("r0"); - unsigned long cpsr = regs->ARM_cpsr; - - rdnv = (rdn == 15) ? pc : regs->uregs[rdn]; - rmv = (rm == 15) ? pc : regs->uregs[rm]; - - __asm__ __volatile__ ( - "msr cpsr_fs, %[cpsr] \n\t" - "blx %[fn] \n\t" - "mrs %[cpsr], cpsr \n\t" - : "=r" (rdnv), [cpsr] "=r" (cpsr) - : "0" (rdnv), "r" (rmv), "1" (cpsr), [fn] "r" (asi->insn_fn) - : "lr", "memory", "cc" - ); - - if (rdn == 15) - rdnv &= ~1; - - regs->uregs[rdn] = rdnv; - regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); -} - -static enum probes_insn __kprobes -t16_decode_hiregs(probes_opcode_t insn, struct arch_probes_insn *asi, - const struct decode_header *d) -{ - insn &= ~0x00ff; - insn |= 0x001; /* Set Rdn = R1 and Rm = R0 */ - ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(insn); - asi->insn_handler = t16_emulate_hiregs; - return INSN_GOOD; -} - -static void __kprobes -t16_emulate_push(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - __asm__ __volatile__ ( - "ldr r9, [%[regs], #13*4] \n\t" - "ldr r8, [%[regs], #14*4] \n\t" - "ldmia %[regs], {r0-r7} \n\t" - "blx %[fn] \n\t" - "str r9, [%[regs], #13*4] \n\t" - : - : [regs] "r" (regs), [fn] "r" (asi->insn_fn) - : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", - "lr", "memory", "cc" - ); -} - -static enum probes_insn __kprobes -t16_decode_push(probes_opcode_t insn, struct arch_probes_insn *asi, - const struct decode_header *d) -{ - /* - * To simulate a PUSH we use a Thumb-2 "STMDB R9!, {registers}" - * and call it with R9=SP and LR in the register list represented - * by R8. - */ - /* 1st half STMDB R9!,{} */ - ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(0xe929); - /* 2nd half (register list) */ - ((u16 *)asi->insn)[1] = __opcode_to_mem_thumb16(insn & 0x1ff); - asi->insn_handler = t16_emulate_push; - return INSN_GOOD; -} - -static void __kprobes -t16_emulate_pop_nopc(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - __asm__ __volatile__ ( - "ldr r9, [%[regs], #13*4] \n\t" - "ldmia %[regs], {r0-r7} \n\t" - "blx %[fn] \n\t" - "stmia %[regs], {r0-r7} \n\t" - "str r9, [%[regs], #13*4] \n\t" - : - : [regs] "r" (regs), [fn] "r" (asi->insn_fn) - : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r9", - "lr", "memory", "cc" - ); -} - -static void __kprobes -t16_emulate_pop_pc(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - register unsigned long pc asm("r8"); - - __asm__ __volatile__ ( - "ldr r9, [%[regs], #13*4] \n\t" - "ldmia %[regs], {r0-r7} \n\t" - "blx %[fn] \n\t" - "stmia %[regs], {r0-r7} \n\t" - "str r9, [%[regs], #13*4] \n\t" - : "=r" (pc) - : [regs] "r" (regs), [fn] "r" (asi->insn_fn) - : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r9", - "lr", "memory", "cc" - ); - - bx_write_pc(pc, regs); -} - -static enum probes_insn __kprobes -t16_decode_pop(probes_opcode_t insn, struct arch_probes_insn *asi, - const struct decode_header *d) -{ - /* - * To simulate a POP we use a Thumb-2 "LDMDB R9!, {registers}" - * and call it with R9=SP and PC in the register list represented - * by R8. - */ - /* 1st half LDMIA R9!,{} */ - ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(0xe8b9); - /* 2nd half (register list) */ - ((u16 *)asi->insn)[1] = __opcode_to_mem_thumb16(insn & 0x1ff); - asi->insn_handler = insn & 0x100 ? t16_emulate_pop_pc - : t16_emulate_pop_nopc; - return INSN_GOOD; -} - -const union decode_action kprobes_t16_actions[NUM_PROBES_T16_ACTIONS] = { - [PROBES_T16_ADD_SP] = {.handler = t16_simulate_add_sp_imm}, - [PROBES_T16_CBZ] = {.handler = t16_simulate_cbz}, - [PROBES_T16_SIGN_EXTEND] = {.handler = t16_emulate_loregs_rwflags}, - [PROBES_T16_PUSH] = {.decoder = t16_decode_push}, - [PROBES_T16_POP] = {.decoder = t16_decode_pop}, - [PROBES_T16_SEV] = {.handler = probes_emulate_none}, - [PROBES_T16_WFE] = {.handler = probes_simulate_nop}, - [PROBES_T16_IT] = {.decoder = t16_decode_it}, - [PROBES_T16_CMP] = {.handler = t16_emulate_loregs_rwflags}, - [PROBES_T16_ADDSUB] = {.handler = t16_emulate_loregs_noitrwflags}, - [PROBES_T16_LOGICAL] = {.handler = t16_emulate_loregs_noitrwflags}, - [PROBES_T16_LDR_LIT] = {.handler = t16_simulate_ldr_literal}, - [PROBES_T16_BLX] = {.handler = t16_simulate_bxblx}, - [PROBES_T16_HIREGOPS] = {.decoder = t16_decode_hiregs}, - [PROBES_T16_LDRHSTRH] = {.handler = t16_emulate_loregs_rwflags}, - [PROBES_T16_LDRSTR] = {.handler = t16_simulate_ldrstr_sp_relative}, - [PROBES_T16_ADR] = {.handler = t16_simulate_reladr}, - [PROBES_T16_LDMSTM] = {.handler = t16_emulate_loregs_rwflags}, - [PROBES_T16_BRANCH_COND] = {.decoder = t16_decode_cond_branch}, - [PROBES_T16_BRANCH] = {.handler = t16_simulate_branch}, -}; - -const union decode_action kprobes_t32_actions[NUM_PROBES_T32_ACTIONS] = { - [PROBES_T32_LDMSTM] = {.decoder = t32_decode_ldmstm}, - [PROBES_T32_LDRDSTRD] = {.handler = t32_emulate_ldrdstrd}, - [PROBES_T32_TABLE_BRANCH] = {.handler = t32_simulate_table_branch}, - [PROBES_T32_TST] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, - [PROBES_T32_MOV] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, - [PROBES_T32_ADDSUB] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, - [PROBES_T32_LOGICAL] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, - [PROBES_T32_CMP] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, - [PROBES_T32_ADDWSUBW_PC] = {.handler = t32_emulate_rd8pc16_noflags,}, - [PROBES_T32_ADDWSUBW] = {.handler = t32_emulate_rd8rn16_noflags}, - [PROBES_T32_MOVW] = {.handler = t32_emulate_rd8rn16_noflags}, - [PROBES_T32_SAT] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, - [PROBES_T32_BITFIELD] = {.handler = t32_emulate_rd8rn16_noflags}, - [PROBES_T32_SEV] = {.handler = probes_emulate_none}, - [PROBES_T32_WFE] = {.handler = probes_simulate_nop}, - [PROBES_T32_MRS] = {.handler = t32_simulate_mrs}, - [PROBES_T32_BRANCH_COND] = {.decoder = t32_decode_cond_branch}, - [PROBES_T32_BRANCH] = {.handler = t32_simulate_branch}, - [PROBES_T32_PLDI] = {.handler = probes_simulate_nop}, - [PROBES_T32_LDR_LIT] = {.handler = t32_simulate_ldr_literal}, - [PROBES_T32_LDRSTR] = {.handler = t32_emulate_ldrstr}, - [PROBES_T32_SIGN_EXTEND] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, - [PROBES_T32_MEDIA] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, - [PROBES_T32_REVERSE] = {.handler = t32_emulate_rd8rn16_noflags}, - [PROBES_T32_MUL_ADD] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, - [PROBES_T32_MUL_ADD2] = {.handler = t32_emulate_rd8rn16rm0ra12_noflags}, - [PROBES_T32_MUL_ADD_LONG] = { - .handler = t32_emulate_rdlo12rdhi8rn16rm0_noflags}, -}; diff --git a/arch/arm/kernel/kprobes.c b/arch/arm/kernel/kprobes.c deleted file mode 100644 index 6d64420..0000000 --- a/arch/arm/kernel/kprobes.c +++ /dev/null @@ -1,628 +0,0 @@ -/* - * arch/arm/kernel/kprobes.c - * - * Kprobes on ARM - * - * Abhishek Sagar - * Copyright (C) 2006, 2007 Motorola Inc. - * - * Nicolas Pitre - * Copyright (C) 2007 Marvell Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "kprobes.h" -#include "probes-arm.h" -#include "probes-thumb.h" -#include "patch.h" - -#define MIN_STACK_SIZE(addr) \ - min((unsigned long)MAX_STACK_SIZE, \ - (unsigned long)current_thread_info() + THREAD_START_SP - (addr)) - -#define flush_insns(addr, size) \ - flush_icache_range((unsigned long)(addr), \ - (unsigned long)(addr) + \ - (size)) - -/* Used as a marker in ARM_pc to note when we're in a jprobe. */ -#define JPROBE_MAGIC_ADDR 0xffffffff - -DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; -DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); - - -int __kprobes arch_prepare_kprobe(struct kprobe *p) -{ - kprobe_opcode_t insn; - kprobe_opcode_t tmp_insn[MAX_INSN_SIZE]; - unsigned long addr = (unsigned long)p->addr; - bool thumb; - kprobe_decode_insn_t *decode_insn; - const union decode_action *actions; - int is; - - if (in_exception_text(addr)) - return -EINVAL; - -#ifdef CONFIG_THUMB2_KERNEL - thumb = true; - addr &= ~1; /* Bit 0 would normally be set to indicate Thumb code */ - insn = __mem_to_opcode_thumb16(((u16 *)addr)[0]); - if (is_wide_instruction(insn)) { - u16 inst2 = __mem_to_opcode_thumb16(((u16 *)addr)[1]); - insn = __opcode_thumb32_compose(insn, inst2); - decode_insn = thumb32_probes_decode_insn; - actions = kprobes_t32_actions; - } else { - decode_insn = thumb16_probes_decode_insn; - actions = kprobes_t16_actions; - } -#else /* !CONFIG_THUMB2_KERNEL */ - thumb = false; - if (addr & 0x3) - return -EINVAL; - insn = __mem_to_opcode_arm(*p->addr); - decode_insn = arm_probes_decode_insn; - actions = kprobes_arm_actions; -#endif - - p->opcode = insn; - p->ainsn.insn = tmp_insn; - - switch ((*decode_insn)(insn, &p->ainsn, true, actions)) { - case INSN_REJECTED: /* not supported */ - return -EINVAL; - - case INSN_GOOD: /* instruction uses slot */ - p->ainsn.insn = get_insn_slot(); - if (!p->ainsn.insn) - return -ENOMEM; - for (is = 0; is < MAX_INSN_SIZE; ++is) - p->ainsn.insn[is] = tmp_insn[is]; - flush_insns(p->ainsn.insn, - sizeof(p->ainsn.insn[0]) * MAX_INSN_SIZE); - p->ainsn.insn_fn = (probes_insn_fn_t *) - ((uintptr_t)p->ainsn.insn | thumb); - break; - - case INSN_GOOD_NO_SLOT: /* instruction doesn't need insn slot */ - p->ainsn.insn = NULL; - break; - } - - return 0; -} - -void __kprobes arch_arm_kprobe(struct kprobe *p) -{ - unsigned int brkp; - void *addr; - - if (IS_ENABLED(CONFIG_THUMB2_KERNEL)) { - /* Remove any Thumb flag */ - addr = (void *)((uintptr_t)p->addr & ~1); - - if (is_wide_instruction(p->opcode)) - brkp = KPROBE_THUMB32_BREAKPOINT_INSTRUCTION; - else - brkp = KPROBE_THUMB16_BREAKPOINT_INSTRUCTION; - } else { - kprobe_opcode_t insn = p->opcode; - - addr = p->addr; - brkp = KPROBE_ARM_BREAKPOINT_INSTRUCTION; - - if (insn >= 0xe0000000) - brkp |= 0xe0000000; /* Unconditional instruction */ - else - brkp |= insn & 0xf0000000; /* Copy condition from insn */ - } - - patch_text(addr, brkp); -} - -/* - * The actual disarming is done here on each CPU and synchronized using - * stop_machine. This synchronization is necessary on SMP to avoid removing - * a probe between the moment the 'Undefined Instruction' exception is raised - * and the moment the exception handler reads the faulting instruction from - * memory. It is also needed to atomically set the two half-words of a 32-bit - * Thumb breakpoint. - */ -int __kprobes __arch_disarm_kprobe(void *p) -{ - struct kprobe *kp = p; - void *addr = (void *)((uintptr_t)kp->addr & ~1); - - __patch_text(addr, kp->opcode); - - return 0; -} - -void __kprobes arch_disarm_kprobe(struct kprobe *p) -{ - stop_machine(__arch_disarm_kprobe, p, cpu_online_mask); -} - -void __kprobes arch_remove_kprobe(struct kprobe *p) -{ - if (p->ainsn.insn) { - free_insn_slot(p->ainsn.insn, 0); - p->ainsn.insn = NULL; - } -} - -static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb) -{ - kcb->prev_kprobe.kp = kprobe_running(); - kcb->prev_kprobe.status = kcb->kprobe_status; -} - -static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb) -{ - __this_cpu_write(current_kprobe, kcb->prev_kprobe.kp); - kcb->kprobe_status = kcb->prev_kprobe.status; -} - -static void __kprobes set_current_kprobe(struct kprobe *p) -{ - __this_cpu_write(current_kprobe, p); -} - -static void __kprobes -singlestep_skip(struct kprobe *p, struct pt_regs *regs) -{ -#ifdef CONFIG_THUMB2_KERNEL - regs->ARM_cpsr = it_advance(regs->ARM_cpsr); - if (is_wide_instruction(p->opcode)) - regs->ARM_pc += 4; - else - regs->ARM_pc += 2; -#else - regs->ARM_pc += 4; -#endif -} - -static inline void __kprobes -singlestep(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb) -{ - p->ainsn.insn_singlestep(p->opcode, &p->ainsn, regs); -} - -/* - * Called with IRQs disabled. IRQs must remain disabled from that point - * all the way until processing this kprobe is complete. The current - * kprobes implementation cannot process more than one nested level of - * kprobe, and that level is reserved for user kprobe handlers, so we can't - * risk encountering a new kprobe in an interrupt handler. - */ -void __kprobes kprobe_handler(struct pt_regs *regs) -{ - struct kprobe *p, *cur; - struct kprobe_ctlblk *kcb; - - kcb = get_kprobe_ctlblk(); - cur = kprobe_running(); - -#ifdef CONFIG_THUMB2_KERNEL - /* - * First look for a probe which was registered using an address with - * bit 0 set, this is the usual situation for pointers to Thumb code. - * If not found, fallback to looking for one with bit 0 clear. - */ - p = get_kprobe((kprobe_opcode_t *)(regs->ARM_pc | 1)); - if (!p) - p = get_kprobe((kprobe_opcode_t *)regs->ARM_pc); - -#else /* ! CONFIG_THUMB2_KERNEL */ - p = get_kprobe((kprobe_opcode_t *)regs->ARM_pc); -#endif - - if (p) { - if (cur) { - /* Kprobe is pending, so we're recursing. */ - switch (kcb->kprobe_status) { - case KPROBE_HIT_ACTIVE: - case KPROBE_HIT_SSDONE: - /* A pre- or post-handler probe got us here. */ - kprobes_inc_nmissed_count(p); - save_previous_kprobe(kcb); - set_current_kprobe(p); - kcb->kprobe_status = KPROBE_REENTER; - singlestep(p, regs, kcb); - restore_previous_kprobe(kcb); - break; - default: - /* impossible cases */ - BUG(); - } - } else if (p->ainsn.insn_check_cc(regs->ARM_cpsr)) { - /* Probe hit and conditional execution check ok. */ - set_current_kprobe(p); - kcb->kprobe_status = KPROBE_HIT_ACTIVE; - - /* - * If we have no pre-handler or it returned 0, we - * continue with normal processing. If we have a - * pre-handler and it returned non-zero, it prepped - * for calling the break_handler below on re-entry, - * so get out doing nothing more here. - */ - if (!p->pre_handler || !p->pre_handler(p, regs)) { - kcb->kprobe_status = KPROBE_HIT_SS; - singlestep(p, regs, kcb); - if (p->post_handler) { - kcb->kprobe_status = KPROBE_HIT_SSDONE; - p->post_handler(p, regs, 0); - } - reset_current_kprobe(); - } - } else { - /* - * Probe hit but conditional execution check failed, - * so just skip the instruction and continue as if - * nothing had happened. - */ - singlestep_skip(p, regs); - } - } else if (cur) { - /* We probably hit a jprobe. Call its break handler. */ - if (cur->break_handler && cur->break_handler(cur, regs)) { - kcb->kprobe_status = KPROBE_HIT_SS; - singlestep(cur, regs, kcb); - if (cur->post_handler) { - kcb->kprobe_status = KPROBE_HIT_SSDONE; - cur->post_handler(cur, regs, 0); - } - } - reset_current_kprobe(); - } else { - /* - * The probe was removed and a race is in progress. - * There is nothing we can do about it. Let's restart - * the instruction. By the time we can restart, the - * real instruction will be there. - */ - } -} - -static int __kprobes kprobe_trap_handler(struct pt_regs *regs, unsigned int instr) -{ - unsigned long flags; - local_irq_save(flags); - kprobe_handler(regs); - local_irq_restore(flags); - return 0; -} - -int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr) -{ - struct kprobe *cur = kprobe_running(); - struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); - - switch (kcb->kprobe_status) { - case KPROBE_HIT_SS: - case KPROBE_REENTER: - /* - * We are here because the instruction being single - * stepped caused a page fault. We reset the current - * kprobe and the PC to point back to the probe address - * and allow the page fault handler to continue as a - * normal page fault. - */ - regs->ARM_pc = (long)cur->addr; - if (kcb->kprobe_status == KPROBE_REENTER) { - restore_previous_kprobe(kcb); - } else { - reset_current_kprobe(); - } - break; - - case KPROBE_HIT_ACTIVE: - case KPROBE_HIT_SSDONE: - /* - * We increment the nmissed count for accounting, - * we can also use npre/npostfault count for accounting - * these specific fault cases. - */ - kprobes_inc_nmissed_count(cur); - - /* - * We come here because instructions in the pre/post - * handler caused the page_fault, this could happen - * if handler tries to access user space by - * copy_from_user(), get_user() etc. Let the - * user-specified handler try to fix it. - */ - if (cur->fault_handler && cur->fault_handler(cur, regs, fsr)) - return 1; - break; - - default: - break; - } - - return 0; -} - -int __kprobes kprobe_exceptions_notify(struct notifier_block *self, - unsigned long val, void *data) -{ - /* - * notify_die() is currently never called on ARM, - * so this callback is currently empty. - */ - return NOTIFY_DONE; -} - -/* - * When a retprobed function returns, trampoline_handler() is called, - * calling the kretprobe's handler. We construct a struct pt_regs to - * give a view of registers r0-r11 to the user return-handler. This is - * not a complete pt_regs structure, but that should be plenty sufficient - * for kretprobe handlers which should normally be interested in r0 only - * anyway. - */ -void __naked __kprobes kretprobe_trampoline(void) -{ - __asm__ __volatile__ ( - "stmdb sp!, {r0 - r11} \n\t" - "mov r0, sp \n\t" - "bl trampoline_handler \n\t" - "mov lr, r0 \n\t" - "ldmia sp!, {r0 - r11} \n\t" -#ifdef CONFIG_THUMB2_KERNEL - "bx lr \n\t" -#else - "mov pc, lr \n\t" -#endif - : : : "memory"); -} - -/* Called from kretprobe_trampoline */ -static __used __kprobes void *trampoline_handler(struct pt_regs *regs) -{ - struct kretprobe_instance *ri = NULL; - struct hlist_head *head, empty_rp; - struct hlist_node *tmp; - unsigned long flags, orig_ret_address = 0; - unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline; - - INIT_HLIST_HEAD(&empty_rp); - kretprobe_hash_lock(current, &head, &flags); - - /* - * It is possible to have multiple instances associated with a given - * task either because multiple functions in the call path have - * a return probe installed on them, and/or more than one return - * probe was registered for a target function. - * - * We can handle this because: - * - instances are always inserted at the head of the list - * - when multiple return probes are registered for the same - * function, the first instance's ret_addr will point to the - * real return address, and all the rest will point to - * kretprobe_trampoline - */ - hlist_for_each_entry_safe(ri, tmp, head, hlist) { - if (ri->task != current) - /* another task is sharing our hash bucket */ - continue; - - if (ri->rp && ri->rp->handler) { - __this_cpu_write(current_kprobe, &ri->rp->kp); - get_kprobe_ctlblk()->kprobe_status = KPROBE_HIT_ACTIVE; - ri->rp->handler(ri, regs); - __this_cpu_write(current_kprobe, NULL); - } - - orig_ret_address = (unsigned long)ri->ret_addr; - recycle_rp_inst(ri, &empty_rp); - - if (orig_ret_address != trampoline_address) - /* - * This is the real return address. Any other - * instances associated with this task are for - * other calls deeper on the call stack - */ - break; - } - - kretprobe_assert(ri, orig_ret_address, trampoline_address); - kretprobe_hash_unlock(current, &flags); - - hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) { - hlist_del(&ri->hlist); - kfree(ri); - } - - return (void *)orig_ret_address; -} - -void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, - struct pt_regs *regs) -{ - ri->ret_addr = (kprobe_opcode_t *)regs->ARM_lr; - - /* Replace the return addr with trampoline addr. */ - regs->ARM_lr = (unsigned long)&kretprobe_trampoline; -} - -int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) -{ - struct jprobe *jp = container_of(p, struct jprobe, kp); - struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); - long sp_addr = regs->ARM_sp; - long cpsr; - - kcb->jprobe_saved_regs = *regs; - memcpy(kcb->jprobes_stack, (void *)sp_addr, MIN_STACK_SIZE(sp_addr)); - regs->ARM_pc = (long)jp->entry; - - cpsr = regs->ARM_cpsr | PSR_I_BIT; -#ifdef CONFIG_THUMB2_KERNEL - /* Set correct Thumb state in cpsr */ - if (regs->ARM_pc & 1) - cpsr |= PSR_T_BIT; - else - cpsr &= ~PSR_T_BIT; -#endif - regs->ARM_cpsr = cpsr; - - preempt_disable(); - return 1; -} - -void __kprobes jprobe_return(void) -{ - struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); - - __asm__ __volatile__ ( - /* - * Setup an empty pt_regs. Fill SP and PC fields as - * they're needed by longjmp_break_handler. - * - * We allocate some slack between the original SP and start of - * our fabricated regs. To be precise we want to have worst case - * covered which is STMFD with all 16 regs so we allocate 2 * - * sizeof(struct_pt_regs)). - * - * This is to prevent any simulated instruction from writing - * over the regs when they are accessing the stack. - */ -#ifdef CONFIG_THUMB2_KERNEL - "sub r0, %0, %1 \n\t" - "mov sp, r0 \n\t" -#else - "sub sp, %0, %1 \n\t" -#endif - "ldr r0, ="__stringify(JPROBE_MAGIC_ADDR)"\n\t" - "str %0, [sp, %2] \n\t" - "str r0, [sp, %3] \n\t" - "mov r0, sp \n\t" - "bl kprobe_handler \n\t" - - /* - * Return to the context saved by setjmp_pre_handler - * and restored by longjmp_break_handler. - */ -#ifdef CONFIG_THUMB2_KERNEL - "ldr lr, [sp, %2] \n\t" /* lr = saved sp */ - "ldrd r0, r1, [sp, %5] \n\t" /* r0,r1 = saved lr,pc */ - "ldr r2, [sp, %4] \n\t" /* r2 = saved psr */ - "stmdb lr!, {r0, r1, r2} \n\t" /* push saved lr and */ - /* rfe context */ - "ldmia sp, {r0 - r12} \n\t" - "mov sp, lr \n\t" - "ldr lr, [sp], #4 \n\t" - "rfeia sp! \n\t" -#else - "ldr r0, [sp, %4] \n\t" - "msr cpsr_cxsf, r0 \n\t" - "ldmia sp, {r0 - pc} \n\t" -#endif - : - : "r" (kcb->jprobe_saved_regs.ARM_sp), - "I" (sizeof(struct pt_regs) * 2), - "J" (offsetof(struct pt_regs, ARM_sp)), - "J" (offsetof(struct pt_regs, ARM_pc)), - "J" (offsetof(struct pt_regs, ARM_cpsr)), - "J" (offsetof(struct pt_regs, ARM_lr)) - : "memory", "cc"); -} - -int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) -{ - struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); - long stack_addr = kcb->jprobe_saved_regs.ARM_sp; - long orig_sp = regs->ARM_sp; - struct jprobe *jp = container_of(p, struct jprobe, kp); - - if (regs->ARM_pc == JPROBE_MAGIC_ADDR) { - if (orig_sp != stack_addr) { - struct pt_regs *saved_regs = - (struct pt_regs *)kcb->jprobe_saved_regs.ARM_sp; - printk("current sp %lx does not match saved sp %lx\n", - orig_sp, stack_addr); - printk("Saved registers for jprobe %p\n", jp); - show_regs(saved_regs); - printk("Current registers\n"); - show_regs(regs); - BUG(); - } - *regs = kcb->jprobe_saved_regs; - memcpy((void *)stack_addr, kcb->jprobes_stack, - MIN_STACK_SIZE(stack_addr)); - preempt_enable_no_resched(); - return 1; - } - return 0; -} - -int __kprobes arch_trampoline_kprobe(struct kprobe *p) -{ - return 0; -} - -#ifdef CONFIG_THUMB2_KERNEL - -static struct undef_hook kprobes_thumb16_break_hook = { - .instr_mask = 0xffff, - .instr_val = KPROBE_THUMB16_BREAKPOINT_INSTRUCTION, - .cpsr_mask = MODE_MASK, - .cpsr_val = SVC_MODE, - .fn = kprobe_trap_handler, -}; - -static struct undef_hook kprobes_thumb32_break_hook = { - .instr_mask = 0xffffffff, - .instr_val = KPROBE_THUMB32_BREAKPOINT_INSTRUCTION, - .cpsr_mask = MODE_MASK, - .cpsr_val = SVC_MODE, - .fn = kprobe_trap_handler, -}; - -#else /* !CONFIG_THUMB2_KERNEL */ - -static struct undef_hook kprobes_arm_break_hook = { - .instr_mask = 0x0fffffff, - .instr_val = KPROBE_ARM_BREAKPOINT_INSTRUCTION, - .cpsr_mask = MODE_MASK, - .cpsr_val = SVC_MODE, - .fn = kprobe_trap_handler, -}; - -#endif /* !CONFIG_THUMB2_KERNEL */ - -int __init arch_init_kprobes() -{ - arm_probes_decode_init(); -#ifdef CONFIG_THUMB2_KERNEL - register_undef_hook(&kprobes_thumb16_break_hook); - register_undef_hook(&kprobes_thumb32_break_hook); -#else - register_undef_hook(&kprobes_arm_break_hook); -#endif - return 0; -} diff --git a/arch/arm/kernel/kprobes.h b/arch/arm/kernel/kprobes.h deleted file mode 100644 index 9a2712e..0000000 --- a/arch/arm/kernel/kprobes.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * arch/arm/kernel/kprobes.h - * - * Copyright (C) 2011 Jon Medhurst . - * - * Some contents moved here from arch/arm/include/asm/kprobes.h which is - * Copyright (C) 2006, 2007 Motorola Inc. - * - * 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. - */ - -#ifndef _ARM_KERNEL_KPROBES_H -#define _ARM_KERNEL_KPROBES_H - -#include "probes.h" - -/* - * These undefined instructions must be unique and - * reserved solely for kprobes' use. - */ -#define KPROBE_ARM_BREAKPOINT_INSTRUCTION 0x07f001f8 -#define KPROBE_THUMB16_BREAKPOINT_INSTRUCTION 0xde18 -#define KPROBE_THUMB32_BREAKPOINT_INSTRUCTION 0xf7f0a018 - -enum probes_insn __kprobes -kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_probes_insn *asi, - const struct decode_header *h); - -typedef enum probes_insn (kprobe_decode_insn_t)(probes_opcode_t, - struct arch_probes_insn *, - bool, - const union decode_action *); - -#ifdef CONFIG_THUMB2_KERNEL - -extern const union decode_action kprobes_t32_actions[]; -extern const union decode_action kprobes_t16_actions[]; - -#else /* !CONFIG_THUMB2_KERNEL */ - -extern const union decode_action kprobes_arm_actions[]; - -#endif - -#endif /* _ARM_KERNEL_KPROBES_H */ diff --git a/arch/arm/kernel/patch.c b/arch/arm/kernel/patch.c index 5038960..69bda1a 100644 --- a/arch/arm/kernel/patch.c +++ b/arch/arm/kernel/patch.c @@ -8,8 +8,7 @@ #include #include #include - -#include "patch.h" +#include struct patch { void *addr; diff --git a/arch/arm/kernel/patch.h b/arch/arm/kernel/patch.h deleted file mode 100644 index 77e054c..0000000 --- a/arch/arm/kernel/patch.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef _ARM_KERNEL_PATCH_H -#define _ARM_KERNEL_PATCH_H - -void patch_text(void *addr, unsigned int insn); -void __patch_text_real(void *addr, unsigned int insn, bool remap); - -static inline void __patch_text(void *addr, unsigned int insn) -{ - __patch_text_real(addr, insn, true); -} - -static inline void __patch_text_early(void *addr, unsigned int insn) -{ - __patch_text_real(addr, insn, false); -} - -#endif diff --git a/arch/arm/kernel/probes-arm.c b/arch/arm/kernel/probes-arm.c deleted file mode 100644 index 8eaef81..0000000 --- a/arch/arm/kernel/probes-arm.c +++ /dev/null @@ -1,734 +0,0 @@ -/* - * arch/arm/kernel/probes-arm.c - * - * Some code moved here from arch/arm/kernel/kprobes-arm.c - * - * Copyright (C) 2006, 2007 Motorola Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ - -#include -#include -#include -#include - -#include "probes.h" -#include "probes-arm.h" - -#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit))))) - -#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25) - -/* - * To avoid the complications of mimicing single-stepping on a - * processor without a Next-PC or a single-step mode, and to - * avoid having to deal with the side-effects of boosting, we - * simulate or emulate (almost) all ARM instructions. - * - * "Simulation" is where the instruction's behavior is duplicated in - * C code. "Emulation" is where the original instruction is rewritten - * and executed, often by altering its registers. - * - * By having all behavior of the kprobe'd instruction completed before - * returning from the kprobe_handler(), all locks (scheduler and - * interrupt) can safely be released. There is no need for secondary - * breakpoints, no race with MP or preemptable kernels, nor having to - * clean up resources counts at a later time impacting overall system - * performance. By rewriting the instruction, only the minimum registers - * need to be loaded and saved back optimizing performance. - * - * Calling the insnslot_*_rwflags version of a function doesn't hurt - * anything even when the CPSR flags aren't updated by the - * instruction. It's just a little slower in return for saving - * a little space by not having a duplicate function that doesn't - * update the flags. (The same optimization can be said for - * instructions that do or don't perform register writeback) - * Also, instructions can either read the flags, only write the - * flags, or read and write the flags. To save combinations - * rather than for sheer performance, flag functions just assume - * read and write of flags. - */ - -void __kprobes simulate_bbl(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - long iaddr = (long) regs->ARM_pc - 4; - int disp = branch_displacement(insn); - - if (insn & (1 << 24)) - regs->ARM_lr = iaddr + 4; - - regs->ARM_pc = iaddr + 8 + disp; -} - -void __kprobes simulate_blx1(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - long iaddr = (long) regs->ARM_pc - 4; - int disp = branch_displacement(insn); - - regs->ARM_lr = iaddr + 4; - regs->ARM_pc = iaddr + 8 + disp + ((insn >> 23) & 0x2); - regs->ARM_cpsr |= PSR_T_BIT; -} - -void __kprobes simulate_blx2bx(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - int rm = insn & 0xf; - long rmv = regs->uregs[rm]; - - if (insn & (1 << 5)) - regs->ARM_lr = (long) regs->ARM_pc; - - regs->ARM_pc = rmv & ~0x1; - regs->ARM_cpsr &= ~PSR_T_BIT; - if (rmv & 0x1) - regs->ARM_cpsr |= PSR_T_BIT; -} - -void __kprobes simulate_mrs(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - int rd = (insn >> 12) & 0xf; - unsigned long mask = 0xf8ff03df; /* Mask out execution state */ - regs->uregs[rd] = regs->ARM_cpsr & mask; -} - -void __kprobes simulate_mov_ipsp(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - regs->uregs[12] = regs->uregs[13]; -} - -/* - * For the instruction masking and comparisons in all the "space_*" - * functions below, Do _not_ rearrange the order of tests unless - * you're very, very sure of what you are doing. For the sake of - * efficiency, the masks for some tests sometimes assume other test - * have been done prior to them so the number of patterns to test - * for an instruction set can be as broad as possible to reduce the - * number of tests needed. - */ - -static const union decode_item arm_1111_table[] = { - /* Unconditional instructions */ - - /* memory hint 1111 0100 x001 xxxx xxxx xxxx xxxx xxxx */ - /* PLDI (immediate) 1111 0100 x101 xxxx xxxx xxxx xxxx xxxx */ - /* PLDW (immediate) 1111 0101 x001 xxxx xxxx xxxx xxxx xxxx */ - /* PLD (immediate) 1111 0101 x101 xxxx xxxx xxxx xxxx xxxx */ - DECODE_SIMULATE (0xfe300000, 0xf4100000, PROBES_PRELOAD_IMM), - - /* memory hint 1111 0110 x001 xxxx xxxx xxxx xxx0 xxxx */ - /* PLDI (register) 1111 0110 x101 xxxx xxxx xxxx xxx0 xxxx */ - /* PLDW (register) 1111 0111 x001 xxxx xxxx xxxx xxx0 xxxx */ - /* PLD (register) 1111 0111 x101 xxxx xxxx xxxx xxx0 xxxx */ - DECODE_SIMULATE (0xfe300010, 0xf6100000, PROBES_PRELOAD_REG), - - /* BLX (immediate) 1111 101x xxxx xxxx xxxx xxxx xxxx xxxx */ - DECODE_SIMULATE (0xfe000000, 0xfa000000, PROBES_BRANCH_IMM), - - /* CPS 1111 0001 0000 xxx0 xxxx xxxx xx0x xxxx */ - /* SETEND 1111 0001 0000 0001 xxxx xxxx 0000 xxxx */ - /* SRS 1111 100x x1x0 xxxx xxxx xxxx xxxx xxxx */ - /* RFE 1111 100x x0x1 xxxx xxxx xxxx xxxx xxxx */ - - /* Coprocessor instructions... */ - /* MCRR2 1111 1100 0100 xxxx xxxx xxxx xxxx xxxx */ - /* MRRC2 1111 1100 0101 xxxx xxxx xxxx xxxx xxxx */ - /* LDC2 1111 110x xxx1 xxxx xxxx xxxx xxxx xxxx */ - /* STC2 1111 110x xxx0 xxxx xxxx xxxx xxxx xxxx */ - /* CDP2 1111 1110 xxxx xxxx xxxx xxxx xxx0 xxxx */ - /* MCR2 1111 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx */ - /* MRC2 1111 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx */ - - /* Other unallocated instructions... */ - DECODE_END -}; - -static const union decode_item arm_cccc_0001_0xx0____0xxx_table[] = { - /* Miscellaneous instructions */ - - /* MRS cpsr cccc 0001 0000 xxxx xxxx xxxx 0000 xxxx */ - DECODE_SIMULATEX(0x0ff000f0, 0x01000000, PROBES_MRS, - REGS(0, NOPC, 0, 0, 0)), - - /* BX cccc 0001 0010 xxxx xxxx xxxx 0001 xxxx */ - DECODE_SIMULATE (0x0ff000f0, 0x01200010, PROBES_BRANCH_REG), - - /* BLX (register) cccc 0001 0010 xxxx xxxx xxxx 0011 xxxx */ - DECODE_SIMULATEX(0x0ff000f0, 0x01200030, PROBES_BRANCH_REG, - REGS(0, 0, 0, 0, NOPC)), - - /* CLZ cccc 0001 0110 xxxx xxxx xxxx 0001 xxxx */ - DECODE_EMULATEX (0x0ff000f0, 0x01600010, PROBES_CLZ, - REGS(0, NOPC, 0, 0, NOPC)), - - /* QADD cccc 0001 0000 xxxx xxxx xxxx 0101 xxxx */ - /* QSUB cccc 0001 0010 xxxx xxxx xxxx 0101 xxxx */ - /* QDADD cccc 0001 0100 xxxx xxxx xxxx 0101 xxxx */ - /* QDSUB cccc 0001 0110 xxxx xxxx xxxx 0101 xxxx */ - DECODE_EMULATEX (0x0f9000f0, 0x01000050, PROBES_SATURATING_ARITHMETIC, - REGS(NOPC, NOPC, 0, 0, NOPC)), - - /* BXJ cccc 0001 0010 xxxx xxxx xxxx 0010 xxxx */ - /* MSR cccc 0001 0x10 xxxx xxxx xxxx 0000 xxxx */ - /* MRS spsr cccc 0001 0100 xxxx xxxx xxxx 0000 xxxx */ - /* BKPT 1110 0001 0010 xxxx xxxx xxxx 0111 xxxx */ - /* SMC cccc 0001 0110 xxxx xxxx xxxx 0111 xxxx */ - /* And unallocated instructions... */ - DECODE_END -}; - -static const union decode_item arm_cccc_0001_0xx0____1xx0_table[] = { - /* Halfword multiply and multiply-accumulate */ - - /* SMLALxy cccc 0001 0100 xxxx xxxx xxxx 1xx0 xxxx */ - DECODE_EMULATEX (0x0ff00090, 0x01400080, PROBES_MUL1, - REGS(NOPC, NOPC, NOPC, 0, NOPC)), - - /* SMULWy cccc 0001 0010 xxxx xxxx xxxx 1x10 xxxx */ - DECODE_OR (0x0ff000b0, 0x012000a0), - /* SMULxy cccc 0001 0110 xxxx xxxx xxxx 1xx0 xxxx */ - DECODE_EMULATEX (0x0ff00090, 0x01600080, PROBES_MUL2, - REGS(NOPC, 0, NOPC, 0, NOPC)), - - /* SMLAxy cccc 0001 0000 xxxx xxxx xxxx 1xx0 xxxx */ - DECODE_OR (0x0ff00090, 0x01000080), - /* SMLAWy cccc 0001 0010 xxxx xxxx xxxx 1x00 xxxx */ - DECODE_EMULATEX (0x0ff000b0, 0x01200080, PROBES_MUL2, - REGS(NOPC, NOPC, NOPC, 0, NOPC)), - - DECODE_END -}; - -static const union decode_item arm_cccc_0000_____1001_table[] = { - /* Multiply and multiply-accumulate */ - - /* MUL cccc 0000 0000 xxxx xxxx xxxx 1001 xxxx */ - /* MULS cccc 0000 0001 xxxx xxxx xxxx 1001 xxxx */ - DECODE_EMULATEX (0x0fe000f0, 0x00000090, PROBES_MUL2, - REGS(NOPC, 0, NOPC, 0, NOPC)), - - /* MLA cccc 0000 0010 xxxx xxxx xxxx 1001 xxxx */ - /* MLAS cccc 0000 0011 xxxx xxxx xxxx 1001 xxxx */ - DECODE_OR (0x0fe000f0, 0x00200090), - /* MLS cccc 0000 0110 xxxx xxxx xxxx 1001 xxxx */ - DECODE_EMULATEX (0x0ff000f0, 0x00600090, PROBES_MUL2, - REGS(NOPC, NOPC, NOPC, 0, NOPC)), - - /* UMAAL cccc 0000 0100 xxxx xxxx xxxx 1001 xxxx */ - DECODE_OR (0x0ff000f0, 0x00400090), - /* UMULL cccc 0000 1000 xxxx xxxx xxxx 1001 xxxx */ - /* UMULLS cccc 0000 1001 xxxx xxxx xxxx 1001 xxxx */ - /* UMLAL cccc 0000 1010 xxxx xxxx xxxx 1001 xxxx */ - /* UMLALS cccc 0000 1011 xxxx xxxx xxxx 1001 xxxx */ - /* SMULL cccc 0000 1100 xxxx xxxx xxxx 1001 xxxx */ - /* SMULLS cccc 0000 1101 xxxx xxxx xxxx 1001 xxxx */ - /* SMLAL cccc 0000 1110 xxxx xxxx xxxx 1001 xxxx */ - /* SMLALS cccc 0000 1111 xxxx xxxx xxxx 1001 xxxx */ - DECODE_EMULATEX (0x0f8000f0, 0x00800090, PROBES_MUL1, - REGS(NOPC, NOPC, NOPC, 0, NOPC)), - - DECODE_END -}; - -static const union decode_item arm_cccc_0001_____1001_table[] = { - /* Synchronization primitives */ - -#if __LINUX_ARM_ARCH__ < 6 - /* Deprecated on ARMv6 and may be UNDEFINED on v7 */ - /* SMP/SWPB cccc 0001 0x00 xxxx xxxx xxxx 1001 xxxx */ - DECODE_EMULATEX (0x0fb000f0, 0x01000090, PROBES_SWP, - REGS(NOPC, NOPC, 0, 0, NOPC)), -#endif - /* LDREX/STREX{,D,B,H} cccc 0001 1xxx xxxx xxxx xxxx 1001 xxxx */ - /* And unallocated instructions... */ - DECODE_END -}; - -static const union decode_item arm_cccc_000x_____1xx1_table[] = { - /* Extra load/store instructions */ - - /* STRHT cccc 0000 xx10 xxxx xxxx xxxx 1011 xxxx */ - /* ??? cccc 0000 xx10 xxxx xxxx xxxx 11x1 xxxx */ - /* LDRHT cccc 0000 xx11 xxxx xxxx xxxx 1011 xxxx */ - /* LDRSBT cccc 0000 xx11 xxxx xxxx xxxx 1101 xxxx */ - /* LDRSHT cccc 0000 xx11 xxxx xxxx xxxx 1111 xxxx */ - DECODE_REJECT (0x0f200090, 0x00200090), - - /* LDRD/STRD lr,pc,{... cccc 000x x0x0 xxxx 111x xxxx 1101 xxxx */ - DECODE_REJECT (0x0e10e0d0, 0x0000e0d0), - - /* LDRD (register) cccc 000x x0x0 xxxx xxxx xxxx 1101 xxxx */ - /* STRD (register) cccc 000x x0x0 xxxx xxxx xxxx 1111 xxxx */ - DECODE_EMULATEX (0x0e5000d0, 0x000000d0, PROBES_LDRSTRD, - REGS(NOPCWB, NOPCX, 0, 0, NOPC)), - - /* LDRD (immediate) cccc 000x x1x0 xxxx xxxx xxxx 1101 xxxx */ - /* STRD (immediate) cccc 000x x1x0 xxxx xxxx xxxx 1111 xxxx */ - DECODE_EMULATEX (0x0e5000d0, 0x004000d0, PROBES_LDRSTRD, - REGS(NOPCWB, NOPCX, 0, 0, 0)), - - /* STRH (register) cccc 000x x0x0 xxxx xxxx xxxx 1011 xxxx */ - DECODE_EMULATEX (0x0e5000f0, 0x000000b0, PROBES_STORE_EXTRA, - REGS(NOPCWB, NOPC, 0, 0, NOPC)), - - /* LDRH (register) cccc 000x x0x1 xxxx xxxx xxxx 1011 xxxx */ - /* LDRSB (register) cccc 000x x0x1 xxxx xxxx xxxx 1101 xxxx */ - /* LDRSH (register) cccc 000x x0x1 xxxx xxxx xxxx 1111 xxxx */ - DECODE_EMULATEX (0x0e500090, 0x00100090, PROBES_LOAD_EXTRA, - REGS(NOPCWB, NOPC, 0, 0, NOPC)), - - /* STRH (immediate) cccc 000x x1x0 xxxx xxxx xxxx 1011 xxxx */ - DECODE_EMULATEX (0x0e5000f0, 0x004000b0, PROBES_STORE_EXTRA, - REGS(NOPCWB, NOPC, 0, 0, 0)), - - /* LDRH (immediate) cccc 000x x1x1 xxxx xxxx xxxx 1011 xxxx */ - /* LDRSB (immediate) cccc 000x x1x1 xxxx xxxx xxxx 1101 xxxx */ - /* LDRSH (immediate) cccc 000x x1x1 xxxx xxxx xxxx 1111 xxxx */ - DECODE_EMULATEX (0x0e500090, 0x00500090, PROBES_LOAD_EXTRA, - REGS(NOPCWB, NOPC, 0, 0, 0)), - - DECODE_END -}; - -static const union decode_item arm_cccc_000x_table[] = { - /* Data-processing (register) */ - - /* S PC, ... cccc 000x xxx1 xxxx 1111 xxxx xxxx xxxx */ - DECODE_REJECT (0x0e10f000, 0x0010f000), - - /* MOV IP, SP 1110 0001 1010 0000 1100 0000 0000 1101 */ - DECODE_SIMULATE (0xffffffff, 0xe1a0c00d, PROBES_MOV_IP_SP), - - /* TST (register) cccc 0001 0001 xxxx xxxx xxxx xxx0 xxxx */ - /* TEQ (register) cccc 0001 0011 xxxx xxxx xxxx xxx0 xxxx */ - /* CMP (register) cccc 0001 0101 xxxx xxxx xxxx xxx0 xxxx */ - /* CMN (register) cccc 0001 0111 xxxx xxxx xxxx xxx0 xxxx */ - DECODE_EMULATEX (0x0f900010, 0x01100000, PROBES_DATA_PROCESSING_REG, - REGS(ANY, 0, 0, 0, ANY)), - - /* MOV (register) cccc 0001 101x xxxx xxxx xxxx xxx0 xxxx */ - /* MVN (register) cccc 0001 111x xxxx xxxx xxxx xxx0 xxxx */ - DECODE_EMULATEX (0x0fa00010, 0x01a00000, PROBES_DATA_PROCESSING_REG, - REGS(0, ANY, 0, 0, ANY)), - - /* AND (register) cccc 0000 000x xxxx xxxx xxxx xxx0 xxxx */ - /* EOR (register) cccc 0000 001x xxxx xxxx xxxx xxx0 xxxx */ - /* SUB (register) cccc 0000 010x xxxx xxxx xxxx xxx0 xxxx */ - /* RSB (register) cccc 0000 011x xxxx xxxx xxxx xxx0 xxxx */ - /* ADD (register) cccc 0000 100x xxxx xxxx xxxx xxx0 xxxx */ - /* ADC (register) cccc 0000 101x xxxx xxxx xxxx xxx0 xxxx */ - /* SBC (register) cccc 0000 110x xxxx xxxx xxxx xxx0 xxxx */ - /* RSC (register) cccc 0000 111x xxxx xxxx xxxx xxx0 xxxx */ - /* ORR (register) cccc 0001 100x xxxx xxxx xxxx xxx0 xxxx */ - /* BIC (register) cccc 0001 110x xxxx xxxx xxxx xxx0 xxxx */ - DECODE_EMULATEX (0x0e000010, 0x00000000, PROBES_DATA_PROCESSING_REG, - REGS(ANY, ANY, 0, 0, ANY)), - - /* TST (reg-shift reg) cccc 0001 0001 xxxx xxxx xxxx 0xx1 xxxx */ - /* TEQ (reg-shift reg) cccc 0001 0011 xxxx xxxx xxxx 0xx1 xxxx */ - /* CMP (reg-shift reg) cccc 0001 0101 xxxx xxxx xxxx 0xx1 xxxx */ - /* CMN (reg-shift reg) cccc 0001 0111 xxxx xxxx xxxx 0xx1 xxxx */ - DECODE_EMULATEX (0x0f900090, 0x01100010, PROBES_DATA_PROCESSING_REG, - REGS(NOPC, 0, NOPC, 0, NOPC)), - - /* MOV (reg-shift reg) cccc 0001 101x xxxx xxxx xxxx 0xx1 xxxx */ - /* MVN (reg-shift reg) cccc 0001 111x xxxx xxxx xxxx 0xx1 xxxx */ - DECODE_EMULATEX (0x0fa00090, 0x01a00010, PROBES_DATA_PROCESSING_REG, - REGS(0, NOPC, NOPC, 0, NOPC)), - - /* AND (reg-shift reg) cccc 0000 000x xxxx xxxx xxxx 0xx1 xxxx */ - /* EOR (reg-shift reg) cccc 0000 001x xxxx xxxx xxxx 0xx1 xxxx */ - /* SUB (reg-shift reg) cccc 0000 010x xxxx xxxx xxxx 0xx1 xxxx */ - /* RSB (reg-shift reg) cccc 0000 011x xxxx xxxx xxxx 0xx1 xxxx */ - /* ADD (reg-shift reg) cccc 0000 100x xxxx xxxx xxxx 0xx1 xxxx */ - /* ADC (reg-shift reg) cccc 0000 101x xxxx xxxx xxxx 0xx1 xxxx */ - /* SBC (reg-shift reg) cccc 0000 110x xxxx xxxx xxxx 0xx1 xxxx */ - /* RSC (reg-shift reg) cccc 0000 111x xxxx xxxx xxxx 0xx1 xxxx */ - /* ORR (reg-shift reg) cccc 0001 100x xxxx xxxx xxxx 0xx1 xxxx */ - /* BIC (reg-shift reg) cccc 0001 110x xxxx xxxx xxxx 0xx1 xxxx */ - DECODE_EMULATEX (0x0e000090, 0x00000010, PROBES_DATA_PROCESSING_REG, - REGS(NOPC, NOPC, NOPC, 0, NOPC)), - - DECODE_END -}; - -static const union decode_item arm_cccc_001x_table[] = { - /* Data-processing (immediate) */ - - /* MOVW cccc 0011 0000 xxxx xxxx xxxx xxxx xxxx */ - /* MOVT cccc 0011 0100 xxxx xxxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0x0fb00000, 0x03000000, PROBES_DATA_PROCESSING_IMM, - REGS(0, NOPC, 0, 0, 0)), - - /* YIELD cccc 0011 0010 0000 xxxx xxxx 0000 0001 */ - DECODE_OR (0x0fff00ff, 0x03200001), - /* SEV cccc 0011 0010 0000 xxxx xxxx 0000 0100 */ - DECODE_EMULATE (0x0fff00ff, 0x03200004, PROBES_EMULATE_NONE), - /* NOP cccc 0011 0010 0000 xxxx xxxx 0000 0000 */ - /* WFE cccc 0011 0010 0000 xxxx xxxx 0000 0010 */ - /* WFI cccc 0011 0010 0000 xxxx xxxx 0000 0011 */ - DECODE_SIMULATE (0x0fff00fc, 0x03200000, PROBES_SIMULATE_NOP), - /* DBG cccc 0011 0010 0000 xxxx xxxx ffff xxxx */ - /* unallocated hints cccc 0011 0010 0000 xxxx xxxx xxxx xxxx */ - /* MSR (immediate) cccc 0011 0x10 xxxx xxxx xxxx xxxx xxxx */ - DECODE_REJECT (0x0fb00000, 0x03200000), - - /* S PC, ... cccc 001x xxx1 xxxx 1111 xxxx xxxx xxxx */ - DECODE_REJECT (0x0e10f000, 0x0210f000), - - /* TST (immediate) cccc 0011 0001 xxxx xxxx xxxx xxxx xxxx */ - /* TEQ (immediate) cccc 0011 0011 xxxx xxxx xxxx xxxx xxxx */ - /* CMP (immediate) cccc 0011 0101 xxxx xxxx xxxx xxxx xxxx */ - /* CMN (immediate) cccc 0011 0111 xxxx xxxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0x0f900000, 0x03100000, PROBES_DATA_PROCESSING_IMM, - REGS(ANY, 0, 0, 0, 0)), - - /* MOV (immediate) cccc 0011 101x xxxx xxxx xxxx xxxx xxxx */ - /* MVN (immediate) cccc 0011 111x xxxx xxxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0x0fa00000, 0x03a00000, PROBES_DATA_PROCESSING_IMM, - REGS(0, ANY, 0, 0, 0)), - - /* AND (immediate) cccc 0010 000x xxxx xxxx xxxx xxxx xxxx */ - /* EOR (immediate) cccc 0010 001x xxxx xxxx xxxx xxxx xxxx */ - /* SUB (immediate) cccc 0010 010x xxxx xxxx xxxx xxxx xxxx */ - /* RSB (immediate) cccc 0010 011x xxxx xxxx xxxx xxxx xxxx */ - /* ADD (immediate) cccc 0010 100x xxxx xxxx xxxx xxxx xxxx */ - /* ADC (immediate) cccc 0010 101x xxxx xxxx xxxx xxxx xxxx */ - /* SBC (immediate) cccc 0010 110x xxxx xxxx xxxx xxxx xxxx */ - /* RSC (immediate) cccc 0010 111x xxxx xxxx xxxx xxxx xxxx */ - /* ORR (immediate) cccc 0011 100x xxxx xxxx xxxx xxxx xxxx */ - /* BIC (immediate) cccc 0011 110x xxxx xxxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0x0e000000, 0x02000000, PROBES_DATA_PROCESSING_IMM, - REGS(ANY, ANY, 0, 0, 0)), - - DECODE_END -}; - -static const union decode_item arm_cccc_0110_____xxx1_table[] = { - /* Media instructions */ - - /* SEL cccc 0110 1000 xxxx xxxx xxxx 1011 xxxx */ - DECODE_EMULATEX (0x0ff000f0, 0x068000b0, PROBES_SATURATE, - REGS(NOPC, NOPC, 0, 0, NOPC)), - - /* SSAT cccc 0110 101x xxxx xxxx xxxx xx01 xxxx */ - /* USAT cccc 0110 111x xxxx xxxx xxxx xx01 xxxx */ - DECODE_OR(0x0fa00030, 0x06a00010), - /* SSAT16 cccc 0110 1010 xxxx xxxx xxxx 0011 xxxx */ - /* USAT16 cccc 0110 1110 xxxx xxxx xxxx 0011 xxxx */ - DECODE_EMULATEX (0x0fb000f0, 0x06a00030, PROBES_SATURATE, - REGS(0, NOPC, 0, 0, NOPC)), - - /* REV cccc 0110 1011 xxxx xxxx xxxx 0011 xxxx */ - /* REV16 cccc 0110 1011 xxxx xxxx xxxx 1011 xxxx */ - /* RBIT cccc 0110 1111 xxxx xxxx xxxx 0011 xxxx */ - /* REVSH cccc 0110 1111 xxxx xxxx xxxx 1011 xxxx */ - DECODE_EMULATEX (0x0fb00070, 0x06b00030, PROBES_REV, - REGS(0, NOPC, 0, 0, NOPC)), - - /* ??? cccc 0110 0x00 xxxx xxxx xxxx xxx1 xxxx */ - DECODE_REJECT (0x0fb00010, 0x06000010), - /* ??? cccc 0110 0xxx xxxx xxxx xxxx 1011 xxxx */ - DECODE_REJECT (0x0f8000f0, 0x060000b0), - /* ??? cccc 0110 0xxx xxxx xxxx xxxx 1101 xxxx */ - DECODE_REJECT (0x0f8000f0, 0x060000d0), - /* SADD16 cccc 0110 0001 xxxx xxxx xxxx 0001 xxxx */ - /* SADDSUBX cccc 0110 0001 xxxx xxxx xxxx 0011 xxxx */ - /* SSUBADDX cccc 0110 0001 xxxx xxxx xxxx 0101 xxxx */ - /* SSUB16 cccc 0110 0001 xxxx xxxx xxxx 0111 xxxx */ - /* SADD8 cccc 0110 0001 xxxx xxxx xxxx 1001 xxxx */ - /* SSUB8 cccc 0110 0001 xxxx xxxx xxxx 1111 xxxx */ - /* QADD16 cccc 0110 0010 xxxx xxxx xxxx 0001 xxxx */ - /* QADDSUBX cccc 0110 0010 xxxx xxxx xxxx 0011 xxxx */ - /* QSUBADDX cccc 0110 0010 xxxx xxxx xxxx 0101 xxxx */ - /* QSUB16 cccc 0110 0010 xxxx xxxx xxxx 0111 xxxx */ - /* QADD8 cccc 0110 0010 xxxx xxxx xxxx 1001 xxxx */ - /* QSUB8 cccc 0110 0010 xxxx xxxx xxxx 1111 xxxx */ - /* SHADD16 cccc 0110 0011 xxxx xxxx xxxx 0001 xxxx */ - /* SHADDSUBX cccc 0110 0011 xxxx xxxx xxxx 0011 xxxx */ - /* SHSUBADDX cccc 0110 0011 xxxx xxxx xxxx 0101 xxxx */ - /* SHSUB16 cccc 0110 0011 xxxx xxxx xxxx 0111 xxxx */ - /* SHADD8 cccc 0110 0011 xxxx xxxx xxxx 1001 xxxx */ - /* SHSUB8 cccc 0110 0011 xxxx xxxx xxxx 1111 xxxx */ - /* UADD16 cccc 0110 0101 xxxx xxxx xxxx 0001 xxxx */ - /* UADDSUBX cccc 0110 0101 xxxx xxxx xxxx 0011 xxxx */ - /* USUBADDX cccc 0110 0101 xxxx xxxx xxxx 0101 xxxx */ - /* USUB16 cccc 0110 0101 xxxx xxxx xxxx 0111 xxxx */ - /* UADD8 cccc 0110 0101 xxxx xxxx xxxx 1001 xxxx */ - /* USUB8 cccc 0110 0101 xxxx xxxx xxxx 1111 xxxx */ - /* UQADD16 cccc 0110 0110 xxxx xxxx xxxx 0001 xxxx */ - /* UQADDSUBX cccc 0110 0110 xxxx xxxx xxxx 0011 xxxx */ - /* UQSUBADDX cccc 0110 0110 xxxx xxxx xxxx 0101 xxxx */ - /* UQSUB16 cccc 0110 0110 xxxx xxxx xxxx 0111 xxxx */ - /* UQADD8 cccc 0110 0110 xxxx xxxx xxxx 1001 xxxx */ - /* UQSUB8 cccc 0110 0110 xxxx xxxx xxxx 1111 xxxx */ - /* UHADD16 cccc 0110 0111 xxxx xxxx xxxx 0001 xxxx */ - /* UHADDSUBX cccc 0110 0111 xxxx xxxx xxxx 0011 xxxx */ - /* UHSUBADDX cccc 0110 0111 xxxx xxxx xxxx 0101 xxxx */ - /* UHSUB16 cccc 0110 0111 xxxx xxxx xxxx 0111 xxxx */ - /* UHADD8 cccc 0110 0111 xxxx xxxx xxxx 1001 xxxx */ - /* UHSUB8 cccc 0110 0111 xxxx xxxx xxxx 1111 xxxx */ - DECODE_EMULATEX (0x0f800010, 0x06000010, PROBES_MMI, - REGS(NOPC, NOPC, 0, 0, NOPC)), - - /* PKHBT cccc 0110 1000 xxxx xxxx xxxx x001 xxxx */ - /* PKHTB cccc 0110 1000 xxxx xxxx xxxx x101 xxxx */ - DECODE_EMULATEX (0x0ff00030, 0x06800010, PROBES_PACK, - REGS(NOPC, NOPC, 0, 0, NOPC)), - - /* ??? cccc 0110 1001 xxxx xxxx xxxx 0111 xxxx */ - /* ??? cccc 0110 1101 xxxx xxxx xxxx 0111 xxxx */ - DECODE_REJECT (0x0fb000f0, 0x06900070), - - /* SXTB16 cccc 0110 1000 1111 xxxx xxxx 0111 xxxx */ - /* SXTB cccc 0110 1010 1111 xxxx xxxx 0111 xxxx */ - /* SXTH cccc 0110 1011 1111 xxxx xxxx 0111 xxxx */ - /* UXTB16 cccc 0110 1100 1111 xxxx xxxx 0111 xxxx */ - /* UXTB cccc 0110 1110 1111 xxxx xxxx 0111 xxxx */ - /* UXTH cccc 0110 1111 1111 xxxx xxxx 0111 xxxx */ - DECODE_EMULATEX (0x0f8f00f0, 0x068f0070, PROBES_EXTEND, - REGS(0, NOPC, 0, 0, NOPC)), - - /* SXTAB16 cccc 0110 1000 xxxx xxxx xxxx 0111 xxxx */ - /* SXTAB cccc 0110 1010 xxxx xxxx xxxx 0111 xxxx */ - /* SXTAH cccc 0110 1011 xxxx xxxx xxxx 0111 xxxx */ - /* UXTAB16 cccc 0110 1100 xxxx xxxx xxxx 0111 xxxx */ - /* UXTAB cccc 0110 1110 xxxx xxxx xxxx 0111 xxxx */ - /* UXTAH cccc 0110 1111 xxxx xxxx xxxx 0111 xxxx */ - DECODE_EMULATEX (0x0f8000f0, 0x06800070, PROBES_EXTEND_ADD, - REGS(NOPCX, NOPC, 0, 0, NOPC)), - - DECODE_END -}; - -static const union decode_item arm_cccc_0111_____xxx1_table[] = { - /* Media instructions */ - - /* UNDEFINED cccc 0111 1111 xxxx xxxx xxxx 1111 xxxx */ - DECODE_REJECT (0x0ff000f0, 0x07f000f0), - - /* SMLALD cccc 0111 0100 xxxx xxxx xxxx 00x1 xxxx */ - /* SMLSLD cccc 0111 0100 xxxx xxxx xxxx 01x1 xxxx */ - DECODE_EMULATEX (0x0ff00090, 0x07400010, PROBES_MUL_ADD_LONG, - REGS(NOPC, NOPC, NOPC, 0, NOPC)), - - /* SMUAD cccc 0111 0000 xxxx 1111 xxxx 00x1 xxxx */ - /* SMUSD cccc 0111 0000 xxxx 1111 xxxx 01x1 xxxx */ - DECODE_OR (0x0ff0f090, 0x0700f010), - /* SMMUL cccc 0111 0101 xxxx 1111 xxxx 00x1 xxxx */ - DECODE_OR (0x0ff0f0d0, 0x0750f010), - /* USAD8 cccc 0111 1000 xxxx 1111 xxxx 0001 xxxx */ - DECODE_EMULATEX (0x0ff0f0f0, 0x0780f010, PROBES_MUL_ADD, - REGS(NOPC, 0, NOPC, 0, NOPC)), - - /* SMLAD cccc 0111 0000 xxxx xxxx xxxx 00x1 xxxx */ - /* SMLSD cccc 0111 0000 xxxx xxxx xxxx 01x1 xxxx */ - DECODE_OR (0x0ff00090, 0x07000010), - /* SMMLA cccc 0111 0101 xxxx xxxx xxxx 00x1 xxxx */ - DECODE_OR (0x0ff000d0, 0x07500010), - /* USADA8 cccc 0111 1000 xxxx xxxx xxxx 0001 xxxx */ - DECODE_EMULATEX (0x0ff000f0, 0x07800010, PROBES_MUL_ADD, - REGS(NOPC, NOPCX, NOPC, 0, NOPC)), - - /* SMMLS cccc 0111 0101 xxxx xxxx xxxx 11x1 xxxx */ - DECODE_EMULATEX (0x0ff000d0, 0x075000d0, PROBES_MUL_ADD, - REGS(NOPC, NOPC, NOPC, 0, NOPC)), - - /* SBFX cccc 0111 101x xxxx xxxx xxxx x101 xxxx */ - /* UBFX cccc 0111 111x xxxx xxxx xxxx x101 xxxx */ - DECODE_EMULATEX (0x0fa00070, 0x07a00050, PROBES_BITFIELD, - REGS(0, NOPC, 0, 0, NOPC)), - - /* BFC cccc 0111 110x xxxx xxxx xxxx x001 1111 */ - DECODE_EMULATEX (0x0fe0007f, 0x07c0001f, PROBES_BITFIELD, - REGS(0, NOPC, 0, 0, 0)), - - /* BFI cccc 0111 110x xxxx xxxx xxxx x001 xxxx */ - DECODE_EMULATEX (0x0fe00070, 0x07c00010, PROBES_BITFIELD, - REGS(0, NOPC, 0, 0, NOPCX)), - - DECODE_END -}; - -static const union decode_item arm_cccc_01xx_table[] = { - /* Load/store word and unsigned byte */ - - /* LDRB/STRB pc,[...] cccc 01xx x0xx xxxx xxxx xxxx xxxx xxxx */ - DECODE_REJECT (0x0c40f000, 0x0440f000), - - /* STRT cccc 01x0 x010 xxxx xxxx xxxx xxxx xxxx */ - /* LDRT cccc 01x0 x011 xxxx xxxx xxxx xxxx xxxx */ - /* STRBT cccc 01x0 x110 xxxx xxxx xxxx xxxx xxxx */ - /* LDRBT cccc 01x0 x111 xxxx xxxx xxxx xxxx xxxx */ - DECODE_REJECT (0x0d200000, 0x04200000), - - /* STR (immediate) cccc 010x x0x0 xxxx xxxx xxxx xxxx xxxx */ - /* STRB (immediate) cccc 010x x1x0 xxxx xxxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0x0e100000, 0x04000000, PROBES_STORE, - REGS(NOPCWB, ANY, 0, 0, 0)), - - /* LDR (immediate) cccc 010x x0x1 xxxx xxxx xxxx xxxx xxxx */ - /* LDRB (immediate) cccc 010x x1x1 xxxx xxxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0x0e100000, 0x04100000, PROBES_LOAD, - REGS(NOPCWB, ANY, 0, 0, 0)), - - /* STR (register) cccc 011x x0x0 xxxx xxxx xxxx xxxx xxxx */ - /* STRB (register) cccc 011x x1x0 xxxx xxxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0x0e100000, 0x06000000, PROBES_STORE, - REGS(NOPCWB, ANY, 0, 0, NOPC)), - - /* LDR (register) cccc 011x x0x1 xxxx xxxx xxxx xxxx xxxx */ - /* LDRB (register) cccc 011x x1x1 xxxx xxxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0x0e100000, 0x06100000, PROBES_LOAD, - REGS(NOPCWB, ANY, 0, 0, NOPC)), - - DECODE_END -}; - -static const union decode_item arm_cccc_100x_table[] = { - /* Block data transfer instructions */ - - /* LDM cccc 100x x0x1 xxxx xxxx xxxx xxxx xxxx */ - /* STM cccc 100x x0x0 xxxx xxxx xxxx xxxx xxxx */ - DECODE_CUSTOM (0x0e400000, 0x08000000, PROBES_LDMSTM), - - /* STM (user registers) cccc 100x x1x0 xxxx xxxx xxxx xxxx xxxx */ - /* LDM (user registers) cccc 100x x1x1 xxxx 0xxx xxxx xxxx xxxx */ - /* LDM (exception ret) cccc 100x x1x1 xxxx 1xxx xxxx xxxx xxxx */ - DECODE_END -}; - -const union decode_item probes_decode_arm_table[] = { - /* - * Unconditional instructions - * 1111 xxxx xxxx xxxx xxxx xxxx xxxx xxxx - */ - DECODE_TABLE (0xf0000000, 0xf0000000, arm_1111_table), - - /* - * Miscellaneous instructions - * cccc 0001 0xx0 xxxx xxxx xxxx 0xxx xxxx - */ - DECODE_TABLE (0x0f900080, 0x01000000, arm_cccc_0001_0xx0____0xxx_table), - - /* - * Halfword multiply and multiply-accumulate - * cccc 0001 0xx0 xxxx xxxx xxxx 1xx0 xxxx - */ - DECODE_TABLE (0x0f900090, 0x01000080, arm_cccc_0001_0xx0____1xx0_table), - - /* - * Multiply and multiply-accumulate - * cccc 0000 xxxx xxxx xxxx xxxx 1001 xxxx - */ - DECODE_TABLE (0x0f0000f0, 0x00000090, arm_cccc_0000_____1001_table), - - /* - * Synchronization primitives - * cccc 0001 xxxx xxxx xxxx xxxx 1001 xxxx - */ - DECODE_TABLE (0x0f0000f0, 0x01000090, arm_cccc_0001_____1001_table), - - /* - * Extra load/store instructions - * cccc 000x xxxx xxxx xxxx xxxx 1xx1 xxxx - */ - DECODE_TABLE (0x0e000090, 0x00000090, arm_cccc_000x_____1xx1_table), - - /* - * Data-processing (register) - * cccc 000x xxxx xxxx xxxx xxxx xxx0 xxxx - * Data-processing (register-shifted register) - * cccc 000x xxxx xxxx xxxx xxxx 0xx1 xxxx - */ - DECODE_TABLE (0x0e000000, 0x00000000, arm_cccc_000x_table), - - /* - * Data-processing (immediate) - * cccc 001x xxxx xxxx xxxx xxxx xxxx xxxx - */ - DECODE_TABLE (0x0e000000, 0x02000000, arm_cccc_001x_table), - - /* - * Media instructions - * cccc 011x xxxx xxxx xxxx xxxx xxx1 xxxx - */ - DECODE_TABLE (0x0f000010, 0x06000010, arm_cccc_0110_____xxx1_table), - DECODE_TABLE (0x0f000010, 0x07000010, arm_cccc_0111_____xxx1_table), - - /* - * Load/store word and unsigned byte - * cccc 01xx xxxx xxxx xxxx xxxx xxxx xxxx - */ - DECODE_TABLE (0x0c000000, 0x04000000, arm_cccc_01xx_table), - - /* - * Block data transfer instructions - * cccc 100x xxxx xxxx xxxx xxxx xxxx xxxx - */ - DECODE_TABLE (0x0e000000, 0x08000000, arm_cccc_100x_table), - - /* B cccc 1010 xxxx xxxx xxxx xxxx xxxx xxxx */ - /* BL cccc 1011 xxxx xxxx xxxx xxxx xxxx xxxx */ - DECODE_SIMULATE (0x0e000000, 0x0a000000, PROBES_BRANCH), - - /* - * Supervisor Call, and coprocessor instructions - */ - - /* MCRR cccc 1100 0100 xxxx xxxx xxxx xxxx xxxx */ - /* MRRC cccc 1100 0101 xxxx xxxx xxxx xxxx xxxx */ - /* LDC cccc 110x xxx1 xxxx xxxx xxxx xxxx xxxx */ - /* STC cccc 110x xxx0 xxxx xxxx xxxx xxxx xxxx */ - /* CDP cccc 1110 xxxx xxxx xxxx xxxx xxx0 xxxx */ - /* MCR cccc 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx */ - /* MRC cccc 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx */ - /* SVC cccc 1111 xxxx xxxx xxxx xxxx xxxx xxxx */ - DECODE_REJECT (0x0c000000, 0x0c000000), - - DECODE_END -}; -#ifdef CONFIG_ARM_KPROBES_TEST_MODULE -EXPORT_SYMBOL_GPL(probes_decode_arm_table); -#endif - -static void __kprobes arm_singlestep(probes_opcode_t insn, - struct arch_probes_insn *asi, struct pt_regs *regs) -{ - regs->ARM_pc += 4; - asi->insn_handler(insn, asi, regs); -} - -/* Return: - * INSN_REJECTED If instruction is one not allowed to kprobe, - * INSN_GOOD If instruction is supported and uses instruction slot, - * INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot. - * - * For instructions we don't want to kprobe (INSN_REJECTED return result): - * These are generally ones that modify the processor state making - * them "hard" to simulate such as switches processor modes or - * make accesses in alternate modes. Any of these could be simulated - * if the work was put into it, but low return considering they - * should also be very rare. - */ -enum probes_insn __kprobes -arm_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, - bool emulate, const union decode_action *actions) -{ - asi->insn_singlestep = arm_singlestep; - asi->insn_check_cc = probes_condition_checks[insn>>28]; - return probes_decode_insn(insn, asi, probes_decode_arm_table, false, - emulate, actions); -} diff --git a/arch/arm/kernel/probes-arm.h b/arch/arm/kernel/probes-arm.h deleted file mode 100644 index ace6572..0000000 --- a/arch/arm/kernel/probes-arm.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * arch/arm/kernel/probes-arm.h - * - * Copyright 2013 Linaro Ltd. - * Written by: David A. Long - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ - -#ifndef _ARM_KERNEL_PROBES_ARM_H -#define _ARM_KERNEL_PROBES_ARM_H - -enum probes_arm_action { - PROBES_EMULATE_NONE, - PROBES_SIMULATE_NOP, - PROBES_PRELOAD_IMM, - PROBES_PRELOAD_REG, - PROBES_BRANCH_IMM, - PROBES_BRANCH_REG, - PROBES_MRS, - PROBES_CLZ, - PROBES_SATURATING_ARITHMETIC, - PROBES_MUL1, - PROBES_MUL2, - PROBES_SWP, - PROBES_LDRSTRD, - PROBES_LOAD, - PROBES_STORE, - PROBES_LOAD_EXTRA, - PROBES_STORE_EXTRA, - PROBES_MOV_IP_SP, - PROBES_DATA_PROCESSING_REG, - PROBES_DATA_PROCESSING_IMM, - PROBES_MOV_HALFWORD, - PROBES_SEV, - PROBES_WFE, - PROBES_SATURATE, - PROBES_REV, - PROBES_MMI, - PROBES_PACK, - PROBES_EXTEND, - PROBES_EXTEND_ADD, - PROBES_MUL_ADD_LONG, - PROBES_MUL_ADD, - PROBES_BITFIELD, - PROBES_BRANCH, - PROBES_LDMSTM, - NUM_PROBES_ARM_ACTIONS -}; - -void __kprobes simulate_bbl(probes_opcode_t opcode, - struct arch_probes_insn *asi, struct pt_regs *regs); -void __kprobes simulate_blx1(probes_opcode_t opcode, - struct arch_probes_insn *asi, struct pt_regs *regs); -void __kprobes simulate_blx2bx(probes_opcode_t opcode, - struct arch_probes_insn *asi, struct pt_regs *regs); -void __kprobes simulate_mrs(probes_opcode_t opcode, - struct arch_probes_insn *asi, struct pt_regs *regs); -void __kprobes simulate_mov_ipsp(probes_opcode_t opcode, - struct arch_probes_insn *asi, struct pt_regs *regs); - -extern const union decode_item probes_decode_arm_table[]; - -enum probes_insn arm_probes_decode_insn(probes_opcode_t, - struct arch_probes_insn *, bool emulate, - const union decode_action *actions); - -#endif diff --git a/arch/arm/kernel/probes-thumb.c b/arch/arm/kernel/probes-thumb.c deleted file mode 100644 index 4131351..0000000 --- a/arch/arm/kernel/probes-thumb.c +++ /dev/null @@ -1,882 +0,0 @@ -/* - * arch/arm/kernel/probes-thumb.c - * - * Copyright (C) 2011 Jon Medhurst . - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include - -#include "probes.h" -#include "probes-thumb.h" - - -static const union decode_item t32_table_1110_100x_x0xx[] = { - /* Load/store multiple instructions */ - - /* Rn is PC 1110 100x x0xx 1111 xxxx xxxx xxxx xxxx */ - DECODE_REJECT (0xfe4f0000, 0xe80f0000), - - /* SRS 1110 1000 00x0 xxxx xxxx xxxx xxxx xxxx */ - /* RFE 1110 1000 00x1 xxxx xxxx xxxx xxxx xxxx */ - DECODE_REJECT (0xffc00000, 0xe8000000), - /* SRS 1110 1001 10x0 xxxx xxxx xxxx xxxx xxxx */ - /* RFE 1110 1001 10x1 xxxx xxxx xxxx xxxx xxxx */ - DECODE_REJECT (0xffc00000, 0xe9800000), - - /* STM Rn, {...pc} 1110 100x x0x0 xxxx 1xxx xxxx xxxx xxxx */ - DECODE_REJECT (0xfe508000, 0xe8008000), - /* LDM Rn, {...lr,pc} 1110 100x x0x1 xxxx 11xx xxxx xxxx xxxx */ - DECODE_REJECT (0xfe50c000, 0xe810c000), - /* LDM/STM Rn, {...sp} 1110 100x x0xx xxxx xx1x xxxx xxxx xxxx */ - DECODE_REJECT (0xfe402000, 0xe8002000), - - /* STMIA 1110 1000 10x0 xxxx xxxx xxxx xxxx xxxx */ - /* LDMIA 1110 1000 10x1 xxxx xxxx xxxx xxxx xxxx */ - /* STMDB 1110 1001 00x0 xxxx xxxx xxxx xxxx xxxx */ - /* LDMDB 1110 1001 00x1 xxxx xxxx xxxx xxxx xxxx */ - DECODE_CUSTOM (0xfe400000, 0xe8000000, PROBES_T32_LDMSTM), - - DECODE_END -}; - -static const union decode_item t32_table_1110_100x_x1xx[] = { - /* Load/store dual, load/store exclusive, table branch */ - - /* STRD (immediate) 1110 1000 x110 xxxx xxxx xxxx xxxx xxxx */ - /* LDRD (immediate) 1110 1000 x111 xxxx xxxx xxxx xxxx xxxx */ - DECODE_OR (0xff600000, 0xe8600000), - /* STRD (immediate) 1110 1001 x1x0 xxxx xxxx xxxx xxxx xxxx */ - /* LDRD (immediate) 1110 1001 x1x1 xxxx xxxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xff400000, 0xe9400000, PROBES_T32_LDRDSTRD, - REGS(NOPCWB, NOSPPC, NOSPPC, 0, 0)), - - /* TBB 1110 1000 1101 xxxx xxxx xxxx 0000 xxxx */ - /* TBH 1110 1000 1101 xxxx xxxx xxxx 0001 xxxx */ - DECODE_SIMULATEX(0xfff000e0, 0xe8d00000, PROBES_T32_TABLE_BRANCH, - REGS(NOSP, 0, 0, 0, NOSPPC)), - - /* STREX 1110 1000 0100 xxxx xxxx xxxx xxxx xxxx */ - /* LDREX 1110 1000 0101 xxxx xxxx xxxx xxxx xxxx */ - /* STREXB 1110 1000 1100 xxxx xxxx xxxx 0100 xxxx */ - /* STREXH 1110 1000 1100 xxxx xxxx xxxx 0101 xxxx */ - /* STREXD 1110 1000 1100 xxxx xxxx xxxx 0111 xxxx */ - /* LDREXB 1110 1000 1101 xxxx xxxx xxxx 0100 xxxx */ - /* LDREXH 1110 1000 1101 xxxx xxxx xxxx 0101 xxxx */ - /* LDREXD 1110 1000 1101 xxxx xxxx xxxx 0111 xxxx */ - /* And unallocated instructions... */ - DECODE_END -}; - -static const union decode_item t32_table_1110_101x[] = { - /* Data-processing (shifted register) */ - - /* TST 1110 1010 0001 xxxx xxxx 1111 xxxx xxxx */ - /* TEQ 1110 1010 1001 xxxx xxxx 1111 xxxx xxxx */ - DECODE_EMULATEX (0xff700f00, 0xea100f00, PROBES_T32_TST, - REGS(NOSPPC, 0, 0, 0, NOSPPC)), - - /* CMN 1110 1011 0001 xxxx xxxx 1111 xxxx xxxx */ - DECODE_OR (0xfff00f00, 0xeb100f00), - /* CMP 1110 1011 1011 xxxx xxxx 1111 xxxx xxxx */ - DECODE_EMULATEX (0xfff00f00, 0xebb00f00, PROBES_T32_TST, - REGS(NOPC, 0, 0, 0, NOSPPC)), - - /* MOV 1110 1010 010x 1111 xxxx xxxx xxxx xxxx */ - /* MVN 1110 1010 011x 1111 xxxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xffcf0000, 0xea4f0000, PROBES_T32_MOV, - REGS(0, 0, NOSPPC, 0, NOSPPC)), - - /* ??? 1110 1010 101x xxxx xxxx xxxx xxxx xxxx */ - /* ??? 1110 1010 111x xxxx xxxx xxxx xxxx xxxx */ - DECODE_REJECT (0xffa00000, 0xeaa00000), - /* ??? 1110 1011 001x xxxx xxxx xxxx xxxx xxxx */ - DECODE_REJECT (0xffe00000, 0xeb200000), - /* ??? 1110 1011 100x xxxx xxxx xxxx xxxx xxxx */ - DECODE_REJECT (0xffe00000, 0xeb800000), - /* ??? 1110 1011 111x xxxx xxxx xxxx xxxx xxxx */ - DECODE_REJECT (0xffe00000, 0xebe00000), - - /* ADD/SUB SP, SP, Rm, LSL #0..3 */ - /* 1110 1011 x0xx 1101 x000 1101 xx00 xxxx */ - DECODE_EMULATEX (0xff4f7f30, 0xeb0d0d00, PROBES_T32_ADDSUB, - REGS(SP, 0, SP, 0, NOSPPC)), - - /* ADD/SUB SP, SP, Rm, shift */ - /* 1110 1011 x0xx 1101 xxxx 1101 xxxx xxxx */ - DECODE_REJECT (0xff4f0f00, 0xeb0d0d00), - - /* ADD/SUB Rd, SP, Rm, shift */ - /* 1110 1011 x0xx 1101 xxxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xff4f0000, 0xeb0d0000, PROBES_T32_ADDSUB, - REGS(SP, 0, NOPC, 0, NOSPPC)), - - /* AND 1110 1010 000x xxxx xxxx xxxx xxxx xxxx */ - /* BIC 1110 1010 001x xxxx xxxx xxxx xxxx xxxx */ - /* ORR 1110 1010 010x xxxx xxxx xxxx xxxx xxxx */ - /* ORN 1110 1010 011x xxxx xxxx xxxx xxxx xxxx */ - /* EOR 1110 1010 100x xxxx xxxx xxxx xxxx xxxx */ - /* PKH 1110 1010 110x xxxx xxxx xxxx xxxx xxxx */ - /* ADD 1110 1011 000x xxxx xxxx xxxx xxxx xxxx */ - /* ADC 1110 1011 010x xxxx xxxx xxxx xxxx xxxx */ - /* SBC 1110 1011 011x xxxx xxxx xxxx xxxx xxxx */ - /* SUB 1110 1011 101x xxxx xxxx xxxx xxxx xxxx */ - /* RSB 1110 1011 110x xxxx xxxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xfe000000, 0xea000000, PROBES_T32_LOGICAL, - REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)), - - DECODE_END -}; - -static const union decode_item t32_table_1111_0x0x___0[] = { - /* Data-processing (modified immediate) */ - - /* TST 1111 0x00 0001 xxxx 0xxx 1111 xxxx xxxx */ - /* TEQ 1111 0x00 1001 xxxx 0xxx 1111 xxxx xxxx */ - DECODE_EMULATEX (0xfb708f00, 0xf0100f00, PROBES_T32_TST, - REGS(NOSPPC, 0, 0, 0, 0)), - - /* CMN 1111 0x01 0001 xxxx 0xxx 1111 xxxx xxxx */ - DECODE_OR (0xfbf08f00, 0xf1100f00), - /* CMP 1111 0x01 1011 xxxx 0xxx 1111 xxxx xxxx */ - DECODE_EMULATEX (0xfbf08f00, 0xf1b00f00, PROBES_T32_CMP, - REGS(NOPC, 0, 0, 0, 0)), - - /* MOV 1111 0x00 010x 1111 0xxx xxxx xxxx xxxx */ - /* MVN 1111 0x00 011x 1111 0xxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xfbcf8000, 0xf04f0000, PROBES_T32_MOV, - REGS(0, 0, NOSPPC, 0, 0)), - - /* ??? 1111 0x00 101x xxxx 0xxx xxxx xxxx xxxx */ - DECODE_REJECT (0xfbe08000, 0xf0a00000), - /* ??? 1111 0x00 110x xxxx 0xxx xxxx xxxx xxxx */ - /* ??? 1111 0x00 111x xxxx 0xxx xxxx xxxx xxxx */ - DECODE_REJECT (0xfbc08000, 0xf0c00000), - /* ??? 1111 0x01 001x xxxx 0xxx xxxx xxxx xxxx */ - DECODE_REJECT (0xfbe08000, 0xf1200000), - /* ??? 1111 0x01 100x xxxx 0xxx xxxx xxxx xxxx */ - DECODE_REJECT (0xfbe08000, 0xf1800000), - /* ??? 1111 0x01 111x xxxx 0xxx xxxx xxxx xxxx */ - DECODE_REJECT (0xfbe08000, 0xf1e00000), - - /* ADD Rd, SP, #imm 1111 0x01 000x 1101 0xxx xxxx xxxx xxxx */ - /* SUB Rd, SP, #imm 1111 0x01 101x 1101 0xxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xfb4f8000, 0xf10d0000, PROBES_T32_ADDSUB, - REGS(SP, 0, NOPC, 0, 0)), - - /* AND 1111 0x00 000x xxxx 0xxx xxxx xxxx xxxx */ - /* BIC 1111 0x00 001x xxxx 0xxx xxxx xxxx xxxx */ - /* ORR 1111 0x00 010x xxxx 0xxx xxxx xxxx xxxx */ - /* ORN 1111 0x00 011x xxxx 0xxx xxxx xxxx xxxx */ - /* EOR 1111 0x00 100x xxxx 0xxx xxxx xxxx xxxx */ - /* ADD 1111 0x01 000x xxxx 0xxx xxxx xxxx xxxx */ - /* ADC 1111 0x01 010x xxxx 0xxx xxxx xxxx xxxx */ - /* SBC 1111 0x01 011x xxxx 0xxx xxxx xxxx xxxx */ - /* SUB 1111 0x01 101x xxxx 0xxx xxxx xxxx xxxx */ - /* RSB 1111 0x01 110x xxxx 0xxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xfa008000, 0xf0000000, PROBES_T32_LOGICAL, - REGS(NOSPPC, 0, NOSPPC, 0, 0)), - - DECODE_END -}; - -static const union decode_item t32_table_1111_0x1x___0[] = { - /* Data-processing (plain binary immediate) */ - - /* ADDW Rd, PC, #imm 1111 0x10 0000 1111 0xxx xxxx xxxx xxxx */ - DECODE_OR (0xfbff8000, 0xf20f0000), - /* SUBW Rd, PC, #imm 1111 0x10 1010 1111 0xxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xfbff8000, 0xf2af0000, PROBES_T32_ADDWSUBW_PC, - REGS(PC, 0, NOSPPC, 0, 0)), - - /* ADDW SP, SP, #imm 1111 0x10 0000 1101 0xxx 1101 xxxx xxxx */ - DECODE_OR (0xfbff8f00, 0xf20d0d00), - /* SUBW SP, SP, #imm 1111 0x10 1010 1101 0xxx 1101 xxxx xxxx */ - DECODE_EMULATEX (0xfbff8f00, 0xf2ad0d00, PROBES_T32_ADDWSUBW, - REGS(SP, 0, SP, 0, 0)), - - /* ADDW 1111 0x10 0000 xxxx 0xxx xxxx xxxx xxxx */ - DECODE_OR (0xfbf08000, 0xf2000000), - /* SUBW 1111 0x10 1010 xxxx 0xxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xfbf08000, 0xf2a00000, PROBES_T32_ADDWSUBW, - REGS(NOPCX, 0, NOSPPC, 0, 0)), - - /* MOVW 1111 0x10 0100 xxxx 0xxx xxxx xxxx xxxx */ - /* MOVT 1111 0x10 1100 xxxx 0xxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xfb708000, 0xf2400000, PROBES_T32_MOVW, - REGS(0, 0, NOSPPC, 0, 0)), - - /* SSAT16 1111 0x11 0010 xxxx 0000 xxxx 00xx xxxx */ - /* SSAT 1111 0x11 00x0 xxxx 0xxx xxxx xxxx xxxx */ - /* USAT16 1111 0x11 1010 xxxx 0000 xxxx 00xx xxxx */ - /* USAT 1111 0x11 10x0 xxxx 0xxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xfb508000, 0xf3000000, PROBES_T32_SAT, - REGS(NOSPPC, 0, NOSPPC, 0, 0)), - - /* SFBX 1111 0x11 0100 xxxx 0xxx xxxx xxxx xxxx */ - /* UFBX 1111 0x11 1100 xxxx 0xxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xfb708000, 0xf3400000, PROBES_T32_BITFIELD, - REGS(NOSPPC, 0, NOSPPC, 0, 0)), - - /* BFC 1111 0x11 0110 1111 0xxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xfbff8000, 0xf36f0000, PROBES_T32_BITFIELD, - REGS(0, 0, NOSPPC, 0, 0)), - - /* BFI 1111 0x11 0110 xxxx 0xxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xfbf08000, 0xf3600000, PROBES_T32_BITFIELD, - REGS(NOSPPCX, 0, NOSPPC, 0, 0)), - - DECODE_END -}; - -static const union decode_item t32_table_1111_0xxx___1[] = { - /* Branches and miscellaneous control */ - - /* YIELD 1111 0011 1010 xxxx 10x0 x000 0000 0001 */ - DECODE_OR (0xfff0d7ff, 0xf3a08001), - /* SEV 1111 0011 1010 xxxx 10x0 x000 0000 0100 */ - DECODE_EMULATE (0xfff0d7ff, 0xf3a08004, PROBES_T32_SEV), - /* NOP 1111 0011 1010 xxxx 10x0 x000 0000 0000 */ - /* WFE 1111 0011 1010 xxxx 10x0 x000 0000 0010 */ - /* WFI 1111 0011 1010 xxxx 10x0 x000 0000 0011 */ - DECODE_SIMULATE (0xfff0d7fc, 0xf3a08000, PROBES_T32_WFE), - - /* MRS Rd, CPSR 1111 0011 1110 xxxx 10x0 xxxx xxxx xxxx */ - DECODE_SIMULATEX(0xfff0d000, 0xf3e08000, PROBES_T32_MRS, - REGS(0, 0, NOSPPC, 0, 0)), - - /* - * Unsupported instructions - * 1111 0x11 1xxx xxxx 10x0 xxxx xxxx xxxx - * - * MSR 1111 0011 100x xxxx 10x0 xxxx xxxx xxxx - * DBG hint 1111 0011 1010 xxxx 10x0 x000 1111 xxxx - * Unallocated hints 1111 0011 1010 xxxx 10x0 x000 xxxx xxxx - * CPS 1111 0011 1010 xxxx 10x0 xxxx xxxx xxxx - * CLREX/DSB/DMB/ISB 1111 0011 1011 xxxx 10x0 xxxx xxxx xxxx - * BXJ 1111 0011 1100 xxxx 10x0 xxxx xxxx xxxx - * SUBS PC,LR,# 1111 0011 1101 xxxx 10x0 xxxx xxxx xxxx - * MRS Rd, SPSR 1111 0011 1111 xxxx 10x0 xxxx xxxx xxxx - * SMC 1111 0111 1111 xxxx 1000 xxxx xxxx xxxx - * UNDEFINED 1111 0111 1111 xxxx 1010 xxxx xxxx xxxx - * ??? 1111 0111 1xxx xxxx 1010 xxxx xxxx xxxx - */ - DECODE_REJECT (0xfb80d000, 0xf3808000), - - /* Bcc 1111 0xxx xxxx xxxx 10x0 xxxx xxxx xxxx */ - DECODE_CUSTOM (0xf800d000, 0xf0008000, PROBES_T32_BRANCH_COND), - - /* BLX 1111 0xxx xxxx xxxx 11x0 xxxx xxxx xxx0 */ - DECODE_OR (0xf800d001, 0xf000c000), - /* B 1111 0xxx xxxx xxxx 10x1 xxxx xxxx xxxx */ - /* BL 1111 0xxx xxxx xxxx 11x1 xxxx xxxx xxxx */ - DECODE_SIMULATE (0xf8009000, 0xf0009000, PROBES_T32_BRANCH), - - DECODE_END -}; - -static const union decode_item t32_table_1111_100x_x0x1__1111[] = { - /* Memory hints */ - - /* PLD (literal) 1111 1000 x001 1111 1111 xxxx xxxx xxxx */ - /* PLI (literal) 1111 1001 x001 1111 1111 xxxx xxxx xxxx */ - DECODE_SIMULATE (0xfe7ff000, 0xf81ff000, PROBES_T32_PLDI), - - /* PLD{W} (immediate) 1111 1000 10x1 xxxx 1111 xxxx xxxx xxxx */ - DECODE_OR (0xffd0f000, 0xf890f000), - /* PLD{W} (immediate) 1111 1000 00x1 xxxx 1111 1100 xxxx xxxx */ - DECODE_OR (0xffd0ff00, 0xf810fc00), - /* PLI (immediate) 1111 1001 1001 xxxx 1111 xxxx xxxx xxxx */ - DECODE_OR (0xfff0f000, 0xf990f000), - /* PLI (immediate) 1111 1001 0001 xxxx 1111 1100 xxxx xxxx */ - DECODE_SIMULATEX(0xfff0ff00, 0xf910fc00, PROBES_T32_PLDI, - REGS(NOPCX, 0, 0, 0, 0)), - - /* PLD{W} (register) 1111 1000 00x1 xxxx 1111 0000 00xx xxxx */ - DECODE_OR (0xffd0ffc0, 0xf810f000), - /* PLI (register) 1111 1001 0001 xxxx 1111 0000 00xx xxxx */ - DECODE_SIMULATEX(0xfff0ffc0, 0xf910f000, PROBES_T32_PLDI, - REGS(NOPCX, 0, 0, 0, NOSPPC)), - - /* Other unallocated instructions... */ - DECODE_END -}; - -static const union decode_item t32_table_1111_100x[] = { - /* Store/Load single data item */ - - /* ??? 1111 100x x11x xxxx xxxx xxxx xxxx xxxx */ - DECODE_REJECT (0xfe600000, 0xf8600000), - - /* ??? 1111 1001 0101 xxxx xxxx xxxx xxxx xxxx */ - DECODE_REJECT (0xfff00000, 0xf9500000), - - /* ??? 1111 100x 0xxx xxxx xxxx 10x0 xxxx xxxx */ - DECODE_REJECT (0xfe800d00, 0xf8000800), - - /* STRBT 1111 1000 0000 xxxx xxxx 1110 xxxx xxxx */ - /* STRHT 1111 1000 0010 xxxx xxxx 1110 xxxx xxxx */ - /* STRT 1111 1000 0100 xxxx xxxx 1110 xxxx xxxx */ - /* LDRBT 1111 1000 0001 xxxx xxxx 1110 xxxx xxxx */ - /* LDRSBT 1111 1001 0001 xxxx xxxx 1110 xxxx xxxx */ - /* LDRHT 1111 1000 0011 xxxx xxxx 1110 xxxx xxxx */ - /* LDRSHT 1111 1001 0011 xxxx xxxx 1110 xxxx xxxx */ - /* LDRT 1111 1000 0101 xxxx xxxx 1110 xxxx xxxx */ - DECODE_REJECT (0xfe800f00, 0xf8000e00), - - /* STR{,B,H} Rn,[PC...] 1111 1000 xxx0 1111 xxxx xxxx xxxx xxxx */ - DECODE_REJECT (0xff1f0000, 0xf80f0000), - - /* STR{,B,H} PC,[Rn...] 1111 1000 xxx0 xxxx 1111 xxxx xxxx xxxx */ - DECODE_REJECT (0xff10f000, 0xf800f000), - - /* LDR (literal) 1111 1000 x101 1111 xxxx xxxx xxxx xxxx */ - DECODE_SIMULATEX(0xff7f0000, 0xf85f0000, PROBES_T32_LDR_LIT, - REGS(PC, ANY, 0, 0, 0)), - - /* STR (immediate) 1111 1000 0100 xxxx xxxx 1xxx xxxx xxxx */ - /* LDR (immediate) 1111 1000 0101 xxxx xxxx 1xxx xxxx xxxx */ - DECODE_OR (0xffe00800, 0xf8400800), - /* STR (immediate) 1111 1000 1100 xxxx xxxx xxxx xxxx xxxx */ - /* LDR (immediate) 1111 1000 1101 xxxx xxxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xffe00000, 0xf8c00000, PROBES_T32_LDRSTR, - REGS(NOPCX, ANY, 0, 0, 0)), - - /* STR (register) 1111 1000 0100 xxxx xxxx 0000 00xx xxxx */ - /* LDR (register) 1111 1000 0101 xxxx xxxx 0000 00xx xxxx */ - DECODE_EMULATEX (0xffe00fc0, 0xf8400000, PROBES_T32_LDRSTR, - REGS(NOPCX, ANY, 0, 0, NOSPPC)), - - /* LDRB (literal) 1111 1000 x001 1111 xxxx xxxx xxxx xxxx */ - /* LDRSB (literal) 1111 1001 x001 1111 xxxx xxxx xxxx xxxx */ - /* LDRH (literal) 1111 1000 x011 1111 xxxx xxxx xxxx xxxx */ - /* LDRSH (literal) 1111 1001 x011 1111 xxxx xxxx xxxx xxxx */ - DECODE_SIMULATEX(0xfe5f0000, 0xf81f0000, PROBES_T32_LDR_LIT, - REGS(PC, NOSPPCX, 0, 0, 0)), - - /* STRB (immediate) 1111 1000 0000 xxxx xxxx 1xxx xxxx xxxx */ - /* STRH (immediate) 1111 1000 0010 xxxx xxxx 1xxx xxxx xxxx */ - /* LDRB (immediate) 1111 1000 0001 xxxx xxxx 1xxx xxxx xxxx */ - /* LDRSB (immediate) 1111 1001 0001 xxxx xxxx 1xxx xxxx xxxx */ - /* LDRH (immediate) 1111 1000 0011 xxxx xxxx 1xxx xxxx xxxx */ - /* LDRSH (immediate) 1111 1001 0011 xxxx xxxx 1xxx xxxx xxxx */ - DECODE_OR (0xfec00800, 0xf8000800), - /* STRB (immediate) 1111 1000 1000 xxxx xxxx xxxx xxxx xxxx */ - /* STRH (immediate) 1111 1000 1010 xxxx xxxx xxxx xxxx xxxx */ - /* LDRB (immediate) 1111 1000 1001 xxxx xxxx xxxx xxxx xxxx */ - /* LDRSB (immediate) 1111 1001 1001 xxxx xxxx xxxx xxxx xxxx */ - /* LDRH (immediate) 1111 1000 1011 xxxx xxxx xxxx xxxx xxxx */ - /* LDRSH (immediate) 1111 1001 1011 xxxx xxxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0xfec00000, 0xf8800000, PROBES_T32_LDRSTR, - REGS(NOPCX, NOSPPCX, 0, 0, 0)), - - /* STRB (register) 1111 1000 0000 xxxx xxxx 0000 00xx xxxx */ - /* STRH (register) 1111 1000 0010 xxxx xxxx 0000 00xx xxxx */ - /* LDRB (register) 1111 1000 0001 xxxx xxxx 0000 00xx xxxx */ - /* LDRSB (register) 1111 1001 0001 xxxx xxxx 0000 00xx xxxx */ - /* LDRH (register) 1111 1000 0011 xxxx xxxx 0000 00xx xxxx */ - /* LDRSH (register) 1111 1001 0011 xxxx xxxx 0000 00xx xxxx */ - DECODE_EMULATEX (0xfe800fc0, 0xf8000000, PROBES_T32_LDRSTR, - REGS(NOPCX, NOSPPCX, 0, 0, NOSPPC)), - - /* Other unallocated instructions... */ - DECODE_END -}; - -static const union decode_item t32_table_1111_1010___1111[] = { - /* Data-processing (register) */ - - /* ??? 1111 1010 011x xxxx 1111 xxxx 1xxx xxxx */ - DECODE_REJECT (0xffe0f080, 0xfa60f080), - - /* SXTH 1111 1010 0000 1111 1111 xxxx 1xxx xxxx */ - /* UXTH 1111 1010 0001 1111 1111 xxxx 1xxx xxxx */ - /* SXTB16 1111 1010 0010 1111 1111 xxxx 1xxx xxxx */ - /* UXTB16 1111 1010 0011 1111 1111 xxxx 1xxx xxxx */ - /* SXTB 1111 1010 0100 1111 1111 xxxx 1xxx xxxx */ - /* UXTB 1111 1010 0101 1111 1111 xxxx 1xxx xxxx */ - DECODE_EMULATEX (0xff8ff080, 0xfa0ff080, PROBES_T32_SIGN_EXTEND, - REGS(0, 0, NOSPPC, 0, NOSPPC)), - - - /* ??? 1111 1010 1xxx xxxx 1111 xxxx 0x11 xxxx */ - DECODE_REJECT (0xff80f0b0, 0xfa80f030), - /* ??? 1111 1010 1x11 xxxx 1111 xxxx 0xxx xxxx */ - DECODE_REJECT (0xffb0f080, 0xfab0f000), - - /* SADD16 1111 1010 1001 xxxx 1111 xxxx 0000 xxxx */ - /* SASX 1111 1010 1010 xxxx 1111 xxxx 0000 xxxx */ - /* SSAX 1111 1010 1110 xxxx 1111 xxxx 0000 xxxx */ - /* SSUB16 1111 1010 1101 xxxx 1111 xxxx 0000 xxxx */ - /* SADD8 1111 1010 1000 xxxx 1111 xxxx 0000 xxxx */ - /* SSUB8 1111 1010 1100 xxxx 1111 xxxx 0000 xxxx */ - - /* QADD16 1111 1010 1001 xxxx 1111 xxxx 0001 xxxx */ - /* QASX 1111 1010 1010 xxxx 1111 xxxx 0001 xxxx */ - /* QSAX 1111 1010 1110 xxxx 1111 xxxx 0001 xxxx */ - /* QSUB16 1111 1010 1101 xxxx 1111 xxxx 0001 xxxx */ - /* QADD8 1111 1010 1000 xxxx 1111 xxxx 0001 xxxx */ - /* QSUB8 1111 1010 1100 xxxx 1111 xxxx 0001 xxxx */ - - /* SHADD16 1111 1010 1001 xxxx 1111 xxxx 0010 xxxx */ - /* SHASX 1111 1010 1010 xxxx 1111 xxxx 0010 xxxx */ - /* SHSAX 1111 1010 1110 xxxx 1111 xxxx 0010 xxxx */ - /* SHSUB16 1111 1010 1101 xxxx 1111 xxxx 0010 xxxx */ - /* SHADD8 1111 1010 1000 xxxx 1111 xxxx 0010 xxxx */ - /* SHSUB8 1111 1010 1100 xxxx 1111 xxxx 0010 xxxx */ - - /* UADD16 1111 1010 1001 xxxx 1111 xxxx 0100 xxxx */ - /* UASX 1111 1010 1010 xxxx 1111 xxxx 0100 xxxx */ - /* USAX 1111 1010 1110 xxxx 1111 xxxx 0100 xxxx */ - /* USUB16 1111 1010 1101 xxxx 1111 xxxx 0100 xxxx */ - /* UADD8 1111 1010 1000 xxxx 1111 xxxx 0100 xxxx */ - /* USUB8 1111 1010 1100 xxxx 1111 xxxx 0100 xxxx */ - - /* UQADD16 1111 1010 1001 xxxx 1111 xxxx 0101 xxxx */ - /* UQASX 1111 1010 1010 xxxx 1111 xxxx 0101 xxxx */ - /* UQSAX 1111 1010 1110 xxxx 1111 xxxx 0101 xxxx */ - /* UQSUB16 1111 1010 1101 xxxx 1111 xxxx 0101 xxxx */ - /* UQADD8 1111 1010 1000 xxxx 1111 xxxx 0101 xxxx */ - /* UQSUB8 1111 1010 1100 xxxx 1111 xxxx 0101 xxxx */ - - /* UHADD16 1111 1010 1001 xxxx 1111 xxxx 0110 xxxx */ - /* UHASX 1111 1010 1010 xxxx 1111 xxxx 0110 xxxx */ - /* UHSAX 1111 1010 1110 xxxx 1111 xxxx 0110 xxxx */ - /* UHSUB16 1111 1010 1101 xxxx 1111 xxxx 0110 xxxx */ - /* UHADD8 1111 1010 1000 xxxx 1111 xxxx 0110 xxxx */ - /* UHSUB8 1111 1010 1100 xxxx 1111 xxxx 0110 xxxx */ - DECODE_OR (0xff80f080, 0xfa80f000), - - /* SXTAH 1111 1010 0000 xxxx 1111 xxxx 1xxx xxxx */ - /* UXTAH 1111 1010 0001 xxxx 1111 xxxx 1xxx xxxx */ - /* SXTAB16 1111 1010 0010 xxxx 1111 xxxx 1xxx xxxx */ - /* UXTAB16 1111 1010 0011 xxxx 1111 xxxx 1xxx xxxx */ - /* SXTAB 1111 1010 0100 xxxx 1111 xxxx 1xxx xxxx */ - /* UXTAB 1111 1010 0101 xxxx 1111 xxxx 1xxx xxxx */ - DECODE_OR (0xff80f080, 0xfa00f080), - - /* QADD 1111 1010 1000 xxxx 1111 xxxx 1000 xxxx */ - /* QDADD 1111 1010 1000 xxxx 1111 xxxx 1001 xxxx */ - /* QSUB 1111 1010 1000 xxxx 1111 xxxx 1010 xxxx */ - /* QDSUB 1111 1010 1000 xxxx 1111 xxxx 1011 xxxx */ - DECODE_OR (0xfff0f0c0, 0xfa80f080), - - /* SEL 1111 1010 1010 xxxx 1111 xxxx 1000 xxxx */ - DECODE_OR (0xfff0f0f0, 0xfaa0f080), - - /* LSL 1111 1010 000x xxxx 1111 xxxx 0000 xxxx */ - /* LSR 1111 1010 001x xxxx 1111 xxxx 0000 xxxx */ - /* ASR 1111 1010 010x xxxx 1111 xxxx 0000 xxxx */ - /* ROR 1111 1010 011x xxxx 1111 xxxx 0000 xxxx */ - DECODE_EMULATEX (0xff80f0f0, 0xfa00f000, PROBES_T32_MEDIA, - REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)), - - /* CLZ 1111 1010 1010 xxxx 1111 xxxx 1000 xxxx */ - DECODE_OR (0xfff0f0f0, 0xfab0f080), - - /* REV 1111 1010 1001 xxxx 1111 xxxx 1000 xxxx */ - /* REV16 1111 1010 1001 xxxx 1111 xxxx 1001 xxxx */ - /* RBIT 1111 1010 1001 xxxx 1111 xxxx 1010 xxxx */ - /* REVSH 1111 1010 1001 xxxx 1111 xxxx 1011 xxxx */ - DECODE_EMULATEX (0xfff0f0c0, 0xfa90f080, PROBES_T32_REVERSE, - REGS(NOSPPC, 0, NOSPPC, 0, SAMEAS16)), - - /* Other unallocated instructions... */ - DECODE_END -}; - -static const union decode_item t32_table_1111_1011_0[] = { - /* Multiply, multiply accumulate, and absolute difference */ - - /* ??? 1111 1011 0000 xxxx 1111 xxxx 0001 xxxx */ - DECODE_REJECT (0xfff0f0f0, 0xfb00f010), - /* ??? 1111 1011 0111 xxxx 1111 xxxx 0001 xxxx */ - DECODE_REJECT (0xfff0f0f0, 0xfb70f010), - - /* SMULxy 1111 1011 0001 xxxx 1111 xxxx 00xx xxxx */ - DECODE_OR (0xfff0f0c0, 0xfb10f000), - /* MUL 1111 1011 0000 xxxx 1111 xxxx 0000 xxxx */ - /* SMUAD{X} 1111 1011 0010 xxxx 1111 xxxx 000x xxxx */ - /* SMULWy 1111 1011 0011 xxxx 1111 xxxx 000x xxxx */ - /* SMUSD{X} 1111 1011 0100 xxxx 1111 xxxx 000x xxxx */ - /* SMMUL{R} 1111 1011 0101 xxxx 1111 xxxx 000x xxxx */ - /* USAD8 1111 1011 0111 xxxx 1111 xxxx 0000 xxxx */ - DECODE_EMULATEX (0xff80f0e0, 0xfb00f000, PROBES_T32_MUL_ADD, - REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)), - - /* ??? 1111 1011 0111 xxxx xxxx xxxx 0001 xxxx */ - DECODE_REJECT (0xfff000f0, 0xfb700010), - - /* SMLAxy 1111 1011 0001 xxxx xxxx xxxx 00xx xxxx */ - DECODE_OR (0xfff000c0, 0xfb100000), - /* MLA 1111 1011 0000 xxxx xxxx xxxx 0000 xxxx */ - /* MLS 1111 1011 0000 xxxx xxxx xxxx 0001 xxxx */ - /* SMLAD{X} 1111 1011 0010 xxxx xxxx xxxx 000x xxxx */ - /* SMLAWy 1111 1011 0011 xxxx xxxx xxxx 000x xxxx */ - /* SMLSD{X} 1111 1011 0100 xxxx xxxx xxxx 000x xxxx */ - /* SMMLA{R} 1111 1011 0101 xxxx xxxx xxxx 000x xxxx */ - /* SMMLS{R} 1111 1011 0110 xxxx xxxx xxxx 000x xxxx */ - /* USADA8 1111 1011 0111 xxxx xxxx xxxx 0000 xxxx */ - DECODE_EMULATEX (0xff8000c0, 0xfb000000, PROBES_T32_MUL_ADD2, - REGS(NOSPPC, NOSPPCX, NOSPPC, 0, NOSPPC)), - - /* Other unallocated instructions... */ - DECODE_END -}; - -static const union decode_item t32_table_1111_1011_1[] = { - /* Long multiply, long multiply accumulate, and divide */ - - /* UMAAL 1111 1011 1110 xxxx xxxx xxxx 0110 xxxx */ - DECODE_OR (0xfff000f0, 0xfbe00060), - /* SMLALxy 1111 1011 1100 xxxx xxxx xxxx 10xx xxxx */ - DECODE_OR (0xfff000c0, 0xfbc00080), - /* SMLALD{X} 1111 1011 1100 xxxx xxxx xxxx 110x xxxx */ - /* SMLSLD{X} 1111 1011 1101 xxxx xxxx xxxx 110x xxxx */ - DECODE_OR (0xffe000e0, 0xfbc000c0), - /* SMULL 1111 1011 1000 xxxx xxxx xxxx 0000 xxxx */ - /* UMULL 1111 1011 1010 xxxx xxxx xxxx 0000 xxxx */ - /* SMLAL 1111 1011 1100 xxxx xxxx xxxx 0000 xxxx */ - /* UMLAL 1111 1011 1110 xxxx xxxx xxxx 0000 xxxx */ - DECODE_EMULATEX (0xff9000f0, 0xfb800000, PROBES_T32_MUL_ADD_LONG, - REGS(NOSPPC, NOSPPC, NOSPPC, 0, NOSPPC)), - - /* SDIV 1111 1011 1001 xxxx xxxx xxxx 1111 xxxx */ - /* UDIV 1111 1011 1011 xxxx xxxx xxxx 1111 xxxx */ - /* Other unallocated instructions... */ - DECODE_END -}; - -const union decode_item probes_decode_thumb32_table[] = { - - /* - * Load/store multiple instructions - * 1110 100x x0xx xxxx xxxx xxxx xxxx xxxx - */ - DECODE_TABLE (0xfe400000, 0xe8000000, t32_table_1110_100x_x0xx), - - /* - * Load/store dual, load/store exclusive, table branch - * 1110 100x x1xx xxxx xxxx xxxx xxxx xxxx - */ - DECODE_TABLE (0xfe400000, 0xe8400000, t32_table_1110_100x_x1xx), - - /* - * Data-processing (shifted register) - * 1110 101x xxxx xxxx xxxx xxxx xxxx xxxx - */ - DECODE_TABLE (0xfe000000, 0xea000000, t32_table_1110_101x), - - /* - * Coprocessor instructions - * 1110 11xx xxxx xxxx xxxx xxxx xxxx xxxx - */ - DECODE_REJECT (0xfc000000, 0xec000000), - - /* - * Data-processing (modified immediate) - * 1111 0x0x xxxx xxxx 0xxx xxxx xxxx xxxx - */ - DECODE_TABLE (0xfa008000, 0xf0000000, t32_table_1111_0x0x___0), - - /* - * Data-processing (plain binary immediate) - * 1111 0x1x xxxx xxxx 0xxx xxxx xxxx xxxx - */ - DECODE_TABLE (0xfa008000, 0xf2000000, t32_table_1111_0x1x___0), - - /* - * Branches and miscellaneous control - * 1111 0xxx xxxx xxxx 1xxx xxxx xxxx xxxx - */ - DECODE_TABLE (0xf8008000, 0xf0008000, t32_table_1111_0xxx___1), - - /* - * Advanced SIMD element or structure load/store instructions - * 1111 1001 xxx0 xxxx xxxx xxxx xxxx xxxx - */ - DECODE_REJECT (0xff100000, 0xf9000000), - - /* - * Memory hints - * 1111 100x x0x1 xxxx 1111 xxxx xxxx xxxx - */ - DECODE_TABLE (0xfe50f000, 0xf810f000, t32_table_1111_100x_x0x1__1111), - - /* - * Store single data item - * 1111 1000 xxx0 xxxx xxxx xxxx xxxx xxxx - * Load single data items - * 1111 100x xxx1 xxxx xxxx xxxx xxxx xxxx - */ - DECODE_TABLE (0xfe000000, 0xf8000000, t32_table_1111_100x), - - /* - * Data-processing (register) - * 1111 1010 xxxx xxxx 1111 xxxx xxxx xxxx - */ - DECODE_TABLE (0xff00f000, 0xfa00f000, t32_table_1111_1010___1111), - - /* - * Multiply, multiply accumulate, and absolute difference - * 1111 1011 0xxx xxxx xxxx xxxx xxxx xxxx - */ - DECODE_TABLE (0xff800000, 0xfb000000, t32_table_1111_1011_0), - - /* - * Long multiply, long multiply accumulate, and divide - * 1111 1011 1xxx xxxx xxxx xxxx xxxx xxxx - */ - DECODE_TABLE (0xff800000, 0xfb800000, t32_table_1111_1011_1), - - /* - * Coprocessor instructions - * 1111 11xx xxxx xxxx xxxx xxxx xxxx xxxx - */ - DECODE_END -}; -#ifdef CONFIG_ARM_KPROBES_TEST_MODULE -EXPORT_SYMBOL_GPL(probes_decode_thumb32_table); -#endif - -static const union decode_item t16_table_1011[] = { - /* Miscellaneous 16-bit instructions */ - - /* ADD (SP plus immediate) 1011 0000 0xxx xxxx */ - /* SUB (SP minus immediate) 1011 0000 1xxx xxxx */ - DECODE_SIMULATE (0xff00, 0xb000, PROBES_T16_ADD_SP), - - /* CBZ 1011 00x1 xxxx xxxx */ - /* CBNZ 1011 10x1 xxxx xxxx */ - DECODE_SIMULATE (0xf500, 0xb100, PROBES_T16_CBZ), - - /* SXTH 1011 0010 00xx xxxx */ - /* SXTB 1011 0010 01xx xxxx */ - /* UXTH 1011 0010 10xx xxxx */ - /* UXTB 1011 0010 11xx xxxx */ - /* REV 1011 1010 00xx xxxx */ - /* REV16 1011 1010 01xx xxxx */ - /* ??? 1011 1010 10xx xxxx */ - /* REVSH 1011 1010 11xx xxxx */ - DECODE_REJECT (0xffc0, 0xba80), - DECODE_EMULATE (0xf500, 0xb000, PROBES_T16_SIGN_EXTEND), - - /* PUSH 1011 010x xxxx xxxx */ - DECODE_CUSTOM (0xfe00, 0xb400, PROBES_T16_PUSH), - /* POP 1011 110x xxxx xxxx */ - DECODE_CUSTOM (0xfe00, 0xbc00, PROBES_T16_POP), - - /* - * If-Then, and hints - * 1011 1111 xxxx xxxx - */ - - /* YIELD 1011 1111 0001 0000 */ - DECODE_OR (0xffff, 0xbf10), - /* SEV 1011 1111 0100 0000 */ - DECODE_EMULATE (0xffff, 0xbf40, PROBES_T16_SEV), - /* NOP 1011 1111 0000 0000 */ - /* WFE 1011 1111 0010 0000 */ - /* WFI 1011 1111 0011 0000 */ - DECODE_SIMULATE (0xffcf, 0xbf00, PROBES_T16_WFE), - /* Unassigned hints 1011 1111 xxxx 0000 */ - DECODE_REJECT (0xff0f, 0xbf00), - /* IT 1011 1111 xxxx xxxx */ - DECODE_CUSTOM (0xff00, 0xbf00, PROBES_T16_IT), - - /* SETEND 1011 0110 010x xxxx */ - /* CPS 1011 0110 011x xxxx */ - /* BKPT 1011 1110 xxxx xxxx */ - /* And unallocated instructions... */ - DECODE_END -}; - -const union decode_item probes_decode_thumb16_table[] = { - - /* - * Shift (immediate), add, subtract, move, and compare - * 00xx xxxx xxxx xxxx - */ - - /* CMP (immediate) 0010 1xxx xxxx xxxx */ - DECODE_EMULATE (0xf800, 0x2800, PROBES_T16_CMP), - - /* ADD (register) 0001 100x xxxx xxxx */ - /* SUB (register) 0001 101x xxxx xxxx */ - /* LSL (immediate) 0000 0xxx xxxx xxxx */ - /* LSR (immediate) 0000 1xxx xxxx xxxx */ - /* ASR (immediate) 0001 0xxx xxxx xxxx */ - /* ADD (immediate, Thumb) 0001 110x xxxx xxxx */ - /* SUB (immediate, Thumb) 0001 111x xxxx xxxx */ - /* MOV (immediate) 0010 0xxx xxxx xxxx */ - /* ADD (immediate, Thumb) 0011 0xxx xxxx xxxx */ - /* SUB (immediate, Thumb) 0011 1xxx xxxx xxxx */ - DECODE_EMULATE (0xc000, 0x0000, PROBES_T16_ADDSUB), - - /* - * 16-bit Thumb data-processing instructions - * 0100 00xx xxxx xxxx - */ - - /* TST (register) 0100 0010 00xx xxxx */ - DECODE_EMULATE (0xffc0, 0x4200, PROBES_T16_CMP), - /* CMP (register) 0100 0010 10xx xxxx */ - /* CMN (register) 0100 0010 11xx xxxx */ - DECODE_EMULATE (0xff80, 0x4280, PROBES_T16_CMP), - /* AND (register) 0100 0000 00xx xxxx */ - /* EOR (register) 0100 0000 01xx xxxx */ - /* LSL (register) 0100 0000 10xx xxxx */ - /* LSR (register) 0100 0000 11xx xxxx */ - /* ASR (register) 0100 0001 00xx xxxx */ - /* ADC (register) 0100 0001 01xx xxxx */ - /* SBC (register) 0100 0001 10xx xxxx */ - /* ROR (register) 0100 0001 11xx xxxx */ - /* RSB (immediate) 0100 0010 01xx xxxx */ - /* ORR (register) 0100 0011 00xx xxxx */ - /* MUL 0100 0011 00xx xxxx */ - /* BIC (register) 0100 0011 10xx xxxx */ - /* MVN (register) 0100 0011 10xx xxxx */ - DECODE_EMULATE (0xfc00, 0x4000, PROBES_T16_LOGICAL), - - /* - * Special data instructions and branch and exchange - * 0100 01xx xxxx xxxx - */ - - /* BLX pc 0100 0111 1111 1xxx */ - DECODE_REJECT (0xfff8, 0x47f8), - - /* BX (register) 0100 0111 0xxx xxxx */ - /* BLX (register) 0100 0111 1xxx xxxx */ - DECODE_SIMULATE (0xff00, 0x4700, PROBES_T16_BLX), - - /* ADD pc, pc 0100 0100 1111 1111 */ - DECODE_REJECT (0xffff, 0x44ff), - - /* ADD (register) 0100 0100 xxxx xxxx */ - /* CMP (register) 0100 0101 xxxx xxxx */ - /* MOV (register) 0100 0110 xxxx xxxx */ - DECODE_CUSTOM (0xfc00, 0x4400, PROBES_T16_HIREGOPS), - - /* - * Load from Literal Pool - * LDR (literal) 0100 1xxx xxxx xxxx - */ - DECODE_SIMULATE (0xf800, 0x4800, PROBES_T16_LDR_LIT), - - /* - * 16-bit Thumb Load/store instructions - * 0101 xxxx xxxx xxxx - * 011x xxxx xxxx xxxx - * 100x xxxx xxxx xxxx - */ - - /* STR (register) 0101 000x xxxx xxxx */ - /* STRH (register) 0101 001x xxxx xxxx */ - /* STRB (register) 0101 010x xxxx xxxx */ - /* LDRSB (register) 0101 011x xxxx xxxx */ - /* LDR (register) 0101 100x xxxx xxxx */ - /* LDRH (register) 0101 101x xxxx xxxx */ - /* LDRB (register) 0101 110x xxxx xxxx */ - /* LDRSH (register) 0101 111x xxxx xxxx */ - /* STR (immediate, Thumb) 0110 0xxx xxxx xxxx */ - /* LDR (immediate, Thumb) 0110 1xxx xxxx xxxx */ - /* STRB (immediate, Thumb) 0111 0xxx xxxx xxxx */ - /* LDRB (immediate, Thumb) 0111 1xxx xxxx xxxx */ - DECODE_EMULATE (0xc000, 0x4000, PROBES_T16_LDRHSTRH), - /* STRH (immediate, Thumb) 1000 0xxx xxxx xxxx */ - /* LDRH (immediate, Thumb) 1000 1xxx xxxx xxxx */ - DECODE_EMULATE (0xf000, 0x8000, PROBES_T16_LDRHSTRH), - /* STR (immediate, Thumb) 1001 0xxx xxxx xxxx */ - /* LDR (immediate, Thumb) 1001 1xxx xxxx xxxx */ - DECODE_SIMULATE (0xf000, 0x9000, PROBES_T16_LDRSTR), - - /* - * Generate PC-/SP-relative address - * ADR (literal) 1010 0xxx xxxx xxxx - * ADD (SP plus immediate) 1010 1xxx xxxx xxxx - */ - DECODE_SIMULATE (0xf000, 0xa000, PROBES_T16_ADR), - - /* - * Miscellaneous 16-bit instructions - * 1011 xxxx xxxx xxxx - */ - DECODE_TABLE (0xf000, 0xb000, t16_table_1011), - - /* STM 1100 0xxx xxxx xxxx */ - /* LDM 1100 1xxx xxxx xxxx */ - DECODE_EMULATE (0xf000, 0xc000, PROBES_T16_LDMSTM), - - /* - * Conditional branch, and Supervisor Call - */ - - /* Permanently UNDEFINED 1101 1110 xxxx xxxx */ - /* SVC 1101 1111 xxxx xxxx */ - DECODE_REJECT (0xfe00, 0xde00), - - /* Conditional branch 1101 xxxx xxxx xxxx */ - DECODE_CUSTOM (0xf000, 0xd000, PROBES_T16_BRANCH_COND), - - /* - * Unconditional branch - * B 1110 0xxx xxxx xxxx - */ - DECODE_SIMULATE (0xf800, 0xe000, PROBES_T16_BRANCH), - - DECODE_END -}; -#ifdef CONFIG_ARM_KPROBES_TEST_MODULE -EXPORT_SYMBOL_GPL(probes_decode_thumb16_table); -#endif - -static unsigned long __kprobes thumb_check_cc(unsigned long cpsr) -{ - if (unlikely(in_it_block(cpsr))) - return probes_condition_checks[current_cond(cpsr)](cpsr); - return true; -} - -static void __kprobes thumb16_singlestep(probes_opcode_t opcode, - struct arch_probes_insn *asi, - struct pt_regs *regs) -{ - regs->ARM_pc += 2; - asi->insn_handler(opcode, asi, regs); - regs->ARM_cpsr = it_advance(regs->ARM_cpsr); -} - -static void __kprobes thumb32_singlestep(probes_opcode_t opcode, - struct arch_probes_insn *asi, - struct pt_regs *regs) -{ - regs->ARM_pc += 4; - asi->insn_handler(opcode, asi, regs); - regs->ARM_cpsr = it_advance(regs->ARM_cpsr); -} - -enum probes_insn __kprobes -thumb16_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, - bool emulate, const union decode_action *actions) -{ - asi->insn_singlestep = thumb16_singlestep; - asi->insn_check_cc = thumb_check_cc; - return probes_decode_insn(insn, asi, probes_decode_thumb16_table, true, - emulate, actions); -} - -enum probes_insn __kprobes -thumb32_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, - bool emulate, const union decode_action *actions) -{ - asi->insn_singlestep = thumb32_singlestep; - asi->insn_check_cc = thumb_check_cc; - return probes_decode_insn(insn, asi, probes_decode_thumb32_table, true, - emulate, actions); -} diff --git a/arch/arm/kernel/probes-thumb.h b/arch/arm/kernel/probes-thumb.h deleted file mode 100644 index 7c6f6eb..0000000 --- a/arch/arm/kernel/probes-thumb.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * arch/arm/kernel/probes-thumb.h - * - * Copyright 2013 Linaro Ltd. - * Written by: David A. Long - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ - -#ifndef _ARM_KERNEL_PROBES_THUMB_H -#define _ARM_KERNEL_PROBES_THUMB_H - -/* - * True if current instruction is in an IT block. - */ -#define in_it_block(cpsr) ((cpsr & 0x06000c00) != 0x00000000) - -/* - * Return the condition code to check for the currently executing instruction. - * This is in ITSTATE<7:4> which is in CPSR<15:12> but is only valid if - * in_it_block returns true. - */ -#define current_cond(cpsr) ((cpsr >> 12) & 0xf) - -enum probes_t32_action { - PROBES_T32_EMULATE_NONE, - PROBES_T32_SIMULATE_NOP, - PROBES_T32_LDMSTM, - PROBES_T32_LDRDSTRD, - PROBES_T32_TABLE_BRANCH, - PROBES_T32_TST, - PROBES_T32_CMP, - PROBES_T32_MOV, - PROBES_T32_ADDSUB, - PROBES_T32_LOGICAL, - PROBES_T32_ADDWSUBW_PC, - PROBES_T32_ADDWSUBW, - PROBES_T32_MOVW, - PROBES_T32_SAT, - PROBES_T32_BITFIELD, - PROBES_T32_SEV, - PROBES_T32_WFE, - PROBES_T32_MRS, - PROBES_T32_BRANCH_COND, - PROBES_T32_BRANCH, - PROBES_T32_PLDI, - PROBES_T32_LDR_LIT, - PROBES_T32_LDRSTR, - PROBES_T32_SIGN_EXTEND, - PROBES_T32_MEDIA, - PROBES_T32_REVERSE, - PROBES_T32_MUL_ADD, - PROBES_T32_MUL_ADD2, - PROBES_T32_MUL_ADD_LONG, - NUM_PROBES_T32_ACTIONS -}; - -enum probes_t16_action { - PROBES_T16_ADD_SP, - PROBES_T16_CBZ, - PROBES_T16_SIGN_EXTEND, - PROBES_T16_PUSH, - PROBES_T16_POP, - PROBES_T16_SEV, - PROBES_T16_WFE, - PROBES_T16_IT, - PROBES_T16_CMP, - PROBES_T16_ADDSUB, - PROBES_T16_LOGICAL, - PROBES_T16_BLX, - PROBES_T16_HIREGOPS, - PROBES_T16_LDR_LIT, - PROBES_T16_LDRHSTRH, - PROBES_T16_LDRSTR, - PROBES_T16_ADR, - PROBES_T16_LDMSTM, - PROBES_T16_BRANCH_COND, - PROBES_T16_BRANCH, - NUM_PROBES_T16_ACTIONS -}; - -extern const union decode_item probes_decode_thumb32_table[]; -extern const union decode_item probes_decode_thumb16_table[]; - -enum probes_insn __kprobes -thumb16_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, - bool emulate, const union decode_action *actions); -enum probes_insn __kprobes -thumb32_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, - bool emulate, const union decode_action *actions); - -#endif diff --git a/arch/arm/kernel/probes.c b/arch/arm/kernel/probes.c deleted file mode 100644 index a8ab540..0000000 --- a/arch/arm/kernel/probes.c +++ /dev/null @@ -1,456 +0,0 @@ -/* - * arch/arm/kernel/probes.c - * - * Copyright (C) 2011 Jon Medhurst . - * - * Some contents moved here from arch/arm/include/asm/kprobes-arm.c which is - * Copyright (C) 2006, 2007 Motorola Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include - -#include "probes.h" - - -#ifndef find_str_pc_offset - -/* - * For STR and STM instructions, an ARM core may choose to use either - * a +8 or a +12 displacement from the current instruction's address. - * Whichever value is chosen for a given core, it must be the same for - * both instructions and may not change. This function measures it. - */ - -int str_pc_offset; - -void __init find_str_pc_offset(void) -{ - int addr, scratch, ret; - - __asm__ ( - "sub %[ret], pc, #4 \n\t" - "str pc, %[addr] \n\t" - "ldr %[scr], %[addr] \n\t" - "sub %[ret], %[scr], %[ret] \n\t" - : [ret] "=r" (ret), [scr] "=r" (scratch), [addr] "+m" (addr)); - - str_pc_offset = ret; -} - -#endif /* !find_str_pc_offset */ - - -#ifndef test_load_write_pc_interworking - -bool load_write_pc_interworks; - -void __init test_load_write_pc_interworking(void) -{ - int arch = cpu_architecture(); - BUG_ON(arch == CPU_ARCH_UNKNOWN); - load_write_pc_interworks = arch >= CPU_ARCH_ARMv5T; -} - -#endif /* !test_load_write_pc_interworking */ - - -#ifndef test_alu_write_pc_interworking - -bool alu_write_pc_interworks; - -void __init test_alu_write_pc_interworking(void) -{ - int arch = cpu_architecture(); - BUG_ON(arch == CPU_ARCH_UNKNOWN); - alu_write_pc_interworks = arch >= CPU_ARCH_ARMv7; -} - -#endif /* !test_alu_write_pc_interworking */ - - -void __init arm_probes_decode_init(void) -{ - find_str_pc_offset(); - test_load_write_pc_interworking(); - test_alu_write_pc_interworking(); -} - - -static unsigned long __kprobes __check_eq(unsigned long cpsr) -{ - return cpsr & PSR_Z_BIT; -} - -static unsigned long __kprobes __check_ne(unsigned long cpsr) -{ - return (~cpsr) & PSR_Z_BIT; -} - -static unsigned long __kprobes __check_cs(unsigned long cpsr) -{ - return cpsr & PSR_C_BIT; -} - -static unsigned long __kprobes __check_cc(unsigned long cpsr) -{ - return (~cpsr) & PSR_C_BIT; -} - -static unsigned long __kprobes __check_mi(unsigned long cpsr) -{ - return cpsr & PSR_N_BIT; -} - -static unsigned long __kprobes __check_pl(unsigned long cpsr) -{ - return (~cpsr) & PSR_N_BIT; -} - -static unsigned long __kprobes __check_vs(unsigned long cpsr) -{ - return cpsr & PSR_V_BIT; -} - -static unsigned long __kprobes __check_vc(unsigned long cpsr) -{ - return (~cpsr) & PSR_V_BIT; -} - -static unsigned long __kprobes __check_hi(unsigned long cpsr) -{ - cpsr &= ~(cpsr >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */ - return cpsr & PSR_C_BIT; -} - -static unsigned long __kprobes __check_ls(unsigned long cpsr) -{ - cpsr &= ~(cpsr >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */ - return (~cpsr) & PSR_C_BIT; -} - -static unsigned long __kprobes __check_ge(unsigned long cpsr) -{ - cpsr ^= (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */ - return (~cpsr) & PSR_N_BIT; -} - -static unsigned long __kprobes __check_lt(unsigned long cpsr) -{ - cpsr ^= (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */ - return cpsr & PSR_N_BIT; -} - -static unsigned long __kprobes __check_gt(unsigned long cpsr) -{ - unsigned long temp = cpsr ^ (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */ - temp |= (cpsr << 1); /* PSR_N_BIT |= PSR_Z_BIT */ - return (~temp) & PSR_N_BIT; -} - -static unsigned long __kprobes __check_le(unsigned long cpsr) -{ - unsigned long temp = cpsr ^ (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */ - temp |= (cpsr << 1); /* PSR_N_BIT |= PSR_Z_BIT */ - return temp & PSR_N_BIT; -} - -static unsigned long __kprobes __check_al(unsigned long cpsr) -{ - return true; -} - -probes_check_cc * const probes_condition_checks[16] = { - &__check_eq, &__check_ne, &__check_cs, &__check_cc, - &__check_mi, &__check_pl, &__check_vs, &__check_vc, - &__check_hi, &__check_ls, &__check_ge, &__check_lt, - &__check_gt, &__check_le, &__check_al, &__check_al -}; - - -void __kprobes probes_simulate_nop(probes_opcode_t opcode, - struct arch_probes_insn *asi, - struct pt_regs *regs) -{ -} - -void __kprobes probes_emulate_none(probes_opcode_t opcode, - struct arch_probes_insn *asi, - struct pt_regs *regs) -{ - asi->insn_fn(); -} - -/* - * Prepare an instruction slot to receive an instruction for emulating. - * This is done by placing a subroutine return after the location where the - * instruction will be placed. We also modify ARM instructions to be - * unconditional as the condition code will already be checked before any - * emulation handler is called. - */ -static probes_opcode_t __kprobes -prepare_emulated_insn(probes_opcode_t insn, struct arch_probes_insn *asi, - bool thumb) -{ -#ifdef CONFIG_THUMB2_KERNEL - if (thumb) { - u16 *thumb_insn = (u16 *)asi->insn; - /* Thumb bx lr */ - thumb_insn[1] = __opcode_to_mem_thumb16(0x4770); - thumb_insn[2] = __opcode_to_mem_thumb16(0x4770); - return insn; - } - asi->insn[1] = __opcode_to_mem_arm(0xe12fff1e); /* ARM bx lr */ -#else - asi->insn[1] = __opcode_to_mem_arm(0xe1a0f00e); /* mov pc, lr */ -#endif - /* Make an ARM instruction unconditional */ - if (insn < 0xe0000000) - insn = (insn | 0xe0000000) & ~0x10000000; - return insn; -} - -/* - * Write a (probably modified) instruction into the slot previously prepared by - * prepare_emulated_insn - */ -static void __kprobes -set_emulated_insn(probes_opcode_t insn, struct arch_probes_insn *asi, - bool thumb) -{ -#ifdef CONFIG_THUMB2_KERNEL - if (thumb) { - u16 *ip = (u16 *)asi->insn; - if (is_wide_instruction(insn)) - *ip++ = __opcode_to_mem_thumb16(insn >> 16); - *ip++ = __opcode_to_mem_thumb16(insn); - return; - } -#endif - asi->insn[0] = __opcode_to_mem_arm(insn); -} - -/* - * When we modify the register numbers encoded in an instruction to be emulated, - * the new values come from this define. For ARM and 32-bit Thumb instructions - * this gives... - * - * bit position 16 12 8 4 0 - * ---------------+---+---+---+---+---+ - * register r2 r0 r1 -- r3 - */ -#define INSN_NEW_BITS 0x00020103 - -/* Each nibble has same value as that at INSN_NEW_BITS bit 16 */ -#define INSN_SAMEAS16_BITS 0x22222222 - -/* - * Validate and modify each of the registers encoded in an instruction. - * - * Each nibble in regs contains a value from enum decode_reg_type. For each - * non-zero value, the corresponding nibble in pinsn is validated and modified - * according to the type. - */ -static bool __kprobes decode_regs(probes_opcode_t *pinsn, u32 regs, bool modify) -{ - probes_opcode_t insn = *pinsn; - probes_opcode_t mask = 0xf; /* Start at least significant nibble */ - - for (; regs != 0; regs >>= 4, mask <<= 4) { - - probes_opcode_t new_bits = INSN_NEW_BITS; - - switch (regs & 0xf) { - - case REG_TYPE_NONE: - /* Nibble not a register, skip to next */ - continue; - - case REG_TYPE_ANY: - /* Any register is allowed */ - break; - - case REG_TYPE_SAMEAS16: - /* Replace register with same as at bit position 16 */ - new_bits = INSN_SAMEAS16_BITS; - break; - - case REG_TYPE_SP: - /* Only allow SP (R13) */ - if ((insn ^ 0xdddddddd) & mask) - goto reject; - break; - - case REG_TYPE_PC: - /* Only allow PC (R15) */ - if ((insn ^ 0xffffffff) & mask) - goto reject; - break; - - case REG_TYPE_NOSP: - /* Reject SP (R13) */ - if (((insn ^ 0xdddddddd) & mask) == 0) - goto reject; - break; - - case REG_TYPE_NOSPPC: - case REG_TYPE_NOSPPCX: - /* Reject SP and PC (R13 and R15) */ - if (((insn ^ 0xdddddddd) & 0xdddddddd & mask) == 0) - goto reject; - break; - - case REG_TYPE_NOPCWB: - if (!is_writeback(insn)) - break; /* No writeback, so any register is OK */ - /* fall through... */ - case REG_TYPE_NOPC: - case REG_TYPE_NOPCX: - /* Reject PC (R15) */ - if (((insn ^ 0xffffffff) & mask) == 0) - goto reject; - break; - } - - /* Replace value of nibble with new register number... */ - insn &= ~mask; - insn |= new_bits & mask; - } - - if (modify) - *pinsn = insn; - - return true; - -reject: - return false; -} - -static const int decode_struct_sizes[NUM_DECODE_TYPES] = { - [DECODE_TYPE_TABLE] = sizeof(struct decode_table), - [DECODE_TYPE_CUSTOM] = sizeof(struct decode_custom), - [DECODE_TYPE_SIMULATE] = sizeof(struct decode_simulate), - [DECODE_TYPE_EMULATE] = sizeof(struct decode_emulate), - [DECODE_TYPE_OR] = sizeof(struct decode_or), - [DECODE_TYPE_REJECT] = sizeof(struct decode_reject) -}; - -/* - * probes_decode_insn operates on data tables in order to decode an ARM - * architecture instruction onto which a kprobe has been placed. - * - * These instruction decoding tables are a concatenation of entries each - * of which consist of one of the following structs: - * - * decode_table - * decode_custom - * decode_simulate - * decode_emulate - * decode_or - * decode_reject - * - * Each of these starts with a struct decode_header which has the following - * fields: - * - * type_regs - * mask - * value - * - * The least significant DECODE_TYPE_BITS of type_regs contains a value - * from enum decode_type, this indicates which of the decode_* structs - * the entry contains. The value DECODE_TYPE_END indicates the end of the - * table. - * - * When the table is parsed, each entry is checked in turn to see if it - * matches the instruction to be decoded using the test: - * - * (insn & mask) == value - * - * If no match is found before the end of the table is reached then decoding - * fails with INSN_REJECTED. - * - * When a match is found, decode_regs() is called to validate and modify each - * of the registers encoded in the instruction; the data it uses to do this - * is (type_regs >> DECODE_TYPE_BITS). A validation failure will cause decoding - * to fail with INSN_REJECTED. - * - * Once the instruction has passed the above tests, further processing - * depends on the type of the table entry's decode struct. - * - */ -int __kprobes -probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, - const union decode_item *table, bool thumb, - bool emulate, const union decode_action *actions) -{ - const struct decode_header *h = (struct decode_header *)table; - const struct decode_header *next; - bool matched = false; - - if (emulate) - insn = prepare_emulated_insn(insn, asi, thumb); - - for (;; h = next) { - enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK; - u32 regs = h->type_regs.bits >> DECODE_TYPE_BITS; - - if (type == DECODE_TYPE_END) - return INSN_REJECTED; - - next = (struct decode_header *) - ((uintptr_t)h + decode_struct_sizes[type]); - - if (!matched && (insn & h->mask.bits) != h->value.bits) - continue; - - if (!decode_regs(&insn, regs, emulate)) - return INSN_REJECTED; - - switch (type) { - - case DECODE_TYPE_TABLE: { - struct decode_table *d = (struct decode_table *)h; - next = (struct decode_header *)d->table.table; - break; - } - - case DECODE_TYPE_CUSTOM: { - struct decode_custom *d = (struct decode_custom *)h; - return actions[d->decoder.action].decoder(insn, asi, h); - } - - case DECODE_TYPE_SIMULATE: { - struct decode_simulate *d = (struct decode_simulate *)h; - asi->insn_handler = actions[d->handler.action].handler; - return INSN_GOOD_NO_SLOT; - } - - case DECODE_TYPE_EMULATE: { - struct decode_emulate *d = (struct decode_emulate *)h; - - if (!emulate) - return actions[d->handler.action].decoder(insn, - asi, h); - - asi->insn_handler = actions[d->handler.action].handler; - set_emulated_insn(insn, asi, thumb); - return INSN_GOOD; - } - - case DECODE_TYPE_OR: - matched = true; - break; - - case DECODE_TYPE_REJECT: - default: - return INSN_REJECTED; - } - } -} diff --git a/arch/arm/kernel/probes.h b/arch/arm/kernel/probes.h deleted file mode 100644 index dba9f24..0000000 --- a/arch/arm/kernel/probes.h +++ /dev/null @@ -1,407 +0,0 @@ -/* - * arch/arm/kernel/probes.h - * - * Copyright (C) 2011 Jon Medhurst . - * - * Some contents moved here from arch/arm/include/asm/kprobes.h which is - * Copyright (C) 2006, 2007 Motorola Inc. - * - * 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. - */ - -#ifndef _ARM_KERNEL_PROBES_H -#define _ARM_KERNEL_PROBES_H - -#include -#include -#include - -void __init arm_probes_decode_init(void); - -extern probes_check_cc * const probes_condition_checks[16]; - -#if __LINUX_ARM_ARCH__ >= 7 - -/* str_pc_offset is architecturally defined from ARMv7 onwards */ -#define str_pc_offset 8 -#define find_str_pc_offset() - -#else /* __LINUX_ARM_ARCH__ < 7 */ - -/* We need a run-time check to determine str_pc_offset */ -extern int str_pc_offset; -void __init find_str_pc_offset(void); - -#endif - - -/* - * Update ITSTATE after normal execution of an IT block instruction. - * - * The 8 IT state bits are split into two parts in CPSR: - * ITSTATE<1:0> are in CPSR<26:25> - * ITSTATE<7:2> are in CPSR<15:10> - */ -static inline unsigned long it_advance(unsigned long cpsr) - { - if ((cpsr & 0x06000400) == 0) { - /* ITSTATE<2:0> == 0 means end of IT block, so clear IT state */ - cpsr &= ~PSR_IT_MASK; - } else { - /* We need to shift left ITSTATE<4:0> */ - const unsigned long mask = 0x06001c00; /* Mask ITSTATE<4:0> */ - unsigned long it = cpsr & mask; - it <<= 1; - it |= it >> (27 - 10); /* Carry ITSTATE<2> to correct place */ - it &= mask; - cpsr &= ~mask; - cpsr |= it; - } - return cpsr; -} - -static inline void __kprobes bx_write_pc(long pcv, struct pt_regs *regs) -{ - long cpsr = regs->ARM_cpsr; - if (pcv & 0x1) { - cpsr |= PSR_T_BIT; - pcv &= ~0x1; - } else { - cpsr &= ~PSR_T_BIT; - pcv &= ~0x2; /* Avoid UNPREDICTABLE address allignment */ - } - regs->ARM_cpsr = cpsr; - regs->ARM_pc = pcv; -} - - -#if __LINUX_ARM_ARCH__ >= 6 - -/* Kernels built for >= ARMv6 should never run on <= ARMv5 hardware, so... */ -#define load_write_pc_interworks true -#define test_load_write_pc_interworking() - -#else /* __LINUX_ARM_ARCH__ < 6 */ - -/* We need run-time testing to determine if load_write_pc() should interwork. */ -extern bool load_write_pc_interworks; -void __init test_load_write_pc_interworking(void); - -#endif - -static inline void __kprobes load_write_pc(long pcv, struct pt_regs *regs) -{ - if (load_write_pc_interworks) - bx_write_pc(pcv, regs); - else - regs->ARM_pc = pcv; -} - - -#if __LINUX_ARM_ARCH__ >= 7 - -#define alu_write_pc_interworks true -#define test_alu_write_pc_interworking() - -#elif __LINUX_ARM_ARCH__ <= 5 - -/* Kernels built for <= ARMv5 should never run on >= ARMv6 hardware, so... */ -#define alu_write_pc_interworks false -#define test_alu_write_pc_interworking() - -#else /* __LINUX_ARM_ARCH__ == 6 */ - -/* We could be an ARMv6 binary on ARMv7 hardware so we need a run-time check. */ -extern bool alu_write_pc_interworks; -void __init test_alu_write_pc_interworking(void); - -#endif /* __LINUX_ARM_ARCH__ == 6 */ - -static inline void __kprobes alu_write_pc(long pcv, struct pt_regs *regs) -{ - if (alu_write_pc_interworks) - bx_write_pc(pcv, regs); - else - regs->ARM_pc = pcv; -} - - -/* - * Test if load/store instructions writeback the address register. - * if P (bit 24) == 0 or W (bit 21) == 1 - */ -#define is_writeback(insn) ((insn ^ 0x01000000) & 0x01200000) - -/* - * The following definitions and macros are used to build instruction - * decoding tables for use by probes_decode_insn. - * - * These tables are a concatenation of entries each of which consist of one of - * the decode_* structs. All of the fields in every type of decode structure - * are of the union type decode_item, therefore the entire decode table can be - * viewed as an array of these and declared like: - * - * static const union decode_item table_name[] = {}; - * - * In order to construct each entry in the table, macros are used to - * initialise a number of sequential decode_item values in a layout which - * matches the relevant struct. E.g. DECODE_SIMULATE initialise a struct - * decode_simulate by initialising four decode_item objects like this... - * - * {.bits = _type}, - * {.bits = _mask}, - * {.bits = _value}, - * {.action = _handler}, - * - * Initialising a specified member of the union means that the compiler - * will produce a warning if the argument is of an incorrect type. - * - * Below is a list of each of the macros used to initialise entries and a - * description of the action performed when that entry is matched to an - * instruction. A match is found when (instruction & mask) == value. - * - * DECODE_TABLE(mask, value, table) - * Instruction decoding jumps to parsing the new sub-table 'table'. - * - * DECODE_CUSTOM(mask, value, decoder) - * The value of 'decoder' is used as an index into the array of - * action functions, and the retrieved decoder function is invoked - * to complete decoding of the instruction. - * - * DECODE_SIMULATE(mask, value, handler) - * The probes instruction handler is set to the value found by - * indexing into the action array using the value of 'handler'. This - * will be used to simulate the instruction when the probe is hit. - * Decoding returns with INSN_GOOD_NO_SLOT. - * - * DECODE_EMULATE(mask, value, handler) - * The probes instruction handler is set to the value found by - * indexing into the action array using the value of 'handler'. This - * will be used to emulate the instruction when the probe is hit. The - * modified instruction (see below) is placed in the probes instruction - * slot so it may be called by the emulation code. Decoding returns - * with INSN_GOOD. - * - * DECODE_REJECT(mask, value) - * Instruction decoding fails with INSN_REJECTED - * - * DECODE_OR(mask, value) - * This allows the mask/value test of multiple table entries to be - * logically ORed. Once an 'or' entry is matched the decoding action to - * be performed is that of the next entry which isn't an 'or'. E.g. - * - * DECODE_OR (mask1, value1) - * DECODE_OR (mask2, value2) - * DECODE_SIMULATE (mask3, value3, simulation_handler) - * - * This means that if any of the three mask/value pairs match the - * instruction being decoded, then 'simulation_handler' will be used - * for it. - * - * Both the SIMULATE and EMULATE macros have a second form which take an - * additional 'regs' argument. - * - * DECODE_SIMULATEX(mask, value, handler, regs) - * DECODE_EMULATEX (mask, value, handler, regs) - * - * These are used to specify what kind of CPU register is encoded in each of the - * least significant 5 nibbles of the instruction being decoded. The regs value - * is specified using the REGS macro, this takes any of the REG_TYPE_* values - * from enum decode_reg_type as arguments; only the '*' part of the name is - * given. E.g. - * - * REGS(0, ANY, NOPC, 0, ANY) - * - * This indicates an instruction is encoded like: - * - * bits 19..16 ignore - * bits 15..12 any register allowed here - * bits 11.. 8 any register except PC allowed here - * bits 7.. 4 ignore - * bits 3.. 0 any register allowed here - * - * This register specification is checked after a decode table entry is found to - * match an instruction (through the mask/value test). Any invalid register then - * found in the instruction will cause decoding to fail with INSN_REJECTED. In - * the above example this would happen if bits 11..8 of the instruction were - * 1111, indicating R15 or PC. - * - * As well as checking for legal combinations of registers, this data is also - * used to modify the registers encoded in the instructions so that an - * emulation routines can use it. (See decode_regs() and INSN_NEW_BITS.) - * - * Here is a real example which matches ARM instructions of the form - * "AND ,,, " - * - * DECODE_EMULATEX (0x0e000090, 0x00000010, PROBES_DATA_PROCESSING_REG, - * REGS(ANY, ANY, NOPC, 0, ANY)), - * ^ ^ ^ ^ - * Rn Rd Rs Rm - * - * Decoding the instruction "AND R4, R5, R6, ASL R15" will be rejected because - * Rs == R15 - * - * Decoding the instruction "AND R4, R5, R6, ASL R7" will be accepted and the - * instruction will be modified to "AND R0, R2, R3, ASL R1" and then placed into - * the kprobes instruction slot. This can then be called later by the handler - * function emulate_rd12rn16rm0rs8_rwflags (a pointer to which is retrieved from - * the indicated slot in the action array), in order to simulate the instruction. - */ - -enum decode_type { - DECODE_TYPE_END, - DECODE_TYPE_TABLE, - DECODE_TYPE_CUSTOM, - DECODE_TYPE_SIMULATE, - DECODE_TYPE_EMULATE, - DECODE_TYPE_OR, - DECODE_TYPE_REJECT, - NUM_DECODE_TYPES /* Must be last enum */ -}; - -#define DECODE_TYPE_BITS 4 -#define DECODE_TYPE_MASK ((1 << DECODE_TYPE_BITS) - 1) - -enum decode_reg_type { - REG_TYPE_NONE = 0, /* Not a register, ignore */ - REG_TYPE_ANY, /* Any register allowed */ - REG_TYPE_SAMEAS16, /* Register should be same as that at bits 19..16 */ - REG_TYPE_SP, /* Register must be SP */ - REG_TYPE_PC, /* Register must be PC */ - REG_TYPE_NOSP, /* Register must not be SP */ - REG_TYPE_NOSPPC, /* Register must not be SP or PC */ - REG_TYPE_NOPC, /* Register must not be PC */ - REG_TYPE_NOPCWB, /* No PC if load/store write-back flag also set */ - - /* The following types are used when the encoding for PC indicates - * another instruction form. This distiction only matters for test - * case coverage checks. - */ - REG_TYPE_NOPCX, /* Register must not be PC */ - REG_TYPE_NOSPPCX, /* Register must not be SP or PC */ - - /* Alias to allow '0' arg to be used in REGS macro. */ - REG_TYPE_0 = REG_TYPE_NONE -}; - -#define REGS(r16, r12, r8, r4, r0) \ - (((REG_TYPE_##r16) << 16) + \ - ((REG_TYPE_##r12) << 12) + \ - ((REG_TYPE_##r8) << 8) + \ - ((REG_TYPE_##r4) << 4) + \ - (REG_TYPE_##r0)) - -union decode_item { - u32 bits; - const union decode_item *table; - int action; -}; - -struct decode_header; -typedef enum probes_insn (probes_custom_decode_t)(probes_opcode_t, - struct arch_probes_insn *, - const struct decode_header *); - -union decode_action { - probes_insn_handler_t *handler; - probes_custom_decode_t *decoder; -}; - -#define DECODE_END \ - {.bits = DECODE_TYPE_END} - - -struct decode_header { - union decode_item type_regs; - union decode_item mask; - union decode_item value; -}; - -#define DECODE_HEADER(_type, _mask, _value, _regs) \ - {.bits = (_type) | ((_regs) << DECODE_TYPE_BITS)}, \ - {.bits = (_mask)}, \ - {.bits = (_value)} - - -struct decode_table { - struct decode_header header; - union decode_item table; -}; - -#define DECODE_TABLE(_mask, _value, _table) \ - DECODE_HEADER(DECODE_TYPE_TABLE, _mask, _value, 0), \ - {.table = (_table)} - - -struct decode_custom { - struct decode_header header; - union decode_item decoder; -}; - -#define DECODE_CUSTOM(_mask, _value, _decoder) \ - DECODE_HEADER(DECODE_TYPE_CUSTOM, _mask, _value, 0), \ - {.action = (_decoder)} - - -struct decode_simulate { - struct decode_header header; - union decode_item handler; -}; - -#define DECODE_SIMULATEX(_mask, _value, _handler, _regs) \ - DECODE_HEADER(DECODE_TYPE_SIMULATE, _mask, _value, _regs), \ - {.action = (_handler)} - -#define DECODE_SIMULATE(_mask, _value, _handler) \ - DECODE_SIMULATEX(_mask, _value, _handler, 0) - - -struct decode_emulate { - struct decode_header header; - union decode_item handler; -}; - -#define DECODE_EMULATEX(_mask, _value, _handler, _regs) \ - DECODE_HEADER(DECODE_TYPE_EMULATE, _mask, _value, _regs), \ - {.action = (_handler)} - -#define DECODE_EMULATE(_mask, _value, _handler) \ - DECODE_EMULATEX(_mask, _value, _handler, 0) - - -struct decode_or { - struct decode_header header; -}; - -#define DECODE_OR(_mask, _value) \ - DECODE_HEADER(DECODE_TYPE_OR, _mask, _value, 0) - -enum probes_insn { - INSN_REJECTED, - INSN_GOOD, - INSN_GOOD_NO_SLOT -}; - -struct decode_reject { - struct decode_header header; -}; - -#define DECODE_REJECT(_mask, _value) \ - DECODE_HEADER(DECODE_TYPE_REJECT, _mask, _value, 0) - -probes_insn_handler_t probes_simulate_nop; -probes_insn_handler_t probes_emulate_none; - -int __kprobes -probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, - const union decode_item *table, bool thumb, bool emulate, - const union decode_action *actions); - -#endif diff --git a/arch/arm/kernel/uprobes-arm.c b/arch/arm/kernel/uprobes-arm.c deleted file mode 100644 index d3b655f..0000000 --- a/arch/arm/kernel/uprobes-arm.c +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (C) 2012 Rabin Vincent - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include - -#include "probes.h" -#include "probes-arm.h" -#include "uprobes.h" - -static int uprobes_substitute_pc(unsigned long *pinsn, u32 oregs) -{ - probes_opcode_t insn = __mem_to_opcode_arm(*pinsn); - probes_opcode_t temp; - probes_opcode_t mask; - int freereg; - u32 free = 0xffff; - u32 regs; - - for (regs = oregs; regs; regs >>= 4, insn >>= 4) { - if ((regs & 0xf) == REG_TYPE_NONE) - continue; - - free &= ~(1 << (insn & 0xf)); - } - - /* No PC, no problem */ - if (free & (1 << 15)) - return 15; - - if (!free) - return -1; - - /* - * fls instead of ffs ensures that for "ldrd r0, r1, [pc]" we would - * pick LR instead of R1. - */ - freereg = free = fls(free) - 1; - - temp = __mem_to_opcode_arm(*pinsn); - insn = temp; - regs = oregs; - mask = 0xf; - - for (; regs; regs >>= 4, mask <<= 4, free <<= 4, temp >>= 4) { - if ((regs & 0xf) == REG_TYPE_NONE) - continue; - - if ((temp & 0xf) != 15) - continue; - - insn &= ~mask; - insn |= free & mask; - } - - *pinsn = __opcode_to_mem_arm(insn); - return freereg; -} - -static void uprobe_set_pc(struct arch_uprobe *auprobe, - struct arch_uprobe_task *autask, - struct pt_regs *regs) -{ - u32 pcreg = auprobe->pcreg; - - autask->backup = regs->uregs[pcreg]; - regs->uregs[pcreg] = regs->ARM_pc + 8; -} - -static void uprobe_unset_pc(struct arch_uprobe *auprobe, - struct arch_uprobe_task *autask, - struct pt_regs *regs) -{ - /* PC will be taken care of by common code */ - regs->uregs[auprobe->pcreg] = autask->backup; -} - -static void uprobe_aluwrite_pc(struct arch_uprobe *auprobe, - struct arch_uprobe_task *autask, - struct pt_regs *regs) -{ - u32 pcreg = auprobe->pcreg; - - alu_write_pc(regs->uregs[pcreg], regs); - regs->uregs[pcreg] = autask->backup; -} - -static void uprobe_write_pc(struct arch_uprobe *auprobe, - struct arch_uprobe_task *autask, - struct pt_regs *regs) -{ - u32 pcreg = auprobe->pcreg; - - load_write_pc(regs->uregs[pcreg], regs); - regs->uregs[pcreg] = autask->backup; -} - -enum probes_insn -decode_pc_ro(probes_opcode_t insn, struct arch_probes_insn *asi, - const struct decode_header *d) -{ - struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe, - asi); - struct decode_emulate *decode = (struct decode_emulate *) d; - u32 regs = decode->header.type_regs.bits >> DECODE_TYPE_BITS; - int reg; - - reg = uprobes_substitute_pc(&auprobe->ixol[0], regs); - if (reg == 15) - return INSN_GOOD; - - if (reg == -1) - return INSN_REJECTED; - - auprobe->pcreg = reg; - auprobe->prehandler = uprobe_set_pc; - auprobe->posthandler = uprobe_unset_pc; - - return INSN_GOOD; -} - -enum probes_insn -decode_wb_pc(probes_opcode_t insn, struct arch_probes_insn *asi, - const struct decode_header *d, bool alu) -{ - struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe, - asi); - enum probes_insn ret = decode_pc_ro(insn, asi, d); - - if (((insn >> 12) & 0xf) == 15) - auprobe->posthandler = alu ? uprobe_aluwrite_pc - : uprobe_write_pc; - - return ret; -} - -enum probes_insn -decode_rd12rn16rm0rs8_rwflags(probes_opcode_t insn, - struct arch_probes_insn *asi, - const struct decode_header *d) -{ - return decode_wb_pc(insn, asi, d, true); -} - -enum probes_insn -decode_ldr(probes_opcode_t insn, struct arch_probes_insn *asi, - const struct decode_header *d) -{ - return decode_wb_pc(insn, asi, d, false); -} - -enum probes_insn -uprobe_decode_ldmstm(probes_opcode_t insn, - struct arch_probes_insn *asi, - const struct decode_header *d) -{ - struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe, - asi); - unsigned reglist = insn & 0xffff; - int rn = (insn >> 16) & 0xf; - int lbit = insn & (1 << 20); - unsigned used = reglist | (1 << rn); - - if (rn == 15) - return INSN_REJECTED; - - if (!(used & (1 << 15))) - return INSN_GOOD; - - if (used & (1 << 14)) - return INSN_REJECTED; - - /* Use LR instead of PC */ - insn ^= 0xc000; - - auprobe->pcreg = 14; - auprobe->ixol[0] = __opcode_to_mem_arm(insn); - - auprobe->prehandler = uprobe_set_pc; - if (lbit) - auprobe->posthandler = uprobe_write_pc; - else - auprobe->posthandler = uprobe_unset_pc; - - return INSN_GOOD; -} - -const union decode_action uprobes_probes_actions[] = { - [PROBES_EMULATE_NONE] = {.handler = probes_simulate_nop}, - [PROBES_SIMULATE_NOP] = {.handler = probes_simulate_nop}, - [PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop}, - [PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop}, - [PROBES_BRANCH_IMM] = {.handler = simulate_blx1}, - [PROBES_MRS] = {.handler = simulate_mrs}, - [PROBES_BRANCH_REG] = {.handler = simulate_blx2bx}, - [PROBES_CLZ] = {.handler = probes_simulate_nop}, - [PROBES_SATURATING_ARITHMETIC] = {.handler = probes_simulate_nop}, - [PROBES_MUL1] = {.handler = probes_simulate_nop}, - [PROBES_MUL2] = {.handler = probes_simulate_nop}, - [PROBES_SWP] = {.handler = probes_simulate_nop}, - [PROBES_LDRSTRD] = {.decoder = decode_pc_ro}, - [PROBES_LOAD_EXTRA] = {.decoder = decode_pc_ro}, - [PROBES_LOAD] = {.decoder = decode_ldr}, - [PROBES_STORE_EXTRA] = {.decoder = decode_pc_ro}, - [PROBES_STORE] = {.decoder = decode_pc_ro}, - [PROBES_MOV_IP_SP] = {.handler = simulate_mov_ipsp}, - [PROBES_DATA_PROCESSING_REG] = { - .decoder = decode_rd12rn16rm0rs8_rwflags}, - [PROBES_DATA_PROCESSING_IMM] = { - .decoder = decode_rd12rn16rm0rs8_rwflags}, - [PROBES_MOV_HALFWORD] = {.handler = probes_simulate_nop}, - [PROBES_SEV] = {.handler = probes_simulate_nop}, - [PROBES_WFE] = {.handler = probes_simulate_nop}, - [PROBES_SATURATE] = {.handler = probes_simulate_nop}, - [PROBES_REV] = {.handler = probes_simulate_nop}, - [PROBES_MMI] = {.handler = probes_simulate_nop}, - [PROBES_PACK] = {.handler = probes_simulate_nop}, - [PROBES_EXTEND] = {.handler = probes_simulate_nop}, - [PROBES_EXTEND_ADD] = {.handler = probes_simulate_nop}, - [PROBES_MUL_ADD_LONG] = {.handler = probes_simulate_nop}, - [PROBES_MUL_ADD] = {.handler = probes_simulate_nop}, - [PROBES_BITFIELD] = {.handler = probes_simulate_nop}, - [PROBES_BRANCH] = {.handler = simulate_bbl}, - [PROBES_LDMSTM] = {.decoder = uprobe_decode_ldmstm} -}; diff --git a/arch/arm/kernel/uprobes.c b/arch/arm/kernel/uprobes.c deleted file mode 100644 index 56adf9c..0000000 --- a/arch/arm/kernel/uprobes.c +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (C) 2012 Rabin Vincent - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "probes.h" -#include "probes-arm.h" -#include "uprobes.h" - -#define UPROBE_TRAP_NR UINT_MAX - -bool is_swbp_insn(uprobe_opcode_t *insn) -{ - return (__mem_to_opcode_arm(*insn) & 0x0fffffff) == - (UPROBE_SWBP_ARM_INSN & 0x0fffffff); -} - -int set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, - unsigned long vaddr) -{ - return uprobe_write_opcode(mm, vaddr, - __opcode_to_mem_arm(auprobe->bpinsn)); -} - -bool arch_uprobe_ignore(struct arch_uprobe *auprobe, struct pt_regs *regs) -{ - if (!auprobe->asi.insn_check_cc(regs->ARM_cpsr)) { - regs->ARM_pc += 4; - return true; - } - - return false; -} - -bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) -{ - probes_opcode_t opcode; - - if (!auprobe->simulate) - return false; - - opcode = __mem_to_opcode_arm(*(unsigned int *) auprobe->insn); - - auprobe->asi.insn_singlestep(opcode, &auprobe->asi, regs); - - return true; -} - -unsigned long -arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, - struct pt_regs *regs) -{ - unsigned long orig_ret_vaddr; - - orig_ret_vaddr = regs->ARM_lr; - /* Replace the return addr with trampoline addr */ - regs->ARM_lr = trampoline_vaddr; - return orig_ret_vaddr; -} - -int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, - unsigned long addr) -{ - unsigned int insn; - unsigned int bpinsn; - enum probes_insn ret; - - /* Thumb not yet support */ - if (addr & 0x3) - return -EINVAL; - - insn = __mem_to_opcode_arm(*(unsigned int *)auprobe->insn); - auprobe->ixol[0] = __opcode_to_mem_arm(insn); - auprobe->ixol[1] = __opcode_to_mem_arm(UPROBE_SS_ARM_INSN); - - ret = arm_probes_decode_insn(insn, &auprobe->asi, false, - uprobes_probes_actions); - switch (ret) { - case INSN_REJECTED: - return -EINVAL; - - case INSN_GOOD_NO_SLOT: - auprobe->simulate = true; - break; - - case INSN_GOOD: - default: - break; - } - - bpinsn = UPROBE_SWBP_ARM_INSN & 0x0fffffff; - if (insn >= 0xe0000000) - bpinsn |= 0xe0000000; /* Unconditional instruction */ - else - bpinsn |= insn & 0xf0000000; /* Copy condition from insn */ - - auprobe->bpinsn = bpinsn; - - return 0; -} - -void arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr, - void *src, unsigned long len) -{ - void *xol_page_kaddr = kmap_atomic(page); - void *dst = xol_page_kaddr + (vaddr & ~PAGE_MASK); - - preempt_disable(); - - /* Initialize the slot */ - memcpy(dst, src, len); - - /* flush caches (dcache/icache) */ - flush_uprobe_xol_access(page, vaddr, dst, len); - - preempt_enable(); - - kunmap_atomic(xol_page_kaddr); -} - - -int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) -{ - struct uprobe_task *utask = current->utask; - - if (auprobe->prehandler) - auprobe->prehandler(auprobe, &utask->autask, regs); - - utask->autask.saved_trap_no = current->thread.trap_no; - current->thread.trap_no = UPROBE_TRAP_NR; - regs->ARM_pc = utask->xol_vaddr; - - return 0; -} - -int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) -{ - struct uprobe_task *utask = current->utask; - - WARN_ON_ONCE(current->thread.trap_no != UPROBE_TRAP_NR); - - current->thread.trap_no = utask->autask.saved_trap_no; - regs->ARM_pc = utask->vaddr + 4; - - if (auprobe->posthandler) - auprobe->posthandler(auprobe, &utask->autask, regs); - - return 0; -} - -bool arch_uprobe_xol_was_trapped(struct task_struct *t) -{ - if (t->thread.trap_no != UPROBE_TRAP_NR) - return true; - - return false; -} - -void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) -{ - struct uprobe_task *utask = current->utask; - - current->thread.trap_no = utask->autask.saved_trap_no; - instruction_pointer_set(regs, utask->vaddr); -} - -int arch_uprobe_exception_notify(struct notifier_block *self, - unsigned long val, void *data) -{ - return NOTIFY_DONE; -} - -static int uprobe_trap_handler(struct pt_regs *regs, unsigned int instr) -{ - unsigned long flags; - - local_irq_save(flags); - instr &= 0x0fffffff; - if (instr == (UPROBE_SWBP_ARM_INSN & 0x0fffffff)) - uprobe_pre_sstep_notifier(regs); - else if (instr == (UPROBE_SS_ARM_INSN & 0x0fffffff)) - uprobe_post_sstep_notifier(regs); - local_irq_restore(flags); - - return 0; -} - -unsigned long uprobe_get_swbp_addr(struct pt_regs *regs) -{ - return instruction_pointer(regs); -} - -static struct undef_hook uprobes_arm_break_hook = { - .instr_mask = 0x0fffffff, - .instr_val = (UPROBE_SWBP_ARM_INSN & 0x0fffffff), - .cpsr_mask = MODE_MASK, - .cpsr_val = USR_MODE, - .fn = uprobe_trap_handler, -}; - -static struct undef_hook uprobes_arm_ss_hook = { - .instr_mask = 0x0fffffff, - .instr_val = (UPROBE_SS_ARM_INSN & 0x0fffffff), - .cpsr_mask = MODE_MASK, - .cpsr_val = USR_MODE, - .fn = uprobe_trap_handler, -}; - -static int arch_uprobes_init(void) -{ - register_undef_hook(&uprobes_arm_break_hook); - register_undef_hook(&uprobes_arm_ss_hook); - - return 0; -} -device_initcall(arch_uprobes_init); diff --git a/arch/arm/kernel/uprobes.h b/arch/arm/kernel/uprobes.h deleted file mode 100644 index 1d0c12d..0000000 --- a/arch/arm/kernel/uprobes.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2012 Rabin Vincent - * - * 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 __ARM_KERNEL_UPROBES_H -#define __ARM_KERNEL_UPROBES_H - -enum probes_insn uprobe_decode_ldmstm(probes_opcode_t insn, - struct arch_probes_insn *asi, - const struct decode_header *d); - -enum probes_insn decode_ldr(probes_opcode_t insn, - struct arch_probes_insn *asi, - const struct decode_header *d); - -enum probes_insn -decode_rd12rn16rm0rs8_rwflags(probes_opcode_t insn, - struct arch_probes_insn *asi, - const struct decode_header *d); - -enum probes_insn -decode_wb_pc(probes_opcode_t insn, struct arch_probes_insn *asi, - const struct decode_header *d, bool alu); - -enum probes_insn -decode_pc_ro(probes_opcode_t insn, struct arch_probes_insn *asi, - const struct decode_header *d); - -extern const union decode_action uprobes_probes_actions[]; - -#endif diff --git a/arch/arm/probes/Makefile b/arch/arm/probes/Makefile new file mode 100644 index 0000000..aa1f859 --- /dev/null +++ b/arch/arm/probes/Makefile @@ -0,0 +1,7 @@ +obj-$(CONFIG_UPROBES) += decode.o decode-arm.o uprobes/ +obj-$(CONFIG_KPROBES) += decode.o kprobes/ +ifdef CONFIG_THUMB2_KERNEL +obj-$(CONFIG_KPROBES) += decode-thumb.o +else +obj-$(CONFIG_KPROBES) += decode-arm.o +endif diff --git a/arch/arm/probes/decode-arm.c b/arch/arm/probes/decode-arm.c new file mode 100644 index 0000000..e39cc75 --- /dev/null +++ b/arch/arm/probes/decode-arm.c @@ -0,0 +1,735 @@ +/* + * + * arch/arm/probes/decode-arm.c + * + * Some code moved here from arch/arm/kernel/kprobes-arm.c + * + * Copyright (C) 2006, 2007 Motorola Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include +#include +#include + +#include "decode.h" +#include "decode-arm.h" + +#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit))))) + +#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25) + +/* + * To avoid the complications of mimicing single-stepping on a + * processor without a Next-PC or a single-step mode, and to + * avoid having to deal with the side-effects of boosting, we + * simulate or emulate (almost) all ARM instructions. + * + * "Simulation" is where the instruction's behavior is duplicated in + * C code. "Emulation" is where the original instruction is rewritten + * and executed, often by altering its registers. + * + * By having all behavior of the kprobe'd instruction completed before + * returning from the kprobe_handler(), all locks (scheduler and + * interrupt) can safely be released. There is no need for secondary + * breakpoints, no race with MP or preemptable kernels, nor having to + * clean up resources counts at a later time impacting overall system + * performance. By rewriting the instruction, only the minimum registers + * need to be loaded and saved back optimizing performance. + * + * Calling the insnslot_*_rwflags version of a function doesn't hurt + * anything even when the CPSR flags aren't updated by the + * instruction. It's just a little slower in return for saving + * a little space by not having a duplicate function that doesn't + * update the flags. (The same optimization can be said for + * instructions that do or don't perform register writeback) + * Also, instructions can either read the flags, only write the + * flags, or read and write the flags. To save combinations + * rather than for sheer performance, flag functions just assume + * read and write of flags. + */ + +void __kprobes simulate_bbl(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + long iaddr = (long) regs->ARM_pc - 4; + int disp = branch_displacement(insn); + + if (insn & (1 << 24)) + regs->ARM_lr = iaddr + 4; + + regs->ARM_pc = iaddr + 8 + disp; +} + +void __kprobes simulate_blx1(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + long iaddr = (long) regs->ARM_pc - 4; + int disp = branch_displacement(insn); + + regs->ARM_lr = iaddr + 4; + regs->ARM_pc = iaddr + 8 + disp + ((insn >> 23) & 0x2); + regs->ARM_cpsr |= PSR_T_BIT; +} + +void __kprobes simulate_blx2bx(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + int rm = insn & 0xf; + long rmv = regs->uregs[rm]; + + if (insn & (1 << 5)) + regs->ARM_lr = (long) regs->ARM_pc; + + regs->ARM_pc = rmv & ~0x1; + regs->ARM_cpsr &= ~PSR_T_BIT; + if (rmv & 0x1) + regs->ARM_cpsr |= PSR_T_BIT; +} + +void __kprobes simulate_mrs(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + int rd = (insn >> 12) & 0xf; + unsigned long mask = 0xf8ff03df; /* Mask out execution state */ + regs->uregs[rd] = regs->ARM_cpsr & mask; +} + +void __kprobes simulate_mov_ipsp(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + regs->uregs[12] = regs->uregs[13]; +} + +/* + * For the instruction masking and comparisons in all the "space_*" + * functions below, Do _not_ rearrange the order of tests unless + * you're very, very sure of what you are doing. For the sake of + * efficiency, the masks for some tests sometimes assume other test + * have been done prior to them so the number of patterns to test + * for an instruction set can be as broad as possible to reduce the + * number of tests needed. + */ + +static const union decode_item arm_1111_table[] = { + /* Unconditional instructions */ + + /* memory hint 1111 0100 x001 xxxx xxxx xxxx xxxx xxxx */ + /* PLDI (immediate) 1111 0100 x101 xxxx xxxx xxxx xxxx xxxx */ + /* PLDW (immediate) 1111 0101 x001 xxxx xxxx xxxx xxxx xxxx */ + /* PLD (immediate) 1111 0101 x101 xxxx xxxx xxxx xxxx xxxx */ + DECODE_SIMULATE (0xfe300000, 0xf4100000, PROBES_PRELOAD_IMM), + + /* memory hint 1111 0110 x001 xxxx xxxx xxxx xxx0 xxxx */ + /* PLDI (register) 1111 0110 x101 xxxx xxxx xxxx xxx0 xxxx */ + /* PLDW (register) 1111 0111 x001 xxxx xxxx xxxx xxx0 xxxx */ + /* PLD (register) 1111 0111 x101 xxxx xxxx xxxx xxx0 xxxx */ + DECODE_SIMULATE (0xfe300010, 0xf6100000, PROBES_PRELOAD_REG), + + /* BLX (immediate) 1111 101x xxxx xxxx xxxx xxxx xxxx xxxx */ + DECODE_SIMULATE (0xfe000000, 0xfa000000, PROBES_BRANCH_IMM), + + /* CPS 1111 0001 0000 xxx0 xxxx xxxx xx0x xxxx */ + /* SETEND 1111 0001 0000 0001 xxxx xxxx 0000 xxxx */ + /* SRS 1111 100x x1x0 xxxx xxxx xxxx xxxx xxxx */ + /* RFE 1111 100x x0x1 xxxx xxxx xxxx xxxx xxxx */ + + /* Coprocessor instructions... */ + /* MCRR2 1111 1100 0100 xxxx xxxx xxxx xxxx xxxx */ + /* MRRC2 1111 1100 0101 xxxx xxxx xxxx xxxx xxxx */ + /* LDC2 1111 110x xxx1 xxxx xxxx xxxx xxxx xxxx */ + /* STC2 1111 110x xxx0 xxxx xxxx xxxx xxxx xxxx */ + /* CDP2 1111 1110 xxxx xxxx xxxx xxxx xxx0 xxxx */ + /* MCR2 1111 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx */ + /* MRC2 1111 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx */ + + /* Other unallocated instructions... */ + DECODE_END +}; + +static const union decode_item arm_cccc_0001_0xx0____0xxx_table[] = { + /* Miscellaneous instructions */ + + /* MRS cpsr cccc 0001 0000 xxxx xxxx xxxx 0000 xxxx */ + DECODE_SIMULATEX(0x0ff000f0, 0x01000000, PROBES_MRS, + REGS(0, NOPC, 0, 0, 0)), + + /* BX cccc 0001 0010 xxxx xxxx xxxx 0001 xxxx */ + DECODE_SIMULATE (0x0ff000f0, 0x01200010, PROBES_BRANCH_REG), + + /* BLX (register) cccc 0001 0010 xxxx xxxx xxxx 0011 xxxx */ + DECODE_SIMULATEX(0x0ff000f0, 0x01200030, PROBES_BRANCH_REG, + REGS(0, 0, 0, 0, NOPC)), + + /* CLZ cccc 0001 0110 xxxx xxxx xxxx 0001 xxxx */ + DECODE_EMULATEX (0x0ff000f0, 0x01600010, PROBES_CLZ, + REGS(0, NOPC, 0, 0, NOPC)), + + /* QADD cccc 0001 0000 xxxx xxxx xxxx 0101 xxxx */ + /* QSUB cccc 0001 0010 xxxx xxxx xxxx 0101 xxxx */ + /* QDADD cccc 0001 0100 xxxx xxxx xxxx 0101 xxxx */ + /* QDSUB cccc 0001 0110 xxxx xxxx xxxx 0101 xxxx */ + DECODE_EMULATEX (0x0f9000f0, 0x01000050, PROBES_SATURATING_ARITHMETIC, + REGS(NOPC, NOPC, 0, 0, NOPC)), + + /* BXJ cccc 0001 0010 xxxx xxxx xxxx 0010 xxxx */ + /* MSR cccc 0001 0x10 xxxx xxxx xxxx 0000 xxxx */ + /* MRS spsr cccc 0001 0100 xxxx xxxx xxxx 0000 xxxx */ + /* BKPT 1110 0001 0010 xxxx xxxx xxxx 0111 xxxx */ + /* SMC cccc 0001 0110 xxxx xxxx xxxx 0111 xxxx */ + /* And unallocated instructions... */ + DECODE_END +}; + +static const union decode_item arm_cccc_0001_0xx0____1xx0_table[] = { + /* Halfword multiply and multiply-accumulate */ + + /* SMLALxy cccc 0001 0100 xxxx xxxx xxxx 1xx0 xxxx */ + DECODE_EMULATEX (0x0ff00090, 0x01400080, PROBES_MUL1, + REGS(NOPC, NOPC, NOPC, 0, NOPC)), + + /* SMULWy cccc 0001 0010 xxxx xxxx xxxx 1x10 xxxx */ + DECODE_OR (0x0ff000b0, 0x012000a0), + /* SMULxy cccc 0001 0110 xxxx xxxx xxxx 1xx0 xxxx */ + DECODE_EMULATEX (0x0ff00090, 0x01600080, PROBES_MUL2, + REGS(NOPC, 0, NOPC, 0, NOPC)), + + /* SMLAxy cccc 0001 0000 xxxx xxxx xxxx 1xx0 xxxx */ + DECODE_OR (0x0ff00090, 0x01000080), + /* SMLAWy cccc 0001 0010 xxxx xxxx xxxx 1x00 xxxx */ + DECODE_EMULATEX (0x0ff000b0, 0x01200080, PROBES_MUL2, + REGS(NOPC, NOPC, NOPC, 0, NOPC)), + + DECODE_END +}; + +static const union decode_item arm_cccc_0000_____1001_table[] = { + /* Multiply and multiply-accumulate */ + + /* MUL cccc 0000 0000 xxxx xxxx xxxx 1001 xxxx */ + /* MULS cccc 0000 0001 xxxx xxxx xxxx 1001 xxxx */ + DECODE_EMULATEX (0x0fe000f0, 0x00000090, PROBES_MUL2, + REGS(NOPC, 0, NOPC, 0, NOPC)), + + /* MLA cccc 0000 0010 xxxx xxxx xxxx 1001 xxxx */ + /* MLAS cccc 0000 0011 xxxx xxxx xxxx 1001 xxxx */ + DECODE_OR (0x0fe000f0, 0x00200090), + /* MLS cccc 0000 0110 xxxx xxxx xxxx 1001 xxxx */ + DECODE_EMULATEX (0x0ff000f0, 0x00600090, PROBES_MUL2, + REGS(NOPC, NOPC, NOPC, 0, NOPC)), + + /* UMAAL cccc 0000 0100 xxxx xxxx xxxx 1001 xxxx */ + DECODE_OR (0x0ff000f0, 0x00400090), + /* UMULL cccc 0000 1000 xxxx xxxx xxxx 1001 xxxx */ + /* UMULLS cccc 0000 1001 xxxx xxxx xxxx 1001 xxxx */ + /* UMLAL cccc 0000 1010 xxxx xxxx xxxx 1001 xxxx */ + /* UMLALS cccc 0000 1011 xxxx xxxx xxxx 1001 xxxx */ + /* SMULL cccc 0000 1100 xxxx xxxx xxxx 1001 xxxx */ + /* SMULLS cccc 0000 1101 xxxx xxxx xxxx 1001 xxxx */ + /* SMLAL cccc 0000 1110 xxxx xxxx xxxx 1001 xxxx */ + /* SMLALS cccc 0000 1111 xxxx xxxx xxxx 1001 xxxx */ + DECODE_EMULATEX (0x0f8000f0, 0x00800090, PROBES_MUL1, + REGS(NOPC, NOPC, NOPC, 0, NOPC)), + + DECODE_END +}; + +static const union decode_item arm_cccc_0001_____1001_table[] = { + /* Synchronization primitives */ + +#if __LINUX_ARM_ARCH__ < 6 + /* Deprecated on ARMv6 and may be UNDEFINED on v7 */ + /* SMP/SWPB cccc 0001 0x00 xxxx xxxx xxxx 1001 xxxx */ + DECODE_EMULATEX (0x0fb000f0, 0x01000090, PROBES_SWP, + REGS(NOPC, NOPC, 0, 0, NOPC)), +#endif + /* LDREX/STREX{,D,B,H} cccc 0001 1xxx xxxx xxxx xxxx 1001 xxxx */ + /* And unallocated instructions... */ + DECODE_END +}; + +static const union decode_item arm_cccc_000x_____1xx1_table[] = { + /* Extra load/store instructions */ + + /* STRHT cccc 0000 xx10 xxxx xxxx xxxx 1011 xxxx */ + /* ??? cccc 0000 xx10 xxxx xxxx xxxx 11x1 xxxx */ + /* LDRHT cccc 0000 xx11 xxxx xxxx xxxx 1011 xxxx */ + /* LDRSBT cccc 0000 xx11 xxxx xxxx xxxx 1101 xxxx */ + /* LDRSHT cccc 0000 xx11 xxxx xxxx xxxx 1111 xxxx */ + DECODE_REJECT (0x0f200090, 0x00200090), + + /* LDRD/STRD lr,pc,{... cccc 000x x0x0 xxxx 111x xxxx 1101 xxxx */ + DECODE_REJECT (0x0e10e0d0, 0x0000e0d0), + + /* LDRD (register) cccc 000x x0x0 xxxx xxxx xxxx 1101 xxxx */ + /* STRD (register) cccc 000x x0x0 xxxx xxxx xxxx 1111 xxxx */ + DECODE_EMULATEX (0x0e5000d0, 0x000000d0, PROBES_LDRSTRD, + REGS(NOPCWB, NOPCX, 0, 0, NOPC)), + + /* LDRD (immediate) cccc 000x x1x0 xxxx xxxx xxxx 1101 xxxx */ + /* STRD (immediate) cccc 000x x1x0 xxxx xxxx xxxx 1111 xxxx */ + DECODE_EMULATEX (0x0e5000d0, 0x004000d0, PROBES_LDRSTRD, + REGS(NOPCWB, NOPCX, 0, 0, 0)), + + /* STRH (register) cccc 000x x0x0 xxxx xxxx xxxx 1011 xxxx */ + DECODE_EMULATEX (0x0e5000f0, 0x000000b0, PROBES_STORE_EXTRA, + REGS(NOPCWB, NOPC, 0, 0, NOPC)), + + /* LDRH (register) cccc 000x x0x1 xxxx xxxx xxxx 1011 xxxx */ + /* LDRSB (register) cccc 000x x0x1 xxxx xxxx xxxx 1101 xxxx */ + /* LDRSH (register) cccc 000x x0x1 xxxx xxxx xxxx 1111 xxxx */ + DECODE_EMULATEX (0x0e500090, 0x00100090, PROBES_LOAD_EXTRA, + REGS(NOPCWB, NOPC, 0, 0, NOPC)), + + /* STRH (immediate) cccc 000x x1x0 xxxx xxxx xxxx 1011 xxxx */ + DECODE_EMULATEX (0x0e5000f0, 0x004000b0, PROBES_STORE_EXTRA, + REGS(NOPCWB, NOPC, 0, 0, 0)), + + /* LDRH (immediate) cccc 000x x1x1 xxxx xxxx xxxx 1011 xxxx */ + /* LDRSB (immediate) cccc 000x x1x1 xxxx xxxx xxxx 1101 xxxx */ + /* LDRSH (immediate) cccc 000x x1x1 xxxx xxxx xxxx 1111 xxxx */ + DECODE_EMULATEX (0x0e500090, 0x00500090, PROBES_LOAD_EXTRA, + REGS(NOPCWB, NOPC, 0, 0, 0)), + + DECODE_END +}; + +static const union decode_item arm_cccc_000x_table[] = { + /* Data-processing (register) */ + + /* S PC, ... cccc 000x xxx1 xxxx 1111 xxxx xxxx xxxx */ + DECODE_REJECT (0x0e10f000, 0x0010f000), + + /* MOV IP, SP 1110 0001 1010 0000 1100 0000 0000 1101 */ + DECODE_SIMULATE (0xffffffff, 0xe1a0c00d, PROBES_MOV_IP_SP), + + /* TST (register) cccc 0001 0001 xxxx xxxx xxxx xxx0 xxxx */ + /* TEQ (register) cccc 0001 0011 xxxx xxxx xxxx xxx0 xxxx */ + /* CMP (register) cccc 0001 0101 xxxx xxxx xxxx xxx0 xxxx */ + /* CMN (register) cccc 0001 0111 xxxx xxxx xxxx xxx0 xxxx */ + DECODE_EMULATEX (0x0f900010, 0x01100000, PROBES_DATA_PROCESSING_REG, + REGS(ANY, 0, 0, 0, ANY)), + + /* MOV (register) cccc 0001 101x xxxx xxxx xxxx xxx0 xxxx */ + /* MVN (register) cccc 0001 111x xxxx xxxx xxxx xxx0 xxxx */ + DECODE_EMULATEX (0x0fa00010, 0x01a00000, PROBES_DATA_PROCESSING_REG, + REGS(0, ANY, 0, 0, ANY)), + + /* AND (register) cccc 0000 000x xxxx xxxx xxxx xxx0 xxxx */ + /* EOR (register) cccc 0000 001x xxxx xxxx xxxx xxx0 xxxx */ + /* SUB (register) cccc 0000 010x xxxx xxxx xxxx xxx0 xxxx */ + /* RSB (register) cccc 0000 011x xxxx xxxx xxxx xxx0 xxxx */ + /* ADD (register) cccc 0000 100x xxxx xxxx xxxx xxx0 xxxx */ + /* ADC (register) cccc 0000 101x xxxx xxxx xxxx xxx0 xxxx */ + /* SBC (register) cccc 0000 110x xxxx xxxx xxxx xxx0 xxxx */ + /* RSC (register) cccc 0000 111x xxxx xxxx xxxx xxx0 xxxx */ + /* ORR (register) cccc 0001 100x xxxx xxxx xxxx xxx0 xxxx */ + /* BIC (register) cccc 0001 110x xxxx xxxx xxxx xxx0 xxxx */ + DECODE_EMULATEX (0x0e000010, 0x00000000, PROBES_DATA_PROCESSING_REG, + REGS(ANY, ANY, 0, 0, ANY)), + + /* TST (reg-shift reg) cccc 0001 0001 xxxx xxxx xxxx 0xx1 xxxx */ + /* TEQ (reg-shift reg) cccc 0001 0011 xxxx xxxx xxxx 0xx1 xxxx */ + /* CMP (reg-shift reg) cccc 0001 0101 xxxx xxxx xxxx 0xx1 xxxx */ + /* CMN (reg-shift reg) cccc 0001 0111 xxxx xxxx xxxx 0xx1 xxxx */ + DECODE_EMULATEX (0x0f900090, 0x01100010, PROBES_DATA_PROCESSING_REG, + REGS(NOPC, 0, NOPC, 0, NOPC)), + + /* MOV (reg-shift reg) cccc 0001 101x xxxx xxxx xxxx 0xx1 xxxx */ + /* MVN (reg-shift reg) cccc 0001 111x xxxx xxxx xxxx 0xx1 xxxx */ + DECODE_EMULATEX (0x0fa00090, 0x01a00010, PROBES_DATA_PROCESSING_REG, + REGS(0, NOPC, NOPC, 0, NOPC)), + + /* AND (reg-shift reg) cccc 0000 000x xxxx xxxx xxxx 0xx1 xxxx */ + /* EOR (reg-shift reg) cccc 0000 001x xxxx xxxx xxxx 0xx1 xxxx */ + /* SUB (reg-shift reg) cccc 0000 010x xxxx xxxx xxxx 0xx1 xxxx */ + /* RSB (reg-shift reg) cccc 0000 011x xxxx xxxx xxxx 0xx1 xxxx */ + /* ADD (reg-shift reg) cccc 0000 100x xxxx xxxx xxxx 0xx1 xxxx */ + /* ADC (reg-shift reg) cccc 0000 101x xxxx xxxx xxxx 0xx1 xxxx */ + /* SBC (reg-shift reg) cccc 0000 110x xxxx xxxx xxxx 0xx1 xxxx */ + /* RSC (reg-shift reg) cccc 0000 111x xxxx xxxx xxxx 0xx1 xxxx */ + /* ORR (reg-shift reg) cccc 0001 100x xxxx xxxx xxxx 0xx1 xxxx */ + /* BIC (reg-shift reg) cccc 0001 110x xxxx xxxx xxxx 0xx1 xxxx */ + DECODE_EMULATEX (0x0e000090, 0x00000010, PROBES_DATA_PROCESSING_REG, + REGS(NOPC, NOPC, NOPC, 0, NOPC)), + + DECODE_END +}; + +static const union decode_item arm_cccc_001x_table[] = { + /* Data-processing (immediate) */ + + /* MOVW cccc 0011 0000 xxxx xxxx xxxx xxxx xxxx */ + /* MOVT cccc 0011 0100 xxxx xxxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0x0fb00000, 0x03000000, PROBES_DATA_PROCESSING_IMM, + REGS(0, NOPC, 0, 0, 0)), + + /* YIELD cccc 0011 0010 0000 xxxx xxxx 0000 0001 */ + DECODE_OR (0x0fff00ff, 0x03200001), + /* SEV cccc 0011 0010 0000 xxxx xxxx 0000 0100 */ + DECODE_EMULATE (0x0fff00ff, 0x03200004, PROBES_EMULATE_NONE), + /* NOP cccc 0011 0010 0000 xxxx xxxx 0000 0000 */ + /* WFE cccc 0011 0010 0000 xxxx xxxx 0000 0010 */ + /* WFI cccc 0011 0010 0000 xxxx xxxx 0000 0011 */ + DECODE_SIMULATE (0x0fff00fc, 0x03200000, PROBES_SIMULATE_NOP), + /* DBG cccc 0011 0010 0000 xxxx xxxx ffff xxxx */ + /* unallocated hints cccc 0011 0010 0000 xxxx xxxx xxxx xxxx */ + /* MSR (immediate) cccc 0011 0x10 xxxx xxxx xxxx xxxx xxxx */ + DECODE_REJECT (0x0fb00000, 0x03200000), + + /* S PC, ... cccc 001x xxx1 xxxx 1111 xxxx xxxx xxxx */ + DECODE_REJECT (0x0e10f000, 0x0210f000), + + /* TST (immediate) cccc 0011 0001 xxxx xxxx xxxx xxxx xxxx */ + /* TEQ (immediate) cccc 0011 0011 xxxx xxxx xxxx xxxx xxxx */ + /* CMP (immediate) cccc 0011 0101 xxxx xxxx xxxx xxxx xxxx */ + /* CMN (immediate) cccc 0011 0111 xxxx xxxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0x0f900000, 0x03100000, PROBES_DATA_PROCESSING_IMM, + REGS(ANY, 0, 0, 0, 0)), + + /* MOV (immediate) cccc 0011 101x xxxx xxxx xxxx xxxx xxxx */ + /* MVN (immediate) cccc 0011 111x xxxx xxxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0x0fa00000, 0x03a00000, PROBES_DATA_PROCESSING_IMM, + REGS(0, ANY, 0, 0, 0)), + + /* AND (immediate) cccc 0010 000x xxxx xxxx xxxx xxxx xxxx */ + /* EOR (immediate) cccc 0010 001x xxxx xxxx xxxx xxxx xxxx */ + /* SUB (immediate) cccc 0010 010x xxxx xxxx xxxx xxxx xxxx */ + /* RSB (immediate) cccc 0010 011x xxxx xxxx xxxx xxxx xxxx */ + /* ADD (immediate) cccc 0010 100x xxxx xxxx xxxx xxxx xxxx */ + /* ADC (immediate) cccc 0010 101x xxxx xxxx xxxx xxxx xxxx */ + /* SBC (immediate) cccc 0010 110x xxxx xxxx xxxx xxxx xxxx */ + /* RSC (immediate) cccc 0010 111x xxxx xxxx xxxx xxxx xxxx */ + /* ORR (immediate) cccc 0011 100x xxxx xxxx xxxx xxxx xxxx */ + /* BIC (immediate) cccc 0011 110x xxxx xxxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0x0e000000, 0x02000000, PROBES_DATA_PROCESSING_IMM, + REGS(ANY, ANY, 0, 0, 0)), + + DECODE_END +}; + +static const union decode_item arm_cccc_0110_____xxx1_table[] = { + /* Media instructions */ + + /* SEL cccc 0110 1000 xxxx xxxx xxxx 1011 xxxx */ + DECODE_EMULATEX (0x0ff000f0, 0x068000b0, PROBES_SATURATE, + REGS(NOPC, NOPC, 0, 0, NOPC)), + + /* SSAT cccc 0110 101x xxxx xxxx xxxx xx01 xxxx */ + /* USAT cccc 0110 111x xxxx xxxx xxxx xx01 xxxx */ + DECODE_OR(0x0fa00030, 0x06a00010), + /* SSAT16 cccc 0110 1010 xxxx xxxx xxxx 0011 xxxx */ + /* USAT16 cccc 0110 1110 xxxx xxxx xxxx 0011 xxxx */ + DECODE_EMULATEX (0x0fb000f0, 0x06a00030, PROBES_SATURATE, + REGS(0, NOPC, 0, 0, NOPC)), + + /* REV cccc 0110 1011 xxxx xxxx xxxx 0011 xxxx */ + /* REV16 cccc 0110 1011 xxxx xxxx xxxx 1011 xxxx */ + /* RBIT cccc 0110 1111 xxxx xxxx xxxx 0011 xxxx */ + /* REVSH cccc 0110 1111 xxxx xxxx xxxx 1011 xxxx */ + DECODE_EMULATEX (0x0fb00070, 0x06b00030, PROBES_REV, + REGS(0, NOPC, 0, 0, NOPC)), + + /* ??? cccc 0110 0x00 xxxx xxxx xxxx xxx1 xxxx */ + DECODE_REJECT (0x0fb00010, 0x06000010), + /* ??? cccc 0110 0xxx xxxx xxxx xxxx 1011 xxxx */ + DECODE_REJECT (0x0f8000f0, 0x060000b0), + /* ??? cccc 0110 0xxx xxxx xxxx xxxx 1101 xxxx */ + DECODE_REJECT (0x0f8000f0, 0x060000d0), + /* SADD16 cccc 0110 0001 xxxx xxxx xxxx 0001 xxxx */ + /* SADDSUBX cccc 0110 0001 xxxx xxxx xxxx 0011 xxxx */ + /* SSUBADDX cccc 0110 0001 xxxx xxxx xxxx 0101 xxxx */ + /* SSUB16 cccc 0110 0001 xxxx xxxx xxxx 0111 xxxx */ + /* SADD8 cccc 0110 0001 xxxx xxxx xxxx 1001 xxxx */ + /* SSUB8 cccc 0110 0001 xxxx xxxx xxxx 1111 xxxx */ + /* QADD16 cccc 0110 0010 xxxx xxxx xxxx 0001 xxxx */ + /* QADDSUBX cccc 0110 0010 xxxx xxxx xxxx 0011 xxxx */ + /* QSUBADDX cccc 0110 0010 xxxx xxxx xxxx 0101 xxxx */ + /* QSUB16 cccc 0110 0010 xxxx xxxx xxxx 0111 xxxx */ + /* QADD8 cccc 0110 0010 xxxx xxxx xxxx 1001 xxxx */ + /* QSUB8 cccc 0110 0010 xxxx xxxx xxxx 1111 xxxx */ + /* SHADD16 cccc 0110 0011 xxxx xxxx xxxx 0001 xxxx */ + /* SHADDSUBX cccc 0110 0011 xxxx xxxx xxxx 0011 xxxx */ + /* SHSUBADDX cccc 0110 0011 xxxx xxxx xxxx 0101 xxxx */ + /* SHSUB16 cccc 0110 0011 xxxx xxxx xxxx 0111 xxxx */ + /* SHADD8 cccc 0110 0011 xxxx xxxx xxxx 1001 xxxx */ + /* SHSUB8 cccc 0110 0011 xxxx xxxx xxxx 1111 xxxx */ + /* UADD16 cccc 0110 0101 xxxx xxxx xxxx 0001 xxxx */ + /* UADDSUBX cccc 0110 0101 xxxx xxxx xxxx 0011 xxxx */ + /* USUBADDX cccc 0110 0101 xxxx xxxx xxxx 0101 xxxx */ + /* USUB16 cccc 0110 0101 xxxx xxxx xxxx 0111 xxxx */ + /* UADD8 cccc 0110 0101 xxxx xxxx xxxx 1001 xxxx */ + /* USUB8 cccc 0110 0101 xxxx xxxx xxxx 1111 xxxx */ + /* UQADD16 cccc 0110 0110 xxxx xxxx xxxx 0001 xxxx */ + /* UQADDSUBX cccc 0110 0110 xxxx xxxx xxxx 0011 xxxx */ + /* UQSUBADDX cccc 0110 0110 xxxx xxxx xxxx 0101 xxxx */ + /* UQSUB16 cccc 0110 0110 xxxx xxxx xxxx 0111 xxxx */ + /* UQADD8 cccc 0110 0110 xxxx xxxx xxxx 1001 xxxx */ + /* UQSUB8 cccc 0110 0110 xxxx xxxx xxxx 1111 xxxx */ + /* UHADD16 cccc 0110 0111 xxxx xxxx xxxx 0001 xxxx */ + /* UHADDSUBX cccc 0110 0111 xxxx xxxx xxxx 0011 xxxx */ + /* UHSUBADDX cccc 0110 0111 xxxx xxxx xxxx 0101 xxxx */ + /* UHSUB16 cccc 0110 0111 xxxx xxxx xxxx 0111 xxxx */ + /* UHADD8 cccc 0110 0111 xxxx xxxx xxxx 1001 xxxx */ + /* UHSUB8 cccc 0110 0111 xxxx xxxx xxxx 1111 xxxx */ + DECODE_EMULATEX (0x0f800010, 0x06000010, PROBES_MMI, + REGS(NOPC, NOPC, 0, 0, NOPC)), + + /* PKHBT cccc 0110 1000 xxxx xxxx xxxx x001 xxxx */ + /* PKHTB cccc 0110 1000 xxxx xxxx xxxx x101 xxxx */ + DECODE_EMULATEX (0x0ff00030, 0x06800010, PROBES_PACK, + REGS(NOPC, NOPC, 0, 0, NOPC)), + + /* ??? cccc 0110 1001 xxxx xxxx xxxx 0111 xxxx */ + /* ??? cccc 0110 1101 xxxx xxxx xxxx 0111 xxxx */ + DECODE_REJECT (0x0fb000f0, 0x06900070), + + /* SXTB16 cccc 0110 1000 1111 xxxx xxxx 0111 xxxx */ + /* SXTB cccc 0110 1010 1111 xxxx xxxx 0111 xxxx */ + /* SXTH cccc 0110 1011 1111 xxxx xxxx 0111 xxxx */ + /* UXTB16 cccc 0110 1100 1111 xxxx xxxx 0111 xxxx */ + /* UXTB cccc 0110 1110 1111 xxxx xxxx 0111 xxxx */ + /* UXTH cccc 0110 1111 1111 xxxx xxxx 0111 xxxx */ + DECODE_EMULATEX (0x0f8f00f0, 0x068f0070, PROBES_EXTEND, + REGS(0, NOPC, 0, 0, NOPC)), + + /* SXTAB16 cccc 0110 1000 xxxx xxxx xxxx 0111 xxxx */ + /* SXTAB cccc 0110 1010 xxxx xxxx xxxx 0111 xxxx */ + /* SXTAH cccc 0110 1011 xxxx xxxx xxxx 0111 xxxx */ + /* UXTAB16 cccc 0110 1100 xxxx xxxx xxxx 0111 xxxx */ + /* UXTAB cccc 0110 1110 xxxx xxxx xxxx 0111 xxxx */ + /* UXTAH cccc 0110 1111 xxxx xxxx xxxx 0111 xxxx */ + DECODE_EMULATEX (0x0f8000f0, 0x06800070, PROBES_EXTEND_ADD, + REGS(NOPCX, NOPC, 0, 0, NOPC)), + + DECODE_END +}; + +static const union decode_item arm_cccc_0111_____xxx1_table[] = { + /* Media instructions */ + + /* UNDEFINED cccc 0111 1111 xxxx xxxx xxxx 1111 xxxx */ + DECODE_REJECT (0x0ff000f0, 0x07f000f0), + + /* SMLALD cccc 0111 0100 xxxx xxxx xxxx 00x1 xxxx */ + /* SMLSLD cccc 0111 0100 xxxx xxxx xxxx 01x1 xxxx */ + DECODE_EMULATEX (0x0ff00090, 0x07400010, PROBES_MUL_ADD_LONG, + REGS(NOPC, NOPC, NOPC, 0, NOPC)), + + /* SMUAD cccc 0111 0000 xxxx 1111 xxxx 00x1 xxxx */ + /* SMUSD cccc 0111 0000 xxxx 1111 xxxx 01x1 xxxx */ + DECODE_OR (0x0ff0f090, 0x0700f010), + /* SMMUL cccc 0111 0101 xxxx 1111 xxxx 00x1 xxxx */ + DECODE_OR (0x0ff0f0d0, 0x0750f010), + /* USAD8 cccc 0111 1000 xxxx 1111 xxxx 0001 xxxx */ + DECODE_EMULATEX (0x0ff0f0f0, 0x0780f010, PROBES_MUL_ADD, + REGS(NOPC, 0, NOPC, 0, NOPC)), + + /* SMLAD cccc 0111 0000 xxxx xxxx xxxx 00x1 xxxx */ + /* SMLSD cccc 0111 0000 xxxx xxxx xxxx 01x1 xxxx */ + DECODE_OR (0x0ff00090, 0x07000010), + /* SMMLA cccc 0111 0101 xxxx xxxx xxxx 00x1 xxxx */ + DECODE_OR (0x0ff000d0, 0x07500010), + /* USADA8 cccc 0111 1000 xxxx xxxx xxxx 0001 xxxx */ + DECODE_EMULATEX (0x0ff000f0, 0x07800010, PROBES_MUL_ADD, + REGS(NOPC, NOPCX, NOPC, 0, NOPC)), + + /* SMMLS cccc 0111 0101 xxxx xxxx xxxx 11x1 xxxx */ + DECODE_EMULATEX (0x0ff000d0, 0x075000d0, PROBES_MUL_ADD, + REGS(NOPC, NOPC, NOPC, 0, NOPC)), + + /* SBFX cccc 0111 101x xxxx xxxx xxxx x101 xxxx */ + /* UBFX cccc 0111 111x xxxx xxxx xxxx x101 xxxx */ + DECODE_EMULATEX (0x0fa00070, 0x07a00050, PROBES_BITFIELD, + REGS(0, NOPC, 0, 0, NOPC)), + + /* BFC cccc 0111 110x xxxx xxxx xxxx x001 1111 */ + DECODE_EMULATEX (0x0fe0007f, 0x07c0001f, PROBES_BITFIELD, + REGS(0, NOPC, 0, 0, 0)), + + /* BFI cccc 0111 110x xxxx xxxx xxxx x001 xxxx */ + DECODE_EMULATEX (0x0fe00070, 0x07c00010, PROBES_BITFIELD, + REGS(0, NOPC, 0, 0, NOPCX)), + + DECODE_END +}; + +static const union decode_item arm_cccc_01xx_table[] = { + /* Load/store word and unsigned byte */ + + /* LDRB/STRB pc,[...] cccc 01xx x0xx xxxx xxxx xxxx xxxx xxxx */ + DECODE_REJECT (0x0c40f000, 0x0440f000), + + /* STRT cccc 01x0 x010 xxxx xxxx xxxx xxxx xxxx */ + /* LDRT cccc 01x0 x011 xxxx xxxx xxxx xxxx xxxx */ + /* STRBT cccc 01x0 x110 xxxx xxxx xxxx xxxx xxxx */ + /* LDRBT cccc 01x0 x111 xxxx xxxx xxxx xxxx xxxx */ + DECODE_REJECT (0x0d200000, 0x04200000), + + /* STR (immediate) cccc 010x x0x0 xxxx xxxx xxxx xxxx xxxx */ + /* STRB (immediate) cccc 010x x1x0 xxxx xxxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0x0e100000, 0x04000000, PROBES_STORE, + REGS(NOPCWB, ANY, 0, 0, 0)), + + /* LDR (immediate) cccc 010x x0x1 xxxx xxxx xxxx xxxx xxxx */ + /* LDRB (immediate) cccc 010x x1x1 xxxx xxxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0x0e100000, 0x04100000, PROBES_LOAD, + REGS(NOPCWB, ANY, 0, 0, 0)), + + /* STR (register) cccc 011x x0x0 xxxx xxxx xxxx xxxx xxxx */ + /* STRB (register) cccc 011x x1x0 xxxx xxxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0x0e100000, 0x06000000, PROBES_STORE, + REGS(NOPCWB, ANY, 0, 0, NOPC)), + + /* LDR (register) cccc 011x x0x1 xxxx xxxx xxxx xxxx xxxx */ + /* LDRB (register) cccc 011x x1x1 xxxx xxxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0x0e100000, 0x06100000, PROBES_LOAD, + REGS(NOPCWB, ANY, 0, 0, NOPC)), + + DECODE_END +}; + +static const union decode_item arm_cccc_100x_table[] = { + /* Block data transfer instructions */ + + /* LDM cccc 100x x0x1 xxxx xxxx xxxx xxxx xxxx */ + /* STM cccc 100x x0x0 xxxx xxxx xxxx xxxx xxxx */ + DECODE_CUSTOM (0x0e400000, 0x08000000, PROBES_LDMSTM), + + /* STM (user registers) cccc 100x x1x0 xxxx xxxx xxxx xxxx xxxx */ + /* LDM (user registers) cccc 100x x1x1 xxxx 0xxx xxxx xxxx xxxx */ + /* LDM (exception ret) cccc 100x x1x1 xxxx 1xxx xxxx xxxx xxxx */ + DECODE_END +}; + +const union decode_item probes_decode_arm_table[] = { + /* + * Unconditional instructions + * 1111 xxxx xxxx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_TABLE (0xf0000000, 0xf0000000, arm_1111_table), + + /* + * Miscellaneous instructions + * cccc 0001 0xx0 xxxx xxxx xxxx 0xxx xxxx + */ + DECODE_TABLE (0x0f900080, 0x01000000, arm_cccc_0001_0xx0____0xxx_table), + + /* + * Halfword multiply and multiply-accumulate + * cccc 0001 0xx0 xxxx xxxx xxxx 1xx0 xxxx + */ + DECODE_TABLE (0x0f900090, 0x01000080, arm_cccc_0001_0xx0____1xx0_table), + + /* + * Multiply and multiply-accumulate + * cccc 0000 xxxx xxxx xxxx xxxx 1001 xxxx + */ + DECODE_TABLE (0x0f0000f0, 0x00000090, arm_cccc_0000_____1001_table), + + /* + * Synchronization primitives + * cccc 0001 xxxx xxxx xxxx xxxx 1001 xxxx + */ + DECODE_TABLE (0x0f0000f0, 0x01000090, arm_cccc_0001_____1001_table), + + /* + * Extra load/store instructions + * cccc 000x xxxx xxxx xxxx xxxx 1xx1 xxxx + */ + DECODE_TABLE (0x0e000090, 0x00000090, arm_cccc_000x_____1xx1_table), + + /* + * Data-processing (register) + * cccc 000x xxxx xxxx xxxx xxxx xxx0 xxxx + * Data-processing (register-shifted register) + * cccc 000x xxxx xxxx xxxx xxxx 0xx1 xxxx + */ + DECODE_TABLE (0x0e000000, 0x00000000, arm_cccc_000x_table), + + /* + * Data-processing (immediate) + * cccc 001x xxxx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_TABLE (0x0e000000, 0x02000000, arm_cccc_001x_table), + + /* + * Media instructions + * cccc 011x xxxx xxxx xxxx xxxx xxx1 xxxx + */ + DECODE_TABLE (0x0f000010, 0x06000010, arm_cccc_0110_____xxx1_table), + DECODE_TABLE (0x0f000010, 0x07000010, arm_cccc_0111_____xxx1_table), + + /* + * Load/store word and unsigned byte + * cccc 01xx xxxx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_TABLE (0x0c000000, 0x04000000, arm_cccc_01xx_table), + + /* + * Block data transfer instructions + * cccc 100x xxxx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_TABLE (0x0e000000, 0x08000000, arm_cccc_100x_table), + + /* B cccc 1010 xxxx xxxx xxxx xxxx xxxx xxxx */ + /* BL cccc 1011 xxxx xxxx xxxx xxxx xxxx xxxx */ + DECODE_SIMULATE (0x0e000000, 0x0a000000, PROBES_BRANCH), + + /* + * Supervisor Call, and coprocessor instructions + */ + + /* MCRR cccc 1100 0100 xxxx xxxx xxxx xxxx xxxx */ + /* MRRC cccc 1100 0101 xxxx xxxx xxxx xxxx xxxx */ + /* LDC cccc 110x xxx1 xxxx xxxx xxxx xxxx xxxx */ + /* STC cccc 110x xxx0 xxxx xxxx xxxx xxxx xxxx */ + /* CDP cccc 1110 xxxx xxxx xxxx xxxx xxx0 xxxx */ + /* MCR cccc 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx */ + /* MRC cccc 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx */ + /* SVC cccc 1111 xxxx xxxx xxxx xxxx xxxx xxxx */ + DECODE_REJECT (0x0c000000, 0x0c000000), + + DECODE_END +}; +#ifdef CONFIG_ARM_KPROBES_TEST_MODULE +EXPORT_SYMBOL_GPL(probes_decode_arm_table); +#endif + +static void __kprobes arm_singlestep(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + regs->ARM_pc += 4; + asi->insn_handler(insn, asi, regs); +} + +/* Return: + * INSN_REJECTED If instruction is one not allowed to kprobe, + * INSN_GOOD If instruction is supported and uses instruction slot, + * INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot. + * + * For instructions we don't want to kprobe (INSN_REJECTED return result): + * These are generally ones that modify the processor state making + * them "hard" to simulate such as switches processor modes or + * make accesses in alternate modes. Any of these could be simulated + * if the work was put into it, but low return considering they + * should also be very rare. + */ +enum probes_insn __kprobes +arm_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, + bool emulate, const union decode_action *actions) +{ + asi->insn_singlestep = arm_singlestep; + asi->insn_check_cc = probes_condition_checks[insn>>28]; + return probes_decode_insn(insn, asi, probes_decode_arm_table, false, + emulate, actions); +} diff --git a/arch/arm/probes/decode-arm.h b/arch/arm/probes/decode-arm.h new file mode 100644 index 0000000..9c56b40 --- /dev/null +++ b/arch/arm/probes/decode-arm.h @@ -0,0 +1,75 @@ +/* + * arch/arm/probes/decode-arm.h + * + * Copyright 2013 Linaro Ltd. + * Written by: David A. Long + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef _ARM_KERNEL_PROBES_ARM_H +#define _ARM_KERNEL_PROBES_ARM_H + +#include "decode.h" + +enum probes_arm_action { + PROBES_EMULATE_NONE, + PROBES_SIMULATE_NOP, + PROBES_PRELOAD_IMM, + PROBES_PRELOAD_REG, + PROBES_BRANCH_IMM, + PROBES_BRANCH_REG, + PROBES_MRS, + PROBES_CLZ, + PROBES_SATURATING_ARITHMETIC, + PROBES_MUL1, + PROBES_MUL2, + PROBES_SWP, + PROBES_LDRSTRD, + PROBES_LOAD, + PROBES_STORE, + PROBES_LOAD_EXTRA, + PROBES_STORE_EXTRA, + PROBES_MOV_IP_SP, + PROBES_DATA_PROCESSING_REG, + PROBES_DATA_PROCESSING_IMM, + PROBES_MOV_HALFWORD, + PROBES_SEV, + PROBES_WFE, + PROBES_SATURATE, + PROBES_REV, + PROBES_MMI, + PROBES_PACK, + PROBES_EXTEND, + PROBES_EXTEND_ADD, + PROBES_MUL_ADD_LONG, + PROBES_MUL_ADD, + PROBES_BITFIELD, + PROBES_BRANCH, + PROBES_LDMSTM, + NUM_PROBES_ARM_ACTIONS +}; + +void __kprobes simulate_bbl(probes_opcode_t opcode, + struct arch_probes_insn *asi, struct pt_regs *regs); +void __kprobes simulate_blx1(probes_opcode_t opcode, + struct arch_probes_insn *asi, struct pt_regs *regs); +void __kprobes simulate_blx2bx(probes_opcode_t opcode, + struct arch_probes_insn *asi, struct pt_regs *regs); +void __kprobes simulate_mrs(probes_opcode_t opcode, + struct arch_probes_insn *asi, struct pt_regs *regs); +void __kprobes simulate_mov_ipsp(probes_opcode_t opcode, + struct arch_probes_insn *asi, struct pt_regs *regs); + +extern const union decode_item probes_decode_arm_table[]; + +enum probes_insn arm_probes_decode_insn(probes_opcode_t, + struct arch_probes_insn *, bool emulate, + const union decode_action *actions); + +#endif diff --git a/arch/arm/probes/decode-thumb.c b/arch/arm/probes/decode-thumb.c new file mode 100644 index 0000000..2f0453a --- /dev/null +++ b/arch/arm/probes/decode-thumb.c @@ -0,0 +1,882 @@ +/* + * arch/arm/probes/decode-thumb.c + * + * Copyright (C) 2011 Jon Medhurst . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include "decode.h" +#include "decode-thumb.h" + + +static const union decode_item t32_table_1110_100x_x0xx[] = { + /* Load/store multiple instructions */ + + /* Rn is PC 1110 100x x0xx 1111 xxxx xxxx xxxx xxxx */ + DECODE_REJECT (0xfe4f0000, 0xe80f0000), + + /* SRS 1110 1000 00x0 xxxx xxxx xxxx xxxx xxxx */ + /* RFE 1110 1000 00x1 xxxx xxxx xxxx xxxx xxxx */ + DECODE_REJECT (0xffc00000, 0xe8000000), + /* SRS 1110 1001 10x0 xxxx xxxx xxxx xxxx xxxx */ + /* RFE 1110 1001 10x1 xxxx xxxx xxxx xxxx xxxx */ + DECODE_REJECT (0xffc00000, 0xe9800000), + + /* STM Rn, {...pc} 1110 100x x0x0 xxxx 1xxx xxxx xxxx xxxx */ + DECODE_REJECT (0xfe508000, 0xe8008000), + /* LDM Rn, {...lr,pc} 1110 100x x0x1 xxxx 11xx xxxx xxxx xxxx */ + DECODE_REJECT (0xfe50c000, 0xe810c000), + /* LDM/STM Rn, {...sp} 1110 100x x0xx xxxx xx1x xxxx xxxx xxxx */ + DECODE_REJECT (0xfe402000, 0xe8002000), + + /* STMIA 1110 1000 10x0 xxxx xxxx xxxx xxxx xxxx */ + /* LDMIA 1110 1000 10x1 xxxx xxxx xxxx xxxx xxxx */ + /* STMDB 1110 1001 00x0 xxxx xxxx xxxx xxxx xxxx */ + /* LDMDB 1110 1001 00x1 xxxx xxxx xxxx xxxx xxxx */ + DECODE_CUSTOM (0xfe400000, 0xe8000000, PROBES_T32_LDMSTM), + + DECODE_END +}; + +static const union decode_item t32_table_1110_100x_x1xx[] = { + /* Load/store dual, load/store exclusive, table branch */ + + /* STRD (immediate) 1110 1000 x110 xxxx xxxx xxxx xxxx xxxx */ + /* LDRD (immediate) 1110 1000 x111 xxxx xxxx xxxx xxxx xxxx */ + DECODE_OR (0xff600000, 0xe8600000), + /* STRD (immediate) 1110 1001 x1x0 xxxx xxxx xxxx xxxx xxxx */ + /* LDRD (immediate) 1110 1001 x1x1 xxxx xxxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xff400000, 0xe9400000, PROBES_T32_LDRDSTRD, + REGS(NOPCWB, NOSPPC, NOSPPC, 0, 0)), + + /* TBB 1110 1000 1101 xxxx xxxx xxxx 0000 xxxx */ + /* TBH 1110 1000 1101 xxxx xxxx xxxx 0001 xxxx */ + DECODE_SIMULATEX(0xfff000e0, 0xe8d00000, PROBES_T32_TABLE_BRANCH, + REGS(NOSP, 0, 0, 0, NOSPPC)), + + /* STREX 1110 1000 0100 xxxx xxxx xxxx xxxx xxxx */ + /* LDREX 1110 1000 0101 xxxx xxxx xxxx xxxx xxxx */ + /* STREXB 1110 1000 1100 xxxx xxxx xxxx 0100 xxxx */ + /* STREXH 1110 1000 1100 xxxx xxxx xxxx 0101 xxxx */ + /* STREXD 1110 1000 1100 xxxx xxxx xxxx 0111 xxxx */ + /* LDREXB 1110 1000 1101 xxxx xxxx xxxx 0100 xxxx */ + /* LDREXH 1110 1000 1101 xxxx xxxx xxxx 0101 xxxx */ + /* LDREXD 1110 1000 1101 xxxx xxxx xxxx 0111 xxxx */ + /* And unallocated instructions... */ + DECODE_END +}; + +static const union decode_item t32_table_1110_101x[] = { + /* Data-processing (shifted register) */ + + /* TST 1110 1010 0001 xxxx xxxx 1111 xxxx xxxx */ + /* TEQ 1110 1010 1001 xxxx xxxx 1111 xxxx xxxx */ + DECODE_EMULATEX (0xff700f00, 0xea100f00, PROBES_T32_TST, + REGS(NOSPPC, 0, 0, 0, NOSPPC)), + + /* CMN 1110 1011 0001 xxxx xxxx 1111 xxxx xxxx */ + DECODE_OR (0xfff00f00, 0xeb100f00), + /* CMP 1110 1011 1011 xxxx xxxx 1111 xxxx xxxx */ + DECODE_EMULATEX (0xfff00f00, 0xebb00f00, PROBES_T32_TST, + REGS(NOPC, 0, 0, 0, NOSPPC)), + + /* MOV 1110 1010 010x 1111 xxxx xxxx xxxx xxxx */ + /* MVN 1110 1010 011x 1111 xxxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xffcf0000, 0xea4f0000, PROBES_T32_MOV, + REGS(0, 0, NOSPPC, 0, NOSPPC)), + + /* ??? 1110 1010 101x xxxx xxxx xxxx xxxx xxxx */ + /* ??? 1110 1010 111x xxxx xxxx xxxx xxxx xxxx */ + DECODE_REJECT (0xffa00000, 0xeaa00000), + /* ??? 1110 1011 001x xxxx xxxx xxxx xxxx xxxx */ + DECODE_REJECT (0xffe00000, 0xeb200000), + /* ??? 1110 1011 100x xxxx xxxx xxxx xxxx xxxx */ + DECODE_REJECT (0xffe00000, 0xeb800000), + /* ??? 1110 1011 111x xxxx xxxx xxxx xxxx xxxx */ + DECODE_REJECT (0xffe00000, 0xebe00000), + + /* ADD/SUB SP, SP, Rm, LSL #0..3 */ + /* 1110 1011 x0xx 1101 x000 1101 xx00 xxxx */ + DECODE_EMULATEX (0xff4f7f30, 0xeb0d0d00, PROBES_T32_ADDSUB, + REGS(SP, 0, SP, 0, NOSPPC)), + + /* ADD/SUB SP, SP, Rm, shift */ + /* 1110 1011 x0xx 1101 xxxx 1101 xxxx xxxx */ + DECODE_REJECT (0xff4f0f00, 0xeb0d0d00), + + /* ADD/SUB Rd, SP, Rm, shift */ + /* 1110 1011 x0xx 1101 xxxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xff4f0000, 0xeb0d0000, PROBES_T32_ADDSUB, + REGS(SP, 0, NOPC, 0, NOSPPC)), + + /* AND 1110 1010 000x xxxx xxxx xxxx xxxx xxxx */ + /* BIC 1110 1010 001x xxxx xxxx xxxx xxxx xxxx */ + /* ORR 1110 1010 010x xxxx xxxx xxxx xxxx xxxx */ + /* ORN 1110 1010 011x xxxx xxxx xxxx xxxx xxxx */ + /* EOR 1110 1010 100x xxxx xxxx xxxx xxxx xxxx */ + /* PKH 1110 1010 110x xxxx xxxx xxxx xxxx xxxx */ + /* ADD 1110 1011 000x xxxx xxxx xxxx xxxx xxxx */ + /* ADC 1110 1011 010x xxxx xxxx xxxx xxxx xxxx */ + /* SBC 1110 1011 011x xxxx xxxx xxxx xxxx xxxx */ + /* SUB 1110 1011 101x xxxx xxxx xxxx xxxx xxxx */ + /* RSB 1110 1011 110x xxxx xxxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xfe000000, 0xea000000, PROBES_T32_LOGICAL, + REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)), + + DECODE_END +}; + +static const union decode_item t32_table_1111_0x0x___0[] = { + /* Data-processing (modified immediate) */ + + /* TST 1111 0x00 0001 xxxx 0xxx 1111 xxxx xxxx */ + /* TEQ 1111 0x00 1001 xxxx 0xxx 1111 xxxx xxxx */ + DECODE_EMULATEX (0xfb708f00, 0xf0100f00, PROBES_T32_TST, + REGS(NOSPPC, 0, 0, 0, 0)), + + /* CMN 1111 0x01 0001 xxxx 0xxx 1111 xxxx xxxx */ + DECODE_OR (0xfbf08f00, 0xf1100f00), + /* CMP 1111 0x01 1011 xxxx 0xxx 1111 xxxx xxxx */ + DECODE_EMULATEX (0xfbf08f00, 0xf1b00f00, PROBES_T32_CMP, + REGS(NOPC, 0, 0, 0, 0)), + + /* MOV 1111 0x00 010x 1111 0xxx xxxx xxxx xxxx */ + /* MVN 1111 0x00 011x 1111 0xxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xfbcf8000, 0xf04f0000, PROBES_T32_MOV, + REGS(0, 0, NOSPPC, 0, 0)), + + /* ??? 1111 0x00 101x xxxx 0xxx xxxx xxxx xxxx */ + DECODE_REJECT (0xfbe08000, 0xf0a00000), + /* ??? 1111 0x00 110x xxxx 0xxx xxxx xxxx xxxx */ + /* ??? 1111 0x00 111x xxxx 0xxx xxxx xxxx xxxx */ + DECODE_REJECT (0xfbc08000, 0xf0c00000), + /* ??? 1111 0x01 001x xxxx 0xxx xxxx xxxx xxxx */ + DECODE_REJECT (0xfbe08000, 0xf1200000), + /* ??? 1111 0x01 100x xxxx 0xxx xxxx xxxx xxxx */ + DECODE_REJECT (0xfbe08000, 0xf1800000), + /* ??? 1111 0x01 111x xxxx 0xxx xxxx xxxx xxxx */ + DECODE_REJECT (0xfbe08000, 0xf1e00000), + + /* ADD Rd, SP, #imm 1111 0x01 000x 1101 0xxx xxxx xxxx xxxx */ + /* SUB Rd, SP, #imm 1111 0x01 101x 1101 0xxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xfb4f8000, 0xf10d0000, PROBES_T32_ADDSUB, + REGS(SP, 0, NOPC, 0, 0)), + + /* AND 1111 0x00 000x xxxx 0xxx xxxx xxxx xxxx */ + /* BIC 1111 0x00 001x xxxx 0xxx xxxx xxxx xxxx */ + /* ORR 1111 0x00 010x xxxx 0xxx xxxx xxxx xxxx */ + /* ORN 1111 0x00 011x xxxx 0xxx xxxx xxxx xxxx */ + /* EOR 1111 0x00 100x xxxx 0xxx xxxx xxxx xxxx */ + /* ADD 1111 0x01 000x xxxx 0xxx xxxx xxxx xxxx */ + /* ADC 1111 0x01 010x xxxx 0xxx xxxx xxxx xxxx */ + /* SBC 1111 0x01 011x xxxx 0xxx xxxx xxxx xxxx */ + /* SUB 1111 0x01 101x xxxx 0xxx xxxx xxxx xxxx */ + /* RSB 1111 0x01 110x xxxx 0xxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xfa008000, 0xf0000000, PROBES_T32_LOGICAL, + REGS(NOSPPC, 0, NOSPPC, 0, 0)), + + DECODE_END +}; + +static const union decode_item t32_table_1111_0x1x___0[] = { + /* Data-processing (plain binary immediate) */ + + /* ADDW Rd, PC, #imm 1111 0x10 0000 1111 0xxx xxxx xxxx xxxx */ + DECODE_OR (0xfbff8000, 0xf20f0000), + /* SUBW Rd, PC, #imm 1111 0x10 1010 1111 0xxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xfbff8000, 0xf2af0000, PROBES_T32_ADDWSUBW_PC, + REGS(PC, 0, NOSPPC, 0, 0)), + + /* ADDW SP, SP, #imm 1111 0x10 0000 1101 0xxx 1101 xxxx xxxx */ + DECODE_OR (0xfbff8f00, 0xf20d0d00), + /* SUBW SP, SP, #imm 1111 0x10 1010 1101 0xxx 1101 xxxx xxxx */ + DECODE_EMULATEX (0xfbff8f00, 0xf2ad0d00, PROBES_T32_ADDWSUBW, + REGS(SP, 0, SP, 0, 0)), + + /* ADDW 1111 0x10 0000 xxxx 0xxx xxxx xxxx xxxx */ + DECODE_OR (0xfbf08000, 0xf2000000), + /* SUBW 1111 0x10 1010 xxxx 0xxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xfbf08000, 0xf2a00000, PROBES_T32_ADDWSUBW, + REGS(NOPCX, 0, NOSPPC, 0, 0)), + + /* MOVW 1111 0x10 0100 xxxx 0xxx xxxx xxxx xxxx */ + /* MOVT 1111 0x10 1100 xxxx 0xxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xfb708000, 0xf2400000, PROBES_T32_MOVW, + REGS(0, 0, NOSPPC, 0, 0)), + + /* SSAT16 1111 0x11 0010 xxxx 0000 xxxx 00xx xxxx */ + /* SSAT 1111 0x11 00x0 xxxx 0xxx xxxx xxxx xxxx */ + /* USAT16 1111 0x11 1010 xxxx 0000 xxxx 00xx xxxx */ + /* USAT 1111 0x11 10x0 xxxx 0xxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xfb508000, 0xf3000000, PROBES_T32_SAT, + REGS(NOSPPC, 0, NOSPPC, 0, 0)), + + /* SFBX 1111 0x11 0100 xxxx 0xxx xxxx xxxx xxxx */ + /* UFBX 1111 0x11 1100 xxxx 0xxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xfb708000, 0xf3400000, PROBES_T32_BITFIELD, + REGS(NOSPPC, 0, NOSPPC, 0, 0)), + + /* BFC 1111 0x11 0110 1111 0xxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xfbff8000, 0xf36f0000, PROBES_T32_BITFIELD, + REGS(0, 0, NOSPPC, 0, 0)), + + /* BFI 1111 0x11 0110 xxxx 0xxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xfbf08000, 0xf3600000, PROBES_T32_BITFIELD, + REGS(NOSPPCX, 0, NOSPPC, 0, 0)), + + DECODE_END +}; + +static const union decode_item t32_table_1111_0xxx___1[] = { + /* Branches and miscellaneous control */ + + /* YIELD 1111 0011 1010 xxxx 10x0 x000 0000 0001 */ + DECODE_OR (0xfff0d7ff, 0xf3a08001), + /* SEV 1111 0011 1010 xxxx 10x0 x000 0000 0100 */ + DECODE_EMULATE (0xfff0d7ff, 0xf3a08004, PROBES_T32_SEV), + /* NOP 1111 0011 1010 xxxx 10x0 x000 0000 0000 */ + /* WFE 1111 0011 1010 xxxx 10x0 x000 0000 0010 */ + /* WFI 1111 0011 1010 xxxx 10x0 x000 0000 0011 */ + DECODE_SIMULATE (0xfff0d7fc, 0xf3a08000, PROBES_T32_WFE), + + /* MRS Rd, CPSR 1111 0011 1110 xxxx 10x0 xxxx xxxx xxxx */ + DECODE_SIMULATEX(0xfff0d000, 0xf3e08000, PROBES_T32_MRS, + REGS(0, 0, NOSPPC, 0, 0)), + + /* + * Unsupported instructions + * 1111 0x11 1xxx xxxx 10x0 xxxx xxxx xxxx + * + * MSR 1111 0011 100x xxxx 10x0 xxxx xxxx xxxx + * DBG hint 1111 0011 1010 xxxx 10x0 x000 1111 xxxx + * Unallocated hints 1111 0011 1010 xxxx 10x0 x000 xxxx xxxx + * CPS 1111 0011 1010 xxxx 10x0 xxxx xxxx xxxx + * CLREX/DSB/DMB/ISB 1111 0011 1011 xxxx 10x0 xxxx xxxx xxxx + * BXJ 1111 0011 1100 xxxx 10x0 xxxx xxxx xxxx + * SUBS PC,LR,# 1111 0011 1101 xxxx 10x0 xxxx xxxx xxxx + * MRS Rd, SPSR 1111 0011 1111 xxxx 10x0 xxxx xxxx xxxx + * SMC 1111 0111 1111 xxxx 1000 xxxx xxxx xxxx + * UNDEFINED 1111 0111 1111 xxxx 1010 xxxx xxxx xxxx + * ??? 1111 0111 1xxx xxxx 1010 xxxx xxxx xxxx + */ + DECODE_REJECT (0xfb80d000, 0xf3808000), + + /* Bcc 1111 0xxx xxxx xxxx 10x0 xxxx xxxx xxxx */ + DECODE_CUSTOM (0xf800d000, 0xf0008000, PROBES_T32_BRANCH_COND), + + /* BLX 1111 0xxx xxxx xxxx 11x0 xxxx xxxx xxx0 */ + DECODE_OR (0xf800d001, 0xf000c000), + /* B 1111 0xxx xxxx xxxx 10x1 xxxx xxxx xxxx */ + /* BL 1111 0xxx xxxx xxxx 11x1 xxxx xxxx xxxx */ + DECODE_SIMULATE (0xf8009000, 0xf0009000, PROBES_T32_BRANCH), + + DECODE_END +}; + +static const union decode_item t32_table_1111_100x_x0x1__1111[] = { + /* Memory hints */ + + /* PLD (literal) 1111 1000 x001 1111 1111 xxxx xxxx xxxx */ + /* PLI (literal) 1111 1001 x001 1111 1111 xxxx xxxx xxxx */ + DECODE_SIMULATE (0xfe7ff000, 0xf81ff000, PROBES_T32_PLDI), + + /* PLD{W} (immediate) 1111 1000 10x1 xxxx 1111 xxxx xxxx xxxx */ + DECODE_OR (0xffd0f000, 0xf890f000), + /* PLD{W} (immediate) 1111 1000 00x1 xxxx 1111 1100 xxxx xxxx */ + DECODE_OR (0xffd0ff00, 0xf810fc00), + /* PLI (immediate) 1111 1001 1001 xxxx 1111 xxxx xxxx xxxx */ + DECODE_OR (0xfff0f000, 0xf990f000), + /* PLI (immediate) 1111 1001 0001 xxxx 1111 1100 xxxx xxxx */ + DECODE_SIMULATEX(0xfff0ff00, 0xf910fc00, PROBES_T32_PLDI, + REGS(NOPCX, 0, 0, 0, 0)), + + /* PLD{W} (register) 1111 1000 00x1 xxxx 1111 0000 00xx xxxx */ + DECODE_OR (0xffd0ffc0, 0xf810f000), + /* PLI (register) 1111 1001 0001 xxxx 1111 0000 00xx xxxx */ + DECODE_SIMULATEX(0xfff0ffc0, 0xf910f000, PROBES_T32_PLDI, + REGS(NOPCX, 0, 0, 0, NOSPPC)), + + /* Other unallocated instructions... */ + DECODE_END +}; + +static const union decode_item t32_table_1111_100x[] = { + /* Store/Load single data item */ + + /* ??? 1111 100x x11x xxxx xxxx xxxx xxxx xxxx */ + DECODE_REJECT (0xfe600000, 0xf8600000), + + /* ??? 1111 1001 0101 xxxx xxxx xxxx xxxx xxxx */ + DECODE_REJECT (0xfff00000, 0xf9500000), + + /* ??? 1111 100x 0xxx xxxx xxxx 10x0 xxxx xxxx */ + DECODE_REJECT (0xfe800d00, 0xf8000800), + + /* STRBT 1111 1000 0000 xxxx xxxx 1110 xxxx xxxx */ + /* STRHT 1111 1000 0010 xxxx xxxx 1110 xxxx xxxx */ + /* STRT 1111 1000 0100 xxxx xxxx 1110 xxxx xxxx */ + /* LDRBT 1111 1000 0001 xxxx xxxx 1110 xxxx xxxx */ + /* LDRSBT 1111 1001 0001 xxxx xxxx 1110 xxxx xxxx */ + /* LDRHT 1111 1000 0011 xxxx xxxx 1110 xxxx xxxx */ + /* LDRSHT 1111 1001 0011 xxxx xxxx 1110 xxxx xxxx */ + /* LDRT 1111 1000 0101 xxxx xxxx 1110 xxxx xxxx */ + DECODE_REJECT (0xfe800f00, 0xf8000e00), + + /* STR{,B,H} Rn,[PC...] 1111 1000 xxx0 1111 xxxx xxxx xxxx xxxx */ + DECODE_REJECT (0xff1f0000, 0xf80f0000), + + /* STR{,B,H} PC,[Rn...] 1111 1000 xxx0 xxxx 1111 xxxx xxxx xxxx */ + DECODE_REJECT (0xff10f000, 0xf800f000), + + /* LDR (literal) 1111 1000 x101 1111 xxxx xxxx xxxx xxxx */ + DECODE_SIMULATEX(0xff7f0000, 0xf85f0000, PROBES_T32_LDR_LIT, + REGS(PC, ANY, 0, 0, 0)), + + /* STR (immediate) 1111 1000 0100 xxxx xxxx 1xxx xxxx xxxx */ + /* LDR (immediate) 1111 1000 0101 xxxx xxxx 1xxx xxxx xxxx */ + DECODE_OR (0xffe00800, 0xf8400800), + /* STR (immediate) 1111 1000 1100 xxxx xxxx xxxx xxxx xxxx */ + /* LDR (immediate) 1111 1000 1101 xxxx xxxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xffe00000, 0xf8c00000, PROBES_T32_LDRSTR, + REGS(NOPCX, ANY, 0, 0, 0)), + + /* STR (register) 1111 1000 0100 xxxx xxxx 0000 00xx xxxx */ + /* LDR (register) 1111 1000 0101 xxxx xxxx 0000 00xx xxxx */ + DECODE_EMULATEX (0xffe00fc0, 0xf8400000, PROBES_T32_LDRSTR, + REGS(NOPCX, ANY, 0, 0, NOSPPC)), + + /* LDRB (literal) 1111 1000 x001 1111 xxxx xxxx xxxx xxxx */ + /* LDRSB (literal) 1111 1001 x001 1111 xxxx xxxx xxxx xxxx */ + /* LDRH (literal) 1111 1000 x011 1111 xxxx xxxx xxxx xxxx */ + /* LDRSH (literal) 1111 1001 x011 1111 xxxx xxxx xxxx xxxx */ + DECODE_SIMULATEX(0xfe5f0000, 0xf81f0000, PROBES_T32_LDR_LIT, + REGS(PC, NOSPPCX, 0, 0, 0)), + + /* STRB (immediate) 1111 1000 0000 xxxx xxxx 1xxx xxxx xxxx */ + /* STRH (immediate) 1111 1000 0010 xxxx xxxx 1xxx xxxx xxxx */ + /* LDRB (immediate) 1111 1000 0001 xxxx xxxx 1xxx xxxx xxxx */ + /* LDRSB (immediate) 1111 1001 0001 xxxx xxxx 1xxx xxxx xxxx */ + /* LDRH (immediate) 1111 1000 0011 xxxx xxxx 1xxx xxxx xxxx */ + /* LDRSH (immediate) 1111 1001 0011 xxxx xxxx 1xxx xxxx xxxx */ + DECODE_OR (0xfec00800, 0xf8000800), + /* STRB (immediate) 1111 1000 1000 xxxx xxxx xxxx xxxx xxxx */ + /* STRH (immediate) 1111 1000 1010 xxxx xxxx xxxx xxxx xxxx */ + /* LDRB (immediate) 1111 1000 1001 xxxx xxxx xxxx xxxx xxxx */ + /* LDRSB (immediate) 1111 1001 1001 xxxx xxxx xxxx xxxx xxxx */ + /* LDRH (immediate) 1111 1000 1011 xxxx xxxx xxxx xxxx xxxx */ + /* LDRSH (immediate) 1111 1001 1011 xxxx xxxx xxxx xxxx xxxx */ + DECODE_EMULATEX (0xfec00000, 0xf8800000, PROBES_T32_LDRSTR, + REGS(NOPCX, NOSPPCX, 0, 0, 0)), + + /* STRB (register) 1111 1000 0000 xxxx xxxx 0000 00xx xxxx */ + /* STRH (register) 1111 1000 0010 xxxx xxxx 0000 00xx xxxx */ + /* LDRB (register) 1111 1000 0001 xxxx xxxx 0000 00xx xxxx */ + /* LDRSB (register) 1111 1001 0001 xxxx xxxx 0000 00xx xxxx */ + /* LDRH (register) 1111 1000 0011 xxxx xxxx 0000 00xx xxxx */ + /* LDRSH (register) 1111 1001 0011 xxxx xxxx 0000 00xx xxxx */ + DECODE_EMULATEX (0xfe800fc0, 0xf8000000, PROBES_T32_LDRSTR, + REGS(NOPCX, NOSPPCX, 0, 0, NOSPPC)), + + /* Other unallocated instructions... */ + DECODE_END +}; + +static const union decode_item t32_table_1111_1010___1111[] = { + /* Data-processing (register) */ + + /* ??? 1111 1010 011x xxxx 1111 xxxx 1xxx xxxx */ + DECODE_REJECT (0xffe0f080, 0xfa60f080), + + /* SXTH 1111 1010 0000 1111 1111 xxxx 1xxx xxxx */ + /* UXTH 1111 1010 0001 1111 1111 xxxx 1xxx xxxx */ + /* SXTB16 1111 1010 0010 1111 1111 xxxx 1xxx xxxx */ + /* UXTB16 1111 1010 0011 1111 1111 xxxx 1xxx xxxx */ + /* SXTB 1111 1010 0100 1111 1111 xxxx 1xxx xxxx */ + /* UXTB 1111 1010 0101 1111 1111 xxxx 1xxx xxxx */ + DECODE_EMULATEX (0xff8ff080, 0xfa0ff080, PROBES_T32_SIGN_EXTEND, + REGS(0, 0, NOSPPC, 0, NOSPPC)), + + + /* ??? 1111 1010 1xxx xxxx 1111 xxxx 0x11 xxxx */ + DECODE_REJECT (0xff80f0b0, 0xfa80f030), + /* ??? 1111 1010 1x11 xxxx 1111 xxxx 0xxx xxxx */ + DECODE_REJECT (0xffb0f080, 0xfab0f000), + + /* SADD16 1111 1010 1001 xxxx 1111 xxxx 0000 xxxx */ + /* SASX 1111 1010 1010 xxxx 1111 xxxx 0000 xxxx */ + /* SSAX 1111 1010 1110 xxxx 1111 xxxx 0000 xxxx */ + /* SSUB16 1111 1010 1101 xxxx 1111 xxxx 0000 xxxx */ + /* SADD8 1111 1010 1000 xxxx 1111 xxxx 0000 xxxx */ + /* SSUB8 1111 1010 1100 xxxx 1111 xxxx 0000 xxxx */ + + /* QADD16 1111 1010 1001 xxxx 1111 xxxx 0001 xxxx */ + /* QASX 1111 1010 1010 xxxx 1111 xxxx 0001 xxxx */ + /* QSAX 1111 1010 1110 xxxx 1111 xxxx 0001 xxxx */ + /* QSUB16 1111 1010 1101 xxxx 1111 xxxx 0001 xxxx */ + /* QADD8 1111 1010 1000 xxxx 1111 xxxx 0001 xxxx */ + /* QSUB8 1111 1010 1100 xxxx 1111 xxxx 0001 xxxx */ + + /* SHADD16 1111 1010 1001 xxxx 1111 xxxx 0010 xxxx */ + /* SHASX 1111 1010 1010 xxxx 1111 xxxx 0010 xxxx */ + /* SHSAX 1111 1010 1110 xxxx 1111 xxxx 0010 xxxx */ + /* SHSUB16 1111 1010 1101 xxxx 1111 xxxx 0010 xxxx */ + /* SHADD8 1111 1010 1000 xxxx 1111 xxxx 0010 xxxx */ + /* SHSUB8 1111 1010 1100 xxxx 1111 xxxx 0010 xxxx */ + + /* UADD16 1111 1010 1001 xxxx 1111 xxxx 0100 xxxx */ + /* UASX 1111 1010 1010 xxxx 1111 xxxx 0100 xxxx */ + /* USAX 1111 1010 1110 xxxx 1111 xxxx 0100 xxxx */ + /* USUB16 1111 1010 1101 xxxx 1111 xxxx 0100 xxxx */ + /* UADD8 1111 1010 1000 xxxx 1111 xxxx 0100 xxxx */ + /* USUB8 1111 1010 1100 xxxx 1111 xxxx 0100 xxxx */ + + /* UQADD16 1111 1010 1001 xxxx 1111 xxxx 0101 xxxx */ + /* UQASX 1111 1010 1010 xxxx 1111 xxxx 0101 xxxx */ + /* UQSAX 1111 1010 1110 xxxx 1111 xxxx 0101 xxxx */ + /* UQSUB16 1111 1010 1101 xxxx 1111 xxxx 0101 xxxx */ + /* UQADD8 1111 1010 1000 xxxx 1111 xxxx 0101 xxxx */ + /* UQSUB8 1111 1010 1100 xxxx 1111 xxxx 0101 xxxx */ + + /* UHADD16 1111 1010 1001 xxxx 1111 xxxx 0110 xxxx */ + /* UHASX 1111 1010 1010 xxxx 1111 xxxx 0110 xxxx */ + /* UHSAX 1111 1010 1110 xxxx 1111 xxxx 0110 xxxx */ + /* UHSUB16 1111 1010 1101 xxxx 1111 xxxx 0110 xxxx */ + /* UHADD8 1111 1010 1000 xxxx 1111 xxxx 0110 xxxx */ + /* UHSUB8 1111 1010 1100 xxxx 1111 xxxx 0110 xxxx */ + DECODE_OR (0xff80f080, 0xfa80f000), + + /* SXTAH 1111 1010 0000 xxxx 1111 xxxx 1xxx xxxx */ + /* UXTAH 1111 1010 0001 xxxx 1111 xxxx 1xxx xxxx */ + /* SXTAB16 1111 1010 0010 xxxx 1111 xxxx 1xxx xxxx */ + /* UXTAB16 1111 1010 0011 xxxx 1111 xxxx 1xxx xxxx */ + /* SXTAB 1111 1010 0100 xxxx 1111 xxxx 1xxx xxxx */ + /* UXTAB 1111 1010 0101 xxxx 1111 xxxx 1xxx xxxx */ + DECODE_OR (0xff80f080, 0xfa00f080), + + /* QADD 1111 1010 1000 xxxx 1111 xxxx 1000 xxxx */ + /* QDADD 1111 1010 1000 xxxx 1111 xxxx 1001 xxxx */ + /* QSUB 1111 1010 1000 xxxx 1111 xxxx 1010 xxxx */ + /* QDSUB 1111 1010 1000 xxxx 1111 xxxx 1011 xxxx */ + DECODE_OR (0xfff0f0c0, 0xfa80f080), + + /* SEL 1111 1010 1010 xxxx 1111 xxxx 1000 xxxx */ + DECODE_OR (0xfff0f0f0, 0xfaa0f080), + + /* LSL 1111 1010 000x xxxx 1111 xxxx 0000 xxxx */ + /* LSR 1111 1010 001x xxxx 1111 xxxx 0000 xxxx */ + /* ASR 1111 1010 010x xxxx 1111 xxxx 0000 xxxx */ + /* ROR 1111 1010 011x xxxx 1111 xxxx 0000 xxxx */ + DECODE_EMULATEX (0xff80f0f0, 0xfa00f000, PROBES_T32_MEDIA, + REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)), + + /* CLZ 1111 1010 1010 xxxx 1111 xxxx 1000 xxxx */ + DECODE_OR (0xfff0f0f0, 0xfab0f080), + + /* REV 1111 1010 1001 xxxx 1111 xxxx 1000 xxxx */ + /* REV16 1111 1010 1001 xxxx 1111 xxxx 1001 xxxx */ + /* RBIT 1111 1010 1001 xxxx 1111 xxxx 1010 xxxx */ + /* REVSH 1111 1010 1001 xxxx 1111 xxxx 1011 xxxx */ + DECODE_EMULATEX (0xfff0f0c0, 0xfa90f080, PROBES_T32_REVERSE, + REGS(NOSPPC, 0, NOSPPC, 0, SAMEAS16)), + + /* Other unallocated instructions... */ + DECODE_END +}; + +static const union decode_item t32_table_1111_1011_0[] = { + /* Multiply, multiply accumulate, and absolute difference */ + + /* ??? 1111 1011 0000 xxxx 1111 xxxx 0001 xxxx */ + DECODE_REJECT (0xfff0f0f0, 0xfb00f010), + /* ??? 1111 1011 0111 xxxx 1111 xxxx 0001 xxxx */ + DECODE_REJECT (0xfff0f0f0, 0xfb70f010), + + /* SMULxy 1111 1011 0001 xxxx 1111 xxxx 00xx xxxx */ + DECODE_OR (0xfff0f0c0, 0xfb10f000), + /* MUL 1111 1011 0000 xxxx 1111 xxxx 0000 xxxx */ + /* SMUAD{X} 1111 1011 0010 xxxx 1111 xxxx 000x xxxx */ + /* SMULWy 1111 1011 0011 xxxx 1111 xxxx 000x xxxx */ + /* SMUSD{X} 1111 1011 0100 xxxx 1111 xxxx 000x xxxx */ + /* SMMUL{R} 1111 1011 0101 xxxx 1111 xxxx 000x xxxx */ + /* USAD8 1111 1011 0111 xxxx 1111 xxxx 0000 xxxx */ + DECODE_EMULATEX (0xff80f0e0, 0xfb00f000, PROBES_T32_MUL_ADD, + REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)), + + /* ??? 1111 1011 0111 xxxx xxxx xxxx 0001 xxxx */ + DECODE_REJECT (0xfff000f0, 0xfb700010), + + /* SMLAxy 1111 1011 0001 xxxx xxxx xxxx 00xx xxxx */ + DECODE_OR (0xfff000c0, 0xfb100000), + /* MLA 1111 1011 0000 xxxx xxxx xxxx 0000 xxxx */ + /* MLS 1111 1011 0000 xxxx xxxx xxxx 0001 xxxx */ + /* SMLAD{X} 1111 1011 0010 xxxx xxxx xxxx 000x xxxx */ + /* SMLAWy 1111 1011 0011 xxxx xxxx xxxx 000x xxxx */ + /* SMLSD{X} 1111 1011 0100 xxxx xxxx xxxx 000x xxxx */ + /* SMMLA{R} 1111 1011 0101 xxxx xxxx xxxx 000x xxxx */ + /* SMMLS{R} 1111 1011 0110 xxxx xxxx xxxx 000x xxxx */ + /* USADA8 1111 1011 0111 xxxx xxxx xxxx 0000 xxxx */ + DECODE_EMULATEX (0xff8000c0, 0xfb000000, PROBES_T32_MUL_ADD2, + REGS(NOSPPC, NOSPPCX, NOSPPC, 0, NOSPPC)), + + /* Other unallocated instructions... */ + DECODE_END +}; + +static const union decode_item t32_table_1111_1011_1[] = { + /* Long multiply, long multiply accumulate, and divide */ + + /* UMAAL 1111 1011 1110 xxxx xxxx xxxx 0110 xxxx */ + DECODE_OR (0xfff000f0, 0xfbe00060), + /* SMLALxy 1111 1011 1100 xxxx xxxx xxxx 10xx xxxx */ + DECODE_OR (0xfff000c0, 0xfbc00080), + /* SMLALD{X} 1111 1011 1100 xxxx xxxx xxxx 110x xxxx */ + /* SMLSLD{X} 1111 1011 1101 xxxx xxxx xxxx 110x xxxx */ + DECODE_OR (0xffe000e0, 0xfbc000c0), + /* SMULL 1111 1011 1000 xxxx xxxx xxxx 0000 xxxx */ + /* UMULL 1111 1011 1010 xxxx xxxx xxxx 0000 xxxx */ + /* SMLAL 1111 1011 1100 xxxx xxxx xxxx 0000 xxxx */ + /* UMLAL 1111 1011 1110 xxxx xxxx xxxx 0000 xxxx */ + DECODE_EMULATEX (0xff9000f0, 0xfb800000, PROBES_T32_MUL_ADD_LONG, + REGS(NOSPPC, NOSPPC, NOSPPC, 0, NOSPPC)), + + /* SDIV 1111 1011 1001 xxxx xxxx xxxx 1111 xxxx */ + /* UDIV 1111 1011 1011 xxxx xxxx xxxx 1111 xxxx */ + /* Other unallocated instructions... */ + DECODE_END +}; + +const union decode_item probes_decode_thumb32_table[] = { + + /* + * Load/store multiple instructions + * 1110 100x x0xx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_TABLE (0xfe400000, 0xe8000000, t32_table_1110_100x_x0xx), + + /* + * Load/store dual, load/store exclusive, table branch + * 1110 100x x1xx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_TABLE (0xfe400000, 0xe8400000, t32_table_1110_100x_x1xx), + + /* + * Data-processing (shifted register) + * 1110 101x xxxx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_TABLE (0xfe000000, 0xea000000, t32_table_1110_101x), + + /* + * Coprocessor instructions + * 1110 11xx xxxx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_REJECT (0xfc000000, 0xec000000), + + /* + * Data-processing (modified immediate) + * 1111 0x0x xxxx xxxx 0xxx xxxx xxxx xxxx + */ + DECODE_TABLE (0xfa008000, 0xf0000000, t32_table_1111_0x0x___0), + + /* + * Data-processing (plain binary immediate) + * 1111 0x1x xxxx xxxx 0xxx xxxx xxxx xxxx + */ + DECODE_TABLE (0xfa008000, 0xf2000000, t32_table_1111_0x1x___0), + + /* + * Branches and miscellaneous control + * 1111 0xxx xxxx xxxx 1xxx xxxx xxxx xxxx + */ + DECODE_TABLE (0xf8008000, 0xf0008000, t32_table_1111_0xxx___1), + + /* + * Advanced SIMD element or structure load/store instructions + * 1111 1001 xxx0 xxxx xxxx xxxx xxxx xxxx + */ + DECODE_REJECT (0xff100000, 0xf9000000), + + /* + * Memory hints + * 1111 100x x0x1 xxxx 1111 xxxx xxxx xxxx + */ + DECODE_TABLE (0xfe50f000, 0xf810f000, t32_table_1111_100x_x0x1__1111), + + /* + * Store single data item + * 1111 1000 xxx0 xxxx xxxx xxxx xxxx xxxx + * Load single data items + * 1111 100x xxx1 xxxx xxxx xxxx xxxx xxxx + */ + DECODE_TABLE (0xfe000000, 0xf8000000, t32_table_1111_100x), + + /* + * Data-processing (register) + * 1111 1010 xxxx xxxx 1111 xxxx xxxx xxxx + */ + DECODE_TABLE (0xff00f000, 0xfa00f000, t32_table_1111_1010___1111), + + /* + * Multiply, multiply accumulate, and absolute difference + * 1111 1011 0xxx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_TABLE (0xff800000, 0xfb000000, t32_table_1111_1011_0), + + /* + * Long multiply, long multiply accumulate, and divide + * 1111 1011 1xxx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_TABLE (0xff800000, 0xfb800000, t32_table_1111_1011_1), + + /* + * Coprocessor instructions + * 1111 11xx xxxx xxxx xxxx xxxx xxxx xxxx + */ + DECODE_END +}; +#ifdef CONFIG_ARM_KPROBES_TEST_MODULE +EXPORT_SYMBOL_GPL(probes_decode_thumb32_table); +#endif + +static const union decode_item t16_table_1011[] = { + /* Miscellaneous 16-bit instructions */ + + /* ADD (SP plus immediate) 1011 0000 0xxx xxxx */ + /* SUB (SP minus immediate) 1011 0000 1xxx xxxx */ + DECODE_SIMULATE (0xff00, 0xb000, PROBES_T16_ADD_SP), + + /* CBZ 1011 00x1 xxxx xxxx */ + /* CBNZ 1011 10x1 xxxx xxxx */ + DECODE_SIMULATE (0xf500, 0xb100, PROBES_T16_CBZ), + + /* SXTH 1011 0010 00xx xxxx */ + /* SXTB 1011 0010 01xx xxxx */ + /* UXTH 1011 0010 10xx xxxx */ + /* UXTB 1011 0010 11xx xxxx */ + /* REV 1011 1010 00xx xxxx */ + /* REV16 1011 1010 01xx xxxx */ + /* ??? 1011 1010 10xx xxxx */ + /* REVSH 1011 1010 11xx xxxx */ + DECODE_REJECT (0xffc0, 0xba80), + DECODE_EMULATE (0xf500, 0xb000, PROBES_T16_SIGN_EXTEND), + + /* PUSH 1011 010x xxxx xxxx */ + DECODE_CUSTOM (0xfe00, 0xb400, PROBES_T16_PUSH), + /* POP 1011 110x xxxx xxxx */ + DECODE_CUSTOM (0xfe00, 0xbc00, PROBES_T16_POP), + + /* + * If-Then, and hints + * 1011 1111 xxxx xxxx + */ + + /* YIELD 1011 1111 0001 0000 */ + DECODE_OR (0xffff, 0xbf10), + /* SEV 1011 1111 0100 0000 */ + DECODE_EMULATE (0xffff, 0xbf40, PROBES_T16_SEV), + /* NOP 1011 1111 0000 0000 */ + /* WFE 1011 1111 0010 0000 */ + /* WFI 1011 1111 0011 0000 */ + DECODE_SIMULATE (0xffcf, 0xbf00, PROBES_T16_WFE), + /* Unassigned hints 1011 1111 xxxx 0000 */ + DECODE_REJECT (0xff0f, 0xbf00), + /* IT 1011 1111 xxxx xxxx */ + DECODE_CUSTOM (0xff00, 0xbf00, PROBES_T16_IT), + + /* SETEND 1011 0110 010x xxxx */ + /* CPS 1011 0110 011x xxxx */ + /* BKPT 1011 1110 xxxx xxxx */ + /* And unallocated instructions... */ + DECODE_END +}; + +const union decode_item probes_decode_thumb16_table[] = { + + /* + * Shift (immediate), add, subtract, move, and compare + * 00xx xxxx xxxx xxxx + */ + + /* CMP (immediate) 0010 1xxx xxxx xxxx */ + DECODE_EMULATE (0xf800, 0x2800, PROBES_T16_CMP), + + /* ADD (register) 0001 100x xxxx xxxx */ + /* SUB (register) 0001 101x xxxx xxxx */ + /* LSL (immediate) 0000 0xxx xxxx xxxx */ + /* LSR (immediate) 0000 1xxx xxxx xxxx */ + /* ASR (immediate) 0001 0xxx xxxx xxxx */ + /* ADD (immediate, Thumb) 0001 110x xxxx xxxx */ + /* SUB (immediate, Thumb) 0001 111x xxxx xxxx */ + /* MOV (immediate) 0010 0xxx xxxx xxxx */ + /* ADD (immediate, Thumb) 0011 0xxx xxxx xxxx */ + /* SUB (immediate, Thumb) 0011 1xxx xxxx xxxx */ + DECODE_EMULATE (0xc000, 0x0000, PROBES_T16_ADDSUB), + + /* + * 16-bit Thumb data-processing instructions + * 0100 00xx xxxx xxxx + */ + + /* TST (register) 0100 0010 00xx xxxx */ + DECODE_EMULATE (0xffc0, 0x4200, PROBES_T16_CMP), + /* CMP (register) 0100 0010 10xx xxxx */ + /* CMN (register) 0100 0010 11xx xxxx */ + DECODE_EMULATE (0xff80, 0x4280, PROBES_T16_CMP), + /* AND (register) 0100 0000 00xx xxxx */ + /* EOR (register) 0100 0000 01xx xxxx */ + /* LSL (register) 0100 0000 10xx xxxx */ + /* LSR (register) 0100 0000 11xx xxxx */ + /* ASR (register) 0100 0001 00xx xxxx */ + /* ADC (register) 0100 0001 01xx xxxx */ + /* SBC (register) 0100 0001 10xx xxxx */ + /* ROR (register) 0100 0001 11xx xxxx */ + /* RSB (immediate) 0100 0010 01xx xxxx */ + /* ORR (register) 0100 0011 00xx xxxx */ + /* MUL 0100 0011 00xx xxxx */ + /* BIC (register) 0100 0011 10xx xxxx */ + /* MVN (register) 0100 0011 10xx xxxx */ + DECODE_EMULATE (0xfc00, 0x4000, PROBES_T16_LOGICAL), + + /* + * Special data instructions and branch and exchange + * 0100 01xx xxxx xxxx + */ + + /* BLX pc 0100 0111 1111 1xxx */ + DECODE_REJECT (0xfff8, 0x47f8), + + /* BX (register) 0100 0111 0xxx xxxx */ + /* BLX (register) 0100 0111 1xxx xxxx */ + DECODE_SIMULATE (0xff00, 0x4700, PROBES_T16_BLX), + + /* ADD pc, pc 0100 0100 1111 1111 */ + DECODE_REJECT (0xffff, 0x44ff), + + /* ADD (register) 0100 0100 xxxx xxxx */ + /* CMP (register) 0100 0101 xxxx xxxx */ + /* MOV (register) 0100 0110 xxxx xxxx */ + DECODE_CUSTOM (0xfc00, 0x4400, PROBES_T16_HIREGOPS), + + /* + * Load from Literal Pool + * LDR (literal) 0100 1xxx xxxx xxxx + */ + DECODE_SIMULATE (0xf800, 0x4800, PROBES_T16_LDR_LIT), + + /* + * 16-bit Thumb Load/store instructions + * 0101 xxxx xxxx xxxx + * 011x xxxx xxxx xxxx + * 100x xxxx xxxx xxxx + */ + + /* STR (register) 0101 000x xxxx xxxx */ + /* STRH (register) 0101 001x xxxx xxxx */ + /* STRB (register) 0101 010x xxxx xxxx */ + /* LDRSB (register) 0101 011x xxxx xxxx */ + /* LDR (register) 0101 100x xxxx xxxx */ + /* LDRH (register) 0101 101x xxxx xxxx */ + /* LDRB (register) 0101 110x xxxx xxxx */ + /* LDRSH (register) 0101 111x xxxx xxxx */ + /* STR (immediate, Thumb) 0110 0xxx xxxx xxxx */ + /* LDR (immediate, Thumb) 0110 1xxx xxxx xxxx */ + /* STRB (immediate, Thumb) 0111 0xxx xxxx xxxx */ + /* LDRB (immediate, Thumb) 0111 1xxx xxxx xxxx */ + DECODE_EMULATE (0xc000, 0x4000, PROBES_T16_LDRHSTRH), + /* STRH (immediate, Thumb) 1000 0xxx xxxx xxxx */ + /* LDRH (immediate, Thumb) 1000 1xxx xxxx xxxx */ + DECODE_EMULATE (0xf000, 0x8000, PROBES_T16_LDRHSTRH), + /* STR (immediate, Thumb) 1001 0xxx xxxx xxxx */ + /* LDR (immediate, Thumb) 1001 1xxx xxxx xxxx */ + DECODE_SIMULATE (0xf000, 0x9000, PROBES_T16_LDRSTR), + + /* + * Generate PC-/SP-relative address + * ADR (literal) 1010 0xxx xxxx xxxx + * ADD (SP plus immediate) 1010 1xxx xxxx xxxx + */ + DECODE_SIMULATE (0xf000, 0xa000, PROBES_T16_ADR), + + /* + * Miscellaneous 16-bit instructions + * 1011 xxxx xxxx xxxx + */ + DECODE_TABLE (0xf000, 0xb000, t16_table_1011), + + /* STM 1100 0xxx xxxx xxxx */ + /* LDM 1100 1xxx xxxx xxxx */ + DECODE_EMULATE (0xf000, 0xc000, PROBES_T16_LDMSTM), + + /* + * Conditional branch, and Supervisor Call + */ + + /* Permanently UNDEFINED 1101 1110 xxxx xxxx */ + /* SVC 1101 1111 xxxx xxxx */ + DECODE_REJECT (0xfe00, 0xde00), + + /* Conditional branch 1101 xxxx xxxx xxxx */ + DECODE_CUSTOM (0xf000, 0xd000, PROBES_T16_BRANCH_COND), + + /* + * Unconditional branch + * B 1110 0xxx xxxx xxxx + */ + DECODE_SIMULATE (0xf800, 0xe000, PROBES_T16_BRANCH), + + DECODE_END +}; +#ifdef CONFIG_ARM_KPROBES_TEST_MODULE +EXPORT_SYMBOL_GPL(probes_decode_thumb16_table); +#endif + +static unsigned long __kprobes thumb_check_cc(unsigned long cpsr) +{ + if (unlikely(in_it_block(cpsr))) + return probes_condition_checks[current_cond(cpsr)](cpsr); + return true; +} + +static void __kprobes thumb16_singlestep(probes_opcode_t opcode, + struct arch_probes_insn *asi, + struct pt_regs *regs) +{ + regs->ARM_pc += 2; + asi->insn_handler(opcode, asi, regs); + regs->ARM_cpsr = it_advance(regs->ARM_cpsr); +} + +static void __kprobes thumb32_singlestep(probes_opcode_t opcode, + struct arch_probes_insn *asi, + struct pt_regs *regs) +{ + regs->ARM_pc += 4; + asi->insn_handler(opcode, asi, regs); + regs->ARM_cpsr = it_advance(regs->ARM_cpsr); +} + +enum probes_insn __kprobes +thumb16_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, + bool emulate, const union decode_action *actions) +{ + asi->insn_singlestep = thumb16_singlestep; + asi->insn_check_cc = thumb_check_cc; + return probes_decode_insn(insn, asi, probes_decode_thumb16_table, true, + emulate, actions); +} + +enum probes_insn __kprobes +thumb32_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, + bool emulate, const union decode_action *actions) +{ + asi->insn_singlestep = thumb32_singlestep; + asi->insn_check_cc = thumb_check_cc; + return probes_decode_insn(insn, asi, probes_decode_thumb32_table, true, + emulate, actions); +} diff --git a/arch/arm/probes/decode-thumb.h b/arch/arm/probes/decode-thumb.h new file mode 100644 index 0000000..039013c --- /dev/null +++ b/arch/arm/probes/decode-thumb.h @@ -0,0 +1,99 @@ +/* + * arch/arm/probes/decode-thumb.h + * + * Copyright 2013 Linaro Ltd. + * Written by: David A. Long + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef _ARM_KERNEL_PROBES_THUMB_H +#define _ARM_KERNEL_PROBES_THUMB_H + +#include "decode.h" + +/* + * True if current instruction is in an IT block. + */ +#define in_it_block(cpsr) ((cpsr & 0x06000c00) != 0x00000000) + +/* + * Return the condition code to check for the currently executing instruction. + * This is in ITSTATE<7:4> which is in CPSR<15:12> but is only valid if + * in_it_block returns true. + */ +#define current_cond(cpsr) ((cpsr >> 12) & 0xf) + +enum probes_t32_action { + PROBES_T32_EMULATE_NONE, + PROBES_T32_SIMULATE_NOP, + PROBES_T32_LDMSTM, + PROBES_T32_LDRDSTRD, + PROBES_T32_TABLE_BRANCH, + PROBES_T32_TST, + PROBES_T32_CMP, + PROBES_T32_MOV, + PROBES_T32_ADDSUB, + PROBES_T32_LOGICAL, + PROBES_T32_ADDWSUBW_PC, + PROBES_T32_ADDWSUBW, + PROBES_T32_MOVW, + PROBES_T32_SAT, + PROBES_T32_BITFIELD, + PROBES_T32_SEV, + PROBES_T32_WFE, + PROBES_T32_MRS, + PROBES_T32_BRANCH_COND, + PROBES_T32_BRANCH, + PROBES_T32_PLDI, + PROBES_T32_LDR_LIT, + PROBES_T32_LDRSTR, + PROBES_T32_SIGN_EXTEND, + PROBES_T32_MEDIA, + PROBES_T32_REVERSE, + PROBES_T32_MUL_ADD, + PROBES_T32_MUL_ADD2, + PROBES_T32_MUL_ADD_LONG, + NUM_PROBES_T32_ACTIONS +}; + +enum probes_t16_action { + PROBES_T16_ADD_SP, + PROBES_T16_CBZ, + PROBES_T16_SIGN_EXTEND, + PROBES_T16_PUSH, + PROBES_T16_POP, + PROBES_T16_SEV, + PROBES_T16_WFE, + PROBES_T16_IT, + PROBES_T16_CMP, + PROBES_T16_ADDSUB, + PROBES_T16_LOGICAL, + PROBES_T16_BLX, + PROBES_T16_HIREGOPS, + PROBES_T16_LDR_LIT, + PROBES_T16_LDRHSTRH, + PROBES_T16_LDRSTR, + PROBES_T16_ADR, + PROBES_T16_LDMSTM, + PROBES_T16_BRANCH_COND, + PROBES_T16_BRANCH, + NUM_PROBES_T16_ACTIONS +}; + +extern const union decode_item probes_decode_thumb32_table[]; +extern const union decode_item probes_decode_thumb16_table[]; + +enum probes_insn __kprobes +thumb16_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, + bool emulate, const union decode_action *actions); +enum probes_insn __kprobes +thumb32_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, + bool emulate, const union decode_action *actions); + +#endif diff --git a/arch/arm/probes/decode.c b/arch/arm/probes/decode.c new file mode 100644 index 0000000..3b05d57 --- /dev/null +++ b/arch/arm/probes/decode.c @@ -0,0 +1,456 @@ +/* + * arch/arm/probes/decode.c + * + * Copyright (C) 2011 Jon Medhurst . + * + * Some contents moved here from arch/arm/include/asm/kprobes-arm.c which is + * Copyright (C) 2006, 2007 Motorola Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include "decode.h" + + +#ifndef find_str_pc_offset + +/* + * For STR and STM instructions, an ARM core may choose to use either + * a +8 or a +12 displacement from the current instruction's address. + * Whichever value is chosen for a given core, it must be the same for + * both instructions and may not change. This function measures it. + */ + +int str_pc_offset; + +void __init find_str_pc_offset(void) +{ + int addr, scratch, ret; + + __asm__ ( + "sub %[ret], pc, #4 \n\t" + "str pc, %[addr] \n\t" + "ldr %[scr], %[addr] \n\t" + "sub %[ret], %[scr], %[ret] \n\t" + : [ret] "=r" (ret), [scr] "=r" (scratch), [addr] "+m" (addr)); + + str_pc_offset = ret; +} + +#endif /* !find_str_pc_offset */ + + +#ifndef test_load_write_pc_interworking + +bool load_write_pc_interworks; + +void __init test_load_write_pc_interworking(void) +{ + int arch = cpu_architecture(); + BUG_ON(arch == CPU_ARCH_UNKNOWN); + load_write_pc_interworks = arch >= CPU_ARCH_ARMv5T; +} + +#endif /* !test_load_write_pc_interworking */ + + +#ifndef test_alu_write_pc_interworking + +bool alu_write_pc_interworks; + +void __init test_alu_write_pc_interworking(void) +{ + int arch = cpu_architecture(); + BUG_ON(arch == CPU_ARCH_UNKNOWN); + alu_write_pc_interworks = arch >= CPU_ARCH_ARMv7; +} + +#endif /* !test_alu_write_pc_interworking */ + + +void __init arm_probes_decode_init(void) +{ + find_str_pc_offset(); + test_load_write_pc_interworking(); + test_alu_write_pc_interworking(); +} + + +static unsigned long __kprobes __check_eq(unsigned long cpsr) +{ + return cpsr & PSR_Z_BIT; +} + +static unsigned long __kprobes __check_ne(unsigned long cpsr) +{ + return (~cpsr) & PSR_Z_BIT; +} + +static unsigned long __kprobes __check_cs(unsigned long cpsr) +{ + return cpsr & PSR_C_BIT; +} + +static unsigned long __kprobes __check_cc(unsigned long cpsr) +{ + return (~cpsr) & PSR_C_BIT; +} + +static unsigned long __kprobes __check_mi(unsigned long cpsr) +{ + return cpsr & PSR_N_BIT; +} + +static unsigned long __kprobes __check_pl(unsigned long cpsr) +{ + return (~cpsr) & PSR_N_BIT; +} + +static unsigned long __kprobes __check_vs(unsigned long cpsr) +{ + return cpsr & PSR_V_BIT; +} + +static unsigned long __kprobes __check_vc(unsigned long cpsr) +{ + return (~cpsr) & PSR_V_BIT; +} + +static unsigned long __kprobes __check_hi(unsigned long cpsr) +{ + cpsr &= ~(cpsr >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */ + return cpsr & PSR_C_BIT; +} + +static unsigned long __kprobes __check_ls(unsigned long cpsr) +{ + cpsr &= ~(cpsr >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */ + return (~cpsr) & PSR_C_BIT; +} + +static unsigned long __kprobes __check_ge(unsigned long cpsr) +{ + cpsr ^= (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */ + return (~cpsr) & PSR_N_BIT; +} + +static unsigned long __kprobes __check_lt(unsigned long cpsr) +{ + cpsr ^= (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */ + return cpsr & PSR_N_BIT; +} + +static unsigned long __kprobes __check_gt(unsigned long cpsr) +{ + unsigned long temp = cpsr ^ (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */ + temp |= (cpsr << 1); /* PSR_N_BIT |= PSR_Z_BIT */ + return (~temp) & PSR_N_BIT; +} + +static unsigned long __kprobes __check_le(unsigned long cpsr) +{ + unsigned long temp = cpsr ^ (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */ + temp |= (cpsr << 1); /* PSR_N_BIT |= PSR_Z_BIT */ + return temp & PSR_N_BIT; +} + +static unsigned long __kprobes __check_al(unsigned long cpsr) +{ + return true; +} + +probes_check_cc * const probes_condition_checks[16] = { + &__check_eq, &__check_ne, &__check_cs, &__check_cc, + &__check_mi, &__check_pl, &__check_vs, &__check_vc, + &__check_hi, &__check_ls, &__check_ge, &__check_lt, + &__check_gt, &__check_le, &__check_al, &__check_al +}; + + +void __kprobes probes_simulate_nop(probes_opcode_t opcode, + struct arch_probes_insn *asi, + struct pt_regs *regs) +{ +} + +void __kprobes probes_emulate_none(probes_opcode_t opcode, + struct arch_probes_insn *asi, + struct pt_regs *regs) +{ + asi->insn_fn(); +} + +/* + * Prepare an instruction slot to receive an instruction for emulating. + * This is done by placing a subroutine return after the location where the + * instruction will be placed. We also modify ARM instructions to be + * unconditional as the condition code will already be checked before any + * emulation handler is called. + */ +static probes_opcode_t __kprobes +prepare_emulated_insn(probes_opcode_t insn, struct arch_probes_insn *asi, + bool thumb) +{ +#ifdef CONFIG_THUMB2_KERNEL + if (thumb) { + u16 *thumb_insn = (u16 *)asi->insn; + /* Thumb bx lr */ + thumb_insn[1] = __opcode_to_mem_thumb16(0x4770); + thumb_insn[2] = __opcode_to_mem_thumb16(0x4770); + return insn; + } + asi->insn[1] = __opcode_to_mem_arm(0xe12fff1e); /* ARM bx lr */ +#else + asi->insn[1] = __opcode_to_mem_arm(0xe1a0f00e); /* mov pc, lr */ +#endif + /* Make an ARM instruction unconditional */ + if (insn < 0xe0000000) + insn = (insn | 0xe0000000) & ~0x10000000; + return insn; +} + +/* + * Write a (probably modified) instruction into the slot previously prepared by + * prepare_emulated_insn + */ +static void __kprobes +set_emulated_insn(probes_opcode_t insn, struct arch_probes_insn *asi, + bool thumb) +{ +#ifdef CONFIG_THUMB2_KERNEL + if (thumb) { + u16 *ip = (u16 *)asi->insn; + if (is_wide_instruction(insn)) + *ip++ = __opcode_to_mem_thumb16(insn >> 16); + *ip++ = __opcode_to_mem_thumb16(insn); + return; + } +#endif + asi->insn[0] = __opcode_to_mem_arm(insn); +} + +/* + * When we modify the register numbers encoded in an instruction to be emulated, + * the new values come from this define. For ARM and 32-bit Thumb instructions + * this gives... + * + * bit position 16 12 8 4 0 + * ---------------+---+---+---+---+---+ + * register r2 r0 r1 -- r3 + */ +#define INSN_NEW_BITS 0x00020103 + +/* Each nibble has same value as that at INSN_NEW_BITS bit 16 */ +#define INSN_SAMEAS16_BITS 0x22222222 + +/* + * Validate and modify each of the registers encoded in an instruction. + * + * Each nibble in regs contains a value from enum decode_reg_type. For each + * non-zero value, the corresponding nibble in pinsn is validated and modified + * according to the type. + */ +static bool __kprobes decode_regs(probes_opcode_t *pinsn, u32 regs, bool modify) +{ + probes_opcode_t insn = *pinsn; + probes_opcode_t mask = 0xf; /* Start at least significant nibble */ + + for (; regs != 0; regs >>= 4, mask <<= 4) { + + probes_opcode_t new_bits = INSN_NEW_BITS; + + switch (regs & 0xf) { + + case REG_TYPE_NONE: + /* Nibble not a register, skip to next */ + continue; + + case REG_TYPE_ANY: + /* Any register is allowed */ + break; + + case REG_TYPE_SAMEAS16: + /* Replace register with same as at bit position 16 */ + new_bits = INSN_SAMEAS16_BITS; + break; + + case REG_TYPE_SP: + /* Only allow SP (R13) */ + if ((insn ^ 0xdddddddd) & mask) + goto reject; + break; + + case REG_TYPE_PC: + /* Only allow PC (R15) */ + if ((insn ^ 0xffffffff) & mask) + goto reject; + break; + + case REG_TYPE_NOSP: + /* Reject SP (R13) */ + if (((insn ^ 0xdddddddd) & mask) == 0) + goto reject; + break; + + case REG_TYPE_NOSPPC: + case REG_TYPE_NOSPPCX: + /* Reject SP and PC (R13 and R15) */ + if (((insn ^ 0xdddddddd) & 0xdddddddd & mask) == 0) + goto reject; + break; + + case REG_TYPE_NOPCWB: + if (!is_writeback(insn)) + break; /* No writeback, so any register is OK */ + /* fall through... */ + case REG_TYPE_NOPC: + case REG_TYPE_NOPCX: + /* Reject PC (R15) */ + if (((insn ^ 0xffffffff) & mask) == 0) + goto reject; + break; + } + + /* Replace value of nibble with new register number... */ + insn &= ~mask; + insn |= new_bits & mask; + } + + if (modify) + *pinsn = insn; + + return true; + +reject: + return false; +} + +static const int decode_struct_sizes[NUM_DECODE_TYPES] = { + [DECODE_TYPE_TABLE] = sizeof(struct decode_table), + [DECODE_TYPE_CUSTOM] = sizeof(struct decode_custom), + [DECODE_TYPE_SIMULATE] = sizeof(struct decode_simulate), + [DECODE_TYPE_EMULATE] = sizeof(struct decode_emulate), + [DECODE_TYPE_OR] = sizeof(struct decode_or), + [DECODE_TYPE_REJECT] = sizeof(struct decode_reject) +}; + +/* + * probes_decode_insn operates on data tables in order to decode an ARM + * architecture instruction onto which a kprobe has been placed. + * + * These instruction decoding tables are a concatenation of entries each + * of which consist of one of the following structs: + * + * decode_table + * decode_custom + * decode_simulate + * decode_emulate + * decode_or + * decode_reject + * + * Each of these starts with a struct decode_header which has the following + * fields: + * + * type_regs + * mask + * value + * + * The least significant DECODE_TYPE_BITS of type_regs contains a value + * from enum decode_type, this indicates which of the decode_* structs + * the entry contains. The value DECODE_TYPE_END indicates the end of the + * table. + * + * When the table is parsed, each entry is checked in turn to see if it + * matches the instruction to be decoded using the test: + * + * (insn & mask) == value + * + * If no match is found before the end of the table is reached then decoding + * fails with INSN_REJECTED. + * + * When a match is found, decode_regs() is called to validate and modify each + * of the registers encoded in the instruction; the data it uses to do this + * is (type_regs >> DECODE_TYPE_BITS). A validation failure will cause decoding + * to fail with INSN_REJECTED. + * + * Once the instruction has passed the above tests, further processing + * depends on the type of the table entry's decode struct. + * + */ +int __kprobes +probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, + const union decode_item *table, bool thumb, + bool emulate, const union decode_action *actions) +{ + const struct decode_header *h = (struct decode_header *)table; + const struct decode_header *next; + bool matched = false; + + if (emulate) + insn = prepare_emulated_insn(insn, asi, thumb); + + for (;; h = next) { + enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK; + u32 regs = h->type_regs.bits >> DECODE_TYPE_BITS; + + if (type == DECODE_TYPE_END) + return INSN_REJECTED; + + next = (struct decode_header *) + ((uintptr_t)h + decode_struct_sizes[type]); + + if (!matched && (insn & h->mask.bits) != h->value.bits) + continue; + + if (!decode_regs(&insn, regs, emulate)) + return INSN_REJECTED; + + switch (type) { + + case DECODE_TYPE_TABLE: { + struct decode_table *d = (struct decode_table *)h; + next = (struct decode_header *)d->table.table; + break; + } + + case DECODE_TYPE_CUSTOM: { + struct decode_custom *d = (struct decode_custom *)h; + return actions[d->decoder.action].decoder(insn, asi, h); + } + + case DECODE_TYPE_SIMULATE: { + struct decode_simulate *d = (struct decode_simulate *)h; + asi->insn_handler = actions[d->handler.action].handler; + return INSN_GOOD_NO_SLOT; + } + + case DECODE_TYPE_EMULATE: { + struct decode_emulate *d = (struct decode_emulate *)h; + + if (!emulate) + return actions[d->handler.action].decoder(insn, + asi, h); + + asi->insn_handler = actions[d->handler.action].handler; + set_emulated_insn(insn, asi, thumb); + return INSN_GOOD; + } + + case DECODE_TYPE_OR: + matched = true; + break; + + case DECODE_TYPE_REJECT: + default: + return INSN_REJECTED; + } + } +} diff --git a/arch/arm/probes/decode.h b/arch/arm/probes/decode.h new file mode 100644 index 0000000..1d0b531 --- /dev/null +++ b/arch/arm/probes/decode.h @@ -0,0 +1,407 @@ +/* + * arch/arm/probes/decode.h + * + * Copyright (C) 2011 Jon Medhurst . + * + * Some contents moved here from arch/arm/include/asm/kprobes.h which is + * Copyright (C) 2006, 2007 Motorola Inc. + * + * 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. + */ + +#ifndef _ARM_KERNEL_PROBES_H +#define _ARM_KERNEL_PROBES_H + +#include +#include +#include + +void __init arm_probes_decode_init(void); + +extern probes_check_cc * const probes_condition_checks[16]; + +#if __LINUX_ARM_ARCH__ >= 7 + +/* str_pc_offset is architecturally defined from ARMv7 onwards */ +#define str_pc_offset 8 +#define find_str_pc_offset() + +#else /* __LINUX_ARM_ARCH__ < 7 */ + +/* We need a run-time check to determine str_pc_offset */ +extern int str_pc_offset; +void __init find_str_pc_offset(void); + +#endif + + +/* + * Update ITSTATE after normal execution of an IT block instruction. + * + * The 8 IT state bits are split into two parts in CPSR: + * ITSTATE<1:0> are in CPSR<26:25> + * ITSTATE<7:2> are in CPSR<15:10> + */ +static inline unsigned long it_advance(unsigned long cpsr) + { + if ((cpsr & 0x06000400) == 0) { + /* ITSTATE<2:0> == 0 means end of IT block, so clear IT state */ + cpsr &= ~PSR_IT_MASK; + } else { + /* We need to shift left ITSTATE<4:0> */ + const unsigned long mask = 0x06001c00; /* Mask ITSTATE<4:0> */ + unsigned long it = cpsr & mask; + it <<= 1; + it |= it >> (27 - 10); /* Carry ITSTATE<2> to correct place */ + it &= mask; + cpsr &= ~mask; + cpsr |= it; + } + return cpsr; +} + +static inline void __kprobes bx_write_pc(long pcv, struct pt_regs *regs) +{ + long cpsr = regs->ARM_cpsr; + if (pcv & 0x1) { + cpsr |= PSR_T_BIT; + pcv &= ~0x1; + } else { + cpsr &= ~PSR_T_BIT; + pcv &= ~0x2; /* Avoid UNPREDICTABLE address allignment */ + } + regs->ARM_cpsr = cpsr; + regs->ARM_pc = pcv; +} + + +#if __LINUX_ARM_ARCH__ >= 6 + +/* Kernels built for >= ARMv6 should never run on <= ARMv5 hardware, so... */ +#define load_write_pc_interworks true +#define test_load_write_pc_interworking() + +#else /* __LINUX_ARM_ARCH__ < 6 */ + +/* We need run-time testing to determine if load_write_pc() should interwork. */ +extern bool load_write_pc_interworks; +void __init test_load_write_pc_interworking(void); + +#endif + +static inline void __kprobes load_write_pc(long pcv, struct pt_regs *regs) +{ + if (load_write_pc_interworks) + bx_write_pc(pcv, regs); + else + regs->ARM_pc = pcv; +} + + +#if __LINUX_ARM_ARCH__ >= 7 + +#define alu_write_pc_interworks true +#define test_alu_write_pc_interworking() + +#elif __LINUX_ARM_ARCH__ <= 5 + +/* Kernels built for <= ARMv5 should never run on >= ARMv6 hardware, so... */ +#define alu_write_pc_interworks false +#define test_alu_write_pc_interworking() + +#else /* __LINUX_ARM_ARCH__ == 6 */ + +/* We could be an ARMv6 binary on ARMv7 hardware so we need a run-time check. */ +extern bool alu_write_pc_interworks; +void __init test_alu_write_pc_interworking(void); + +#endif /* __LINUX_ARM_ARCH__ == 6 */ + +static inline void __kprobes alu_write_pc(long pcv, struct pt_regs *regs) +{ + if (alu_write_pc_interworks) + bx_write_pc(pcv, regs); + else + regs->ARM_pc = pcv; +} + + +/* + * Test if load/store instructions writeback the address register. + * if P (bit 24) == 0 or W (bit 21) == 1 + */ +#define is_writeback(insn) ((insn ^ 0x01000000) & 0x01200000) + +/* + * The following definitions and macros are used to build instruction + * decoding tables for use by probes_decode_insn. + * + * These tables are a concatenation of entries each of which consist of one of + * the decode_* structs. All of the fields in every type of decode structure + * are of the union type decode_item, therefore the entire decode table can be + * viewed as an array of these and declared like: + * + * static const union decode_item table_name[] = {}; + * + * In order to construct each entry in the table, macros are used to + * initialise a number of sequential decode_item values in a layout which + * matches the relevant struct. E.g. DECODE_SIMULATE initialise a struct + * decode_simulate by initialising four decode_item objects like this... + * + * {.bits = _type}, + * {.bits = _mask}, + * {.bits = _value}, + * {.action = _handler}, + * + * Initialising a specified member of the union means that the compiler + * will produce a warning if the argument is of an incorrect type. + * + * Below is a list of each of the macros used to initialise entries and a + * description of the action performed when that entry is matched to an + * instruction. A match is found when (instruction & mask) == value. + * + * DECODE_TABLE(mask, value, table) + * Instruction decoding jumps to parsing the new sub-table 'table'. + * + * DECODE_CUSTOM(mask, value, decoder) + * The value of 'decoder' is used as an index into the array of + * action functions, and the retrieved decoder function is invoked + * to complete decoding of the instruction. + * + * DECODE_SIMULATE(mask, value, handler) + * The probes instruction handler is set to the value found by + * indexing into the action array using the value of 'handler'. This + * will be used to simulate the instruction when the probe is hit. + * Decoding returns with INSN_GOOD_NO_SLOT. + * + * DECODE_EMULATE(mask, value, handler) + * The probes instruction handler is set to the value found by + * indexing into the action array using the value of 'handler'. This + * will be used to emulate the instruction when the probe is hit. The + * modified instruction (see below) is placed in the probes instruction + * slot so it may be called by the emulation code. Decoding returns + * with INSN_GOOD. + * + * DECODE_REJECT(mask, value) + * Instruction decoding fails with INSN_REJECTED + * + * DECODE_OR(mask, value) + * This allows the mask/value test of multiple table entries to be + * logically ORed. Once an 'or' entry is matched the decoding action to + * be performed is that of the next entry which isn't an 'or'. E.g. + * + * DECODE_OR (mask1, value1) + * DECODE_OR (mask2, value2) + * DECODE_SIMULATE (mask3, value3, simulation_handler) + * + * This means that if any of the three mask/value pairs match the + * instruction being decoded, then 'simulation_handler' will be used + * for it. + * + * Both the SIMULATE and EMULATE macros have a second form which take an + * additional 'regs' argument. + * + * DECODE_SIMULATEX(mask, value, handler, regs) + * DECODE_EMULATEX (mask, value, handler, regs) + * + * These are used to specify what kind of CPU register is encoded in each of the + * least significant 5 nibbles of the instruction being decoded. The regs value + * is specified using the REGS macro, this takes any of the REG_TYPE_* values + * from enum decode_reg_type as arguments; only the '*' part of the name is + * given. E.g. + * + * REGS(0, ANY, NOPC, 0, ANY) + * + * This indicates an instruction is encoded like: + * + * bits 19..16 ignore + * bits 15..12 any register allowed here + * bits 11.. 8 any register except PC allowed here + * bits 7.. 4 ignore + * bits 3.. 0 any register allowed here + * + * This register specification is checked after a decode table entry is found to + * match an instruction (through the mask/value test). Any invalid register then + * found in the instruction will cause decoding to fail with INSN_REJECTED. In + * the above example this would happen if bits 11..8 of the instruction were + * 1111, indicating R15 or PC. + * + * As well as checking for legal combinations of registers, this data is also + * used to modify the registers encoded in the instructions so that an + * emulation routines can use it. (See decode_regs() and INSN_NEW_BITS.) + * + * Here is a real example which matches ARM instructions of the form + * "AND ,,, " + * + * DECODE_EMULATEX (0x0e000090, 0x00000010, PROBES_DATA_PROCESSING_REG, + * REGS(ANY, ANY, NOPC, 0, ANY)), + * ^ ^ ^ ^ + * Rn Rd Rs Rm + * + * Decoding the instruction "AND R4, R5, R6, ASL R15" will be rejected because + * Rs == R15 + * + * Decoding the instruction "AND R4, R5, R6, ASL R7" will be accepted and the + * instruction will be modified to "AND R0, R2, R3, ASL R1" and then placed into + * the kprobes instruction slot. This can then be called later by the handler + * function emulate_rd12rn16rm0rs8_rwflags (a pointer to which is retrieved from + * the indicated slot in the action array), in order to simulate the instruction. + */ + +enum decode_type { + DECODE_TYPE_END, + DECODE_TYPE_TABLE, + DECODE_TYPE_CUSTOM, + DECODE_TYPE_SIMULATE, + DECODE_TYPE_EMULATE, + DECODE_TYPE_OR, + DECODE_TYPE_REJECT, + NUM_DECODE_TYPES /* Must be last enum */ +}; + +#define DECODE_TYPE_BITS 4 +#define DECODE_TYPE_MASK ((1 << DECODE_TYPE_BITS) - 1) + +enum decode_reg_type { + REG_TYPE_NONE = 0, /* Not a register, ignore */ + REG_TYPE_ANY, /* Any register allowed */ + REG_TYPE_SAMEAS16, /* Register should be same as that at bits 19..16 */ + REG_TYPE_SP, /* Register must be SP */ + REG_TYPE_PC, /* Register must be PC */ + REG_TYPE_NOSP, /* Register must not be SP */ + REG_TYPE_NOSPPC, /* Register must not be SP or PC */ + REG_TYPE_NOPC, /* Register must not be PC */ + REG_TYPE_NOPCWB, /* No PC if load/store write-back flag also set */ + + /* The following types are used when the encoding for PC indicates + * another instruction form. This distiction only matters for test + * case coverage checks. + */ + REG_TYPE_NOPCX, /* Register must not be PC */ + REG_TYPE_NOSPPCX, /* Register must not be SP or PC */ + + /* Alias to allow '0' arg to be used in REGS macro. */ + REG_TYPE_0 = REG_TYPE_NONE +}; + +#define REGS(r16, r12, r8, r4, r0) \ + (((REG_TYPE_##r16) << 16) + \ + ((REG_TYPE_##r12) << 12) + \ + ((REG_TYPE_##r8) << 8) + \ + ((REG_TYPE_##r4) << 4) + \ + (REG_TYPE_##r0)) + +union decode_item { + u32 bits; + const union decode_item *table; + int action; +}; + +struct decode_header; +typedef enum probes_insn (probes_custom_decode_t)(probes_opcode_t, + struct arch_probes_insn *, + const struct decode_header *); + +union decode_action { + probes_insn_handler_t *handler; + probes_custom_decode_t *decoder; +}; + +#define DECODE_END \ + {.bits = DECODE_TYPE_END} + + +struct decode_header { + union decode_item type_regs; + union decode_item mask; + union decode_item value; +}; + +#define DECODE_HEADER(_type, _mask, _value, _regs) \ + {.bits = (_type) | ((_regs) << DECODE_TYPE_BITS)}, \ + {.bits = (_mask)}, \ + {.bits = (_value)} + + +struct decode_table { + struct decode_header header; + union decode_item table; +}; + +#define DECODE_TABLE(_mask, _value, _table) \ + DECODE_HEADER(DECODE_TYPE_TABLE, _mask, _value, 0), \ + {.table = (_table)} + + +struct decode_custom { + struct decode_header header; + union decode_item decoder; +}; + +#define DECODE_CUSTOM(_mask, _value, _decoder) \ + DECODE_HEADER(DECODE_TYPE_CUSTOM, _mask, _value, 0), \ + {.action = (_decoder)} + + +struct decode_simulate { + struct decode_header header; + union decode_item handler; +}; + +#define DECODE_SIMULATEX(_mask, _value, _handler, _regs) \ + DECODE_HEADER(DECODE_TYPE_SIMULATE, _mask, _value, _regs), \ + {.action = (_handler)} + +#define DECODE_SIMULATE(_mask, _value, _handler) \ + DECODE_SIMULATEX(_mask, _value, _handler, 0) + + +struct decode_emulate { + struct decode_header header; + union decode_item handler; +}; + +#define DECODE_EMULATEX(_mask, _value, _handler, _regs) \ + DECODE_HEADER(DECODE_TYPE_EMULATE, _mask, _value, _regs), \ + {.action = (_handler)} + +#define DECODE_EMULATE(_mask, _value, _handler) \ + DECODE_EMULATEX(_mask, _value, _handler, 0) + + +struct decode_or { + struct decode_header header; +}; + +#define DECODE_OR(_mask, _value) \ + DECODE_HEADER(DECODE_TYPE_OR, _mask, _value, 0) + +enum probes_insn { + INSN_REJECTED, + INSN_GOOD, + INSN_GOOD_NO_SLOT +}; + +struct decode_reject { + struct decode_header header; +}; + +#define DECODE_REJECT(_mask, _value) \ + DECODE_HEADER(DECODE_TYPE_REJECT, _mask, _value, 0) + +probes_insn_handler_t probes_simulate_nop; +probes_insn_handler_t probes_emulate_none; + +int __kprobes +probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, + const union decode_item *table, bool thumb, bool emulate, + const union decode_action *actions); + +#endif diff --git a/arch/arm/probes/kprobes/Makefile b/arch/arm/probes/kprobes/Makefile new file mode 100644 index 0000000..eb38a42 --- /dev/null +++ b/arch/arm/probes/kprobes/Makefile @@ -0,0 +1,11 @@ +obj-$(CONFIG_KPROBES) += core.o actions-common.o +obj-$(CONFIG_ARM_KPROBES_TEST) += test-kprobes.o +test-kprobes-objs := test-core.o + +ifdef CONFIG_THUMB2_KERNEL +obj-$(CONFIG_KPROBES) += actions-thumb.o +test-kprobes-objs += test-thumb.o +else +obj-$(CONFIG_KPROBES) += actions-arm.o +test-kprobes-objs += test-arm.o +endif diff --git a/arch/arm/probes/kprobes/actions-arm.c b/arch/arm/probes/kprobes/actions-arm.c new file mode 100644 index 0000000..8797879 --- /dev/null +++ b/arch/arm/probes/kprobes/actions-arm.c @@ -0,0 +1,343 @@ +/* + * arch/arm/probes/kprobes/actions-arm.c + * + * Copyright (C) 2006, 2007 Motorola Inc. + * + * 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. + */ + +/* + * We do not have hardware single-stepping on ARM, This + * effort is further complicated by the ARM not having a + * "next PC" register. Instructions that change the PC + * can't be safely single-stepped in a MP environment, so + * we have a lot of work to do: + * + * In the prepare phase: + * *) If it is an instruction that does anything + * with the CPU mode, we reject it for a kprobe. + * (This is out of laziness rather than need. The + * instructions could be simulated.) + * + * *) Otherwise, decode the instruction rewriting its + * registers to take fixed, ordered registers and + * setting a handler for it to run the instruction. + * + * In the execution phase by an instruction's handler: + * + * *) If the PC is written to by the instruction, the + * instruction must be fully simulated in software. + * + * *) Otherwise, a modified form of the instruction is + * directly executed. Its handler calls the + * instruction in insn[0]. In insn[1] is a + * "mov pc, lr" to return. + * + * Before calling, load up the reordered registers + * from the original instruction's registers. If one + * of the original input registers is the PC, compute + * and adjust the appropriate input register. + * + * After call completes, copy the output registers to + * the original instruction's original registers. + * + * We don't use a real breakpoint instruction since that + * would have us in the kernel go from SVC mode to SVC + * mode losing the link register. Instead we use an + * undefined instruction. To simplify processing, the + * undefined instruction used for kprobes must be reserved + * exclusively for kprobes use. + * + * TODO: ifdef out some instruction decoding based on architecture. + */ + +#include +#include +#include + +#include "../decode-arm.h" +#include "core.h" + +#if __LINUX_ARM_ARCH__ >= 6 +#define BLX(reg) "blx "reg" \n\t" +#else +#define BLX(reg) "mov lr, pc \n\t" \ + "mov pc, "reg" \n\t" +#endif + +static void __kprobes +emulate_ldrdstrd(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long pc = regs->ARM_pc + 4; + int rt = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + int rm = insn & 0xf; + + register unsigned long rtv asm("r0") = regs->uregs[rt]; + register unsigned long rt2v asm("r1") = regs->uregs[rt+1]; + register unsigned long rnv asm("r2") = (rn == 15) ? pc + : regs->uregs[rn]; + register unsigned long rmv asm("r3") = regs->uregs[rm]; + + __asm__ __volatile__ ( + BLX("%[fn]") + : "=r" (rtv), "=r" (rt2v), "=r" (rnv) + : "0" (rtv), "1" (rt2v), "2" (rnv), "r" (rmv), + [fn] "r" (asi->insn_fn) + : "lr", "memory", "cc" + ); + + regs->uregs[rt] = rtv; + regs->uregs[rt+1] = rt2v; + if (is_writeback(insn)) + regs->uregs[rn] = rnv; +} + +static void __kprobes +emulate_ldr(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long pc = regs->ARM_pc + 4; + int rt = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + int rm = insn & 0xf; + + register unsigned long rtv asm("r0"); + register unsigned long rnv asm("r2") = (rn == 15) ? pc + : regs->uregs[rn]; + register unsigned long rmv asm("r3") = regs->uregs[rm]; + + __asm__ __volatile__ ( + BLX("%[fn]") + : "=r" (rtv), "=r" (rnv) + : "1" (rnv), "r" (rmv), [fn] "r" (asi->insn_fn) + : "lr", "memory", "cc" + ); + + if (rt == 15) + load_write_pc(rtv, regs); + else + regs->uregs[rt] = rtv; + + if (is_writeback(insn)) + regs->uregs[rn] = rnv; +} + +static void __kprobes +emulate_str(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long rtpc = regs->ARM_pc - 4 + str_pc_offset; + unsigned long rnpc = regs->ARM_pc + 4; + int rt = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + int rm = insn & 0xf; + + register unsigned long rtv asm("r0") = (rt == 15) ? rtpc + : regs->uregs[rt]; + register unsigned long rnv asm("r2") = (rn == 15) ? rnpc + : regs->uregs[rn]; + register unsigned long rmv asm("r3") = regs->uregs[rm]; + + __asm__ __volatile__ ( + BLX("%[fn]") + : "=r" (rnv) + : "r" (rtv), "0" (rnv), "r" (rmv), [fn] "r" (asi->insn_fn) + : "lr", "memory", "cc" + ); + + if (is_writeback(insn)) + regs->uregs[rn] = rnv; +} + +static void __kprobes +emulate_rd12rn16rm0rs8_rwflags(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long pc = regs->ARM_pc + 4; + int rd = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + int rm = insn & 0xf; + int rs = (insn >> 8) & 0xf; + + register unsigned long rdv asm("r0") = regs->uregs[rd]; + register unsigned long rnv asm("r2") = (rn == 15) ? pc + : regs->uregs[rn]; + register unsigned long rmv asm("r3") = (rm == 15) ? pc + : regs->uregs[rm]; + register unsigned long rsv asm("r1") = regs->uregs[rs]; + unsigned long cpsr = regs->ARM_cpsr; + + __asm__ __volatile__ ( + "msr cpsr_fs, %[cpsr] \n\t" + BLX("%[fn]") + "mrs %[cpsr], cpsr \n\t" + : "=r" (rdv), [cpsr] "=r" (cpsr) + : "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv), + "1" (cpsr), [fn] "r" (asi->insn_fn) + : "lr", "memory", "cc" + ); + + if (rd == 15) + alu_write_pc(rdv, regs); + else + regs->uregs[rd] = rdv; + regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); +} + +static void __kprobes +emulate_rd12rn16rm0_rwflags_nopc(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + int rd = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + int rm = insn & 0xf; + + register unsigned long rdv asm("r0") = regs->uregs[rd]; + register unsigned long rnv asm("r2") = regs->uregs[rn]; + register unsigned long rmv asm("r3") = regs->uregs[rm]; + unsigned long cpsr = regs->ARM_cpsr; + + __asm__ __volatile__ ( + "msr cpsr_fs, %[cpsr] \n\t" + BLX("%[fn]") + "mrs %[cpsr], cpsr \n\t" + : "=r" (rdv), [cpsr] "=r" (cpsr) + : "0" (rdv), "r" (rnv), "r" (rmv), + "1" (cpsr), [fn] "r" (asi->insn_fn) + : "lr", "memory", "cc" + ); + + regs->uregs[rd] = rdv; + regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); +} + +static void __kprobes +emulate_rd16rn12rm0rs8_rwflags_nopc(probes_opcode_t insn, + struct arch_probes_insn *asi, + struct pt_regs *regs) +{ + int rd = (insn >> 16) & 0xf; + int rn = (insn >> 12) & 0xf; + int rm = insn & 0xf; + int rs = (insn >> 8) & 0xf; + + register unsigned long rdv asm("r2") = regs->uregs[rd]; + register unsigned long rnv asm("r0") = regs->uregs[rn]; + register unsigned long rmv asm("r3") = regs->uregs[rm]; + register unsigned long rsv asm("r1") = regs->uregs[rs]; + unsigned long cpsr = regs->ARM_cpsr; + + __asm__ __volatile__ ( + "msr cpsr_fs, %[cpsr] \n\t" + BLX("%[fn]") + "mrs %[cpsr], cpsr \n\t" + : "=r" (rdv), [cpsr] "=r" (cpsr) + : "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv), + "1" (cpsr), [fn] "r" (asi->insn_fn) + : "lr", "memory", "cc" + ); + + regs->uregs[rd] = rdv; + regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); +} + +static void __kprobes +emulate_rd12rm0_noflags_nopc(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + int rd = (insn >> 12) & 0xf; + int rm = insn & 0xf; + + register unsigned long rdv asm("r0") = regs->uregs[rd]; + register unsigned long rmv asm("r3") = regs->uregs[rm]; + + __asm__ __volatile__ ( + BLX("%[fn]") + : "=r" (rdv) + : "0" (rdv), "r" (rmv), [fn] "r" (asi->insn_fn) + : "lr", "memory", "cc" + ); + + regs->uregs[rd] = rdv; +} + +static void __kprobes +emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(probes_opcode_t insn, + struct arch_probes_insn *asi, + struct pt_regs *regs) +{ + int rdlo = (insn >> 12) & 0xf; + int rdhi = (insn >> 16) & 0xf; + int rn = insn & 0xf; + int rm = (insn >> 8) & 0xf; + + register unsigned long rdlov asm("r0") = regs->uregs[rdlo]; + register unsigned long rdhiv asm("r2") = regs->uregs[rdhi]; + register unsigned long rnv asm("r3") = regs->uregs[rn]; + register unsigned long rmv asm("r1") = regs->uregs[rm]; + unsigned long cpsr = regs->ARM_cpsr; + + __asm__ __volatile__ ( + "msr cpsr_fs, %[cpsr] \n\t" + BLX("%[fn]") + "mrs %[cpsr], cpsr \n\t" + : "=r" (rdlov), "=r" (rdhiv), [cpsr] "=r" (cpsr) + : "0" (rdlov), "1" (rdhiv), "r" (rnv), "r" (rmv), + "2" (cpsr), [fn] "r" (asi->insn_fn) + : "lr", "memory", "cc" + ); + + regs->uregs[rdlo] = rdlov; + regs->uregs[rdhi] = rdhiv; + regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); +} + +const union decode_action kprobes_arm_actions[NUM_PROBES_ARM_ACTIONS] = { + [PROBES_EMULATE_NONE] = {.handler = probes_emulate_none}, + [PROBES_SIMULATE_NOP] = {.handler = probes_simulate_nop}, + [PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop}, + [PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop}, + [PROBES_BRANCH_IMM] = {.handler = simulate_blx1}, + [PROBES_MRS] = {.handler = simulate_mrs}, + [PROBES_BRANCH_REG] = {.handler = simulate_blx2bx}, + [PROBES_CLZ] = {.handler = emulate_rd12rm0_noflags_nopc}, + [PROBES_SATURATING_ARITHMETIC] = { + .handler = emulate_rd12rn16rm0_rwflags_nopc}, + [PROBES_MUL1] = {.handler = emulate_rdlo12rdhi16rn0rm8_rwflags_nopc}, + [PROBES_MUL2] = {.handler = emulate_rd16rn12rm0rs8_rwflags_nopc}, + [PROBES_SWP] = {.handler = emulate_rd12rn16rm0_rwflags_nopc}, + [PROBES_LDRSTRD] = {.handler = emulate_ldrdstrd}, + [PROBES_LOAD_EXTRA] = {.handler = emulate_ldr}, + [PROBES_LOAD] = {.handler = emulate_ldr}, + [PROBES_STORE_EXTRA] = {.handler = emulate_str}, + [PROBES_STORE] = {.handler = emulate_str}, + [PROBES_MOV_IP_SP] = {.handler = simulate_mov_ipsp}, + [PROBES_DATA_PROCESSING_REG] = { + .handler = emulate_rd12rn16rm0rs8_rwflags}, + [PROBES_DATA_PROCESSING_IMM] = { + .handler = emulate_rd12rn16rm0rs8_rwflags}, + [PROBES_MOV_HALFWORD] = {.handler = emulate_rd12rm0_noflags_nopc}, + [PROBES_SEV] = {.handler = probes_emulate_none}, + [PROBES_WFE] = {.handler = probes_simulate_nop}, + [PROBES_SATURATE] = {.handler = emulate_rd12rn16rm0_rwflags_nopc}, + [PROBES_REV] = {.handler = emulate_rd12rm0_noflags_nopc}, + [PROBES_MMI] = {.handler = emulate_rd12rn16rm0_rwflags_nopc}, + [PROBES_PACK] = {.handler = emulate_rd12rn16rm0_rwflags_nopc}, + [PROBES_EXTEND] = {.handler = emulate_rd12rm0_noflags_nopc}, + [PROBES_EXTEND_ADD] = {.handler = emulate_rd12rn16rm0_rwflags_nopc}, + [PROBES_MUL_ADD_LONG] = { + .handler = emulate_rdlo12rdhi16rn0rm8_rwflags_nopc}, + [PROBES_MUL_ADD] = {.handler = emulate_rd16rn12rm0rs8_rwflags_nopc}, + [PROBES_BITFIELD] = {.handler = emulate_rd12rm0_noflags_nopc}, + [PROBES_BRANCH] = {.handler = simulate_bbl}, + [PROBES_LDMSTM] = {.decoder = kprobe_decode_ldmstm} +}; diff --git a/arch/arm/probes/kprobes/actions-common.c b/arch/arm/probes/kprobes/actions-common.c new file mode 100644 index 0000000..bd20a71 --- /dev/null +++ b/arch/arm/probes/kprobes/actions-common.c @@ -0,0 +1,171 @@ +/* + * arch/arm/probes/kprobes/actions-common.c + * + * Copyright (C) 2011 Jon Medhurst . + * + * Some contents moved here from arch/arm/include/asm/kprobes-arm.c which is + * Copyright (C) 2006, 2007 Motorola Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include "core.h" + + +static void __kprobes simulate_ldm1stm1(probes_opcode_t insn, + struct arch_probes_insn *asi, + struct pt_regs *regs) +{ + int rn = (insn >> 16) & 0xf; + int lbit = insn & (1 << 20); + int wbit = insn & (1 << 21); + int ubit = insn & (1 << 23); + int pbit = insn & (1 << 24); + long *addr = (long *)regs->uregs[rn]; + int reg_bit_vector; + int reg_count; + + reg_count = 0; + reg_bit_vector = insn & 0xffff; + while (reg_bit_vector) { + reg_bit_vector &= (reg_bit_vector - 1); + ++reg_count; + } + + if (!ubit) + addr -= reg_count; + addr += (!pbit == !ubit); + + reg_bit_vector = insn & 0xffff; + while (reg_bit_vector) { + int reg = __ffs(reg_bit_vector); + reg_bit_vector &= (reg_bit_vector - 1); + if (lbit) + regs->uregs[reg] = *addr++; + else + *addr++ = regs->uregs[reg]; + } + + if (wbit) { + if (!ubit) + addr -= reg_count; + addr -= (!pbit == !ubit); + regs->uregs[rn] = (long)addr; + } +} + +static void __kprobes simulate_stm1_pc(probes_opcode_t insn, + struct arch_probes_insn *asi, + struct pt_regs *regs) +{ + unsigned long addr = regs->ARM_pc - 4; + + regs->ARM_pc = (long)addr + str_pc_offset; + simulate_ldm1stm1(insn, asi, regs); + regs->ARM_pc = (long)addr + 4; +} + +static void __kprobes simulate_ldm1_pc(probes_opcode_t insn, + struct arch_probes_insn *asi, + struct pt_regs *regs) +{ + simulate_ldm1stm1(insn, asi, regs); + load_write_pc(regs->ARM_pc, regs); +} + +static void __kprobes +emulate_generic_r0_12_noflags(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + register void *rregs asm("r1") = regs; + register void *rfn asm("lr") = asi->insn_fn; + + __asm__ __volatile__ ( + "stmdb sp!, {%[regs], r11} \n\t" + "ldmia %[regs], {r0-r12} \n\t" +#if __LINUX_ARM_ARCH__ >= 6 + "blx %[fn] \n\t" +#else + "str %[fn], [sp, #-4]! \n\t" + "adr lr, 1f \n\t" + "ldr pc, [sp], #4 \n\t" + "1: \n\t" +#endif + "ldr lr, [sp], #4 \n\t" /* lr = regs */ + "stmia lr, {r0-r12} \n\t" + "ldr r11, [sp], #4 \n\t" + : [regs] "=r" (rregs), [fn] "=r" (rfn) + : "0" (rregs), "1" (rfn) + : "r0", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r12", "memory", "cc" + ); +} + +static void __kprobes +emulate_generic_r2_14_noflags(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + emulate_generic_r0_12_noflags(insn, asi, + (struct pt_regs *)(regs->uregs+2)); +} + +static void __kprobes +emulate_ldm_r3_15(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + emulate_generic_r0_12_noflags(insn, asi, + (struct pt_regs *)(regs->uregs+3)); + load_write_pc(regs->ARM_pc, regs); +} + +enum probes_insn __kprobes +kprobe_decode_ldmstm(probes_opcode_t insn, struct arch_probes_insn *asi, + const struct decode_header *h) +{ + probes_insn_handler_t *handler = 0; + unsigned reglist = insn & 0xffff; + int is_ldm = insn & 0x100000; + int rn = (insn >> 16) & 0xf; + + if (rn <= 12 && (reglist & 0xe000) == 0) { + /* Instruction only uses registers in the range R0..R12 */ + handler = emulate_generic_r0_12_noflags; + + } else if (rn >= 2 && (reglist & 0x8003) == 0) { + /* Instruction only uses registers in the range R2..R14 */ + rn -= 2; + reglist >>= 2; + handler = emulate_generic_r2_14_noflags; + + } else if (rn >= 3 && (reglist & 0x0007) == 0) { + /* Instruction only uses registers in the range R3..R15 */ + if (is_ldm && (reglist & 0x8000)) { + rn -= 3; + reglist >>= 3; + handler = emulate_ldm_r3_15; + } + } + + if (handler) { + /* We can emulate the instruction in (possibly) modified form */ + asi->insn[0] = __opcode_to_mem_arm((insn & 0xfff00000) | + (rn << 16) | reglist); + asi->insn_handler = handler; + return INSN_GOOD; + } + + /* Fallback to slower simulation... */ + if (reglist & 0x8000) + handler = is_ldm ? simulate_ldm1_pc : simulate_stm1_pc; + else + handler = simulate_ldm1stm1; + asi->insn_handler = handler; + return INSN_GOOD_NO_SLOT; +} + diff --git a/arch/arm/probes/kprobes/actions-thumb.c b/arch/arm/probes/kprobes/actions-thumb.c new file mode 100644 index 0000000..6c4e60b --- /dev/null +++ b/arch/arm/probes/kprobes/actions-thumb.c @@ -0,0 +1,666 @@ +/* + * arch/arm/probes/kprobes/actions-thumb.c + * + * Copyright (C) 2011 Jon Medhurst . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include "../decode-thumb.h" +#include "core.h" + +/* These emulation encodings are functionally equivalent... */ +#define t32_emulate_rd8rn16rm0ra12_noflags \ + t32_emulate_rdlo12rdhi8rn16rm0_noflags + +/* t32 thumb actions */ + +static void __kprobes +t32_simulate_table_branch(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long pc = regs->ARM_pc; + int rn = (insn >> 16) & 0xf; + int rm = insn & 0xf; + + unsigned long rnv = (rn == 15) ? pc : regs->uregs[rn]; + unsigned long rmv = regs->uregs[rm]; + unsigned int halfwords; + + if (insn & 0x10) /* TBH */ + halfwords = ((u16 *)rnv)[rmv]; + else /* TBB */ + halfwords = ((u8 *)rnv)[rmv]; + + regs->ARM_pc = pc + 2 * halfwords; +} + +static void __kprobes +t32_simulate_mrs(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + int rd = (insn >> 8) & 0xf; + unsigned long mask = 0xf8ff03df; /* Mask out execution state */ + regs->uregs[rd] = regs->ARM_cpsr & mask; +} + +static void __kprobes +t32_simulate_cond_branch(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long pc = regs->ARM_pc; + + long offset = insn & 0x7ff; /* imm11 */ + offset += (insn & 0x003f0000) >> 5; /* imm6 */ + offset += (insn & 0x00002000) << 4; /* J1 */ + offset += (insn & 0x00000800) << 7; /* J2 */ + offset -= (insn & 0x04000000) >> 7; /* Apply sign bit */ + + regs->ARM_pc = pc + (offset * 2); +} + +static enum probes_insn __kprobes +t32_decode_cond_branch(probes_opcode_t insn, struct arch_probes_insn *asi, + const struct decode_header *d) +{ + int cc = (insn >> 22) & 0xf; + asi->insn_check_cc = probes_condition_checks[cc]; + asi->insn_handler = t32_simulate_cond_branch; + return INSN_GOOD_NO_SLOT; +} + +static void __kprobes +t32_simulate_branch(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long pc = regs->ARM_pc; + + long offset = insn & 0x7ff; /* imm11 */ + offset += (insn & 0x03ff0000) >> 5; /* imm10 */ + offset += (insn & 0x00002000) << 9; /* J1 */ + offset += (insn & 0x00000800) << 10; /* J2 */ + if (insn & 0x04000000) + offset -= 0x00800000; /* Apply sign bit */ + else + offset ^= 0x00600000; /* Invert J1 and J2 */ + + if (insn & (1 << 14)) { + /* BL or BLX */ + regs->ARM_lr = regs->ARM_pc | 1; + if (!(insn & (1 << 12))) { + /* BLX so switch to ARM mode */ + regs->ARM_cpsr &= ~PSR_T_BIT; + pc &= ~3; + } + } + + regs->ARM_pc = pc + (offset * 2); +} + +static void __kprobes +t32_simulate_ldr_literal(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long addr = regs->ARM_pc & ~3; + int rt = (insn >> 12) & 0xf; + unsigned long rtv; + + long offset = insn & 0xfff; + if (insn & 0x00800000) + addr += offset; + else + addr -= offset; + + if (insn & 0x00400000) { + /* LDR */ + rtv = *(unsigned long *)addr; + if (rt == 15) { + bx_write_pc(rtv, regs); + return; + } + } else if (insn & 0x00200000) { + /* LDRH */ + if (insn & 0x01000000) + rtv = *(s16 *)addr; + else + rtv = *(u16 *)addr; + } else { + /* LDRB */ + if (insn & 0x01000000) + rtv = *(s8 *)addr; + else + rtv = *(u8 *)addr; + } + + regs->uregs[rt] = rtv; +} + +static enum probes_insn __kprobes +t32_decode_ldmstm(probes_opcode_t insn, struct arch_probes_insn *asi, + const struct decode_header *d) +{ + enum probes_insn ret = kprobe_decode_ldmstm(insn, asi, d); + + /* Fixup modified instruction to have halfwords in correct order...*/ + insn = __mem_to_opcode_arm(asi->insn[0]); + ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(insn >> 16); + ((u16 *)asi->insn)[1] = __opcode_to_mem_thumb16(insn & 0xffff); + + return ret; +} + +static void __kprobes +t32_emulate_ldrdstrd(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long pc = regs->ARM_pc & ~3; + int rt1 = (insn >> 12) & 0xf; + int rt2 = (insn >> 8) & 0xf; + int rn = (insn >> 16) & 0xf; + + register unsigned long rt1v asm("r0") = regs->uregs[rt1]; + register unsigned long rt2v asm("r1") = regs->uregs[rt2]; + register unsigned long rnv asm("r2") = (rn == 15) ? pc + : regs->uregs[rn]; + + __asm__ __volatile__ ( + "blx %[fn]" + : "=r" (rt1v), "=r" (rt2v), "=r" (rnv) + : "0" (rt1v), "1" (rt2v), "2" (rnv), [fn] "r" (asi->insn_fn) + : "lr", "memory", "cc" + ); + + if (rn != 15) + regs->uregs[rn] = rnv; /* Writeback base register */ + regs->uregs[rt1] = rt1v; + regs->uregs[rt2] = rt2v; +} + +static void __kprobes +t32_emulate_ldrstr(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + int rt = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + int rm = insn & 0xf; + + register unsigned long rtv asm("r0") = regs->uregs[rt]; + register unsigned long rnv asm("r2") = regs->uregs[rn]; + register unsigned long rmv asm("r3") = regs->uregs[rm]; + + __asm__ __volatile__ ( + "blx %[fn]" + : "=r" (rtv), "=r" (rnv) + : "0" (rtv), "1" (rnv), "r" (rmv), [fn] "r" (asi->insn_fn) + : "lr", "memory", "cc" + ); + + regs->uregs[rn] = rnv; /* Writeback base register */ + if (rt == 15) /* Can't be true for a STR as they aren't allowed */ + bx_write_pc(rtv, regs); + else + regs->uregs[rt] = rtv; +} + +static void __kprobes +t32_emulate_rd8rn16rm0_rwflags(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + int rd = (insn >> 8) & 0xf; + int rn = (insn >> 16) & 0xf; + int rm = insn & 0xf; + + register unsigned long rdv asm("r1") = regs->uregs[rd]; + register unsigned long rnv asm("r2") = regs->uregs[rn]; + register unsigned long rmv asm("r3") = regs->uregs[rm]; + unsigned long cpsr = regs->ARM_cpsr; + + __asm__ __volatile__ ( + "msr cpsr_fs, %[cpsr] \n\t" + "blx %[fn] \n\t" + "mrs %[cpsr], cpsr \n\t" + : "=r" (rdv), [cpsr] "=r" (cpsr) + : "0" (rdv), "r" (rnv), "r" (rmv), + "1" (cpsr), [fn] "r" (asi->insn_fn) + : "lr", "memory", "cc" + ); + + regs->uregs[rd] = rdv; + regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); +} + +static void __kprobes +t32_emulate_rd8pc16_noflags(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long pc = regs->ARM_pc; + int rd = (insn >> 8) & 0xf; + + register unsigned long rdv asm("r1") = regs->uregs[rd]; + register unsigned long rnv asm("r2") = pc & ~3; + + __asm__ __volatile__ ( + "blx %[fn]" + : "=r" (rdv) + : "0" (rdv), "r" (rnv), [fn] "r" (asi->insn_fn) + : "lr", "memory", "cc" + ); + + regs->uregs[rd] = rdv; +} + +static void __kprobes +t32_emulate_rd8rn16_noflags(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + int rd = (insn >> 8) & 0xf; + int rn = (insn >> 16) & 0xf; + + register unsigned long rdv asm("r1") = regs->uregs[rd]; + register unsigned long rnv asm("r2") = regs->uregs[rn]; + + __asm__ __volatile__ ( + "blx %[fn]" + : "=r" (rdv) + : "0" (rdv), "r" (rnv), [fn] "r" (asi->insn_fn) + : "lr", "memory", "cc" + ); + + regs->uregs[rd] = rdv; +} + +static void __kprobes +t32_emulate_rdlo12rdhi8rn16rm0_noflags(probes_opcode_t insn, + struct arch_probes_insn *asi, + struct pt_regs *regs) +{ + int rdlo = (insn >> 12) & 0xf; + int rdhi = (insn >> 8) & 0xf; + int rn = (insn >> 16) & 0xf; + int rm = insn & 0xf; + + register unsigned long rdlov asm("r0") = regs->uregs[rdlo]; + register unsigned long rdhiv asm("r1") = regs->uregs[rdhi]; + register unsigned long rnv asm("r2") = regs->uregs[rn]; + register unsigned long rmv asm("r3") = regs->uregs[rm]; + + __asm__ __volatile__ ( + "blx %[fn]" + : "=r" (rdlov), "=r" (rdhiv) + : "0" (rdlov), "1" (rdhiv), "r" (rnv), "r" (rmv), + [fn] "r" (asi->insn_fn) + : "lr", "memory", "cc" + ); + + regs->uregs[rdlo] = rdlov; + regs->uregs[rdhi] = rdhiv; +} +/* t16 thumb actions */ + +static void __kprobes +t16_simulate_bxblx(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long pc = regs->ARM_pc + 2; + int rm = (insn >> 3) & 0xf; + unsigned long rmv = (rm == 15) ? pc : regs->uregs[rm]; + + if (insn & (1 << 7)) /* BLX ? */ + regs->ARM_lr = regs->ARM_pc | 1; + + bx_write_pc(rmv, regs); +} + +static void __kprobes +t16_simulate_ldr_literal(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long *base = (unsigned long *)((regs->ARM_pc + 2) & ~3); + long index = insn & 0xff; + int rt = (insn >> 8) & 0x7; + regs->uregs[rt] = base[index]; +} + +static void __kprobes +t16_simulate_ldrstr_sp_relative(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long* base = (unsigned long *)regs->ARM_sp; + long index = insn & 0xff; + int rt = (insn >> 8) & 0x7; + if (insn & 0x800) /* LDR */ + regs->uregs[rt] = base[index]; + else /* STR */ + base[index] = regs->uregs[rt]; +} + +static void __kprobes +t16_simulate_reladr(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long base = (insn & 0x800) ? regs->ARM_sp + : ((regs->ARM_pc + 2) & ~3); + long offset = insn & 0xff; + int rt = (insn >> 8) & 0x7; + regs->uregs[rt] = base + offset * 4; +} + +static void __kprobes +t16_simulate_add_sp_imm(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + long imm = insn & 0x7f; + if (insn & 0x80) /* SUB */ + regs->ARM_sp -= imm * 4; + else /* ADD */ + regs->ARM_sp += imm * 4; +} + +static void __kprobes +t16_simulate_cbz(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + int rn = insn & 0x7; + probes_opcode_t nonzero = regs->uregs[rn] ? insn : ~insn; + if (nonzero & 0x800) { + long i = insn & 0x200; + long imm5 = insn & 0xf8; + unsigned long pc = regs->ARM_pc + 2; + regs->ARM_pc = pc + (i >> 3) + (imm5 >> 2); + } +} + +static void __kprobes +t16_simulate_it(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + /* + * The 8 IT state bits are split into two parts in CPSR: + * ITSTATE<1:0> are in CPSR<26:25> + * ITSTATE<7:2> are in CPSR<15:10> + * The new IT state is in the lower byte of insn. + */ + unsigned long cpsr = regs->ARM_cpsr; + cpsr &= ~PSR_IT_MASK; + cpsr |= (insn & 0xfc) << 8; + cpsr |= (insn & 0x03) << 25; + regs->ARM_cpsr = cpsr; +} + +static void __kprobes +t16_singlestep_it(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + regs->ARM_pc += 2; + t16_simulate_it(insn, asi, regs); +} + +static enum probes_insn __kprobes +t16_decode_it(probes_opcode_t insn, struct arch_probes_insn *asi, + const struct decode_header *d) +{ + asi->insn_singlestep = t16_singlestep_it; + return INSN_GOOD_NO_SLOT; +} + +static void __kprobes +t16_simulate_cond_branch(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long pc = regs->ARM_pc + 2; + long offset = insn & 0x7f; + offset -= insn & 0x80; /* Apply sign bit */ + regs->ARM_pc = pc + (offset * 2); +} + +static enum probes_insn __kprobes +t16_decode_cond_branch(probes_opcode_t insn, struct arch_probes_insn *asi, + const struct decode_header *d) +{ + int cc = (insn >> 8) & 0xf; + asi->insn_check_cc = probes_condition_checks[cc]; + asi->insn_handler = t16_simulate_cond_branch; + return INSN_GOOD_NO_SLOT; +} + +static void __kprobes +t16_simulate_branch(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long pc = regs->ARM_pc + 2; + long offset = insn & 0x3ff; + offset -= insn & 0x400; /* Apply sign bit */ + regs->ARM_pc = pc + (offset * 2); +} + +static unsigned long __kprobes +t16_emulate_loregs(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long oldcpsr = regs->ARM_cpsr; + unsigned long newcpsr; + + __asm__ __volatile__ ( + "msr cpsr_fs, %[oldcpsr] \n\t" + "ldmia %[regs], {r0-r7} \n\t" + "blx %[fn] \n\t" + "stmia %[regs], {r0-r7} \n\t" + "mrs %[newcpsr], cpsr \n\t" + : [newcpsr] "=r" (newcpsr) + : [oldcpsr] "r" (oldcpsr), [regs] "r" (regs), + [fn] "r" (asi->insn_fn) + : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "lr", "memory", "cc" + ); + + return (oldcpsr & ~APSR_MASK) | (newcpsr & APSR_MASK); +} + +static void __kprobes +t16_emulate_loregs_rwflags(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + regs->ARM_cpsr = t16_emulate_loregs(insn, asi, regs); +} + +static void __kprobes +t16_emulate_loregs_noitrwflags(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long cpsr = t16_emulate_loregs(insn, asi, regs); + if (!in_it_block(cpsr)) + regs->ARM_cpsr = cpsr; +} + +static void __kprobes +t16_emulate_hiregs(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + unsigned long pc = regs->ARM_pc + 2; + int rdn = (insn & 0x7) | ((insn & 0x80) >> 4); + int rm = (insn >> 3) & 0xf; + + register unsigned long rdnv asm("r1"); + register unsigned long rmv asm("r0"); + unsigned long cpsr = regs->ARM_cpsr; + + rdnv = (rdn == 15) ? pc : regs->uregs[rdn]; + rmv = (rm == 15) ? pc : regs->uregs[rm]; + + __asm__ __volatile__ ( + "msr cpsr_fs, %[cpsr] \n\t" + "blx %[fn] \n\t" + "mrs %[cpsr], cpsr \n\t" + : "=r" (rdnv), [cpsr] "=r" (cpsr) + : "0" (rdnv), "r" (rmv), "1" (cpsr), [fn] "r" (asi->insn_fn) + : "lr", "memory", "cc" + ); + + if (rdn == 15) + rdnv &= ~1; + + regs->uregs[rdn] = rdnv; + regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); +} + +static enum probes_insn __kprobes +t16_decode_hiregs(probes_opcode_t insn, struct arch_probes_insn *asi, + const struct decode_header *d) +{ + insn &= ~0x00ff; + insn |= 0x001; /* Set Rdn = R1 and Rm = R0 */ + ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(insn); + asi->insn_handler = t16_emulate_hiregs; + return INSN_GOOD; +} + +static void __kprobes +t16_emulate_push(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + __asm__ __volatile__ ( + "ldr r9, [%[regs], #13*4] \n\t" + "ldr r8, [%[regs], #14*4] \n\t" + "ldmia %[regs], {r0-r7} \n\t" + "blx %[fn] \n\t" + "str r9, [%[regs], #13*4] \n\t" + : + : [regs] "r" (regs), [fn] "r" (asi->insn_fn) + : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", + "lr", "memory", "cc" + ); +} + +static enum probes_insn __kprobes +t16_decode_push(probes_opcode_t insn, struct arch_probes_insn *asi, + const struct decode_header *d) +{ + /* + * To simulate a PUSH we use a Thumb-2 "STMDB R9!, {registers}" + * and call it with R9=SP and LR in the register list represented + * by R8. + */ + /* 1st half STMDB R9!,{} */ + ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(0xe929); + /* 2nd half (register list) */ + ((u16 *)asi->insn)[1] = __opcode_to_mem_thumb16(insn & 0x1ff); + asi->insn_handler = t16_emulate_push; + return INSN_GOOD; +} + +static void __kprobes +t16_emulate_pop_nopc(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + __asm__ __volatile__ ( + "ldr r9, [%[regs], #13*4] \n\t" + "ldmia %[regs], {r0-r7} \n\t" + "blx %[fn] \n\t" + "stmia %[regs], {r0-r7} \n\t" + "str r9, [%[regs], #13*4] \n\t" + : + : [regs] "r" (regs), [fn] "r" (asi->insn_fn) + : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r9", + "lr", "memory", "cc" + ); +} + +static void __kprobes +t16_emulate_pop_pc(probes_opcode_t insn, + struct arch_probes_insn *asi, struct pt_regs *regs) +{ + register unsigned long pc asm("r8"); + + __asm__ __volatile__ ( + "ldr r9, [%[regs], #13*4] \n\t" + "ldmia %[regs], {r0-r7} \n\t" + "blx %[fn] \n\t" + "stmia %[regs], {r0-r7} \n\t" + "str r9, [%[regs], #13*4] \n\t" + : "=r" (pc) + : [regs] "r" (regs), [fn] "r" (asi->insn_fn) + : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r9", + "lr", "memory", "cc" + ); + + bx_write_pc(pc, regs); +} + +static enum probes_insn __kprobes +t16_decode_pop(probes_opcode_t insn, struct arch_probes_insn *asi, + const struct decode_header *d) +{ + /* + * To simulate a POP we use a Thumb-2 "LDMDB R9!, {registers}" + * and call it with R9=SP and PC in the register list represented + * by R8. + */ + /* 1st half LDMIA R9!,{} */ + ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(0xe8b9); + /* 2nd half (register list) */ + ((u16 *)asi->insn)[1] = __opcode_to_mem_thumb16(insn & 0x1ff); + asi->insn_handler = insn & 0x100 ? t16_emulate_pop_pc + : t16_emulate_pop_nopc; + return INSN_GOOD; +} + +const union decode_action kprobes_t16_actions[NUM_PROBES_T16_ACTIONS] = { + [PROBES_T16_ADD_SP] = {.handler = t16_simulate_add_sp_imm}, + [PROBES_T16_CBZ] = {.handler = t16_simulate_cbz}, + [PROBES_T16_SIGN_EXTEND] = {.handler = t16_emulate_loregs_rwflags}, + [PROBES_T16_PUSH] = {.decoder = t16_decode_push}, + [PROBES_T16_POP] = {.decoder = t16_decode_pop}, + [PROBES_T16_SEV] = {.handler = probes_emulate_none}, + [PROBES_T16_WFE] = {.handler = probes_simulate_nop}, + [PROBES_T16_IT] = {.decoder = t16_decode_it}, + [PROBES_T16_CMP] = {.handler = t16_emulate_loregs_rwflags}, + [PROBES_T16_ADDSUB] = {.handler = t16_emulate_loregs_noitrwflags}, + [PROBES_T16_LOGICAL] = {.handler = t16_emulate_loregs_noitrwflags}, + [PROBES_T16_LDR_LIT] = {.handler = t16_simulate_ldr_literal}, + [PROBES_T16_BLX] = {.handler = t16_simulate_bxblx}, + [PROBES_T16_HIREGOPS] = {.decoder = t16_decode_hiregs}, + [PROBES_T16_LDRHSTRH] = {.handler = t16_emulate_loregs_rwflags}, + [PROBES_T16_LDRSTR] = {.handler = t16_simulate_ldrstr_sp_relative}, + [PROBES_T16_ADR] = {.handler = t16_simulate_reladr}, + [PROBES_T16_LDMSTM] = {.handler = t16_emulate_loregs_rwflags}, + [PROBES_T16_BRANCH_COND] = {.decoder = t16_decode_cond_branch}, + [PROBES_T16_BRANCH] = {.handler = t16_simulate_branch}, +}; + +const union decode_action kprobes_t32_actions[NUM_PROBES_T32_ACTIONS] = { + [PROBES_T32_LDMSTM] = {.decoder = t32_decode_ldmstm}, + [PROBES_T32_LDRDSTRD] = {.handler = t32_emulate_ldrdstrd}, + [PROBES_T32_TABLE_BRANCH] = {.handler = t32_simulate_table_branch}, + [PROBES_T32_TST] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, + [PROBES_T32_MOV] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, + [PROBES_T32_ADDSUB] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, + [PROBES_T32_LOGICAL] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, + [PROBES_T32_CMP] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, + [PROBES_T32_ADDWSUBW_PC] = {.handler = t32_emulate_rd8pc16_noflags,}, + [PROBES_T32_ADDWSUBW] = {.handler = t32_emulate_rd8rn16_noflags}, + [PROBES_T32_MOVW] = {.handler = t32_emulate_rd8rn16_noflags}, + [PROBES_T32_SAT] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, + [PROBES_T32_BITFIELD] = {.handler = t32_emulate_rd8rn16_noflags}, + [PROBES_T32_SEV] = {.handler = probes_emulate_none}, + [PROBES_T32_WFE] = {.handler = probes_simulate_nop}, + [PROBES_T32_MRS] = {.handler = t32_simulate_mrs}, + [PROBES_T32_BRANCH_COND] = {.decoder = t32_decode_cond_branch}, + [PROBES_T32_BRANCH] = {.handler = t32_simulate_branch}, + [PROBES_T32_PLDI] = {.handler = probes_simulate_nop}, + [PROBES_T32_LDR_LIT] = {.handler = t32_simulate_ldr_literal}, + [PROBES_T32_LDRSTR] = {.handler = t32_emulate_ldrstr}, + [PROBES_T32_SIGN_EXTEND] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, + [PROBES_T32_MEDIA] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, + [PROBES_T32_REVERSE] = {.handler = t32_emulate_rd8rn16_noflags}, + [PROBES_T32_MUL_ADD] = {.handler = t32_emulate_rd8rn16rm0_rwflags}, + [PROBES_T32_MUL_ADD2] = {.handler = t32_emulate_rd8rn16rm0ra12_noflags}, + [PROBES_T32_MUL_ADD_LONG] = { + .handler = t32_emulate_rdlo12rdhi8rn16rm0_noflags}, +}; diff --git a/arch/arm/probes/kprobes/core.c b/arch/arm/probes/kprobes/core.c new file mode 100644 index 0000000..701f49d --- /dev/null +++ b/arch/arm/probes/kprobes/core.c @@ -0,0 +1,628 @@ +/* + * arch/arm/kernel/kprobes.c + * + * Kprobes on ARM + * + * Abhishek Sagar + * Copyright (C) 2006, 2007 Motorola Inc. + * + * Nicolas Pitre + * Copyright (C) 2007 Marvell Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../decode-arm.h" +#include "../decode-thumb.h" +#include "core.h" + +#define MIN_STACK_SIZE(addr) \ + min((unsigned long)MAX_STACK_SIZE, \ + (unsigned long)current_thread_info() + THREAD_START_SP - (addr)) + +#define flush_insns(addr, size) \ + flush_icache_range((unsigned long)(addr), \ + (unsigned long)(addr) + \ + (size)) + +/* Used as a marker in ARM_pc to note when we're in a jprobe. */ +#define JPROBE_MAGIC_ADDR 0xffffffff + +DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; +DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); + + +int __kprobes arch_prepare_kprobe(struct kprobe *p) +{ + kprobe_opcode_t insn; + kprobe_opcode_t tmp_insn[MAX_INSN_SIZE]; + unsigned long addr = (unsigned long)p->addr; + bool thumb; + kprobe_decode_insn_t *decode_insn; + const union decode_action *actions; + int is; + + if (in_exception_text(addr)) + return -EINVAL; + +#ifdef CONFIG_THUMB2_KERNEL + thumb = true; + addr &= ~1; /* Bit 0 would normally be set to indicate Thumb code */ + insn = __mem_to_opcode_thumb16(((u16 *)addr)[0]); + if (is_wide_instruction(insn)) { + u16 inst2 = __mem_to_opcode_thumb16(((u16 *)addr)[1]); + insn = __opcode_thumb32_compose(insn, inst2); + decode_insn = thumb32_probes_decode_insn; + actions = kprobes_t32_actions; + } else { + decode_insn = thumb16_probes_decode_insn; + actions = kprobes_t16_actions; + } +#else /* !CONFIG_THUMB2_KERNEL */ + thumb = false; + if (addr & 0x3) + return -EINVAL; + insn = __mem_to_opcode_arm(*p->addr); + decode_insn = arm_probes_decode_insn; + actions = kprobes_arm_actions; +#endif + + p->opcode = insn; + p->ainsn.insn = tmp_insn; + + switch ((*decode_insn)(insn, &p->ainsn, true, actions)) { + case INSN_REJECTED: /* not supported */ + return -EINVAL; + + case INSN_GOOD: /* instruction uses slot */ + p->ainsn.insn = get_insn_slot(); + if (!p->ainsn.insn) + return -ENOMEM; + for (is = 0; is < MAX_INSN_SIZE; ++is) + p->ainsn.insn[is] = tmp_insn[is]; + flush_insns(p->ainsn.insn, + sizeof(p->ainsn.insn[0]) * MAX_INSN_SIZE); + p->ainsn.insn_fn = (probes_insn_fn_t *) + ((uintptr_t)p->ainsn.insn | thumb); + break; + + case INSN_GOOD_NO_SLOT: /* instruction doesn't need insn slot */ + p->ainsn.insn = NULL; + break; + } + + return 0; +} + +void __kprobes arch_arm_kprobe(struct kprobe *p) +{ + unsigned int brkp; + void *addr; + + if (IS_ENABLED(CONFIG_THUMB2_KERNEL)) { + /* Remove any Thumb flag */ + addr = (void *)((uintptr_t)p->addr & ~1); + + if (is_wide_instruction(p->opcode)) + brkp = KPROBE_THUMB32_BREAKPOINT_INSTRUCTION; + else + brkp = KPROBE_THUMB16_BREAKPOINT_INSTRUCTION; + } else { + kprobe_opcode_t insn = p->opcode; + + addr = p->addr; + brkp = KPROBE_ARM_BREAKPOINT_INSTRUCTION; + + if (insn >= 0xe0000000) + brkp |= 0xe0000000; /* Unconditional instruction */ + else + brkp |= insn & 0xf0000000; /* Copy condition from insn */ + } + + patch_text(addr, brkp); +} + +/* + * The actual disarming is done here on each CPU and synchronized using + * stop_machine. This synchronization is necessary on SMP to avoid removing + * a probe between the moment the 'Undefined Instruction' exception is raised + * and the moment the exception handler reads the faulting instruction from + * memory. It is also needed to atomically set the two half-words of a 32-bit + * Thumb breakpoint. + */ +int __kprobes __arch_disarm_kprobe(void *p) +{ + struct kprobe *kp = p; + void *addr = (void *)((uintptr_t)kp->addr & ~1); + + __patch_text(addr, kp->opcode); + + return 0; +} + +void __kprobes arch_disarm_kprobe(struct kprobe *p) +{ + stop_machine(__arch_disarm_kprobe, p, cpu_online_mask); +} + +void __kprobes arch_remove_kprobe(struct kprobe *p) +{ + if (p->ainsn.insn) { + free_insn_slot(p->ainsn.insn, 0); + p->ainsn.insn = NULL; + } +} + +static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb) +{ + kcb->prev_kprobe.kp = kprobe_running(); + kcb->prev_kprobe.status = kcb->kprobe_status; +} + +static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb) +{ + __this_cpu_write(current_kprobe, kcb->prev_kprobe.kp); + kcb->kprobe_status = kcb->prev_kprobe.status; +} + +static void __kprobes set_current_kprobe(struct kprobe *p) +{ + __this_cpu_write(current_kprobe, p); +} + +static void __kprobes +singlestep_skip(struct kprobe *p, struct pt_regs *regs) +{ +#ifdef CONFIG_THUMB2_KERNEL + regs->ARM_cpsr = it_advance(regs->ARM_cpsr); + if (is_wide_instruction(p->opcode)) + regs->ARM_pc += 4; + else + regs->ARM_pc += 2; +#else + regs->ARM_pc += 4; +#endif +} + +static inline void __kprobes +singlestep(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb) +{ + p->ainsn.insn_singlestep(p->opcode, &p->ainsn, regs); +} + +/* + * Called with IRQs disabled. IRQs must remain disabled from that point + * all the way until processing this kprobe is complete. The current + * kprobes implementation cannot process more than one nested level of + * kprobe, and that level is reserved for user kprobe handlers, so we can't + * risk encountering a new kprobe in an interrupt handler. + */ +void __kprobes kprobe_handler(struct pt_regs *regs) +{ + struct kprobe *p, *cur; + struct kprobe_ctlblk *kcb; + + kcb = get_kprobe_ctlblk(); + cur = kprobe_running(); + +#ifdef CONFIG_THUMB2_KERNEL + /* + * First look for a probe which was registered using an address with + * bit 0 set, this is the usual situation for pointers to Thumb code. + * If not found, fallback to looking for one with bit 0 clear. + */ + p = get_kprobe((kprobe_opcode_t *)(regs->ARM_pc | 1)); + if (!p) + p = get_kprobe((kprobe_opcode_t *)regs->ARM_pc); + +#else /* ! CONFIG_THUMB2_KERNEL */ + p = get_kprobe((kprobe_opcode_t *)regs->ARM_pc); +#endif + + if (p) { + if (cur) { + /* Kprobe is pending, so we're recursing. */ + switch (kcb->kprobe_status) { + case KPROBE_HIT_ACTIVE: + case KPROBE_HIT_SSDONE: + /* A pre- or post-handler probe got us here. */ + kprobes_inc_nmissed_count(p); + save_previous_kprobe(kcb); + set_current_kprobe(p); + kcb->kprobe_status = KPROBE_REENTER; + singlestep(p, regs, kcb); + restore_previous_kprobe(kcb); + break; + default: + /* impossible cases */ + BUG(); + } + } else if (p->ainsn.insn_check_cc(regs->ARM_cpsr)) { + /* Probe hit and conditional execution check ok. */ + set_current_kprobe(p); + kcb->kprobe_status = KPROBE_HIT_ACTIVE; + + /* + * If we have no pre-handler or it returned 0, we + * continue with normal processing. If we have a + * pre-handler and it returned non-zero, it prepped + * for calling the break_handler below on re-entry, + * so get out doing nothing more here. + */ + if (!p->pre_handler || !p->pre_handler(p, regs)) { + kcb->kprobe_status = KPROBE_HIT_SS; + singlestep(p, regs, kcb); + if (p->post_handler) { + kcb->kprobe_status = KPROBE_HIT_SSDONE; + p->post_handler(p, regs, 0); + } + reset_current_kprobe(); + } + } else { + /* + * Probe hit but conditional execution check failed, + * so just skip the instruction and continue as if + * nothing had happened. + */ + singlestep_skip(p, regs); + } + } else if (cur) { + /* We probably hit a jprobe. Call its break handler. */ + if (cur->break_handler && cur->break_handler(cur, regs)) { + kcb->kprobe_status = KPROBE_HIT_SS; + singlestep(cur, regs, kcb); + if (cur->post_handler) { + kcb->kprobe_status = KPROBE_HIT_SSDONE; + cur->post_handler(cur, regs, 0); + } + } + reset_current_kprobe(); + } else { + /* + * The probe was removed and a race is in progress. + * There is nothing we can do about it. Let's restart + * the instruction. By the time we can restart, the + * real instruction will be there. + */ + } +} + +static int __kprobes kprobe_trap_handler(struct pt_regs *regs, unsigned int instr) +{ + unsigned long flags; + local_irq_save(flags); + kprobe_handler(regs); + local_irq_restore(flags); + return 0; +} + +int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr) +{ + struct kprobe *cur = kprobe_running(); + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + + switch (kcb->kprobe_status) { + case KPROBE_HIT_SS: + case KPROBE_REENTER: + /* + * We are here because the instruction being single + * stepped caused a page fault. We reset the current + * kprobe and the PC to point back to the probe address + * and allow the page fault handler to continue as a + * normal page fault. + */ + regs->ARM_pc = (long)cur->addr; + if (kcb->kprobe_status == KPROBE_REENTER) { + restore_previous_kprobe(kcb); + } else { + reset_current_kprobe(); + } + break; + + case KPROBE_HIT_ACTIVE: + case KPROBE_HIT_SSDONE: + /* + * We increment the nmissed count for accounting, + * we can also use npre/npostfault count for accounting + * these specific fault cases. + */ + kprobes_inc_nmissed_count(cur); + + /* + * We come here because instructions in the pre/post + * handler caused the page_fault, this could happen + * if handler tries to access user space by + * copy_from_user(), get_user() etc. Let the + * user-specified handler try to fix it. + */ + if (cur->fault_handler && cur->fault_handler(cur, regs, fsr)) + return 1; + break; + + default: + break; + } + + return 0; +} + +int __kprobes kprobe_exceptions_notify(struct notifier_block *self, + unsigned long val, void *data) +{ + /* + * notify_die() is currently never called on ARM, + * so this callback is currently empty. + */ + return NOTIFY_DONE; +} + +/* + * When a retprobed function returns, trampoline_handler() is called, + * calling the kretprobe's handler. We construct a struct pt_regs to + * give a view of registers r0-r11 to the user return-handler. This is + * not a complete pt_regs structure, but that should be plenty sufficient + * for kretprobe handlers which should normally be interested in r0 only + * anyway. + */ +void __naked __kprobes kretprobe_trampoline(void) +{ + __asm__ __volatile__ ( + "stmdb sp!, {r0 - r11} \n\t" + "mov r0, sp \n\t" + "bl trampoline_handler \n\t" + "mov lr, r0 \n\t" + "ldmia sp!, {r0 - r11} \n\t" +#ifdef CONFIG_THUMB2_KERNEL + "bx lr \n\t" +#else + "mov pc, lr \n\t" +#endif + : : : "memory"); +} + +/* Called from kretprobe_trampoline */ +static __used __kprobes void *trampoline_handler(struct pt_regs *regs) +{ + struct kretprobe_instance *ri = NULL; + struct hlist_head *head, empty_rp; + struct hlist_node *tmp; + unsigned long flags, orig_ret_address = 0; + unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline; + + INIT_HLIST_HEAD(&empty_rp); + kretprobe_hash_lock(current, &head, &flags); + + /* + * It is possible to have multiple instances associated with a given + * task either because multiple functions in the call path have + * a return probe installed on them, and/or more than one return + * probe was registered for a target function. + * + * We can handle this because: + * - instances are always inserted at the head of the list + * - when multiple return probes are registered for the same + * function, the first instance's ret_addr will point to the + * real return address, and all the rest will point to + * kretprobe_trampoline + */ + hlist_for_each_entry_safe(ri, tmp, head, hlist) { + if (ri->task != current) + /* another task is sharing our hash bucket */ + continue; + + if (ri->rp && ri->rp->handler) { + __this_cpu_write(current_kprobe, &ri->rp->kp); + get_kprobe_ctlblk()->kprobe_status = KPROBE_HIT_ACTIVE; + ri->rp->handler(ri, regs); + __this_cpu_write(current_kprobe, NULL); + } + + orig_ret_address = (unsigned long)ri->ret_addr; + recycle_rp_inst(ri, &empty_rp); + + if (orig_ret_address != trampoline_address) + /* + * This is the real return address. Any other + * instances associated with this task are for + * other calls deeper on the call stack + */ + break; + } + + kretprobe_assert(ri, orig_ret_address, trampoline_address); + kretprobe_hash_unlock(current, &flags); + + hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) { + hlist_del(&ri->hlist); + kfree(ri); + } + + return (void *)orig_ret_address; +} + +void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, + struct pt_regs *regs) +{ + ri->ret_addr = (kprobe_opcode_t *)regs->ARM_lr; + + /* Replace the return addr with trampoline addr. */ + regs->ARM_lr = (unsigned long)&kretprobe_trampoline; +} + +int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) +{ + struct jprobe *jp = container_of(p, struct jprobe, kp); + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + long sp_addr = regs->ARM_sp; + long cpsr; + + kcb->jprobe_saved_regs = *regs; + memcpy(kcb->jprobes_stack, (void *)sp_addr, MIN_STACK_SIZE(sp_addr)); + regs->ARM_pc = (long)jp->entry; + + cpsr = regs->ARM_cpsr | PSR_I_BIT; +#ifdef CONFIG_THUMB2_KERNEL + /* Set correct Thumb state in cpsr */ + if (regs->ARM_pc & 1) + cpsr |= PSR_T_BIT; + else + cpsr &= ~PSR_T_BIT; +#endif + regs->ARM_cpsr = cpsr; + + preempt_disable(); + return 1; +} + +void __kprobes jprobe_return(void) +{ + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + + __asm__ __volatile__ ( + /* + * Setup an empty pt_regs. Fill SP and PC fields as + * they're needed by longjmp_break_handler. + * + * We allocate some slack between the original SP and start of + * our fabricated regs. To be precise we want to have worst case + * covered which is STMFD with all 16 regs so we allocate 2 * + * sizeof(struct_pt_regs)). + * + * This is to prevent any simulated instruction from writing + * over the regs when they are accessing the stack. + */ +#ifdef CONFIG_THUMB2_KERNEL + "sub r0, %0, %1 \n\t" + "mov sp, r0 \n\t" +#else + "sub sp, %0, %1 \n\t" +#endif + "ldr r0, ="__stringify(JPROBE_MAGIC_ADDR)"\n\t" + "str %0, [sp, %2] \n\t" + "str r0, [sp, %3] \n\t" + "mov r0, sp \n\t" + "bl kprobe_handler \n\t" + + /* + * Return to the context saved by setjmp_pre_handler + * and restored by longjmp_break_handler. + */ +#ifdef CONFIG_THUMB2_KERNEL + "ldr lr, [sp, %2] \n\t" /* lr = saved sp */ + "ldrd r0, r1, [sp, %5] \n\t" /* r0,r1 = saved lr,pc */ + "ldr r2, [sp, %4] \n\t" /* r2 = saved psr */ + "stmdb lr!, {r0, r1, r2} \n\t" /* push saved lr and */ + /* rfe context */ + "ldmia sp, {r0 - r12} \n\t" + "mov sp, lr \n\t" + "ldr lr, [sp], #4 \n\t" + "rfeia sp! \n\t" +#else + "ldr r0, [sp, %4] \n\t" + "msr cpsr_cxsf, r0 \n\t" + "ldmia sp, {r0 - pc} \n\t" +#endif + : + : "r" (kcb->jprobe_saved_regs.ARM_sp), + "I" (sizeof(struct pt_regs) * 2), + "J" (offsetof(struct pt_regs, ARM_sp)), + "J" (offsetof(struct pt_regs, ARM_pc)), + "J" (offsetof(struct pt_regs, ARM_cpsr)), + "J" (offsetof(struct pt_regs, ARM_lr)) + : "memory", "cc"); +} + +int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) +{ + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + long stack_addr = kcb->jprobe_saved_regs.ARM_sp; + long orig_sp = regs->ARM_sp; + struct jprobe *jp = container_of(p, struct jprobe, kp); + + if (regs->ARM_pc == JPROBE_MAGIC_ADDR) { + if (orig_sp != stack_addr) { + struct pt_regs *saved_regs = + (struct pt_regs *)kcb->jprobe_saved_regs.ARM_sp; + printk("current sp %lx does not match saved sp %lx\n", + orig_sp, stack_addr); + printk("Saved registers for jprobe %p\n", jp); + show_regs(saved_regs); + printk("Current registers\n"); + show_regs(regs); + BUG(); + } + *regs = kcb->jprobe_saved_regs; + memcpy((void *)stack_addr, kcb->jprobes_stack, + MIN_STACK_SIZE(stack_addr)); + preempt_enable_no_resched(); + return 1; + } + return 0; +} + +int __kprobes arch_trampoline_kprobe(struct kprobe *p) +{ + return 0; +} + +#ifdef CONFIG_THUMB2_KERNEL + +static struct undef_hook kprobes_thumb16_break_hook = { + .instr_mask = 0xffff, + .instr_val = KPROBE_THUMB16_BREAKPOINT_INSTRUCTION, + .cpsr_mask = MODE_MASK, + .cpsr_val = SVC_MODE, + .fn = kprobe_trap_handler, +}; + +static struct undef_hook kprobes_thumb32_break_hook = { + .instr_mask = 0xffffffff, + .instr_val = KPROBE_THUMB32_BREAKPOINT_INSTRUCTION, + .cpsr_mask = MODE_MASK, + .cpsr_val = SVC_MODE, + .fn = kprobe_trap_handler, +}; + +#else /* !CONFIG_THUMB2_KERNEL */ + +static struct undef_hook kprobes_arm_break_hook = { + .instr_mask = 0x0fffffff, + .instr_val = KPROBE_ARM_BREAKPOINT_INSTRUCTION, + .cpsr_mask = MODE_MASK, + .cpsr_val = SVC_MODE, + .fn = kprobe_trap_handler, +}; + +#endif /* !CONFIG_THUMB2_KERNEL */ + +int __init arch_init_kprobes() +{ + arm_probes_decode_init(); +#ifdef CONFIG_THUMB2_KERNEL + register_undef_hook(&kprobes_thumb16_break_hook); + register_undef_hook(&kprobes_thumb32_break_hook); +#else + register_undef_hook(&kprobes_arm_break_hook); +#endif + return 0; +} diff --git a/arch/arm/probes/kprobes/core.h b/arch/arm/probes/kprobes/core.h new file mode 100644 index 0000000..2e1e5a3 --- /dev/null +++ b/arch/arm/probes/kprobes/core.h @@ -0,0 +1,53 @@ +/* + * arch/arm/kernel/kprobes.h + * + * Copyright (C) 2011 Jon Medhurst . + * + * Some contents moved here from arch/arm/include/asm/kprobes.h which is + * Copyright (C) 2006, 2007 Motorola Inc. + * + * 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. + */ + +#ifndef _ARM_KERNEL_KPROBES_H +#define _ARM_KERNEL_KPROBES_H + +#include +#include "../decode.h" + +/* + * These undefined instructions must be unique and + * reserved solely for kprobes' use. + */ +#define KPROBE_ARM_BREAKPOINT_INSTRUCTION 0x07f001f8 +#define KPROBE_THUMB16_BREAKPOINT_INSTRUCTION 0xde18 +#define KPROBE_THUMB32_BREAKPOINT_INSTRUCTION 0xf7f0a018 + +enum probes_insn __kprobes +kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_probes_insn *asi, + const struct decode_header *h); + +typedef enum probes_insn (kprobe_decode_insn_t)(probes_opcode_t, + struct arch_probes_insn *, + bool, + const union decode_action *); + +#ifdef CONFIG_THUMB2_KERNEL + +extern const union decode_action kprobes_t32_actions[]; +extern const union decode_action kprobes_t16_actions[]; + +#else /* !CONFIG_THUMB2_KERNEL */ + +extern const union decode_action kprobes_arm_actions[]; + +#endif + +#endif /* _ARM_KERNEL_KPROBES_H */ diff --git a/arch/arm/probes/kprobes/test-arm.c b/arch/arm/probes/kprobes/test-arm.c new file mode 100644 index 0000000..d9a1255 --- /dev/null +++ b/arch/arm/probes/kprobes/test-arm.c @@ -0,0 +1,1346 @@ +/* + * arch/arm/kernel/kprobes-test-arm.c + * + * Copyright (C) 2011 Jon Medhurst . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include "test-core.h" + + +#define TEST_ISA "32" + +#define TEST_ARM_TO_THUMB_INTERWORK_R(code1, reg, val, code2) \ + TESTCASE_START(code1 #reg code2) \ + TEST_ARG_REG(reg, val) \ + TEST_ARG_REG(14, 99f) \ + TEST_ARG_END("") \ + "50: nop \n\t" \ + "1: "code1 #reg code2" \n\t" \ + " bx lr \n\t" \ + ".thumb \n\t" \ + "3: adr lr, 2f \n\t" \ + " bx lr \n\t" \ + ".arm \n\t" \ + "2: nop \n\t" \ + TESTCASE_END + +#define TEST_ARM_TO_THUMB_INTERWORK_P(code1, reg, val, code2) \ + TESTCASE_START(code1 #reg code2) \ + TEST_ARG_PTR(reg, val) \ + TEST_ARG_REG(14, 99f) \ + TEST_ARG_MEM(15, 3f+1) \ + TEST_ARG_END("") \ + "50: nop \n\t" \ + "1: "code1 #reg code2" \n\t" \ + " bx lr \n\t" \ + ".thumb \n\t" \ + "3: adr lr, 2f \n\t" \ + " bx lr \n\t" \ + ".arm \n\t" \ + "2: nop \n\t" \ + TESTCASE_END + + +void kprobe_arm_test_cases(void) +{ + kprobe_test_flags = 0; + + TEST_GROUP("Data-processing (register), (register-shifted register), (immediate)") + +#define _DATA_PROCESSING_DNM(op,s,val) \ + TEST_RR( op "eq" s " r0, r",1, VAL1,", r",2, val, "") \ + TEST_RR( op "ne" s " r1, r",1, VAL1,", r",2, val, ", lsl #3") \ + TEST_RR( op "cs" s " r2, r",3, VAL1,", r",2, val, ", lsr #4") \ + TEST_RR( op "cc" s " r3, r",3, VAL1,", r",2, val, ", asr #5") \ + TEST_RR( op "mi" s " r4, r",5, VAL1,", r",2, N(val),", asr #6") \ + TEST_RR( op "pl" s " r5, r",5, VAL1,", r",2, val, ", ror #7") \ + TEST_RR( op "vs" s " r6, r",7, VAL1,", r",2, val, ", rrx") \ + TEST_R( op "vc" s " r6, r",7, VAL1,", pc, lsl #3") \ + TEST_R( op "vc" s " r6, r",7, VAL1,", sp, lsr #4") \ + TEST_R( op "vc" s " r6, pc, r",7, VAL1,", asr #5") \ + TEST_R( op "vc" s " r6, sp, r",7, VAL1,", ror #6") \ + TEST_RRR( op "hi" s " r8, r",9, VAL1,", r",14,val, ", lsl r",0, 3,"")\ + TEST_RRR( op "ls" s " r9, r",9, VAL1,", r",14,val, ", lsr r",7, 4,"")\ + TEST_RRR( op "ge" s " r10, r",11,VAL1,", r",14,val, ", asr r",7, 5,"")\ + TEST_RRR( op "lt" s " r11, r",11,VAL1,", r",14,N(val),", asr r",7, 6,"")\ + TEST_RR( op "gt" s " r12, r13" ", r",14,val, ", ror r",14,7,"")\ + TEST_RR( op "le" s " r14, r",0, val, ", r13" ", lsl r",14,8,"")\ + TEST_R( op "eq" s " r0, r",11,VAL1,", #0xf5") \ + TEST_R( op "ne" s " r11, r",0, VAL1,", #0xf5000000") \ + TEST_R( op s " r7, r",8, VAL2,", #0x000af000") \ + TEST( op s " r4, pc" ", #0x00005a00") + +#define DATA_PROCESSING_DNM(op,val) \ + _DATA_PROCESSING_DNM(op,"",val) \ + _DATA_PROCESSING_DNM(op,"s",val) + +#define DATA_PROCESSING_NM(op,val) \ + TEST_RR( op "ne r",1, VAL1,", r",2, val, "") \ + TEST_RR( op "eq r",1, VAL1,", r",2, val, ", lsl #3") \ + TEST_RR( op "cc r",3, VAL1,", r",2, val, ", lsr #4") \ + TEST_RR( op "cs r",3, VAL1,", r",2, val, ", asr #5") \ + TEST_RR( op "pl r",5, VAL1,", r",2, N(val),", asr #6") \ + TEST_RR( op "mi r",5, VAL1,", r",2, val, ", ror #7") \ + TEST_RR( op "vc r",7, VAL1,", r",2, val, ", rrx") \ + TEST_R ( op "vs r",7, VAL1,", pc, lsl #3") \ + TEST_R ( op "vs r",7, VAL1,", sp, lsr #4") \ + TEST_R( op "vs pc, r",7, VAL1,", asr #5") \ + TEST_R( op "vs sp, r",7, VAL1,", ror #6") \ + TEST_RRR( op "ls r",9, VAL1,", r",14,val, ", lsl r",0, 3,"") \ + TEST_RRR( op "hi r",9, VAL1,", r",14,val, ", lsr r",7, 4,"") \ + TEST_RRR( op "lt r",11,VAL1,", r",14,val, ", asr r",7, 5,"") \ + TEST_RRR( op "ge r",11,VAL1,", r",14,N(val),", asr r",7, 6,"") \ + TEST_RR( op "le r13" ", r",14,val, ", ror r",14,7,"") \ + TEST_RR( op "gt r",0, val, ", r13" ", lsl r",14,8,"") \ + TEST_R( op "eq r",11,VAL1,", #0xf5") \ + TEST_R( op "ne r",0, VAL1,", #0xf5000000") \ + TEST_R( op " r",8, VAL2,", #0x000af000") + +#define _DATA_PROCESSING_DM(op,s,val) \ + TEST_R( op "eq" s " r0, r",1, val, "") \ + TEST_R( op "ne" s " r1, r",1, val, ", lsl #3") \ + TEST_R( op "cs" s " r2, r",3, val, ", lsr #4") \ + TEST_R( op "cc" s " r3, r",3, val, ", asr #5") \ + TEST_R( op "mi" s " r4, r",5, N(val),", asr #6") \ + TEST_R( op "pl" s " r5, r",5, val, ", ror #7") \ + TEST_R( op "vs" s " r6, r",10,val, ", rrx") \ + TEST( op "vs" s " r7, pc, lsl #3") \ + TEST( op "vs" s " r7, sp, lsr #4") \ + TEST_RR( op "vc" s " r8, r",7, val, ", lsl r",0, 3,"") \ + TEST_RR( op "hi" s " r9, r",9, val, ", lsr r",7, 4,"") \ + TEST_RR( op "ls" s " r10, r",9, val, ", asr r",7, 5,"") \ + TEST_RR( op "ge" s " r11, r",11,N(val),", asr r",7, 6,"") \ + TEST_RR( op "lt" s " r12, r",11,val, ", ror r",14,7,"") \ + TEST_R( op "gt" s " r14, r13" ", lsl r",14,8,"") \ + TEST( op "eq" s " r0, #0xf5") \ + TEST( op "ne" s " r11, #0xf5000000") \ + TEST( op s " r7, #0x000af000") \ + TEST( op s " r4, #0x00005a00") + +#define DATA_PROCESSING_DM(op,val) \ + _DATA_PROCESSING_DM(op,"",val) \ + _DATA_PROCESSING_DM(op,"s",val) + + DATA_PROCESSING_DNM("and",0xf00f00ff) + DATA_PROCESSING_DNM("eor",0xf00f00ff) + DATA_PROCESSING_DNM("sub",VAL2) + DATA_PROCESSING_DNM("rsb",VAL2) + DATA_PROCESSING_DNM("add",VAL2) + DATA_PROCESSING_DNM("adc",VAL2) + DATA_PROCESSING_DNM("sbc",VAL2) + DATA_PROCESSING_DNM("rsc",VAL2) + DATA_PROCESSING_NM("tst",0xf00f00ff) + DATA_PROCESSING_NM("teq",0xf00f00ff) + DATA_PROCESSING_NM("cmp",VAL2) + DATA_PROCESSING_NM("cmn",VAL2) + DATA_PROCESSING_DNM("orr",0xf00f00ff) + DATA_PROCESSING_DM("mov",VAL2) + DATA_PROCESSING_DNM("bic",0xf00f00ff) + DATA_PROCESSING_DM("mvn",VAL2) + + TEST("mov ip, sp") /* This has special case emulation code */ + + TEST_SUPPORTED("mov pc, #0x1000"); + TEST_SUPPORTED("mov sp, #0x1000"); + TEST_SUPPORTED("cmp pc, #0x1000"); + TEST_SUPPORTED("cmp sp, #0x1000"); + + /* Data-processing with PC and a shift count in a register */ + TEST_UNSUPPORTED(__inst_arm(0xe15c0f1e) " @ cmp r12, r14, asl pc") + TEST_UNSUPPORTED(__inst_arm(0xe1a0cf1e) " @ mov r12, r14, asl pc") + TEST_UNSUPPORTED(__inst_arm(0xe08caf1e) " @ add r10, r12, r14, asl pc") + TEST_UNSUPPORTED(__inst_arm(0xe151021f) " @ cmp r1, pc, lsl r2") + TEST_UNSUPPORTED(__inst_arm(0xe17f0211) " @ cmn pc, r1, lsl r2") + TEST_UNSUPPORTED(__inst_arm(0xe1a0121f) " @ mov r1, pc, lsl r2") + TEST_UNSUPPORTED(__inst_arm(0xe1a0f211) " @ mov pc, r1, lsl r2") + TEST_UNSUPPORTED(__inst_arm(0xe042131f) " @ sub r1, r2, pc, lsl r3") + TEST_UNSUPPORTED(__inst_arm(0xe1cf1312) " @ bic r1, pc, r2, lsl r3") + TEST_UNSUPPORTED(__inst_arm(0xe081f312) " @ add pc, r1, r2, lsl r3") + + /* Data-processing with PC as a target and status registers updated */ + TEST_UNSUPPORTED("movs pc, r1") + TEST_UNSUPPORTED("movs pc, r1, lsl r2") + TEST_UNSUPPORTED("movs pc, #0x10000") + TEST_UNSUPPORTED("adds pc, lr, r1") + TEST_UNSUPPORTED("adds pc, lr, r1, lsl r2") + TEST_UNSUPPORTED("adds pc, lr, #4") + + /* Data-processing with SP as target */ + TEST("add sp, sp, #16") + TEST("sub sp, sp, #8") + TEST("bic sp, sp, #0x20") + TEST("orr sp, sp, #0x20") + TEST_PR( "add sp, r",10,0,", r",11,4,"") + TEST_PRR("add sp, r",10,0,", r",11,4,", asl r",12,1,"") + TEST_P( "mov sp, r",10,0,"") + TEST_PR( "mov sp, r",10,0,", asl r",12,0,"") + + /* Data-processing with PC as target */ + TEST_BF( "add pc, pc, #2f-1b-8") + TEST_BF_R ("add pc, pc, r",14,2f-1f-8,"") + TEST_BF_R ("add pc, r",14,2f-1f-8,", pc") + TEST_BF_R ("mov pc, r",0,2f,"") + TEST_BF_R ("add pc, pc, r",14,(2f-1f-8)*2,", asr #1") + TEST_BB( "sub pc, pc, #1b-2b+8") +#if __LINUX_ARM_ARCH__ == 6 && !defined(CONFIG_CPU_V7) + TEST_BB( "sub pc, pc, #1b-2b+8-2") /* UNPREDICTABLE before and after ARMv6 */ +#endif + TEST_BB_R( "sub pc, pc, r",14, 1f-2f+8,"") + TEST_BB_R( "rsb pc, r",14,1f-2f+8,", pc") + TEST_R( "add pc, pc, r",10,-2,", asl #1") +#ifdef CONFIG_THUMB2_KERNEL + TEST_ARM_TO_THUMB_INTERWORK_R("add pc, pc, r",0,3f-1f-8+1,"") + TEST_ARM_TO_THUMB_INTERWORK_R("sub pc, r",0,3f+8+1,", #8") +#endif + TEST_GROUP("Miscellaneous instructions") + + TEST("mrs r0, cpsr") + TEST("mrspl r7, cpsr") + TEST("mrs r14, cpsr") + TEST_UNSUPPORTED(__inst_arm(0xe10ff000) " @ mrs r15, cpsr") + TEST_UNSUPPORTED("mrs r0, spsr") + TEST_UNSUPPORTED("mrs lr, spsr") + + TEST_UNSUPPORTED("msr cpsr, r0") + TEST_UNSUPPORTED("msr cpsr_f, lr") + TEST_UNSUPPORTED("msr spsr, r0") + + TEST_BF_R("bx r",0,2f,"") + TEST_BB_R("bx r",7,2f,"") + TEST_BF_R("bxeq r",14,2f,"") + +#if __LINUX_ARM_ARCH__ >= 5 + TEST_R("clz r0, r",0, 0x0,"") + TEST_R("clzeq r7, r",14,0x1,"") + TEST_R("clz lr, r",7, 0xffffffff,"") + TEST( "clz r4, sp") + TEST_UNSUPPORTED(__inst_arm(0x016fff10) " @ clz pc, r0") + TEST_UNSUPPORTED(__inst_arm(0x016f0f1f) " @ clz r0, pc") + +#if __LINUX_ARM_ARCH__ >= 6 + TEST_UNSUPPORTED("bxj r0") +#endif + + TEST_BF_R("blx r",0,2f,"") + TEST_BB_R("blx r",7,2f,"") + TEST_BF_R("blxeq r",14,2f,"") + TEST_UNSUPPORTED(__inst_arm(0x0120003f) " @ blx pc") + + TEST_RR( "qadd r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "qaddvs lr, r",9, VAL2,", r",8, VAL1,"") + TEST_R( "qadd lr, r",9, VAL2,", r13") + TEST_RR( "qsub r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "qsubvs lr, r",9, VAL2,", r",8, VAL1,"") + TEST_R( "qsub lr, r",9, VAL2,", r13") + TEST_RR( "qdadd r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "qdaddvs lr, r",9, VAL2,", r",8, VAL1,"") + TEST_R( "qdadd lr, r",9, VAL2,", r13") + TEST_RR( "qdsub r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "qdsubvs lr, r",9, VAL2,", r",8, VAL1,"") + TEST_R( "qdsub lr, r",9, VAL2,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe101f050) " @ qadd pc, r0, r1") + TEST_UNSUPPORTED(__inst_arm(0xe121f050) " @ qsub pc, r0, r1") + TEST_UNSUPPORTED(__inst_arm(0xe141f050) " @ qdadd pc, r0, r1") + TEST_UNSUPPORTED(__inst_arm(0xe161f050) " @ qdsub pc, r0, r1") + TEST_UNSUPPORTED(__inst_arm(0xe16f2050) " @ qdsub r2, r0, pc") + TEST_UNSUPPORTED(__inst_arm(0xe161205f) " @ qdsub r2, pc, r1") + + TEST_UNSUPPORTED("bkpt 0xffff") + TEST_UNSUPPORTED("bkpt 0x0000") + + TEST_UNSUPPORTED(__inst_arm(0xe1600070) " @ smc #0") + + TEST_GROUP("Halfword multiply and multiply-accumulate") + + TEST_RRR( "smlabb r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "smlabbge r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_RR( "smlabb lr, r",1, VAL2,", r",2, VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe10f3281) " @ smlabb pc, r1, r2, r3") + TEST_RRR( "smlatb r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "smlatbge r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_RR( "smlatb lr, r",1, VAL2,", r",2, VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe10f32a1) " @ smlatb pc, r1, r2, r3") + TEST_RRR( "smlabt r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "smlabtge r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_RR( "smlabt lr, r",1, VAL2,", r",2, VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe10f32c1) " @ smlabt pc, r1, r2, r3") + TEST_RRR( "smlatt r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "smlattge r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_RR( "smlatt lr, r",1, VAL2,", r",2, VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe10f32e1) " @ smlatt pc, r1, r2, r3") + + TEST_RRR( "smlawb r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "smlawbge r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_RR( "smlawb lr, r",1, VAL2,", r",2, VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe12f3281) " @ smlawb pc, r1, r2, r3") + TEST_RRR( "smlawt r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "smlawtge r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_RR( "smlawt lr, r",1, VAL2,", r",2, VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe12f32c1) " @ smlawt pc, r1, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe12032cf) " @ smlawt r0, pc, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe1203fc1) " @ smlawt r0, r1, pc, r3") + TEST_UNSUPPORTED(__inst_arm(0xe120f2c1) " @ smlawt r0, r1, r2, pc") + + TEST_RR( "smulwb r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "smulwbge r7, r",8, VAL3,", r",9, VAL1,"") + TEST_R( "smulwb lr, r",1, VAL2,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe12f02a1) " @ smulwb pc, r1, r2") + TEST_RR( "smulwt r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "smulwtge r7, r",8, VAL3,", r",9, VAL1,"") + TEST_R( "smulwt lr, r",1, VAL2,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe12f02e1) " @ smulwt pc, r1, r2") + + TEST_RRRR( "smlalbb r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) + TEST_RRRR( "smlalbble r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) + TEST_RRR( "smlalbb r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe14f1382) " @ smlalbb pc, r1, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe141f382) " @ smlalbb r1, pc, r2, r3") + TEST_RRRR( "smlaltb r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) + TEST_RRRR( "smlaltble r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) + TEST_RRR( "smlaltb r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe14f13a2) " @ smlaltb pc, r1, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe141f3a2) " @ smlaltb r1, pc, r2, r3") + TEST_RRRR( "smlalbt r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) + TEST_RRRR( "smlalbtle r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) + TEST_RRR( "smlalbt r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe14f13c2) " @ smlalbt pc, r1, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe141f3c2) " @ smlalbt r1, pc, r2, r3") + TEST_RRRR( "smlaltt r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) + TEST_RRRR( "smlalttle r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) + TEST_RRR( "smlaltt r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe14f13e2) " @ smlalbb pc, r1, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe140f3e2) " @ smlalbb r0, pc, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe14013ef) " @ smlalbb r0, r1, pc, r3") + TEST_UNSUPPORTED(__inst_arm(0xe1401fe2) " @ smlalbb r0, r1, r2, pc") + + TEST_RR( "smulbb r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "smulbbge r7, r",8, VAL3,", r",9, VAL1,"") + TEST_R( "smulbb lr, r",1, VAL2,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe16f0281) " @ smulbb pc, r1, r2") + TEST_RR( "smultb r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "smultbge r7, r",8, VAL3,", r",9, VAL1,"") + TEST_R( "smultb lr, r",1, VAL2,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe16f02a1) " @ smultb pc, r1, r2") + TEST_RR( "smulbt r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "smulbtge r7, r",8, VAL3,", r",9, VAL1,"") + TEST_R( "smulbt lr, r",1, VAL2,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe16f02c1) " @ smultb pc, r1, r2") + TEST_RR( "smultt r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "smulttge r7, r",8, VAL3,", r",9, VAL1,"") + TEST_R( "smultt lr, r",1, VAL2,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe16f02e1) " @ smultt pc, r1, r2") + TEST_UNSUPPORTED(__inst_arm(0xe16002ef) " @ smultt r0, pc, r2") + TEST_UNSUPPORTED(__inst_arm(0xe1600fe1) " @ smultt r0, r1, pc") +#endif + + TEST_GROUP("Multiply and multiply-accumulate") + + TEST_RR( "mul r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "mulls r7, r",8, VAL2,", r",9, VAL2,"") + TEST_R( "mul lr, r",4, VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe00f0291) " @ mul pc, r1, r2") + TEST_UNSUPPORTED(__inst_arm(0xe000029f) " @ mul r0, pc, r2") + TEST_UNSUPPORTED(__inst_arm(0xe0000f91) " @ mul r0, r1, pc") + TEST_RR( "muls r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "mullss r7, r",8, VAL2,", r",9, VAL2,"") + TEST_R( "muls lr, r",4, VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe01f0291) " @ muls pc, r1, r2") + + TEST_RRR( "mla r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "mlahi r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_RR( "mla lr, r",1, VAL2,", r",2, VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe02f3291) " @ mla pc, r1, r2, r3") + TEST_RRR( "mlas r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "mlahis r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_RR( "mlas lr, r",1, VAL2,", r",2, VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe03f3291) " @ mlas pc, r1, r2, r3") + +#if __LINUX_ARM_ARCH__ >= 6 + TEST_RR( "umaal r0, r1, r",2, VAL1,", r",3, VAL2,"") + TEST_RR( "umaalls r7, r8, r",9, VAL2,", r",10, VAL1,"") + TEST_R( "umaal lr, r12, r",11,VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe041f392) " @ umaal pc, r1, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe04f0392) " @ umaal r0, pc, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe0500090) " @ undef") + TEST_UNSUPPORTED(__inst_arm(0xe05fff9f) " @ undef") +#endif + +#if __LINUX_ARM_ARCH__ >= 7 + TEST_RRR( "mls r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "mlshi r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_RR( "mls lr, r",1, VAL2,", r",2, VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe06f3291) " @ mls pc, r1, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe060329f) " @ mls r0, pc, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe0603f91) " @ mls r0, r1, pc, r3") + TEST_UNSUPPORTED(__inst_arm(0xe060f291) " @ mls r0, r1, r2, pc") +#endif + + TEST_UNSUPPORTED(__inst_arm(0xe0700090) " @ undef") + TEST_UNSUPPORTED(__inst_arm(0xe07fff9f) " @ undef") + + TEST_RR( "umull r0, r1, r",2, VAL1,", r",3, VAL2,"") + TEST_RR( "umullls r7, r8, r",9, VAL2,", r",10, VAL1,"") + TEST_R( "umull lr, r12, r",11,VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe081f392) " @ umull pc, r1, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe08f1392) " @ umull r1, pc, r2, r3") + TEST_RR( "umulls r0, r1, r",2, VAL1,", r",3, VAL2,"") + TEST_RR( "umulllss r7, r8, r",9, VAL2,", r",10, VAL1,"") + TEST_R( "umulls lr, r12, r",11,VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe091f392) " @ umulls pc, r1, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe09f1392) " @ umulls r1, pc, r2, r3") + + TEST_RRRR( "umlal r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) + TEST_RRRR( "umlalle r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) + TEST_RRR( "umlal r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe0af1392) " @ umlal pc, r1, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe0a1f392) " @ umlal r1, pc, r2, r3") + TEST_RRRR( "umlals r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) + TEST_RRRR( "umlalles r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) + TEST_RRR( "umlals r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe0bf1392) " @ umlals pc, r1, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe0b1f392) " @ umlals r1, pc, r2, r3") + + TEST_RR( "smull r0, r1, r",2, VAL1,", r",3, VAL2,"") + TEST_RR( "smullls r7, r8, r",9, VAL2,", r",10, VAL1,"") + TEST_R( "smull lr, r12, r",11,VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe0c1f392) " @ smull pc, r1, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe0cf1392) " @ smull r1, pc, r2, r3") + TEST_RR( "smulls r0, r1, r",2, VAL1,", r",3, VAL2,"") + TEST_RR( "smulllss r7, r8, r",9, VAL2,", r",10, VAL1,"") + TEST_R( "smulls lr, r12, r",11,VAL3,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe0d1f392) " @ smulls pc, r1, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe0df1392) " @ smulls r1, pc, r2, r3") + + TEST_RRRR( "smlal r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) + TEST_RRRR( "smlalle r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) + TEST_RRR( "smlal r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe0ef1392) " @ smlal pc, r1, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe0e1f392) " @ smlal r1, pc, r2, r3") + TEST_RRRR( "smlals r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) + TEST_RRRR( "smlalles r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) + TEST_RRR( "smlals r",14,VAL3,", r",7, VAL4,", r",5, VAL1,", r13") + TEST_UNSUPPORTED(__inst_arm(0xe0ff1392) " @ smlals pc, r1, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe0f0f392) " @ smlals r0, pc, r2, r3") + TEST_UNSUPPORTED(__inst_arm(0xe0f0139f) " @ smlals r0, r1, pc, r3") + TEST_UNSUPPORTED(__inst_arm(0xe0f01f92) " @ smlals r0, r1, r2, pc") + + TEST_GROUP("Synchronization primitives") + +#if __LINUX_ARM_ARCH__ < 6 + TEST_RP("swp lr, r",7,VAL2,", [r",8,0,"]") + TEST_R( "swpvs r0, r",1,VAL1,", [sp]") + TEST_RP("swp sp, r",14,VAL2,", [r",12,13*4,"]") +#else + TEST_UNSUPPORTED(__inst_arm(0xe108e097) " @ swp lr, r7, [r8]") + TEST_UNSUPPORTED(__inst_arm(0x610d0091) " @ swpvs r0, r1, [sp]") + TEST_UNSUPPORTED(__inst_arm(0xe10cd09e) " @ swp sp, r14 [r12]") +#endif + TEST_UNSUPPORTED(__inst_arm(0xe102f091) " @ swp pc, r1, [r2]") + TEST_UNSUPPORTED(__inst_arm(0xe102009f) " @ swp r0, pc, [r2]") + TEST_UNSUPPORTED(__inst_arm(0xe10f0091) " @ swp r0, r1, [pc]") +#if __LINUX_ARM_ARCH__ < 6 + TEST_RP("swpb lr, r",7,VAL2,", [r",8,0,"]") + TEST_R( "swpvsb r0, r",1,VAL1,", [sp]") +#else + TEST_UNSUPPORTED(__inst_arm(0xe148e097) " @ swpb lr, r7, [r8]") + TEST_UNSUPPORTED(__inst_arm(0x614d0091) " @ swpvsb r0, r1, [sp]") +#endif + TEST_UNSUPPORTED(__inst_arm(0xe142f091) " @ swpb pc, r1, [r2]") + + TEST_UNSUPPORTED(__inst_arm(0xe1100090)) /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe1200090)) /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe1300090)) /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe1500090)) /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe1600090)) /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe1700090)) /* Unallocated space */ +#if __LINUX_ARM_ARCH__ >= 6 + TEST_UNSUPPORTED("ldrex r2, [sp]") +#endif +#if (__LINUX_ARM_ARCH__ >= 7) || defined(CONFIG_CPU_32v6K) + TEST_UNSUPPORTED("strexd r0, r2, r3, [sp]") + TEST_UNSUPPORTED("ldrexd r2, r3, [sp]") + TEST_UNSUPPORTED("strexb r0, r2, [sp]") + TEST_UNSUPPORTED("ldrexb r2, [sp]") + TEST_UNSUPPORTED("strexh r0, r2, [sp]") + TEST_UNSUPPORTED("ldrexh r2, [sp]") +#endif + TEST_GROUP("Extra load/store instructions") + + TEST_RPR( "strh r",0, VAL1,", [r",1, 48,", -r",2, 24,"]") + TEST_RPR( "streqh r",14,VAL2,", [r",13,0, ", r",12, 48,"]") + TEST_RPR( "strh r",1, VAL1,", [r",2, 24,", r",3, 48,"]!") + TEST_RPR( "strneh r",12,VAL2,", [r",11,48,", -r",10,24,"]!") + TEST_RPR( "strh r",2, VAL1,", [r",3, 24,"], r",4, 48,"") + TEST_RPR( "strh r",10,VAL2,", [r",9, 48,"], -r",11,24,"") + TEST_UNSUPPORTED(__inst_arm(0xe1afc0ba) " @ strh r12, [pc, r10]!") + TEST_UNSUPPORTED(__inst_arm(0xe089f0bb) " @ strh pc, [r9], r11") + TEST_UNSUPPORTED(__inst_arm(0xe089a0bf) " @ strh r10, [r9], pc") + + TEST_PR( "ldrh r0, [r",0, 48,", -r",2, 24,"]") + TEST_PR( "ldrcsh r14, [r",13,0, ", r",12, 48,"]") + TEST_PR( "ldrh r1, [r",2, 24,", r",3, 48,"]!") + TEST_PR( "ldrcch r12, [r",11,48,", -r",10,24,"]!") + TEST_PR( "ldrh r2, [r",3, 24,"], r",4, 48,"") + TEST_PR( "ldrh r10, [r",9, 48,"], -r",11,24,"") + TEST_UNSUPPORTED(__inst_arm(0xe1bfc0ba) " @ ldrh r12, [pc, r10]!") + TEST_UNSUPPORTED(__inst_arm(0xe099f0bb) " @ ldrh pc, [r9], r11") + TEST_UNSUPPORTED(__inst_arm(0xe099a0bf) " @ ldrh r10, [r9], pc") + + TEST_RP( "strh r",0, VAL1,", [r",1, 24,", #-2]") + TEST_RP( "strmih r",14,VAL2,", [r",13,0, ", #2]") + TEST_RP( "strh r",1, VAL1,", [r",2, 24,", #4]!") + TEST_RP( "strplh r",12,VAL2,", [r",11,24,", #-4]!") + TEST_RP( "strh r",2, VAL1,", [r",3, 24,"], #48") + TEST_RP( "strh r",10,VAL2,", [r",9, 64,"], #-48") + TEST_UNSUPPORTED(__inst_arm(0xe1efc3b0) " @ strh r12, [pc, #48]!") + TEST_UNSUPPORTED(__inst_arm(0xe0c9f3b0) " @ strh pc, [r9], #48") + + TEST_P( "ldrh r0, [r",0, 24,", #-2]") + TEST_P( "ldrvsh r14, [r",13,0, ", #2]") + TEST_P( "ldrh r1, [r",2, 24,", #4]!") + TEST_P( "ldrvch r12, [r",11,24,", #-4]!") + TEST_P( "ldrh r2, [r",3, 24,"], #48") + TEST_P( "ldrh r10, [r",9, 64,"], #-48") + TEST( "ldrh r0, [pc, #0]") + TEST_UNSUPPORTED(__inst_arm(0xe1ffc3b0) " @ ldrh r12, [pc, #48]!") + TEST_UNSUPPORTED(__inst_arm(0xe0d9f3b0) " @ ldrh pc, [r9], #48") + + TEST_PR( "ldrsb r0, [r",0, 48,", -r",2, 24,"]") + TEST_PR( "ldrhisb r14, [r",13,0,", r",12, 48,"]") + TEST_PR( "ldrsb r1, [r",2, 24,", r",3, 48,"]!") + TEST_PR( "ldrlssb r12, [r",11,48,", -r",10,24,"]!") + TEST_PR( "ldrsb r2, [r",3, 24,"], r",4, 48,"") + TEST_PR( "ldrsb r10, [r",9, 48,"], -r",11,24,"") + TEST_UNSUPPORTED(__inst_arm(0xe1bfc0da) " @ ldrsb r12, [pc, r10]!") + TEST_UNSUPPORTED(__inst_arm(0xe099f0db) " @ ldrsb pc, [r9], r11") + + TEST_P( "ldrsb r0, [r",0, 24,", #-1]") + TEST_P( "ldrgesb r14, [r",13,0, ", #1]") + TEST_P( "ldrsb r1, [r",2, 24,", #4]!") + TEST_P( "ldrltsb r12, [r",11,24,", #-4]!") + TEST_P( "ldrsb r2, [r",3, 24,"], #48") + TEST_P( "ldrsb r10, [r",9, 64,"], #-48") + TEST( "ldrsb r0, [pc, #0]") + TEST_UNSUPPORTED(__inst_arm(0xe1ffc3d0) " @ ldrsb r12, [pc, #48]!") + TEST_UNSUPPORTED(__inst_arm(0xe0d9f3d0) " @ ldrsb pc, [r9], #48") + + TEST_PR( "ldrsh r0, [r",0, 48,", -r",2, 24,"]") + TEST_PR( "ldrgtsh r14, [r",13,0, ", r",12, 48,"]") + TEST_PR( "ldrsh r1, [r",2, 24,", r",3, 48,"]!") + TEST_PR( "ldrlesh r12, [r",11,48,", -r",10,24,"]!") + TEST_PR( "ldrsh r2, [r",3, 24,"], r",4, 48,"") + TEST_PR( "ldrsh r10, [r",9, 48,"], -r",11,24,"") + TEST_UNSUPPORTED(__inst_arm(0xe1bfc0fa) " @ ldrsh r12, [pc, r10]!") + TEST_UNSUPPORTED(__inst_arm(0xe099f0fb) " @ ldrsh pc, [r9], r11") + + TEST_P( "ldrsh r0, [r",0, 24,", #-1]") + TEST_P( "ldreqsh r14, [r",13,0 ,", #1]") + TEST_P( "ldrsh r1, [r",2, 24,", #4]!") + TEST_P( "ldrnesh r12, [r",11,24,", #-4]!") + TEST_P( "ldrsh r2, [r",3, 24,"], #48") + TEST_P( "ldrsh r10, [r",9, 64,"], #-48") + TEST( "ldrsh r0, [pc, #0]") + TEST_UNSUPPORTED(__inst_arm(0xe1ffc3f0) " @ ldrsh r12, [pc, #48]!") + TEST_UNSUPPORTED(__inst_arm(0xe0d9f3f0) " @ ldrsh pc, [r9], #48") + +#if __LINUX_ARM_ARCH__ >= 7 + TEST_UNSUPPORTED("strht r1, [r2], r3") + TEST_UNSUPPORTED("ldrht r1, [r2], r3") + TEST_UNSUPPORTED("strht r1, [r2], #48") + TEST_UNSUPPORTED("ldrht r1, [r2], #48") + TEST_UNSUPPORTED("ldrsbt r1, [r2], r3") + TEST_UNSUPPORTED("ldrsbt r1, [r2], #48") + TEST_UNSUPPORTED("ldrsht r1, [r2], r3") + TEST_UNSUPPORTED("ldrsht r1, [r2], #48") +#endif + +#if __LINUX_ARM_ARCH__ >= 5 + TEST_RPR( "strd r",0, VAL1,", [r",1, 48,", -r",2,24,"]") + TEST_RPR( "strccd r",8, VAL2,", [r",13,0, ", r",12,48,"]") + TEST_RPR( "strd r",4, VAL1,", [r",2, 24,", r",3, 48,"]!") + TEST_RPR( "strcsd r",12,VAL2,", [r",11,48,", -r",10,24,"]!") + TEST_RPR( "strd r",2, VAL1,", [r",5, 24,"], r",4,48,"") + TEST_RPR( "strd r",10,VAL2,", [r",9, 48,"], -r",7,24,"") + TEST_UNSUPPORTED(__inst_arm(0xe1afc0fa) " @ strd r12, [pc, r10]!") + + TEST_PR( "ldrd r0, [r",0, 48,", -r",2,24,"]") + TEST_PR( "ldrmid r8, [r",13,0, ", r",12,48,"]") + TEST_PR( "ldrd r4, [r",2, 24,", r",3, 48,"]!") + TEST_PR( "ldrpld r6, [r",11,48,", -r",10,24,"]!") + TEST_PR( "ldrd r2, [r",5, 24,"], r",4,48,"") + TEST_PR( "ldrd r10, [r",9,48,"], -r",7,24,"") + TEST_UNSUPPORTED(__inst_arm(0xe1afc0da) " @ ldrd r12, [pc, r10]!") + TEST_UNSUPPORTED(__inst_arm(0xe089f0db) " @ ldrd pc, [r9], r11") + TEST_UNSUPPORTED(__inst_arm(0xe089e0db) " @ ldrd lr, [r9], r11") + TEST_UNSUPPORTED(__inst_arm(0xe089c0df) " @ ldrd r12, [r9], pc") + + TEST_RP( "strd r",0, VAL1,", [r",1, 24,", #-8]") + TEST_RP( "strvsd r",8, VAL2,", [r",13,0, ", #8]") + TEST_RP( "strd r",4, VAL1,", [r",2, 24,", #16]!") + TEST_RP( "strvcd r",12,VAL2,", [r",11,24,", #-16]!") + TEST_RP( "strd r",2, VAL1,", [r",4, 24,"], #48") + TEST_RP( "strd r",10,VAL2,", [r",9, 64,"], #-48") + TEST_UNSUPPORTED(__inst_arm(0xe1efc3f0) " @ strd r12, [pc, #48]!") + + TEST_P( "ldrd r0, [r",0, 24,", #-8]") + TEST_P( "ldrhid r8, [r",13,0, ", #8]") + TEST_P( "ldrd r4, [r",2, 24,", #16]!") + TEST_P( "ldrlsd r6, [r",11,24,", #-16]!") + TEST_P( "ldrd r2, [r",5, 24,"], #48") + TEST_P( "ldrd r10, [r",9,6,"], #-48") + TEST_UNSUPPORTED(__inst_arm(0xe1efc3d0) " @ ldrd r12, [pc, #48]!") + TEST_UNSUPPORTED(__inst_arm(0xe0c9f3d0) " @ ldrd pc, [r9], #48") + TEST_UNSUPPORTED(__inst_arm(0xe0c9e3d0) " @ ldrd lr, [r9], #48") +#endif + + TEST_GROUP("Miscellaneous") + +#if __LINUX_ARM_ARCH__ >= 7 + TEST("movw r0, #0") + TEST("movw r0, #0xffff") + TEST("movw lr, #0xffff") + TEST_UNSUPPORTED(__inst_arm(0xe300f000) " @ movw pc, #0") + TEST_R("movt r",0, VAL1,", #0") + TEST_R("movt r",0, VAL2,", #0xffff") + TEST_R("movt r",14,VAL1,", #0xffff") + TEST_UNSUPPORTED(__inst_arm(0xe340f000) " @ movt pc, #0") +#endif + + TEST_UNSUPPORTED("msr cpsr, 0x13") + TEST_UNSUPPORTED("msr cpsr_f, 0xf0000000") + TEST_UNSUPPORTED("msr spsr, 0x13") + +#if __LINUX_ARM_ARCH__ >= 7 + TEST_SUPPORTED("yield") + TEST("sev") + TEST("nop") + TEST("wfi") + TEST_SUPPORTED("wfe") + TEST_UNSUPPORTED("dbg #0") +#endif + + TEST_GROUP("Load/store word and unsigned byte") + +#define LOAD_STORE(byte) \ + TEST_RP( "str"byte" r",0, VAL1,", [r",1, 24,", #-2]") \ + TEST_RP( "str"byte" r",14,VAL2,", [r",13,0, ", #2]") \ + TEST_RP( "str"byte" r",1, VAL1,", [r",2, 24,", #4]!") \ + TEST_RP( "str"byte" r",12,VAL2,", [r",11,24,", #-4]!") \ + TEST_RP( "str"byte" r",2, VAL1,", [r",3, 24,"], #48") \ + TEST_RP( "str"byte" r",10,VAL2,", [r",9, 64,"], #-48") \ + TEST_RPR("str"byte" r",0, VAL1,", [r",1, 48,", -r",2, 24,"]") \ + TEST_RPR("str"byte" r",14,VAL2,", [r",13,0, ", r",12, 48,"]") \ + TEST_RPR("str"byte" r",1, VAL1,", [r",2, 24,", r",3, 48,"]!") \ + TEST_RPR("str"byte" r",12,VAL2,", [r",11,48,", -r",10,24,"]!") \ + TEST_RPR("str"byte" r",2, VAL1,", [r",3, 24,"], r",4, 48,"") \ + TEST_RPR("str"byte" r",10,VAL2,", [r",9, 48,"], -r",11,24,"") \ + TEST_RPR("str"byte" r",0, VAL1,", [r",1, 24,", r",2, 32,", asl #1]")\ + TEST_RPR("str"byte" r",14,VAL2,", [r",13,0, ", r",12, 32,", lsr #2]")\ + TEST_RPR("str"byte" r",1, VAL1,", [r",2, 24,", r",3, 32,", asr #3]!")\ + TEST_RPR("str"byte" r",12,VAL2,", [r",11,24,", r",10, 4,", ror #31]!")\ + TEST_P( "ldr"byte" r0, [r",0, 24,", #-2]") \ + TEST_P( "ldr"byte" r14, [r",13,0, ", #2]") \ + TEST_P( "ldr"byte" r1, [r",2, 24,", #4]!") \ + TEST_P( "ldr"byte" r12, [r",11,24,", #-4]!") \ + TEST_P( "ldr"byte" r2, [r",3, 24,"], #48") \ + TEST_P( "ldr"byte" r10, [r",9, 64,"], #-48") \ + TEST_PR( "ldr"byte" r0, [r",0, 48,", -r",2, 24,"]") \ + TEST_PR( "ldr"byte" r14, [r",13,0, ", r",12, 48,"]") \ + TEST_PR( "ldr"byte" r1, [r",2, 24,", r",3, 48,"]!") \ + TEST_PR( "ldr"byte" r12, [r",11,48,", -r",10,24,"]!") \ + TEST_PR( "ldr"byte" r2, [r",3, 24,"], r",4, 48,"") \ + TEST_PR( "ldr"byte" r10, [r",9, 48,"], -r",11,24,"") \ + TEST_PR( "ldr"byte" r0, [r",0, 24,", r",2, 32,", asl #1]") \ + TEST_PR( "ldr"byte" r14, [r",13,0, ", r",12, 32,", lsr #2]") \ + TEST_PR( "ldr"byte" r1, [r",2, 24,", r",3, 32,", asr #3]!") \ + TEST_PR( "ldr"byte" r12, [r",11,24,", r",10, 4,", ror #31]!") \ + TEST( "ldr"byte" r0, [pc, #0]") \ + TEST_R( "ldr"byte" r12, [pc, r",14,0,"]") + + LOAD_STORE("") + TEST_P( "str pc, [r",0,0,", #15*4]") + TEST_R( "str pc, [sp, r",2,15*4,"]") + TEST_BF( "ldr pc, [sp, #15*4]") + TEST_BF_R("ldr pc, [sp, r",2,15*4,"]") + + TEST_P( "str sp, [r",0,0,", #13*4]") + TEST_R( "str sp, [sp, r",2,13*4,"]") + TEST_BF( "ldr sp, [sp, #13*4]") + TEST_BF_R("ldr sp, [sp, r",2,13*4,"]") + +#ifdef CONFIG_THUMB2_KERNEL + TEST_ARM_TO_THUMB_INTERWORK_P("ldr pc, [r",0,0,", #15*4]") +#endif + TEST_UNSUPPORTED(__inst_arm(0xe5af6008) " @ str r6, [pc, #8]!") + TEST_UNSUPPORTED(__inst_arm(0xe7af6008) " @ str r6, [pc, r8]!") + TEST_UNSUPPORTED(__inst_arm(0xe5bf6008) " @ ldr r6, [pc, #8]!") + TEST_UNSUPPORTED(__inst_arm(0xe7bf6008) " @ ldr r6, [pc, r8]!") + TEST_UNSUPPORTED(__inst_arm(0xe788600f) " @ str r6, [r8, pc]") + TEST_UNSUPPORTED(__inst_arm(0xe798600f) " @ ldr r6, [r8, pc]") + + LOAD_STORE("b") + TEST_UNSUPPORTED(__inst_arm(0xe5f7f008) " @ ldrb pc, [r7, #8]!") + TEST_UNSUPPORTED(__inst_arm(0xe7f7f008) " @ ldrb pc, [r7, r8]!") + TEST_UNSUPPORTED(__inst_arm(0xe5ef6008) " @ strb r6, [pc, #8]!") + TEST_UNSUPPORTED(__inst_arm(0xe7ef6008) " @ strb r6, [pc, r3]!") + TEST_UNSUPPORTED(__inst_arm(0xe5ff6008) " @ ldrb r6, [pc, #8]!") + TEST_UNSUPPORTED(__inst_arm(0xe7ff6008) " @ ldrb r6, [pc, r3]!") + + TEST_UNSUPPORTED("ldrt r0, [r1], #4") + TEST_UNSUPPORTED("ldrt r1, [r2], r3") + TEST_UNSUPPORTED("strt r2, [r3], #4") + TEST_UNSUPPORTED("strt r3, [r4], r5") + TEST_UNSUPPORTED("ldrbt r4, [r5], #4") + TEST_UNSUPPORTED("ldrbt r5, [r6], r7") + TEST_UNSUPPORTED("strbt r6, [r7], #4") + TEST_UNSUPPORTED("strbt r7, [r8], r9") + +#if __LINUX_ARM_ARCH__ >= 7 + TEST_GROUP("Parallel addition and subtraction, signed") + + TEST_UNSUPPORTED(__inst_arm(0xe6000010) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe60fffff) "") /* Unallocated space */ + + TEST_RR( "sadd16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "sadd16 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe61cff1a) " @ sadd16 pc, r12, r10") + TEST_RR( "sasx r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "sasx r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe61cff3a) " @ sasx pc, r12, r10") + TEST_RR( "ssax r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "ssax r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe61cff5a) " @ ssax pc, r12, r10") + TEST_RR( "ssub16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "ssub16 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe61cff7a) " @ ssub16 pc, r12, r10") + TEST_RR( "sadd8 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "sadd8 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe61cff9a) " @ sadd8 pc, r12, r10") + TEST_UNSUPPORTED(__inst_arm(0xe61000b0) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe61fffbf) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe61000d0) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe61fffdf) "") /* Unallocated space */ + TEST_RR( "ssub8 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "ssub8 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe61cfffa) " @ ssub8 pc, r12, r10") + + TEST_RR( "qadd16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "qadd16 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe62cff1a) " @ qadd16 pc, r12, r10") + TEST_RR( "qasx r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "qasx r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe62cff3a) " @ qasx pc, r12, r10") + TEST_RR( "qsax r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "qsax r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe62cff5a) " @ qsax pc, r12, r10") + TEST_RR( "qsub16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "qsub16 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe62cff7a) " @ qsub16 pc, r12, r10") + TEST_RR( "qadd8 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "qadd8 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe62cff9a) " @ qadd8 pc, r12, r10") + TEST_UNSUPPORTED(__inst_arm(0xe62000b0) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe62fffbf) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe62000d0) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe62fffdf) "") /* Unallocated space */ + TEST_RR( "qsub8 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "qsub8 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe62cfffa) " @ qsub8 pc, r12, r10") + + TEST_RR( "shadd16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "shadd16 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe63cff1a) " @ shadd16 pc, r12, r10") + TEST_RR( "shasx r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "shasx r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe63cff3a) " @ shasx pc, r12, r10") + TEST_RR( "shsax r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "shsax r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe63cff5a) " @ shsax pc, r12, r10") + TEST_RR( "shsub16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "shsub16 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe63cff7a) " @ shsub16 pc, r12, r10") + TEST_RR( "shadd8 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "shadd8 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe63cff9a) " @ shadd8 pc, r12, r10") + TEST_UNSUPPORTED(__inst_arm(0xe63000b0) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe63fffbf) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe63000d0) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe63fffdf) "") /* Unallocated space */ + TEST_RR( "shsub8 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "shsub8 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe63cfffa) " @ shsub8 pc, r12, r10") + + TEST_GROUP("Parallel addition and subtraction, unsigned") + + TEST_UNSUPPORTED(__inst_arm(0xe6400010) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe64fffff) "") /* Unallocated space */ + + TEST_RR( "uadd16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uadd16 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe65cff1a) " @ uadd16 pc, r12, r10") + TEST_RR( "uasx r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uasx r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe65cff3a) " @ uasx pc, r12, r10") + TEST_RR( "usax r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "usax r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe65cff5a) " @ usax pc, r12, r10") + TEST_RR( "usub16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "usub16 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe65cff7a) " @ usub16 pc, r12, r10") + TEST_RR( "uadd8 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uadd8 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe65cff9a) " @ uadd8 pc, r12, r10") + TEST_UNSUPPORTED(__inst_arm(0xe65000b0) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe65fffbf) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe65000d0) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe65fffdf) "") /* Unallocated space */ + TEST_RR( "usub8 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "usub8 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe65cfffa) " @ usub8 pc, r12, r10") + + TEST_RR( "uqadd16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uqadd16 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe66cff1a) " @ uqadd16 pc, r12, r10") + TEST_RR( "uqasx r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uqasx r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe66cff3a) " @ uqasx pc, r12, r10") + TEST_RR( "uqsax r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uqsax r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe66cff5a) " @ uqsax pc, r12, r10") + TEST_RR( "uqsub16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uqsub16 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe66cff7a) " @ uqsub16 pc, r12, r10") + TEST_RR( "uqadd8 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uqadd8 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe66cff9a) " @ uqadd8 pc, r12, r10") + TEST_UNSUPPORTED(__inst_arm(0xe66000b0) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe66fffbf) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe66000d0) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe66fffdf) "") /* Unallocated space */ + TEST_RR( "uqsub8 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uqsub8 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe66cfffa) " @ uqsub8 pc, r12, r10") + + TEST_RR( "uhadd16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uhadd16 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe67cff1a) " @ uhadd16 pc, r12, r10") + TEST_RR( "uhasx r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uhasx r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe67cff3a) " @ uhasx pc, r12, r10") + TEST_RR( "uhsax r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uhsax r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe67cff5a) " @ uhsax pc, r12, r10") + TEST_RR( "uhsub16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uhsub16 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe67cff7a) " @ uhsub16 pc, r12, r10") + TEST_RR( "uhadd8 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uhadd8 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe67cff9a) " @ uhadd8 pc, r12, r10") + TEST_UNSUPPORTED(__inst_arm(0xe67000b0) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe67fffbf) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe67000d0) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe67fffdf) "") /* Unallocated space */ + TEST_RR( "uhsub8 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uhsub8 r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe67cfffa) " @ uhsub8 pc, r12, r10") + TEST_UNSUPPORTED(__inst_arm(0xe67feffa) " @ uhsub8 r14, pc, r10") + TEST_UNSUPPORTED(__inst_arm(0xe67cefff) " @ uhsub8 r14, r12, pc") +#endif /* __LINUX_ARM_ARCH__ >= 7 */ + +#if __LINUX_ARM_ARCH__ >= 6 + TEST_GROUP("Packing, unpacking, saturation, and reversal") + + TEST_RR( "pkhbt r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "pkhbt r14,r",12, HH1,", r",10,HH2,", lsl #2") + TEST_UNSUPPORTED(__inst_arm(0xe68cf11a) " @ pkhbt pc, r12, r10, lsl #2") + TEST_RR( "pkhtb r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "pkhtb r14,r",12, HH1,", r",10,HH2,", asr #2") + TEST_UNSUPPORTED(__inst_arm(0xe68cf15a) " @ pkhtb pc, r12, r10, asr #2") + TEST_UNSUPPORTED(__inst_arm(0xe68fe15a) " @ pkhtb r14, pc, r10, asr #2") + TEST_UNSUPPORTED(__inst_arm(0xe68ce15f) " @ pkhtb r14, r12, pc, asr #2") + TEST_UNSUPPORTED(__inst_arm(0xe6900010) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe69fffdf) "") /* Unallocated space */ + + TEST_R( "ssat r0, #24, r",0, VAL1,"") + TEST_R( "ssat r14, #24, r",12, VAL2,"") + TEST_R( "ssat r0, #24, r",0, VAL1,", lsl #8") + TEST_R( "ssat r14, #24, r",12, VAL2,", asr #8") + TEST_UNSUPPORTED(__inst_arm(0xe6b7f01c) " @ ssat pc, #24, r12") + + TEST_R( "usat r0, #24, r",0, VAL1,"") + TEST_R( "usat r14, #24, r",12, VAL2,"") + TEST_R( "usat r0, #24, r",0, VAL1,", lsl #8") + TEST_R( "usat r14, #24, r",12, VAL2,", asr #8") + TEST_UNSUPPORTED(__inst_arm(0xe6f7f01c) " @ usat pc, #24, r12") + + TEST_RR( "sxtab16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "sxtab16 r14,r",12, HH2,", r",10,HH1,", ror #8") + TEST_R( "sxtb16 r8, r",7, HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe68cf47a) " @ sxtab16 pc,r12, r10, ror #8") + + TEST_RR( "sel r0, r",0, VAL1,", r",1, VAL2,"") + TEST_RR( "sel r14, r",12,VAL1,", r",10, VAL2,"") + TEST_UNSUPPORTED(__inst_arm(0xe68cffba) " @ sel pc, r12, r10") + TEST_UNSUPPORTED(__inst_arm(0xe68fefba) " @ sel r14, pc, r10") + TEST_UNSUPPORTED(__inst_arm(0xe68cefbf) " @ sel r14, r12, pc") + + TEST_R( "ssat16 r0, #12, r",0, HH1,"") + TEST_R( "ssat16 r14, #12, r",12, HH2,"") + TEST_UNSUPPORTED(__inst_arm(0xe6abff3c) " @ ssat16 pc, #12, r12") + + TEST_RR( "sxtab r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "sxtab r14,r",12, HH2,", r",10,HH1,", ror #8") + TEST_R( "sxtb r8, r",7, HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe6acf47a) " @ sxtab pc,r12, r10, ror #8") + + TEST_R( "rev r0, r",0, VAL1,"") + TEST_R( "rev r14, r",12, VAL2,"") + TEST_UNSUPPORTED(__inst_arm(0xe6bfff3c) " @ rev pc, r12") + + TEST_RR( "sxtah r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "sxtah r14,r",12, HH2,", r",10,HH1,", ror #8") + TEST_R( "sxth r8, r",7, HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe6bcf47a) " @ sxtah pc,r12, r10, ror #8") + + TEST_R( "rev16 r0, r",0, VAL1,"") + TEST_R( "rev16 r14, r",12, VAL2,"") + TEST_UNSUPPORTED(__inst_arm(0xe6bfffbc) " @ rev16 pc, r12") + + TEST_RR( "uxtab16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uxtab16 r14,r",12, HH2,", r",10,HH1,", ror #8") + TEST_R( "uxtb16 r8, r",7, HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe6ccf47a) " @ uxtab16 pc,r12, r10, ror #8") + + TEST_R( "usat16 r0, #12, r",0, HH1,"") + TEST_R( "usat16 r14, #12, r",12, HH2,"") + TEST_UNSUPPORTED(__inst_arm(0xe6ecff3c) " @ usat16 pc, #12, r12") + TEST_UNSUPPORTED(__inst_arm(0xe6ecef3f) " @ usat16 r14, #12, pc") + + TEST_RR( "uxtab r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uxtab r14,r",12, HH2,", r",10,HH1,", ror #8") + TEST_R( "uxtb r8, r",7, HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe6ecf47a) " @ uxtab pc,r12, r10, ror #8") + +#if __LINUX_ARM_ARCH__ >= 7 + TEST_R( "rbit r0, r",0, VAL1,"") + TEST_R( "rbit r14, r",12, VAL2,"") + TEST_UNSUPPORTED(__inst_arm(0xe6ffff3c) " @ rbit pc, r12") +#endif + + TEST_RR( "uxtah r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uxtah r14,r",12, HH2,", r",10,HH1,", ror #8") + TEST_R( "uxth r8, r",7, HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe6fff077) " @ uxth pc, r7") + TEST_UNSUPPORTED(__inst_arm(0xe6ff807f) " @ uxth r8, pc") + TEST_UNSUPPORTED(__inst_arm(0xe6fcf47a) " @ uxtah pc, r12, r10, ror #8") + TEST_UNSUPPORTED(__inst_arm(0xe6fce47f) " @ uxtah r14, r12, pc, ror #8") + + TEST_R( "revsh r0, r",0, VAL1,"") + TEST_R( "revsh r14, r",12, VAL2,"") + TEST_UNSUPPORTED(__inst_arm(0xe6ffff3c) " @ revsh pc, r12") + TEST_UNSUPPORTED(__inst_arm(0xe6ffef3f) " @ revsh r14, pc") + + TEST_UNSUPPORTED(__inst_arm(0xe6900070) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe69fff7f) "") /* Unallocated space */ + + TEST_UNSUPPORTED(__inst_arm(0xe6d00070) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_arm(0xe6dfff7f) "") /* Unallocated space */ +#endif /* __LINUX_ARM_ARCH__ >= 6 */ + +#if __LINUX_ARM_ARCH__ >= 6 + TEST_GROUP("Signed multiplies") + + TEST_RRR( "smlad r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") + TEST_RRR( "smlad r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") + TEST_UNSUPPORTED(__inst_arm(0xe70f8a1c) " @ smlad pc, r12, r10, r8") + TEST_RRR( "smladx r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") + TEST_RRR( "smladx r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") + TEST_UNSUPPORTED(__inst_arm(0xe70f8a3c) " @ smladx pc, r12, r10, r8") + + TEST_RR( "smuad r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "smuad r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe70ffa1c) " @ smuad pc, r12, r10") + TEST_RR( "smuadx r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "smuadx r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe70ffa3c) " @ smuadx pc, r12, r10") + + TEST_RRR( "smlsd r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") + TEST_RRR( "smlsd r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") + TEST_UNSUPPORTED(__inst_arm(0xe70f8a5c) " @ smlsd pc, r12, r10, r8") + TEST_RRR( "smlsdx r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") + TEST_RRR( "smlsdx r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") + TEST_UNSUPPORTED(__inst_arm(0xe70f8a7c) " @ smlsdx pc, r12, r10, r8") + + TEST_RR( "smusd r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "smusd r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe70ffa5c) " @ smusd pc, r12, r10") + TEST_RR( "smusdx r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "smusdx r14, r",12,HH2,", r",10,HH1,"") + TEST_UNSUPPORTED(__inst_arm(0xe70ffa7c) " @ smusdx pc, r12, r10") + + TEST_RRRR( "smlald r",0, VAL1,", r",1, VAL2, ", r",0, HH1,", r",1, HH2) + TEST_RRRR( "smlald r",11,VAL2,", r",10,VAL1, ", r",9, HH2,", r",8, HH1) + TEST_UNSUPPORTED(__inst_arm(0xe74af819) " @ smlald pc, r10, r9, r8") + TEST_UNSUPPORTED(__inst_arm(0xe74fb819) " @ smlald r11, pc, r9, r8") + TEST_UNSUPPORTED(__inst_arm(0xe74ab81f) " @ smlald r11, r10, pc, r8") + TEST_UNSUPPORTED(__inst_arm(0xe74abf19) " @ smlald r11, r10, r9, pc") + + TEST_RRRR( "smlaldx r",0, VAL1,", r",1, VAL2, ", r",0, HH1,", r",1, HH2) + TEST_RRRR( "smlaldx r",11,VAL2,", r",10,VAL1, ", r",9, HH2,", r",8, HH1) + TEST_UNSUPPORTED(__inst_arm(0xe74af839) " @ smlaldx pc, r10, r9, r8") + TEST_UNSUPPORTED(__inst_arm(0xe74fb839) " @ smlaldx r11, pc, r9, r8") + + TEST_RRR( "smmla r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") + TEST_RRR( "smmla r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") + TEST_UNSUPPORTED(__inst_arm(0xe75f8a1c) " @ smmla pc, r12, r10, r8") + TEST_RRR( "smmlar r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") + TEST_RRR( "smmlar r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") + TEST_UNSUPPORTED(__inst_arm(0xe75f8a3c) " @ smmlar pc, r12, r10, r8") + + TEST_RR( "smmul r0, r",0, VAL1,", r",1, VAL2,"") + TEST_RR( "smmul r14, r",12,VAL2,", r",10,VAL1,"") + TEST_UNSUPPORTED(__inst_arm(0xe75ffa1c) " @ smmul pc, r12, r10") + TEST_RR( "smmulr r0, r",0, VAL1,", r",1, VAL2,"") + TEST_RR( "smmulr r14, r",12,VAL2,", r",10,VAL1,"") + TEST_UNSUPPORTED(__inst_arm(0xe75ffa3c) " @ smmulr pc, r12, r10") + + TEST_RRR( "smmls r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") + TEST_RRR( "smmls r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") + TEST_UNSUPPORTED(__inst_arm(0xe75f8adc) " @ smmls pc, r12, r10, r8") + TEST_RRR( "smmlsr r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") + TEST_RRR( "smmlsr r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") + TEST_UNSUPPORTED(__inst_arm(0xe75f8afc) " @ smmlsr pc, r12, r10, r8") + TEST_UNSUPPORTED(__inst_arm(0xe75e8aff) " @ smmlsr r14, pc, r10, r8") + TEST_UNSUPPORTED(__inst_arm(0xe75e8ffc) " @ smmlsr r14, r12, pc, r8") + TEST_UNSUPPORTED(__inst_arm(0xe75efafc) " @ smmlsr r14, r12, r10, pc") + + TEST_RR( "usad8 r0, r",0, VAL1,", r",1, VAL2,"") + TEST_RR( "usad8 r14, r",12,VAL2,", r",10,VAL1,"") + TEST_UNSUPPORTED(__inst_arm(0xe75ffa1c) " @ usad8 pc, r12, r10") + TEST_UNSUPPORTED(__inst_arm(0xe75efa1f) " @ usad8 r14, pc, r10") + TEST_UNSUPPORTED(__inst_arm(0xe75eff1c) " @ usad8 r14, r12, pc") + + TEST_RRR( "usada8 r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL3,"") + TEST_RRR( "usada8 r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL3,"") + TEST_UNSUPPORTED(__inst_arm(0xe78f8a1c) " @ usada8 pc, r12, r10, r8") + TEST_UNSUPPORTED(__inst_arm(0xe78e8a1f) " @ usada8 r14, pc, r10, r8") + TEST_UNSUPPORTED(__inst_arm(0xe78e8f1c) " @ usada8 r14, r12, pc, r8") +#endif /* __LINUX_ARM_ARCH__ >= 6 */ + +#if __LINUX_ARM_ARCH__ >= 7 + TEST_GROUP("Bit Field") + + TEST_R( "sbfx r0, r",0 , VAL1,", #0, #31") + TEST_R( "sbfxeq r14, r",12, VAL2,", #8, #16") + TEST_R( "sbfx r4, r",10, VAL1,", #16, #15") + TEST_UNSUPPORTED(__inst_arm(0xe7aff45c) " @ sbfx pc, r12, #8, #16") + + TEST_R( "ubfx r0, r",0 , VAL1,", #0, #31") + TEST_R( "ubfxcs r14, r",12, VAL2,", #8, #16") + TEST_R( "ubfx r4, r",10, VAL1,", #16, #15") + TEST_UNSUPPORTED(__inst_arm(0xe7eff45c) " @ ubfx pc, r12, #8, #16") + TEST_UNSUPPORTED(__inst_arm(0xe7efc45f) " @ ubfx r12, pc, #8, #16") + + TEST_R( "bfc r",0, VAL1,", #4, #20") + TEST_R( "bfcvs r",14,VAL2,", #4, #20") + TEST_R( "bfc r",7, VAL1,", #0, #31") + TEST_R( "bfc r",8, VAL2,", #0, #31") + TEST_UNSUPPORTED(__inst_arm(0xe7def01f) " @ bfc pc, #0, #31"); + + TEST_RR( "bfi r",0, VAL1,", r",0 , VAL2,", #0, #31") + TEST_RR( "bfipl r",12,VAL1,", r",14 , VAL2,", #4, #20") + TEST_UNSUPPORTED(__inst_arm(0xe7d7f21e) " @ bfi pc, r14, #4, #20") + + TEST_UNSUPPORTED(__inst_arm(0x07f000f0) "") /* Permanently UNDEFINED */ + TEST_UNSUPPORTED(__inst_arm(0x07ffffff) "") /* Permanently UNDEFINED */ +#endif /* __LINUX_ARM_ARCH__ >= 6 */ + + TEST_GROUP("Branch, branch with link, and block data transfer") + + TEST_P( "stmda r",0, 16*4,", {r0}") + TEST_P( "stmeqda r",4, 16*4,", {r0-r15}") + TEST_P( "stmneda r",8, 16*4,"!, {r8-r15}") + TEST_P( "stmda r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") + TEST_P( "stmda r",13,0, "!, {pc}") + + TEST_P( "ldmda r",0, 16*4,", {r0}") + TEST_BF_P("ldmcsda r",4, 15*4,", {r0-r15}") + TEST_BF_P("ldmccda r",7, 15*4,"!, {r8-r15}") + TEST_P( "ldmda r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") + TEST_BF_P("ldmda r",14,15*4,"!, {pc}") + + TEST_P( "stmia r",0, 16*4,", {r0}") + TEST_P( "stmmiia r",4, 16*4,", {r0-r15}") + TEST_P( "stmplia r",8, 16*4,"!, {r8-r15}") + TEST_P( "stmia r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") + TEST_P( "stmia r",14,0, "!, {pc}") + + TEST_P( "ldmia r",0, 16*4,", {r0}") + TEST_BF_P("ldmvsia r",4, 0, ", {r0-r15}") + TEST_BF_P("ldmvcia r",7, 8*4, "!, {r8-r15}") + TEST_P( "ldmia r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") + TEST_BF_P("ldmia r",14,15*4,"!, {pc}") + + TEST_P( "stmdb r",0, 16*4,", {r0}") + TEST_P( "stmhidb r",4, 16*4,", {r0-r15}") + TEST_P( "stmlsdb r",8, 16*4,"!, {r8-r15}") + TEST_P( "stmdb r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") + TEST_P( "stmdb r",13,4, "!, {pc}") + + TEST_P( "ldmdb r",0, 16*4,", {r0}") + TEST_BF_P("ldmgedb r",4, 16*4,", {r0-r15}") + TEST_BF_P("ldmltdb r",7, 16*4,"!, {r8-r15}") + TEST_P( "ldmdb r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") + TEST_BF_P("ldmdb r",14,16*4,"!, {pc}") + + TEST_P( "stmib r",0, 16*4,", {r0}") + TEST_P( "stmgtib r",4, 16*4,", {r0-r15}") + TEST_P( "stmleib r",8, 16*4,"!, {r8-r15}") + TEST_P( "stmib r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") + TEST_P( "stmib r",13,-4, "!, {pc}") + + TEST_P( "ldmib r",0, 16*4,", {r0}") + TEST_BF_P("ldmeqib r",4, -4,", {r0-r15}") + TEST_BF_P("ldmneib r",7, 7*4,"!, {r8-r15}") + TEST_P( "ldmib r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") + TEST_BF_P("ldmib r",14,14*4,"!, {pc}") + + TEST_P( "stmdb r",13,16*4,"!, {r3-r12,lr}") + TEST_P( "stmeqdb r",13,16*4,"!, {r3-r12}") + TEST_P( "stmnedb r",2, 16*4,", {r3-r12,lr}") + TEST_P( "stmdb r",13,16*4,"!, {r2-r12,lr}") + TEST_P( "stmdb r",0, 16*4,", {r0-r12}") + TEST_P( "stmdb r",0, 16*4,", {r0-r12,lr}") + + TEST_BF_P("ldmia r",13,5*4, "!, {r3-r12,pc}") + TEST_P( "ldmccia r",13,5*4, "!, {r3-r12}") + TEST_BF_P("ldmcsia r",2, 5*4, "!, {r3-r12,pc}") + TEST_BF_P("ldmia r",13,4*4, "!, {r2-r12,pc}") + TEST_P( "ldmia r",0, 16*4,", {r0-r12}") + TEST_P( "ldmia r",0, 16*4,", {r0-r12,lr}") + +#ifdef CONFIG_THUMB2_KERNEL + TEST_ARM_TO_THUMB_INTERWORK_P("ldmplia r",0,15*4,", {pc}") + TEST_ARM_TO_THUMB_INTERWORK_P("ldmmiia r",13,0,", {r0-r15}") +#endif + TEST_BF("b 2f") + TEST_BF("bl 2f") + TEST_BB("b 2b") + TEST_BB("bl 2b") + + TEST_BF("beq 2f") + TEST_BF("bleq 2f") + TEST_BB("bne 2b") + TEST_BB("blne 2b") + + TEST_BF("bgt 2f") + TEST_BF("blgt 2f") + TEST_BB("blt 2b") + TEST_BB("bllt 2b") + + TEST_GROUP("Supervisor Call, and coprocessor instructions") + + /* + * We can't really test these by executing them, so all + * we can do is check that probes are, or are not allowed. + * At the moment none are allowed... + */ +#define TEST_COPROCESSOR(code) TEST_UNSUPPORTED(code) + +#define COPROCESSOR_INSTRUCTIONS_ST_LD(two,cc) \ + TEST_COPROCESSOR("stc"two" 0, cr0, [r13, #4]") \ + TEST_COPROCESSOR("stc"two" 0, cr0, [r13, #-4]") \ + TEST_COPROCESSOR("stc"two" 0, cr0, [r13, #4]!") \ + TEST_COPROCESSOR("stc"two" 0, cr0, [r13, #-4]!") \ + TEST_COPROCESSOR("stc"two" 0, cr0, [r13], #4") \ + TEST_COPROCESSOR("stc"two" 0, cr0, [r13], #-4") \ + TEST_COPROCESSOR("stc"two" 0, cr0, [r13], {1}") \ + TEST_COPROCESSOR("stc"two"l 0, cr0, [r13, #4]") \ + TEST_COPROCESSOR("stc"two"l 0, cr0, [r13, #-4]") \ + TEST_COPROCESSOR("stc"two"l 0, cr0, [r13, #4]!") \ + TEST_COPROCESSOR("stc"two"l 0, cr0, [r13, #-4]!") \ + TEST_COPROCESSOR("stc"two"l 0, cr0, [r13], #4") \ + TEST_COPROCESSOR("stc"two"l 0, cr0, [r13], #-4") \ + TEST_COPROCESSOR("stc"two"l 0, cr0, [r13], {1}") \ + TEST_COPROCESSOR("ldc"two" 0, cr0, [r13, #4]") \ + TEST_COPROCESSOR("ldc"two" 0, cr0, [r13, #-4]") \ + TEST_COPROCESSOR("ldc"two" 0, cr0, [r13, #4]!") \ + TEST_COPROCESSOR("ldc"two" 0, cr0, [r13, #-4]!") \ + TEST_COPROCESSOR("ldc"two" 0, cr0, [r13], #4") \ + TEST_COPROCESSOR("ldc"two" 0, cr0, [r13], #-4") \ + TEST_COPROCESSOR("ldc"two" 0, cr0, [r13], {1}") \ + TEST_COPROCESSOR("ldc"two"l 0, cr0, [r13, #4]") \ + TEST_COPROCESSOR("ldc"two"l 0, cr0, [r13, #-4]") \ + TEST_COPROCESSOR("ldc"two"l 0, cr0, [r13, #4]!") \ + TEST_COPROCESSOR("ldc"two"l 0, cr0, [r13, #-4]!") \ + TEST_COPROCESSOR("ldc"two"l 0, cr0, [r13], #4") \ + TEST_COPROCESSOR("ldc"two"l 0, cr0, [r13], #-4") \ + TEST_COPROCESSOR("ldc"two"l 0, cr0, [r13], {1}") \ + \ + TEST_COPROCESSOR( "stc"two" 0, cr0, [r15, #4]") \ + TEST_COPROCESSOR( "stc"two" 0, cr0, [r15, #-4]") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##daf0001) " @ stc"two" 0, cr0, [r15, #4]!") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##d2f0001) " @ stc"two" 0, cr0, [r15, #-4]!") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##caf0001) " @ stc"two" 0, cr0, [r15], #4") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##c2f0001) " @ stc"two" 0, cr0, [r15], #-4") \ + TEST_COPROCESSOR( "stc"two" 0, cr0, [r15], {1}") \ + TEST_COPROCESSOR( "stc"two"l 0, cr0, [r15, #4]") \ + TEST_COPROCESSOR( "stc"two"l 0, cr0, [r15, #-4]") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##def0001) " @ stc"two"l 0, cr0, [r15, #4]!") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##d6f0001) " @ stc"two"l 0, cr0, [r15, #-4]!") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##cef0001) " @ stc"two"l 0, cr0, [r15], #4") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##c6f0001) " @ stc"two"l 0, cr0, [r15], #-4") \ + TEST_COPROCESSOR( "stc"two"l 0, cr0, [r15], {1}") \ + TEST_COPROCESSOR( "ldc"two" 0, cr0, [r15, #4]") \ + TEST_COPROCESSOR( "ldc"two" 0, cr0, [r15, #-4]") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##dbf0001) " @ ldc"two" 0, cr0, [r15, #4]!") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##d3f0001) " @ ldc"two" 0, cr0, [r15, #-4]!") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##cbf0001) " @ ldc"two" 0, cr0, [r15], #4") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##c3f0001) " @ ldc"two" 0, cr0, [r15], #-4") \ + TEST_COPROCESSOR( "ldc"two" 0, cr0, [r15], {1}") \ + TEST_COPROCESSOR( "ldc"two"l 0, cr0, [r15, #4]") \ + TEST_COPROCESSOR( "ldc"two"l 0, cr0, [r15, #-4]") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##dff0001) " @ ldc"two"l 0, cr0, [r15, #4]!") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##d7f0001) " @ ldc"two"l 0, cr0, [r15, #-4]!") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##cff0001) " @ ldc"two"l 0, cr0, [r15], #4") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##c7f0001) " @ ldc"two"l 0, cr0, [r15], #-4") \ + TEST_COPROCESSOR( "ldc"two"l 0, cr0, [r15], {1}") + +#define COPROCESSOR_INSTRUCTIONS_MC_MR(two,cc) \ + \ + TEST_COPROCESSOR( "mcrr"two" 0, 15, r0, r14, cr0") \ + TEST_COPROCESSOR( "mcrr"two" 15, 0, r14, r0, cr15") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##c4f00f0) " @ mcrr"two" 0, 15, r0, r15, cr0") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##c40ff0f) " @ mcrr"two" 15, 0, r15, r0, cr15") \ + TEST_COPROCESSOR( "mrrc"two" 0, 15, r0, r14, cr0") \ + TEST_COPROCESSOR( "mrrc"two" 15, 0, r14, r0, cr15") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##c5f00f0) " @ mrrc"two" 0, 15, r0, r15, cr0") \ + TEST_UNSUPPORTED(__inst_arm(0x##cc##c50ff0f) " @ mrrc"two" 15, 0, r15, r0, cr15") \ + TEST_COPROCESSOR( "cdp"two" 15, 15, cr15, cr15, cr15, 7") \ + TEST_COPROCESSOR( "cdp"two" 0, 0, cr0, cr0, cr0, 0") \ + TEST_COPROCESSOR( "mcr"two" 15, 7, r15, cr15, cr15, 7") \ + TEST_COPROCESSOR( "mcr"two" 0, 0, r0, cr0, cr0, 0") \ + TEST_COPROCESSOR( "mrc"two" 15, 7, r15, cr15, cr15, 7") \ + TEST_COPROCESSOR( "mrc"two" 0, 0, r0, cr0, cr0, 0") + + COPROCESSOR_INSTRUCTIONS_ST_LD("",e) +#if __LINUX_ARM_ARCH__ >= 5 + COPROCESSOR_INSTRUCTIONS_MC_MR("",e) +#endif + TEST_UNSUPPORTED("svc 0") + TEST_UNSUPPORTED("svc 0xffffff") + + TEST_UNSUPPORTED("svc 0") + + TEST_GROUP("Unconditional instruction") + +#if __LINUX_ARM_ARCH__ >= 6 + TEST_UNSUPPORTED("srsda sp, 0x13") + TEST_UNSUPPORTED("srsdb sp, 0x13") + TEST_UNSUPPORTED("srsia sp, 0x13") + TEST_UNSUPPORTED("srsib sp, 0x13") + TEST_UNSUPPORTED("srsda sp!, 0x13") + TEST_UNSUPPORTED("srsdb sp!, 0x13") + TEST_UNSUPPORTED("srsia sp!, 0x13") + TEST_UNSUPPORTED("srsib sp!, 0x13") + + TEST_UNSUPPORTED("rfeda sp") + TEST_UNSUPPORTED("rfedb sp") + TEST_UNSUPPORTED("rfeia sp") + TEST_UNSUPPORTED("rfeib sp") + TEST_UNSUPPORTED("rfeda sp!") + TEST_UNSUPPORTED("rfedb sp!") + TEST_UNSUPPORTED("rfeia sp!") + TEST_UNSUPPORTED("rfeib sp!") + TEST_UNSUPPORTED(__inst_arm(0xf81d0a00) " @ rfeda pc") + TEST_UNSUPPORTED(__inst_arm(0xf91d0a00) " @ rfedb pc") + TEST_UNSUPPORTED(__inst_arm(0xf89d0a00) " @ rfeia pc") + TEST_UNSUPPORTED(__inst_arm(0xf99d0a00) " @ rfeib pc") + TEST_UNSUPPORTED(__inst_arm(0xf83d0a00) " @ rfeda pc!") + TEST_UNSUPPORTED(__inst_arm(0xf93d0a00) " @ rfedb pc!") + TEST_UNSUPPORTED(__inst_arm(0xf8bd0a00) " @ rfeia pc!") + TEST_UNSUPPORTED(__inst_arm(0xf9bd0a00) " @ rfeib pc!") +#endif /* __LINUX_ARM_ARCH__ >= 6 */ + +#if __LINUX_ARM_ARCH__ >= 6 + TEST_X( "blx __dummy_thumb_subroutine_even", + ".thumb \n\t" + ".space 4 \n\t" + ".type __dummy_thumb_subroutine_even, %%function \n\t" + "__dummy_thumb_subroutine_even: \n\t" + "mov r0, pc \n\t" + "bx lr \n\t" + ".arm \n\t" + ) + TEST( "blx __dummy_thumb_subroutine_even") + + TEST_X( "blx __dummy_thumb_subroutine_odd", + ".thumb \n\t" + ".space 2 \n\t" + ".type __dummy_thumb_subroutine_odd, %%function \n\t" + "__dummy_thumb_subroutine_odd: \n\t" + "mov r0, pc \n\t" + "bx lr \n\t" + ".arm \n\t" + ) + TEST( "blx __dummy_thumb_subroutine_odd") +#endif /* __LINUX_ARM_ARCH__ >= 6 */ + +#if __LINUX_ARM_ARCH__ >= 5 + COPROCESSOR_INSTRUCTIONS_ST_LD("2",f) +#endif +#if __LINUX_ARM_ARCH__ >= 6 + COPROCESSOR_INSTRUCTIONS_MC_MR("2",f) +#endif + + TEST_GROUP("Miscellaneous instructions, memory hints, and Advanced SIMD instructions") + +#if __LINUX_ARM_ARCH__ >= 6 + TEST_UNSUPPORTED("cps 0x13") + TEST_UNSUPPORTED("cpsie i") + TEST_UNSUPPORTED("cpsid i") + TEST_UNSUPPORTED("cpsie i,0x13") + TEST_UNSUPPORTED("cpsid i,0x13") + TEST_UNSUPPORTED("setend le") + TEST_UNSUPPORTED("setend be") +#endif + +#if __LINUX_ARM_ARCH__ >= 7 + TEST_P("pli [r",0,0b,", #16]") + TEST( "pli [pc, #0]") + TEST_RR("pli [r",12,0b,", r",0, 16,"]") + TEST_RR("pli [r",0, 0b,", -r",12,16,", lsl #4]") +#endif + +#if __LINUX_ARM_ARCH__ >= 5 + TEST_P("pld [r",0,32,", #-16]") + TEST( "pld [pc, #0]") + TEST_PR("pld [r",7, 24, ", r",0, 16,"]") + TEST_PR("pld [r",8, 24, ", -r",12,16,", lsl #4]") +#endif + +#if __LINUX_ARM_ARCH__ >= 7 + TEST_SUPPORTED( __inst_arm(0xf590f000) " @ pldw [r0, #0]") + TEST_SUPPORTED( __inst_arm(0xf797f000) " @ pldw [r7, r0]") + TEST_SUPPORTED( __inst_arm(0xf798f18c) " @ pldw [r8, r12, lsl #3]"); +#endif + +#if __LINUX_ARM_ARCH__ >= 7 + TEST_UNSUPPORTED("clrex") + TEST_UNSUPPORTED("dsb") + TEST_UNSUPPORTED("dmb") + TEST_UNSUPPORTED("isb") +#endif + + verbose("\n"); +} + diff --git a/arch/arm/probes/kprobes/test-core.c b/arch/arm/probes/kprobes/test-core.c new file mode 100644 index 0000000..7ab633d --- /dev/null +++ b/arch/arm/probes/kprobes/test-core.c @@ -0,0 +1,1713 @@ +/* + * arch/arm/kernel/kprobes-test.c + * + * Copyright (C) 2011 Jon Medhurst . + * + * 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 file contains test code for ARM kprobes. + * + * The top level function run_all_tests() executes tests for all of the + * supported instruction sets: ARM, 16-bit Thumb, and 32-bit Thumb. These tests + * fall into two categories; run_api_tests() checks basic functionality of the + * kprobes API, and run_test_cases() is a comprehensive test for kprobes + * instruction decoding and simulation. + * + * run_test_cases() first checks the kprobes decoding table for self consistency + * (using table_test()) then executes a series of test cases for each of the CPU + * instruction forms. coverage_start() and coverage_end() are used to verify + * that these test cases cover all of the possible combinations of instructions + * described by the kprobes decoding tables. + * + * The individual test cases are in kprobes-test-arm.c and kprobes-test-thumb.c + * which use the macros defined in kprobes-test.h. The rest of this + * documentation will describe the operation of the framework used by these + * test cases. + */ + +/* + * TESTING METHODOLOGY + * ------------------- + * + * The methodology used to test an ARM instruction 'test_insn' is to use + * inline assembler like: + * + * test_before: nop + * test_case: test_insn + * test_after: nop + * + * When the test case is run a kprobe is placed of each nop. The + * post-handler of the test_before probe is used to modify the saved CPU + * register context to that which we require for the test case. The + * pre-handler of the of the test_after probe saves a copy of the CPU + * register context. In this way we can execute test_insn with a specific + * register context and see the results afterwards. + * + * To actually test the kprobes instruction emulation we perform the above + * step a second time but with an additional kprobe on the test_case + * instruction itself. If the emulation is accurate then the results seen + * by the test_after probe will be identical to the first run which didn't + * have a probe on test_case. + * + * Each test case is run several times with a variety of variations in the + * flags value of stored in CPSR, and for Thumb code, different ITState. + * + * For instructions which can modify PC, a second test_after probe is used + * like this: + * + * test_before: nop + * test_case: test_insn + * test_after: nop + * b test_done + * test_after2: nop + * test_done: + * + * The test case is constructed such that test_insn branches to + * test_after2, or, if testing a conditional instruction, it may just + * continue to test_after. The probes inserted at both locations let us + * determine which happened. A similar approach is used for testing + * backwards branches... + * + * b test_before + * b test_done @ helps to cope with off by 1 branches + * test_after2: nop + * b test_done + * test_before: nop + * test_case: test_insn + * test_after: nop + * test_done: + * + * The macros used to generate the assembler instructions describe above + * are TEST_INSTRUCTION, TEST_BRANCH_F (branch forwards) and TEST_BRANCH_B + * (branch backwards). In these, the local variables numbered 1, 50, 2 and + * 99 represent: test_before, test_case, test_after2 and test_done. + * + * FRAMEWORK + * --------- + * + * Each test case is wrapped between the pair of macros TESTCASE_START and + * TESTCASE_END. As well as performing the inline assembler boilerplate, + * these call out to the kprobes_test_case_start() and + * kprobes_test_case_end() functions which drive the execution of the test + * case. The specific arguments to use for each test case are stored as + * inline data constructed using the various TEST_ARG_* macros. Putting + * this all together, a simple test case may look like: + * + * TESTCASE_START("Testing mov r0, r7") + * TEST_ARG_REG(7, 0x12345678) // Set r7=0x12345678 + * TEST_ARG_END("") + * TEST_INSTRUCTION("mov r0, r7") + * TESTCASE_END + * + * Note, in practice the single convenience macro TEST_R would be used for this + * instead. + * + * The above would expand to assembler looking something like: + * + * @ TESTCASE_START + * bl __kprobes_test_case_start + * .pushsection .rodata + * "10: + * .ascii "mov r0, r7" @ text title for test case + * .byte 0 + * .popsection + * @ start of inline data... + * .word 10b @ pointer to title in .rodata section + * + * @ TEST_ARG_REG + * .byte ARG_TYPE_REG + * .byte 7 + * .short 0 + * .word 0x1234567 + * + * @ TEST_ARG_END + * .byte ARG_TYPE_END + * .byte TEST_ISA @ flags, including ISA being tested + * .short 50f-0f @ offset of 'test_before' + * .short 2f-0f @ offset of 'test_after2' (if relevent) + * .short 99f-0f @ offset of 'test_done' + * @ start of test case code... + * 0: + * .code TEST_ISA @ switch to ISA being tested + * + * @ TEST_INSTRUCTION + * 50: nop @ location for 'test_before' probe + * 1: mov r0, r7 @ the test case instruction 'test_insn' + * nop @ location for 'test_after' probe + * + * // TESTCASE_END + * 2: + * 99: bl __kprobes_test_case_end_##TEST_ISA + * .code NONMAL_ISA + * + * When the above is execute the following happens... + * + * __kprobes_test_case_start() is an assembler wrapper which sets up space + * for a stack buffer and calls the C function kprobes_test_case_start(). + * This C function will do some initial processing of the inline data and + * setup some global state. It then inserts the test_before and test_after + * kprobes and returns a value which causes the assembler wrapper to jump + * to the start of the test case code, (local label '0'). + * + * When the test case code executes, the test_before probe will be hit and + * test_before_post_handler will call setup_test_context(). This fills the + * stack buffer and CPU registers with a test pattern and then processes + * the test case arguments. In our example there is one TEST_ARG_REG which + * indicates that R7 should be loaded with the value 0x12345678. + * + * When the test_before probe ends, the test case continues and executes + * the "mov r0, r7" instruction. It then hits the test_after probe and the + * pre-handler for this (test_after_pre_handler) will save a copy of the + * CPU register context. This should now have R0 holding the same value as + * R7. + * + * Finally we get to the call to __kprobes_test_case_end_{32,16}. This is + * an assembler wrapper which switches back to the ISA used by the test + * code and calls the C function kprobes_test_case_end(). + * + * For each run through the test case, test_case_run_count is incremented + * by one. For even runs, kprobes_test_case_end() saves a copy of the + * register and stack buffer contents from the test case just run. It then + * inserts a kprobe on the test case instruction 'test_insn' and returns a + * value to cause the test case code to be re-run. + * + * For odd numbered runs, kprobes_test_case_end() compares the register and + * stack buffer contents to those that were saved on the previous even + * numbered run (the one without the kprobe on test_insn). These should be + * the same if the kprobe instruction simulation routine is correct. + * + * The pair of test case runs is repeated with different combinations of + * flag values in CPSR and, for Thumb, different ITState. This is + * controlled by test_context_cpsr(). + * + * BUILDING TEST CASES + * ------------------- + * + * + * As an aid to building test cases, the stack buffer is initialised with + * some special values: + * + * [SP+13*4] Contains SP+120. This can be used to test instructions + * which load a value into SP. + * + * [SP+15*4] When testing branching instructions using TEST_BRANCH_{F,B}, + * this holds the target address of the branch, 'test_after2'. + * This can be used to test instructions which load a PC value + * from memory. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "test-core.h" +#include "../decode-arm.h" +#include "../decode-thumb.h" + + +#define BENCHMARKING 1 + + +/* + * Test basic API + */ + +static bool test_regs_ok; +static int test_func_instance; +static int pre_handler_called; +static int post_handler_called; +static int jprobe_func_called; +static int kretprobe_handler_called; +static int tests_failed; + +#define FUNC_ARG1 0x12345678 +#define FUNC_ARG2 0xabcdef + + +#ifndef CONFIG_THUMB2_KERNEL + +long arm_func(long r0, long r1); + +static void __used __naked __arm_kprobes_test_func(void) +{ + __asm__ __volatile__ ( + ".arm \n\t" + ".type arm_func, %%function \n\t" + "arm_func: \n\t" + "adds r0, r0, r1 \n\t" + "bx lr \n\t" + ".code "NORMAL_ISA /* Back to Thumb if necessary */ + : : : "r0", "r1", "cc" + ); +} + +#else /* CONFIG_THUMB2_KERNEL */ + +long thumb16_func(long r0, long r1); +long thumb32even_func(long r0, long r1); +long thumb32odd_func(long r0, long r1); + +static void __used __naked __thumb_kprobes_test_funcs(void) +{ + __asm__ __volatile__ ( + ".type thumb16_func, %%function \n\t" + "thumb16_func: \n\t" + "adds.n r0, r0, r1 \n\t" + "bx lr \n\t" + + ".align \n\t" + ".type thumb32even_func, %%function \n\t" + "thumb32even_func: \n\t" + "adds.w r0, r0, r1 \n\t" + "bx lr \n\t" + + ".align \n\t" + "nop.n \n\t" + ".type thumb32odd_func, %%function \n\t" + "thumb32odd_func: \n\t" + "adds.w r0, r0, r1 \n\t" + "bx lr \n\t" + + : : : "r0", "r1", "cc" + ); +} + +#endif /* CONFIG_THUMB2_KERNEL */ + + +static int call_test_func(long (*func)(long, long), bool check_test_regs) +{ + long ret; + + ++test_func_instance; + test_regs_ok = false; + + ret = (*func)(FUNC_ARG1, FUNC_ARG2); + if (ret != FUNC_ARG1 + FUNC_ARG2) { + pr_err("FAIL: call_test_func: func returned %lx\n", ret); + return false; + } + + if (check_test_regs && !test_regs_ok) { + pr_err("FAIL: test regs not OK\n"); + return false; + } + + return true; +} + +static int __kprobes pre_handler(struct kprobe *p, struct pt_regs *regs) +{ + pre_handler_called = test_func_instance; + if (regs->ARM_r0 == FUNC_ARG1 && regs->ARM_r1 == FUNC_ARG2) + test_regs_ok = true; + return 0; +} + +static void __kprobes post_handler(struct kprobe *p, struct pt_regs *regs, + unsigned long flags) +{ + post_handler_called = test_func_instance; + if (regs->ARM_r0 != FUNC_ARG1 + FUNC_ARG2 || regs->ARM_r1 != FUNC_ARG2) + test_regs_ok = false; +} + +static struct kprobe the_kprobe = { + .addr = 0, + .pre_handler = pre_handler, + .post_handler = post_handler +}; + +static int test_kprobe(long (*func)(long, long)) +{ + int ret; + + the_kprobe.addr = (kprobe_opcode_t *)func; + ret = register_kprobe(&the_kprobe); + if (ret < 0) { + pr_err("FAIL: register_kprobe failed with %d\n", ret); + return ret; + } + + ret = call_test_func(func, true); + + unregister_kprobe(&the_kprobe); + the_kprobe.flags = 0; /* Clear disable flag to allow reuse */ + + if (!ret) + return -EINVAL; + if (pre_handler_called != test_func_instance) { + pr_err("FAIL: kprobe pre_handler not called\n"); + return -EINVAL; + } + if (post_handler_called != test_func_instance) { + pr_err("FAIL: kprobe post_handler not called\n"); + return -EINVAL; + } + if (!call_test_func(func, false)) + return -EINVAL; + if (pre_handler_called == test_func_instance || + post_handler_called == test_func_instance) { + pr_err("FAIL: probe called after unregistering\n"); + return -EINVAL; + } + + return 0; +} + +static void __kprobes jprobe_func(long r0, long r1) +{ + jprobe_func_called = test_func_instance; + if (r0 == FUNC_ARG1 && r1 == FUNC_ARG2) + test_regs_ok = true; + jprobe_return(); +} + +static struct jprobe the_jprobe = { + .entry = jprobe_func, +}; + +static int test_jprobe(long (*func)(long, long)) +{ + int ret; + + the_jprobe.kp.addr = (kprobe_opcode_t *)func; + ret = register_jprobe(&the_jprobe); + if (ret < 0) { + pr_err("FAIL: register_jprobe failed with %d\n", ret); + return ret; + } + + ret = call_test_func(func, true); + + unregister_jprobe(&the_jprobe); + the_jprobe.kp.flags = 0; /* Clear disable flag to allow reuse */ + + if (!ret) + return -EINVAL; + if (jprobe_func_called != test_func_instance) { + pr_err("FAIL: jprobe handler function not called\n"); + return -EINVAL; + } + if (!call_test_func(func, false)) + return -EINVAL; + if (jprobe_func_called == test_func_instance) { + pr_err("FAIL: probe called after unregistering\n"); + return -EINVAL; + } + + return 0; +} + +static int __kprobes +kretprobe_handler(struct kretprobe_instance *ri, struct pt_regs *regs) +{ + kretprobe_handler_called = test_func_instance; + if (regs_return_value(regs) == FUNC_ARG1 + FUNC_ARG2) + test_regs_ok = true; + return 0; +} + +static struct kretprobe the_kretprobe = { + .handler = kretprobe_handler, +}; + +static int test_kretprobe(long (*func)(long, long)) +{ + int ret; + + the_kretprobe.kp.addr = (kprobe_opcode_t *)func; + ret = register_kretprobe(&the_kretprobe); + if (ret < 0) { + pr_err("FAIL: register_kretprobe failed with %d\n", ret); + return ret; + } + + ret = call_test_func(func, true); + + unregister_kretprobe(&the_kretprobe); + the_kretprobe.kp.flags = 0; /* Clear disable flag to allow reuse */ + + if (!ret) + return -EINVAL; + if (kretprobe_handler_called != test_func_instance) { + pr_err("FAIL: kretprobe handler not called\n"); + return -EINVAL; + } + if (!call_test_func(func, false)) + return -EINVAL; + if (jprobe_func_called == test_func_instance) { + pr_err("FAIL: kretprobe called after unregistering\n"); + return -EINVAL; + } + + return 0; +} + +static int run_api_tests(long (*func)(long, long)) +{ + int ret; + + pr_info(" kprobe\n"); + ret = test_kprobe(func); + if (ret < 0) + return ret; + + pr_info(" jprobe\n"); + ret = test_jprobe(func); +#if defined(CONFIG_THUMB2_KERNEL) && !defined(MODULE) + if (ret == -EINVAL) { + pr_err("FAIL: Known longtime bug with jprobe on Thumb kernels\n"); + tests_failed = ret; + ret = 0; + } +#endif + if (ret < 0) + return ret; + + pr_info(" kretprobe\n"); + ret = test_kretprobe(func); + if (ret < 0) + return ret; + + return 0; +} + + +/* + * Benchmarking + */ + +#if BENCHMARKING + +static void __naked benchmark_nop(void) +{ + __asm__ __volatile__ ( + "nop \n\t" + "bx lr" + ); +} + +#ifdef CONFIG_THUMB2_KERNEL +#define wide ".w" +#else +#define wide +#endif + +static void __naked benchmark_pushpop1(void) +{ + __asm__ __volatile__ ( + "stmdb"wide" sp!, {r3-r11,lr} \n\t" + "ldmia"wide" sp!, {r3-r11,pc}" + ); +} + +static void __naked benchmark_pushpop2(void) +{ + __asm__ __volatile__ ( + "stmdb"wide" sp!, {r0-r8,lr} \n\t" + "ldmia"wide" sp!, {r0-r8,pc}" + ); +} + +static void __naked benchmark_pushpop3(void) +{ + __asm__ __volatile__ ( + "stmdb"wide" sp!, {r4,lr} \n\t" + "ldmia"wide" sp!, {r4,pc}" + ); +} + +static void __naked benchmark_pushpop4(void) +{ + __asm__ __volatile__ ( + "stmdb"wide" sp!, {r0,lr} \n\t" + "ldmia"wide" sp!, {r0,pc}" + ); +} + + +#ifdef CONFIG_THUMB2_KERNEL + +static void __naked benchmark_pushpop_thumb(void) +{ + __asm__ __volatile__ ( + "push.n {r0-r7,lr} \n\t" + "pop.n {r0-r7,pc}" + ); +} + +#endif + +static int __kprobes +benchmark_pre_handler(struct kprobe *p, struct pt_regs *regs) +{ + return 0; +} + +static int benchmark(void(*fn)(void)) +{ + unsigned n, i, t, t0; + + for (n = 1000; ; n *= 2) { + t0 = sched_clock(); + for (i = n; i > 0; --i) + fn(); + t = sched_clock() - t0; + if (t >= 250000000) + break; /* Stop once we took more than 0.25 seconds */ + } + return t / n; /* Time for one iteration in nanoseconds */ +}; + +static int kprobe_benchmark(void(*fn)(void), unsigned offset) +{ + struct kprobe k = { + .addr = (kprobe_opcode_t *)((uintptr_t)fn + offset), + .pre_handler = benchmark_pre_handler, + }; + + int ret = register_kprobe(&k); + if (ret < 0) { + pr_err("FAIL: register_kprobe failed with %d\n", ret); + return ret; + } + + ret = benchmark(fn); + + unregister_kprobe(&k); + return ret; +}; + +struct benchmarks { + void (*fn)(void); + unsigned offset; + const char *title; +}; + +static int run_benchmarks(void) +{ + int ret; + struct benchmarks list[] = { + {&benchmark_nop, 0, "nop"}, + /* + * benchmark_pushpop{1,3} will have the optimised + * instruction emulation, whilst benchmark_pushpop{2,4} will + * be the equivalent unoptimised instructions. + */ + {&benchmark_pushpop1, 0, "stmdb sp!, {r3-r11,lr}"}, + {&benchmark_pushpop1, 4, "ldmia sp!, {r3-r11,pc}"}, + {&benchmark_pushpop2, 0, "stmdb sp!, {r0-r8,lr}"}, + {&benchmark_pushpop2, 4, "ldmia sp!, {r0-r8,pc}"}, + {&benchmark_pushpop3, 0, "stmdb sp!, {r4,lr}"}, + {&benchmark_pushpop3, 4, "ldmia sp!, {r4,pc}"}, + {&benchmark_pushpop4, 0, "stmdb sp!, {r0,lr}"}, + {&benchmark_pushpop4, 4, "ldmia sp!, {r0,pc}"}, +#ifdef CONFIG_THUMB2_KERNEL + {&benchmark_pushpop_thumb, 0, "push.n {r0-r7,lr}"}, + {&benchmark_pushpop_thumb, 2, "pop.n {r0-r7,pc}"}, +#endif + {0} + }; + + struct benchmarks *b; + for (b = list; b->fn; ++b) { + ret = kprobe_benchmark(b->fn, b->offset); + if (ret < 0) + return ret; + pr_info(" %dns for kprobe %s\n", ret, b->title); + } + + pr_info("\n"); + return 0; +} + +#endif /* BENCHMARKING */ + + +/* + * Decoding table self-consistency tests + */ + +static const int decode_struct_sizes[NUM_DECODE_TYPES] = { + [DECODE_TYPE_TABLE] = sizeof(struct decode_table), + [DECODE_TYPE_CUSTOM] = sizeof(struct decode_custom), + [DECODE_TYPE_SIMULATE] = sizeof(struct decode_simulate), + [DECODE_TYPE_EMULATE] = sizeof(struct decode_emulate), + [DECODE_TYPE_OR] = sizeof(struct decode_or), + [DECODE_TYPE_REJECT] = sizeof(struct decode_reject) +}; + +static int table_iter(const union decode_item *table, + int (*fn)(const struct decode_header *, void *), + void *args) +{ + const struct decode_header *h = (struct decode_header *)table; + int result; + + for (;;) { + enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK; + + if (type == DECODE_TYPE_END) + return 0; + + result = fn(h, args); + if (result) + return result; + + h = (struct decode_header *) + ((uintptr_t)h + decode_struct_sizes[type]); + + } +} + +static int table_test_fail(const struct decode_header *h, const char* message) +{ + + pr_err("FAIL: kprobes test failure \"%s\" (mask %08x, value %08x)\n", + message, h->mask.bits, h->value.bits); + return -EINVAL; +} + +struct table_test_args { + const union decode_item *root_table; + u32 parent_mask; + u32 parent_value; +}; + +static int table_test_fn(const struct decode_header *h, void *args) +{ + struct table_test_args *a = (struct table_test_args *)args; + enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK; + + if (h->value.bits & ~h->mask.bits) + return table_test_fail(h, "Match value has bits not in mask"); + + if ((h->mask.bits & a->parent_mask) != a->parent_mask) + return table_test_fail(h, "Mask has bits not in parent mask"); + + if ((h->value.bits ^ a->parent_value) & a->parent_mask) + return table_test_fail(h, "Value is inconsistent with parent"); + + if (type == DECODE_TYPE_TABLE) { + struct decode_table *d = (struct decode_table *)h; + struct table_test_args args2 = *a; + args2.parent_mask = h->mask.bits; + args2.parent_value = h->value.bits; + return table_iter(d->table.table, table_test_fn, &args2); + } + + return 0; +} + +static int table_test(const union decode_item *table) +{ + struct table_test_args args = { + .root_table = table, + .parent_mask = 0, + .parent_value = 0 + }; + return table_iter(args.root_table, table_test_fn, &args); +} + + +/* + * Decoding table test coverage analysis + * + * coverage_start() builds a coverage_table which contains a list of + * coverage_entry's to match each entry in the specified kprobes instruction + * decoding table. + * + * When test cases are run, coverage_add() is called to process each case. + * This looks up the corresponding entry in the coverage_table and sets it as + * being matched, as well as clearing the regs flag appropriate for the test. + * + * After all test cases have been run, coverage_end() is called to check that + * all entries in coverage_table have been matched and that all regs flags are + * cleared. I.e. that all possible combinations of instructions described by + * the kprobes decoding tables have had a test case executed for them. + */ + +bool coverage_fail; + +#define MAX_COVERAGE_ENTRIES 256 + +struct coverage_entry { + const struct decode_header *header; + unsigned regs; + unsigned nesting; + char matched; +}; + +struct coverage_table { + struct coverage_entry *base; + unsigned num_entries; + unsigned nesting; +}; + +struct coverage_table coverage; + +#define COVERAGE_ANY_REG (1<<0) +#define COVERAGE_SP (1<<1) +#define COVERAGE_PC (1<<2) +#define COVERAGE_PCWB (1<<3) + +static const char coverage_register_lookup[16] = { + [REG_TYPE_ANY] = COVERAGE_ANY_REG | COVERAGE_SP | COVERAGE_PC, + [REG_TYPE_SAMEAS16] = COVERAGE_ANY_REG, + [REG_TYPE_SP] = COVERAGE_SP, + [REG_TYPE_PC] = COVERAGE_PC, + [REG_TYPE_NOSP] = COVERAGE_ANY_REG | COVERAGE_SP, + [REG_TYPE_NOSPPC] = COVERAGE_ANY_REG | COVERAGE_SP | COVERAGE_PC, + [REG_TYPE_NOPC] = COVERAGE_ANY_REG | COVERAGE_PC, + [REG_TYPE_NOPCWB] = COVERAGE_ANY_REG | COVERAGE_PC | COVERAGE_PCWB, + [REG_TYPE_NOPCX] = COVERAGE_ANY_REG, + [REG_TYPE_NOSPPCX] = COVERAGE_ANY_REG | COVERAGE_SP, +}; + +unsigned coverage_start_registers(const struct decode_header *h) +{ + unsigned regs = 0; + int i; + for (i = 0; i < 20; i += 4) { + int r = (h->type_regs.bits >> (DECODE_TYPE_BITS + i)) & 0xf; + regs |= coverage_register_lookup[r] << i; + } + return regs; +} + +static int coverage_start_fn(const struct decode_header *h, void *args) +{ + struct coverage_table *coverage = (struct coverage_table *)args; + enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK; + struct coverage_entry *entry = coverage->base + coverage->num_entries; + + if (coverage->num_entries == MAX_COVERAGE_ENTRIES - 1) { + pr_err("FAIL: Out of space for test coverage data"); + return -ENOMEM; + } + + ++coverage->num_entries; + + entry->header = h; + entry->regs = coverage_start_registers(h); + entry->nesting = coverage->nesting; + entry->matched = false; + + if (type == DECODE_TYPE_TABLE) { + struct decode_table *d = (struct decode_table *)h; + int ret; + ++coverage->nesting; + ret = table_iter(d->table.table, coverage_start_fn, coverage); + --coverage->nesting; + return ret; + } + + return 0; +} + +static int coverage_start(const union decode_item *table) +{ + coverage.base = kmalloc(MAX_COVERAGE_ENTRIES * + sizeof(struct coverage_entry), GFP_KERNEL); + coverage.num_entries = 0; + coverage.nesting = 0; + return table_iter(table, coverage_start_fn, &coverage); +} + +static void +coverage_add_registers(struct coverage_entry *entry, kprobe_opcode_t insn) +{ + int regs = entry->header->type_regs.bits >> DECODE_TYPE_BITS; + int i; + for (i = 0; i < 20; i += 4) { + enum decode_reg_type reg_type = (regs >> i) & 0xf; + int reg = (insn >> i) & 0xf; + int flag; + + if (!reg_type) + continue; + + if (reg == 13) + flag = COVERAGE_SP; + else if (reg == 15) + flag = COVERAGE_PC; + else + flag = COVERAGE_ANY_REG; + entry->regs &= ~(flag << i); + + switch (reg_type) { + + case REG_TYPE_NONE: + case REG_TYPE_ANY: + case REG_TYPE_SAMEAS16: + break; + + case REG_TYPE_SP: + if (reg != 13) + return; + break; + + case REG_TYPE_PC: + if (reg != 15) + return; + break; + + case REG_TYPE_NOSP: + if (reg == 13) + return; + break; + + case REG_TYPE_NOSPPC: + case REG_TYPE_NOSPPCX: + if (reg == 13 || reg == 15) + return; + break; + + case REG_TYPE_NOPCWB: + if (!is_writeback(insn)) + break; + if (reg == 15) { + entry->regs &= ~(COVERAGE_PCWB << i); + return; + } + break; + + case REG_TYPE_NOPC: + case REG_TYPE_NOPCX: + if (reg == 15) + return; + break; + } + + } +} + +static void coverage_add(kprobe_opcode_t insn) +{ + struct coverage_entry *entry = coverage.base; + struct coverage_entry *end = coverage.base + coverage.num_entries; + bool matched = false; + unsigned nesting = 0; + + for (; entry < end; ++entry) { + const struct decode_header *h = entry->header; + enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK; + + if (entry->nesting > nesting) + continue; /* Skip sub-table we didn't match */ + + if (entry->nesting < nesting) + break; /* End of sub-table we were scanning */ + + if (!matched) { + if ((insn & h->mask.bits) != h->value.bits) + continue; + entry->matched = true; + } + + switch (type) { + + case DECODE_TYPE_TABLE: + ++nesting; + break; + + case DECODE_TYPE_CUSTOM: + case DECODE_TYPE_SIMULATE: + case DECODE_TYPE_EMULATE: + coverage_add_registers(entry, insn); + return; + + case DECODE_TYPE_OR: + matched = true; + break; + + case DECODE_TYPE_REJECT: + default: + return; + } + + } +} + +static void coverage_end(void) +{ + struct coverage_entry *entry = coverage.base; + struct coverage_entry *end = coverage.base + coverage.num_entries; + + for (; entry < end; ++entry) { + u32 mask = entry->header->mask.bits; + u32 value = entry->header->value.bits; + + if (entry->regs) { + pr_err("FAIL: Register test coverage missing for %08x %08x (%05x)\n", + mask, value, entry->regs); + coverage_fail = true; + } + if (!entry->matched) { + pr_err("FAIL: Test coverage entry missing for %08x %08x\n", + mask, value); + coverage_fail = true; + } + } + + kfree(coverage.base); +} + + +/* + * Framework for instruction set test cases + */ + +void __naked __kprobes_test_case_start(void) +{ + __asm__ __volatile__ ( + "stmdb sp!, {r4-r11} \n\t" + "sub sp, sp, #"__stringify(TEST_MEMORY_SIZE)"\n\t" + "bic r0, lr, #1 @ r0 = inline data \n\t" + "mov r1, sp \n\t" + "bl kprobes_test_case_start \n\t" + "bx r0 \n\t" + ); +} + +#ifndef CONFIG_THUMB2_KERNEL + +void __naked __kprobes_test_case_end_32(void) +{ + __asm__ __volatile__ ( + "mov r4, lr \n\t" + "bl kprobes_test_case_end \n\t" + "cmp r0, #0 \n\t" + "movne pc, r0 \n\t" + "mov r0, r4 \n\t" + "add sp, sp, #"__stringify(TEST_MEMORY_SIZE)"\n\t" + "ldmia sp!, {r4-r11} \n\t" + "mov pc, r0 \n\t" + ); +} + +#else /* CONFIG_THUMB2_KERNEL */ + +void __naked __kprobes_test_case_end_16(void) +{ + __asm__ __volatile__ ( + "mov r4, lr \n\t" + "bl kprobes_test_case_end \n\t" + "cmp r0, #0 \n\t" + "bxne r0 \n\t" + "mov r0, r4 \n\t" + "add sp, sp, #"__stringify(TEST_MEMORY_SIZE)"\n\t" + "ldmia sp!, {r4-r11} \n\t" + "bx r0 \n\t" + ); +} + +void __naked __kprobes_test_case_end_32(void) +{ + __asm__ __volatile__ ( + ".arm \n\t" + "orr lr, lr, #1 @ will return to Thumb code \n\t" + "ldr pc, 1f \n\t" + "1: \n\t" + ".word __kprobes_test_case_end_16 \n\t" + ); +} + +#endif + + +int kprobe_test_flags; +int kprobe_test_cc_position; + +static int test_try_count; +static int test_pass_count; +static int test_fail_count; + +static struct pt_regs initial_regs; +static struct pt_regs expected_regs; +static struct pt_regs result_regs; + +static u32 expected_memory[TEST_MEMORY_SIZE/sizeof(u32)]; + +static const char *current_title; +static struct test_arg *current_args; +static u32 *current_stack; +static uintptr_t current_branch_target; + +static uintptr_t current_code_start; +static kprobe_opcode_t current_instruction; + + +#define TEST_CASE_PASSED -1 +#define TEST_CASE_FAILED -2 + +static int test_case_run_count; +static bool test_case_is_thumb; +static int test_instance; + +/* + * We ignore the state of the imprecise abort disable flag (CPSR.A) because this + * can change randomly as the kernel doesn't take care to preserve or initialise + * this across context switches. Also, with Security Extentions, the flag may + * not be under control of the kernel; for this reason we ignore the state of + * the FIQ disable flag CPSR.F as well. + */ +#define PSR_IGNORE_BITS (PSR_A_BIT | PSR_F_BIT) + +static unsigned long test_check_cc(int cc, unsigned long cpsr) +{ + int ret = arm_check_condition(cc << 28, cpsr); + + return (ret != ARM_OPCODE_CONDTEST_FAIL); +} + +static int is_last_scenario; +static int probe_should_run; /* 0 = no, 1 = yes, -1 = unknown */ +static int memory_needs_checking; + +static unsigned long test_context_cpsr(int scenario) +{ + unsigned long cpsr; + + probe_should_run = 1; + + /* Default case is that we cycle through 16 combinations of flags */ + cpsr = (scenario & 0xf) << 28; /* N,Z,C,V flags */ + cpsr |= (scenario & 0xf) << 16; /* GE flags */ + cpsr |= (scenario & 0x1) << 27; /* Toggle Q flag */ + + if (!test_case_is_thumb) { + /* Testing ARM code */ + int cc = current_instruction >> 28; + + probe_should_run = test_check_cc(cc, cpsr) != 0; + if (scenario == 15) + is_last_scenario = true; + + } else if (kprobe_test_flags & TEST_FLAG_NO_ITBLOCK) { + /* Testing Thumb code without setting ITSTATE */ + if (kprobe_test_cc_position) { + int cc = (current_instruction >> kprobe_test_cc_position) & 0xf; + probe_should_run = test_check_cc(cc, cpsr) != 0; + } + + if (scenario == 15) + is_last_scenario = true; + + } else if (kprobe_test_flags & TEST_FLAG_FULL_ITBLOCK) { + /* Testing Thumb code with all combinations of ITSTATE */ + unsigned x = (scenario >> 4); + unsigned cond_base = x % 7; /* ITSTATE<7:5> */ + unsigned mask = x / 7 + 2; /* ITSTATE<4:0>, bits reversed */ + + if (mask > 0x1f) { + /* Finish by testing state from instruction 'itt al' */ + cond_base = 7; + mask = 0x4; + if ((scenario & 0xf) == 0xf) + is_last_scenario = true; + } + + cpsr |= cond_base << 13; /* ITSTATE<7:5> */ + cpsr |= (mask & 0x1) << 12; /* ITSTATE<4> */ + cpsr |= (mask & 0x2) << 10; /* ITSTATE<3> */ + cpsr |= (mask & 0x4) << 8; /* ITSTATE<2> */ + cpsr |= (mask & 0x8) << 23; /* ITSTATE<1> */ + cpsr |= (mask & 0x10) << 21; /* ITSTATE<0> */ + + probe_should_run = test_check_cc((cpsr >> 12) & 0xf, cpsr) != 0; + + } else { + /* Testing Thumb code with several combinations of ITSTATE */ + switch (scenario) { + case 16: /* Clear NZCV flags and 'it eq' state (false as Z=0) */ + cpsr = 0x00000800; + probe_should_run = 0; + break; + case 17: /* Set NZCV flags and 'it vc' state (false as V=1) */ + cpsr = 0xf0007800; + probe_should_run = 0; + break; + case 18: /* Clear NZCV flags and 'it ls' state (true as C=0) */ + cpsr = 0x00009800; + break; + case 19: /* Set NZCV flags and 'it cs' state (true as C=1) */ + cpsr = 0xf0002800; + is_last_scenario = true; + break; + } + } + + return cpsr; +} + +static void setup_test_context(struct pt_regs *regs) +{ + int scenario = test_case_run_count>>1; + unsigned long val; + struct test_arg *args; + int i; + + is_last_scenario = false; + memory_needs_checking = false; + + /* Initialise test memory on stack */ + val = (scenario & 1) ? VALM : ~VALM; + for (i = 0; i < TEST_MEMORY_SIZE / sizeof(current_stack[0]); ++i) + current_stack[i] = val + (i << 8); + /* Put target of branch on stack for tests which load PC from memory */ + if (current_branch_target) + current_stack[15] = current_branch_target; + /* Put a value for SP on stack for tests which load SP from memory */ + current_stack[13] = (u32)current_stack + 120; + + /* Initialise register values to their default state */ + val = (scenario & 2) ? VALR : ~VALR; + for (i = 0; i < 13; ++i) + regs->uregs[i] = val ^ (i << 8); + regs->ARM_lr = val ^ (14 << 8); + regs->ARM_cpsr &= ~(APSR_MASK | PSR_IT_MASK); + regs->ARM_cpsr |= test_context_cpsr(scenario); + + /* Perform testcase specific register setup */ + args = current_args; + for (; args[0].type != ARG_TYPE_END; ++args) + switch (args[0].type) { + case ARG_TYPE_REG: { + struct test_arg_regptr *arg = + (struct test_arg_regptr *)args; + regs->uregs[arg->reg] = arg->val; + break; + } + case ARG_TYPE_PTR: { + struct test_arg_regptr *arg = + (struct test_arg_regptr *)args; + regs->uregs[arg->reg] = + (unsigned long)current_stack + arg->val; + memory_needs_checking = true; + break; + } + case ARG_TYPE_MEM: { + struct test_arg_mem *arg = (struct test_arg_mem *)args; + current_stack[arg->index] = arg->val; + break; + } + default: + break; + } +} + +struct test_probe { + struct kprobe kprobe; + bool registered; + int hit; +}; + +static void unregister_test_probe(struct test_probe *probe) +{ + if (probe->registered) { + unregister_kprobe(&probe->kprobe); + probe->kprobe.flags = 0; /* Clear disable flag to allow reuse */ + } + probe->registered = false; +} + +static int register_test_probe(struct test_probe *probe) +{ + int ret; + + if (probe->registered) + BUG(); + + ret = register_kprobe(&probe->kprobe); + if (ret >= 0) { + probe->registered = true; + probe->hit = -1; + } + return ret; +} + +static int __kprobes +test_before_pre_handler(struct kprobe *p, struct pt_regs *regs) +{ + container_of(p, struct test_probe, kprobe)->hit = test_instance; + return 0; +} + +static void __kprobes +test_before_post_handler(struct kprobe *p, struct pt_regs *regs, + unsigned long flags) +{ + setup_test_context(regs); + initial_regs = *regs; + initial_regs.ARM_cpsr &= ~PSR_IGNORE_BITS; +} + +static int __kprobes +test_case_pre_handler(struct kprobe *p, struct pt_regs *regs) +{ + container_of(p, struct test_probe, kprobe)->hit = test_instance; + return 0; +} + +static int __kprobes +test_after_pre_handler(struct kprobe *p, struct pt_regs *regs) +{ + if (container_of(p, struct test_probe, kprobe)->hit == test_instance) + return 0; /* Already run for this test instance */ + + result_regs = *regs; + result_regs.ARM_cpsr &= ~PSR_IGNORE_BITS; + + /* Undo any changes done to SP by the test case */ + regs->ARM_sp = (unsigned long)current_stack; + + container_of(p, struct test_probe, kprobe)->hit = test_instance; + return 0; +} + +static struct test_probe test_before_probe = { + .kprobe.pre_handler = test_before_pre_handler, + .kprobe.post_handler = test_before_post_handler, +}; + +static struct test_probe test_case_probe = { + .kprobe.pre_handler = test_case_pre_handler, +}; + +static struct test_probe test_after_probe = { + .kprobe.pre_handler = test_after_pre_handler, +}; + +static struct test_probe test_after2_probe = { + .kprobe.pre_handler = test_after_pre_handler, +}; + +static void test_case_cleanup(void) +{ + unregister_test_probe(&test_before_probe); + unregister_test_probe(&test_case_probe); + unregister_test_probe(&test_after_probe); + unregister_test_probe(&test_after2_probe); +} + +static void print_registers(struct pt_regs *regs) +{ + pr_err("r0 %08lx | r1 %08lx | r2 %08lx | r3 %08lx\n", + regs->ARM_r0, regs->ARM_r1, regs->ARM_r2, regs->ARM_r3); + pr_err("r4 %08lx | r5 %08lx | r6 %08lx | r7 %08lx\n", + regs->ARM_r4, regs->ARM_r5, regs->ARM_r6, regs->ARM_r7); + pr_err("r8 %08lx | r9 %08lx | r10 %08lx | r11 %08lx\n", + regs->ARM_r8, regs->ARM_r9, regs->ARM_r10, regs->ARM_fp); + pr_err("r12 %08lx | sp %08lx | lr %08lx | pc %08lx\n", + regs->ARM_ip, regs->ARM_sp, regs->ARM_lr, regs->ARM_pc); + pr_err("cpsr %08lx\n", regs->ARM_cpsr); +} + +static void print_memory(u32 *mem, size_t size) +{ + int i; + for (i = 0; i < size / sizeof(u32); i += 4) + pr_err("%08x %08x %08x %08x\n", mem[i], mem[i+1], + mem[i+2], mem[i+3]); +} + +static size_t expected_memory_size(u32 *sp) +{ + size_t size = sizeof(expected_memory); + int offset = (uintptr_t)sp - (uintptr_t)current_stack; + if (offset > 0) + size -= offset; + return size; +} + +static void test_case_failed(const char *message) +{ + test_case_cleanup(); + + pr_err("FAIL: %s\n", message); + pr_err("FAIL: Test %s\n", current_title); + pr_err("FAIL: Scenario %d\n", test_case_run_count >> 1); +} + +static unsigned long next_instruction(unsigned long pc) +{ +#ifdef CONFIG_THUMB2_KERNEL + if ((pc & 1) && + !is_wide_instruction(__mem_to_opcode_thumb16(*(u16 *)(pc - 1)))) + return pc + 2; + else +#endif + return pc + 4; +} + +static uintptr_t __used kprobes_test_case_start(const char **title, void *stack) +{ + struct test_arg *args; + struct test_arg_end *end_arg; + unsigned long test_code; + + current_title = *title++; + args = (struct test_arg *)title; + current_args = args; + current_stack = stack; + + ++test_try_count; + + while (args->type != ARG_TYPE_END) + ++args; + end_arg = (struct test_arg_end *)args; + + test_code = (unsigned long)(args + 1); /* Code starts after args */ + + test_case_is_thumb = end_arg->flags & ARG_FLAG_THUMB; + if (test_case_is_thumb) + test_code |= 1; + + current_code_start = test_code; + + current_branch_target = 0; + if (end_arg->branch_offset != end_arg->end_offset) + current_branch_target = test_code + end_arg->branch_offset; + + test_code += end_arg->code_offset; + test_before_probe.kprobe.addr = (kprobe_opcode_t *)test_code; + + test_code = next_instruction(test_code); + test_case_probe.kprobe.addr = (kprobe_opcode_t *)test_code; + + if (test_case_is_thumb) { + u16 *p = (u16 *)(test_code & ~1); + current_instruction = __mem_to_opcode_thumb16(p[0]); + if (is_wide_instruction(current_instruction)) { + u16 instr2 = __mem_to_opcode_thumb16(p[1]); + current_instruction = __opcode_thumb32_compose(current_instruction, instr2); + } + } else { + current_instruction = __mem_to_opcode_arm(*(u32 *)test_code); + } + + if (current_title[0] == '.') + verbose("%s\n", current_title); + else + verbose("%s\t@ %0*x\n", current_title, + test_case_is_thumb ? 4 : 8, + current_instruction); + + test_code = next_instruction(test_code); + test_after_probe.kprobe.addr = (kprobe_opcode_t *)test_code; + + if (kprobe_test_flags & TEST_FLAG_NARROW_INSTR) { + if (!test_case_is_thumb || + is_wide_instruction(current_instruction)) { + test_case_failed("expected 16-bit instruction"); + goto fail; + } + } else { + if (test_case_is_thumb && + !is_wide_instruction(current_instruction)) { + test_case_failed("expected 32-bit instruction"); + goto fail; + } + } + + coverage_add(current_instruction); + + if (end_arg->flags & ARG_FLAG_UNSUPPORTED) { + if (register_test_probe(&test_case_probe) < 0) + goto pass; + test_case_failed("registered probe for unsupported instruction"); + goto fail; + } + + if (end_arg->flags & ARG_FLAG_SUPPORTED) { + if (register_test_probe(&test_case_probe) >= 0) + goto pass; + test_case_failed("couldn't register probe for supported instruction"); + goto fail; + } + + if (register_test_probe(&test_before_probe) < 0) { + test_case_failed("register test_before_probe failed"); + goto fail; + } + if (register_test_probe(&test_after_probe) < 0) { + test_case_failed("register test_after_probe failed"); + goto fail; + } + if (current_branch_target) { + test_after2_probe.kprobe.addr = + (kprobe_opcode_t *)current_branch_target; + if (register_test_probe(&test_after2_probe) < 0) { + test_case_failed("register test_after2_probe failed"); + goto fail; + } + } + + /* Start first run of test case */ + test_case_run_count = 0; + ++test_instance; + return current_code_start; +pass: + test_case_run_count = TEST_CASE_PASSED; + return (uintptr_t)test_after_probe.kprobe.addr; +fail: + test_case_run_count = TEST_CASE_FAILED; + return (uintptr_t)test_after_probe.kprobe.addr; +} + +static bool check_test_results(void) +{ + size_t mem_size = 0; + u32 *mem = 0; + + if (memcmp(&expected_regs, &result_regs, sizeof(expected_regs))) { + test_case_failed("registers differ"); + goto fail; + } + + if (memory_needs_checking) { + mem = (u32 *)result_regs.ARM_sp; + mem_size = expected_memory_size(mem); + if (memcmp(expected_memory, mem, mem_size)) { + test_case_failed("test memory differs"); + goto fail; + } + } + + return true; + +fail: + pr_err("initial_regs:\n"); + print_registers(&initial_regs); + pr_err("expected_regs:\n"); + print_registers(&expected_regs); + pr_err("result_regs:\n"); + print_registers(&result_regs); + + if (mem) { + pr_err("current_stack=%p\n", current_stack); + pr_err("expected_memory:\n"); + print_memory(expected_memory, mem_size); + pr_err("result_memory:\n"); + print_memory(mem, mem_size); + } + + return false; +} + +static uintptr_t __used kprobes_test_case_end(void) +{ + if (test_case_run_count < 0) { + if (test_case_run_count == TEST_CASE_PASSED) + /* kprobes_test_case_start did all the needed testing */ + goto pass; + else + /* kprobes_test_case_start failed */ + goto fail; + } + + if (test_before_probe.hit != test_instance) { + test_case_failed("test_before_handler not run"); + goto fail; + } + + if (test_after_probe.hit != test_instance && + test_after2_probe.hit != test_instance) { + test_case_failed("test_after_handler not run"); + goto fail; + } + + /* + * Even numbered test runs ran without a probe on the test case so + * we can gather reference results. The subsequent odd numbered run + * will have the probe inserted. + */ + if ((test_case_run_count & 1) == 0) { + /* Save results from run without probe */ + u32 *mem = (u32 *)result_regs.ARM_sp; + expected_regs = result_regs; + memcpy(expected_memory, mem, expected_memory_size(mem)); + + /* Insert probe onto test case instruction */ + if (register_test_probe(&test_case_probe) < 0) { + test_case_failed("register test_case_probe failed"); + goto fail; + } + } else { + /* Check probe ran as expected */ + if (probe_should_run == 1) { + if (test_case_probe.hit != test_instance) { + test_case_failed("test_case_handler not run"); + goto fail; + } + } else if (probe_should_run == 0) { + if (test_case_probe.hit == test_instance) { + test_case_failed("test_case_handler ran"); + goto fail; + } + } + + /* Remove probe for any subsequent reference run */ + unregister_test_probe(&test_case_probe); + + if (!check_test_results()) + goto fail; + + if (is_last_scenario) + goto pass; + } + + /* Do next test run */ + ++test_case_run_count; + ++test_instance; + return current_code_start; +fail: + ++test_fail_count; + goto end; +pass: + ++test_pass_count; +end: + test_case_cleanup(); + return 0; +} + + +/* + * Top level test functions + */ + +static int run_test_cases(void (*tests)(void), const union decode_item *table) +{ + int ret; + + pr_info(" Check decoding tables\n"); + ret = table_test(table); + if (ret) + return ret; + + pr_info(" Run test cases\n"); + ret = coverage_start(table); + if (ret) + return ret; + + tests(); + + coverage_end(); + return 0; +} + + +static int __init run_all_tests(void) +{ + int ret = 0; + + pr_info("Beginning kprobe tests...\n"); + +#ifndef CONFIG_THUMB2_KERNEL + + pr_info("Probe ARM code\n"); + ret = run_api_tests(arm_func); + if (ret) + goto out; + + pr_info("ARM instruction simulation\n"); + ret = run_test_cases(kprobe_arm_test_cases, probes_decode_arm_table); + if (ret) + goto out; + +#else /* CONFIG_THUMB2_KERNEL */ + + pr_info("Probe 16-bit Thumb code\n"); + ret = run_api_tests(thumb16_func); + if (ret) + goto out; + + pr_info("Probe 32-bit Thumb code, even halfword\n"); + ret = run_api_tests(thumb32even_func); + if (ret) + goto out; + + pr_info("Probe 32-bit Thumb code, odd halfword\n"); + ret = run_api_tests(thumb32odd_func); + if (ret) + goto out; + + pr_info("16-bit Thumb instruction simulation\n"); + ret = run_test_cases(kprobe_thumb16_test_cases, + probes_decode_thumb16_table); + if (ret) + goto out; + + pr_info("32-bit Thumb instruction simulation\n"); + ret = run_test_cases(kprobe_thumb32_test_cases, + probes_decode_thumb32_table); + if (ret) + goto out; +#endif + + pr_info("Total instruction simulation tests=%d, pass=%d fail=%d\n", + test_try_count, test_pass_count, test_fail_count); + if (test_fail_count) { + ret = -EINVAL; + goto out; + } + +#if BENCHMARKING + pr_info("Benchmarks\n"); + ret = run_benchmarks(); + if (ret) + goto out; +#endif + +#if __LINUX_ARM_ARCH__ >= 7 + /* We are able to run all test cases so coverage should be complete */ + if (coverage_fail) { + pr_err("FAIL: Test coverage checks failed\n"); + ret = -EINVAL; + goto out; + } +#endif + +out: + if (ret == 0) + ret = tests_failed; + if (ret == 0) + pr_info("Finished kprobe tests OK\n"); + else + pr_err("kprobe tests failed\n"); + + return ret; +} + + +/* + * Module setup + */ + +#ifdef MODULE + +static void __exit kprobe_test_exit(void) +{ +} + +module_init(run_all_tests) +module_exit(kprobe_test_exit) +MODULE_LICENSE("GPL"); + +#else /* !MODULE */ + +late_initcall(run_all_tests); + +#endif diff --git a/arch/arm/probes/kprobes/test-core.h b/arch/arm/probes/kprobes/test-core.h new file mode 100644 index 0000000..9991754 --- /dev/null +++ b/arch/arm/probes/kprobes/test-core.h @@ -0,0 +1,435 @@ +/* + * arch/arm/probes/kprobes/test-core.h + * + * Copyright (C) 2011 Jon Medhurst . + * + * 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. + */ + +#define VERBOSE 0 /* Set to '1' for more logging of test cases */ + +#ifdef CONFIG_THUMB2_KERNEL +#define NORMAL_ISA "16" +#else +#define NORMAL_ISA "32" +#endif + + +/* Flags used in kprobe_test_flags */ +#define TEST_FLAG_NO_ITBLOCK (1<<0) +#define TEST_FLAG_FULL_ITBLOCK (1<<1) +#define TEST_FLAG_NARROW_INSTR (1<<2) + +extern int kprobe_test_flags; +extern int kprobe_test_cc_position; + + +#define TEST_MEMORY_SIZE 256 + + +/* + * Test case structures. + * + * The arguments given to test cases can be one of three types. + * + * ARG_TYPE_REG + * Load a register with the given value. + * + * ARG_TYPE_PTR + * Load a register with a pointer into the stack buffer (SP + given value). + * + * ARG_TYPE_MEM + * Store the given value into the stack buffer at [SP+index]. + * + */ + +#define ARG_TYPE_END 0 +#define ARG_TYPE_REG 1 +#define ARG_TYPE_PTR 2 +#define ARG_TYPE_MEM 3 + +#define ARG_FLAG_UNSUPPORTED 0x01 +#define ARG_FLAG_SUPPORTED 0x02 +#define ARG_FLAG_THUMB 0x10 /* Must be 16 so TEST_ISA can be used */ +#define ARG_FLAG_ARM 0x20 /* Must be 32 so TEST_ISA can be used */ + +struct test_arg { + u8 type; /* ARG_TYPE_x */ + u8 _padding[7]; +}; + +struct test_arg_regptr { + u8 type; /* ARG_TYPE_REG or ARG_TYPE_PTR */ + u8 reg; + u8 _padding[2]; + u32 val; +}; + +struct test_arg_mem { + u8 type; /* ARG_TYPE_MEM */ + u8 index; + u8 _padding[2]; + u32 val; +}; + +struct test_arg_end { + u8 type; /* ARG_TYPE_END */ + u8 flags; /* ARG_FLAG_x */ + u16 code_offset; + u16 branch_offset; + u16 end_offset; +}; + + +/* + * Building blocks for test cases. + * + * Each test case is wrapped between TESTCASE_START and TESTCASE_END. + * + * To specify arguments for a test case the TEST_ARG_{REG,PTR,MEM} macros are + * used followed by a terminating TEST_ARG_END. + * + * After this, the instruction to be tested is defined with TEST_INSTRUCTION. + * Or for branches, TEST_BRANCH_B and TEST_BRANCH_F (branch forwards/backwards). + * + * Some specific test cases may make use of other custom constructs. + */ + +#if VERBOSE +#define verbose(fmt, ...) pr_info(fmt, ##__VA_ARGS__) +#else +#define verbose(fmt, ...) +#endif + +#define TEST_GROUP(title) \ + verbose("\n"); \ + verbose(title"\n"); \ + verbose("---------------------------------------------------------\n"); + +#define TESTCASE_START(title) \ + __asm__ __volatile__ ( \ + "bl __kprobes_test_case_start \n\t" \ + ".pushsection .rodata \n\t" \ + "10: \n\t" \ + /* don't use .asciz here as 'title' may be */ \ + /* multiple strings to be concatenated. */ \ + ".ascii "#title" \n\t" \ + ".byte 0 \n\t" \ + ".popsection \n\t" \ + ".word 10b \n\t" + +#define TEST_ARG_REG(reg, val) \ + ".byte "__stringify(ARG_TYPE_REG)" \n\t" \ + ".byte "#reg" \n\t" \ + ".short 0 \n\t" \ + ".word "#val" \n\t" + +#define TEST_ARG_PTR(reg, val) \ + ".byte "__stringify(ARG_TYPE_PTR)" \n\t" \ + ".byte "#reg" \n\t" \ + ".short 0 \n\t" \ + ".word "#val" \n\t" + +#define TEST_ARG_MEM(index, val) \ + ".byte "__stringify(ARG_TYPE_MEM)" \n\t" \ + ".byte "#index" \n\t" \ + ".short 0 \n\t" \ + ".word "#val" \n\t" + +#define TEST_ARG_END(flags) \ + ".byte "__stringify(ARG_TYPE_END)" \n\t" \ + ".byte "TEST_ISA flags" \n\t" \ + ".short 50f-0f \n\t" \ + ".short 2f-0f \n\t" \ + ".short 99f-0f \n\t" \ + ".code "TEST_ISA" \n\t" \ + "0: \n\t" + +#define TEST_INSTRUCTION(instruction) \ + "50: nop \n\t" \ + "1: "instruction" \n\t" \ + " nop \n\t" + +#define TEST_BRANCH_F(instruction) \ + TEST_INSTRUCTION(instruction) \ + " b 99f \n\t" \ + "2: nop \n\t" + +#define TEST_BRANCH_B(instruction) \ + " b 50f \n\t" \ + " b 99f \n\t" \ + "2: nop \n\t" \ + " b 99f \n\t" \ + TEST_INSTRUCTION(instruction) + +#define TEST_BRANCH_FX(instruction, codex) \ + TEST_INSTRUCTION(instruction) \ + " b 99f \n\t" \ + codex" \n\t" \ + " b 99f \n\t" \ + "2: nop \n\t" + +#define TEST_BRANCH_BX(instruction, codex) \ + " b 50f \n\t" \ + " b 99f \n\t" \ + "2: nop \n\t" \ + " b 99f \n\t" \ + codex" \n\t" \ + TEST_INSTRUCTION(instruction) + +#define TESTCASE_END \ + "2: \n\t" \ + "99: \n\t" \ + " bl __kprobes_test_case_end_"TEST_ISA" \n\t" \ + ".code "NORMAL_ISA" \n\t" \ + : : \ + : "r0", "r1", "r2", "r3", "ip", "lr", "memory", "cc" \ + ); + + +/* + * Macros to define test cases. + * + * Those of the form TEST_{R,P,M}* can be used to define test cases + * which take combinations of the three basic types of arguments. E.g. + * + * TEST_R One register argument + * TEST_RR Two register arguments + * TEST_RPR A register, a pointer, then a register argument + * + * For testing instructions which may branch, there are macros TEST_BF_* + * and TEST_BB_* for branching forwards and backwards. + * + * TEST_SUPPORTED and TEST_UNSUPPORTED don't cause the code to be executed, + * the just verify that a kprobe is or is not allowed on the given instruction. + */ + +#define TEST(code) \ + TESTCASE_START(code) \ + TEST_ARG_END("") \ + TEST_INSTRUCTION(code) \ + TESTCASE_END + +#define TEST_UNSUPPORTED(code) \ + TESTCASE_START(code) \ + TEST_ARG_END("|"__stringify(ARG_FLAG_UNSUPPORTED)) \ + TEST_INSTRUCTION(code) \ + TESTCASE_END + +#define TEST_SUPPORTED(code) \ + TESTCASE_START(code) \ + TEST_ARG_END("|"__stringify(ARG_FLAG_SUPPORTED)) \ + TEST_INSTRUCTION(code) \ + TESTCASE_END + +#define TEST_R(code1, reg, val, code2) \ + TESTCASE_START(code1 #reg code2) \ + TEST_ARG_REG(reg, val) \ + TEST_ARG_END("") \ + TEST_INSTRUCTION(code1 #reg code2) \ + TESTCASE_END + +#define TEST_RR(code1, reg1, val1, code2, reg2, val2, code3) \ + TESTCASE_START(code1 #reg1 code2 #reg2 code3) \ + TEST_ARG_REG(reg1, val1) \ + TEST_ARG_REG(reg2, val2) \ + TEST_ARG_END("") \ + TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3) \ + TESTCASE_END + +#define TEST_RRR(code1, reg1, val1, code2, reg2, val2, code3, reg3, val3, code4)\ + TESTCASE_START(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ + TEST_ARG_REG(reg1, val1) \ + TEST_ARG_REG(reg2, val2) \ + TEST_ARG_REG(reg3, val3) \ + TEST_ARG_END("") \ + TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ + TESTCASE_END + +#define TEST_RRRR(code1, reg1, val1, code2, reg2, val2, code3, reg3, val3, code4, reg4, val4) \ + TESTCASE_START(code1 #reg1 code2 #reg2 code3 #reg3 code4 #reg4) \ + TEST_ARG_REG(reg1, val1) \ + TEST_ARG_REG(reg2, val2) \ + TEST_ARG_REG(reg3, val3) \ + TEST_ARG_REG(reg4, val4) \ + TEST_ARG_END("") \ + TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3 #reg3 code4 #reg4) \ + TESTCASE_END + +#define TEST_P(code1, reg1, val1, code2) \ + TESTCASE_START(code1 #reg1 code2) \ + TEST_ARG_PTR(reg1, val1) \ + TEST_ARG_END("") \ + TEST_INSTRUCTION(code1 #reg1 code2) \ + TESTCASE_END + +#define TEST_PR(code1, reg1, val1, code2, reg2, val2, code3) \ + TESTCASE_START(code1 #reg1 code2 #reg2 code3) \ + TEST_ARG_PTR(reg1, val1) \ + TEST_ARG_REG(reg2, val2) \ + TEST_ARG_END("") \ + TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3) \ + TESTCASE_END + +#define TEST_RP(code1, reg1, val1, code2, reg2, val2, code3) \ + TESTCASE_START(code1 #reg1 code2 #reg2 code3) \ + TEST_ARG_REG(reg1, val1) \ + TEST_ARG_PTR(reg2, val2) \ + TEST_ARG_END("") \ + TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3) \ + TESTCASE_END + +#define TEST_PRR(code1, reg1, val1, code2, reg2, val2, code3, reg3, val3, code4)\ + TESTCASE_START(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ + TEST_ARG_PTR(reg1, val1) \ + TEST_ARG_REG(reg2, val2) \ + TEST_ARG_REG(reg3, val3) \ + TEST_ARG_END("") \ + TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ + TESTCASE_END + +#define TEST_RPR(code1, reg1, val1, code2, reg2, val2, code3, reg3, val3, code4)\ + TESTCASE_START(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ + TEST_ARG_REG(reg1, val1) \ + TEST_ARG_PTR(reg2, val2) \ + TEST_ARG_REG(reg3, val3) \ + TEST_ARG_END("") \ + TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ + TESTCASE_END + +#define TEST_RRP(code1, reg1, val1, code2, reg2, val2, code3, reg3, val3, code4)\ + TESTCASE_START(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ + TEST_ARG_REG(reg1, val1) \ + TEST_ARG_REG(reg2, val2) \ + TEST_ARG_PTR(reg3, val3) \ + TEST_ARG_END("") \ + TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3 #reg3 code4) \ + TESTCASE_END + +#define TEST_BF_P(code1, reg1, val1, code2) \ + TESTCASE_START(code1 #reg1 code2) \ + TEST_ARG_PTR(reg1, val1) \ + TEST_ARG_END("") \ + TEST_BRANCH_F(code1 #reg1 code2) \ + TESTCASE_END + +#define TEST_BF(code) \ + TESTCASE_START(code) \ + TEST_ARG_END("") \ + TEST_BRANCH_F(code) \ + TESTCASE_END + +#define TEST_BB(code) \ + TESTCASE_START(code) \ + TEST_ARG_END("") \ + TEST_BRANCH_B(code) \ + TESTCASE_END + +#define TEST_BF_R(code1, reg, val, code2) \ + TESTCASE_START(code1 #reg code2) \ + TEST_ARG_REG(reg, val) \ + TEST_ARG_END("") \ + TEST_BRANCH_F(code1 #reg code2) \ + TESTCASE_END + +#define TEST_BB_R(code1, reg, val, code2) \ + TESTCASE_START(code1 #reg code2) \ + TEST_ARG_REG(reg, val) \ + TEST_ARG_END("") \ + TEST_BRANCH_B(code1 #reg code2) \ + TESTCASE_END + +#define TEST_BF_RR(code1, reg1, val1, code2, reg2, val2, code3) \ + TESTCASE_START(code1 #reg1 code2 #reg2 code3) \ + TEST_ARG_REG(reg1, val1) \ + TEST_ARG_REG(reg2, val2) \ + TEST_ARG_END("") \ + TEST_BRANCH_F(code1 #reg1 code2 #reg2 code3) \ + TESTCASE_END + +#define TEST_BF_X(code, codex) \ + TESTCASE_START(code) \ + TEST_ARG_END("") \ + TEST_BRANCH_FX(code, codex) \ + TESTCASE_END + +#define TEST_BB_X(code, codex) \ + TESTCASE_START(code) \ + TEST_ARG_END("") \ + TEST_BRANCH_BX(code, codex) \ + TESTCASE_END + +#define TEST_BF_RX(code1, reg, val, code2, codex) \ + TESTCASE_START(code1 #reg code2) \ + TEST_ARG_REG(reg, val) \ + TEST_ARG_END("") \ + TEST_BRANCH_FX(code1 #reg code2, codex) \ + TESTCASE_END + +#define TEST_X(code, codex) \ + TESTCASE_START(code) \ + TEST_ARG_END("") \ + TEST_INSTRUCTION(code) \ + " b 99f \n\t" \ + " "codex" \n\t" \ + TESTCASE_END + +#define TEST_RX(code1, reg, val, code2, codex) \ + TESTCASE_START(code1 #reg code2) \ + TEST_ARG_REG(reg, val) \ + TEST_ARG_END("") \ + TEST_INSTRUCTION(code1 __stringify(reg) code2) \ + " b 99f \n\t" \ + " "codex" \n\t" \ + TESTCASE_END + +#define TEST_RRX(code1, reg1, val1, code2, reg2, val2, code3, codex) \ + TESTCASE_START(code1 #reg1 code2 #reg2 code3) \ + TEST_ARG_REG(reg1, val1) \ + TEST_ARG_REG(reg2, val2) \ + TEST_ARG_END("") \ + TEST_INSTRUCTION(code1 __stringify(reg1) code2 __stringify(reg2) code3) \ + " b 99f \n\t" \ + " "codex" \n\t" \ + TESTCASE_END + + +/* + * Macros for defining space directives spread over multiple lines. + * These are required so the compiler guesses better the length of inline asm + * code and will spill the literal pool early enough to avoid generating PC + * relative loads with out of range offsets. + */ +#define TWICE(x) x x +#define SPACE_0x8 TWICE(".space 4\n\t") +#define SPACE_0x10 TWICE(SPACE_0x8) +#define SPACE_0x20 TWICE(SPACE_0x10) +#define SPACE_0x40 TWICE(SPACE_0x20) +#define SPACE_0x80 TWICE(SPACE_0x40) +#define SPACE_0x100 TWICE(SPACE_0x80) +#define SPACE_0x200 TWICE(SPACE_0x100) +#define SPACE_0x400 TWICE(SPACE_0x200) +#define SPACE_0x800 TWICE(SPACE_0x400) +#define SPACE_0x1000 TWICE(SPACE_0x800) + + +/* Various values used in test cases... */ +#define N(val) (val ^ 0xffffffff) +#define VAL1 0x12345678 +#define VAL2 N(VAL1) +#define VAL3 0xa5f801 +#define VAL4 N(VAL3) +#define VALM 0x456789ab +#define VALR 0xdeaddead +#define HH1 0x0123fecb +#define HH2 0xa9874567 + + +#ifdef CONFIG_THUMB2_KERNEL +void kprobe_thumb16_test_cases(void); +void kprobe_thumb32_test_cases(void); +#else +void kprobe_arm_test_cases(void); +#endif diff --git a/arch/arm/probes/kprobes/test-thumb.c b/arch/arm/probes/kprobes/test-thumb.c new file mode 100644 index 0000000..6c6e9a9 --- /dev/null +++ b/arch/arm/probes/kprobes/test-thumb.c @@ -0,0 +1,1188 @@ +/* + * arch/arm/probes/kprobes/test-thumb.c + * + * Copyright (C) 2011 Jon Medhurst . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include "test-core.h" + + +#define TEST_ISA "16" + +#define DONT_TEST_IN_ITBLOCK(tests) \ + kprobe_test_flags |= TEST_FLAG_NO_ITBLOCK; \ + tests \ + kprobe_test_flags &= ~TEST_FLAG_NO_ITBLOCK; + +#define CONDITION_INSTRUCTIONS(cc_pos, tests) \ + kprobe_test_cc_position = cc_pos; \ + DONT_TEST_IN_ITBLOCK(tests) \ + kprobe_test_cc_position = 0; + +#define TEST_ITBLOCK(code) \ + kprobe_test_flags |= TEST_FLAG_FULL_ITBLOCK; \ + TESTCASE_START(code) \ + TEST_ARG_END("") \ + "50: nop \n\t" \ + "1: "code" \n\t" \ + " mov r1, #0x11 \n\t" \ + " mov r2, #0x22 \n\t" \ + " mov r3, #0x33 \n\t" \ + "2: nop \n\t" \ + TESTCASE_END \ + kprobe_test_flags &= ~TEST_FLAG_FULL_ITBLOCK; + +#define TEST_THUMB_TO_ARM_INTERWORK_P(code1, reg, val, code2) \ + TESTCASE_START(code1 #reg code2) \ + TEST_ARG_PTR(reg, val) \ + TEST_ARG_REG(14, 99f+1) \ + TEST_ARG_MEM(15, 3f) \ + TEST_ARG_END("") \ + " nop \n\t" /* To align 1f */ \ + "50: nop \n\t" \ + "1: "code1 #reg code2" \n\t" \ + " bx lr \n\t" \ + ".arm \n\t" \ + "3: adr lr, 2f+1 \n\t" \ + " bx lr \n\t" \ + ".thumb \n\t" \ + "2: nop \n\t" \ + TESTCASE_END + + +void kprobe_thumb16_test_cases(void) +{ + kprobe_test_flags = TEST_FLAG_NARROW_INSTR; + + TEST_GROUP("Shift (immediate), add, subtract, move, and compare") + + TEST_R( "lsls r7, r",0,VAL1,", #5") + TEST_R( "lsls r0, r",7,VAL2,", #11") + TEST_R( "lsrs r7, r",0,VAL1,", #5") + TEST_R( "lsrs r0, r",7,VAL2,", #11") + TEST_R( "asrs r7, r",0,VAL1,", #5") + TEST_R( "asrs r0, r",7,VAL2,", #11") + TEST_RR( "adds r2, r",0,VAL1,", r",7,VAL2,"") + TEST_RR( "adds r5, r",7,VAL2,", r",0,VAL2,"") + TEST_RR( "subs r2, r",0,VAL1,", r",7,VAL2,"") + TEST_RR( "subs r5, r",7,VAL2,", r",0,VAL2,"") + TEST_R( "adds r7, r",0,VAL1,", #5") + TEST_R( "adds r0, r",7,VAL2,", #2") + TEST_R( "subs r7, r",0,VAL1,", #5") + TEST_R( "subs r0, r",7,VAL2,", #2") + TEST( "movs.n r0, #0x5f") + TEST( "movs.n r7, #0xa0") + TEST_R( "cmp.n r",0,0x5e, ", #0x5f") + TEST_R( "cmp.n r",5,0x15f,", #0x5f") + TEST_R( "cmp.n r",7,0xa0, ", #0xa0") + TEST_R( "adds.n r",0,VAL1,", #0x5f") + TEST_R( "adds.n r",7,VAL2,", #0xa0") + TEST_R( "subs.n r",0,VAL1,", #0x5f") + TEST_R( "subs.n r",7,VAL2,", #0xa0") + + TEST_GROUP("16-bit Thumb data-processing instructions") + +#define DATA_PROCESSING16(op,val) \ + TEST_RR( op" r",0,VAL1,", r",7,val,"") \ + TEST_RR( op" r",7,VAL2,", r",0,val,"") + + DATA_PROCESSING16("ands",0xf00f00ff) + DATA_PROCESSING16("eors",0xf00f00ff) + DATA_PROCESSING16("lsls",11) + DATA_PROCESSING16("lsrs",11) + DATA_PROCESSING16("asrs",11) + DATA_PROCESSING16("adcs",VAL2) + DATA_PROCESSING16("sbcs",VAL2) + DATA_PROCESSING16("rors",11) + DATA_PROCESSING16("tst",0xf00f00ff) + TEST_R("rsbs r",0,VAL1,", #0") + TEST_R("rsbs r",7,VAL2,", #0") + DATA_PROCESSING16("cmp",0xf00f00ff) + DATA_PROCESSING16("cmn",0xf00f00ff) + DATA_PROCESSING16("orrs",0xf00f00ff) + DATA_PROCESSING16("muls",VAL2) + DATA_PROCESSING16("bics",0xf00f00ff) + DATA_PROCESSING16("mvns",VAL2) + + TEST_GROUP("Special data instructions and branch and exchange") + + TEST_RR( "add r",0, VAL1,", r",7,VAL2,"") + TEST_RR( "add r",3, VAL2,", r",8,VAL3,"") + TEST_RR( "add r",8, VAL3,", r",0,VAL1,"") + TEST_R( "add sp" ", r",8,-8, "") + TEST_R( "add r",14,VAL1,", pc") + TEST_BF_R("add pc" ", r",0,2f-1f-8,"") + TEST_UNSUPPORTED(__inst_thumb16(0x44ff) " @ add pc, pc") + + TEST_RR( "cmp r",3,VAL1,", r",8,VAL2,"") + TEST_RR( "cmp r",8,VAL2,", r",0,VAL1,"") + TEST_R( "cmp sp" ", r",8,-8, "") + + TEST_R( "mov r0, r",7,VAL2,"") + TEST_R( "mov r3, r",8,VAL3,"") + TEST_R( "mov r8, r",0,VAL1,"") + TEST_P( "mov sp, r",8,-8, "") + TEST( "mov lr, pc") + TEST_BF_R("mov pc, r",0,2f, "") + + TEST_BF_R("bx r",0, 2f+1,"") + TEST_BF_R("bx r",14,2f+1,"") + TESTCASE_START("bx pc") + TEST_ARG_REG(14, 99f+1) + TEST_ARG_END("") + " nop \n\t" /* To align the bx pc*/ + "50: nop \n\t" + "1: bx pc \n\t" + " bx lr \n\t" + ".arm \n\t" + " adr lr, 2f+1 \n\t" + " bx lr \n\t" + ".thumb \n\t" + "2: nop \n\t" + TESTCASE_END + + TEST_BF_R("blx r",0, 2f+1,"") + TEST_BB_R("blx r",14,2f+1,"") + TEST_UNSUPPORTED(__inst_thumb16(0x47f8) " @ blx pc") + + TEST_GROUP("Load from Literal Pool") + + TEST_X( "ldr r0, 3f", + ".align \n\t" + "3: .word "__stringify(VAL1)) + TEST_X( "ldr r7, 3f", + ".space 128 \n\t" + ".align \n\t" + "3: .word "__stringify(VAL2)) + + TEST_GROUP("16-bit Thumb Load/store instructions") + + TEST_RPR("str r",0, VAL1,", [r",1, 24,", r",2, 48,"]") + TEST_RPR("str r",7, VAL2,", [r",6, 24,", r",5, 48,"]") + TEST_RPR("strh r",0, VAL1,", [r",1, 24,", r",2, 48,"]") + TEST_RPR("strh r",7, VAL2,", [r",6, 24,", r",5, 48,"]") + TEST_RPR("strb r",0, VAL1,", [r",1, 24,", r",2, 48,"]") + TEST_RPR("strb r",7, VAL2,", [r",6, 24,", r",5, 48,"]") + TEST_PR( "ldrsb r0, [r",1, 24,", r",2, 48,"]") + TEST_PR( "ldrsb r7, [r",6, 24,", r",5, 50,"]") + TEST_PR( "ldr r0, [r",1, 24,", r",2, 48,"]") + TEST_PR( "ldr r7, [r",6, 24,", r",5, 48,"]") + TEST_PR( "ldrh r0, [r",1, 24,", r",2, 48,"]") + TEST_PR( "ldrh r7, [r",6, 24,", r",5, 50,"]") + TEST_PR( "ldrb r0, [r",1, 24,", r",2, 48,"]") + TEST_PR( "ldrb r7, [r",6, 24,", r",5, 50,"]") + TEST_PR( "ldrsh r0, [r",1, 24,", r",2, 48,"]") + TEST_PR( "ldrsh r7, [r",6, 24,", r",5, 50,"]") + + TEST_RP("str r",0, VAL1,", [r",1, 24,", #120]") + TEST_RP("str r",7, VAL2,", [r",6, 24,", #120]") + TEST_P( "ldr r0, [r",1, 24,", #120]") + TEST_P( "ldr r7, [r",6, 24,", #120]") + TEST_RP("strb r",0, VAL1,", [r",1, 24,", #30]") + TEST_RP("strb r",7, VAL2,", [r",6, 24,", #30]") + TEST_P( "ldrb r0, [r",1, 24,", #30]") + TEST_P( "ldrb r7, [r",6, 24,", #30]") + TEST_RP("strh r",0, VAL1,", [r",1, 24,", #60]") + TEST_RP("strh r",7, VAL2,", [r",6, 24,", #60]") + TEST_P( "ldrh r0, [r",1, 24,", #60]") + TEST_P( "ldrh r7, [r",6, 24,", #60]") + + TEST_R( "str r",0, VAL1,", [sp, #0]") + TEST_R( "str r",7, VAL2,", [sp, #160]") + TEST( "ldr r0, [sp, #0]") + TEST( "ldr r7, [sp, #160]") + + TEST_RP("str r",0, VAL1,", [r",0, 24,"]") + TEST_P( "ldr r0, [r",0, 24,"]") + + TEST_GROUP("Generate PC-/SP-relative address") + + TEST("add r0, pc, #4") + TEST("add r7, pc, #1020") + TEST("add r0, sp, #4") + TEST("add r7, sp, #1020") + + TEST_GROUP("Miscellaneous 16-bit instructions") + + TEST_UNSUPPORTED( "cpsie i") + TEST_UNSUPPORTED( "cpsid i") + TEST_UNSUPPORTED( "setend le") + TEST_UNSUPPORTED( "setend be") + + TEST("add sp, #"__stringify(TEST_MEMORY_SIZE)) /* Assumes TEST_MEMORY_SIZE < 0x400 */ + TEST("sub sp, #0x7f*4") + +DONT_TEST_IN_ITBLOCK( + TEST_BF_R( "cbnz r",0,0, ", 2f") + TEST_BF_R( "cbz r",2,-1,", 2f") + TEST_BF_RX( "cbnz r",4,1, ", 2f", SPACE_0x20) + TEST_BF_RX( "cbz r",7,0, ", 2f", SPACE_0x40) +) + TEST_R("sxth r0, r",7, HH1,"") + TEST_R("sxth r7, r",0, HH2,"") + TEST_R("sxtb r0, r",7, HH1,"") + TEST_R("sxtb r7, r",0, HH2,"") + TEST_R("uxth r0, r",7, HH1,"") + TEST_R("uxth r7, r",0, HH2,"") + TEST_R("uxtb r0, r",7, HH1,"") + TEST_R("uxtb r7, r",0, HH2,"") + TEST_R("rev r0, r",7, VAL1,"") + TEST_R("rev r7, r",0, VAL2,"") + TEST_R("rev16 r0, r",7, VAL1,"") + TEST_R("rev16 r7, r",0, VAL2,"") + TEST_UNSUPPORTED(__inst_thumb16(0xba80) "") + TEST_UNSUPPORTED(__inst_thumb16(0xbabf) "") + TEST_R("revsh r0, r",7, VAL1,"") + TEST_R("revsh r7, r",0, VAL2,"") + +#define TEST_POPPC(code, offset) \ + TESTCASE_START(code) \ + TEST_ARG_PTR(13, offset) \ + TEST_ARG_END("") \ + TEST_BRANCH_F(code) \ + TESTCASE_END + + TEST("push {r0}") + TEST("push {r7}") + TEST("push {r14}") + TEST("push {r0-r7,r14}") + TEST("push {r0,r2,r4,r6,r14}") + TEST("push {r1,r3,r5,r7}") + TEST("pop {r0}") + TEST("pop {r7}") + TEST("pop {r0,r2,r4,r6}") + TEST_POPPC("pop {pc}",15*4) + TEST_POPPC("pop {r0-r7,pc}",7*4) + TEST_POPPC("pop {r1,r3,r5,r7,pc}",11*4) + TEST_THUMB_TO_ARM_INTERWORK_P("pop {pc} @ ",13,15*4,"") + TEST_THUMB_TO_ARM_INTERWORK_P("pop {r0-r7,pc} @ ",13,7*4,"") + + TEST_UNSUPPORTED("bkpt.n 0") + TEST_UNSUPPORTED("bkpt.n 255") + + TEST_SUPPORTED("yield") + TEST("sev") + TEST("nop") + TEST("wfi") + TEST_SUPPORTED("wfe") + TEST_UNSUPPORTED(__inst_thumb16(0xbf50) "") /* Unassigned hints */ + TEST_UNSUPPORTED(__inst_thumb16(0xbff0) "") /* Unassigned hints */ + +#define TEST_IT(code, code2) \ + TESTCASE_START(code) \ + TEST_ARG_END("") \ + "50: nop \n\t" \ + "1: "code" \n\t" \ + " "code2" \n\t" \ + "2: nop \n\t" \ + TESTCASE_END + +DONT_TEST_IN_ITBLOCK( + TEST_IT("it eq","moveq r0,#0") + TEST_IT("it vc","movvc r0,#0") + TEST_IT("it le","movle r0,#0") + TEST_IT("ite eq","moveq r0,#0\n\t movne r1,#1") + TEST_IT("itet vc","movvc r0,#0\n\t movvs r1,#1\n\t movvc r2,#2") + TEST_IT("itete le","movle r0,#0\n\t movgt r1,#1\n\t movle r2,#2\n\t movgt r3,#3") + TEST_IT("itttt le","movle r0,#0\n\t movle r1,#1\n\t movle r2,#2\n\t movle r3,#3") + TEST_IT("iteee le","movle r0,#0\n\t movgt r1,#1\n\t movgt r2,#2\n\t movgt r3,#3") +) + + TEST_GROUP("Load and store multiple") + + TEST_P("ldmia r",4, 16*4,"!, {r0,r7}") + TEST_P("ldmia r",7, 16*4,"!, {r0-r6}") + TEST_P("stmia r",4, 16*4,"!, {r0,r7}") + TEST_P("stmia r",0, 16*4,"!, {r0-r7}") + + TEST_GROUP("Conditional branch and Supervisor Call instructions") + +CONDITION_INSTRUCTIONS(8, + TEST_BF("beq 2f") + TEST_BB("bne 2b") + TEST_BF("bgt 2f") + TEST_BB("blt 2b") +) + TEST_UNSUPPORTED(__inst_thumb16(0xde00) "") + TEST_UNSUPPORTED(__inst_thumb16(0xdeff) "") + TEST_UNSUPPORTED("svc #0x00") + TEST_UNSUPPORTED("svc #0xff") + + TEST_GROUP("Unconditional branch") + + TEST_BF( "b 2f") + TEST_BB( "b 2b") + TEST_BF_X("b 2f", SPACE_0x400) + TEST_BB_X("b 2b", SPACE_0x400) + + TEST_GROUP("Testing instructions in IT blocks") + + TEST_ITBLOCK("subs.n r0, r0") + + verbose("\n"); +} + + +void kprobe_thumb32_test_cases(void) +{ + kprobe_test_flags = 0; + + TEST_GROUP("Load/store multiple") + + TEST_UNSUPPORTED("rfedb sp") + TEST_UNSUPPORTED("rfeia sp") + TEST_UNSUPPORTED("rfedb sp!") + TEST_UNSUPPORTED("rfeia sp!") + + TEST_P( "stmia r",0, 16*4,", {r0,r8}") + TEST_P( "stmia r",4, 16*4,", {r0-r12,r14}") + TEST_P( "stmia r",7, 16*4,"!, {r8-r12,r14}") + TEST_P( "stmia r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") + + TEST_P( "ldmia r",0, 16*4,", {r0,r8}") + TEST_P( "ldmia r",4, 0, ", {r0-r12,r14}") + TEST_BF_P("ldmia r",5, 8*4, "!, {r6-r12,r15}") + TEST_P( "ldmia r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") + TEST_BF_P("ldmia r",14,14*4,"!, {r4,pc}") + + TEST_P( "stmdb r",0, 16*4,", {r0,r8}") + TEST_P( "stmdb r",4, 16*4,", {r0-r12,r14}") + TEST_P( "stmdb r",5, 16*4,"!, {r8-r12,r14}") + TEST_P( "stmdb r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") + + TEST_P( "ldmdb r",0, 16*4,", {r0,r8}") + TEST_P( "ldmdb r",4, 16*4,", {r0-r12,r14}") + TEST_BF_P("ldmdb r",5, 16*4,"!, {r6-r12,r15}") + TEST_P( "ldmdb r",12,16*4,"!, {r1,r3,r5,r7,r8-r11,r14}") + TEST_BF_P("ldmdb r",14,16*4,"!, {r4,pc}") + + TEST_P( "stmdb r",13,16*4,"!, {r3-r12,lr}") + TEST_P( "stmdb r",13,16*4,"!, {r3-r12}") + TEST_P( "stmdb r",2, 16*4,", {r3-r12,lr}") + TEST_P( "stmdb r",13,16*4,"!, {r2-r12,lr}") + TEST_P( "stmdb r",0, 16*4,", {r0-r12}") + TEST_P( "stmdb r",0, 16*4,", {r0-r12,lr}") + + TEST_BF_P("ldmia r",13,5*4, "!, {r3-r12,pc}") + TEST_P( "ldmia r",13,5*4, "!, {r3-r12}") + TEST_BF_P("ldmia r",2, 5*4, "!, {r3-r12,pc}") + TEST_BF_P("ldmia r",13,4*4, "!, {r2-r12,pc}") + TEST_P( "ldmia r",0, 16*4,", {r0-r12}") + TEST_P( "ldmia r",0, 16*4,", {r0-r12,lr}") + + TEST_THUMB_TO_ARM_INTERWORK_P("ldmia r",0,14*4,", {r12,pc}") + TEST_THUMB_TO_ARM_INTERWORK_P("ldmia r",13,2*4,", {r0-r12,pc}") + + TEST_UNSUPPORTED(__inst_thumb32(0xe88f0101) " @ stmia pc, {r0,r8}") + TEST_UNSUPPORTED(__inst_thumb32(0xe92f5f00) " @ stmdb pc!, {r8-r12,r14}") + TEST_UNSUPPORTED(__inst_thumb32(0xe8bdc000) " @ ldmia r13!, {r14,pc}") + TEST_UNSUPPORTED(__inst_thumb32(0xe93ec000) " @ ldmdb r14!, {r14,pc}") + TEST_UNSUPPORTED(__inst_thumb32(0xe8a73f00) " @ stmia r7!, {r8-r12,sp}") + TEST_UNSUPPORTED(__inst_thumb32(0xe8a79f00) " @ stmia r7!, {r8-r12,pc}") + TEST_UNSUPPORTED(__inst_thumb32(0xe93e2010) " @ ldmdb r14!, {r4,sp}") + + TEST_GROUP("Load/store double or exclusive, table branch") + + TEST_P( "ldrd r0, r1, [r",1, 24,", #-16]") + TEST( "ldrd r12, r14, [sp, #16]") + TEST_P( "ldrd r1, r0, [r",7, 24,", #-16]!") + TEST( "ldrd r14, r12, [sp, #16]!") + TEST_P( "ldrd r1, r0, [r",7, 24,"], #16") + TEST( "ldrd r7, r8, [sp], #-16") + + TEST_X( "ldrd r12, r14, 3f", + ".align 3 \n\t" + "3: .word "__stringify(VAL1)" \n\t" + " .word "__stringify(VAL2)) + + TEST_UNSUPPORTED(__inst_thumb32(0xe9ffec04) " @ ldrd r14, r12, [pc, #16]!") + TEST_UNSUPPORTED(__inst_thumb32(0xe8ffec04) " @ ldrd r14, r12, [pc], #16") + TEST_UNSUPPORTED(__inst_thumb32(0xe9d4d800) " @ ldrd sp, r8, [r4]") + TEST_UNSUPPORTED(__inst_thumb32(0xe9d4f800) " @ ldrd pc, r8, [r4]") + TEST_UNSUPPORTED(__inst_thumb32(0xe9d47d00) " @ ldrd r7, sp, [r4]") + TEST_UNSUPPORTED(__inst_thumb32(0xe9d47f00) " @ ldrd r7, pc, [r4]") + + TEST_RRP("strd r",0, VAL1,", r",1, VAL2,", [r",1, 24,", #-16]") + TEST_RR( "strd r",12,VAL2,", r",14,VAL1,", [sp, #16]") + TEST_RRP("strd r",1, VAL1,", r",0, VAL2,", [r",7, 24,", #-16]!") + TEST_RR( "strd r",14,VAL2,", r",12,VAL1,", [sp, #16]!") + TEST_RRP("strd r",1, VAL1,", r",0, VAL2,", [r",7, 24,"], #16") + TEST_RR( "strd r",7, VAL2,", r",8, VAL1,", [sp], #-16") + TEST_UNSUPPORTED(__inst_thumb32(0xe9efec04) " @ strd r14, r12, [pc, #16]!") + TEST_UNSUPPORTED(__inst_thumb32(0xe8efec04) " @ strd r14, r12, [pc], #16") + + TEST_RX("tbb [pc, r",0, (9f-(1f+4)),"]", + "9: \n\t" + ".byte (2f-1b-4)>>1 \n\t" + ".byte (3f-1b-4)>>1 \n\t" + "3: mvn r0, r0 \n\t" + "2: nop \n\t") + + TEST_RX("tbb [pc, r",4, (9f-(1f+4)+1),"]", + "9: \n\t" + ".byte (2f-1b-4)>>1 \n\t" + ".byte (3f-1b-4)>>1 \n\t" + "3: mvn r0, r0 \n\t" + "2: nop \n\t") + + TEST_RRX("tbb [r",1,9f,", r",2,0,"]", + "9: \n\t" + ".byte (2f-1b-4)>>1 \n\t" + ".byte (3f-1b-4)>>1 \n\t" + "3: mvn r0, r0 \n\t" + "2: nop \n\t") + + TEST_RX("tbh [pc, r",7, (9f-(1f+4))>>1,"]", + "9: \n\t" + ".short (2f-1b-4)>>1 \n\t" + ".short (3f-1b-4)>>1 \n\t" + "3: mvn r0, r0 \n\t" + "2: nop \n\t") + + TEST_RX("tbh [pc, r",12, ((9f-(1f+4))>>1)+1,"]", + "9: \n\t" + ".short (2f-1b-4)>>1 \n\t" + ".short (3f-1b-4)>>1 \n\t" + "3: mvn r0, r0 \n\t" + "2: nop \n\t") + + TEST_RRX("tbh [r",1,9f, ", r",14,1,"]", + "9: \n\t" + ".short (2f-1b-4)>>1 \n\t" + ".short (3f-1b-4)>>1 \n\t" + "3: mvn r0, r0 \n\t" + "2: nop \n\t") + + TEST_UNSUPPORTED(__inst_thumb32(0xe8d1f01f) " @ tbh [r1, pc]") + TEST_UNSUPPORTED(__inst_thumb32(0xe8d1f01d) " @ tbh [r1, sp]") + TEST_UNSUPPORTED(__inst_thumb32(0xe8ddf012) " @ tbh [sp, r2]") + + TEST_UNSUPPORTED("strexb r0, r1, [r2]") + TEST_UNSUPPORTED("strexh r0, r1, [r2]") + TEST_UNSUPPORTED("strexd r0, r1, [r2]") + TEST_UNSUPPORTED("ldrexb r0, [r1]") + TEST_UNSUPPORTED("ldrexh r0, [r1]") + TEST_UNSUPPORTED("ldrexd r0, [r1]") + + TEST_GROUP("Data-processing (shifted register) and (modified immediate)") + +#define _DATA_PROCESSING32_DNM(op,s,val) \ + TEST_RR(op s".w r0, r",1, VAL1,", r",2, val, "") \ + TEST_RR(op s" r1, r",1, VAL1,", r",2, val, ", lsl #3") \ + TEST_RR(op s" r2, r",3, VAL1,", r",2, val, ", lsr #4") \ + TEST_RR(op s" r3, r",3, VAL1,", r",2, val, ", asr #5") \ + TEST_RR(op s" r4, r",5, VAL1,", r",2, N(val),", asr #6") \ + TEST_RR(op s" r5, r",5, VAL1,", r",2, val, ", ror #7") \ + TEST_RR(op s" r8, r",9, VAL1,", r",10,val, ", rrx") \ + TEST_R( op s" r0, r",11,VAL1,", #0x00010001") \ + TEST_R( op s" r11, r",0, VAL1,", #0xf5000000") \ + TEST_R( op s" r7, r",8, VAL2,", #0x000af000") + +#define DATA_PROCESSING32_DNM(op,val) \ + _DATA_PROCESSING32_DNM(op,"",val) \ + _DATA_PROCESSING32_DNM(op,"s",val) + +#define DATA_PROCESSING32_NM(op,val) \ + TEST_RR(op".w r",1, VAL1,", r",2, val, "") \ + TEST_RR(op" r",1, VAL1,", r",2, val, ", lsl #3") \ + TEST_RR(op" r",3, VAL1,", r",2, val, ", lsr #4") \ + TEST_RR(op" r",3, VAL1,", r",2, val, ", asr #5") \ + TEST_RR(op" r",5, VAL1,", r",2, N(val),", asr #6") \ + TEST_RR(op" r",5, VAL1,", r",2, val, ", ror #7") \ + TEST_RR(op" r",9, VAL1,", r",10,val, ", rrx") \ + TEST_R( op" r",11,VAL1,", #0x00010001") \ + TEST_R( op" r",0, VAL1,", #0xf5000000") \ + TEST_R( op" r",8, VAL2,", #0x000af000") + +#define _DATA_PROCESSING32_DM(op,s,val) \ + TEST_R( op s".w r0, r",14, val, "") \ + TEST_R( op s" r1, r",12, val, ", lsl #3") \ + TEST_R( op s" r2, r",11, val, ", lsr #4") \ + TEST_R( op s" r3, r",10, val, ", asr #5") \ + TEST_R( op s" r4, r",9, N(val),", asr #6") \ + TEST_R( op s" r5, r",8, val, ", ror #7") \ + TEST_R( op s" r8, r",7,val, ", rrx") \ + TEST( op s" r0, #0x00010001") \ + TEST( op s" r11, #0xf5000000") \ + TEST( op s" r7, #0x000af000") \ + TEST( op s" r4, #0x00005a00") + +#define DATA_PROCESSING32_DM(op,val) \ + _DATA_PROCESSING32_DM(op,"",val) \ + _DATA_PROCESSING32_DM(op,"s",val) + + DATA_PROCESSING32_DNM("and",0xf00f00ff) + DATA_PROCESSING32_NM("tst",0xf00f00ff) + DATA_PROCESSING32_DNM("bic",0xf00f00ff) + DATA_PROCESSING32_DNM("orr",0xf00f00ff) + DATA_PROCESSING32_DM("mov",VAL2) + DATA_PROCESSING32_DNM("orn",0xf00f00ff) + DATA_PROCESSING32_DM("mvn",VAL2) + DATA_PROCESSING32_DNM("eor",0xf00f00ff) + DATA_PROCESSING32_NM("teq",0xf00f00ff) + DATA_PROCESSING32_DNM("add",VAL2) + DATA_PROCESSING32_NM("cmn",VAL2) + DATA_PROCESSING32_DNM("adc",VAL2) + DATA_PROCESSING32_DNM("sbc",VAL2) + DATA_PROCESSING32_DNM("sub",VAL2) + DATA_PROCESSING32_NM("cmp",VAL2) + DATA_PROCESSING32_DNM("rsb",VAL2) + + TEST_RR("pkhbt r0, r",0, HH1,", r",1, HH2,"") + TEST_RR("pkhbt r14,r",12, HH1,", r",10,HH2,", lsl #2") + TEST_RR("pkhtb r0, r",0, HH1,", r",1, HH2,"") + TEST_RR("pkhtb r14,r",12, HH1,", r",10,HH2,", asr #2") + + TEST_UNSUPPORTED(__inst_thumb32(0xea170f0d) " @ tst.w r7, sp") + TEST_UNSUPPORTED(__inst_thumb32(0xea170f0f) " @ tst.w r7, pc") + TEST_UNSUPPORTED(__inst_thumb32(0xea1d0f07) " @ tst.w sp, r7") + TEST_UNSUPPORTED(__inst_thumb32(0xea1f0f07) " @ tst.w pc, r7") + TEST_UNSUPPORTED(__inst_thumb32(0xf01d1f08) " @ tst sp, #0x00080008") + TEST_UNSUPPORTED(__inst_thumb32(0xf01f1f08) " @ tst pc, #0x00080008") + + TEST_UNSUPPORTED(__inst_thumb32(0xea970f0d) " @ teq.w r7, sp") + TEST_UNSUPPORTED(__inst_thumb32(0xea970f0f) " @ teq.w r7, pc") + TEST_UNSUPPORTED(__inst_thumb32(0xea9d0f07) " @ teq.w sp, r7") + TEST_UNSUPPORTED(__inst_thumb32(0xea9f0f07) " @ teq.w pc, r7") + TEST_UNSUPPORTED(__inst_thumb32(0xf09d1f08) " @ tst sp, #0x00080008") + TEST_UNSUPPORTED(__inst_thumb32(0xf09f1f08) " @ tst pc, #0x00080008") + + TEST_UNSUPPORTED(__inst_thumb32(0xeb170f0d) " @ cmn.w r7, sp") + TEST_UNSUPPORTED(__inst_thumb32(0xeb170f0f) " @ cmn.w r7, pc") + TEST_P("cmn.w sp, r",7,0,"") + TEST_UNSUPPORTED(__inst_thumb32(0xeb1f0f07) " @ cmn.w pc, r7") + TEST( "cmn sp, #0x00080008") + TEST_UNSUPPORTED(__inst_thumb32(0xf11f1f08) " @ cmn pc, #0x00080008") + + TEST_UNSUPPORTED(__inst_thumb32(0xebb70f0d) " @ cmp.w r7, sp") + TEST_UNSUPPORTED(__inst_thumb32(0xebb70f0f) " @ cmp.w r7, pc") + TEST_P("cmp.w sp, r",7,0,"") + TEST_UNSUPPORTED(__inst_thumb32(0xebbf0f07) " @ cmp.w pc, r7") + TEST( "cmp sp, #0x00080008") + TEST_UNSUPPORTED(__inst_thumb32(0xf1bf1f08) " @ cmp pc, #0x00080008") + + TEST_UNSUPPORTED(__inst_thumb32(0xea5f070d) " @ movs.w r7, sp") + TEST_UNSUPPORTED(__inst_thumb32(0xea5f070f) " @ movs.w r7, pc") + TEST_UNSUPPORTED(__inst_thumb32(0xea5f0d07) " @ movs.w sp, r7") + TEST_UNSUPPORTED(__inst_thumb32(0xea4f0f07) " @ mov.w pc, r7") + TEST_UNSUPPORTED(__inst_thumb32(0xf04f1d08) " @ mov sp, #0x00080008") + TEST_UNSUPPORTED(__inst_thumb32(0xf04f1f08) " @ mov pc, #0x00080008") + + TEST_R("add.w r0, sp, r",1, 4,"") + TEST_R("adds r0, sp, r",1, 4,", asl #3") + TEST_R("add r0, sp, r",1, 4,", asl #4") + TEST_R("add r0, sp, r",1, 16,", ror #1") + TEST_R("add.w sp, sp, r",1, 4,"") + TEST_R("add sp, sp, r",1, 4,", asl #3") + TEST_UNSUPPORTED(__inst_thumb32(0xeb0d1d01) " @ add sp, sp, r1, asl #4") + TEST_UNSUPPORTED(__inst_thumb32(0xeb0d0d71) " @ add sp, sp, r1, ror #1") + TEST( "add.w r0, sp, #24") + TEST( "add.w sp, sp, #24") + TEST_UNSUPPORTED(__inst_thumb32(0xeb0d0f01) " @ add pc, sp, r1") + TEST_UNSUPPORTED(__inst_thumb32(0xeb0d000f) " @ add r0, sp, pc") + TEST_UNSUPPORTED(__inst_thumb32(0xeb0d000d) " @ add r0, sp, sp") + TEST_UNSUPPORTED(__inst_thumb32(0xeb0d0d0f) " @ add sp, sp, pc") + TEST_UNSUPPORTED(__inst_thumb32(0xeb0d0d0d) " @ add sp, sp, sp") + + TEST_R("sub.w r0, sp, r",1, 4,"") + TEST_R("subs r0, sp, r",1, 4,", asl #3") + TEST_R("sub r0, sp, r",1, 4,", asl #4") + TEST_R("sub r0, sp, r",1, 16,", ror #1") + TEST_R("sub.w sp, sp, r",1, 4,"") + TEST_R("sub sp, sp, r",1, 4,", asl #3") + TEST_UNSUPPORTED(__inst_thumb32(0xebad1d01) " @ sub sp, sp, r1, asl #4") + TEST_UNSUPPORTED(__inst_thumb32(0xebad0d71) " @ sub sp, sp, r1, ror #1") + TEST_UNSUPPORTED(__inst_thumb32(0xebad0f01) " @ sub pc, sp, r1") + TEST( "sub.w r0, sp, #24") + TEST( "sub.w sp, sp, #24") + + TEST_UNSUPPORTED(__inst_thumb32(0xea02010f) " @ and r1, r2, pc") + TEST_UNSUPPORTED(__inst_thumb32(0xea0f0103) " @ and r1, pc, r3") + TEST_UNSUPPORTED(__inst_thumb32(0xea020f03) " @ and pc, r2, r3") + TEST_UNSUPPORTED(__inst_thumb32(0xea02010d) " @ and r1, r2, sp") + TEST_UNSUPPORTED(__inst_thumb32(0xea0d0103) " @ and r1, sp, r3") + TEST_UNSUPPORTED(__inst_thumb32(0xea020d03) " @ and sp, r2, r3") + TEST_UNSUPPORTED(__inst_thumb32(0xf00d1108) " @ and r1, sp, #0x00080008") + TEST_UNSUPPORTED(__inst_thumb32(0xf00f1108) " @ and r1, pc, #0x00080008") + TEST_UNSUPPORTED(__inst_thumb32(0xf0021d08) " @ and sp, r8, #0x00080008") + TEST_UNSUPPORTED(__inst_thumb32(0xf0021f08) " @ and pc, r8, #0x00080008") + + TEST_UNSUPPORTED(__inst_thumb32(0xeb02010f) " @ add r1, r2, pc") + TEST_UNSUPPORTED(__inst_thumb32(0xeb0f0103) " @ add r1, pc, r3") + TEST_UNSUPPORTED(__inst_thumb32(0xeb020f03) " @ add pc, r2, r3") + TEST_UNSUPPORTED(__inst_thumb32(0xeb02010d) " @ add r1, r2, sp") + TEST_SUPPORTED( __inst_thumb32(0xeb0d0103) " @ add r1, sp, r3") + TEST_UNSUPPORTED(__inst_thumb32(0xeb020d03) " @ add sp, r2, r3") + TEST_SUPPORTED( __inst_thumb32(0xf10d1108) " @ add r1, sp, #0x00080008") + TEST_UNSUPPORTED(__inst_thumb32(0xf10d1f08) " @ add pc, sp, #0x00080008") + TEST_UNSUPPORTED(__inst_thumb32(0xf10f1108) " @ add r1, pc, #0x00080008") + TEST_UNSUPPORTED(__inst_thumb32(0xf1021d08) " @ add sp, r8, #0x00080008") + TEST_UNSUPPORTED(__inst_thumb32(0xf1021f08) " @ add pc, r8, #0x00080008") + + TEST_UNSUPPORTED(__inst_thumb32(0xeaa00000) "") + TEST_UNSUPPORTED(__inst_thumb32(0xeaf00000) "") + TEST_UNSUPPORTED(__inst_thumb32(0xeb200000) "") + TEST_UNSUPPORTED(__inst_thumb32(0xeb800000) "") + TEST_UNSUPPORTED(__inst_thumb32(0xebe00000) "") + + TEST_UNSUPPORTED(__inst_thumb32(0xf0a00000) "") + TEST_UNSUPPORTED(__inst_thumb32(0xf0c00000) "") + TEST_UNSUPPORTED(__inst_thumb32(0xf0f00000) "") + TEST_UNSUPPORTED(__inst_thumb32(0xf1200000) "") + TEST_UNSUPPORTED(__inst_thumb32(0xf1800000) "") + TEST_UNSUPPORTED(__inst_thumb32(0xf1e00000) "") + + TEST_GROUP("Coprocessor instructions") + + TEST_UNSUPPORTED(__inst_thumb32(0xec000000) "") + TEST_UNSUPPORTED(__inst_thumb32(0xeff00000) "") + TEST_UNSUPPORTED(__inst_thumb32(0xfc000000) "") + TEST_UNSUPPORTED(__inst_thumb32(0xfff00000) "") + + TEST_GROUP("Data-processing (plain binary immediate)") + + TEST_R("addw r0, r",1, VAL1,", #0x123") + TEST( "addw r14, sp, #0xf5a") + TEST( "addw sp, sp, #0x20") + TEST( "addw r7, pc, #0x888") + TEST_UNSUPPORTED(__inst_thumb32(0xf20f1f20) " @ addw pc, pc, #0x120") + TEST_UNSUPPORTED(__inst_thumb32(0xf20d1f20) " @ addw pc, sp, #0x120") + TEST_UNSUPPORTED(__inst_thumb32(0xf20f1d20) " @ addw sp, pc, #0x120") + TEST_UNSUPPORTED(__inst_thumb32(0xf2001d20) " @ addw sp, r0, #0x120") + + TEST_R("subw r0, r",1, VAL1,", #0x123") + TEST( "subw r14, sp, #0xf5a") + TEST( "subw sp, sp, #0x20") + TEST( "subw r7, pc, #0x888") + TEST_UNSUPPORTED(__inst_thumb32(0xf2af1f20) " @ subw pc, pc, #0x120") + TEST_UNSUPPORTED(__inst_thumb32(0xf2ad1f20) " @ subw pc, sp, #0x120") + TEST_UNSUPPORTED(__inst_thumb32(0xf2af1d20) " @ subw sp, pc, #0x120") + TEST_UNSUPPORTED(__inst_thumb32(0xf2a01d20) " @ subw sp, r0, #0x120") + + TEST("movw r0, #0") + TEST("movw r0, #0xffff") + TEST("movw lr, #0xffff") + TEST_UNSUPPORTED(__inst_thumb32(0xf2400d00) " @ movw sp, #0") + TEST_UNSUPPORTED(__inst_thumb32(0xf2400f00) " @ movw pc, #0") + + TEST_R("movt r",0, VAL1,", #0") + TEST_R("movt r",0, VAL2,", #0xffff") + TEST_R("movt r",14,VAL1,", #0xffff") + TEST_UNSUPPORTED(__inst_thumb32(0xf2c00d00) " @ movt sp, #0") + TEST_UNSUPPORTED(__inst_thumb32(0xf2c00f00) " @ movt pc, #0") + + TEST_R( "ssat r0, #24, r",0, VAL1,"") + TEST_R( "ssat r14, #24, r",12, VAL2,"") + TEST_R( "ssat r0, #24, r",0, VAL1,", lsl #8") + TEST_R( "ssat r14, #24, r",12, VAL2,", asr #8") + TEST_UNSUPPORTED(__inst_thumb32(0xf30c0d17) " @ ssat sp, #24, r12") + TEST_UNSUPPORTED(__inst_thumb32(0xf30c0f17) " @ ssat pc, #24, r12") + TEST_UNSUPPORTED(__inst_thumb32(0xf30d0c17) " @ ssat r12, #24, sp") + TEST_UNSUPPORTED(__inst_thumb32(0xf30f0c17) " @ ssat r12, #24, pc") + + TEST_R( "usat r0, #24, r",0, VAL1,"") + TEST_R( "usat r14, #24, r",12, VAL2,"") + TEST_R( "usat r0, #24, r",0, VAL1,", lsl #8") + TEST_R( "usat r14, #24, r",12, VAL2,", asr #8") + TEST_UNSUPPORTED(__inst_thumb32(0xf38c0d17) " @ usat sp, #24, r12") + TEST_UNSUPPORTED(__inst_thumb32(0xf38c0f17) " @ usat pc, #24, r12") + TEST_UNSUPPORTED(__inst_thumb32(0xf38d0c17) " @ usat r12, #24, sp") + TEST_UNSUPPORTED(__inst_thumb32(0xf38f0c17) " @ usat r12, #24, pc") + + TEST_R( "ssat16 r0, #12, r",0, HH1,"") + TEST_R( "ssat16 r14, #12, r",12, HH2,"") + TEST_UNSUPPORTED(__inst_thumb32(0xf32c0d0b) " @ ssat16 sp, #12, r12") + TEST_UNSUPPORTED(__inst_thumb32(0xf32c0f0b) " @ ssat16 pc, #12, r12") + TEST_UNSUPPORTED(__inst_thumb32(0xf32d0c0b) " @ ssat16 r12, #12, sp") + TEST_UNSUPPORTED(__inst_thumb32(0xf32f0c0b) " @ ssat16 r12, #12, pc") + + TEST_R( "usat16 r0, #12, r",0, HH1,"") + TEST_R( "usat16 r14, #12, r",12, HH2,"") + TEST_UNSUPPORTED(__inst_thumb32(0xf3ac0d0b) " @ usat16 sp, #12, r12") + TEST_UNSUPPORTED(__inst_thumb32(0xf3ac0f0b) " @ usat16 pc, #12, r12") + TEST_UNSUPPORTED(__inst_thumb32(0xf3ad0c0b) " @ usat16 r12, #12, sp") + TEST_UNSUPPORTED(__inst_thumb32(0xf3af0c0b) " @ usat16 r12, #12, pc") + + TEST_R( "sbfx r0, r",0 , VAL1,", #0, #31") + TEST_R( "sbfx r14, r",12, VAL2,", #8, #16") + TEST_R( "sbfx r4, r",10, VAL1,", #16, #15") + TEST_UNSUPPORTED(__inst_thumb32(0xf34c2d0f) " @ sbfx sp, r12, #8, #16") + TEST_UNSUPPORTED(__inst_thumb32(0xf34c2f0f) " @ sbfx pc, r12, #8, #16") + TEST_UNSUPPORTED(__inst_thumb32(0xf34d2c0f) " @ sbfx r12, sp, #8, #16") + TEST_UNSUPPORTED(__inst_thumb32(0xf34f2c0f) " @ sbfx r12, pc, #8, #16") + + TEST_R( "ubfx r0, r",0 , VAL1,", #0, #31") + TEST_R( "ubfx r14, r",12, VAL2,", #8, #16") + TEST_R( "ubfx r4, r",10, VAL1,", #16, #15") + TEST_UNSUPPORTED(__inst_thumb32(0xf3cc2d0f) " @ ubfx sp, r12, #8, #16") + TEST_UNSUPPORTED(__inst_thumb32(0xf3cc2f0f) " @ ubfx pc, r12, #8, #16") + TEST_UNSUPPORTED(__inst_thumb32(0xf3cd2c0f) " @ ubfx r12, sp, #8, #16") + TEST_UNSUPPORTED(__inst_thumb32(0xf3cf2c0f) " @ ubfx r12, pc, #8, #16") + + TEST_R( "bfc r",0, VAL1,", #4, #20") + TEST_R( "bfc r",14,VAL2,", #4, #20") + TEST_R( "bfc r",7, VAL1,", #0, #31") + TEST_R( "bfc r",8, VAL2,", #0, #31") + TEST_UNSUPPORTED(__inst_thumb32(0xf36f0d1e) " @ bfc sp, #0, #31") + TEST_UNSUPPORTED(__inst_thumb32(0xf36f0f1e) " @ bfc pc, #0, #31") + + TEST_RR( "bfi r",0, VAL1,", r",0 , VAL2,", #0, #31") + TEST_RR( "bfi r",12,VAL1,", r",14 , VAL2,", #4, #20") + TEST_UNSUPPORTED(__inst_thumb32(0xf36e1d17) " @ bfi sp, r14, #4, #20") + TEST_UNSUPPORTED(__inst_thumb32(0xf36e1f17) " @ bfi pc, r14, #4, #20") + TEST_UNSUPPORTED(__inst_thumb32(0xf36d1e17) " @ bfi r14, sp, #4, #20") + + TEST_GROUP("Branches and miscellaneous control") + +CONDITION_INSTRUCTIONS(22, + TEST_BF("beq.w 2f") + TEST_BB("bne.w 2b") + TEST_BF("bgt.w 2f") + TEST_BB("blt.w 2b") + TEST_BF_X("bpl.w 2f", SPACE_0x1000) +) + + TEST_UNSUPPORTED("msr cpsr, r0") + TEST_UNSUPPORTED("msr cpsr_f, r1") + TEST_UNSUPPORTED("msr spsr, r2") + + TEST_UNSUPPORTED("cpsie.w i") + TEST_UNSUPPORTED("cpsid.w i") + TEST_UNSUPPORTED("cps 0x13") + + TEST_SUPPORTED("yield.w") + TEST("sev.w") + TEST("nop.w") + TEST("wfi.w") + TEST_SUPPORTED("wfe.w") + TEST_UNSUPPORTED("dbg.w #0") + + TEST_UNSUPPORTED("clrex") + TEST_UNSUPPORTED("dsb") + TEST_UNSUPPORTED("dmb") + TEST_UNSUPPORTED("isb") + + TEST_UNSUPPORTED("bxj r0") + + TEST_UNSUPPORTED("subs pc, lr, #4") + + TEST("mrs r0, cpsr") + TEST("mrs r14, cpsr") + TEST_UNSUPPORTED(__inst_thumb32(0xf3ef8d00) " @ mrs sp, spsr") + TEST_UNSUPPORTED(__inst_thumb32(0xf3ef8f00) " @ mrs pc, spsr") + TEST_UNSUPPORTED("mrs r0, spsr") + TEST_UNSUPPORTED("mrs lr, spsr") + + TEST_UNSUPPORTED(__inst_thumb32(0xf7f08000) " @ smc #0") + + TEST_UNSUPPORTED(__inst_thumb32(0xf7f0a000) " @ undefeined") + + TEST_BF( "b.w 2f") + TEST_BB( "b.w 2b") + TEST_BF_X("b.w 2f", SPACE_0x1000) + + TEST_BF( "bl.w 2f") + TEST_BB( "bl.w 2b") + TEST_BB_X("bl.w 2b", SPACE_0x1000) + + TEST_X( "blx __dummy_arm_subroutine", + ".arm \n\t" + ".align \n\t" + ".type __dummy_arm_subroutine, %%function \n\t" + "__dummy_arm_subroutine: \n\t" + "mov r0, pc \n\t" + "bx lr \n\t" + ".thumb \n\t" + ) + TEST( "blx __dummy_arm_subroutine") + + TEST_GROUP("Store single data item") + +#define SINGLE_STORE(size) \ + TEST_RP( "str"size" r",0, VAL1,", [r",11,-1024,", #1024]") \ + TEST_RP( "str"size" r",14,VAL2,", [r",1, -1024,", #1080]") \ + TEST_RP( "str"size" r",0, VAL1,", [r",11,256, ", #-120]") \ + TEST_RP( "str"size" r",14,VAL2,", [r",1, 256, ", #-128]") \ + TEST_RP( "str"size" r",0, VAL1,", [r",11,24, "], #120") \ + TEST_RP( "str"size" r",14,VAL2,", [r",1, 24, "], #128") \ + TEST_RP( "str"size" r",0, VAL1,", [r",11,24, "], #-120") \ + TEST_RP( "str"size" r",14,VAL2,", [r",1, 24, "], #-128") \ + TEST_RP( "str"size" r",0, VAL1,", [r",11,24, ", #120]!") \ + TEST_RP( "str"size" r",14,VAL2,", [r",1, 24, ", #128]!") \ + TEST_RP( "str"size" r",0, VAL1,", [r",11,256, ", #-120]!") \ + TEST_RP( "str"size" r",14,VAL2,", [r",1, 256, ", #-128]!") \ + TEST_RPR("str"size".w r",0, VAL1,", [r",1, 0,", r",2, 4,"]") \ + TEST_RPR("str"size" r",14,VAL2,", [r",10,0,", r",11,4,", lsl #1]") \ + TEST_R( "str"size".w r",7, VAL1,", [sp, #24]") \ + TEST_RP( "str"size".w r",0, VAL2,", [r",0,0, "]") \ + TEST_UNSUPPORTED("str"size"t r0, [r1, #4]") + + SINGLE_STORE("b") + SINGLE_STORE("h") + SINGLE_STORE("") + + TEST("str sp, [sp]") + TEST_UNSUPPORTED(__inst_thumb32(0xf8cfe000) " @ str r14, [pc]") + TEST_UNSUPPORTED(__inst_thumb32(0xf8cef000) " @ str pc, [r14]") + + TEST_GROUP("Advanced SIMD element or structure load/store instructions") + + TEST_UNSUPPORTED(__inst_thumb32(0xf9000000) "") + TEST_UNSUPPORTED(__inst_thumb32(0xf92fffff) "") + TEST_UNSUPPORTED(__inst_thumb32(0xf9800000) "") + TEST_UNSUPPORTED(__inst_thumb32(0xf9efffff) "") + + TEST_GROUP("Load single data item and memory hints") + +#define SINGLE_LOAD(size) \ + TEST_P( "ldr"size" r0, [r",11,-1024, ", #1024]") \ + TEST_P( "ldr"size" r14, [r",1, -1024,", #1080]") \ + TEST_P( "ldr"size" r0, [r",11,256, ", #-120]") \ + TEST_P( "ldr"size" r14, [r",1, 256, ", #-128]") \ + TEST_P( "ldr"size" r0, [r",11,24, "], #120") \ + TEST_P( "ldr"size" r14, [r",1, 24, "], #128") \ + TEST_P( "ldr"size" r0, [r",11,24, "], #-120") \ + TEST_P( "ldr"size" r14, [r",1,24, "], #-128") \ + TEST_P( "ldr"size" r0, [r",11,24, ", #120]!") \ + TEST_P( "ldr"size" r14, [r",1, 24, ", #128]!") \ + TEST_P( "ldr"size" r0, [r",11,256, ", #-120]!") \ + TEST_P( "ldr"size" r14, [r",1, 256, ", #-128]!") \ + TEST_PR("ldr"size".w r0, [r",1, 0,", r",2, 4,"]") \ + TEST_PR("ldr"size" r14, [r",10,0,", r",11,4,", lsl #1]") \ + TEST_X( "ldr"size".w r0, 3f", \ + ".align 3 \n\t" \ + "3: .word "__stringify(VAL1)) \ + TEST_X( "ldr"size".w r14, 3f", \ + ".align 3 \n\t" \ + "3: .word "__stringify(VAL2)) \ + TEST( "ldr"size".w r7, 3b") \ + TEST( "ldr"size".w r7, [sp, #24]") \ + TEST_P( "ldr"size".w r0, [r",0,0, "]") \ + TEST_UNSUPPORTED("ldr"size"t r0, [r1, #4]") + + SINGLE_LOAD("b") + SINGLE_LOAD("sb") + SINGLE_LOAD("h") + SINGLE_LOAD("sh") + SINGLE_LOAD("") + + TEST_BF_P("ldr pc, [r",14, 15*4,"]") + TEST_P( "ldr sp, [r",14, 13*4,"]") + TEST_BF_R("ldr pc, [sp, r",14, 15*4,"]") + TEST_R( "ldr sp, [sp, r",14, 13*4,"]") + TEST_THUMB_TO_ARM_INTERWORK_P("ldr pc, [r",0,0,", #15*4]") + TEST_SUPPORTED("ldr sp, 99f") + TEST_SUPPORTED("ldr pc, 99f") + + TEST_UNSUPPORTED(__inst_thumb32(0xf854700d) " @ ldr r7, [r4, sp]") + TEST_UNSUPPORTED(__inst_thumb32(0xf854700f) " @ ldr r7, [r4, pc]") + TEST_UNSUPPORTED(__inst_thumb32(0xf814700d) " @ ldrb r7, [r4, sp]") + TEST_UNSUPPORTED(__inst_thumb32(0xf814700f) " @ ldrb r7, [r4, pc]") + TEST_UNSUPPORTED(__inst_thumb32(0xf89fd004) " @ ldrb sp, 99f") + TEST_UNSUPPORTED(__inst_thumb32(0xf814d008) " @ ldrb sp, [r4, r8]") + TEST_UNSUPPORTED(__inst_thumb32(0xf894d000) " @ ldrb sp, [r4]") + + TEST_UNSUPPORTED(__inst_thumb32(0xf8600000) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_thumb32(0xf9ffffff) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_thumb32(0xf9500000) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_thumb32(0xf95fffff) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_thumb32(0xf8000800) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_thumb32(0xf97ffaff) "") /* Unallocated space */ + + TEST( "pli [pc, #4]") + TEST( "pli [pc, #-4]") + TEST( "pld [pc, #4]") + TEST( "pld [pc, #-4]") + + TEST_P( "pld [r",0,-1024,", #1024]") + TEST( __inst_thumb32(0xf8b0f400) " @ pldw [r0, #1024]") + TEST_P( "pli [r",4, 0b,", #1024]") + TEST_P( "pld [r",7, 120,", #-120]") + TEST( __inst_thumb32(0xf837fc78) " @ pldw [r7, #-120]") + TEST_P( "pli [r",11,120,", #-120]") + TEST( "pld [sp, #0]") + + TEST_PR("pld [r",7, 24, ", r",0, 16,"]") + TEST_PR("pld [r",8, 24, ", r",12,16,", lsl #3]") + TEST_SUPPORTED(__inst_thumb32(0xf837f000) " @ pldw [r7, r0]") + TEST_SUPPORTED(__inst_thumb32(0xf838f03c) " @ pldw [r8, r12, lsl #3]"); + TEST_RR("pli [r",12,0b,", r",0, 16,"]") + TEST_RR("pli [r",0, 0b,", r",12,16,", lsl #3]") + TEST_R( "pld [sp, r",1, 16,"]") + TEST_UNSUPPORTED(__inst_thumb32(0xf817f00d) " @pld [r7, sp]") + TEST_UNSUPPORTED(__inst_thumb32(0xf817f00f) " @pld [r7, pc]") + + TEST_GROUP("Data-processing (register)") + +#define SHIFTS32(op) \ + TEST_RR(op" r0, r",1, VAL1,", r",2, 3, "") \ + TEST_RR(op" r14, r",12,VAL2,", r",11,10,"") + + SHIFTS32("lsl") + SHIFTS32("lsls") + SHIFTS32("lsr") + SHIFTS32("lsrs") + SHIFTS32("asr") + SHIFTS32("asrs") + SHIFTS32("ror") + SHIFTS32("rors") + + TEST_UNSUPPORTED(__inst_thumb32(0xfa01ff02) " @ lsl pc, r1, r2") + TEST_UNSUPPORTED(__inst_thumb32(0xfa01fd02) " @ lsl sp, r1, r2") + TEST_UNSUPPORTED(__inst_thumb32(0xfa0ff002) " @ lsl r0, pc, r2") + TEST_UNSUPPORTED(__inst_thumb32(0xfa0df002) " @ lsl r0, sp, r2") + TEST_UNSUPPORTED(__inst_thumb32(0xfa01f00f) " @ lsl r0, r1, pc") + TEST_UNSUPPORTED(__inst_thumb32(0xfa01f00d) " @ lsl r0, r1, sp") + + TEST_RR( "sxtah r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "sxtah r14,r",12, HH2,", r",10,HH1,", ror #8") + TEST_R( "sxth r8, r",7, HH1,"") + + TEST_UNSUPPORTED(__inst_thumb32(0xfa0fff87) " @ sxth pc, r7"); + TEST_UNSUPPORTED(__inst_thumb32(0xfa0ffd87) " @ sxth sp, r7"); + TEST_UNSUPPORTED(__inst_thumb32(0xfa0ff88f) " @ sxth r8, pc"); + TEST_UNSUPPORTED(__inst_thumb32(0xfa0ff88d) " @ sxth r8, sp"); + + TEST_RR( "uxtah r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uxtah r14,r",12, HH2,", r",10,HH1,", ror #8") + TEST_R( "uxth r8, r",7, HH1,"") + + TEST_RR( "sxtab16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "sxtab16 r14,r",12, HH2,", r",10,HH1,", ror #8") + TEST_R( "sxtb16 r8, r",7, HH1,"") + + TEST_RR( "uxtab16 r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uxtab16 r14,r",12, HH2,", r",10,HH1,", ror #8") + TEST_R( "uxtb16 r8, r",7, HH1,"") + + TEST_RR( "sxtab r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "sxtab r14,r",12, HH2,", r",10,HH1,", ror #8") + TEST_R( "sxtb r8, r",7, HH1,"") + + TEST_RR( "uxtab r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "uxtab r14,r",12, HH2,", r",10,HH1,", ror #8") + TEST_R( "uxtb r8, r",7, HH1,"") + + TEST_UNSUPPORTED(__inst_thumb32(0xfa6000f0) "") + TEST_UNSUPPORTED(__inst_thumb32(0xfa7fffff) "") + +#define PARALLEL_ADD_SUB(op) \ + TEST_RR( op"add16 r0, r",0, HH1,", r",1, HH2,"") \ + TEST_RR( op"add16 r14, r",12,HH2,", r",10,HH1,"") \ + TEST_RR( op"asx r0, r",0, HH1,", r",1, HH2,"") \ + TEST_RR( op"asx r14, r",12,HH2,", r",10,HH1,"") \ + TEST_RR( op"sax r0, r",0, HH1,", r",1, HH2,"") \ + TEST_RR( op"sax r14, r",12,HH2,", r",10,HH1,"") \ + TEST_RR( op"sub16 r0, r",0, HH1,", r",1, HH2,"") \ + TEST_RR( op"sub16 r14, r",12,HH2,", r",10,HH1,"") \ + TEST_RR( op"add8 r0, r",0, HH1,", r",1, HH2,"") \ + TEST_RR( op"add8 r14, r",12,HH2,", r",10,HH1,"") \ + TEST_RR( op"sub8 r0, r",0, HH1,", r",1, HH2,"") \ + TEST_RR( op"sub8 r14, r",12,HH2,", r",10,HH1,"") + + TEST_GROUP("Parallel addition and subtraction, signed") + + PARALLEL_ADD_SUB("s") + PARALLEL_ADD_SUB("q") + PARALLEL_ADD_SUB("sh") + + TEST_GROUP("Parallel addition and subtraction, unsigned") + + PARALLEL_ADD_SUB("u") + PARALLEL_ADD_SUB("uq") + PARALLEL_ADD_SUB("uh") + + TEST_GROUP("Miscellaneous operations") + + TEST_RR("qadd r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR("qadd lr, r",9, VAL2,", r",8, VAL1,"") + TEST_RR("qsub r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR("qsub lr, r",9, VAL2,", r",8, VAL1,"") + TEST_RR("qdadd r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR("qdadd lr, r",9, VAL2,", r",8, VAL1,"") + TEST_RR("qdsub r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR("qdsub lr, r",9, VAL2,", r",8, VAL1,"") + + TEST_R("rev.w r0, r",0, VAL1,"") + TEST_R("rev r14, r",12, VAL2,"") + TEST_R("rev16.w r0, r",0, VAL1,"") + TEST_R("rev16 r14, r",12, VAL2,"") + TEST_R("rbit r0, r",0, VAL1,"") + TEST_R("rbit r14, r",12, VAL2,"") + TEST_R("revsh.w r0, r",0, VAL1,"") + TEST_R("revsh r14, r",12, VAL2,"") + + TEST_UNSUPPORTED(__inst_thumb32(0xfa9cff8c) " @ rev pc, r12"); + TEST_UNSUPPORTED(__inst_thumb32(0xfa9cfd8c) " @ rev sp, r12"); + TEST_UNSUPPORTED(__inst_thumb32(0xfa9ffe8f) " @ rev r14, pc"); + TEST_UNSUPPORTED(__inst_thumb32(0xfa9dfe8d) " @ rev r14, sp"); + + TEST_RR("sel r0, r",0, VAL1,", r",1, VAL2,"") + TEST_RR("sel r14, r",12,VAL1,", r",10, VAL2,"") + + TEST_R("clz r0, r",0, 0x0,"") + TEST_R("clz r7, r",14,0x1,"") + TEST_R("clz lr, r",7, 0xffffffff,"") + + TEST_UNSUPPORTED(__inst_thumb32(0xfa80f030) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_thumb32(0xfaffff7f) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_thumb32(0xfab0f000) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_thumb32(0xfaffff7f) "") /* Unallocated space */ + + TEST_GROUP("Multiply, multiply accumulate, and absolute difference operations") + + TEST_RR( "mul r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "mul r7, r",8, VAL2,", r",9, VAL2,"") + TEST_UNSUPPORTED(__inst_thumb32(0xfb08ff09) " @ mul pc, r8, r9") + TEST_UNSUPPORTED(__inst_thumb32(0xfb08fd09) " @ mul sp, r8, r9") + TEST_UNSUPPORTED(__inst_thumb32(0xfb0ff709) " @ mul r7, pc, r9") + TEST_UNSUPPORTED(__inst_thumb32(0xfb0df709) " @ mul r7, sp, r9") + TEST_UNSUPPORTED(__inst_thumb32(0xfb08f70f) " @ mul r7, r8, pc") + TEST_UNSUPPORTED(__inst_thumb32(0xfb08f70d) " @ mul r7, r8, sp") + + TEST_RRR( "mla r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "mla r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_UNSUPPORTED(__inst_thumb32(0xfb08af09) " @ mla pc, r8, r9, r10"); + TEST_UNSUPPORTED(__inst_thumb32(0xfb08ad09) " @ mla sp, r8, r9, r10"); + TEST_UNSUPPORTED(__inst_thumb32(0xfb0fa709) " @ mla r7, pc, r9, r10"); + TEST_UNSUPPORTED(__inst_thumb32(0xfb0da709) " @ mla r7, sp, r9, r10"); + TEST_UNSUPPORTED(__inst_thumb32(0xfb08a70f) " @ mla r7, r8, pc, r10"); + TEST_UNSUPPORTED(__inst_thumb32(0xfb08a70d) " @ mla r7, r8, sp, r10"); + TEST_UNSUPPORTED(__inst_thumb32(0xfb08d709) " @ mla r7, r8, r9, sp"); + + TEST_RRR( "mls r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "mls r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + + TEST_RRR( "smlabb r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "smlabb r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_RRR( "smlatb r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "smlatb r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_RRR( "smlabt r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "smlabt r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_RRR( "smlatt r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "smlatt r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_RR( "smulbb r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "smulbb r7, r",8, VAL3,", r",9, VAL1,"") + TEST_RR( "smultb r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "smultb r7, r",8, VAL3,", r",9, VAL1,"") + TEST_RR( "smulbt r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "smulbt r7, r",8, VAL3,", r",9, VAL1,"") + TEST_RR( "smultt r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "smultt r7, r",8, VAL3,", r",9, VAL1,"") + + TEST_RRR( "smlad r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") + TEST_RRR( "smlad r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") + TEST_RRR( "smladx r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") + TEST_RRR( "smladx r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") + TEST_RR( "smuad r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "smuad r14, r",12,HH2,", r",10,HH1,"") + TEST_RR( "smuadx r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "smuadx r14, r",12,HH2,", r",10,HH1,"") + + TEST_RRR( "smlawb r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "smlawb r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_RRR( "smlawt r0, r",1, VAL1,", r",2, VAL2,", r",3, VAL3,"") + TEST_RRR( "smlawt r7, r",8, VAL3,", r",9, VAL1,", r",10, VAL2,"") + TEST_RR( "smulwb r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "smulwb r7, r",8, VAL3,", r",9, VAL1,"") + TEST_RR( "smulwt r0, r",1, VAL1,", r",2, VAL2,"") + TEST_RR( "smulwt r7, r",8, VAL3,", r",9, VAL1,"") + + TEST_RRR( "smlsd r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") + TEST_RRR( "smlsd r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") + TEST_RRR( "smlsdx r0, r",0, HH1,", r",1, HH2,", r",2, VAL1,"") + TEST_RRR( "smlsdx r14, r",12,HH2,", r",10,HH1,", r",8, VAL2,"") + TEST_RR( "smusd r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "smusd r14, r",12,HH2,", r",10,HH1,"") + TEST_RR( "smusdx r0, r",0, HH1,", r",1, HH2,"") + TEST_RR( "smusdx r14, r",12,HH2,", r",10,HH1,"") + + TEST_RRR( "smmla r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") + TEST_RRR( "smmla r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") + TEST_RRR( "smmlar r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") + TEST_RRR( "smmlar r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") + TEST_RR( "smmul r0, r",0, VAL1,", r",1, VAL2,"") + TEST_RR( "smmul r14, r",12,VAL2,", r",10,VAL1,"") + TEST_RR( "smmulr r0, r",0, VAL1,", r",1, VAL2,"") + TEST_RR( "smmulr r14, r",12,VAL2,", r",10,VAL1,"") + + TEST_RRR( "smmls r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") + TEST_RRR( "smmls r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") + TEST_RRR( "smmlsr r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL1,"") + TEST_RRR( "smmlsr r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL2,"") + + TEST_RRR( "usada8 r0, r",0, VAL1,", r",1, VAL2,", r",2, VAL3,"") + TEST_RRR( "usada8 r14, r",12,VAL2,", r",10,VAL1,", r",8, VAL3,"") + TEST_RR( "usad8 r0, r",0, VAL1,", r",1, VAL2,"") + TEST_RR( "usad8 r14, r",12,VAL2,", r",10,VAL1,"") + + TEST_UNSUPPORTED(__inst_thumb32(0xfb00f010) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_thumb32(0xfb0fff1f) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_thumb32(0xfb70f010) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_thumb32(0xfb7fff1f) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_thumb32(0xfb700010) "") /* Unallocated space */ + TEST_UNSUPPORTED(__inst_thumb32(0xfb7fff1f) "") /* Unallocated space */ + + TEST_GROUP("Long multiply, long multiply accumulate, and divide") + + TEST_RR( "smull r0, r1, r",2, VAL1,", r",3, VAL2,"") + TEST_RR( "smull r7, r8, r",9, VAL2,", r",10, VAL1,"") + TEST_UNSUPPORTED(__inst_thumb32(0xfb89f80a) " @ smull pc, r8, r9, r10"); + TEST_UNSUPPORTED(__inst_thumb32(0xfb89d80a) " @ smull sp, r8, r9, r10"); + TEST_UNSUPPORTED(__inst_thumb32(0xfb897f0a) " @ smull r7, pc, r9, r10"); + TEST_UNSUPPORTED(__inst_thumb32(0xfb897d0a) " @ smull r7, sp, r9, r10"); + TEST_UNSUPPORTED(__inst_thumb32(0xfb8f780a) " @ smull r7, r8, pc, r10"); + TEST_UNSUPPORTED(__inst_thumb32(0xfb8d780a) " @ smull r7, r8, sp, r10"); + TEST_UNSUPPORTED(__inst_thumb32(0xfb89780f) " @ smull r7, r8, r9, pc"); + TEST_UNSUPPORTED(__inst_thumb32(0xfb89780d) " @ smull r7, r8, r9, sp"); + + TEST_RR( "umull r0, r1, r",2, VAL1,", r",3, VAL2,"") + TEST_RR( "umull r7, r8, r",9, VAL2,", r",10, VAL1,"") + + TEST_RRRR( "smlal r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) + TEST_RRRR( "smlal r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) + + TEST_RRRR( "smlalbb r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) + TEST_RRRR( "smlalbb r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) + TEST_RRRR( "smlalbt r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) + TEST_RRRR( "smlalbt r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) + TEST_RRRR( "smlaltb r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) + TEST_RRRR( "smlaltb r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) + TEST_RRRR( "smlaltt r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) + TEST_RRRR( "smlaltt r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) + + TEST_RRRR( "smlald r",0, VAL1,", r",1, VAL2, ", r",0, HH1,", r",1, HH2) + TEST_RRRR( "smlald r",11,VAL2,", r",10,VAL1, ", r",9, HH2,", r",8, HH1) + TEST_RRRR( "smlaldx r",0, VAL1,", r",1, VAL2, ", r",0, HH1,", r",1, HH2) + TEST_RRRR( "smlaldx r",11,VAL2,", r",10,VAL1, ", r",9, HH2,", r",8, HH1) + + TEST_RRRR( "smlsld r",0, VAL1,", r",1, VAL2, ", r",0, HH1,", r",1, HH2) + TEST_RRRR( "smlsld r",11,VAL2,", r",10,VAL1, ", r",9, HH2,", r",8, HH1) + TEST_RRRR( "smlsldx r",0, VAL1,", r",1, VAL2, ", r",0, HH1,", r",1, HH2) + TEST_RRRR( "smlsldx r",11,VAL2,", r",10,VAL1, ", r",9, HH2,", r",8, HH1) + + TEST_RRRR( "umlal r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) + TEST_RRRR( "umlal r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) + TEST_RRRR( "umaal r",0, VAL1,", r",1, VAL2,", r",2, VAL3,", r",3, VAL4) + TEST_RRRR( "umaal r",8, VAL4,", r",9, VAL1,", r",10,VAL2,", r",11,VAL3) + + TEST_GROUP("Coprocessor instructions") + + TEST_UNSUPPORTED(__inst_thumb32(0xfc000000) "") + TEST_UNSUPPORTED(__inst_thumb32(0xffffffff) "") + + TEST_GROUP("Testing instructions in IT blocks") + + TEST_ITBLOCK("sub.w r0, r0") + + verbose("\n"); +} + diff --git a/arch/arm/probes/uprobes/Makefile b/arch/arm/probes/uprobes/Makefile new file mode 100644 index 0000000..e1dc3d0 --- /dev/null +++ b/arch/arm/probes/uprobes/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_UPROBES) += core.o actions-arm.o diff --git a/arch/arm/probes/uprobes/actions-arm.c b/arch/arm/probes/uprobes/actions-arm.c new file mode 100644 index 0000000..1dd4916 --- /dev/null +++ b/arch/arm/probes/uprobes/actions-arm.c @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2012 Rabin Vincent + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include "../decode.h" +#include "../decode-arm.h" +#include "core.h" + +static int uprobes_substitute_pc(unsigned long *pinsn, u32 oregs) +{ + probes_opcode_t insn = __mem_to_opcode_arm(*pinsn); + probes_opcode_t temp; + probes_opcode_t mask; + int freereg; + u32 free = 0xffff; + u32 regs; + + for (regs = oregs; regs; regs >>= 4, insn >>= 4) { + if ((regs & 0xf) == REG_TYPE_NONE) + continue; + + free &= ~(1 << (insn & 0xf)); + } + + /* No PC, no problem */ + if (free & (1 << 15)) + return 15; + + if (!free) + return -1; + + /* + * fls instead of ffs ensures that for "ldrd r0, r1, [pc]" we would + * pick LR instead of R1. + */ + freereg = free = fls(free) - 1; + + temp = __mem_to_opcode_arm(*pinsn); + insn = temp; + regs = oregs; + mask = 0xf; + + for (; regs; regs >>= 4, mask <<= 4, free <<= 4, temp >>= 4) { + if ((regs & 0xf) == REG_TYPE_NONE) + continue; + + if ((temp & 0xf) != 15) + continue; + + insn &= ~mask; + insn |= free & mask; + } + + *pinsn = __opcode_to_mem_arm(insn); + return freereg; +} + +static void uprobe_set_pc(struct arch_uprobe *auprobe, + struct arch_uprobe_task *autask, + struct pt_regs *regs) +{ + u32 pcreg = auprobe->pcreg; + + autask->backup = regs->uregs[pcreg]; + regs->uregs[pcreg] = regs->ARM_pc + 8; +} + +static void uprobe_unset_pc(struct arch_uprobe *auprobe, + struct arch_uprobe_task *autask, + struct pt_regs *regs) +{ + /* PC will be taken care of by common code */ + regs->uregs[auprobe->pcreg] = autask->backup; +} + +static void uprobe_aluwrite_pc(struct arch_uprobe *auprobe, + struct arch_uprobe_task *autask, + struct pt_regs *regs) +{ + u32 pcreg = auprobe->pcreg; + + alu_write_pc(regs->uregs[pcreg], regs); + regs->uregs[pcreg] = autask->backup; +} + +static void uprobe_write_pc(struct arch_uprobe *auprobe, + struct arch_uprobe_task *autask, + struct pt_regs *regs) +{ + u32 pcreg = auprobe->pcreg; + + load_write_pc(regs->uregs[pcreg], regs); + regs->uregs[pcreg] = autask->backup; +} + +enum probes_insn +decode_pc_ro(probes_opcode_t insn, struct arch_probes_insn *asi, + const struct decode_header *d) +{ + struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe, + asi); + struct decode_emulate *decode = (struct decode_emulate *) d; + u32 regs = decode->header.type_regs.bits >> DECODE_TYPE_BITS; + int reg; + + reg = uprobes_substitute_pc(&auprobe->ixol[0], regs); + if (reg == 15) + return INSN_GOOD; + + if (reg == -1) + return INSN_REJECTED; + + auprobe->pcreg = reg; + auprobe->prehandler = uprobe_set_pc; + auprobe->posthandler = uprobe_unset_pc; + + return INSN_GOOD; +} + +enum probes_insn +decode_wb_pc(probes_opcode_t insn, struct arch_probes_insn *asi, + const struct decode_header *d, bool alu) +{ + struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe, + asi); + enum probes_insn ret = decode_pc_ro(insn, asi, d); + + if (((insn >> 12) & 0xf) == 15) + auprobe->posthandler = alu ? uprobe_aluwrite_pc + : uprobe_write_pc; + + return ret; +} + +enum probes_insn +decode_rd12rn16rm0rs8_rwflags(probes_opcode_t insn, + struct arch_probes_insn *asi, + const struct decode_header *d) +{ + return decode_wb_pc(insn, asi, d, true); +} + +enum probes_insn +decode_ldr(probes_opcode_t insn, struct arch_probes_insn *asi, + const struct decode_header *d) +{ + return decode_wb_pc(insn, asi, d, false); +} + +enum probes_insn +uprobe_decode_ldmstm(probes_opcode_t insn, + struct arch_probes_insn *asi, + const struct decode_header *d) +{ + struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe, + asi); + unsigned reglist = insn & 0xffff; + int rn = (insn >> 16) & 0xf; + int lbit = insn & (1 << 20); + unsigned used = reglist | (1 << rn); + + if (rn == 15) + return INSN_REJECTED; + + if (!(used & (1 << 15))) + return INSN_GOOD; + + if (used & (1 << 14)) + return INSN_REJECTED; + + /* Use LR instead of PC */ + insn ^= 0xc000; + + auprobe->pcreg = 14; + auprobe->ixol[0] = __opcode_to_mem_arm(insn); + + auprobe->prehandler = uprobe_set_pc; + if (lbit) + auprobe->posthandler = uprobe_write_pc; + else + auprobe->posthandler = uprobe_unset_pc; + + return INSN_GOOD; +} + +const union decode_action uprobes_probes_actions[] = { + [PROBES_EMULATE_NONE] = {.handler = probes_simulate_nop}, + [PROBES_SIMULATE_NOP] = {.handler = probes_simulate_nop}, + [PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop}, + [PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop}, + [PROBES_BRANCH_IMM] = {.handler = simulate_blx1}, + [PROBES_MRS] = {.handler = simulate_mrs}, + [PROBES_BRANCH_REG] = {.handler = simulate_blx2bx}, + [PROBES_CLZ] = {.handler = probes_simulate_nop}, + [PROBES_SATURATING_ARITHMETIC] = {.handler = probes_simulate_nop}, + [PROBES_MUL1] = {.handler = probes_simulate_nop}, + [PROBES_MUL2] = {.handler = probes_simulate_nop}, + [PROBES_SWP] = {.handler = probes_simulate_nop}, + [PROBES_LDRSTRD] = {.decoder = decode_pc_ro}, + [PROBES_LOAD_EXTRA] = {.decoder = decode_pc_ro}, + [PROBES_LOAD] = {.decoder = decode_ldr}, + [PROBES_STORE_EXTRA] = {.decoder = decode_pc_ro}, + [PROBES_STORE] = {.decoder = decode_pc_ro}, + [PROBES_MOV_IP_SP] = {.handler = simulate_mov_ipsp}, + [PROBES_DATA_PROCESSING_REG] = { + .decoder = decode_rd12rn16rm0rs8_rwflags}, + [PROBES_DATA_PROCESSING_IMM] = { + .decoder = decode_rd12rn16rm0rs8_rwflags}, + [PROBES_MOV_HALFWORD] = {.handler = probes_simulate_nop}, + [PROBES_SEV] = {.handler = probes_simulate_nop}, + [PROBES_WFE] = {.handler = probes_simulate_nop}, + [PROBES_SATURATE] = {.handler = probes_simulate_nop}, + [PROBES_REV] = {.handler = probes_simulate_nop}, + [PROBES_MMI] = {.handler = probes_simulate_nop}, + [PROBES_PACK] = {.handler = probes_simulate_nop}, + [PROBES_EXTEND] = {.handler = probes_simulate_nop}, + [PROBES_EXTEND_ADD] = {.handler = probes_simulate_nop}, + [PROBES_MUL_ADD_LONG] = {.handler = probes_simulate_nop}, + [PROBES_MUL_ADD] = {.handler = probes_simulate_nop}, + [PROBES_BITFIELD] = {.handler = probes_simulate_nop}, + [PROBES_BRANCH] = {.handler = simulate_bbl}, + [PROBES_LDMSTM] = {.decoder = uprobe_decode_ldmstm} +}; diff --git a/arch/arm/probes/uprobes/core.c b/arch/arm/probes/uprobes/core.c new file mode 100644 index 0000000..b2954f6 --- /dev/null +++ b/arch/arm/probes/uprobes/core.c @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2012 Rabin Vincent + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../decode.h" +#include "../decode-arm.h" +#include "core.h" + +#define UPROBE_TRAP_NR UINT_MAX + +bool is_swbp_insn(uprobe_opcode_t *insn) +{ + return (__mem_to_opcode_arm(*insn) & 0x0fffffff) == + (UPROBE_SWBP_ARM_INSN & 0x0fffffff); +} + +int set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, + unsigned long vaddr) +{ + return uprobe_write_opcode(mm, vaddr, + __opcode_to_mem_arm(auprobe->bpinsn)); +} + +bool arch_uprobe_ignore(struct arch_uprobe *auprobe, struct pt_regs *regs) +{ + if (!auprobe->asi.insn_check_cc(regs->ARM_cpsr)) { + regs->ARM_pc += 4; + return true; + } + + return false; +} + +bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) +{ + probes_opcode_t opcode; + + if (!auprobe->simulate) + return false; + + opcode = __mem_to_opcode_arm(*(unsigned int *) auprobe->insn); + + auprobe->asi.insn_singlestep(opcode, &auprobe->asi, regs); + + return true; +} + +unsigned long +arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, + struct pt_regs *regs) +{ + unsigned long orig_ret_vaddr; + + orig_ret_vaddr = regs->ARM_lr; + /* Replace the return addr with trampoline addr */ + regs->ARM_lr = trampoline_vaddr; + return orig_ret_vaddr; +} + +int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, + unsigned long addr) +{ + unsigned int insn; + unsigned int bpinsn; + enum probes_insn ret; + + /* Thumb not yet support */ + if (addr & 0x3) + return -EINVAL; + + insn = __mem_to_opcode_arm(*(unsigned int *)auprobe->insn); + auprobe->ixol[0] = __opcode_to_mem_arm(insn); + auprobe->ixol[1] = __opcode_to_mem_arm(UPROBE_SS_ARM_INSN); + + ret = arm_probes_decode_insn(insn, &auprobe->asi, false, + uprobes_probes_actions); + switch (ret) { + case INSN_REJECTED: + return -EINVAL; + + case INSN_GOOD_NO_SLOT: + auprobe->simulate = true; + break; + + case INSN_GOOD: + default: + break; + } + + bpinsn = UPROBE_SWBP_ARM_INSN & 0x0fffffff; + if (insn >= 0xe0000000) + bpinsn |= 0xe0000000; /* Unconditional instruction */ + else + bpinsn |= insn & 0xf0000000; /* Copy condition from insn */ + + auprobe->bpinsn = bpinsn; + + return 0; +} + +void arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr, + void *src, unsigned long len) +{ + void *xol_page_kaddr = kmap_atomic(page); + void *dst = xol_page_kaddr + (vaddr & ~PAGE_MASK); + + preempt_disable(); + + /* Initialize the slot */ + memcpy(dst, src, len); + + /* flush caches (dcache/icache) */ + flush_uprobe_xol_access(page, vaddr, dst, len); + + preempt_enable(); + + kunmap_atomic(xol_page_kaddr); +} + + +int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) +{ + struct uprobe_task *utask = current->utask; + + if (auprobe->prehandler) + auprobe->prehandler(auprobe, &utask->autask, regs); + + utask->autask.saved_trap_no = current->thread.trap_no; + current->thread.trap_no = UPROBE_TRAP_NR; + regs->ARM_pc = utask->xol_vaddr; + + return 0; +} + +int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) +{ + struct uprobe_task *utask = current->utask; + + WARN_ON_ONCE(current->thread.trap_no != UPROBE_TRAP_NR); + + current->thread.trap_no = utask->autask.saved_trap_no; + regs->ARM_pc = utask->vaddr + 4; + + if (auprobe->posthandler) + auprobe->posthandler(auprobe, &utask->autask, regs); + + return 0; +} + +bool arch_uprobe_xol_was_trapped(struct task_struct *t) +{ + if (t->thread.trap_no != UPROBE_TRAP_NR) + return true; + + return false; +} + +void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) +{ + struct uprobe_task *utask = current->utask; + + current->thread.trap_no = utask->autask.saved_trap_no; + instruction_pointer_set(regs, utask->vaddr); +} + +int arch_uprobe_exception_notify(struct notifier_block *self, + unsigned long val, void *data) +{ + return NOTIFY_DONE; +} + +static int uprobe_trap_handler(struct pt_regs *regs, unsigned int instr) +{ + unsigned long flags; + + local_irq_save(flags); + instr &= 0x0fffffff; + if (instr == (UPROBE_SWBP_ARM_INSN & 0x0fffffff)) + uprobe_pre_sstep_notifier(regs); + else if (instr == (UPROBE_SS_ARM_INSN & 0x0fffffff)) + uprobe_post_sstep_notifier(regs); + local_irq_restore(flags); + + return 0; +} + +unsigned long uprobe_get_swbp_addr(struct pt_regs *regs) +{ + return instruction_pointer(regs); +} + +static struct undef_hook uprobes_arm_break_hook = { + .instr_mask = 0x0fffffff, + .instr_val = (UPROBE_SWBP_ARM_INSN & 0x0fffffff), + .cpsr_mask = MODE_MASK, + .cpsr_val = USR_MODE, + .fn = uprobe_trap_handler, +}; + +static struct undef_hook uprobes_arm_ss_hook = { + .instr_mask = 0x0fffffff, + .instr_val = (UPROBE_SS_ARM_INSN & 0x0fffffff), + .cpsr_mask = MODE_MASK, + .cpsr_val = USR_MODE, + .fn = uprobe_trap_handler, +}; + +static int arch_uprobes_init(void) +{ + register_undef_hook(&uprobes_arm_break_hook); + register_undef_hook(&uprobes_arm_ss_hook); + + return 0; +} +device_initcall(arch_uprobes_init); diff --git a/arch/arm/probes/uprobes/core.h b/arch/arm/probes/uprobes/core.h new file mode 100644 index 0000000..1d0c12d --- /dev/null +++ b/arch/arm/probes/uprobes/core.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2012 Rabin Vincent + * + * 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 __ARM_KERNEL_UPROBES_H +#define __ARM_KERNEL_UPROBES_H + +enum probes_insn uprobe_decode_ldmstm(probes_opcode_t insn, + struct arch_probes_insn *asi, + const struct decode_header *d); + +enum probes_insn decode_ldr(probes_opcode_t insn, + struct arch_probes_insn *asi, + const struct decode_header *d); + +enum probes_insn +decode_rd12rn16rm0rs8_rwflags(probes_opcode_t insn, + struct arch_probes_insn *asi, + const struct decode_header *d); + +enum probes_insn +decode_wb_pc(probes_opcode_t insn, struct arch_probes_insn *asi, + const struct decode_header *d, bool alu); + +enum probes_insn +decode_pc_ro(probes_opcode_t insn, struct arch_probes_insn *asi, + const struct decode_header *d); + +extern const union decode_action uprobes_probes_actions[]; + +#endif -- cgit v0.10.2 From 832607e79d7423c67ef8a3de8dfa3d25b5b0bf86 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Wed, 7 Jan 2015 11:42:30 +0000 Subject: ARM: probes: Use correct action types for MOVW, SEV and WFI This doesn't correct any bugs when probing these instructions but makes MOVW slightly faster and makes everything more symmetric with the Thumb instruction cases. We can also remove the now redundant PROBES_EMULATE_NONE and PROBES_SIMULATE_NOP actions. Signed-off-by: Jon Medhurst diff --git a/arch/arm/probes/decode-arm.c b/arch/arm/probes/decode-arm.c index e39cc75..04114f7 100644 --- a/arch/arm/probes/decode-arm.c +++ b/arch/arm/probes/decode-arm.c @@ -370,17 +370,17 @@ static const union decode_item arm_cccc_001x_table[] = { /* MOVW cccc 0011 0000 xxxx xxxx xxxx xxxx xxxx */ /* MOVT cccc 0011 0100 xxxx xxxx xxxx xxxx xxxx */ - DECODE_EMULATEX (0x0fb00000, 0x03000000, PROBES_DATA_PROCESSING_IMM, + DECODE_EMULATEX (0x0fb00000, 0x03000000, PROBES_MOV_HALFWORD, REGS(0, NOPC, 0, 0, 0)), /* YIELD cccc 0011 0010 0000 xxxx xxxx 0000 0001 */ DECODE_OR (0x0fff00ff, 0x03200001), /* SEV cccc 0011 0010 0000 xxxx xxxx 0000 0100 */ - DECODE_EMULATE (0x0fff00ff, 0x03200004, PROBES_EMULATE_NONE), + DECODE_EMULATE (0x0fff00ff, 0x03200004, PROBES_SEV), /* NOP cccc 0011 0010 0000 xxxx xxxx 0000 0000 */ /* WFE cccc 0011 0010 0000 xxxx xxxx 0000 0010 */ /* WFI cccc 0011 0010 0000 xxxx xxxx 0000 0011 */ - DECODE_SIMULATE (0x0fff00fc, 0x03200000, PROBES_SIMULATE_NOP), + DECODE_SIMULATE (0x0fff00fc, 0x03200000, PROBES_WFE), /* DBG cccc 0011 0010 0000 xxxx xxxx ffff xxxx */ /* unallocated hints cccc 0011 0010 0000 xxxx xxxx xxxx xxxx */ /* MSR (immediate) cccc 0011 0x10 xxxx xxxx xxxx xxxx xxxx */ diff --git a/arch/arm/probes/decode-arm.h b/arch/arm/probes/decode-arm.h index 9c56b40..cb0b263 100644 --- a/arch/arm/probes/decode-arm.h +++ b/arch/arm/probes/decode-arm.h @@ -18,8 +18,6 @@ #include "decode.h" enum probes_arm_action { - PROBES_EMULATE_NONE, - PROBES_SIMULATE_NOP, PROBES_PRELOAD_IMM, PROBES_PRELOAD_REG, PROBES_BRANCH_IMM, diff --git a/arch/arm/probes/kprobes/actions-arm.c b/arch/arm/probes/kprobes/actions-arm.c index 8797879..2206f2d 100644 --- a/arch/arm/probes/kprobes/actions-arm.c +++ b/arch/arm/probes/kprobes/actions-arm.c @@ -302,8 +302,6 @@ emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(probes_opcode_t insn, } const union decode_action kprobes_arm_actions[NUM_PROBES_ARM_ACTIONS] = { - [PROBES_EMULATE_NONE] = {.handler = probes_emulate_none}, - [PROBES_SIMULATE_NOP] = {.handler = probes_simulate_nop}, [PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop}, [PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop}, [PROBES_BRANCH_IMM] = {.handler = simulate_blx1}, diff --git a/arch/arm/probes/uprobes/actions-arm.c b/arch/arm/probes/uprobes/actions-arm.c index 1dd4916..76eb449 100644 --- a/arch/arm/probes/uprobes/actions-arm.c +++ b/arch/arm/probes/uprobes/actions-arm.c @@ -195,8 +195,6 @@ uprobe_decode_ldmstm(probes_opcode_t insn, } const union decode_action uprobes_probes_actions[] = { - [PROBES_EMULATE_NONE] = {.handler = probes_simulate_nop}, - [PROBES_SIMULATE_NOP] = {.handler = probes_simulate_nop}, [PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop}, [PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop}, [PROBES_BRANCH_IMM] = {.handler = simulate_blx1}, -- cgit v0.10.2 From 83803d97dae1eaf6850a45ef8ee179cc66e147dc Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Mon, 5 Jan 2015 19:29:18 +0800 Subject: ARM: kprobes: introduces checker This patch introdces 'checker' to decoding phase, and calls checkers when instruction decoding. This allows further decoding for specific instructions. This patch introduces a stub call of checkers in kprobe arch_prepare_kprobe() as an example and for further expansion. Signed-off-by: Wang Nan Reviewed-by: Jon Medhurst Reviewed-by: Masami Hiramatsu Signed-off-by: Jon Medhurst diff --git a/arch/arm/probes/decode-arm.c b/arch/arm/probes/decode-arm.c index 04114f7..f72c33a 100644 --- a/arch/arm/probes/decode-arm.c +++ b/arch/arm/probes/decode-arm.c @@ -726,10 +726,11 @@ static void __kprobes arm_singlestep(probes_opcode_t insn, */ enum probes_insn __kprobes arm_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, - bool emulate, const union decode_action *actions) + bool emulate, const union decode_action *actions, + const struct decode_checker *checkers[]) { asi->insn_singlestep = arm_singlestep; asi->insn_check_cc = probes_condition_checks[insn>>28]; return probes_decode_insn(insn, asi, probes_decode_arm_table, false, - emulate, actions); + emulate, actions, checkers); } diff --git a/arch/arm/probes/decode-arm.h b/arch/arm/probes/decode-arm.h index cb0b263..b3b80f6 100644 --- a/arch/arm/probes/decode-arm.h +++ b/arch/arm/probes/decode-arm.h @@ -68,6 +68,7 @@ extern const union decode_item probes_decode_arm_table[]; enum probes_insn arm_probes_decode_insn(probes_opcode_t, struct arch_probes_insn *, bool emulate, - const union decode_action *actions); + const union decode_action *actions, + const struct decode_checker *checkers[]); #endif diff --git a/arch/arm/probes/decode-thumb.c b/arch/arm/probes/decode-thumb.c index 2f0453a..985e7dd 100644 --- a/arch/arm/probes/decode-thumb.c +++ b/arch/arm/probes/decode-thumb.c @@ -863,20 +863,22 @@ static void __kprobes thumb32_singlestep(probes_opcode_t opcode, enum probes_insn __kprobes thumb16_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, - bool emulate, const union decode_action *actions) + bool emulate, const union decode_action *actions, + const struct decode_checker *checkers[]) { asi->insn_singlestep = thumb16_singlestep; asi->insn_check_cc = thumb_check_cc; return probes_decode_insn(insn, asi, probes_decode_thumb16_table, true, - emulate, actions); + emulate, actions, checkers); } enum probes_insn __kprobes thumb32_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, - bool emulate, const union decode_action *actions) + bool emulate, const union decode_action *actions, + const struct decode_checker *checkers[]) { asi->insn_singlestep = thumb32_singlestep; asi->insn_check_cc = thumb_check_cc; return probes_decode_insn(insn, asi, probes_decode_thumb32_table, true, - emulate, actions); + emulate, actions, checkers); } diff --git a/arch/arm/probes/decode-thumb.h b/arch/arm/probes/decode-thumb.h index 039013c..8457add 100644 --- a/arch/arm/probes/decode-thumb.h +++ b/arch/arm/probes/decode-thumb.h @@ -91,9 +91,11 @@ extern const union decode_item probes_decode_thumb16_table[]; enum probes_insn __kprobes thumb16_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, - bool emulate, const union decode_action *actions); + bool emulate, const union decode_action *actions, + const struct decode_checker *checkers[]); enum probes_insn __kprobes thumb32_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, - bool emulate, const union decode_action *actions); + bool emulate, const union decode_action *actions, + const struct decode_checker *checkers[]); #endif diff --git a/arch/arm/probes/decode.c b/arch/arm/probes/decode.c index 3b05d57..c7d4420 100644 --- a/arch/arm/probes/decode.c +++ b/arch/arm/probes/decode.c @@ -342,6 +342,31 @@ static const int decode_struct_sizes[NUM_DECODE_TYPES] = { [DECODE_TYPE_REJECT] = sizeof(struct decode_reject) }; +static int run_checkers(const struct decode_checker *checkers[], + int action, probes_opcode_t insn, + struct arch_probes_insn *asi, + const struct decode_header *h) +{ + const struct decode_checker **p; + + if (!checkers) + return INSN_GOOD; + + p = checkers; + while (*p != NULL) { + int retval; + probes_check_t *checker_func = (*p)[action].checker; + + retval = INSN_GOOD; + if (checker_func) + retval = checker_func(insn, asi, h); + if (retval == INSN_REJECTED) + return retval; + p++; + } + return INSN_GOOD; +} + /* * probes_decode_insn operates on data tables in order to decode an ARM * architecture instruction onto which a kprobe has been placed. @@ -388,11 +413,17 @@ static const int decode_struct_sizes[NUM_DECODE_TYPES] = { int __kprobes probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, const union decode_item *table, bool thumb, - bool emulate, const union decode_action *actions) + bool emulate, const union decode_action *actions, + const struct decode_checker *checkers[]) { const struct decode_header *h = (struct decode_header *)table; const struct decode_header *next; bool matched = false; + /* + * @insn can be modified by decode_regs. Save its original + * value for checkers. + */ + probes_opcode_t origin_insn = insn; if (emulate) insn = prepare_emulated_insn(insn, asi, thumb); @@ -422,24 +453,41 @@ probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, } case DECODE_TYPE_CUSTOM: { + int err; struct decode_custom *d = (struct decode_custom *)h; - return actions[d->decoder.action].decoder(insn, asi, h); + int action = d->decoder.action; + + err = run_checkers(checkers, action, origin_insn, asi, h); + if (err == INSN_REJECTED) + return INSN_REJECTED; + return actions[action].decoder(insn, asi, h); } case DECODE_TYPE_SIMULATE: { + int err; struct decode_simulate *d = (struct decode_simulate *)h; - asi->insn_handler = actions[d->handler.action].handler; + int action = d->handler.action; + + err = run_checkers(checkers, action, origin_insn, asi, h); + if (err == INSN_REJECTED) + return INSN_REJECTED; + asi->insn_handler = actions[action].handler; return INSN_GOOD_NO_SLOT; } case DECODE_TYPE_EMULATE: { + int err; struct decode_emulate *d = (struct decode_emulate *)h; + int action = d->handler.action; + + err = run_checkers(checkers, action, origin_insn, asi, h); + if (err == INSN_REJECTED) + return INSN_REJECTED; if (!emulate) - return actions[d->handler.action].decoder(insn, - asi, h); + return actions[action].decoder(insn, asi, h); - asi->insn_handler = actions[d->handler.action].handler; + asi->insn_handler = actions[action].handler; set_emulated_insn(insn, asi, thumb); return INSN_GOOD; } diff --git a/arch/arm/probes/decode.h b/arch/arm/probes/decode.h index 1d0b531..f9b08ba 100644 --- a/arch/arm/probes/decode.h +++ b/arch/arm/probes/decode.h @@ -314,6 +314,14 @@ union decode_action { probes_custom_decode_t *decoder; }; +typedef enum probes_insn (probes_check_t)(probes_opcode_t, + struct arch_probes_insn *, + const struct decode_header *); + +struct decode_checker { + probes_check_t *checker; +}; + #define DECODE_END \ {.bits = DECODE_TYPE_END} @@ -402,6 +410,7 @@ probes_insn_handler_t probes_emulate_none; int __kprobes probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, const union decode_item *table, bool thumb, bool emulate, - const union decode_action *actions); + const union decode_action *actions, + const struct decode_checker **checkers); #endif diff --git a/arch/arm/probes/kprobes/actions-arm.c b/arch/arm/probes/kprobes/actions-arm.c index 2206f2d..fbd93a9 100644 --- a/arch/arm/probes/kprobes/actions-arm.c +++ b/arch/arm/probes/kprobes/actions-arm.c @@ -339,3 +339,5 @@ const union decode_action kprobes_arm_actions[NUM_PROBES_ARM_ACTIONS] = { [PROBES_BRANCH] = {.handler = simulate_bbl}, [PROBES_LDMSTM] = {.decoder = kprobe_decode_ldmstm} }; + +const struct decode_checker *kprobes_arm_checkers[] = {NULL}; diff --git a/arch/arm/probes/kprobes/actions-thumb.c b/arch/arm/probes/kprobes/actions-thumb.c index 6c4e60b..2796121 100644 --- a/arch/arm/probes/kprobes/actions-thumb.c +++ b/arch/arm/probes/kprobes/actions-thumb.c @@ -664,3 +664,6 @@ const union decode_action kprobes_t32_actions[NUM_PROBES_T32_ACTIONS] = { [PROBES_T32_MUL_ADD_LONG] = { .handler = t32_emulate_rdlo12rdhi8rn16rm0_noflags}, }; + +const struct decode_checker *kprobes_t32_checkers[] = {NULL}; +const struct decode_checker *kprobes_t16_checkers[] = {NULL}; diff --git a/arch/arm/probes/kprobes/core.c b/arch/arm/probes/kprobes/core.c index 701f49d..74f3dc3 100644 --- a/arch/arm/probes/kprobes/core.c +++ b/arch/arm/probes/kprobes/core.c @@ -61,6 +61,7 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p) kprobe_decode_insn_t *decode_insn; const union decode_action *actions; int is; + const struct decode_checker **checkers; if (in_exception_text(addr)) return -EINVAL; @@ -74,9 +75,11 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p) insn = __opcode_thumb32_compose(insn, inst2); decode_insn = thumb32_probes_decode_insn; actions = kprobes_t32_actions; + checkers = kprobes_t32_checkers; } else { decode_insn = thumb16_probes_decode_insn; actions = kprobes_t16_actions; + checkers = kprobes_t16_checkers; } #else /* !CONFIG_THUMB2_KERNEL */ thumb = false; @@ -85,12 +88,13 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p) insn = __mem_to_opcode_arm(*p->addr); decode_insn = arm_probes_decode_insn; actions = kprobes_arm_actions; + checkers = kprobes_arm_checkers; #endif p->opcode = insn; p->ainsn.insn = tmp_insn; - switch ((*decode_insn)(insn, &p->ainsn, true, actions)) { + switch ((*decode_insn)(insn, &p->ainsn, true, actions, checkers)) { case INSN_REJECTED: /* not supported */ return -EINVAL; diff --git a/arch/arm/probes/kprobes/core.h b/arch/arm/probes/kprobes/core.h index 2e1e5a3..f88c79f 100644 --- a/arch/arm/probes/kprobes/core.h +++ b/arch/arm/probes/kprobes/core.h @@ -37,16 +37,19 @@ kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_probes_insn *asi, typedef enum probes_insn (kprobe_decode_insn_t)(probes_opcode_t, struct arch_probes_insn *, bool, - const union decode_action *); + const union decode_action *, + const struct decode_checker *[*]); #ifdef CONFIG_THUMB2_KERNEL extern const union decode_action kprobes_t32_actions[]; extern const union decode_action kprobes_t16_actions[]; - +extern const struct decode_checker *kprobes_t32_checkers[]; +extern const struct decode_checker *kprobes_t16_checkers[]; #else /* !CONFIG_THUMB2_KERNEL */ extern const union decode_action kprobes_arm_actions[]; +extern const struct decode_checker *kprobes_arm_checkers[]; #endif diff --git a/arch/arm/probes/uprobes/core.c b/arch/arm/probes/uprobes/core.c index b2954f6..d1329f1 100644 --- a/arch/arm/probes/uprobes/core.c +++ b/arch/arm/probes/uprobes/core.c @@ -88,7 +88,7 @@ int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, auprobe->ixol[1] = __opcode_to_mem_arm(UPROBE_SS_ARM_INSN); ret = arm_probes_decode_insn(insn, &auprobe->asi, false, - uprobes_probes_actions); + uprobes_probes_actions, NULL); switch (ret) { case INSN_REJECTED: return -EINVAL; -- cgit v0.10.2 From 6624cf651f1a14363d0385f36dc255d304ac7ebb Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Mon, 5 Jan 2015 19:29:21 +0800 Subject: ARM: kprobes: collects stack consumption for store instructions This patch uses the previously introduced checker functionality on store instructions to record their stack consumption information to arch_probes_insn. Signed-off-by: Wang Nan Reviewed-by: Jon Medhurst Signed-off-by: Jon Medhurst diff --git a/arch/arm/include/asm/probes.h b/arch/arm/include/asm/probes.h index 806cfe6..6026deb 100644 --- a/arch/arm/include/asm/probes.h +++ b/arch/arm/include/asm/probes.h @@ -38,6 +38,7 @@ struct arch_probes_insn { probes_check_cc *insn_check_cc; probes_insn_singlestep_t *insn_singlestep; probes_insn_fn_t *insn_fn; + int stack_space; }; #endif diff --git a/arch/arm/probes/decode.c b/arch/arm/probes/decode.c index c7d4420..f9d7c42 100644 --- a/arch/arm/probes/decode.c +++ b/arch/arm/probes/decode.c @@ -425,6 +425,16 @@ probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, */ probes_opcode_t origin_insn = insn; + /* + * stack_space is initialized to 0 here. Checker functions + * should update is value if they find this is a stack store + * instruction: positive value means bytes of stack usage, + * negitive value means unable to determine stack usage + * statically. For instruction doesn't store to stack, checker + * do nothing with it. + */ + asi->stack_space = 0; + if (emulate) insn = prepare_emulated_insn(insn, asi, thumb); diff --git a/arch/arm/probes/kprobes/Makefile b/arch/arm/probes/kprobes/Makefile index eb38a42..bc8d504 100644 --- a/arch/arm/probes/kprobes/Makefile +++ b/arch/arm/probes/kprobes/Makefile @@ -1,11 +1,11 @@ -obj-$(CONFIG_KPROBES) += core.o actions-common.o +obj-$(CONFIG_KPROBES) += core.o actions-common.o checkers-common.o obj-$(CONFIG_ARM_KPROBES_TEST) += test-kprobes.o test-kprobes-objs := test-core.o ifdef CONFIG_THUMB2_KERNEL -obj-$(CONFIG_KPROBES) += actions-thumb.o +obj-$(CONFIG_KPROBES) += actions-thumb.o checkers-thumb.o test-kprobes-objs += test-thumb.o else -obj-$(CONFIG_KPROBES) += actions-arm.o +obj-$(CONFIG_KPROBES) += actions-arm.o checkers-arm.o test-kprobes-objs += test-arm.o endif diff --git a/arch/arm/probes/kprobes/actions-arm.c b/arch/arm/probes/kprobes/actions-arm.c index fbd93a9..06988ef 100644 --- a/arch/arm/probes/kprobes/actions-arm.c +++ b/arch/arm/probes/kprobes/actions-arm.c @@ -64,6 +64,7 @@ #include "../decode-arm.h" #include "core.h" +#include "checkers.h" #if __LINUX_ARM_ARCH__ >= 6 #define BLX(reg) "blx "reg" \n\t" @@ -340,4 +341,4 @@ const union decode_action kprobes_arm_actions[NUM_PROBES_ARM_ACTIONS] = { [PROBES_LDMSTM] = {.decoder = kprobe_decode_ldmstm} }; -const struct decode_checker *kprobes_arm_checkers[] = {NULL}; +const struct decode_checker *kprobes_arm_checkers[] = {arm_stack_checker, NULL}; diff --git a/arch/arm/probes/kprobes/actions-thumb.c b/arch/arm/probes/kprobes/actions-thumb.c index 2796121..07cfd9b 100644 --- a/arch/arm/probes/kprobes/actions-thumb.c +++ b/arch/arm/probes/kprobes/actions-thumb.c @@ -15,6 +15,7 @@ #include "../decode-thumb.h" #include "core.h" +#include "checkers.h" /* These emulation encodings are functionally equivalent... */ #define t32_emulate_rd8rn16rm0ra12_noflags \ @@ -665,5 +666,5 @@ const union decode_action kprobes_t32_actions[NUM_PROBES_T32_ACTIONS] = { .handler = t32_emulate_rdlo12rdhi8rn16rm0_noflags}, }; -const struct decode_checker *kprobes_t32_checkers[] = {NULL}; -const struct decode_checker *kprobes_t16_checkers[] = {NULL}; +const struct decode_checker *kprobes_t32_checkers[] = {t32_stack_checker, NULL}; +const struct decode_checker *kprobes_t16_checkers[] = {t16_stack_checker, NULL}; diff --git a/arch/arm/probes/kprobes/checkers-arm.c b/arch/arm/probes/kprobes/checkers-arm.c new file mode 100644 index 0000000..f817663 --- /dev/null +++ b/arch/arm/probes/kprobes/checkers-arm.c @@ -0,0 +1,99 @@ +/* + * arch/arm/probes/kprobes/checkers-arm.c + * + * Copyright (C) 2014 Huawei Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include "../decode.h" +#include "../decode-arm.h" +#include "checkers.h" + +static enum probes_insn __kprobes arm_check_stack(probes_opcode_t insn, + struct arch_probes_insn *asi, + const struct decode_header *h) +{ + /* + * PROBES_LDRSTRD, PROBES_LDMSTM, PROBES_STORE, + * PROBES_STORE_EXTRA may get here. Simply mark all normal + * insns as STACK_USE_NONE. + */ + static const union decode_item table[] = { + /* + * 'STR{,D,B,H}, Rt, [Rn, Rm]' should be marked as UNKNOWN + * if Rn or Rm is SP. + * x + * STR (register) cccc 011x x0x0 xxxx xxxx xxxx xxxx xxxx + * STRB (register) cccc 011x x1x0 xxxx xxxx xxxx xxxx xxxx + */ + DECODE_OR (0x0e10000f, 0x0600000d), + DECODE_OR (0x0e1f0000, 0x060d0000), + + /* + * x + * STRD (register) cccc 000x x0x0 xxxx xxxx xxxx 1111 xxxx + * STRH (register) cccc 000x x0x0 xxxx xxxx xxxx 1011 xxxx + */ + DECODE_OR (0x0e5000bf, 0x000000bd), + DECODE_CUSTOM (0x0e5f00b0, 0x000d00b0, STACK_USE_UNKNOWN), + + /* + * For PROBES_LDMSTM, only stmdx sp, [...] need to examine + * + * Bit B/A (bit 24) encodes arithmetic operation order. 1 means + * before, 0 means after. + * Bit I/D (bit 23) encodes arithmetic operation. 1 means + * increment, 0 means decrement. + * + * So: + * B I + * / / + * A D | Rn | + * STMDX SP, [...] cccc 100x 00x0 xxxx xxxx xxxx xxxx xxxx + */ + DECODE_CUSTOM (0x0edf0000, 0x080d0000, STACK_USE_STMDX), + + /* P U W | Rn | Rt | imm12 |*/ + /* STR (immediate) cccc 010x x0x0 1101 xxxx xxxx xxxx xxxx */ + /* STRB (immediate) cccc 010x x1x0 1101 xxxx xxxx xxxx xxxx */ + /* P U W | Rn | Rt |imm4| |imm4|*/ + /* STRD (immediate) cccc 000x x1x0 1101 xxxx xxxx 1111 xxxx */ + /* STRH (immediate) cccc 000x x1x0 1101 xxxx xxxx 1011 xxxx */ + /* + * index = (P == '1'); add = (U == '1'). + * Above insns with: + * index == 0 (str{,d,h} rx, [sp], #+/-imm) or + * add == 1 (str{,d,h} rx, [sp, #+]) + * should be STACK_USE_NONE. + * Only str{,b,d,h} rx,[sp,#-n] (P == 1 and U == 0) are + * required to be examined. + */ + /* STR{,B} Rt,[SP,#-n] cccc 0101 0xx0 1101 xxxx xxxx xxxx xxxx */ + DECODE_CUSTOM (0x0f9f0000, 0x050d0000, STACK_USE_FIXED_XXX), + + /* STR{D,H} Rt,[SP,#-n] cccc 0001 01x0 1101 xxxx xxxx 1x11 xxxx */ + DECODE_CUSTOM (0x0fdf00b0, 0x014d00b0, STACK_USE_FIXED_X0X), + + /* fall through */ + DECODE_CUSTOM (0, 0, STACK_USE_NONE), + DECODE_END + }; + + return probes_decode_insn(insn, asi, table, false, false, stack_check_actions, NULL); +} + +const struct decode_checker arm_stack_checker[NUM_PROBES_ARM_ACTIONS] = { + [PROBES_LDRSTRD] = {.checker = arm_check_stack}, + [PROBES_STORE_EXTRA] = {.checker = arm_check_stack}, + [PROBES_STORE] = {.checker = arm_check_stack}, + [PROBES_LDMSTM] = {.checker = arm_check_stack}, +}; diff --git a/arch/arm/probes/kprobes/checkers-common.c b/arch/arm/probes/kprobes/checkers-common.c new file mode 100644 index 0000000..971119c --- /dev/null +++ b/arch/arm/probes/kprobes/checkers-common.c @@ -0,0 +1,101 @@ +/* + * arch/arm/probes/kprobes/checkers-common.c + * + * Copyright (C) 2014 Huawei Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include "../decode.h" +#include "../decode-arm.h" +#include "checkers.h" + +enum probes_insn checker_stack_use_none(probes_opcode_t insn, + struct arch_probes_insn *asi, + const struct decode_header *h) +{ + asi->stack_space = 0; + return INSN_GOOD_NO_SLOT; +} + +enum probes_insn checker_stack_use_unknown(probes_opcode_t insn, + struct arch_probes_insn *asi, + const struct decode_header *h) +{ + asi->stack_space = -1; + return INSN_GOOD_NO_SLOT; +} + +#ifdef CONFIG_THUMB2_KERNEL +enum probes_insn checker_stack_use_imm_0xx(probes_opcode_t insn, + struct arch_probes_insn *asi, + const struct decode_header *h) +{ + int imm = insn & 0xff; + asi->stack_space = imm; + return INSN_GOOD_NO_SLOT; +} + +/* + * Different from other insn uses imm8, the real addressing offset of + * STRD in T32 encoding should be imm8 * 4. See ARMARM description. + */ +enum probes_insn checker_stack_use_t32strd(probes_opcode_t insn, + struct arch_probes_insn *asi, + const struct decode_header *h) +{ + int imm = insn & 0xff; + asi->stack_space = imm << 2; + return INSN_GOOD_NO_SLOT; +} +#else +enum probes_insn checker_stack_use_imm_x0x(probes_opcode_t insn, + struct arch_probes_insn *asi, + const struct decode_header *h) +{ + int imm = ((insn & 0xf00) >> 4) + (insn & 0xf); + asi->stack_space = imm; + return INSN_GOOD_NO_SLOT; +} +#endif + +enum probes_insn checker_stack_use_imm_xxx(probes_opcode_t insn, + struct arch_probes_insn *asi, + const struct decode_header *h) +{ + int imm = insn & 0xfff; + asi->stack_space = imm; + return INSN_GOOD_NO_SLOT; +} + +enum probes_insn checker_stack_use_stmdx(probes_opcode_t insn, + struct arch_probes_insn *asi, + const struct decode_header *h) +{ + unsigned int reglist = insn & 0xffff; + int pbit = insn & (1 << 24); + asi->stack_space = (hweight32(reglist) - (!pbit ? 1 : 0)) * 4; + + return INSN_GOOD_NO_SLOT; +} + +const union decode_action stack_check_actions[] = { + [STACK_USE_NONE] = {.decoder = checker_stack_use_none}, + [STACK_USE_UNKNOWN] = {.decoder = checker_stack_use_unknown}, +#ifdef CONFIG_THUMB2_KERNEL + [STACK_USE_FIXED_0XX] = {.decoder = checker_stack_use_imm_0xx}, + [STACK_USE_T32STRD] = {.decoder = checker_stack_use_t32strd}, +#else + [STACK_USE_FIXED_X0X] = {.decoder = checker_stack_use_imm_x0x}, +#endif + [STACK_USE_FIXED_XXX] = {.decoder = checker_stack_use_imm_xxx}, + [STACK_USE_STMDX] = {.decoder = checker_stack_use_stmdx}, +}; diff --git a/arch/arm/probes/kprobes/checkers-thumb.c b/arch/arm/probes/kprobes/checkers-thumb.c new file mode 100644 index 0000000..d608e3b --- /dev/null +++ b/arch/arm/probes/kprobes/checkers-thumb.c @@ -0,0 +1,110 @@ +/* + * arch/arm/probes/kprobes/checkers-thumb.c + * + * Copyright (C) 2014 Huawei Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include "../decode.h" +#include "../decode-thumb.h" +#include "checkers.h" + +static enum probes_insn __kprobes t32_check_stack(probes_opcode_t insn, + struct arch_probes_insn *asi, + const struct decode_header *h) +{ + /* + * PROBES_T32_LDMSTM, PROBES_T32_LDRDSTRD and PROBES_T32_LDRSTR + * may get here. Simply mark all normal insns as STACK_USE_NONE. + */ + static const union decode_item table[] = { + + /* + * First, filter out all ldr insns to make our life easier. + * Following load insns may come here: + * LDM, LDRD, LDR. + * In T32 encoding, bit 20 is enough for distinguishing + * load and store. All load insns have this bit set, when + * all store insns have this bit clear. + */ + DECODE_CUSTOM (0x00100000, 0x00100000, STACK_USE_NONE), + + /* + * Mark all 'STR{,B,H}, Rt, [Rn, Rm]' as STACK_USE_UNKNOWN + * if Rn or Rm is SP. T32 doesn't encode STRD. + */ + /* xx | Rn | Rt | | Rm |*/ + /* STR (register) 1111 1000 0100 xxxx xxxx 0000 00xx xxxx */ + /* STRB (register) 1111 1000 0000 xxxx xxxx 0000 00xx xxxx */ + /* STRH (register) 1111 1000 0010 xxxx xxxx 0000 00xx xxxx */ + /* INVALID INSN 1111 1000 0110 xxxx xxxx 0000 00xx xxxx */ + /* By Introducing INVALID INSN, bit 21 and 22 can be ignored. */ + DECODE_OR (0xff9f0fc0, 0xf80d0000), + DECODE_CUSTOM (0xff900fcf, 0xf800000d, STACK_USE_UNKNOWN), + + + /* xx | Rn | Rt | PUW| imm8 |*/ + /* STR (imm 8) 1111 1000 0100 1101 xxxx 110x xxxx xxxx */ + /* STRB (imm 8) 1111 1000 0000 1101 xxxx 110x xxxx xxxx */ + /* STRH (imm 8) 1111 1000 0010 1101 xxxx 110x xxxx xxxx */ + /* INVALID INSN 1111 1000 0110 1101 xxxx 110x xxxx xxxx */ + /* Only consider U == 0 and P == 1: strx rx, [sp, #-] */ + DECODE_CUSTOM (0xff9f0e00, 0xf80d0c00, STACK_USE_FIXED_0XX), + + /* For STR{,B,H} (imm 12), offset is always positive, so ignore them. */ + + /* P U W | Rn | Rt | Rt2| imm8 |*/ + /* STRD (immediate) 1110 1001 01x0 1101 xxxx xxxx xxxx xxxx */ + /* + * Only consider U == 0 and P == 1. + * Also note that STRD in T32 encoding is special: + * imm = ZeroExtend(imm8:'00', 32) + */ + DECODE_CUSTOM (0xffdf0000, 0xe94d0000, STACK_USE_T32STRD), + + /* | Rn | */ + /* STMDB 1110 1001 00x0 1101 xxxx xxxx xxxx xxxx */ + DECODE_CUSTOM (0xffdf0000, 0xe90d0000, STACK_USE_STMDX), + + /* fall through */ + DECODE_CUSTOM (0, 0, STACK_USE_NONE), + DECODE_END + }; + + return probes_decode_insn(insn, asi, table, false, false, stack_check_actions, NULL); +} + +const struct decode_checker t32_stack_checker[NUM_PROBES_T32_ACTIONS] = { + [PROBES_T32_LDMSTM] = {.checker = t32_check_stack}, + [PROBES_T32_LDRDSTRD] = {.checker = t32_check_stack}, + [PROBES_T32_LDRSTR] = {.checker = t32_check_stack}, +}; + +/* + * See following comments. This insn must be 'push'. + */ +static enum probes_insn __kprobes t16_check_stack(probes_opcode_t insn, + struct arch_probes_insn *asi, + const struct decode_header *h) +{ + unsigned int reglist = insn & 0x1ff; + asi->stack_space = hweight32(reglist) * 4; + return INSN_GOOD; +} + +/* + * T16 encoding is simple: only the 'push' insn can need extra stack space. + * Other insns, like str, can only use r0-r7 as Rn. + */ +const struct decode_checker t16_stack_checker[NUM_PROBES_T16_ACTIONS] = { + [PROBES_T16_PUSH] = {.checker = t16_check_stack}, +}; diff --git a/arch/arm/probes/kprobes/checkers.h b/arch/arm/probes/kprobes/checkers.h new file mode 100644 index 0000000..bddfa0e --- /dev/null +++ b/arch/arm/probes/kprobes/checkers.h @@ -0,0 +1,54 @@ +/* + * arch/arm/probes/kprobes/checkers.h + * + * Copyright (C) 2014 Huawei Inc. + * + * 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. + */ +#ifndef _ARM_KERNEL_PROBES_CHECKERS_H +#define _ARM_KERNEL_PROBES_CHECKERS_H + +#include +#include +#include "../decode.h" + +extern probes_check_t checker_stack_use_none; +extern probes_check_t checker_stack_use_unknown; +#ifdef CONFIG_THUMB2_KERNEL +extern probes_check_t checker_stack_use_imm_0xx; +#else +extern probes_check_t checker_stack_use_imm_x0x; +#endif +extern probes_check_t checker_stack_use_imm_xxx; +extern probes_check_t checker_stack_use_stmdx; + +enum { + STACK_USE_NONE, + STACK_USE_UNKNOWN, +#ifdef CONFIG_THUMB2_KERNEL + STACK_USE_FIXED_0XX, + STACK_USE_T32STRD, +#else + STACK_USE_FIXED_X0X, +#endif + STACK_USE_FIXED_XXX, + STACK_USE_STMDX, + NUM_STACK_USE_TYPES +}; + +extern const union decode_action stack_check_actions[]; + +#ifndef CONFIG_THUMB2_KERNEL +extern const struct decode_checker arm_stack_checker[]; +#else +#endif +extern const struct decode_checker t32_stack_checker[]; +extern const struct decode_checker t16_stack_checker[]; +#endif -- cgit v0.10.2 From a0266c214fab21371a499e6ab1c9385cc6589189 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Mon, 5 Jan 2015 19:29:25 +0800 Subject: ARM: kprobes: disallow probing stack consuming instructions This patch prohibits probing instructions for which the stack requirements are unable to be determined statically. Some test cases are found not work again after the modification, this patch also removes them. Signed-off-by: Wang Nan Reviewed-by: Jon Medhurst Signed-off-by: Jon Medhurst diff --git a/arch/arm/include/asm/kprobes.h b/arch/arm/include/asm/kprobes.h index 49fa0df..56f9ac6 100644 --- a/arch/arm/include/asm/kprobes.h +++ b/arch/arm/include/asm/kprobes.h @@ -22,7 +22,6 @@ #define __ARCH_WANT_KPROBES_INSN_SLOT #define MAX_INSN_SIZE 2 -#define MAX_STACK_SIZE 64 /* 32 would probably be OK */ #define flush_insn_slot(p) do { } while (0) #define kretprobe_blacklist_size 0 diff --git a/arch/arm/include/asm/probes.h b/arch/arm/include/asm/probes.h index 6026deb..cd9e815 100644 --- a/arch/arm/include/asm/probes.h +++ b/arch/arm/include/asm/probes.h @@ -19,6 +19,8 @@ #ifndef _ASM_PROBES_H #define _ASM_PROBES_H +#ifndef __ASSEMBLY__ + typedef u32 probes_opcode_t; struct arch_probes_insn; @@ -41,4 +43,14 @@ struct arch_probes_insn { int stack_space; }; +#endif /* __ASSEMBLY__ */ + +/* + * We assume one instruction can consume at most 64 bytes stack, which is + * 'push {r0-r15}'. Instructions consume more or unknown stack space like + * 'str r0, [sp, #-80]' and 'str r0, [sp, r1]' should be prohibit to probe. + * Both kprobe and jprobe use this macro. + */ +#define MAX_STACK_SIZE 64 + #endif diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S index 2f5555d..672b219 100644 --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S @@ -31,6 +31,7 @@ #include "entry-header.S" #include +#include /* * Interrupt handling. @@ -249,7 +250,7 @@ __und_svc: @ If a kprobe is about to simulate a "stmdb sp..." instruction, @ it obviously needs free stack space which then will belong to @ the saved context. - svc_entry 64 + svc_entry MAX_STACK_SIZE #else svc_entry #endif diff --git a/arch/arm/probes/kprobes/core.c b/arch/arm/probes/kprobes/core.c index 74f3dc3..3a58db4 100644 --- a/arch/arm/probes/kprobes/core.c +++ b/arch/arm/probes/kprobes/core.c @@ -115,6 +115,15 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p) break; } + /* + * Never instrument insn like 'str r0, [sp, +/-r1]'. Also, insn likes + * 'str r0, [sp, #-68]' should also be prohibited. + * See __und_svc. + */ + if ((p->ainsn.stack_space < 0) || + (p->ainsn.stack_space > MAX_STACK_SIZE)) + return -EINVAL; + return 0; } diff --git a/arch/arm/probes/kprobes/test-arm.c b/arch/arm/probes/kprobes/test-arm.c index d9a1255..fdeb300 100644 --- a/arch/arm/probes/kprobes/test-arm.c +++ b/arch/arm/probes/kprobes/test-arm.c @@ -476,7 +476,8 @@ void kprobe_arm_test_cases(void) TEST_GROUP("Extra load/store instructions") TEST_RPR( "strh r",0, VAL1,", [r",1, 48,", -r",2, 24,"]") - TEST_RPR( "streqh r",14,VAL2,", [r",13,0, ", r",12, 48,"]") + TEST_RPR( "streqh r",14,VAL2,", [r",11,0, ", r",12, 48,"]") + TEST_UNSUPPORTED( "streqh r14, [r13, r12]") TEST_RPR( "strh r",1, VAL1,", [r",2, 24,", r",3, 48,"]!") TEST_RPR( "strneh r",12,VAL2,", [r",11,48,", -r",10,24,"]!") TEST_RPR( "strh r",2, VAL1,", [r",3, 24,"], r",4, 48,"") @@ -565,7 +566,8 @@ void kprobe_arm_test_cases(void) #if __LINUX_ARM_ARCH__ >= 5 TEST_RPR( "strd r",0, VAL1,", [r",1, 48,", -r",2,24,"]") - TEST_RPR( "strccd r",8, VAL2,", [r",13,0, ", r",12,48,"]") + TEST_RPR( "strccd r",8, VAL2,", [r",11,0, ", r",12,48,"]") + TEST_UNSUPPORTED( "strccd r8, [r13, r12]") TEST_RPR( "strd r",4, VAL1,", [r",2, 24,", r",3, 48,"]!") TEST_RPR( "strcsd r",12,VAL2,", [r",11,48,", -r",10,24,"]!") TEST_RPR( "strd r",2, VAL1,", [r",5, 24,"], r",4,48,"") @@ -638,13 +640,15 @@ void kprobe_arm_test_cases(void) TEST_RP( "str"byte" r",2, VAL1,", [r",3, 24,"], #48") \ TEST_RP( "str"byte" r",10,VAL2,", [r",9, 64,"], #-48") \ TEST_RPR("str"byte" r",0, VAL1,", [r",1, 48,", -r",2, 24,"]") \ - TEST_RPR("str"byte" r",14,VAL2,", [r",13,0, ", r",12, 48,"]") \ + TEST_RPR("str"byte" r",14,VAL2,", [r",11,0, ", r",12, 48,"]") \ + TEST_UNSUPPORTED("str"byte" r14, [r13, r12]") \ TEST_RPR("str"byte" r",1, VAL1,", [r",2, 24,", r",3, 48,"]!") \ TEST_RPR("str"byte" r",12,VAL2,", [r",11,48,", -r",10,24,"]!") \ TEST_RPR("str"byte" r",2, VAL1,", [r",3, 24,"], r",4, 48,"") \ TEST_RPR("str"byte" r",10,VAL2,", [r",9, 48,"], -r",11,24,"") \ TEST_RPR("str"byte" r",0, VAL1,", [r",1, 24,", r",2, 32,", asl #1]")\ - TEST_RPR("str"byte" r",14,VAL2,", [r",13,0, ", r",12, 32,", lsr #2]")\ + TEST_RPR("str"byte" r",14,VAL2,", [r",11,0, ", r",12, 32,", lsr #2]")\ + TEST_UNSUPPORTED("str"byte" r14, [r13, r12, lsr #2]")\ TEST_RPR("str"byte" r",1, VAL1,", [r",2, 24,", r",3, 32,", asr #3]!")\ TEST_RPR("str"byte" r",12,VAL2,", [r",11,24,", r",10, 4,", ror #31]!")\ TEST_P( "ldr"byte" r0, [r",0, 24,", #-2]") \ @@ -668,12 +672,12 @@ void kprobe_arm_test_cases(void) LOAD_STORE("") TEST_P( "str pc, [r",0,0,", #15*4]") - TEST_R( "str pc, [sp, r",2,15*4,"]") + TEST_UNSUPPORTED( "str pc, [sp, r2]") TEST_BF( "ldr pc, [sp, #15*4]") TEST_BF_R("ldr pc, [sp, r",2,15*4,"]") TEST_P( "str sp, [r",0,0,", #13*4]") - TEST_R( "str sp, [sp, r",2,13*4,"]") + TEST_UNSUPPORTED( "str sp, [sp, r2]") TEST_BF( "ldr sp, [sp, #13*4]") TEST_BF_R("ldr sp, [sp, r",2,13*4,"]") -- cgit v0.10.2 From 8d257e95a9e643518e72232bf852b054a3d06c95 Mon Sep 17 00:00:00 2001 From: "Jon Medhurst (Tixy)" Date: Mon, 5 Jan 2015 19:29:29 +0800 Subject: ARM: kprobes: Add test cases for stack consuming instructions These have extra 'checker' functions associated with them so lets make sure those get covered by testing. As they may create uninitialised space on the stack we also update the test code to ensure such space is consistent between test runs. This is done by disabling interrupts in setup_test_context(). Signed-off-by: Jon Medhurst diff --git a/arch/arm/probes/kprobes/test-arm.c b/arch/arm/probes/kprobes/test-arm.c index fdeb300..9b3b1b4 100644 --- a/arch/arm/probes/kprobes/test-arm.c +++ b/arch/arm/probes/kprobes/test-arm.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "test-core.h" @@ -478,6 +479,7 @@ void kprobe_arm_test_cases(void) TEST_RPR( "strh r",0, VAL1,", [r",1, 48,", -r",2, 24,"]") TEST_RPR( "streqh r",14,VAL2,", [r",11,0, ", r",12, 48,"]") TEST_UNSUPPORTED( "streqh r14, [r13, r12]") + TEST_UNSUPPORTED( "streqh r14, [r12, r13]") TEST_RPR( "strh r",1, VAL1,", [r",2, 24,", r",3, 48,"]!") TEST_RPR( "strneh r",12,VAL2,", [r",11,48,", -r",10,24,"]!") TEST_RPR( "strh r",2, VAL1,", [r",3, 24,"], r",4, 48,"") @@ -502,6 +504,9 @@ void kprobe_arm_test_cases(void) TEST_RP( "strplh r",12,VAL2,", [r",11,24,", #-4]!") TEST_RP( "strh r",2, VAL1,", [r",3, 24,"], #48") TEST_RP( "strh r",10,VAL2,", [r",9, 64,"], #-48") + TEST_RP( "strh r",3, VAL1,", [r",13,TEST_MEMORY_SIZE,", #-"__stringify(MAX_STACK_SIZE)"]!") + TEST_UNSUPPORTED("strh r3, [r13, #-"__stringify(MAX_STACK_SIZE)"-8]!") + TEST_RP( "strh r",4, VAL1,", [r",14,TEST_MEMORY_SIZE,", #-"__stringify(MAX_STACK_SIZE)"-8]!") TEST_UNSUPPORTED(__inst_arm(0xe1efc3b0) " @ strh r12, [pc, #48]!") TEST_UNSUPPORTED(__inst_arm(0xe0c9f3b0) " @ strh pc, [r9], #48") @@ -568,6 +573,7 @@ void kprobe_arm_test_cases(void) TEST_RPR( "strd r",0, VAL1,", [r",1, 48,", -r",2,24,"]") TEST_RPR( "strccd r",8, VAL2,", [r",11,0, ", r",12,48,"]") TEST_UNSUPPORTED( "strccd r8, [r13, r12]") + TEST_UNSUPPORTED( "strccd r8, [r12, r13]") TEST_RPR( "strd r",4, VAL1,", [r",2, 24,", r",3, 48,"]!") TEST_RPR( "strcsd r",12,VAL2,", [r",11,48,", -r",10,24,"]!") TEST_RPR( "strd r",2, VAL1,", [r",5, 24,"], r",4,48,"") @@ -591,6 +597,9 @@ void kprobe_arm_test_cases(void) TEST_RP( "strvcd r",12,VAL2,", [r",11,24,", #-16]!") TEST_RP( "strd r",2, VAL1,", [r",4, 24,"], #48") TEST_RP( "strd r",10,VAL2,", [r",9, 64,"], #-48") + TEST_RP( "strd r",6, VAL1,", [r",13,TEST_MEMORY_SIZE,", #-"__stringify(MAX_STACK_SIZE)"]!") + TEST_UNSUPPORTED("strd r6, [r13, #-"__stringify(MAX_STACK_SIZE)"-8]!") + TEST_RP( "strd r",4, VAL1,", [r",12,TEST_MEMORY_SIZE,", #-"__stringify(MAX_STACK_SIZE)"-8]!") TEST_UNSUPPORTED(__inst_arm(0xe1efc3f0) " @ strd r12, [pc, #48]!") TEST_P( "ldrd r0, [r",0, 24,", #-8]") @@ -639,16 +648,20 @@ void kprobe_arm_test_cases(void) TEST_RP( "str"byte" r",12,VAL2,", [r",11,24,", #-4]!") \ TEST_RP( "str"byte" r",2, VAL1,", [r",3, 24,"], #48") \ TEST_RP( "str"byte" r",10,VAL2,", [r",9, 64,"], #-48") \ + TEST_RP( "str"byte" r",3, VAL1,", [r",13,TEST_MEMORY_SIZE,", #-"__stringify(MAX_STACK_SIZE)"]!") \ + TEST_UNSUPPORTED("str"byte" r3, [r13, #-"__stringify(MAX_STACK_SIZE)"-8]!") \ + TEST_RP( "str"byte" r",4, VAL1,", [r",10,TEST_MEMORY_SIZE,", #-"__stringify(MAX_STACK_SIZE)"-8]!") \ TEST_RPR("str"byte" r",0, VAL1,", [r",1, 48,", -r",2, 24,"]") \ TEST_RPR("str"byte" r",14,VAL2,", [r",11,0, ", r",12, 48,"]") \ - TEST_UNSUPPORTED("str"byte" r14, [r13, r12]") \ + TEST_UNSUPPORTED("str"byte" r14, [r13, r12]") \ + TEST_UNSUPPORTED("str"byte" r14, [r12, r13]") \ TEST_RPR("str"byte" r",1, VAL1,", [r",2, 24,", r",3, 48,"]!") \ TEST_RPR("str"byte" r",12,VAL2,", [r",11,48,", -r",10,24,"]!") \ TEST_RPR("str"byte" r",2, VAL1,", [r",3, 24,"], r",4, 48,"") \ TEST_RPR("str"byte" r",10,VAL2,", [r",9, 48,"], -r",11,24,"") \ TEST_RPR("str"byte" r",0, VAL1,", [r",1, 24,", r",2, 32,", asl #1]")\ TEST_RPR("str"byte" r",14,VAL2,", [r",11,0, ", r",12, 32,", lsr #2]")\ - TEST_UNSUPPORTED("str"byte" r14, [r13, r12, lsr #2]")\ + TEST_UNSUPPORTED("str"byte" r14, [r13, r12, lsr #2]") \ TEST_RPR("str"byte" r",1, VAL1,", [r",2, 24,", r",3, 32,", asr #3]!")\ TEST_RPR("str"byte" r",12,VAL2,", [r",11,24,", r",10, 4,", ror #31]!")\ TEST_P( "ldr"byte" r0, [r",0, 24,", #-2]") \ diff --git a/arch/arm/probes/kprobes/test-core.c b/arch/arm/probes/kprobes/test-core.c index 7ab633d..7c5ddd5 100644 --- a/arch/arm/probes/kprobes/test-core.c +++ b/arch/arm/probes/kprobes/test-core.c @@ -1196,6 +1196,13 @@ static void setup_test_context(struct pt_regs *regs) regs->uregs[arg->reg] = (unsigned long)current_stack + arg->val; memory_needs_checking = true; + /* + * Test memory at an address below SP is in danger of + * being altered by an interrupt occurring and pushing + * data onto the stack. Disable interrupts to stop this. + */ + if (arg->reg == 13) + regs->ARM_cpsr |= PSR_I_BIT; break; } case ARG_TYPE_MEM: { @@ -1272,6 +1279,8 @@ test_after_pre_handler(struct kprobe *p, struct pt_regs *regs) /* Undo any changes done to SP by the test case */ regs->ARM_sp = (unsigned long)current_stack; + /* Enable interrupts in case setup_test_context disabled them */ + regs->ARM_cpsr &= ~PSR_I_BIT; container_of(p, struct test_probe, kprobe)->hit = test_instance; return 0; diff --git a/arch/arm/probes/kprobes/test-thumb.c b/arch/arm/probes/kprobes/test-thumb.c index 6c6e9a9..e8cf193 100644 --- a/arch/arm/probes/kprobes/test-thumb.c +++ b/arch/arm/probes/kprobes/test-thumb.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "test-core.h" @@ -416,6 +417,9 @@ void kprobe_thumb32_test_cases(void) TEST_RR( "strd r",14,VAL2,", r",12,VAL1,", [sp, #16]!") TEST_RRP("strd r",1, VAL1,", r",0, VAL2,", [r",7, 24,"], #16") TEST_RR( "strd r",7, VAL2,", r",8, VAL1,", [sp], #-16") + TEST_RRP("strd r",6, VAL1,", r",7, VAL2,", [r",13, TEST_MEMORY_SIZE,", #-"__stringify(MAX_STACK_SIZE)"]!") + TEST_UNSUPPORTED("strd r6, r7, [r13, #-"__stringify(MAX_STACK_SIZE)"-8]!") + TEST_RRP("strd r",4, VAL1,", r",5, VAL2,", [r",14, TEST_MEMORY_SIZE,", #-"__stringify(MAX_STACK_SIZE)"-8]!") TEST_UNSUPPORTED(__inst_thumb32(0xe9efec04) " @ strd r14, r12, [pc, #16]!") TEST_UNSUPPORTED(__inst_thumb32(0xe8efec04) " @ strd r14, r12, [pc], #16") @@ -821,14 +825,22 @@ CONDITION_INSTRUCTIONS(22, TEST_RP( "str"size" r",14,VAL2,", [r",1, 256, ", #-128]!") \ TEST_RPR("str"size".w r",0, VAL1,", [r",1, 0,", r",2, 4,"]") \ TEST_RPR("str"size" r",14,VAL2,", [r",10,0,", r",11,4,", lsl #1]") \ + TEST_UNSUPPORTED("str"size" r0, [r13, r1]") \ TEST_R( "str"size".w r",7, VAL1,", [sp, #24]") \ TEST_RP( "str"size".w r",0, VAL2,", [r",0,0, "]") \ + TEST_RP( "str"size" r",6, VAL1,", [r",13, TEST_MEMORY_SIZE,", #-"__stringify(MAX_STACK_SIZE)"]!") \ + TEST_UNSUPPORTED("str"size" r6, [r13, #-"__stringify(MAX_STACK_SIZE)"-8]!") \ + TEST_RP( "str"size" r",4, VAL2,", [r",12, TEST_MEMORY_SIZE,", #-"__stringify(MAX_STACK_SIZE)"-8]!") \ TEST_UNSUPPORTED("str"size"t r0, [r1, #4]") SINGLE_STORE("b") SINGLE_STORE("h") SINGLE_STORE("") + TEST_UNSUPPORTED(__inst_thumb32(0xf801000d) " @ strb r0, [r1, r13]") + TEST_UNSUPPORTED(__inst_thumb32(0xf821000d) " @ strh r0, [r1, r13]") + TEST_UNSUPPORTED(__inst_thumb32(0xf841000d) " @ str r0, [r1, r13]") + TEST("str sp, [sp]") TEST_UNSUPPORTED(__inst_thumb32(0xf8cfe000) " @ str r14, [pc]") TEST_UNSUPPORTED(__inst_thumb32(0xf8cef000) " @ str pc, [r14]") -- cgit v0.10.2 From cbf6ab52add20b845f903decc973afbd5463c527 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Mon, 5 Jan 2015 19:29:32 +0800 Subject: kprobes: Pass the original kprobe for preparing optimized kprobe Pass the original kprobe for preparing an optimized kprobe arch-dep part, since for some architecture (e.g. ARM32) requires the information in original kprobe. Signed-off-by: Masami Hiramatsu Signed-off-by: Wang Nan Signed-off-by: Jon Medhurst diff --git a/arch/x86/kernel/kprobes/opt.c b/arch/x86/kernel/kprobes/opt.c index 7c523bb..0dd8d08 100644 --- a/arch/x86/kernel/kprobes/opt.c +++ b/arch/x86/kernel/kprobes/opt.c @@ -322,7 +322,8 @@ void arch_remove_optimized_kprobe(struct optimized_kprobe *op) * Target instructions MUST be relocatable (checked inside) * This is called when new aggr(opt)probe is allocated or reused. */ -int arch_prepare_optimized_kprobe(struct optimized_kprobe *op) +int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, + struct kprobe *__unused) { u8 *buf; int ret; diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h index 5297f9f..1ab5475 100644 --- a/include/linux/kprobes.h +++ b/include/linux/kprobes.h @@ -308,7 +308,8 @@ struct optimized_kprobe { /* Architecture dependent functions for direct jump optimization */ extern int arch_prepared_optinsn(struct arch_optimized_insn *optinsn); extern int arch_check_optimized_kprobe(struct optimized_kprobe *op); -extern int arch_prepare_optimized_kprobe(struct optimized_kprobe *op); +extern int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, + struct kprobe *orig); extern void arch_remove_optimized_kprobe(struct optimized_kprobe *op); extern void arch_optimize_kprobes(struct list_head *oplist); extern void arch_unoptimize_kprobes(struct list_head *oplist, diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 06f5830..bad4e95 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -717,7 +717,7 @@ static void prepare_optimized_kprobe(struct kprobe *p) struct optimized_kprobe *op; op = container_of(p, struct optimized_kprobe, kp); - arch_prepare_optimized_kprobe(op); + arch_prepare_optimized_kprobe(op, p); } /* Allocate new optimized_kprobe and try to prepare optimized instructions */ @@ -731,7 +731,7 @@ static struct kprobe *alloc_aggr_kprobe(struct kprobe *p) INIT_LIST_HEAD(&op->list); op->kp.addr = p->addr; - arch_prepare_optimized_kprobe(op); + arch_prepare_optimized_kprobe(op, p); return &op->kp; } -- cgit v0.10.2 From 0dc016dbd820260b8ea74337980735b8c88d4ef2 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Fri, 9 Jan 2015 14:37:36 +0800 Subject: ARM: kprobes: enable OPTPROBES for ARM 32 This patch introduce kprobeopt for ARM 32. Limitations: - Currently only kernel compiled with ARM ISA is supported. - Offset between probe point and optinsn slot must not larger than 32MiB. Masami Hiramatsu suggests replacing 2 words, it will make things complex. Futher patch can make such optimization. Kprobe opt on ARM is relatively simpler than kprobe opt on x86 because ARM instruction is always 4 bytes aligned and 4 bytes long. This patch replace probed instruction by a 'b', branch to trampoline code and then calls optimized_callback(). optimized_callback() calls opt_pre_handler() to execute kprobe handler. It also emulate/simulate replaced instruction. When unregistering kprobe, the deferred manner of unoptimizer may leave branch instruction before optimizer is called. Different from x86_64, which only copy the probed insn after optprobe_template_end and reexecute them, this patch call singlestep to emulate/simulate the insn directly. Futher patch can optimize this behavior. Signed-off-by: Wang Nan Acked-by: Masami Hiramatsu Cc: Will Deacon Reviewed-by: Jon Medhurst (Tixy) Signed-off-by: Jon Medhurst diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 97d07ed..3d5dc2d 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -60,6 +60,7 @@ config ARM select HAVE_MEMBLOCK select HAVE_MOD_ARCH_SPECIFIC if ARM_UNWIND select HAVE_OPROFILE if (HAVE_PERF_EVENTS) + select HAVE_OPTPROBES if !THUMB2_KERNEL select HAVE_PERF_EVENTS select HAVE_PERF_REGS select HAVE_PERF_USER_STACK_DUMP diff --git a/arch/arm/include/asm/insn.h b/arch/arm/include/asm/insn.h new file mode 100644 index 0000000..e96065d --- /dev/null +++ b/arch/arm/include/asm/insn.h @@ -0,0 +1,29 @@ +#ifndef __ASM_ARM_INSN_H +#define __ASM_ARM_INSN_H + +static inline unsigned long +arm_gen_nop(void) +{ +#ifdef CONFIG_THUMB2_KERNEL + return 0xf3af8000; /* nop.w */ +#else + return 0xe1a00000; /* mov r0, r0 */ +#endif +} + +unsigned long +__arm_gen_branch(unsigned long pc, unsigned long addr, bool link); + +static inline unsigned long +arm_gen_branch(unsigned long pc, unsigned long addr) +{ + return __arm_gen_branch(pc, addr, false); +} + +static inline unsigned long +arm_gen_branch_link(unsigned long pc, unsigned long addr) +{ + return __arm_gen_branch(pc, addr, true); +} + +#endif diff --git a/arch/arm/include/asm/kprobes.h b/arch/arm/include/asm/kprobes.h index 56f9ac6..50ff3bc 100644 --- a/arch/arm/include/asm/kprobes.h +++ b/arch/arm/include/asm/kprobes.h @@ -50,5 +50,34 @@ int kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr); int kprobe_exceptions_notify(struct notifier_block *self, unsigned long val, void *data); +/* optinsn template addresses */ +extern __visible kprobe_opcode_t optprobe_template_entry; +extern __visible kprobe_opcode_t optprobe_template_val; +extern __visible kprobe_opcode_t optprobe_template_call; +extern __visible kprobe_opcode_t optprobe_template_end; +extern __visible kprobe_opcode_t optprobe_template_sub_sp; +extern __visible kprobe_opcode_t optprobe_template_add_sp; + +#define MAX_OPTIMIZED_LENGTH 4 +#define MAX_OPTINSN_SIZE \ + ((unsigned long)&optprobe_template_end - \ + (unsigned long)&optprobe_template_entry) +#define RELATIVEJUMP_SIZE 4 + +struct arch_optimized_insn { + /* + * copy of the original instructions. + * Different from x86, ARM kprobe_opcode_t is u32. + */ +#define MAX_COPIED_INSN DIV_ROUND_UP(RELATIVEJUMP_SIZE, sizeof(kprobe_opcode_t)) + kprobe_opcode_t copied_insn[MAX_COPIED_INSN]; + /* detour code buffer */ + kprobe_opcode_t *insn; + /* + * We always copy one instruction on ARM, + * so size will always be 4, and unlike x86, there is no + * need for a size field. + */ +}; #endif /* _ARM_KPROBES_H */ diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 9c51a43..902397d 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -52,7 +52,7 @@ obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o insn.o obj-$(CONFIG_JUMP_LABEL) += jump_label.o insn.o patch.o obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o # Main staffs in KPROBES are in arch/arm/probes/ . -obj-$(CONFIG_KPROBES) += patch.o +obj-$(CONFIG_KPROBES) += patch.o insn.o obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o obj-$(CONFIG_ARM_THUMBEE) += thumbee.o obj-$(CONFIG_KGDB) += kgdb.o patch.o diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c index b8c75e4..709ee1d 100644 --- a/arch/arm/kernel/ftrace.c +++ b/arch/arm/kernel/ftrace.c @@ -20,8 +20,7 @@ #include #include #include - -#include "insn.h" +#include #ifdef CONFIG_THUMB2_KERNEL #define NOP 0xf85deb04 /* pop.w {lr} */ diff --git a/arch/arm/kernel/insn.h b/arch/arm/kernel/insn.h deleted file mode 100644 index e96065d..0000000 --- a/arch/arm/kernel/insn.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef __ASM_ARM_INSN_H -#define __ASM_ARM_INSN_H - -static inline unsigned long -arm_gen_nop(void) -{ -#ifdef CONFIG_THUMB2_KERNEL - return 0xf3af8000; /* nop.w */ -#else - return 0xe1a00000; /* mov r0, r0 */ -#endif -} - -unsigned long -__arm_gen_branch(unsigned long pc, unsigned long addr, bool link); - -static inline unsigned long -arm_gen_branch(unsigned long pc, unsigned long addr) -{ - return __arm_gen_branch(pc, addr, false); -} - -static inline unsigned long -arm_gen_branch_link(unsigned long pc, unsigned long addr) -{ - return __arm_gen_branch(pc, addr, true); -} - -#endif diff --git a/arch/arm/kernel/jump_label.c b/arch/arm/kernel/jump_label.c index d8da0759..e39cbf4 100644 --- a/arch/arm/kernel/jump_label.c +++ b/arch/arm/kernel/jump_label.c @@ -1,8 +1,7 @@ #include #include #include - -#include "insn.h" +#include #ifdef HAVE_JUMP_LABEL diff --git a/arch/arm/probes/kprobes/Makefile b/arch/arm/probes/kprobes/Makefile index bc8d504..76a36bf 100644 --- a/arch/arm/probes/kprobes/Makefile +++ b/arch/arm/probes/kprobes/Makefile @@ -7,5 +7,6 @@ obj-$(CONFIG_KPROBES) += actions-thumb.o checkers-thumb.o test-kprobes-objs += test-thumb.o else obj-$(CONFIG_KPROBES) += actions-arm.o checkers-arm.o +obj-$(CONFIG_OPTPROBES) += opt-arm.o test-kprobes-objs += test-arm.o endif diff --git a/arch/arm/probes/kprobes/core.c b/arch/arm/probes/kprobes/core.c index 3a58db4..a4ec240 100644 --- a/arch/arm/probes/kprobes/core.c +++ b/arch/arm/probes/kprobes/core.c @@ -163,19 +163,31 @@ void __kprobes arch_arm_kprobe(struct kprobe *p) * memory. It is also needed to atomically set the two half-words of a 32-bit * Thumb breakpoint. */ -int __kprobes __arch_disarm_kprobe(void *p) -{ - struct kprobe *kp = p; - void *addr = (void *)((uintptr_t)kp->addr & ~1); - - __patch_text(addr, kp->opcode); +struct patch { + void *addr; + unsigned int insn; +}; +static int __kprobes_remove_breakpoint(void *data) +{ + struct patch *p = data; + __patch_text(p->addr, p->insn); return 0; } +void __kprobes kprobes_remove_breakpoint(void *addr, unsigned int insn) +{ + struct patch p = { + .addr = addr, + .insn = insn, + }; + stop_machine(__kprobes_remove_breakpoint, &p, cpu_online_mask); +} + void __kprobes arch_disarm_kprobe(struct kprobe *p) { - stop_machine(__arch_disarm_kprobe, p, cpu_online_mask); + kprobes_remove_breakpoint((void *)((uintptr_t)p->addr & ~1), + p->opcode); } void __kprobes arch_remove_kprobe(struct kprobe *p) diff --git a/arch/arm/probes/kprobes/core.h b/arch/arm/probes/kprobes/core.h index f88c79f..b3036c5 100644 --- a/arch/arm/probes/kprobes/core.h +++ b/arch/arm/probes/kprobes/core.h @@ -30,6 +30,8 @@ #define KPROBE_THUMB16_BREAKPOINT_INSTRUCTION 0xde18 #define KPROBE_THUMB32_BREAKPOINT_INSTRUCTION 0xf7f0a018 +extern void kprobes_remove_breakpoint(void *addr, unsigned int insn); + enum probes_insn __kprobes kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_probes_insn *asi, const struct decode_header *h); diff --git a/arch/arm/probes/kprobes/opt-arm.c b/arch/arm/probes/kprobes/opt-arm.c new file mode 100644 index 0000000..13d5232 --- /dev/null +++ b/arch/arm/probes/kprobes/opt-arm.c @@ -0,0 +1,322 @@ +/* + * Kernel Probes Jump Optimization (Optprobes) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2002, 2004 + * Copyright (C) Hitachi Ltd., 2012 + * Copyright (C) Huawei Inc., 2014 + */ + +#include +#include +#include +#include +/* for arm_gen_branch */ +#include +/* for patch_text */ +#include + +#include "core.h" + +/* + * NOTE: the first sub and add instruction will be modified according + * to the stack cost of the instruction. + */ +asm ( + ".global optprobe_template_entry\n" + "optprobe_template_entry:\n" + ".global optprobe_template_sub_sp\n" + "optprobe_template_sub_sp:" + " sub sp, sp, #0xff\n" + " stmia sp, {r0 - r14} \n" + ".global optprobe_template_add_sp\n" + "optprobe_template_add_sp:" + " add r3, sp, #0xff\n" + " str r3, [sp, #52]\n" + " mrs r4, cpsr\n" + " str r4, [sp, #64]\n" + " mov r1, sp\n" + " ldr r0, 1f\n" + " ldr r2, 2f\n" + /* + * AEABI requires an 8-bytes alignment stack. If + * SP % 8 != 0 (SP % 4 == 0 should be ensured), + * alloc more bytes here. + */ + " and r4, sp, #4\n" + " sub sp, sp, r4\n" +#if __LINUX_ARM_ARCH__ >= 5 + " blx r2\n" +#else + " mov lr, pc\n" + " mov pc, r2\n" +#endif + " add sp, sp, r4\n" + " ldr r1, [sp, #64]\n" + " tst r1, #"__stringify(PSR_T_BIT)"\n" + " ldrne r2, [sp, #60]\n" + " orrne r2, #1\n" + " strne r2, [sp, #60] @ set bit0 of PC for thumb\n" + " msr cpsr_cxsf, r1\n" + " ldmia sp, {r0 - r15}\n" + ".global optprobe_template_val\n" + "optprobe_template_val:\n" + "1: .long 0\n" + ".global optprobe_template_call\n" + "optprobe_template_call:\n" + "2: .long 0\n" + ".global optprobe_template_end\n" + "optprobe_template_end:\n"); + +#define TMPL_VAL_IDX \ + ((unsigned long *)&optprobe_template_val - (unsigned long *)&optprobe_template_entry) +#define TMPL_CALL_IDX \ + ((unsigned long *)&optprobe_template_call - (unsigned long *)&optprobe_template_entry) +#define TMPL_END_IDX \ + ((unsigned long *)&optprobe_template_end - (unsigned long *)&optprobe_template_entry) +#define TMPL_ADD_SP \ + ((unsigned long *)&optprobe_template_add_sp - (unsigned long *)&optprobe_template_entry) +#define TMPL_SUB_SP \ + ((unsigned long *)&optprobe_template_sub_sp - (unsigned long *)&optprobe_template_entry) + +/* + * ARM can always optimize an instruction when using ARM ISA, except + * instructions like 'str r0, [sp, r1]' which store to stack and unable + * to determine stack space consumption statically. + */ +int arch_prepared_optinsn(struct arch_optimized_insn *optinsn) +{ + return optinsn->insn != NULL; +} + +/* + * In ARM ISA, kprobe opt always replace one instruction (4 bytes + * aligned and 4 bytes long). It is impossible to encounter another + * kprobe in the address range. So always return 0. + */ +int arch_check_optimized_kprobe(struct optimized_kprobe *op) +{ + return 0; +} + +/* Caller must ensure addr & 3 == 0 */ +static int can_optimize(struct kprobe *kp) +{ + if (kp->ainsn.stack_space < 0) + return 0; + /* + * 255 is the biggest imm can be used in 'sub r0, r0, #'. + * Number larger than 255 needs special encoding. + */ + if (kp->ainsn.stack_space > 255 - sizeof(struct pt_regs)) + return 0; + return 1; +} + +/* Free optimized instruction slot */ +static void +__arch_remove_optimized_kprobe(struct optimized_kprobe *op, int dirty) +{ + if (op->optinsn.insn) { + free_optinsn_slot(op->optinsn.insn, dirty); + op->optinsn.insn = NULL; + } +} + +extern void kprobe_handler(struct pt_regs *regs); + +static void +optimized_callback(struct optimized_kprobe *op, struct pt_regs *regs) +{ + unsigned long flags; + struct kprobe *p = &op->kp; + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + + /* Save skipped registers */ + regs->ARM_pc = (unsigned long)op->kp.addr; + regs->ARM_ORIG_r0 = ~0UL; + + local_irq_save(flags); + + if (kprobe_running()) { + kprobes_inc_nmissed_count(&op->kp); + } else { + __this_cpu_write(current_kprobe, &op->kp); + kcb->kprobe_status = KPROBE_HIT_ACTIVE; + opt_pre_handler(&op->kp, regs); + __this_cpu_write(current_kprobe, NULL); + } + + /* In each case, we must singlestep the replaced instruction. */ + op->kp.ainsn.insn_singlestep(p->opcode, &p->ainsn, regs); + + local_irq_restore(flags); +} + +int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, struct kprobe *orig) +{ + kprobe_opcode_t *code; + unsigned long rel_chk; + unsigned long val; + unsigned long stack_protect = sizeof(struct pt_regs); + + if (!can_optimize(orig)) + return -EILSEQ; + + code = get_optinsn_slot(); + if (!code) + return -ENOMEM; + + /* + * Verify if the address gap is in 32MiB range, because this uses + * a relative jump. + * + * kprobe opt use a 'b' instruction to branch to optinsn.insn. + * According to ARM manual, branch instruction is: + * + * 31 28 27 24 23 0 + * +------+---+---+---+---+----------------+ + * | cond | 1 | 0 | 1 | 0 | imm24 | + * +------+---+---+---+---+----------------+ + * + * imm24 is a signed 24 bits integer. The real branch offset is computed + * by: imm32 = SignExtend(imm24:'00', 32); + * + * So the maximum forward branch should be: + * (0x007fffff << 2) = 0x01fffffc = 0x1fffffc + * The maximum backword branch should be: + * (0xff800000 << 2) = 0xfe000000 = -0x2000000 + * + * We can simply check (rel & 0xfe000003): + * if rel is positive, (rel & 0xfe000000) shoule be 0 + * if rel is negitive, (rel & 0xfe000000) should be 0xfe000000 + * the last '3' is used for alignment checking. + */ + rel_chk = (unsigned long)((long)code - + (long)orig->addr + 8) & 0xfe000003; + + if ((rel_chk != 0) && (rel_chk != 0xfe000000)) { + /* + * Different from x86, we free code buf directly instead of + * calling __arch_remove_optimized_kprobe() because + * we have not fill any field in op. + */ + free_optinsn_slot(code, 0); + return -ERANGE; + } + + /* Copy arch-dep-instance from template. */ + memcpy(code, &optprobe_template_entry, + TMPL_END_IDX * sizeof(kprobe_opcode_t)); + + /* Adjust buffer according to instruction. */ + BUG_ON(orig->ainsn.stack_space < 0); + + stack_protect += orig->ainsn.stack_space; + + /* Should have been filtered by can_optimize(). */ + BUG_ON(stack_protect > 255); + + /* Create a 'sub sp, sp, #' */ + code[TMPL_SUB_SP] = __opcode_to_mem_arm(0xe24dd000 | stack_protect); + /* Create a 'add r3, sp, #' */ + code[TMPL_ADD_SP] = __opcode_to_mem_arm(0xe28d3000 | stack_protect); + + /* Set probe information */ + val = (unsigned long)op; + code[TMPL_VAL_IDX] = val; + + /* Set probe function call */ + val = (unsigned long)optimized_callback; + code[TMPL_CALL_IDX] = val; + + flush_icache_range((unsigned long)code, + (unsigned long)(&code[TMPL_END_IDX])); + + /* Set op->optinsn.insn means prepared. */ + op->optinsn.insn = code; + return 0; +} + +void __kprobes arch_optimize_kprobes(struct list_head *oplist) +{ + struct optimized_kprobe *op, *tmp; + + list_for_each_entry_safe(op, tmp, oplist, list) { + unsigned long insn; + WARN_ON(kprobe_disabled(&op->kp)); + + /* + * Backup instructions which will be replaced + * by jump address + */ + memcpy(op->optinsn.copied_insn, op->kp.addr, + RELATIVEJUMP_SIZE); + + insn = arm_gen_branch((unsigned long)op->kp.addr, + (unsigned long)op->optinsn.insn); + BUG_ON(insn == 0); + + /* + * Make it a conditional branch if replaced insn + * is consitional + */ + insn = (__mem_to_opcode_arm( + op->optinsn.copied_insn[0]) & 0xf0000000) | + (insn & 0x0fffffff); + + /* + * Similar to __arch_disarm_kprobe, operations which + * removing breakpoints must be wrapped by stop_machine + * to avoid racing. + */ + kprobes_remove_breakpoint(op->kp.addr, insn); + + list_del_init(&op->list); + } +} + +void arch_unoptimize_kprobe(struct optimized_kprobe *op) +{ + arch_arm_kprobe(&op->kp); +} + +/* + * Recover original instructions and breakpoints from relative jumps. + * Caller must call with locking kprobe_mutex. + */ +void arch_unoptimize_kprobes(struct list_head *oplist, + struct list_head *done_list) +{ + struct optimized_kprobe *op, *tmp; + + list_for_each_entry_safe(op, tmp, oplist, list) { + arch_unoptimize_kprobe(op); + list_move(&op->list, done_list); + } +} + +int arch_within_optimized_kprobe(struct optimized_kprobe *op, + unsigned long addr) +{ + return ((unsigned long)op->kp.addr <= addr && + (unsigned long)op->kp.addr + RELATIVEJUMP_SIZE > addr); +} + +void arch_remove_optimized_kprobe(struct optimized_kprobe *op) +{ + __arch_remove_optimized_kprobe(op, 1); +} -- cgit v0.10.2 From 4cd872d973c7e1ce6a41e36db9d9352152da32d4 Mon Sep 17 00:00:00 2001 From: "Jon Medhurst (Tixy)" Date: Mon, 5 Jan 2015 19:29:40 +0800 Subject: ARM: kprobes: Fix unreliable MRS instruction tests For the instruction 'mrs Rn, cpsr' the resulting value of Rn can vary due to external factors we can't control. So get the test code to mask out these indeterminate bits. Signed-off-by: Jon Medhurst diff --git a/arch/arm/probes/kprobes/test-arm.c b/arch/arm/probes/kprobes/test-arm.c index 9b3b1b4..e72b07e 100644 --- a/arch/arm/probes/kprobes/test-arm.c +++ b/arch/arm/probes/kprobes/test-arm.c @@ -204,9 +204,9 @@ void kprobe_arm_test_cases(void) #endif TEST_GROUP("Miscellaneous instructions") - TEST("mrs r0, cpsr") - TEST("mrspl r7, cpsr") - TEST("mrs r14, cpsr") + TEST_RMASKED("mrs r",0,~PSR_IGNORE_BITS,", cpsr") + TEST_RMASKED("mrspl r",7,~PSR_IGNORE_BITS,", cpsr") + TEST_RMASKED("mrs r",14,~PSR_IGNORE_BITS,", cpsr") TEST_UNSUPPORTED(__inst_arm(0xe10ff000) " @ mrs r15, cpsr") TEST_UNSUPPORTED("mrs r0, spsr") TEST_UNSUPPORTED("mrs lr, spsr") diff --git a/arch/arm/probes/kprobes/test-core.c b/arch/arm/probes/kprobes/test-core.c index 7c5ddd5..e495127 100644 --- a/arch/arm/probes/kprobes/test-core.c +++ b/arch/arm/probes/kprobes/test-core.c @@ -1056,15 +1056,6 @@ static int test_case_run_count; static bool test_case_is_thumb; static int test_instance; -/* - * We ignore the state of the imprecise abort disable flag (CPSR.A) because this - * can change randomly as the kernel doesn't take care to preserve or initialise - * this across context switches. Also, with Security Extentions, the flag may - * not be under control of the kernel; for this reason we ignore the state of - * the FIQ disable flag CPSR.F as well. - */ -#define PSR_IGNORE_BITS (PSR_A_BIT | PSR_F_BIT) - static unsigned long test_check_cc(int cc, unsigned long cpsr) { int ret = arm_check_condition(cc << 28, cpsr); @@ -1271,11 +1262,21 @@ test_case_pre_handler(struct kprobe *p, struct pt_regs *regs) static int __kprobes test_after_pre_handler(struct kprobe *p, struct pt_regs *regs) { + struct test_arg *args; + if (container_of(p, struct test_probe, kprobe)->hit == test_instance) return 0; /* Already run for this test instance */ result_regs = *regs; + + /* Mask out results which are indeterminate */ result_regs.ARM_cpsr &= ~PSR_IGNORE_BITS; + for (args = current_args; args[0].type != ARG_TYPE_END; ++args) + if (args[0].type == ARG_TYPE_REG_MASKED) { + struct test_arg_regptr *arg = + (struct test_arg_regptr *)args; + result_regs.uregs[arg->reg] &= arg->val; + } /* Undo any changes done to SP by the test case */ regs->ARM_sp = (unsigned long)current_stack; diff --git a/arch/arm/probes/kprobes/test-core.h b/arch/arm/probes/kprobes/test-core.h index 9991754..9428520 100644 --- a/arch/arm/probes/kprobes/test-core.h +++ b/arch/arm/probes/kprobes/test-core.h @@ -45,10 +45,11 @@ extern int kprobe_test_cc_position; * */ -#define ARG_TYPE_END 0 -#define ARG_TYPE_REG 1 -#define ARG_TYPE_PTR 2 -#define ARG_TYPE_MEM 3 +#define ARG_TYPE_END 0 +#define ARG_TYPE_REG 1 +#define ARG_TYPE_PTR 2 +#define ARG_TYPE_MEM 3 +#define ARG_TYPE_REG_MASKED 4 #define ARG_FLAG_UNSUPPORTED 0x01 #define ARG_FLAG_SUPPORTED 0x02 @@ -61,7 +62,7 @@ struct test_arg { }; struct test_arg_regptr { - u8 type; /* ARG_TYPE_REG or ARG_TYPE_PTR */ + u8 type; /* ARG_TYPE_REG or ARG_TYPE_PTR or ARG_TYPE_REG_MASKED */ u8 reg; u8 _padding[2]; u32 val; @@ -138,6 +139,12 @@ struct test_arg_end { ".short 0 \n\t" \ ".word "#val" \n\t" +#define TEST_ARG_REG_MASKED(reg, val) \ + ".byte "__stringify(ARG_TYPE_REG_MASKED)" \n\t" \ + ".byte "#reg" \n\t" \ + ".short 0 \n\t" \ + ".word "#val" \n\t" + #define TEST_ARG_END(flags) \ ".byte "__stringify(ARG_TYPE_END)" \n\t" \ ".byte "TEST_ISA flags" \n\t" \ @@ -395,6 +402,22 @@ struct test_arg_end { " "codex" \n\t" \ TESTCASE_END +#define TEST_RMASKED(code1, reg, mask, code2) \ + TESTCASE_START(code1 #reg code2) \ + TEST_ARG_REG_MASKED(reg, mask) \ + TEST_ARG_END("") \ + TEST_INSTRUCTION(code1 #reg code2) \ + TESTCASE_END + +/* + * We ignore the state of the imprecise abort disable flag (CPSR.A) because this + * can change randomly as the kernel doesn't take care to preserve or initialise + * this across context switches. Also, with Security Extensions, the flag may + * not be under control of the kernel; for this reason we ignore the state of + * the FIQ disable flag CPSR.F as well. + */ +#define PSR_IGNORE_BITS (PSR_A_BIT | PSR_F_BIT) + /* * Macros for defining space directives spread over multiple lines. diff --git a/arch/arm/probes/kprobes/test-thumb.c b/arch/arm/probes/kprobes/test-thumb.c index e8cf193..b683b45 100644 --- a/arch/arm/probes/kprobes/test-thumb.c +++ b/arch/arm/probes/kprobes/test-thumb.c @@ -778,8 +778,8 @@ CONDITION_INSTRUCTIONS(22, TEST_UNSUPPORTED("subs pc, lr, #4") - TEST("mrs r0, cpsr") - TEST("mrs r14, cpsr") + TEST_RMASKED("mrs r",0,~PSR_IGNORE_BITS,", cpsr") + TEST_RMASKED("mrs r",14,~PSR_IGNORE_BITS,", cpsr") TEST_UNSUPPORTED(__inst_thumb32(0xf3ef8d00) " @ mrs sp, spsr") TEST_UNSUPPORTED(__inst_thumb32(0xf3ef8f00) " @ mrs pc, spsr") TEST_UNSUPPORTED("mrs r0, spsr") -- cgit v0.10.2 From 28a1899db30a9325498aef114055506286dc8010 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Mon, 5 Jan 2015 19:29:44 +0800 Subject: ARM: kprobes: check register usage for probed instruction. This patch utilizes the previously introduced checker to check register usage for probed ARM instruction and saves it in a mask. A further patch will use such information to avoid simulation or emulation. Signed-off-by: Wang Nan Reviewed-by: Jon Medhurst Signed-off-by: Jon Medhurst diff --git a/arch/arm/include/asm/probes.h b/arch/arm/include/asm/probes.h index cd9e815..b668e60 100644 --- a/arch/arm/include/asm/probes.h +++ b/arch/arm/include/asm/probes.h @@ -41,6 +41,7 @@ struct arch_probes_insn { probes_insn_singlestep_t *insn_singlestep; probes_insn_fn_t *insn_fn; int stack_space; + unsigned long register_usage_flags; }; #endif /* __ASSEMBLY__ */ diff --git a/arch/arm/probes/decode.c b/arch/arm/probes/decode.c index f9d7c42..880ebe0 100644 --- a/arch/arm/probes/decode.c +++ b/arch/arm/probes/decode.c @@ -435,6 +435,13 @@ probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi, */ asi->stack_space = 0; + /* + * Similarly to stack_space, register_usage_flags is filled by + * checkers. Its default value is set to ~0, which is 'all + * registers are used', to prevent any potential optimization. + */ + asi->register_usage_flags = ~0UL; + if (emulate) insn = prepare_emulated_insn(insn, asi, thumb); diff --git a/arch/arm/probes/kprobes/actions-arm.c b/arch/arm/probes/kprobes/actions-arm.c index 06988ef..b9056d6 100644 --- a/arch/arm/probes/kprobes/actions-arm.c +++ b/arch/arm/probes/kprobes/actions-arm.c @@ -341,4 +341,4 @@ const union decode_action kprobes_arm_actions[NUM_PROBES_ARM_ACTIONS] = { [PROBES_LDMSTM] = {.decoder = kprobe_decode_ldmstm} }; -const struct decode_checker *kprobes_arm_checkers[] = {arm_stack_checker, NULL}; +const struct decode_checker *kprobes_arm_checkers[] = {arm_stack_checker, arm_regs_checker, NULL}; diff --git a/arch/arm/probes/kprobes/checkers-arm.c b/arch/arm/probes/kprobes/checkers-arm.c index f817663..7b98173 100644 --- a/arch/arm/probes/kprobes/checkers-arm.c +++ b/arch/arm/probes/kprobes/checkers-arm.c @@ -97,3 +97,96 @@ const struct decode_checker arm_stack_checker[NUM_PROBES_ARM_ACTIONS] = { [PROBES_STORE] = {.checker = arm_check_stack}, [PROBES_LDMSTM] = {.checker = arm_check_stack}, }; + +static enum probes_insn __kprobes arm_check_regs_nouse(probes_opcode_t insn, + struct arch_probes_insn *asi, + const struct decode_header *h) +{ + asi->register_usage_flags = 0; + return INSN_GOOD; +} + +static enum probes_insn arm_check_regs_normal(probes_opcode_t insn, + struct arch_probes_insn *asi, + const struct decode_header *h) +{ + u32 regs = h->type_regs.bits >> DECODE_TYPE_BITS; + int i; + + asi->register_usage_flags = 0; + for (i = 0; i < 5; regs >>= 4, insn >>= 4, i++) + if (regs & 0xf) + asi->register_usage_flags |= 1 << (insn & 0xf); + + return INSN_GOOD; +} + + +static enum probes_insn arm_check_regs_ldmstm(probes_opcode_t insn, + struct arch_probes_insn *asi, + const struct decode_header *h) +{ + unsigned int reglist = insn & 0xffff; + unsigned int rn = (insn >> 16) & 0xf; + asi->register_usage_flags = reglist | (1 << rn); + return INSN_GOOD; +} + +static enum probes_insn arm_check_regs_mov_ip_sp(probes_opcode_t insn, + struct arch_probes_insn *asi, + const struct decode_header *h) +{ + /* Instruction is 'mov ip, sp' i.e. 'mov r12, r13' */ + asi->register_usage_flags = (1 << 12) | (1<< 13); + return INSN_GOOD; +} + +/* + * | Rn |Rt/d| | Rm | + * LDRD (register) cccc 000x x0x0 xxxx xxxx xxxx 1101 xxxx + * STRD (register) cccc 000x x0x0 xxxx xxxx xxxx 1111 xxxx + * | Rn |Rt/d| |imm4L| + * LDRD (immediate) cccc 000x x1x0 xxxx xxxx xxxx 1101 xxxx + * STRD (immediate) cccc 000x x1x0 xxxx xxxx xxxx 1111 xxxx + * + * Such instructions access Rt/d and its next register, so different + * from others, a specific checker is required to handle this extra + * implicit register usage. + */ +static enum probes_insn arm_check_regs_ldrdstrd(probes_opcode_t insn, + struct arch_probes_insn *asi, + const struct decode_header *h) +{ + int rdt = (insn >> 12) & 0xf; + arm_check_regs_normal(insn, asi, h); + asi->register_usage_flags |= 1 << (rdt + 1); + return INSN_GOOD; +} + + +const struct decode_checker arm_regs_checker[NUM_PROBES_ARM_ACTIONS] = { + [PROBES_MRS] = {.checker = arm_check_regs_normal}, + [PROBES_SATURATING_ARITHMETIC] = {.checker = arm_check_regs_normal}, + [PROBES_MUL1] = {.checker = arm_check_regs_normal}, + [PROBES_MUL2] = {.checker = arm_check_regs_normal}, + [PROBES_MUL_ADD_LONG] = {.checker = arm_check_regs_normal}, + [PROBES_MUL_ADD] = {.checker = arm_check_regs_normal}, + [PROBES_LOAD] = {.checker = arm_check_regs_normal}, + [PROBES_LOAD_EXTRA] = {.checker = arm_check_regs_normal}, + [PROBES_STORE] = {.checker = arm_check_regs_normal}, + [PROBES_STORE_EXTRA] = {.checker = arm_check_regs_normal}, + [PROBES_DATA_PROCESSING_REG] = {.checker = arm_check_regs_normal}, + [PROBES_DATA_PROCESSING_IMM] = {.checker = arm_check_regs_normal}, + [PROBES_SEV] = {.checker = arm_check_regs_nouse}, + [PROBES_WFE] = {.checker = arm_check_regs_nouse}, + [PROBES_SATURATE] = {.checker = arm_check_regs_normal}, + [PROBES_REV] = {.checker = arm_check_regs_normal}, + [PROBES_MMI] = {.checker = arm_check_regs_normal}, + [PROBES_PACK] = {.checker = arm_check_regs_normal}, + [PROBES_EXTEND] = {.checker = arm_check_regs_normal}, + [PROBES_EXTEND_ADD] = {.checker = arm_check_regs_normal}, + [PROBES_BITFIELD] = {.checker = arm_check_regs_normal}, + [PROBES_LDMSTM] = {.checker = arm_check_regs_ldmstm}, + [PROBES_MOV_IP_SP] = {.checker = arm_check_regs_mov_ip_sp}, + [PROBES_LDRSTRD] = {.checker = arm_check_regs_ldrdstrd}, +}; diff --git a/arch/arm/probes/kprobes/checkers.h b/arch/arm/probes/kprobes/checkers.h index bddfa0e..cf6c9e7 100644 --- a/arch/arm/probes/kprobes/checkers.h +++ b/arch/arm/probes/kprobes/checkers.h @@ -47,6 +47,7 @@ extern const union decode_action stack_check_actions[]; #ifndef CONFIG_THUMB2_KERNEL extern const struct decode_checker arm_stack_checker[]; +extern const struct decode_checker arm_regs_checker[]; #else #endif extern const struct decode_checker t32_stack_checker[]; -- cgit v0.10.2 From bfc9657d752c47d59dc0bab85ebdc19cf60100dd Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Mon, 5 Jan 2015 19:34:47 +0800 Subject: ARM: optprobes: execute instruction during restoring if possible. This patch removes software emulation or simulation for most of probed instructions. If the instruction doesn't use PC relative addressing, it will be translated into following instructions in the restore code in code template: ldmia {r0 - r14} // restore all instruction except PC // direct execute the probed instruction b next_insn // branch to next instruction. Signed-off-by: Wang Nan Reviewed-by: Masami Hiramatsu Signed-off-by: Jon Medhurst diff --git a/arch/arm/include/asm/kprobes.h b/arch/arm/include/asm/kprobes.h index 50ff3bc..3ea9be5 100644 --- a/arch/arm/include/asm/kprobes.h +++ b/arch/arm/include/asm/kprobes.h @@ -57,6 +57,9 @@ extern __visible kprobe_opcode_t optprobe_template_call; extern __visible kprobe_opcode_t optprobe_template_end; extern __visible kprobe_opcode_t optprobe_template_sub_sp; extern __visible kprobe_opcode_t optprobe_template_add_sp; +extern __visible kprobe_opcode_t optprobe_template_restore_begin; +extern __visible kprobe_opcode_t optprobe_template_restore_orig_insn; +extern __visible kprobe_opcode_t optprobe_template_restore_end; #define MAX_OPTIMIZED_LENGTH 4 #define MAX_OPTINSN_SIZE \ diff --git a/arch/arm/include/asm/probes.h b/arch/arm/include/asm/probes.h index b668e60..1e5b9bb 100644 --- a/arch/arm/include/asm/probes.h +++ b/arch/arm/include/asm/probes.h @@ -42,6 +42,7 @@ struct arch_probes_insn { probes_insn_fn_t *insn_fn; int stack_space; unsigned long register_usage_flags; + bool kprobe_direct_exec; }; #endif /* __ASSEMBLY__ */ diff --git a/arch/arm/probes/kprobes/opt-arm.c b/arch/arm/probes/kprobes/opt-arm.c index 13d5232..bcdecc2 100644 --- a/arch/arm/probes/kprobes/opt-arm.c +++ b/arch/arm/probes/kprobes/opt-arm.c @@ -32,6 +32,14 @@ #include "core.h" /* + * See register_usage_flags. If the probed instruction doesn't use PC, + * we can copy it into template and have it executed directly without + * simulation or emulation. + */ +#define ARM_REG_PC 15 +#define can_kprobe_direct_exec(m) (!test_bit(ARM_REG_PC, &(m))) + +/* * NOTE: the first sub and add instruction will be modified according * to the stack cost of the instruction. */ @@ -71,7 +79,15 @@ asm ( " orrne r2, #1\n" " strne r2, [sp, #60] @ set bit0 of PC for thumb\n" " msr cpsr_cxsf, r1\n" + ".global optprobe_template_restore_begin\n" + "optprobe_template_restore_begin:\n" " ldmia sp, {r0 - r15}\n" + ".global optprobe_template_restore_orig_insn\n" + "optprobe_template_restore_orig_insn:\n" + " nop\n" + ".global optprobe_template_restore_end\n" + "optprobe_template_restore_end:\n" + " nop\n" ".global optprobe_template_val\n" "optprobe_template_val:\n" "1: .long 0\n" @@ -91,6 +107,12 @@ asm ( ((unsigned long *)&optprobe_template_add_sp - (unsigned long *)&optprobe_template_entry) #define TMPL_SUB_SP \ ((unsigned long *)&optprobe_template_sub_sp - (unsigned long *)&optprobe_template_entry) +#define TMPL_RESTORE_BEGIN \ + ((unsigned long *)&optprobe_template_restore_begin - (unsigned long *)&optprobe_template_entry) +#define TMPL_RESTORE_ORIGN_INSN \ + ((unsigned long *)&optprobe_template_restore_orig_insn - (unsigned long *)&optprobe_template_entry) +#define TMPL_RESTORE_END \ + ((unsigned long *)&optprobe_template_restore_end - (unsigned long *)&optprobe_template_entry) /* * ARM can always optimize an instruction when using ARM ISA, except @@ -160,8 +182,12 @@ optimized_callback(struct optimized_kprobe *op, struct pt_regs *regs) __this_cpu_write(current_kprobe, NULL); } - /* In each case, we must singlestep the replaced instruction. */ - op->kp.ainsn.insn_singlestep(p->opcode, &p->ainsn, regs); + /* + * We singlestep the replaced instruction only when it can't be + * executed directly during restore. + */ + if (!p->ainsn.kprobe_direct_exec) + op->kp.ainsn.insn_singlestep(p->opcode, &p->ainsn, regs); local_irq_restore(flags); } @@ -243,6 +269,28 @@ int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, struct kprobe *or val = (unsigned long)optimized_callback; code[TMPL_CALL_IDX] = val; + /* If possible, copy insn and have it executed during restore */ + orig->ainsn.kprobe_direct_exec = false; + if (can_kprobe_direct_exec(orig->ainsn.register_usage_flags)) { + kprobe_opcode_t final_branch = arm_gen_branch( + (unsigned long)(&code[TMPL_RESTORE_END]), + (unsigned long)(op->kp.addr) + 4); + if (final_branch != 0) { + /* + * Replace original 'ldmia sp, {r0 - r15}' with + * 'ldmia {r0 - r14}', restore all registers except pc. + */ + code[TMPL_RESTORE_BEGIN] = __opcode_to_mem_arm(0xe89d7fff); + + /* The original probed instruction */ + code[TMPL_RESTORE_ORIGN_INSN] = __opcode_to_mem_arm(orig->opcode); + + /* Jump back to next instruction */ + code[TMPL_RESTORE_END] = __opcode_to_mem_arm(final_branch); + orig->ainsn.kprobe_direct_exec = true; + } + } + flush_icache_range((unsigned long)code, (unsigned long)(&code[TMPL_END_IDX])); -- cgit v0.10.2 From fb892bd0fdcb2e5eac9c105cf68def90396ed8cc Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Mon, 19 Jan 2015 15:15:36 +0000 Subject: ARM: kprobes: Eliminate test code's use of BX instruction on ARMv4 CPUs Non-T variants of ARMv4 CPUs don't support the BX instruction so eliminate its use. Signed-off-by: Jon Medhurst diff --git a/arch/arm/probes/kprobes/test-arm.c b/arch/arm/probes/kprobes/test-arm.c index e72b07e..8866aed 100644 --- a/arch/arm/probes/kprobes/test-arm.c +++ b/arch/arm/probes/kprobes/test-arm.c @@ -215,9 +215,12 @@ void kprobe_arm_test_cases(void) TEST_UNSUPPORTED("msr cpsr_f, lr") TEST_UNSUPPORTED("msr spsr, r0") +#if __LINUX_ARM_ARCH__ >= 5 || \ + (__LINUX_ARM_ARCH__ == 4 && !defined(CONFIG_CPU_32v4)) TEST_BF_R("bx r",0,2f,"") TEST_BB_R("bx r",7,2f,"") TEST_BF_R("bxeq r",14,2f,"") +#endif #if __LINUX_ARM_ARCH__ >= 5 TEST_R("clz r0, r",0, 0x0,"") diff --git a/arch/arm/probes/kprobes/test-core.c b/arch/arm/probes/kprobes/test-core.c index e495127..9775de2 100644 --- a/arch/arm/probes/kprobes/test-core.c +++ b/arch/arm/probes/kprobes/test-core.c @@ -236,6 +236,8 @@ static int tests_failed; #ifndef CONFIG_THUMB2_KERNEL +#define RET(reg) "mov pc, "#reg + long arm_func(long r0, long r1); static void __used __naked __arm_kprobes_test_func(void) @@ -245,7 +247,7 @@ static void __used __naked __arm_kprobes_test_func(void) ".type arm_func, %%function \n\t" "arm_func: \n\t" "adds r0, r0, r1 \n\t" - "bx lr \n\t" + "mov pc, lr \n\t" ".code "NORMAL_ISA /* Back to Thumb if necessary */ : : : "r0", "r1", "cc" ); @@ -253,6 +255,8 @@ static void __used __naked __arm_kprobes_test_func(void) #else /* CONFIG_THUMB2_KERNEL */ +#define RET(reg) "bx "#reg + long thumb16_func(long r0, long r1); long thumb32even_func(long r0, long r1); long thumb32odd_func(long r0, long r1); @@ -494,7 +498,7 @@ static void __naked benchmark_nop(void) { __asm__ __volatile__ ( "nop \n\t" - "bx lr" + RET(lr)" \n\t" ); } @@ -977,7 +981,7 @@ void __naked __kprobes_test_case_start(void) "bic r0, lr, #1 @ r0 = inline data \n\t" "mov r1, sp \n\t" "bl kprobes_test_case_start \n\t" - "bx r0 \n\t" + RET(r0)" \n\t" ); } -- cgit v0.10.2 From 4e1c0664de11e4b5861957ab4ddff2aeeffd042f Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Tue, 10 Feb 2015 15:03:22 +0800 Subject: ARM: kprobes: Fix compilation error caused by superfluous '*' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is a superfluous '*' in the definition of kprobe_decode_insn_t which on older versions of GCC (4.2.4) causes the compilation error: In file included from arch/arm/probes/kprobes/core.c:37: arch/arm/probes/kprobes/core.h:43: error: '[*]' not allowed in other than a declaration Fix this by removing the unneeded character. Reported-by: Janusz Użycki Signed-off-by: Jon Medhurst diff --git a/arch/arm/probes/kprobes/core.h b/arch/arm/probes/kprobes/core.h index b3036c5..ec5d1f2 100644 --- a/arch/arm/probes/kprobes/core.h +++ b/arch/arm/probes/kprobes/core.h @@ -40,7 +40,7 @@ typedef enum probes_insn (kprobe_decode_insn_t)(probes_opcode_t, struct arch_probes_insn *, bool, const union decode_action *, - const struct decode_checker *[*]); + const struct decode_checker *[]); #ifdef CONFIG_THUMB2_KERNEL -- cgit v0.10.2