]>
Commit | Line | Data |
---|---|---|
f9849036 AB |
1 | /*\r |
2 | * Copyright (c) 2014, Linaro Ltd. All rights reserved.\r | |
3 | *\r | |
4 | * This program and the accompanying materials\r | |
5 | * are licensed and made available under the terms and conditions of the BSD License\r | |
6 | * which accompanies this distribution. The full text of the license may be found at\r | |
7 | * http://opensource.org/licenses/bsd-license.php\r | |
8 | *\r | |
9 | * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r | |
10 | * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r | |
11 | */\r | |
12 | \r | |
13 | /*\r | |
14 | * Theory of operation\r | |
15 | * -------------------\r | |
16 | *\r | |
17 | * This code parses a Flattened Device Tree binary (DTB) to find the base of\r | |
18 | * system RAM. It is written in assembly so that it can be executed before a\r | |
19 | * stack has been set up.\r | |
20 | *\r | |
21 | * To find the base of system RAM, we have to traverse the FDT to find a memory\r | |
22 | * node. In the context of this implementation, the first node that has a\r | |
23 | * device_type property with the value 'memory' and a 'reg' property is\r | |
24 | * acceptable, and the name of the node (memory[@xxx]) is ignored, as are any\r | |
25 | * other nodes that match the above constraints.\r | |
26 | *\r | |
27 | * In pseudo code, this implementation does the following:\r | |
28 | *\r | |
29 | * for each node {\r | |
30 | * have_device_type = false\r | |
31 | * have_reg = false\r | |
32 | *\r | |
33 | * for each property {\r | |
34 | * if property value == 'memory' {\r | |
35 | * if property name == 'device_type' {\r | |
36 | * have_device_type = true\r | |
37 | * }\r | |
38 | * } else {\r | |
39 | * if property name == 'reg' {\r | |
40 | * have_reg = true\r | |
41 | * membase = property value[0]\r | |
42 | * memsize = property value[1]\r | |
43 | * }\r | |
44 | * }\r | |
45 | * }\r | |
46 | * if have_device_type and have_reg {\r | |
47 | * return membase and memsize\r | |
48 | * }\r | |
49 | * }\r | |
50 | * return NOT_FOUND\r | |
51 | */\r | |
52 | \r | |
53 | #define FDT_MAGIC 0xedfe0dd0\r | |
54 | \r | |
55 | #define FDT_BEGIN_NODE 0x1\r | |
56 | #define FDT_END_NODE 0x2\r | |
57 | #define FDT_PROP 0x3\r | |
58 | #define FDT_END 0x9\r | |
59 | \r | |
60 | xMEMSIZE .req x0 // recorded system RAM size\r | |
61 | xMEMBASE .req x1 // recorded system RAM base\r | |
62 | \r | |
63 | xLR .req x8 // our preserved link register\r | |
64 | xDTP .req x9 // pointer to traverse the DT structure\r | |
65 | xSTRTAB .req x10 // pointer to the DTB string table\r | |
66 | xMEMNODE .req x11 // bit field to record found properties\r | |
67 | \r | |
68 | #define HAVE_REG 0x1\r | |
69 | #define HAVE_DEVICE_TYPE 0x2\r | |
70 | \r | |
71 | .text\r | |
72 | .align 3\r | |
73 | _memory:\r | |
74 | .asciz "memory"\r | |
75 | _reg:\r | |
76 | .asciz "reg"\r | |
77 | _device_type:\r | |
78 | .asciz "device_type"\r | |
79 | \r | |
80 | /*\r | |
81 | * Compare strings in x4 and x5, return in w7\r | |
82 | */\r | |
83 | .align 3\r | |
84 | strcmp:\r | |
85 | ldrb w2, [x4], #1\r | |
86 | ldrb w3, [x5], #1\r | |
87 | subs w7, w2, w3\r | |
88 | cbz w2, 0f\r | |
89 | cbz w3, 0f\r | |
90 | beq strcmp\r | |
91 | 0: ret\r | |
92 | \r | |
93 | .globl find_memnode\r | |
94 | find_memnode:\r | |
95 | // preserve link register\r | |
96 | mov xLR, x30\r | |
97 | mov xDTP, x0\r | |
98 | \r | |
99 | /*\r | |
100 | * Check the DTB magic at offset 0\r | |
101 | */\r | |
102 | movz w4, #:abs_g0_nc:FDT_MAGIC\r | |
103 | movk w4, #:abs_g1:FDT_MAGIC\r | |
104 | ldr w5, [xDTP]\r | |
105 | cmp w4, w5\r | |
106 | bne err_invalid_magic\r | |
107 | \r | |
108 | /*\r | |
109 | * Read the string offset and store it for later use\r | |
110 | */\r | |
111 | ldr w4, [xDTP, #12]\r | |
112 | rev w4, w4\r | |
113 | add xSTRTAB, xDTP, x4\r | |
114 | \r | |
115 | /*\r | |
116 | * Read the struct offset and add it to the DT pointer\r | |
117 | */\r | |
118 | ldr w5, [xDTP, #8]\r | |
119 | rev w5, w5\r | |
120 | add xDTP, xDTP, x5\r | |
121 | \r | |
122 | /*\r | |
123 | * Check current tag for FDT_BEGIN_NODE\r | |
124 | */\r | |
125 | ldr w5, [xDTP]\r | |
126 | rev w5, w5\r | |
127 | cmp w5, #FDT_BEGIN_NODE\r | |
128 | bne err_unexpected_begin_tag\r | |
129 | \r | |
130 | begin_node:\r | |
131 | mov xMEMNODE, #0\r | |
132 | add xDTP, xDTP, #4\r | |
133 | \r | |
134 | /*\r | |
135 | * Advance xDTP past NULL terminated string\r | |
136 | */\r | |
137 | 0: ldrb w4, [xDTP], #1\r | |
138 | cbnz w4, 0b\r | |
139 | \r | |
140 | next_tag:\r | |
141 | /*\r | |
142 | * Align the DT pointer xDTP to the next 32-bit boundary\r | |
143 | */\r | |
144 | add xDTP, xDTP, #3\r | |
145 | and xDTP, xDTP, #~3\r | |
146 | \r | |
147 | /*\r | |
148 | * Read the next tag, could be BEGIN_NODE, END_NODE, PROP, END\r | |
149 | */\r | |
150 | ldr w5, [xDTP]\r | |
151 | rev w5, w5\r | |
152 | cmp w5, #FDT_BEGIN_NODE\r | |
153 | beq begin_node\r | |
154 | cmp w5, #FDT_END_NODE\r | |
155 | beq end_node\r | |
156 | cmp w5, #FDT_PROP\r | |
157 | beq prop_node\r | |
158 | cmp w5, #FDT_END\r | |
159 | beq err_end_of_fdt\r | |
160 | b err_unexpected_tag\r | |
161 | \r | |
162 | prop_node:\r | |
163 | /*\r | |
164 | * If propname == 'reg', record as membase and memsize\r | |
165 | * If propname == 'device_type' and value == 'memory',\r | |
166 | * set the 'is_memnode' flag for this node\r | |
167 | */\r | |
168 | ldr w6, [xDTP, #4]\r | |
169 | add xDTP, xDTP, #12\r | |
170 | rev w6, w6\r | |
171 | mov x5, xDTP\r | |
172 | adr x4, _memory\r | |
173 | bl strcmp\r | |
174 | \r | |
175 | /*\r | |
176 | * Get handle to property name\r | |
177 | */\r | |
178 | ldr w5, [xDTP, #-4]\r | |
179 | rev w5, w5\r | |
180 | add x5, xSTRTAB, x5\r | |
181 | \r | |
182 | cbz w7, check_device_type\r | |
183 | \r | |
184 | /*\r | |
185 | * Check for 'reg' property\r | |
186 | */\r | |
187 | adr x4, _reg\r | |
188 | bl strcmp\r | |
189 | cbnz w7, inc_and_next_tag\r | |
190 | \r | |
191 | /*\r | |
192 | * Extract two 64-bit quantities from the 'reg' property. These values\r | |
193 | * will only be used if the node also turns out to have a device_type\r | |
194 | * property with a value of 'memory'.\r | |
195 | *\r | |
196 | * NOTE: xDTP is only guaranteed to be 32 bit aligned, and we are most\r | |
197 | * likely executing with the MMU off, so we cannot use 64 bit\r | |
198 | * wide accesses here.\r | |
199 | */\r | |
200 | ldp w4, w5, [xDTP]\r | |
201 | orr xMEMBASE, x4, x5, lsl #32\r | |
202 | ldp w4, w5, [xDTP, #8]\r | |
203 | orr xMEMSIZE, x4, x5, lsl #32\r | |
204 | rev xMEMBASE, xMEMBASE\r | |
205 | rev xMEMSIZE, xMEMSIZE\r | |
206 | orr xMEMNODE, xMEMNODE, #HAVE_REG\r | |
207 | b inc_and_next_tag\r | |
208 | \r | |
209 | check_device_type:\r | |
210 | /*\r | |
211 | * Check whether the current property's name is 'device_type'\r | |
212 | */\r | |
213 | adr x4, _device_type\r | |
214 | bl strcmp\r | |
215 | cbnz w7, inc_and_next_tag\r | |
216 | orr xMEMNODE, xMEMNODE, #HAVE_DEVICE_TYPE\r | |
217 | \r | |
218 | inc_and_next_tag:\r | |
219 | add xDTP, xDTP, x6\r | |
220 | b next_tag\r | |
221 | \r | |
222 | end_node:\r | |
223 | /*\r | |
224 | * Check for device_type = memory and reg = xxxx\r | |
225 | * If we have both, we are done\r | |
226 | */\r | |
227 | add xDTP, xDTP, #4\r | |
228 | cmp xMEMNODE, #(HAVE_REG | HAVE_DEVICE_TYPE)\r | |
229 | bne next_tag\r | |
230 | \r | |
231 | ret xLR\r | |
232 | \r | |
233 | err_invalid_magic:\r | |
234 | err_unexpected_begin_tag:\r | |
235 | err_unexpected_tag:\r | |
236 | err_end_of_fdt:\r | |
237 | wfi\r |