xref: /openbmc/qemu/hw/nvram/aspeed_otp.c (revision d6dc027cf3da5cc933b8906225bf2a276b0dadb7)
1 /*
2  *  ASPEED OTP (One-Time Programmable) memory
3  *
4  *  Copyright (C) 2025 Aspeed
5  *
6  *  SPDX-License-Identifier: GPL-2.0-or-later
7  */
8 
9 #include "qemu/osdep.h"
10 #include "qemu/log.h"
11 #include "qapi/error.h"
12 #include "system/block-backend.h"
13 #include "hw/qdev-properties.h"
14 #include "hw/nvram/aspeed_otp.h"
15 #include "hw/nvram/trace.h"
16 
aspeed_otp_read(void * opaque,hwaddr offset,unsigned size)17 static uint64_t aspeed_otp_read(void *opaque, hwaddr offset, unsigned size)
18 {
19     AspeedOTPState *s = opaque;
20     uint64_t val = 0;
21 
22     memcpy(&val, s->storage + offset, size);
23 
24     return val;
25 }
26 
valid_program_data(uint32_t otp_addr,uint32_t value,uint32_t prog_bit)27 static bool valid_program_data(uint32_t otp_addr,
28                                  uint32_t value, uint32_t prog_bit)
29 {
30     uint32_t programmed_bits, has_programmable_bits;
31     bool is_odd = otp_addr & 1;
32 
33     /*
34      * prog_bit uses 0s to indicate target bits to program:
35      *   - if OTP word is even-indexed, programmed bits flip 0->1
36      *   - if odd, bits flip 1->0
37      * Bit programming is one-way only and irreversible.
38      */
39     if (is_odd) {
40         programmed_bits = ~value & prog_bit;
41     } else {
42         programmed_bits = value & (~prog_bit);
43     }
44 
45     /* If any bit can be programmed, accept the request */
46     has_programmable_bits = value ^ (~prog_bit);
47 
48     if (programmed_bits) {
49         trace_aspeed_otp_prog_conflict(otp_addr, programmed_bits);
50         for (int i = 0; i < 32; ++i) {
51             if (programmed_bits & (1U << i)) {
52                 trace_aspeed_otp_prog_bit(i);
53             }
54         }
55     }
56 
57     return has_programmable_bits != 0;
58 }
59 
program_otpmem_data(void * opaque,uint32_t otp_addr,uint32_t prog_bit,uint32_t * value)60 static bool program_otpmem_data(void *opaque, uint32_t otp_addr,
61                              uint32_t prog_bit, uint32_t *value)
62 {
63     AspeedOTPState *s = opaque;
64     bool is_odd = otp_addr & 1;
65     uint32_t otp_offset = otp_addr << 2;
66 
67     memcpy(value, s->storage + otp_offset, sizeof(uint32_t));
68 
69     if (!valid_program_data(otp_addr, *value, prog_bit)) {
70         return false;
71     }
72 
73     if (is_odd) {
74         *value &= ~prog_bit;
75     } else {
76         *value |= ~prog_bit;
77     }
78 
79     return true;
80 }
81 
aspeed_otp_write(void * opaque,hwaddr otp_addr,uint64_t val,unsigned size)82 static void aspeed_otp_write(void *opaque, hwaddr otp_addr,
83                                 uint64_t val, unsigned size)
84 {
85     AspeedOTPState *s = opaque;
86     uint32_t otp_offset, value;
87 
88     if (!program_otpmem_data(s, otp_addr, val, &value)) {
89         qemu_log_mask(LOG_GUEST_ERROR,
90                       "%s: Failed to program data, value = %x, bit = %"PRIx64"\n",
91                       __func__, value, val);
92         return;
93     }
94 
95     otp_offset = otp_addr << 2;
96     memcpy(s->storage + otp_offset, &value, size);
97 
98     if (s->blk) {
99         if (blk_pwrite(s->blk, otp_offset, size, &value, 0) < 0) {
100             qemu_log_mask(LOG_GUEST_ERROR,
101                           "%s: Failed to write %x to %x\n",
102                           __func__, value, otp_offset);
103 
104             return;
105         }
106     }
107     trace_aspeed_otp_prog(otp_offset, val, value);
108 }
109 
aspeed_otp_init_storage(AspeedOTPState * s,Error ** errp)110 static bool aspeed_otp_init_storage(AspeedOTPState *s, Error **errp)
111 {
112     uint32_t *p;
113     int i, num;
114     uint64_t perm;
115 
116     if (s->blk) {
117         perm = BLK_PERM_CONSISTENT_READ |
118                (blk_supports_write_perm(s->blk) ? BLK_PERM_WRITE : 0);
119         if (blk_set_perm(s->blk, perm, BLK_PERM_ALL, errp) < 0) {
120             return false;
121         }
122         if (blk_pread(s->blk, 0, s->size, s->storage, 0) < 0) {
123             error_setg(errp, "Failed to read the initial flash content");
124             return false;
125         }
126     } else {
127         num = s->size / sizeof(uint32_t);
128         p = (uint32_t *)s->storage;
129         for (i = 0; i < num; i++) {
130             p[i] = (i % 2 == 0) ? 0x00000000 : 0xFFFFFFFF;
131         }
132     }
133     return true;
134 }
135 
136 static const MemoryRegionOps aspeed_otp_ops = {
137     .read = aspeed_otp_read,
138     .write = aspeed_otp_write,
139     .endianness = DEVICE_LITTLE_ENDIAN,
140     .valid.min_access_size = 1,
141     .valid.max_access_size = 4,
142     .valid.unaligned = true,
143     .impl.unaligned = true
144 };
145 
aspeed_otp_realize(DeviceState * dev,Error ** errp)146 static void aspeed_otp_realize(DeviceState *dev, Error **errp)
147 {
148     AspeedOTPState *s = ASPEED_OTP(dev);
149 
150     if (s->size == 0) {
151         error_setg(errp, "aspeed.otp: 'size' property must be set");
152         return;
153     }
154 
155     s->storage = blk_blockalign(s->blk, s->size);
156 
157     if (!aspeed_otp_init_storage(s, errp)) {
158         return;
159     }
160 
161     memory_region_init_io(&s->mmio, OBJECT(dev), &aspeed_otp_ops,
162                           s, "aspeed.otp", s->size);
163     address_space_init(&s->as, &s->mmio, NULL);
164 }
165 
166 static const Property aspeed_otp_properties[] = {
167     DEFINE_PROP_UINT64("size", AspeedOTPState, size, 0),
168     DEFINE_PROP_DRIVE("drive", AspeedOTPState, blk),
169 };
170 
aspeed_otp_class_init(ObjectClass * klass,const void * data)171 static void aspeed_otp_class_init(ObjectClass *klass, const void *data)
172 {
173     DeviceClass *dc = DEVICE_CLASS(klass);
174     dc->realize = aspeed_otp_realize;
175     device_class_set_props(dc, aspeed_otp_properties);
176 }
177 
178 static const TypeInfo aspeed_otp_info = {
179     .name          = TYPE_ASPEED_OTP,
180     .parent        = TYPE_DEVICE,
181     .instance_size = sizeof(AspeedOTPState),
182     .class_init    = aspeed_otp_class_init,
183 };
184 
aspeed_otp_register_types(void)185 static void aspeed_otp_register_types(void)
186 {
187     type_register_static(&aspeed_otp_info);
188 }
189 
190 type_init(aspeed_otp_register_types)
191