lkml.org 
[lkml]   [2017]   [Aug]   [7]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[RFC v1 2/4] ipmi_bmc: device interface to IPMI BMC framework
    Date
    From: Benjamin Fair <benjaminfair@google.com>

    This creates a char device which allows userspace programs to send and
    receive IPMI messages. Messages are only routed to userspace if no other
    kernel driver can handle them.

    Signed-off-by: Benjamin Fair <benjaminfair@google.com>
    Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
    ---
    drivers/char/ipmi_bmc/Kconfig | 6 +
    drivers/char/ipmi_bmc/Makefile | 1 +
    drivers/char/ipmi_bmc/ipmi_bmc_devintf.c | 241 +++++++++++++++++++++++++++++++
    3 files changed, 248 insertions(+)
    create mode 100644 drivers/char/ipmi_bmc/ipmi_bmc_devintf.c

    diff --git a/drivers/char/ipmi_bmc/Kconfig b/drivers/char/ipmi_bmc/Kconfig
    index b6af38455702..262a17866aa2 100644
    --- a/drivers/char/ipmi_bmc/Kconfig
    +++ b/drivers/char/ipmi_bmc/Kconfig
    @@ -11,6 +11,12 @@ menuconfig IPMI_BMC

    if IPMI_BMC

    +config IPMI_BMC_DEVICE_INTERFACE
    + tristate 'Device interface for BMC-side IPMI'
    + help
    + This provides a file interface to the IPMI BMC core so userland
    + processes may use IPMI.
    +
    config IPMI_BMC_BT_I2C
    depends on I2C
    select I2C_SLAVE
    diff --git a/drivers/char/ipmi_bmc/Makefile b/drivers/char/ipmi_bmc/Makefile
    index 9c7cd48d899f..ead8abffbd11 100644
    --- a/drivers/char/ipmi_bmc/Makefile
    +++ b/drivers/char/ipmi_bmc/Makefile
    @@ -3,5 +3,6 @@
    #

    obj-$(CONFIG_IPMI_BMC) += ipmi_bmc.o
    +obj-$(CONFIG_IPMI_BMC_DEVICE_INTERFACE) += ipmi_bmc_devintf.o
    obj-$(CONFIG_IPMI_BMC_BT_I2C) += ipmi_bmc_bt_i2c.o
    obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += ipmi_bmc_bt_aspeed.o
    diff --git a/drivers/char/ipmi_bmc/ipmi_bmc_devintf.c b/drivers/char/ipmi_bmc/ipmi_bmc_devintf.c
    new file mode 100644
    index 000000000000..2421237ed575
    --- /dev/null
    +++ b/drivers/char/ipmi_bmc/ipmi_bmc_devintf.c
    @@ -0,0 +1,241 @@
    +/*
    + * Copyright 2017 Google Inc.
    + *
    + * This program is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU General Public License
    + * version 2 as published by the Free Software Foundation.
    + *
    + * This program is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    + * GNU General Public License for more details.
    + */
    +
    +#include <linux/atomic.h>
    +#include <linux/delay.h>
    +#include <linux/fs.h>
    +#include <linux/io.h>
    +#include <linux/ipmi_bmc.h>
    +#include <linux/kfifo.h>
    +#include <linux/log2.h>
    +#include <linux/miscdevice.h>
    +#include <linux/module.h>
    +#include <linux/mutex.h>
    +#include <linux/poll.h>
    +#include <linux/sched.h>
    +#include <linux/slab.h>
    +#include <linux/timer.h>
    +#include <linux/uaccess.h>
    +#include <linux/wait.h>
    +
    +#define PFX "IPMI BMC devintf: "
    +
    +#define DEVICE_NAME "ipmi-bt-host"
    +
    +/* Must be a power of two */
    +#define REQUEST_FIFO_SIZE roundup_pow_of_two(BT_MSG_SEQ_MAX)
    +
    +struct bmc_devintf_data {
    + struct miscdevice miscdev;
    + struct ipmi_bmc_device bmc_device;
    + struct ipmi_bmc_ctx *bmc_ctx;
    + wait_queue_head_t wait_queue;
    + /* FIFO of waiting messages */
    + DECLARE_KFIFO(requests, struct bt_msg, REQUEST_FIFO_SIZE);
    +};
    +
    +static inline struct bmc_devintf_data *file_to_bmc_devintf_data(
    + struct file *file)
    +{
    + return container_of(file->private_data, struct bmc_devintf_data,
    + miscdev);
    +}
    +
    +static ssize_t ipmi_bmc_devintf_read(struct file *file, char __user *buf,
    + size_t count, loff_t *ppos)
    +{
    + struct bmc_devintf_data *devintf_data = file_to_bmc_devintf_data(file);
    + bool non_blocking = file->f_flags & O_NONBLOCK;
    + struct bt_msg msg;
    +
    + if (non_blocking && kfifo_is_empty(&devintf_data->requests)) {
    + return -EAGAIN;
    + } else if (!non_blocking) {
    + if (wait_event_interruptible(devintf_data->wait_queue,
    + !kfifo_is_empty(&devintf_data->requests)))
    + return -ERESTARTSYS;
    + }
    +
    + /* TODO(benjaminfair): eliminate this extra copy */
    + if (unlikely(!kfifo_get(&devintf_data->requests, &msg))) {
    + pr_err(PFX "Unable to read request from fifo\n");
    + return -EIO;
    + }
    +
    + /* TODO(benjaminfair): handle partial reads of a message */
    + if (count > bt_msg_len(&msg))
    + count = bt_msg_len(&msg);
    +
    + if (copy_to_user(buf, &msg, count))
    + return -EFAULT;
    +
    + return count;
    +}
    +
    +static ssize_t ipmi_bmc_devintf_write(struct file *file, const char __user *buf,
    + size_t count, loff_t *ppos)
    +{
    + struct bmc_devintf_data *devintf_data = file_to_bmc_devintf_data(file);
    + bool non_blocking = file->f_flags & O_NONBLOCK;
    + struct bt_msg msg;
    + ssize_t ret = 0;
    +
    + if (count > sizeof(struct bt_msg))
    + return -EINVAL;
    +
    + if (copy_from_user(&msg, buf, count))
    + return -EFAULT;
    +
    + if (count != bt_msg_len(&msg))
    + return -EINVAL;
    +
    + ret = ipmi_bmc_send_response(devintf_data->bmc_ctx, &msg);
    +
    + /* Try again if blocking is allowed */
    + while (!non_blocking && ret == -EAGAIN) {
    + if (wait_event_interruptible(devintf_data->wait_queue,
    + ipmi_bmc_is_response_open(
    + devintf_data->bmc_ctx)))
    + return -ERESTARTSYS;
    +
    + ret = ipmi_bmc_send_response(devintf_data->bmc_ctx, &msg);
    + }
    +
    + if (ret < 0)
    + return ret;
    + else
    + return count;
    +}
    +
    +static unsigned int ipmi_bmc_devintf_poll(struct file *file, poll_table *wait)
    +{
    + struct bmc_devintf_data *devintf_data = file_to_bmc_devintf_data(file);
    + unsigned int mask = 0;
    +
    + poll_wait(file, &devintf_data->wait_queue, wait);
    +
    + if (!kfifo_is_empty(&devintf_data->requests))
    + mask |= POLLIN;
    + if (ipmi_bmc_is_response_open(devintf_data->bmc_ctx))
    + mask |= POLLOUT;
    +
    + return mask;
    +}
    +
    +static const struct file_operations ipmi_bmc_fops = {
    + .owner = THIS_MODULE,
    + .read = ipmi_bmc_devintf_read,
    + .write = ipmi_bmc_devintf_write,
    + .poll = ipmi_bmc_devintf_poll,
    +};
    +
    +static inline struct bmc_devintf_data *device_to_bmc_devintf_data(
    + struct ipmi_bmc_device *device)
    +{
    + return container_of(device, struct bmc_devintf_data, bmc_device);
    +}
    +
    +static int ipmi_bmc_devintf_handle_request(struct ipmi_bmc_device *device,
    + struct bt_msg *bt_request)
    +{
    + struct bmc_devintf_data *devintf_data =
    + device_to_bmc_devintf_data(device);
    +
    + if (!bt_request->len)
    + return -EINVAL;
    +
    + if (!kfifo_put(&devintf_data->requests, *bt_request))
    + return -EBUSY;
    +
    + wake_up_all(&devintf_data->wait_queue);
    +
    + return 0;
    +}
    +
    +static bool ipmi_bmc_devintf_match_request(struct ipmi_bmc_device *device,
    + struct bt_msg *bt_request)
    +{
    + /* Since this is a default device, match all requests */
    + return true;
    +}
    +
    +static void ipmi_bmc_devintf_signal_response_open(
    + struct ipmi_bmc_device *device)
    +{
    + struct bmc_devintf_data *devintf_data =
    + device_to_bmc_devintf_data(device);
    +
    + wake_up_all(&devintf_data->wait_queue);
    +}
    +
    +/*
    + * TODO: if we want to support multiple interfaces, initialize this global
    + * variable elsewhere
    + */
    +static struct bmc_devintf_data *devintf_data;
    +
    +static int __init init_ipmi_bmc_devintf(void)
    +{
    + int ret;
    +
    + devintf_data = kzalloc(sizeof(*devintf_data), GFP_KERNEL);
    + if (!devintf_data)
    + return -ENOMEM;
    +
    + init_waitqueue_head(&devintf_data->wait_queue);
    + INIT_KFIFO(devintf_data->requests);
    +
    + devintf_data->bmc_device.handle_request =
    + ipmi_bmc_devintf_handle_request;
    + devintf_data->bmc_device.match_request =
    + ipmi_bmc_devintf_match_request;
    + devintf_data->bmc_device.signal_response_open =
    + ipmi_bmc_devintf_signal_response_open;
    +
    + devintf_data->miscdev.minor = MISC_DYNAMIC_MINOR;
    + devintf_data->miscdev.name = DEVICE_NAME;
    + devintf_data->miscdev.fops = &ipmi_bmc_fops;
    +
    + devintf_data->bmc_ctx = ipmi_bmc_get_global_ctx();
    +
    + ret = ipmi_bmc_register_default_device(devintf_data->bmc_ctx,
    + &devintf_data->bmc_device);
    + if (ret) {
    + pr_err(PFX "unable to register IPMI BMC device\n");
    + return ret;
    + }
    +
    + ret = misc_register(&devintf_data->miscdev);
    + if (ret) {
    + ipmi_bmc_unregister_default_device(devintf_data->bmc_ctx,
    + &devintf_data->bmc_device);
    + pr_err(PFX "unable to register misc device\n");
    + return ret;
    + }
    +
    + pr_info(PFX "initialized\n");
    + return 0;
    +}
    +module_init(init_ipmi_bmc_devintf);
    +
    +static void __exit exit_ipmi_bmc_devintf(void)
    +{
    + misc_deregister(&devintf_data->miscdev);
    + WARN_ON(ipmi_bmc_unregister_default_device(devintf_data->bmc_ctx,
    + &devintf_data->bmc_device));
    +}
    +module_exit(exit_ipmi_bmc_devintf);
    +
    +MODULE_AUTHOR("Benjamin Fair <benjaminfair@google.com>");
    +MODULE_DESCRIPTION("Device file interface to IPMI Block Transfer core.");
    +MODULE_LICENSE("GPL v2");
    --
    2.14.0.rc1.383.gd1ce394fe2-goog
    \
     
     \ /
      Last update: 2017-08-08 05:54    [W:3.364 / U:0.108 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site