]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/SioBusDxe/SioBusDxe.c
OvmfPkg: Add an Super IO bus driver
[mirror_edk2.git] / OvmfPkg / SioBusDxe / SioBusDxe.c
1 /** @file
2 The SioBusDxe driver is used to create child devices on the ISA bus and
3 installs the Super I/O protocols on them.
4
5 Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
6
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions
9 of the BSD License which accompanies this distribution. The
10 full text of the license may be found at
11 http://opensource.org/licenses/bsd-license.php
12
13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15
16 **/
17
18 #include "SioBusDxe.h"
19
20 //
21 // SioBus Driver Binding Protocol
22 //
23 EFI_DRIVER_BINDING_PROTOCOL gSioBusDriverBinding = {
24 SioBusDriverBindingSupported,
25 SioBusDriverBindingStart,
26 SioBusDriverBindingStop,
27 0x10,
28 NULL,
29 NULL
30 };
31
32
33 /**
34 Tests to see if this driver supports a given controller. If a child device is
35 provided, it further tests to see if this driver supports creating a handle
36 for the specified child device.
37
38 This function checks to see if the driver specified by This supports the
39 device specified by ControllerHandle. Drivers will typically use the device
40 path attached to ControllerHandle and/or the services from the bus I/O
41 abstraction attached to ControllerHandle to determine if the driver supports
42 ControllerHandle. This function may be called many times during platform
43 initialization. In order to reduce boot times, the tests performed by this
44 function must be very small, and take as little time as possible to execute.
45 This function must not change the state of any hardware devices, and this
46 function must be aware that the device specified by ControllerHandle may
47 already be managed by the same driver or a different driver. This function
48 must match its calls to AllocatePages() with FreePages(), AllocatePool() with
49 FreePool(), and OpenProtocol() with CloseProtocol(). Since ControllerHandle
50 may have been previously started by the same driver, if a protocol is already
51 in the opened state, then it must not be closed with CloseProtocol(). This is
52 required to guarantee the state of ControllerHandle is not modified by this
53 function.
54
55 @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL
56 instance.
57 @param[in] ControllerHandle The handle of the controller to test. This
58 handle must support a protocol interface
59 that supplies an I/O abstraction to the
60 driver.
61 @param[in] RemainingDevicePath A pointer to the remaining portion of a
62 device path. This parameter is ignored by
63 device drivers, and is optional for bus
64 drivers. For bus drivers, if this parameter
65 is not NULL, then the bus driver must
66 determine if the bus controller specified by
67 ControllerHandle and the child controller
68 specified by RemainingDevicePath are both
69 supported by this bus driver.
70
71 @retval EFI_SUCCESS The device specified by ControllerHandle and
72 RemainingDevicePath is supported by the
73 driver specified by This.
74 @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
75 RemainingDevicePath is already being managed
76 by the driver specified by This.
77 @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
78 RemainingDevicePath is already being managed
79 by a different driver or an application that
80 requires exclusive access.
81 @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
82 RemainingDevicePath is not supported by the
83 driver specified by This.
84
85 **/
86 EFI_STATUS
87 EFIAPI
88 SioBusDriverBindingSupported (
89 IN EFI_DRIVER_BINDING_PROTOCOL *This,
90 IN EFI_HANDLE Controller,
91 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
92 )
93 {
94 EFI_STATUS Status;
95 EFI_PCI_IO_PROTOCOL *PciIo;
96 PCI_TYPE00 Pci;
97 UINTN SegmentNumber;
98 UINTN BusNumber;
99 UINTN DeviceNumber;
100 UINTN FunctionNumber;
101
102 //
103 // Get PciIo protocol instance
104 //
105 Status = gBS->OpenProtocol (
106 Controller,
107 &gEfiPciIoProtocolGuid,
108 (VOID**)&PciIo,
109 This->DriverBindingHandle,
110 Controller,
111 EFI_OPEN_PROTOCOL_BY_DRIVER
112 );
113 if (EFI_ERROR(Status)) {
114 return Status;
115 }
116
117 Status = PciIo->Pci.Read (
118 PciIo,
119 EfiPciIoWidthUint32,
120 0,
121 sizeof(Pci) / sizeof(UINT32),
122 &Pci);
123
124 if (!EFI_ERROR (Status)) {
125 Status = EFI_UNSUPPORTED;
126 if ((Pci.Hdr.Command & 0x03) == 0x03) {
127 if (Pci.Hdr.ClassCode[2] == PCI_CLASS_BRIDGE) {
128 //
129 // See if this is a standard PCI to ISA Bridge from the Base Code and
130 // Class Code
131 //
132 if (Pci.Hdr.ClassCode[1] == PCI_CLASS_BRIDGE_ISA) {
133 Status = EFI_SUCCESS;
134 }
135
136 //
137 // See if this is an Intel PCI to ISA bridge in Positive Decode Mode
138 //
139 if (Pci.Hdr.ClassCode[1] == PCI_CLASS_BRIDGE_ISA_PDECODE &&
140 Pci.Hdr.VendorId == 0x8086) {
141 //
142 // See if this is on Function #0 to avoid false positives on
143 // PCI_CLASS_BRIDGE_OTHER that has the same value as
144 // PCI_CLASS_BRIDGE_ISA_PDECODE
145 //
146 Status = PciIo->GetLocation (
147 PciIo,
148 &SegmentNumber,
149 &BusNumber,
150 &DeviceNumber,
151 &FunctionNumber
152 );
153 if (!EFI_ERROR (Status) && FunctionNumber == 0) {
154 Status = EFI_SUCCESS;
155 } else {
156 Status = EFI_UNSUPPORTED;
157 }
158 }
159 }
160 }
161 }
162
163 gBS->CloseProtocol (
164 Controller,
165 &gEfiPciIoProtocolGuid,
166 This->DriverBindingHandle,
167 Controller
168 );
169
170 return Status;
171 }
172
173 /**
174 Starts a device controller or a bus controller.
175
176 The Start() function is designed to be invoked from the EFI boot service
177 ConnectController(). As a result, much of the error checking on the
178 parameters to Start() has been moved into this common boot service. It is
179 legal to call Start() from other locations, but the following calling
180 restrictions must be followed or the system behavior will not be
181 deterministic.
182 1. ControllerHandle must be a valid EFI_HANDLE.
183 2. If RemainingDevicePath is not NULL, then it must be a pointer to a
184 naturally aligned EFI_DEVICE_PATH_PROTOCOL.
185 3. Prior to calling Start(), the Supported() function for the driver
186 specified by This must have been called with the same calling parameters,
187 and Supported() must have returned EFI_SUCCESS.
188
189 @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL
190 instance.
191 @param[in] ControllerHandle The handle of the controller to start. This
192 handle must support a protocol interface
193 that supplies an I/O abstraction to the
194 driver.
195 @param[in] RemainingDevicePath A pointer to the remaining portion of a
196 device path. This parameter is ignored by
197 device drivers, and is optional for bus
198 drivers. For a bus driver, if this parameter
199 is NULL, then handles for all the children
200 of Controller are created by this driver. If
201 this parameter is not NULL and the first
202 Device Path Node is not the End of Device
203 Path Node, then only the handle for the
204 child device specified by the first Device
205 Path Node of RemainingDevicePath is created
206 by this driver. If the first Device Path
207 Node of RemainingDevicePath is the End of
208 Device Path Node, no child handle is created
209 by this driver.
210
211 @retval EFI_SUCCESS The device was started.
212 @retval EFI_DEVICE_ERROR The device could not be started due to a
213 device error.
214 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a
215 lack of resources.
216 @retval Others The driver failded to start the device.
217
218 **/
219 EFI_STATUS
220 EFIAPI
221 SioBusDriverBindingStart (
222 IN EFI_DRIVER_BINDING_PROTOCOL *This,
223 IN EFI_HANDLE Controller,
224 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
225 )
226 {
227 EFI_STATUS Status;
228 EFI_PCI_IO_PROTOCOL *PciIo;
229 EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
230 UINT64 Supports;
231 UINT64 OriginalAttributes;
232 UINT64 Attributes;
233 BOOLEAN Enabled;
234 SIO_BUS_DRIVER_PRIVATE_DATA *Private;
235 UINT32 ChildDeviceNumber;
236
237 Enabled = FALSE;
238 Supports = 0;
239 OriginalAttributes = 0;
240 Private = NULL;
241
242 //
243 // Open the PCI I/O Protocol Interface
244 //
245 PciIo = NULL;
246 Status = gBS->OpenProtocol (
247 Controller,
248 &gEfiPciIoProtocolGuid,
249 (VOID**) &PciIo,
250 This->DriverBindingHandle,
251 Controller,
252 EFI_OPEN_PROTOCOL_BY_DRIVER
253 );
254 if (EFI_ERROR (Status)) {
255 return Status;
256 }
257
258 //
259 // Open Device Path Protocol
260 //
261 Status = gBS->OpenProtocol (
262 Controller,
263 &gEfiDevicePathProtocolGuid,
264 (VOID **) &ParentDevicePath,
265 This->DriverBindingHandle,
266 Controller,
267 EFI_OPEN_PROTOCOL_BY_DRIVER
268 );
269 if (EFI_ERROR (Status)) {
270 gBS->CloseProtocol (
271 Controller,
272 &gEfiPciIoProtocolGuid,
273 This->DriverBindingHandle,
274 Controller
275 );
276 return Status;
277 }
278
279 //
280 // Get supported PCI attributes
281 //
282 Status = PciIo->Attributes (
283 PciIo,
284 EfiPciIoAttributeOperationSupported,
285 0,
286 &Supports
287 );
288 if (EFI_ERROR (Status)) {
289 goto Done;
290 }
291
292 Supports &= (UINT64) (EFI_PCI_IO_ATTRIBUTE_ISA_IO |
293 EFI_PCI_IO_ATTRIBUTE_ISA_IO_16);
294 if (Supports == 0 ||
295 Supports == (EFI_PCI_IO_ATTRIBUTE_ISA_IO |
296 EFI_PCI_IO_ATTRIBUTE_ISA_IO_16)) {
297 Status = EFI_UNSUPPORTED;
298 goto Done;
299 }
300
301 Status = PciIo->Attributes (
302 PciIo,
303 EfiPciIoAttributeOperationGet,
304 0,
305 &OriginalAttributes
306 );
307 if (EFI_ERROR (Status)) {
308 goto Done;
309 }
310
311 Attributes = EFI_PCI_DEVICE_ENABLE |
312 Supports |
313 EFI_PCI_IO_ATTRIBUTE_ISA_MOTHERBOARD_IO;
314 Status = PciIo->Attributes (
315 PciIo,
316 EfiPciIoAttributeOperationEnable,
317 Attributes,
318 NULL
319 );
320 if (EFI_ERROR (Status)) {
321 goto Done;
322 }
323
324 Enabled = TRUE;
325
326 //
327 // Store the OriginalAttributes for the restore in BindingStop()
328 //
329 Private = AllocateZeroPool (sizeof (SIO_BUS_DRIVER_PRIVATE_DATA));
330 if (Private == NULL) {
331 Status = EFI_OUT_OF_RESOURCES;
332 goto Done;
333 }
334 Private->PciIo = PciIo;
335 Private->OriginalAttributes = OriginalAttributes;
336
337 Status = gBS->InstallProtocolInterface (
338 &Controller,
339 &gEfiCallerIdGuid,
340 EFI_NATIVE_INTERFACE,
341 Private
342 );
343 if (EFI_ERROR (Status)) {
344 goto Done;
345 }
346
347 //
348 // Report status code for the start of general controller initialization
349 //
350 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
351 EFI_PROGRESS_CODE,
352 (EFI_IO_BUS_LPC | EFI_IOB_PC_INIT),
353 ParentDevicePath
354 );
355
356 //
357 // Report status code for the start of enabling devices on the bus
358 //
359 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
360 EFI_PROGRESS_CODE,
361 (EFI_IO_BUS_LPC | EFI_IOB_PC_ENABLE),
362 ParentDevicePath
363 );
364
365 //
366 // Create all the children upon the first entrance
367 //
368 ChildDeviceNumber = SioCreateAllChildDevices (
369 This,
370 Controller,
371 PciIo,
372 ParentDevicePath
373 );
374 if (ChildDeviceNumber == 0) {
375 Status = EFI_DEVICE_ERROR;
376 }
377
378 Done:
379 if (EFI_ERROR (Status)) {
380 if (PciIo != NULL && Enabled) {
381 PciIo->Attributes (
382 PciIo,
383 EfiPciIoAttributeOperationSet,
384 OriginalAttributes,
385 NULL
386 );
387 }
388
389 gBS->CloseProtocol (
390 Controller,
391 &gEfiDevicePathProtocolGuid,
392 This->DriverBindingHandle,
393 Controller
394 );
395
396 gBS->CloseProtocol (
397 Controller,
398 &gEfiPciIoProtocolGuid,
399 This->DriverBindingHandle,
400 Controller
401 );
402
403 if (Private != NULL) {
404 gBS->UninstallMultipleProtocolInterfaces (
405 Controller,
406 &gEfiCallerIdGuid,
407 Private,
408 NULL
409 );
410 FreePool (Private);
411 }
412
413 return Status;
414 }
415
416 return EFI_SUCCESS;
417 }
418
419 /**
420 Stops a device controller or a bus controller.
421
422 The Stop() function is designed to be invoked from the EFI boot service
423 DisconnectController(). As a result, much of the error checking on the
424 parameters to Stop() has been moved into this common boot service. It is
425 legal to call Stop() from other locations, but the following calling
426 restrictions must be followed or the system behavior will not be
427 deterministic.
428 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous
429 call to this same driver's Start() function.
430 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a
431 valid EFI_HANDLE. In addition, all of these handles must have been created
432 in this driver's Start() function, and the Start() function must have
433 called OpenProtocol() on ControllerHandle with an Attribute of
434 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
435
436 @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL
437 instance.
438 @param[in] ControllerHandle A handle to the device being stopped. The
439 handle must support a bus specific I/O
440 protocol for the driver to use to stop the
441 device.
442 @param[in] NumberOfChildren The number of child device handles in
443 ChildHandleBuffer.
444 @param[in] ChildHandleBuffer An array of child handles to be freed. May be
445 NULL if NumberOfChildren is 0.
446
447 @retval EFI_SUCCESS The device was stopped.
448 @retval EFI_DEVICE_ERROR The device could not be stopped due to a
449 device error.
450
451 **/
452 EFI_STATUS
453 EFIAPI
454 SioBusDriverBindingStop (
455 IN EFI_DRIVER_BINDING_PROTOCOL *This,
456 IN EFI_HANDLE Controller,
457 IN UINTN NumberOfChildren,
458 IN EFI_HANDLE *ChildHandleBuffer
459 )
460 {
461 EFI_STATUS Status;
462 SIO_BUS_DRIVER_PRIVATE_DATA *Private;
463 UINTN Index;
464 BOOLEAN AllChildrenStopped;
465 EFI_SIO_PROTOCOL *Sio;
466 SIO_DEV *SioDevice;
467 EFI_PCI_IO_PROTOCOL *PciIo;
468
469 if (NumberOfChildren == 0) {
470 //
471 // Restore PCI attributes
472 //
473 Status = gBS->OpenProtocol (
474 Controller,
475 &gEfiCallerIdGuid,
476 (VOID **) &Private,
477 This->DriverBindingHandle,
478 Controller,
479 EFI_OPEN_PROTOCOL_GET_PROTOCOL
480 );
481 if (EFI_ERROR (Status)) {
482 return Status;
483 }
484
485 Status = Private->PciIo->Attributes (
486 Private->PciIo,
487 EfiPciIoAttributeOperationSet,
488 Private->OriginalAttributes,
489 NULL
490 );
491 if (EFI_ERROR (Status)) {
492 return Status;
493 }
494
495 gBS->UninstallProtocolInterface (
496 Controller,
497 &gEfiCallerIdGuid,
498 Private
499 );
500 FreePool (Private);
501
502 //
503 // Close the bus driver
504 //
505 Status = gBS->CloseProtocol (
506 Controller,
507 &gEfiDevicePathProtocolGuid,
508 This->DriverBindingHandle,
509 Controller
510 );
511 if (EFI_ERROR (Status)) {
512 return Status;
513 }
514
515 Status = gBS->CloseProtocol (
516 Controller,
517 &gEfiPciIoProtocolGuid,
518 This->DriverBindingHandle,
519 Controller
520 );
521 if (EFI_ERROR (Status)) {
522 return Status;
523 }
524
525 return EFI_SUCCESS;
526 }
527
528 //
529 // Stop all the children
530 //
531 AllChildrenStopped = TRUE;
532
533 for (Index = 0; Index < NumberOfChildren; Index++) {
534 Status = gBS->OpenProtocol (
535 ChildHandleBuffer[Index],
536 &gEfiSioProtocolGuid,
537 (VOID **) &Sio,
538 This->DriverBindingHandle,
539 Controller,
540 EFI_OPEN_PROTOCOL_GET_PROTOCOL
541 );
542 if (!EFI_ERROR (Status)) {
543 SioDevice = SIO_DEV_FROM_SIO (Sio);
544
545 //
546 // Close the child handle
547 //
548 Status = gBS->CloseProtocol (
549 Controller,
550 &gEfiPciIoProtocolGuid,
551 This->DriverBindingHandle,
552 ChildHandleBuffer[Index]
553 );
554 Status = gBS->UninstallMultipleProtocolInterfaces (
555 ChildHandleBuffer[Index],
556 &gEfiDevicePathProtocolGuid,
557 SioDevice->DevicePath,
558 &gEfiSioProtocolGuid,
559 &SioDevice->Sio,
560 NULL
561 );
562
563 if (!EFI_ERROR (Status)) {
564 FreePool (SioDevice->DevicePath);
565 FreePool (SioDevice);
566 } else {
567 //
568 // Re-open PCI IO Protocol on behalf of the child device
569 // because of failure of destroying the child device handle
570 //
571 gBS->OpenProtocol (
572 Controller,
573 &gEfiPciIoProtocolGuid,
574 (VOID **) &PciIo,
575 This->DriverBindingHandle,
576 ChildHandleBuffer[Index],
577 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
578 );
579 }
580 }
581
582 if (EFI_ERROR (Status)) {
583 AllChildrenStopped = FALSE;
584 }
585 }
586
587 if (!AllChildrenStopped) {
588 return EFI_DEVICE_ERROR;
589 }
590
591 return EFI_SUCCESS;
592 }
593
594 /**
595 The entry point for the SioBusDxe driver.
596
597 @param[in] ImageHandle The firmware allocated handle for the EFI image.
598 @param[in] SystemTable A pointer to the EFI System Table.
599
600 @retval EFI_SUCCESS The entry point is executed successfully.
601 @retval other Some error occurs when executing this entry point.
602
603 **/
604 EFI_STATUS
605 EFIAPI
606 SioBusDxeDriverEntryPoint (
607 IN EFI_HANDLE ImageHandle,
608 IN EFI_SYSTEM_TABLE *SystemTable
609 )
610 {
611 //
612 // Install driver model protocol(s).
613 //
614 return EfiLibInstallDriverBindingComponentName2 (
615 ImageHandle,
616 SystemTable,
617 &gSioBusDriverBinding,
618 ImageHandle,
619 &gSioBusComponentName,
620 &gSioBusComponentName2
621 );
622 }