Commit 191a41cc3295f1f468f70fe52b4b2ec93992abce

Authored by Heinrich Schuchardt
Committed by Alexander Graf
1 parent fe1599daf5

efi_loader: open_info in OpenProtocol

efi_open_protocol has to keep track of opened protocols.

OpenProtocol enters the agent and controller handle
information into this list.

A unit test is supplied with a subsequent patch.

Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
Signed-off-by: Alexander Graf <agraf@suse.de>

Showing 1 changed file with 103 additions and 4 deletions Side-by-side Diff

lib/efi_loader/efi_boottime.c
... ... @@ -2123,6 +2123,101 @@
2123 2123 /*
2124 2124 * Open protocol interface on a handle.
2125 2125 *
  2126 + * @handler handler of a protocol
  2127 + * @protocol_interface interface implementing the protocol
  2128 + * @agent_handle handle of the driver
  2129 + * @controller_handle handle of the controller
  2130 + * @attributes attributes indicating how to open the protocol
  2131 + * @return status code
  2132 + */
  2133 +static efi_status_t efi_protocol_open(
  2134 + struct efi_handler *handler,
  2135 + void **protocol_interface, void *agent_handle,
  2136 + void *controller_handle, uint32_t attributes)
  2137 +{
  2138 + struct efi_open_protocol_info_item *item;
  2139 + struct efi_open_protocol_info_entry *match = NULL;
  2140 + bool opened_by_driver = false;
  2141 + bool opened_exclusive = false;
  2142 +
  2143 + /* If there is no agent, only return the interface */
  2144 + if (!agent_handle)
  2145 + goto out;
  2146 +
  2147 + /* For TEST_PROTOCOL ignore interface attribute */
  2148 + if (attributes != EFI_OPEN_PROTOCOL_TEST_PROTOCOL)
  2149 + *protocol_interface = NULL;
  2150 +
  2151 + /*
  2152 + * Check if the protocol is already opened by a driver with the same
  2153 + * attributes or opened exclusively
  2154 + */
  2155 + list_for_each_entry(item, &handler->open_infos, link) {
  2156 + if (item->info.agent_handle == agent_handle) {
  2157 + if ((attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) &&
  2158 + (item->info.attributes == attributes))
  2159 + return EFI_ALREADY_STARTED;
  2160 + }
  2161 + if (item->info.attributes & EFI_OPEN_PROTOCOL_EXCLUSIVE)
  2162 + opened_exclusive = true;
  2163 + }
  2164 +
  2165 + /* Only one controller can open the protocol exclusively */
  2166 + if (opened_exclusive && attributes &
  2167 + (EFI_OPEN_PROTOCOL_EXCLUSIVE | EFI_OPEN_PROTOCOL_BY_DRIVER))
  2168 + return EFI_ACCESS_DENIED;
  2169 +
  2170 + /* Prepare exclusive opening */
  2171 + if (attributes & EFI_OPEN_PROTOCOL_EXCLUSIVE) {
  2172 + /* Try to disconnect controllers */
  2173 + list_for_each_entry(item, &handler->open_infos, link) {
  2174 + if (item->info.attributes ==
  2175 + EFI_OPEN_PROTOCOL_BY_DRIVER)
  2176 + EFI_CALL(efi_disconnect_controller(
  2177 + item->info.controller_handle,
  2178 + item->info.agent_handle,
  2179 + NULL));
  2180 + }
  2181 + opened_by_driver = false;
  2182 + /* Check if all controllers are disconnected */
  2183 + list_for_each_entry(item, &handler->open_infos, link) {
  2184 + if (item->info.attributes & EFI_OPEN_PROTOCOL_BY_DRIVER)
  2185 + opened_by_driver = true;
  2186 + }
  2187 + /* Only one controller can be conncected */
  2188 + if (opened_by_driver)
  2189 + return EFI_ACCESS_DENIED;
  2190 + }
  2191 +
  2192 + /* Find existing entry */
  2193 + list_for_each_entry(item, &handler->open_infos, link) {
  2194 + if (item->info.agent_handle == agent_handle &&
  2195 + item->info.controller_handle == controller_handle)
  2196 + match = &item->info;
  2197 + }
  2198 + /* None found, create one */
  2199 + if (!match) {
  2200 + match = efi_create_open_info(handler);
  2201 + if (!match)
  2202 + return EFI_OUT_OF_RESOURCES;
  2203 + }
  2204 +
  2205 + match->agent_handle = agent_handle;
  2206 + match->controller_handle = controller_handle;
  2207 + match->attributes = attributes;
  2208 + match->open_count++;
  2209 +
  2210 +out:
  2211 + /* For TEST_PROTOCOL ignore interface attribute. */
  2212 + if (attributes != EFI_OPEN_PROTOCOL_TEST_PROTOCOL)
  2213 + *protocol_interface = handler->protocol_interface;
  2214 +
  2215 + return EFI_SUCCESS;
  2216 +}
  2217 +
  2218 +/*
  2219 + * Open protocol interface on a handle.
  2220 + *
2126 2221 * This function implements the OpenProtocol interface.
2127 2222 * See the Unified Extensible Firmware Interface (UEFI) specification
2128 2223 * for details.
2129 2224  
2130 2225  
2131 2226  
... ... @@ -2161,12 +2256,16 @@
2161 2256 case EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER:
2162 2257 if (controller_handle == handle)
2163 2258 goto out;
  2259 + /* fall-through */
2164 2260 case EFI_OPEN_PROTOCOL_BY_DRIVER:
2165 2261 case EFI_OPEN_PROTOCOL_BY_DRIVER | EFI_OPEN_PROTOCOL_EXCLUSIVE:
2166   - if (controller_handle == NULL)
  2262 + /* Check that the controller handle is valid */
  2263 + if (!efi_search_obj(controller_handle))
2167 2264 goto out;
  2265 + /* fall-through */
2168 2266 case EFI_OPEN_PROTOCOL_EXCLUSIVE:
2169   - if (agent_handle == NULL)
  2267 + /* Check that the agent handle is valid */
  2268 + if (!efi_search_obj(agent_handle))
2170 2269 goto out;
2171 2270 break;
2172 2271 default:
... ... @@ -2177,8 +2276,8 @@
2177 2276 if (r != EFI_SUCCESS)
2178 2277 goto out;
2179 2278  
2180   - if (attributes != EFI_OPEN_PROTOCOL_TEST_PROTOCOL)
2181   - *protocol_interface = handler->protocol_interface;
  2279 + r = efi_protocol_open(handler, protocol_interface, agent_handle,
  2280 + controller_handle, attributes);
2182 2281 out:
2183 2282 return EFI_EXIT(r);
2184 2283 }