Re: [PATCH 2.6.12-rc6] char: Add Dell Systems Management Base driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 167, 06 16, 2005 at 12:30:24PM -0500, Doug Warzecha wrote:
> This patch adds the Dell Systems Management Base driver.
> 
> The Dell Systems Management Base driver is a character driver that
> provides support needed by Dell systems management software to manage
> certain Dell systems.  The driver implements ioctls for Dell systems
> management software to use to communicate with the driver.
> 
> This driver has been tested with Dell systems management software
> on a variety of Dell systems.
> 
> By making a contribution to this project, I certify that:
> The contribution was created in whole or in part by me and
> I have the right to submit it under the open source license
> indicated in the file.
> 
> Signed-off-by: Doug Warzecha <[email protected]>
> ---
> 
> diff -uprN linux-2.6.12-rc6.orig/drivers/char/dcdbas.c linux-2.6.12-rc6/drivers/char/dcdbas.c
> --- linux-2.6.12-rc6.orig/drivers/char/dcdbas.c	1969-12-31 18:00:00.000000000 -0600
> +++ linux-2.6.12-rc6/drivers/char/dcdbas.c	2005-06-16 11:14:24.937360712 -0500
> @@ -0,0 +1,666 @@
> +/*
> + *  Dell Systems Management Base driver for Linux kernels 2.4 and 2.6
> + *
> + *  Copyright (C) 1995-2005 Dell Inc.
> + *
> + *  The Dell Systems Management Base driver provides support needed by
> + *  Dell systems management software to manage Dell systems.
> + *  The systems management software uses ioctls to communicate
> + *  with the driver.
> + *
> + *  The DRIVER_VERSION and MODULE_DESCRIPTION macros must specify
> + *  the driver version as defined below to provide proper driver
> + *  versioning for systems management scripts.
> + *
> + *  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/errno.h>
> +#include <linux/fs.h>
> +#include <linux/init.h>
> +#include <linux/ioctl.h>
> +#include <linux/kernel.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/semaphore.h>
> +#include <asm/uaccess.h>
> +
> +#include "dcdbas.h"
> +
> +#define DRIVER_NAME		"dcdbas"
> +#define DRIVER_VERSION		"5.6.0-1"
> +#define DRIVER_DESCRIPTION	"Systems Management Base driver"
> +
> +static int driver_major;
> +static atomic_t hold_on_shutdown;
> +static struct semaphore tvm_lock;
> +static u8 *tvm_dma_buf;
> +static u32 tvm_dma_buf_phys_addr;
> +static unsigned long tvm_dma_buf_size;
> +static u8 tvm_hc_action;
> +static u8 tvm_smi_type;
> +
> +/**
> + * tvm_alloc_suitable - allocate memory suitable for TVM system management
> + * @size: size of memory to allocate
> + * @phys_max: maximum physical memory address
> + */
> +static void *tvm_alloc_suitable(unsigned long size, unsigned long phys_max)
> +{
> +	void *ptr;
> +	unsigned int flags = GFP_KERNEL;
> +
> +	while ((ptr = kmalloc(size, flags)) != NULL) {
> +		if ((virt_to_phys(ptr) + size - 1) <= phys_max)
> +			break;
> +
> +		kfree(ptr);
> +		ptr = NULL;
> +
> +		if (flags & GFP_DMA)
> +			break;
> +
> +		flags |= GFP_DMA;
> +	}
> +	return ptr;
> +}
> +
> +/**
> + * tvm_free_suitable - free memory suitable for TVM system management
> + * @ptr: virtual address of memory to free
> + */
> +static void tvm_free_suitable(void *ptr)
> +{
> +	kfree(ptr);
> +}
> +
> +/**
> + * tvm_free_dma_buf - free buffer allocated for TVM system support
> + */
> +static void tvm_free_dma_buf(void)
> +{
> +	if (tvm_dma_buf == NULL)
> +		return;
> +
> +	pr_debug("%s: phys: %x size: %lu\n",
> +		__FUNCTION__, tvm_dma_buf_phys_addr, tvm_dma_buf_size);
> +
> +	tvm_free_suitable(tvm_dma_buf);
> +	tvm_dma_buf = NULL;
> +	tvm_dma_buf_phys_addr = 0;
> +	tvm_dma_buf_size = 0;
> +}
> +
> +/**
> + * tvm_realloc_dma_buf - reallocate buffer for TVM system support if needed
> + * @size: size of memory needed
> + */
> +static s32 tvm_realloc_dma_buf(unsigned long size)
> +{
> +	u8 *buf;
> +
> +	if (tvm_dma_buf_size >= size) {
> +		if ((size != 0) && (tvm_dma_buf == NULL)) {
> +			pr_debug("%s: corruption detected\n", __FUNCTION__);
> +			return ESM_STATUS_CMD_DEVICE_BAD;
> +		}
> +
> +		/* current buffer is big enough */
> +		return ESM_STATUS_CMD_SUCCESS;
> +	}
> +
> +	/* new buffer is needed */
> +	buf = tvm_alloc_suitable(size, TVM_PHYS_MAX);
> +	if (buf == NULL) {
> +		printk(KERN_INFO
> +			"%s failed to allocate memory of size %lu for TVM\n",
> +			DRIVER_NAME, size);
> +		return ESM_STATUS_CMD_UNSUCCESSFUL;
> +	}
> +
> +	/* 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: %lu\n",
> +		__FUNCTION__, tvm_dma_buf_phys_addr, tvm_dma_buf_size);
> +
> +	return ESM_STATUS_CMD_SUCCESS;
> +}
> +
> +/**
> + * tvm_read_dma_buf - read data from TVM buffer
> + * @tmr: IOCTL data
> + */
> +static s32 tvm_read_dma_buf(struct dcdbas_tvm_mem_read *tmr)
> +{
> +	struct apm_cmd_t *apm_cmd;
> +	unsigned long size_needed;
> +
> +	pr_debug("%s: size: %u\n", __FUNCTION__, tmr->size);
> +
> +	if (tvm_dma_buf == NULL) {
> +		pr_debug("%s: TVM buffer not allocated\n", __FUNCTION__);
> +		return ESM_STATUS_CMD_DEVICE_BAD;
> +	}
> +
> +	apm_cmd = (struct apm_cmd_t *)tvm_dma_buf;
> +
> +	size_needed = tmr->size;
> +	if (apm_cmd->command & ESM_APM_LONG_CMD_FORMAT)
> +		size_needed +=
> +			(sizeof(struct apm_cmd_t) - ESM_APM_CMD_HEADER_SIZE);
> +
> +	if (size_needed > tvm_dma_buf_size) {
> +		pr_debug("%s: TVM buffer too small\n", __FUNCTION__);
> +		return ESM_STATUS_CMD_DEVICE_BAD;
> +	}
> +
> +	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_t),
> +				tmr->size - ESM_APM_CMD_HEADER_SIZE);
> +		}
> +	} else {
> +		/* short command */
> +		memcpy(tmr->buffer, tvm_dma_buf, tmr->size);
> +	}
> +
> +	return ESM_STATUS_CMD_SUCCESS;
> +}
> +
> +/**
> + * tvm_write_dma_buf - write data to TVM buffer
> + * @tmw: IOCTL data
> + */
> +static s32 tvm_write_dma_buf(struct dcdbas_tvm_mem_write *tmw)
> +{
> +	struct apm_cmd_t *apm_cmd = (struct apm_cmd_t *)tmw->buffer;
> +	u32 size_needed = tmw->size;
> +	s32 status;
> +
> +	pr_debug("%s: size: %u\n", __FUNCTION__, tmw->size);
> +
> +	if (apm_cmd->command & ESM_APM_LONG_CMD_FORMAT)
> +		size_needed +=
> +			(sizeof(struct apm_cmd_t) - ESM_APM_CMD_HEADER_SIZE);
> +
> +	status = tvm_realloc_dma_buf(size_needed);
> +	if (status != ESM_STATUS_CMD_SUCCESS)
> +		return status;
> +
> +	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_t),
> +				tmw->buffer + ESM_APM_CMD_HEADER_SIZE,
> +				tmw->size - ESM_APM_CMD_HEADER_SIZE);
> +		}
> +
> +		/* create scatter/gather list */
> +		apm_cmd = (struct apm_cmd_t *)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_t));
> +	} else {
> +		/* short command */
> +		memcpy(tvm_dma_buf, tmw->buffer, tmw->size);
> +	}
> +
> +	tmw->phys_address = tvm_dma_buf_phys_addr;
> +
> +	return ESM_STATUS_CMD_SUCCESS;
> +}
> +
> +/**
> + * tvm_alloc_dma_buf - allocate buffer for TVM system support
> + * @tma: IOCTL data
> + */
> +static s32 tvm_alloc_dma_buf(struct dcdbas_tvm_mem_alloc *tma)
> +{
> +	s32 status;
> +
> +	pr_debug("%s: size: %u\n", __FUNCTION__, tma->size);
> +
> +	status = tvm_realloc_dma_buf(tma->size);
> +	if (status == ESM_STATUS_CMD_SUCCESS)
> +		tma->phys_address = tvm_dma_buf_phys_addr;
> +	else
> +		tma->phys_address = 0;
> +
> +	return status;
> +}
> +
> +/**
> + * tvm_set_hc_action - set TVM host control action
> + * @thca: IOCTL data
> + */
> +static s32 tvm_set_hc_action(struct dcdbas_tvm_hc_action *thca)
> +{
> +	pr_debug("%s: action_bitmap: %x smi_type: %u\n",
> +		__FUNCTION__, thca->action_bitmap, thca->smi_type);
> +
> +	if (tvm_dma_buf == NULL) {
> +		pr_debug("%s: TVM buffer not allocated\n", __FUNCTION__);
> +		return ESM_STATUS_CMD_UNSUCCESSFUL;
> +	}
> +
> +	tvm_hc_action = thca->action_bitmap;
> +	tvm_smi_type = thca->smi_type;
> +
> +	return ESM_STATUS_CMD_SUCCESS;
> +}
> +
> +/**
> + * tvm_perform_cmd - perform command for TVM system support
> + *
> + * The caller must set up the command in tvm_dma_buf.
> + */
> +static s32 tvm_perform_cmd(void)
> +{
> +	struct apm_cmd_t *apm_cmd;
> +	u8 *data;
> +	u32 num_ticks;
> +	s8 cmd_status;
> +	u8 index;
> +
> +	apm_cmd = (struct apm_cmd_t *)tvm_dma_buf;
> +	apm_cmd->status = ESM_STATUS_CMD_UNSUCCESSFUL;
> +
> +	switch (tvm_smi_type) {
> +	case TVM_SMITYPE_TYPE1:
> +	{
> +		/* 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++) {
> +			outb(index,
> +				(CMOS_BASE_PORT + CMOS_PAGE2_INDEX_PORT_PIIX4));
> +			outb(*data++,
> +				(CMOS_BASE_PORT + CMOS_PAGE2_DATA_PORT_PIIX4));
> +		}
> +
> +		cmd_status = ESM_STATUS_CMD_UNSUCCESSFUL;
> +
> +		/* first set status to -1 as called by spec */
> +		outb((u8)cmd_status, PCAT_APM_STATUS_PORT);
> +
> +		/* generate SMM call */
> +		outb(ESM_APM_CMD, PCAT_APM_CONTROL_PORT);
> +
> +		/* restore RTC index pointer */
> +		outb(0x0C, 0x70);
> +		inb(0x70);

Should these port accesses be protected with rtc_lock ?

> +		/* 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:
> +	{
> +		/* 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++) {
> +			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);
> +
> +		/* restore RTC index pointer */
> +		outb(0x0C, 0x70);
> +		inb(0x70);

Same here.

> +		/* 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_t *apm_cmd;
> +
> +	if (tvm_dma_buf == NULL) {
> +		pr_debug("%s: TVM memory not allocated\n", __FUNCTION__);
> +		return ESM_STATUS_CMD_UNSUCCESSFUL;
> +	}
> +
> +	apm_cmd = (struct apm_cmd_t *)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(__i386__)
> +	struct dcdbas_callintf_cmd *ci_cmd = &ireq->data.callintf_cmd;
> +	u32 command_buffer_phys_addr = virt_to_phys(ci_cmd->command_buffer);
> +	cpumask_t old_mask;
> +
> +	pr_debug("%s: cmdaddr: %x cmdcode: %x phys: %x\n",
> +		__FUNCTION__, ci_cmd->command_address, ci_cmd->command_code,
> +		command_buffer_phys_addr);
> +
> +	/* 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__);
> +		return -EBUSY;
> +	}
> +
> +	/* generate SMI */
> +	asm("pushl %ebx");
> +	asm("pushl %ecx");
> +	asm("rep" : : "b" (command_buffer_phys_addr));
> +	asm("rep" : : "c" (ci_cmd->signature));
> +	outb(ci_cmd->command_code, ci_cmd->command_address);
> +	asm("popl %ecx");
> +	asm("popl %ebx");

What this strange code wants to do ?

> +	set_cpus_allowed(current, old_mask);
> +	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;
> +		ireq->hdr.status = tvm_read_dma_buf(&ireq->data.tvm_mem_read);
> +		up(&tvm_lock);
> +		break;
> +
> +	case ESM_TVM_WRITE_MEM:
> +		if (down_interruptible(&tvm_lock))
> +			return -ERESTARTSYS;
> +		ireq->hdr.status = tvm_write_dma_buf(&ireq->data.tvm_mem_write);
> +		up(&tvm_lock);
> +		break;
> +
> +	case ESM_TVM_ALLOC_MEM:
> +		if (down_interruptible(&tvm_lock))
> +			return -ERESTARTSYS;
> +		ireq->hdr.status = tvm_alloc_dma_buf(&ireq->data.tvm_mem_alloc);
> +		up(&tvm_lock);
> +		break;
> +
> +	case ESM_TVM_HC_ACTION:
> +		if (down_interruptible(&tvm_lock))
> +			return -ERESTARTSYS;
> +		ireq->hdr.status = tvm_set_hc_action(&ireq->data.tvm_hc_action);
> +		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 */
> +		atomic_set(&hold_on_shutdown, 1);
> +		ireq->hdr.status = ESM_STATUS_CMD_SUCCESS;
> +		break;
> +
> +	case ESM_CANCEL_HOLD_OS_ON_SHUTDOWN:
> +		atomic_set(&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_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)
> +{
> +	struct dcdbas_ioctl_req *ubuf = (struct dcdbas_ioctl_req *)arg;
> +	struct dcdbas_ioctl_req *kbuf = NULL;
> +	struct dcdbas_ioctl_hdr hdr;
> +	unsigned long size;
> +	int retval;
> +
> +	if (cmd != IOCTL_DCDBAS_CMD)
> +		return -EINVAL;
> +
> +	if (copy_from_user(&hdr, ubuf, sizeof(struct dcdbas_ioctl_hdr)))
> +		return -EFAULT;
> +
> +	size = sizeof(struct dcdbas_ioctl_hdr) + hdr.data_size;
> +	if (size > MAX_DCDBAS_IOCTL_REQ_SIZE)
> +		return -EINVAL;
> +
> +	if ((kbuf = kmalloc(size, GFP_KERNEL)) == NULL) {
> +		printk(KERN_INFO
> +			"%s failed to allocate ioctl memory size %lu\n",
> +			DRIVER_NAME, size);
> +		return -ENOMEM;
> +	}
> +
> +	if (copy_from_user(kbuf, ubuf, size)) {
> +		kfree(kbuf);
> +		return -EFAULT;
> +	}
> +
> +	if ((retval = dcdbas_dispatch_ioctl(kbuf)) != 0) {
> +		kfree(kbuf);
> +		return retval;
> +	}
> +
> +	if (copy_to_user(ubuf, kbuf, size)) {
> +		kfree(kbuf);
> +		return -EFAULT;
> +	}
> +
> +	kfree(kbuf);
> +
> +	return 0;
> +}

Using forward goto is preferred error handling method.

> +/**
> + * 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 int msg_done = 0;
> +
> +	switch (code) {
> +	case SYS_DOWN:
> +	case SYS_HALT:
> +	case SYS_POWER_OFF:
> +		if (atomic_read(&hold_on_shutdown)) {
> +			/* firmware is going to perform host control action */
> +			if (!msg_done) {
> +				printk(KERN_WARNING
> +					"Please wait for shutdown "
> +					"action to complete...\n");
> +				msg_done = 1;
> +			}
> +			register_reboot_notifier(nb);
> +			dcdbas_host_control();
> +		}
> +		break;
> +	}
> +
> +	return NOTIFY_DONE;
> +}
> +
> +static struct file_operations dcdbas_fops = {
> +	owner:	THIS_MODULE,
> +	ioctl:	dcdbas_ioctl,
> +};
> +
> +static struct notifier_block dcdbas_reboot_nb = {
> +	dcdbas_reboot_notify,
> +	NULL,
> +	0
> +};
> +
> +/**
> + * dcdbas_init - initialize module
> + */
> +static int __init dcdbas_init(void)
> +{
> +	int retval;
> +
> +	tvm_hc_action = HC_ACTION_NONE;
> +	tvm_smi_type = TVM_SMITYPE_NONE;
> +	sema_init(&tvm_lock, 1);
> +
> +	retval = register_chrdev(0, DRIVER_NAME, &dcdbas_fops);
> +	if (retval < 0) {
> +		printk(KERN_WARNING
> +			"%s: register_chrdev failed with error %d\n",
> +			DRIVER_NAME, retval);
> +		return retval;
> +	}
> +	driver_major = retval;
> +
> +	register_reboot_notifier(&dcdbas_reboot_nb);
> +	dcdbas_register_ioctl32(IOCTL_DCDBAS_CMD);
> +
> +	printk(KERN_INFO"%s version %s\n", DRIVER_NAME, DRIVER_VERSION);
> +
> +	return 0;
> +}

So this driver will happily load and do crazy things with NVRAM and SMI
on completely innocent non-Dell computer ? It doesn't seems right to me.

I think you can use DMI to detect "certain Dell systems" :)


> +/**
> + * dcdbas_exit - perform module 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");
> +
> diff -uprN linux-2.6.12-rc6.orig/drivers/char/dcdbas.h linux-2.6.12-rc6/drivers/char/dcdbas.h
> --- linux-2.6.12-rc6.orig/drivers/char/dcdbas.h	1969-12-31 18:00:00.000000000 -0600
> +++ linux-2.6.12-rc6/drivers/char/dcdbas.h	2005-06-16 11:14:24.937360712 -0500
> @@ -0,0 +1,196 @@
> +/*
> + *  Dell Systems Management Base driver for Linux kernels 2.4 and 2.6
> + *
> + *  Copyright (C) 1995-2005 Dell Inc.
> + *
> + *  The Dell Systems Management Base driver provides support needed by
> + *  Dell systems management software to manage Dell systems.
> + *  The systems management software uses ioctls to communicate
> + *  with the driver.
> + *
> + *  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.
> + */
> +
> +#ifndef _DCDBAS_H_
> +#define _DCDBAS_H_
> +
> +#include <linux/config.h>
> +#include <linux/kernel.h>
> +#include <linux/input.h>
> +#include <linux/ioctl.h>
> +#include <linux/sched.h>
> +#include <linux/types.h>
> +
> +#ifdef CONFIG_X86_64
> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
> +#include <linux/ioctl32.h>
> +#else
> +#include <asm/ioctl32.h>
> +#endif
> +#endif
> +
> +#ifdef CONFIG_X86_64
> +/* use 64bit ioctl handler for 32bit ioctls */
> +#define dcdbas_register_ioctl32(cmd)	register_ioctl32_conversion(cmd, NULL)
> +#define dcdbas_unregister_ioctl32(cmd)	unregister_ioctl32_conversion(cmd)
> +#else
> +#define dcdbas_register_ioctl32(cmd)	{}
> +#define dcdbas_unregister_ioctl32(cmd)	{}
> +#endif
> +
> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
> +#define CPUMASK_OF_CPU(cpu)		cpumask_of_cpu(cpu)
> +#else
> +#define CPUMASK_OF_CPU(cpu)		(1 << cpu_logical_map(cpu))
> +typedef unsigned long cpumask_t;
> +#define MODULE_VERSION(VERSION)
> +#endif
> +
> +/*
> + * IOCTL command values
> + */
> +#define DCDBAS_IOC_TYPE				'U'
> +#define IOCTL_DCDBAS_CMD			_IO(DCDBAS_IOC_TYPE, 1)
> +
> +/*
> + * IOCTL request type values
> + */
> +#define ESM_HOLD_OS_ON_SHUTDOWN			(41)
> +#define ESM_CANCEL_HOLD_OS_ON_SHUTDOWN		(42)
> +#define ESM_TVM_HC_ACTION			(43)
> +#define ESM_TVM_ALLOC_MEM			(44)
> +#define ESM_CALLINTF_REQ			(47)
> +#define ESM_TVM_READ_MEM			(48)
> +#define ESM_TVM_WRITE_MEM			(49)
> +
> +#define MAX_DCDBAS_IOCTL_REQ_SIZE		(128 * 1024)
> +
> +/*
> + * IOCTL status values
> + */
> +#define ESM_STATUS_CMD_UNSUCCESSFUL		(-1)
> +#define ESM_STATUS_CMD_SUCCESS			(0)
> +#define ESM_STATUS_CMD_NOT_IMPLEMENTED		(1)
> +#define ESM_STATUS_CMD_BAD			(2)
> +#define ESM_STATUS_CMD_TIMEOUT			(3)
> +#define ESM_STATUS_CMD_NO_SUCH_DEVICE		(7)
> +#define ESM_STATUS_CMD_DEVICE_BAD		(9)
> +
> +/*
> + * Host control action values
> + */
> +#define HC_ACTION_NONE				(0)
> +#define HC_ACTION_HOST_CONTROL_POWEROFF		BIT(1)
> +#define HC_ACTION_HOST_CONTROL_POWERCYCLE	BIT(2)
> +
> +/*
> + * TVM SMI type values
> + */
> +#define TVM_SMITYPE_NONE			(0)
> +#define TVM_SMITYPE_TYPE1			(1)
> +#define TVM_SMITYPE_TYPE2			(2)
> +#define TVM_SMITYPE_TYPE3			(3)
> +
> +/*
> + * APM command values
> + */
> +#define ESM_APM_CMD				(0x0A0)
> +#define ESM_APM_CMD_HEADER_SIZE			(4)
> +#define ESM_APM_POWER_CYCLE			(0x10)
> +#define ESM_APM_LONG_CMD_FORMAT			BIT(7)
> +
> +#define CMOS_BASE_PORT				(0x070)
> +#define CMOS_PAGE1_INDEX_PORT			(0)
> +#define CMOS_PAGE1_DATA_PORT			(1)
> +#define CMOS_PAGE2_INDEX_PORT_PIIX4		(2)
> +#define CMOS_PAGE2_DATA_PORT_PIIX4		(3)
> +#define PE1400_APM_CONTROL_PORT			(0x0B0)
> +#define PCAT_APM_CONTROL_PORT			(0x0B2)
> +#define PCAT_APM_STATUS_PORT			(0x0B3)
> +#define PE1300_CMOS_CMD_STRUCT_PTR		(0x38)
> +#define PE1400_CMOS_CMD_STRUCT_PTR		(0x70)
> +
> +#define MAX_SYSMGMT_SHORTCMD_PARMBUF_LEN	(14)
> +#define MAX_SYSMGMT_LONGCMD_SGENTRY_NUM		(16)
> +
> +#define TIMEOUT_USEC_SHORT_SEMA_BLOCKING	(10000)
> +#define EXPIRED_TIMER				(0)
> +
> +#define TVM_PHYS_MAX				(0xffffffffUL)
> +
> +struct dcdbas_ioctl_hdr {
> +	u64 reserved;
> +	s32 status;
> +	u32 req_type;
> +	u32 data_size;
> +} __attribute__ ((packed));
> +
> +struct dcdbas_tvm_mem_alloc {
> +	u32 phys_address;
> +	u32 size;
> +} __attribute__ ((packed));
> +
> +struct dcdbas_tvm_mem_read {
> +	u32 size;
> +	u8 buffer[1];
> +} __attribute__ ((packed));
> +
> +struct dcdbas_tvm_mem_write {
> +	u32 phys_address;
> +	u32 size;
> +	u8 buffer[1];
> +} __attribute__ ((packed));
> +
> +struct dcdbas_tvm_hc_action {
> +	u8 action_bitmap;
> +	u8 smi_type;
> +} __attribute__ ((packed));
> +
> +struct dcdbas_callintf_cmd {
> +	u16 command_address;
> +	u8 command_code;
> +	u8 reserved[1];
> +	u32 signature;
> +	u32 command_buffer_size;
> +	u8 command_buffer[1];
> +} __attribute__ ((packed));
> +
> +struct dcdbas_ioctl_req {
> +	struct dcdbas_ioctl_hdr hdr;
> +	union __attribute__ ((packed)) {
> +		struct dcdbas_tvm_mem_alloc	tvm_mem_alloc;
> +		struct dcdbas_tvm_mem_read	tvm_mem_read;
> +		struct dcdbas_tvm_mem_write	tvm_mem_write;
> +		struct dcdbas_tvm_hc_action	tvm_hc_action;
> +		struct dcdbas_callintf_cmd	callintf_cmd;
> +	} data;
> +} __attribute__ ((packed));
> +
> +struct apm_cmd_t {
> +	u8 command;
> +	s8 status;
> +	u16 reserved;
> +	union __attribute__ ((packed)) {
> +		struct __attribute__ ((packed)) {
> +			u8 parm[MAX_SYSMGMT_SHORTCMD_PARMBUF_LEN];
> +		} shortreq;
> +
> +		struct __attribute__ ((packed)) {
> +			u16 num_sg_entries;
> +			struct __attribute__ ((packed)) {
> +				u32 size;
> +				u64 addr;
> +			} sglist[MAX_SYSMGMT_LONGCMD_SGENTRY_NUM];
> +		} longreq;
> +	} parameters;
> +} __attribute__ ((packed));
> +
> +#endif /* _DCDBAS_H_ */
> +
> diff -uprN linux-2.6.12-rc6.orig/drivers/char/Kconfig linux-2.6.12-rc6/drivers/char/Kconfig
> --- linux-2.6.12-rc6.orig/drivers/char/Kconfig	2005-06-15 19:09:47.000000000 -0500
> +++ linux-2.6.12-rc6/drivers/char/Kconfig	2005-06-16 11:18:03.664109192 -0500
> @@ -998,5 +998,21 @@ config MMTIMER
>  
>  source "drivers/char/tpm/Kconfig"
>  
> +menu "Dell Systems Management"
> +
> +config DCDBAS
> +	tristate "Dell Systems Management Base driver"
> +	default m
> +	help
> +	  The Dell Systems Management Base driver provides support
> +	  needed by Dell systems management software to manage
> +	  certain Dell systems.
> +
> +	  Say Y or M here if you plan to use Dell systems management
> +	  software to manage your system.  If you say M here, the
> +	  driver will be compiled as a module called dcdbas.
> +
> +endmenu
> +
>  endmenu
>  
> diff -uprN linux-2.6.12-rc6.orig/drivers/char/Makefile linux-2.6.12-rc6/drivers/char/Makefile
> --- linux-2.6.12-rc6.orig/drivers/char/Makefile	2005-06-15 19:09:47.000000000 -0500
> +++ linux-2.6.12-rc6/drivers/char/Makefile	2005-06-16 11:18:07.973454072 -0500
> @@ -91,6 +91,8 @@ obj-$(CONFIG_IPMI_HANDLER) += ipmi/
>  
>  obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o
>  obj-$(CONFIG_TCG_TPM) += tpm/
> +obj-$(CONFIG_DCDBAS) += dcdbas.o
> +
>  # Files generated that shall be removed upon make clean
>  clean-files := consolemap_deftbl.c defkeymap.c qtronixmap.c
>  
> diff -uprN linux-2.6.12-rc6.orig/MAINTAINERS linux-2.6.12-rc6/MAINTAINERS
> --- linux-2.6.12-rc6.orig/MAINTAINERS	2005-06-15 19:09:46.000000000 -0500
> +++ linux-2.6.12-rc6/MAINTAINERS	2005-06-16 11:14:14.194993800 -0500
> @@ -681,6 +681,11 @@ M:	[email protected]
>  W:	http://www.debian.org/~dz/i8k/
>  S:	Maintained
>  
> +DELL SYSTEMS MANAGEMENT BASE DRIVER (dcdbas)
> +P:	Doug Warzecha
> +M:	[email protected]
> +S:	Maintained
> +
>  DEVICE-MAPPER
>  P:	Alasdair Kergon
>  L:	[email protected]

-- 
Andrey Panin		| Linux and UNIX system administrator
[email protected]		| PGP key: wwwkeys.pgp.net

Attachment: signature.asc
Description: Digital signature


[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux