]>
Commit | Line | Data |
---|---|---|
2a22d05d AG |
1 | /* |
2 | * EFI application disk support | |
3 | * | |
4 | * Copyright (c) 2016 Alexander Graf | |
5 | * | |
6 | * SPDX-License-Identifier: GPL-2.0+ | |
7 | */ | |
8 | ||
9 | #include <common.h> | |
10 | #include <efi_loader.h> | |
11 | #include <inttypes.h> | |
12 | #include <part.h> | |
13 | #include <malloc.h> | |
14 | ||
15 | static const efi_guid_t efi_block_io_guid = BLOCK_IO_GUID; | |
16 | ||
17 | struct efi_disk_obj { | |
18 | /* Generic EFI object parent class data */ | |
19 | struct efi_object parent; | |
20 | /* EFI Interface callback struct for block I/O */ | |
21 | struct efi_block_io ops; | |
22 | /* U-Boot ifname for block device */ | |
23 | const char *ifname; | |
24 | /* U-Boot dev_index for block device */ | |
25 | int dev_index; | |
26 | /* EFI Interface Media descriptor struct, referenced by ops */ | |
27 | struct efi_block_io_media media; | |
28 | /* EFI device path to this block device */ | |
29 | struct efi_device_path_file_path *dp; | |
8c3df0bf AG |
30 | /* Offset into disk for simple partitions */ |
31 | lbaint_t offset; | |
2a22d05d AG |
32 | }; |
33 | ||
2a22d05d AG |
34 | static efi_status_t efi_disk_open_block(void *handle, efi_guid_t *protocol, |
35 | void **protocol_interface, void *agent_handle, | |
36 | void *controller_handle, uint32_t attributes) | |
37 | { | |
38 | struct efi_disk_obj *diskobj = handle; | |
39 | ||
40 | *protocol_interface = &diskobj->ops; | |
41 | ||
42 | return EFI_SUCCESS; | |
43 | } | |
44 | ||
45 | static efi_status_t efi_disk_open_dp(void *handle, efi_guid_t *protocol, | |
46 | void **protocol_interface, void *agent_handle, | |
47 | void *controller_handle, uint32_t attributes) | |
48 | { | |
49 | struct efi_disk_obj *diskobj = handle; | |
50 | ||
51 | *protocol_interface = diskobj->dp; | |
52 | ||
53 | return EFI_SUCCESS; | |
54 | } | |
55 | ||
56 | static efi_status_t EFIAPI efi_disk_reset(struct efi_block_io *this, | |
57 | char extended_verification) | |
58 | { | |
59 | EFI_ENTRY("%p, %x", this, extended_verification); | |
60 | return EFI_EXIT(EFI_DEVICE_ERROR); | |
61 | } | |
62 | ||
63 | enum efi_disk_direction { | |
64 | EFI_DISK_READ, | |
65 | EFI_DISK_WRITE, | |
66 | }; | |
67 | ||
68 | static efi_status_t EFIAPI efi_disk_rw_blocks(struct efi_block_io *this, | |
69 | u32 media_id, u64 lba, unsigned long buffer_size, | |
70 | void *buffer, enum efi_disk_direction direction) | |
71 | { | |
72 | struct efi_disk_obj *diskobj; | |
73 | struct blk_desc *desc; | |
74 | int blksz; | |
75 | int blocks; | |
76 | unsigned long n; | |
77 | ||
78 | EFI_ENTRY("%p, %x, %"PRIx64", %lx, %p", this, media_id, lba, | |
79 | buffer_size, buffer); | |
80 | ||
81 | diskobj = container_of(this, struct efi_disk_obj, ops); | |
82 | if (!(desc = blk_get_dev(diskobj->ifname, diskobj->dev_index))) | |
83 | return EFI_EXIT(EFI_DEVICE_ERROR); | |
84 | blksz = desc->blksz; | |
85 | blocks = buffer_size / blksz; | |
8c3df0bf | 86 | lba += diskobj->offset; |
2a22d05d AG |
87 | |
88 | #ifdef DEBUG_EFI | |
89 | printf("EFI: %s:%d blocks=%x lba=%"PRIx64" blksz=%x dir=%d\n", __func__, | |
90 | __LINE__, blocks, lba, blksz, direction); | |
91 | #endif | |
92 | ||
93 | /* We only support full block access */ | |
94 | if (buffer_size & (blksz - 1)) | |
95 | return EFI_EXIT(EFI_DEVICE_ERROR); | |
96 | ||
97 | if (direction == EFI_DISK_READ) | |
98 | n = desc->block_read(desc, lba, blocks, buffer); | |
99 | else | |
100 | n = desc->block_write(desc, lba, blocks, buffer); | |
101 | ||
102 | /* We don't do interrupts, so check for timers cooperatively */ | |
103 | efi_timer_check(); | |
104 | ||
105 | #ifdef DEBUG_EFI | |
106 | printf("EFI: %s:%d n=%lx blocks=%x\n", __func__, __LINE__, n, blocks); | |
107 | #endif | |
108 | if (n != blocks) | |
109 | return EFI_EXIT(EFI_DEVICE_ERROR); | |
110 | ||
111 | return EFI_EXIT(EFI_SUCCESS); | |
112 | } | |
113 | ||
114 | static efi_status_t efi_disk_read_blocks(struct efi_block_io *this, | |
115 | u32 media_id, u64 lba, unsigned long buffer_size, | |
116 | void *buffer) | |
117 | { | |
118 | return efi_disk_rw_blocks(this, media_id, lba, buffer_size, buffer, | |
119 | EFI_DISK_READ); | |
120 | } | |
121 | ||
122 | static efi_status_t efi_disk_write_blocks(struct efi_block_io *this, | |
123 | u32 media_id, u64 lba, unsigned long buffer_size, | |
124 | void *buffer) | |
125 | { | |
126 | return efi_disk_rw_blocks(this, media_id, lba, buffer_size, buffer, | |
127 | EFI_DISK_WRITE); | |
128 | } | |
129 | ||
130 | static efi_status_t EFIAPI efi_disk_flush_blocks(struct efi_block_io *this) | |
131 | { | |
132 | /* We always write synchronously */ | |
133 | EFI_ENTRY("%p", this); | |
134 | return EFI_EXIT(EFI_SUCCESS); | |
135 | } | |
136 | ||
137 | static const struct efi_block_io block_io_disk_template = { | |
138 | .reset = &efi_disk_reset, | |
139 | .read_blocks = &efi_disk_read_blocks, | |
140 | .write_blocks = &efi_disk_write_blocks, | |
141 | .flush_blocks = &efi_disk_flush_blocks, | |
142 | }; | |
143 | ||
4a12a97c AG |
144 | static void efi_disk_add_dev(char *name, |
145 | const struct block_drvr *cur_drvr, | |
146 | const struct blk_desc *desc, | |
147 | int dev_index, | |
148 | lbaint_t offset) | |
149 | { | |
150 | struct efi_disk_obj *diskobj; | |
151 | struct efi_device_path_file_path *dp; | |
152 | int objlen = sizeof(*diskobj) + (sizeof(*dp) * 2); | |
153 | ||
154 | diskobj = calloc(1, objlen); | |
155 | ||
156 | /* Fill in object data */ | |
157 | diskobj->parent.protocols[0].guid = &efi_block_io_guid; | |
158 | diskobj->parent.protocols[0].open = efi_disk_open_block; | |
159 | diskobj->parent.protocols[1].guid = &efi_guid_device_path; | |
160 | diskobj->parent.protocols[1].open = efi_disk_open_dp; | |
161 | diskobj->parent.handle = diskobj; | |
162 | diskobj->ops = block_io_disk_template; | |
163 | diskobj->ifname = cur_drvr->name; | |
164 | diskobj->dev_index = dev_index; | |
8c3df0bf | 165 | diskobj->offset = offset; |
4a12a97c AG |
166 | |
167 | /* Fill in EFI IO Media info (for read/write callbacks) */ | |
168 | diskobj->media.removable_media = desc->removable; | |
169 | diskobj->media.media_present = 1; | |
170 | diskobj->media.block_size = desc->blksz; | |
171 | diskobj->media.io_align = desc->blksz; | |
172 | diskobj->media.last_block = desc->lba; | |
173 | diskobj->ops.media = &diskobj->media; | |
174 | ||
175 | /* Fill in device path */ | |
176 | dp = (void*)&diskobj[1]; | |
177 | diskobj->dp = dp; | |
178 | dp[0].dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE; | |
179 | dp[0].dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH; | |
180 | dp[0].dp.length = sizeof(*dp); | |
181 | ascii2unicode(dp[0].str, name); | |
182 | ||
183 | dp[1].dp.type = DEVICE_PATH_TYPE_END; | |
184 | dp[1].dp.sub_type = DEVICE_PATH_SUB_TYPE_END; | |
185 | dp[1].dp.length = sizeof(*dp); | |
186 | ||
187 | /* Hook up to the device list */ | |
188 | list_add_tail(&diskobj->parent.link, &efi_obj_list); | |
189 | } | |
190 | ||
8c3df0bf AG |
191 | static int efi_disk_create_eltorito(struct blk_desc *desc, |
192 | const struct block_drvr *cur_drvr, | |
193 | int diskid) | |
194 | { | |
195 | int disks = 0; | |
196 | #ifdef CONFIG_ISO_PARTITION | |
ecbe1a07 | 197 | char devname[32] = { 0 }; /* dp->str is u16[32] long */ |
8c3df0bf AG |
198 | disk_partition_t info; |
199 | int part = 1; | |
200 | ||
201 | if (desc->part_type != PART_TYPE_ISO) | |
202 | return 0; | |
203 | ||
204 | while (!part_get_info(desc, part, &info)) { | |
205 | snprintf(devname, sizeof(devname), "%s%d:%d", cur_drvr->name, | |
206 | diskid, part); | |
207 | efi_disk_add_dev(devname, cur_drvr, desc, diskid, info.start); | |
208 | part++; | |
209 | disks++; | |
210 | } | |
211 | #endif | |
212 | ||
213 | return disks; | |
214 | } | |
215 | ||
2a22d05d AG |
216 | /* |
217 | * U-Boot doesn't have a list of all online disk devices. So when running our | |
218 | * EFI payload, we scan through all of the potentially available ones and | |
219 | * store them in our object pool. | |
220 | * | |
221 | * This gets called from do_bootefi_exec(). | |
222 | */ | |
223 | int efi_disk_register(void) | |
224 | { | |
225 | const struct block_drvr *cur_drvr; | |
226 | int i; | |
227 | int disks = 0; | |
228 | ||
229 | /* Search for all available disk devices */ | |
230 | for (cur_drvr = block_drvr; cur_drvr->name; cur_drvr++) { | |
231 | printf("Scanning disks on %s...\n", cur_drvr->name); | |
232 | for (i = 0; i < 4; i++) { | |
233 | struct blk_desc *desc; | |
ecbe1a07 | 234 | char devname[32] = { 0 }; /* dp->str is u16[32] long */ |
2a22d05d AG |
235 | |
236 | desc = blk_get_dev(cur_drvr->name, i); | |
237 | if (!desc) | |
238 | continue; | |
239 | if (desc->type == DEV_TYPE_UNKNOWN) | |
240 | continue; | |
241 | ||
2a22d05d AG |
242 | snprintf(devname, sizeof(devname), "%s%d", |
243 | cur_drvr->name, i); | |
4a12a97c | 244 | efi_disk_add_dev(devname, cur_drvr, desc, i, 0); |
2a22d05d | 245 | disks++; |
8c3df0bf AG |
246 | |
247 | /* | |
248 | * El Torito images show up as block devices | |
249 | * in an EFI world, so let's create them here | |
250 | */ | |
251 | disks += efi_disk_create_eltorito(desc, cur_drvr, i); | |
2a22d05d AG |
252 | } |
253 | } | |
254 | printf("Found %d disks\n", disks); | |
255 | ||
256 | return 0; | |
257 | } |