1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) IBM Corporation, 2024 4 */ 5 6 #define pr_fmt(fmt) "htmdump: " fmt 7 8 #include <linux/debugfs.h> 9 #include <linux/module.h> 10 #include <asm/io.h> 11 #include <asm/machdep.h> 12 #include <asm/plpar_wrappers.h> 13 #include <asm/kvm_guest.h> 14 15 static void *htm_buf; 16 static void *htm_status_buf; 17 static void *htm_info_buf; 18 static void *htm_caps_buf; 19 static u32 nodeindex; 20 static u32 nodalchipindex; 21 static u32 coreindexonchip; 22 static u32 htmtype; 23 static u32 htmconfigure; 24 static u32 htmstart; 25 static u32 htmsetup; 26 static u64 htmflags; 27 28 static struct dentry *htmdump_debugfs_dir; 29 #define HTM_ENABLE 1 30 #define HTM_DISABLE 0 31 #define HTM_NOWRAP 1 32 #define HTM_WRAP 0 33 34 /* 35 * Check the return code for H_HTM hcall. 36 * Return non-zero value (1) if either H_PARTIAL or H_SUCCESS 37 * is returned. For other return codes: 38 * Return zero if H_NOT_AVAILABLE. 39 * Return -EBUSY if hcall return busy. 40 * Return -EINVAL if any parameter or operation is not valid. 41 * Return -EPERM if HTM Virtualization Engine Technology code 42 * is not applied. 43 * Return -EIO if the HTM state is not valid. 44 */ 45 static ssize_t htm_return_check(long rc) 46 { 47 switch (rc) { 48 case H_SUCCESS: 49 /* H_PARTIAL for the case where all available data can't be 50 * returned due to buffer size constraint. 51 */ 52 case H_PARTIAL: 53 break; 54 /* H_NOT_AVAILABLE indicates reading from an offset outside the range, 55 * i.e. past end of file. 56 */ 57 case H_NOT_AVAILABLE: 58 return 0; 59 case H_BUSY: 60 case H_LONG_BUSY_ORDER_1_MSEC: 61 case H_LONG_BUSY_ORDER_10_MSEC: 62 case H_LONG_BUSY_ORDER_100_MSEC: 63 case H_LONG_BUSY_ORDER_1_SEC: 64 case H_LONG_BUSY_ORDER_10_SEC: 65 case H_LONG_BUSY_ORDER_100_SEC: 66 return -EBUSY; 67 case H_PARAMETER: 68 case H_P2: 69 case H_P3: 70 case H_P4: 71 case H_P5: 72 case H_P6: 73 return -EINVAL; 74 case H_STATE: 75 return -EIO; 76 case H_AUTHORITY: 77 return -EPERM; 78 } 79 80 /* 81 * Return 1 for H_SUCCESS/H_PARTIAL 82 */ 83 return 1; 84 } 85 86 static ssize_t htmdump_read(struct file *filp, char __user *ubuf, 87 size_t count, loff_t *ppos) 88 { 89 void *htm_buf = filp->private_data; 90 unsigned long page, read_size, available; 91 loff_t offset; 92 long rc, ret; 93 94 page = ALIGN_DOWN(*ppos, PAGE_SIZE); 95 offset = (*ppos) % PAGE_SIZE; 96 97 /* 98 * Invoke H_HTM call with: 99 * - operation as htm dump (H_HTM_OP_DUMP_DATA) 100 * - last three values are address, size and offset 101 */ 102 rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip, 103 htmtype, H_HTM_OP_DUMP_DATA, virt_to_phys(htm_buf), 104 PAGE_SIZE, page); 105 106 ret = htm_return_check(rc); 107 if (ret <= 0) { 108 pr_debug("H_HTM hcall failed for op: H_HTM_OP_DUMP_DATA, returning %ld\n", ret); 109 return ret; 110 } 111 112 available = PAGE_SIZE; 113 read_size = min(count, available); 114 *ppos += read_size; 115 return simple_read_from_buffer(ubuf, count, &offset, htm_buf, available); 116 } 117 118 static const struct file_operations htmdump_fops = { 119 .llseek = NULL, 120 .read = htmdump_read, 121 .open = simple_open, 122 }; 123 124 static int htmconfigure_set(void *data, u64 val) 125 { 126 long rc, ret; 127 unsigned long param1 = -1, param2 = -1; 128 129 /* 130 * value as 1 : configure HTM. 131 * value as 0 : deconfigure HTM. Return -EINVAL for 132 * other values. 133 */ 134 if (val == HTM_ENABLE) { 135 /* 136 * Invoke H_HTM call with: 137 * - operation as htm configure (H_HTM_OP_CONFIGURE) 138 * - If htmflags is set, param1 and param2 will be -1 139 * which is an indicator to use default htm mode reg mask 140 * and htm mode reg value. 141 * - last three values are unused, hence set to zero 142 */ 143 if (!htmflags) { 144 param1 = 0; 145 param2 = 0; 146 } 147 148 rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip, 149 htmtype, H_HTM_OP_CONFIGURE, param1, param2, 0); 150 } else if (val == HTM_DISABLE) { 151 /* 152 * Invoke H_HTM call with: 153 * - operation as htm deconfigure (H_HTM_OP_DECONFIGURE) 154 * - last three values are unused, hence set to zero 155 */ 156 rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip, 157 htmtype, H_HTM_OP_DECONFIGURE, 0, 0, 0); 158 } else 159 return -EINVAL; 160 161 ret = htm_return_check(rc); 162 if (ret <= 0) { 163 pr_debug("H_HTM hcall failed, returning %ld\n", ret); 164 return ret; 165 } 166 167 /* Set htmconfigure if operation succeeds */ 168 htmconfigure = val; 169 170 return 0; 171 } 172 173 static int htmconfigure_get(void *data, u64 *val) 174 { 175 *val = htmconfigure; 176 return 0; 177 } 178 179 static int htmstart_set(void *data, u64 val) 180 { 181 long rc, ret; 182 183 /* 184 * value as 1: start HTM 185 * value as 0: stop HTM 186 * Return -EINVAL for other values. 187 */ 188 if (val == HTM_ENABLE) { 189 /* 190 * Invoke H_HTM call with: 191 * - operation as htm start (H_HTM_OP_START) 192 * - last three values are unused, hence set to zero 193 */ 194 rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip, 195 htmtype, H_HTM_OP_START, 0, 0, 0); 196 197 } else if (val == HTM_DISABLE) { 198 /* 199 * Invoke H_HTM call with: 200 * - operation as htm stop (H_HTM_OP_STOP) 201 * - last three values are unused, hence set to zero 202 */ 203 rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip, 204 htmtype, H_HTM_OP_STOP, 0, 0, 0); 205 } else 206 return -EINVAL; 207 208 ret = htm_return_check(rc); 209 if (ret <= 0) { 210 pr_debug("H_HTM hcall failed, returning %ld\n", ret); 211 return ret; 212 } 213 214 /* Set htmstart if H_HTM_OP_START/H_HTM_OP_STOP operation succeeds */ 215 htmstart = val; 216 217 return 0; 218 } 219 220 static int htmstart_get(void *data, u64 *val) 221 { 222 *val = htmstart; 223 return 0; 224 } 225 226 static ssize_t htmstatus_read(struct file *filp, char __user *ubuf, 227 size_t count, loff_t *ppos) 228 { 229 void *htm_status_buf = filp->private_data; 230 long rc, ret; 231 u64 *num_entries; 232 u64 to_copy; 233 int htmstatus_flag; 234 235 /* 236 * Invoke H_HTM call with: 237 * - operation as htm status (H_HTM_OP_STATUS) 238 * - last three values as addr, size and offset 239 */ 240 rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip, 241 htmtype, H_HTM_OP_STATUS, virt_to_phys(htm_status_buf), 242 PAGE_SIZE, 0); 243 244 ret = htm_return_check(rc); 245 if (ret <= 0) { 246 pr_debug("H_HTM hcall failed for op: H_HTM_OP_STATUS, returning %ld\n", ret); 247 return ret; 248 } 249 250 /* 251 * HTM status buffer, start of buffer + 0x10 gives the 252 * number of HTM entries in the buffer. Each nest htm status 253 * entry is 0x6 bytes where each core htm status entry is 254 * 0x8 bytes. 255 * So total count to copy is: 256 * 32 bytes (for first 7 fields) + (number of HTM entries * entry size) 257 */ 258 num_entries = htm_status_buf + 0x10; 259 if (htmtype == 0x2) 260 htmstatus_flag = 0x8; 261 else 262 htmstatus_flag = 0x6; 263 to_copy = 32 + (be64_to_cpu(*num_entries) * htmstatus_flag); 264 return simple_read_from_buffer(ubuf, count, ppos, htm_status_buf, to_copy); 265 } 266 267 static const struct file_operations htmstatus_fops = { 268 .llseek = NULL, 269 .read = htmstatus_read, 270 .open = simple_open, 271 }; 272 273 static ssize_t htminfo_read(struct file *filp, char __user *ubuf, 274 size_t count, loff_t *ppos) 275 { 276 void *htm_info_buf = filp->private_data; 277 long rc, ret; 278 u64 *num_entries; 279 u64 to_copy; 280 281 /* 282 * Invoke H_HTM call with: 283 * - operation as htm status (H_HTM_OP_STATUS) 284 * - last three values as addr, size and offset 285 */ 286 rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip, 287 htmtype, H_HTM_OP_DUMP_SYSPROC_CONF, virt_to_phys(htm_info_buf), 288 PAGE_SIZE, 0); 289 290 ret = htm_return_check(rc); 291 if (ret <= 0) { 292 pr_debug("H_HTM hcall failed for op: H_HTM_OP_DUMP_SYSPROC_CONF, returning %ld\n", ret); 293 return ret; 294 } 295 296 /* 297 * HTM status buffer, start of buffer + 0x10 gives the 298 * number of HTM entries in the buffer. Each entry of processor 299 * is 16 bytes. 300 * 301 * So total count to copy is: 302 * 32 bytes (for first 5 fields) + (number of HTM entries * entry size) 303 */ 304 num_entries = htm_info_buf + 0x10; 305 to_copy = 32 + (be64_to_cpu(*num_entries) * 16); 306 return simple_read_from_buffer(ubuf, count, ppos, htm_info_buf, to_copy); 307 } 308 309 static ssize_t htmcaps_read(struct file *filp, char __user *ubuf, 310 size_t count, loff_t *ppos) 311 { 312 void *htm_caps_buf = filp->private_data; 313 long rc, ret; 314 315 /* 316 * Invoke H_HTM call with: 317 * - operation as htm capabilities (H_HTM_OP_CAPABILITIES) 318 * - last three values as addr, size (0x80 for Capabilities Output Buffer 319 * and zero 320 */ 321 rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip, 322 htmtype, H_HTM_OP_CAPABILITIES, virt_to_phys(htm_caps_buf), 323 0x80, 0); 324 325 ret = htm_return_check(rc); 326 if (ret <= 0) { 327 pr_debug("H_HTM hcall failed for op: H_HTM_OP_CAPABILITIES, returning %ld\n", ret); 328 return ret; 329 } 330 331 return simple_read_from_buffer(ubuf, count, ppos, htm_caps_buf, 0x80); 332 } 333 334 static const struct file_operations htminfo_fops = { 335 .llseek = NULL, 336 .read = htminfo_read, 337 .open = simple_open, 338 }; 339 340 static const struct file_operations htmcaps_fops = { 341 .llseek = NULL, 342 .read = htmcaps_read, 343 .open = simple_open, 344 }; 345 346 static int htmsetup_set(void *data, u64 val) 347 { 348 long rc, ret; 349 350 /* 351 * Input value: HTM buffer size in the power of 2 352 * example: hex value 0x21 ( decimal: 33 ) is for 353 * 8GB 354 * Invoke H_HTM call with: 355 * - operation as htm start (H_HTM_OP_SETUP) 356 * - parameter 1 set to input value. 357 * - last two values are unused, hence set to zero 358 */ 359 rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip, 360 htmtype, H_HTM_OP_SETUP, val, 0, 0); 361 362 ret = htm_return_check(rc); 363 if (ret <= 0) { 364 pr_debug("H_HTM hcall failed for op: H_HTM_OP_SETUP, returning %ld\n", ret); 365 return ret; 366 } 367 368 /* Set htmsetup if H_HTM_OP_SETUP operation succeeds */ 369 htmsetup = val; 370 371 return 0; 372 } 373 374 static int htmsetup_get(void *data, u64 *val) 375 { 376 *val = htmsetup; 377 return 0; 378 } 379 380 static int htmflags_set(void *data, u64 val) 381 { 382 /* 383 * Input value: 384 * Currently supported flag value is to enable/disable 385 * HTM buffer wrap. wrap is used along with "configure" 386 * to prevent HTM buffer from wrapping. 387 * Writing 1 will set noWrap while configuring HTM 388 */ 389 if (val == HTM_NOWRAP) 390 htmflags = H_HTM_FLAGS_NOWRAP; 391 else if (val == HTM_WRAP) 392 htmflags = 0; 393 else 394 return -EINVAL; 395 396 return 0; 397 } 398 399 static int htmflags_get(void *data, u64 *val) 400 { 401 *val = htmflags; 402 return 0; 403 } 404 405 DEFINE_SIMPLE_ATTRIBUTE(htmconfigure_fops, htmconfigure_get, htmconfigure_set, "%llu\n"); 406 DEFINE_SIMPLE_ATTRIBUTE(htmstart_fops, htmstart_get, htmstart_set, "%llu\n"); 407 DEFINE_SIMPLE_ATTRIBUTE(htmsetup_fops, htmsetup_get, htmsetup_set, "%llu\n"); 408 DEFINE_SIMPLE_ATTRIBUTE(htmflags_fops, htmflags_get, htmflags_set, "%llu\n"); 409 410 static int htmdump_init_debugfs(void) 411 { 412 htm_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); 413 if (!htm_buf) { 414 pr_err("Failed to allocate htmdump buf\n"); 415 return -ENOMEM; 416 } 417 418 htmdump_debugfs_dir = debugfs_create_dir("htmdump", 419 arch_debugfs_dir); 420 421 debugfs_create_u32("nodeindex", 0600, 422 htmdump_debugfs_dir, &nodeindex); 423 debugfs_create_u32("nodalchipindex", 0600, 424 htmdump_debugfs_dir, &nodalchipindex); 425 debugfs_create_u32("coreindexonchip", 0600, 426 htmdump_debugfs_dir, &coreindexonchip); 427 debugfs_create_u32("htmtype", 0600, 428 htmdump_debugfs_dir, &htmtype); 429 debugfs_create_file("trace", 0400, htmdump_debugfs_dir, htm_buf, &htmdump_fops); 430 431 /* 432 * Debugfs interface files to control HTM operations: 433 */ 434 debugfs_create_file("htmconfigure", 0600, htmdump_debugfs_dir, NULL, &htmconfigure_fops); 435 debugfs_create_file("htmstart", 0600, htmdump_debugfs_dir, NULL, &htmstart_fops); 436 debugfs_create_file("htmsetup", 0600, htmdump_debugfs_dir, NULL, &htmsetup_fops); 437 debugfs_create_file("htmflags", 0600, htmdump_debugfs_dir, NULL, &htmflags_fops); 438 439 /* Debugfs interface file to present status of HTM */ 440 htm_status_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); 441 if (!htm_status_buf) { 442 pr_err("Failed to allocate htmstatus buf\n"); 443 return -ENOMEM; 444 } 445 446 /* Debugfs interface file to present System Processor Configuration */ 447 htm_info_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); 448 if (!htm_info_buf) { 449 pr_err("Failed to allocate htm info buf\n"); 450 return -ENOMEM; 451 } 452 453 /* Debugfs interface file to present HTM capabilities */ 454 htm_caps_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); 455 if (!htm_caps_buf) { 456 pr_err("Failed to allocate htm caps buf\n"); 457 return -ENOMEM; 458 } 459 460 debugfs_create_file("htmstatus", 0400, htmdump_debugfs_dir, htm_status_buf, &htmstatus_fops); 461 debugfs_create_file("htminfo", 0400, htmdump_debugfs_dir, htm_info_buf, &htminfo_fops); 462 debugfs_create_file("htmcaps", 0400, htmdump_debugfs_dir, htm_caps_buf, &htmcaps_fops); 463 464 return 0; 465 } 466 467 static int __init htmdump_init(void) 468 { 469 /* Disable on kvm guest */ 470 if (is_kvm_guest()) { 471 pr_info("htmdump not supported inside KVM guest\n"); 472 return -EOPNOTSUPP; 473 } 474 475 if (htmdump_init_debugfs()) 476 return -ENOMEM; 477 478 return 0; 479 } 480 481 static void __exit htmdump_exit(void) 482 { 483 debugfs_remove_recursive(htmdump_debugfs_dir); 484 kfree(htm_buf); 485 } 486 487 module_init(htmdump_init); 488 module_exit(htmdump_exit); 489 MODULE_DESCRIPTION("PHYP Hardware Trace Macro (HTM) data dumper"); 490 MODULE_LICENSE("GPL"); 491