1 /*
2  * Copyright (c) 2006-2024, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2022-08-25     GuEe-GUI     first version
9  */
10 
11 #include <rtthread.h>
12 #include <rtdevice.h>
13 #include <drivers/platform.h>
14 #include <drivers/core/bus.h>
15 #include <drivers/serial_dm.h>
16 
17 #define DBG_TAG "rtdm.ofw"
18 #define DBG_LVL DBG_INFO
19 #include <rtdbg.h>
20 
21 #include "ofw_internal.h"
22 
rt_ofw_stub_probe_range(struct rt_ofw_node * np,const struct rt_ofw_stub * stub_start,const struct rt_ofw_stub * stub_end)23 struct rt_ofw_stub *rt_ofw_stub_probe_range(struct rt_ofw_node *np,
24         const struct rt_ofw_stub *stub_start, const struct rt_ofw_stub *stub_end)
25 {
26     const struct rt_ofw_stub *stub = RT_NULL;
27 
28     if (np && stub_start && stub_end &&
29         rt_ofw_node_is_available(np) &&
30         !rt_ofw_node_test_flag(np, RT_OFW_F_READLY) &&
31         !rt_ofw_node_test_flag(np, RT_OFW_F_SYSTEM))
32     {
33         struct rt_ofw_prop *compat_prop = rt_ofw_get_prop(np, "compatible", RT_NULL);
34 
35         if (compat_prop)
36         {
37             rt_ofw_foreach_stub(stub, stub_start, stub_end)
38             {
39                 struct rt_ofw_node_id *id;
40 
41                 if (!stub->ids)
42                 {
43                     continue;
44                 }
45 
46                 id = rt_ofw_prop_match(compat_prop, stub->ids);
47 
48                 if (id)
49                 {
50                     if (!stub->handler(np, id))
51                     {
52                         rt_ofw_node_set_flag(np, RT_OFW_F_READLY);
53                     }
54 
55                     break;
56                 }
57             }
58         }
59     }
60 
61     return (struct rt_ofw_stub *)stub;
62 }
63 
64 struct ofw_obj_cmp_list
65 {
66     const char *cells_name;
67     const char *obj_name;
68     rt_size_t obj_size;
69 };
70 
71 static const struct ofw_obj_cmp_list ofw_obj_cmp_list[] =
72 {
73 #ifdef RT_USING_CLK
74     { "#clock-cells", RT_CLK_NODE_OBJ_NAME, sizeof(struct rt_clk_node) },
75 #endif
76 #ifdef RT_USING_RESET
77     { "#reset-cells", RT_RESET_CONTROLLER_OBJ_NAME, sizeof(struct rt_reset_controller) },
78 #endif
79     { "#power-domain-cells", RT_POWER_DOMAIN_PROXY_OBJ_NAME, sizeof(struct rt_dm_power_domain_proxy) },
80     { "#power-domain-cells", RT_POWER_DOMAIN_OBJ_NAME, sizeof(struct rt_dm_power_domain) },
81 };
82 
ofw_parse_object(struct rt_ofw_node * np,const char * cells_name)83 static struct rt_object *ofw_parse_object(struct rt_ofw_node *np, const char *cells_name)
84 {
85     const struct ofw_obj_cmp_list *item;
86     struct rt_object *obj = rt_ofw_data(np), *ret_obj = RT_NULL;
87     RT_BITMAP_DECLARE(idx_mask, RT_ARRAY_SIZE(ofw_obj_cmp_list)) = {};
88 
89     for (int i = 0; i < RT_ARRAY_SIZE(ofw_obj_cmp_list); ++i)
90     {
91         item = &ofw_obj_cmp_list[i];
92 
93         if (!rt_ofw_prop_read_bool(np, item->cells_name))
94         {
95             rt_bitmap_set_bit(idx_mask, i);
96         }
97     }
98 
99     while (!ret_obj)
100     {
101         int i = 0;
102 
103         /* Is print ? */
104         if (!((rt_uint32_t)(obj->name[0] - 0x20) < 0x5f))
105         {
106             break;
107         }
108 
109         rt_bitmap_for_each_clear_bit(idx_mask, i, RT_ARRAY_SIZE(ofw_obj_cmp_list))
110         {
111             item = &ofw_obj_cmp_list[i];
112 
113             if (!rt_strncmp(item->obj_name, obj->name, RT_NAME_MAX))
114             {
115                 if (!rt_strcmp(item->cells_name, cells_name))
116                 {
117                     ret_obj = obj;
118                     break;
119                 }
120 
121                 obj = (struct rt_object *)((rt_ubase_t)obj + item->obj_size);
122                 break;
123             }
124         }
125 
126         if (i >= RT_ARRAY_SIZE(ofw_obj_cmp_list))
127         {
128             LOG_E("Invalid rt_object = %s", obj->name);
129 
130             break;
131         }
132     }
133 
134     return ret_obj;
135 }
136 
rt_ofw_parse_object(struct rt_ofw_node * np,const char * obj_name,const char * cells_name)137 struct rt_object *rt_ofw_parse_object(struct rt_ofw_node *np, const char *obj_name, const char *cells_name)
138 {
139     struct rt_object *obj = RT_NULL, *test_obj;
140 
141     if (np && (test_obj = rt_ofw_data(np)) && cells_name)
142     {
143         /* The composite object is rare, so we try to find this object as much as possible at once. */
144         if (obj_name && !rt_strcmp(test_obj->name, obj_name))
145         {
146             obj = test_obj;
147         }
148         else
149         {
150             obj = ofw_parse_object(np, cells_name);
151         }
152     }
153 
154     return obj;
155 }
156 
ofw_console_serial_find(char * dst_con,struct rt_ofw_node * np)157 static const char *ofw_console_serial_find(char *dst_con, struct rt_ofw_node *np)
158 {
159     rt_object_t rt_obj = RT_NULL;
160     const char *ofw_name = RT_NULL;
161     struct rt_serial_device *rt_serial = rt_ofw_data(np);
162 
163     if (rt_serial)
164     {
165         rt_obj = &rt_serial->parent.parent;
166     }
167 
168     /*
169      * This is a dangerous behavior because rt_data can be modified by other
170      * user. But fortunately, we can check if the rt_data is a rt_device or
171      * rt_serial_device.
172      */
173     if (rt_obj && rt_object_get_type(rt_obj) == RT_Object_Class_Device &&
174         rt_serial->parent.type == RT_Device_Class_Char)
175     {
176         ofw_name = np->full_name;
177         rt_strncpy(dst_con, rt_obj->name, RT_NAME_MAX);
178     }
179 
180     return ofw_name;
181 }
182 
tty_device_compare(rt_device_t dev,void * data)183 static int tty_device_compare(rt_device_t dev, void *data)
184 {
185     rt_ubase_t *args_list;
186     char *dst_con;
187     const char *console, **ofw_name;
188     const struct rt_ofw_node_id *id;
189     struct rt_platform_device *pdev;
190     int *tty_idx, tty_id, tty_name_len;
191 
192     pdev = rt_container_of(dev, struct rt_platform_device, parent);
193     id = pdev->id;
194 
195     args_list = data;
196     tty_idx = (int *)args_list[0];
197     tty_id = args_list[1];
198     console = (const char *)args_list[2];
199     tty_name_len = args_list[3];
200     dst_con = (char *)args_list[4];
201     ofw_name = (const char **)args_list[5];
202 
203     if (id && id->type[0] && !strncmp(id->type, console, tty_name_len))
204     {
205         if (*tty_idx == tty_id)
206         {
207             *ofw_name = ofw_console_serial_find(dst_con, pdev->parent.ofw_node);
208 
209             return RT_EOK;
210         }
211 
212         ++*tty_idx;
213     }
214 
215     return -RT_EEMPTY;
216 }
217 
ofw_console_tty_find(char * dst_con,const char * con)218 static const char *ofw_console_tty_find(char *dst_con, const char *con)
219 {
220     const char *ofw_name = RT_NULL;
221     static rt_bus_t platform_bus = RT_NULL;
222 
223     if (!platform_bus)
224     {
225         platform_bus = rt_bus_find_by_name("platform");
226     }
227 
228     if (platform_bus)
229     {
230         rt_ubase_t args_list[6];
231         const char *console = con;
232         int tty_idx = 0, tty_id = 0, tty_name_len;
233 
234         con += sizeof("tty") - 1;
235 
236         while (*con && !(*con >= '0' && *con <= '9'))
237         {
238             ++con;
239         }
240 
241         tty_name_len = con - console;
242 
243         while (*con && *con >= '0' && *con <= '9')
244         {
245             tty_id *= 10;
246             tty_id += *con - '0';
247 
248             ++con;
249         }
250 
251         args_list[0] = (rt_ubase_t)&tty_idx;
252         args_list[1] = tty_id;
253         args_list[2] = (rt_ubase_t)console;
254         args_list[3] = tty_name_len;
255         args_list[4] = (rt_ubase_t)dst_con;
256         args_list[5] = (rt_ubase_t)&ofw_name;
257         rt_bus_for_each_dev(platform_bus, &args_list, tty_device_compare);
258     }
259 
260     return ofw_name;
261 }
262 
rt_ofw_console_setup(void)263 rt_err_t rt_ofw_console_setup(void)
264 {
265     rt_err_t err = -RT_ENOSYS;
266     char con_name[RT_NAME_MAX], *options = RT_NULL;
267     const char *ofw_name = RT_NULL, *stdout_path, *con;
268 
269     /* chosen.console > chosen.stdout-path > RT_CONSOLE_DEVICE_NAME */
270 
271     con = rt_ofw_bootargs_select("console=", 0);
272 
273     for (int i = 1; con; ++i)
274     {
275         if (!rt_strncmp(con, "uart", sizeof("uart") - 1))
276         {
277             rt_strncpy(con_name, con, RT_NAME_MAX);
278 
279             err = RT_EOK;
280             break;
281         }
282         else if (!rt_strncmp(con, "tty", sizeof("tty") - 1))
283         {
284             ofw_name = ofw_console_tty_find(con_name, con);
285 
286             if (ofw_name)
287             {
288                 const char *ch = con;
289 
290                 while (*ch && *ch != ' ')
291                 {
292                     if (*ch++ == ',')
293                     {
294                         options = (char *)ch;
295 
296                         break;
297                     }
298                 }
299 
300                 err = RT_EOK;
301                 break;
302             }
303         }
304 
305         con = rt_ofw_bootargs_select("console=", i);
306     }
307 
308     if (err == -RT_ENOSYS && !rt_ofw_prop_read_string(ofw_node_chosen, "stdout-path", &stdout_path))
309     {
310         struct rt_ofw_node *stdout_np = rt_ofw_find_node_by_path(stdout_path);
311 
312         if (stdout_np && (ofw_name = ofw_console_serial_find(con_name, stdout_np)))
313         {
314             err = RT_EOK;
315         }
316     }
317 
318     if (err == -RT_ENOSYS)
319     {
320         rt_device_t serial = rt_device_find(RT_CONSOLE_DEVICE_NAME);
321 
322         if (serial)
323         {
324             ofw_name = rt_ofw_node_full_name(serial->ofw_node);
325 
326             con = RT_CONSOLE_DEVICE_NAME;
327         }
328         else
329         {
330             LOG_W("Console will probably fail to setup");
331         }
332     }
333     else
334     {
335         con = con_name;
336     }
337 
338     rt_console_set_device(con);
339 
340     if (options)
341     {
342         rt_device_t con_dev = rt_console_get_device();
343 
344         if (con_dev)
345         {
346             struct serial_configure con_conf = serial_cfg_from_args(options);
347 
348             rt_device_control(con_dev, RT_DEVICE_CTRL_CONFIG, &con_conf);
349         }
350     }
351 
352     rt_fdt_earlycon_kick(FDT_EARLYCON_KICK_COMPLETED);
353 
354     LOG_I("Console: %s (%s)", con, ofw_name ? ofw_name : "<unknown>");
355 
356     return err;
357 }
358 
rt_ofw_bootargs_select(const char * key,int index)359 const char *rt_ofw_bootargs_select(const char *key, int index)
360 {
361     const char *value = RT_NULL;
362 
363     if (key && index >= 0)
364     {
365         static char **values = RT_NULL;
366         static rt_size_t bootargs_nr = 1;
367         const char *bootargs = RT_NULL, *ch;
368 
369         if (bootargs_nr && !values &&
370             (bootargs_nr = 0, !rt_ofw_prop_read_string(ofw_node_chosen, "bootargs", &bootargs)) &&
371             bootargs && (bootargs = rt_strdup(bootargs)))
372         {
373             rt_bool_t quotes = RT_TRUE;
374             rt_size_t length = rt_strlen(bootargs);
375             const char *bootargs_end = bootargs + length;
376 
377             for (ch = bootargs; ch < bootargs_end; ++ch)
378             {
379                 if (*ch == '"')
380                 {
381                     quotes = !quotes;
382                     continue;
383                 }
384 
385                 if (*ch != ' ' || !quotes)
386                 {
387                     continue;
388                 }
389 
390                 ++bootargs_nr;
391 
392                 while (*ch == ' ' && ch < bootargs_end)
393                 {
394                     *(char *)ch++ = '\0';
395                 }
396                 if (*ch == '\0')
397                 {
398                     /* space in the end */
399                     --bootargs_nr;
400                 }
401                 --ch;
402             }
403 
404             if (bootargs_nr)
405             {
406                 /* last arg */
407                 ++bootargs_nr;
408                 values = rt_malloc(sizeof(char *) * bootargs_nr);
409             }
410 
411             if (values)
412             {
413                 int idx = 0;
414 
415                 for (int i = 0; i < length; ++i)
416                 {
417                     for (; i < length && !bootargs[i]; ++i)
418                     {
419                     }
420 
421                     if (i < length)
422                     {
423                         values[idx++] = (char *)&bootargs[i];
424                     }
425 
426                     for (; i < length && bootargs[i]; ++i)
427                     {
428                     }
429                 }
430             }
431             else
432             {
433                 rt_free((char *)bootargs);
434             }
435         }
436 
437         if (values)
438         {
439             int keylen = rt_strlen(key);
440 
441             for (int idx = 0, count = 0; idx < bootargs_nr; ++idx)
442             {
443                 if (!rt_strncmp(values[idx], key, keylen))
444                 {
445                     if (count == index)
446                     {
447                         value = values[idx] + keylen;
448 
449                         break;
450                     }
451 
452                     ++count;
453                 }
454             }
455         }
456     }
457 
458     return value;
459 }
460 
461 #ifdef RT_USING_CONSOLE
dts_put_depth(int depth)462 static void dts_put_depth(int depth)
463 {
464     while (depth --> 0)
465     {
466         rt_kputs("    ");
467     }
468 }
469 
dts_test_string_list(const void * value,int size)470 static rt_bool_t dts_test_string_list(const void *value, int size)
471 {
472     const char *str, *str_start, *str_end;
473 
474     if (!size)
475     {
476         return RT_FALSE;
477     }
478 
479     str = value;
480 
481     /* String end with '\0' */
482     if (str[size - 1] != '\0')
483     {
484         return RT_FALSE;
485     }
486 
487     /* Get string list end */
488     str_end = str + size;
489 
490     while (str < str_end)
491     {
492         str_start = str;
493         /* Before string list end, not '\0' and a printable characters */
494         while (str < str_end && *str && ((unsigned char)*str >= ' ' && (unsigned char)*str <= '~'))
495         {
496             ++str;
497         }
498 
499         /* Not zero, or not increased */
500         if (*str != '\0' || str == str_start)
501         {
502             return RT_FALSE;
503         }
504 
505         /* Next string */
506         ++str;
507     }
508 
509     return RT_TRUE;
510 }
511 
ofw_node_dump_dts(struct rt_ofw_node * np,rt_bool_t sibling_too)512 static void ofw_node_dump_dts(struct rt_ofw_node *np, rt_bool_t sibling_too)
513 {
514     int depth = 0;
515     struct rt_ofw_prop *prop;
516     struct rt_ofw_node *org_np = np;
517 
518     while (np)
519     {
520         dts_put_depth(depth);
521         rt_kputs(np->full_name);
522         rt_kputs(" {\n");
523 
524         prop = np->props;
525 
526         /* Print prop start */
527         ++depth;
528 
529         while (prop)
530         {
531             dts_put_depth(depth);
532 
533             rt_kputs(prop->name);
534 
535             /* Have value? */
536             if (prop->length > 0)
537             {
538                 int length = prop->length;
539                 void *value = prop->value;
540 
541                 rt_kputs(" = ");
542 
543                 if (dts_test_string_list(value, length))
544                 {
545                     /* String list */
546                     char *str = value;
547 
548                     do {
549                         rt_kputs("\"");
550                         rt_kputs(str);
551                         rt_kputs("\", ");
552 
553                         str += rt_strlen(str) + 1;
554 
555                     } while (str < (char *)value + length);
556 
557                     rt_kputs("\b\b");
558                 }
559                 else if ((length % 4) == 0)
560                 {
561                     /* u32 data in <?> */
562                     fdt32_t *cell = value;
563 
564                     rt_kputs("<");
565 
566                     length /= 4;
567 
568                     for (int i = 0; i < length; ++i)
569                     {
570                         rt_kprintf("0x%02x ", fdt32_to_cpu(cell[i]));
571                     }
572 
573                     rt_kputs("\b>");
574                 }
575                 else
576                 {
577                     /* Byte data in [?] */
578                     rt_uint8_t *byte = value;
579 
580                     rt_kputs("[");
581 
582                     for (int i = 0; i < length; ++i)
583                     {
584                        rt_kprintf("%02x ", *byte++);
585                     }
586 
587                     rt_kputs("\b]");
588                 }
589             }
590 
591             rt_kputs(";\n");
592 
593             prop = prop->next;
594         }
595 
596         --depth;
597         /* Print prop end */
598 
599         if (np->child)
600         {
601             rt_kputs("\n");
602             np = np->child;
603             ++depth;
604         }
605         else
606         {
607             dts_put_depth(depth);
608             rt_kputs("};\n");
609 
610             if (!sibling_too && org_np == np)
611             {
612                 break;
613             }
614 
615             while (np->parent && !np->sibling)
616             {
617                 np = np->parent;
618                 --depth;
619 
620                 if (depth >= 0)
621                 {
622                     dts_put_depth(depth);
623                     rt_kputs("};\n");
624                 }
625             }
626 
627             if (!sibling_too && org_np == np)
628             {
629                 break;
630             }
631 
632             np = np->sibling;
633 
634             if (np)
635             {
636                 rt_kputs("\n");
637             }
638         }
639     }
640 }
641 
rt_ofw_node_dump_dts(struct rt_ofw_node * np,rt_bool_t sibling_too)642 void rt_ofw_node_dump_dts(struct rt_ofw_node *np, rt_bool_t sibling_too)
643 {
644     if (np)
645     {
646         if (!rt_strcmp(np->name, "/"))
647         {
648             struct fdt_info *header = (struct fdt_info *)np->name;
649             struct fdt_reserve_entry *rsvmap = header->rsvmap;
650 
651             /*
652              * Shall be present to identify the file as a version 1 DTS
653              * (dts files without this tag will be treated by dtc
654              * as being in the obsolete version 0, which uses
655              * a different format for integers in addition to
656              * other small but incompatible changes).
657              */
658             rt_kprintf("/dts-v1/;\n\n");
659 
660             for (int i = header->rsvmap_nr - 1; i >= 0; --i)
661             {
662                 rt_kprintf("/memreserve/\t%p %p;\n",
663                         ofw_static_cast(rt_size_t, fdt64_to_cpu(rsvmap->address)),
664                         ofw_static_cast(rt_size_t, fdt64_to_cpu(rsvmap->size)));
665 
666                 ++rsvmap;
667             }
668 
669             rt_kputs(np->name);
670         }
671 
672         ofw_node_dump_dts(np, sibling_too);
673     }
674 }
675 
676 #ifdef RT_USING_MSH
ofw_dts(int argc,char ** argv)677 static void ofw_dts(int argc, char**argv)
678 {
679     if (ofw_node_root)
680     {
681         if (argc == 1)
682         {
683             rt_ofw_node_dump_dts(ofw_node_root, RT_TRUE);
684         }
685         else if (argv[1][0] == '/')
686         {
687             struct rt_ofw_node *np = rt_ofw_find_node_by_path(argv[1]);
688 
689             if (np)
690             {
691                 rt_ofw_node_dump_dts(np, RT_FALSE);
692             }
693             else
694             {
695                 rt_kprintf("%s not found.\n", argv[1]);
696             }
697         }
698         else if (argc >= 2 && !rt_strcmp(argv[1], "list") && argv[2][0] == '/')
699         {
700             struct rt_ofw_node *np = rt_ofw_find_node_by_path(argv[2]);
701 
702             if (np)
703             {
704                 const char *split = np == ofw_node_root ? "" : "/";
705 
706                 np = np->child;
707 
708                 while (np)
709                 {
710                     rt_kprintf("%s%s%s\n", argv[2], split, np->full_name);
711                     np = np->sibling;
712                 }
713             }
714             else
715             {
716                 rt_kprintf("%s not found.\n", argv[2]);
717             }
718         }
719         else
720         {
721             rt_kprintf("Usage: %s {list} {path}\n", __func__);
722         }
723     }
724     else
725     {
726         rt_kprintf("\"/\" path not found.");
727     }
728 }
729 MSH_CMD_EXPORT(ofw_dts, dump the dts or node for this platform);
730 #endif /* RT_USING_MSH */
731 #endif /* RT_USING_CONSOLE */
732