1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41#ifdef UBI_LINUX
42#include <linux/err.h>
43#include <asm/uaccess.h>
44#include <asm/div64.h>
45#endif
46
47#include <ubi_uboot.h>
48#include "ubi.h"
49
50
51
52
53
54
55
56
57
58static int set_update_marker(struct ubi_device *ubi, struct ubi_volume *vol)
59{
60 int err;
61 struct ubi_vtbl_record vtbl_rec;
62
63 dbg_msg("set update marker for volume %d", vol->vol_id);
64
65 if (vol->upd_marker) {
66 ubi_assert(ubi->vtbl[vol->vol_id].upd_marker);
67 dbg_msg("already set");
68 return 0;
69 }
70
71 memcpy(&vtbl_rec, &ubi->vtbl[vol->vol_id],
72 sizeof(struct ubi_vtbl_record));
73 vtbl_rec.upd_marker = 1;
74
75 mutex_lock(&ubi->volumes_mutex);
76 err = ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec);
77 mutex_unlock(&ubi->volumes_mutex);
78 vol->upd_marker = 1;
79 return err;
80}
81
82
83
84
85
86
87
88
89
90
91
92static int clear_update_marker(struct ubi_device *ubi, struct ubi_volume *vol,
93 long long bytes)
94{
95 int err;
96 uint64_t tmp;
97 struct ubi_vtbl_record vtbl_rec;
98
99 dbg_msg("clear update marker for volume %d", vol->vol_id);
100
101 memcpy(&vtbl_rec, &ubi->vtbl[vol->vol_id],
102 sizeof(struct ubi_vtbl_record));
103 ubi_assert(vol->upd_marker && vtbl_rec.upd_marker);
104 vtbl_rec.upd_marker = 0;
105
106 if (vol->vol_type == UBI_STATIC_VOLUME) {
107 vol->corrupted = 0;
108 vol->used_bytes = tmp = bytes;
109 vol->last_eb_bytes = do_div(tmp, vol->usable_leb_size);
110 vol->used_ebs = tmp;
111 if (vol->last_eb_bytes)
112 vol->used_ebs += 1;
113 else
114 vol->last_eb_bytes = vol->usable_leb_size;
115 }
116
117 mutex_lock(&ubi->volumes_mutex);
118 err = ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec);
119 mutex_unlock(&ubi->volumes_mutex);
120 vol->upd_marker = 0;
121 return err;
122}
123
124
125
126
127
128
129
130
131
132
133
134int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol,
135 long long bytes)
136{
137 int i, err;
138 uint64_t tmp;
139
140 dbg_msg("start update of volume %d, %llu bytes", vol->vol_id, bytes);
141 ubi_assert(!vol->updating && !vol->changing_leb);
142 vol->updating = 1;
143
144 err = set_update_marker(ubi, vol);
145 if (err)
146 return err;
147
148
149 for (i = 0; i < vol->reserved_pebs; i++) {
150 err = ubi_eba_unmap_leb(ubi, vol, i);
151 if (err)
152 return err;
153 }
154
155 if (bytes == 0) {
156 err = clear_update_marker(ubi, vol, 0);
157 if (err)
158 return err;
159 err = ubi_wl_flush(ubi);
160 if (!err)
161 vol->updating = 0;
162 }
163
164 vol->upd_buf = vmalloc(ubi->leb_size);
165 if (!vol->upd_buf)
166 return -ENOMEM;
167
168 tmp = bytes;
169 vol->upd_ebs = !!do_div(tmp, vol->usable_leb_size);
170 vol->upd_ebs += tmp;
171 vol->upd_bytes = bytes;
172 vol->upd_received = 0;
173 return 0;
174}
175
176
177
178
179
180
181
182
183
184
185int ubi_start_leb_change(struct ubi_device *ubi, struct ubi_volume *vol,
186 const struct ubi_leb_change_req *req)
187{
188 ubi_assert(!vol->updating && !vol->changing_leb);
189
190 dbg_msg("start changing LEB %d:%d, %u bytes",
191 vol->vol_id, req->lnum, req->bytes);
192 if (req->bytes == 0)
193 return ubi_eba_atomic_leb_change(ubi, vol, req->lnum, NULL, 0,
194 req->dtype);
195
196 vol->upd_bytes = req->bytes;
197 vol->upd_received = 0;
198 vol->changing_leb = 1;
199 vol->ch_lnum = req->lnum;
200 vol->ch_dtype = req->dtype;
201
202 vol->upd_buf = vmalloc(req->bytes);
203 if (!vol->upd_buf)
204 return -ENOMEM;
205
206 return 0;
207}
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
239 void *buf, int len, int used_ebs)
240{
241 int err;
242
243 if (vol->vol_type == UBI_DYNAMIC_VOLUME) {
244 int l = ALIGN(len, ubi->min_io_size);
245
246 memset(buf + len, 0xFF, l - len);
247 len = ubi_calc_data_len(ubi, buf, l);
248 if (len == 0) {
249 dbg_msg("all %d bytes contain 0xFF - skip", len);
250 return 0;
251 }
252
253 err = ubi_eba_write_leb(ubi, vol, lnum, buf, 0, len, UBI_UNKNOWN);
254 } else {
255
256
257
258
259
260
261
262
263
264 memset(buf + len, 0, vol->usable_leb_size - len);
265 err = ubi_eba_write_leb_st(ubi, vol, lnum, buf, len,
266 UBI_UNKNOWN, used_ebs);
267 }
268
269 return err;
270}
271
272
273
274
275
276
277
278
279
280
281
282
283
284int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol,
285 const void __user *buf, int count)
286{
287 uint64_t tmp;
288 int lnum, offs, err = 0, len, to_write = count;
289
290 dbg_msg("write %d of %lld bytes, %lld already passed",
291 count, vol->upd_bytes, vol->upd_received);
292
293 if (ubi->ro_mode)
294 return -EROFS;
295
296 tmp = vol->upd_received;
297 offs = do_div(tmp, vol->usable_leb_size);
298 lnum = tmp;
299
300 if (vol->upd_received + count > vol->upd_bytes)
301 to_write = count = vol->upd_bytes - vol->upd_received;
302
303
304
305
306
307 if (offs != 0) {
308
309
310
311
312
313
314
315 len = vol->usable_leb_size - offs;
316 if (len > count)
317 len = count;
318
319 err = copy_from_user(vol->upd_buf + offs, buf, len);
320 if (err)
321 return -EFAULT;
322
323 if (offs + len == vol->usable_leb_size ||
324 vol->upd_received + len == vol->upd_bytes) {
325 int flush_len = offs + len;
326
327
328
329
330
331 ubi_assert(flush_len <= vol->usable_leb_size);
332 err = write_leb(ubi, vol, lnum, vol->upd_buf, flush_len,
333 vol->upd_ebs);
334 if (err)
335 return err;
336 }
337
338 vol->upd_received += len;
339 count -= len;
340 buf += len;
341 lnum += 1;
342 }
343
344
345
346
347
348 while (count) {
349 if (count > vol->usable_leb_size)
350 len = vol->usable_leb_size;
351 else
352 len = count;
353
354 err = copy_from_user(vol->upd_buf, buf, len);
355 if (err)
356 return -EFAULT;
357
358 if (len == vol->usable_leb_size ||
359 vol->upd_received + len == vol->upd_bytes) {
360 err = write_leb(ubi, vol, lnum, vol->upd_buf,
361 len, vol->upd_ebs);
362 if (err)
363 break;
364 }
365
366 vol->upd_received += len;
367 count -= len;
368 lnum += 1;
369 buf += len;
370 }
371
372 ubi_assert(vol->upd_received <= vol->upd_bytes);
373 if (vol->upd_received == vol->upd_bytes) {
374
375 err = clear_update_marker(ubi, vol, vol->upd_bytes);
376 if (err)
377 return err;
378 err = ubi_wl_flush(ubi);
379 if (err == 0) {
380 vol->updating = 0;
381 err = to_write;
382 vfree(vol->upd_buf);
383 }
384 }
385
386 return err;
387}
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol,
403 const void __user *buf, int count)
404{
405 int err;
406
407 dbg_msg("write %d of %lld bytes, %lld already passed",
408 count, vol->upd_bytes, vol->upd_received);
409
410 if (ubi->ro_mode)
411 return -EROFS;
412
413 if (vol->upd_received + count > vol->upd_bytes)
414 count = vol->upd_bytes - vol->upd_received;
415
416 err = copy_from_user(vol->upd_buf + vol->upd_received, buf, count);
417 if (err)
418 return -EFAULT;
419
420 vol->upd_received += count;
421
422 if (vol->upd_received == vol->upd_bytes) {
423 int len = ALIGN((int)vol->upd_bytes, ubi->min_io_size);
424
425 memset(vol->upd_buf + vol->upd_bytes, 0xFF, len - vol->upd_bytes);
426 len = ubi_calc_data_len(ubi, vol->upd_buf, len);
427 err = ubi_eba_atomic_leb_change(ubi, vol, vol->ch_lnum,
428 vol->upd_buf, len, UBI_UNKNOWN);
429 if (err)
430 return err;
431 }
432
433 ubi_assert(vol->upd_received <= vol->upd_bytes);
434 if (vol->upd_received == vol->upd_bytes) {
435 vol->changing_leb = 0;
436 err = count;
437 vfree(vol->upd_buf);
438 }
439
440 return err;
441}
442