blob: 2dfa0023f20b2be0c28c57ed3f13977aec37a524 [file] [log] [blame]
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +02001/*
2 * Copyright (C) 2014 Vladimir Serbinenko
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +020012 */
13
14#include <stdio.h>
15#include <sys/mman.h>
16#include <stdint.h>
17#include <string.h>
18#include <stdlib.h>
19#include <unistd.h>
20#include <sys/types.h>
21#include <sys/stat.h>
22#include <fcntl.h>
Patrick Rudolph17856b72017-11-11 10:17:53 +010023#include <getopt.h>
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +020024#include <errno.h>
Patrick Rudolph17856b72017-11-11 10:17:53 +010025#include <stdarg.h>
26#include <commonlib/helpers.h>
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +020027
28typedef uint8_t u8;
29typedef uint16_t u16;
30typedef uint32_t u32;
31
Alex Feinman2223cbf2019-02-20 11:49:51 -080032#define DEF_ALLOC 1024
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +020033
34typedef struct {
35 u16 signature;
36 u8 size;
Patrick Rudolph17856b72017-11-11 10:17:53 +010037 u8 entrypoint[4];
38 u8 checksum;
39 u8 reserved[16];
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +020040 u16 pcir_offset;
41 u16 vbt_offset;
42} __attribute__ ((packed)) optionrom_header_t;
43
Patrick Rudolph17856b72017-11-11 10:17:53 +010044typedef struct {
45 u32 signature;
46 u16 vendor;
47 u16 device;
48 u16 reserved1;
49 u16 length;
50 u8 revision;
51 u8 classcode[3];
52 u16 imagelength;
53 u16 coderevision;
54 u8 codetype;
55 u8 indicator;
56 u16 reserved2;
57} __attribute__((packed)) optionrom_pcir_t;
58
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +020059struct vbt_header {
60 u8 signature[20];
61 u16 version;
62 u16 header_size;
63 u16 vbt_size;
64 u8 vbt_checksum;
65 u8 reserved0;
66 u32 bdb_offset;
67 u32 aim_offset[4];
68} __attribute__ ((packed));
69
70struct bdb_header {
71 u8 signature[16];
72 u16 version;
73 u16 header_size;
74 u16 bdb_size;
75};
76
77struct vbios_data {
78 u8 type; /* 0 == desktop, 1 == mobile */
79 u8 relstage;
80 u8 chipset;
81 u8 lvds_present:1;
82 u8 tv_present:1;
83 u8 rsvd2:6; /* finish byte */
84 u8 rsvd3[4];
85 u8 signon[155];
86 u8 copyright[61];
87 u16 code_segment;
88 u8 dos_boot_mode;
89 u8 bandwidth_percent;
90 u8 rsvd4; /* popup memory size */
91 u8 resize_pci_bios;
92 u8 rsvd5; /* is crt already on ddc2 */
93} __attribute__ ((packed));
94
95struct bdb_general_features {
96 /* bits 1 */
97 u8 panel_fitting:2;
98 u8 flexaim:1;
99 u8 msg_enable:1;
100 u8 clear_screen:3;
101 u8 color_flip:1;
102
103 /* bits 2 */
104 u8 download_ext_vbt:1;
105 u8 enable_ssc:1;
106 u8 ssc_freq:1;
107 u8 enable_lfp_on_override:1;
108 u8 disable_ssc_ddt:1;
109 u8 rsvd7:1;
110 u8 display_clock_mode:1;
111 u8 rsvd8:1; /* finish byte */
112
113 /* bits 3 */
114 u8 disable_smooth_vision:1;
115 u8 single_dvi:1;
116 u8 rsvd9:1;
117 u8 fdi_rx_polarity_inverted:1;
118 u8 rsvd10:4; /* finish byte */
119
120 /* bits 4 */
121 u8 legacy_monitor_detect;
122
123 /* bits 5 */
124 u8 int_crt_support:1;
125 u8 int_tv_support:1;
126 u8 int_efp_support:1;
127 u8 dp_ssc_enb:1; /* PCH attached eDP supports SSC */
128 u8 dp_ssc_freq:1; /* SSC freq for PCH attached eDP */
129 u8 rsvd11:3; /* finish byte */
130} __attribute__ ((packed));
131
132struct common_child_dev_config {
133 u16 handle;
134 u16 device_type;
135 u8 not_common1[12];
136 u8 dvo_port;
137 u8 i2c_pin;
138 u8 slave_addr;
139 u8 ddc_pin;
140 u16 edid_ptr;
141 u8 not_common3[6];
142 u8 dvo_wiring;
143 u8 not_common4[4];
144} __attribute__ ((packed));
145
146struct bdb_general_definitions {
147 /* DDC GPIO */
148 u8 crt_ddc_gmbus_pin;
149
150 /* DPMS bits */
151 u8 dpms_acpi:1;
152 u8 skip_boot_crt_detect:1;
153 u8 dpms_aim:1;
154 u8 rsvd1:5; /* finish byte */
155
156 /* boot device bits */
157 u8 boot_display[2];
158 u8 child_dev_size;
159
160 /*
161 * Device info:
162 * If TV is present, it'll be at devices[0].
163 * LVDS will be next, either devices[0] or [1], if present.
164 * On some platforms the number of device is 6. But could be as few as
165 * 4 if both TV and LVDS are missing.
166 * And the device num is related with the size of general definition
167 * block. It is obtained by using the following formula:
168 * number = (block_size - sizeof(bdb_general_definitions))/
169 * sizeof(child_device_config);
170 */
171 struct common_child_dev_config devices[0];
172} __attribute__ ((packed));
173
174struct bdb_driver_features {
175 u8 boot_dev_algorithm:1;
176 u8 block_display_switch:1;
177 u8 allow_display_switch:1;
178 u8 hotplug_dvo:1;
179 u8 dual_view_zoom:1;
180 u8 int15h_hook:1;
181 u8 sprite_in_clone:1;
182 u8 primary_lfp_id:1;
183
184 u16 boot_mode_x;
185 u16 boot_mode_y;
186 u8 boot_mode_bpp;
187 u8 boot_mode_refresh;
188
189 u16 enable_lfp_primary:1;
190 u16 selective_mode_pruning:1;
191 u16 dual_frequency:1;
192 u16 render_clock_freq:1; /* 0: high freq; 1: low freq */
193 u16 nt_clone_support:1;
194 u16 power_scheme_ui:1; /* 0: CUI; 1: 3rd party */
195 u16 sprite_display_assign:1; /* 0: secondary; 1: primary */
196 u16 cui_aspect_scaling:1;
197 u16 preserve_aspect_ratio:1;
198 u16 sdvo_device_power_down:1;
199 u16 crt_hotplug:1;
200 u16 lvds_config:2;
201 u16 tv_hotplug:1;
202 u16 hdmi_config:2;
203
204 u8 static_display:1;
205 u8 reserved2:7;
206 u16 legacy_crt_max_x;
207 u16 legacy_crt_max_y;
208 u8 legacy_crt_max_refresh;
209
210 u8 hdmi_termination;
211 u8 custom_vbt_version;
212} __attribute__ ((packed));
213
214struct bdb_lvds_options {
215 u8 panel_type;
216 u8 rsvd1;
217 /* LVDS capabilities, stored in a dword */
218 u8 pfit_mode:2;
219 u8 pfit_text_mode_enhanced:1;
220 u8 pfit_gfx_mode_enhanced:1;
221 u8 pfit_ratio_auto:1;
222 u8 pixel_dither:1;
223 u8 lvds_edid:1;
224 u8 rsvd2:1;
225 u8 rsvd4;
226} __attribute__ ((packed));
227
228struct bdb_sdvo_lvds_options {
229 u8 panel_backlight;
230 u8 h40_set_panel_type;
231 u8 panel_type;
232 u8 ssc_clk_freq;
233 u16 als_low_trip;
234 u16 als_high_trip;
235 u8 sclalarcoeff_tab_row_num;
236 u8 sclalarcoeff_tab_row_size;
237 u8 coefficient[8];
238 u8 panel_misc_bits_1;
239 u8 panel_misc_bits_2;
240 u8 panel_misc_bits_3;
241 u8 panel_misc_bits_4;
242} __attribute__ ((packed));
243
244
Patrick Rudolph17856b72017-11-11 10:17:53 +0100245static const size_t ignore_checksum = 1;
246
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200247#define BDB_GENERAL_FEATURES 1
248#define BDB_GENERAL_DEFINITIONS 2
249
250#define BDB_DRIVER_FEATURES 12
251#define BDB_SDVO_LVDS_OPTIONS 22
252#define BDB_SDVO_PANEL_DTDS 23
253#define BDB_LVDS_OPTIONS 40
254#define BDB_LVDS_LFP_DATA_PTRS 41
255#define BDB_LVDS_LFP_DATA 42
256
257#define BDB_SKIP 254
258
Patrick Rudolph17856b72017-11-11 10:17:53 +0100259/* print helpers */
260static void print(const char *format, ...)
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200261{
Patrick Rudolph17856b72017-11-11 10:17:53 +0100262 va_list args;
263 fprintf(stdout, "VBTTOOL: ");
264 va_start(args, format);
265 vfprintf(stdout, format, args);
266 va_end(args);
267}
268
269static void printt(const char *format, ...)
270{
271 va_list args;
272 fprintf(stdout, "\t");
273 va_start(args, format);
274 vfprintf(stdout, format, args);
275 va_end(args);
276}
277
278static void printwarn(const char *format, ...)
279{
280 va_list args;
281 fprintf(stderr, "VBTTOOL: WARN: ");
282 va_start(args, format);
283 vfprintf(stderr, format, args);
284 va_end(args);
285}
286
287static void printerr(const char *format, ...)
288{
289 va_list args;
290 fprintf(stderr, "VBTTOOL: ERR: ");
291 va_start(args, format);
292 vfprintf(stderr, format, args);
293 va_end(args);
294}
295
296struct fileobject {
297 unsigned char *data;
298 size_t size;
299};
300
301/* file object helpers */
302
303/*
304 * Alloc a file object of given size.
305 * Returns NULL on error.
306 */
307static struct fileobject *malloc_fo(const size_t size)
308{
309 struct fileobject *fo;
310 if (!size)
311 return NULL;
312
313 fo = malloc(sizeof(*fo));
314 if (!fo)
315 return NULL;
316 fo->data = malloc(size);
317 if (!fo->data) {
318 free(fo);
319 return NULL;
320 }
321 fo->size = size;
322
323 return fo;
324}
325
326/* Free a fileobject structure */
327static void free_fo(struct fileobject *fo)
328{
329 if (fo) {
330 free(fo->data);
331 free(fo);
332 }
333}
334
335/* Resize file object and keep memory content */
336static struct fileobject *remalloc_fo(struct fileobject *old,
337 const size_t size)
338{
339 struct fileobject *fo = old;
340
341 if (!old || !size)
342 return NULL;
343
344 fo->data = realloc(fo->data, size);
Patrick Rudolph17856b72017-11-11 10:17:53 +0100345 if (!fo->data)
346 return NULL;
347
Patrick Rudolphaece0062018-10-14 11:34:04 +0200348 if (fo->size < size)
349 memset(&fo->data[fo->size], 0, size - fo->size);
350
Patrick Rudolph17856b72017-11-11 10:17:53 +0100351 fo->size = size;
352
353 return fo;
354}
355
356/*
357 * Creates a new subregion copy of fileobject.
358 * Returns NULL if offset is greater than fileobject size.
359 * Returns NULL on error.
360 */
361static struct fileobject *malloc_fo_sub(const struct fileobject *old,
362 const size_t off)
363{
364 struct fileobject *fo;
365
366 if (!old || off > old->size)
367 return NULL;
368
369 fo = malloc_fo(old->size - off);
370 if (!fo)
371 return NULL;
372
373 memcpy(fo->data, old->data + off, fo->size);
374
375 return fo;
376}
377
378/* file helpers */
379
380/* Create fileobject from file */
381static struct fileobject *read_file(const char *filename)
382{
383 FILE *fd = fopen(filename, "rb");
Alex Feinman2223cbf2019-02-20 11:49:51 -0800384 off_t read_size = DEF_ALLOC;
Patrick Rudolph17856b72017-11-11 10:17:53 +0100385
386 if (!fd) {
387 printerr("%s open failed: %s\n", filename, strerror(errno));
388 return NULL;
389 }
390
Alex Feinman2223cbf2019-02-20 11:49:51 -0800391 struct fileobject *fo = malloc_fo(read_size);
Patrick Rudolph17856b72017-11-11 10:17:53 +0100392 if (!fo) {
393 printerr("malloc failed\n");
394 fclose(fd);
395 return NULL;
396 }
397
Alex Feinman2223cbf2019-02-20 11:49:51 -0800398 off_t total_bytes_read = 0, bytes_read;
399 while ((bytes_read = fread(fo->data + total_bytes_read, 1, read_size, fd)) > 0) {
400 total_bytes_read += bytes_read;
401 struct fileobject *newfo = remalloc_fo(fo, fo->size + read_size);
402 if (!newfo) {
403 fclose(fd);
404 free_fo(fo);
405 return NULL;
406 }
407 fo = newfo;
408 }
409
410 if (!total_bytes_read) {
Patrick Rudolph17856b72017-11-11 10:17:53 +0100411 fclose(fd);
412 free_fo(fo);
413 return NULL;
414 }
415
416 if (fclose(fd)) {
417 printerr("%s close failed: %s\n", filename, strerror(errno));
418 free_fo(fo);
419 return NULL;
420 }
421
Alex Feinman2223cbf2019-02-20 11:49:51 -0800422 fo->size = total_bytes_read;
Patrick Rudolph17856b72017-11-11 10:17:53 +0100423
424 return fo;
425}
426
427/* Create fileobject from physical memory at given address of size 64 KiB */
428static struct fileobject *read_physmem(size_t addr)
429{
430 const int fd = open("/dev/mem", O_RDONLY);
431 const size_t size = 64 * 2 * KiB;
432 if (fd < 0) {
433 printerr("/dev/mem open failed: %s\n", strerror(errno));
434 return NULL;
435 }
436
437 const void *data = mmap(0, size, PROT_READ, MAP_SHARED, fd, addr);
438 if (data == MAP_FAILED) {
439 close(fd);
440 printerr("mmap failed: %s\n", strerror(errno));
441 return NULL;
442 }
443
444 struct fileobject *fo = malloc_fo(size);
445 if (!fo) {
446 printerr("malloc failed\n");
447 munmap((void *)data, size);
448 close(fd);
449 return NULL;
450 }
451
452 memcpy(fo->data, data, size);
453 munmap((void *)data, size);
454
455 if (close(fd)) {
456 printerr("/dev/mem close failed: %s\n", strerror(errno));
457 free_fo(fo);
458 return NULL;
459 }
460
461 return fo;
462}
463
464/* Write fileobject contents to file */
465static int write_file(const char *filename, const struct fileobject *fo)
466{
467 FILE *fd_out = fopen(filename, "wb");
468
469 if (!fd_out) {
470 printerr("%s open failed: %s\n", filename, strerror(errno));
471 return 1;
472 }
473 if (fwrite(fo->data, 1, fo->size, fd_out) != fo->size) {
474 fclose(fd_out);
475 return 1;
476 }
477 return fclose(fd_out);
478}
479
480/* dump VBT contents in human readable form */
481static void dump_vbt(const struct fileobject *fo)
482{
483 if (fo->size < sizeof(struct vbt_header))
484 return;
485
486 const struct vbt_header *head = (const struct vbt_header *)fo->data;
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200487 const struct bdb_header *bdb;
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200488 const u8 *ptr;
Patrick Rudolph17856b72017-11-11 10:17:53 +0100489 int i;
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200490 int is_first_skip = 1;
491
Patrick Rudolph17856b72017-11-11 10:17:53 +0100492 printt("signature: <%20.20s>\n", head->signature);
493 printt("version: %d.%02d\n", head->version / 100,
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200494 head->version % 100);
495 if (head->header_size != sizeof(struct vbt_header))
Patrick Rudolph17856b72017-11-11 10:17:53 +0100496 printt("header size: 0x%x\n", head->header_size);
497 printt("VBT size: 0x%x\n", head->vbt_size);
498 printt("VBT checksum: 0x%x\n", head->vbt_checksum);
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200499 if (head->reserved0)
Patrick Rudolph17856b72017-11-11 10:17:53 +0100500 printt("header reserved0: 0x%x\n", head->reserved0);
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200501 if (head->bdb_offset != sizeof(struct vbt_header))
Patrick Rudolph17856b72017-11-11 10:17:53 +0100502 printt("BDB offset: 0x%x\n", head->bdb_offset);
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200503
504 for (i = 0; i < 4; i++)
505 if (head->aim_offset[i])
Patrick Rudolph17856b72017-11-11 10:17:53 +0100506 printt("AIM[%d] offset: 0x%x\n", i,
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200507 head->aim_offset[i]);
Patrick Rudolph17856b72017-11-11 10:17:53 +0100508 if (head->bdb_offset + sizeof(*bdb) > fo->size)
509 return;
510 bdb = (const void *) (fo->data + head->bdb_offset);
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200511
512 if (memcmp("BIOS_DATA_BLOCK ", bdb->signature, 16) != 0) {
Patrick Rudolph17856b72017-11-11 10:17:53 +0100513 printerr("invalid BDB signature:%s\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200514 bdb->signature);
515 exit(1);
516 }
Patrick Rudolph17856b72017-11-11 10:17:53 +0100517 printt("BDB version: %d.%02d\n", bdb->version / 100,
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200518 bdb->version % 100);
519 if (bdb->header_size != sizeof(struct bdb_header))
Patrick Rudolph17856b72017-11-11 10:17:53 +0100520 printt("BDB header size: 0x%x\n", bdb->header_size);
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200521 if (bdb->bdb_size != head->vbt_size - head->bdb_offset)
Patrick Rudolph17856b72017-11-11 10:17:53 +0100522 printt("BDB size: 0x%x\n", bdb->bdb_size);
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200523 for (ptr = (const u8 *) bdb + bdb->header_size;
524 ptr < (const u8 *) bdb + bdb->bdb_size;) {
525 u16 secsz = (ptr[1] | (ptr[2] << 8));
526 u8 sectype = ptr[0];
527 const u8 *section = ptr + 3;
528
Patrick Rudolph17856b72017-11-11 10:17:53 +0100529 printt("section type %d, size 0x%x\n", sectype, secsz);
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200530 ptr += secsz + 3;
531 switch (sectype) {
532 case BDB_GENERAL_FEATURES:{
533 const struct bdb_general_features *sec =
534 (const void *) section;
Patrick Rudolph17856b72017-11-11 10:17:53 +0100535 printt("General features:\n");
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200536
537 if (sec->panel_fitting)
Patrick Rudolph17856b72017-11-11 10:17:53 +0100538 printt("\tpanel_fitting = 0x%x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200539 sec->panel_fitting);
540 if (sec->flexaim)
Patrick Rudolph17856b72017-11-11 10:17:53 +0100541 printt("\tflexaim = 0x%x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200542 sec->flexaim);
543 if (sec->msg_enable)
Patrick Rudolph17856b72017-11-11 10:17:53 +0100544 printt("\tmsg_enable = 0x%x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200545 sec->msg_enable);
546 if (sec->clear_screen)
Patrick Rudolph17856b72017-11-11 10:17:53 +0100547 printt("\tclear_screen = 0x%x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200548 sec->clear_screen);
549 if (sec->color_flip)
Patrick Rudolph17856b72017-11-11 10:17:53 +0100550 printt("\tcolor_flip = 0x%x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200551 sec->color_flip);
552 if (sec->download_ext_vbt)
Patrick Rudolph17856b72017-11-11 10:17:53 +0100553 printt
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200554 ("\tdownload_ext_vbt = 0x%x\n",
555 sec->download_ext_vbt);
Patrick Rudolph17856b72017-11-11 10:17:53 +0100556 printt("\t*enable_ssc = 0x%x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200557 sec->enable_ssc);
Patrick Rudolph17856b72017-11-11 10:17:53 +0100558 printt("\t*ssc_freq = 0x%x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200559 sec->ssc_freq);
560 if (sec->enable_lfp_on_override)
Patrick Rudolph17856b72017-11-11 10:17:53 +0100561 printt
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200562 ("\tenable_lfp_on_override = 0x%x\n",
563 sec->enable_lfp_on_override);
564 if (sec->disable_ssc_ddt)
Patrick Rudolph17856b72017-11-11 10:17:53 +0100565 printt
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200566 ("\tdisable_ssc_ddt = 0x%x\n",
567 sec->disable_ssc_ddt);
568 if (sec->rsvd7)
Patrick Rudolph17856b72017-11-11 10:17:53 +0100569 printt("\trsvd7 = 0x%x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200570 sec->rsvd7);
Patrick Rudolph17856b72017-11-11 10:17:53 +0100571 printt("\t*display_clock_mode = 0x%x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200572 sec->display_clock_mode);
573 if (sec->rsvd8)
Patrick Rudolph17856b72017-11-11 10:17:53 +0100574 printt("\trsvd8 = 0x%x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200575 sec->rsvd8);
Patrick Rudolph17856b72017-11-11 10:17:53 +0100576 printt("\tdisable_smooth_vision = 0x%x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200577 sec->disable_smooth_vision);
578 if (sec->single_dvi)
Patrick Rudolph17856b72017-11-11 10:17:53 +0100579 printt("\tsingle_dvi = 0x%x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200580 sec->single_dvi);
581 if (sec->rsvd9)
Patrick Rudolph17856b72017-11-11 10:17:53 +0100582 printt("\trsvd9 = 0x%x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200583 sec->rsvd9);
Patrick Rudolph17856b72017-11-11 10:17:53 +0100584 printt
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200585 ("\t*fdi_rx_polarity_inverted = 0x%x\n",
586 sec->fdi_rx_polarity_inverted);
587 if (sec->rsvd10)
Patrick Rudolph17856b72017-11-11 10:17:53 +0100588 printt("\trsvd10 = 0x%x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200589 sec->rsvd10);
590 if (sec->legacy_monitor_detect)
Patrick Rudolph17856b72017-11-11 10:17:53 +0100591 printt
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200592 ("\tlegacy_monitor_detect = 0x%x\n",
593 sec->legacy_monitor_detect);
Patrick Rudolph17856b72017-11-11 10:17:53 +0100594 printt("\t*int_crt_support = 0x%x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200595 sec->int_crt_support);
Patrick Rudolph17856b72017-11-11 10:17:53 +0100596 printt("\t*int_tv_support = 0x%x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200597 sec->int_tv_support);
598 if (sec->int_efp_support)
Patrick Rudolph17856b72017-11-11 10:17:53 +0100599 printt
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200600 ("\tint_efp_support = 0x%x\n",
601 sec->int_efp_support);
602 if (sec->dp_ssc_enb)
Patrick Rudolph17856b72017-11-11 10:17:53 +0100603 printt("\tdp_ssc_enb = 0x%x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200604 sec->dp_ssc_enb);
605 if (sec->dp_ssc_freq)
Patrick Rudolph17856b72017-11-11 10:17:53 +0100606 printt("\tdp_ssc_freq = 0x%x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200607 sec->dp_ssc_freq);
608 if (sec->rsvd11)
Patrick Rudolph17856b72017-11-11 10:17:53 +0100609 printt("\trsvd11 = 0x%x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200610 sec->rsvd11);
611 break;
612 }
613 case BDB_DRIVER_FEATURES:{
614 const struct bdb_driver_features *sec =
615 (const void *) section;
Patrick Rudolph17856b72017-11-11 10:17:53 +0100616 printt("\t*LVDS config: %d\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200617 sec->lvds_config);
Patrick Rudolph17856b72017-11-11 10:17:53 +0100618 printt("\t*Dual frequency: %d\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200619 sec->dual_frequency);
620
621 break;
622 }
623 case BDB_SDVO_LVDS_OPTIONS:{
624 const struct bdb_sdvo_lvds_options *sec =
625 (const void *) section;
Patrick Rudolph17856b72017-11-11 10:17:53 +0100626 printt("\t*Panel type: %d\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200627 sec->panel_type);
628
629 break;
630 }
631 case BDB_GENERAL_DEFINITIONS:{
632 const struct bdb_general_definitions *sec =
633 (const void *) section;
634 int ndev;
Patrick Rudolph17856b72017-11-11 10:17:53 +0100635 printt("\t*CRT DDC GMBUS pin: %d\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200636 sec->crt_ddc_gmbus_pin);
637
Patrick Rudolph17856b72017-11-11 10:17:53 +0100638 printt("\tDPMS ACPI: %d\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200639 sec->dpms_acpi);
Patrick Rudolph17856b72017-11-11 10:17:53 +0100640 printt("\tSkip boot CRT detect: %d\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200641 sec->skip_boot_crt_detect);
Patrick Rudolph17856b72017-11-11 10:17:53 +0100642 printt("\tDPMS aim: %d\n", sec->dpms_aim);
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200643 if (sec->rsvd1)
Patrick Rudolph17856b72017-11-11 10:17:53 +0100644 printt("\trsvd1: 0x%x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200645 sec->rsvd1);
Patrick Rudolph17856b72017-11-11 10:17:53 +0100646 printt("\tboot_display: { %x, %x }\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200647 sec->boot_display[0],
648 sec->boot_display[1]);
649 if (sec->child_dev_size !=
650 sizeof(struct common_child_dev_config))
Patrick Rudolph17856b72017-11-11 10:17:53 +0100651 printt("\tchild_dev_size: %d\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200652 sec->child_dev_size);
653 ndev = (secsz - sizeof(*sec)) /
654 sizeof(struct common_child_dev_config);
Patrick Rudolph17856b72017-11-11 10:17:53 +0100655 printt("\t%d devices\n", ndev);
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200656 for (i = 0; i < ndev; i++) {
Patrick Rudolph17856b72017-11-11 10:17:53 +0100657 printt("\t*device type: %x ",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200658 sec->devices[i].
659 device_type);
660#define DEVICE_TYPE_INT_LFP 0x1022
661#define DEVICE_TYPE_INT_TV 0x1009
662#define DEVICE_TYPE_EFP_DVI_HOTPLUG_PWR 0x6052
663 switch (sec->devices[i].device_type) {
664 case DEVICE_TYPE_INT_LFP:
Patrick Rudolph17856b72017-11-11 10:17:53 +0100665 printt("(flat panel)\n");
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200666 break;
667 case DEVICE_TYPE_INT_TV:
Patrick Rudolph17856b72017-11-11 10:17:53 +0100668 printt("(TV)\n");
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200669 break;
670 case DEVICE_TYPE_EFP_DVI_HOTPLUG_PWR:
Patrick Rudolph17856b72017-11-11 10:17:53 +0100671 printt
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200672 ("(DVI)\n");
673 break;
674 case 0:
Patrick Rudolph17856b72017-11-11 10:17:53 +0100675 printt("(Empty)\n");
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200676 break;
677 default:
Patrick Rudolph17856b72017-11-11 10:17:53 +0100678 printt("(Unknown)\n");
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200679 break;
680 }
681 if (!sec->devices[i].device_type)
682 continue;
Patrick Rudolph17856b72017-11-11 10:17:53 +0100683 printt("\t *dvo_port: %x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200684 sec->devices[i].dvo_port);
Patrick Rudolph17856b72017-11-11 10:17:53 +0100685 printt("\t *i2c_pin: %x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200686 sec->devices[i].i2c_pin);
Patrick Rudolph17856b72017-11-11 10:17:53 +0100687 printt("\t *slave_addr: %x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200688 sec->devices[i].slave_addr);
Patrick Rudolph17856b72017-11-11 10:17:53 +0100689 printt("\t *ddc_pin: %x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200690 sec->devices[i].ddc_pin);
Patrick Rudolph17856b72017-11-11 10:17:53 +0100691 printt("\t *dvo_wiring: %x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200692 sec->devices[i].dvo_wiring);
Patrick Rudolph17856b72017-11-11 10:17:53 +0100693 printt("\t edid_ptr: %x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200694 sec->devices[i].edid_ptr);
695 }
696
697 break;
698 }
699 case BDB_SKIP:{
700 const struct vbios_data *sec =
701 (const void *) section;
702 if (!is_first_skip)
703 break;
704 is_first_skip = 0;
Patrick Rudolph17856b72017-11-11 10:17:53 +0100705 printt("\ttype: %x\n", sec->type);
706 printt("\trelstage: %x\n", sec->relstage);
707 printt("\tchipset: %x\n", sec->chipset);
708 printt(sec->lvds_present ? "\tLVDS\n"
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200709 : "\tNo LVDS\n");
Patrick Rudolph17856b72017-11-11 10:17:53 +0100710 printt(sec->tv_present ? "\tTV\n"
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200711 : "\tNo TV\n");
712 if (sec->rsvd2)
Patrick Rudolph17856b72017-11-11 10:17:53 +0100713 printt("\trsvd2: 0x%x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200714 sec->rsvd2);
715 for (i = 0; i < 4; i++)
716 if (sec->rsvd3[i])
Patrick Rudolph17856b72017-11-11 10:17:53 +0100717 printt
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200718 ("\trsvd3[%d]: 0x%x\n",
719 i, sec->rsvd3[i]);
Patrick Rudolph17856b72017-11-11 10:17:53 +0100720 printt("\tSignon: %.155s\n", sec->signon);
721 printt("\tCopyright: %.155s\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200722 sec->copyright);
Patrick Rudolph17856b72017-11-11 10:17:53 +0100723 printt("\tCode segment: %x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200724 sec->code_segment);
Patrick Rudolph17856b72017-11-11 10:17:53 +0100725 printt("\tDOS Boot mode: %x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200726 sec->dos_boot_mode);
Patrick Rudolph17856b72017-11-11 10:17:53 +0100727 printt("\tBandwidth percent: %x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200728 sec->bandwidth_percent);
729 if (sec->rsvd4)
Patrick Rudolph17856b72017-11-11 10:17:53 +0100730 printt("\trsvd4: 0x%x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200731 sec->rsvd4);
Patrick Rudolph17856b72017-11-11 10:17:53 +0100732 printt("\tBandwidth percent: %x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200733 sec->resize_pci_bios);
734 if (sec->rsvd5)
Patrick Rudolph17856b72017-11-11 10:17:53 +0100735 printt("\trsvd5: 0x%x\n",
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200736 sec->rsvd5);
737 break;
738 }
739 }
740 }
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200741}
742
Patrick Rudolph17856b72017-11-11 10:17:53 +0100743/* Returns a new fileobject containing a valid VBT */
744static void parse_vbt(const struct fileobject *fo,
745 struct fileobject **vbt)
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200746{
Patrick Rudolph17856b72017-11-11 10:17:53 +0100747 *vbt = NULL;
748
749 if (fo->size < sizeof(struct vbt_header)) {
750 printerr("image is to small\n");
751 return;
752 }
753
754 const struct vbt_header *head =
755 (const struct vbt_header *)fo->data;
756
757 if (memcmp(head->signature, "$VBT", 4) != 0) {
758 printerr("invalid VBT signature\n");
759 return;
760 }
761
762 if (!head->vbt_size || head->vbt_size > fo->size) {
763 printerr("invalid VBT size\n");
764 return;
765 }
766
767 if (!head->bdb_offset ||
768 head->bdb_offset > fo->size - sizeof(struct bdb_header)) {
769 printerr("invalid BDB offset\n");
770 return;
771 }
772
773 if (!head->header_size || head->header_size > fo->size) {
774 printerr("invalid header size\n");
775 return;
776 }
777
778 const struct bdb_header *const bdb_head =
779 (const struct bdb_header *)((const u8 *)head + head->bdb_offset);
780 if (memcmp(bdb_head->signature, "BIOS_DATA_BLOCK ", 16) != 0) {
781 printerr("invalid BDB signature\n");
782 return;
783 }
784
785 if (!bdb_head->header_size || bdb_head->header_size > fo->size) {
786 printerr("invalid BDB header size\n");
787 return;
788 }
789
790 /* Duplicate fo as caller is owner and remalloc frees the object */
Jacob Garber4fbd22e2019-05-08 16:28:27 -0600791 struct fileobject *dupfo = malloc_fo_sub(fo, 0);
792 if (!dupfo) {
793 printerr("malloc failed\n");
794 return;
795 }
796
797 struct fileobject *newfo = remalloc_fo(dupfo, head->vbt_size);
798 if (!newfo) {
799 printerr("remalloc failed\n");
800 free_fo(dupfo);
801 return;
802 }
803
804 *vbt = newfo;
Patrick Rudolph17856b72017-11-11 10:17:53 +0100805}
806
807/* Option ROM checksum */
808static u8 checksum_vbios(const optionrom_header_t *oh)
809{
810 const u8 *ptr = (const u8 *)oh;
811 size_t i;
812
813 u8 cksum = 0;
814 for (i = 0; i < ((MIN(oh->size, 128)) * 512); i++)
815 cksum += ptr[i];
816
817 return cksum;
818}
819
820/* Verify Option ROM contents */
821static int is_valid_vbios(const struct fileobject *fo)
822{
823 if (fo->size > 64 * 2 * KiB) {
824 printerr("VBIOS is to big\n");
825 return 0;
826 }
827
828 if (fo->size < sizeof(optionrom_header_t)) {
829 printerr("VBIOS is to small\n");
830 return 0;
831 }
832
833 const optionrom_header_t *oh = (const optionrom_header_t *)fo->data;
834
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200835 if (oh->signature != 0xaa55) {
Patrick Rudolph17856b72017-11-11 10:17:53 +0100836 printerr("bad oprom signature: 0x%x\n", oh->signature);
837 return 0;
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200838 }
Patrick Rudolph17856b72017-11-11 10:17:53 +0100839
840 if (oh->size == 0 || oh->size > 0x80 || oh->size * 512 > fo->size) {
841 printerr("bad oprom size: 0x%x\n", oh->size);
842 return 0;
843 }
844
845 const u8 cksum = checksum_vbios(oh);
846 if (cksum) {
847 if (!ignore_checksum) {
848 printerr("bad oprom checksum: 0x%x\n", cksum);
849 return 0;
850 }
851 printwarn("bad oprom checksum: 0x%x\n", cksum);
852 }
853
854 if (oh->pcir_offset + sizeof(optionrom_pcir_t) > fo->size) {
855 printerr("bad pcir offset: 0x%x\n", oh->pcir_offset);
856 return 0;
857 }
858
859 if (oh->pcir_offset) {
860 const optionrom_pcir_t *pcir;
861 pcir = (const optionrom_pcir_t *)
862 ((const u8 *)oh + oh->pcir_offset);
863
864 if (pcir->signature != 0x52494350) {
865 printerr("Invalid PCIR signature\n");
866 return 0;
867 }
868
869 if (pcir->vendor != 0x8086) {
870 printerr("Not an Intel VBIOS\n");
871 return 0;
872 }
873
874 if (pcir->classcode[0] != 0 || pcir->classcode[1] != 0 ||
875 pcir->classcode[2] != 3) {
876 printerr("Not a VGA Option Rom\n");
877 return 0;
878 }
879 } else {
880 printwarn("no PCIR header found\n");
881 }
882
883 return 1;
884}
885
886/*
887 * Parse Option ROM and return a valid VBT fileobject.
888 * Caller has to make sure that it is a valid VBIOS.
889 * Return a NULL fileobject on error.
890 */
891static void parse_vbios(const struct fileobject *fo,
892 struct fileobject **vbt)
893{
894 const optionrom_header_t *oh = (const optionrom_header_t *)fo->data;
Patrick Rudolph17856b72017-11-11 10:17:53 +0100895 size_t i;
896
897 *vbt = NULL;
898
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200899 if (!oh->vbt_offset) {
Patrick Rudolph17856b72017-11-11 10:17:53 +0100900 printerr("no VBT found\n");
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +0200901 return;
902 }
Patrick Rudolph17856b72017-11-11 10:17:53 +0100903
904 if (oh->vbt_offset > (fo->size - sizeof(struct vbt_header))) {
905 printerr("invalid VBT offset\n");
906 return;
907 }
908
Patrick Rudolphaece0062018-10-14 11:34:04 +0200909 struct fileobject *fo_vbt = malloc_fo_sub(fo, oh->vbt_offset);
910 if (fo_vbt) {
911 parse_vbt(fo_vbt, vbt);
912 free_fo(fo_vbt);
Patrick Rudolph17856b72017-11-11 10:17:53 +0100913 if (*vbt)
914 return;
915 }
Patrick Rudolphaece0062018-10-14 11:34:04 +0200916 printwarn("VBT wasn't found at specified offset of %04x\n",
917 oh->vbt_offset);
Patrick Rudolph17856b72017-11-11 10:17:53 +0100918
919 for (i = sizeof(optionrom_header_t);
920 i <= fo->size - sizeof(struct vbt_header); i++) {
921 struct fileobject *fo_vbt = malloc_fo_sub(fo, i);
922 if (!fo_vbt)
Patrick Rudolphaece0062018-10-14 11:34:04 +0200923 break;
Patrick Rudolph17856b72017-11-11 10:17:53 +0100924
Patrick Rudolphaece0062018-10-14 11:34:04 +0200925 parse_vbt(fo_vbt, vbt);
Patrick Rudolph17856b72017-11-11 10:17:53 +0100926
927 free_fo(fo_vbt);
928
929 if (*vbt)
930 return;
931 }
932}
933
934/* Short VBT summary in human readable form */
935static void print_vbt(const struct fileobject *fo)
936{
937 const struct bdb_header *bdb;
938
939 if (fo->size < sizeof(struct vbt_header))
940 return;
941
942 const struct vbt_header *head = (const struct vbt_header *)fo->data;
943
944 print("Found VBT:\n");
945 printt("signature: <%20.20s>\n", head->signature);
946 printt("version: %d.%02d\n", head->version / 100, head->version % 100);
947 if (head->header_size != sizeof(struct vbt_header))
948 printt("header size: 0x%x\n", head->header_size);
949 printt("VBT size: 0x%x\n", head->vbt_size);
950 printt("VBT checksum: 0x%x\n", head->vbt_checksum);
951
952 if (head->bdb_offset > (fo->size - sizeof(struct bdb_header))) {
953 printerr("invalid BDB offset\n");
954 return;
955 }
956 bdb = (const struct bdb_header *)
957 ((const char *)head + head->bdb_offset);
958
959 if (memcmp("BIOS_DATA_BLOCK ", bdb->signature, 16) != 0) {
960 printerr("invalid BDB signature:%s\n", bdb->signature);
961 return;
962 }
963 printt("BDB version: %u.%02u\n", bdb->version / 100,
964 bdb->version % 100);
965}
966
967static void print_usage(const char *argv0, struct option *long_options)
968{
969 size_t i = 0;
970 printf("\nUsage:\n");
971 printf("%s --<SOURCECMD> [filename] --<DESTCMD> [filename]\n\n", argv0);
972 printf("SOURCECMD set the VBT source. Supported:\n");
973 printf(" %-10s: Legacy BIOS area at phys. memory 0xc0000\n",
974 "inlegacy");
975 printf(" %-10s: Read raw Intel VBT file\n", "invbt");
976 printf(" %-10s: Read VBT from Intel Option ROM file\n\n", "inoprom");
977 printf("DESTCMD set the VBT destination. Supported:\n");
978 printf(" %-10s: Print VBT in human readable form\n", "outdump");
979 printf(" %-10s: Write raw Intel VBT file\n", "outvbt");
980 printf(" %-10s: Patch existing Intel Option ROM\n\n", "patchoprom");
981
982 printf("Supported arguments:\n");
983 while (long_options[i].name) {
984 printf("\t-%c --%s %s\n", long_options[i].val,
985 long_options[i].name, long_options[i].has_arg ?
986 "<path>" : "");
987 i++;
988 };
989}
990
Patrick Rudolphaece0062018-10-14 11:34:04 +0200991/* Fix VBIOS header and PCIR */
992static int fix_vbios_header(struct fileobject *fo)
Patrick Rudolph17856b72017-11-11 10:17:53 +0100993{
Patrick Rudolphaece0062018-10-14 11:34:04 +0200994 if (!fo || fo->size < sizeof(optionrom_header_t))
Patrick Rudolph17856b72017-11-11 10:17:53 +0100995 return 1;
996
997 optionrom_header_t *oh = (optionrom_header_t *)fo->data;
998
Patrick Rudolphaece0062018-10-14 11:34:04 +0200999 /* Fix size alignment */
1000 if (fo->size % 512) {
1001 print("Aligning size to 512\n");
1002 fo = remalloc_fo(fo, (fo->size + 511) / 512 * 512);
1003 if (!fo)
1004 return 1;
1005 oh = (optionrom_header_t *)fo->data;
Patrick Rudolph17856b72017-11-11 10:17:53 +01001006 }
Patrick Rudolphaece0062018-10-14 11:34:04 +02001007
1008 /* Fix size field */
1009 oh->size = fo->size / 512;
1010
1011 /* Fix checksum field */
1012 oh->checksum = -(checksum_vbios(oh) - oh->checksum);
Patrick Rudolph17856b72017-11-11 10:17:53 +01001013
1014 return 0;
1015}
1016
1017/* Return the VBT structure size in bytes */
1018static size_t vbt_size(const struct fileobject *fo)
1019{
1020 if (!fo || fo->size < sizeof(struct vbt_header))
1021 return 0;
1022 const struct vbt_header *head = (const struct vbt_header *)fo->data;
1023
1024 return head->vbt_size;
1025}
1026
1027/*
1028 * Patch an Intel Option ROM with new VBT.
1029 * Caller has to make sure that VBIOS and VBT are valid.
1030 * Return 1 on error.
1031 */
1032static int patch_vbios(struct fileobject *fo,
1033 const struct fileobject *fo_vbt)
1034{
1035 optionrom_header_t *oh = (optionrom_header_t *)fo->data;
1036 struct vbt_header *head;
1037
1038 struct fileobject *old_vbt = NULL;
1039 parse_vbios(fo, &old_vbt);
1040
1041 if (old_vbt) {
1042 if (oh->vbt_offset + vbt_size(old_vbt) == fo->size) {
1043 /* Located at the end of file - reduce file size */
Jacob Garber2be617b2019-05-07 19:49:37 -06001044 if (fo->size < vbt_size(old_vbt)) {
1045 free_fo(old_vbt);
Patrick Rudolph17856b72017-11-11 10:17:53 +01001046 return 1;
Jacob Garber2be617b2019-05-07 19:49:37 -06001047 }
Patrick Rudolph17856b72017-11-11 10:17:53 +01001048 fo = remalloc_fo(fo, fo->size - vbt_size(old_vbt));
1049 if (!fo) {
1050 printerr("Failed to allocate memory\n");
Patrick Rudolphaece0062018-10-14 11:34:04 +02001051 free_fo(old_vbt);
Patrick Rudolph17856b72017-11-11 10:17:53 +01001052 return 1;
1053 }
Patrick Rudolphaece0062018-10-14 11:34:04 +02001054 oh = (optionrom_header_t *)fo->data;
Patrick Rudolph17856b72017-11-11 10:17:53 +01001055 oh->vbt_offset = 0;
1056 } else if (vbt_size(old_vbt) < vbt_size(fo_vbt)) {
1057 /* In the middle of the file - Remove old VBT */
1058 memset(fo->data + oh->vbt_offset, 0xff,
1059 vbt_size(old_vbt));
1060 oh->vbt_offset = 0;
1061 } else {
1062 /* New VBT overwrites existing one - Clear memory */
1063 memset(fo->data + oh->vbt_offset, 0xff,
1064 vbt_size(old_vbt));
1065 }
1066
1067 free_fo(old_vbt);
1068 }
1069
1070 if (!oh->vbt_offset) {
1071 print("increasing VBIOS to append VBT\n");
Patrick Rudolphaece0062018-10-14 11:34:04 +02001072 if ((fo->size + vbt_size(fo_vbt)) >= 2 * 64 * KiB) {
Patrick Rudolph17856b72017-11-11 10:17:53 +01001073 printerr("VBT won't fit\n");
1074 return 1;
1075 }
1076
1077 oh->vbt_offset = fo->size;
1078 fo = remalloc_fo(fo, fo->size + vbt_size(fo_vbt));
1079 if (!fo) {
1080 printerr("Failed to allocate memory\n");
1081 return 1;
1082 }
Patrick Rudolphaece0062018-10-14 11:34:04 +02001083 oh = (optionrom_header_t *)fo->data;
Patrick Rudolph17856b72017-11-11 10:17:53 +01001084 }
1085
1086 head = (struct vbt_header *)((u8 *)oh + oh->vbt_offset);
1087 memcpy(head, fo_vbt->data, vbt_size(fo_vbt));
1088
1089 return 0;
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +02001090}
1091
1092int main(int argc, char **argv)
1093{
Patrick Rudolphaece0062018-10-14 11:34:04 +02001094 int opt, ret, option_index = 0;
Patrick Rudolph17856b72017-11-11 10:17:53 +01001095
1096 size_t has_input = 0, has_output = 0;
1097 size_t dump = 0, in_legacy = 0;
1098 char *in_vbt = NULL, *in_oprom = NULL;
1099 char *out_vbt = NULL, *patch_oprom = NULL;
1100 static struct option long_options[] = {
1101 {"help", 0, 0, 'h'},
1102 {"outdump", 0, 0, 'd'},
1103 {"inlegacy", 0, 0, 'l'},
1104 {"invbt", required_argument, 0, 'f'},
1105 {"inoprom", required_argument, 0, 'o'},
1106 {"outvbt", required_argument, 0, 'v'},
1107 {"patchoprom", required_argument, 0, 'p'},
1108 {0, 0, 0, 0}
1109 };
1110
1111 while ((opt = getopt_long(argc, argv, "hdlf:o:v:p:i",
1112 long_options, &option_index)) != EOF) {
1113 switch (opt) {
1114 case 'd':
1115 dump = 1;
1116 has_output = 1;
1117 break;
1118 case 'l':
1119 in_legacy = 1;
1120 has_input = 1;
1121 break;
1122 case 'f':
1123 in_vbt = strdup(optarg);
1124 has_input = 1;
1125 break;
1126 case 'o':
1127 in_oprom = strdup(optarg);
1128 has_input = 1;
1129 break;
1130 case 'v':
1131 out_vbt = strdup(optarg);
1132 has_output = 1;
1133 break;
1134 case 'p':
1135 patch_oprom = strdup(optarg);
1136 has_output = 1;
1137 break;
1138 case '?':
1139 case 'h':
1140 print_usage(argv[0], long_options);
1141 exit(0);
1142 break;
1143 }
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +02001144 }
Patrick Rudolph17856b72017-11-11 10:17:53 +01001145
1146 if (!has_input)
1147 printerr("No input specified !\n");
1148 if (!has_output)
1149 printerr("No output specified !\n");
1150 if (argc < 2 || argc > 6 || !has_input || !has_output) {
1151 print_usage(argv[0], long_options);
Patrick Georgi1fcebe72016-07-29 18:28:42 +02001152 return 1;
1153 }
1154
Patrick Rudolph17856b72017-11-11 10:17:53 +01001155 struct fileobject *fo;
1156
1157 if (in_legacy)
1158 fo = read_physmem(0xc0000);
1159 else if (in_vbt)
1160 fo = read_file(in_vbt);
1161 else
1162 fo = read_file(in_oprom);
1163
1164 if (!fo) {
1165 printerr("Failed to read input file\n");
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +02001166 return 1;
1167 }
Patrick Rudolph17856b72017-11-11 10:17:53 +01001168
1169 struct fileobject *vbt = NULL;
1170 if (in_legacy || in_oprom) {
1171 if (!is_valid_vbios(fo)) {
1172 printerr("Invalid input file\n");
1173
1174 free_fo(fo);
1175 return 1;
1176 }
1177 parse_vbios(fo, &vbt);
1178 } else
1179 parse_vbt(fo, &vbt);
1180
1181 free_fo(fo);
1182
1183 if (!vbt) {
1184 printerr("Failed to find VBT.\n");
1185 return 1;
1186 }
1187
1188 if (!dump)
1189 print_vbt(vbt);
1190
Patrick Rudolphaece0062018-10-14 11:34:04 +02001191 ret = 0;
Patrick Rudolph17856b72017-11-11 10:17:53 +01001192 if (dump) {
1193 dump_vbt(vbt);
1194 } else if (out_vbt) {
Patrick Rudolphaece0062018-10-14 11:34:04 +02001195 if (write_file(out_vbt, vbt)) {
Patrick Rudolph17856b72017-11-11 10:17:53 +01001196 printerr("Failed to write VBT\n");
Patrick Rudolphaece0062018-10-14 11:34:04 +02001197 ret = 1;
1198 } else {
Patrick Rudolph17856b72017-11-11 10:17:53 +01001199 print("VBT written to %s\n", out_vbt);
Patrick Rudolphaece0062018-10-14 11:34:04 +02001200 }
Patrick Rudolph17856b72017-11-11 10:17:53 +01001201 } else if (patch_oprom) {
1202 fo = read_file(patch_oprom);
1203 if (!fo) {
1204 printerr("Failed to read input file\n");
Patrick Rudolphaece0062018-10-14 11:34:04 +02001205 ret = 1;
Patrick Rudolph17856b72017-11-11 10:17:53 +01001206 }
Patrick Rudolphaece0062018-10-14 11:34:04 +02001207 if (ret != 1 && !is_valid_vbios(fo)) {
Patrick Rudolph17856b72017-11-11 10:17:53 +01001208 printerr("Invalid input file\n");
Patrick Rudolphaece0062018-10-14 11:34:04 +02001209 ret = 1;
Patrick Rudolph17856b72017-11-11 10:17:53 +01001210 }
Patrick Rudolphaece0062018-10-14 11:34:04 +02001211 if (ret != 1 && patch_vbios(fo, vbt)) {
Patrick Rudolph17856b72017-11-11 10:17:53 +01001212 printerr("Failed to patch VBIOS\n");
Patrick Rudolphaece0062018-10-14 11:34:04 +02001213 ret = 1;
Patrick Rudolph17856b72017-11-11 10:17:53 +01001214 }
Patrick Rudolphaece0062018-10-14 11:34:04 +02001215 if (ret != 1 && fix_vbios_header(fo)) {
1216 printerr("Failed to fix VBIOS header\n");
1217 ret = 1;
1218 }
1219 if (ret != 1 && write_file(patch_oprom, fo)) {
Patrick Rudolph17856b72017-11-11 10:17:53 +01001220 printerr("Failed to write VBIOS\n");
Patrick Rudolphaece0062018-10-14 11:34:04 +02001221 ret = 1;
Patrick Rudolph17856b72017-11-11 10:17:53 +01001222 }
1223 free_fo(fo);
Patrick Rudolphaece0062018-10-14 11:34:04 +02001224 if (ret != 1)
1225 print("VBIOS %s successfully patched\n", patch_oprom);
Patrick Rudolph17856b72017-11-11 10:17:53 +01001226 }
1227
1228 /* cleanup */
1229 if (patch_oprom)
1230 free(patch_oprom);
1231 if (out_vbt)
1232 free(out_vbt);
1233 if (in_vbt)
1234 free(in_vbt);
1235 if (in_oprom)
1236 free(in_oprom);
1237
1238 free_fo(vbt);
1239
Patrick Rudolphaece0062018-10-14 11:34:04 +02001240 return ret;
Vladimir Serbinenko5a6b8d12014-05-24 16:34:29 +02001241}