����JFIF��x�x����'
Server IP : 78.140.185.180 / Your IP : 216.73.216.178 Web Server : LiteSpeed System : Linux cpanel13.v.fozzy.com 4.18.0-513.11.1.lve.el8.x86_64 #1 SMP Thu Jan 18 16:21:02 UTC 2024 x86_64 User : builderbox ( 1072) PHP Version : 7.3.33 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : OFF | Pkexec : OFF Directory : /opt/dell/srvadmin/src/srvadmin-hapi/dks/dcdbas/kernel24/ |
Upload File : |
/* * dcdbas.c: Dell Systems Management Base Driver * * Copyright (C) 1995-2006 Dell Inc. * * The Dell Systems Management Base driver is a character driver that * implements ioctls for Dell systems management software to use to * communicate with the driver. The driver provides support for Dell * systems management software to manage the following Dell PowerEdge * systems: 300, 1300, 1400, 400SC, 500SC, 1500SC, 1550, 600SC, 1600SC, * 650, 1655MC, 700, and 750. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License v2.0 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/config.h> #include <linux/errno.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/ioctl.h> #include <linux/kernel.h> #include <linux/mc146818rtc.h> #include <linux/mm.h> #include <linux/module.h> #include <linux/reboot.h> #include <linux/sched.h> #include <linux/slab.h> #include <linux/smp.h> #include <linux/string.h> #include <linux/types.h> #include <linux/version.h> #include <asm/io.h> #include <asm/scatterlist.h> #include <asm/semaphore.h> #include <asm/uaccess.h> #include "dcdbas.h" #define DRIVER_NAME "dcdbas" #define DRIVER_VERSION "5.6.0-1.1" #define DRIVER_DESCRIPTION "Dell Systems Management Base Driver" static int driver_major; static int hold_on_shutdown; static struct semaphore tvm_lock; static u8 *tvm_dma_buf; static u32 tvm_dma_buf_phys_addr; static unsigned int tvm_dma_buf_size; static u8 tvm_hc_action; static u8 tvm_smi_type; /** * dcdbas_alloc_32bit - allocate 32-bit addressable memory */ static void *dcdbas_alloc_32bit(size_t size, unsigned int flags) { void *mem; u64 mask = 0xffffffff; unsigned int order = get_order(size); while ((mem = (void *)__get_free_pages(flags, order)) != NULL) { if (((u64)virt_to_phys(mem) & ~mask) == 0) { memset(mem, 0, size); break; } free_pages((unsigned long)mem, order); mem = NULL; if (flags & GFP_DMA) break; flags |= GFP_DMA; } return mem; } /** * dcdbas_free_32bit - free 32-bit addressable memory */ static void dcdbas_free_32bit(void *mem, size_t size) { free_pages((unsigned long)mem, get_order(size)); } /** * tvm_free_dma_buf - free buffer allocated for TVM systems management */ static void tvm_free_dma_buf(void) { if (tvm_dma_buf == NULL) return; pr_debug("%s: phys: %x size: %u\n", __FUNCTION__, tvm_dma_buf_phys_addr, tvm_dma_buf_size); dcdbas_free_32bit(tvm_dma_buf, tvm_dma_buf_size); tvm_dma_buf = NULL; tvm_dma_buf_phys_addr = 0; tvm_dma_buf_size = 0; } /** * tvm_realloc_dma_buf - reallocate buffer for TVM systems management if needed */ static int tvm_realloc_dma_buf(unsigned int size) { void *buf; if (size > MAX_TVM_DMA_BUF_SIZE) return -EINVAL; if (tvm_dma_buf_size >= size) { if ((size != 0) && (tvm_dma_buf == NULL)) { pr_debug("%s: corruption detected\n", __FUNCTION__); return -EFAULT; } /* current buffer is big enough */ return 0; } /* new buffer is needed */ buf = dcdbas_alloc_32bit(size, GFP_KERNEL); if (buf == NULL) { printk(KERN_INFO"%s: failed to allocate TVM memory size %u\n", DRIVER_NAME, size); return -ENOMEM; } /* free any existing buffer */ tvm_free_dma_buf(); /* set up new buffer for use */ tvm_dma_buf = buf; tvm_dma_buf_phys_addr = (u32)virt_to_phys(buf); tvm_dma_buf_size = size; pr_debug("%s: phys: %x size: %u\n", __FUNCTION__, tvm_dma_buf_phys_addr, tvm_dma_buf_size); return 0; } /** * tvm_read_dma_buf - read systems management command response from TVM buffer * @ireq: IOCTL data */ static int tvm_read_dma_buf(struct dcdbas_ioctl_req *ireq) { struct dcdbas_tvm_mem_read *tmr; struct apm_cmd *apm_cmd; unsigned int data_size_needed, buf_size_needed; data_size_needed = sizeof(struct dcdbas_tvm_mem_read); if (ireq->hdr.data_size < data_size_needed) return -EINVAL; tmr = &ireq->data.tvm_mem_read; pr_debug("%s: size: %u\n", __FUNCTION__, tmr->size); if (tmr->size < ESM_APM_CMD_HEADER_SIZE || tmr->size > MAX_DCDBAS_IOCTL_DATA_SIZE) return -EINVAL; data_size_needed = sizeof(struct dcdbas_tvm_mem_read) - sizeof(tmr->buffer) + tmr->size; if (ireq->hdr.data_size < data_size_needed) return -EINVAL; if (tvm_dma_buf == NULL || tvm_dma_buf_size < ESM_APM_CMD_HEADER_SIZE) { ireq->hdr.status = ESM_STATUS_CMD_DEVICE_BAD; return 0; } apm_cmd = (struct apm_cmd *)tvm_dma_buf; buf_size_needed = tmr->size; if (apm_cmd->command & ESM_APM_LONG_CMD_FORMAT) buf_size_needed += (sizeof(struct apm_cmd) - ESM_APM_CMD_HEADER_SIZE); if (tvm_dma_buf_size < buf_size_needed) { ireq->hdr.status = ESM_STATUS_CMD_DEVICE_BAD; return 0; } if (apm_cmd->command & ESM_APM_LONG_CMD_FORMAT) { /* long command */ memcpy(tmr->buffer, tvm_dma_buf, ESM_APM_CMD_HEADER_SIZE); if (tmr->size > ESM_APM_CMD_HEADER_SIZE) { memcpy(tmr->buffer + ESM_APM_CMD_HEADER_SIZE, tvm_dma_buf + sizeof(struct apm_cmd), tmr->size - ESM_APM_CMD_HEADER_SIZE); } } else { /* short command */ memcpy(tmr->buffer, tvm_dma_buf, tmr->size); } ireq->hdr.status = ESM_STATUS_CMD_SUCCESS; return 0; } /** * tvm_write_dma_buf - write systems management command request to TVM buffer * @ireq: IOCTL data */ static int tvm_write_dma_buf(struct dcdbas_ioctl_req *ireq) { struct dcdbas_tvm_mem_write *tmw; struct apm_cmd *apm_cmd; unsigned int data_size_needed, buf_size_needed; int ret; data_size_needed = sizeof(struct dcdbas_tvm_mem_write); if (ireq->hdr.data_size < data_size_needed) return -EINVAL; tmw = &ireq->data.tvm_mem_write; pr_debug("%s: size: %u\n", __FUNCTION__, tmw->size); if (tmw->size < ESM_APM_CMD_HEADER_SIZE || tmw->size > MAX_DCDBAS_IOCTL_DATA_SIZE) return -EINVAL; data_size_needed = sizeof(struct dcdbas_tvm_mem_write) - sizeof(tmw->buffer) + tmw->size; if (ireq->hdr.data_size < data_size_needed) return -EINVAL; apm_cmd = (struct apm_cmd *)tmw->buffer; buf_size_needed = tmw->size; if (apm_cmd->command & ESM_APM_LONG_CMD_FORMAT) buf_size_needed += (sizeof(struct apm_cmd) - ESM_APM_CMD_HEADER_SIZE); /* make sure buffer is big enough for command */ ret = tvm_realloc_dma_buf(buf_size_needed); if (ret) return ret; if (apm_cmd->command & ESM_APM_LONG_CMD_FORMAT) { /* long command */ memcpy(tvm_dma_buf, tmw->buffer, ESM_APM_CMD_HEADER_SIZE); if (tmw->size > ESM_APM_CMD_HEADER_SIZE) { memcpy(tvm_dma_buf + sizeof(struct apm_cmd), tmw->buffer + ESM_APM_CMD_HEADER_SIZE, tmw->size - ESM_APM_CMD_HEADER_SIZE); } /* create scatter/gather list */ apm_cmd = (struct apm_cmd *)tvm_dma_buf; apm_cmd->parameters.longreq.num_sg_entries = 1; apm_cmd->parameters.longreq.sglist[0].size = (tmw->size - ESM_APM_CMD_HEADER_SIZE); apm_cmd->parameters.longreq.sglist[0].addr = (tvm_dma_buf_phys_addr + sizeof(struct apm_cmd)); } else { /* short command */ memcpy(tvm_dma_buf, tmw->buffer, tmw->size); } tmw->phys_address = tvm_dma_buf_phys_addr; ireq->hdr.status = ESM_STATUS_CMD_SUCCESS; return 0; } /** * tvm_alloc_dma_buf - allocate buffer for TVM systems management * @ireq: IOCTL data */ static int tvm_alloc_dma_buf(struct dcdbas_ioctl_req *ireq) { struct dcdbas_tvm_mem_alloc *tma; int ret; if (ireq->hdr.data_size < sizeof(struct dcdbas_tvm_mem_alloc)) return -EINVAL; tma = &ireq->data.tvm_mem_alloc; pr_debug("%s: size: %u\n", __FUNCTION__, tma->size); ret = tvm_realloc_dma_buf(tma->size); if (ret) return ret; tma->phys_address = tvm_dma_buf_phys_addr; ireq->hdr.status = ESM_STATUS_CMD_SUCCESS; return 0; } /** * tvm_set_hc_action - set TVM system host control action * @ireq: IOCTL data */ static int tvm_set_hc_action(struct dcdbas_ioctl_req *ireq) { struct dcdbas_tvm_hc_action *thca; int ret; if (ireq->hdr.data_size < sizeof(struct dcdbas_tvm_hc_action)) return -EINVAL; /* make sure buffer is available for host control command */ ret = tvm_realloc_dma_buf(sizeof(struct apm_cmd)); if (ret) return ret; thca = &ireq->data.tvm_hc_action; pr_debug("%s: action_bitmap: %x smi_type: %u\n", __FUNCTION__, thca->action_bitmap, thca->smi_type); tvm_hc_action = thca->action_bitmap; tvm_smi_type = thca->smi_type; ireq->hdr.status = ESM_STATUS_CMD_SUCCESS; return 0; } /** * tvm_perform_cmd - perform command for TVM systems management * * The caller must set up the command in tvm_dma_buf. */ static s32 tvm_perform_cmd(void) { struct apm_cmd *apm_cmd; u8 *data; #if defined(CONFIG_X86) || defined(CONFIG_X86_64) unsigned long flags; #endif u32 num_ticks; s8 cmd_status; u8 index; apm_cmd = (struct apm_cmd *)tvm_dma_buf; apm_cmd->status = ESM_STATUS_CMD_UNSUCCESSFUL; switch (tvm_smi_type) { case TVM_SMITYPE_TYPE1: { #if defined(CONFIG_X86) || defined(CONFIG_X86_64) spin_lock_irqsave(&rtc_lock, flags); #endif /* write physical address one byte at a time */ data = (u8 *)&tvm_dma_buf_phys_addr; for (index = PE1300_CMOS_CMD_STRUCT_PTR; index < (PE1300_CMOS_CMD_STRUCT_PTR + 4); index++, data++) { outb(index, (CMOS_BASE_PORT + CMOS_PAGE2_INDEX_PORT_PIIX4)); outb(*data, (CMOS_BASE_PORT + CMOS_PAGE2_DATA_PORT_PIIX4)); } /* first set status to -1 as called by spec */ cmd_status = ESM_STATUS_CMD_UNSUCCESSFUL; outb((u8)cmd_status, PCAT_APM_STATUS_PORT); /* generate SMM call */ outb(ESM_APM_CMD, PCAT_APM_CONTROL_PORT); #if defined(CONFIG_X86) || defined(CONFIG_X86_64) spin_unlock_irqrestore(&rtc_lock, flags); #endif /* wait a few to see if it executed */ num_ticks = TIMEOUT_USEC_SHORT_SEMA_BLOCKING; while ((cmd_status = inb(PCAT_APM_STATUS_PORT)) == ESM_STATUS_CMD_UNSUCCESSFUL) { num_ticks--; if (num_ticks == EXPIRED_TIMER) return ESM_STATUS_CMD_TIMEOUT; } } break; case TVM_SMITYPE_TYPE2: case TVM_SMITYPE_TYPE3: { #if defined(CONFIG_X86) || defined(CONFIG_X86_64) spin_lock_irqsave(&rtc_lock, flags); #endif /* write physical address one byte at a time */ data = (u8 *)&tvm_dma_buf_phys_addr; for (index = PE1400_CMOS_CMD_STRUCT_PTR; index < (PE1400_CMOS_CMD_STRUCT_PTR + 4); index++, data++) { outb(index, (CMOS_BASE_PORT + CMOS_PAGE1_INDEX_PORT)); outb(*data, (CMOS_BASE_PORT + CMOS_PAGE1_DATA_PORT)); } /* generate SMM call */ if (tvm_smi_type == TVM_SMITYPE_TYPE3) outb(ESM_APM_CMD, PCAT_APM_CONTROL_PORT); else outb(ESM_APM_CMD, PE1400_APM_CONTROL_PORT); #if defined(CONFIG_X86) || defined(CONFIG_X86_64) /* restore RTC index pointer since it was written to above */ CMOS_READ(RTC_REG_C); spin_unlock_irqrestore(&rtc_lock, flags); #endif /* read control port back to serialize write */ cmd_status = inb(PE1400_APM_CONTROL_PORT); /* wait a few to see if it executed */ num_ticks = TIMEOUT_USEC_SHORT_SEMA_BLOCKING; while (apm_cmd->status == ESM_STATUS_CMD_UNSUCCESSFUL) { num_ticks--; if (num_ticks == EXPIRED_TIMER) return ESM_STATUS_CMD_TIMEOUT; } } break; default: return ESM_STATUS_CMD_NOT_IMPLEMENTED; } return ESM_STATUS_CMD_SUCCESS; } /** * tvm_host_control - initiate host control action on TVM system */ static s32 tvm_host_control(void) { struct apm_cmd *apm_cmd; if (tvm_dma_buf == NULL || tvm_dma_buf_size < sizeof(struct apm_cmd)) { pr_debug("%s: TVM buffer error\n", __FUNCTION__); return ESM_STATUS_CMD_DEVICE_BAD; } apm_cmd = (struct apm_cmd *)tvm_dma_buf; /* power off takes precedence */ if (tvm_hc_action & HC_ACTION_HOST_CONTROL_POWEROFF) { tvm_hc_action = HC_ACTION_NONE; apm_cmd->command = ESM_APM_POWER_CYCLE; apm_cmd->reserved = 0; *((s16 *)&apm_cmd->parameters.shortreq.parm[0]) = (s16)0; return tvm_perform_cmd(); } if (tvm_hc_action & HC_ACTION_HOST_CONTROL_POWERCYCLE) { tvm_hc_action = HC_ACTION_NONE; apm_cmd->command = ESM_APM_POWER_CYCLE; apm_cmd->reserved = 0; *((s16 *)&apm_cmd->parameters.shortreq.parm[0]) = (s16)20; return tvm_perform_cmd(); } tvm_hc_action = HC_ACTION_NONE; return ESM_STATUS_CMD_UNSUCCESSFUL; } /** * callintf_generate_smi - generate SMI for calling interface request * @ireq: IOCTL data */ static int callintf_generate_smi(struct dcdbas_ioctl_req *ireq) { #if defined(CONFIG_X86) || defined(CONFIG_X86_64) struct dcdbas_callintf_cmd *ci_cmd; u32 command_buffer_phys_addr; #ifdef CONFIG_SMP cpumask_t old_mask; #endif if (ireq->hdr.data_size < sizeof(struct dcdbas_callintf_cmd)) return -EINVAL; ci_cmd = &ireq->data.callintf_cmd; command_buffer_phys_addr = virt_to_phys(ci_cmd->command_buffer); #ifdef CONFIG_SMP /* SMI requires CPU 0 */ old_mask = current->cpus_allowed; set_cpus_allowed(current, cpumask_of_cpu(0)); if (smp_processor_id() != 0) { pr_debug("%s: failed to get CPU 0\n", __FUNCTION__); set_cpus_allowed(current, old_mask); return -EBUSY; } #endif /* * SMI requires command buffer physical address in ebx and * signature in ecx. */ /* generate SMI */ asm volatile ( "outb %b0,%w1" : /* no output args */ : "a" (ci_cmd->command_code), "d" (ci_cmd->command_address), "b" (command_buffer_phys_addr), "c" (ci_cmd->signature) : "memory" ); #ifdef CONFIG_SMP set_cpus_allowed(current, old_mask); #endif ireq->hdr.status = ESM_STATUS_CMD_SUCCESS; return 0; #else return -ENOSYS; #endif } /** * dcdbas_host_control - initiate host control action */ static void dcdbas_host_control(void) { if (tvm_hc_action != HC_ACTION_NONE) tvm_host_control(); } /** * dcdbas_dispatch_ioctl - dispatch IOCTL request * @ireq: IOCTL request */ static int dcdbas_dispatch_ioctl(struct dcdbas_ioctl_req *ireq) { int retval = 0; pr_debug("%s: req_type: %u\n", __FUNCTION__, ireq->hdr.req_type); switch (ireq->hdr.req_type) { case ESM_TVM_READ_MEM: if (down_interruptible(&tvm_lock)) return -ERESTARTSYS; retval = tvm_read_dma_buf(ireq); up(&tvm_lock); break; case ESM_TVM_WRITE_MEM: if (down_interruptible(&tvm_lock)) return -ERESTARTSYS; retval = tvm_write_dma_buf(ireq); up(&tvm_lock); break; case ESM_TVM_ALLOC_MEM: if (down_interruptible(&tvm_lock)) return -ERESTARTSYS; retval = tvm_alloc_dma_buf(ireq); up(&tvm_lock); break; case ESM_TVM_HC_ACTION: if (down_interruptible(&tvm_lock)) return -ERESTARTSYS; retval = tvm_set_hc_action(ireq); up(&tvm_lock); break; case ESM_CALLINTF_REQ: retval = callintf_generate_smi(ireq); break; case ESM_HOLD_OS_ON_SHUTDOWN: /* firmware is going to perform host control action */ hold_on_shutdown = 1; ireq->hdr.status = ESM_STATUS_CMD_SUCCESS; break; case ESM_CANCEL_HOLD_OS_ON_SHUTDOWN: hold_on_shutdown = 0; ireq->hdr.status = ESM_STATUS_CMD_SUCCESS; break; default: pr_debug("%s: unsupported req_type\n", __FUNCTION__); ireq->hdr.status = ESM_STATUS_CMD_NOT_IMPLEMENTED; break; } return retval; } /** * dcdbas_do_ioctl - process ioctl request * @filp: file object for device * @cmd: IOCTL command * @arg: IOCTL request data */ static int dcdbas_do_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct dcdbas_ioctl_req __user *ubuf; struct dcdbas_ioctl_req *kbuf; struct dcdbas_ioctl_hdr hdr; unsigned long size; int ret; if (cmd != IOCTL_DCDBAS_CMD) { ret = -EINVAL; goto out1; } ubuf = (struct dcdbas_ioctl_req __user *)arg; if (copy_from_user(&hdr, ubuf, sizeof(struct dcdbas_ioctl_hdr))) { ret = -EFAULT; goto out1; } if (hdr.data_size > MAX_DCDBAS_IOCTL_DATA_SIZE) { ret = -EINVAL; goto out1; } size = sizeof(struct dcdbas_ioctl_hdr) + hdr.data_size; if ((kbuf = dcdbas_alloc_32bit(size, GFP_KERNEL)) == NULL) { printk(KERN_INFO "%s: failed to allocate ioctl memory size %lu\n", DRIVER_NAME, size); ret = -ENOMEM; goto out1; } if (copy_from_user(kbuf, ubuf, size)) { ret = -EFAULT; goto out2; } if ((ret = dcdbas_dispatch_ioctl(kbuf)) != 0) goto out2; if (copy_to_user(ubuf, kbuf, size)) ret = -EFAULT; out2: dcdbas_free_32bit(kbuf, size); out1: return ret; } /** * dcdbas_ioctl - ioctl handler * @inode: inode for device * @filp: file object for device * @cmd: IOCTL command * @arg: IOCTL request data */ static int dcdbas_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { return dcdbas_do_ioctl(filp, cmd, arg); } #ifdef HAVE_COMPAT_IOCTL /** * dcdbas_compat_ioctl - compat ioctl handler * @filp: file object for device * @cmd: IOCTL command * @arg: IOCTL request data */ static long dcdbas_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { return dcdbas_do_ioctl(filp, cmd, arg); } #endif /** * dcdbas_reboot_notify - handle reboot notification * @nb: info about registered reboot notifier * @code: notification code * @unused: unused argument */ static int dcdbas_reboot_notify(struct notifier_block *nb, unsigned long code, void *unused) { static unsigned int notify_cnt = 0; switch (code) { case SYS_DOWN: case SYS_HALT: case SYS_POWER_OFF: if (hold_on_shutdown) { /* firmware is going to perform host control action */ if (++notify_cnt == 2) { printk(KERN_WARNING "Please wait for shutdown " "action to complete...\n"); dcdbas_host_control(); } /* * register again and initiate the host control * action on the second notification to allow * everyone that registered to be notified */ register_reboot_notifier(nb); } break; } return NOTIFY_DONE; } static struct file_operations dcdbas_fops = { .owner = THIS_MODULE, .ioctl = dcdbas_ioctl, #ifdef HAVE_COMPAT_IOCTL .compat_ioctl = dcdbas_compat_ioctl, #endif }; static struct notifier_block dcdbas_reboot_nb = { .notifier_call = dcdbas_reboot_notify, .next = NULL, .priority = 0 }; /** * dcdbas_init - initialize driver */ static int __init dcdbas_init(void) { int ret; tvm_hc_action = HC_ACTION_NONE; tvm_smi_type = TVM_SMITYPE_NONE; sema_init(&tvm_lock, 1); ret = register_chrdev(0, DRIVER_NAME, &dcdbas_fops); if (ret < 0) { printk(KERN_INFO"%s: register_chrdev failed with error %d\n", DRIVER_NAME, ret); goto error1; } driver_major = ret; register_reboot_notifier(&dcdbas_reboot_nb); dcdbas_register_ioctl32(IOCTL_DCDBAS_CMD); printk(KERN_INFO"%s: %s (version %s)\n", DRIVER_NAME, DRIVER_DESCRIPTION, DRIVER_VERSION); return 0; error1: return ret; } /** * dcdbas_exit - perform driver cleanup */ static void __exit dcdbas_exit(void) { dcdbas_unregister_ioctl32(IOCTL_DCDBAS_CMD); unregister_reboot_notifier(&dcdbas_reboot_nb); unregister_chrdev(driver_major, DRIVER_NAME); tvm_free_dma_buf(); } module_init(dcdbas_init); module_exit(dcdbas_exit); MODULE_DESCRIPTION(DRIVER_DESCRIPTION" (version "DRIVER_VERSION")"); MODULE_VERSION(DRIVER_VERSION); MODULE_AUTHOR("Dell Inc."); MODULE_LICENSE("GPL");