summaryrefslogtreecommitdiffstats
path: root/qemu/roms/openbios/arch/ppc/ofmem.c
blob: c9b066ed6d4249f09e2aadbd30cd60c1302cbe3f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
/*
 *   Creation Date: <1999/11/07 19:02:11 samuel>
 *   Time-stamp: <2004/01/07 19:42:36 samuel>
 *
 *	<ofmem.c>
 *
 *	OF Memory manager
 *
 *   Copyright (C) 1999-2004 Samuel Rydh (samuel@ibrium.se)
 *   Copyright (C) 2004 Stefan Reinauer
 *
 *   This program is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU General Public License
 *   as published by the Free Software Foundation
 *
 */

/* TODO: Clean up MOLisms in a decent way */

#include "config.h"
#include "libopenbios/bindings.h"
#include "libc/string.h"
#include "libopenbios/ofmem.h"
#include "kernel.h"
#ifdef I_WANT_MOLISMS
#include "mol/prom.h"
#include "mol/mol.h"
#endif
#include "mmutypes.h"
#include "asm/processor.h"
#ifdef I_WANT_MOLISMS
#include "osi_calls.h"
#endif

#define BIT(n)		(1U<<(31-(n)))

/* called from assembly */
extern void	dsi_exception( void );
extern void	isi_exception( void );
extern void	setup_mmu( unsigned long code_base, unsigned long code_size, unsigned long ramsize );

/****************************************************************
 * Memory usage (before of_quiesce is called)
 *
 *			Physical
 *
 *	0x00000000	Exception vectors
 *	0x00004000	Free space
 *	0x01e00000	Open Firmware (us)
 *	0x01f00000	OF allocations
 *	0x01ff0000	PTE Hash
 *	0x02000000-	Free space
 *
 * Allocations grow downwards from 0x01e00000
 *
 ****************************************************************/

#define HASH_SIZE		(2 << 15)
#define SEGR_BASE		0x400		/* segment number range to use */

#define FREE_BASE_1		0x00004000
#define OF_CODE_START		0x01e00000
/* #define OF_MALLOC_BASE	0x01f00000 */
extern char _end[];
#define OF_MALLOC_BASE		_end

#define HASH_BASE		(0x02000000 - HASH_SIZE)
#define FREE_BASE_2		0x02000000

#define RAMSIZE			0x02000000	/* XXXXXXXXXXXXXXXXXXX FIXME XXXXXXXXXXXXXXX */

static ofmem_t s_ofmem;

#define IO_BASE			0x80000000
#define OFMEM (&s_ofmem)

static inline unsigned long
get_hash_base( void )
{
	return HASH_BASE;
}

static inline unsigned long
get_hash_size( void )
{
	return HASH_SIZE;
}

static ucell get_heap_top( void )
{
	return HASH_BASE;
}

static inline size_t ALIGN_SIZE(size_t x, size_t a)
{
    return (x + a - 1) & ~(a-1);
}

ofmem_t* ofmem_arch_get_private(void)
{
	return OFMEM;
}

void* ofmem_arch_get_malloc_base(void)
{
	return OF_MALLOC_BASE;
}

ucell ofmem_arch_get_heap_top(void)
{
	return get_heap_top();
}

ucell ofmem_arch_get_virt_top(void)
{
	return IO_BASE;
}

void ofmem_arch_unmap_pages(ucell virt, ucell size)
{
	/* kill page mappings in provided range */
}

void ofmem_arch_map_pages(ucell phys, ucell virt, ucell size, ucell mode)
{
	/* none yet */
}

/************************************************************************/
/*	OF private allocations						*/
/************************************************************************/

void *
malloc( int size )
{
	return ofmem_malloc(size);
}

void
free( void *ptr )
{
	return ofmem_free(ptr);
}

void *
realloc( void *ptr, size_t size )
{
	return ofmem_realloc(ptr, size);
}


/************************************************************************/
/*	misc								*/
/************************************************************************/

ucell ofmem_arch_default_translation_mode( ucell phys )
{
	/* XXX: Guard bit not set as it should! */
	if( phys < IO_BASE || phys >= 0xffc00000 )
		return 0x02;	/*0xa*/	/* wim GxPp */
	return 0x6a;		/* WIm GxPp, I/O */
}


/************************************************************************/
/*	page fault handler						*/
/************************************************************************/

static ucell
ea_to_phys( ucell ea, ucell *mode )
{
	ucell phys;

	/* hardcode our translation needs */
	if( ea >= OF_CODE_START && ea < FREE_BASE_2 ) {
		*mode = ofmem_arch_default_translation_mode( ea );
		return ea;
	}

	phys = ofmem_translate(ea, mode);
	if( phys == (ucell)-1 ) {
#ifdef I_WANT_MOLISMS
		if( ea != 0x80816c00 )
			printk("ea_to_phys: no translation for %08lx, using 1-1\n", ea );
#endif
		phys = ea;
		*mode = ofmem_arch_default_translation_mode( phys );

#ifdef I_WANT_MOLISMS
		forth_segv_handler( (char*)ea );
		OSI_Debugger(1);
#endif
		/* print_virt_range(); */
		/* print_phys_range(); */
		/* print_trans(); */
	}
	return phys;
}

static void
hash_page( ucell ea, ucell phys, ucell mode )
{
	static int next_grab_slot=0;
	unsigned long *upte, cmp, hash1;
	int i, vsid, found;
	mPTE_t *pp;

	vsid = (ea>>28) + SEGR_BASE;
	cmp = BIT(0) | (vsid << 7) | ((ea & 0x0fffffff) >> 22);

	hash1 = vsid;
	hash1 ^= (ea >> 12) & 0xffff;
	hash1 &= (get_hash_size() - 1) >> 6;

	pp = (mPTE_t*)(get_hash_base() + (hash1 << 6));
	upte = (unsigned long*)pp;

	/* replace old translation */
	for( found=0, i=0; !found && i<8; i++ )
		if( cmp == upte[i*2] )
			found=1;

	/* otherwise use a free slot */
	for( i=0; !found && i<8; i++ )
		if( !pp[i].v )
			found=1;

	/* out of slots, just evict one */
	if( !found ) {
		i = next_grab_slot + 1;
		next_grab_slot = (next_grab_slot + 1) % 8;
	}
	i--;
	upte[i*2] = cmp;
	upte[i*2+1] = (phys & ~0xfff) | mode;

	asm volatile( "tlbie %0"  :: "r"(ea) );
}

void
dsi_exception( void )
{
	unsigned long dar, dsisr;
	ucell mode;
	ucell phys;

	asm volatile("mfdar %0" : "=r" (dar) : );
	asm volatile("mfdsisr %0" : "=r" (dsisr) : );

	//printk("dsi-exception @ %08lx <%08lx>\n", dar, dsisr );

	phys = ea_to_phys(dar, &mode);
	hash_page( dar, phys, mode );
}

void
isi_exception( void )
{
	unsigned long nip, srr1;
	ucell mode;
	ucell phys;

	asm volatile("mfsrr0 %0" : "=r" (nip) : );
	asm volatile("mfsrr1 %0" : "=r" (srr1) : );

	//printk("isi-exception @ %08lx <%08lx>\n", nip, srr1 );

	phys = ea_to_phys(nip, &mode);
	hash_page( nip, phys, mode );
}


/************************************************************************/
/*	init / cleanup							*/
/************************************************************************/

void
setup_mmu( unsigned long code_base, unsigned long code_size, unsigned long ramsize )
{
	unsigned long sdr1 = HASH_BASE | ((HASH_SIZE-1) >> 16);
	unsigned long sr_base = (0x20 << 24) | SEGR_BASE;
	unsigned long msr;
	int i;

	asm volatile("mtsdr1 %0" :: "r" (sdr1) );
	for( i=0; i<16; i++ ) {
		int j = i << 28;
		asm volatile("mtsrin %0,%1" :: "r" (sr_base + i), "r" (j) );
	}
	asm volatile("mfmsr %0" : "=r" (msr) : );
	msr |= MSR_IR | MSR_DR;
	asm volatile("mtmsr %0" :: "r" (msr) );
}

void
ofmem_init( void )
{
	ofmem_t *ofmem = OFMEM;
	/* In case we can't rely on memory being zero initialized */
	memset(ofmem, 0, sizeof(ofmem));

	ofmem->ramsize = RAMSIZE;

	ofmem_claim_phys( 0, FREE_BASE_1, 0 );
	ofmem_claim_virt( 0, FREE_BASE_1, 0 );
	ofmem_claim_phys( OF_CODE_START, FREE_BASE_2 - OF_CODE_START, 0 );
	ofmem_claim_virt( OF_CODE_START, FREE_BASE_2 - OF_CODE_START, 0 );
}