[efi] Add the "snpnet" driver
[gpxe.git] / src / drivers / net / efi / snponly.c
1 /*
2  * Copyright (C) 2010 VMware, Inc.  All Rights Reserved.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19 FILE_LICENCE ( GPL2_OR_LATER );
20
21 #include <string.h>
22 #include <errno.h>
23 #include <gpxe/device.h>
24 #include <gpxe/init.h>
25 #include <gpxe/efi/efi.h>
26 #include <gpxe/efi/Protocol/SimpleNetwork.h>
27 #include "snp.h"
28 #include "snpnet.h"
29
30 /** @file
31  *
32  * Chain-loading Simple Network Protocol Bus Driver
33  *
34  * This bus driver allows gPXE to use the EFI Simple Network Protocol provided
35  * by the platform to transmit and receive packets. It attaches to only the
36  * device handle that gPXE was loaded from, that is, it will only use the
37  * Simple Network Protocol on the current loaded image's device handle.
38  *
39  * Eseentially, this driver provides the EFI equivalent of the "undionly"
40  * driver.
41  */
42
43 /** The one and only SNP network device */
44 static struct snp_device snponly_dev;
45
46 /** EFI simple network protocol GUID */
47 static EFI_GUID efi_simple_network_protocol_guid
48         = EFI_SIMPLE_NETWORK_PROTOCOL_GUID;
49
50 /**
51  * Probe SNP root bus
52  *
53  * @v rootdev           SNP bus root device
54  *
55  * Look at the loaded image's device handle and see if the simple network
56  * protocol exists. If so, register a driver for it.
57  */
58 static int snpbus_probe ( struct root_device *rootdev ) {
59         EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
60         EFI_STATUS efirc;
61         int rc;
62         void *snp;
63
64         efirc = bs->OpenProtocol ( efi_loaded_image->DeviceHandle,
65                                    &efi_simple_network_protocol_guid,
66                                    &snp, efi_image_handle, NULL,
67                                    EFI_OPEN_PROTOCOL_GET_PROTOCOL );
68         if ( efirc ) {
69                 DBG ( "Could not find Simple Network Protocol!\n" );
70                 return -ENODEV;
71         }
72         snponly_dev.snp = snp;
73
74         /* Add to device hierarchy */
75         strncpy ( snponly_dev.dev.name, "EFI SNP",
76                   ( sizeof ( snponly_dev.dev.name ) - 1 ) );
77         snponly_dev.dev.parent = &rootdev->dev;
78         list_add ( &snponly_dev.dev.siblings, &rootdev->dev.children);
79         INIT_LIST_HEAD ( &snponly_dev.dev.children );
80
81         /* Create network device */
82         if ( ( rc = snpnet_probe ( &snponly_dev ) ) != 0 )
83                 goto err;
84
85         return 0;
86
87 err:
88         list_del ( &snponly_dev.dev.siblings );
89         return rc;
90 }
91
92 /**
93  * Remove SNP root bus
94  *
95  * @v rootdev           SNP bus root device
96  */
97 static void snpbus_remove ( struct root_device *rootdev __unused ) {
98         snpnet_remove ( &snponly_dev );
99         list_del ( &snponly_dev.dev.siblings );
100 }
101
102 /** SNP bus root device driver */
103 static struct root_driver snp_root_driver = {
104         .probe = snpbus_probe,
105         .remove = snpbus_remove,
106 };
107
108 /** SNP bus root device */
109 struct root_device snp_root_device __root_device = {
110         .dev = { .name = "EFI SNP" },
111         .driver = &snp_root_driver,
112 };
113
114 /**
115  * Prepare for exit
116  *
117  * @v flags             Shutdown flags
118  */
119 static void snponly_shutdown ( int flags ) {
120         /* If we are shutting down to boot an OS, make sure the SNP does not
121          * stay active.
122          */
123         if ( flags & SHUTDOWN_BOOT )
124                 snponly_dev.removal_state = EfiSimpleNetworkStopped;
125 }
126
127 struct startup_fn startup_snponly __startup_fn ( STARTUP_LATE ) = {
128         .shutdown = snponly_shutdown,
129 };