1 /* 2 * Copyright 2012 Red Hat Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 */ 23 #include "priv.h" 24 25 static int 26 acpi_read_bios(acpi_handle rom_handle, u8 *bios, u32 offset, u32 length) 27 { 28 #if defined(CONFIG_ACPI) && defined(CONFIG_X86) 29 acpi_status status; 30 union acpi_object rom_arg_elements[2], *obj; 31 struct acpi_object_list rom_arg; 32 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL}; 33 34 rom_arg.count = 2; 35 rom_arg.pointer = &rom_arg_elements[0]; 36 37 rom_arg_elements[0].type = ACPI_TYPE_INTEGER; 38 rom_arg_elements[0].integer.value = offset; 39 40 rom_arg_elements[1].type = ACPI_TYPE_INTEGER; 41 rom_arg_elements[1].integer.value = length; 42 43 status = acpi_evaluate_object(rom_handle, NULL, &rom_arg, &buffer); 44 if (ACPI_FAILURE(status)) { 45 pr_info("failed to evaluate ROM got %s\n", 46 acpi_format_exception(status)); 47 return -ENODEV; 48 } 49 obj = (union acpi_object *)buffer.pointer; 50 length = min(length, obj->buffer.length); 51 memcpy(bios+offset, obj->buffer.pointer, length); 52 kfree(buffer.pointer); 53 return length; 54 #else 55 return -EINVAL; 56 #endif 57 } 58 59 /* This version of the shadow function disobeys the ACPI spec and tries 60 * to fetch in units of more than 4KiB at a time. This is a LOT faster 61 * on some systems, such as Lenovo W530. 62 */ 63 static u32 64 acpi_read_fast(void *data, u32 offset, u32 length, struct nvkm_bios *bios) 65 { 66 u32 limit = (offset + length + 0xfff) & ~0xfff; 67 u32 start = offset & ~0x00000fff; 68 u32 fetch = limit - start; 69 70 if (nvbios_extend(bios, limit) >= 0) { 71 int ret = acpi_read_bios(data, bios->data, start, fetch); 72 if (ret == fetch) 73 return fetch; 74 } 75 76 return 0; 77 } 78 79 /* Other systems, such as the one in fdo#55948, will report a success 80 * but only return 4KiB of data. The common bios fetching logic will 81 * detect an invalid image, and fall back to this version of the read 82 * function. 83 */ 84 static u32 85 acpi_read_slow(void *data, u32 offset, u32 length, struct nvkm_bios *bios) 86 { 87 u32 limit = (offset + length + 0xfff) & ~0xfff; 88 u32 start = offset & ~0xfff; 89 u32 fetch = 0; 90 91 if (nvbios_extend(bios, limit) >= 0) { 92 while (start + fetch < limit) { 93 int ret = acpi_read_bios(data, bios->data, 94 start + fetch, 0x1000); 95 if (ret != 0x1000) 96 break; 97 fetch += 0x1000; 98 } 99 } 100 101 return fetch; 102 } 103 104 static void * 105 acpi_init(struct nvkm_bios *bios, const char *name) 106 { 107 #if defined(CONFIG_ACPI) && defined(CONFIG_X86) 108 acpi_status status; 109 acpi_handle dhandle, rom_handle; 110 111 dhandle = ACPI_HANDLE(bios->subdev.device->dev); 112 if (!dhandle) 113 return ERR_PTR(-ENODEV); 114 115 status = acpi_get_handle(dhandle, "_ROM", &rom_handle); 116 if (ACPI_FAILURE(status)) 117 return ERR_PTR(-ENODEV); 118 119 return rom_handle; 120 #else 121 return ERR_PTR(-ENODEV); 122 #endif 123 } 124 125 const struct nvbios_source 126 nvbios_acpi_fast = { 127 .name = "ACPI", 128 .init = acpi_init, 129 .read = acpi_read_fast, 130 .rw = false, 131 .require_checksum = true, 132 }; 133 134 const struct nvbios_source 135 nvbios_acpi_slow = { 136 .name = "ACPI", 137 .init = acpi_init, 138 .read = acpi_read_slow, 139 .rw = false, 140 }; 141