aboutsummaryrefslogtreecommitdiff
path: root/post
diff options
context:
space:
mode:
authorBin Meng <bmeng.cn@gmail.com>2018-06-12 08:36:22 -0700
committerBin Meng <bmeng.cn@gmail.com>2018-06-17 21:16:04 +0800
commit4a08c74697c679607f1ddb56167c74fc31d53068 (patch)
tree4816671c72cba2b2aee0935d0ac44229da285ee9 /post
parentd1fe9927465c3df03c7c0c8f62bd8468d24f66bc (diff)
downloadu-boot-4a08c74697c679607f1ddb56167c74fc31d53068.zip
u-boot-4a08c74697c679607f1ddb56167c74fc31d53068.tar.gz
u-boot-4a08c74697c679607f1ddb56167c74fc31d53068.tar.bz2
dm: video: Add an EFI framebuffer driver
This adds a DM video driver for U-Boot as the EFI payload. The driver makes use of all necessary information from the passed EFI GOP info to create a linear framebuffer device, as if it were initialized by U-Boot itself. Signed-off-by: Bin Meng <bmeng.cn@gmail.com> Reviewed-by: Anatolij Gustschin <agust@denx.de>
Diffstat (limited to 'post')
0 files changed, 0 insertions, 0 deletions
ass="hl opt">); while (1) { uint64_t candlen = key_lens[_index]; if (candlen == hlen && !memcmp (values[_index]->str, string, len)) { hashp = values[_index]; if (hashp->alignment < alignment) hashp->alignment = alignment; return hashp; } if (!(candlen & (uint32_t)-1)) break; _index = (_index + 1) & (nbuckets - 1); } hashp = sec_merge_hash_insert (table, string, hash, len, _index); if (hashp == NULL) return NULL; hashp->alignment = alignment; table->size++; BFD_ASSERT (table->size == table->table.count); if (table->first == NULL) table->first = hashp; else table->last->next = hashp; table->last = hashp; return hashp; } /* Create a new hash table. */ static struct sec_merge_hash * sec_merge_init (unsigned int entsize, bool strings) { struct sec_merge_hash *table; table = (struct sec_merge_hash *) bfd_malloc (sizeof (struct sec_merge_hash)); if (table == NULL) return NULL; if (! bfd_hash_table_init_n (&table->table, NULL, sizeof (struct sec_merge_hash_entry), 0x2000)) { free (table); return NULL; } table->size = 0; table->first = NULL; table->last = NULL; table->entsize = entsize; table->strings = strings; table->nbuckets = 0x2000; table->key_lens = objalloc_alloc ((struct objalloc *) table->table.memory, table->nbuckets * sizeof (table->key_lens[0])); memset (table->key_lens, 0, table->nbuckets * sizeof (table->key_lens[0])); table->values = objalloc_alloc ((struct objalloc *) table->table.memory, table->nbuckets * sizeof (table->values[0])); memset (table->values, 0, table->nbuckets * sizeof (table->values[0])); return table; } /* Append the tuple of input-offset O corresponding to hash table ENTRY into SECINFO, such that we later may lookup the entry just by O. */ static bool append_offsetmap (struct sec_merge_sec_info *secinfo, mapofs_type o, struct sec_merge_hash_entry *entry) { if ((secinfo->noffsetmap & 2047) == 0) { bfd_size_type amt; amt = (secinfo->noffsetmap + 2048); secinfo->map_ofs = bfd_realloc (secinfo->map_ofs, amt * sizeof(secinfo->map_ofs[0])); if (!secinfo->map_ofs) return false; secinfo->map = bfd_realloc (secinfo->map, amt * sizeof(secinfo->map[0])); if (!secinfo->map) return false; } unsigned int i = secinfo->noffsetmap++; MAP_OFS(secinfo, i) = o; secinfo->map[i].entry = entry; return true; } /* Prepare the input-offset-to-entry tables after output offsets are determined. */ static void prepare_offsetmap (struct sec_merge_sec_info *secinfo) { unsigned int noffsetmap = secinfo->noffsetmap; unsigned int i, lbi; bfd_size_type l, sz, amt; secinfo->fast_state = 1; for (i = 0; i < noffsetmap; i++) MAP_IDX(secinfo, i) = secinfo->map[i].entry->u.index; sz = secinfo->sec->rawsize; amt = (sz / OFSDIV + 1) * sizeof (secinfo->ofstolowbound[0]); secinfo->ofstolowbound = bfd_zmalloc (amt); if (!secinfo->ofstolowbound) return; for (l = lbi = 0; l < sz; l += OFSDIV) { /* No need for bounds checking on lbi, as we've added a sentinel that's larger than any offset. */ while (MAP_OFS(secinfo, lbi) <= l) lbi++; //BFD_ASSERT ((l / OFSDIV) <= (i / OFSDIV)); secinfo->ofstolowbound[l / OFSDIV] = lbi; } secinfo->fast_state = 2; } static bool sec_merge_emit (bfd *abfd, struct sec_merge_sec_info *secinfo, unsigned char *contents) { struct sec_merge_hash_entry *entry = secinfo->first_str; asection *sec = secinfo->sec; file_ptr offset = sec->output_offset; char *pad = NULL; bfd_size_type off = 0; unsigned int opb = bfd_octets_per_byte (abfd, sec); int alignment_power = sec->output_section->alignment_power * opb; bfd_size_type pad_len; /* Octets. */ /* FIXME: If alignment_power is 0 then really we should scan the entry list for the largest required alignment and use that. */ pad_len = alignment_power ? ((bfd_size_type) 1 << alignment_power) : 16; pad = (char *) bfd_zmalloc (pad_len); if (pad == NULL) return false; for (; entry != NULL; entry = entry->next) { const char *str; bfd_size_type len; if (!entry->len) continue; BFD_ASSERT (entry->alignment); len = -off & (entry->alignment - 1); if (len != 0) { BFD_ASSERT (len <= pad_len); if (contents) { memcpy (contents + offset, pad, len); offset += len; } else if (bfd_write (pad, len, abfd) != len) goto err; off += len; } str = entry->str; len = entry->len; if (contents) { memcpy (contents + offset, str, len); offset += len; } else if (bfd_write (str, len, abfd) != len) goto err; off += len; } BFD_ASSERT (!entry); /* Trailing alignment needed? */ off = sec->size - off; if (1 && off != 0) { BFD_ASSERT (off <= pad_len); if (contents) memcpy (contents + offset, pad, off); else if (bfd_write (pad, off, abfd) != off) goto err; } free (pad); return true; err: free (pad); return false; } /* Register a SEC_MERGE section as a candidate for merging. This function is called for all non-dynamic SEC_MERGE input sections. */ bool _bfd_add_merge_section (bfd *abfd, void **psinfo, asection *sec, void **psecinfo) { struct sec_merge_info *sinfo; struct sec_merge_sec_info *secinfo; asection *repr; unsigned int alignment_power; /* Octets. */ unsigned int align; /* Octets. */ unsigned int opb = bfd_octets_per_byte (abfd, sec); if ((abfd->flags & DYNAMIC) != 0 || (sec->flags & SEC_MERGE) == 0) abort (); if (sec->size == 0 || (sec->flags & SEC_EXCLUDE) != 0 || sec->entsize == 0) return true; if (sec->size % sec->entsize != 0) return true; if ((sec->flags & SEC_RELOC) != 0) { /* We aren't prepared to handle relocations in merged sections. */ return true; } if (sec->size > (mapofs_type)-1) { /* Input offsets must be representable by mapofs_type. */ return true; } #ifndef CHAR_BIT #define CHAR_BIT 8 #endif alignment_power = sec->alignment_power * opb; if (alignment_power >= sizeof (align) * CHAR_BIT) return true; align = 1u << alignment_power; if ((sec->entsize < align && ((sec->entsize & (sec->entsize - 1)) || !(sec->flags & SEC_STRINGS))) || (sec->entsize > align && (sec->entsize & (align - 1)))) { /* Sanity check. If string character size is smaller than alignment, then we require character size to be a power of 2, otherwise character size must be integer multiple of alignment. For non-string constants, alignment must be smaller than or equal to entity size and entity size must be integer multiple of alignment. */ return true; } /* Initialize the descriptor for this input section. */ *psecinfo = secinfo = bfd_zalloc (abfd, sizeof (*secinfo)); if (*psecinfo == NULL) goto error_return; secinfo->sec = sec; secinfo->psecinfo = psecinfo; /* Search for a matching output merged section. */ for (sinfo = (struct sec_merge_info *) *psinfo; sinfo; sinfo = sinfo->next) if (sinfo->chain && (repr = sinfo->chain->sec) && ! ((repr->flags ^ sec->flags) & (SEC_MERGE | SEC_STRINGS)) && repr->entsize == sec->entsize && repr->alignment_power == sec->alignment_power && repr->output_section == sec->output_section) break; if (sinfo == NULL) { /* Initialize the information we need to keep track of. */ sinfo = (struct sec_merge_info *) bfd_alloc (abfd, sizeof (struct sec_merge_info)); if (sinfo == NULL) goto error_return; sinfo->next = (struct sec_merge_info *) *psinfo; sinfo->chain = NULL; sinfo->last = &sinfo->chain; *psinfo = sinfo; sinfo->htab = sec_merge_init (sec->entsize, (sec->flags & SEC_STRINGS)); if (sinfo->htab == NULL) goto error_return; } *sinfo->last = secinfo; sinfo->last = &secinfo->next; secinfo->sinfo = sinfo; secinfo->reprsec = sinfo->chain->sec; return true; error_return: *psecinfo = NULL; return false; } /* Record one whole input section (described by SECINFO) into the hash table SINFO. */ static bool record_section (struct sec_merge_info *sinfo, struct sec_merge_sec_info *secinfo) { asection *sec = secinfo->sec; struct sec_merge_hash_entry *entry; unsigned char *p, *end; bfd_vma mask, eltalign; unsigned int align; bfd_size_type amt; bfd_byte *contents; amt = sec->size; if (sec->flags & SEC_STRINGS) /* Some versions of gcc may emit a string without a zero terminator. See http://gcc.gnu.org/ml/gcc-patches/2006-06/msg01004.html Allocate space for an extra zero. */ amt += sec->entsize; contents = bfd_malloc (amt); if (!contents) goto error_return; /* Slurp in all section contents (possibly decompressing it). */ sec->rawsize = sec->size; if (sec->flags & SEC_STRINGS) memset (contents + sec->size, 0, sec->entsize); if (! bfd_get_full_section_contents (sec->owner, sec, &contents)) goto error_return; /* Now populate the hash table and offset mapping. */ /* Presize the hash table for what we're going to add. We overestimate quite a bit, but if it turns out to be too much then other sections merged into this area will make use of that as well. */ if (!sec_merge_maybe_resize (sinfo->htab, 1 + sec->size / 2)) { bfd_set_error (bfd_error_no_memory); goto error_return; } /* Walk through the contents, calculate hashes and length of all blobs (strings or fixed-size entries) we find and fill the hash and offset tables. */ align = sec->alignment_power; mask = ((bfd_vma) 1 << align) - 1; end = contents + sec->size; for (p = contents; p < end;) { unsigned len; uint32_t hash = hashit (sinfo->htab, (char*) p, &len); unsigned int ofs = p - contents; eltalign = ofs; eltalign = ((eltalign ^ (eltalign - 1)) + 1) >> 1; if (!eltalign || eltalign > mask) eltalign = mask + 1; entry = sec_merge_hash_lookup (sinfo->htab, (char *) p, len, hash, (unsigned) eltalign); if (! entry) goto error_return; if (! append_offsetmap (secinfo, ofs, entry)) goto error_return; p += len; } /* Add a sentinel element that's conceptually behind all others. */ append_offsetmap (secinfo, sec->size, NULL); /* But don't count it. */ secinfo->noffsetmap--; free (contents); contents = NULL; /*printf ("ZZZ %s:%s %u entries\n", sec->owner->filename, sec->name, (unsigned)secinfo->noffsetmap);*/ return true; error_return: free (contents); contents = NULL; for (secinfo = sinfo->chain; secinfo; secinfo = secinfo->next) *secinfo->psecinfo = NULL; return false; } /* qsort comparison function. Won't ever return zero as all entries differ, so there is no issue with qsort stability here. */ static int strrevcmp (const void *a, const void *b) { struct sec_merge_hash_entry *A = *(struct sec_merge_hash_entry **) a; struct sec_merge_hash_entry *B = *(struct sec_merge_hash_entry **) b; unsigned int lenA = A->len; unsigned int lenB = B->len; const unsigned char *s = (const unsigned char *) A->str + lenA - 1; const unsigned char *t = (const unsigned char *) B->str + lenB - 1; int l = lenA < lenB ? lenA : lenB; while (l) { if (*s != *t) return (int) *s - (int) *t; s--; t--; l--; } return lenA - lenB; } /* Like strrevcmp, but for the case where all strings have the same alignment > entsize. */ static int strrevcmp_align (const void *a, const void *b) { struct sec_merge_hash_entry *A = *(struct sec_merge_hash_entry **) a; struct sec_merge_hash_entry *B = *(struct sec_merge_hash_entry **) b; unsigned int lenA = A->len; unsigned int lenB = B->len; const unsigned char *s = (const unsigned char *) A->str + lenA - 1; const unsigned char *t = (const unsigned char *) B->str + lenB - 1; int l = lenA < lenB ? lenA : lenB; int tail_align = (lenA & (A->alignment - 1)) - (lenB & (A->alignment - 1)); if (tail_align != 0) return tail_align; while (l) { if (*s != *t) return (int) *s - (int) *t; s--; t--; l--; } return lenA - lenB; } static inline int is_suffix (const struct sec_merge_hash_entry *A, const struct sec_merge_hash_entry *B) { if (A->len <= B->len) /* B cannot be a suffix of A unless A is equal to B, which is guaranteed not to be equal by the hash table. */ return 0; return memcmp (A->str + (A->len - B->len), B->str, B->len) == 0; } /* This is a helper function for _bfd_merge_sections. It attempts to merge strings matching suffixes of longer strings. */ static struct sec_merge_sec_info * merge_strings (struct sec_merge_info *sinfo) { struct sec_merge_hash_entry **array, **a, *e; struct sec_merge_sec_info *secinfo; bfd_size_type size, amt; unsigned int alignment = 0; /* Now sort the strings */ amt = sinfo->htab->size * sizeof (struct sec_merge_hash_entry *); array = (struct sec_merge_hash_entry **) bfd_malloc (amt); if (array == NULL) return NULL; for (e = sinfo->htab->first, a = array; e; e = e->next) if (e->alignment) { *a++ = e; /* Adjust the length to not include the zero terminator. */ e->len -= sinfo->htab->entsize; if (alignment != e->alignment) { if (alignment == 0) alignment = e->alignment; else alignment = (unsigned) -1; } } sinfo->htab->size = a - array; if (sinfo->htab->size != 0) { qsort (array, (size_t) sinfo->htab->size, sizeof (struct sec_merge_hash_entry *), (alignment != (unsigned) -1 && alignment > sinfo->htab->entsize ? strrevcmp_align : strrevcmp)); /* Loop over the sorted array and merge suffixes */ e = *--a; e->len += sinfo->htab->entsize; while (--a >= array) { struct sec_merge_hash_entry *cmp = *a; cmp->len += sinfo->htab->entsize; if (e->alignment >= cmp->alignment && !((e->len - cmp->len) & (cmp->alignment - 1)) && is_suffix (e, cmp)) { cmp->u.suffix = e; cmp->alignment = 0; } else e = cmp; } } free (array); /* Now assign positions to the strings we want to keep. */ size = 0; secinfo = sinfo->chain; for (e = sinfo->htab->first; e; e = e->next) { if (e->alignment) { size = (size + e->alignment - 1) & ~((bfd_vma) e->alignment - 1); e->u.index = size; size += e->len; } } secinfo->sec->size = size; /* And now adjust the rest, removing them from the chain (but not hashtable) at the same time. */ for (a = &sinfo->htab->first, e = *a; e; e = e->next) if (e->alignment) a = &e->next; else { *a = e->next; if (e->len) { e->alignment = e->u.suffix->alignment; e->u.index = e->u.suffix->u.index + (e->u.suffix->len - e->len); } } BFD_ASSERT (!secinfo->first_str); secinfo->first_str = sinfo->htab->first; return secinfo; } /* This function is called once after all SEC_MERGE sections are registered with _bfd_merge_section. */ bool _bfd_merge_sections (bfd *abfd, struct bfd_link_info *info ATTRIBUTE_UNUSED, void *xsinfo, void (*remove_hook) (bfd *, asection *)) { struct sec_merge_info *sinfo; for (sinfo = (struct sec_merge_info *) xsinfo; sinfo; sinfo = sinfo->next) { struct sec_merge_sec_info *secinfo; bfd_size_type align; /* Bytes. */ if (! sinfo->chain) continue; /* Record the sections into the hash table. */ align = 1; for (secinfo = sinfo->chain; secinfo; secinfo = secinfo->next) if (secinfo->sec->flags & SEC_EXCLUDE) { *secinfo->psecinfo = NULL; if (remove_hook) (*remove_hook) (abfd, secinfo->sec); } else { if (!record_section (sinfo, secinfo)) return false; if (align) { unsigned int opb = bfd_octets_per_byte (abfd, secinfo->sec); align = (bfd_size_type) 1 << secinfo->sec->alignment_power; if (((secinfo->sec->size / opb) & (align - 1)) != 0) align = 0; } } if (sinfo->htab->first == NULL) continue; if (sinfo->htab->strings) { secinfo = merge_strings (sinfo); if (!secinfo) return false; } else { struct sec_merge_hash_entry *e = sinfo->htab->first; bfd_size_type size = 0; /* Octets. */ /* Things are much simpler for non-strings. Just assign them slots in the section. */ secinfo = sinfo->chain; BFD_ASSERT (!secinfo->first_str); secinfo->first_str = e; for (e = sinfo->htab->first; e; e = e->next) { if (e->alignment) { size = (size + e->alignment - 1) & ~((bfd_vma) e->alignment - 1); e->u.index = size; size += e->len; } } secinfo->sec->size = size; } /* If the input sections were padded according to their alignments, then pad the output too. */ if (align) secinfo->sec->size = (secinfo->sec->size + align - 1) & -align; /* Finally remove all input sections which have not made it into the hash table at all. */ for (secinfo = sinfo->chain; secinfo; secinfo = secinfo->next) if (secinfo->first_str == NULL) secinfo->sec->flags |= SEC_EXCLUDE | SEC_KEEP; } return true; } /* Write out the merged section. */ bool _bfd_write_merged_section (bfd *output_bfd, asection *sec, void *psecinfo) { struct sec_merge_sec_info *secinfo; file_ptr pos; unsigned char *contents; Elf_Internal_Shdr *hdr; secinfo = (struct sec_merge_sec_info *) psecinfo; if (!secinfo) return false; if (secinfo->first_str == NULL) return true; /* FIXME: octets_per_byte. */ hdr = &elf_section_data (sec->output_section)->this_hdr; if (hdr->sh_offset == (file_ptr) -1) { /* We must compress this section. Write output to the buffer. */ contents = hdr->contents; if (contents == NULL) abort (); } else { contents = NULL; pos = sec->output_section->filepos + sec->output_offset; if (bfd_seek (output_bfd, pos, SEEK_SET) != 0) return false; } BFD_ASSERT (sec == secinfo->sec); BFD_ASSERT (secinfo == secinfo->sinfo->chain); if (! sec_merge_emit (output_bfd, secinfo, contents)) return false; return true; } /* Adjust an address in the SEC_MERGE section. Given OFFSET within *PSEC, this returns the new offset in the adjusted SEC_MERGE section and writes the new section back into *PSEC. */ bfd_vma _bfd_merged_section_offset (bfd *output_bfd ATTRIBUTE_UNUSED, asection **psec, void *psecinfo, bfd_vma offset) { struct sec_merge_sec_info *secinfo; asection *sec = *psec; secinfo = (struct sec_merge_sec_info *) psecinfo; if (!secinfo) return offset; if (offset >= sec->rawsize) { if (offset > sec->rawsize) _bfd_error_handler /* xgettext:c-format */ (_("%pB: access beyond end of merged section (%" PRId64 ")"), sec->owner, (int64_t) offset); return secinfo->first_str ? sec->size : 0; } if (secinfo->fast_state != 2) { if (!secinfo->fast_state) prepare_offsetmap (secinfo); if (secinfo->fast_state != 2) return offset; } long lb = secinfo->ofstolowbound[offset / OFSDIV]; *psec = secinfo->reprsec; /* No need for bounds checking on lb, as we've added a sentinel that's larger than any offset. */ while (MAP_OFS(secinfo, lb) <= offset) lb++; lb--; /*printf ("YYY (%s:%s):%u -> (%s):%u\n", sec->owner->filename, sec->name, (unsigned)offset, (*psec)->name, (unsigned)lb);*/ return MAP_IDX(secinfo, lb) + offset - MAP_OFS(secinfo, lb); } /* Tidy up when done. */ void _bfd_merge_sections_free (void *xsinfo) { struct sec_merge_info *sinfo; for (sinfo = (struct sec_merge_info *) xsinfo; sinfo; sinfo = sinfo->next) { struct sec_merge_sec_info *secinfo; for (secinfo = sinfo->chain; secinfo; secinfo = secinfo->next) { free (secinfo->ofstolowbound); free (secinfo->map); free (secinfo->map_ofs); } bfd_hash_table_free (&sinfo->htab->table); free (sinfo->htab); } }