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