1 /*
2  * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
3  * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without modification,
6  * are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice, this list of
9  *    conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice, this list
12  *    of conditions and the following disclaimer in the documentation and/or other materials
13  *    provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its contributors may be used
16  *    to endorse or promote products derived from this software without specific prior written
17  *    permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include "mtd_partition.h"
33 #include "stdlib.h"
34 #include "stdio.h"
35 #include "pthread.h"
36 #include "mtd_list.h"
37 #include "los_config.h"
38 #include "los_mux.h"
39 #include "fs/driver.h"
40 #include "mtd/mtd_legacy_lite.h"
41 
42 #define DRIVER_NAME_ADD_SIZE    3
43 pthread_mutex_t g_mtdPartitionLock = PTHREAD_MUTEX_INITIALIZER;
44 
45 static VOID YaffsLockInit(VOID) __attribute__((weakref("yaffsfs_OSInitialisation")));
46 static VOID YaffsLockDeinit(VOID) __attribute__((weakref("yaffsfs_OsDestroy")));
47 static INT32 Jffs2LockInit(VOID) __attribute__((weakref("Jffs2MutexCreate")));
48 static VOID Jffs2LockDeinit(VOID) __attribute__((weakref("Jffs2MutexDelete")));
49 
50 partition_param *g_nandPartParam = NULL;
51 partition_param *g_spinorPartParam = NULL;
52 mtd_partition *g_spinorPartitionHead = NULL;
53 mtd_partition *g_nandPartitionHead = NULL;
54 
55 #define RWE_RW_RW 0755
56 
GetNandPartParamnull57 partition_param *GetNandPartParam(VOID)
58 {
59     return g_nandPartParam;
60 }
61 
GetSpinorPartParamnull62 partition_param *GetSpinorPartParam(VOID)
63 {
64     return g_spinorPartParam;
65 }
66 
GetSpinorPartitionHeadnull67 mtd_partition *GetSpinorPartitionHead(VOID)
68 {
69     return g_spinorPartitionHead;
70 }
71 
72 
MtdNandParamAssign(partition_param *nandParam, const struct MtdDev *nandMtd)73 static VOID MtdNandParamAssign(partition_param *nandParam, const struct MtdDev *nandMtd)
74 {
75     LOS_ListInit(&g_nandPartitionHead->node_info);
76     /*
77      * If the user do not want to use block mtd or char mtd ,
78      * you can change the NANDBLK_NAME or NANDCHR_NAME to NULL.
79      */
80     nandParam->flash_mtd = (struct MtdDev *)nandMtd;
81     nandParam->flash_ops = GetDevNandOps();
82     nandParam->char_ops = GetMtdCharFops();
83     nandParam->blockname = NANDBLK_NAME;
84     nandParam->charname = NANDCHR_NAME;
85     nandParam->partition_head = g_nandPartitionHead;
86     nandParam->block_size = nandMtd->eraseSize;
87 }
88 
MtdDeinitNandParamnull89 static VOID MtdDeinitNandParam(VOID)
90 {
91     if (YaffsLockDeinit != NULL) {
92         YaffsLockDeinit();
93     }
94 }
95 
MtdInitNandParam(partition_param *nandParam)96 static partition_param *MtdInitNandParam(partition_param *nandParam)
97 {
98     struct MtdDev *nandMtd = GetMtd("nand");
99     if (nandMtd == NULL) {
100         return NULL;
101     }
102     if (nandParam == NULL) {
103         if (YaffsLockInit != NULL) {
104             YaffsLockInit();
105         }
106         nandParam = (partition_param *)zalloc(sizeof(partition_param));
107         if (nandParam == NULL) {
108             MtdDeinitNandParam();
109             return NULL;
110         }
111         g_nandPartitionHead = (mtd_partition *)zalloc(sizeof(mtd_partition));
112         if (g_nandPartitionHead == NULL) {
113             MtdDeinitNandParam();
114             free(nandParam);
115             return NULL;
116         }
117 
118         MtdNandParamAssign(nandParam, nandMtd);
119     }
120 
121     return nandParam;
122 }
123 
MtdNorParamAssign(partition_param *spinorParam, const struct MtdDev *spinorMtd)124 static VOID MtdNorParamAssign(partition_param *spinorParam, const struct MtdDev *spinorMtd)
125 {
126     LOS_ListInit(&g_spinorPartitionHead->node_info);
127     /*
128      * If the user do not want to use block mtd or char mtd ,
129      * you can change the SPIBLK_NAME or SPICHR_NAME to NULL.
130      */
131     spinorParam->flash_mtd = (struct MtdDev *)spinorMtd;
132     spinorParam->flash_ops = GetDevSpinorOps();
133     spinorParam->char_ops = GetMtdCharFops();
134     spinorParam->blockname = SPIBLK_NAME;
135     spinorParam->charname = SPICHR_NAME;
136     spinorParam->partition_head = g_spinorPartitionHead;
137     spinorParam->block_size = spinorMtd->eraseSize;
138 }
139 
MtdDeinitSpinorParamnull140 static VOID MtdDeinitSpinorParam(VOID)
141 {
142     if (Jffs2LockDeinit != NULL) {
143         Jffs2LockDeinit();
144     }
145 }
146 
MtdInitSpinorParam(partition_param *spinorParam)147 static partition_param *MtdInitSpinorParam(partition_param *spinorParam)
148 {
149     struct MtdDev *spinorMtd = GetMtd("spinor");
150     if (spinorMtd == NULL) {
151         return NULL;
152     }
153     if (spinorParam == NULL) {
154         if (Jffs2LockInit != NULL) {
155             if (Jffs2LockInit() != 0) { /* create jffs2 lock failed */
156                 return NULL;
157             }
158         }
159         spinorParam = (partition_param *)zalloc(sizeof(partition_param));
160         if (spinorParam == NULL) {
161             PRINT_ERR("%s, partition_param malloc failed\n", __FUNCTION__);
162             MtdDeinitSpinorParam();
163             return NULL;
164         }
165         g_spinorPartitionHead = (mtd_partition *)zalloc(sizeof(mtd_partition));
166         if (g_spinorPartitionHead == NULL) {
167             PRINT_ERR("%s, mtd_partition malloc failed\n", __FUNCTION__);
168             MtdDeinitSpinorParam();
169             free(spinorParam);
170             return NULL;
171         }
172 
173         MtdNorParamAssign(spinorParam, spinorMtd);
174     }
175 
176     return spinorParam;
177 }
178 
179 /* According the flash-type to init the param of the partition. */
MtdInitFsparParam(const CHAR *type, partition_param **fsparParam)180 static INT32 MtdInitFsparParam(const CHAR *type, partition_param **fsparParam)
181 {
182     if (strcmp(type, "nand") == 0) {
183         g_nandPartParam = MtdInitNandParam(g_nandPartParam);
184         *fsparParam = g_nandPartParam;
185     } else if (strcmp(type, "spinor") == 0 || strcmp(type, "cfi-flash") == 0) {
186         g_spinorPartParam = MtdInitSpinorParam(g_spinorPartParam);
187         *fsparParam = g_spinorPartParam;
188     } else {
189         return -EINVAL;
190     }
191 
192     if ((*fsparParam == NULL) || ((VOID *)((*fsparParam)->flash_mtd) == NULL)) {
193         return -ENODEV;
194     }
195 
196     return ENOERR;
197 }
198 
199 /* According the flash-type to deinit the param of the partition. */
MtdDeinitFsparParam(const CHAR *type)200 static INT32 MtdDeinitFsparParam(const CHAR *type)
201 {
202     if (strcmp(type, "nand") == 0) {
203         MtdDeinitNandParam();
204         g_nandPartParam = NULL;
205     } else if (strcmp(type, "spinor") == 0 || strcmp(type, "cfi-flash") == 0) {
206         MtdDeinitSpinorParam();
207         g_spinorPartParam = NULL;
208     } else {
209         return -EINVAL;
210     }
211 
212     return ENOERR;
213 }
214 
AddParamCheck(UINT32 startAddr, const partition_param *param, UINT32 partitionNum, UINT32 length)215 static INT32 AddParamCheck(UINT32 startAddr,
216                            const partition_param *param,
217                            UINT32 partitionNum,
218                            UINT32 length)
219 {
220     UINT32 startBlk, endBlk;
221     mtd_partition *node = NULL;
222     if ((param->blockname == NULL) && (param->charname == NULL)) {
223         return -EINVAL;
224     }
225 
226     if ((length == 0) || (length < param->block_size) ||
227         (((UINT64)(startAddr) + length) > param->flash_mtd->size)) {
228         return -EINVAL;
229     }
230 
231     ALIGN_ASSIGN(length, startAddr, startBlk, endBlk, param->block_size);
232 
233     if (startBlk > endBlk) {
234         return -EINVAL;
235     }
236     LOS_DL_LIST_FOR_EACH_ENTRY(node, &param->partition_head->node_info, mtd_partition, node_info) {
237         if ((node->start_block != 0) && (node->patitionnum == partitionNum)) {
238             return -EINVAL;
239         }
240         if ((startBlk > node->end_block) || (endBlk < node->start_block)) {
241             continue;
242         }
243         return -EINVAL;
244     }
245 
246     return ENOERR;
247 }
248 
BlockDriverRegisterOperate(mtd_partition *newNode, const partition_param *param, UINT32 partitionNum)249 static INT32 BlockDriverRegisterOperate(mtd_partition *newNode,
250                                         const partition_param *param,
251                                         UINT32 partitionNum)
252 {
253     INT32 ret;
254     size_t driverNameSize;
255 
256     if (param->blockname != NULL) {
257         driverNameSize = strlen(param->blockname) + DRIVER_NAME_ADD_SIZE;
258         newNode->blockdriver_name = (CHAR *)malloc(driverNameSize);
259         if (newNode->blockdriver_name == NULL) {
260             return -ENOMEM;
261         }
262 
263         ret = snprintf_s(newNode->blockdriver_name, driverNameSize,
264             driverNameSize - 1, "%s%u", param->blockname, partitionNum);
265         if (ret < 0) {
266             free(newNode->blockdriver_name);
267             newNode->blockdriver_name = NULL;
268             return -ENAMETOOLONG;
269         }
270 
271         ret = register_blockdriver(newNode->blockdriver_name, param->flash_ops,
272             RWE_RW_RW, newNode);
273         if (ret) {
274             free(newNode->blockdriver_name);
275             newNode->blockdriver_name = NULL;
276             PRINT_ERR("register blkdev partition error\n");
277             return ret;
278         }
279     } else {
280         newNode->blockdriver_name = NULL;
281     }
282     return ENOERR;
283 }
284 
CharDriverRegisterOperate(mtd_partition *newNode, const partition_param *param, UINT32 partitionNum)285 static INT32 CharDriverRegisterOperate(mtd_partition *newNode,
286                                        const partition_param *param,
287                                        UINT32 partitionNum)
288 {
289     INT32 ret;
290     size_t driverNameSize;
291 
292     if (param->charname != NULL) {
293         driverNameSize = strlen(param->charname) + DRIVER_NAME_ADD_SIZE;
294         newNode->chardriver_name = (CHAR *)malloc(driverNameSize);
295         if (newNode->chardriver_name == NULL) {
296             return -ENOMEM;
297         }
298 
299         ret = snprintf_s(newNode->chardriver_name, driverNameSize,
300             driverNameSize - 1, "%s%u", param->charname, partitionNum);
301         if (ret < 0) {
302             free(newNode->chardriver_name);
303             newNode->chardriver_name = NULL;
304             return -ENAMETOOLONG;
305         }
306 
307         ret = register_driver(newNode->chardriver_name, param->char_ops, RWE_RW_RW, newNode);
308         if (ret) {
309             PRINT_ERR("register chardev partition error\n");
310             free(newNode->chardriver_name);
311             newNode->chardriver_name = NULL;
312             return ret;
313         }
314     } else {
315         newNode->chardriver_name = NULL;
316     }
317     return ENOERR;
318 }
319 
BlockDriverUnregister(mtd_partition *node)320 static INT32 BlockDriverUnregister(mtd_partition *node)
321 {
322     INT32 ret;
323 
324     if (node->blockdriver_name != NULL) {
325         ret = unregister_blockdriver(node->blockdriver_name);
326         if (ret == -EBUSY) {
327             PRINT_ERR("unregister blkdev partition error:%d\n", ret);
328             return ret;
329         }
330         free(node->blockdriver_name);
331         node->blockdriver_name = NULL;
332     }
333     return ENOERR;
334 }
335 
CharDriverUnregister(mtd_partition *node)336 static INT32 CharDriverUnregister(mtd_partition *node)
337 {
338     INT32 ret;
339 
340     if (node->chardriver_name != NULL) {
341         ret = unregister_driver(node->chardriver_name);
342         if (ret == -EBUSY) {
343             PRINT_ERR("unregister chardev partition error:%d\n", ret);
344             return ret;
345         }
346         free(node->chardriver_name);
347         node->chardriver_name = NULL;
348     }
349 
350     return ENOERR;
351 }
352 
353 /*
354  * Attention: both startAddr and length should be aligned with block size.
355  * If not, the actual start address and length won't be what you expected.
356  */
add_mtd_partition(const CHAR *type, UINT32 startAddr, UINT32 length, UINT32 partitionNum)357 INT32 add_mtd_partition(const CHAR *type, UINT32 startAddr,
358                         UINT32 length, UINT32 partitionNum)
359 {
360     INT32 ret;
361     mtd_partition *newNode = NULL;
362     partition_param *param = NULL;
363 
364     if ((partitionNum >= CONFIG_MTD_PATTITION_NUM) || (type == NULL)) {
365         return -EINVAL;
366     }
367 
368     ret = pthread_mutex_lock(&g_mtdPartitionLock);
369     if (ret != ENOERR) {
370         PRINT_ERR("%s %d, mutex lock failed, error:%d\n", __FUNCTION__, __LINE__, ret);
371     }
372 
373     ret = MtdInitFsparParam(type, &param);
374     if (ret != ENOERR) {
375         goto ERROR_OUT;
376     }
377 
378     ret = AddParamCheck(startAddr, param, partitionNum, length);
379     if (ret != ENOERR) {
380         goto ERROR_OUT;
381     }
382 
383     newNode = (mtd_partition *)zalloc(sizeof(mtd_partition));
384     if (newNode == NULL) {
385         (VOID)pthread_mutex_unlock(&g_mtdPartitionLock);
386         return -ENOMEM;
387     }
388 
389     PAR_ASSIGNMENT(newNode, length, startAddr, partitionNum, param->flash_mtd, param->block_size);
390 
391     ret = BlockDriverRegisterOperate(newNode, param, partitionNum);
392     if (ret) {
393         goto ERROR_OUT1;
394     }
395 
396     ret = CharDriverRegisterOperate(newNode, param, partitionNum);
397     if (ret) {
398         goto ERROR_OUT2;
399     }
400 
401     LOS_ListTailInsert(&param->partition_head->node_info, &newNode->node_info);
402     (VOID)LOS_MuxInit(&newNode->lock, NULL);
403 
404     ret = pthread_mutex_unlock(&g_mtdPartitionLock);
405     if (ret != ENOERR) {
406         PRINT_ERR("%s %d, mutex unlock failed, error:%d\n", __FUNCTION__, __LINE__, ret);
407     }
408 
409     return ENOERR;
410 ERROR_OUT2:
411     (VOID)BlockDriverUnregister(newNode);
412 ERROR_OUT1:
413     free(newNode);
414 ERROR_OUT:
415     (VOID)pthread_mutex_unlock(&g_mtdPartitionLock);
416     return ret;
417 }
418 
DeleteParamCheck(UINT32 partitionNum, const CHAR *type, partition_param **param)419 static INT32 DeleteParamCheck(UINT32 partitionNum,
420                               const CHAR *type,
421                               partition_param **param)
422 {
423     if (strcmp(type, "nand") == 0) {
424         *param = g_nandPartParam;
425     } else if (strcmp(type, "spinor") == 0 || strcmp(type, "cfi-flash") == 0) {
426         *param = g_spinorPartParam;
427     } else {
428         PRINT_ERR("type error \n");
429         return -EINVAL;
430     }
431 
432     if ((partitionNum >= CONFIG_MTD_PATTITION_NUM) ||
433         ((*param) == NULL) || ((*param)->flash_mtd == NULL)) {
434         return -EINVAL;
435     }
436     return ENOERR;
437 }
438 
DeletePartitionUnregister(mtd_partition *node)439 static INT32 DeletePartitionUnregister(mtd_partition *node)
440 {
441     INT32 ret;
442 
443     ret = BlockDriverUnregister(node);
444     if (ret == -EBUSY) {
445         return ret;
446     }
447 
448     ret = CharDriverUnregister(node);
449     if (ret == -EBUSY) {
450         return ret;
451     }
452 
453     return ENOERR;
454 }
455 
OsNodeGet(mtd_partition **node, UINT32 partitionNum, const partition_param *param)456 static INT32 OsNodeGet(mtd_partition **node, UINT32 partitionNum, const partition_param *param)
457 {
458     LOS_DL_LIST_FOR_EACH_ENTRY(*node, &param->partition_head->node_info, mtd_partition, node_info) {
459         if ((*node)->patitionnum == partitionNum) {
460             break;
461         }
462     }
463     if ((*node == NULL) || ((*node)->patitionnum != partitionNum) ||
464         ((*node)->mountpoint_name != NULL)) {
465         return -EINVAL;
466     }
467 
468     return ENOERR;
469 }
470 
OsResourceRelease(mtd_partition *node, const CHAR *type, partition_param *param)471 static INT32 OsResourceRelease(mtd_partition *node, const CHAR *type, partition_param *param)
472 {
473     (VOID)LOS_MuxDestroy(&node->lock);
474     LOS_ListDelete(&node->node_info);
475     (VOID)memset_s(node, sizeof(mtd_partition), 0, sizeof(mtd_partition));
476     free(node);
477     (VOID)FreeMtd(param->flash_mtd);
478     if (LOS_ListEmpty(&param->partition_head->node_info)) {
479         free(param->partition_head);
480         param->partition_head = NULL;
481         free(param);
482 
483         if (MtdDeinitFsparParam(type) != ENOERR) {
484             return -EINVAL;
485         }
486     }
487     return ENOERR;
488 }
489 
delete_mtd_partition(UINT32 partitionNum, const CHAR *type)490 INT32 delete_mtd_partition(UINT32 partitionNum, const CHAR *type)
491 {
492     INT32 ret;
493     mtd_partition *node = NULL;
494     partition_param *param = NULL;
495 
496     if (type == NULL) {
497         return -EINVAL;
498     }
499 
500     ret = pthread_mutex_lock(&g_mtdPartitionLock);
501     if (ret != ENOERR) {
502         PRINT_ERR("%s %d, mutex lock failed, error:%d\n", __FUNCTION__, __LINE__, ret);
503     }
504 
505     ret = DeleteParamCheck(partitionNum, type, &param);
506     if (ret) {
507         PRINT_ERR("delete_mtd_partition param invalid\n");
508         (VOID)pthread_mutex_unlock(&g_mtdPartitionLock);
509         return ret;
510     }
511 
512     ret = OsNodeGet(&node, partitionNum, param);
513     if (ret) {
514         (VOID)pthread_mutex_unlock(&g_mtdPartitionLock);
515         return ret;
516     }
517 
518     ret = DeletePartitionUnregister(node);
519     if (ret) {
520         PRINT_ERR("DeletePartitionUnregister error:%d\n", ret);
521         (VOID)pthread_mutex_unlock(&g_mtdPartitionLock);
522         return ret;
523     }
524 
525     ret = OsResourceRelease(node, type, param);
526     if (ret) {
527         PRINT_ERR("DeletePartitionUnregister error:%d\n", ret);
528         (VOID)pthread_mutex_unlock(&g_mtdPartitionLock);
529         return ret;
530     }
531 
532     ret = pthread_mutex_unlock(&g_mtdPartitionLock);
533     if (ret != ENOERR) {
534         PRINT_ERR("%s %d, mutex unlock failed, error:%d\n", __FUNCTION__, __LINE__, ret);
535     }
536     return ENOERR;
537 }
538 
539