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