blob: f8badadf66047a7053bafad9b8a17533dce733f8 [file] [log] [blame]
Tom Rini70df9d62018-05-07 17:02:21 -04001// SPDX-License-Identifier: GPL-2.0+
Heinrich Schuchardt11206f42018-01-21 19:29:30 +01002/*
3 * Uclass for EFI drivers
4 *
5 * Copyright (c) 2017 Heinrich Schuchardt
6 *
Heinrich Schuchardt11206f42018-01-21 19:29:30 +01007 * For each EFI driver the uclass
8 * - creates a handle
9 * - installs the driver binding protocol
10 *
11 * The uclass provides the bind, start, and stop entry points for the driver
12 * binding protocol.
13 *
14 * In bind() and stop() it checks if the controller implements the protocol
15 * supported by the EFI driver. In the start() function it calls the bind()
16 * function of the EFI driver. In the stop() function it destroys the child
17 * controllers.
18 */
19
20#include <efi_driver.h>
Simon Glass9bc15642020-02-03 07:36:16 -070021#include <malloc.h>
Heinrich Schuchardt11206f42018-01-21 19:29:30 +010022
Heinrich Schuchardt55de1002018-09-18 18:52:46 +020023/**
24 * check_node_type() - check node type
Heinrich Schuchardt11206f42018-01-21 19:29:30 +010025 *
Heinrich Schuchardt55de1002018-09-18 18:52:46 +020026 * We do not support partitions as controller handles.
27 *
28 * @handle: handle to be checked
29 * Return: status code
Heinrich Schuchardt11206f42018-01-21 19:29:30 +010030 */
31static efi_status_t check_node_type(efi_handle_t handle)
32{
33 efi_status_t r, ret = EFI_SUCCESS;
34 const struct efi_device_path *dp;
35
36 /* Open the device path protocol */
37 r = EFI_CALL(systab.boottime->open_protocol(
38 handle, &efi_guid_device_path, (void **)&dp,
39 NULL, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL));
40 if (r == EFI_SUCCESS && dp) {
41 /* Get the last node */
42 const struct efi_device_path *node = efi_dp_last_node(dp);
43 /* We do not support partitions as controller */
44 if (!node || node->type == DEVICE_PATH_TYPE_MEDIA_DEVICE)
45 ret = EFI_UNSUPPORTED;
46 }
47 return ret;
48}
49
Heinrich Schuchardt55de1002018-09-18 18:52:46 +020050/**
51 * efi_uc_supported() - check if the driver supports the controller
Heinrich Schuchardt11206f42018-01-21 19:29:30 +010052 *
Heinrich Schuchardt55de1002018-09-18 18:52:46 +020053 * @this: driver binding protocol
54 * @controller_handle: handle of the controller
55 * @remaining_device_path: path specifying the child controller
56 * Return: status code
Heinrich Schuchardt11206f42018-01-21 19:29:30 +010057 */
58static efi_status_t EFIAPI efi_uc_supported(
59 struct efi_driver_binding_protocol *this,
60 efi_handle_t controller_handle,
61 struct efi_device_path *remaining_device_path)
62{
63 efi_status_t r, ret;
64 void *interface;
65 struct efi_driver_binding_extended_protocol *bp =
66 (struct efi_driver_binding_extended_protocol *)this;
67
68 EFI_ENTRY("%p, %p, %ls", this, controller_handle,
69 efi_dp_str(remaining_device_path));
70
71 ret = EFI_CALL(systab.boottime->open_protocol(
72 controller_handle, bp->ops->protocol,
73 &interface, this->driver_binding_handle,
74 controller_handle, EFI_OPEN_PROTOCOL_BY_DRIVER));
75 switch (ret) {
76 case EFI_ACCESS_DENIED:
77 case EFI_ALREADY_STARTED:
78 goto out;
79 case EFI_SUCCESS:
80 break;
81 default:
82 ret = EFI_UNSUPPORTED;
83 goto out;
84 }
85
86 ret = check_node_type(controller_handle);
87
88 r = EFI_CALL(systab.boottime->close_protocol(
89 controller_handle, bp->ops->protocol,
90 this->driver_binding_handle,
91 controller_handle));
92 if (r != EFI_SUCCESS)
93 ret = EFI_UNSUPPORTED;
94out:
95 return EFI_EXIT(ret);
96}
97
Heinrich Schuchardt55de1002018-09-18 18:52:46 +020098/**
99 * efi_uc_start() - create child controllers and attach driver
Heinrich Schuchardt11206f42018-01-21 19:29:30 +0100100 *
Heinrich Schuchardt55de1002018-09-18 18:52:46 +0200101 * @this: driver binding protocol
102 * @controller_handle: handle of the controller
103 * @remaining_device_path: path specifying the child controller
104 * Return: status code
Heinrich Schuchardt11206f42018-01-21 19:29:30 +0100105 */
106static efi_status_t EFIAPI efi_uc_start(
107 struct efi_driver_binding_protocol *this,
108 efi_handle_t controller_handle,
109 struct efi_device_path *remaining_device_path)
110{
111 efi_status_t r, ret;
112 void *interface = NULL;
113 struct efi_driver_binding_extended_protocol *bp =
114 (struct efi_driver_binding_extended_protocol *)this;
115
Heinrich Schuchardt64dc7af2020-01-10 12:33:16 +0100116 EFI_ENTRY("%p, %p, %ls", this, controller_handle,
Heinrich Schuchardt11206f42018-01-21 19:29:30 +0100117 efi_dp_str(remaining_device_path));
118
119 /* Attach driver to controller */
120 ret = EFI_CALL(systab.boottime->open_protocol(
121 controller_handle, bp->ops->protocol,
122 &interface, this->driver_binding_handle,
123 controller_handle, EFI_OPEN_PROTOCOL_BY_DRIVER));
124 switch (ret) {
125 case EFI_ACCESS_DENIED:
126 case EFI_ALREADY_STARTED:
127 goto out;
128 case EFI_SUCCESS:
129 break;
130 default:
131 ret = EFI_UNSUPPORTED;
132 goto out;
133 }
134 ret = check_node_type(controller_handle);
135 if (ret != EFI_SUCCESS) {
136 r = EFI_CALL(systab.boottime->close_protocol(
137 controller_handle, bp->ops->protocol,
138 this->driver_binding_handle,
139 controller_handle));
140 if (r != EFI_SUCCESS)
141 EFI_PRINT("Failure to close handle\n");
142 goto out;
143 }
144
145 /* TODO: driver specific stuff */
146 bp->ops->bind(controller_handle, interface);
147
148out:
149 return EFI_EXIT(ret);
150}
151
Heinrich Schuchardt55de1002018-09-18 18:52:46 +0200152/**
153 * disconnect_child() - remove a single child controller from the parent
154 * controller
Heinrich Schuchardt11206f42018-01-21 19:29:30 +0100155 *
Heinrich Schuchardt55de1002018-09-18 18:52:46 +0200156 * @controller_handle: parent controller
157 * @child_handle: child controller
158 * Return: status code
Heinrich Schuchardt11206f42018-01-21 19:29:30 +0100159 */
160static efi_status_t disconnect_child(efi_handle_t controller_handle,
161 efi_handle_t child_handle)
162{
163 efi_status_t ret;
164 efi_guid_t *guid_controller = NULL;
165 efi_guid_t *guid_child_controller = NULL;
166
167 ret = EFI_CALL(systab.boottime->close_protocol(
168 controller_handle, guid_controller,
169 child_handle, child_handle));
170 if (ret != EFI_SUCCESS) {
171 EFI_PRINT("Cannot close protocol\n");
172 return ret;
173 }
174 ret = EFI_CALL(systab.boottime->uninstall_protocol_interface(
175 child_handle, guid_child_controller, NULL));
176 if (ret != EFI_SUCCESS) {
177 EFI_PRINT("Cannot uninstall protocol interface\n");
178 return ret;
179 }
180 return ret;
181}
182
Heinrich Schuchardt55de1002018-09-18 18:52:46 +0200183/**
184 * efi_uc_stop() - Remove child controllers and disconnect the controller
Heinrich Schuchardt11206f42018-01-21 19:29:30 +0100185 *
Heinrich Schuchardt55de1002018-09-18 18:52:46 +0200186 * @this: driver binding protocol
187 * @controller_handle: handle of the controller
188 * @number_of_children: number of child controllers to remove
189 * @child_handle_buffer: handles of the child controllers to remove
190 * Return: status code
Heinrich Schuchardt11206f42018-01-21 19:29:30 +0100191 */
192static efi_status_t EFIAPI efi_uc_stop(
193 struct efi_driver_binding_protocol *this,
194 efi_handle_t controller_handle,
195 size_t number_of_children,
196 efi_handle_t *child_handle_buffer)
197{
198 efi_status_t ret;
199 efi_uintn_t count;
200 struct efi_open_protocol_info_entry *entry_buffer;
Heinrich Schuchardta3a4a9c2020-01-09 23:26:43 +0100201 struct efi_driver_binding_extended_protocol *bp =
202 (struct efi_driver_binding_extended_protocol *)this;
Heinrich Schuchardt11206f42018-01-21 19:29:30 +0100203
Heinrich Schuchardt64dc7af2020-01-10 12:33:16 +0100204 EFI_ENTRY("%p, %p, %zu, %p", this, controller_handle,
Heinrich Schuchardt11206f42018-01-21 19:29:30 +0100205 number_of_children, child_handle_buffer);
206
207 /* Destroy provided child controllers */
208 if (number_of_children) {
209 efi_uintn_t i;
210
211 for (i = 0; i < number_of_children; ++i) {
212 ret = disconnect_child(controller_handle,
213 child_handle_buffer[i]);
214 if (ret != EFI_SUCCESS)
215 return ret;
216 }
217 return EFI_SUCCESS;
218 }
219
220 /* Destroy all children */
221 ret = EFI_CALL(systab.boottime->open_protocol_information(
Heinrich Schuchardta3a4a9c2020-01-09 23:26:43 +0100222 controller_handle, bp->ops->protocol,
Heinrich Schuchardt11206f42018-01-21 19:29:30 +0100223 &entry_buffer, &count));
224 if (ret != EFI_SUCCESS)
225 goto out;
226 while (count) {
227 if (entry_buffer[--count].attributes &
228 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) {
229 ret = disconnect_child(
230 controller_handle,
231 entry_buffer[count].agent_handle);
232 if (ret != EFI_SUCCESS)
233 goto out;
234 }
235 }
236 ret = EFI_CALL(systab.boottime->free_pool(entry_buffer));
237 if (ret != EFI_SUCCESS)
Heinrich Schuchardt924045e2019-01-06 17:12:43 +0100238 printf("%s: ERROR: Cannot free pool\n", __func__);
Heinrich Schuchardt11206f42018-01-21 19:29:30 +0100239
240 /* Detach driver from controller */
241 ret = EFI_CALL(systab.boottime->close_protocol(
Heinrich Schuchardta3a4a9c2020-01-09 23:26:43 +0100242 controller_handle, bp->ops->protocol,
Heinrich Schuchardt11206f42018-01-21 19:29:30 +0100243 this->driver_binding_handle, controller_handle));
244out:
245 return EFI_EXIT(ret);
246}
247
Heinrich Schuchardt55de1002018-09-18 18:52:46 +0200248/**
249 * efi_add_driver() - add driver
250 *
251 * @drv: driver to add
252 * Return: status code
253 */
Heinrich Schuchardt11206f42018-01-21 19:29:30 +0100254static efi_status_t efi_add_driver(struct driver *drv)
255{
256 efi_status_t ret;
257 const struct efi_driver_ops *ops = drv->ops;
258 struct efi_driver_binding_extended_protocol *bp;
259
260 debug("EFI: Adding driver '%s'\n", drv->name);
261 if (!ops->protocol) {
262 printf("EFI: ERROR: protocol GUID missing for driver '%s'\n",
263 drv->name);
264 return EFI_INVALID_PARAMETER;
265 }
266 bp = calloc(1, sizeof(struct efi_driver_binding_extended_protocol));
267 if (!bp)
268 return EFI_OUT_OF_RESOURCES;
269
270 bp->bp.supported = efi_uc_supported;
271 bp->bp.start = efi_uc_start;
272 bp->bp.stop = efi_uc_stop;
273 bp->bp.version = 0xffffffff;
274 bp->ops = drv->ops;
275
276 ret = efi_create_handle(&bp->bp.driver_binding_handle);
277 if (ret != EFI_SUCCESS) {
278 free(bp);
279 goto out;
280 }
281 bp->bp.image_handle = bp->bp.driver_binding_handle;
282 ret = efi_add_protocol(bp->bp.driver_binding_handle,
283 &efi_guid_driver_binding_protocol, bp);
284 if (ret != EFI_SUCCESS) {
285 efi_delete_handle(bp->bp.driver_binding_handle);
286 free(bp);
287 goto out;
288 }
289out:
290 return ret;
291}
292
Heinrich Schuchardt55de1002018-09-18 18:52:46 +0200293/**
294 * efi_driver_init() - initialize the EFI drivers
295 *
296 * Called by efi_init_obj_list().
Heinrich Schuchardt11206f42018-01-21 19:29:30 +0100297 *
Heinrich Schuchardt55de1002018-09-18 18:52:46 +0200298 * Return: 0 = success, any other value will stop further execution
Heinrich Schuchardt11206f42018-01-21 19:29:30 +0100299 */
Heinrich Schuchardt8f3cc5b2018-02-01 12:53:32 +0100300efi_status_t efi_driver_init(void)
Heinrich Schuchardt11206f42018-01-21 19:29:30 +0100301{
302 struct driver *drv;
Heinrich Schuchardt8f3cc5b2018-02-01 12:53:32 +0100303 efi_status_t ret = EFI_SUCCESS;
Heinrich Schuchardt11206f42018-01-21 19:29:30 +0100304
Heinrich Schuchardt11206f42018-01-21 19:29:30 +0100305 debug("EFI: Initializing EFI driver framework\n");
306 for (drv = ll_entry_start(struct driver, driver);
307 drv < ll_entry_end(struct driver, driver); ++drv) {
308 if (drv->id == UCLASS_EFI) {
309 ret = efi_add_driver(drv);
Heinrich Schuchardt8f3cc5b2018-02-01 12:53:32 +0100310 if (ret != EFI_SUCCESS) {
Heinrich Schuchardt11206f42018-01-21 19:29:30 +0100311 printf("EFI: ERROR: failed to add driver %s\n",
312 drv->name);
313 break;
314 }
315 }
316 }
317 return ret;
318}
319
Heinrich Schuchardt55de1002018-09-18 18:52:46 +0200320/**
321 * efi_uc_init() - initialize the EFI uclass
322 *
323 * @class: the EFI uclass
324 * Return: 0 = success
325 */
Heinrich Schuchardt11206f42018-01-21 19:29:30 +0100326static int efi_uc_init(struct uclass *class)
327{
328 printf("EFI: Initializing UCLASS_EFI\n");
329 return 0;
330}
331
Heinrich Schuchardt55de1002018-09-18 18:52:46 +0200332/**
333 * efi_uc_destroy() - destroy the EFI uclass
334 *
335 * @class: the EFI uclass
336 * Return: 0 = success
337 */
Heinrich Schuchardt11206f42018-01-21 19:29:30 +0100338static int efi_uc_destroy(struct uclass *class)
339{
340 printf("Destroying UCLASS_EFI\n");
341 return 0;
342}
343
344UCLASS_DRIVER(efi) = {
345 .name = "efi",
346 .id = UCLASS_EFI,
347 .init = efi_uc_init,
348 .destroy = efi_uc_destroy,
349};