Coverage Report

Created: 2026-03-11 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/u-boot/boot/bootstd-uclass.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0+
2
/*
3
 * Uclass implementation for standard boot
4
 *
5
 * Copyright 2021 Google LLC
6
 * Written by Simon Glass <sjg@chromium.org>
7
 */
8
9
#include <alist.h>
10
#include <bootflow.h>
11
#include <bootstd.h>
12
#include <dm.h>
13
#include <env.h>
14
#include <log.h>
15
#include <malloc.h>
16
#include <dm/device-internal.h>
17
#include <dm/lists.h>
18
#include <dm/read.h>
19
#include <dm/uclass-internal.h>
20
21
DECLARE_GLOBAL_DATA_PTR;
22
23
/* These are used if filename-prefixes is not present */
24
const char *const default_prefixes[] = {"/", "/boot/", NULL};
25
26
static int bootstd_of_to_plat(struct udevice *dev)
27
0
{
28
0
  struct bootstd_priv *priv = dev_get_priv(dev);
29
0
  int ret;
30
31
0
  if (IS_ENABLED(CONFIG_BOOTSTD_FULL)) {
32
    /* Don't check errors since livetree and flattree are different */
33
0
    ret = dev_read_string_list(dev, "filename-prefixes",
34
0
             &priv->prefixes);
35
0
    dev_read_string_list(dev, "bootdev-order",
36
0
             &priv->bootdev_order);
37
38
0
    priv->theme = ofnode_find_subnode(dev_ofnode(dev), "theme");
39
0
  }
40
41
0
  return 0;
42
0
}
43
44
static void bootstd_clear_glob_(struct bootstd_priv *priv)
45
0
{
46
0
  struct bootflow *bflow;
47
48
0
  alist_for_each(bflow, &priv->bootflows)
49
0
    bootflow_remove(bflow);
50
0
  alist_empty(&priv->bootflows);
51
0
}
52
53
void bootstd_clear_glob(void)
54
0
{
55
0
  struct bootstd_priv *std;
56
57
0
  if (bootstd_get_priv(&std))
58
0
    return;
59
60
0
  bootstd_clear_glob_(std);
61
0
}
62
63
int bootstd_add_bootflow(struct bootflow *bflow)
64
0
{
65
0
  struct bootstd_priv *std;
66
0
  int ret;
67
68
0
  ret = bootstd_get_priv(&std);
69
0
  if (ret)
70
0
    return ret;
71
72
0
  ret = std->bootflows.count;
73
0
  bflow = alist_add(&std->bootflows, *bflow);
74
0
  if (!bflow)
75
0
    return log_msg_ret("bf2", -ENOMEM);
76
77
0
  return ret;
78
0
}
79
80
int bootstd_clear_bootflows_for_bootdev(struct udevice *dev)
81
0
{
82
0
  struct bootstd_priv *std = bootstd_try_priv();
83
0
  struct bootflow *from, *to;
84
85
  /* if bootstd does not exist we cannot have any bootflows */
86
0
  if (!std)
87
0
    return 0;
88
89
  /* Drop any bootflows that mention this dev */
90
0
  alist_for_each_filter(from, to, &std->bootflows) {
91
0
    if (from->dev == dev)
92
0
      bootflow_remove(from);
93
0
    else
94
0
      *to++ = *from;
95
0
  }
96
0
  alist_update_end(&std->bootflows, to);
97
98
0
  return 0;
99
0
}
100
101
static int bootstd_remove(struct udevice *dev)
102
0
{
103
0
  struct bootstd_priv *priv = dev_get_priv(dev);
104
105
0
  free(priv->prefixes);
106
0
  free(priv->bootdev_order);
107
0
  bootstd_clear_glob_(priv);
108
109
0
  return 0;
110
0
}
111
112
const char *const *const bootstd_get_bootdev_order(struct udevice *dev,
113
               bool *okp)
114
0
{
115
0
  struct bootstd_priv *std = dev_get_priv(dev);
116
0
  const char *targets = env_get("boot_targets");
117
118
0
  *okp = true;
119
0
  log_debug("- targets %s %p\n", targets, std->bootdev_order);
120
0
  if (targets && *targets) {
121
0
    str_free_list(std->env_order);
122
0
    std->env_order = str_to_list(targets);
123
0
    if (!std->env_order) {
124
0
      *okp = false;
125
0
      return NULL;
126
0
    }
127
0
    return std->env_order;
128
0
  }
129
130
0
  return std->bootdev_order;
131
0
}
132
133
const char *const *const bootstd_get_prefixes(struct udevice *dev)
134
0
{
135
0
  struct bootstd_priv *std = dev_get_priv(dev);
136
137
0
  return std->prefixes ? std->prefixes : default_prefixes;
138
0
}
139
140
struct bootstd_priv *bootstd_try_priv(void)
141
0
{
142
0
  struct udevice *dev;
143
144
0
  dev = uclass_try_first_device(UCLASS_BOOTSTD);
145
0
  if (!dev || !device_active(dev))
146
0
    return NULL;
147
148
0
  return dev_get_priv(dev);
149
0
}
150
151
int bootstd_get_priv(struct bootstd_priv **stdp)
152
0
{
153
0
  struct udevice *dev;
154
0
  int ret;
155
156
0
  ret = uclass_first_device_err(UCLASS_BOOTSTD, &dev);
157
0
  if (ret)
158
0
    return ret;
159
0
  *stdp = dev_get_priv(dev);
160
161
0
  return 0;
162
0
}
163
164
static int bootstd_probe(struct udevice *dev)
165
0
{
166
0
  struct bootstd_priv *std = dev_get_priv(dev);
167
168
0
  alist_init_struct(&std->bootflows, struct bootflow);
169
170
0
  return 0;
171
0
}
172
173
/* For now, bind the bootmethod device if none are found in the devicetree */
174
int dm_scan_other(bool pre_reloc_only)
175
0
{
176
0
  struct driver *drv = ll_entry_start(struct driver, driver);
177
0
  const int n_ents = ll_entry_count(struct driver, driver);
178
0
  struct udevice *dev, *bootstd;
179
0
  int i, ret;
180
181
  /* These are not needed before relocation */
182
0
  if (!(gd->flags & GD_FLG_RELOC))
183
0
    return 0;
184
185
  /* Create a bootstd device if needed */
186
0
  uclass_find_first_device(UCLASS_BOOTSTD, &bootstd);
187
0
  if (!bootstd) {
188
0
    ret = device_bind_driver(gd->dm_root, "bootstd_drv", "bootstd",
189
0
           &bootstd);
190
0
    if (ret)
191
0
      return log_msg_ret("bootstd", ret);
192
0
  }
193
194
  /* If there are no bootmeth devices, create them */
195
0
  uclass_find_first_device(UCLASS_BOOTMETH, &dev);
196
0
  if (dev)
197
0
    return 0;
198
199
0
  for (i = 0; i < n_ents; i++, drv++) {
200
0
    if (drv->id == UCLASS_BOOTMETH) {
201
0
      const char *name = drv->name;
202
203
0
      if (!strncmp("bootmeth_", name, 9))
204
0
        name += 9;
205
0
      ret = device_bind(bootstd, drv, name, 0, ofnode_null(),
206
0
            &dev);
207
0
      if (ret)
208
0
        return log_msg_ret("meth", ret);
209
0
    }
210
0
  }
211
212
0
  return 0;
213
0
}
214
215
static const struct udevice_id bootstd_ids[] = {
216
  { .compatible = "u-boot,boot-std" },
217
  { }
218
};
219
220
U_BOOT_DRIVER(bootstd_drv) = {
221
  .id   = UCLASS_BOOTSTD,
222
  .name   = "bootstd_drv",
223
  .of_to_plat = bootstd_of_to_plat,
224
  .probe    = bootstd_probe,
225
  .remove   = bootstd_remove,
226
  .of_match = bootstd_ids,
227
  .priv_auto  = sizeof(struct bootstd_priv),
228
};
229
230
UCLASS_DRIVER(bootstd) = {
231
  .id   = UCLASS_BOOTSTD,
232
  .name   = "bootstd",
233
#if CONFIG_IS_ENABLED(OF_REAL)
234
  .post_bind  = dm_scan_fdt_dev,
235
#endif
236
};