]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * libqos fw_cfg support | |
3 | * | |
4 | * Copyright IBM, Corp. 2012-2013 | |
5 | * Copyright (C) 2013 Red Hat Inc. | |
6 | * | |
7 | * Authors: | |
8 | * Anthony Liguori <aliguori@us.ibm.com> | |
9 | * Markus Armbruster <armbru@redhat.com> | |
10 | * | |
11 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
12 | * See the COPYING file in the top-level directory. | |
13 | */ | |
14 | ||
15 | #include "qemu/osdep.h" | |
16 | #include "fw_cfg.h" | |
17 | #include "libqtest.h" | |
18 | #include "qemu/bswap.h" | |
19 | #include "hw/nvram/fw_cfg.h" | |
20 | ||
21 | void qfw_cfg_select(QFWCFG *fw_cfg, uint16_t key) | |
22 | { | |
23 | fw_cfg->select(fw_cfg, key); | |
24 | } | |
25 | ||
26 | void qfw_cfg_read_data(QFWCFG *fw_cfg, void *data, size_t len) | |
27 | { | |
28 | fw_cfg->read(fw_cfg, data, len); | |
29 | } | |
30 | ||
31 | void qfw_cfg_get(QFWCFG *fw_cfg, uint16_t key, void *data, size_t len) | |
32 | { | |
33 | qfw_cfg_select(fw_cfg, key); | |
34 | qfw_cfg_read_data(fw_cfg, data, len); | |
35 | } | |
36 | ||
37 | uint16_t qfw_cfg_get_u16(QFWCFG *fw_cfg, uint16_t key) | |
38 | { | |
39 | uint16_t value; | |
40 | qfw_cfg_get(fw_cfg, key, &value, sizeof(value)); | |
41 | return le16_to_cpu(value); | |
42 | } | |
43 | ||
44 | uint32_t qfw_cfg_get_u32(QFWCFG *fw_cfg, uint16_t key) | |
45 | { | |
46 | uint32_t value; | |
47 | qfw_cfg_get(fw_cfg, key, &value, sizeof(value)); | |
48 | return le32_to_cpu(value); | |
49 | } | |
50 | ||
51 | uint64_t qfw_cfg_get_u64(QFWCFG *fw_cfg, uint16_t key) | |
52 | { | |
53 | uint64_t value; | |
54 | qfw_cfg_get(fw_cfg, key, &value, sizeof(value)); | |
55 | return le64_to_cpu(value); | |
56 | } | |
57 | ||
58 | static void mm_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key) | |
59 | { | |
60 | qtest_writew(fw_cfg->qts, fw_cfg->base, key); | |
61 | } | |
62 | ||
63 | /* | |
64 | * The caller need check the return value. When the return value is | |
65 | * nonzero, it means that some bytes have been transferred. | |
66 | * | |
67 | * If the fw_cfg file in question is smaller than the allocated & passed-in | |
68 | * buffer, then the buffer has been populated only in part. | |
69 | * | |
70 | * If the fw_cfg file in question is larger than the passed-in | |
71 | * buffer, then the return value explains how much room would have been | |
72 | * necessary in total. And, while the caller's buffer has been fully | |
73 | * populated, it has received only a starting slice of the fw_cfg file. | |
74 | */ | |
75 | size_t qfw_cfg_get_file(QFWCFG *fw_cfg, const char *filename, | |
76 | void *data, size_t buflen) | |
77 | { | |
78 | uint32_t count; | |
79 | uint32_t i; | |
80 | unsigned char *filesbuf = NULL; | |
81 | size_t dsize; | |
82 | FWCfgFile *pdir_entry; | |
83 | size_t filesize = 0; | |
84 | ||
85 | qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, &count, sizeof(count)); | |
86 | count = be32_to_cpu(count); | |
87 | dsize = sizeof(uint32_t) + count * sizeof(struct fw_cfg_file); | |
88 | filesbuf = g_malloc(dsize); | |
89 | qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, filesbuf, dsize); | |
90 | pdir_entry = (FWCfgFile *)(filesbuf + sizeof(uint32_t)); | |
91 | for (i = 0; i < count; ++i, ++pdir_entry) { | |
92 | if (!strcmp(pdir_entry->name, filename)) { | |
93 | uint32_t len = be32_to_cpu(pdir_entry->size); | |
94 | uint16_t sel = be16_to_cpu(pdir_entry->select); | |
95 | filesize = len; | |
96 | if (len > buflen) { | |
97 | len = buflen; | |
98 | } | |
99 | qfw_cfg_get(fw_cfg, sel, data, len); | |
100 | break; | |
101 | } | |
102 | } | |
103 | g_free(filesbuf); | |
104 | return filesize; | |
105 | } | |
106 | ||
107 | static void mm_fw_cfg_read(QFWCFG *fw_cfg, void *data, size_t len) | |
108 | { | |
109 | uint8_t *ptr = data; | |
110 | int i; | |
111 | ||
112 | for (i = 0; i < len; i++) { | |
113 | ptr[i] = qtest_readb(fw_cfg->qts, fw_cfg->base + 2); | |
114 | } | |
115 | } | |
116 | ||
117 | QFWCFG *mm_fw_cfg_init(QTestState *qts, uint64_t base) | |
118 | { | |
119 | QFWCFG *fw_cfg = g_malloc0(sizeof(*fw_cfg)); | |
120 | ||
121 | fw_cfg->base = base; | |
122 | fw_cfg->qts = qts; | |
123 | fw_cfg->select = mm_fw_cfg_select; | |
124 | fw_cfg->read = mm_fw_cfg_read; | |
125 | ||
126 | return fw_cfg; | |
127 | } | |
128 | ||
129 | void mm_fw_cfg_uninit(QFWCFG *fw_cfg) | |
130 | { | |
131 | g_free(fw_cfg); | |
132 | } | |
133 | ||
134 | static void io_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key) | |
135 | { | |
136 | qtest_outw(fw_cfg->qts, fw_cfg->base, key); | |
137 | } | |
138 | ||
139 | static void io_fw_cfg_read(QFWCFG *fw_cfg, void *data, size_t len) | |
140 | { | |
141 | uint8_t *ptr = data; | |
142 | int i; | |
143 | ||
144 | for (i = 0; i < len; i++) { | |
145 | ptr[i] = qtest_inb(fw_cfg->qts, fw_cfg->base + 1); | |
146 | } | |
147 | } | |
148 | ||
149 | QFWCFG *io_fw_cfg_init(QTestState *qts, uint16_t base) | |
150 | { | |
151 | QFWCFG *fw_cfg = g_malloc0(sizeof(*fw_cfg)); | |
152 | ||
153 | fw_cfg->base = base; | |
154 | fw_cfg->qts = qts; | |
155 | fw_cfg->select = io_fw_cfg_select; | |
156 | fw_cfg->read = io_fw_cfg_read; | |
157 | ||
158 | return fw_cfg; | |
159 | } | |
160 | ||
161 | void io_fw_cfg_uninit(QFWCFG *fw_cfg) | |
162 | { | |
163 | g_free(fw_cfg); | |
164 | } |