From 7c116db24d944ff04a67c8bd89cb32c5cd0894ff Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 9 Jul 2020 21:48:41 +0100 Subject: efi/libstub/arm64: Retain 2MB kernel Image alignment if !KASLR Since commit 82046702e288 ("efi/libstub/arm64: Replace 'preferred' offset with alignment check"), loading a relocatable arm64 kernel at a physical address which is not 2MB aligned and subsequently booting with EFI will leave the Image in-place, relying on the kernel to relocate itself early during boot. In conjunction with commit dd4bc6076587 ("arm64: warn on incorrect placement of the kernel by the bootloader"), which enables CONFIG_RELOCATABLE by default, this effectively means that entering an arm64 kernel loaded at an alignment smaller than 2MB with EFI (e.g. using QEMU) will result in silent relocation at runtime. Unfortunately, this has a subtle but confusing affect for developers trying to inspect the PC value during a crash and comparing it to the symbol addresses in vmlinux using tools such as 'nm' or 'addr2line'; all text addresses will be displaced by a sub-2MB offset, resulting in the wrong symbol being identified in many cases. Passing "nokaslr" on the command line or disabling "CONFIG_RANDOMIZE_BASE" does not help, since the EFI stub only copies the kernel Image to a 2MB boundary if it is not relocatable. Adjust the EFI stub for arm64 so that the minimum Image alignment is 2MB unless KASLR is in use. Cc: Mark Rutland Cc: Catalin Marinas Cc: Marc Zyngier Cc: David Brazdil Acked-by: Ard Biesheuvel Signed-off-by: Will Deacon --- drivers/firmware/efi/libstub/arm64-stub.c | 25 ++++++++++++++----------- drivers/firmware/efi/libstub/efi-stub-helper.c | 2 +- 2 files changed, 15 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/arm64-stub.c b/drivers/firmware/efi/libstub/arm64-stub.c index 7f6a57dec513..e5bfac79e5ac 100644 --- a/drivers/firmware/efi/libstub/arm64-stub.c +++ b/drivers/firmware/efi/libstub/arm64-stub.c @@ -35,13 +35,16 @@ efi_status_t check_platform_features(void) } /* - * Relocatable kernels can fix up the misalignment with respect to - * MIN_KIMG_ALIGN, so they only require a minimum alignment of EFI_KIMG_ALIGN - * (which accounts for the alignment of statically allocated objects such as - * the swapper stack.) + * Although relocatable kernels can fix up the misalignment with respect to + * MIN_KIMG_ALIGN, the resulting virtual text addresses are subtly out of + * sync with those recorded in the vmlinux when kaslr is disabled but the + * image required relocation anyway. Therefore retain 2M alignment unless + * KASLR is in use. */ -static const u64 min_kimg_align = IS_ENABLED(CONFIG_RELOCATABLE) ? EFI_KIMG_ALIGN - : MIN_KIMG_ALIGN; +static u64 min_kimg_align(void) +{ + return efi_nokaslr ? MIN_KIMG_ALIGN : EFI_KIMG_ALIGN; +} efi_status_t handle_kernel_image(unsigned long *image_addr, unsigned long *image_size, @@ -74,21 +77,21 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, kernel_size = _edata - _text; kernel_memsize = kernel_size + (_end - _edata); - *reserve_size = kernel_memsize + TEXT_OFFSET % min_kimg_align; + *reserve_size = kernel_memsize + TEXT_OFFSET % min_kimg_align(); if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && phys_seed != 0) { /* * If KASLR is enabled, and we have some randomness available, * locate the kernel at a randomized offset in physical memory. */ - status = efi_random_alloc(*reserve_size, min_kimg_align, + status = efi_random_alloc(*reserve_size, min_kimg_align(), reserve_addr, phys_seed); } else { status = EFI_OUT_OF_RESOURCES; } if (status != EFI_SUCCESS) { - if (IS_ALIGNED((u64)_text - TEXT_OFFSET, min_kimg_align)) { + if (IS_ALIGNED((u64)_text - TEXT_OFFSET, min_kimg_align())) { /* * Just execute from wherever we were loaded by the * UEFI PE/COFF loader if the alignment is suitable. @@ -99,7 +102,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, } status = efi_allocate_pages_aligned(*reserve_size, reserve_addr, - ULONG_MAX, min_kimg_align); + ULONG_MAX, min_kimg_align()); if (status != EFI_SUCCESS) { efi_err("Failed to relocate kernel\n"); @@ -108,7 +111,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, } } - *image_addr = *reserve_addr + TEXT_OFFSET % min_kimg_align; + *image_addr = *reserve_addr + TEXT_OFFSET % min_kimg_align(); memcpy((void *)*image_addr, _text, kernel_size); return EFI_SUCCESS; diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 89f075275300..7038287a4608 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -19,7 +19,7 @@ #include "efistub.h" bool efi_nochunk; -bool efi_nokaslr; +bool efi_nokaslr = !IS_ENABLED(CONFIG_RANDOMIZE_BASE); bool efi_noinitrd; int efi_loglevel = CONSOLE_LOGLEVEL_DEFAULT; bool efi_novamap; -- cgit v1.2.3 From bdc5c744c7b6457d18a95c26769dad0e7f480a08 Mon Sep 17 00:00:00 2001 From: Qi Liu Date: Thu, 16 Jul 2020 17:19:25 +0800 Subject: drivers/perf: Fix kernel panic when rmmod PMU modules during perf sampling When users try to remove PMU modules during perf sampling, kernel panic will happen because the pmu->read() is a NULL pointer here. INFO on HiSilicon hip08 platform as follow: pc : hisi_uncore_pmu_event_update+0x30/0xa4 [hisi_uncore_pmu] lr : hisi_uncore_pmu_read+0x20/0x2c [hisi_uncore_pmu] sp : ffff800010103e90 x29: ffff800010103e90 x28: ffff0027db0c0e40 x27: ffffa29a76f129d8 x26: ffffa29a77ceb000 x25: ffffa29a773a5000 x24: ffffa29a77392000 x23: ffffddffe5943f08 x22: ffff002784285960 x21: ffff002784285800 x20: ffff0027d2e76c80 x19: ffff0027842859e0 x18: ffff80003498bcc8 x17: ffffa29a76afe910 x16: ffffa29a7583f530 x15: 16151a1512061a1e x14: 0000000000000000 x13: ffffa29a76f1e238 x12: 0000000000000001 x11: 0000000000000400 x10: 00000000000009f0 x9 : ffff8000107b3e70 x8 : ffff0027db0c1890 x7 : ffffa29a773a7000 x6 : 00000007f5131013 x5 : 00000007f5131013 x4 : 09f257d417c00000 x3 : 00000002187bd7ce x2 : ffffa29a38f0f0d8 x1 : ffffa29a38eae268 x0 : ffff0027d2e76c80 Call trace: hisi_uncore_pmu_event_update+0x30/0xa4 [hisi_uncore_pmu] hisi_uncore_pmu_read+0x20/0x2c [hisi_uncore_pmu] __perf_event_read+0x1a0/0x1f8 flush_smp_call_function_queue+0xa0/0x160 generic_smp_call_function_single_interrupt+0x18/0x20 handle_IPI+0x31c/0x4dc gic_handle_irq+0x2c8/0x310 el1_irq+0xcc/0x180 arch_cpu_idle+0x4c/0x20c default_idle_call+0x20/0x30 do_idle+0x1b4/0x270 cpu_startup_entry+0x28/0x30 secondary_start_kernel+0x1a4/0x1fc To solve the above issue, current module should be registered to kernel, so that try_module_get() can be invoked when perf sampling starts. This adds the reference counting of module and could prevent users from removing modules during sampling. Reported-by: Haifeng Wang Signed-off-by: Qi Liu Reviewed-by: John Garry Link: https://lore.kernel.org/r/1594891165-8228-1-git-send-email-liuqi115@huawei.com Signed-off-by: Will Deacon --- drivers/perf/arm_smmuv3_pmu.c | 1 + drivers/perf/fsl_imx8_ddr_perf.c | 1 + drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c | 1 + drivers/perf/hisilicon/hisi_uncore_hha_pmu.c | 1 + drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c | 1 + 5 files changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/perf/arm_smmuv3_pmu.c b/drivers/perf/arm_smmuv3_pmu.c index 48e28ef93a70..90caba56dfbc 100644 --- a/drivers/perf/arm_smmuv3_pmu.c +++ b/drivers/perf/arm_smmuv3_pmu.c @@ -742,6 +742,7 @@ static int smmu_pmu_probe(struct platform_device *pdev) platform_set_drvdata(pdev, smmu_pmu); smmu_pmu->pmu = (struct pmu) { + .module = THIS_MODULE, .task_ctx_nr = perf_invalid_context, .pmu_enable = smmu_pmu_enable, .pmu_disable = smmu_pmu_disable, diff --git a/drivers/perf/fsl_imx8_ddr_perf.c b/drivers/perf/fsl_imx8_ddr_perf.c index 90884d14f95f..2aed2d96f8ae 100644 --- a/drivers/perf/fsl_imx8_ddr_perf.c +++ b/drivers/perf/fsl_imx8_ddr_perf.c @@ -512,6 +512,7 @@ static int ddr_perf_init(struct ddr_pmu *pmu, void __iomem *base, { *pmu = (struct ddr_pmu) { .pmu = (struct pmu) { + .module = THIS_MODULE, .capabilities = PERF_PMU_CAP_NO_EXCLUDE, .task_ctx_nr = perf_invalid_context, .attr_groups = attr_groups, diff --git a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c index 15713faaa07e..71587f19fcd5 100644 --- a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c @@ -378,6 +378,7 @@ static int hisi_ddrc_pmu_probe(struct platform_device *pdev) ddrc_pmu->sccl_id, ddrc_pmu->index_id); ddrc_pmu->pmu = (struct pmu) { .name = name, + .module = THIS_MODULE, .task_ctx_nr = perf_invalid_context, .event_init = hisi_uncore_pmu_event_init, .pmu_enable = hisi_uncore_pmu_enable, diff --git a/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c b/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c index dcc5600788a9..c199de7b219d 100644 --- a/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c @@ -390,6 +390,7 @@ static int hisi_hha_pmu_probe(struct platform_device *pdev) hha_pmu->sccl_id, hha_pmu->index_id); hha_pmu->pmu = (struct pmu) { .name = name, + .module = THIS_MODULE, .task_ctx_nr = perf_invalid_context, .event_init = hisi_uncore_pmu_event_init, .pmu_enable = hisi_uncore_pmu_enable, diff --git a/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c b/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c index 7719ae4e2c56..567d7e6f69d8 100644 --- a/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c @@ -380,6 +380,7 @@ static int hisi_l3c_pmu_probe(struct platform_device *pdev) l3c_pmu->sccl_id, l3c_pmu->index_id); l3c_pmu->pmu = (struct pmu) { .name = name, + .module = THIS_MODULE, .task_ctx_nr = perf_invalid_context, .event_init = hisi_uncore_pmu_event_init, .pmu_enable = hisi_uncore_pmu_enable, -- cgit v1.2.3 From f32ed8eb0e3f0d0ef4ddb854554d60ca5863a9f9 Mon Sep 17 00:00:00 2001 From: Qi Liu Date: Fri, 17 Jul 2020 16:49:23 +0800 Subject: drivers/perf: Prevent forced unbinding of PMU drivers Forcefully unbinding PMU drivers during perf sampling will lead to a kernel panic, because the perf upper-layer framework call a NULL pointer in this situation. To solve this issue, "suppress_bind_attrs" should be set to true, so that bind/unbind can be disabled via sysfs and prevent unbinding PMU drivers during perf sampling. Signed-off-by: Qi Liu Reviewed-by: John Garry Link: https://lore.kernel.org/r/1594975763-32966-1-git-send-email-liuqi115@huawei.com Signed-off-by: Will Deacon --- drivers/perf/arm-cci.c | 1 + drivers/perf/arm-ccn.c | 1 + drivers/perf/arm_dsu_pmu.c | 1 + drivers/perf/arm_smmuv3_pmu.c | 1 + drivers/perf/arm_spe_pmu.c | 1 + drivers/perf/fsl_imx8_ddr_perf.c | 1 + drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c | 1 + drivers/perf/hisilicon/hisi_uncore_hha_pmu.c | 1 + drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c | 1 + drivers/perf/qcom_l2_pmu.c | 1 + drivers/perf/qcom_l3_pmu.c | 1 + drivers/perf/thunderx2_pmu.c | 1 + drivers/perf/xgene_pmu.c | 1 + 13 files changed, 13 insertions(+) (limited to 'drivers') diff --git a/drivers/perf/arm-cci.c b/drivers/perf/arm-cci.c index 1b8e337a29ca..87c4be9dd412 100644 --- a/drivers/perf/arm-cci.c +++ b/drivers/perf/arm-cci.c @@ -1718,6 +1718,7 @@ static struct platform_driver cci_pmu_driver = { .driver = { .name = DRIVER_NAME, .of_match_table = arm_cci_pmu_matches, + .suppress_bind_attrs = true, }, .probe = cci_pmu_probe, .remove = cci_pmu_remove, diff --git a/drivers/perf/arm-ccn.c b/drivers/perf/arm-ccn.c index d50edef91f59..7b7d23f25713 100644 --- a/drivers/perf/arm-ccn.c +++ b/drivers/perf/arm-ccn.c @@ -1545,6 +1545,7 @@ static struct platform_driver arm_ccn_driver = { .driver = { .name = "arm-ccn", .of_match_table = arm_ccn_match, + .suppress_bind_attrs = true, }, .probe = arm_ccn_probe, .remove = arm_ccn_remove, diff --git a/drivers/perf/arm_dsu_pmu.c b/drivers/perf/arm_dsu_pmu.c index 518d0603e24f..96ed93cc78e6 100644 --- a/drivers/perf/arm_dsu_pmu.c +++ b/drivers/perf/arm_dsu_pmu.c @@ -757,6 +757,7 @@ static struct platform_driver dsu_pmu_driver = { .driver = { .name = DRVNAME, .of_match_table = of_match_ptr(dsu_pmu_of_match), + .suppress_bind_attrs = true, }, .probe = dsu_pmu_device_probe, .remove = dsu_pmu_device_remove, diff --git a/drivers/perf/arm_smmuv3_pmu.c b/drivers/perf/arm_smmuv3_pmu.c index 90caba56dfbc..4cdb35d166ac 100644 --- a/drivers/perf/arm_smmuv3_pmu.c +++ b/drivers/perf/arm_smmuv3_pmu.c @@ -860,6 +860,7 @@ static void smmu_pmu_shutdown(struct platform_device *pdev) static struct platform_driver smmu_pmu_driver = { .driver = { .name = "arm-smmu-v3-pmcg", + .suppress_bind_attrs = true, }, .probe = smmu_pmu_probe, .remove = smmu_pmu_remove, diff --git a/drivers/perf/arm_spe_pmu.c b/drivers/perf/arm_spe_pmu.c index d80f48798bce..e51ddb6d63ed 100644 --- a/drivers/perf/arm_spe_pmu.c +++ b/drivers/perf/arm_spe_pmu.c @@ -1226,6 +1226,7 @@ static struct platform_driver arm_spe_pmu_driver = { .driver = { .name = DRVNAME, .of_match_table = of_match_ptr(arm_spe_pmu_of_match), + .suppress_bind_attrs = true, }, .probe = arm_spe_pmu_device_probe, .remove = arm_spe_pmu_device_remove, diff --git a/drivers/perf/fsl_imx8_ddr_perf.c b/drivers/perf/fsl_imx8_ddr_perf.c index 2aed2d96f8ae..397540a4b799 100644 --- a/drivers/perf/fsl_imx8_ddr_perf.c +++ b/drivers/perf/fsl_imx8_ddr_perf.c @@ -707,6 +707,7 @@ static struct platform_driver imx_ddr_pmu_driver = { .driver = { .name = "imx-ddr-pmu", .of_match_table = imx_ddr_pmu_dt_ids, + .suppress_bind_attrs = true, }, .probe = ddr_perf_probe, .remove = ddr_perf_remove, diff --git a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c index 71587f19fcd5..5e3645c96443 100644 --- a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c @@ -419,6 +419,7 @@ static struct platform_driver hisi_ddrc_pmu_driver = { .driver = { .name = "hisi_ddrc_pmu", .acpi_match_table = ACPI_PTR(hisi_ddrc_pmu_acpi_match), + .suppress_bind_attrs = true, }, .probe = hisi_ddrc_pmu_probe, .remove = hisi_ddrc_pmu_remove, diff --git a/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c b/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c index c199de7b219d..5eb8168029c0 100644 --- a/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c @@ -431,6 +431,7 @@ static struct platform_driver hisi_hha_pmu_driver = { .driver = { .name = "hisi_hha_pmu", .acpi_match_table = ACPI_PTR(hisi_hha_pmu_acpi_match), + .suppress_bind_attrs = true, }, .probe = hisi_hha_pmu_probe, .remove = hisi_hha_pmu_remove, diff --git a/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c b/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c index 567d7e6f69d8..3e8b5eab5514 100644 --- a/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c @@ -421,6 +421,7 @@ static struct platform_driver hisi_l3c_pmu_driver = { .driver = { .name = "hisi_l3c_pmu", .acpi_match_table = ACPI_PTR(hisi_l3c_pmu_acpi_match), + .suppress_bind_attrs = true, }, .probe = hisi_l3c_pmu_probe, .remove = hisi_l3c_pmu_remove, diff --git a/drivers/perf/qcom_l2_pmu.c b/drivers/perf/qcom_l2_pmu.c index 21d6991dbe0b..4da37f650f98 100644 --- a/drivers/perf/qcom_l2_pmu.c +++ b/drivers/perf/qcom_l2_pmu.c @@ -1028,6 +1028,7 @@ static struct platform_driver l2_cache_pmu_driver = { .driver = { .name = "qcom-l2cache-pmu", .acpi_match_table = ACPI_PTR(l2_cache_pmu_acpi_match), + .suppress_bind_attrs = true, }, .probe = l2_cache_pmu_probe, .remove = l2_cache_pmu_remove, diff --git a/drivers/perf/qcom_l3_pmu.c b/drivers/perf/qcom_l3_pmu.c index 656e830798d9..9ddb577c542b 100644 --- a/drivers/perf/qcom_l3_pmu.c +++ b/drivers/perf/qcom_l3_pmu.c @@ -814,6 +814,7 @@ static struct platform_driver qcom_l3_cache_pmu_driver = { .driver = { .name = "qcom-l3cache-pmu", .acpi_match_table = ACPI_PTR(qcom_l3_cache_pmu_acpi_match), + .suppress_bind_attrs = true, }, .probe = qcom_l3_cache_pmu_probe, }; diff --git a/drivers/perf/thunderx2_pmu.c b/drivers/perf/thunderx2_pmu.c index 51b31d6ff2c4..aac9823b0c6b 100644 --- a/drivers/perf/thunderx2_pmu.c +++ b/drivers/perf/thunderx2_pmu.c @@ -1017,6 +1017,7 @@ static struct platform_driver tx2_uncore_driver = { .driver = { .name = "tx2-uncore-pmu", .acpi_match_table = ACPI_PTR(tx2_uncore_acpi_match), + .suppress_bind_attrs = true, }, .probe = tx2_uncore_probe, .remove = tx2_uncore_remove, diff --git a/drivers/perf/xgene_pmu.c b/drivers/perf/xgene_pmu.c index 46ee6807d533..edac28cd25dd 100644 --- a/drivers/perf/xgene_pmu.c +++ b/drivers/perf/xgene_pmu.c @@ -1975,6 +1975,7 @@ static struct platform_driver xgene_pmu_driver = { .name = "xgene-pmu", .of_match_table = xgene_pmu_of_match, .acpi_match_table = ACPI_PTR(xgene_pmu_acpi_match), + .suppress_bind_attrs = true, }, }; -- cgit v1.2.3