?? eeprom.c
字號:
/* * Driver for the EEPROM encryption key storage on the Geode GX * Copyright (C) 2003 Advanced Micro Devices, Inc. All Rights Reserved. *//* This driver allows the user to read and write the 238 bytes of user on the EEPROM. This is the space above the access control bytes and the hidden key, which couldn't be read even if we wanted to. As far as the user is concerned, he can seek, read and (if the bytes are set), write up to 238 bytes of general storage. Setting the key and the access control bytes is handled by the ioctl commands.*/#include <linux/kernel.h>#include <linux/module.h>#include <linux/fs.h>#include <linux/miscdevice.h>#include <asm/io.h>#include <asm/uaccess.h>#include <asm/delay.h>#include "geode_crypto.h"static inteeprom_read_dev(struct geode_crypt_driver *driver, u8 *data, int offset, int size) { u32 ctrl = readl(driver->base + EEPROM_CTRL_REG); int i = 0; int ret = -EINVAL; /* Sanity check */ if (offset + size > 255) return -EINVAL; /* Just a simple wait loop */ for(i = 0; i < size; i++) { u32 reg = ctrl; u32 value = 0; writel(0x0 | ((offset + i) & 0xFF), driver->base + EEPROM_ADDR_REG); reg &= ~EEPROM_CTRL_WRITE; reg |= EEPROM_CTRL_START; writel(reg, driver->base + EEPROM_CTRL_REG); while(1) { reg = readl(driver->base + EEPROM_CTRL_REG); if (reg & EEPROM_CTRL_EXCEPT) goto exit_read; else if (!(reg & EEPROM_CTRL_START)) break; udelay(10); } value = readl(driver->base + EEPROM_DATA_REG); data[i] = value & 0xFF; } ret = 0; exit_read: writel(ctrl | EEPROM_CTRL_EXCEPT, driver->base + EEPROM_CTRL_REG); return ret;}static int eeprom_write_dev(struct geode_crypt_driver *driver, u8 *data, int offset, int size) { int i = 0; int ret = -EINVAL; u32 in = readl(driver->base + EEPROM_SEC_REG); u32 mask = offset < 127 ? EEPROM_ACCESS_WPL : EEPROM_ACCESS_WPU; u32 ctrl = readl(driver->base + EEPROM_CTRL_REG); /* Sanity check */ if (offset + size > 255) return -EINVAL; /* Make sure that we have permission to write */ if ((in & mask) != mask) return -EACCES; /* Just a simple wait loop */ for(i = 0; i < size; i++) { u32 reg = 0; writel(0x0 | ((offset + i) & 0xFF), driver->base + EEPROM_ADDR_REG); writel(0x0 | (data[i] & 0xFF), driver->base + EEPROM_DATA_REG); writel(ctrl | EEPROM_CTRL_WRITE | EEPROM_CTRL_START, driver->base + EEPROM_CTRL_REG); while(1) { reg = readl(driver->base + EEPROM_CTRL_REG); if (reg & EEPROM_CTRL_EXCEPT) goto exit_write; else if (!(reg & EEPROM_CTRL_START)) break; udelay(10); } } ret = 0; exit_write: writel(ctrl | EEPROM_CTRL_EXCEPT, driver->base + EEPROM_CTRL_REG); return ret;}static loff_t eeprom_lseek(struct file * file, loff_t offset, int orig) { switch(orig) { case 0: if (offset < 0 || offset > 237) return -EINVAL; file->f_pos = (unsigned int) offset; return file->f_pos; case 1: if (file->f_pos + offset > 237) return -EINVAL; if (file->f_pos + offset < 0) return -EINVAL; file->f_pos += offset; return file->f_pos; default: return -EINVAL; }}static ssize_t eeprom_write(struct file *filep, const char *buffer, size_t count, loff_t * ppos) { struct geode_crypt_driver *driver = (struct geode_crypt_driver *) filep->private_data; u8 local[238]; int len, ret; if (filep->f_pos > 237) return 0; len = (filep->f_pos + count > 237) ? 238 - filep->f_pos : count; if (copy_from_user(local, buffer, len)) return -EFAULT; down(&driver->eeprom_mutex); ret = eeprom_write_dev(driver, local, 18 + filep->f_pos, len); up(&driver->eeprom_mutex); if (ret) return ret; *ppos += len; return len;}static ssize_t eeprom_read(struct file *filep, char *buffer, size_t count, loff_t * ppos) { struct geode_crypt_driver *driver = (struct geode_crypt_driver *) filep->private_data; u8 local[238]; int len, ret; if (filep->f_pos > EEPROM_SIZE) return 0; len = (filep->f_pos + count > EEPROM_SIZE) ? (EEPROM_SIZE + 1) - filep->f_pos : count; down(&driver->eeprom_mutex); ret = eeprom_read_dev(driver, local, EEPROM_ID_OFFSET + filep->f_pos, len); up(&driver->eeprom_mutex); if (ret) return ret; if (copy_to_user(buffer, local, len)) return -EFAULT; *ppos += len; return len;} static int eeprom_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { struct geode_crypt_driver *driver = (struct geode_crypt_driver *) filp->private_data; u32 in, mask; /* Return -EINVAL if there is an exepction on the device */ in = readl(driver->base + EEPROM_CTRL_REG); if (in & EEPROM_CTRL_EXCEPT) return -EINVAL; switch(cmd) { case EIOCSKEYOFF: in |= EEPROM_CTRL_KEYDISABLE; writel(in, driver->base + EEPROM_CTRL_REG); return 0; case EIOCSLOCKUP: case EIOCSLOCKLW: mask = (cmd == EIOCSLOCKLW) ? EEPROM_ACCESS_WPL : EEPROM_ACCESS_WPU; in = readl(driver->base + EEPROM_SEC_REG); /* If write access is enabled, then we can lock it */ /* This will take effect on the next reset */ if ((in & mask) == mask) { char ctrl = 0x00; eeprom_read_dev(driver, &ctrl, 0, 1); ctrl &= ~mask; eeprom_write_dev(driver, &ctrl, 0, 1); } return 0; case EIOSKEY: { u8 key[32]; if (!arg) return -EINVAL; copy_from_user((u8 *) key, (u8 *) arg, sizeof(key)); return eeprom_write_dev(driver, (u8 *) key, EEPROM_KEY_OFFSET, 16); } case EIOGPERMS: { int ret = 0; in = readl(driver->base + EEPROM_SEC_REG); if ((in & EEPROM_ACCESS_WPL) == EEPROM_ACCESS_WPL) ret |= EEPROM_LOWER_RW; if ((in & EEPROM_ACCESS_WPU) == EEPROM_ACCESS_WPL) ret |= EEPROM_UPPER_RW; return ret; } } return -EINVAL;}extern struct geode_crypt_driver *crypto_driver;static int eeprom_open(struct inode * inode, struct file * filp) { filp->private_data = (void *) crypto_driver; return 0;}static struct file_operations geode_eeprom_fops = { owner: THIS_MODULE, open: eeprom_open, read: eeprom_read, write: eeprom_write, llseek: eeprom_lseek, ioctl: eeprom_ioctl,}; struct miscdevice geode_eeprom_device = { GEODE_EEPROM_MINOR_DEV, "geode_eeprom", &geode_eeprom_fops};
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -