blob: 46b69b479cb6beb4e6bf5ebe8888cfa933f594e9 [file] [log] [blame]
Heinrich Schuchardt05ef48a2018-01-21 19:29:30 +01001/*
2 * Uclass for EFI drivers
3 *
4 * Copyright (c) 2017 Heinrich Schuchardt
5 *
6 * SPDX-License-Identifier: GPL-2.0+
7 *
8 * For each EFI driver the uclass
9 * - creates a handle
10 * - installs the driver binding protocol
11 *
12 * The uclass provides the bind, start, and stop entry points for the driver
13 * binding protocol.
14 *
15 * In bind() and stop() it checks if the controller implements the protocol
16 * supported by the EFI driver. In the start() function it calls the bind()
17 * function of the EFI driver. In the stop() function it destroys the child
18 * controllers.
19 */
20
21#include <efi_driver.h>
22
23/*
24 * Check node type. We do not support partitions as controller handles.
25 *
26 * @handle handle to be checked
27 * @return status code
28 */
29static efi_status_t check_node_type(efi_handle_t handle)
30{
31 efi_status_t r, ret = EFI_SUCCESS;
32 const struct efi_device_path *dp;
33
34 /* Open the device path protocol */
35 r = EFI_CALL(systab.boottime->open_protocol(
36 handle, &efi_guid_device_path, (void **)&dp,
37 NULL, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL));
38 if (r == EFI_SUCCESS && dp) {
39 /* Get the last node */
40 const struct efi_device_path *node = efi_dp_last_node(dp);
41 /* We do not support partitions as controller */
42 if (!node || node->type == DEVICE_PATH_TYPE_MEDIA_DEVICE)
43 ret = EFI_UNSUPPORTED;
44 }
45 return ret;
46}
47
48/*
49 * Check if the driver supports the controller.
50 *
51 * @this driver binding protocol
52 * @controller_handle handle of the controller
53 * @remaining_device_path path specifying the child controller
54 * @return status code
55 */
56static efi_status_t EFIAPI efi_uc_supported(
57 struct efi_driver_binding_protocol *this,
58 efi_handle_t controller_handle,
59 struct efi_device_path *remaining_device_path)
60{
61 efi_status_t r, ret;
62 void *interface;
63 struct efi_driver_binding_extended_protocol *bp =
64 (struct efi_driver_binding_extended_protocol *)this;
65
66 EFI_ENTRY("%p, %p, %ls", this, controller_handle,
67 efi_dp_str(remaining_device_path));
68
69 ret = EFI_CALL(systab.boottime->open_protocol(
70 controller_handle, bp->ops->protocol,
71 &interface, this->driver_binding_handle,
72 controller_handle, EFI_OPEN_PROTOCOL_BY_DRIVER));
73 switch (ret) {
74 case EFI_ACCESS_DENIED:
75 case EFI_ALREADY_STARTED:
76 goto out;
77 case EFI_SUCCESS:
78 break;
79 default:
80 ret = EFI_UNSUPPORTED;
81 goto out;
82 }
83
84 ret = check_node_type(controller_handle);
85
86 r = EFI_CALL(systab.boottime->close_protocol(
87 controller_handle, bp->ops->protocol,
88 this->driver_binding_handle,
89 controller_handle));
90 if (r != EFI_SUCCESS)
91 ret = EFI_UNSUPPORTED;
92out:
93 return EFI_EXIT(ret);
94}
95
96/*
97 * Create child controllers and attach driver.
98 *
99 * @this driver binding protocol
100 * @controller_handle handle of the controller
101 * @remaining_device_path path specifying the child controller
102 * @return status code
103 */
104static efi_status_t EFIAPI efi_uc_start(
105 struct efi_driver_binding_protocol *this,
106 efi_handle_t controller_handle,
107 struct efi_device_path *remaining_device_path)
108{
109 efi_status_t r, ret;
110 void *interface = NULL;
111 struct efi_driver_binding_extended_protocol *bp =
112 (struct efi_driver_binding_extended_protocol *)this;
113
114 EFI_ENTRY("%p, %pUl, %ls", this, controller_handle,
115 efi_dp_str(remaining_device_path));
116
117 /* Attach driver to controller */
118 ret = EFI_CALL(systab.boottime->open_protocol(
119 controller_handle, bp->ops->protocol,
120 &interface, this->driver_binding_handle,
121 controller_handle, EFI_OPEN_PROTOCOL_BY_DRIVER));
122 switch (ret) {
123 case EFI_ACCESS_DENIED:
124 case EFI_ALREADY_STARTED:
125 goto out;
126 case EFI_SUCCESS:
127 break;
128 default:
129 ret = EFI_UNSUPPORTED;
130 goto out;
131 }
132 ret = check_node_type(controller_handle);
133 if (ret != EFI_SUCCESS) {
134 r = EFI_CALL(systab.boottime->close_protocol(
135 controller_handle, bp->ops->protocol,
136 this->driver_binding_handle,
137 controller_handle));
138 if (r != EFI_SUCCESS)
139 EFI_PRINT("Failure to close handle\n");
140 goto out;
141 }
142
143 /* TODO: driver specific stuff */
144 bp->ops->bind(controller_handle, interface);
145
146out:
147 return EFI_EXIT(ret);
148}
149
150/*
151 * Remove a single child controller from the parent controller.
152 *
153 * @controller_handle parent controller
154 * @child_handle child controller
155 * @return status code
156 */
157static efi_status_t disconnect_child(efi_handle_t controller_handle,
158 efi_handle_t child_handle)
159{
160 efi_status_t ret;
161 efi_guid_t *guid_controller = NULL;
162 efi_guid_t *guid_child_controller = NULL;
163
164 ret = EFI_CALL(systab.boottime->close_protocol(
165 controller_handle, guid_controller,
166 child_handle, child_handle));
167 if (ret != EFI_SUCCESS) {
168 EFI_PRINT("Cannot close protocol\n");
169 return ret;
170 }
171 ret = EFI_CALL(systab.boottime->uninstall_protocol_interface(
172 child_handle, guid_child_controller, NULL));
173 if (ret != EFI_SUCCESS) {
174 EFI_PRINT("Cannot uninstall protocol interface\n");
175 return ret;
176 }
177 return ret;
178}
179
180/*
181 * Remove child controllers and disconnect the controller.
182 *
183 * @this driver binding protocol
184 * @controller_handle handle of the controller
185 * @number_of_children number of child controllers to remove
186 * @child_handle_buffer handles of the child controllers to remove
187 * @return status code
188 */
189static efi_status_t EFIAPI efi_uc_stop(
190 struct efi_driver_binding_protocol *this,
191 efi_handle_t controller_handle,
192 size_t number_of_children,
193 efi_handle_t *child_handle_buffer)
194{
195 efi_status_t ret;
196 efi_uintn_t count;
197 struct efi_open_protocol_info_entry *entry_buffer;
198 efi_guid_t *guid_controller = NULL;
199
200 EFI_ENTRY("%p, %pUl, %zu, %p", this, controller_handle,
201 number_of_children, child_handle_buffer);
202
203 /* Destroy provided child controllers */
204 if (number_of_children) {
205 efi_uintn_t i;
206
207 for (i = 0; i < number_of_children; ++i) {
208 ret = disconnect_child(controller_handle,
209 child_handle_buffer[i]);
210 if (ret != EFI_SUCCESS)
211 return ret;
212 }
213 return EFI_SUCCESS;
214 }
215
216 /* Destroy all children */
217 ret = EFI_CALL(systab.boottime->open_protocol_information(
218 controller_handle, guid_controller,
219 &entry_buffer, &count));
220 if (ret != EFI_SUCCESS)
221 goto out;
222 while (count) {
223 if (entry_buffer[--count].attributes &
224 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) {
225 ret = disconnect_child(
226 controller_handle,
227 entry_buffer[count].agent_handle);
228 if (ret != EFI_SUCCESS)
229 goto out;
230 }
231 }
232 ret = EFI_CALL(systab.boottime->free_pool(entry_buffer));
233 if (ret != EFI_SUCCESS)
234 printf("%s(%u) %s: ERROR: Cannot free pool\n",
235 __FILE__, __LINE__, __func__);
236
237 /* Detach driver from controller */
238 ret = EFI_CALL(systab.boottime->close_protocol(
239 controller_handle, guid_controller,
240 this->driver_binding_handle, controller_handle));
241out:
242 return EFI_EXIT(ret);
243}
244
245static efi_status_t efi_add_driver(struct driver *drv)
246{
247 efi_status_t ret;
248 const struct efi_driver_ops *ops = drv->ops;
249 struct efi_driver_binding_extended_protocol *bp;
250
251 debug("EFI: Adding driver '%s'\n", drv->name);
252 if (!ops->protocol) {
253 printf("EFI: ERROR: protocol GUID missing for driver '%s'\n",
254 drv->name);
255 return EFI_INVALID_PARAMETER;
256 }
257 bp = calloc(1, sizeof(struct efi_driver_binding_extended_protocol));
258 if (!bp)
259 return EFI_OUT_OF_RESOURCES;
260
261 bp->bp.supported = efi_uc_supported;
262 bp->bp.start = efi_uc_start;
263 bp->bp.stop = efi_uc_stop;
264 bp->bp.version = 0xffffffff;
265 bp->ops = drv->ops;
266
267 ret = efi_create_handle(&bp->bp.driver_binding_handle);
268 if (ret != EFI_SUCCESS) {
269 free(bp);
270 goto out;
271 }
272 bp->bp.image_handle = bp->bp.driver_binding_handle;
273 ret = efi_add_protocol(bp->bp.driver_binding_handle,
274 &efi_guid_driver_binding_protocol, bp);
275 if (ret != EFI_SUCCESS) {
276 efi_delete_handle(bp->bp.driver_binding_handle);
277 free(bp);
278 goto out;
279 }
280out:
281 return ret;
282}
283
284/*
285 * Initialize the EFI drivers.
286 * Called by board_init_r().
287 *
288 * @return 0 = success, any other value will stop further execution
289 */
Heinrich Schuchardt038782a2018-02-01 12:53:32 +0100290efi_status_t efi_driver_init(void)
Heinrich Schuchardt05ef48a2018-01-21 19:29:30 +0100291{
292 struct driver *drv;
Heinrich Schuchardt038782a2018-02-01 12:53:32 +0100293 efi_status_t ret = EFI_SUCCESS;
Heinrich Schuchardt05ef48a2018-01-21 19:29:30 +0100294
295 /* Save 'gd' pointer */
296 efi_save_gd();
297
298 debug("EFI: Initializing EFI driver framework\n");
299 for (drv = ll_entry_start(struct driver, driver);
300 drv < ll_entry_end(struct driver, driver); ++drv) {
301 if (drv->id == UCLASS_EFI) {
302 ret = efi_add_driver(drv);
Heinrich Schuchardt038782a2018-02-01 12:53:32 +0100303 if (ret != EFI_SUCCESS) {
Heinrich Schuchardt05ef48a2018-01-21 19:29:30 +0100304 printf("EFI: ERROR: failed to add driver %s\n",
305 drv->name);
306 break;
307 }
308 }
309 }
310 return ret;
311}
312
313static int efi_uc_init(struct uclass *class)
314{
315 printf("EFI: Initializing UCLASS_EFI\n");
316 return 0;
317}
318
319static int efi_uc_destroy(struct uclass *class)
320{
321 printf("Destroying UCLASS_EFI\n");
322 return 0;
323}
324
325UCLASS_DRIVER(efi) = {
326 .name = "efi",
327 .id = UCLASS_EFI,
328 .init = efi_uc_init,
329 .destroy = efi_uc_destroy,
330};