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", ®_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