1 /*
2  * Copyright (c) 2024 LK Trusty Authors. All Rights Reserved.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files
6  * (the "Software"), to deal in the Software without restriction,
7  * including without limitation the rights to use, copy, modify, merge,
8  * publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 
24 #include <lib/device_tree/libfdt_helpers.h>
25 #include "err.h"
26 #include "lk/types.h"
27 
28 /**
29  * fdt_helper_read_cells32 - Read and convert cells.
30  * @fdt_prop:       Pointer to property to read from.
31  * @fdt_prop_len:   Length of @fdt_prop in bytes (not count of 32 bit values).
32  * @row_index:      Row to read from.
33  * @before_cells:   Number of cells to ignore in each row before the cells we
34  *                  read and convert.
35  * @cells:          Number of cells to read and convert to cpu endian.
36  * @after_cells:    Number of cells to ignore in each row after the cell we read
37  *                  and convert.
38  * @valp:           Pointer to store converted value in.
39  *
40  * Return:
41  * * %0:                Success.
42  * * %ERR_OUT_OF_RANGE: @fdt_prop_len is too small for the requested read.
43  * * %ERR_TOO_BIG:      value does not fit in uint64_t (only possible if
44  *                      cells > 2).
45  */
fdt_helper_read_cells32(const fdt32_t * fdt_prop,int fdt_prop_len,int row_index,int before_cells,int cells,int after_cells,uint64_t * valp)46 status_t fdt_helper_read_cells32(const fdt32_t* fdt_prop,
47                                  int fdt_prop_len,
48                                  int row_index,
49                                  int before_cells,
50                                  int cells,
51                                  int after_cells,
52                                  uint64_t* valp) {
53     uint64_t ret = 0;
54     int cell_index =
55             row_index * (before_cells + cells + after_cells) + before_cells;
56     if (cell_index + cells > (fdt_prop_len / (int)sizeof(fdt32_t))) {
57         return ERR_OUT_OF_RANGE;
58     }
59     while (cells-- > 0) {
60         if (ret > UINT32_MAX) {
61             return ERR_TOO_BIG;
62         }
63         ret = ret << 32 | fdt32_to_cpu(fdt_prop[cell_index++]);
64     }
65     *valp = ret;
66     return 0;
67 }
68 
69 /**
70  * fdt_helper_get_reg - Get address and size from reg property of a node.
71  * @fdt:            Pointer to device tree.
72  * @nodeoffset:     Node to use.
73  * @reg_index:      Row to read from.
74  *
75  * Return:
76  * * %0:                Success.
77  * * %ERR_NOT_FOUND:    Node does not have a reg property.
78  * * %ERR_OUT_OF_RANGE: reg property is too small for the requested read.
79  * * %ERR_TOO_BIG:      reg value does not fit in paddr_t or size_t.
80  */
fdt_helper_get_reg(const void * fdt,int nodeoffset,int reg_index,paddr_t * addrp,size_t * sizep)81 status_t fdt_helper_get_reg(const void* fdt,
82                             int nodeoffset,
83                             int reg_index,
84                             paddr_t* addrp,
85                             size_t* sizep) {
86     status_t ret = 0;
87     int parent_offset = fdt_parent_offset(fdt, nodeoffset);
88     int address_cells = fdt_address_cells(fdt, parent_offset);
89     int size_cells = fdt_size_cells(fdt, parent_offset);
90     int reg_prop_len;
91     const fdt32_t* reg_prop =
92             fdt_getprop(fdt, nodeoffset, "reg", &reg_prop_len);
93     if (!reg_prop) {
94         return ERR_NOT_FOUND;
95     }
96     if (addrp) {
97         uint64_t addr64;
98         ret = fdt_helper_read_cells32(reg_prop, reg_prop_len, reg_index, 0,
99                                       address_cells, size_cells, &addr64);
100         if (ret) {
101             return ret;
102         }
103         if (addr64 > PADDR_MAX) {
104             return ERR_TOO_BIG;
105         }
106         *addrp = addr64;
107     }
108     if (sizep) {
109         uint64_t size64;
110         ret = fdt_helper_read_cells32(reg_prop, reg_prop_len, reg_index,
111                                       address_cells, size_cells, 0, &size64);
112         if (ret) {
113             return ret;
114         }
115         if (size64 > SIZE_MAX) {
116             return ERR_TOO_BIG;
117         }
118         *sizep = size64;
119     }
120     return 0;
121 }
122