X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fchar%2Fipmi%2Fipmi_devintf.c;h=7c0684deea06a8d1d479f2939ededa537c3bf65d;hb=987b0145d94eecf292d8b301228356f44611ab7c;hp=afd1de325f93c2e6f8e2872d2190faf1a9064517;hpb=daddc0d38b3571bed170afa273a49a0eba090c1e;p=linux-2.6.git diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c index afd1de325..7c0684dee 100644 --- a/drivers/char/ipmi/ipmi_devintf.c +++ b/drivers/char/ipmi/ipmi_devintf.c @@ -44,8 +44,8 @@ #include #include #include - -#define IPMI_DEVINTF_VERSION "v31" +#include +#include struct ipmi_file_private { @@ -174,7 +174,7 @@ static int handle_send_req(ipmi_user_t user, { int rv; struct ipmi_addr addr; - unsigned char *msgdata; + struct kernel_ipmi_msg msg; if (req->addr_len > sizeof(struct ipmi_addr)) return -EINVAL; @@ -182,8 +182,11 @@ static int handle_send_req(ipmi_user_t user, if (copy_from_user(&addr, req->addr, req->addr_len)) return -EFAULT; - msgdata = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL); - if (!msgdata) + msg.netfn = req->msg.netfn; + msg.cmd = req->msg.cmd; + msg.data_len = req->msg.data_len; + msg.data = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL); + if (!msg.data) return -ENOMEM; /* From here out we cannot return, we must jump to "out" for @@ -199,7 +202,7 @@ static int handle_send_req(ipmi_user_t user, goto out; } - if (copy_from_user(&msgdata, + if (copy_from_user(msg.data, req->msg.data, req->msg.data_len)) { @@ -207,20 +210,19 @@ static int handle_send_req(ipmi_user_t user, goto out; } } else { - req->msg.data_len = 0; + msg.data_len = 0; } - req->msg.data = msgdata; rv = ipmi_request_settime(user, &addr, req->msgid, - &(req->msg), + &msg, NULL, 0, retries, retry_time_ms); out: - kfree(msgdata); + kfree(msg.data); return rv; } @@ -231,6 +233,7 @@ static int ipmi_ioctl(struct inode *inode, { int rv = -EINVAL; struct ipmi_file_private *priv = file->private_data; + void __user *arg = (void __user *)data; switch (cmd) { @@ -238,7 +241,7 @@ static int ipmi_ioctl(struct inode *inode, { struct ipmi_req req; - if (copy_from_user(&req, (void *) data, sizeof(req))) { + if (copy_from_user(&req, arg, sizeof(req))) { rv = -EFAULT; break; } @@ -254,7 +257,7 @@ static int ipmi_ioctl(struct inode *inode, { struct ipmi_req_settime req; - if (copy_from_user(&req, (void *) data, sizeof(req))) { + if (copy_from_user(&req, arg, sizeof(req))) { rv = -EFAULT; break; } @@ -277,7 +280,7 @@ static int ipmi_ioctl(struct inode *inode, rv = 0; - if (copy_from_user(&rsp, (void *) data, sizeof(rsp))) { + if (copy_from_user(&rsp, arg, sizeof(rsp))) { rv = -EFAULT; break; } @@ -344,7 +347,7 @@ static int ipmi_ioctl(struct inode *inode, rsp.msg.data_len = 0; } - if (copy_to_user((void *) data, &rsp, sizeof(rsp))) { + if (copy_to_user(arg, &rsp, sizeof(rsp))) { rv = -EFAULT; goto recv_putback_on_err; } @@ -371,7 +374,7 @@ static int ipmi_ioctl(struct inode *inode, { struct ipmi_cmdspec val; - if (copy_from_user(&val, (void *) data, sizeof(val))) { + if (copy_from_user(&val, arg, sizeof(val))) { rv = -EFAULT; break; } @@ -384,7 +387,7 @@ static int ipmi_ioctl(struct inode *inode, { struct ipmi_cmdspec val; - if (copy_from_user(&val, (void *) data, sizeof(val))) { + if (copy_from_user(&val, arg, sizeof(val))) { rv = -EFAULT; break; } @@ -397,7 +400,7 @@ static int ipmi_ioctl(struct inode *inode, { int val; - if (copy_from_user(&val, (void *) data, sizeof(val))) { + if (copy_from_user(&val, arg, sizeof(val))) { rv = -EFAULT; break; } @@ -406,31 +409,35 @@ static int ipmi_ioctl(struct inode *inode, break; } + /* The next four are legacy, not per-channel. */ case IPMICTL_SET_MY_ADDRESS_CMD: { unsigned int val; - if (copy_from_user(&val, (void *) data, sizeof(val))) { + if (copy_from_user(&val, arg, sizeof(val))) { rv = -EFAULT; break; } - ipmi_set_my_address(priv->user, val); - rv = 0; + rv = ipmi_set_my_address(priv->user, 0, val); break; } case IPMICTL_GET_MY_ADDRESS_CMD: { - unsigned int val; + unsigned int val; + unsigned char rval; + + rv = ipmi_get_my_address(priv->user, 0, &rval); + if (rv) + break; - val = ipmi_get_my_address(priv->user); + val = rval; - if (copy_to_user((void *) data, &val, sizeof(val))) { + if (copy_to_user(arg, &val, sizeof(val))) { rv = -EFAULT; break; } - rv = 0; break; } @@ -438,34 +445,104 @@ static int ipmi_ioctl(struct inode *inode, { unsigned int val; - if (copy_from_user(&val, (void *) data, sizeof(val))) { + if (copy_from_user(&val, arg, sizeof(val))) { rv = -EFAULT; break; } - ipmi_set_my_LUN(priv->user, val); - rv = 0; + rv = ipmi_set_my_LUN(priv->user, 0, val); break; } case IPMICTL_GET_MY_LUN_CMD: { - unsigned int val; + unsigned int val; + unsigned char rval; - val = ipmi_get_my_LUN(priv->user); + rv = ipmi_get_my_LUN(priv->user, 0, &rval); + if (rv) + break; - if (copy_to_user((void *) data, &val, sizeof(val))) { + val = rval; + + if (copy_to_user(arg, &val, sizeof(val))) { + rv = -EFAULT; + break; + } + break; + } + + case IPMICTL_SET_MY_CHANNEL_ADDRESS_CMD: + { + struct ipmi_channel_lun_address_set val; + + if (copy_from_user(&val, arg, sizeof(val))) { + rv = -EFAULT; + break; + } + + return ipmi_set_my_address(priv->user, val.channel, val.value); + break; + } + + case IPMICTL_GET_MY_CHANNEL_ADDRESS_CMD: + { + struct ipmi_channel_lun_address_set val; + + if (copy_from_user(&val, arg, sizeof(val))) { + rv = -EFAULT; + break; + } + + rv = ipmi_get_my_address(priv->user, val.channel, &val.value); + if (rv) + break; + + if (copy_to_user(arg, &val, sizeof(val))) { rv = -EFAULT; break; } - rv = 0; break; } + + case IPMICTL_SET_MY_CHANNEL_LUN_CMD: + { + struct ipmi_channel_lun_address_set val; + + if (copy_from_user(&val, arg, sizeof(val))) { + rv = -EFAULT; + break; + } + + rv = ipmi_set_my_LUN(priv->user, val.channel, val.value); + break; + } + + case IPMICTL_GET_MY_CHANNEL_LUN_CMD: + { + struct ipmi_channel_lun_address_set val; + + if (copy_from_user(&val, arg, sizeof(val))) { + rv = -EFAULT; + break; + } + + rv = ipmi_get_my_LUN(priv->user, val.channel, &val.value); + if (rv) + break; + + if (copy_to_user(arg, &val, sizeof(val))) { + rv = -EFAULT; + break; + } + break; + } + case IPMICTL_SET_TIMING_PARMS_CMD: { struct ipmi_timing_parms parms; - if (copy_from_user(&parms, (void *) data, sizeof(parms))) { + if (copy_from_user(&parms, arg, sizeof(parms))) { rv = -EFAULT; break; } @@ -483,7 +560,7 @@ static int ipmi_ioctl(struct inode *inode, parms.retries = priv->default_retries; parms.retry_time_ms = priv->default_retry_time_ms; - if (copy_to_user((void *) data, &parms, sizeof(parms))) { + if (copy_to_user(arg, &parms, sizeof(parms))) { rv = -EFAULT; break; } @@ -496,10 +573,206 @@ static int ipmi_ioctl(struct inode *inode, return rv; } +#ifdef CONFIG_COMPAT + +/* + * The following code contains code for supporting 32-bit compatible + * ioctls on 64-bit kernels. This allows running 32-bit apps on the + * 64-bit kernel + */ +#define COMPAT_IPMICTL_SEND_COMMAND \ + _IOR(IPMI_IOC_MAGIC, 13, struct compat_ipmi_req) +#define COMPAT_IPMICTL_SEND_COMMAND_SETTIME \ + _IOR(IPMI_IOC_MAGIC, 21, struct compat_ipmi_req_settime) +#define COMPAT_IPMICTL_RECEIVE_MSG \ + _IOWR(IPMI_IOC_MAGIC, 12, struct compat_ipmi_recv) +#define COMPAT_IPMICTL_RECEIVE_MSG_TRUNC \ + _IOWR(IPMI_IOC_MAGIC, 11, struct compat_ipmi_recv) + +struct compat_ipmi_msg { + u8 netfn; + u8 cmd; + u16 data_len; + compat_uptr_t data; +}; + +struct compat_ipmi_req { + compat_uptr_t addr; + compat_uint_t addr_len; + compat_long_t msgid; + struct compat_ipmi_msg msg; +}; + +struct compat_ipmi_recv { + compat_int_t recv_type; + compat_uptr_t addr; + compat_uint_t addr_len; + compat_long_t msgid; + struct compat_ipmi_msg msg; +}; + +struct compat_ipmi_req_settime { + struct compat_ipmi_req req; + compat_int_t retries; + compat_uint_t retry_time_ms; +}; + +/* + * Define some helper functions for copying IPMI data + */ +static long get_compat_ipmi_msg(struct ipmi_msg *p64, + struct compat_ipmi_msg __user *p32) +{ + compat_uptr_t tmp; + + if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + __get_user(p64->netfn, &p32->netfn) || + __get_user(p64->cmd, &p32->cmd) || + __get_user(p64->data_len, &p32->data_len) || + __get_user(tmp, &p32->data)) + return -EFAULT; + p64->data = compat_ptr(tmp); + return 0; +} + +static long put_compat_ipmi_msg(struct ipmi_msg *p64, + struct compat_ipmi_msg __user *p32) +{ + if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) || + __put_user(p64->netfn, &p32->netfn) || + __put_user(p64->cmd, &p32->cmd) || + __put_user(p64->data_len, &p32->data_len)) + return -EFAULT; + return 0; +} + +static long get_compat_ipmi_req(struct ipmi_req *p64, + struct compat_ipmi_req __user *p32) +{ + + compat_uptr_t tmp; + + if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + __get_user(tmp, &p32->addr) || + __get_user(p64->addr_len, &p32->addr_len) || + __get_user(p64->msgid, &p32->msgid) || + get_compat_ipmi_msg(&p64->msg, &p32->msg)) + return -EFAULT; + p64->addr = compat_ptr(tmp); + return 0; +} + +static long get_compat_ipmi_req_settime(struct ipmi_req_settime *p64, + struct compat_ipmi_req_settime __user *p32) +{ + if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + get_compat_ipmi_req(&p64->req, &p32->req) || + __get_user(p64->retries, &p32->retries) || + __get_user(p64->retry_time_ms, &p32->retry_time_ms)) + return -EFAULT; + return 0; +} + +static long get_compat_ipmi_recv(struct ipmi_recv *p64, + struct compat_ipmi_recv __user *p32) +{ + compat_uptr_t tmp; + + if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + __get_user(p64->recv_type, &p32->recv_type) || + __get_user(tmp, &p32->addr) || + __get_user(p64->addr_len, &p32->addr_len) || + __get_user(p64->msgid, &p32->msgid) || + get_compat_ipmi_msg(&p64->msg, &p32->msg)) + return -EFAULT; + p64->addr = compat_ptr(tmp); + return 0; +} + +static long put_compat_ipmi_recv(struct ipmi_recv *p64, + struct compat_ipmi_recv __user *p32) +{ + if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) || + __put_user(p64->recv_type, &p32->recv_type) || + __put_user(p64->addr_len, &p32->addr_len) || + __put_user(p64->msgid, &p32->msgid) || + put_compat_ipmi_msg(&p64->msg, &p32->msg)) + return -EFAULT; + return 0; +} + +/* + * Handle compatibility ioctls + */ +static long compat_ipmi_ioctl(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + int rc; + struct ipmi_file_private *priv = filep->private_data; + + switch(cmd) { + case COMPAT_IPMICTL_SEND_COMMAND: + { + struct ipmi_req rp; + + if (get_compat_ipmi_req(&rp, compat_ptr(arg))) + return -EFAULT; + + return handle_send_req(priv->user, &rp, + priv->default_retries, + priv->default_retry_time_ms); + } + case COMPAT_IPMICTL_SEND_COMMAND_SETTIME: + { + struct ipmi_req_settime sp; + + if (get_compat_ipmi_req_settime(&sp, compat_ptr(arg))) + return -EFAULT; + + return handle_send_req(priv->user, &sp.req, + sp.retries, sp.retry_time_ms); + } + case COMPAT_IPMICTL_RECEIVE_MSG: + case COMPAT_IPMICTL_RECEIVE_MSG_TRUNC: + { + struct ipmi_recv __user *precv64; + struct ipmi_recv recv64; + + if (get_compat_ipmi_recv(&recv64, compat_ptr(arg))) + return -EFAULT; + + precv64 = compat_alloc_user_space(sizeof(recv64)); + if (copy_to_user(precv64, &recv64, sizeof(recv64))) + return -EFAULT; + + rc = ipmi_ioctl(filep->f_dentry->d_inode, filep, + ((cmd == COMPAT_IPMICTL_RECEIVE_MSG) + ? IPMICTL_RECEIVE_MSG + : IPMICTL_RECEIVE_MSG_TRUNC), + (unsigned long) precv64); + if (rc != 0) + return rc; + + if (copy_from_user(&recv64, precv64, sizeof(recv64))) + return -EFAULT; + + if (put_compat_ipmi_recv(&recv64, compat_ptr(arg))) + return -EFAULT; + + return rc; + } + default: + return ipmi_ioctl(filep->f_dentry->d_inode, filep, cmd, arg); + } +} +#endif static struct file_operations ipmi_fops = { .owner = THIS_MODULE, .ioctl = ipmi_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = compat_ipmi_ioctl, +#endif .open = ipmi_open, .release = ipmi_release, .fasync = ipmi_fasync, @@ -516,15 +789,21 @@ MODULE_PARM_DESC(ipmi_major, "Sets the major number of the IPMI device. By" " interface. Other values will set the major device number" " to that value."); +static struct class *ipmi_class; + static void ipmi_new_smi(int if_num) { - devfs_mk_cdev(MKDEV(ipmi_major, if_num), - S_IFCHR | S_IRUSR | S_IWUSR, + dev_t dev = MKDEV(ipmi_major, if_num); + + devfs_mk_cdev(dev, S_IFCHR | S_IRUSR | S_IWUSR, "ipmidev/%d", if_num); + + class_device_create(ipmi_class, NULL, dev, NULL, "ipmi%d", if_num); } static void ipmi_smi_gone(int if_num) { + class_device_destroy(ipmi_class, MKDEV(ipmi_major, if_num)); devfs_remove("ipmidev/%d", if_num); } @@ -542,11 +821,17 @@ static __init int init_ipmi_devintf(void) if (ipmi_major < 0) return -EINVAL; - printk(KERN_INFO "ipmi device interface version " - IPMI_DEVINTF_VERSION "\n"); + printk(KERN_INFO "ipmi device interface\n"); + + ipmi_class = class_create(THIS_MODULE, "ipmi"); + if (IS_ERR(ipmi_class)) { + printk(KERN_ERR "ipmi: can't register device class\n"); + return PTR_ERR(ipmi_class); + } rv = register_chrdev(ipmi_major, DEVICE_NAME, &ipmi_fops); if (rv < 0) { + class_destroy(ipmi_class); printk(KERN_ERR "ipmi: can't get major %d\n", ipmi_major); return rv; } @@ -560,6 +845,7 @@ static __init int init_ipmi_devintf(void) rv = ipmi_smi_watcher_register(&smi_watcher); if (rv) { unregister_chrdev(ipmi_major, DEVICE_NAME); + class_destroy(ipmi_class); printk(KERN_WARNING "ipmi: can't register smi watcher\n"); return rv; } @@ -570,6 +856,7 @@ module_init(init_ipmi_devintf); static __exit void cleanup_ipmi(void) { + class_destroy(ipmi_class); ipmi_smi_watcher_unregister(&smi_watcher); devfs_remove(DEVICE_NAME); unregister_chrdev(ipmi_major, DEVICE_NAME); @@ -577,3 +864,5 @@ static __exit void cleanup_ipmi(void) module_exit(cleanup_ipmi); MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Corey Minyard "); +MODULE_DESCRIPTION("Linux device interface for the IPMI message handler.");