~pmdj/ubuntu/trusty/qemu/2.9+applesmc+fadtv3

« back to all changes in this revision

Viewing changes to roms/ipxe/src/interface/efi/efi_utils.c

  • Committer: Phil Dennis-Jordan
  • Date: 2017-07-21 08:03:43 UTC
  • mfrom: (1.1.1)
  • Revision ID: phil@philjordan.eu-20170721080343-2yr2vdj7713czahv
New upstream release 2.9.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2011 Michael Brown <mbrown@fensystems.co.uk>.
 
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 Street, Fifth Floor, Boston, MA
 
17
 * 02110-1301, USA.
 
18
 */
 
19
 
 
20
FILE_LICENCE ( GPL2_OR_LATER );
 
21
 
 
22
#include <stdio.h>
 
23
#include <string.h>
 
24
#include <errno.h>
 
25
#include <ipxe/efi/efi.h>
 
26
#include <ipxe/efi/efi_pci.h>
 
27
#include <ipxe/efi/efi_utils.h>
 
28
 
 
29
/** @file
 
30
 *
 
31
 * EFI utilities
 
32
 *
 
33
 */
 
34
 
 
35
/**
 
36
 * Find end of device path
 
37
 *
 
38
 * @v path              Path to device
 
39
 * @ret path_end        End of device path
 
40
 */
 
41
EFI_DEVICE_PATH_PROTOCOL * efi_devpath_end ( EFI_DEVICE_PATH_PROTOCOL *path ) {
 
42
 
 
43
        while ( path->Type != END_DEVICE_PATH_TYPE ) {
 
44
                path = ( ( ( void * ) path ) +
 
45
                         /* There's this amazing new-fangled thing known as
 
46
                          * a UINT16, but who wants to use one of those? */
 
47
                         ( ( path->Length[1] << 8 ) | path->Length[0] ) );
 
48
        }
 
49
 
 
50
        return path;
 
51
}
 
52
 
 
53
/**
 
54
 * Find length of device path (excluding terminator)
 
55
 *
 
56
 * @v path              Path to device
 
57
 * @ret path_len        Length of device path
 
58
 */
 
59
size_t efi_devpath_len ( EFI_DEVICE_PATH_PROTOCOL *path ) {
 
60
        EFI_DEVICE_PATH_PROTOCOL *end = efi_devpath_end ( path );
 
61
 
 
62
        return ( ( ( void * ) end ) - ( ( void * ) path ) );
 
63
}
 
64
 
 
65
/**
 
66
 * Locate parent device supporting a given protocol
 
67
 *
 
68
 * @v device            EFI device handle
 
69
 * @v protocol          Protocol GUID
 
70
 * @v parent            Parent EFI device handle to fill in
 
71
 * @ret rc              Return status code
 
72
 */
 
73
int efi_locate_device ( EFI_HANDLE device, EFI_GUID *protocol,
 
74
                        EFI_HANDLE *parent ) {
 
75
        EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
 
76
        union {
 
77
                EFI_DEVICE_PATH_PROTOCOL *path;
 
78
                void *interface;
 
79
        } path;
 
80
        EFI_DEVICE_PATH_PROTOCOL *devpath;
 
81
        EFI_STATUS efirc;
 
82
        int rc;
 
83
 
 
84
        /* Get device path */
 
85
        if ( ( efirc = bs->OpenProtocol ( device,
 
86
                                          &efi_device_path_protocol_guid,
 
87
                                          &path.interface,
 
88
                                          efi_image_handle, device,
 
89
                                          EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
 
90
                rc = -EEFI ( efirc );
 
91
                DBGC ( device, "EFIDEV %s cannot open device path: %s\n",
 
92
                       efi_handle_name ( device ), strerror ( rc ) );
 
93
                goto err_open_device_path;
 
94
        }
 
95
        devpath = path.path;
 
96
 
 
97
        /* Check for presence of specified protocol */
 
98
        if ( ( efirc = bs->LocateDevicePath ( protocol, &devpath,
 
99
                                              parent ) ) != 0 ) {
 
100
                rc = -EEFI ( efirc );
 
101
                DBGC ( device, "EFIDEV %s has no parent supporting %s: %s\n",
 
102
                       efi_handle_name ( device ),
 
103
                       efi_guid_ntoa ( protocol ), strerror ( rc ) );
 
104
                goto err_locate_protocol;
 
105
        }
 
106
 
 
107
        /* Success */
 
108
        rc = 0;
 
109
 
 
110
 err_locate_protocol:
 
111
        bs->CloseProtocol ( device, &efi_device_path_protocol_guid,
 
112
                            efi_image_handle, device );
 
113
 err_open_device_path:
 
114
        return rc;
 
115
}
 
116
 
 
117
/**
 
118
 * Add EFI device as child of another EFI device
 
119
 *
 
120
 * @v parent            EFI parent device handle
 
121
 * @v child             EFI child device handle
 
122
 * @ret rc              Return status code
 
123
 */
 
124
int efi_child_add ( EFI_HANDLE parent, EFI_HANDLE child ) {
 
125
        EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
 
126
        void *devpath;
 
127
        EFI_STATUS efirc;
 
128
        int rc;
 
129
 
 
130
        /* Re-open the device path protocol */
 
131
        if ( ( efirc = bs->OpenProtocol ( parent,
 
132
                                          &efi_device_path_protocol_guid,
 
133
                                          &devpath,
 
134
                                          efi_image_handle, child,
 
135
                                          EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
 
136
                                          ) ) != 0 ) {
 
137
                rc = -EEFI ( efirc );
 
138
                DBGC ( parent, "EFIDEV %s could not add child",
 
139
                       efi_handle_name ( parent ) );
 
140
                DBGC ( parent, " %s: %s\n",
 
141
                       efi_handle_name ( child ), strerror ( rc ) );
 
142
                DBGC_EFI_OPENERS ( parent, parent,
 
143
                                   &efi_device_path_protocol_guid );
 
144
                return rc;
 
145
        }
 
146
 
 
147
        DBGC2 ( parent, "EFIDEV %s added child", efi_handle_name ( parent ) );
 
148
        DBGC2 ( parent, " %s\n", efi_handle_name ( child ) );
 
149
        return 0;
 
150
}
 
151
 
 
152
/**
 
153
 * Remove EFI device as child of another EFI device
 
154
 *
 
155
 * @v parent            EFI parent device handle
 
156
 * @v child             EFI child device handle
 
157
 */
 
158
void efi_child_del ( EFI_HANDLE parent, EFI_HANDLE child ) {
 
159
        EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
 
160
 
 
161
        bs->CloseProtocol ( parent, &efi_device_path_protocol_guid,
 
162
                            efi_image_handle, child );
 
163
        DBGC2 ( parent, "EFIDEV %s removed child", efi_handle_name ( parent ) );
 
164
        DBGC2 ( parent, " %s\n", efi_handle_name ( child ) );
 
165
}
 
166
 
 
167
/**
 
168
 * Get underlying PCI device information
 
169
 *
 
170
 * @v device            EFI device handle
 
171
 * @v prefix            Device name prefix
 
172
 * @v dev               Generic device to fill in
 
173
 * @ret rc              Return status code
 
174
 */
 
175
static int efi_pci_info ( EFI_HANDLE device, const char *prefix,
 
176
                          struct device *dev ) {
 
177
        EFI_HANDLE pci_device;
 
178
        struct pci_device pci;
 
179
        int rc;
 
180
 
 
181
        /* Find parent PCI device */
 
182
        if ( ( rc = efi_locate_device ( device, &efi_pci_io_protocol_guid,
 
183
                                        &pci_device ) ) != 0 ) {
 
184
                DBGC ( device, "EFIDEV %s is not a PCI device: %s\n",
 
185
                       efi_handle_name ( device ), strerror ( rc ) );
 
186
                return rc;
 
187
        }
 
188
 
 
189
        /* Get PCI device information */
 
190
        if ( ( rc = efipci_info ( pci_device, &pci ) ) != 0 ) {
 
191
                DBGC ( device, "EFIDEV %s could not get PCI information: %s\n",
 
192
                       efi_handle_name ( device ), strerror ( rc ) );
 
193
                return rc;
 
194
        }
 
195
 
 
196
        /* Populate device information */
 
197
        memcpy ( &dev->desc, &pci.dev.desc, sizeof ( dev->desc ) );
 
198
        snprintf ( dev->name, sizeof ( dev->name ), "%s-%s",
 
199
                   prefix, pci.dev.name );
 
200
 
 
201
        return 0;
 
202
}
 
203
 
 
204
/**
 
205
 * Get underlying device information
 
206
 *
 
207
 * @v device            EFI device handle
 
208
 * @v prefix            Device name prefix
 
209
 * @v dev               Generic device to fill in
 
210
 */
 
211
void efi_device_info ( EFI_HANDLE device, const char *prefix,
 
212
                       struct device *dev ) {
 
213
        int rc;
 
214
 
 
215
        /* Try getting underlying PCI device information */
 
216
        if ( ( rc = efi_pci_info ( device, prefix, dev ) ) == 0 )
 
217
                return;
 
218
 
 
219
        /* If we cannot get any underlying device information, fall
 
220
         * back to providing information about the EFI handle.
 
221
         */
 
222
        DBGC ( device, "EFIDEV %s could not get underlying device "
 
223
               "information\n", efi_handle_name ( device ) );
 
224
        dev->desc.bus_type = BUS_TYPE_EFI;
 
225
        snprintf ( dev->name, sizeof ( dev->name ), "%s-%p", prefix, device );
 
226
}