1 // SPDX-License-Identifier: GPL-2.0-only
4 * Copyright (C) 2017 The Chromium OS Authors <chromium-os-dev@chromium.org>
6 * This file is released under the GPLv2.
9 #include <linux/ctype.h>
10 #include <linux/delay.h>
11 #include <linux/device.h>
12 #include <linux/device-mapper.h>
13 #include <linux/init.h>
14 #include <linux/list.h>
15 #include <linux/moduleparam.h>
17 #define DM_MSG_PREFIX "init"
18 #define DM_MAX_DEVICES 256
19 #define DM_MAX_TARGETS 256
20 #define DM_MAX_STR_SIZE 4096
21 #define DM_MAX_WAITFOR 256
25 static char *waitfor
[DM_MAX_WAITFOR
];
28 * Format: dm-mod.create=<name>,<uuid>,<minor>,<flags>,<table>[,<table>+][;<name>,<uuid>,<minor>,<flags>,<table>[,<table>+]+]
29 * Table format: <start_sector> <num_sectors> <target_type> <target_args>
30 * Block devices to wait for to become available before setting up tables:
31 * dm-mod.waitfor=<device1>[,..,<deviceN>]
33 * See Documentation/admin-guide/device-mapper/dm-init.rst for dm-mod.create="..." format
39 struct dm_target_spec
*table
[DM_MAX_TARGETS
];
40 char *target_args_array
[DM_MAX_TARGETS
];
41 struct list_head list
;
44 static const char * const dm_allowed_targets
[] __initconst
= {
53 static int __init
dm_verify_target_type(const char *target
)
57 for (i
= 0; i
< ARRAY_SIZE(dm_allowed_targets
); i
++) {
58 if (!strcmp(dm_allowed_targets
[i
], target
))
64 static void __init
dm_setup_cleanup(struct list_head
*devices
)
66 struct dm_device
*dev
, *tmp
;
69 list_for_each_entry_safe(dev
, tmp
, devices
, list
) {
71 for (i
= 0; i
< dev
->dmi
.target_count
; i
++) {
73 kfree(dev
->target_args_array
[i
]);
80 * str_field_delimit - delimit a string based on a separator char.
81 * @str: the pointer to the string to delimit.
82 * @separator: char that delimits the field
84 * Find a @separator and replace it by '\0'.
85 * Remove leading and trailing spaces.
86 * Return the remainder string after the @separator.
88 static char __init
*str_field_delimit(char **str
, char separator
)
92 /* TODO: add support for escaped characters */
93 *str
= skip_spaces(*str
);
94 s
= strchr(*str
, separator
);
95 /* Delimit the field and remove trailing spaces */
99 return s
? ++s
: NULL
;
103 * dm_parse_table_entry - parse a table entry
104 * @dev: device to store the parsed information.
105 * @str: the pointer to a string with the format:
106 * <start_sector> <num_sectors> <target_type> <target_args>[, ...]
108 * Return the remainder string after the table entry, i.e, after the comma which
109 * delimits the entry or NULL if reached the end of the string.
111 static char __init
*dm_parse_table_entry(struct dm_device
*dev
, char *str
)
113 const unsigned int n
= dev
->dmi
.target_count
- 1;
114 struct dm_target_spec
*sp
;
121 /* Delimit first 3 fields that are separated by space */
122 for (i
= 0; i
< ARRAY_SIZE(field
) - 1; i
++) {
123 field
[i
+ 1] = str_field_delimit(&field
[i
], ' ');
125 return ERR_PTR(-EINVAL
);
127 /* Delimit last field that can be terminated by comma */
128 next
= str_field_delimit(&field
[i
], ',');
130 sp
= kzalloc(sizeof(*sp
), GFP_KERNEL
);
132 return ERR_PTR(-ENOMEM
);
136 if (kstrtoull(field
[0], 0, &sp
->sector_start
))
137 return ERR_PTR(-EINVAL
);
139 if (kstrtoull(field
[1], 0, &sp
->length
))
140 return ERR_PTR(-EINVAL
);
142 strscpy(sp
->target_type
, field
[2], sizeof(sp
->target_type
));
143 if (dm_verify_target_type(sp
->target_type
)) {
144 DMERR("invalid type \"%s\"", sp
->target_type
);
145 return ERR_PTR(-EINVAL
);
148 dev
->target_args_array
[n
] = kstrndup(field
[3], DM_MAX_STR_SIZE
,
150 if (!dev
->target_args_array
[n
])
151 return ERR_PTR(-ENOMEM
);
157 * dm_parse_table - parse "dm-mod.create=" table field
158 * @dev: device to store the parsed information.
159 * @str: the pointer to a string with the format:
162 static int __init
dm_parse_table(struct dm_device
*dev
, char *str
)
164 char *table_entry
= str
;
166 while (table_entry
) {
167 DMDEBUG("parsing table \"%s\"", str
);
168 if (++dev
->dmi
.target_count
> DM_MAX_TARGETS
) {
169 DMERR("too many targets %u > %d",
170 dev
->dmi
.target_count
, DM_MAX_TARGETS
);
173 table_entry
= dm_parse_table_entry(dev
, table_entry
);
174 if (IS_ERR(table_entry
)) {
175 DMERR("couldn't parse table");
176 return PTR_ERR(table_entry
);
184 * dm_parse_device_entry - parse a device entry
185 * @dev: device to store the parsed information.
186 * @str: the pointer to a string with the format:
187 * name,uuid,minor,flags,table[; ...]
189 * Return the remainder string after the table entry, i.e, after the semi-colon
190 * which delimits the entry or NULL if reached the end of the string.
192 static char __init
*dm_parse_device_entry(struct dm_device
*dev
, char *str
)
194 /* There are 5 fields: name,uuid,minor,flags,table; */
200 /* Delimit first 4 fields that are separated by comma */
201 for (i
= 0; i
< ARRAY_SIZE(field
) - 1; i
++) {
202 field
[i
+1] = str_field_delimit(&field
[i
], ',');
204 return ERR_PTR(-EINVAL
);
206 /* Delimit last field that can be delimited by semi-colon */
207 next
= str_field_delimit(&field
[i
], ';');
210 strscpy(dev
->dmi
.name
, field
[0], sizeof(dev
->dmi
.name
));
212 strscpy(dev
->dmi
.uuid
, field
[1], sizeof(dev
->dmi
.uuid
));
214 if (strlen(field
[2])) {
215 if (kstrtoull(field
[2], 0, &dev
->dmi
.dev
) ||
216 dev
->dmi
.dev
>= (1 << MINORBITS
))
217 return ERR_PTR(-EINVAL
);
218 dev
->dmi
.dev
= huge_encode_dev((dev_t
)dev
->dmi
.dev
);
219 dev
->dmi
.flags
|= DM_PERSISTENT_DEV_FLAG
;
222 if (!strcmp(field
[3], "ro"))
223 dev
->dmi
.flags
|= DM_READONLY_FLAG
;
224 else if (strcmp(field
[3], "rw"))
225 return ERR_PTR(-EINVAL
);
227 if (dm_parse_table(dev
, field
[4]))
228 return ERR_PTR(-EINVAL
);
234 * dm_parse_devices - parse "dm-mod.create=" argument
235 * @devices: list of struct dm_device to store the parsed information.
236 * @str: the pointer to a string with the format:
237 * <device>[;<device>+]
239 static int __init
dm_parse_devices(struct list_head
*devices
, char *str
)
241 unsigned long ndev
= 0;
242 struct dm_device
*dev
;
245 DMDEBUG("parsing \"%s\"", str
);
247 dev
= kzalloc(sizeof(*dev
), GFP_KERNEL
);
250 list_add_tail(&dev
->list
, devices
);
252 if (++ndev
> DM_MAX_DEVICES
) {
253 DMERR("too many devices %lu > %d",
254 ndev
, DM_MAX_DEVICES
);
258 device
= dm_parse_device_entry(dev
, device
);
259 if (IS_ERR(device
)) {
260 DMERR("couldn't parse device");
261 return PTR_ERR(device
);
269 * dm_init_init - parse "dm-mod.create=" argument and configure drivers
271 static int __init
dm_init_init(void)
273 struct dm_device
*dev
;
281 if (strlen(create
) >= DM_MAX_STR_SIZE
) {
282 DMERR("Argument is too big. Limit is %d", DM_MAX_STR_SIZE
);
285 str
= kstrndup(create
, DM_MAX_STR_SIZE
, GFP_KERNEL
);
289 r
= dm_parse_devices(&devices
, str
);
293 DMINFO("waiting for all devices to be available before creating mapped devices");
294 wait_for_device_probe();
296 for (i
= 0; i
< ARRAY_SIZE(waitfor
); i
++) {
300 DMINFO("waiting for device %s ...", waitfor
[i
]);
301 while (early_lookup_bdev(waitfor
[i
], &dev
))
307 DMINFO("all devices available");
309 list_for_each_entry(dev
, &devices
, list
) {
310 if (dm_early_create(&dev
->dmi
, dev
->table
,
311 dev
->target_args_array
))
316 dm_setup_cleanup(&devices
);
320 late_initcall(dm_init_init
);
322 module_param(create
, charp
, 0);
323 MODULE_PARM_DESC(create
, "Create a mapped device in early boot");
325 module_param_array(waitfor
, charp
, NULL
, 0);
326 MODULE_PARM_DESC(waitfor
, "Devices to wait for before setting up tables");