Merge branch 'master' of git://git.denx.de/u-boot-samsung
[platform/kernel/u-boot.git] / lib / efi_loader / efi_disk.c
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;
30         /* Offset into disk for simple partitions */
31         lbaint_t offset;
32 };
33
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;
86         lba += diskobj->offset;
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
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;
165         diskobj->offset = offset;
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
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
197         char devname[32] = { 0 }; /* dp->str is u16[32] long */
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
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;
234                         char devname[32] = { 0 }; /* dp->str is u16[32] long */
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
242                         snprintf(devname, sizeof(devname), "%s%d",
243                                  cur_drvr->name, i);
244                         efi_disk_add_dev(devname, cur_drvr, desc, i, 0);
245                         disks++;
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);
252                 }
253         }
254         printf("Found %d disks\n", disks);
255
256         return 0;
257 }