root / hw / ppc440_bamboo.c @ 3960b04d
History | View | Annotate | Download (10.1 kB)
1 |
/*
|
---|---|
2 |
* Qemu PowerPC 440 Bamboo board emulation
|
3 |
*
|
4 |
* Copyright 2007 IBM Corporation.
|
5 |
* Authors:
|
6 |
* Jerone Young <jyoung5@us.ibm.com>
|
7 |
* Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
|
8 |
* Hollis Blanchard <hollisb@us.ibm.com>
|
9 |
*
|
10 |
* This work is licensed under the GNU GPL license version 2 or later.
|
11 |
*
|
12 |
*/
|
13 |
|
14 |
#include "config.h" |
15 |
#include "qemu-common.h" |
16 |
#include "net.h" |
17 |
#include "hw.h" |
18 |
#include "pci.h" |
19 |
#include "boards.h" |
20 |
#include "kvm.h" |
21 |
#include "kvm_ppc.h" |
22 |
#include "device_tree.h" |
23 |
#include "loader.h" |
24 |
#include "elf.h" |
25 |
#include "exec-memory.h" |
26 |
#include "pc.h" |
27 |
#include "ppc.h" |
28 |
#include "ppc405.h" |
29 |
#include "sysemu.h" |
30 |
|
31 |
#define BINARY_DEVICE_TREE_FILE "bamboo.dtb" |
32 |
|
33 |
/* from u-boot */
|
34 |
#define KERNEL_ADDR 0x1000000 |
35 |
#define FDT_ADDR 0x1800000 |
36 |
#define RAMDISK_ADDR 0x1900000 |
37 |
|
38 |
#define PPC440EP_PCI_CONFIG 0xeec00000 |
39 |
#define PPC440EP_PCI_INTACK 0xeed00000 |
40 |
#define PPC440EP_PCI_SPECIAL 0xeed00000 |
41 |
#define PPC440EP_PCI_REGS 0xef400000 |
42 |
#define PPC440EP_PCI_IO 0xe8000000 |
43 |
#define PPC440EP_PCI_IOLEN 0x00010000 |
44 |
|
45 |
#define PPC440EP_SDRAM_NR_BANKS 4 |
46 |
|
47 |
static const unsigned int ppc440ep_sdram_bank_sizes[] = { |
48 |
256<<20, 128<<20, 64<<20, 32<<20, 16<<20, 8<<20, 0 |
49 |
}; |
50 |
|
51 |
static target_phys_addr_t entry;
|
52 |
|
53 |
static PCIBus *ppc4xx_pci_init(CPUState *env, qemu_irq pci_irqs[4], |
54 |
target_phys_addr_t config_space, |
55 |
target_phys_addr_t int_ack, |
56 |
target_phys_addr_t special_cycle, |
57 |
target_phys_addr_t registers) |
58 |
{ |
59 |
return NULL; |
60 |
} |
61 |
|
62 |
CPUState *ppc440ep_init(MemoryRegion *address_space_mem, ram_addr_t *ram_size, |
63 |
PCIBus **pcip, const unsigned int pci_irq_nrs[4], |
64 |
int do_init, const char *cpu_model) |
65 |
{ |
66 |
MemoryRegion *ram_memories |
67 |
= g_malloc(PPC440EP_SDRAM_NR_BANKS * sizeof(*ram_memories));
|
68 |
target_phys_addr_t ram_bases[PPC440EP_SDRAM_NR_BANKS]; |
69 |
target_phys_addr_t ram_sizes[PPC440EP_SDRAM_NR_BANKS]; |
70 |
CPUState *env; |
71 |
qemu_irq *pic; |
72 |
qemu_irq *irqs; |
73 |
qemu_irq *pci_irqs; |
74 |
|
75 |
if (cpu_model == NULL) { |
76 |
cpu_model = "440EP";
|
77 |
} |
78 |
env = cpu_init(cpu_model); |
79 |
if (!env) {
|
80 |
fprintf(stderr, "Unable to initialize CPU!\n");
|
81 |
exit(1);
|
82 |
} |
83 |
|
84 |
ppc_booke_timers_init(env, 400000000, 0); |
85 |
ppc_dcr_init(env, NULL, NULL); |
86 |
|
87 |
/* interrupt controller */
|
88 |
irqs = g_malloc0(sizeof(qemu_irq) * PPCUIC_OUTPUT_NB);
|
89 |
irqs[PPCUIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]; |
90 |
irqs[PPCUIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]; |
91 |
pic = ppcuic_init(env, irqs, 0x0C0, 0, 1); |
92 |
|
93 |
/* SDRAM controller */
|
94 |
memset(ram_bases, 0, sizeof(ram_bases)); |
95 |
memset(ram_sizes, 0, sizeof(ram_sizes)); |
96 |
*ram_size = ppc4xx_sdram_adjust(*ram_size, PPC440EP_SDRAM_NR_BANKS, |
97 |
ram_memories, |
98 |
ram_bases, ram_sizes, |
99 |
ppc440ep_sdram_bank_sizes); |
100 |
/* XXX 440EP's ECC interrupts are on UIC1, but we've only created UIC0. */
|
101 |
ppc4xx_sdram_init(env, pic[14], PPC440EP_SDRAM_NR_BANKS, ram_memories,
|
102 |
ram_bases, ram_sizes, do_init); |
103 |
|
104 |
/* PCI */
|
105 |
pci_irqs = g_malloc(sizeof(qemu_irq) * 4); |
106 |
pci_irqs[0] = pic[pci_irq_nrs[0]]; |
107 |
pci_irqs[1] = pic[pci_irq_nrs[1]]; |
108 |
pci_irqs[2] = pic[pci_irq_nrs[2]]; |
109 |
pci_irqs[3] = pic[pci_irq_nrs[3]]; |
110 |
*pcip = ppc4xx_pci_init(env, pci_irqs, |
111 |
PPC440EP_PCI_CONFIG, |
112 |
PPC440EP_PCI_INTACK, |
113 |
PPC440EP_PCI_SPECIAL, |
114 |
PPC440EP_PCI_REGS); |
115 |
if (!*pcip)
|
116 |
printf("couldn't create PCI controller!\n");
|
117 |
|
118 |
isa_mmio_init(PPC440EP_PCI_IO, PPC440EP_PCI_IOLEN); |
119 |
|
120 |
if (serial_hds[0] != NULL) { |
121 |
serial_mm_init(address_space_mem, 0xef600300, 0, pic[0], |
122 |
PPC_SERIAL_MM_BAUDBASE, serial_hds[0],
|
123 |
DEVICE_BIG_ENDIAN); |
124 |
} |
125 |
if (serial_hds[1] != NULL) { |
126 |
serial_mm_init(address_space_mem, 0xef600400, 0, pic[1], |
127 |
PPC_SERIAL_MM_BAUDBASE, serial_hds[1],
|
128 |
DEVICE_BIG_ENDIAN); |
129 |
} |
130 |
|
131 |
return env;
|
132 |
} |
133 |
|
134 |
static int bamboo_load_device_tree(target_phys_addr_t addr, |
135 |
uint32_t ramsize, |
136 |
target_phys_addr_t initrd_base, |
137 |
target_phys_addr_t initrd_size, |
138 |
const char *kernel_cmdline) |
139 |
{ |
140 |
int ret = -1; |
141 |
#ifdef CONFIG_FDT
|
142 |
uint32_t mem_reg_property[] = { 0, 0, ramsize }; |
143 |
char *filename;
|
144 |
int fdt_size;
|
145 |
void *fdt;
|
146 |
uint32_t tb_freq = 400000000;
|
147 |
uint32_t clock_freq = 400000000;
|
148 |
|
149 |
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); |
150 |
if (!filename) {
|
151 |
goto out;
|
152 |
} |
153 |
fdt = load_device_tree(filename, &fdt_size); |
154 |
g_free(filename); |
155 |
if (fdt == NULL) { |
156 |
goto out;
|
157 |
} |
158 |
|
159 |
/* Manipulate device tree in memory. */
|
160 |
|
161 |
ret = qemu_devtree_setprop(fdt, "/memory", "reg", mem_reg_property, |
162 |
sizeof(mem_reg_property));
|
163 |
if (ret < 0) |
164 |
fprintf(stderr, "couldn't set /memory/reg\n");
|
165 |
|
166 |
ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start", |
167 |
initrd_base); |
168 |
if (ret < 0) |
169 |
fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n");
|
170 |
|
171 |
ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-end", |
172 |
(initrd_base + initrd_size)); |
173 |
if (ret < 0) |
174 |
fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n");
|
175 |
|
176 |
ret = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", |
177 |
kernel_cmdline); |
178 |
if (ret < 0) |
179 |
fprintf(stderr, "couldn't set /chosen/bootargs\n");
|
180 |
|
181 |
/* Copy data from the host device tree into the guest. Since the guest can
|
182 |
* directly access the timebase without host involvement, we must expose
|
183 |
* the correct frequencies. */
|
184 |
if (kvm_enabled()) {
|
185 |
tb_freq = kvmppc_get_tbfreq(); |
186 |
clock_freq = kvmppc_get_clockfreq(); |
187 |
} |
188 |
|
189 |
qemu_devtree_setprop_cell(fdt, "/cpus/cpu@0", "clock-frequency", |
190 |
clock_freq); |
191 |
qemu_devtree_setprop_cell(fdt, "/cpus/cpu@0", "timebase-frequency", |
192 |
tb_freq); |
193 |
|
194 |
ret = rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); |
195 |
g_free(fdt); |
196 |
|
197 |
out:
|
198 |
#endif
|
199 |
|
200 |
return ret;
|
201 |
} |
202 |
|
203 |
/* Create reset TLB entries for BookE, spanning the 32bit addr space. */
|
204 |
static void mmubooke_create_initial_mapping(CPUState *env, |
205 |
target_ulong va, |
206 |
target_phys_addr_t pa) |
207 |
{ |
208 |
ppcemb_tlb_t *tlb = &env->tlb.tlbe[0];
|
209 |
|
210 |
tlb->attr = 0;
|
211 |
tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4);
|
212 |
tlb->size = 1 << 31; /* up to 0x80000000 */ |
213 |
tlb->EPN = va & TARGET_PAGE_MASK; |
214 |
tlb->RPN = pa & TARGET_PAGE_MASK; |
215 |
tlb->PID = 0;
|
216 |
|
217 |
tlb = &env->tlb.tlbe[1];
|
218 |
tlb->attr = 0;
|
219 |
tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4);
|
220 |
tlb->size = 1 << 31; /* up to 0xffffffff */ |
221 |
tlb->EPN = 0x80000000 & TARGET_PAGE_MASK;
|
222 |
tlb->RPN = 0x80000000 & TARGET_PAGE_MASK;
|
223 |
tlb->PID = 0;
|
224 |
} |
225 |
|
226 |
static void main_cpu_reset(void *opaque) |
227 |
{ |
228 |
CPUState *env = opaque; |
229 |
|
230 |
cpu_reset(env); |
231 |
env->gpr[1] = (16<<20) - 8; |
232 |
env->gpr[3] = FDT_ADDR;
|
233 |
env->nip = entry; |
234 |
|
235 |
/* Create a mapping for the kernel. */
|
236 |
mmubooke_create_initial_mapping(env, 0, 0); |
237 |
} |
238 |
|
239 |
static void bamboo_init(ram_addr_t ram_size, |
240 |
const char *boot_device, |
241 |
const char *kernel_filename, |
242 |
const char *kernel_cmdline, |
243 |
const char *initrd_filename, |
244 |
const char *cpu_model) |
245 |
{ |
246 |
unsigned int pci_irq_nrs[4] = { 28, 27, 26, 25 }; |
247 |
MemoryRegion *address_space_mem = get_system_memory(); |
248 |
PCIBus *pcibus; |
249 |
CPUState *env; |
250 |
uint64_t elf_entry; |
251 |
uint64_t elf_lowaddr; |
252 |
target_phys_addr_t loadaddr = 0;
|
253 |
target_long initrd_size = 0;
|
254 |
int success;
|
255 |
int i;
|
256 |
|
257 |
/* Setup CPU. */
|
258 |
env = ppc440ep_init(address_space_mem, &ram_size, &pcibus, |
259 |
pci_irq_nrs, 1, cpu_model);
|
260 |
qemu_register_reset(main_cpu_reset, env); |
261 |
|
262 |
if (pcibus) {
|
263 |
/* Register network interfaces. */
|
264 |
for (i = 0; i < nb_nics; i++) { |
265 |
/* There are no PCI NICs on the Bamboo board, but there are
|
266 |
* PCI slots, so we can pick whatever default model we want. */
|
267 |
pci_nic_init_nofail(&nd_table[i], "e1000", NULL); |
268 |
} |
269 |
} |
270 |
|
271 |
/* Load kernel. */
|
272 |
if (kernel_filename) {
|
273 |
success = load_uimage(kernel_filename, &entry, &loadaddr, NULL);
|
274 |
if (success < 0) { |
275 |
success = load_elf(kernel_filename, NULL, NULL, &elf_entry, |
276 |
&elf_lowaddr, NULL, 1, ELF_MACHINE, 0); |
277 |
entry = elf_entry; |
278 |
loadaddr = elf_lowaddr; |
279 |
} |
280 |
/* XXX try again as binary */
|
281 |
if (success < 0) { |
282 |
fprintf(stderr, "qemu: could not load kernel '%s'\n",
|
283 |
kernel_filename); |
284 |
exit(1);
|
285 |
} |
286 |
} |
287 |
|
288 |
/* Load initrd. */
|
289 |
if (initrd_filename) {
|
290 |
initrd_size = load_image_targphys(initrd_filename, RAMDISK_ADDR, |
291 |
ram_size - RAMDISK_ADDR); |
292 |
|
293 |
if (initrd_size < 0) { |
294 |
fprintf(stderr, "qemu: could not load ram disk '%s' at %x\n",
|
295 |
initrd_filename, RAMDISK_ADDR); |
296 |
exit(1);
|
297 |
} |
298 |
} |
299 |
|
300 |
/* If we're loading a kernel directly, we must load the device tree too. */
|
301 |
if (kernel_filename) {
|
302 |
if (bamboo_load_device_tree(FDT_ADDR, ram_size, RAMDISK_ADDR,
|
303 |
initrd_size, kernel_cmdline) < 0) {
|
304 |
fprintf(stderr, "couldn't load device tree\n");
|
305 |
exit(1);
|
306 |
} |
307 |
} |
308 |
|
309 |
if (kvm_enabled())
|
310 |
kvmppc_init(); |
311 |
} |
312 |
|
313 |
static QEMUMachine bamboo_machine = {
|
314 |
.name = "bamboo",
|
315 |
.desc = "bamboo",
|
316 |
.init = bamboo_init, |
317 |
}; |
318 |
|
319 |
static void bamboo_machine_init(void) |
320 |
{ |
321 |
qemu_register_machine(&bamboo_machine); |
322 |
} |
323 |
|
324 |
machine_init(bamboo_machine_init); |