1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * PCI HotPlug Controller Core 4 * 5 * Copyright (C) 2001-2002 Greg Kroah-Hartman (greg@kroah.com) 6 * Copyright (C) 2001-2002 IBM Corp. 7 * 8 * All rights reserved. 9 * 10 * Send feedback to <kristen.c.accardi@intel.com> 11 * 12 * Authors: 13 * Greg Kroah-Hartman <greg@kroah.com> 14 * Scott Murray <scottm@somanetworks.com> 15 */ 16 17 #include <linux/module.h> 18 #include <linux/moduleparam.h> 19 #include <linux/kernel.h> 20 #include <linux/types.h> 21 #include <linux/kobject.h> 22 #include <linux/sysfs.h> 23 #include <linux/pagemap.h> 24 #include <linux/init.h> 25 #include <linux/mount.h> 26 #include <linux/namei.h> 27 #include <linux/pci.h> 28 #include <linux/pci_hotplug.h> 29 #include <linux/uaccess.h> 30 #include "../pci.h" 31 #include "cpci_hotplug.h" 32 33 #define MY_NAME "pci_hotplug" 34 35 #define dbg(fmt, arg...) do { if (debug) printk(KERN_DEBUG "%s: %s: " fmt, MY_NAME, __func__, ## arg); } while (0) 36 #define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME, ## arg) 37 #define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME, ## arg) 38 #define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME, ## arg) 39 40 /* local variables */ 41 static bool debug; 42 43 /* Weee, fun with macros... */ 44 #define GET_STATUS(name, type) \ 45 static int get_##name(struct hotplug_slot *slot, type *value) \ 46 { \ 47 const struct hotplug_slot_ops *ops = slot->ops; \ 48 int retval = 0; \ 49 if (ops->get_##name) \ 50 retval = ops->get_##name(slot, value); \ 51 return retval; \ 52 } 53 54 GET_STATUS(power_status, u8) 55 GET_STATUS(attention_status, u8) 56 GET_STATUS(latch_status, u8) 57 GET_STATUS(adapter_status, u8) 58 59 static ssize_t power_read_file(struct pci_slot *pci_slot, char *buf) 60 { 61 int retval; 62 u8 value; 63 64 retval = get_power_status(pci_slot->hotplug, &value); 65 if (retval) 66 return retval; 67 68 return sysfs_emit(buf, "%d\n", value); 69 } 70 71 static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf, 72 size_t count) 73 { 74 struct hotplug_slot *slot = pci_slot->hotplug; 75 unsigned long lpower; 76 u8 power; 77 int retval = 0; 78 79 lpower = simple_strtoul(buf, NULL, 10); 80 power = (u8)(lpower & 0xff); 81 dbg("power = %d\n", power); 82 83 switch (power) { 84 case 0: 85 if (slot->ops->disable_slot) 86 retval = slot->ops->disable_slot(slot); 87 break; 88 89 case 1: 90 if (slot->ops->enable_slot) 91 retval = slot->ops->enable_slot(slot); 92 break; 93 94 default: 95 err("Illegal value specified for power\n"); 96 retval = -EINVAL; 97 } 98 99 if (retval) 100 return retval; 101 return count; 102 } 103 104 static struct pci_slot_attribute hotplug_slot_attr_power = { 105 .attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR}, 106 .show = power_read_file, 107 .store = power_write_file 108 }; 109 110 static ssize_t attention_read_file(struct pci_slot *pci_slot, char *buf) 111 { 112 int retval; 113 u8 value; 114 115 retval = get_attention_status(pci_slot->hotplug, &value); 116 if (retval) 117 return retval; 118 119 return sysfs_emit(buf, "%d\n", value); 120 } 121 122 static ssize_t attention_write_file(struct pci_slot *pci_slot, const char *buf, 123 size_t count) 124 { 125 struct hotplug_slot *slot = pci_slot->hotplug; 126 const struct hotplug_slot_ops *ops = slot->ops; 127 unsigned long lattention; 128 u8 attention; 129 int retval = 0; 130 131 lattention = simple_strtoul(buf, NULL, 10); 132 attention = (u8)(lattention & 0xff); 133 dbg(" - attention = %d\n", attention); 134 135 if (ops->set_attention_status) 136 retval = ops->set_attention_status(slot, attention); 137 138 if (retval) 139 return retval; 140 return count; 141 } 142 143 static struct pci_slot_attribute hotplug_slot_attr_attention = { 144 .attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR}, 145 .show = attention_read_file, 146 .store = attention_write_file 147 }; 148 149 static ssize_t latch_read_file(struct pci_slot *pci_slot, char *buf) 150 { 151 int retval; 152 u8 value; 153 154 retval = get_latch_status(pci_slot->hotplug, &value); 155 if (retval) 156 return retval; 157 158 return sysfs_emit(buf, "%d\n", value); 159 } 160 161 static struct pci_slot_attribute hotplug_slot_attr_latch = { 162 .attr = {.name = "latch", .mode = S_IFREG | S_IRUGO}, 163 .show = latch_read_file, 164 }; 165 166 static ssize_t presence_read_file(struct pci_slot *pci_slot, char *buf) 167 { 168 int retval; 169 u8 value; 170 171 retval = get_adapter_status(pci_slot->hotplug, &value); 172 if (retval) 173 return retval; 174 175 return sysfs_emit(buf, "%d\n", value); 176 } 177 178 static struct pci_slot_attribute hotplug_slot_attr_presence = { 179 .attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO}, 180 .show = presence_read_file, 181 }; 182 183 static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf, 184 size_t count) 185 { 186 struct hotplug_slot *slot = pci_slot->hotplug; 187 unsigned long ltest; 188 u32 test; 189 int retval = 0; 190 191 ltest = simple_strtoul(buf, NULL, 10); 192 test = (u32)(ltest & 0xffffffff); 193 dbg("test = %d\n", test); 194 195 if (slot->ops->hardware_test) 196 retval = slot->ops->hardware_test(slot, test); 197 198 if (retval) 199 return retval; 200 return count; 201 } 202 203 static struct pci_slot_attribute hotplug_slot_attr_test = { 204 .attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR}, 205 .store = test_write_file 206 }; 207 208 static bool has_power_file(struct hotplug_slot *slot) 209 { 210 if ((slot->ops->enable_slot) || 211 (slot->ops->disable_slot) || 212 (slot->ops->get_power_status)) 213 return true; 214 return false; 215 } 216 217 static bool has_attention_file(struct hotplug_slot *slot) 218 { 219 if ((slot->ops->set_attention_status) || 220 (slot->ops->get_attention_status)) 221 return true; 222 return false; 223 } 224 225 static bool has_latch_file(struct hotplug_slot *slot) 226 { 227 if (slot->ops->get_latch_status) 228 return true; 229 return false; 230 } 231 232 static bool has_adapter_file(struct hotplug_slot *slot) 233 { 234 if (slot->ops->get_adapter_status) 235 return true; 236 return false; 237 } 238 239 static bool has_test_file(struct hotplug_slot *slot) 240 { 241 if (slot->ops->hardware_test) 242 return true; 243 return false; 244 } 245 246 static int fs_add_slot(struct hotplug_slot *slot, struct pci_slot *pci_slot) 247 { 248 struct kobject *kobj; 249 int retval = 0; 250 251 /* Create symbolic link to the hotplug driver module */ 252 kobj = kset_find_obj(module_kset, slot->mod_name); 253 if (kobj) { 254 retval = sysfs_create_link(&pci_slot->kobj, kobj, "module"); 255 if (retval) 256 dev_err(&pci_slot->bus->dev, 257 "Error creating sysfs link (%d)\n", retval); 258 kobject_put(kobj); 259 } 260 261 if (has_power_file(slot)) { 262 retval = sysfs_create_file(&pci_slot->kobj, 263 &hotplug_slot_attr_power.attr); 264 if (retval) 265 goto exit_power; 266 } 267 268 if (has_attention_file(slot)) { 269 retval = sysfs_create_file(&pci_slot->kobj, 270 &hotplug_slot_attr_attention.attr); 271 if (retval) 272 goto exit_attention; 273 } 274 275 if (has_latch_file(slot)) { 276 retval = sysfs_create_file(&pci_slot->kobj, 277 &hotplug_slot_attr_latch.attr); 278 if (retval) 279 goto exit_latch; 280 } 281 282 if (has_adapter_file(slot)) { 283 retval = sysfs_create_file(&pci_slot->kobj, 284 &hotplug_slot_attr_presence.attr); 285 if (retval) 286 goto exit_adapter; 287 } 288 289 if (has_test_file(slot)) { 290 retval = sysfs_create_file(&pci_slot->kobj, 291 &hotplug_slot_attr_test.attr); 292 if (retval) 293 goto exit_test; 294 } 295 296 goto exit; 297 298 exit_test: 299 if (has_adapter_file(slot)) 300 sysfs_remove_file(&pci_slot->kobj, 301 &hotplug_slot_attr_presence.attr); 302 exit_adapter: 303 if (has_latch_file(slot)) 304 sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_latch.attr); 305 exit_latch: 306 if (has_attention_file(slot)) 307 sysfs_remove_file(&pci_slot->kobj, 308 &hotplug_slot_attr_attention.attr); 309 exit_attention: 310 if (has_power_file(slot)) 311 sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_power.attr); 312 exit_power: 313 sysfs_remove_link(&pci_slot->kobj, "module"); 314 exit: 315 return retval; 316 } 317 318 static void fs_remove_slot(struct hotplug_slot *slot, struct pci_slot *pci_slot) 319 { 320 if (has_power_file(slot)) 321 sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_power.attr); 322 323 if (has_attention_file(slot)) 324 sysfs_remove_file(&pci_slot->kobj, 325 &hotplug_slot_attr_attention.attr); 326 327 if (has_latch_file(slot)) 328 sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_latch.attr); 329 330 if (has_adapter_file(slot)) 331 sysfs_remove_file(&pci_slot->kobj, 332 &hotplug_slot_attr_presence.attr); 333 334 if (has_test_file(slot)) 335 sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_test.attr); 336 337 sysfs_remove_link(&pci_slot->kobj, "module"); 338 } 339 340 /** 341 * __pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem 342 * @slot: pointer to the &struct hotplug_slot to register 343 * @bus: bus this slot is on 344 * @devnr: device number 345 * @name: name registered with kobject core 346 * @owner: caller module owner 347 * @mod_name: caller module name 348 * 349 * Prepares a hotplug slot for in-kernel use and immediately publishes it to 350 * user space in one go. Drivers may alternatively carry out the two steps 351 * separately by invoking pci_hp_initialize() and pci_hp_add(). 352 * 353 * Returns 0 if successful, anything else for an error. 354 */ 355 int __pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus, 356 int devnr, const char *name, 357 struct module *owner, const char *mod_name) 358 { 359 int result; 360 361 result = __pci_hp_initialize(slot, bus, devnr, name, owner, mod_name); 362 if (result) 363 return result; 364 365 result = pci_hp_add(slot); 366 if (result) 367 pci_hp_destroy(slot); 368 369 return result; 370 } 371 EXPORT_SYMBOL_GPL(__pci_hp_register); 372 373 /** 374 * __pci_hp_initialize - prepare hotplug slot for in-kernel use 375 * @slot: pointer to the &struct hotplug_slot to initialize 376 * @bus: bus this slot is on 377 * @devnr: slot number 378 * @name: name registered with kobject core 379 * @owner: caller module owner 380 * @mod_name: caller module name 381 * 382 * Allocate and fill in a PCI slot for use by a hotplug driver. Once this has 383 * been called, the driver may invoke hotplug_slot_name() to get the slot's 384 * unique name. The driver must be prepared to handle a ->reset_slot callback 385 * from this point on. 386 * 387 * Returns 0 on success or a negative int on error. 388 */ 389 int __pci_hp_initialize(struct hotplug_slot *slot, struct pci_bus *bus, 390 int devnr, const char *name, struct module *owner, 391 const char *mod_name) 392 { 393 struct pci_slot *pci_slot; 394 395 if (slot == NULL) 396 return -ENODEV; 397 if (slot->ops == NULL) 398 return -EINVAL; 399 400 slot->owner = owner; 401 slot->mod_name = mod_name; 402 403 /* 404 * No problems if we call this interface from both ACPI_PCI_SLOT 405 * driver and call it here again. If we've already created the 406 * pci_slot, the interface will simply bump the refcount. 407 */ 408 pci_slot = pci_create_slot(bus, devnr, name, slot); 409 if (IS_ERR(pci_slot)) 410 return PTR_ERR(pci_slot); 411 412 slot->pci_slot = pci_slot; 413 pci_slot->hotplug = slot; 414 return 0; 415 } 416 EXPORT_SYMBOL_GPL(__pci_hp_initialize); 417 418 /** 419 * pci_hp_add - publish hotplug slot to user space 420 * @slot: pointer to the &struct hotplug_slot to publish 421 * 422 * Make a hotplug slot's sysfs interface available and inform user space of its 423 * addition by sending a uevent. The hotplug driver must be prepared to handle 424 * all &struct hotplug_slot_ops callbacks from this point on. 425 * 426 * Returns 0 on success or a negative int on error. 427 */ 428 int pci_hp_add(struct hotplug_slot *slot) 429 { 430 struct pci_slot *pci_slot; 431 int result; 432 433 if (WARN_ON(!slot)) 434 return -EINVAL; 435 436 pci_slot = slot->pci_slot; 437 438 result = fs_add_slot(slot, pci_slot); 439 if (result) 440 return result; 441 442 kobject_uevent(&pci_slot->kobj, KOBJ_ADD); 443 return 0; 444 } 445 EXPORT_SYMBOL_GPL(pci_hp_add); 446 447 /** 448 * pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem 449 * @slot: pointer to the &struct hotplug_slot to deregister 450 * 451 * The @slot must have been registered with the pci hotplug subsystem 452 * previously with a call to pci_hp_register(). 453 */ 454 void pci_hp_deregister(struct hotplug_slot *slot) 455 { 456 pci_hp_del(slot); 457 pci_hp_destroy(slot); 458 } 459 EXPORT_SYMBOL_GPL(pci_hp_deregister); 460 461 /** 462 * pci_hp_del - unpublish hotplug slot from user space 463 * @slot: pointer to the &struct hotplug_slot to unpublish 464 * 465 * Remove a hotplug slot's sysfs interface. 466 */ 467 void pci_hp_del(struct hotplug_slot *slot) 468 { 469 if (WARN_ON(!slot)) 470 return; 471 472 fs_remove_slot(slot, slot->pci_slot); 473 } 474 EXPORT_SYMBOL_GPL(pci_hp_del); 475 476 /** 477 * pci_hp_destroy - remove hotplug slot from in-kernel use 478 * @slot: pointer to the &struct hotplug_slot to destroy 479 * 480 * Destroy a PCI slot used by a hotplug driver. Once this has been called, 481 * the driver may no longer invoke hotplug_slot_name() to get the slot's 482 * unique name. The driver no longer needs to handle a ->reset_slot callback 483 * from this point on. 484 */ 485 void pci_hp_destroy(struct hotplug_slot *slot) 486 { 487 struct pci_slot *pci_slot = slot->pci_slot; 488 489 slot->pci_slot = NULL; 490 pci_slot->hotplug = NULL; 491 pci_destroy_slot(pci_slot); 492 } 493 EXPORT_SYMBOL_GPL(pci_hp_destroy); 494 495 static int __init pci_hotplug_init(void) 496 { 497 int result; 498 499 result = cpci_hotplug_init(debug); 500 if (result) { 501 err("cpci_hotplug_init with error %d\n", result); 502 return result; 503 } 504 505 return result; 506 } 507 device_initcall(pci_hotplug_init); 508 509 /* 510 * not really modular, but the easiest way to keep compat with existing 511 * bootargs behaviour is to continue using module_param here. 512 */ 513 module_param(debug, bool, 0644); 514 MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); 515