Use static partition node
[platform/core/system/initrd-flash.git] / src / dfu.c
1 /*
2  * flash-manager - Tizen kernel-level image flashing solution
3  *
4  * Licensed under the Apache License, Version 2.0 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <stdio.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <unistd.h>
21 #include <errno.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 #include <sys/queue.h>
25 #include <sys/wait.h>
26 #include <sys/fcntl.h>
27 #include <sys/mount.h>
28 #include <pthread.h>
29 #include <blkid/blkid.h>
30
31 #include "dfu.h"
32 #include "thor-proto.h"
33
34 static char *dfu_info[DFU_INFO_NUM][DFU_INFO_MAX] = {NULL,};
35
36 void *dfu_get_buffer(unsigned long size)
37 {
38         int wait = 100000; /* 100ms */
39         void *buf;
40
41         while (!(buf = malloc(size))) {
42                 /*
43                  * In the case of not enough memory, instead of returning error,
44                  * transfer thread waits until dfu writes and frees buffers.
45                  */
46                 usleep(wait);
47                 wait *= 2;
48         }
49
50         return buf;
51 }
52
53 void dfu_put_buffer(void *ptr)
54 {
55         free(ptr);
56 }
57
58 static int find_match(const char *name)
59 {
60         int i;
61
62         for (i = 0; i < DFU_INFO_NUM; i++) {
63                 char *entry = dfu_info[i][DFU_INFO_NAME];
64
65                 if (entry && !strncmp(entry, name, strlen(entry)))
66                         return i;
67         }
68
69         return -ENOENT;
70 }
71
72 int dfu_request_io(struct tfm_context *ctx, unsigned long len)
73 {
74         int notify = 0;
75         struct dfu_frame *frame;
76
77         frame = malloc(sizeof(*frame));
78         if (!frame)
79                 return -ENOMEM;
80
81         frame->buf = ctx->transfer_buffer;
82         frame->len = len;
83
84         pthread_mutex_lock(&ctx->dfu_mutex);
85
86         if (TAILQ_EMPTY(&ctx->dfu_ioq_head))
87                 notify = 1;
88
89         /* dfu_thread_main() de-queues i/o request and processes it */
90         TAILQ_INSERT_TAIL(&ctx->dfu_ioq_head, frame, entry);
91
92         pthread_mutex_unlock(&ctx->dfu_mutex);
93
94         if (notify)
95                 pthread_cond_signal(&ctx->dfu_data_arrive);
96
97         return 0;
98 }
99
100 static void mount_dev(const char *dev, const char *fstype)
101 {
102         int ret;
103
104         ret = mkdir(DFU_MOUNT_PATH,  0600);
105         if (ret < 0) {
106                 fprintf(stderr, "Failed to create target directory\n");
107                 exit(-1);
108         }
109
110         ret = mount(dev, DFU_MOUNT_PATH, fstype, 0, NULL);
111         if (ret < 0) {
112                 fprintf(stderr, "Failed to mount target partition\n");
113                 rmdir(DFU_MOUNT_PATH);
114                 exit(-1);
115         }
116 }
117
118 static void umount_dev(void)
119 {
120         int ret;
121
122         ret = umount(DFU_MOUNT_PATH);
123         if (ret < 0) {
124                 fprintf(stderr, "Failed to mount target partition\n");
125
126                 /* umount is failed, but anyway try to delete mount point */
127                 rmdir(DFU_MOUNT_PATH);
128                 exit(-1);
129         }
130
131         rmdir(DFU_MOUNT_PATH);
132 }
133
134 void dfu_sync(struct tfm_context *ctx)
135 {
136         char **info = ctx->dfu_info;
137
138         pthread_mutex_lock(&ctx->dfu_sync_mutex);
139         if (!ctx->transfer_done)
140                 pthread_cond_wait(&ctx->dfu_write_done, &ctx->dfu_sync_mutex);
141         pthread_mutex_unlock(&ctx->dfu_sync_mutex);
142
143         switch (*info[DFU_INFO_MODE]) {
144         case DFU_INFO_MODE_FILE:
145                 fsync(ctx->dfu_fd);
146                 close(ctx->dfu_fd);
147                 umount_dev();
148                 break;
149         case DFU_INFO_MODE_PARTITION:
150                 close(ctx->dfu_fd);
151                 break;
152         default:
153                 break;
154         }
155
156         pthread_cond_signal(&ctx->dfu_sync_done);
157
158         fprintf(stdout, "finished\n");
159 }
160
161 static char *get_partition_fstype(const char *devname)
162 {
163         blkid_tag_iterate tag_iter;
164         blkid_dev dev;
165         blkid_cache cache = NULL;
166         const char *type, *value;
167         int ret;
168
169         ret = blkid_get_cache(&cache, NULL);
170         if (ret < 0)
171                 return NULL;
172
173         blkid_probe_all(cache);
174
175         dev = blkid_get_dev(cache, devname, 0);
176         if (!dev)
177                 return NULL;
178
179         dev = blkid_verify(cache, dev);
180         if (!dev)
181                 return NULL;
182
183         tag_iter = blkid_tag_iterate_begin(dev);
184         while (blkid_tag_next(tag_iter, &type, &value) == 0) {
185                 if (!strncmp(type, "TYPE", 4)) {
186                         char *fstype = strdup(value);
187
188                         blkid_tag_iterate_end(tag_iter);
189                         blkid_put_cache(cache);
190                         return fstype;
191                 }
192         }
193         blkid_tag_iterate_end(tag_iter);
194         blkid_put_cache(cache);
195
196         return NULL;
197 }
198
199 static int dfu_start_entity(struct tfm_context *ctx, int idx, unsigned long size)
200 {
201         char **info = dfu_info[idx];
202         char *file;
203         int fd;
204
205         switch (*info[DFU_INFO_MODE]) {
206         case DFU_INFO_MODE_PARTITION:
207                 file = strdup(info[DFU_INFO_PARTITION]);
208                 if (!file)
209                         return -ENOMEM;
210                 break;
211         case DFU_INFO_MODE_FILE:
212         {
213                 int path_prefix = strlen(DFU_MOUNT_PATH);
214                 int path_suffix = strlen(info[DFU_INFO_PATH]);
215                 int path_name = strlen(info[DFU_INFO_NAME]);
216                 char fstype;
217
218                 fstype = get_partition_fstype(info[DFU_INFO_PARTITION]);
219                 if (!fstype) {
220                         fprintf(stderr, "failed to get partition filesystem type: %s", info[DFU_INFO_PARTITION]);
221                         return -EINVAL;
222                 }
223
224                 mount_dev(info[DFU_INFO_PARTITION], fstype);
225                 free(fstype);
226
227                 file = malloc(path_prefix + path_suffix + path_name + 1);
228                 if (!file)
229                         return -ENOMEM;
230
231                 strncpy(file, DFU_MOUNT_PATH, path_prefix + 1);
232                 strncat(file, info[DFU_INFO_PATH], path_suffix);
233                 strncat(file, info[DFU_INFO_NAME], path_name);
234                 break;
235         }
236         default:
237                 fprintf(stderr, "flash entry '%s' has wrong mode\n", info[DFU_INFO_NAME]);
238                 return -EINVAL;
239         }
240
241         fd = open(file, O_WRONLY);
242         if (fd < 0) {
243                 fprintf(stderr, "cannot open target: %s\n", info[DFU_INFO_NAME]);
244                 free(file);
245                 return -EIO;
246         }
247
248         ctx->dfu_fd = fd;
249
250         ctx->dfu_info = info;
251         ctx->transfer_done = 0;
252
253         free(file);
254
255         return 0;
256 }
257
258 int dfu_start(struct tfm_context *ctx, const char *entity)
259 {
260         unsigned long size = ctx->thor_file_size;
261         int idx, ret;
262
263         idx = find_match(entity);
264         if (idx < 0) {
265                 fprintf(stderr, "Cannot find dfu info for %s\n", entity);
266                 return -EINVAL;
267         }
268
269         ret = dfu_start_entity(ctx, idx, size);
270         if (ret < 0) {
271                 fprintf(stderr, "Cannot start download: %s\n", entity);
272                 return -EINVAL;
273         }
274
275         fprintf(stdout, "Start download: %s...", entity);
276
277         return 0;
278 }
279
280 static void dfu_thread_cleanup(void *ptr)
281 {
282         struct tfm_context *ctx = ptr;
283         struct dfu_frame *frame;
284
285         while (!TAILQ_EMPTY(&ctx->dfu_ioq_head)) {
286                 frame = TAILQ_FIRST(&ctx->dfu_ioq_head);
287
288                 TAILQ_REMOVE(&ctx->dfu_ioq_head, frame, entry);
289
290                 dfu_put_buffer(frame->buf);
291                 free(frame);
292         }
293 }
294
295 static void *dfu_thread_main(void *ptr)
296 {
297         struct tfm_context *ctx = ptr;
298         struct dfu_frame *frame;
299         int state = DFU_THREAD_STATE_IDLE;
300         uint64_t progress = 0;
301         int ret;
302
303         pthread_cleanup_push(dfu_thread_cleanup, ptr);
304
305         while (state != DFU_THREAD_STATE_ERROR) {
306                 pthread_mutex_lock(&ctx->dfu_mutex);
307
308                 while (TAILQ_EMPTY(&ctx->dfu_ioq_head)) {
309                         pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
310                         pthread_cond_wait(&ctx->dfu_data_arrive, &ctx->dfu_mutex);
311                 }
312
313                 if (state == DFU_THREAD_STATE_IDLE) {
314                         pthread_mutex_unlock(&ctx->dfu_mutex);
315                         state = DFU_THREAD_STATE_FLASHING;
316                         pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
317                         pthread_mutex_lock(&ctx->dfu_mutex);
318                 }
319
320                 frame = TAILQ_FIRST(&ctx->dfu_ioq_head);
321
322                 TAILQ_REMOVE(&ctx->dfu_ioq_head, frame, entry);
323
324                 pthread_mutex_unlock(&ctx->dfu_mutex);
325
326                 ret = write(ctx->dfu_fd, frame->buf, frame->len);
327
328                 if (ret < frame->len) {
329                         fprintf(stdout, "Error occurs while flashing\n");
330                         state = DFU_THREAD_STATE_ERROR;
331                 }
332
333                 progress += frame->len;
334
335                 dfu_put_buffer(frame->buf);
336                 free(frame);
337
338                 /* transfer finished */
339                 if (state != DFU_THREAD_STATE_ERROR && progress >= ctx->thor_file_size) {
340                         progress = 0;
341                         ctx->transfer_done = 1;
342
343                         state = DFU_THREAD_STATE_IDLE;
344                         pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
345
346                         pthread_cond_signal(&ctx->dfu_write_done);
347
348                         pthread_mutex_lock(&ctx->dfu_sync_mutex);
349                         pthread_cond_wait(&ctx->dfu_sync_done,
350                                         &ctx->dfu_sync_mutex);
351                         pthread_mutex_unlock(&ctx->dfu_sync_mutex);
352                 }
353         }
354
355         pthread_cleanup_pop(1);
356
357         return NULL;
358 }
359
360 static int parse_dfu_info(char *buf, char **info)
361 {
362         char *ptr;
363         int i;
364
365         for (i = 0; i < DFU_INFO_MAX; i++) {
366                 ptr = strsep(&buf, DFU_DELIMITER);
367                 if (!ptr)
368                         return -EINVAL;
369
370                 info[i] = strdup(ptr);
371                 if (!info[i])
372                         return -ENOMEM;
373         }
374
375         return 0;
376 }
377
378 static void destroy_dfu_info(void)
379 {
380         int i, j;
381
382         for (i = 0; i < DFU_INFO_NUM; i++) {
383                 for (j = 0; j < DFU_INFO_MAX; j++)
384                         if (dfu_info[i][j]) {
385                                 free(dfu_info[i][j]);
386                                 dfu_info[i][j] = NULL;
387                         }
388         }
389 }
390
391
392 static int init_dfu_info(const char *dfu_info_file)
393 {
394         FILE *fp;
395         char buf[1024];
396         int i = 0;
397         int ret;
398
399         fp = fopen(dfu_info_file, "r");
400         if (!fp)
401                 return -ENOENT;
402
403         while (i < DFU_INFO_NUM && !feof(fp)) {
404                 if (fgets(buf, 1024, fp) == NULL)
405                         break;
406
407                 ret = parse_dfu_info(buf, dfu_info[i++]);
408                 if (ret < 0) {
409                         fprintf(stderr, "cannot parse dfu info");
410                         goto err_free_all;
411                 }
412         }
413
414         fclose(fp);
415
416         return 0;
417
418 err_free_all:
419         fclose(fp);
420         destroy_dfu_info();
421
422         return ret;
423 }
424
425 int dfu_init(struct tfm_context *ctx, const char *dfu_info_file)
426 {
427         int ret;
428
429         ret = init_dfu_info(dfu_info_file);
430         if (ret < 0) {
431                 fprintf(stderr, "failed to get flash entries\n");
432                 return ret;
433         }
434
435         TAILQ_INIT(&ctx->dfu_ioq_head);
436
437         pthread_mutex_init(&ctx->dfu_mutex, NULL);
438         pthread_mutex_init(&ctx->dfu_sync_mutex, NULL);
439         pthread_cond_init(&ctx->dfu_data_arrive, NULL);
440         pthread_cond_init(&ctx->dfu_write_done, NULL);
441         pthread_cond_init(&ctx->dfu_sync_done, NULL);
442
443         ret = pthread_create(&ctx->dfu_thread, NULL, dfu_thread_main, ctx);
444         if (ret < 0) {
445                 fprintf(stderr, "failed to create thread for dfu\n");
446                 return ret;
447         }
448
449         return 0;
450 }
451
452 void dfu_exit(struct tfm_context *ctx)
453 {
454         pthread_cancel(ctx->dfu_thread);
455         pthread_join(ctx->dfu_thread, NULL);
456         destroy_dfu_info();
457         if (ctx->connect) {
458                 free(ctx->connect);
459                 ctx->connect = NULL;
460         }
461 }