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