]>
Commit | Line | Data |
---|---|---|
f739fcd8 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
2a22d05d AG |
2 | /* |
3 | * EFI application disk support | |
4 | * | |
5 | * Copyright (c) 2016 Alexander Graf | |
2a22d05d AG |
6 | */ |
7 | ||
af457cfc HS |
8 | #define LOG_CATEGORY LOGC_EFI |
9 | ||
2a22d05d | 10 | #include <common.h> |
6dd9faf8 | 11 | #include <blk.h> |
487d756f | 12 | #include <dm.h> |
2a22d05d | 13 | #include <efi_loader.h> |
86740067 | 14 | #include <fs.h> |
af457cfc | 15 | #include <log.h> |
2a22d05d AG |
16 | #include <part.h> |
17 | #include <malloc.h> | |
18 | ||
11078bb2 HS |
19 | struct efi_system_partition efi_system_partition; |
20 | ||
dec88e41 | 21 | const efi_guid_t efi_block_io_guid = EFI_BLOCK_IO_PROTOCOL_GUID; |
2a22d05d | 22 | |
d39646a3 HS |
23 | /** |
24 | * struct efi_disk_obj - EFI disk object | |
25 | * | |
26 | * @header: EFI object header | |
27 | * @ops: EFI disk I/O protocol interface | |
28 | * @ifname: interface name for block device | |
29 | * @dev_index: device index of block device | |
30 | * @media: block I/O media information | |
31 | * @dp: device path to the block device | |
32 | * @part: partition | |
33 | * @volume: simple file system protocol of the partition | |
34 | * @offset: offset into disk for simple partition | |
35 | * @desc: internal block device descriptor | |
36 | */ | |
2a22d05d | 37 | struct efi_disk_obj { |
d39646a3 | 38 | struct efi_object header; |
2a22d05d | 39 | struct efi_block_io ops; |
2a22d05d | 40 | const char *ifname; |
2a22d05d | 41 | int dev_index; |
2a22d05d | 42 | struct efi_block_io_media media; |
884bcf6f | 43 | struct efi_device_path *dp; |
884bcf6f | 44 | unsigned int part; |
2a92080d | 45 | struct efi_simple_file_system_protocol *volume; |
8c3df0bf | 46 | lbaint_t offset; |
884bcf6f | 47 | struct blk_desc *desc; |
2a22d05d AG |
48 | }; |
49 | ||
cda9b352 HS |
50 | /** |
51 | * efi_disk_reset() - reset block device | |
52 | * | |
53 | * This function implements the Reset service of the EFI_BLOCK_IO_PROTOCOL. | |
54 | * | |
55 | * As U-Boot's block devices do not have a reset function simply return | |
56 | * EFI_SUCCESS. | |
57 | * | |
58 | * See the Unified Extensible Firmware Interface (UEFI) specification for | |
59 | * details. | |
60 | * | |
61 | * @this: pointer to the BLOCK_IO_PROTOCOL | |
62 | * @extended_verification: extended verification | |
63 | * Return: status code | |
64 | */ | |
2a22d05d AG |
65 | static efi_status_t EFIAPI efi_disk_reset(struct efi_block_io *this, |
66 | char extended_verification) | |
67 | { | |
68 | EFI_ENTRY("%p, %x", this, extended_verification); | |
cda9b352 | 69 | return EFI_EXIT(EFI_SUCCESS); |
2a22d05d AG |
70 | } |
71 | ||
72 | enum efi_disk_direction { | |
73 | EFI_DISK_READ, | |
74 | EFI_DISK_WRITE, | |
75 | }; | |
76 | ||
a80a232e | 77 | static efi_status_t efi_disk_rw_blocks(struct efi_block_io *this, |
2a22d05d AG |
78 | u32 media_id, u64 lba, unsigned long buffer_size, |
79 | void *buffer, enum efi_disk_direction direction) | |
80 | { | |
81 | struct efi_disk_obj *diskobj; | |
82 | struct blk_desc *desc; | |
83 | int blksz; | |
84 | int blocks; | |
85 | unsigned long n; | |
86 | ||
2a22d05d | 87 | diskobj = container_of(this, struct efi_disk_obj, ops); |
f9d334bd | 88 | desc = (struct blk_desc *) diskobj->desc; |
2a22d05d AG |
89 | blksz = desc->blksz; |
90 | blocks = buffer_size / blksz; | |
8c3df0bf | 91 | lba += diskobj->offset; |
2a22d05d | 92 | |
9d3f3398 HS |
93 | EFI_PRINT("blocks=%x lba=%llx blksz=%x dir=%d\n", |
94 | blocks, lba, blksz, direction); | |
2a22d05d AG |
95 | |
96 | /* We only support full block access */ | |
97 | if (buffer_size & (blksz - 1)) | |
f59f0825 | 98 | return EFI_BAD_BUFFER_SIZE; |
2a22d05d AG |
99 | |
100 | if (direction == EFI_DISK_READ) | |
487d756f | 101 | n = blk_dread(desc, lba, blocks, buffer); |
2a22d05d | 102 | else |
487d756f | 103 | n = blk_dwrite(desc, lba, blocks, buffer); |
2a22d05d AG |
104 | |
105 | /* We don't do interrupts, so check for timers cooperatively */ | |
106 | efi_timer_check(); | |
107 | ||
9d3f3398 | 108 | EFI_PRINT("n=%lx blocks=%x\n", n, blocks); |
edcef3ba | 109 | |
2a22d05d | 110 | if (n != blocks) |
3304990b | 111 | return EFI_DEVICE_ERROR; |
2a22d05d | 112 | |
3304990b | 113 | return EFI_SUCCESS; |
2a22d05d AG |
114 | } |
115 | ||
55976b7f HS |
116 | /** |
117 | * efi_disk_read_blocks() - reads blocks from device | |
118 | * | |
119 | * This function implements the ReadBlocks service of the EFI_BLOCK_IO_PROTOCOL. | |
120 | * | |
121 | * See the Unified Extensible Firmware Interface (UEFI) specification for | |
122 | * details. | |
123 | * | |
124 | * @this: pointer to the BLOCK_IO_PROTOCOL | |
125 | * @media_id: id of the medium to be read from | |
126 | * @lba: starting logical block for reading | |
127 | * @buffer_size: size of the read buffer | |
128 | * @buffer: pointer to the destination buffer | |
129 | * Return: status code | |
130 | */ | |
e275458c | 131 | static efi_status_t EFIAPI efi_disk_read_blocks(struct efi_block_io *this, |
4f94865b | 132 | u32 media_id, u64 lba, efi_uintn_t buffer_size, |
2a22d05d AG |
133 | void *buffer) |
134 | { | |
51735ae0 AG |
135 | void *real_buffer = buffer; |
136 | efi_status_t r; | |
137 | ||
f59f0825 HS |
138 | if (!this) |
139 | return EFI_INVALID_PARAMETER; | |
140 | /* TODO: check for media changes */ | |
141 | if (media_id != this->media->media_id) | |
142 | return EFI_MEDIA_CHANGED; | |
143 | if (!this->media->media_present) | |
144 | return EFI_NO_MEDIA; | |
688e8825 HS |
145 | /* media->io_align is a power of 2 or 0 */ |
146 | if (this->media->io_align && | |
147 | (uintptr_t)buffer & (this->media->io_align - 1)) | |
f59f0825 HS |
148 | return EFI_INVALID_PARAMETER; |
149 | if (lba * this->media->block_size + buffer_size > | |
150 | this->media->last_block * this->media->block_size) | |
151 | return EFI_INVALID_PARAMETER; | |
152 | ||
51735ae0 AG |
153 | #ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER |
154 | if (buffer_size > EFI_LOADER_BOUNCE_BUFFER_SIZE) { | |
155 | r = efi_disk_read_blocks(this, media_id, lba, | |
156 | EFI_LOADER_BOUNCE_BUFFER_SIZE, buffer); | |
157 | if (r != EFI_SUCCESS) | |
158 | return r; | |
159 | return efi_disk_read_blocks(this, media_id, lba + | |
160 | EFI_LOADER_BOUNCE_BUFFER_SIZE / this->media->block_size, | |
161 | buffer_size - EFI_LOADER_BOUNCE_BUFFER_SIZE, | |
162 | buffer + EFI_LOADER_BOUNCE_BUFFER_SIZE); | |
163 | } | |
164 | ||
165 | real_buffer = efi_bounce_buffer; | |
166 | #endif | |
167 | ||
dee37fc9 | 168 | EFI_ENTRY("%p, %x, %llx, %zx, %p", this, media_id, lba, |
51735ae0 AG |
169 | buffer_size, buffer); |
170 | ||
171 | r = efi_disk_rw_blocks(this, media_id, lba, buffer_size, real_buffer, | |
172 | EFI_DISK_READ); | |
173 | ||
174 | /* Copy from bounce buffer to real buffer if necessary */ | |
175 | if ((r == EFI_SUCCESS) && (real_buffer != buffer)) | |
176 | memcpy(buffer, real_buffer, buffer_size); | |
177 | ||
178 | return EFI_EXIT(r); | |
2a22d05d AG |
179 | } |
180 | ||
55976b7f HS |
181 | /** |
182 | * efi_disk_write_blocks() - writes blocks to device | |
183 | * | |
184 | * This function implements the WriteBlocks service of the | |
185 | * EFI_BLOCK_IO_PROTOCOL. | |
186 | * | |
187 | * See the Unified Extensible Firmware Interface (UEFI) specification for | |
188 | * details. | |
189 | * | |
190 | * @this: pointer to the BLOCK_IO_PROTOCOL | |
191 | * @media_id: id of the medium to be written to | |
192 | * @lba: starting logical block for writing | |
193 | * @buffer_size: size of the write buffer | |
194 | * @buffer: pointer to the source buffer | |
195 | * Return: status code | |
196 | */ | |
e275458c | 197 | static efi_status_t EFIAPI efi_disk_write_blocks(struct efi_block_io *this, |
4f94865b | 198 | u32 media_id, u64 lba, efi_uintn_t buffer_size, |
2a22d05d AG |
199 | void *buffer) |
200 | { | |
51735ae0 AG |
201 | void *real_buffer = buffer; |
202 | efi_status_t r; | |
203 | ||
f59f0825 HS |
204 | if (!this) |
205 | return EFI_INVALID_PARAMETER; | |
206 | if (this->media->read_only) | |
207 | return EFI_WRITE_PROTECTED; | |
208 | /* TODO: check for media changes */ | |
209 | if (media_id != this->media->media_id) | |
210 | return EFI_MEDIA_CHANGED; | |
211 | if (!this->media->media_present) | |
212 | return EFI_NO_MEDIA; | |
688e8825 HS |
213 | /* media->io_align is a power of 2 or 0 */ |
214 | if (this->media->io_align && | |
215 | (uintptr_t)buffer & (this->media->io_align - 1)) | |
f59f0825 HS |
216 | return EFI_INVALID_PARAMETER; |
217 | if (lba * this->media->block_size + buffer_size > | |
218 | this->media->last_block * this->media->block_size) | |
219 | return EFI_INVALID_PARAMETER; | |
220 | ||
51735ae0 AG |
221 | #ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER |
222 | if (buffer_size > EFI_LOADER_BOUNCE_BUFFER_SIZE) { | |
223 | r = efi_disk_write_blocks(this, media_id, lba, | |
224 | EFI_LOADER_BOUNCE_BUFFER_SIZE, buffer); | |
225 | if (r != EFI_SUCCESS) | |
226 | return r; | |
227 | return efi_disk_write_blocks(this, media_id, lba + | |
228 | EFI_LOADER_BOUNCE_BUFFER_SIZE / this->media->block_size, | |
229 | buffer_size - EFI_LOADER_BOUNCE_BUFFER_SIZE, | |
230 | buffer + EFI_LOADER_BOUNCE_BUFFER_SIZE); | |
231 | } | |
232 | ||
233 | real_buffer = efi_bounce_buffer; | |
234 | #endif | |
235 | ||
dee37fc9 | 236 | EFI_ENTRY("%p, %x, %llx, %zx, %p", this, media_id, lba, |
51735ae0 AG |
237 | buffer_size, buffer); |
238 | ||
239 | /* Populate bounce buffer if necessary */ | |
240 | if (real_buffer != buffer) | |
241 | memcpy(real_buffer, buffer, buffer_size); | |
242 | ||
243 | r = efi_disk_rw_blocks(this, media_id, lba, buffer_size, real_buffer, | |
244 | EFI_DISK_WRITE); | |
245 | ||
246 | return EFI_EXIT(r); | |
2a22d05d AG |
247 | } |
248 | ||
55976b7f HS |
249 | /** |
250 | * efi_disk_flush_blocks() - flushes modified data to the device | |
251 | * | |
252 | * This function implements the FlushBlocks service of the | |
253 | * EFI_BLOCK_IO_PROTOCOL. | |
254 | * | |
255 | * As we always write synchronously nothing is done here. | |
256 | * | |
257 | * See the Unified Extensible Firmware Interface (UEFI) specification for | |
258 | * details. | |
259 | * | |
260 | * @this: pointer to the BLOCK_IO_PROTOCOL | |
261 | * Return: status code | |
262 | */ | |
2a22d05d AG |
263 | static efi_status_t EFIAPI efi_disk_flush_blocks(struct efi_block_io *this) |
264 | { | |
2a22d05d AG |
265 | EFI_ENTRY("%p", this); |
266 | return EFI_EXIT(EFI_SUCCESS); | |
267 | } | |
268 | ||
269 | static const struct efi_block_io block_io_disk_template = { | |
270 | .reset = &efi_disk_reset, | |
271 | .read_blocks = &efi_disk_read_blocks, | |
272 | .write_blocks = &efi_disk_write_blocks, | |
273 | .flush_blocks = &efi_disk_flush_blocks, | |
274 | }; | |
275 | ||
47a95963 HS |
276 | /** |
277 | * efi_fs_from_path() - retrieve simple file system protocol | |
278 | * | |
279 | * Gets the simple file system protocol for a file device path. | |
110d80a1 HS |
280 | * |
281 | * The full path provided is split into device part and into a file | |
282 | * part. The device part is used to find the handle on which the | |
283 | * simple file system protocol is installed. | |
284 | * | |
47a95963 HS |
285 | * @full_path: device path including device and file |
286 | * Return: simple file system protocol | |
2a92080d RC |
287 | */ |
288 | struct efi_simple_file_system_protocol * | |
110d80a1 | 289 | efi_fs_from_path(struct efi_device_path *full_path) |
2a92080d RC |
290 | { |
291 | struct efi_object *efiobj; | |
110d80a1 HS |
292 | struct efi_handler *handler; |
293 | struct efi_device_path *device_path; | |
294 | struct efi_device_path *file_path; | |
295 | efi_status_t ret; | |
2a92080d | 296 | |
110d80a1 HS |
297 | /* Split the path into a device part and a file part */ |
298 | ret = efi_dp_split_file_path(full_path, &device_path, &file_path); | |
299 | if (ret != EFI_SUCCESS) | |
300 | return NULL; | |
301 | efi_free_pool(file_path); | |
302 | ||
303 | /* Get the EFI object for the partition */ | |
304 | efiobj = efi_dp_find_obj(device_path, NULL); | |
305 | efi_free_pool(device_path); | |
2a92080d RC |
306 | if (!efiobj) |
307 | return NULL; | |
308 | ||
110d80a1 HS |
309 | /* Find the simple file system protocol */ |
310 | ret = efi_search_protocol(efiobj, &efi_simple_file_system_protocol_guid, | |
311 | &handler); | |
312 | if (ret != EFI_SUCCESS) | |
313 | return NULL; | |
2a92080d | 314 | |
110d80a1 HS |
315 | /* Return the simple file system protocol for the partition */ |
316 | return handler->protocol_interface; | |
2a92080d RC |
317 | } |
318 | ||
86740067 AT |
319 | /** |
320 | * efi_fs_exists() - check if a partition bears a file system | |
321 | * | |
322 | * @desc: block device descriptor | |
323 | * @part: partition number | |
324 | * Return: 1 if a file system exists on the partition | |
325 | * 0 otherwise | |
326 | */ | |
327 | static int efi_fs_exists(struct blk_desc *desc, int part) | |
328 | { | |
329 | if (fs_set_blk_dev_with_part(desc, part)) | |
330 | return 0; | |
331 | ||
332 | if (fs_get_type() == FS_TYPE_ANY) | |
333 | return 0; | |
334 | ||
335 | fs_close(); | |
336 | ||
337 | return 1; | |
338 | } | |
339 | ||
55976b7f | 340 | /** |
47a95963 | 341 | * efi_disk_add_dev() - create a handle for a partition or disk |
93945f2c | 342 | * |
47a95963 HS |
343 | * @parent: parent handle |
344 | * @dp_parent: parent device path | |
345 | * @if_typename: interface name for block device | |
346 | * @desc: internal block device | |
347 | * @dev_index: device index for block device | |
8d0949b3 | 348 | * @part_info: partition info |
55976b7f HS |
349 | * @part: partition |
350 | * @disk: pointer to receive the created handle | |
47a95963 | 351 | * Return: disk object |
93945f2c | 352 | */ |
df9cf561 | 353 | static efi_status_t efi_disk_add_dev( |
64e4db0f HS |
354 | efi_handle_t parent, |
355 | struct efi_device_path *dp_parent, | |
356 | const char *if_typename, | |
357 | struct blk_desc *desc, | |
358 | int dev_index, | |
8d0949b3 | 359 | struct disk_partition *part_info, |
df9cf561 HS |
360 | unsigned int part, |
361 | struct efi_disk_obj **disk) | |
4a12a97c AG |
362 | { |
363 | struct efi_disk_obj *diskobj; | |
0a87e05d | 364 | struct efi_object *handle; |
4b9f7aaf | 365 | efi_status_t ret; |
4a12a97c | 366 | |
0812d1a0 AG |
367 | /* Don't add empty devices */ |
368 | if (!desc->lba) | |
df9cf561 | 369 | return EFI_NOT_READY; |
0812d1a0 | 370 | |
884bcf6f | 371 | diskobj = calloc(1, sizeof(*diskobj)); |
4b9f7aaf | 372 | if (!diskobj) |
df9cf561 | 373 | return EFI_OUT_OF_RESOURCES; |
4b9f7aaf HS |
374 | |
375 | /* Hook up to the device list */ | |
d39646a3 | 376 | efi_add_handle(&diskobj->header); |
4a12a97c AG |
377 | |
378 | /* Fill in object data */ | |
8d0949b3 | 379 | if (part_info) { |
64e4db0f | 380 | struct efi_device_path *node = efi_dp_part_node(desc, part); |
26448513 HS |
381 | struct efi_handler *handler; |
382 | void *protocol_interface; | |
383 | ||
384 | /* Parent must expose EFI_BLOCK_IO_PROTOCOL */ | |
385 | ret = efi_search_protocol(parent, &efi_block_io_guid, &handler); | |
386 | if (ret != EFI_SUCCESS) | |
387 | goto error; | |
388 | ||
389 | /* | |
390 | * Link the partition (child controller) to the block device | |
391 | * (controller). | |
392 | */ | |
393 | ret = efi_protocol_open(handler, &protocol_interface, NULL, | |
394 | &diskobj->header, | |
395 | EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER); | |
396 | if (ret != EFI_SUCCESS) | |
397 | goto error; | |
64e4db0f HS |
398 | |
399 | diskobj->dp = efi_dp_append_node(dp_parent, node); | |
400 | efi_free_pool(node); | |
8d0949b3 HS |
401 | diskobj->offset = part_info->start; |
402 | diskobj->media.last_block = part_info->size - 1; | |
64e4db0f HS |
403 | } else { |
404 | diskobj->dp = efi_dp_from_part(desc, part); | |
8d0949b3 HS |
405 | diskobj->offset = 0; |
406 | diskobj->media.last_block = desc->lba - 1; | |
64e4db0f | 407 | } |
884bcf6f | 408 | diskobj->part = part; |
0a87e05d HS |
409 | |
410 | /* | |
411 | * Install the device path and the block IO protocol. | |
412 | * | |
413 | * InstallMultipleProtocolInterfaces() checks if the device path is | |
414 | * already installed on an other handle and returns EFI_ALREADY_STARTED | |
415 | * in this case. | |
416 | */ | |
417 | handle = &diskobj->header; | |
418 | ret = EFI_CALL(efi_install_multiple_protocol_interfaces( | |
419 | &handle, &efi_guid_device_path, diskobj->dp, | |
420 | &efi_block_io_guid, &diskobj->ops, NULL)); | |
4b9f7aaf | 421 | if (ret != EFI_SUCCESS) |
df9cf561 | 422 | return ret; |
0a87e05d HS |
423 | |
424 | /* | |
425 | * On partitions or whole disks without partitions install the | |
426 | * simple file system protocol if a file system is available. | |
427 | */ | |
89cb6a5d AT |
428 | if ((part || desc->part_type == PART_TYPE_UNKNOWN) && |
429 | efi_fs_exists(desc, part)) { | |
2a92080d RC |
430 | diskobj->volume = efi_simple_file_system(desc, part, |
431 | diskobj->dp); | |
d39646a3 | 432 | ret = efi_add_protocol(&diskobj->header, |
4b9f7aaf | 433 | &efi_simple_file_system_protocol_guid, |
22de1de9 | 434 | diskobj->volume); |
4b9f7aaf | 435 | if (ret != EFI_SUCCESS) |
df9cf561 | 436 | return ret; |
2a92080d | 437 | } |
4a12a97c | 438 | diskobj->ops = block_io_disk_template; |
487d756f | 439 | diskobj->ifname = if_typename; |
4a12a97c | 440 | diskobj->dev_index = dev_index; |
f9d334bd | 441 | diskobj->desc = desc; |
4a12a97c AG |
442 | |
443 | /* Fill in EFI IO Media info (for read/write callbacks) */ | |
444 | diskobj->media.removable_media = desc->removable; | |
445 | diskobj->media.media_present = 1; | |
f59f0825 HS |
446 | /* |
447 | * MediaID is just an arbitrary counter. | |
448 | * We have to change it if the medium is removed or changed. | |
449 | */ | |
450 | diskobj->media.media_id = 1; | |
4a12a97c AG |
451 | diskobj->media.block_size = desc->blksz; |
452 | diskobj->media.io_align = desc->blksz; | |
9f888969 | 453 | if (part) |
5f770836 | 454 | diskobj->media.logical_partition = 1; |
4a12a97c | 455 | diskobj->ops.media = &diskobj->media; |
df9cf561 HS |
456 | if (disk) |
457 | *disk = diskobj; | |
11078bb2 | 458 | |
8d0949b3 HS |
459 | EFI_PRINT("BlockIO: part %u, present %d, logical %d, removable %d" |
460 | ", offset " LBAF ", last_block %llu\n", | |
461 | diskobj->part, | |
462 | diskobj->media.media_present, | |
463 | diskobj->media.logical_partition, | |
464 | diskobj->media.removable_media, | |
465 | diskobj->offset, | |
466 | diskobj->media.last_block); | |
467 | ||
11078bb2 HS |
468 | /* Store first EFI system partition */ |
469 | if (part && !efi_system_partition.if_type) { | |
470 | int r; | |
0528979f | 471 | struct disk_partition info; |
11078bb2 HS |
472 | |
473 | r = part_get_info(desc, part, &info); | |
474 | if (r) | |
475 | return EFI_DEVICE_ERROR; | |
476 | if (info.bootable & PART_EFI_SYSTEM_PARTITION) { | |
477 | efi_system_partition.if_type = desc->if_type; | |
478 | efi_system_partition.devnum = desc->devnum; | |
479 | efi_system_partition.part = part; | |
480 | EFI_PRINT("EFI system partition: %s %d:%d\n", | |
481 | blk_get_if_type_name(desc->if_type), | |
482 | desc->devnum, part); | |
483 | } | |
484 | } | |
df9cf561 | 485 | return EFI_SUCCESS; |
26448513 HS |
486 | error: |
487 | efi_delete_handle(&diskobj->header); | |
488 | return ret; | |
4a12a97c AG |
489 | } |
490 | ||
47a95963 HS |
491 | /** |
492 | * efi_disk_create_partitions() - create handles and protocols for partitions | |
64e4db0f | 493 | * |
47a95963 HS |
494 | * Create handles and protocols for the partitions of a block device. |
495 | * | |
496 | * @parent: handle of the parent disk | |
55976b7f | 497 | * @desc: block device |
47a95963 HS |
498 | * @if_typename: interface type |
499 | * @diskid: device number | |
500 | * @pdevname: device name | |
501 | * Return: number of partitions created | |
64e4db0f HS |
502 | */ |
503 | int efi_disk_create_partitions(efi_handle_t parent, struct blk_desc *desc, | |
504 | const char *if_typename, int diskid, | |
505 | const char *pdevname) | |
8c3df0bf AG |
506 | { |
507 | int disks = 0; | |
ecbe1a07 | 508 | char devname[32] = { 0 }; /* dp->str is u16[32] long */ |
16a73b24 | 509 | int part; |
64e4db0f HS |
510 | struct efi_device_path *dp = NULL; |
511 | efi_status_t ret; | |
512 | struct efi_handler *handler; | |
513 | ||
514 | /* Get the device path of the parent */ | |
515 | ret = efi_search_protocol(parent, &efi_guid_device_path, &handler); | |
516 | if (ret == EFI_SUCCESS) | |
517 | dp = handler->protocol_interface; | |
8c3df0bf | 518 | |
c034b7fd | 519 | /* Add devices for each partition */ |
16a73b24 | 520 | for (part = 1; part <= MAX_SEARCH_PARTITIONS; part++) { |
8d0949b3 HS |
521 | struct disk_partition info; |
522 | ||
16a73b24 JG |
523 | if (part_get_info(desc, part, &info)) |
524 | continue; | |
f9d334bd AG |
525 | snprintf(devname, sizeof(devname), "%s:%d", pdevname, |
526 | part); | |
df9cf561 | 527 | ret = efi_disk_add_dev(parent, dp, if_typename, desc, diskid, |
8d0949b3 | 528 | &info, part, NULL); |
df9cf561 | 529 | if (ret != EFI_SUCCESS) { |
af457cfc | 530 | log_err("Adding partition %s failed\n", pdevname); |
df9cf561 HS |
531 | continue; |
532 | } | |
8c3df0bf AG |
533 | disks++; |
534 | } | |
884bcf6f | 535 | |
8c3df0bf AG |
536 | return disks; |
537 | } | |
538 | ||
47a95963 HS |
539 | /** |
540 | * efi_disk_register() - register block devices | |
541 | * | |
2a22d05d AG |
542 | * U-Boot doesn't have a list of all online disk devices. So when running our |
543 | * EFI payload, we scan through all of the potentially available ones and | |
544 | * store them in our object pool. | |
545 | * | |
47a95963 HS |
546 | * This function is called in efi_init_obj_list(). |
547 | * | |
487d756f SG |
548 | * TODO([email protected]): Actually with CONFIG_BLK, U-Boot does have this. |
549 | * Consider converting the code to look up devices as needed. The EFI device | |
550 | * could be a child of the UCLASS_BLK block device, perhaps. | |
551 | * | |
47a95963 | 552 | * Return: status code |
2a22d05d | 553 | */ |
df9cf561 | 554 | efi_status_t efi_disk_register(void) |
2a22d05d | 555 | { |
64e4db0f | 556 | struct efi_disk_obj *disk; |
2a22d05d | 557 | int disks = 0; |
df9cf561 | 558 | efi_status_t ret; |
487d756f SG |
559 | #ifdef CONFIG_BLK |
560 | struct udevice *dev; | |
561 | ||
df9cf561 | 562 | for (uclass_first_device_check(UCLASS_BLK, &dev); dev; |
70bfcdc6 | 563 | uclass_next_device_check(&dev)) { |
caa4daa2 | 564 | struct blk_desc *desc = dev_get_uclass_plat(dev); |
9bfca9f9 | 565 | const char *if_typename = blk_get_if_type_name(desc->if_type); |
487d756f | 566 | |
c034b7fd | 567 | /* Add block device for the full device */ |
af457cfc | 568 | log_info("Scanning disk %s...\n", dev->name); |
df9cf561 | 569 | ret = efi_disk_add_dev(NULL, NULL, if_typename, |
8d0949b3 | 570 | desc, desc->devnum, NULL, 0, &disk); |
df9cf561 | 571 | if (ret == EFI_NOT_READY) { |
af457cfc | 572 | log_notice("Disk %s not ready\n", dev->name); |
df9cf561 HS |
573 | continue; |
574 | } | |
575 | if (ret) { | |
af457cfc HS |
576 | log_err("ERROR: failure to add disk device %s, r = %lu\n", |
577 | dev->name, ret & ~EFI_ERROR_MASK); | |
df9cf561 HS |
578 | return ret; |
579 | } | |
487d756f SG |
580 | disks++; |
581 | ||
c034b7fd | 582 | /* Partitions show up as block devices in EFI */ |
64e4db0f | 583 | disks += efi_disk_create_partitions( |
d39646a3 | 584 | &disk->header, desc, if_typename, |
64e4db0f | 585 | desc->devnum, dev->name); |
487d756f SG |
586 | } |
587 | #else | |
588 | int i, if_type; | |
2a22d05d AG |
589 | |
590 | /* Search for all available disk devices */ | |
6dd9faf8 | 591 | for (if_type = 0; if_type < IF_TYPE_COUNT; if_type++) { |
487d756f SG |
592 | const struct blk_driver *cur_drvr; |
593 | const char *if_typename; | |
594 | ||
6dd9faf8 SG |
595 | cur_drvr = blk_driver_lookup_type(if_type); |
596 | if (!cur_drvr) | |
597 | continue; | |
598 | ||
487d756f | 599 | if_typename = cur_drvr->if_typename; |
af457cfc | 600 | log_info("Scanning disks on %s...\n", if_typename); |
2a22d05d AG |
601 | for (i = 0; i < 4; i++) { |
602 | struct blk_desc *desc; | |
ecbe1a07 | 603 | char devname[32] = { 0 }; /* dp->str is u16[32] long */ |
2a22d05d | 604 | |
6dd9faf8 | 605 | desc = blk_get_devnum_by_type(if_type, i); |
2a22d05d AG |
606 | if (!desc) |
607 | continue; | |
608 | if (desc->type == DEV_TYPE_UNKNOWN) | |
609 | continue; | |
610 | ||
2a22d05d | 611 | snprintf(devname, sizeof(devname), "%s%d", |
487d756f | 612 | if_typename, i); |
77511b3b | 613 | |
c034b7fd | 614 | /* Add block device for the full device */ |
df9cf561 | 615 | ret = efi_disk_add_dev(NULL, NULL, if_typename, desc, |
8d0949b3 | 616 | i, NULL, 0, &disk); |
df9cf561 | 617 | if (ret == EFI_NOT_READY) { |
af457cfc | 618 | log_notice("Disk %s not ready\n", devname); |
df9cf561 HS |
619 | continue; |
620 | } | |
621 | if (ret) { | |
af457cfc HS |
622 | log_err("ERROR: failure to add disk device %s, r = %lu\n", |
623 | devname, ret & ~EFI_ERROR_MASK); | |
df9cf561 HS |
624 | return ret; |
625 | } | |
2a22d05d | 626 | disks++; |
8c3df0bf | 627 | |
c034b7fd | 628 | /* Partitions show up as block devices in EFI */ |
fae0118e | 629 | disks += efi_disk_create_partitions |
d39646a3 | 630 | (&disk->header, desc, |
fae0118e | 631 | if_typename, i, devname); |
2a22d05d AG |
632 | } |
633 | } | |
487d756f | 634 | #endif |
af457cfc | 635 | log_info("Found %d disks\n", disks); |
2a22d05d | 636 | |
df9cf561 | 637 | return EFI_SUCCESS; |
2a22d05d | 638 | } |
41fd5068 AT |
639 | |
640 | /** | |
641 | * efi_disk_is_system_part() - check if handle refers to an EFI system partition | |
642 | * | |
643 | * @handle: handle of partition | |
644 | * | |
645 | * Return: true if handle refers to an EFI system partition | |
646 | */ | |
647 | bool efi_disk_is_system_part(efi_handle_t handle) | |
648 | { | |
649 | struct efi_handler *handler; | |
650 | struct efi_disk_obj *diskobj; | |
0528979f | 651 | struct disk_partition info; |
41fd5068 AT |
652 | efi_status_t ret; |
653 | int r; | |
654 | ||
655 | /* check if this is a block device */ | |
656 | ret = efi_search_protocol(handle, &efi_block_io_guid, &handler); | |
657 | if (ret != EFI_SUCCESS) | |
658 | return false; | |
659 | ||
660 | diskobj = container_of(handle, struct efi_disk_obj, header); | |
661 | ||
662 | r = part_get_info(diskobj->desc, diskobj->part, &info); | |
663 | if (r) | |
664 | return false; | |
665 | ||
666 | return !!(info.bootable & PART_EFI_SYSTEM_PARTITION); | |
667 | } |