| .. | .. |
|---|
| 4 | 4 | * |
|---|
| 5 | 5 | * Copyright (c) 2003 Manuel Estrada Sainz |
|---|
| 6 | 6 | * |
|---|
| 7 | | - * Please see Documentation/firmware_class/ for more information. |
|---|
| 7 | + * Please see Documentation/driver-api/firmware/ for more information. |
|---|
| 8 | 8 | * |
|---|
| 9 | 9 | */ |
|---|
| 10 | 10 | |
|---|
| .. | .. |
|---|
| 12 | 12 | |
|---|
| 13 | 13 | #include <linux/capability.h> |
|---|
| 14 | 14 | #include <linux/device.h> |
|---|
| 15 | +#include <linux/kernel_read_file.h> |
|---|
| 15 | 16 | #include <linux/module.h> |
|---|
| 16 | 17 | #include <linux/init.h> |
|---|
| 17 | 18 | #include <linux/timer.h> |
|---|
| .. | .. |
|---|
| 33 | 34 | #include <linux/syscore_ops.h> |
|---|
| 34 | 35 | #include <linux/reboot.h> |
|---|
| 35 | 36 | #include <linux/security.h> |
|---|
| 37 | +#include <linux/xz.h> |
|---|
| 36 | 38 | |
|---|
| 37 | 39 | #include <generated/utsrelease.h> |
|---|
| 38 | 40 | |
|---|
| .. | .. |
|---|
| 163 | 165 | return __fw_state_wait_common(fw_priv, MAX_SCHEDULE_TIMEOUT); |
|---|
| 164 | 166 | } |
|---|
| 165 | 167 | |
|---|
| 166 | | -static int fw_cache_piggyback_on_request(const char *name); |
|---|
| 168 | +static void fw_cache_piggyback_on_request(struct fw_priv *fw_priv); |
|---|
| 167 | 169 | |
|---|
| 168 | 170 | static struct fw_priv *__allocate_fw_priv(const char *fw_name, |
|---|
| 169 | 171 | struct firmware_cache *fwc, |
|---|
| 170 | | - void *dbuf, size_t size) |
|---|
| 172 | + void *dbuf, |
|---|
| 173 | + size_t size, |
|---|
| 174 | + size_t offset, |
|---|
| 175 | + u32 opt_flags) |
|---|
| 171 | 176 | { |
|---|
| 172 | 177 | struct fw_priv *fw_priv; |
|---|
| 178 | + |
|---|
| 179 | + /* For a partial read, the buffer must be preallocated. */ |
|---|
| 180 | + if ((opt_flags & FW_OPT_PARTIAL) && !dbuf) |
|---|
| 181 | + return NULL; |
|---|
| 182 | + |
|---|
| 183 | + /* Only partial reads are allowed to use an offset. */ |
|---|
| 184 | + if (offset != 0 && !(opt_flags & FW_OPT_PARTIAL)) |
|---|
| 185 | + return NULL; |
|---|
| 173 | 186 | |
|---|
| 174 | 187 | fw_priv = kzalloc(sizeof(*fw_priv), GFP_ATOMIC); |
|---|
| 175 | 188 | if (!fw_priv) |
|---|
| .. | .. |
|---|
| 185 | 198 | fw_priv->fwc = fwc; |
|---|
| 186 | 199 | fw_priv->data = dbuf; |
|---|
| 187 | 200 | fw_priv->allocated_size = size; |
|---|
| 201 | + fw_priv->offset = offset; |
|---|
| 202 | + fw_priv->opt_flags = opt_flags; |
|---|
| 188 | 203 | fw_state_init(fw_priv); |
|---|
| 189 | 204 | #ifdef CONFIG_FW_LOADER_USER_HELPER |
|---|
| 190 | 205 | INIT_LIST_HEAD(&fw_priv->pending_list); |
|---|
| .. | .. |
|---|
| 209 | 224 | /* Returns 1 for batching firmware requests with the same name */ |
|---|
| 210 | 225 | static int alloc_lookup_fw_priv(const char *fw_name, |
|---|
| 211 | 226 | struct firmware_cache *fwc, |
|---|
| 212 | | - struct fw_priv **fw_priv, void *dbuf, |
|---|
| 213 | | - size_t size, enum fw_opt opt_flags) |
|---|
| 227 | + struct fw_priv **fw_priv, |
|---|
| 228 | + void *dbuf, |
|---|
| 229 | + size_t size, |
|---|
| 230 | + size_t offset, |
|---|
| 231 | + u32 opt_flags) |
|---|
| 214 | 232 | { |
|---|
| 215 | 233 | struct fw_priv *tmp; |
|---|
| 216 | 234 | |
|---|
| 217 | 235 | spin_lock(&fwc->lock); |
|---|
| 218 | | - if (!(opt_flags & FW_OPT_NOCACHE)) { |
|---|
| 236 | + /* |
|---|
| 237 | + * Do not merge requests that are marked to be non-cached or |
|---|
| 238 | + * are performing partial reads. |
|---|
| 239 | + */ |
|---|
| 240 | + if (!(opt_flags & (FW_OPT_NOCACHE | FW_OPT_PARTIAL))) { |
|---|
| 219 | 241 | tmp = __lookup_fw_priv(fw_name); |
|---|
| 220 | 242 | if (tmp) { |
|---|
| 221 | 243 | kref_get(&tmp->ref); |
|---|
| .. | .. |
|---|
| 226 | 248 | } |
|---|
| 227 | 249 | } |
|---|
| 228 | 250 | |
|---|
| 229 | | - tmp = __allocate_fw_priv(fw_name, fwc, dbuf, size); |
|---|
| 251 | + tmp = __allocate_fw_priv(fw_name, fwc, dbuf, size, offset, opt_flags); |
|---|
| 230 | 252 | if (tmp) { |
|---|
| 231 | 253 | INIT_LIST_HEAD(&tmp->list); |
|---|
| 232 | 254 | if (!(opt_flags & FW_OPT_NOCACHE)) |
|---|
| .. | .. |
|---|
| 252 | 274 | list_del(&fw_priv->list); |
|---|
| 253 | 275 | spin_unlock(&fwc->lock); |
|---|
| 254 | 276 | |
|---|
| 255 | | -#ifdef CONFIG_FW_LOADER_USER_HELPER |
|---|
| 256 | | - if (fw_priv->is_paged_buf) { |
|---|
| 257 | | - int i; |
|---|
| 258 | | - vunmap(fw_priv->data); |
|---|
| 259 | | - for (i = 0; i < fw_priv->nr_pages; i++) |
|---|
| 260 | | - __free_page(fw_priv->pages[i]); |
|---|
| 261 | | - vfree(fw_priv->pages); |
|---|
| 262 | | - } else |
|---|
| 263 | | -#endif |
|---|
| 264 | | - if (!fw_priv->allocated_size) |
|---|
| 277 | + if (fw_is_paged_buf(fw_priv)) |
|---|
| 278 | + fw_free_paged_buf(fw_priv); |
|---|
| 279 | + else if (!fw_priv->allocated_size) |
|---|
| 265 | 280 | vfree(fw_priv->data); |
|---|
| 281 | + |
|---|
| 266 | 282 | kfree_const(fw_priv->fw_name); |
|---|
| 267 | 283 | kfree(fw_priv); |
|---|
| 268 | 284 | } |
|---|
| .. | .. |
|---|
| 274 | 290 | if (!kref_put(&fw_priv->ref, __free_fw_priv)) |
|---|
| 275 | 291 | spin_unlock(&fwc->lock); |
|---|
| 276 | 292 | } |
|---|
| 293 | + |
|---|
| 294 | +#ifdef CONFIG_FW_LOADER_PAGED_BUF |
|---|
| 295 | +bool fw_is_paged_buf(struct fw_priv *fw_priv) |
|---|
| 296 | +{ |
|---|
| 297 | + return fw_priv->is_paged_buf; |
|---|
| 298 | +} |
|---|
| 299 | + |
|---|
| 300 | +void fw_free_paged_buf(struct fw_priv *fw_priv) |
|---|
| 301 | +{ |
|---|
| 302 | + int i; |
|---|
| 303 | + |
|---|
| 304 | + if (!fw_priv->pages) |
|---|
| 305 | + return; |
|---|
| 306 | + |
|---|
| 307 | + vunmap(fw_priv->data); |
|---|
| 308 | + |
|---|
| 309 | + for (i = 0; i < fw_priv->nr_pages; i++) |
|---|
| 310 | + __free_page(fw_priv->pages[i]); |
|---|
| 311 | + kvfree(fw_priv->pages); |
|---|
| 312 | + fw_priv->pages = NULL; |
|---|
| 313 | + fw_priv->page_array_size = 0; |
|---|
| 314 | + fw_priv->nr_pages = 0; |
|---|
| 315 | +} |
|---|
| 316 | + |
|---|
| 317 | +int fw_grow_paged_buf(struct fw_priv *fw_priv, int pages_needed) |
|---|
| 318 | +{ |
|---|
| 319 | + /* If the array of pages is too small, grow it */ |
|---|
| 320 | + if (fw_priv->page_array_size < pages_needed) { |
|---|
| 321 | + int new_array_size = max(pages_needed, |
|---|
| 322 | + fw_priv->page_array_size * 2); |
|---|
| 323 | + struct page **new_pages; |
|---|
| 324 | + |
|---|
| 325 | + new_pages = kvmalloc_array(new_array_size, sizeof(void *), |
|---|
| 326 | + GFP_KERNEL); |
|---|
| 327 | + if (!new_pages) |
|---|
| 328 | + return -ENOMEM; |
|---|
| 329 | + memcpy(new_pages, fw_priv->pages, |
|---|
| 330 | + fw_priv->page_array_size * sizeof(void *)); |
|---|
| 331 | + memset(&new_pages[fw_priv->page_array_size], 0, sizeof(void *) * |
|---|
| 332 | + (new_array_size - fw_priv->page_array_size)); |
|---|
| 333 | + kvfree(fw_priv->pages); |
|---|
| 334 | + fw_priv->pages = new_pages; |
|---|
| 335 | + fw_priv->page_array_size = new_array_size; |
|---|
| 336 | + } |
|---|
| 337 | + |
|---|
| 338 | + while (fw_priv->nr_pages < pages_needed) { |
|---|
| 339 | + fw_priv->pages[fw_priv->nr_pages] = |
|---|
| 340 | + alloc_page(GFP_KERNEL | __GFP_HIGHMEM); |
|---|
| 341 | + |
|---|
| 342 | + if (!fw_priv->pages[fw_priv->nr_pages]) |
|---|
| 343 | + return -ENOMEM; |
|---|
| 344 | + fw_priv->nr_pages++; |
|---|
| 345 | + } |
|---|
| 346 | + |
|---|
| 347 | + return 0; |
|---|
| 348 | +} |
|---|
| 349 | + |
|---|
| 350 | +int fw_map_paged_buf(struct fw_priv *fw_priv) |
|---|
| 351 | +{ |
|---|
| 352 | + /* one pages buffer should be mapped/unmapped only once */ |
|---|
| 353 | + if (!fw_priv->pages) |
|---|
| 354 | + return 0; |
|---|
| 355 | + |
|---|
| 356 | + vunmap(fw_priv->data); |
|---|
| 357 | + fw_priv->data = vmap(fw_priv->pages, fw_priv->nr_pages, 0, |
|---|
| 358 | + PAGE_KERNEL_RO); |
|---|
| 359 | + if (!fw_priv->data) |
|---|
| 360 | + return -ENOMEM; |
|---|
| 361 | + |
|---|
| 362 | + return 0; |
|---|
| 363 | +} |
|---|
| 364 | +#endif |
|---|
| 365 | + |
|---|
| 366 | +/* |
|---|
| 367 | + * XZ-compressed firmware support |
|---|
| 368 | + */ |
|---|
| 369 | +#ifdef CONFIG_FW_LOADER_COMPRESS |
|---|
| 370 | +/* show an error and return the standard error code */ |
|---|
| 371 | +static int fw_decompress_xz_error(struct device *dev, enum xz_ret xz_ret) |
|---|
| 372 | +{ |
|---|
| 373 | + if (xz_ret != XZ_STREAM_END) { |
|---|
| 374 | + dev_warn(dev, "xz decompression failed (xz_ret=%d)\n", xz_ret); |
|---|
| 375 | + return xz_ret == XZ_MEM_ERROR ? -ENOMEM : -EINVAL; |
|---|
| 376 | + } |
|---|
| 377 | + return 0; |
|---|
| 378 | +} |
|---|
| 379 | + |
|---|
| 380 | +/* single-shot decompression onto the pre-allocated buffer */ |
|---|
| 381 | +static int fw_decompress_xz_single(struct device *dev, struct fw_priv *fw_priv, |
|---|
| 382 | + size_t in_size, const void *in_buffer) |
|---|
| 383 | +{ |
|---|
| 384 | + struct xz_dec *xz_dec; |
|---|
| 385 | + struct xz_buf xz_buf; |
|---|
| 386 | + enum xz_ret xz_ret; |
|---|
| 387 | + |
|---|
| 388 | + xz_dec = xz_dec_init(XZ_SINGLE, (u32)-1); |
|---|
| 389 | + if (!xz_dec) |
|---|
| 390 | + return -ENOMEM; |
|---|
| 391 | + |
|---|
| 392 | + xz_buf.in_size = in_size; |
|---|
| 393 | + xz_buf.in = in_buffer; |
|---|
| 394 | + xz_buf.in_pos = 0; |
|---|
| 395 | + xz_buf.out_size = fw_priv->allocated_size; |
|---|
| 396 | + xz_buf.out = fw_priv->data; |
|---|
| 397 | + xz_buf.out_pos = 0; |
|---|
| 398 | + |
|---|
| 399 | + xz_ret = xz_dec_run(xz_dec, &xz_buf); |
|---|
| 400 | + xz_dec_end(xz_dec); |
|---|
| 401 | + |
|---|
| 402 | + fw_priv->size = xz_buf.out_pos; |
|---|
| 403 | + return fw_decompress_xz_error(dev, xz_ret); |
|---|
| 404 | +} |
|---|
| 405 | + |
|---|
| 406 | +/* decompression on paged buffer and map it */ |
|---|
| 407 | +static int fw_decompress_xz_pages(struct device *dev, struct fw_priv *fw_priv, |
|---|
| 408 | + size_t in_size, const void *in_buffer) |
|---|
| 409 | +{ |
|---|
| 410 | + struct xz_dec *xz_dec; |
|---|
| 411 | + struct xz_buf xz_buf; |
|---|
| 412 | + enum xz_ret xz_ret; |
|---|
| 413 | + struct page *page; |
|---|
| 414 | + int err = 0; |
|---|
| 415 | + |
|---|
| 416 | + xz_dec = xz_dec_init(XZ_DYNALLOC, (u32)-1); |
|---|
| 417 | + if (!xz_dec) |
|---|
| 418 | + return -ENOMEM; |
|---|
| 419 | + |
|---|
| 420 | + xz_buf.in_size = in_size; |
|---|
| 421 | + xz_buf.in = in_buffer; |
|---|
| 422 | + xz_buf.in_pos = 0; |
|---|
| 423 | + |
|---|
| 424 | + fw_priv->is_paged_buf = true; |
|---|
| 425 | + fw_priv->size = 0; |
|---|
| 426 | + do { |
|---|
| 427 | + if (fw_grow_paged_buf(fw_priv, fw_priv->nr_pages + 1)) { |
|---|
| 428 | + err = -ENOMEM; |
|---|
| 429 | + goto out; |
|---|
| 430 | + } |
|---|
| 431 | + |
|---|
| 432 | + /* decompress onto the new allocated page */ |
|---|
| 433 | + page = fw_priv->pages[fw_priv->nr_pages - 1]; |
|---|
| 434 | + xz_buf.out = kmap(page); |
|---|
| 435 | + xz_buf.out_pos = 0; |
|---|
| 436 | + xz_buf.out_size = PAGE_SIZE; |
|---|
| 437 | + xz_ret = xz_dec_run(xz_dec, &xz_buf); |
|---|
| 438 | + kunmap(page); |
|---|
| 439 | + fw_priv->size += xz_buf.out_pos; |
|---|
| 440 | + /* partial decompression means either end or error */ |
|---|
| 441 | + if (xz_buf.out_pos != PAGE_SIZE) |
|---|
| 442 | + break; |
|---|
| 443 | + } while (xz_ret == XZ_OK); |
|---|
| 444 | + |
|---|
| 445 | + err = fw_decompress_xz_error(dev, xz_ret); |
|---|
| 446 | + if (!err) |
|---|
| 447 | + err = fw_map_paged_buf(fw_priv); |
|---|
| 448 | + |
|---|
| 449 | + out: |
|---|
| 450 | + xz_dec_end(xz_dec); |
|---|
| 451 | + return err; |
|---|
| 452 | +} |
|---|
| 453 | + |
|---|
| 454 | +static int fw_decompress_xz(struct device *dev, struct fw_priv *fw_priv, |
|---|
| 455 | + size_t in_size, const void *in_buffer) |
|---|
| 456 | +{ |
|---|
| 457 | + /* if the buffer is pre-allocated, we can perform in single-shot mode */ |
|---|
| 458 | + if (fw_priv->data) |
|---|
| 459 | + return fw_decompress_xz_single(dev, fw_priv, in_size, in_buffer); |
|---|
| 460 | + else |
|---|
| 461 | + return fw_decompress_xz_pages(dev, fw_priv, in_size, in_buffer); |
|---|
| 462 | +} |
|---|
| 463 | +#endif /* CONFIG_FW_LOADER_COMPRESS */ |
|---|
| 277 | 464 | |
|---|
| 278 | 465 | /* direct firmware loading support */ |
|---|
| 279 | 466 | static char fw_path_para[256]; |
|---|
| .. | .. |
|---|
| 294 | 481 | MODULE_PARM_DESC(path, "customized firmware image search path with a higher priority than default path"); |
|---|
| 295 | 482 | |
|---|
| 296 | 483 | static int |
|---|
| 297 | | -fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv) |
|---|
| 484 | +fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv, |
|---|
| 485 | + const char *suffix, |
|---|
| 486 | + int (*decompress)(struct device *dev, |
|---|
| 487 | + struct fw_priv *fw_priv, |
|---|
| 488 | + size_t in_size, |
|---|
| 489 | + const void *in_buffer)) |
|---|
| 298 | 490 | { |
|---|
| 299 | | - loff_t size; |
|---|
| 491 | + size_t size; |
|---|
| 300 | 492 | int i, len; |
|---|
| 301 | 493 | int rc = -ENOENT; |
|---|
| 302 | 494 | char *path; |
|---|
| 303 | | - enum kernel_read_file_id id = READING_FIRMWARE; |
|---|
| 304 | 495 | size_t msize = INT_MAX; |
|---|
| 496 | + void *buffer = NULL; |
|---|
| 305 | 497 | |
|---|
| 306 | 498 | /* Already populated data member means we're loading into a buffer */ |
|---|
| 307 | | - if (fw_priv->data) { |
|---|
| 308 | | - id = READING_FIRMWARE_PREALLOC_BUFFER; |
|---|
| 499 | + if (!decompress && fw_priv->data) { |
|---|
| 500 | + buffer = fw_priv->data; |
|---|
| 309 | 501 | msize = fw_priv->allocated_size; |
|---|
| 310 | 502 | } |
|---|
| 311 | 503 | |
|---|
| .. | .. |
|---|
| 314 | 506 | return -ENOMEM; |
|---|
| 315 | 507 | |
|---|
| 316 | 508 | for (i = 0; i < ARRAY_SIZE(fw_path); i++) { |
|---|
| 509 | + size_t file_size = 0; |
|---|
| 510 | + size_t *file_size_ptr = NULL; |
|---|
| 511 | + |
|---|
| 317 | 512 | /* skip the unset customized path */ |
|---|
| 318 | 513 | if (!fw_path[i][0]) |
|---|
| 319 | 514 | continue; |
|---|
| 320 | 515 | |
|---|
| 321 | | - len = snprintf(path, PATH_MAX, "%s/%s", |
|---|
| 322 | | - fw_path[i], fw_priv->fw_name); |
|---|
| 516 | + len = snprintf(path, PATH_MAX, "%s/%s%s", |
|---|
| 517 | + fw_path[i], fw_priv->fw_name, suffix); |
|---|
| 323 | 518 | if (len >= PATH_MAX) { |
|---|
| 324 | 519 | rc = -ENAMETOOLONG; |
|---|
| 325 | 520 | break; |
|---|
| 326 | 521 | } |
|---|
| 327 | 522 | |
|---|
| 328 | 523 | fw_priv->size = 0; |
|---|
| 329 | | - rc = kernel_read_file_from_path(path, &fw_priv->data, &size, |
|---|
| 330 | | - msize, id); |
|---|
| 331 | | - if (rc) { |
|---|
| 332 | | - if (rc == -ENOENT) |
|---|
| 333 | | - dev_dbg(device, "loading %s failed with error %d\n", |
|---|
| 334 | | - path, rc); |
|---|
| 335 | | - else |
|---|
| 524 | + |
|---|
| 525 | + /* |
|---|
| 526 | + * The total file size is only examined when doing a partial |
|---|
| 527 | + * read; the "full read" case needs to fail if the whole |
|---|
| 528 | + * firmware was not completely loaded. |
|---|
| 529 | + */ |
|---|
| 530 | + if ((fw_priv->opt_flags & FW_OPT_PARTIAL) && buffer) |
|---|
| 531 | + file_size_ptr = &file_size; |
|---|
| 532 | + |
|---|
| 533 | + /* load firmware files from the mount namespace of init */ |
|---|
| 534 | + rc = kernel_read_file_from_path_initns(path, fw_priv->offset, |
|---|
| 535 | + &buffer, msize, |
|---|
| 536 | + file_size_ptr, |
|---|
| 537 | + READING_FIRMWARE); |
|---|
| 538 | + if (rc < 0) { |
|---|
| 539 | + if (rc != -ENOENT) |
|---|
| 336 | 540 | dev_warn(device, "loading %s failed with error %d\n", |
|---|
| 337 | 541 | path, rc); |
|---|
| 542 | + else |
|---|
| 543 | + dev_dbg(device, "loading %s failed for no such file or directory.\n", |
|---|
| 544 | + path); |
|---|
| 338 | 545 | continue; |
|---|
| 339 | 546 | } |
|---|
| 340 | | - dev_dbg(device, "direct-loading %s\n", fw_priv->fw_name); |
|---|
| 341 | | - fw_priv->size = size; |
|---|
| 547 | + size = rc; |
|---|
| 548 | + rc = 0; |
|---|
| 549 | + |
|---|
| 550 | + dev_dbg(device, "Loading firmware from %s\n", path); |
|---|
| 551 | + if (decompress) { |
|---|
| 552 | + dev_dbg(device, "f/w decompressing %s\n", |
|---|
| 553 | + fw_priv->fw_name); |
|---|
| 554 | + rc = decompress(device, fw_priv, size, buffer); |
|---|
| 555 | + /* discard the superfluous original content */ |
|---|
| 556 | + vfree(buffer); |
|---|
| 557 | + buffer = NULL; |
|---|
| 558 | + if (rc) { |
|---|
| 559 | + fw_free_paged_buf(fw_priv); |
|---|
| 560 | + continue; |
|---|
| 561 | + } |
|---|
| 562 | + } else { |
|---|
| 563 | + dev_dbg(device, "direct-loading %s\n", |
|---|
| 564 | + fw_priv->fw_name); |
|---|
| 565 | + if (!fw_priv->data) |
|---|
| 566 | + fw_priv->data = buffer; |
|---|
| 567 | + fw_priv->size = size; |
|---|
| 568 | + } |
|---|
| 342 | 569 | fw_state_done(fw_priv); |
|---|
| 343 | 570 | break; |
|---|
| 344 | 571 | } |
|---|
| .. | .. |
|---|
| 362 | 589 | static void fw_set_page_data(struct fw_priv *fw_priv, struct firmware *fw) |
|---|
| 363 | 590 | { |
|---|
| 364 | 591 | fw->priv = fw_priv; |
|---|
| 365 | | -#ifdef CONFIG_FW_LOADER_USER_HELPER |
|---|
| 366 | | - fw->pages = fw_priv->pages; |
|---|
| 367 | | -#endif |
|---|
| 368 | 592 | fw->size = fw_priv->size; |
|---|
| 369 | 593 | fw->data = fw_priv->data; |
|---|
| 370 | 594 | |
|---|
| .. | .. |
|---|
| 449 | 673 | } |
|---|
| 450 | 674 | #endif |
|---|
| 451 | 675 | |
|---|
| 452 | | -int assign_fw(struct firmware *fw, struct device *device, |
|---|
| 453 | | - enum fw_opt opt_flags) |
|---|
| 676 | +int assign_fw(struct firmware *fw, struct device *device) |
|---|
| 454 | 677 | { |
|---|
| 455 | 678 | struct fw_priv *fw_priv = fw->priv; |
|---|
| 456 | 679 | int ret; |
|---|
| .. | .. |
|---|
| 469 | 692 | * should be fixed in devres or driver core. |
|---|
| 470 | 693 | */ |
|---|
| 471 | 694 | /* don't cache firmware handled without uevent */ |
|---|
| 472 | | - if (device && (opt_flags & FW_OPT_UEVENT) && |
|---|
| 473 | | - !(opt_flags & FW_OPT_NOCACHE)) { |
|---|
| 695 | + if (device && (fw_priv->opt_flags & FW_OPT_UEVENT) && |
|---|
| 696 | + !(fw_priv->opt_flags & FW_OPT_NOCACHE)) { |
|---|
| 474 | 697 | ret = fw_add_devm_name(device, fw_priv->fw_name); |
|---|
| 475 | 698 | if (ret) { |
|---|
| 476 | 699 | mutex_unlock(&fw_lock); |
|---|
| .. | .. |
|---|
| 482 | 705 | * After caching firmware image is started, let it piggyback |
|---|
| 483 | 706 | * on request firmware. |
|---|
| 484 | 707 | */ |
|---|
| 485 | | - if (!(opt_flags & FW_OPT_NOCACHE) && |
|---|
| 486 | | - fw_priv->fwc->state == FW_LOADER_START_CACHE) { |
|---|
| 487 | | - if (fw_cache_piggyback_on_request(fw_priv->fw_name)) |
|---|
| 488 | | - kref_get(&fw_priv->ref); |
|---|
| 489 | | - } |
|---|
| 708 | + if (!(fw_priv->opt_flags & FW_OPT_NOCACHE) && |
|---|
| 709 | + fw_priv->fwc->state == FW_LOADER_START_CACHE) |
|---|
| 710 | + fw_cache_piggyback_on_request(fw_priv); |
|---|
| 490 | 711 | |
|---|
| 491 | 712 | /* pass the pages buffer to driver at the last minute */ |
|---|
| 492 | 713 | fw_set_page_data(fw_priv, fw); |
|---|
| .. | .. |
|---|
| 501 | 722 | static int |
|---|
| 502 | 723 | _request_firmware_prepare(struct firmware **firmware_p, const char *name, |
|---|
| 503 | 724 | struct device *device, void *dbuf, size_t size, |
|---|
| 504 | | - enum fw_opt opt_flags) |
|---|
| 725 | + size_t offset, u32 opt_flags) |
|---|
| 505 | 726 | { |
|---|
| 506 | 727 | struct firmware *firmware; |
|---|
| 507 | 728 | struct fw_priv *fw_priv; |
|---|
| .. | .. |
|---|
| 520 | 741 | } |
|---|
| 521 | 742 | |
|---|
| 522 | 743 | ret = alloc_lookup_fw_priv(name, &fw_cache, &fw_priv, dbuf, size, |
|---|
| 523 | | - opt_flags); |
|---|
| 744 | + offset, opt_flags); |
|---|
| 524 | 745 | |
|---|
| 525 | 746 | /* |
|---|
| 526 | 747 | * bind with 'priv' now to avoid warning in failure path |
|---|
| .. | .. |
|---|
| 569 | 790 | static int |
|---|
| 570 | 791 | _request_firmware(const struct firmware **firmware_p, const char *name, |
|---|
| 571 | 792 | struct device *device, void *buf, size_t size, |
|---|
| 572 | | - enum fw_opt opt_flags) |
|---|
| 793 | + size_t offset, u32 opt_flags) |
|---|
| 573 | 794 | { |
|---|
| 574 | 795 | struct firmware *fw = NULL; |
|---|
| 796 | + bool nondirect = false; |
|---|
| 575 | 797 | int ret; |
|---|
| 576 | 798 | |
|---|
| 577 | 799 | if (!firmware_p) |
|---|
| .. | .. |
|---|
| 583 | 805 | } |
|---|
| 584 | 806 | |
|---|
| 585 | 807 | ret = _request_firmware_prepare(&fw, name, device, buf, size, |
|---|
| 586 | | - opt_flags); |
|---|
| 808 | + offset, opt_flags); |
|---|
| 587 | 809 | if (ret <= 0) /* error or already assigned */ |
|---|
| 588 | 810 | goto out; |
|---|
| 589 | 811 | |
|---|
| 590 | | - ret = fw_get_filesystem_firmware(device, fw->priv); |
|---|
| 812 | + ret = fw_get_filesystem_firmware(device, fw->priv, "", NULL); |
|---|
| 813 | + |
|---|
| 814 | + /* Only full reads can support decompression, platform, and sysfs. */ |
|---|
| 815 | + if (!(opt_flags & FW_OPT_PARTIAL)) |
|---|
| 816 | + nondirect = true; |
|---|
| 817 | + |
|---|
| 818 | +#ifdef CONFIG_FW_LOADER_COMPRESS |
|---|
| 819 | + if (ret == -ENOENT && nondirect) |
|---|
| 820 | + ret = fw_get_filesystem_firmware(device, fw->priv, ".xz", |
|---|
| 821 | + fw_decompress_xz); |
|---|
| 822 | +#endif |
|---|
| 823 | + if (ret == -ENOENT && nondirect) |
|---|
| 824 | + ret = firmware_fallback_platform(fw->priv); |
|---|
| 825 | + |
|---|
| 591 | 826 | if (ret) { |
|---|
| 592 | 827 | if (!(opt_flags & FW_OPT_NO_WARN)) |
|---|
| 593 | 828 | dev_warn(device, |
|---|
| 594 | 829 | "Direct firmware load for %s failed with error %d\n", |
|---|
| 595 | 830 | name, ret); |
|---|
| 596 | | - ret = firmware_fallback_sysfs(fw, name, device, opt_flags, ret); |
|---|
| 831 | + if (nondirect) |
|---|
| 832 | + ret = firmware_fallback_sysfs(fw, name, device, |
|---|
| 833 | + opt_flags, ret); |
|---|
| 597 | 834 | } else |
|---|
| 598 | | - ret = assign_fw(fw, device, opt_flags); |
|---|
| 835 | + ret = assign_fw(fw, device); |
|---|
| 599 | 836 | |
|---|
| 600 | 837 | out: |
|---|
| 601 | 838 | if (ret < 0) { |
|---|
| .. | .. |
|---|
| 636 | 873 | |
|---|
| 637 | 874 | /* Need to pin this module until return */ |
|---|
| 638 | 875 | __module_get(THIS_MODULE); |
|---|
| 639 | | - ret = _request_firmware(firmware_p, name, device, NULL, 0, |
|---|
| 876 | + ret = _request_firmware(firmware_p, name, device, NULL, 0, 0, |
|---|
| 640 | 877 | FW_OPT_UEVENT); |
|---|
| 641 | 878 | module_put(THIS_MODULE); |
|---|
| 642 | 879 | return ret; |
|---|
| .. | .. |
|---|
| 649 | 886 | * @name: name of firmware file |
|---|
| 650 | 887 | * @device: device for which firmware is being loaded |
|---|
| 651 | 888 | * |
|---|
| 652 | | - * This function is similar in behaviour to request_firmware(), except |
|---|
| 653 | | - * it doesn't produce warning messages when the file is not found. |
|---|
| 654 | | - * The sysfs fallback mechanism is enabled if direct filesystem lookup fails, |
|---|
| 655 | | - * however, however failures to find the firmware file with it are still |
|---|
| 656 | | - * suppressed. It is therefore up to the driver to check for the return value |
|---|
| 657 | | - * of this call and to decide when to inform the users of errors. |
|---|
| 889 | + * This function is similar in behaviour to request_firmware(), except it |
|---|
| 890 | + * doesn't produce warning messages when the file is not found. The sysfs |
|---|
| 891 | + * fallback mechanism is enabled if direct filesystem lookup fails. However, |
|---|
| 892 | + * failures to find the firmware file with it are still suppressed. It is |
|---|
| 893 | + * therefore up to the driver to check for the return value of this call and to |
|---|
| 894 | + * decide when to inform the users of errors. |
|---|
| 658 | 895 | **/ |
|---|
| 659 | 896 | int firmware_request_nowarn(const struct firmware **firmware, const char *name, |
|---|
| 660 | 897 | struct device *device) |
|---|
| .. | .. |
|---|
| 663 | 900 | |
|---|
| 664 | 901 | /* Need to pin this module until return */ |
|---|
| 665 | 902 | __module_get(THIS_MODULE); |
|---|
| 666 | | - ret = _request_firmware(firmware, name, device, NULL, 0, |
|---|
| 903 | + ret = _request_firmware(firmware, name, device, NULL, 0, 0, |
|---|
| 667 | 904 | FW_OPT_UEVENT | FW_OPT_NO_WARN); |
|---|
| 668 | 905 | module_put(THIS_MODULE); |
|---|
| 669 | 906 | return ret; |
|---|
| .. | .. |
|---|
| 687 | 924 | int ret; |
|---|
| 688 | 925 | |
|---|
| 689 | 926 | __module_get(THIS_MODULE); |
|---|
| 690 | | - ret = _request_firmware(firmware_p, name, device, NULL, 0, |
|---|
| 927 | + ret = _request_firmware(firmware_p, name, device, NULL, 0, 0, |
|---|
| 691 | 928 | FW_OPT_UEVENT | FW_OPT_NO_WARN | |
|---|
| 692 | | - FW_OPT_NOFALLBACK); |
|---|
| 929 | + FW_OPT_NOFALLBACK_SYSFS); |
|---|
| 693 | 930 | module_put(THIS_MODULE); |
|---|
| 694 | 931 | return ret; |
|---|
| 695 | 932 | } |
|---|
| 696 | 933 | EXPORT_SYMBOL_GPL(request_firmware_direct); |
|---|
| 934 | + |
|---|
| 935 | +/** |
|---|
| 936 | + * firmware_request_platform() - request firmware with platform-fw fallback |
|---|
| 937 | + * @firmware: pointer to firmware image |
|---|
| 938 | + * @name: name of firmware file |
|---|
| 939 | + * @device: device for which firmware is being loaded |
|---|
| 940 | + * |
|---|
| 941 | + * This function is similar in behaviour to request_firmware, except that if |
|---|
| 942 | + * direct filesystem lookup fails, it will fallback to looking for a copy of the |
|---|
| 943 | + * requested firmware embedded in the platform's main (e.g. UEFI) firmware. |
|---|
| 944 | + **/ |
|---|
| 945 | +int firmware_request_platform(const struct firmware **firmware, |
|---|
| 946 | + const char *name, struct device *device) |
|---|
| 947 | +{ |
|---|
| 948 | + int ret; |
|---|
| 949 | + |
|---|
| 950 | + /* Need to pin this module until return */ |
|---|
| 951 | + __module_get(THIS_MODULE); |
|---|
| 952 | + ret = _request_firmware(firmware, name, device, NULL, 0, 0, |
|---|
| 953 | + FW_OPT_UEVENT | FW_OPT_FALLBACK_PLATFORM); |
|---|
| 954 | + module_put(THIS_MODULE); |
|---|
| 955 | + return ret; |
|---|
| 956 | +} |
|---|
| 957 | +EXPORT_SYMBOL_GPL(firmware_request_platform); |
|---|
| 697 | 958 | |
|---|
| 698 | 959 | /** |
|---|
| 699 | 960 | * firmware_request_cache() - cache firmware for suspend so resume can use it |
|---|
| .. | .. |
|---|
| 744 | 1005 | return -EOPNOTSUPP; |
|---|
| 745 | 1006 | |
|---|
| 746 | 1007 | __module_get(THIS_MODULE); |
|---|
| 747 | | - ret = _request_firmware(firmware_p, name, device, buf, size, |
|---|
| 1008 | + ret = _request_firmware(firmware_p, name, device, buf, size, 0, |
|---|
| 748 | 1009 | FW_OPT_UEVENT | FW_OPT_NOCACHE); |
|---|
| 749 | 1010 | module_put(THIS_MODULE); |
|---|
| 750 | 1011 | return ret; |
|---|
| 751 | 1012 | } |
|---|
| 752 | 1013 | EXPORT_SYMBOL(request_firmware_into_buf); |
|---|
| 1014 | + |
|---|
| 1015 | +/** |
|---|
| 1016 | + * request_partial_firmware_into_buf() - load partial firmware into a previously allocated buffer |
|---|
| 1017 | + * @firmware_p: pointer to firmware image |
|---|
| 1018 | + * @name: name of firmware file |
|---|
| 1019 | + * @device: device for which firmware is being loaded and DMA region allocated |
|---|
| 1020 | + * @buf: address of buffer to load firmware into |
|---|
| 1021 | + * @size: size of buffer |
|---|
| 1022 | + * @offset: offset into file to read |
|---|
| 1023 | + * |
|---|
| 1024 | + * This function works pretty much like request_firmware_into_buf except |
|---|
| 1025 | + * it allows a partial read of the file. |
|---|
| 1026 | + */ |
|---|
| 1027 | +int |
|---|
| 1028 | +request_partial_firmware_into_buf(const struct firmware **firmware_p, |
|---|
| 1029 | + const char *name, struct device *device, |
|---|
| 1030 | + void *buf, size_t size, size_t offset) |
|---|
| 1031 | +{ |
|---|
| 1032 | + int ret; |
|---|
| 1033 | + |
|---|
| 1034 | + if (fw_cache_is_setup(device, name)) |
|---|
| 1035 | + return -EOPNOTSUPP; |
|---|
| 1036 | + |
|---|
| 1037 | + __module_get(THIS_MODULE); |
|---|
| 1038 | + ret = _request_firmware(firmware_p, name, device, buf, size, offset, |
|---|
| 1039 | + FW_OPT_UEVENT | FW_OPT_NOCACHE | |
|---|
| 1040 | + FW_OPT_PARTIAL); |
|---|
| 1041 | + module_put(THIS_MODULE); |
|---|
| 1042 | + return ret; |
|---|
| 1043 | +} |
|---|
| 1044 | +EXPORT_SYMBOL(request_partial_firmware_into_buf); |
|---|
| 753 | 1045 | |
|---|
| 754 | 1046 | /** |
|---|
| 755 | 1047 | * release_firmware() - release the resource associated with a firmware image |
|---|
| .. | .. |
|---|
| 773 | 1065 | struct device *device; |
|---|
| 774 | 1066 | void *context; |
|---|
| 775 | 1067 | void (*cont)(const struct firmware *fw, void *context); |
|---|
| 776 | | - enum fw_opt opt_flags; |
|---|
| 1068 | + u32 opt_flags; |
|---|
| 777 | 1069 | }; |
|---|
| 778 | 1070 | |
|---|
| 779 | 1071 | static void request_firmware_work_func(struct work_struct *work) |
|---|
| .. | .. |
|---|
| 783 | 1075 | |
|---|
| 784 | 1076 | fw_work = container_of(work, struct firmware_work, work); |
|---|
| 785 | 1077 | |
|---|
| 786 | | - _request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0, |
|---|
| 1078 | + _request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0, 0, |
|---|
| 787 | 1079 | fw_work->opt_flags); |
|---|
| 788 | 1080 | fw_work->cont(fw, fw_work->context); |
|---|
| 789 | 1081 | put_device(fw_work->device); /* taken in request_firmware_nowait() */ |
|---|
| .. | .. |
|---|
| 964 | 1256 | return 0; |
|---|
| 965 | 1257 | } |
|---|
| 966 | 1258 | |
|---|
| 967 | | -static int fw_cache_piggyback_on_request(const char *name) |
|---|
| 1259 | +static void fw_cache_piggyback_on_request(struct fw_priv *fw_priv) |
|---|
| 968 | 1260 | { |
|---|
| 969 | | - struct firmware_cache *fwc = &fw_cache; |
|---|
| 1261 | + const char *name = fw_priv->fw_name; |
|---|
| 1262 | + struct firmware_cache *fwc = fw_priv->fwc; |
|---|
| 970 | 1263 | struct fw_cache_entry *fce; |
|---|
| 971 | | - int ret = 0; |
|---|
| 972 | 1264 | |
|---|
| 973 | 1265 | spin_lock(&fwc->name_lock); |
|---|
| 974 | 1266 | if (__fw_entry_found(name)) |
|---|
| .. | .. |
|---|
| 976 | 1268 | |
|---|
| 977 | 1269 | fce = alloc_fw_cache_entry(name); |
|---|
| 978 | 1270 | if (fce) { |
|---|
| 979 | | - ret = 1; |
|---|
| 980 | 1271 | list_add(&fce->list, &fwc->fw_names); |
|---|
| 1272 | + kref_get(&fw_priv->ref); |
|---|
| 981 | 1273 | pr_debug("%s: fw: %s\n", __func__, name); |
|---|
| 982 | 1274 | } |
|---|
| 983 | 1275 | found: |
|---|
| 984 | 1276 | spin_unlock(&fwc->name_lock); |
|---|
| 985 | | - return ret; |
|---|
| 986 | 1277 | } |
|---|
| 987 | 1278 | |
|---|
| 988 | 1279 | static void free_fw_cache_entry(struct fw_cache_entry *fce) |
|---|
| .. | .. |
|---|
| 1213 | 1504 | unregister_pm_notifier(&fw_cache.pm_notify); |
|---|
| 1214 | 1505 | } |
|---|
| 1215 | 1506 | #else |
|---|
| 1216 | | -static int fw_cache_piggyback_on_request(const char *name) |
|---|
| 1507 | +static void fw_cache_piggyback_on_request(struct fw_priv *fw_priv) |
|---|
| 1217 | 1508 | { |
|---|
| 1218 | | - return 0; |
|---|
| 1219 | 1509 | } |
|---|
| 1220 | 1510 | static inline int register_fw_pm_ops(void) |
|---|
| 1221 | 1511 | { |
|---|