summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/powerpc/shared/bootloader/qemu_fakeres.c
blob: 51f03a070fc0f5e00c06cbfda8c63ff40bc74063 (plain) (blame)
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
#include <bsp/residual.h>
#include <stdint.h>

/* Magic knowledge - qemu loads image here. 
 * However, we use the value from NVRAM if possible...
 */
#define KERNELBASE 0x01000000

/* When starting qemu make sure to give the correct
 * amount of memory!
 *
 * NOTE: Code now supports reading the actual memory
 *       amount from NVRAM. The residual copy in RAM
 *       is fixed-up accordingly.
 */
#define MEM_MEGS 32

/* Mock up a minimal/fake residual; just enough to make the
 * bootloader happy.
 */
struct fake_data {
	unsigned long dat_len;
	unsigned long res_off;
	unsigned long cmd_off;
	unsigned long cmd_len;
	unsigned long img_adr;
	RESIDUAL      fake_residual;
	char          cmdline[1024];
} fake_data = {
dat_len: sizeof(fake_data),
res_off: (unsigned long) &fake_data.fake_residual
        -(unsigned long) &fake_data,
cmd_off: (unsigned long) &fake_data.cmdline
        -(unsigned long) &fake_data,
cmd_len: sizeof(fake_data.cmdline),
img_adr: KERNELBASE,
fake_residual: 
{
  ResidualLength: sizeof(RESIDUAL),
  Version:          0,
  Revision:         0,
  EC:               0,
  VitalProductData: {
	FirmwareSupplier:  QEMU,
	ProcessorHz:    300000000, /* fantasy */
	ProcessorBusHz: 100000000, /* qemu timebase clock */
	TimeBaseDivisor:1*1000,
  },
  MaxNumCpus:       1,
  ActualNumCpus:    1,
  Cpus: {
    {
	CpuType:        0x00040103, /* FIXME: fill from PVR */
	CpuNumber:      0,  
    CpuState:       0,
    },
  },
  /* Memory */
  TotalMemory:      1024*1024*MEM_MEGS,
  GoodMemory:       1024*1024*MEM_MEGS,
  ActualNumMemSegs: 13,
  Segs: {
    { 0x2000, 0xFFF00, 0x00100 },
    { 0x0020, MEM_MEGS*0x100, 0x80000 - MEM_MEGS*0x100 },
    { 0x0008, 0x00800, 0x00168 },
    { 0x0004, 0x00000, 0x00005 },
    { 0x0001, 0x006F1, 0x0010F },
    { 0x0002, 0x006AD, 0x00044 },
    { 0x0010, 0x00005, 0x006A8 },
    { 0x0010, 0x00968, MEM_MEGS*0x100 - 0x00968 },
    { 0x0800, 0xC0000, 0x3F000 },
    { 0x0600, 0xBF800, 0x00800 },
    { 0x0500, 0x81000, 0x3E800 },
    { 0x0480, 0x80800, 0x00800 },
    { 0x0440, 0x80000, 0x00800 }  
  },
  ActualNumMemories: 0,
  Memories: {
	{0},
  },
  /* Devices */
  ActualNumDevices:  1,
  Devices: {
	{
	DeviceId: {
		BusId:     PROCESSORDEVICE,
		BaseType:  BridgeController,
		SubType:   PCIBridge,
		Interface: PCIBridgeIndirect,
	},
	}
  },
  DevicePnPHeap: {0}
},
/* This is overwritten by command line passed by qemu. */
cmdline: {
	'-','-','n','e','2','k','-','i','r','q','=','9',
	0,
}
};

/* Read one byte from NVRAM */
static inline uint8_t
nvram_rd(void)
{
uint8_t rval = *(volatile uint8_t*)0x80000077;
	asm volatile("eieio");
	return rval;
}

/* Set NVRAM address pointer */
static inline void
nvram_addr(uint16_t addr)
{
	*(volatile uint8_t*)0x80000074 = (addr & 0xff);
	asm volatile("eieio");
	*(volatile uint8_t*)0x80000075 = ((addr>>8) & 0xff);
	asm volatile("eieio");
}

/* Read a 32-bit (big-endian) work from NVRAM */
static uint32_t
nvram_rdl_be(uint16_t addr)
{
int i;
uint32_t rval = 0;
	for ( i=0; i<sizeof(rval); i++ ) { 
		nvram_addr( addr + i );
		rval = (rval<<8) | nvram_rd();
	}
	return rval;
}


/* !!! NOTE !!!
 *
 * We use a special hack to propagate command-line info to the bootloader.
 * This is NOT PreP compliant (but who cares).
 * We set R6 and R7 to point to the start/end of the command line string
 * and hacked the bootloader so it uses R6/R7 (provided that the firmware
 * is detected as 'QEMU').
 *
 * (see bootloader/mm.c, bootloader/misc.c,  bootloader/bootldr.h, -- boot_data.cmd_line[])
 */
uint32_t
res_copy(void)
{
struct   fake_data *p;
uint32_t addr, cmdl, l, imga;
uint32_t mem_sz, pgs;
int      i;
int      have_nvram;

	/* Make sure we have a valid NVRAM -- just check for 'QEMU' at the
	 * beginning 
	 */
	have_nvram = ( (('Q'<<24) | ('E'<<16) | ('M'<< 8) | ('U'<< 0)) == nvram_rdl_be( 0x0000 ) );

	if ( !have_nvram ) {
		/* reading NVRAM failed - fall back to using the static residual copy;
		 * this means no support for variable memory size or 'true' command line.
		 */
		return (uint32_t)&fake_data;
	}

	/* Dilemma - we don't really know where to put the residual copy
     * (original is in ROM and cannot be modified).
	 * We can't put it at the top of memory since the bootloader starts
	 * allocating memory from there, before saving the residual, that is.
	 * Too close to the final image might not work either because RTEMS
	 * zeroes its BSS *before* making its copies of the residual and commandline.
	 *
 	 * For now we hope that appending to the kernel image works (and that
	 * the bootloader puts it somewhere safe).
	 */
	imga  = nvram_rdl_be( 0x0038 );
	addr  = imga + nvram_rdl_be( 0x003c );
	addr += 0x1f;
	addr &= ~(0x1f);

	p     = (struct fake_data *)addr;

	/* commandline + length from NVRAM */
	cmdl  = nvram_rdl_be( 0x0040 );
	l     = nvram_rdl_be( 0x0044 );

	if ( l > 0 ) {
		/* have a command-line; copy it into our local buffer */
		if ( l > sizeof( p->cmdline ) - 1 ) {
			l = sizeof( p->cmdline ) - 1;
		}
		/* original may overlap our buffer; must safely move around */
		if ( p->cmdline < (char*)cmdl ) {
			for ( i=0; i<l; i++ ) {
				p->cmdline[i] = ((char*)cmdl)[i];
			}
		} else {
			for ( i=l-1; i>=0; i-- ) {
				p->cmdline[i] = ((char*)cmdl)[i];
			}
		}
	}
	p->cmdline[l]      = 0;
	/* Copy rest of residual */
	for ( i=0; i<sizeof(p->fake_residual); i++ )
		((char*)&p->fake_residual)[i] = ((char*)&fake_data.fake_residual)[i];
	p->dat_len         = fake_data.dat_len;
	p->res_off         = fake_data.res_off;
	p->cmd_off         = fake_data.cmd_off;
	p->cmd_len         = l+1;
	p->img_adr         = imga;

	/* Fix up memory in residual from NVRAM settings */

	mem_sz = nvram_rdl_be( 0x0030 );
	pgs    = mem_sz >> 12;

	p->fake_residual.TotalMemory = mem_sz;
	p->fake_residual.GoodMemory  = mem_sz;

	p->fake_residual.Segs[1].BasePage  = pgs;
	p->fake_residual.Segs[1].PageCount = 0x80000 - pgs;
	p->fake_residual.Segs[7].PageCount = pgs - 0x00968;

	return (uint32_t)p;
}