nvresolution.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. /* Copyright (c) 2014 Tom Li
  2. *
  3. * Permission is hereby granted, free of charge, to any person obtaining a copy
  4. * of this software and associated documentation files (the "Software"), to deal
  5. * in the Software without restriction, including without limitation the rights
  6. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. * copies of the Software, and to permit persons to whom the Software is
  8. * furnished to do so, subject to the following conditions:
  9. *
  10. * The above copyright notice and this permission notice shall be included in
  11. * all copies or substantial portions of the Software.
  12. *
  13. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. * THE SOFTWARE.
  20. *
  21. * Credits:
  22. *
  23. * 915Resolution - The source of all the resolution hacks
  24. * http://915resolution.mango-lang.org/
  25. *
  26. * DieBuche - The original work on hacking legacy NVIDIA/ATI VBIOS
  27. * http://www.insanelymac.com/forum/topic/211294-information-on-vesa-modes-in-atinvidia-bios/
  28. *
  29. * --- my nvesabios for hacking
  30. * https://github.com/biergaizi/nvesabios
  31. *
  32. * Clover
  33. * http://sourceforge.net/projects/cloverefiboot/
  34. *
  35. * pene - Reverse Engineering on NVIDIA VBIOS
  36. * http://www.projectosx.com/forum/index.php?showtopic=2562&st=2840&p=22683&#entry22683
  37. *
  38. * Chameleon
  39. * http://forge.voodooprojects.org/p/chameleon
  40. *
  41. * dmazar - Providing the information about unlocking PAM on newer CPUs
  42. * http://www.insanelymac.com/forum/topic/284296-solved-resolution-hack-for-1st-gen-intel-hd-graphics/
  43. *
  44. * GNU GRUB
  45. * http://www.gnu.org/software/grub/
  46. *
  47. * VBIOS images submitted by Hackintosh users
  48. */
  49. #include <stdio.h>
  50. #include <stdbool.h>
  51. #include <stdint.h>
  52. #include <stdlib.h>
  53. #include <string.h>
  54. #include <unistd.h>
  55. #include <fcntl.h>
  56. #include <sys/mman.h>
  57. #include <sys/io.h>
  58. #define VBIOS_START 0xc0000
  59. #define VBIOS_SIZE 0x10000
  60. #define VBIOS_FILE "/dev/mem"
  61. #define MECH_ONE_ADDR 0xCF8
  62. #define MECH_ONE_DATA 0xCFC
  63. #include "nvidia.h"
  64. #include "intel.h"
  65. #define VENDOR_ID_MASK 0x0000FFFF
  66. typedef struct {
  67. int bios_fd;
  68. uint8_t *bios_ptr;
  69. char *nv_mode_table;
  70. uint32_t mode_table_size;
  71. unsigned int chipset_id;
  72. int chipset_type;
  73. bool locked;
  74. uint32_t pam_addr;
  75. uint8_t b1;
  76. uint8_t b2;
  77. } vbios_map;
  78. void detect_chipset_info(vbios_map *);
  79. vbios_map *map_vbios(void);
  80. vbios_map *open_vbios(void);
  81. void close_vbios(vbios_map *);
  82. void unlock_vbios(vbios_map *);
  83. void relock_vbios(vbios_map *);
  84. void search_and_replace(uint8_t *, const int, const uint8_t *, const int, const uint8_t *);
  85. void set_mode(vbios_map *, int, int);
  86. void show_help(char *name);
  87. void detect_chipset_info(vbios_map *map)
  88. {
  89. /* query chipset id */
  90. outl(0x80000000, MECH_ONE_ADDR);
  91. map->chipset_id = inl(MECH_ONE_DATA);
  92. int i;
  93. for (i = 0; i < INTEL_CORE_2GEN_DEVID; i++) {
  94. if (map->chipset_id == intel_core_2gen_devid[i]) {
  95. map->chipset_type = INTEL_CORE_2GEN;
  96. map->pam_addr = INTEL_CORE_2GEN_PAM;
  97. return;
  98. }
  99. }
  100. uint8_t bus;
  101. uint16_t dev_id;
  102. if (((map->chipset_id) & VENDOR_ID_MASK) == INTEL_VENDOR_ID) {
  103. for (i = 0; i < INTEL_CORE_1GEN_BUSES; i++) {
  104. bus = intel_core_1gen_buses[i];
  105. outl(1 << 31 | bus << 16 | 0 << 11 | 1 << 8 | 0, MECH_ONE_ADDR);
  106. dev_id = inw(MECH_ONE_DATA + 2);
  107. int j;
  108. for (j = 0; j < INTEL_CORE_1GEN_DEVID; j++) {
  109. if (dev_id == intel_core_1gen_devid[j]) {
  110. map->chipset_type = INTEL_CORE_1GEN;
  111. map->pam_addr = INTEL_CORE_1GEN_PAM(bus);
  112. return;
  113. }
  114. }
  115. }
  116. }
  117. /* Core i7 Processor Family for the LGA-2011
  118. * http://www.intel.com/content/www/us/en/processors/core/core-i7-lga-2011-datasheet-vol-2.html
  119. * Bus 0, Dev 5, Func 0, DevId = 3C28 contains reg CPUBUSNO at 108h, bits 15:8 = Uncore bus
  120. * Bus "Uncore bus", Dev 12, Func 6, DevId = 3CF4 is System Address Decoder and PAM regs are at 40h-46h
  121. */
  122. /* TODO: moving these magic into intel.h, but it seems hard to do. */
  123. outl(1 << 31 | 0 << 16 | 5 << 11 | 0 << 8 | 0, MECH_ONE_ADDR);
  124. dev_id = inw(MECH_ONE_DATA + 2);
  125. if (dev_id == 0x3C28) {
  126. outl(1 << 31 | 0 << 16 | 5 << 11 | 0 << 8 | 0x108, MECH_ONE_ADDR);
  127. bus = inb(MECH_ONE_DATA + 1);
  128. outl(1 << 31 | bus << 16 | 12 << 11 | 6 << 8 | 0, MECH_ONE_ADDR);
  129. dev_id = inw(MECH_ONE_DATA + 2);
  130. if (dev_id == 0x3CF4) {
  131. map->chipset_type = INTEL_CORE_LGA2011;
  132. map->pam_addr = bus;
  133. return;
  134. }
  135. }
  136. }
  137. vbios_map *map_vbios(void)
  138. {
  139. vbios_map *map = malloc(sizeof(vbios_map));
  140. memset(map, 0, sizeof(vbios_map));
  141. map->bios_fd = open(VBIOS_FILE, O_RDWR);
  142. if (map->bios_fd < 0) {
  143. fprintf(stderr, "Unable to open %s.\n", VBIOS_FILE);
  144. exit(1);
  145. }
  146. map->bios_ptr = mmap(0, VBIOS_SIZE, PROT_READ | PROT_WRITE,
  147. MAP_SHARED, map->bios_fd, VBIOS_START);
  148. if (map->bios_ptr == MAP_FAILED) {
  149. fprintf(stderr, "Unable to mmap() the video BIOS.\n");
  150. close(map->bios_fd);
  151. exit(1);
  152. }
  153. return map;
  154. }
  155. vbios_map *open_vbios(void)
  156. {
  157. vbios_map *map = map_vbios();
  158. /* get the permission in order to write registers */
  159. if (iopl(3) < 0) {
  160. fprintf(stderr, "Unable to obtain the proper IO permissions.\n");
  161. exit(1);
  162. }
  163. detect_chipset_info(map);
  164. uint16_t nv_data_table_offset = 0;
  165. uint16_t *nv_data_table = NULL;
  166. NV_VESA_TABLE *std_vesa = NULL;
  167. int i;
  168. for (i = 0; i < 512; i++) {
  169. if (memcmp(&map->bios_ptr[i], nv_string, 4) != 0) {
  170. continue;
  171. }
  172. int j;
  173. for (j = 0; i < 0x300; j++) {
  174. if (memcmp(&map->bios_ptr[j], nv_pattern, 4) != 0) {
  175. continue;
  176. }
  177. nv_data_table_offset = *((uint16_t *) (&map->bios_ptr[j + 4]));
  178. break;
  179. }
  180. nv_data_table = (uint16_t *) (map->bios_ptr + (nv_data_table_offset + OFFSET_TO_VESA_TABLE_INDEX));
  181. std_vesa = (NV_VESA_TABLE *) (map->bios_ptr + *nv_data_table);
  182. map->nv_mode_table = (char *) std_vesa + sizeof(NV_COMMON_TABLE_HEADER);
  183. if (!map->nv_mode_table) {
  184. fprintf(stderr, "Unable to locate the nv_mode_table.");
  185. close_vbios(map);
  186. return NULL;
  187. }
  188. map->mode_table_size = std_vesa->header.size;
  189. break;
  190. }
  191. return map;
  192. }
  193. void close_vbios(vbios_map *map)
  194. {
  195. munmap(map->bios_ptr, VBIOS_SIZE);
  196. close(map->bios_fd);
  197. free(map);
  198. }
  199. void unlock_vbios(vbios_map *map)
  200. {
  201. outl(map->pam_addr, MECH_ONE_ADDR);
  202. map->b1 = inb(MECH_ONE_DATA + 1);
  203. map->b2 = inb(MECH_ONE_DATA + 2);
  204. outl(map->pam_addr, MECH_ONE_ADDR);
  205. outb(0x33, MECH_ONE_DATA + 1);
  206. outb(0x33, MECH_ONE_DATA + 2);
  207. /* just select a random index to test it */
  208. uint8_t orig_val = map->bios_ptr[0xa123];
  209. map->bios_ptr[0xa123] += 1;
  210. if (map->bios_ptr[0xa123] == orig_val) {
  211. fprintf(stderr, "Registers set, but still unable to modify VBIOS.\n");
  212. exit(1);
  213. }
  214. map->bios_ptr[0xa123] = orig_val;
  215. map->locked = false;
  216. }
  217. void relock_vbios(vbios_map *map)
  218. {
  219. outl(map->pam_addr, MECH_ONE_ADDR);
  220. outb(map->b1, MECH_ONE_DATA + 1);
  221. outb(map->b2, MECH_ONE_DATA + 2);
  222. map->locked = true;
  223. }
  224. void search_and_replace(uint8_t *target, const int target_size, const uint8_t *search, const int search_size, const uint8_t *replace)
  225. {
  226. int i;
  227. for (i = 0; i < target_size; i++) {
  228. if (memcmp(&target[i], search, search_size) == 0) {
  229. memcpy(&target[i], replace, search_size);
  230. /* once is enough. */
  231. return;
  232. }
  233. }
  234. fprintf(stderr, "Unable to find the default mode in order to replace it.\n");
  235. fprintf(stderr, "It probably means you replace the mode already.\n");
  236. exit(1);
  237. }
  238. void set_mode(vbios_map *map, int width, int height)
  239. {
  240. unlock_vbios(map);
  241. int i;
  242. for (i = 0; i < NV_TOTAL_RESOLUTIONS; i++) {
  243. if (nv_resolution[i].height == height && nv_resolution[i].width == width) {
  244. break;
  245. }
  246. }
  247. if (i == NV_TOTAL_RESOLUTIONS) {
  248. fprintf(stderr, "The specified mode doesn't available in the database.\n");
  249. exit(1);
  250. return;
  251. }
  252. search_and_replace(map->bios_ptr, VBIOS_SIZE, nv_sample0, 17, nv_key0[i].matrix);
  253. search_and_replace(map->bios_ptr, VBIOS_SIZE, nv_sample1, 9, nv_key1[i].matrix);
  254. search_and_replace(map->bios_ptr, VBIOS_SIZE, nv_sample2, 13, nv_key2[i].matrix);
  255. search_and_replace(map->bios_ptr, VBIOS_SIZE, nv_sample3, 5, nv_key3[i].matrix);
  256. /* What is it? */
  257. if (((*map->bios_ptr + 0x34) & 0x8F) == 0x80) {
  258. *(map->bios_ptr + 0x34) |= 0x01;
  259. }
  260. relock_vbios(map);
  261. }
  262. void show_help(char *name)
  263. {
  264. printf("Usage: %s [width] [height]\n", name);
  265. return;
  266. }
  267. int main(int argc, char **argv)
  268. {
  269. if (argc != 3) {
  270. show_help(argv[0]);
  271. return 0;
  272. }
  273. int width = atoi(argv[1]);
  274. int height = atoi(argv[2]);
  275. vbios_map *map = open_vbios();
  276. set_mode(map, width, height);
  277. close_vbios(map);
  278. printf("Hacked resolution to %dx%d.\n", width, height);
  279. return 0;
  280. }