diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 2591c21b2b5d..fb0b8375d5ae 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -89,6 +90,7 @@ struct at24_data { struct nvmem_device *nvmem; struct regulator *vcc_reg; + void (*read_post)(unsigned int off, char *buf, size_t count); /* * Some chips tie up multiple I2C addresses; dummy devices reserve @@ -121,6 +123,7 @@ MODULE_PARM_DESC(at24_write_timeout, "Time (in ms) to try writes (default 25)"); struct at24_chip_data { u32 byte_len; u8 flags; + void (*read_post)(unsigned int off, char *buf, size_t count); }; #define AT24_CHIP_DATA(_name, _len, _flags) \ @@ -128,6 +131,32 @@ struct at24_chip_data { .byte_len = _len, .flags = _flags, \ } +#define AT24_CHIP_DATA_CB(_name, _len, _flags, _read_post) \ + static const struct at24_chip_data _name = { \ + .byte_len = _len, .flags = _flags, \ + .read_post = _read_post, \ + } + +static void at24_read_post_vaio(unsigned int off, char *buf, size_t count) +{ + int i; + + if (capable(CAP_SYS_ADMIN)) + return; + + /* + * Hide VAIO private settings to regular users: + * - BIOS passwords: bytes 0x00 to 0x0f + * - UUID: bytes 0x10 to 0x1f + * - Serial number: 0xc0 to 0xdf + */ + for (i = 0; i < count; i++) { + if ((off + i <= 0x1f) || + (off + i >= 0xc0 && off + i <= 0xdf)) + buf[i] = 0; + } +} + /* needs 8 addresses as A0-A2 are ignored */ AT24_CHIP_DATA(at24_data_24c00, 128 / 8, AT24_FLAG_TAKE8ADDR); /* old variants can't be handled with this generic entry! */ @@ -144,6 +173,10 @@ AT24_CHIP_DATA(at24_data_24mac602, 64 / 8, /* spd is a 24c02 in memory DIMMs */ AT24_CHIP_DATA(at24_data_spd, 2048 / 8, AT24_FLAG_READONLY | AT24_FLAG_IRUGO); +/* 24c02_vaio is a 24c02 on some Sony laptops */ +AT24_CHIP_DATA_CB(at24_data_24c02_vaio, 2048 / 8, + AT24_FLAG_READONLY | AT24_FLAG_IRUGO, + at24_read_post_vaio); AT24_CHIP_DATA(at24_data_24c04, 4096 / 8, 0); AT24_CHIP_DATA(at24_data_24cs04, 16, AT24_FLAG_SERIAL | AT24_FLAG_READONLY); @@ -177,6 +210,7 @@ static const struct i2c_device_id at24_ids[] = { { "24mac402", (kernel_ulong_t)&at24_data_24mac402 }, { "24mac602", (kernel_ulong_t)&at24_data_24mac602 }, { "spd", (kernel_ulong_t)&at24_data_spd }, + { "24c02-vaio", (kernel_ulong_t)&at24_data_24c02_vaio }, { "24c04", (kernel_ulong_t)&at24_data_24c04 }, { "24cs04", (kernel_ulong_t)&at24_data_24cs04 }, { "24c08", (kernel_ulong_t)&at24_data_24c08 }, @@ -389,6 +423,9 @@ static int at24_read(void *priv, unsigned int off, void *val, size_t count) struct device *dev; char *buf = val; int ret; + unsigned int orig_off = off; + char *orig_buf = buf; + size_t orig_count = count; at24 = priv; dev = at24_base_client_dev(at24); @@ -427,6 +464,9 @@ static int at24_read(void *priv, unsigned int off, void *val, size_t count) pm_runtime_put(dev); + if (unlikely(at24->read_post)) + at24->read_post(orig_off, orig_buf, orig_count); + return 0; } @@ -654,6 +694,7 @@ static int at24_probe(struct i2c_client *client) at24->byte_len = byte_len; at24->page_size = page_size; at24->flags = flags; + at24->read_post = cdata->read_post; at24->num_addresses = num_addresses; at24->offset_adj = at24_get_offset_adj(flags, byte_len); at24->client[0].client = client;