Path: blob/master/arch/powerpc/platforms/pseries/htmdump.c
26481 views
// SPDX-License-Identifier: GPL-2.0-or-later1/*2* Copyright (C) IBM Corporation, 20243*/45#define pr_fmt(fmt) "htmdump: " fmt67#include <linux/debugfs.h>8#include <linux/module.h>9#include <asm/io.h>10#include <asm/machdep.h>11#include <asm/plpar_wrappers.h>12#include <asm/kvm_guest.h>1314static void *htm_buf;15static void *htm_status_buf;16static void *htm_info_buf;17static void *htm_caps_buf;18static u32 nodeindex;19static u32 nodalchipindex;20static u32 coreindexonchip;21static u32 htmtype;22static u32 htmconfigure;23static u32 htmstart;24static u32 htmsetup;25static u64 htmflags;2627static struct dentry *htmdump_debugfs_dir;28#define HTM_ENABLE 129#define HTM_DISABLE 030#define HTM_NOWRAP 131#define HTM_WRAP 03233/*34* Check the return code for H_HTM hcall.35* Return non-zero value (1) if either H_PARTIAL or H_SUCCESS36* is returned. For other return codes:37* Return zero if H_NOT_AVAILABLE.38* Return -EBUSY if hcall return busy.39* Return -EINVAL if any parameter or operation is not valid.40* Return -EPERM if HTM Virtualization Engine Technology code41* is not applied.42* Return -EIO if the HTM state is not valid.43*/44static ssize_t htm_return_check(long rc)45{46switch (rc) {47case H_SUCCESS:48/* H_PARTIAL for the case where all available data can't be49* returned due to buffer size constraint.50*/51case H_PARTIAL:52break;53/* H_NOT_AVAILABLE indicates reading from an offset outside the range,54* i.e. past end of file.55*/56case H_NOT_AVAILABLE:57return 0;58case H_BUSY:59case H_LONG_BUSY_ORDER_1_MSEC:60case H_LONG_BUSY_ORDER_10_MSEC:61case H_LONG_BUSY_ORDER_100_MSEC:62case H_LONG_BUSY_ORDER_1_SEC:63case H_LONG_BUSY_ORDER_10_SEC:64case H_LONG_BUSY_ORDER_100_SEC:65return -EBUSY;66case H_PARAMETER:67case H_P2:68case H_P3:69case H_P4:70case H_P5:71case H_P6:72return -EINVAL;73case H_STATE:74return -EIO;75case H_AUTHORITY:76return -EPERM;77}7879/*80* Return 1 for H_SUCCESS/H_PARTIAL81*/82return 1;83}8485static ssize_t htmdump_read(struct file *filp, char __user *ubuf,86size_t count, loff_t *ppos)87{88void *htm_buf = filp->private_data;89unsigned long page, read_size, available;90loff_t offset;91long rc, ret;9293page = ALIGN_DOWN(*ppos, PAGE_SIZE);94offset = (*ppos) % PAGE_SIZE;9596/*97* Invoke H_HTM call with:98* - operation as htm dump (H_HTM_OP_DUMP_DATA)99* - last three values are address, size and offset100*/101rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip,102htmtype, H_HTM_OP_DUMP_DATA, virt_to_phys(htm_buf),103PAGE_SIZE, page);104105ret = htm_return_check(rc);106if (ret <= 0) {107pr_debug("H_HTM hcall failed for op: H_HTM_OP_DUMP_DATA, returning %ld\n", ret);108return ret;109}110111available = PAGE_SIZE;112read_size = min(count, available);113*ppos += read_size;114return simple_read_from_buffer(ubuf, count, &offset, htm_buf, available);115}116117static const struct file_operations htmdump_fops = {118.llseek = NULL,119.read = htmdump_read,120.open = simple_open,121};122123static int htmconfigure_set(void *data, u64 val)124{125long rc, ret;126unsigned long param1 = -1, param2 = -1;127128/*129* value as 1 : configure HTM.130* value as 0 : deconfigure HTM. Return -EINVAL for131* other values.132*/133if (val == HTM_ENABLE) {134/*135* Invoke H_HTM call with:136* - operation as htm configure (H_HTM_OP_CONFIGURE)137* - If htmflags is set, param1 and param2 will be -1138* which is an indicator to use default htm mode reg mask139* and htm mode reg value.140* - last three values are unused, hence set to zero141*/142if (!htmflags) {143param1 = 0;144param2 = 0;145}146147rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip,148htmtype, H_HTM_OP_CONFIGURE, param1, param2, 0);149} else if (val == HTM_DISABLE) {150/*151* Invoke H_HTM call with:152* - operation as htm deconfigure (H_HTM_OP_DECONFIGURE)153* - last three values are unused, hence set to zero154*/155rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip,156htmtype, H_HTM_OP_DECONFIGURE, 0, 0, 0);157} else158return -EINVAL;159160ret = htm_return_check(rc);161if (ret <= 0) {162pr_debug("H_HTM hcall failed, returning %ld\n", ret);163return ret;164}165166/* Set htmconfigure if operation succeeds */167htmconfigure = val;168169return 0;170}171172static int htmconfigure_get(void *data, u64 *val)173{174*val = htmconfigure;175return 0;176}177178static int htmstart_set(void *data, u64 val)179{180long rc, ret;181182/*183* value as 1: start HTM184* value as 0: stop HTM185* Return -EINVAL for other values.186*/187if (val == HTM_ENABLE) {188/*189* Invoke H_HTM call with:190* - operation as htm start (H_HTM_OP_START)191* - last three values are unused, hence set to zero192*/193rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip,194htmtype, H_HTM_OP_START, 0, 0, 0);195196} else if (val == HTM_DISABLE) {197/*198* Invoke H_HTM call with:199* - operation as htm stop (H_HTM_OP_STOP)200* - last three values are unused, hence set to zero201*/202rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip,203htmtype, H_HTM_OP_STOP, 0, 0, 0);204} else205return -EINVAL;206207ret = htm_return_check(rc);208if (ret <= 0) {209pr_debug("H_HTM hcall failed, returning %ld\n", ret);210return ret;211}212213/* Set htmstart if H_HTM_OP_START/H_HTM_OP_STOP operation succeeds */214htmstart = val;215216return 0;217}218219static int htmstart_get(void *data, u64 *val)220{221*val = htmstart;222return 0;223}224225static ssize_t htmstatus_read(struct file *filp, char __user *ubuf,226size_t count, loff_t *ppos)227{228void *htm_status_buf = filp->private_data;229long rc, ret;230u64 *num_entries;231u64 to_copy;232int htmstatus_flag;233234/*235* Invoke H_HTM call with:236* - operation as htm status (H_HTM_OP_STATUS)237* - last three values as addr, size and offset238*/239rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip,240htmtype, H_HTM_OP_STATUS, virt_to_phys(htm_status_buf),241PAGE_SIZE, 0);242243ret = htm_return_check(rc);244if (ret <= 0) {245pr_debug("H_HTM hcall failed for op: H_HTM_OP_STATUS, returning %ld\n", ret);246return ret;247}248249/*250* HTM status buffer, start of buffer + 0x10 gives the251* number of HTM entries in the buffer. Each nest htm status252* entry is 0x6 bytes where each core htm status entry is253* 0x8 bytes.254* So total count to copy is:255* 32 bytes (for first 7 fields) + (number of HTM entries * entry size)256*/257num_entries = htm_status_buf + 0x10;258if (htmtype == 0x2)259htmstatus_flag = 0x8;260else261htmstatus_flag = 0x6;262to_copy = 32 + (be64_to_cpu(*num_entries) * htmstatus_flag);263return simple_read_from_buffer(ubuf, count, ppos, htm_status_buf, to_copy);264}265266static const struct file_operations htmstatus_fops = {267.llseek = NULL,268.read = htmstatus_read,269.open = simple_open,270};271272static ssize_t htminfo_read(struct file *filp, char __user *ubuf,273size_t count, loff_t *ppos)274{275void *htm_info_buf = filp->private_data;276long rc, ret;277u64 *num_entries;278u64 to_copy;279280/*281* Invoke H_HTM call with:282* - operation as htm status (H_HTM_OP_STATUS)283* - last three values as addr, size and offset284*/285rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip,286htmtype, H_HTM_OP_DUMP_SYSPROC_CONF, virt_to_phys(htm_info_buf),287PAGE_SIZE, 0);288289ret = htm_return_check(rc);290if (ret <= 0) {291pr_debug("H_HTM hcall failed for op: H_HTM_OP_DUMP_SYSPROC_CONF, returning %ld\n", ret);292return ret;293}294295/*296* HTM status buffer, start of buffer + 0x10 gives the297* number of HTM entries in the buffer. Each entry of processor298* is 16 bytes.299*300* So total count to copy is:301* 32 bytes (for first 5 fields) + (number of HTM entries * entry size)302*/303num_entries = htm_info_buf + 0x10;304to_copy = 32 + (be64_to_cpu(*num_entries) * 16);305return simple_read_from_buffer(ubuf, count, ppos, htm_info_buf, to_copy);306}307308static ssize_t htmcaps_read(struct file *filp, char __user *ubuf,309size_t count, loff_t *ppos)310{311void *htm_caps_buf = filp->private_data;312long rc, ret;313314/*315* Invoke H_HTM call with:316* - operation as htm capabilities (H_HTM_OP_CAPABILITIES)317* - last three values as addr, size (0x80 for Capabilities Output Buffer318* and zero319*/320rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip,321htmtype, H_HTM_OP_CAPABILITIES, virt_to_phys(htm_caps_buf),3220x80, 0);323324ret = htm_return_check(rc);325if (ret <= 0) {326pr_debug("H_HTM hcall failed for op: H_HTM_OP_CAPABILITIES, returning %ld\n", ret);327return ret;328}329330return simple_read_from_buffer(ubuf, count, ppos, htm_caps_buf, 0x80);331}332333static const struct file_operations htminfo_fops = {334.llseek = NULL,335.read = htminfo_read,336.open = simple_open,337};338339static const struct file_operations htmcaps_fops = {340.llseek = NULL,341.read = htmcaps_read,342.open = simple_open,343};344345static int htmsetup_set(void *data, u64 val)346{347long rc, ret;348349/*350* Input value: HTM buffer size in the power of 2351* example: hex value 0x21 ( decimal: 33 ) is for352* 8GB353* Invoke H_HTM call with:354* - operation as htm start (H_HTM_OP_SETUP)355* - parameter 1 set to input value.356* - last two values are unused, hence set to zero357*/358rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip,359htmtype, H_HTM_OP_SETUP, val, 0, 0);360361ret = htm_return_check(rc);362if (ret <= 0) {363pr_debug("H_HTM hcall failed for op: H_HTM_OP_SETUP, returning %ld\n", ret);364return ret;365}366367/* Set htmsetup if H_HTM_OP_SETUP operation succeeds */368htmsetup = val;369370return 0;371}372373static int htmsetup_get(void *data, u64 *val)374{375*val = htmsetup;376return 0;377}378379static int htmflags_set(void *data, u64 val)380{381/*382* Input value:383* Currently supported flag value is to enable/disable384* HTM buffer wrap. wrap is used along with "configure"385* to prevent HTM buffer from wrapping.386* Writing 1 will set noWrap while configuring HTM387*/388if (val == HTM_NOWRAP)389htmflags = H_HTM_FLAGS_NOWRAP;390else if (val == HTM_WRAP)391htmflags = 0;392else393return -EINVAL;394395return 0;396}397398static int htmflags_get(void *data, u64 *val)399{400*val = htmflags;401return 0;402}403404DEFINE_SIMPLE_ATTRIBUTE(htmconfigure_fops, htmconfigure_get, htmconfigure_set, "%llu\n");405DEFINE_SIMPLE_ATTRIBUTE(htmstart_fops, htmstart_get, htmstart_set, "%llu\n");406DEFINE_SIMPLE_ATTRIBUTE(htmsetup_fops, htmsetup_get, htmsetup_set, "%llu\n");407DEFINE_SIMPLE_ATTRIBUTE(htmflags_fops, htmflags_get, htmflags_set, "%llu\n");408409static int htmdump_init_debugfs(void)410{411htm_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);412if (!htm_buf) {413pr_err("Failed to allocate htmdump buf\n");414return -ENOMEM;415}416417htmdump_debugfs_dir = debugfs_create_dir("htmdump",418arch_debugfs_dir);419420debugfs_create_u32("nodeindex", 0600,421htmdump_debugfs_dir, &nodeindex);422debugfs_create_u32("nodalchipindex", 0600,423htmdump_debugfs_dir, &nodalchipindex);424debugfs_create_u32("coreindexonchip", 0600,425htmdump_debugfs_dir, &coreindexonchip);426debugfs_create_u32("htmtype", 0600,427htmdump_debugfs_dir, &htmtype);428debugfs_create_file("trace", 0400, htmdump_debugfs_dir, htm_buf, &htmdump_fops);429430/*431* Debugfs interface files to control HTM operations:432*/433debugfs_create_file("htmconfigure", 0600, htmdump_debugfs_dir, NULL, &htmconfigure_fops);434debugfs_create_file("htmstart", 0600, htmdump_debugfs_dir, NULL, &htmstart_fops);435debugfs_create_file("htmsetup", 0600, htmdump_debugfs_dir, NULL, &htmsetup_fops);436debugfs_create_file("htmflags", 0600, htmdump_debugfs_dir, NULL, &htmflags_fops);437438/* Debugfs interface file to present status of HTM */439htm_status_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);440if (!htm_status_buf) {441pr_err("Failed to allocate htmstatus buf\n");442return -ENOMEM;443}444445/* Debugfs interface file to present System Processor Configuration */446htm_info_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);447if (!htm_info_buf) {448pr_err("Failed to allocate htm info buf\n");449return -ENOMEM;450}451452/* Debugfs interface file to present HTM capabilities */453htm_caps_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);454if (!htm_caps_buf) {455pr_err("Failed to allocate htm caps buf\n");456return -ENOMEM;457}458459debugfs_create_file("htmstatus", 0400, htmdump_debugfs_dir, htm_status_buf, &htmstatus_fops);460debugfs_create_file("htminfo", 0400, htmdump_debugfs_dir, htm_info_buf, &htminfo_fops);461debugfs_create_file("htmcaps", 0400, htmdump_debugfs_dir, htm_caps_buf, &htmcaps_fops);462463return 0;464}465466static int __init htmdump_init(void)467{468/* Disable on kvm guest */469if (is_kvm_guest()) {470pr_info("htmdump not supported inside KVM guest\n");471return -EOPNOTSUPP;472}473474if (htmdump_init_debugfs())475return -ENOMEM;476477return 0;478}479480static void __exit htmdump_exit(void)481{482debugfs_remove_recursive(htmdump_debugfs_dir);483kfree(htm_buf);484}485486module_init(htmdump_init);487module_exit(htmdump_exit);488MODULE_DESCRIPTION("PHYP Hardware Trace Macro (HTM) data dumper");489MODULE_LICENSE("GPL");490491492