1*9f31ef5aSJens Wiklander // SPDX-License-Identifier: BSD-2-Clause 2*9f31ef5aSJens Wiklander /* 3*9f31ef5aSJens Wiklander * Copyright (c) 2019, Linaro Limited 4*9f31ef5aSJens Wiklander */ 5*9f31ef5aSJens Wiklander 6*9f31ef5aSJens Wiklander #include <kernel/panic.h> 7*9f31ef5aSJens Wiklander #include <kernel/refcount.h> 8*9f31ef5aSJens Wiklander #include <mm/file.h> 9*9f31ef5aSJens Wiklander #include <mm/fobj.h> 10*9f31ef5aSJens Wiklander #include <stdlib.h> 11*9f31ef5aSJens Wiklander #include <string.h> 12*9f31ef5aSJens Wiklander #include <sys/queue.h> 13*9f31ef5aSJens Wiklander #include <types_ext.h> 14*9f31ef5aSJens Wiklander #include <util.h> 15*9f31ef5aSJens Wiklander 16*9f31ef5aSJens Wiklander /* 17*9f31ef5aSJens Wiklander * struct file - file resources 18*9f31ef5aSJens Wiklander * @tag: Tag or hash uniquely identifying a file 19*9f31ef5aSJens Wiklander * @taglen: Byte length of @tag 20*9f31ef5aSJens Wiklander * @refc: Reference counter 21*9f31ef5aSJens Wiklander * @link: Linked list element 22*9f31ef5aSJens Wiklander * @num_slices: Number of elements in the @slices array below 23*9f31ef5aSJens Wiklander * @slices: Array of file slices holding the fobjs of this file 24*9f31ef5aSJens Wiklander * 25*9f31ef5aSJens Wiklander * A file is constructed of slices which may be shared in different 26*9f31ef5aSJens Wiklander * mappings/contexts. There may be holes in the file for ranges of the file 27*9f31ef5aSJens Wiklander * that can't be shared. 28*9f31ef5aSJens Wiklander */ 29*9f31ef5aSJens Wiklander struct file { 30*9f31ef5aSJens Wiklander uint8_t tag[FILE_TAG_SIZE]; 31*9f31ef5aSJens Wiklander unsigned int taglen; 32*9f31ef5aSJens Wiklander struct refcount refc; 33*9f31ef5aSJens Wiklander TAILQ_ENTRY(file) link; 34*9f31ef5aSJens Wiklander unsigned int num_slices; 35*9f31ef5aSJens Wiklander struct file_slice slices[]; 36*9f31ef5aSJens Wiklander }; 37*9f31ef5aSJens Wiklander 38*9f31ef5aSJens Wiklander static struct mutex file_mu = MUTEX_INITIALIZER; 39*9f31ef5aSJens Wiklander static TAILQ_HEAD(, file) file_head = TAILQ_HEAD_INITIALIZER(file_head); 40*9f31ef5aSJens Wiklander 41*9f31ef5aSJens Wiklander static int file_tag_cmp(const struct file *f, const uint8_t *tag, 42*9f31ef5aSJens Wiklander unsigned int taglen) 43*9f31ef5aSJens Wiklander { 44*9f31ef5aSJens Wiklander if (f->taglen != taglen) 45*9f31ef5aSJens Wiklander return -1; 46*9f31ef5aSJens Wiklander return memcmp(tag, f->tag, taglen); 47*9f31ef5aSJens Wiklander } 48*9f31ef5aSJens Wiklander 49*9f31ef5aSJens Wiklander static struct file *file_find_tag_unlocked(const uint8_t *tag, 50*9f31ef5aSJens Wiklander unsigned int taglen) 51*9f31ef5aSJens Wiklander { 52*9f31ef5aSJens Wiklander struct file *f = NULL; 53*9f31ef5aSJens Wiklander 54*9f31ef5aSJens Wiklander TAILQ_FOREACH(f, &file_head, link) 55*9f31ef5aSJens Wiklander if (!file_tag_cmp(f, tag, taglen)) 56*9f31ef5aSJens Wiklander return f; 57*9f31ef5aSJens Wiklander 58*9f31ef5aSJens Wiklander return NULL; 59*9f31ef5aSJens Wiklander } 60*9f31ef5aSJens Wiklander 61*9f31ef5aSJens Wiklander static void file_free(struct file *f) 62*9f31ef5aSJens Wiklander { 63*9f31ef5aSJens Wiklander size_t n = 0; 64*9f31ef5aSJens Wiklander 65*9f31ef5aSJens Wiklander for (n = 0; n < f->num_slices; n++) 66*9f31ef5aSJens Wiklander fobj_put(f->slices[n].fobj); 67*9f31ef5aSJens Wiklander 68*9f31ef5aSJens Wiklander free(f); 69*9f31ef5aSJens Wiklander } 70*9f31ef5aSJens Wiklander 71*9f31ef5aSJens Wiklander struct file *file_new(uint8_t *tag, unsigned int taglen, 72*9f31ef5aSJens Wiklander struct file_slice *slices, unsigned int num_slices) 73*9f31ef5aSJens Wiklander { 74*9f31ef5aSJens Wiklander unsigned int s = 0; 75*9f31ef5aSJens Wiklander unsigned int n = 0; 76*9f31ef5aSJens Wiklander struct file *f = NULL; 77*9f31ef5aSJens Wiklander bool did_insert = false; 78*9f31ef5aSJens Wiklander 79*9f31ef5aSJens Wiklander if (taglen > sizeof(f->tag)) 80*9f31ef5aSJens Wiklander return NULL; 81*9f31ef5aSJens Wiklander if (MUL_OVERFLOW(num_slices, sizeof(*slices), &s)) 82*9f31ef5aSJens Wiklander return NULL; 83*9f31ef5aSJens Wiklander if (ADD_OVERFLOW(s, sizeof(*f), &s)) 84*9f31ef5aSJens Wiklander return NULL; 85*9f31ef5aSJens Wiklander f = calloc(1, s); 86*9f31ef5aSJens Wiklander if (!f) 87*9f31ef5aSJens Wiklander return NULL; 88*9f31ef5aSJens Wiklander 89*9f31ef5aSJens Wiklander memcpy(f->tag, tag, taglen); 90*9f31ef5aSJens Wiklander f->taglen = taglen; 91*9f31ef5aSJens Wiklander refcount_set(&f->refc, 1); 92*9f31ef5aSJens Wiklander for (n = 0; n < num_slices; n++) { 93*9f31ef5aSJens Wiklander if (file_find_slice(f, slices[n].page_offset)) 94*9f31ef5aSJens Wiklander goto err; 95*9f31ef5aSJens Wiklander 96*9f31ef5aSJens Wiklander f->slices[n].fobj = fobj_get(slices[n].fobj); 97*9f31ef5aSJens Wiklander f->num_slices = n + 1; 98*9f31ef5aSJens Wiklander if (!f->slices[n].fobj || 99*9f31ef5aSJens Wiklander ADD_OVERFLOW(slices[n].page_offset, 100*9f31ef5aSJens Wiklander slices[n].fobj->num_pages, &s)) 101*9f31ef5aSJens Wiklander goto err; 102*9f31ef5aSJens Wiklander f->slices[n].page_offset = slices[n].page_offset; 103*9f31ef5aSJens Wiklander } 104*9f31ef5aSJens Wiklander 105*9f31ef5aSJens Wiklander mutex_lock(&file_mu); 106*9f31ef5aSJens Wiklander if (!file_find_tag_unlocked(tag, taglen)) { 107*9f31ef5aSJens Wiklander TAILQ_INSERT_TAIL(&file_head, f, link); 108*9f31ef5aSJens Wiklander did_insert = true; 109*9f31ef5aSJens Wiklander } 110*9f31ef5aSJens Wiklander mutex_unlock(&file_mu); 111*9f31ef5aSJens Wiklander 112*9f31ef5aSJens Wiklander if (did_insert) 113*9f31ef5aSJens Wiklander return f; 114*9f31ef5aSJens Wiklander err: 115*9f31ef5aSJens Wiklander file_free(f); 116*9f31ef5aSJens Wiklander 117*9f31ef5aSJens Wiklander return NULL; 118*9f31ef5aSJens Wiklander } 119*9f31ef5aSJens Wiklander 120*9f31ef5aSJens Wiklander struct file *file_get(struct file *f) 121*9f31ef5aSJens Wiklander { 122*9f31ef5aSJens Wiklander if (f && !refcount_inc(&f->refc)) 123*9f31ef5aSJens Wiklander panic(); 124*9f31ef5aSJens Wiklander 125*9f31ef5aSJens Wiklander return f; 126*9f31ef5aSJens Wiklander } 127*9f31ef5aSJens Wiklander 128*9f31ef5aSJens Wiklander struct file *file_get_by_tag(uint8_t *tag, unsigned int taglen) 129*9f31ef5aSJens Wiklander { 130*9f31ef5aSJens Wiklander struct file *f = NULL; 131*9f31ef5aSJens Wiklander 132*9f31ef5aSJens Wiklander mutex_lock(&file_mu); 133*9f31ef5aSJens Wiklander f = file_get(file_find_tag_unlocked(tag, taglen)); 134*9f31ef5aSJens Wiklander mutex_unlock(&file_mu); 135*9f31ef5aSJens Wiklander 136*9f31ef5aSJens Wiklander return f; 137*9f31ef5aSJens Wiklander } 138*9f31ef5aSJens Wiklander 139*9f31ef5aSJens Wiklander void file_put(struct file *f) 140*9f31ef5aSJens Wiklander { 141*9f31ef5aSJens Wiklander bool did_remove = false; 142*9f31ef5aSJens Wiklander 143*9f31ef5aSJens Wiklander if (!f) 144*9f31ef5aSJens Wiklander return; 145*9f31ef5aSJens Wiklander 146*9f31ef5aSJens Wiklander mutex_lock(&file_mu); 147*9f31ef5aSJens Wiklander if (refcount_dec(&f->refc)) { 148*9f31ef5aSJens Wiklander TAILQ_REMOVE(&file_head, f, link); 149*9f31ef5aSJens Wiklander did_remove = true; 150*9f31ef5aSJens Wiklander } 151*9f31ef5aSJens Wiklander mutex_unlock(&file_mu); 152*9f31ef5aSJens Wiklander 153*9f31ef5aSJens Wiklander if (did_remove) 154*9f31ef5aSJens Wiklander file_free(f); 155*9f31ef5aSJens Wiklander } 156*9f31ef5aSJens Wiklander 157*9f31ef5aSJens Wiklander struct file_slice *file_find_slice(struct file *f, unsigned int page_offset) 158*9f31ef5aSJens Wiklander { 159*9f31ef5aSJens Wiklander size_t n = 0; 160*9f31ef5aSJens Wiklander 161*9f31ef5aSJens Wiklander if (!f) 162*9f31ef5aSJens Wiklander return NULL; 163*9f31ef5aSJens Wiklander 164*9f31ef5aSJens Wiklander for (n = 0; n < f->num_slices; n++) { 165*9f31ef5aSJens Wiklander struct file_slice *fs = f->slices + n; 166*9f31ef5aSJens Wiklander 167*9f31ef5aSJens Wiklander if (page_offset >= fs->page_offset && 168*9f31ef5aSJens Wiklander page_offset < fs->page_offset + fs->fobj->num_pages) 169*9f31ef5aSJens Wiklander return fs; 170*9f31ef5aSJens Wiklander } 171*9f31ef5aSJens Wiklander 172*9f31ef5aSJens Wiklander return NULL; 173*9f31ef5aSJens Wiklander } 174