| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| // Flashmap support. | ||
| // | ||
| // Copyright (C) 2019 3mdeb | ||
| // | ||
| // This file may be distributed under the terms of the GNU LGPLv3 license. | ||
|
|
||
| #include "malloc.h" // malloc | ||
| #include "string.h" // memcpy | ||
| #include "output.h" // dprintf | ||
| #include "util.h" // find_cb_subtable | ||
| #include "fmap.h" // find_fmap_directory | ||
|
|
||
| static void* fmap_entry = NULL; | ||
| static void* rom_begin = NULL; | ||
|
|
||
| #define ROM_END ((void *)0xFFFFFFFF) | ||
|
|
||
| #define CB_TAG_BOOT_MEDIA_PARAMS 0x0030 | ||
|
|
||
| struct cb_boot_media_params { | ||
| u32 tag; | ||
| u32 size; | ||
| /* offsets are relative to start of boot media */ | ||
| u64 fmap_offset; | ||
| u64 cbfs_offset; | ||
| u64 cbfs_size; | ||
| u64 boot_media_size; | ||
| }; | ||
|
|
||
| void find_fmap_directory(void) | ||
| { | ||
| struct cb_boot_media_params *cbbmp; | ||
|
|
||
| // Find coreboot table. | ||
| struct cb_header *cbh = find_cb_table(); | ||
|
|
||
| if (!cbh) { | ||
| dprintf(1, "coreboot table not found\n"); | ||
| return; | ||
| } | ||
|
|
||
| cbbmp = find_cb_subtable(cbh, CB_TAG_BOOT_MEDIA_PARAMS); | ||
|
|
||
| if (!cbbmp) { | ||
| dprintf(1, "Boot Media Params not found\n"); | ||
| return; | ||
| } | ||
|
|
||
| if (cbbmp->fmap_offset != 0 && cbbmp->boot_media_size != 0) { | ||
| rom_begin = (void *)(ROM_END - cbbmp->boot_media_size + 1); | ||
| fmap_entry = (void *)((u32)rom_begin + (u32)cbbmp->fmap_offset); | ||
| dprintf(1, "FMAP found @ %p\n", fmap_entry); | ||
| } else { | ||
| dprintf(1, "FMAP not found\n"); | ||
| } | ||
| } | ||
|
|
||
| int fmap_locate_area(const char *name, struct region *ar) | ||
| { | ||
| size_t offset; | ||
|
|
||
| if (!fmap_entry || !rom_begin) | ||
| return -1; | ||
|
|
||
| /* Start reading the areas just after fmap header. */ | ||
| offset = sizeof(struct fmap); | ||
|
|
||
| struct fmap_area *area = malloc_tmp(sizeof(*area)); | ||
| if (!area) { | ||
| warn_noalloc(); | ||
| return -1; | ||
| } | ||
|
|
||
| while (1) { | ||
| iomemcpy(area, fmap_entry + offset, sizeof(*area)); | ||
|
|
||
| if (strcmp((const char *)area->name, name)) { | ||
| offset += sizeof(struct fmap_area); | ||
| continue; | ||
| } | ||
|
|
||
| dprintf(1, "FMAP: area %s found @ %p (%d bytes)\n", | ||
| name, (void *) area->offset, area->size); | ||
|
|
||
| ar->offset = (u32)rom_begin + area->offset; | ||
| ar->size = area->size; | ||
| free(area); | ||
| return 0; | ||
| } | ||
|
|
||
| free(area); | ||
| dprintf(1, "FMAP: area %s not found\n", name); | ||
|
|
||
| return -1; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| // Flashmap support. | ||
| // | ||
| // Copyright (C) 2019 3mdeb | ||
| // | ||
| // This file may be distributed under the terms of the GNU LGPLv3 license. | ||
|
|
||
| #ifndef _FMAP_H | ||
| #define _FMAP_H | ||
|
|
||
| #define FMAP_SIGNATURE "__FMAP__" | ||
| #define FMAP_VER_MAJOR 1 /* this header's FMAP minor version */ | ||
| #define FMAP_VER_MINOR 1 /* this header's FMAP minor version */ | ||
| #define FMAP_STRLEN 32 /* maximum length for strings, */ | ||
| /* including null-terminator */ | ||
|
|
||
| enum fmap_flags { | ||
| FMAP_AREA_STATIC = 1 << 0, | ||
| FMAP_AREA_COMPRESSED = 1 << 1, | ||
| FMAP_AREA_RO = 1 << 2, | ||
| FMAP_AREA_PRESERVE = 1 << 3, | ||
| }; | ||
|
|
||
| /* Mapping of volatile and static regions in firmware binary */ | ||
| struct fmap_area { | ||
| u32 offset; /* offset relative to base */ | ||
| u32 size; /* size in bytes */ | ||
| u8 name[FMAP_STRLEN]; /* descriptive name */ | ||
| u16 flags; /* flags for this area */ | ||
| } PACKED; | ||
|
|
||
| struct fmap { | ||
| u8 signature[8]; /* "__FMAP__" (0x5F5F464D41505F5F) */ | ||
| u8 ver_major; /* major version */ | ||
| u8 ver_minor; /* minor version */ | ||
| u64 base; /* address of the firmware binary */ | ||
| u32 size; /* size of firmware binary in bytes */ | ||
| u8 name[FMAP_STRLEN]; /* name of this firmware binary */ | ||
| u16 nareas; /* number of areas described by | ||
| fmap_areas[] below */ | ||
| struct fmap_area areas[]; | ||
| } PACKED; | ||
|
|
||
| struct region { | ||
| size_t offset; | ||
| size_t size; | ||
| }; | ||
|
|
||
| void find_fmap_directory(void); | ||
| int fmap_locate_area(const char *name, struct region *ar); | ||
|
|
||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| //***************************************************************************** | ||
| // | ||
| // Copyright (c) 2015 Eltan B.V. All rights reserved. | ||
| // Software License Agreement | ||
| // | ||
| // THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED | ||
| // OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF | ||
| // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. | ||
| // ELTAN SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, | ||
| // OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER. | ||
| // | ||
| //***************************************************************************** | ||
|
|
||
| #ifndef __COREBOOT_H | ||
| #define __COREBOOT_H | ||
|
|
||
| struct cbmem_entry { | ||
| u32 magic; | ||
| u32 id; | ||
| u64 base; | ||
| u64 size; | ||
| }; | ||
|
|
||
| // | ||
| // Intended for cbfs file overrides | ||
| // | ||
| struct file_container { | ||
| u32 file_signature; /* "FILE" */ | ||
| u32 file_size; /* size of file_data[] */ | ||
| char file_name[32]; | ||
| char file_data[0]; /* Variable size */ | ||
| }; | ||
|
|
||
| #define CBMEM_ID_FILE 0x46494c45 //'FILE' | ||
| #define CB_TAG_FILE 0x25 | ||
| #define CBMEM_MAGIC 0x434f5245 | ||
|
|
||
| struct cbfile_record { | ||
| u32 tag; | ||
| u32 size; | ||
| u64 forward; | ||
| }; | ||
| char * get_cbmem_file( char * filename, int * size ); | ||
|
|
||
| #endif //__COREBOOT_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| /* | ||
| * Copyright (c) 2014 The Chromium OS Authors. All rights reserved. | ||
| * Use of this source code is governed by a BSD-style license that can be | ||
| * found in the LICENSE file. | ||
| */ | ||
|
|
||
| #include "lib_vpd.h" | ||
| #include "output.h" // dprintf | ||
|
|
||
| /* Given an encoded string, this functions decodes the length field which varies | ||
| * from 1 byte to many bytes. | ||
| * | ||
| * The in points the actual byte going to be decoded. The *length returns | ||
| * the decoded length field. The number of consumed bytes will be stroed in | ||
| * decoded_len. | ||
| * | ||
| * Returns VPD_FAIL if more bit is 1, but actually reaches the end of string. | ||
| */ | ||
| int VPDdecodeLen(const s32 max_len,const u8 *in, s32 *length, s32 *decoded_len) | ||
| { | ||
| u8 more; | ||
| int i = 0; | ||
|
|
||
| if (!length) | ||
| return VPD_FAIL; | ||
|
|
||
| if (!decoded_len) | ||
| return VPD_FAIL; | ||
|
|
||
| *length = 0; | ||
| do { | ||
| if (i >= max_len) | ||
| return VPD_FAIL; | ||
|
|
||
| more = in[i] & 0x80; | ||
| *length <<= 7; | ||
| *length |= in[i] & 0x7f; | ||
| ++i; | ||
| } while (more); | ||
|
|
||
| *decoded_len = i; | ||
|
|
||
| return VPD_OK; | ||
| } | ||
|
|
||
| /* Given the encoded string, this function invokes callback with extracted | ||
| * (key, value). The *consumed will be plused the number of bytes consumed in | ||
| * this function. | ||
| * | ||
| * The input_buf points to the first byte of the input buffer. | ||
| * | ||
| * The *consumed starts from 0, which is actually the next byte to be decoded. | ||
| * It can be non-zero to be used in multiple calls. | ||
| * | ||
| * If one entry is successfully decoded, sends it to callback and returns the | ||
| * result. | ||
| */ | ||
| int decodeVpdString(const s32 max_len, const u8 *input_buf, s32 *consumed, | ||
| VpdDecodeCallback callback, void *callback_arg) | ||
| { | ||
| int type; | ||
| s32 key_len, value_len; | ||
| s32 decoded_len; | ||
| const u8 *key, *value; | ||
|
|
||
| /* type */ | ||
| if (*consumed >= max_len) | ||
| return VPD_FAIL; | ||
|
|
||
| type = input_buf[*consumed]; | ||
| switch (type) { | ||
| case VPD_TYPE_INFO: | ||
| case VPD_TYPE_STRING: | ||
| (*consumed)++; | ||
| /* key */ | ||
| if (VPD_OK != VPDdecodeLen(max_len - *consumed, | ||
| &input_buf[*consumed], &key_len, | ||
| &decoded_len) || | ||
| *consumed + decoded_len >= max_len) { | ||
| return VPD_FAIL; | ||
| } | ||
|
|
||
| *consumed += decoded_len; | ||
| key = &input_buf[*consumed]; | ||
| *consumed += key_len; | ||
|
|
||
| /* value */ | ||
| if (VPD_OK != VPDdecodeLen(max_len - *consumed, | ||
| &input_buf[*consumed], | ||
| &value_len, &decoded_len) || | ||
| *consumed + decoded_len > max_len) { | ||
| return VPD_FAIL; | ||
| } | ||
| *consumed += decoded_len; | ||
| value = &input_buf[*consumed]; | ||
| *consumed += value_len; | ||
|
|
||
| if (type == VPD_TYPE_STRING) | ||
| return callback(key, key_len, value, value_len, | ||
| callback_arg); | ||
|
|
||
| return VPD_OK; | ||
|
|
||
| default: | ||
| return VPD_FAIL; | ||
| break; | ||
| } | ||
|
|
||
| return VPD_OK; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,199 @@ | ||
| /* | ||
| * Copyright (c) 2013 The Chromium OS Authors. All rights reserved. | ||
| * Use of this source code is governed by a BSD-style license that can be | ||
| * found in the LICENSE file. | ||
| * | ||
| * Copyright (c) 2018 Libretrend LDA | ||
| */ | ||
|
|
||
| #ifndef __LIB_VPD__ | ||
| #define __LIB_VPD__ | ||
|
|
||
| #include "types.h" | ||
|
|
||
| enum { | ||
| VPD_OK = 0, | ||
| VPD_FAIL, | ||
| }; | ||
|
|
||
| enum { | ||
| VPD_TYPE_TERMINATOR = 0, | ||
| VPD_TYPE_STRING, | ||
| VPD_TYPE_INFO = 0xfe, | ||
| VPD_TYPE_IMPLICIT_TERMINATOR = 0xff, | ||
| }; | ||
|
|
||
| enum { | ||
| VPD_AS_LONG_AS = -1, | ||
| }; | ||
|
|
||
| enum { /* export_type */ | ||
| VPD_EXPORT_KEY_VALUE = 1, | ||
| VPD_EXPORT_VALUE, | ||
| VPD_EXPORT_AS_PARAMETER, | ||
| }; | ||
|
|
||
| /* Callback for decodeVpdString to invoke. */ | ||
| typedef int VpdDecodeCallback(const u8 *key, s32 key_len, const u8 *value, | ||
| s32 value_len, void *arg); | ||
|
|
||
| /* Container data types */ | ||
| struct StringPair { | ||
| u8 *key; | ||
| u8 *value; | ||
| int pad_len; | ||
| int filter_out; /* TRUE means not exported. */ | ||
| struct StringPair *next; | ||
| }; | ||
|
|
||
| struct PairContainer { | ||
| struct StringPair *first; | ||
| }; | ||
|
|
||
|
|
||
| /*********************************************************************** | ||
| * Encode and decode VPD entries | ||
| ***********************************************************************/ | ||
|
|
||
| /* Encodes the len into multiple bytes with the following format. | ||
| * | ||
| * 7 6 ............ 0 | ||
| * +----+------------------+ | ||
| * |More| Length | ... | ||
| * +----+------------------+ | ||
| * | ||
| * The encode_buf points to the actual position we are going to store. | ||
| * encoded_len will return the exact bytes we encoded in this function. | ||
| * Returns fail if the buffer is not long enough. | ||
| */ | ||
| int VPDencodeLen(const s32 len, u8 *encode_buf, const s32 max_len, | ||
| s32 *encoded_len); | ||
|
|
||
| /* Given an encoded string, this functions decodes the length field which varies | ||
| * from 1 byte to many bytes. | ||
| * | ||
| * The in points the actual byte going to be decoded. The *length returns | ||
| * the decoded length field. The number of consumed bytes will be stroed in | ||
| * decoded_len. | ||
| * | ||
| * Returns VPD_FAIL if more bit is 1, but actually reaches the end of string. | ||
| */ | ||
| int VPDdecodeLen(const s32 max_len, const u8 *in, s32 *length, | ||
| s32 *decoded_len); | ||
|
|
||
|
|
||
| /* Encodes the terminator. | ||
| * When calling, the output_buf should point to the start of buffer while | ||
| * *generated_len should contain how many bytes exist in buffer now. | ||
| * After return, *generated_len would be plused the number of bytes generated | ||
| * in this function. | ||
| */ | ||
| int encodeVpdTerminator(const int max_buffer_len, u8 *output_buf, | ||
| int *generated_len); | ||
|
|
||
| /* Encodes the given type/key/value pair into buffer. | ||
| * | ||
| * The pad_value_len is used to control the output value length. | ||
| * When pad_value_len > 0, the value is outputted into fixed length (pad \0 | ||
| * if the value is shorter). Truncated if too long. | ||
| * pad_value_len == VPD_NO_LIMIT, output the value as long as possible. | ||
| * This is useful when we want to fix the structure layout at beginning. | ||
| * | ||
| * The encoded string will be stored in output_buf. If it is longer than | ||
| * max_buffer_len, this function returns fail. If the buffer is long enough, | ||
| * the generated_len will be updated. | ||
| * | ||
| * When calling, the output_buf should point to the start of buffer while | ||
| * *generated_len should contain how many bytes exist in buffer now. | ||
| * After return, *generated_len would be plused the number of bytes generated | ||
| * in this function. | ||
| * | ||
| * The initial value of *generated_len can be non-zero, so that this value | ||
| * can be used between multiple calls to encodeVpd2Pair(). | ||
| */ | ||
| int encodeVpdString(const u8 *key, const u8 *value, const int pad_value_len, | ||
| const int max_buffer_len, u8 *output_buf, | ||
| int *generated_len); | ||
|
|
||
|
|
||
| /* Given the encoded string, this function invokes callback with extracted | ||
| * (key, value). The *consumed will be plused the number of bytes consumed in | ||
| * this function. | ||
| * | ||
| * The input_buf points to the first byte of the input buffer. | ||
| * | ||
| * The *consumed starts from 0, which is actually the next byte to be decoded. | ||
| * It can be non-zero to be used in multiple calls. | ||
| * | ||
| * If one entry is successfully decoded, sends it to callback and returns the | ||
| * result. | ||
| */ | ||
| int decodeVpdString(const s32 max_len, const u8 *input_buf, s32 *consumed, | ||
| VpdDecodeCallback callback, void *callback_arg); | ||
|
|
||
| /*********************************************************************** | ||
| * Container helpers | ||
| ***********************************************************************/ | ||
| void VPDinitContainer(struct PairContainer *container); | ||
|
|
||
| struct StringPair *VPDfindString(struct PairContainer *container, const u8 *key, | ||
| struct StringPair ***prev_next); | ||
|
|
||
| /* If key is already existed in container, its value will be replaced. | ||
| * If not existed, creates new entry in container. | ||
| */ | ||
| void VPDsetString(struct PairContainer *container, const u8 *key, | ||
| const u8 *value, const int pad_len); | ||
|
|
||
| /* merge all entries in src into dst. If key is duplicate, overwrite it. | ||
| */ | ||
| void VPDmergeContainer(struct PairContainer *dst, | ||
| const struct PairContainer *src); | ||
|
|
||
| /* subtract src from dst. | ||
| */ | ||
| int VPDsubtractContainer(struct PairContainer *dst, | ||
| const struct PairContainer *src); | ||
|
|
||
| /* Given a container, encode its all entries into the buffer. | ||
| */ | ||
| int VPDencodeContainer(const struct PairContainer *container, | ||
| const int max_buf_len, u8 *buf, int *generated); | ||
|
|
||
| /* Given a VPD blob, decode its entries and push into container. | ||
| */ | ||
| int VPDdecodeToContainer(struct PairContainer *container, const s32 max_len, | ||
| const u8 *input_buf, s32 *consumed); | ||
|
|
||
| /* Set filter for exporting functions. | ||
| * If filter is NULL, resets the filter so that everything can be exported. | ||
| */ | ||
| int VPDsetContainerFilter(struct PairContainer *container, const u8 *filter); | ||
|
|
||
| /* | ||
| * Remove a key. | ||
| * Returns VPD_OK if deleted successfully. Otherwise, VPD_FAIL. | ||
| */ | ||
| int VPDdeleteKey(struct PairContainer *container, const u8 *key); | ||
|
|
||
| /* | ||
| * Returns number of pairs in container. | ||
| */ | ||
| int VPDlenOfContainer(const struct PairContainer *container); | ||
|
|
||
| /* | ||
| * Export the container content with human-readable text. | ||
| * | ||
| * The buf points to the first byte of buffer and *generated contains the number | ||
| * of bytes already existed in buffer. | ||
| * | ||
| * Afterward, the *generated will be plused on exact bytes this function has | ||
| * generated. | ||
| */ | ||
| int VPDexportContainer(const int export_type, | ||
| const struct PairContainer *container, | ||
| const int max_buf_len, u8 *buf, int *generated); | ||
|
|
||
| void VPDdestroyContainer(struct PairContainer *container); | ||
|
|
||
| #endif /* __LIB_VPD__ */ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,158 @@ | ||
| // VPD interface support. | ||
| // | ||
| // Copyright (C) 2018 Libretrend LDA | ||
| // | ||
| // This file may be distributed under the terms of the GNU LGPLv3 license. | ||
|
|
||
| #include "bregs.h" // bregs | ||
| #include "block.h" // MAXDESCSIZE | ||
| #include "byteorder.h" // be32_to_cpu | ||
| #include "config.h" // CONFIG_* | ||
| #include "malloc.h" // free | ||
| #include "output.h" // dprintf | ||
| #include "romfile.h" // romfile_findprefix | ||
| #include "stacks.h" // yield | ||
| #include "string.h" // memcpy | ||
| #include "util.h" // find_cb_table | ||
|
|
||
| #include "vpd.h" | ||
| #include "lib_vpd.h" | ||
| #include "vpd_tables.h" // vpd_gets | ||
|
|
||
| #define CB_TAG_VPD 0x002c | ||
| #define CROSVPD_CBMEM_MAGIC 0x43524f53 | ||
| #define CROSVPD_CBMEM_VERSION 0x0001 | ||
| #define MAX_INPUT 60 | ||
| #define ENTER_KEY 28 | ||
|
|
||
| struct cbmem_vpd *vpd; | ||
|
|
||
| struct cbmem_vpd { | ||
| u32 magic; | ||
| u32 version; | ||
| u32 ro_size; | ||
| u32 rw_size; | ||
| u8 blob[0]; | ||
| /* The blob contains both RO and RW data. It starts with RO (0 .. | ||
| * ro_size) and then RW (ro_size .. ro_size+rw_size). | ||
| */ | ||
| }; | ||
|
|
||
| struct cb_ref { | ||
| u32 tag; | ||
| u32 size; | ||
| u64 cbmem_addr; | ||
| }; | ||
|
|
||
| struct vpd_gets_arg { | ||
| const u8 *key; | ||
| const u8 *value; | ||
| s32 key_len, value_len; | ||
| int matched; | ||
| }; | ||
|
|
||
| static struct cbmem_vpd* find_vpd(void) | ||
| { | ||
| struct cb_header *cbh = find_cb_table(); | ||
| if (!cbh) { | ||
| dprintf(1, "Unable to find coreboot table\n"); | ||
| return NULL; | ||
| } | ||
|
|
||
| struct cb_ref *cbref = find_cb_subtable(cbh, CB_TAG_VPD); | ||
| if (!cbref) { | ||
| dprintf(1, "Unable to find VPD table\n"); | ||
| return NULL; | ||
| } | ||
| struct cbmem_vpd *cb_vpd = (void*)(u32)cbref->cbmem_addr; | ||
|
|
||
| dprintf(1, "VPD found @ %llx\n", cbref->cbmem_addr); | ||
| dprintf(1, "magic: %x version: %x ro_size: %x rw_size: %x\n", | ||
| cb_vpd->magic, cb_vpd->version, cb_vpd->ro_size, cb_vpd->rw_size); | ||
|
|
||
|
|
||
| if ((cb_vpd->magic != CROSVPD_CBMEM_MAGIC) && | ||
| (cb_vpd->version != CROSVPD_CBMEM_VERSION)) { | ||
| dprintf(1, "Wrong VPD CBMEM magic\n"); | ||
| return NULL; | ||
| } | ||
|
|
||
| if ((cb_vpd->ro_size == 0) && (cb_vpd->rw_size == 0)) { | ||
| dprintf(1, "VPD uninitialized or empty\n"); | ||
| return NULL; | ||
| } | ||
| vpd = cb_vpd; | ||
|
|
||
| return cb_vpd; | ||
| } | ||
|
|
||
| static int vpd_gets_callback(const u8 *key, s32 key_len, const u8 *value, | ||
| s32 value_len, void *arg) | ||
| { | ||
| struct vpd_gets_arg *result = (struct vpd_gets_arg *)arg; | ||
|
|
||
| if (key_len != result->key_len || | ||
| memcmp(key, result->key, key_len) != 0) | ||
| /* Returns VPD_OK to continue parsing. */ | ||
| return VPD_OK; | ||
|
|
||
| result->matched = 1; | ||
| result->value = value; | ||
| result->value_len = value_len; | ||
| /* Returns VPD_FAIL to stop parsing. */ | ||
| return VPD_FAIL; | ||
| } | ||
|
|
||
| const void *vpd_find_key(const char *key, int *size, enum vpd_region region) | ||
| { | ||
| struct vpd_gets_arg arg = {0}; | ||
| int consumed = 0; | ||
|
|
||
| if (!vpd) { | ||
| if (!find_vpd()) | ||
| return NULL; | ||
| } | ||
|
|
||
| arg.key = (const u8 *)key; | ||
| arg.key_len = strlen(key); | ||
|
|
||
| if (region == VPD_ANY || region == VPD_RO) | ||
| while (VPD_OK == decodeVpdString(vpd->ro_size, vpd->blob, | ||
| &consumed, vpd_gets_callback, &arg)) { | ||
| /* Iterate until found or no more entries. */ | ||
| } | ||
|
|
||
| if (!arg.matched && region != VPD_RO) | ||
| while (VPD_OK == decodeVpdString(vpd->rw_size, | ||
| vpd->blob + vpd->ro_size, &consumed, | ||
| vpd_gets_callback, &arg)) { | ||
| /* Iterate until found or no more entries. */ | ||
| } | ||
|
|
||
| if (!arg.matched) | ||
| return NULL; | ||
|
|
||
| *size = arg.value_len; | ||
| return arg.value; | ||
| } | ||
|
|
||
| char *vpd_gets(const char *key, char *buffer, int size, enum vpd_region region) | ||
| { | ||
| const void *string_address; | ||
| int string_size; | ||
|
|
||
| string_address = vpd_find_key(key, &string_size, region); | ||
|
|
||
| if (!string_address) | ||
| return NULL; | ||
|
|
||
| if (size > (string_size + 1)) { | ||
| memcpy(buffer, string_address, string_size); | ||
| buffer[string_size] = '\0'; | ||
| } else { | ||
| memcpy(buffer, string_address, size - 1); | ||
| buffer[size - 1] = '\0'; | ||
| } | ||
| return buffer; | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| /* | ||
| * Copyright (c) 2014 The Chromium OS Authors. All rights reserved. | ||
| * Use of this source code is governed by a BSD-style license that can be | ||
| * found in the LICENSE file. | ||
| */ | ||
|
|
||
| #ifndef __VPD_H__ | ||
| #define __VPD_H__ | ||
|
|
||
| enum vpd_region { | ||
| VPD_ANY = 0, | ||
| VPD_RO = 1, | ||
| VPD_RW = 2 | ||
| }; | ||
| /* | ||
| * Reads VPD string value by key. | ||
| * | ||
| * Reads in at most one less than size characters from VPD and stores them | ||
| * into buffer. A terminating null byte ('\0') is stored after the last | ||
| * character in the buffer. | ||
| * | ||
| * Returns NULL if key is not found, otherwise buffer. | ||
| */ | ||
| char *vpd_gets(const char *key, char *buffer, int size, enum vpd_region region); | ||
|
|
||
| /* | ||
| * Find VPD value by key. | ||
| * | ||
| * Searches for a VPD entry in the VPD cache. If found, places the size of the | ||
| * entry into '*size' and returns the pointer to the entry data. | ||
| * | ||
| * This function presumes that VPD is cached in DRAM (which is the case in the | ||
| * current implementation) and as such returns the pointer into the cache. The | ||
| * user is not supposed to modify the data, and does not have to free the | ||
| * memory. | ||
| * | ||
| * Returns NULL if key is not found. | ||
| */ | ||
|
|
||
| const void *vpd_find_key(const char *key, int *size, enum vpd_region region); | ||
|
|
||
| #endif /* __VPD_H__ */ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,114 @@ | ||
| /* | ||
| * Copyright (c) 2014 The Chromium OS Authors. All rights reserved. | ||
| * Use of this source code is governed by a BSD-style license that can be | ||
| * found in the LICENSE file. | ||
| * | ||
| * Ported from mosys project (http://code.google.com/p/mosys/). | ||
| */ | ||
|
|
||
| #ifndef __LIB_VPD_TABLES_H__ | ||
| #define __LIB_VPD_TABLES_H__ | ||
|
|
||
| #include "types.h" | ||
|
|
||
| #define VPD_ENTRY_MAGIC "_SM_" | ||
| #define VPD_INFO_MAGIC \ | ||
| "\xfe" /* type: VPD header */ \ | ||
| "\x09" /* key length, 9 = 1 + 8 */ \ | ||
| "\x01" /* info version, 1 */ \ | ||
| "gVpdInfo" /* signature, 8 bytes */ \ | ||
| "\x04" /* value length */ | ||
|
|
||
| /* Google specific VPD info */ | ||
| struct google_vpd_info { | ||
| union { | ||
| struct { | ||
| u8 type; | ||
| u8 key_len; | ||
| u8 info_ver; | ||
| u8 signature[8]; | ||
| u8 value_len; | ||
| } tlv; | ||
| u8 magic[12]; | ||
| } header; | ||
| u32 size; | ||
| } PACKED; | ||
|
|
||
| /* Entry */ | ||
| struct vpd_entry { | ||
| u8 anchor_string[4]; | ||
| u8 entry_cksum; | ||
| u8 entry_length; | ||
| u8 major_ver; | ||
| u8 minor_ver; | ||
| u16 max_size; | ||
| u8 entry_rev; | ||
| u8 format_area[5]; | ||
| u8 inter_anchor_string[5]; | ||
| u8 inter_anchor_cksum; | ||
| u16 table_length; | ||
| u32 table_address; | ||
| u16 table_entry_count; | ||
| u8 bcd_revision; | ||
| } PACKED; | ||
|
|
||
| /* Header */ | ||
| struct vpd_header { | ||
| u8 type; | ||
| u8 length; | ||
| u16 handle; | ||
| } PACKED; | ||
|
|
||
| /* Type 0 - firmware information */ | ||
| struct vpd_table_firmware { | ||
| u8 vendor; | ||
| u8 version; | ||
| u16 start_address; | ||
| u8 release_date; | ||
| u8 rom_size_64k_blocks; | ||
| u32 characteristics; | ||
| u8 extension[2]; /* v2.4+ */ | ||
| u8 major_ver; /* v2.4+ */ | ||
| u8 minor_ver; /* v2.4+ */ | ||
| u8 ec_major_ver; /* v2.4+ */ | ||
| u8 ec_minor_ver; /* v2.4+ */ | ||
| } PACKED; | ||
|
|
||
| /* Type 1 - system information */ | ||
| struct vpd_table_system { | ||
| u8 manufacturer; | ||
| u8 name; | ||
| u8 version; | ||
| u8 serial_number; | ||
| u8 uuid[16]; | ||
| u8 wakeup_type; | ||
| u8 sku_number; /* v2.4+ */ | ||
| u8 family; /* v2.4+ */ | ||
| } PACKED; | ||
|
|
||
| /* Type 127 - end of table */ | ||
| struct vpd_table_eot { | ||
| struct vpd_header header; | ||
| } PACKED; | ||
|
|
||
| /* Type 241 - binary blob pointer */ | ||
| struct vpd_table_binary_blob_pointer { | ||
| u8 struct_major_version; | ||
| u8 struct_minor_version; | ||
| u8 vendor; | ||
| u8 description; | ||
| u8 major_version; | ||
| u8 minor_version; | ||
| u8 variant; | ||
| u8 reserved[5]; | ||
| u8 uuid[16]; | ||
| u32 offset; | ||
| u32 size; | ||
| } PACKED; | ||
|
|
||
| /* The length and number of strings defined here is not a limitation of VPD. | ||
| * These numbers were deemed good enough during development. */ | ||
| #define VPD_MAX_STRINGS 10 | ||
| #define VPD_MAX_STRING_LENGTH 64 | ||
|
|
||
| #endif /* __LIB_VPD_TABLES_H__ */ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| #include "config.h" // CONFIG_DEBUG_LEVEL | ||
| #include "malloc.h" // free | ||
| #include "output.h" // dprintf | ||
| #include "stacks.h" // run_thread | ||
| #include "string.h" // memset | ||
| #include "util.h" // acpi_dsdt_* | ||
| #include "virtio-pci.h" | ||
| #include "virtio-blk.h" | ||
| #include "virtio-scsi.h" | ||
| #include "virtio-ring.h" | ||
| #include "virtio-mmio.h" | ||
|
|
||
| void virtio_mmio_setup_acpi(void) | ||
| { | ||
| static const char *virtio_hid = "LNRO0005"; | ||
| struct acpi_device *dev; | ||
| u64 mem, irq, unused; | ||
|
|
||
| for (dev = acpi_dsdt_find_string(NULL, virtio_hid); | ||
| dev != NULL; | ||
| dev = acpi_dsdt_find_string(dev, virtio_hid)) { | ||
| if (acpi_dsdt_find_mem(dev, &mem, &unused) < 0) | ||
| continue; | ||
| if (acpi_dsdt_find_irq(dev, &irq) < 0) | ||
| continue; | ||
| dprintf(1, "ACPI: virtio-mmio device %s at 0x%llx, irq %lld\n", | ||
| acpi_dsdt_name(dev), mem, irq); | ||
| virtio_mmio_setup_one(mem); | ||
| } | ||
| } | ||
|
|
||
| void virtio_mmio_setup_one(u64 addr) | ||
| { | ||
| static const char *names[] = { | ||
| [ 1 ] = "net", | ||
| [ 2 ] = "blk", | ||
| [ 3 ] = "console", | ||
| [ 4 ] = "rng", | ||
| [ 8 ] = "scsi", | ||
| [ 9 ] = "9p", | ||
| [ 16 ] = "gpu", | ||
| [ 19 ] = "vsock", | ||
| [ 18 ] = "input", | ||
| [ 26 ] = "fs", | ||
| }; | ||
| const char *name; | ||
| u32 magic, version, devid; | ||
| void *mmio; | ||
|
|
||
| if (addr >= 0x100000000) { | ||
| dprintf(1, "virtio-mmio: %llx: above 4G\n", addr); | ||
| return; | ||
| } | ||
|
|
||
| mmio = (void*)(u32)(addr); | ||
| magic = readl(mmio); | ||
| if (magic != 0x74726976) { | ||
| dprintf(1, "virtio-mmio: %llx: magic mismatch\n", addr); | ||
| return; | ||
| } | ||
| version = readl(mmio+4); | ||
| if (version != 1 /* legacy */ && | ||
| version != 2 /* 1.0 */) { | ||
| dprintf(1, "virtio-mmio: %llx: unknown version %d\n", addr, version); | ||
| return; | ||
| } | ||
| devid = readl(mmio+8); | ||
|
|
||
| name = (devid < ARRAY_SIZE(names) && names[devid] != NULL) | ||
| ? names[devid] : "unknown"; | ||
| dprintf(1, "virtio-mmio: %llx: device id %x (%s%s)\n", | ||
| addr, devid, name, version == 1 ? ", legacy" : ""); | ||
|
|
||
| switch (devid) { | ||
| case 2: /* blk */ | ||
| run_thread(init_virtio_blk_mmio, mmio); | ||
| break; | ||
| case 8: /* scsi */ | ||
| run_thread(init_virtio_scsi_mmio, mmio); | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| void vp_init_mmio(struct vp_device *vp, void *mmio) | ||
| { | ||
| memset(vp, 0, sizeof(*vp)); | ||
| vp->use_mmio = 1; | ||
| vp->common.mode = VP_ACCESS_MMIO; | ||
| vp->common.memaddr = mmio; | ||
| vp->device.mode = VP_ACCESS_MMIO; | ||
| vp->device.memaddr = mmio + 0x100; | ||
| vp_reset(vp); | ||
| vp_set_status(vp, VIRTIO_CONFIG_S_ACKNOWLEDGE | | ||
| VIRTIO_CONFIG_S_DRIVER); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| #ifndef _VIRTIO_MMIO_H | ||
| #define _VIRTIO_MMIO_H | ||
|
|
||
| struct vp_device; | ||
|
|
||
| typedef struct virtio_mmio_cfg { | ||
| u32 magic; | ||
| u32 version; | ||
| u32 device_id; | ||
| u32 vendor_id; | ||
|
|
||
| u32 device_feature; | ||
| u32 device_feature_select; | ||
| u32 res_18; | ||
| u32 res_1c; | ||
|
|
||
| u32 guest_feature; | ||
| u32 guest_feature_select; | ||
| u32 legacy_guest_page_size; | ||
| u32 res_2c; | ||
|
|
||
| u32 queue_select; | ||
| u32 queue_num_max; | ||
| u32 queue_num; | ||
| u32 legacy_queue_align; | ||
|
|
||
| u32 legacy_queue_pfn; | ||
| u32 queue_ready; | ||
| u32 res_48; | ||
| u32 res_4c; | ||
|
|
||
| u32 queue_notify; | ||
| u32 res_54; | ||
| u32 res_58; | ||
| u32 res_5c; | ||
|
|
||
| u32 irq_status; | ||
| u32 irq_ack; | ||
| u32 res_68; | ||
| u32 res_6c; | ||
|
|
||
| u32 device_status; | ||
| u32 res_74; | ||
| u32 res_78; | ||
| u32 res_7c; | ||
|
|
||
| u32 queue_desc_lo; | ||
| u32 queue_desc_hi; | ||
| u32 res_88; | ||
| u32 res_8c; | ||
|
|
||
| u32 queue_driver_lo; | ||
| u32 queue_driver_hi; | ||
| u32 res_98; | ||
| u32 res_9c; | ||
|
|
||
| u32 queue_device_lo; | ||
| u32 queue_device_hi; | ||
| u32 res_a8; | ||
| u32 shm_sel; | ||
|
|
||
| u32 shmem_len_lo; | ||
| u32 shmem_len_hi; | ||
| u32 shmem_base_lo; | ||
| u32 shmem_base_hi; | ||
|
|
||
| u32 res_c0_f7[14]; | ||
|
|
||
| u32 res_f8; | ||
| u32 config_generation; | ||
| } virtio_mmio_cfg; | ||
|
|
||
| void virtio_mmio_setup_acpi(void); | ||
| void virtio_mmio_setup_one(u64 mmio); | ||
| void vp_init_mmio(struct vp_device *vp, void *mmio); | ||
|
|
||
| #endif /* _VIRTIO_MMIO_H */ |