FILE
crashlog.c
File preview with syntax highlighting.
linux-6.19.14/drivers/platform/x86/intel/pmt/crashlog.c
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Intel Platform Monitoring Technology Crashlog driver 4 * 5 * Copyright (c) 2020, Intel Corporation. 6 * All Rights Reserved. 7 * 8 * Author: "Alexander Duyck" <alexander.h.duyck@linux.intel.com> 9 */ 10 11#include <linux/auxiliary_bus.h> 12#include <linux/cleanup.h> 13#include <linux/intel_vsec.h> 14#include <linux/kernel.h> 15#include <linux/module.h> 16#include <linux/mutex.h> 17#include <linux/pci.h> 18#include <linux/slab.h> 19#include <linux/uaccess.h> 20#include <linux/overflow.h> 21 22#include "class.h" 23 24/* Crashlog discovery header types */ 25#define CRASH_TYPE_OOBMSM 1 26 27/* Crashlog Discovery Header */ 28#define CONTROL_OFFSET 0x0 29#define GUID_OFFSET 0x4 30#define BASE_OFFSET 0x8 31#define SIZE_OFFSET 0xC 32#define GET_ACCESS(v) ((v) & GENMASK(3, 0)) 33#define GET_TYPE(v) (((v) & GENMASK(7, 4)) >> 4) 34#define GET_VERSION(v) (((v) & GENMASK(19, 16)) >> 16) 35/* size is in bytes */ 36#define GET_SIZE(v) ((v) * sizeof(u32)) 37 38/* 39 * Type 1 Version 0 40 * status and control registers are combined. 41 * 42 * Bits 29 and 30 control the state of bit 31. 43 * Bit 29 will clear bit 31, if set, allowing a new crashlog to be captured. 44 * Bit 30 will immediately trigger a crashlog to be generated, setting bit 31. 45 * Bit 31 is the read-only status with a 1 indicating log is complete. 46 */ 47#define TYPE1_VER0_STATUS_OFFSET 0x00 48#define TYPE1_VER0_CONTROL_OFFSET 0x00 49 50#define TYPE1_VER0_DISABLE BIT(28) 51#define TYPE1_VER0_CLEAR BIT(29) 52#define TYPE1_VER0_EXECUTE BIT(30) 53#define TYPE1_VER0_COMPLETE BIT(31) 54#define TYPE1_VER0_TRIGGER_MASK GENMASK(31, 28) 55 56/* 57 * Type 1 Version 2 58 * status and control are different registers 59 */ 60#define TYPE1_VER2_STATUS_OFFSET 0x00 61#define TYPE1_VER2_CONTROL_OFFSET 0x14 62 63/* status register */ 64#define TYPE1_VER2_CLEAR_SUPPORT BIT(20) 65#define TYPE1_VER2_REARMED BIT(25) 66#define TYPE1_VER2_ERROR BIT(26) 67#define TYPE1_VER2_CONSUMED BIT(27) 68#define TYPE1_VER2_DISABLED BIT(28) 69#define TYPE1_VER2_CLEARED BIT(29) 70#define TYPE1_VER2_IN_PROGRESS BIT(30) 71#define TYPE1_VER2_COMPLETE BIT(31) 72 73/* control register */ 74#define TYPE1_VER2_CONSUME BIT(25) 75#define TYPE1_VER2_REARM BIT(28) 76#define TYPE1_VER2_EXECUTE BIT(29) 77#define TYPE1_VER2_CLEAR BIT(30) 78#define TYPE1_VER2_DISABLE BIT(31) 79#define TYPE1_VER2_TRIGGER_MASK \ 80 (TYPE1_VER2_EXECUTE | TYPE1_VER2_CLEAR | TYPE1_VER2_DISABLE) 81 82/* After offset, order alphabetically, not bit ordered */ 83struct crashlog_status { 84 u32 offset; 85 u32 clear_supported; 86 u32 cleared; 87 u32 complete; 88 u32 consumed; 89 u32 disabled; 90 u32 error; 91 u32 in_progress; 92 u32 rearmed; 93}; 94 95struct crashlog_control { 96 u32 offset; 97 u32 trigger_mask; 98 u32 clear; 99 u32 consume; 100 u32 disable; 101 u32 manual; 102 u32 rearm; 103}; 104 105struct crashlog_info { 106 const struct crashlog_status status; 107 const struct crashlog_control control; 108 const struct attribute_group *attr_grp; 109}; 110 111struct crashlog_entry { 112 /* entry must be first member of struct */ 113 struct intel_pmt_entry entry; 114 struct mutex control_mutex; 115 const struct crashlog_info *info; 116}; 117 118struct pmt_crashlog_priv { 119 int num_entries; 120 struct crashlog_entry entry[]; 121}; 122 123/* 124 * I/O 125 */ 126 127/* Read, modify, write the control register, setting or clearing @bit based on @set */ 128static void pmt_crashlog_rmw(struct crashlog_entry *crashlog, u32 bit, bool set) 129{ 130 const struct crashlog_control *control = &crashlog->info->control; 131 struct intel_pmt_entry *entry = &crashlog->entry; 132 u32 reg = readl(entry->disc_table + control->offset); 133 134 reg &= ~control->trigger_mask; 135 136 if (set) 137 reg |= bit; 138 else 139 reg &= ~bit; 140 141 writel(reg, entry->disc_table + control->offset); 142} 143 144/* Read the status register and see if the specified @bit is set */ 145static bool pmt_crashlog_rc(struct crashlog_entry *crashlog, u32 bit) 146{ 147 const struct crashlog_status *status = &crashlog->info->status; 148 u32 reg = readl(crashlog->entry.disc_table + status->offset); 149 150 return !!(reg & bit); 151} 152 153static bool pmt_crashlog_complete(struct crashlog_entry *crashlog) 154{ 155 /* return current value of the crashlog complete flag */ 156 return pmt_crashlog_rc(crashlog, crashlog->info->status.complete); 157} 158 159static bool pmt_crashlog_disabled(struct crashlog_entry *crashlog) 160{ 161 /* return current value of the crashlog disabled flag */ 162 return pmt_crashlog_rc(crashlog, crashlog->info->status.disabled); 163} 164 165static bool pmt_crashlog_supported(struct intel_pmt_entry *entry, u32 *crash_type, u32 *version) 166{ 167 u32 discovery_header = readl(entry->disc_table + CONTROL_OFFSET); 168 169 *crash_type = GET_TYPE(discovery_header); 170 *version = GET_VERSION(discovery_header); 171 172 /* 173 * Currently we only recognize OOBMSM (type 1) and version 0 or 2 174 * devices. 175 * 176 * Ignore all other crashlog devices in the system. 177 */ 178 if (*crash_type == CRASH_TYPE_OOBMSM && (*version == 0 || *version == 2)) 179 return true; 180 181 return false; 182} 183 184static void pmt_crashlog_set_disable(struct crashlog_entry *crashlog, 185 bool disable) 186{ 187 pmt_crashlog_rmw(crashlog, crashlog->info->control.disable, disable); 188} 189 190static void pmt_crashlog_set_clear(struct crashlog_entry *crashlog) 191{ 192 pmt_crashlog_rmw(crashlog, crashlog->info->control.clear, true); 193} 194 195static void pmt_crashlog_set_execute(struct crashlog_entry *crashlog) 196{ 197 pmt_crashlog_rmw(crashlog, crashlog->info->control.manual, true); 198} 199 200static bool pmt_crashlog_cleared(struct crashlog_entry *crashlog) 201{ 202 return pmt_crashlog_rc(crashlog, crashlog->info->status.cleared); 203} 204 205static bool pmt_crashlog_consumed(struct crashlog_entry *crashlog) 206{ 207 return pmt_crashlog_rc(crashlog, crashlog->info->status.consumed); 208} 209 210static void pmt_crashlog_set_consumed(struct crashlog_entry *crashlog) 211{ 212 pmt_crashlog_rmw(crashlog, crashlog->info->control.consume, true); 213} 214 215static bool pmt_crashlog_error(struct crashlog_entry *crashlog) 216{ 217 return pmt_crashlog_rc(crashlog, crashlog->info->status.error); 218} 219 220static bool pmt_crashlog_rearm(struct crashlog_entry *crashlog) 221{ 222 return pmt_crashlog_rc(crashlog, crashlog->info->status.rearmed); 223} 224 225static void pmt_crashlog_set_rearm(struct crashlog_entry *crashlog) 226{ 227 pmt_crashlog_rmw(crashlog, crashlog->info->control.rearm, true); 228} 229 230/* 231 * sysfs 232 */ 233static ssize_t 234clear_show(struct device *dev, struct device_attribute *attr, char *buf) 235{ 236 struct crashlog_entry *crashlog = dev_get_drvdata(dev); 237 bool cleared = pmt_crashlog_cleared(crashlog); 238 239 return sysfs_emit(buf, "%d\n", cleared); 240} 241 242static ssize_t 243clear_store(struct device *dev, struct device_attribute *attr, 244 const char *buf, size_t count) 245{ 246 struct crashlog_entry *crashlog; 247 bool clear; 248 int result; 249 250 crashlog = dev_get_drvdata(dev); 251 252 result = kstrtobool(buf, &clear); 253 if (result) 254 return result; 255 256 /* set bit only */ 257 if (!clear) 258 return -EINVAL; 259 260 guard(mutex)(&crashlog->control_mutex); 261 262 pmt_crashlog_set_clear(crashlog); 263 264 return count; 265} 266static DEVICE_ATTR_RW(clear); 267 268static ssize_t 269consumed_show(struct device *dev, struct device_attribute *attr, char *buf) 270{ 271 struct crashlog_entry *crashlog = dev_get_drvdata(dev); 272 bool consumed = pmt_crashlog_consumed(crashlog); 273 274 return sysfs_emit(buf, "%d\n", consumed); 275} 276 277static ssize_t 278consumed_store(struct device *dev, struct device_attribute *attr, const char *buf, 279 size_t count) 280{ 281 struct crashlog_entry *crashlog; 282 bool consumed; 283 int result; 284 285 crashlog = dev_get_drvdata(dev); 286 287 result = kstrtobool(buf, &consumed); 288 if (result) 289 return result; 290 291 /* set bit only */ 292 if (!consumed) 293 return -EINVAL; 294 295 guard(mutex)(&crashlog->control_mutex); 296 297 if (pmt_crashlog_disabled(crashlog)) 298 return -EBUSY; 299 300 if (!pmt_crashlog_complete(crashlog)) 301 return -EEXIST; 302 303 pmt_crashlog_set_consumed(crashlog); 304 305 return count; 306} 307static DEVICE_ATTR_RW(consumed); 308 309static ssize_t 310enable_show(struct device *dev, struct device_attribute *attr, char *buf) 311{ 312 struct crashlog_entry *crashlog = dev_get_drvdata(dev); 313 bool enabled = !pmt_crashlog_disabled(crashlog); 314 315 return sprintf(buf, "%d\n", enabled); 316} 317 318static ssize_t 319enable_store(struct device *dev, struct device_attribute *attr, 320 const char *buf, size_t count) 321{ 322 struct crashlog_entry *crashlog; 323 bool enabled; 324 int result; 325 326 crashlog = dev_get_drvdata(dev); 327 328 result = kstrtobool(buf, &enabled); 329 if (result) 330 return result; 331 332 guard(mutex)(&crashlog->control_mutex); 333 334 pmt_crashlog_set_disable(crashlog, !enabled); 335 336 return count; 337} 338static DEVICE_ATTR_RW(enable); 339 340static ssize_t 341error_show(struct device *dev, struct device_attribute *attr, char *buf) 342{ 343 struct crashlog_entry *crashlog = dev_get_drvdata(dev); 344 bool error = pmt_crashlog_error(crashlog); 345 346 return sysfs_emit(buf, "%d\n", error); 347} 348static DEVICE_ATTR_RO(error); 349 350static ssize_t 351rearm_show(struct device *dev, struct device_attribute *attr, char *buf) 352{ 353 struct crashlog_entry *crashlog = dev_get_drvdata(dev); 354 int rearmed = pmt_crashlog_rearm(crashlog); 355 356 return sysfs_emit(buf, "%d\n", rearmed); 357} 358 359static ssize_t 360rearm_store(struct device *dev, struct device_attribute *attr, const char *buf, 361 size_t count) 362{ 363 struct crashlog_entry *crashlog; 364 bool rearm; 365 int result; 366 367 crashlog = dev_get_drvdata(dev); 368 369 result = kstrtobool(buf, &rearm); 370 if (result) 371 return result; 372 373 /* set only */ 374 if (!rearm) 375 return -EINVAL; 376 377 guard(mutex)(&crashlog->control_mutex); 378 379 pmt_crashlog_set_rearm(crashlog); 380 381 return count; 382} 383static DEVICE_ATTR_RW(rearm); 384 385static ssize_t 386trigger_show(struct device *dev, struct device_attribute *attr, char *buf) 387{ 388 struct crashlog_entry *crashlog; 389 bool trigger; 390 391 crashlog = dev_get_drvdata(dev); 392 trigger = pmt_crashlog_complete(crashlog); 393 394 return sprintf(buf, "%d\n", trigger); 395} 396 397static ssize_t 398trigger_store(struct device *dev, struct device_attribute *attr, 399 const char *buf, size_t count) 400{ 401 struct crashlog_entry *crashlog; 402 bool trigger; 403 int result; 404 405 crashlog = dev_get_drvdata(dev); 406 407 result = kstrtobool(buf, &trigger); 408 if (result) 409 return result; 410 411 guard(mutex)(&crashlog->control_mutex); 412 413 /* if device is currently disabled, return busy */ 414 if (pmt_crashlog_disabled(crashlog)) 415 return -EBUSY; 416 417 if (!trigger) { 418 pmt_crashlog_set_clear(crashlog); 419 return count; 420 } 421 422 /* we cannot trigger a new crash if one is still pending */ 423 if (pmt_crashlog_complete(crashlog)) 424 return -EEXIST; 425 426 pmt_crashlog_set_execute(crashlog); 427 428 return count; 429} 430static DEVICE_ATTR_RW(trigger); 431 432static struct attribute *pmt_crashlog_type1_ver0_attrs[] = { 433 &dev_attr_enable.attr, 434 &dev_attr_trigger.attr, 435 NULL 436}; 437 438static struct attribute *pmt_crashlog_type1_ver2_attrs[] = { 439 &dev_attr_clear.attr, 440 &dev_attr_consumed.attr, 441 &dev_attr_enable.attr, 442 &dev_attr_error.attr, 443 &dev_attr_rearm.attr, 444 &dev_attr_trigger.attr, 445 NULL 446}; 447 448static const struct attribute_group pmt_crashlog_type1_ver0_group = { 449 .attrs = pmt_crashlog_type1_ver0_attrs, 450}; 451 452static const struct attribute_group pmt_crashlog_type1_ver2_group = { 453 .attrs = pmt_crashlog_type1_ver2_attrs, 454}; 455 456static const struct crashlog_info crashlog_type1_ver0 = { 457 .status.offset = TYPE1_VER0_STATUS_OFFSET, 458 .status.cleared = TYPE1_VER0_CLEAR, 459 .status.complete = TYPE1_VER0_COMPLETE, 460 .status.disabled = TYPE1_VER0_DISABLE, 461 462 .control.offset = TYPE1_VER0_CONTROL_OFFSET, 463 .control.trigger_mask = TYPE1_VER0_TRIGGER_MASK, 464 .control.clear = TYPE1_VER0_CLEAR, 465 .control.disable = TYPE1_VER0_DISABLE, 466 .control.manual = TYPE1_VER0_EXECUTE, 467 .attr_grp = &pmt_crashlog_type1_ver0_group, 468}; 469 470static const struct crashlog_info crashlog_type1_ver2 = { 471 .status.offset = TYPE1_VER2_STATUS_OFFSET, 472 .status.clear_supported = TYPE1_VER2_CLEAR_SUPPORT, 473 .status.cleared = TYPE1_VER2_CLEARED, 474 .status.complete = TYPE1_VER2_COMPLETE, 475 .status.consumed = TYPE1_VER2_CONSUMED, 476 .status.disabled = TYPE1_VER2_DISABLED, 477 .status.error = TYPE1_VER2_ERROR, 478 .status.in_progress = TYPE1_VER2_IN_PROGRESS, 479 .status.rearmed = TYPE1_VER2_REARMED, 480 481 .control.offset = TYPE1_VER2_CONTROL_OFFSET, 482 .control.trigger_mask = TYPE1_VER2_TRIGGER_MASK, 483 .control.clear = TYPE1_VER2_CLEAR, 484 .control.consume = TYPE1_VER2_CONSUME, 485 .control.disable = TYPE1_VER2_DISABLE, 486 .control.manual = TYPE1_VER2_EXECUTE, 487 .control.rearm = TYPE1_VER2_REARM, 488 .attr_grp = &pmt_crashlog_type1_ver2_group, 489}; 490 491static const struct crashlog_info *select_crashlog_info(u32 type, u32 version) 492{ 493 if (version == 0) 494 return &crashlog_type1_ver0; 495 496 return &crashlog_type1_ver2; 497} 498 499static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry, 500 struct device *dev) 501{ 502 void __iomem *disc_table = entry->disc_table; 503 struct intel_pmt_header *header = &entry->header; 504 struct crashlog_entry *crashlog; 505 u32 version; 506 u32 type; 507 508 if (!pmt_crashlog_supported(entry, &type, &version)) 509 return 1; 510 511 /* initialize the crashlog struct */ 512 crashlog = container_of(entry, struct crashlog_entry, entry); 513 mutex_init(&crashlog->control_mutex); 514 515 crashlog->info = select_crashlog_info(type, version); 516 517 header->access_type = GET_ACCESS(readl(disc_table)); 518 header->guid = readl(disc_table + GUID_OFFSET); 519 header->base_offset = readl(disc_table + BASE_OFFSET); 520 521 /* Size is measured in DWORDS, but accessor returns bytes */ 522 header->size = GET_SIZE(readl(disc_table + SIZE_OFFSET)); 523 524 entry->attr_grp = crashlog->info->attr_grp; 525 526 return 0; 527} 528 529static DEFINE_XARRAY_ALLOC(crashlog_array); 530static struct intel_pmt_namespace pmt_crashlog_ns = { 531 .name = "crashlog", 532 .xa = &crashlog_array, 533 .pmt_header_decode = pmt_crashlog_header_decode, 534}; 535 536/* 537 * initialization 538 */ 539static void pmt_crashlog_remove(struct auxiliary_device *auxdev) 540{ 541 struct pmt_crashlog_priv *priv = auxiliary_get_drvdata(auxdev); 542 int i; 543 544 for (i = 0; i < priv->num_entries; i++) { 545 struct crashlog_entry *crashlog = &priv->entry[i]; 546 547 intel_pmt_dev_destroy(&crashlog->entry, &pmt_crashlog_ns); 548 mutex_destroy(&crashlog->control_mutex); 549 } 550} 551 552static int pmt_crashlog_probe(struct auxiliary_device *auxdev, 553 const struct auxiliary_device_id *id) 554{ 555 struct intel_vsec_device *intel_vsec_dev = auxdev_to_ivdev(auxdev); 556 struct pmt_crashlog_priv *priv; 557 size_t size; 558 int i, ret; 559 560 size = struct_size(priv, entry, intel_vsec_dev->num_resources); 561 priv = devm_kzalloc(&auxdev->dev, size, GFP_KERNEL); 562 if (!priv) 563 return -ENOMEM; 564 565 auxiliary_set_drvdata(auxdev, priv); 566 567 for (i = 0; i < intel_vsec_dev->num_resources; i++) { 568 struct intel_pmt_entry *entry = &priv->entry[priv->num_entries].entry; 569 570 ret = intel_pmt_dev_create(entry, &pmt_crashlog_ns, intel_vsec_dev, i); 571 if (ret < 0) 572 goto abort_probe; 573 if (ret) 574 continue; 575 576 priv->num_entries++; 577 } 578 579 return 0; 580abort_probe: 581 pmt_crashlog_remove(auxdev); 582 return ret; 583} 584 585static const struct auxiliary_device_id pmt_crashlog_id_table[] = { 586 { .name = "intel_vsec.crashlog" }, 587 {} 588}; 589MODULE_DEVICE_TABLE(auxiliary, pmt_crashlog_id_table); 590 591static struct auxiliary_driver pmt_crashlog_aux_driver = { 592 .id_table = pmt_crashlog_id_table, 593 .remove = pmt_crashlog_remove, 594 .probe = pmt_crashlog_probe, 595}; 596 597static int __init pmt_crashlog_init(void) 598{ 599 return auxiliary_driver_register(&pmt_crashlog_aux_driver); 600} 601 602static void __exit pmt_crashlog_exit(void) 603{ 604 auxiliary_driver_unregister(&pmt_crashlog_aux_driver); 605 xa_destroy(&crashlog_array); 606} 607 608module_init(pmt_crashlog_init); 609module_exit(pmt_crashlog_exit); 610 611MODULE_AUTHOR("Alexander Duyck <alexander.h.duyck@linux.intel.com>"); 612MODULE_DESCRIPTION("Intel PMT Crashlog driver"); 613MODULE_LICENSE("GPL v2"); 614MODULE_IMPORT_NS("INTEL_PMT");
~/
Read-only file preview.