Showing error 1645

User: Jiri Slaby
Error type: Invalid Pointer Dereference
Error type description: A pointer which is invalid is being dereferenced
File location: drivers/pci/hotplug/acpiphp_ibm.c
Line in file: 191
Project: Linux Kernel
Project version: 2.6.28
Tools: Smatch (1.59)
Entered: 2013-09-10 07:54:05 UTC


Source:

  1/*
  2 * ACPI PCI Hot Plug IBM Extension
  3 *
  4 * Copyright (C) 2004 Vernon Mauery <vernux@us.ibm.com>
  5 * Copyright (C) 2004 IBM Corp.
  6 *
  7 * All rights reserved.
  8 *
  9 * This program is free software; you can redistribute it and/or modify
 10 * it under the terms of the GNU General Public License as published by
 11 * the Free Software Foundation; either version 2 of the License, or (at
 12 * your option) any later version.
 13 *
 14 * This program is distributed in the hope that it will be useful, but
 15 * WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
 17 * NON INFRINGEMENT.  See the GNU General Public License for more
 18 * details.
 19 *
 20 * You should have received a copy of the GNU General Public License
 21 * along with this program; if not, write to the Free Software
 22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 23 *
 24 * Send feedback to <vernux@us.ibm.com>
 25 *
 26 */
 27
 28#include <linux/init.h>
 29#include <linux/module.h>
 30#include <linux/kernel.h>
 31#include <acpi/acpi_bus.h>
 32#include <linux/sysfs.h>
 33#include <linux/kobject.h>
 34#include <asm/uaccess.h>
 35#include <linux/moduleparam.h>
 36#include <linux/pci.h>
 37
 38#include "acpiphp.h"
 39#include "../pci.h"
 40
 41#define DRIVER_VERSION        "1.0.1"
 42#define DRIVER_AUTHOR        "Irene Zubarev <zubarev@us.ibm.com>, Vernon Mauery <vernux@us.ibm.com>"
 43#define DRIVER_DESC        "ACPI Hot Plug PCI Controller Driver IBM extension"
 44
 45static int debug;
 46
 47MODULE_AUTHOR(DRIVER_AUTHOR);
 48MODULE_DESCRIPTION(DRIVER_DESC);
 49MODULE_LICENSE("GPL");
 50MODULE_VERSION(DRIVER_VERSION);
 51module_param(debug, bool, 0644);
 52MODULE_PARM_DESC(debug, " Debugging mode enabled or not");
 53#define MY_NAME "acpiphp_ibm"
 54
 55#undef dbg
 56#define dbg(format, arg...)                                \
 57do {                                                        \
 58        if (debug)                                        \
 59                printk(KERN_DEBUG "%s: " format,        \
 60                                MY_NAME , ## arg);        \
 61} while (0)
 62
 63#define FOUND_APCI 0x61504349
 64/* these are the names for the IBM ACPI pseudo-device */
 65#define IBM_HARDWARE_ID1 "IBM37D0"
 66#define IBM_HARDWARE_ID2 "IBM37D4"
 67
 68#define hpslot_to_sun(A) (((struct slot *)((A)->private))->acpi_slot->sun)
 69
 70/* union apci_descriptor - allows access to the
 71 * various device descriptors that are embedded in the
 72 * aPCI table
 73 */
 74union apci_descriptor {
 75        struct {
 76                char sig[4];
 77                u8   len;
 78        } header;
 79        struct {
 80                u8  type;
 81                u8  len;
 82                u16 slot_id;
 83                u8  bus_id;
 84                u8  dev_num;
 85                u8  slot_num;
 86                u8  slot_attr[2];
 87                u8  attn;
 88                u8  status[2];
 89                u8  sun;
 90                u8  res[3];
 91        } slot;
 92        struct {
 93                u8 type;
 94                u8 len;
 95        } generic;
 96};
 97
 98/* struct notification - keeps info about the device
 99 * that cause the ACPI notification event
100 */
101struct notification {
102        struct acpi_device *device;
103        u8                  event;
104};
105
106static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status);
107static int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status);
108static void ibm_handle_events(acpi_handle handle, u32 event, void *context);
109static int ibm_get_table_from_acpi(char **bufp);
110static ssize_t ibm_read_apci_table(struct kobject *kobj,
111                                   struct bin_attribute *bin_attr,
112                                   char *buffer, loff_t pos, size_t size);
113static acpi_status __init ibm_find_acpi_device(acpi_handle handle,
114                u32 lvl, void *context, void **rv);
115static int __init ibm_acpiphp_init(void);
116static void __exit ibm_acpiphp_exit(void);
117
118static acpi_handle ibm_acpi_handle;
119static struct notification ibm_note;
120static struct bin_attribute ibm_apci_table_attr = {
121            .attr = {
122                    .name = "apci_table",
123                    .mode = S_IRUGO,
124            },
125            .read = ibm_read_apci_table,
126            .write = NULL,
127};
128static struct acpiphp_attention_info ibm_attention_info = 
129{
130        .set_attn = ibm_set_attention_status,
131        .get_attn = ibm_get_attention_status,
132        .owner = THIS_MODULE,
133};
134
135/**
136 * ibm_slot_from_id - workaround for bad ibm hardware
137 * @id: the slot number that linux refers to the slot by
138 *
139 * Description: This method returns the aCPI slot descriptor
140 * corresponding to the Linux slot number.  This descriptor
141 * has info about the aPCI slot id and attention status.
142 * This descriptor must be freed using kfree when done.
143 */
144static union apci_descriptor *ibm_slot_from_id(int id)
145{
146        int ind = 0, size;
147        union apci_descriptor *ret = NULL, *des;
148        char *table;
149
150        size = ibm_get_table_from_acpi(&table);
151        des = (union apci_descriptor *)table;
152        if (memcmp(des->header.sig, "aPCI", 4) != 0)
153                goto ibm_slot_done;
154
155        des = (union apci_descriptor *)&table[ind += des->header.len];
156        while (ind < size && (des->generic.type != 0x82 ||
157                        des->slot.slot_num != id)) {
158                des = (union apci_descriptor *)&table[ind += des->generic.len];
159        }
160
161        if (ind < size && des->slot.slot_num == id)
162                ret = des;
163
164ibm_slot_done:
165        if (ret) {
166                ret = kmalloc(sizeof(union apci_descriptor), GFP_KERNEL);
167                memcpy(ret, des, sizeof(union apci_descriptor));
168        }
169        kfree(table);
170        return ret;
171}
172
173/**
174 * ibm_set_attention_status - callback method to set the attention LED
175 * @slot: the hotplug_slot to work with
176 * @status: what to set the LED to (0 or 1)
177 *
178 * Description: This method is registered with the acpiphp module as a
179 * callback to do the device specific task of setting the LED status.
180 */
181static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status)
182{
183        union acpi_object args[2]; 
184        struct acpi_object_list params = { .pointer = args, .count = 2 };
185        acpi_status stat; 
186        unsigned long long rc;
187        union apci_descriptor *ibm_slot;
188
189        ibm_slot = ibm_slot_from_id(hpslot_to_sun(slot));
190
191        dbg("%s: set slot %d (%d) attention status to %d\n", __func__,
192                        ibm_slot->slot.slot_num, ibm_slot->slot.slot_id,
193                        (status ? 1 : 0));
194
195        args[0].type = ACPI_TYPE_INTEGER;
196        args[0].integer.value = ibm_slot->slot.slot_id;
197        args[1].type = ACPI_TYPE_INTEGER;
198        args[1].integer.value = (status) ? 1 : 0;
199
200        kfree(ibm_slot);
201
202        stat = acpi_evaluate_integer(ibm_acpi_handle, "APLS", &params, &rc);
203        if (ACPI_FAILURE(stat)) {
204                err("APLS evaluation failed:  0x%08x\n", stat);
205                return -ENODEV;
206        } else if (!rc) {
207                err("APLS method failed:  0x%08llx\n", rc);
208                return -ERANGE;
209        }
210        return 0;
211}
212
213/**
214 * ibm_get_attention_status - callback method to get attention LED status
215 * @slot: the hotplug_slot to work with
216 * @status: returns what the LED is set to (0 or 1)
217 *
218 * Description: This method is registered with the acpiphp module as a
219 * callback to do the device specific task of getting the LED status.
220 * 
221 * Because there is no direct method of getting the LED status directly
222 * from an ACPI call, we read the aPCI table and parse out our
223 * slot descriptor to read the status from that.
224 */
225static int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status)
226{
227        union apci_descriptor *ibm_slot;
228
229        ibm_slot = ibm_slot_from_id(hpslot_to_sun(slot));
230
231        if (ibm_slot->slot.attn & 0xa0 || ibm_slot->slot.status[1] & 0x08)
232                *status = 1;
233        else
234                *status = 0;
235
236        dbg("%s: get slot %d (%d) attention status is %d\n", __func__,
237                        ibm_slot->slot.slot_num, ibm_slot->slot.slot_id,
238                        *status);
239
240        kfree(ibm_slot);
241        return 0;
242}
243
244/**
245 * ibm_handle_events - listens for ACPI events for the IBM37D0 device
246 * @handle: an ACPI handle to the device that caused the event
247 * @event: the event info (device specific)
248 * @context: passed context (our notification struct)
249 *
250 * Description: This method is registered as a callback with the ACPI
251 * subsystem it is called when this device has an event to notify the OS of.
252 *
253 * The events actually come from the device as two events that get
254 * synthesized into one event with data by this function.  The event
255 * ID comes first and then the slot number that caused it.  We report
256 * this as one event to the OS.
257 *
258 * From section 5.6.2.2 of the ACPI 2.0 spec, I understand that the OSPM will
259 * only re-enable the interrupt that causes this event AFTER this method
260 * has returned, thereby enforcing serial access for the notification struct.
261 */
262static void ibm_handle_events(acpi_handle handle, u32 event, void *context)
263{
264        u8 detail = event & 0x0f;
265        u8 subevent = event & 0xf0;
266        struct notification *note = context;
267
268        dbg("%s: Received notification %02x\n", __func__, event);
269
270        if (subevent == 0x80) {
271                dbg("%s: generationg bus event\n", __func__);
272                acpi_bus_generate_proc_event(note->device, note->event, detail);
273                acpi_bus_generate_netlink_event(note->device->pnp.device_class,
274                                                  note->device->dev.bus_id,
275                                                  note->event, detail);
276        } else
277                note->event = event;
278}
279
280/**
281 * ibm_get_table_from_acpi - reads the APLS buffer from ACPI
282 * @bufp: address to pointer to allocate for the table
283 *
284 * Description: This method reads the APLS buffer in from ACPI and
285 * stores the "stripped" table into a single buffer
286 * it allocates and passes the address back in bufp.
287 *
288 * If NULL is passed in as buffer, this method only calculates
289 * the size of the table and returns that without filling
290 * in the buffer.
291 *
292 * Returns < 0 on error or the size of the table on success.
293 */
294static int ibm_get_table_from_acpi(char **bufp)
295{
296        union acpi_object *package;
297        struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
298        acpi_status status;
299        char *lbuf = NULL;
300        int i, size = -EIO;
301
302        status = acpi_evaluate_object(ibm_acpi_handle, "APCI", NULL, &buffer);
303        if (ACPI_FAILURE(status)) {
304                err("%s:  APCI evaluation failed\n", __func__);
305                return -ENODEV;
306        }
307
308        package = (union acpi_object *) buffer.pointer;
309        if (!(package) ||
310                        (package->type != ACPI_TYPE_PACKAGE) ||
311                        !(package->package.elements)) {
312                err("%s:  Invalid APCI object\n", __func__);
313                goto read_table_done;
314        }
315
316        for(size = 0, i = 0; i < package->package.count; i++) {
317                if (package->package.elements[i].type != ACPI_TYPE_BUFFER) {
318                        err("%s:  Invalid APCI element %d\n", __func__, i);
319                        goto read_table_done;
320                }
321                size += package->package.elements[i].buffer.length;
322        }
323
324        if (bufp == NULL)
325                goto read_table_done;
326
327        lbuf = kzalloc(size, GFP_KERNEL);
328        dbg("%s: element count: %i, ASL table size: %i, &table = 0x%p\n",
329                        __func__, package->package.count, size, lbuf);
330
331        if (lbuf) {
332                *bufp = lbuf;
333        } else {
334                size = -ENOMEM;
335                goto read_table_done;
336        }
337
338        size = 0;
339        for (i=0; i<package->package.count; i++) {
340                memcpy(&lbuf[size],
341                                package->package.elements[i].buffer.pointer,
342                                package->package.elements[i].buffer.length);
343                size += package->package.elements[i].buffer.length;
344        }
345
346read_table_done:
347        kfree(buffer.pointer);
348        return size;
349}
350
351/**
352 * ibm_read_apci_table - callback for the sysfs apci_table file
353 * @kobj: the kobject this binary attribute is a part of
354 * @bin_attr: struct bin_attribute for this file
355 * @buffer: the kernel space buffer to fill
356 * @pos: the offset into the file
357 * @size: the number of bytes requested
358 *
359 * Description: Gets registered with sysfs as the reader callback
360 * to be executed when /sys/bus/pci/slots/apci_table gets read.
361 *
362 * Since we don't get notified on open and close for this file,
363 * things get really tricky here...
364 * our solution is to only allow reading the table in all at once.
365 */
366static ssize_t ibm_read_apci_table(struct kobject *kobj,
367                                   struct bin_attribute *bin_attr,
368                                   char *buffer, loff_t pos, size_t size)
369{
370        int bytes_read = -EINVAL;
371        char *table = NULL;
372        
373        dbg("%s: pos = %d, size = %zd\n", __func__, (int)pos, size);
374
375        if (pos == 0) {
376                bytes_read = ibm_get_table_from_acpi(&table);
377                if (bytes_read > 0 && bytes_read <= size)
378                        memcpy(buffer, table, bytes_read);
379                kfree(table);
380        }
381        return bytes_read;
382}
383
384/**
385 * ibm_find_acpi_device - callback to find our ACPI device
386 * @handle: the ACPI handle of the device we are inspecting
387 * @lvl: depth into the namespace tree
388 * @context: a pointer to our handle to fill when we find the device
389 * @rv: a return value to fill if desired
390 *
391 * Description: Used as a callback when calling acpi_walk_namespace
392 * to find our device.  When this method returns non-zero
393 * acpi_walk_namespace quits its search and returns our value.
394 */
395static acpi_status __init ibm_find_acpi_device(acpi_handle handle,
396                u32 lvl, void *context, void **rv)
397{
398        acpi_handle *phandle = (acpi_handle *)context;
399        acpi_status status; 
400        struct acpi_device_info *info;
401        struct acpi_buffer info_buffer = { ACPI_ALLOCATE_BUFFER, NULL };
402        int retval = 0;
403
404        status = acpi_get_object_info(handle, &info_buffer);
405        if (ACPI_FAILURE(status)) {
406                err("%s:  Failed to get device information status=0x%x\n",
407                        __func__, status);
408                return retval;
409        }
410        info = info_buffer.pointer;
411        info->hardware_id.value[sizeof(info->hardware_id.value) - 1] = '\0';
412
413        if (info->current_status && (info->valid & ACPI_VALID_HID) &&
414                        (!strcmp(info->hardware_id.value, IBM_HARDWARE_ID1) ||
415                         !strcmp(info->hardware_id.value, IBM_HARDWARE_ID2))) {
416                dbg("found hardware: %s, handle: %p\n",
417                        info->hardware_id.value, handle);
418                *phandle = handle;
419                /* returning non-zero causes the search to stop
420                 * and returns this value to the caller of 
421                 * acpi_walk_namespace, but it also causes some warnings
422                 * in the acpi debug code to print...
423                 */
424                retval = FOUND_APCI;
425        }
426        kfree(info);
427        return retval;
428}
429
430static int __init ibm_acpiphp_init(void)
431{
432        int retval = 0;
433        acpi_status status;
434        struct acpi_device *device;
435        struct kobject *sysdir = &pci_slots_kset->kobj;
436
437        dbg("%s\n", __func__);
438
439        if (acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
440                        ACPI_UINT32_MAX, ibm_find_acpi_device,
441                        &ibm_acpi_handle, NULL) != FOUND_APCI) {
442                err("%s: acpi_walk_namespace failed\n", __func__);
443                retval = -ENODEV;
444                goto init_return;
445        }
446        dbg("%s: found IBM aPCI device\n", __func__);
447        if (acpi_bus_get_device(ibm_acpi_handle, &device)) {
448                err("%s: acpi_bus_get_device failed\n", __func__);
449                retval = -ENODEV;
450                goto init_return;
451        }
452        if (acpiphp_register_attention(&ibm_attention_info)) {
453                retval = -ENODEV;
454                goto init_return;
455        }
456
457        ibm_note.device = device;
458        status = acpi_install_notify_handler(ibm_acpi_handle,
459                        ACPI_DEVICE_NOTIFY, ibm_handle_events,
460                        &ibm_note);
461        if (ACPI_FAILURE(status)) {
462                err("%s: Failed to register notification handler\n",
463                                __func__);
464                retval = -EBUSY;
465                goto init_cleanup;
466        }
467
468        ibm_apci_table_attr.size = ibm_get_table_from_acpi(NULL);
469        retval = sysfs_create_bin_file(sysdir, &ibm_apci_table_attr);
470
471        return retval;
472
473init_cleanup:
474        acpiphp_unregister_attention(&ibm_attention_info);
475init_return:
476        return retval;
477}
478
479static void __exit ibm_acpiphp_exit(void)
480{
481        acpi_status status;
482        struct kobject *sysdir = &pci_slots_kset->kobj;
483
484        dbg("%s\n", __func__);
485
486        if (acpiphp_unregister_attention(&ibm_attention_info))
487                err("%s: attention info deregistration failed", __func__);
488
489        status = acpi_remove_notify_handler(
490                           ibm_acpi_handle,
491                           ACPI_DEVICE_NOTIFY,
492                           ibm_handle_events);
493        if (ACPI_FAILURE(status))
494                err("%s: Notification handler removal failed\n", __func__);
495        /* remove the /sys entries */
496        sysfs_remove_bin_file(sysdir, &ibm_apci_table_attr);
497}
498
499module_init(ibm_acpiphp_init);
500module_exit(ibm_acpiphp_exit);