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