11cf4323eSThomas Huth /*
21cf4323eSThomas Huth * libqos fw_cfg support
31cf4323eSThomas Huth *
41cf4323eSThomas Huth * Copyright IBM, Corp. 2012-2013
51cf4323eSThomas Huth * Copyright (C) 2013 Red Hat Inc.
61cf4323eSThomas Huth *
71cf4323eSThomas Huth * Authors:
81cf4323eSThomas Huth * Anthony Liguori <aliguori@us.ibm.com>
91cf4323eSThomas Huth * Markus Armbruster <armbru@redhat.com>
101cf4323eSThomas Huth *
111cf4323eSThomas Huth * This work is licensed under the terms of the GNU GPL, version 2 or later.
121cf4323eSThomas Huth * See the COPYING file in the top-level directory.
131cf4323eSThomas Huth */
141cf4323eSThomas Huth
151cf4323eSThomas Huth #include "qemu/osdep.h"
16a2ce7dbdSPaolo Bonzini #include "fw_cfg.h"
17*907b5105SMarc-André Lureau #include "../libqtest.h"
181cf4323eSThomas Huth #include "qemu/bswap.h"
191cf4323eSThomas Huth #include "hw/nvram/fw_cfg.h"
201cf4323eSThomas Huth
qfw_cfg_select(QFWCFG * fw_cfg,uint16_t key)211cf4323eSThomas Huth void qfw_cfg_select(QFWCFG *fw_cfg, uint16_t key)
221cf4323eSThomas Huth {
231cf4323eSThomas Huth fw_cfg->select(fw_cfg, key);
241cf4323eSThomas Huth }
251cf4323eSThomas Huth
qfw_cfg_read_data(QFWCFG * fw_cfg,void * data,size_t len)261cf4323eSThomas Huth void qfw_cfg_read_data(QFWCFG *fw_cfg, void *data, size_t len)
271cf4323eSThomas Huth {
281cf4323eSThomas Huth fw_cfg->read(fw_cfg, data, len);
291cf4323eSThomas Huth }
301cf4323eSThomas Huth
qfw_cfg_get(QFWCFG * fw_cfg,uint16_t key,void * data,size_t len)311cf4323eSThomas Huth void qfw_cfg_get(QFWCFG *fw_cfg, uint16_t key, void *data, size_t len)
321cf4323eSThomas Huth {
331cf4323eSThomas Huth qfw_cfg_select(fw_cfg, key);
341cf4323eSThomas Huth qfw_cfg_read_data(fw_cfg, data, len);
351cf4323eSThomas Huth }
361cf4323eSThomas Huth
qfw_cfg_get_u16(QFWCFG * fw_cfg,uint16_t key)371cf4323eSThomas Huth uint16_t qfw_cfg_get_u16(QFWCFG *fw_cfg, uint16_t key)
381cf4323eSThomas Huth {
391cf4323eSThomas Huth uint16_t value;
401cf4323eSThomas Huth qfw_cfg_get(fw_cfg, key, &value, sizeof(value));
411cf4323eSThomas Huth return le16_to_cpu(value);
421cf4323eSThomas Huth }
431cf4323eSThomas Huth
qfw_cfg_get_u32(QFWCFG * fw_cfg,uint16_t key)441cf4323eSThomas Huth uint32_t qfw_cfg_get_u32(QFWCFG *fw_cfg, uint16_t key)
451cf4323eSThomas Huth {
461cf4323eSThomas Huth uint32_t value;
471cf4323eSThomas Huth qfw_cfg_get(fw_cfg, key, &value, sizeof(value));
481cf4323eSThomas Huth return le32_to_cpu(value);
491cf4323eSThomas Huth }
501cf4323eSThomas Huth
qfw_cfg_get_u64(QFWCFG * fw_cfg,uint16_t key)511cf4323eSThomas Huth uint64_t qfw_cfg_get_u64(QFWCFG *fw_cfg, uint16_t key)
521cf4323eSThomas Huth {
531cf4323eSThomas Huth uint64_t value;
541cf4323eSThomas Huth qfw_cfg_get(fw_cfg, key, &value, sizeof(value));
551cf4323eSThomas Huth return le64_to_cpu(value);
561cf4323eSThomas Huth }
571cf4323eSThomas Huth
mm_fw_cfg_select(QFWCFG * fw_cfg,uint16_t key)581cf4323eSThomas Huth static void mm_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key)
591cf4323eSThomas Huth {
601cf4323eSThomas Huth qtest_writew(fw_cfg->qts, fw_cfg->base, key);
611cf4323eSThomas Huth }
621cf4323eSThomas Huth
631cf4323eSThomas Huth /*
641cf4323eSThomas Huth * The caller need check the return value. When the return value is
651cf4323eSThomas Huth * nonzero, it means that some bytes have been transferred.
661cf4323eSThomas Huth *
671cf4323eSThomas Huth * If the fw_cfg file in question is smaller than the allocated & passed-in
681cf4323eSThomas Huth * buffer, then the buffer has been populated only in part.
691cf4323eSThomas Huth *
701cf4323eSThomas Huth * If the fw_cfg file in question is larger than the passed-in
711cf4323eSThomas Huth * buffer, then the return value explains how much room would have been
721cf4323eSThomas Huth * necessary in total. And, while the caller's buffer has been fully
731cf4323eSThomas Huth * populated, it has received only a starting slice of the fw_cfg file.
741cf4323eSThomas Huth */
qfw_cfg_get_file(QFWCFG * fw_cfg,const char * filename,void * data,size_t buflen)751cf4323eSThomas Huth size_t qfw_cfg_get_file(QFWCFG *fw_cfg, const char *filename,
761cf4323eSThomas Huth void *data, size_t buflen)
771cf4323eSThomas Huth {
781cf4323eSThomas Huth uint32_t count;
791cf4323eSThomas Huth uint32_t i;
801cf4323eSThomas Huth unsigned char *filesbuf = NULL;
811cf4323eSThomas Huth size_t dsize;
821cf4323eSThomas Huth FWCfgFile *pdir_entry;
831cf4323eSThomas Huth size_t filesize = 0;
841cf4323eSThomas Huth
851cf4323eSThomas Huth qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, &count, sizeof(count));
861cf4323eSThomas Huth count = be32_to_cpu(count);
871cf4323eSThomas Huth dsize = sizeof(uint32_t) + count * sizeof(struct fw_cfg_file);
881cf4323eSThomas Huth filesbuf = g_malloc(dsize);
891cf4323eSThomas Huth qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, filesbuf, dsize);
901cf4323eSThomas Huth pdir_entry = (FWCfgFile *)(filesbuf + sizeof(uint32_t));
911cf4323eSThomas Huth for (i = 0; i < count; ++i, ++pdir_entry) {
921cf4323eSThomas Huth if (!strcmp(pdir_entry->name, filename)) {
931cf4323eSThomas Huth uint32_t len = be32_to_cpu(pdir_entry->size);
941cf4323eSThomas Huth uint16_t sel = be16_to_cpu(pdir_entry->select);
951cf4323eSThomas Huth filesize = len;
961cf4323eSThomas Huth if (len > buflen) {
971cf4323eSThomas Huth len = buflen;
981cf4323eSThomas Huth }
991cf4323eSThomas Huth qfw_cfg_get(fw_cfg, sel, data, len);
1001cf4323eSThomas Huth break;
1011cf4323eSThomas Huth }
1021cf4323eSThomas Huth }
1031cf4323eSThomas Huth g_free(filesbuf);
1041cf4323eSThomas Huth return filesize;
1051cf4323eSThomas Huth }
1061cf4323eSThomas Huth
mm_fw_cfg_read(QFWCFG * fw_cfg,void * data,size_t len)1071cf4323eSThomas Huth static void mm_fw_cfg_read(QFWCFG *fw_cfg, void *data, size_t len)
1081cf4323eSThomas Huth {
1091cf4323eSThomas Huth uint8_t *ptr = data;
1101cf4323eSThomas Huth int i;
1111cf4323eSThomas Huth
1121cf4323eSThomas Huth for (i = 0; i < len; i++) {
1131cf4323eSThomas Huth ptr[i] = qtest_readb(fw_cfg->qts, fw_cfg->base + 2);
1141cf4323eSThomas Huth }
1151cf4323eSThomas Huth }
1161cf4323eSThomas Huth
mm_fw_cfg_init(QTestState * qts,uint64_t base)1171cf4323eSThomas Huth QFWCFG *mm_fw_cfg_init(QTestState *qts, uint64_t base)
1181cf4323eSThomas Huth {
1191cf4323eSThomas Huth QFWCFG *fw_cfg = g_malloc0(sizeof(*fw_cfg));
1201cf4323eSThomas Huth
1211cf4323eSThomas Huth fw_cfg->base = base;
1221cf4323eSThomas Huth fw_cfg->qts = qts;
1231cf4323eSThomas Huth fw_cfg->select = mm_fw_cfg_select;
1241cf4323eSThomas Huth fw_cfg->read = mm_fw_cfg_read;
1251cf4323eSThomas Huth
1261cf4323eSThomas Huth return fw_cfg;
1271cf4323eSThomas Huth }
1281cf4323eSThomas Huth
mm_fw_cfg_uninit(QFWCFG * fw_cfg)1291cf4323eSThomas Huth void mm_fw_cfg_uninit(QFWCFG *fw_cfg)
1301cf4323eSThomas Huth {
1311cf4323eSThomas Huth g_free(fw_cfg);
1321cf4323eSThomas Huth }
1331cf4323eSThomas Huth
io_fw_cfg_select(QFWCFG * fw_cfg,uint16_t key)1341cf4323eSThomas Huth static void io_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key)
1351cf4323eSThomas Huth {
1361cf4323eSThomas Huth qtest_outw(fw_cfg->qts, fw_cfg->base, key);
1371cf4323eSThomas Huth }
1381cf4323eSThomas Huth
io_fw_cfg_read(QFWCFG * fw_cfg,void * data,size_t len)1391cf4323eSThomas Huth static void io_fw_cfg_read(QFWCFG *fw_cfg, void *data, size_t len)
1401cf4323eSThomas Huth {
1411cf4323eSThomas Huth uint8_t *ptr = data;
1421cf4323eSThomas Huth int i;
1431cf4323eSThomas Huth
1441cf4323eSThomas Huth for (i = 0; i < len; i++) {
1451cf4323eSThomas Huth ptr[i] = qtest_inb(fw_cfg->qts, fw_cfg->base + 1);
1461cf4323eSThomas Huth }
1471cf4323eSThomas Huth }
1481cf4323eSThomas Huth
io_fw_cfg_init(QTestState * qts,uint16_t base)1491cf4323eSThomas Huth QFWCFG *io_fw_cfg_init(QTestState *qts, uint16_t base)
1501cf4323eSThomas Huth {
1511cf4323eSThomas Huth QFWCFG *fw_cfg = g_malloc0(sizeof(*fw_cfg));
1521cf4323eSThomas Huth
1531cf4323eSThomas Huth fw_cfg->base = base;
1541cf4323eSThomas Huth fw_cfg->qts = qts;
1551cf4323eSThomas Huth fw_cfg->select = io_fw_cfg_select;
1561cf4323eSThomas Huth fw_cfg->read = io_fw_cfg_read;
1571cf4323eSThomas Huth
1581cf4323eSThomas Huth return fw_cfg;
1591cf4323eSThomas Huth }
1601cf4323eSThomas Huth
io_fw_cfg_uninit(QFWCFG * fw_cfg)1611cf4323eSThomas Huth void io_fw_cfg_uninit(QFWCFG *fw_cfg)
1621cf4323eSThomas Huth {
1631cf4323eSThomas Huth g_free(fw_cfg);
1641cf4323eSThomas Huth }
165