1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * (C) Copyright Linaro, Ltd. 2018 |
4 | * (C) Copyright Arm Holdings. 2017 |
5 | * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2005. |
6 | */ |
7 | |
8 | #include <stdlib.h> |
9 | #include <yaml.h> |
10 | #include "dtc.h" |
11 | #include "srcpos.h" |
12 | |
13 | char *yaml_error_name[] = { |
14 | [YAML_NO_ERROR] = "no error" , |
15 | [YAML_MEMORY_ERROR] = "memory error" , |
16 | [YAML_READER_ERROR] = "reader error" , |
17 | [YAML_SCANNER_ERROR] = "scanner error" , |
18 | [YAML_PARSER_ERROR] = "parser error" , |
19 | [YAML_COMPOSER_ERROR] = "composer error" , |
20 | [YAML_WRITER_ERROR] = "writer error" , |
21 | [YAML_EMITTER_ERROR] = "emitter error" , |
22 | }; |
23 | |
24 | #define yaml_emitter_emit_or_die(emitter, event) ( \ |
25 | { \ |
26 | if (!yaml_emitter_emit(emitter, event)) \ |
27 | die("yaml '%s': %s in %s, line %i\n", \ |
28 | yaml_error_name[(emitter)->error], \ |
29 | (emitter)->problem, __func__, __LINE__); \ |
30 | }) |
31 | |
32 | static void yaml_propval_int(yaml_emitter_t *emitter, struct marker *markers, |
33 | char *data, unsigned int seq_offset, unsigned int len, int width) |
34 | { |
35 | yaml_event_t event; |
36 | void *tag; |
37 | unsigned int off; |
38 | |
39 | switch(width) { |
40 | case 1: tag = "!u8" ; break; |
41 | case 2: tag = "!u16" ; break; |
42 | case 4: tag = "!u32" ; break; |
43 | case 8: tag = "!u64" ; break; |
44 | default: |
45 | die(str: "Invalid width %i" , width); |
46 | } |
47 | assert(len % width == 0); |
48 | |
49 | yaml_sequence_start_event_initialize(&event, NULL, |
50 | (yaml_char_t *)tag, width == 4, YAML_FLOW_SEQUENCE_STYLE); |
51 | yaml_emitter_emit_or_die(emitter, &event); |
52 | |
53 | for (off = 0; off < len; off += width) { |
54 | char buf[32]; |
55 | struct marker *m; |
56 | bool is_phandle = false; |
57 | |
58 | switch(width) { |
59 | case 1: |
60 | sprintf(s: buf, format: "0x%" PRIx8, *(uint8_t*)(data + off)); |
61 | break; |
62 | case 2: |
63 | sprintf(s: buf, format: "0x%" PRIx16, dtb_ld16(p: data + off)); |
64 | break; |
65 | case 4: |
66 | sprintf(s: buf, format: "0x%" PRIx32, dtb_ld32(p: data + off)); |
67 | m = markers; |
68 | is_phandle = false; |
69 | for_each_marker_of_type(m, REF_PHANDLE) { |
70 | if (m->offset == (seq_offset + off)) { |
71 | is_phandle = true; |
72 | break; |
73 | } |
74 | } |
75 | break; |
76 | case 8: |
77 | sprintf(s: buf, format: "0x%" PRIx64, dtb_ld64(p: data + off)); |
78 | break; |
79 | } |
80 | |
81 | if (is_phandle) |
82 | yaml_scalar_event_initialize(&event, NULL, |
83 | (yaml_char_t*)"!phandle" , (yaml_char_t *)buf, |
84 | strlen(buf), 0, 0, YAML_PLAIN_SCALAR_STYLE); |
85 | else |
86 | yaml_scalar_event_initialize(&event, NULL, |
87 | (yaml_char_t*)YAML_INT_TAG, (yaml_char_t *)buf, |
88 | strlen(buf), 1, 1, YAML_PLAIN_SCALAR_STYLE); |
89 | yaml_emitter_emit_or_die(emitter, &event); |
90 | } |
91 | |
92 | yaml_sequence_end_event_initialize(&event); |
93 | yaml_emitter_emit_or_die(emitter, &event); |
94 | } |
95 | |
96 | static void yaml_propval_string(yaml_emitter_t *emitter, char *str, int len) |
97 | { |
98 | yaml_event_t event; |
99 | int i; |
100 | |
101 | assert(str[len-1] == '\0'); |
102 | |
103 | /* Make sure the entire string is in the lower 7-bit ascii range */ |
104 | for (i = 0; i < len; i++) |
105 | assert(isascii(str[i])); |
106 | |
107 | yaml_scalar_event_initialize(&event, NULL, |
108 | (yaml_char_t *)YAML_STR_TAG, (yaml_char_t*)str, |
109 | len-1, 0, 1, YAML_DOUBLE_QUOTED_SCALAR_STYLE); |
110 | yaml_emitter_emit_or_die(emitter, &event); |
111 | } |
112 | |
113 | static void yaml_propval(yaml_emitter_t *emitter, struct property *prop) |
114 | { |
115 | yaml_event_t event; |
116 | unsigned int len = prop->val.len; |
117 | struct marker *m = prop->val.markers; |
118 | struct marker *markers = prop->val.markers; |
119 | |
120 | /* Emit the property name */ |
121 | yaml_scalar_event_initialize(&event, NULL, |
122 | (yaml_char_t *)YAML_STR_TAG, (yaml_char_t*)prop->name, |
123 | strlen(prop->name), 1, 1, YAML_PLAIN_SCALAR_STYLE); |
124 | yaml_emitter_emit_or_die(emitter, &event); |
125 | |
126 | /* Boolean properties are easiest to deal with. Length is zero, so just emit 'true' */ |
127 | if (len == 0) { |
128 | yaml_scalar_event_initialize(&event, NULL, |
129 | (yaml_char_t *)YAML_BOOL_TAG, |
130 | (yaml_char_t*)"true" , |
131 | strlen("true" ), 1, 0, YAML_PLAIN_SCALAR_STYLE); |
132 | yaml_emitter_emit_or_die(emitter, &event); |
133 | return; |
134 | } |
135 | |
136 | if (!m) |
137 | die(str: "No markers present in property '%s' value\n" , prop->name); |
138 | |
139 | yaml_sequence_start_event_initialize(&event, NULL, |
140 | (yaml_char_t *)YAML_SEQ_TAG, 1, YAML_FLOW_SEQUENCE_STYLE); |
141 | yaml_emitter_emit_or_die(emitter, &event); |
142 | |
143 | for_each_marker(m) { |
144 | int chunk_len; |
145 | char *data = &prop->val.val[m->offset]; |
146 | |
147 | if (m->type < TYPE_UINT8) |
148 | continue; |
149 | |
150 | chunk_len = type_marker_length(m) ? : len; |
151 | assert(chunk_len > 0); |
152 | len -= chunk_len; |
153 | |
154 | switch(m->type) { |
155 | case TYPE_UINT16: |
156 | yaml_propval_int(emitter, markers, data, m->offset, chunk_len, 2); |
157 | break; |
158 | case TYPE_UINT32: |
159 | yaml_propval_int(emitter, markers, data, m->offset, chunk_len, 4); |
160 | break; |
161 | case TYPE_UINT64: |
162 | yaml_propval_int(emitter, markers, data, m->offset, chunk_len, 8); |
163 | break; |
164 | case TYPE_STRING: |
165 | yaml_propval_string(emitter, data, chunk_len); |
166 | break; |
167 | default: |
168 | yaml_propval_int(emitter, markers, data, m->offset, chunk_len, 1); |
169 | break; |
170 | } |
171 | } |
172 | |
173 | yaml_sequence_end_event_initialize(&event); |
174 | yaml_emitter_emit_or_die(emitter, &event); |
175 | } |
176 | |
177 | |
178 | static void yaml_tree(struct node *tree, yaml_emitter_t *emitter) |
179 | { |
180 | struct property *prop; |
181 | struct node *child; |
182 | yaml_event_t event; |
183 | |
184 | if (tree->deleted) |
185 | return; |
186 | |
187 | yaml_mapping_start_event_initialize(&event, NULL, |
188 | (yaml_char_t *)YAML_MAP_TAG, 1, YAML_ANY_MAPPING_STYLE); |
189 | yaml_emitter_emit_or_die(emitter, &event); |
190 | |
191 | for_each_property(tree, prop) |
192 | yaml_propval(emitter, prop); |
193 | |
194 | /* Loop over all the children, emitting them into the map */ |
195 | for_each_child(tree, child) { |
196 | yaml_scalar_event_initialize(&event, NULL, |
197 | (yaml_char_t *)YAML_STR_TAG, (yaml_char_t*)child->name, |
198 | strlen(child->name), 1, 0, YAML_PLAIN_SCALAR_STYLE); |
199 | yaml_emitter_emit_or_die(emitter, &event); |
200 | yaml_tree(child, emitter); |
201 | } |
202 | |
203 | yaml_mapping_end_event_initialize(&event); |
204 | yaml_emitter_emit_or_die(emitter, &event); |
205 | } |
206 | |
207 | void dt_to_yaml(FILE *f, struct dt_info *dti) |
208 | { |
209 | yaml_emitter_t emitter; |
210 | yaml_event_t event; |
211 | |
212 | yaml_emitter_initialize(&emitter); |
213 | yaml_emitter_set_output_file(&emitter, f); |
214 | yaml_stream_start_event_initialize(&event, YAML_UTF8_ENCODING); |
215 | yaml_emitter_emit_or_die(&emitter, &event); |
216 | |
217 | yaml_document_start_event_initialize(&event, NULL, NULL, NULL, 0); |
218 | yaml_emitter_emit_or_die(&emitter, &event); |
219 | |
220 | yaml_sequence_start_event_initialize(&event, NULL, (yaml_char_t *)YAML_SEQ_TAG, 1, YAML_ANY_SEQUENCE_STYLE); |
221 | yaml_emitter_emit_or_die(&emitter, &event); |
222 | |
223 | yaml_tree(dti->dt, &emitter); |
224 | |
225 | yaml_sequence_end_event_initialize(&event); |
226 | yaml_emitter_emit_or_die(&emitter, &event); |
227 | |
228 | yaml_document_end_event_initialize(&event, 0); |
229 | yaml_emitter_emit_or_die(&emitter, &event); |
230 | |
231 | yaml_stream_end_event_initialize(&event); |
232 | yaml_emitter_emit_or_die(&emitter, &event); |
233 | |
234 | yaml_emitter_delete(&emitter); |
235 | } |
236 | |