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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
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
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
|
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2024 The Android Open Source Project
*/
#include <blk.h>
#include <div64.h>
#include <fastboot.h>
#include <fastboot-internal.h>
#include <fb_block.h>
#include <image-sparse.h>
#include <malloc.h>
#include <part.h>
/**
* FASTBOOT_MAX_BLOCKS_ERASE - maximum blocks to erase per derase call
*
* in the ERASE case we can have much larger buffer size since
* we're not transferring an actual buffer
*/
#define FASTBOOT_MAX_BLOCKS_ERASE 1048576
/**
* FASTBOOT_MAX_BLOCKS_SOFT_ERASE - maximum blocks to software erase at once
*/
#define FASTBOOT_MAX_BLOCKS_SOFT_ERASE 4096
/**
* FASTBOOT_MAX_BLOCKS_WRITE - maximum blocks to write per dwrite call
*/
#define FASTBOOT_MAX_BLOCKS_WRITE 65536
struct fb_block_sparse {
struct blk_desc *dev_desc;
};
/* Write 0s instead of using erase operation, inefficient but functional */
static lbaint_t fb_block_soft_erase(struct blk_desc *block_dev, lbaint_t blk,
lbaint_t cur_blkcnt, lbaint_t erase_buf_blks,
void *erase_buffer)
{
lbaint_t blks_written = 0;
lbaint_t j;
memset(erase_buffer, 0, erase_buf_blks * block_dev->blksz);
for (j = 0; j < cur_blkcnt; j += erase_buf_blks) {
lbaint_t remain = min(cur_blkcnt - j, erase_buf_blks);
blks_written += blk_dwrite(block_dev, blk + j,
remain, erase_buffer);
printf(".");
}
return blks_written;
}
static lbaint_t fb_block_write(struct blk_desc *block_dev, lbaint_t start,
lbaint_t blkcnt, const void *buffer)
{
lbaint_t blk = start;
lbaint_t blks_written = 0;
lbaint_t blks = 0;
void *erase_buf = NULL;
int erase_buf_blks = 0;
lbaint_t step = buffer ? FASTBOOT_MAX_BLOCKS_WRITE : FASTBOOT_MAX_BLOCKS_ERASE;
lbaint_t i;
for (i = 0; i < blkcnt; i += step) {
lbaint_t cur_blkcnt = min(blkcnt - i, step);
if (buffer) {
if (fastboot_progress_callback)
fastboot_progress_callback("writing");
blks_written = blk_dwrite(block_dev, blk, cur_blkcnt,
buffer + (i * block_dev->blksz));
} else {
if (fastboot_progress_callback)
fastboot_progress_callback("erasing");
if (!erase_buf) {
blks_written = blk_derase(block_dev, blk, cur_blkcnt);
/* Allocate erase buffer if erase is not implemented */
if ((long)blks_written == -ENOSYS) {
erase_buf_blks = min_t(long, blkcnt,
FASTBOOT_MAX_BLOCKS_SOFT_ERASE);
erase_buf = malloc(erase_buf_blks * block_dev->blksz);
printf("Slowly writing empty buffers due to missing erase operation\n");
}
}
if (erase_buf)
blks_written = fb_block_soft_erase(block_dev, blk, cur_blkcnt,
erase_buf_blks, erase_buf);
}
blk += blks_written;
blks += blks_written;
}
if (erase_buf)
free(erase_buf);
return blks;
}
static lbaint_t fb_block_sparse_write(struct sparse_storage *info,
lbaint_t blk, lbaint_t blkcnt,
const void *buffer)
{
struct fb_block_sparse *sparse = info->priv;
struct blk_desc *dev_desc = sparse->dev_desc;
return fb_block_write(dev_desc, blk, blkcnt, buffer);
}
static lbaint_t fb_block_sparse_reserve(struct sparse_storage *info,
lbaint_t blk, lbaint_t blkcnt)
{
return blkcnt;
}
int fastboot_block_get_part_info(const char *part_name,
struct blk_desc **dev_desc,
struct disk_partition *part_info,
char *response)
{
int ret;
const char *interface = config_opt_enabled(CONFIG_FASTBOOT_FLASH_BLOCK,
CONFIG_FASTBOOT_FLASH_BLOCK_INTERFACE_NAME,
NULL);
const int device = config_opt_enabled(CONFIG_FASTBOOT_FLASH_BLOCK,
CONFIG_FASTBOOT_FLASH_BLOCK_DEVICE_ID, -1);
if (!part_name || !strcmp(part_name, "")) {
fastboot_fail("partition not given", response);
return -ENOENT;
}
if (!interface || !strcmp(interface, "")) {
fastboot_fail("block interface isn't provided", response);
return -EINVAL;
}
*dev_desc = blk_get_dev(interface, device);
if (!dev_desc) {
fastboot_fail("no such device", response);
return -ENODEV;
}
ret = part_get_info_by_name(*dev_desc, part_name, part_info);
if (ret < 0)
fastboot_fail("failed to get partition info", response);
return ret;
}
void fastboot_block_raw_erase_disk(struct blk_desc *dev_desc, const char *disk_name,
char *response)
{
lbaint_t written;
debug("Start Erasing %s...\n", disk_name);
written = fb_block_write(dev_desc, 0, dev_desc->lba, NULL);
if (written != dev_desc->lba) {
pr_err("Failed to erase %s\n", disk_name);
fastboot_response("FAIL", response, "Failed to erase %s", disk_name);
return;
}
printf("........ erased " LBAFU " bytes from '%s'\n",
dev_desc->lba * dev_desc->blksz, disk_name);
fastboot_okay(NULL, response);
}
void fastboot_block_raw_erase(struct blk_desc *dev_desc, struct disk_partition *info,
const char *part_name, uint alignment, char *response)
{
lbaint_t written, blks_start, blks_size;
if (alignment) {
blks_start = (info->start + alignment - 1) & ~(alignment - 1);
if (info->size >= alignment)
blks_size = (info->size - (blks_start - info->start)) &
(~(alignment - 1));
else
blks_size = 0;
printf("Erasing blocks " LBAFU " to " LBAFU " due to alignment\n",
blks_start, blks_start + blks_size);
} else {
blks_start = info->start;
blks_size = info->size;
}
written = fb_block_write(dev_desc, blks_start, blks_size, NULL);
if (written != blks_size) {
fastboot_fail("failed to erase partition", response);
return;
}
printf("........ erased " LBAFU " bytes from '%s'\n",
blks_size * info->blksz, part_name);
fastboot_okay(NULL, response);
}
void fastboot_block_erase(const char *part_name, char *response)
{
struct blk_desc *dev_desc;
struct disk_partition part_info;
if (fastboot_block_get_part_info(part_name, &dev_desc, &part_info, response) < 0)
return;
fastboot_block_raw_erase(dev_desc, &part_info, part_name, 0, response);
}
void fastboot_block_write_raw_disk(struct blk_desc *dev_desc, const char *disk_name,
void *buffer, u32 download_bytes, char *response)
{
lbaint_t blkcnt;
lbaint_t blks;
/* determine number of blocks to write */
blkcnt = ((download_bytes + (dev_desc->blksz - 1)) & ~(dev_desc->blksz - 1));
blkcnt = lldiv(blkcnt, dev_desc->blksz);
if (blkcnt > dev_desc->lba) {
pr_err("too large for disk: '%s'\n", disk_name);
fastboot_fail("too large for disk", response);
return;
}
printf("Flashing Raw Image\n");
blks = fb_block_write(dev_desc, 0, blkcnt, buffer);
if (blks != blkcnt) {
pr_err("failed writing to %s\n", disk_name);
fastboot_fail("failed writing to device", response);
return;
}
printf("........ wrote " LBAFU " bytes to '%s'\n", blkcnt * dev_desc->blksz,
disk_name);
fastboot_okay(NULL, response);
}
void fastboot_block_write_raw_image(struct blk_desc *dev_desc,
struct disk_partition *info, const char *part_name,
void *buffer, u32 download_bytes, char *response)
{
lbaint_t blkcnt;
lbaint_t blks;
/* determine number of blocks to write */
blkcnt = ((download_bytes + (info->blksz - 1)) & ~(info->blksz - 1));
blkcnt = lldiv(blkcnt, info->blksz);
if (blkcnt > info->size) {
pr_err("too large for partition: '%s'\n", part_name);
fastboot_fail("too large for partition", response);
return;
}
printf("Flashing Raw Image\n");
blks = fb_block_write(dev_desc, info->start, blkcnt, buffer);
if (blks != blkcnt) {
pr_err("failed writing to device %d\n", dev_desc->devnum);
fastboot_fail("failed writing to device", response);
return;
}
printf("........ wrote " LBAFU " bytes to '%s'\n", blkcnt * info->blksz,
part_name);
fastboot_okay(NULL, response);
}
void fastboot_block_write_sparse_image(struct blk_desc *dev_desc, struct disk_partition *info,
const char *part_name, void *buffer, char *response)
{
struct fb_block_sparse sparse_priv;
struct sparse_storage sparse;
int err;
sparse_priv.dev_desc = dev_desc;
sparse.blksz = info->blksz;
sparse.start = info->start;
sparse.size = info->size;
sparse.write = fb_block_sparse_write;
sparse.reserve = fb_block_sparse_reserve;
sparse.mssg = fastboot_fail;
printf("Flashing sparse image at offset " LBAFU "\n",
sparse.start);
sparse.priv = &sparse_priv;
err = write_sparse_image(&sparse, part_name, buffer,
response);
if (!err)
fastboot_okay(NULL, response);
}
void fastboot_block_flash_write(const char *part_name, void *download_buffer,
u32 download_bytes, char *response)
{
struct blk_desc *dev_desc;
struct disk_partition part_info;
if (fastboot_block_get_part_info(part_name, &dev_desc, &part_info, response) < 0)
return;
if (is_sparse_image(download_buffer)) {
fastboot_block_write_sparse_image(dev_desc, &part_info, part_name,
download_buffer, response);
} else {
fastboot_block_write_raw_image(dev_desc, &part_info, part_name,
download_buffer, download_bytes, response);
}
}
|