// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2022, Athira Rajeev, IBM Corp. * Copyright 2022, Madhavan Srinivasan, IBM Corp. * Copyright 2022, Kajol Jain, IBM Corp. */ #include #include #include #include #include #include #include #include #include "misc.h" #define PAGE_SIZE sysconf(_SC_PAGESIZE) /* Storage for platform version */ int pvr; u64 platform_extended_mask; /* Mask and Shift for Event code fields */ int ev_mask_pmcxsel, ev_shift_pmcxsel; //pmcxsel field int ev_mask_marked, ev_shift_marked; //marked filed int ev_mask_comb, ev_shift_comb; //combine field int ev_mask_unit, ev_shift_unit; //unit field int ev_mask_pmc, ev_shift_pmc; //pmc field int ev_mask_cache, ev_shift_cache; //Cache sel field int ev_mask_sample, ev_shift_sample; //Random sampling field int ev_mask_thd_sel, ev_shift_thd_sel; //thresh_sel field int ev_mask_thd_start, ev_shift_thd_start; //thresh_start field int ev_mask_thd_stop, ev_shift_thd_stop; //thresh_stop field int ev_mask_thd_cmp, ev_shift_thd_cmp; //thresh cmp field int ev_mask_sm, ev_shift_sm; //SDAR mode field int ev_mask_rsq, ev_shift_rsq; //radix scope qual field int ev_mask_l2l3, ev_shift_l2l3; //l2l3 sel field int ev_mask_mmcr3_src, ev_shift_mmcr3_src; //mmcr3 field static void init_ev_encodes(void) { ev_mask_pmcxsel = 0xff; ev_shift_pmcxsel = 0; ev_mask_marked = 1; ev_shift_marked = 8; ev_mask_unit = 0xf; ev_shift_unit = 12; ev_mask_pmc = 0xf; ev_shift_pmc = 16; ev_mask_sample = 0x1f; ev_shift_sample = 24; ev_mask_thd_sel = 0x7; ev_shift_thd_sel = 29; ev_mask_thd_start = 0xf; ev_shift_thd_start = 36; ev_mask_thd_stop = 0xf; ev_shift_thd_stop = 32; switch (pvr) { case POWER10: ev_mask_thd_cmp = 0x3ffff; ev_shift_thd_cmp = 0; ev_mask_rsq = 1; ev_shift_rsq = 9; ev_mask_comb = 3; ev_shift_comb = 10; ev_mask_cache = 3; ev_shift_cache = 20; ev_mask_sm = 0x3; ev_shift_sm = 22; ev_mask_l2l3 = 0x1f; ev_shift_l2l3 = 40; ev_mask_mmcr3_src = 0x7fff; ev_shift_mmcr3_src = 45; break; case POWER9: ev_mask_comb = 3; ev_shift_comb = 10; ev_mask_cache = 0xf; ev_shift_cache = 20; ev_mask_thd_cmp = 0x3ff; ev_shift_thd_cmp = 40; ev_mask_sm = 0x3; ev_shift_sm = 50; break; default: FAIL_IF_EXIT(1); } } /* Return the extended regs mask value */ static u64 perf_get_platform_reg_mask(void) { if (have_hwcap2(PPC_FEATURE2_ARCH_3_1)) return PERF_POWER10_MASK; if (have_hwcap2(PPC_FEATURE2_ARCH_3_00)) return PERF_POWER9_MASK; return -1; } int check_extended_regs_support(void) { int fd; struct event event; event_init(&event, 0x1001e); event.attr.type = 4; event.attr.sample_period = 1; event.attr.disabled = 1; event.attr.sample_type = PERF_SAMPLE_REGS_INTR; event.attr.sample_regs_intr = platform_extended_mask; fd = event_open(&event); if (fd != -1) return 0; return -1; } int platform_check_for_tests(void) { pvr = PVR_VER(mfspr(SPRN_PVR)); /* * Check for supported platforms * for sampling test */ if ((pvr != POWER10) && (pvr != POWER9)) goto out; /* * Check PMU driver registered by looking for * PPC_FEATURE2_EBB bit in AT_HWCAP2 */ if (!have_hwcap2(PPC_FEATURE2_EBB) || !have_hwcap2(PPC_FEATURE2_ARCH_3_00)) goto out; return 0; out: printf("%s: Tests unsupported for this platform\n", __func__); return -1; } int check_pvr_for_sampling_tests(void) { SKIP_IF(platform_check_for_tests()); platform_extended_mask = perf_get_platform_reg_mask(); /* check if platform supports extended regs */ if (check_extended_regs_support()) goto out; init_ev_encodes(); return 0; out: printf("%s: Sampling tests un-supported\n", __func__); return -1; } /* * Allocate mmap buffer of "mmap_pages" number of * pages. */ void *event_sample_buf_mmap(int fd, int mmap_pages) { size_t page_size = sysconf(_SC_PAGESIZE); size_t mmap_size; void *buff; if (mmap_pages <= 0) return NULL; if (fd <= 0) return NULL; mmap_size = page_size * (1 + mmap_pages); buff = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (buff == MAP_FAILED) { perror("mmap() failed."); return NULL; } return buff; } /* * Post process the mmap buffer. * - If sample_count != NULL then return count of total * number of samples present in the mmap buffer. * - If sample_count == NULL then return the address * of first sample from the mmap buffer */ void *__event_read_samples(void *sample_buff, size_t *size, u64 *sample_count) { size_t page_size = sysconf(_SC_PAGESIZE); struct perf_event_header *header = sample_buff + page_size; struct perf_event_mmap_page *metadata_page = sample_buff; unsigned long data_head, data_tail; /* * PERF_RECORD_SAMPLE: * struct { * struct perf_event_header hdr; * u64 data[]; * }; */ data_head = metadata_page->data_head; /* sync memory before reading sample */ mb(); data_tail = metadata_page->data_tail; /* Check for sample_count */ if (sample_count) *sample_count = 0; while (1) { /* * Reads the mmap data buffer by moving * the data_tail to know the last read data. * data_head points to head in data buffer. * refer "struct perf_event_mmap_page" in * "include/uapi/linux/perf_event.h". */ if (data_head - data_tail < sizeof(header)) return NULL; data_tail += sizeof(header); if (header->type == PERF_RECORD_SAMPLE) { *size = (header->size - sizeof(header)); if (!sample_count) return sample_buff + page_size + data_tail; data_tail += *size; *sample_count += 1; } else { *size = (header->size - sizeof(header)); if ((metadata_page->data_tail + *size) > metadata_page->data_head) data_tail = metadata_page->data_head; else data_tail += *size; } header = (struct perf_event_header *)((void *)header + header->size); } return NULL; } int collect_samples(void *sample_buff) { u64 sample_count; size_t size = 0; __event_read_samples(sample_buff, &size, &sample_count); return sample_count; } static void *perf_read_first_sample(void *sample_buff, size_t *size) { return __event_read_samples(sample_buff, size, NULL); } u64 *get_intr_regs(struct event *event, void *sample_buff) { u64 type = event->attr.sample_type; u64 *intr_regs; size_t size = 0; if ((type ^ (PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_BRANCH_STACK)) && (type ^ PERF_SAMPLE_REGS_INTR)) return NULL; intr_regs = (u64 *)perf_read_first_sample(sample_buff, &size); if (!intr_regs) return NULL; if (type & PERF_SAMPLE_BRANCH_STACK) { /* * PERF_RECORD_SAMPLE and PERF_SAMPLE_BRANCH_STACK: * struct { * struct perf_event_header hdr; * u64 number_of_branches; * struct perf_branch_entry[number_of_branches]; * u64 data[]; * }; * struct perf_branch_entry { * u64 from; * u64 to; * u64 misc; * }; */ intr_regs += ((*intr_regs) * 3) + 1; } /* * First entry in the sample buffer used to specify * PERF_SAMPLE_REGS_ABI_64, skip perf regs abi to access * interrupt registers. */ ++intr_regs; return intr_regs; } static const int __perf_reg_mask(const char *register_name) { if (!strcmp(register_name, "R0")) return 0; else if (!strcmp(register_name, "R1")) return 1; else if (!strcmp(register_name, "R2")) return 2; else if (!strcmp(register_name, "R3")) return 3; else if (!strcmp(register_name, "R4")) return 4; else if (!strcmp(register_name, "R5")) return 5; else if (!strcmp(register_name, "R6")) return 6; else if (!strcmp(register_name, "R7")) return 7; else if (!strcmp(register_name, "R8")) return 8; else if (!strcmp(register_name, "R9")) return 9; else if (!strcmp(register_name, "R10")) return 10; else if (!strcmp(register_name, "R11")) return 11; else if (!strcmp(register_name, "R12")) return 12; else if (!strcmp(register_name, "R13")) return 13; else if (!strcmp(register_name, "R14")) return 14; else if (!strcmp(register_name, "R15")) return 15; else if (!strcmp(register_name, "R16")) return 16; else if (!strcmp(register_name, "R17")) return 17; else if (!strcmp(register_name, "R18")) return 18; else if (!strcmp(register_name, "R19")) return 19; else if (!strcmp(register_name, "R20")) return 20; else if (!strcmp(register_name, "R21")) return 21; else if (!strcmp(register_name, "R22")) return 22; else if (!strcmp(register_name, "R23")) return 23; else if (!strcmp(register_name, "R24")) return 24; else if (!strcmp(register_name, "R25")) return 25; else if (!strcmp(register_name, "R26")) return 26; else if (!strcmp(register_name, "R27")) return 27; else if (!strcmp(register_name, "R28")) return 28; else if (!strcmp(register_name, "R29")) return 29; else if (!strcmp(register_name, "R30")) return 30; else if (!strcmp(register_name, "R31")) return 31; else if (!strcmp(register_name, "NIP")) return 32; else if (!strcmp(register_name, "MSR")) return 33; else if (!strcmp(register_name, "ORIG_R3")) return 34; else if (!strcmp(register_name, "CTR")) return 35; else if (!strcmp(register_name, "LINK")) return 36; else if (!strcmp(register_name, "XER")) return 37; else if (!strcmp(register_name, "CCR")) return 38; else if (!strcmp(register_name, "SOFTE")) return 39; else if (!strcmp(register_name, "TRAP")) return 40; else if (!strcmp(register_name, "DAR")) return 41; else if (!strcmp(register_name, "DSISR")) return 42; else if (!strcmp(register_name, "SIER")) return 43; else if (!strcmp(register_name, "MMCRA")) return 44; else if (!strcmp(register_name, "MMCR0")) return 45; else if (!strcmp(register_name, "MMCR1")) return 46; else if (!strcmp(register_name, "MMCR2")) return 47; else if (!strcmp(register_name, "MMCR3")) return 48; else if (!strcmp(register_name, "SIER2")) return 49; else if (!strcmp(register_name, "SIER3")) return 50; else if (!strcmp(register_name, "PMC1")) return 51; else if (!strcmp(register_name, "PMC2")) return 52; else if (!strcmp(register_name, "PMC3")) return 53; else if (!strcmp(register_name, "PMC4")) return 54; else if (!strcmp(register_name, "PMC5")) return 55; else if (!strcmp(register_name, "PMC6")) return 56; else if (!strcmp(register_name, "SDAR")) return 57; else if (!strcmp(register_name, "SIAR")) return 58; else return -1; } u64 get_reg_value(u64 *intr_regs, char *register_name) { int register_bit_position; register_bit_position = __perf_reg_mask(register_name); if (register_bit_position < 0 || (!((platform_extended_mask >> (register_bit_position - 1)) & 1))) return -1; return *(intr_regs + register_bit_position); } int get_thresh_cmp_val(struct event event) { int exp = 0; u64 result = 0; u64 value; if (!have_hwcap2(PPC_FEATURE2_ARCH_3_1)) return EV_CODE_EXTRACT(event.attr.config, thd_cmp); value = EV_CODE_EXTRACT(event.attr.config1, thd_cmp); if (!value) return value; /* * Incase of P10, thresh_cmp value is not part of raw event code * and provided via attr.config1 parameter. To program threshold in MMCRA, * take a 18 bit number N and shift right 2 places and increment * the exponent E by 1 until the upper 10 bits of N are zero. * Write E to the threshold exponent and write the lower 8 bits of N * to the threshold mantissa. * The max threshold that can be written is 261120. */ if (value > 261120) value = 261120; while ((64 - __builtin_clzl(value)) > 8) { exp++; value >>= 2; } /* * Note that it is invalid to write a mantissa with the * upper 2 bits of mantissa being zero, unless the * exponent is also zero. */ if (!(value & 0xC0) && exp) result = -1; else result = (exp << 8) | value; return result; } /* * Utility function to check for generic compat PMU * by comparing base_platform value from auxv and real * PVR value. */ static bool auxv_generic_compat_pmu(void) { int base_pvr = 0; if (!strcmp(auxv_base_platform(), "power9")) base_pvr = POWER9; else if (!strcmp(auxv_base_platform(), "power10")) base_pvr = POWER10; return (!base_pvr); } /* * Check for generic compat PMU. * First check for presence of pmu_name from * "/sys/bus/event_source/devices/cpu/caps". * If doesn't exist, fallback to using value * auxv. */ bool check_for_generic_compat_pmu(void) { char pmu_name[256]; memset(pmu_name, 0, sizeof(pmu_name)); if (read_sysfs_file("bus/event_source/devices/cpu/caps/pmu_name", pmu_name, sizeof(pmu_name)) < 0) return auxv_generic_compat_pmu(); if (!strcmp(pmu_name, "ISAv3")) return true; else return false; } /* * Check if system is booted in compat mode. */ bool check_for_compat_mode(void) { char *platform = auxv_platform(); char *base_platform = auxv_base_platform(); return strcmp(platform, base_platform); }