diff options
| author | Andre Silva <andasilv@amd.com> | 2026-06-12 15:39:16 +0000 |
|---|---|---|
| committer | Mitchell Horne <mhorne@FreeBSD.org> | 2026-09-03 17:17:24 +0000 |
| commit | e555692d1bbbe8918054746f21013fff6256974e (patch) | |
| tree | da6d1f3313b5efbe8f5b38d7edc324cd56b6d60e | |
| parent | f19f44634afd859e445c7b13bbdf715a493b499b (diff) | |
hwpmc: Add ATF regression tests for hwpmc EXTERROR diagnostics
Root-only ATF program hitting negative allocate/attach/read-write paths
and asserting the exterr(3) text. AMD/IBS cases skip without the PMC
class; program skips without hwpmc.
Additional changes by mhorne@:
- Move and rename to the established test directory tests/sys/pmc
- Remove broken test amd_missing_pmu_flag; fixed by recent change
6c4d9b9af1a3
- Add ATF_REQUIRE_FEATURE("exterr_strings") to skip the tests on kernels
compiled without the strings
- Remove arch-conditional compilation; tests are properly gated by PMC
class check
- Fix copyright formatting
Reviewed by: Ali Mashtizadeh <ali@mashtizadeh.com>
Signed-off-by: Andre Silva <andasilv@amd.com>
Co-authored-by: mhorne
Sponsored by: AMD
Pull Request: https://github.com/freebsd/freebsd-src/pull/2180
| -rw-r--r-- | tests/sys/pmc/Makefile | 5 | ||||
| -rw-r--r-- | tests/sys/pmc/pmc_exterr_test.c | 449 |
2 files changed, 454 insertions, 0 deletions
diff --git a/tests/sys/pmc/Makefile b/tests/sys/pmc/Makefile index c9c138250932..311b2ef6343c 100644 --- a/tests/sys/pmc/Makefile +++ b/tests/sys/pmc/Makefile @@ -4,11 +4,16 @@ ATF_TESTS_C+= pmc_api_test ATF_TESTS_C+= pmc_credexec_test ATF_TESTS_C+= pmc_detach_test ATF_TESTS_C+= pmc_exec_test +ATF_TESTS_C+= pmc_exterr_test ATF_TESTS_C+= pmc_lifecycle_test ATF_TESTS_C+= pmc_log_test ATF_TESTS_C+= pmc_wrap_test +TEST_METADATA.pmc_exterr_test+= required_user="root" + LIBADD+= pmc LIBADD.pmc_detach_test+= pthread +CFLAGS.pmc_exterr_test.c+= -I${SRCTOP}/tests + .include <bsd.test.mk> diff --git a/tests/sys/pmc/pmc_exterr_test.c b/tests/sys/pmc/pmc_exterr_test.c new file mode 100644 index 000000000000..bc4d260f2ed4 --- /dev/null +++ b/tests/sys/pmc/pmc_exterr_test.c @@ -0,0 +1,449 @@ +/* + * Copyright (c) 2026 Advanced Micro Devices, Inc. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <sys/param.h> +#include <sys/exterrvar.h> +#include <sys/module.h> +#include <sys/pmc.h> + +#include <dev/hwpmc/hwpmc_amd.h> +#include <dev/hwpmc/hwpmc_ibs.h> + +#include <atf-c.h> +#include <errno.h> +#include <exterr.h> +#include <pmc.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "freebsd_test_suite/macros.h" + +static int +hwpmc_syscall_num(void) +{ + struct module_stat ms; + int modid; + + modid = modfind(PMC_MODULE_NAME); + if (modid < 0) + return (-1); + + ms.version = sizeof(ms); + if (modstat(modid, &ms) < 0) + return (-1); + + return (ms.data.intval); +} + +static int +hwpmc_call(int op, void *arg) +{ + int sc; + + sc = hwpmc_syscall_num(); + if (sc < 0) + return (-1); + + return (syscall(sc, op, arg)); +} + +static void +clear_exterr(void) +{ + char exterr[UEXTERROR_MAXLEN]; + + ATF_REQUIRE_ERRNO(EINVAL, exterrctl(EXTERRCTL_UD, 0, NULL) == -1); + ATF_REQUIRE_EQ(0, uexterr_gettext(exterr, sizeof(exterr))); + ATF_REQUIRE_STREQ("", exterr); +} + +static void +require_exterr(const char *needle) +{ + char exterr[UEXTERROR_MAXLEN]; + + ATF_REQUIRE_EQ(0, uexterr_gettext(exterr, sizeof(exterr))); + ATF_REQUIRE_MSG(strstr(exterr, needle) != NULL, + "extended error \"%s\" does not contain \"%s\"", exterr, needle); +} + +static void +require_hwpmc(void) +{ + + if (pmc_init() == 0) + return; + + atf_tc_skip("hwpmc is unavailable: %s", strerror(errno)); +} + +static void +require_class(enum pmc_class class, const char *name) +{ + const struct pmc_cpuinfo *pci; + uint32_t i; + + ATF_REQUIRE_EQ(0, pmc_cpuinfo(&pci)); + for (i = 0; i < pci->pm_nclass; i++) { + if (pci->pm_classes[i].pm_class == class) + return; + } + atf_tc_skip("%s PMCs are unavailable on this system", name); +} + +static void +allocate_soft_pmc(enum pmc_mode mode, int cpu, pmc_id_t *pmcid) +{ + + ATF_REQUIRE_EQ(0, pmc_allocate("SOFT-CLOCK.HARD", mode, 0, cpu, pmcid, + 1)); +} + +ATF_TC_WITHOUT_HEAD(pmcallocate_invalid_mode); +ATF_TC_BODY(pmcallocate_invalid_mode, tc) +{ + struct pmc_op_pmcallocate pa; + + ATF_REQUIRE_FEATURE("exterr_strings"); + require_hwpmc(); + clear_exterr(); + + memset(&pa, 0, sizeof(pa)); + pa.pm_class = PMC_CLASS_SOFT; + pa.pm_mode = (enum pmc_mode)0xff; + pa.pm_cpu = PMC_CPU_ANY; + + ATF_REQUIRE_ERRNO(EINVAL, hwpmc_call(PMC_OP_PMCALLOCATE, &pa) == -1); + require_exterr("Invalid PMC mode"); +} + +ATF_TC_WITHOUT_HEAD(pmcallocate_invalid_cpu); +ATF_TC_BODY(pmcallocate_invalid_cpu, tc) +{ + struct pmc_op_pmcallocate pa; + + ATF_REQUIRE_FEATURE("exterr_strings"); + require_hwpmc(); + clear_exterr(); + + memset(&pa, 0, sizeof(pa)); + pa.pm_class = PMC_CLASS_SOFT; + pa.pm_mode = PMC_MODE_TC; + pa.pm_cpu = (uint32_t)pmc_ncpu(); + + ATF_REQUIRE_ERRNO(EINVAL, hwpmc_call(PMC_OP_PMCALLOCATE, &pa) == -1); + require_exterr("Invalid PMC CPU"); +} + +ATF_TC_WITHOUT_HEAD(pmcallocate_invalid_flags); +ATF_TC_BODY(pmcallocate_invalid_flags, tc) +{ + struct pmc_op_pmcallocate pa; + + ATF_REQUIRE_FEATURE("exterr_strings"); + require_hwpmc(); + clear_exterr(); + + memset(&pa, 0, sizeof(pa)); + pa.pm_class = PMC_CLASS_SOFT; + pa.pm_mode = PMC_MODE_TC; + pa.pm_cpu = PMC_CPU_ANY; + pa.pm_flags = 1U << 31; + + ATF_REQUIRE_ERRNO(EINVAL, hwpmc_call(PMC_OP_PMCALLOCATE, &pa) == -1); + require_exterr("Invalid PMC flags"); +} + +ATF_TC_WITHOUT_HEAD(pmcattach_system_mode); +ATF_TC_BODY(pmcattach_system_mode, tc) +{ + struct pmc_op_pmcattach pa; + pmc_id_t pmcid; + + ATF_REQUIRE_FEATURE("exterr_strings"); + require_hwpmc(); + clear_exterr(); + allocate_soft_pmc(PMC_MODE_SC, 0, &pmcid); + + memset(&pa, 0, sizeof(pa)); + pa.pm_pmc = pmcid; + pa.pm_pid = getpid(); + + ATF_REQUIRE_ERRNO(EINVAL, hwpmc_call(PMC_OP_PMCATTACH, &pa) == -1); + require_exterr("Cannot attach a system-mode PMC"); + + ATF_REQUIRE_EQ(0, pmc_release(pmcid)); +} + +ATF_TC_WITHOUT_HEAD(pmcattach_running_pmc); +ATF_TC_BODY(pmcattach_running_pmc, tc) +{ + struct pmc_op_pmcattach pa; + pmc_id_t pmcid; + + ATF_REQUIRE_FEATURE("exterr_strings"); + require_hwpmc(); + clear_exterr(); + allocate_soft_pmc(PMC_MODE_TC, PMC_CPU_ANY, &pmcid); + ATF_REQUIRE_EQ(0, pmc_start(pmcid)); + + memset(&pa, 0, sizeof(pa)); + pa.pm_pmc = pmcid; + pa.pm_pid = getpid(); + + ATF_REQUIRE_ERRNO(EBUSY, hwpmc_call(PMC_OP_PMCATTACH, &pa) == -1); + require_exterr("PMC must be stopped before attach"); + + ATF_REQUIRE_EQ(0, pmc_stop(pmcid)); + ATF_REQUIRE_EQ(0, pmc_release(pmcid)); +} + +ATF_TC_WITHOUT_HEAD(pmcrw_no_flags); +ATF_TC_BODY(pmcrw_no_flags, tc) +{ + struct pmc_op_pmcrw prw; + pmc_id_t pmcid; + + ATF_REQUIRE_FEATURE("exterr_strings"); + require_hwpmc(); + clear_exterr(); + allocate_soft_pmc(PMC_MODE_TC, PMC_CPU_ANY, &pmcid); + + memset(&prw, 0, sizeof(prw)); + prw.pm_pmcid = pmcid; + + ATF_REQUIRE_ERRNO(EINVAL, hwpmc_call(PMC_OP_PMCRW, &prw) == -1); + require_exterr("PMCRW requires PMC_F_OLDVALUE"); + + ATF_REQUIRE_EQ(0, pmc_release(pmcid)); +} + +ATF_TC_WITHOUT_HEAD(pmcrw_write_running); +ATF_TC_BODY(pmcrw_write_running, tc) +{ + struct pmc_op_pmcrw prw; + pmc_id_t pmcid; + + ATF_REQUIRE_FEATURE("exterr_strings"); + require_hwpmc(); + clear_exterr(); + allocate_soft_pmc(PMC_MODE_TC, PMC_CPU_ANY, &pmcid); + ATF_REQUIRE_EQ(0, pmc_start(pmcid)); + + memset(&prw, 0, sizeof(prw)); + prw.pm_flags = PMC_F_NEWVALUE; + prw.pm_pmcid = pmcid; + prw.pm_value = 1; + + ATF_REQUIRE_ERRNO(EBUSY, hwpmc_call(PMC_OP_PMCRW, &prw) == -1); + require_exterr("Cannot write a PMC while it is running"); + + ATF_REQUIRE_EQ(0, pmc_stop(pmcid)); + ATF_REQUIRE_EQ(0, pmc_release(pmcid)); +} + +ATF_TC_WITHOUT_HEAD(amd_invalid_subclass); +ATF_TC_BODY(amd_invalid_subclass, tc) +{ + struct pmc_op_pmcallocate pa; + + ATF_REQUIRE_FEATURE("exterr_strings"); + require_hwpmc(); + require_class(PMC_CLASS_K8, "AMD"); + clear_exterr(); + + memset(&pa, 0, sizeof(pa)); + pa.pm_class = PMC_CLASS_K8; + pa.pm_mode = PMC_MODE_TC; + pa.pm_cpu = PMC_CPU_ANY; + pa.pm_flags = PMC_F_EV_PMU; + pa.pm_md.pm_amd.pm_amd_sub_class = UINT32_MAX; + + ATF_REQUIRE_ERRNO(EINVAL, hwpmc_call(PMC_OP_PMCALLOCATE, &pa) == -1); + require_exterr("No PMC row accepted"); +} + +ATF_TC_WITHOUT_HEAD(amd_invalid_config_bits); +ATF_TC_BODY(amd_invalid_config_bits, tc) +{ + struct pmc_op_pmcallocate pa; + char exterr[UEXTERROR_MAXLEN]; + + ATF_REQUIRE_FEATURE("exterr_strings"); + require_hwpmc(); + require_class(PMC_CLASS_K8, "AMD"); + if (!pmc_pmu_enabled()) + atf_tc_skip("AMD PMU raw allocation path is unavailable"); + clear_exterr(); + + memset(&pa, 0, sizeof(pa)); + pa.pm_class = PMC_CLASS_K8; + pa.pm_mode = PMC_MODE_TC; + pa.pm_cpu = PMC_CPU_ANY; + pa.pm_flags = PMC_F_EV_PMU; + pa.pm_md.pm_amd.pm_amd_sub_class = PMC_AMD_SUB_CLASS_CORE; + pa.pm_md.pm_amd.pm_amd_config = ~0ULL; + + ATF_REQUIRE_ERRNO(EINVAL, hwpmc_call(PMC_OP_PMCALLOCATE, &pa) == -1); + ATF_REQUIRE_EQ(0, uexterr_gettext(exterr, sizeof(exterr))); + if (strstr(exterr, "No PMC row accepted") != NULL) + atf_tc_skip("AMD core reserved-bit diagnostic was not returned"); + ATF_REQUIRE_MSG(strstr(exterr, + "AMD PMU config has unsupported bits") != NULL, + "extended error \"%s\" does not report core reserved-bit failure", + exterr); +} + +/* + * DF subclass with reserved config bits. On a system with Data Fabric + * PMC support, the DF row matches class+subclass and the config-mask + * check fires with the "AMD PMU config has unsupported bits" diagnostic. + * On a system without DF support every row mismatches subclass and we + * surface "No PMC row accepted"; the test skips in that case rather + * than fail. + */ +ATF_TC_WITHOUT_HEAD(amd_invalid_df_config_bits); +ATF_TC_BODY(amd_invalid_df_config_bits, tc) +{ + struct pmc_op_pmcallocate pa; + char exterr[UEXTERROR_MAXLEN]; + + ATF_REQUIRE_FEATURE("exterr_strings"); + require_hwpmc(); + require_class(PMC_CLASS_K8, "AMD"); + if (!pmc_pmu_enabled()) + atf_tc_skip("AMD PMU raw allocation path is unavailable"); + clear_exterr(); + + memset(&pa, 0, sizeof(pa)); + pa.pm_class = PMC_CLASS_K8; + pa.pm_mode = PMC_MODE_SC; + pa.pm_cpu = 0; + pa.pm_flags = PMC_F_EV_PMU; + pa.pm_md.pm_amd.pm_amd_sub_class = PMC_AMD_SUB_CLASS_DATA_FABRIC; + pa.pm_md.pm_amd.pm_amd_config = ~0ULL; + + ATF_REQUIRE_ERRNO(EINVAL, hwpmc_call(PMC_OP_PMCALLOCATE, &pa) == -1); + ATF_REQUIRE_EQ(0, uexterr_gettext(exterr, sizeof(exterr))); + if (strstr(exterr, "No PMC row accepted") != NULL) + atf_tc_skip("Data Fabric PMCs are unavailable on this system"); + ATF_REQUIRE_MSG(strstr(exterr, + "AMD PMU config has unsupported bits") != NULL, + "extended error \"%s\" does not report DF reserved-bit failure", + exterr); + fprintf(stderr, "DF reserved-bit allocation: errno=EINVAL exterr=%s\n", + exterr); +} + +ATF_TC_WITHOUT_HEAD(ibs_missing_system_capability); +ATF_TC_BODY(ibs_missing_system_capability, tc) +{ + struct pmc_op_pmcallocate pa; + + ATF_REQUIRE_FEATURE("exterr_strings"); + require_hwpmc(); + require_class(PMC_CLASS_IBS, "IBS"); + clear_exterr(); + + memset(&pa, 0, sizeof(pa)); + pa.pm_class = PMC_CLASS_IBS; + pa.pm_mode = PMC_MODE_SS; + pa.pm_cpu = 0; + pa.pm_md.pm_ibs.ibs_type = IBS_PMC_OP; + + ATF_REQUIRE_ERRNO(EINVAL, hwpmc_call(PMC_OP_PMCALLOCATE, &pa) == -1); + require_exterr("IBS requires SYSTEM capability"); +} + +/* + * An out-of-range ibs_type is a row discriminator mismatch on every IBS + * row (bare EINVAL), so the generic loop-exhaustion message is surfaced. + */ +ATF_TC_WITHOUT_HEAD(ibs_invalid_type); +ATF_TC_BODY(ibs_invalid_type, tc) +{ + struct pmc_op_pmcallocate pa; + + ATF_REQUIRE_FEATURE("exterr_strings"); + require_hwpmc(); + require_class(PMC_CLASS_IBS, "IBS"); + clear_exterr(); + + memset(&pa, 0, sizeof(pa)); + pa.pm_caps = PMC_CAP_SYSTEM; + pa.pm_class = PMC_CLASS_IBS; + pa.pm_mode = PMC_MODE_SS; + pa.pm_cpu = 0; + pa.pm_md.pm_ibs.ibs_type = UINT32_MAX; + + ATF_REQUIRE_ERRNO(EINVAL, hwpmc_call(PMC_OP_PMCALLOCATE, &pa) == -1); + require_exterr("No PMC row accepted"); +} + +ATF_TC_WITHOUT_HEAD(ibs_invalid_config_bits); +ATF_TC_BODY(ibs_invalid_config_bits, tc) +{ + struct pmc_op_pmcallocate pa; + + ATF_REQUIRE_FEATURE("exterr_strings"); + require_hwpmc(); + require_class(PMC_CLASS_IBS, "IBS"); + clear_exterr(); + + memset(&pa, 0, sizeof(pa)); + pa.pm_caps = PMC_CAP_SYSTEM; + pa.pm_class = PMC_CLASS_IBS; + pa.pm_mode = PMC_MODE_SS; + pa.pm_cpu = 0; + pa.pm_md.pm_ibs.ibs_type = IBS_PMC_OP; + pa.pm_md.pm_ibs.ibs_ctl = 1ULL << 58; + + ATF_REQUIRE_ERRNO(EINVAL, hwpmc_call(PMC_OP_PMCALLOCATE, &pa) == -1); + require_exterr("IBS op ctl config"); +} + +ATF_TC_WITHOUT_HEAD(ibs_nonsampling_mode); +ATF_TC_BODY(ibs_nonsampling_mode, tc) +{ + struct pmc_op_pmcallocate pa; + + ATF_REQUIRE_FEATURE("exterr_strings"); + require_hwpmc(); + require_class(PMC_CLASS_IBS, "IBS"); + clear_exterr(); + + memset(&pa, 0, sizeof(pa)); + pa.pm_caps = PMC_CAP_SYSTEM; + pa.pm_class = PMC_CLASS_IBS; + pa.pm_mode = PMC_MODE_SC; + pa.pm_cpu = 0; + pa.pm_md.pm_ibs.ibs_type = IBS_PMC_OP; + + ATF_REQUIRE_ERRNO(EINVAL, hwpmc_call(PMC_OP_PMCALLOCATE, &pa) == -1); + require_exterr("IBS only supports sampling mode"); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, pmcallocate_invalid_mode); + ATF_TP_ADD_TC(tp, pmcallocate_invalid_cpu); + ATF_TP_ADD_TC(tp, pmcallocate_invalid_flags); + ATF_TP_ADD_TC(tp, pmcattach_system_mode); + ATF_TP_ADD_TC(tp, pmcattach_running_pmc); + ATF_TP_ADD_TC(tp, pmcrw_no_flags); + ATF_TP_ADD_TC(tp, pmcrw_write_running); + ATF_TP_ADD_TC(tp, amd_invalid_subclass); + ATF_TP_ADD_TC(tp, amd_invalid_config_bits); + ATF_TP_ADD_TC(tp, amd_invalid_df_config_bits); + ATF_TP_ADD_TC(tp, ibs_missing_system_capability); + ATF_TP_ADD_TC(tp, ibs_invalid_type); + ATF_TP_ADD_TC(tp, ibs_invalid_config_bits); + ATF_TP_ADD_TC(tp, ibs_nonsampling_mode); + + return (atf_no_error()); +} |
