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
43pthread_mutex_t g_mtdPartitionLock = PTHREAD_MUTEX_INITIALIZER;
44
45static VOID YaffsLockInit(VOID) __attribute__((weakref("yaffsfs_OSInitialisation")));
46static VOID YaffsLockDeinit(VOID) __attribute__((weakref("yaffsfs_OsDestroy")));
47static INT32 Jffs2LockInit(VOID) __attribute__((weakref("Jffs2MutexCreate")));
48static VOID Jffs2LockDeinit(VOID) __attribute__((weakref("Jffs2MutexDelete")));
49
50partition_param *g_nandPartParam = NULL;
51partition_param *g_spinorPartParam = NULL;
52mtd_partition *g_spinorPartitionHead = NULL;
53mtd_partition *g_nandPartitionHead = NULL;
54
55#define RWE_RW_RW 0755
56
57partition_param *GetNandPartParam(VOID)
58{
59    return g_nandPartParam;
60}
61
62partition_param *GetSpinorPartParam(VOID)
63{
64    return g_spinorPartParam;
65}
66
67mtd_partition *GetSpinorPartitionHead(VOID)
68{
69    return g_spinorPartitionHead;
70}
71
72
73static 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
89static VOID MtdDeinitNandParam(VOID)
90{
91    if (YaffsLockDeinit != NULL) {
92        YaffsLockDeinit();
93    }
94}
95
96static 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
124static 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
140static VOID MtdDeinitSpinorParam(VOID)
141{
142    if (Jffs2LockDeinit != NULL) {
143        Jffs2LockDeinit();
144    }
145}
146
147static 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. */
180static 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. */
200static 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
215static 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
249static 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
285static 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
320static 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
336static 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 */
357INT32 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;
410ERROR_OUT2:
411    (VOID)BlockDriverUnregister(newNode);
412ERROR_OUT1:
413    free(newNode);
414ERROR_OUT:
415    (VOID)pthread_mutex_unlock(&g_mtdPartitionLock);
416    return ret;
417}
418
419static 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
439static 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
456static 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
471static 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
490INT32 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