1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
|
// SPDX-License-Identifier: GPL-2.0-only
#include <common.h>
#include <driver.h>
#include <init.h>
#include <malloc.h>
#include <fs.h>
#include <string.h>
#include <command.h>
#include <errno.h>
#include <linux/stat.h>
#include <xfuncs.h>
#include <fcntl.h>
#include <efi.h>
#include <block.h>
#include <efi/efi-payload.h>
#include <efi/efi-device.h>
#include <bootsource.h>
#define EFI_BLOCK_IO_PROTOCOL_REVISION2 0x00020001
#define EFI_BLOCK_IO_PROTOCOL_REVISION3 ((2<<16) | (31))
struct efi_bio_priv {
struct efi_block_io_protocol *protocol;
struct device *dev;
struct block_device blk;
u32 media_id;
void (*efi_info)(struct device *);
};
static int efi_bio_read(struct block_device *blk, void *buffer, sector_t block,
blkcnt_t num_blocks)
{
struct efi_bio_priv *priv = container_of(blk, struct efi_bio_priv, blk);
efi_status_t efiret;
efiret = priv->protocol->read(priv->protocol, priv->media_id,
block, num_blocks * 512, buffer);
if (EFI_ERROR(efiret))
return -efi_errno(efiret);
return 0;
}
static int efi_bio_write(struct block_device *blk,
const void *buffer, sector_t block, blkcnt_t num_blocks)
{
struct efi_bio_priv *priv = container_of(blk, struct efi_bio_priv, blk);
efi_status_t efiret;
efiret = priv->protocol->write(priv->protocol, priv->media_id,
block, num_blocks * 512, (void *)buffer);
if (EFI_ERROR(efiret))
return -efi_errno(efiret);
return 0;
}
static int efi_bio_flush(struct block_device *blk)
{
struct efi_bio_priv *priv = container_of(blk, struct efi_bio_priv, blk);
efi_status_t efiret;
efiret = priv->protocol->flush(priv->protocol);
if (EFI_ERROR(efiret))
return -efi_errno(efiret);
return 0;
}
static struct block_device_ops efi_bio_ops = {
.read = efi_bio_read,
.write = efi_bio_write,
.flush = efi_bio_flush,
};
static void efi_bio_print_info(struct device *dev)
{
struct efi_bio_priv *priv = dev->priv;
struct efi_block_io_media *media = priv->protocol->media;
u64 revision = priv->protocol->revision;
printf("Block I/O Media:\n");
printf(" revision: 0x%016llx\n", revision);
printf(" media_id: 0x%08x\n", media->media_id);
printf(" removable_media: %d\n", media->removable_media);
printf(" media_present: %d\n", media->media_present);
printf(" logical_partition: %d\n", media->logical_partition);
printf(" read_only: %d\n", media->read_only);
printf(" write_caching: %d\n", media->write_caching);
printf(" block_size: 0x%08x\n", media->block_size);
printf(" io_align: 0x%08x\n", media->io_align);
printf(" last_block: 0x%016llx\n", media->last_block);
if (revision < EFI_BLOCK_IO_PROTOCOL_REVISION2)
goto out;
printf(" lowest_aligned_lba: 0x%08llx\n",
media->lowest_aligned_lba);
printf(" logical_blocks_per_physical_block: 0x%08x\n",
media->logical_blocks_per_physical_block);
if (revision < EFI_BLOCK_IO_PROTOCOL_REVISION3)
goto out;
printf(" optimal_transfer_length_granularity: 0x%08x\n",
media->optimal_transfer_length_granularity);
out:
if (priv->efi_info)
priv->efi_info(dev);
}
static bool is_bio_usbdev(struct efi_device *efidev)
{
return IS_ENABLED(CONFIG_EFI_BLK_SEPARATE_USBDISK) &&
efi_device_has_guid(efidev, EFI_USB_IO_PROTOCOL_GUID);
}
static int efi_bio_probe(struct efi_device *efidev)
{
int instance;
struct efi_bio_priv *priv;
struct efi_block_io_media *media;
struct device *dev = &efidev->dev;
priv = xzalloc(sizeof(*priv));
BS->handle_protocol(efidev->handle, &efi_block_io_protocol_guid,
(void **)&priv->protocol);
if (!priv->protocol)
return -ENODEV;
dev->priv = priv;
priv->efi_info = dev->info;
dev->info = efi_bio_print_info;
media = priv->protocol->media;
if (__is_defined(DEBUG))
efi_bio_print_info(dev);
priv->dev = &efidev->dev;
if (is_bio_usbdev(efidev)) {
instance = cdev_find_free_index("usbdisk");
priv->blk.cdev.name = xasprintf("usbdisk%d", instance);
} else {
instance = cdev_find_free_index("disk");
priv->blk.cdev.name = xasprintf("disk%d", instance);
}
priv->blk.blockbits = ffs(media->block_size) - 1;
priv->blk.num_blocks = media->last_block + 1;
priv->blk.ops = &efi_bio_ops;
priv->blk.dev = &efidev->dev;
priv->blk.type = BLK_TYPE_VIRTUAL;
priv->media_id = media->media_id;
if (efi_get_bootsource() == efidev)
bootsource_set_raw_instance(instance);
return blockdevice_register(&priv->blk);
}
static struct efi_driver efi_bio_driver = {
.driver = {
.name = "efi-block-io",
},
.probe = efi_bio_probe,
.guid = EFI_BLOCK_IO_PROTOCOL_GUID,
};
device_efi_driver(efi_bio_driver);
|