1/*  *********************************************************************
2    *  Broadcom Common Firmware Environment (CFE)
3    *
4    *  X86 simulator sparse memory		File: X86MEM.C
5    *
6    *  This module implements X86 memory for the X86 emulator
7    *  used by the BIOS simulator.  To avoid allocating the
8    *  entire 1MB of PC's addressable memory, this is a "sparse"
9    *  memory model, allocating chunks of storage as needed.
10    *  VGA BIOSes seem to do all sorts of bizarre things to memory
11    *  so this helps reduce the total amount we need to allocate
12    *  significantly.
13    *
14    *  In addition, this module lets the simulator "hook"
15    *  ranges of memory to be handled by a callback
16    *  routine.  This is used so that we can redirect
17    *  accesses to VGA memory space to the PCI bus handler.
18    *
19    *  Author:  Mitch Lichtenberg
20    *
21    *********************************************************************
22    *
23    *  Copyright 2000,2001,2002,2003
24    *  Broadcom Corporation. All rights reserved.
25    *
26    *  This software is furnished under license and may be used and
27    *  copied only in accordance with the following terms and
28    *  conditions.  Subject to these conditions, you may download,
29    *  copy, install, use, modify and distribute modified or unmodified
30    *  copies of this software in source and/or binary form.  No title
31    *  or ownership is transferred hereby.
32    *
33    *  1) Any source code used, modified or distributed must reproduce
34    *     and retain this copyright notice and list of conditions
35    *     as they appear in the source file.
36    *
37    *  2) No right is granted to use any trade name, trademark, or
38    *     logo of Broadcom Corporation.  The "Broadcom Corporation"
39    *     name may not be used to endorse or promote products derived
40    *     from this software without the prior written permission of
41    *     Broadcom Corporation.
42    *
43    *  3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
44    *     IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
45    *     WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
46    *     PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
47    *     SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
48    *     PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT,
49    *     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
50    *     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
51    *     GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
52    *     BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
53    *     OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
54    *     TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF
55    *     THE POSSIBILITY OF SUCH DAMAGE.
56    ********************************************************************* */
57
58
59#include "lib_types.h"
60#include "lib_string.h"
61#include "lib_malloc.h"
62#include "lib_printf.h"
63#include "x86mem.h"
64
65/*  *********************************************************************
66    *  Macros
67    ********************************************************************* */
68
69#define BSWAP_SHORT(s) ((((s) >> 8) & 0xFF) | (((s)&0xFF) << 8))
70#define BSWAP_LONG(s) ((((s) & 0xFF000000) >> 24) | \
71                       (((s) & 0x00FF0000) >> 8) | \
72                       (((s) & 0x0000FF00) << 8) | \
73                       (((s) & 0x000000FF) << 24))
74
75
76/*  *********************************************************************
77    *  X86MEM_INIT()
78    *
79    *  Initialize an X86mem object
80    *
81    *  Input parameters:
82    *  	   mem - X86mem object
83    *
84    *  Return value:
85    *  	   nothing
86    ********************************************************************* */
87
88void x86mem_init(x86mem_t *mem)
89{
90    memset(mem,0,sizeof(mem));
91}
92
93/*  *********************************************************************
94    *  X86MEM_UNINIT(mem)
95    *
96    *  Uninitialize an  X86mem object, freeing any storage
97    *  associated with it.
98    *
99    *  Input parameters:
100    *  	   mem - x86mem object
101    *
102    *  Return value:
103    *  	   nothing
104    ********************************************************************* */
105
106void x86mem_uninit(x86mem_t *mem)
107{
108    int idx;
109
110    for (idx = 0; idx < X86MEM_CHUNKS; idx++) {
111	if (mem->data[idx]) {
112	    KFREE(mem->data[idx]);
113	    mem->data[idx] = NULL;
114	    }
115	}
116}
117
118/*  *********************************************************************
119    *  X86MEM_READB(mem,addr)
120    *
121    *  Read a byte of memory from the X86mem object.
122    *
123    *  Input parameters:
124    *  	   mem - x86mem object
125    *  	   addr - address of byte to read
126    *
127    *  Return value:
128    *  	   byte read
129    ********************************************************************* */
130
131uint8_t x86mem_readb(x86mem_t *mem,uint32_t addr)
132{
133    uint8_t *p;
134
135    if (mem->read[X86MEM_REGION(addr)]) {
136	return (uint8_t) (*(mem->read[X86MEM_REGION(addr)]))(mem,addr,1);
137	}
138
139    p = (mem->data[X86MEM_REGION(addr)]);
140
141    if (p) {
142	return *(p + X86MEM_OFFSET(addr));
143	}
144    else {
145	return 0;
146	}
147}
148
149/*  *********************************************************************
150    *  X86MEM_READW(mem,addr)
151    *
152    *  Read a 16-bit word of memory from the X86mem object.
153    *
154    *  Input parameters:
155    *  	   mem - x86mem object
156    *  	   addr - address of word to read
157    *
158    *  Return value:
159    *  	   word read
160    ********************************************************************* */
161
162uint16_t x86mem_readw(x86mem_t *mem,uint32_t addr)
163{
164    uint8_t *p;
165    uint16_t ret;
166
167    if (mem->read[X86MEM_REGION(addr)]) {
168	return (uint8_t) (*(mem->read[X86MEM_REGION(addr)]))(mem,addr,2);
169	}
170
171    p = (mem->data[X86MEM_REGION(addr)]);
172    if (!p) return 0;
173
174    if ((addr & 1) || (X86MEM_OFFSET(addr) == X86MEM_CHUNKSIZE-1)) {
175
176	ret = ((uint16_t) x86mem_readb(mem,addr+0)) |
177	    (((uint16_t) x86mem_readb(mem,addr+1)) << 8);
178	return ret;
179	}
180    else {
181	ret =  *((uint16_t *) (p+X86MEM_OFFSET(addr)));
182#ifdef __MIPSEB
183	ret = BSWAP_SHORT(ret);
184#endif
185	}
186
187    return ret;
188}
189
190/*  *********************************************************************
191    *  X86MEM_READL(mem,addr)
192    *
193    *  Read a 32-bit dword of memory from the X86mem object.
194    *
195    *  Input parameters:
196    *  	   mem - x86mem object
197    *  	   addr - address of dword to read
198    *
199    *  Return value:
200    *  	   dword read
201    ********************************************************************* */
202
203uint32_t x86mem_readl(x86mem_t *mem,uint32_t addr)
204{
205    uint8_t *p;
206    uint32_t ret;
207
208    if (mem->read[X86MEM_REGION(addr)]) {
209	return (uint8_t) (*(mem->read[X86MEM_REGION(addr)]))(mem,addr,4);
210	}
211
212    p = (mem->data[X86MEM_REGION(addr)]);
213    if (!p) return 0;
214
215    if ((addr & 3) || (X86MEM_OFFSET(addr) >= X86MEM_CHUNKSIZE-3)) {
216	ret = ((uint32_t) x86mem_readb(mem,addr+0)) |
217	    (((uint32_t) x86mem_readb(mem,addr+1)) << 8) |
218	    (((uint32_t) x86mem_readb(mem,addr+2)) << 16) |
219	    (((uint32_t) x86mem_readb(mem,addr+3)) << 24);
220	}
221    else {
222	ret = *((uint32_t *) (p+X86MEM_OFFSET(addr)));
223#ifdef __MIPSEB
224	ret = BSWAP_LONG(ret);
225#endif
226	}
227
228    return ret;
229}
230
231/*  *********************************************************************
232    *  X86MEM_WRITEB(mem,addr,data)
233    *
234    *  Write a byte to the X86mem object
235    *
236    *  Input parameters:
237    *  	   mem - x86mem object
238    *  	   addr - address of byte to write
239    *      data - data to write
240    *
241    *  Return value:
242    *  	   nothing
243    ********************************************************************* */
244void x86mem_writeb(x86mem_t *mem,uint32_t addr,uint8_t data)
245{
246    uint8_t *p;
247
248    if (mem->write[X86MEM_REGION(addr)]) {
249	(*(mem->write[X86MEM_REGION(addr)]))(mem,addr,data,1);
250	return;
251	}
252
253    p = (mem->data[X86MEM_REGION(addr)]);
254
255    if (p) {
256        *(p + X86MEM_OFFSET(addr)) = data;
257	}
258    else {
259	p = mem->data[X86MEM_REGION(addr)] = KMALLOC(X86MEM_CHUNKSIZE,sizeof(uint32_t));
260	if (p) {
261	    memset(p,0,X86MEM_CHUNKSIZE);
262	    *(p + X86MEM_OFFSET(addr)) = data;
263	    }
264	}
265}
266
267/*  *********************************************************************
268    *  X86MEM_WRITEW(mem,addr,data)
269    *
270    *  Write a 16-bit word to the X86mem object
271    *
272    *  Input parameters:
273    *  	   mem - x86mem object
274    *  	   addr - address of word to write
275    *      data - data to write
276    *
277    *  Return value:
278    *  	   nothing
279    ********************************************************************* */
280void x86mem_writew(x86mem_t *mem,uint32_t addr,uint16_t data)
281{
282    uint8_t *p;
283
284    if (mem->write[X86MEM_REGION(addr)]) {
285	(*(mem->write[X86MEM_REGION(addr)]))(mem,addr,data,2);
286	return;
287	}
288
289    p = (mem->data[X86MEM_REGION(addr)]);
290
291    if (!p || (addr & 1) || (X86MEM_OFFSET(addr) == X86MEM_CHUNKSIZE-1)) {
292	x86mem_writeb(mem,addr+0,(data & 0xFF));
293	x86mem_writeb(mem,addr+1,((data >> 8) & 0xFF));
294	}
295    else {
296#ifdef __MIPSEB
297	data = BSWAP_SHORT(data);
298#endif
299	*((uint16_t *) (p+X86MEM_OFFSET(addr))) = data;
300	}
301}
302
303/*  *********************************************************************
304    *  X86MEM_WRITEL(mem,addr,data)
305    *
306    *  Write a 32-bit dword to the X86mem object
307    *
308    *  Input parameters:
309    *  	   mem - x86mem object
310    *  	   addr - address of dword to write
311    *      data - data to write
312    *
313    *  Return value:
314    *  	   nothing
315    ********************************************************************* */
316void x86mem_writel(x86mem_t *mem,uint32_t addr,uint32_t data)
317{
318    uint8_t *p;
319
320    if (mem->write[X86MEM_REGION(addr)]) {
321	(*(mem->write[X86MEM_REGION(addr)]))(mem,addr,data,4);
322	return;
323	}
324
325    p = (mem->data[X86MEM_REGION(addr)]);
326
327    if (!p || (addr & 3) || (X86MEM_OFFSET(addr) >= X86MEM_CHUNKSIZE-3)) {
328	x86mem_writeb(mem,addr+0,(data & 0xFF));
329	x86mem_writeb(mem,addr+1,((data >> 8) & 0xFF));
330	x86mem_writeb(mem,addr+2,((data >> 16) & 0xFF));
331	x86mem_writeb(mem,addr+3,((data >> 24) & 0xFF));
332	}
333    else {
334#ifdef __MIPSEB
335	data = BSWAP_LONG(data);
336#endif
337	*((uint32_t *) (p+X86MEM_OFFSET(addr))) = data;
338	}
339}
340
341/*  *********************************************************************
342    *  X86MEM_MEMCPY(mem,dest,src,cnt)
343    *
344    *  memcpy data into the X86mem object
345    *
346    *  Input parameters:
347    *  	   mem - x86mem object
348    *  	   destaddr - destination x86mem address
349    *  	   src - source local address
350    *  	   cnt - number of bytes to copy
351    *
352    *  Return value:
353    *  	   nothing
354    ********************************************************************* */
355
356void x86mem_memcpy(x86mem_t *mem,uint32_t destaddr,uint8_t *src,int count)
357{
358    while (count) {
359	x86mem_writeb(mem,destaddr,*src);
360	destaddr++;
361	src++;
362	count--;
363	}
364}
365
366
367/*  *********************************************************************
368    *  X86MEM_HOOK(mem,addr,readf,writef)
369    *
370    *  Establish a hook for a block of simulated memory
371    *
372    *  Input parameters:
373    *  	   mem - x86mem object
374    *  	   addr - address in memory, should be aligned on a "chunk"
375    *  	          boundary.
376    *  	   readf - function to call on READ accesses
377    *  	   writef - function to call on WRITE accesses
378    *
379    *  Return value:
380    *  	   nothing
381    ********************************************************************* */
382
383void x86mem_hook(x86mem_t *mem,uint32_t chunkaddr,
384		 uint32_t (*readf)(x86mem_t *mem,uint32_t addr,int size),
385		 void (*writef)(x86mem_t *mem,uint32_t addr,uint32_t val,int size))
386{
387    if (mem->data[X86MEM_REGION(chunkaddr)]) {
388	KFREE(mem->data[X86MEM_REGION(chunkaddr)]);
389	mem->data[X86MEM_REGION(chunkaddr)] = NULL;
390	}
391    mem->read[X86MEM_REGION(chunkaddr)] = readf;
392    mem->write[X86MEM_REGION(chunkaddr)] = writef;
393}
394
395/*  *********************************************************************
396    *  X86MEM_HOOK_RANGE(mem,addr,size,readf,writef)
397    *
398    *  Establish a hook for a block of simulated memory
399    *
400    *  Input parameters:
401    *  	   mem - x86mem object
402    *  	   addr - address in memory, should be aligned on a "chunk"
403    *  	          boundary.
404    *      size - size of region to hook.  Should be a multiple
405    *             of the chunk size
406    *  	   readf - function to call on READ accesses
407    *  	   writef - function to call on WRITE accesses
408    *
409    *  Return value:
410    *  	   nothing
411    ********************************************************************* */
412
413void x86mem_hook_range(x86mem_t *mem,uint32_t chunkaddr,int size,
414		 uint32_t (*readf)(x86mem_t *mem,uint32_t addr,int size),
415		 void (*writef)(x86mem_t *mem,uint32_t addr,uint32_t val,int size))
416{
417    while (size > 0) {
418	x86mem_hook(mem,chunkaddr,readf,writef);
419	size -= X86MEM_CHUNKSIZE;
420	chunkaddr += X86MEM_CHUNKSIZE;
421	}
422}
423
424