Skip to content

Commit fd3bb8f

Browse files
Evan Greengregkh
authored andcommitted
nvmem: core: Add support for keepout regions
Introduce support into the nvmem core for arrays of register ranges that should not result in actual device access. For these regions a constant byte (repeated) is returned instead on read, and writes are quietly ignored and returned as successful. This is useful for instance if certain efuse regions are protected from access by Linux because they contain secret info to another part of the system (like an integrated modem). Signed-off-by: Evan Green <evgreen@chromium.org> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> Link: https://lore.kernel.org/r/20201127102837.19366-3-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 8f3991f commit fd3bb8f

File tree

2 files changed

+166
-4
lines changed

2 files changed

+166
-4
lines changed

drivers/nvmem/core.c

Lines changed: 149 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ struct nvmem_device {
3434
struct bin_attribute eeprom;
3535
struct device *base_dev;
3636
struct list_head cells;
37+
const struct nvmem_keepout *keepout;
38+
unsigned int nkeepout;
3739
nvmem_reg_read_t reg_read;
3840
nvmem_reg_write_t reg_write;
3941
struct gpio_desc *wp_gpio;
@@ -66,17 +68,17 @@ static LIST_HEAD(nvmem_lookup_list);
6668

6769
static BLOCKING_NOTIFIER_HEAD(nvmem_notifier);
6870

69-
static int nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset,
70-
void *val, size_t bytes)
71+
static int __nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset,
72+
void *val, size_t bytes)
7173
{
7274
if (nvmem->reg_read)
7375
return nvmem->reg_read(nvmem->priv, offset, val, bytes);
7476

7577
return -EINVAL;
7678
}
7779

78-
static int nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset,
79-
void *val, size_t bytes)
80+
static int __nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset,
81+
void *val, size_t bytes)
8082
{
8183
int ret;
8284

@@ -90,6 +92,88 @@ static int nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset,
9092
return -EINVAL;
9193
}
9294

95+
static int nvmem_access_with_keepouts(struct nvmem_device *nvmem,
96+
unsigned int offset, void *val,
97+
size_t bytes, int write)
98+
{
99+
100+
unsigned int end = offset + bytes;
101+
unsigned int kend, ksize;
102+
const struct nvmem_keepout *keepout = nvmem->keepout;
103+
const struct nvmem_keepout *keepoutend = keepout + nvmem->nkeepout;
104+
int rc;
105+
106+
/*
107+
* Skip all keepouts before the range being accessed.
108+
* Keepouts are sorted.
109+
*/
110+
while ((keepout < keepoutend) && (keepout->end <= offset))
111+
keepout++;
112+
113+
while ((offset < end) && (keepout < keepoutend)) {
114+
/* Access the valid portion before the keepout. */
115+
if (offset < keepout->start) {
116+
kend = min(end, keepout->start);
117+
ksize = kend - offset;
118+
if (write)
119+
rc = __nvmem_reg_write(nvmem, offset, val, ksize);
120+
else
121+
rc = __nvmem_reg_read(nvmem, offset, val, ksize);
122+
123+
if (rc)
124+
return rc;
125+
126+
offset += ksize;
127+
val += ksize;
128+
}
129+
130+
/*
131+
* Now we're aligned to the start of this keepout zone. Go
132+
* through it.
133+
*/
134+
kend = min(end, keepout->end);
135+
ksize = kend - offset;
136+
if (!write)
137+
memset(val, keepout->value, ksize);
138+
139+
val += ksize;
140+
offset += ksize;
141+
keepout++;
142+
}
143+
144+
/*
145+
* If we ran out of keepouts but there's still stuff to do, send it
146+
* down directly
147+
*/
148+
if (offset < end) {
149+
ksize = end - offset;
150+
if (write)
151+
return __nvmem_reg_write(nvmem, offset, val, ksize);
152+
else
153+
return __nvmem_reg_read(nvmem, offset, val, ksize);
154+
}
155+
156+
return 0;
157+
}
158+
159+
static int nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset,
160+
void *val, size_t bytes)
161+
{
162+
if (!nvmem->nkeepout)
163+
return __nvmem_reg_read(nvmem, offset, val, bytes);
164+
165+
return nvmem_access_with_keepouts(nvmem, offset, val, bytes, false);
166+
}
167+
168+
static int nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset,
169+
void *val, size_t bytes)
170+
{
171+
if (!nvmem->nkeepout)
172+
return __nvmem_reg_write(nvmem, offset, val, bytes);
173+
174+
return nvmem_access_with_keepouts(nvmem, offset, val, bytes, true);
175+
}
176+
93177
#ifdef CONFIG_NVMEM_SYSFS
94178
static const char * const nvmem_type_str[] = {
95179
[NVMEM_TYPE_UNKNOWN] = "Unknown",
@@ -533,6 +617,59 @@ nvmem_find_cell_by_name(struct nvmem_device *nvmem, const char *cell_id)
533617
return cell;
534618
}
535619

620+
static int nvmem_validate_keepouts(struct nvmem_device *nvmem)
621+
{
622+
unsigned int cur = 0;
623+
const struct nvmem_keepout *keepout = nvmem->keepout;
624+
const struct nvmem_keepout *keepoutend = keepout + nvmem->nkeepout;
625+
626+
while (keepout < keepoutend) {
627+
/* Ensure keepouts are sorted and don't overlap. */
628+
if (keepout->start < cur) {
629+
dev_err(&nvmem->dev,
630+
"Keepout regions aren't sorted or overlap.\n");
631+
632+
return -ERANGE;
633+
}
634+
635+
if (keepout->end < keepout->start) {
636+
dev_err(&nvmem->dev,
637+
"Invalid keepout region.\n");
638+
639+
return -EINVAL;
640+
}
641+
642+
/*
643+
* Validate keepouts (and holes between) don't violate
644+
* word_size constraints.
645+
*/
646+
if ((keepout->end - keepout->start < nvmem->word_size) ||
647+
((keepout->start != cur) &&
648+
(keepout->start - cur < nvmem->word_size))) {
649+
650+
dev_err(&nvmem->dev,
651+
"Keepout regions violate word_size constraints.\n");
652+
653+
return -ERANGE;
654+
}
655+
656+
/* Validate keepouts don't violate stride (alignment). */
657+
if (!IS_ALIGNED(keepout->start, nvmem->stride) ||
658+
!IS_ALIGNED(keepout->end, nvmem->stride)) {
659+
660+
dev_err(&nvmem->dev,
661+
"Keepout regions violate stride.\n");
662+
663+
return -EINVAL;
664+
}
665+
666+
cur = keepout->end;
667+
keepout++;
668+
}
669+
670+
return 0;
671+
}
672+
536673
static int nvmem_add_cells_from_of(struct nvmem_device *nvmem)
537674
{
538675
struct device_node *parent, *child;
@@ -647,6 +784,8 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
647784
nvmem->type = config->type;
648785
nvmem->reg_read = config->reg_read;
649786
nvmem->reg_write = config->reg_write;
787+
nvmem->keepout = config->keepout;
788+
nvmem->nkeepout = config->nkeepout;
650789
if (!config->no_of_node)
651790
nvmem->dev.of_node = config->dev->of_node;
652791

@@ -671,6 +810,12 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
671810
nvmem->dev.groups = nvmem_dev_groups;
672811
#endif
673812

813+
if (nvmem->nkeepout) {
814+
rval = nvmem_validate_keepouts(nvmem);
815+
if (rval)
816+
goto err_put_device;
817+
}
818+
674819
dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name);
675820

676821
rval = device_register(&nvmem->dev);

include/linux/nvmem-provider.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,19 @@ enum nvmem_type {
3030
#define NVMEM_DEVID_NONE (-1)
3131
#define NVMEM_DEVID_AUTO (-2)
3232

33+
/**
34+
* struct nvmem_keepout - NVMEM register keepout range.
35+
*
36+
* @start: The first byte offset to avoid.
37+
* @end: One beyond the last byte offset to avoid.
38+
* @value: The byte to fill reads with for this region.
39+
*/
40+
struct nvmem_keepout {
41+
unsigned int start;
42+
unsigned int end;
43+
unsigned char value;
44+
};
45+
3346
/**
3447
* struct nvmem_config - NVMEM device configuration
3548
*
@@ -39,6 +52,8 @@ enum nvmem_type {
3952
* @owner: Pointer to exporter module. Used for refcounting.
4053
* @cells: Optional array of pre-defined NVMEM cells.
4154
* @ncells: Number of elements in cells.
55+
* @keepout: Optional array of keepout ranges (sorted ascending by start).
56+
* @nkeepout: Number of elements in the keepout array.
4257
* @type: Type of the nvmem storage
4358
* @read_only: Device is read-only.
4459
* @root_only: Device is accessibly to root only.
@@ -66,6 +81,8 @@ struct nvmem_config {
6681
struct gpio_desc *wp_gpio;
6782
const struct nvmem_cell_info *cells;
6883
int ncells;
84+
const struct nvmem_keepout *keepout;
85+
unsigned int nkeepout;
6986
enum nvmem_type type;
7087
bool read_only;
7188
bool root_only;

0 commit comments

Comments
 (0)