Revision 3475187d
b/Changelog | ||
---|---|---|
4 | 4 |
- Windows 2000 install disk full hack (original idea from Vladimir |
5 | 5 |
N. Oleynik) |
6 | 6 |
- VMDK disk image creation (Filip Navara) |
7 |
- SPARC64 progress (Blue Swirl) |
|
7 | 8 |
|
8 | 9 |
version 0.7.0: |
9 | 10 |
|
b/Makefile.target | ||
---|---|---|
349 | 349 |
VL_OBJS+= ppc_prep.o ppc_chrp.o cuda.o adb.o openpic.o heathrow_pic.o mixeng.o |
350 | 350 |
endif |
351 | 351 |
ifeq ($(TARGET_BASE_ARCH), sparc) |
352 |
VL_OBJS+= sun4m.o tcx.o lance.o iommu.o m48t08.o magic-load.o slavio_intctl.o slavio_timer.o slavio_serial.o fdc.o esp.o |
|
352 |
ifeq ($(TARGET_ARCH), sparc64) |
|
353 |
VL_OBJS+= sun4u.o m48t08.o magic-load.o slavio_serial.o |
|
354 |
else |
|
355 |
VL_OBJS+= sun4m.o tcx.o lance.o iommu.o m48t08.o magic-load.o slavio_intctl.o slavio_timer.o slavio_serial.o slavio_misc.o fdc.o esp.o |
|
356 |
endif |
|
353 | 357 |
endif |
354 | 358 |
ifdef CONFIG_GDBSTUB |
355 | 359 |
VL_OBJS+=gdbstub.o |
... | ... | |
442 | 446 |
endif |
443 | 447 |
|
444 | 448 |
ifeq ($(TARGET_BASE_ARCH), sparc) |
445 |
op.o: op.c op_template.h op_mem.h |
|
449 |
op.o: op.c op_template.h op_mem.h fop_template.h fbranch_template.h |
|
450 |
magic_load.o: elf_op.h |
|
446 | 451 |
endif |
447 | 452 |
|
448 | 453 |
ifeq ($(TARGET_ARCH), ppc) |
b/cpu-exec.c | ||
---|---|---|
47 | 47 |
longjmp(env->jmp_env, 1); |
48 | 48 |
} |
49 | 49 |
#endif |
50 |
#ifndef TARGET_SPARC |
|
51 |
#define reg_T2 |
|
52 |
#endif |
|
50 | 53 |
|
51 | 54 |
/* exit the current TB from a signal handler. The host registers are |
52 | 55 |
restored in a state compatible with the CPU emulator |
... | ... | |
74 | 77 |
|
75 | 78 |
int cpu_exec(CPUState *env1) |
76 | 79 |
{ |
77 |
int saved_T0, saved_T1, saved_T2; |
|
80 |
int saved_T0, saved_T1; |
|
81 |
#if defined(reg_T2) |
|
82 |
int saved_T2; |
|
83 |
#endif |
|
78 | 84 |
CPUState *saved_env; |
85 |
#if defined(TARGET_I386) |
|
79 | 86 |
#ifdef reg_EAX |
80 | 87 |
int saved_EAX; |
81 | 88 |
#endif |
... | ... | |
100 | 107 |
#ifdef reg_EDI |
101 | 108 |
int saved_EDI; |
102 | 109 |
#endif |
110 |
#elif defined(TARGET_SPARC) |
|
111 |
#if defined(reg_REGWPTR) |
|
112 |
uint32_t *saved_regwptr; |
|
113 |
#endif |
|
114 |
#endif |
|
103 | 115 |
#ifdef __sparc__ |
104 | 116 |
int saved_i7, tmp_T0; |
105 | 117 |
#endif |
... | ... | |
115 | 127 |
env = env1; |
116 | 128 |
saved_T0 = T0; |
117 | 129 |
saved_T1 = T1; |
130 |
#if defined(reg_T2) |
|
118 | 131 |
saved_T2 = T2; |
132 |
#endif |
|
119 | 133 |
#ifdef __sparc__ |
120 | 134 |
/* we also save i7 because longjmp may not restore it */ |
121 | 135 |
asm volatile ("mov %%i7, %0" : "=r" (saved_i7)); |
... | ... | |
164 | 178 |
env->cpsr = psr & ~CACHED_CPSR_BITS; |
165 | 179 |
} |
166 | 180 |
#elif defined(TARGET_SPARC) |
181 |
#if defined(reg_REGWPTR) |
|
182 |
saved_regwptr = REGWPTR; |
|
183 |
#endif |
|
167 | 184 |
#elif defined(TARGET_PPC) |
168 | 185 |
#else |
169 | 186 |
#error unsupported target CPU |
... | ... | |
354 | 371 |
cpu_dump_state(env, logfile, fprintf, 0); |
355 | 372 |
env->cpsr &= ~CACHED_CPSR_BITS; |
356 | 373 |
#elif defined(TARGET_SPARC) |
357 |
cpu_dump_state (env, logfile, fprintf, 0); |
|
374 |
REGWPTR = env->regbase + (env->cwp * 16); |
|
375 |
env->regwptr = REGWPTR; |
|
376 |
cpu_dump_state(env, logfile, fprintf, 0); |
|
358 | 377 |
#elif defined(TARGET_PPC) |
359 | 378 |
cpu_dump_state(env, logfile, fprintf, 0); |
360 | 379 |
#else |
... | ... | |
376 | 395 |
cs_base = 0; |
377 | 396 |
pc = env->regs[15]; |
378 | 397 |
#elif defined(TARGET_SPARC) |
379 |
flags = 0; |
|
398 |
#ifdef TARGET_SPARC64 |
|
399 |
flags = (env->pstate << 2) | ((env->lsu & (DMMU_E | IMMU_E)) >> 2); |
|
400 |
#else |
|
401 |
flags = env->psrs | ((env->mmuregs[0] & (MMU_E | MMU_NF)) << 1); |
|
402 |
#endif |
|
380 | 403 |
cs_base = env->npc; |
381 | 404 |
pc = env->pc; |
382 | 405 |
#elif defined(TARGET_PPC) |
... | ... | |
657 | 680 |
env->cpsr = compute_cpsr(); |
658 | 681 |
/* XXX: Save/restore host fpu exception state?. */ |
659 | 682 |
#elif defined(TARGET_SPARC) |
683 |
#if defined(reg_REGWPTR) |
|
684 |
REGWPTR = saved_regwptr; |
|
685 |
#endif |
|
660 | 686 |
#elif defined(TARGET_PPC) |
661 | 687 |
#else |
662 | 688 |
#error unsupported target CPU |
... | ... | |
666 | 692 |
#endif |
667 | 693 |
T0 = saved_T0; |
668 | 694 |
T1 = saved_T1; |
695 |
#if defined(reg_T2) |
|
669 | 696 |
T2 = saved_T2; |
697 |
#endif |
|
670 | 698 |
env = saved_env; |
671 | 699 |
return ret; |
672 | 700 |
} |
b/disas.c | ||
---|---|---|
155 | 155 |
print_insn = print_insn_arm; |
156 | 156 |
#elif defined(TARGET_SPARC) |
157 | 157 |
print_insn = print_insn_sparc; |
158 |
#ifdef TARGET_SPARC64 |
|
159 |
disasm_info.mach = bfd_mach_sparc_v9b; |
|
160 |
#endif |
|
158 | 161 |
#elif defined(TARGET_PPC) |
159 | 162 |
if (cpu_single_env->msr[MSR_LE]) |
160 | 163 |
disasm_info.endian = BFD_ENDIAN_LITTLE; |
b/gdbstub.c | ||
---|---|---|
1 | 1 |
/* |
2 | 2 |
* gdb server stub |
3 | 3 |
* |
4 |
* Copyright (c) 2003 Fabrice Bellard |
|
4 |
* Copyright (c) 2003-2005 Fabrice Bellard
|
|
5 | 5 |
* |
6 | 6 |
* This library is free software; you can redistribute it and/or |
7 | 7 |
* modify it under the terms of the GNU Lesser General Public |
... | ... | |
293 | 293 |
#elif defined (TARGET_SPARC) |
294 | 294 |
static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf) |
295 | 295 |
{ |
296 |
uint32_t *registers = (uint32_t *)mem_buf, tmp;
|
|
296 |
target_ulong *registers = (target_ulong *)mem_buf;
|
|
297 | 297 |
int i; |
298 | 298 |
|
299 | 299 |
/* fill in g0..g7 */ |
... | ... | |
308 | 308 |
for (i = 0; i < 32; i++) { |
309 | 309 |
registers[i + 32] = tswapl(*((uint32_t *)&env->fpr[i])); |
310 | 310 |
} |
311 |
#ifndef TARGET_SPARC64 |
|
311 | 312 |
/* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */ |
312 | 313 |
registers[64] = tswapl(env->y); |
313 |
tmp = GET_PSR(env); |
|
314 |
registers[65] = tswapl(tmp); |
|
314 |
{ |
|
315 |
target_ulong tmp; |
|
316 |
|
|
317 |
tmp = GET_PSR(env); |
|
318 |
registers[65] = tswapl(tmp); |
|
319 |
} |
|
315 | 320 |
registers[66] = tswapl(env->wim); |
316 | 321 |
registers[67] = tswapl(env->tbr); |
317 | 322 |
registers[68] = tswapl(env->pc); |
... | ... | |
319 | 324 |
registers[70] = tswapl(env->fsr); |
320 | 325 |
registers[71] = 0; /* csr */ |
321 | 326 |
registers[72] = 0; |
322 |
|
|
323 |
return 73 * 4; |
|
327 |
return 73 * sizeof(target_ulong); |
|
328 |
#else |
|
329 |
for (i = 0; i < 32; i += 2) { |
|
330 |
registers[i/2 + 64] = tswapl(*((uint64_t *)&env->fpr[i])); |
|
331 |
} |
|
332 |
registers[81] = tswapl(env->pc); |
|
333 |
registers[82] = tswapl(env->npc); |
|
334 |
registers[83] = tswapl(env->tstate[env->tl]); |
|
335 |
registers[84] = tswapl(env->fsr); |
|
336 |
registers[85] = tswapl(env->fprs); |
|
337 |
registers[86] = tswapl(env->y); |
|
338 |
return 87 * sizeof(target_ulong); |
|
339 |
#endif |
|
324 | 340 |
} |
325 | 341 |
|
326 | 342 |
static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size) |
327 | 343 |
{ |
328 |
uint32_t *registers = (uint32_t *)mem_buf;
|
|
344 |
target_ulong *registers = (target_ulong *)mem_buf;
|
|
329 | 345 |
int i; |
330 | 346 |
|
331 | 347 |
/* fill in g0..g7 */ |
... | ... | |
334 | 350 |
} |
335 | 351 |
/* fill in register window */ |
336 | 352 |
for(i = 0; i < 24; i++) { |
337 |
env->regwptr[i] = tswapl(registers[i]); |
|
353 |
env->regwptr[i] = tswapl(registers[i + 8]);
|
|
338 | 354 |
} |
339 | 355 |
/* fill in fprs */ |
340 | 356 |
for (i = 0; i < 32; i++) { |
341 | 357 |
*((uint32_t *)&env->fpr[i]) = tswapl(registers[i + 32]); |
342 | 358 |
} |
359 |
#ifndef TARGET_SPARC64 |
|
343 | 360 |
/* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */ |
344 | 361 |
env->y = tswapl(registers[64]); |
345 | 362 |
PUT_PSR(env, tswapl(registers[65])); |
... | ... | |
348 | 365 |
env->pc = tswapl(registers[68]); |
349 | 366 |
env->npc = tswapl(registers[69]); |
350 | 367 |
env->fsr = tswapl(registers[70]); |
368 |
#else |
|
369 |
for (i = 0; i < 32; i += 2) { |
|
370 |
uint64_t tmp; |
|
371 |
tmp = tswapl(registers[i/2 + 64]) << 32; |
|
372 |
tmp |= tswapl(registers[i/2 + 64 + 1]); |
|
373 |
*((uint64_t *)&env->fpr[i]) = tmp; |
|
374 |
} |
|
375 |
env->pc = tswapl(registers[81]); |
|
376 |
env->npc = tswapl(registers[82]); |
|
377 |
env->tstate[env->tl] = tswapl(registers[83]); |
|
378 |
env->fsr = tswapl(registers[84]); |
|
379 |
env->fprs = tswapl(registers[85]); |
|
380 |
env->y = tswapl(registers[86]); |
|
381 |
#endif |
|
351 | 382 |
} |
352 | 383 |
#elif defined (TARGET_ARM) |
353 | 384 |
static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf) |
b/hw/elf_ops.h | ||
---|---|---|
1 |
#ifdef BSWAP_NEEDED |
|
2 |
static void glue(bswap_ehdr, SZ)(struct elfhdr *ehdr) |
|
3 |
{ |
|
4 |
bswap16s(&ehdr->e_type); /* Object file type */ |
|
5 |
bswap16s(&ehdr->e_machine); /* Architecture */ |
|
6 |
bswap32s(&ehdr->e_version); /* Object file version */ |
|
7 |
bswapSZs(&ehdr->e_entry); /* Entry point virtual address */ |
|
8 |
bswapSZs(&ehdr->e_phoff); /* Program header table file offset */ |
|
9 |
bswapSZs(&ehdr->e_shoff); /* Section header table file offset */ |
|
10 |
bswap32s(&ehdr->e_flags); /* Processor-specific flags */ |
|
11 |
bswap16s(&ehdr->e_ehsize); /* ELF header size in bytes */ |
|
12 |
bswap16s(&ehdr->e_phentsize); /* Program header table entry size */ |
|
13 |
bswap16s(&ehdr->e_phnum); /* Program header table entry count */ |
|
14 |
bswap16s(&ehdr->e_shentsize); /* Section header table entry size */ |
|
15 |
bswap16s(&ehdr->e_shnum); /* Section header table entry count */ |
|
16 |
bswap16s(&ehdr->e_shstrndx); /* Section header string table index */ |
|
17 |
} |
|
18 |
|
|
19 |
static void glue(bswap_phdr, SZ)(struct elf_phdr *phdr) |
|
20 |
{ |
|
21 |
bswap32s(&phdr->p_type); /* Segment type */ |
|
22 |
bswapSZs(&phdr->p_offset); /* Segment file offset */ |
|
23 |
bswapSZs(&phdr->p_vaddr); /* Segment virtual address */ |
|
24 |
bswapSZs(&phdr->p_paddr); /* Segment physical address */ |
|
25 |
bswapSZs(&phdr->p_filesz); /* Segment size in file */ |
|
26 |
bswapSZs(&phdr->p_memsz); /* Segment size in memory */ |
|
27 |
bswap32s(&phdr->p_flags); /* Segment flags */ |
|
28 |
bswapSZs(&phdr->p_align); /* Segment alignment */ |
|
29 |
} |
|
30 |
|
|
31 |
static void glue(bswap_shdr, SZ)(struct elf_shdr *shdr) |
|
32 |
{ |
|
33 |
bswap32s(&shdr->sh_name); |
|
34 |
bswap32s(&shdr->sh_type); |
|
35 |
bswapSZs(&shdr->sh_flags); |
|
36 |
bswapSZs(&shdr->sh_addr); |
|
37 |
bswapSZs(&shdr->sh_offset); |
|
38 |
bswapSZs(&shdr->sh_size); |
|
39 |
bswap32s(&shdr->sh_link); |
|
40 |
bswap32s(&shdr->sh_info); |
|
41 |
bswapSZs(&shdr->sh_addralign); |
|
42 |
bswapSZs(&shdr->sh_entsize); |
|
43 |
} |
|
44 |
|
|
45 |
static void glue(bswap_sym, SZ)(struct elf_sym *sym) |
|
46 |
{ |
|
47 |
bswap32s(&sym->st_name); |
|
48 |
bswapSZs(&sym->st_value); |
|
49 |
bswapSZs(&sym->st_size); |
|
50 |
bswap16s(&sym->st_shndx); |
|
51 |
} |
|
52 |
#endif |
|
53 |
|
|
54 |
static int glue(find_phdr, SZ)(struct elfhdr *ehdr, int fd, struct elf_phdr *phdr, elf_word type) |
|
55 |
{ |
|
56 |
int i, retval; |
|
57 |
|
|
58 |
retval = lseek(fd, ehdr->e_phoff, SEEK_SET); |
|
59 |
if (retval < 0) |
|
60 |
return -1; |
|
61 |
|
|
62 |
for (i = 0; i < ehdr->e_phnum; i++) { |
|
63 |
retval = read(fd, phdr, sizeof(*phdr)); |
|
64 |
if (retval < 0) |
|
65 |
return -1; |
|
66 |
glue(bswap_phdr, SZ)(phdr); |
|
67 |
if (phdr->p_type == type) |
|
68 |
return 0; |
|
69 |
} |
|
70 |
return -1; |
|
71 |
} |
|
72 |
|
|
73 |
static void * glue(find_shdr, SZ)(struct elfhdr *ehdr, int fd, struct elf_shdr *shdr, elf_word type) |
|
74 |
{ |
|
75 |
int i, retval; |
|
76 |
|
|
77 |
retval = lseek(fd, ehdr->e_shoff, SEEK_SET); |
|
78 |
if (retval < 0) |
|
79 |
return NULL; |
|
80 |
|
|
81 |
for (i = 0; i < ehdr->e_shnum; i++) { |
|
82 |
retval = read(fd, shdr, sizeof(*shdr)); |
|
83 |
if (retval < 0) |
|
84 |
return NULL; |
|
85 |
glue(bswap_shdr, SZ)(shdr); |
|
86 |
if (shdr->sh_type == type) |
|
87 |
return qemu_malloc(shdr->sh_size); |
|
88 |
} |
|
89 |
return NULL; |
|
90 |
} |
|
91 |
|
|
92 |
static void * glue(find_strtab, SZ)(struct elfhdr *ehdr, int fd, struct elf_shdr *shdr, struct elf_shdr *symtab) |
|
93 |
{ |
|
94 |
int retval; |
|
95 |
|
|
96 |
retval = lseek(fd, ehdr->e_shoff + sizeof(struct elf_shdr) * symtab->sh_link, SEEK_SET); |
|
97 |
if (retval < 0) |
|
98 |
return NULL; |
|
99 |
|
|
100 |
retval = read(fd, shdr, sizeof(*shdr)); |
|
101 |
if (retval < 0) |
|
102 |
return NULL; |
|
103 |
glue(bswap_shdr, SZ)(shdr); |
|
104 |
if (shdr->sh_type == SHT_STRTAB) |
|
105 |
return qemu_malloc(shdr->sh_size);; |
|
106 |
return NULL; |
|
107 |
} |
|
108 |
|
|
109 |
static int glue(read_program, SZ)(int fd, struct elf_phdr *phdr, void *dst, elf_word entry) |
|
110 |
{ |
|
111 |
int retval; |
|
112 |
retval = lseek(fd, phdr->p_offset + entry - phdr->p_vaddr, SEEK_SET); |
|
113 |
if (retval < 0) |
|
114 |
return -1; |
|
115 |
return read(fd, dst, phdr->p_filesz); |
|
116 |
} |
|
117 |
|
|
118 |
static int glue(read_section, SZ)(int fd, struct elf_shdr *s, void *dst) |
|
119 |
{ |
|
120 |
int retval; |
|
121 |
|
|
122 |
retval = lseek(fd, s->sh_offset, SEEK_SET); |
|
123 |
if (retval < 0) |
|
124 |
return -1; |
|
125 |
retval = read(fd, dst, s->sh_size); |
|
126 |
if (retval < 0) |
|
127 |
return -1; |
|
128 |
return 0; |
|
129 |
} |
|
130 |
|
|
131 |
static void * glue(process_section, SZ)(struct elfhdr *ehdr, int fd, struct elf_shdr *shdr, elf_word type) |
|
132 |
{ |
|
133 |
void *dst; |
|
134 |
|
|
135 |
dst = glue(find_shdr, SZ)(ehdr, fd, shdr, type); |
|
136 |
if (!dst) |
|
137 |
goto error; |
|
138 |
|
|
139 |
if (glue(read_section, SZ)(fd, shdr, dst)) |
|
140 |
goto error; |
|
141 |
return dst; |
|
142 |
error: |
|
143 |
qemu_free(dst); |
|
144 |
return NULL; |
|
145 |
} |
|
146 |
|
|
147 |
static void * glue(process_strtab, SZ)(struct elfhdr *ehdr, int fd, struct elf_shdr *shdr, struct elf_shdr *symtab) |
|
148 |
{ |
|
149 |
void *dst; |
|
150 |
|
|
151 |
dst = glue(find_strtab, SZ)(ehdr, fd, shdr, symtab); |
|
152 |
if (!dst) |
|
153 |
goto error; |
|
154 |
|
|
155 |
if (glue(read_section, SZ)(fd, shdr, dst)) |
|
156 |
goto error; |
|
157 |
return dst; |
|
158 |
error: |
|
159 |
qemu_free(dst); |
|
160 |
return NULL; |
|
161 |
} |
|
162 |
|
|
163 |
static void glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd) |
|
164 |
{ |
|
165 |
struct elf_shdr symtab, strtab; |
|
166 |
struct elf_sym *syms; |
|
167 |
#if (SZ == 64) |
|
168 |
struct elf32_sym *syms32; |
|
169 |
#endif |
|
170 |
struct syminfo *s; |
|
171 |
int nsyms, i; |
|
172 |
char *str; |
|
173 |
|
|
174 |
/* Symbol table */ |
|
175 |
syms = glue(process_section, SZ)(ehdr, fd, &symtab, SHT_SYMTAB); |
|
176 |
if (!syms) |
|
177 |
return; |
|
178 |
|
|
179 |
nsyms = symtab.sh_size / sizeof(struct elf_sym); |
|
180 |
#if (SZ == 64) |
|
181 |
syms32 = qemu_mallocz(nsyms * sizeof(struct elf32_sym)); |
|
182 |
#endif |
|
183 |
for (i = 0; i < nsyms; i++) { |
|
184 |
glue(bswap_sym, SZ)(&syms[i]); |
|
185 |
#if (SZ == 64) |
|
186 |
syms32[i].st_name = syms[i].st_name; |
|
187 |
syms32[i].st_info = syms[i].st_info; |
|
188 |
syms32[i].st_other = syms[i].st_other; |
|
189 |
syms32[i].st_shndx = syms[i].st_shndx; |
|
190 |
syms32[i].st_value = syms[i].st_value & 0xffffffff; |
|
191 |
syms32[i].st_size = syms[i].st_size & 0xffffffff; |
|
192 |
#endif |
|
193 |
} |
|
194 |
/* String table */ |
|
195 |
str = glue(process_strtab, SZ)(ehdr, fd, &strtab, &symtab); |
|
196 |
if (!str) |
|
197 |
goto error_freesyms; |
|
198 |
|
|
199 |
/* Commit */ |
|
200 |
s = qemu_mallocz(sizeof(*s)); |
|
201 |
#if (SZ == 64) |
|
202 |
s->disas_symtab = syms32; |
|
203 |
qemu_free(syms); |
|
204 |
#else |
|
205 |
s->disas_symtab = syms; |
|
206 |
#endif |
|
207 |
s->disas_num_syms = nsyms; |
|
208 |
s->disas_strtab = str; |
|
209 |
s->next = syminfos; |
|
210 |
syminfos = s; |
|
211 |
return; |
|
212 |
error_freesyms: |
|
213 |
#if (SZ == 64) |
|
214 |
qemu_free(syms32); |
|
215 |
#endif |
|
216 |
qemu_free(syms); |
|
217 |
return; |
|
218 |
} |
b/hw/magic-load.c | ||
---|---|---|
56 | 56 |
|
57 | 57 |
#include "elf.h" |
58 | 58 |
|
59 |
#ifdef BSWAP_NEEDED |
|
60 |
static void bswap_ehdr(Elf32_Ehdr *ehdr) |
|
61 |
{ |
|
62 |
bswap16s(&ehdr->e_type); /* Object file type */ |
|
63 |
bswap16s(&ehdr->e_machine); /* Architecture */ |
|
64 |
bswap32s(&ehdr->e_version); /* Object file version */ |
|
65 |
bswap32s(&ehdr->e_entry); /* Entry point virtual address */ |
|
66 |
bswap32s(&ehdr->e_phoff); /* Program header table file offset */ |
|
67 |
bswap32s(&ehdr->e_shoff); /* Section header table file offset */ |
|
68 |
bswap32s(&ehdr->e_flags); /* Processor-specific flags */ |
|
69 |
bswap16s(&ehdr->e_ehsize); /* ELF header size in bytes */ |
|
70 |
bswap16s(&ehdr->e_phentsize); /* Program header table entry size */ |
|
71 |
bswap16s(&ehdr->e_phnum); /* Program header table entry count */ |
|
72 |
bswap16s(&ehdr->e_shentsize); /* Section header table entry size */ |
|
73 |
bswap16s(&ehdr->e_shnum); /* Section header table entry count */ |
|
74 |
bswap16s(&ehdr->e_shstrndx); /* Section header string table index */ |
|
75 |
} |
|
76 |
|
|
77 |
static void bswap_phdr(Elf32_Phdr *phdr) |
|
78 |
{ |
|
79 |
bswap32s(&phdr->p_type); /* Segment type */ |
|
80 |
bswap32s(&phdr->p_offset); /* Segment file offset */ |
|
81 |
bswap32s(&phdr->p_vaddr); /* Segment virtual address */ |
|
82 |
bswap32s(&phdr->p_paddr); /* Segment physical address */ |
|
83 |
bswap32s(&phdr->p_filesz); /* Segment size in file */ |
|
84 |
bswap32s(&phdr->p_memsz); /* Segment size in memory */ |
|
85 |
bswap32s(&phdr->p_flags); /* Segment flags */ |
|
86 |
bswap32s(&phdr->p_align); /* Segment alignment */ |
|
87 |
} |
|
88 |
|
|
89 |
static void bswap_shdr(Elf32_Shdr *shdr) |
|
90 |
{ |
|
91 |
bswap32s(&shdr->sh_name); |
|
92 |
bswap32s(&shdr->sh_type); |
|
93 |
bswap32s(&shdr->sh_flags); |
|
94 |
bswap32s(&shdr->sh_addr); |
|
95 |
bswap32s(&shdr->sh_offset); |
|
96 |
bswap32s(&shdr->sh_size); |
|
97 |
bswap32s(&shdr->sh_link); |
|
98 |
bswap32s(&shdr->sh_info); |
|
99 |
bswap32s(&shdr->sh_addralign); |
|
100 |
bswap32s(&shdr->sh_entsize); |
|
101 |
} |
|
102 |
|
|
103 |
static void bswap_sym(Elf32_Sym *sym) |
|
104 |
{ |
|
105 |
bswap32s(&sym->st_name); |
|
106 |
bswap32s(&sym->st_value); |
|
107 |
bswap32s(&sym->st_size); |
|
108 |
bswap16s(&sym->st_shndx); |
|
109 |
} |
|
110 |
#else |
|
111 |
#define bswap_ehdr(e) do { } while (0) |
|
112 |
#define bswap_phdr(e) do { } while (0) |
|
113 |
#define bswap_shdr(e) do { } while (0) |
|
114 |
#define bswap_sym(e) do { } while (0) |
|
59 |
#ifndef BSWAP_NEEDED |
|
60 |
#define bswap_ehdr32(e) do { } while (0) |
|
61 |
#define bswap_phdr32(e) do { } while (0) |
|
62 |
#define bswap_shdr32(e) do { } while (0) |
|
63 |
#define bswap_sym32(e) do { } while (0) |
|
64 |
#ifdef TARGET_SPARC64 |
|
65 |
#define bswap_ehdr64(e) do { } while (0) |
|
66 |
#define bswap_phdr64(e) do { } while (0) |
|
67 |
#define bswap_shdr64(e) do { } while (0) |
|
68 |
#define bswap_sym64(e) do { } while (0) |
|
69 |
#endif |
|
115 | 70 |
#endif |
116 | 71 |
|
117 |
static int find_phdr(struct elfhdr *ehdr, int fd, struct elf_phdr *phdr, uint32_t type) |
|
118 |
{ |
|
119 |
int i, retval; |
|
120 |
|
|
121 |
retval = lseek(fd, ehdr->e_phoff, SEEK_SET); |
|
122 |
if (retval < 0) |
|
123 |
return -1; |
|
124 |
|
|
125 |
for (i = 0; i < ehdr->e_phnum; i++) { |
|
126 |
retval = read(fd, phdr, sizeof(*phdr)); |
|
127 |
if (retval < 0) |
|
128 |
return -1; |
|
129 |
bswap_phdr(phdr); |
|
130 |
if (phdr->p_type == type) |
|
131 |
return 0; |
|
132 |
} |
|
133 |
return -1; |
|
134 |
} |
|
135 |
|
|
136 |
static void *find_shdr(struct elfhdr *ehdr, int fd, struct elf_shdr *shdr, uint32_t type) |
|
137 |
{ |
|
138 |
int i, retval; |
|
139 |
|
|
140 |
retval = lseek(fd, ehdr->e_shoff, SEEK_SET); |
|
141 |
if (retval < 0) |
|
142 |
return NULL; |
|
143 |
|
|
144 |
for (i = 0; i < ehdr->e_shnum; i++) { |
|
145 |
retval = read(fd, shdr, sizeof(*shdr)); |
|
146 |
if (retval < 0) |
|
147 |
return NULL; |
|
148 |
bswap_shdr(shdr); |
|
149 |
if (shdr->sh_type == type) |
|
150 |
return qemu_malloc(shdr->sh_size); |
|
151 |
} |
|
152 |
return NULL; |
|
153 |
} |
|
154 |
|
|
155 |
static void *find_strtab(struct elfhdr *ehdr, int fd, struct elf_shdr *shdr, struct elf_shdr *symtab) |
|
156 |
{ |
|
157 |
int retval; |
|
158 |
|
|
159 |
retval = lseek(fd, ehdr->e_shoff + sizeof(struct elf_shdr) * symtab->sh_link, SEEK_SET); |
|
160 |
if (retval < 0) |
|
161 |
return NULL; |
|
162 |
|
|
163 |
retval = read(fd, shdr, sizeof(*shdr)); |
|
164 |
if (retval < 0) |
|
165 |
return NULL; |
|
166 |
bswap_shdr(shdr); |
|
167 |
if (shdr->sh_type == SHT_STRTAB) |
|
168 |
return qemu_malloc(shdr->sh_size);; |
|
169 |
return NULL; |
|
170 |
} |
|
171 |
|
|
172 |
static int read_program(int fd, struct elf_phdr *phdr, void *dst, uint32_t entry) |
|
173 |
{ |
|
174 |
int retval; |
|
175 |
retval = lseek(fd, phdr->p_offset + entry - phdr->p_vaddr, SEEK_SET); |
|
176 |
if (retval < 0) |
|
177 |
return -1; |
|
178 |
return read(fd, dst, phdr->p_filesz); |
|
179 |
} |
|
180 |
|
|
181 |
static int read_section(int fd, struct elf_shdr *s, void *dst) |
|
182 |
{ |
|
183 |
int retval; |
|
184 |
|
|
185 |
retval = lseek(fd, s->sh_offset, SEEK_SET); |
|
186 |
if (retval < 0) |
|
187 |
return -1; |
|
188 |
retval = read(fd, dst, s->sh_size); |
|
189 |
if (retval < 0) |
|
190 |
return -1; |
|
191 |
return 0; |
|
192 |
} |
|
193 |
|
|
194 |
static void *process_section(struct elfhdr *ehdr, int fd, struct elf_shdr *shdr, uint32_t type) |
|
195 |
{ |
|
196 |
void *dst; |
|
197 |
|
|
198 |
dst = find_shdr(ehdr, fd, shdr, type); |
|
199 |
if (!dst) |
|
200 |
goto error; |
|
201 |
|
|
202 |
if (read_section(fd, shdr, dst)) |
|
203 |
goto error; |
|
204 |
return dst; |
|
205 |
error: |
|
206 |
qemu_free(dst); |
|
207 |
return NULL; |
|
208 |
} |
|
209 |
|
|
210 |
static void *process_strtab(struct elfhdr *ehdr, int fd, struct elf_shdr *shdr, struct elf_shdr *symtab) |
|
211 |
{ |
|
212 |
void *dst; |
|
213 |
|
|
214 |
dst = find_strtab(ehdr, fd, shdr, symtab); |
|
215 |
if (!dst) |
|
216 |
goto error; |
|
217 |
|
|
218 |
if (read_section(fd, shdr, dst)) |
|
219 |
goto error; |
|
220 |
return dst; |
|
221 |
error: |
|
222 |
qemu_free(dst); |
|
223 |
return NULL; |
|
224 |
} |
|
225 |
|
|
226 |
static void load_symbols(struct elfhdr *ehdr, int fd) |
|
227 |
{ |
|
228 |
struct elf_shdr symtab, strtab; |
|
229 |
struct elf_sym *syms; |
|
230 |
struct syminfo *s; |
|
231 |
int nsyms, i; |
|
232 |
char *str; |
|
233 |
|
|
234 |
/* Symbol table */ |
|
235 |
syms = process_section(ehdr, fd, &symtab, SHT_SYMTAB); |
|
236 |
if (!syms) |
|
237 |
return; |
|
238 |
|
|
239 |
nsyms = symtab.sh_size / sizeof(struct elf_sym); |
|
240 |
for (i = 0; i < nsyms; i++) |
|
241 |
bswap_sym(&syms[i]); |
|
242 |
|
|
243 |
/* String table */ |
|
244 |
str = process_strtab(ehdr, fd, &strtab, &symtab); |
|
245 |
if (!str) |
|
246 |
goto error_freesyms; |
|
247 |
|
|
248 |
/* Commit */ |
|
249 |
s = qemu_mallocz(sizeof(*s)); |
|
250 |
s->disas_symtab = syms; |
|
251 |
s->disas_num_syms = nsyms; |
|
252 |
s->disas_strtab = str; |
|
253 |
s->next = syminfos; |
|
254 |
syminfos = s; |
|
255 |
return; |
|
256 |
error_freesyms: |
|
257 |
qemu_free(syms); |
|
258 |
return; |
|
259 |
} |
|
72 |
#define SZ 32 |
|
73 |
#define elf_word uint32_t |
|
74 |
#define bswapSZs bswap32s |
|
75 |
#include "elf_ops.h" |
|
76 |
|
|
77 |
#ifdef TARGET_SPARC64 |
|
78 |
#undef elfhdr |
|
79 |
#undef elf_phdr |
|
80 |
#undef elf_shdr |
|
81 |
#undef elf_sym |
|
82 |
#undef elf_note |
|
83 |
#undef elf_word |
|
84 |
#undef bswapSZs |
|
85 |
#undef SZ |
|
86 |
#define elfhdr elf64_hdr |
|
87 |
#define elf_phdr elf64_phdr |
|
88 |
#define elf_note elf64_note |
|
89 |
#define elf_shdr elf64_shdr |
|
90 |
#define elf_sym elf64_sym |
|
91 |
#define elf_word uint64_t |
|
92 |
#define bswapSZs bswap64s |
|
93 |
#define SZ 64 |
|
94 |
#include "elf_ops.h" |
|
95 |
#endif |
|
260 | 96 |
|
261 | 97 |
int load_elf(const char *filename, uint8_t *addr) |
262 | 98 |
{ |
263 |
struct elfhdr ehdr; |
|
264 |
struct elf_phdr phdr; |
|
99 |
struct elf32_hdr ehdr; |
|
265 | 100 |
int retval, fd; |
101 |
Elf32_Half machine; |
|
266 | 102 |
|
267 | 103 |
fd = open(filename, O_RDONLY | O_BINARY); |
268 | 104 |
if (fd < 0) |
... | ... | |
272 | 108 |
if (retval < 0) |
273 | 109 |
goto error; |
274 | 110 |
|
275 |
bswap_ehdr(&ehdr); |
|
276 |
|
|
277 | 111 |
if (ehdr.e_ident[0] != 0x7f || ehdr.e_ident[1] != 'E' |
278 |
|| ehdr.e_ident[2] != 'L' || ehdr.e_ident[3] != 'F' |
|
279 |
|| (ehdr.e_machine != EM_SPARC |
|
280 |
&& ehdr.e_machine != EM_SPARC32PLUS)) |
|
112 |
|| ehdr.e_ident[2] != 'L' || ehdr.e_ident[3] != 'F') |
|
281 | 113 |
goto error; |
114 |
machine = tswap16(ehdr.e_machine); |
|
115 |
if (machine == EM_SPARC || machine == EM_SPARC32PLUS) { |
|
116 |
struct elf32_phdr phdr; |
|
282 | 117 |
|
283 |
if (find_phdr(&ehdr, fd, &phdr, PT_LOAD)) |
|
284 |
goto error; |
|
285 |
retval = read_program(fd, &phdr, addr, ehdr.e_entry); |
|
286 |
if (retval < 0) |
|
287 |
goto error; |
|
118 |
bswap_ehdr32(&ehdr); |
|
288 | 119 |
|
289 |
load_symbols(&ehdr, fd); |
|
120 |
if (find_phdr32(&ehdr, fd, &phdr, PT_LOAD)) |
|
121 |
goto error; |
|
122 |
retval = read_program32(fd, &phdr, addr, ehdr.e_entry); |
|
123 |
if (retval < 0) |
|
124 |
goto error; |
|
125 |
load_symbols32(&ehdr, fd); |
|
126 |
} |
|
127 |
#ifdef TARGET_SPARC64 |
|
128 |
else if (machine == EM_SPARCV9) { |
|
129 |
struct elf64_hdr ehdr64; |
|
130 |
struct elf64_phdr phdr; |
|
131 |
|
|
132 |
lseek(fd, 0, SEEK_SET); |
|
133 |
|
|
134 |
retval = read(fd, &ehdr64, sizeof(ehdr64)); |
|
135 |
if (retval < 0) |
|
136 |
goto error; |
|
137 |
|
|
138 |
bswap_ehdr64(&ehdr64); |
|
139 |
|
|
140 |
if (find_phdr64(&ehdr64, fd, &phdr, PT_LOAD)) |
|
141 |
goto error; |
|
142 |
retval = read_program64(fd, &phdr, addr, ehdr64.e_entry); |
|
143 |
if (retval < 0) |
|
144 |
goto error; |
|
145 |
load_symbols64(&ehdr64, fd); |
|
146 |
} |
|
147 |
#endif |
|
290 | 148 |
|
291 | 149 |
close(fd); |
292 | 150 |
return retval; |
b/hw/slavio_intctl.c | ||
---|---|---|
144 | 144 |
switch (saddr) { |
145 | 145 |
case 2: // clear (enable) |
146 | 146 |
// Force clear unused bits |
147 |
val &= ~0x7fb2007f;
|
|
147 |
val &= ~0x4fb2007f;
|
|
148 | 148 |
s->intregm_disabled &= ~val; |
149 | 149 |
DPRINTF("Enabled master irq mask %x, curmask %x\n", val, s->intregm_disabled); |
150 | 150 |
slavio_check_interrupts(s); |
151 | 151 |
break; |
152 | 152 |
case 3: // set (disable, clear pending) |
153 | 153 |
// Force clear unused bits |
154 |
val &= ~0x7fb2007f;
|
|
154 |
val &= ~0x4fb2007f;
|
|
155 | 155 |
s->intregm_disabled |= val; |
156 | 156 |
s->intregm_pending &= ~val; |
157 | 157 |
DPRINTF("Disabled master irq mask %x, curmask %x\n", val, s->intregm_disabled); |
... | ... | |
208 | 208 |
|
209 | 209 |
static const uint32_t intbit_to_level[32] = { |
210 | 210 |
2, 3, 5, 7, 9, 11, 0, 14, 3, 5, 7, 9, 11, 13, 12, 12, |
211 |
6, 0, 4, 10, 8, 0, 11, 0, 0, 0, 0, 0, 15, 0, 0, 0,
|
|
211 |
6, 0, 4, 10, 8, 0, 11, 0, 0, 0, 0, 0, 15, 0, 15, 0,
|
|
212 | 212 |
}; |
213 | 213 |
|
214 | 214 |
static void slavio_check_interrupts(void *opaque) |
b/hw/slavio_misc.c | ||
---|---|---|
1 |
/* |
|
2 |
* QEMU Sparc SLAVIO aux io port emulation |
|
3 |
* |
|
4 |
* Copyright (c) 2005 Fabrice Bellard |
|
5 |
* |
|
6 |
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|
7 |
* of this software and associated documentation files (the "Software"), to deal |
|
8 |
* in the Software without restriction, including without limitation the rights |
|
9 |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
10 |
* copies of the Software, and to permit persons to whom the Software is |
|
11 |
* furnished to do so, subject to the following conditions: |
|
12 |
* |
|
13 |
* The above copyright notice and this permission notice shall be included in |
|
14 |
* all copies or substantial portions of the Software. |
|
15 |
* |
|
16 |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
17 |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
18 |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
|
19 |
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
20 |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
21 |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|
22 |
* THE SOFTWARE. |
|
23 |
*/ |
|
24 |
#include "vl.h" |
|
25 |
/* debug misc */ |
|
26 |
//#define DEBUG_MISC |
|
27 |
|
|
28 |
/* |
|
29 |
* This is the auxio port, chip control and system control part of |
|
30 |
* chip STP2001 (Slave I/O), also produced as NCR89C105. See |
|
31 |
* http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt |
|
32 |
* |
|
33 |
* This also includes the PMC CPU idle controller. |
|
34 |
*/ |
|
35 |
|
|
36 |
#ifdef DEBUG_MISC |
|
37 |
#define MISC_DPRINTF(fmt, args...) \ |
|
38 |
do { printf("MISC: " fmt , ##args); } while (0) |
|
39 |
#else |
|
40 |
#define MISC_DPRINTF(fmt, args...) |
|
41 |
#endif |
|
42 |
|
|
43 |
typedef struct MiscState { |
|
44 |
int irq; |
|
45 |
uint8_t config; |
|
46 |
uint8_t aux1, aux2; |
|
47 |
uint8_t diag, mctrl; |
|
48 |
} MiscState; |
|
49 |
|
|
50 |
#define MISC_MAXADDR 1 |
|
51 |
|
|
52 |
static void slavio_misc_update_irq(void *opaque) |
|
53 |
{ |
|
54 |
MiscState *s = opaque; |
|
55 |
|
|
56 |
if ((s->aux2 & 0x4) && (s->config & 0x8)) { |
|
57 |
pic_set_irq(s->irq, 1); |
|
58 |
} else { |
|
59 |
pic_set_irq(s->irq, 0); |
|
60 |
} |
|
61 |
} |
|
62 |
|
|
63 |
static void slavio_misc_reset(void *opaque) |
|
64 |
{ |
|
65 |
MiscState *s = opaque; |
|
66 |
|
|
67 |
// Diagnostic register not cleared in reset |
|
68 |
s->config = s->aux1 = s->aux2 = s->mctrl = 0; |
|
69 |
} |
|
70 |
|
|
71 |
void slavio_set_power_fail(void *opaque, int power_failing) |
|
72 |
{ |
|
73 |
MiscState *s = opaque; |
|
74 |
|
|
75 |
MISC_DPRINTF("Power fail: %d, config: %d\n", power_failing, s->config); |
|
76 |
if (power_failing && (s->config & 0x8)) { |
|
77 |
s->aux2 |= 0x4; |
|
78 |
} else { |
|
79 |
s->aux2 &= ~0x4; |
|
80 |
} |
|
81 |
slavio_misc_update_irq(s); |
|
82 |
} |
|
83 |
|
|
84 |
static void slavio_misc_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) |
|
85 |
{ |
|
86 |
MiscState *s = opaque; |
|
87 |
|
|
88 |
switch (addr & 0xfff0000) { |
|
89 |
case 0x1800000: |
|
90 |
MISC_DPRINTF("Write config %2.2x\n", val & 0xff); |
|
91 |
s->config = val & 0xff; |
|
92 |
slavio_misc_update_irq(s); |
|
93 |
break; |
|
94 |
case 0x1900000: |
|
95 |
MISC_DPRINTF("Write aux1 %2.2x\n", val & 0xff); |
|
96 |
s->aux1 = val & 0xff; |
|
97 |
break; |
|
98 |
case 0x1910000: |
|
99 |
val &= 0x3; |
|
100 |
MISC_DPRINTF("Write aux2 %2.2x\n", val); |
|
101 |
val |= s->aux2 & 0x4; |
|
102 |
if (val & 0x2) // Clear Power Fail int |
|
103 |
val &= 0x1; |
|
104 |
s->aux2 = val; |
|
105 |
if (val & 1) |
|
106 |
qemu_system_shutdown_request(); |
|
107 |
slavio_misc_update_irq(s); |
|
108 |
break; |
|
109 |
case 0x1a00000: |
|
110 |
MISC_DPRINTF("Write diag %2.2x\n", val & 0xff); |
|
111 |
s->diag = val & 0xff; |
|
112 |
break; |
|
113 |
case 0x1b00000: |
|
114 |
MISC_DPRINTF("Write modem control %2.2x\n", val & 0xff); |
|
115 |
s->mctrl = val & 0xff; |
|
116 |
break; |
|
117 |
case 0x1f00000: |
|
118 |
MISC_DPRINTF("Write system control %2.2x\n", val & 0xff); |
|
119 |
if (val & 1) |
|
120 |
qemu_system_reset_request(); |
|
121 |
break; |
|
122 |
case 0xa000000: |
|
123 |
MISC_DPRINTF("Write power management %2.2x\n", val & 0xff); |
|
124 |
#if 0 |
|
125 |
// XXX: halting CPU does not work |
|
126 |
raise_exception(EXCP_HLT); |
|
127 |
cpu_loop_exit(); |
|
128 |
#endif |
|
129 |
break; |
|
130 |
} |
|
131 |
} |
|
132 |
|
|
133 |
static uint32_t slavio_misc_mem_readb(void *opaque, target_phys_addr_t addr) |
|
134 |
{ |
|
135 |
MiscState *s = opaque; |
|
136 |
uint32_t ret = 0; |
|
137 |
|
|
138 |
switch (addr & 0xfff0000) { |
|
139 |
case 0x1800000: |
|
140 |
ret = s->config; |
|
141 |
MISC_DPRINTF("Read config %2.2x\n", ret); |
|
142 |
break; |
|
143 |
case 0x1900000: |
|
144 |
ret = s->aux1; |
|
145 |
MISC_DPRINTF("Read aux1 %2.2x\n", ret); |
|
146 |
break; |
|
147 |
case 0x1910000: |
|
148 |
ret = s->aux2; |
|
149 |
MISC_DPRINTF("Read aux2 %2.2x\n", ret); |
|
150 |
break; |
|
151 |
case 0x1a00000: |
|
152 |
ret = s->diag; |
|
153 |
MISC_DPRINTF("Read diag %2.2x\n", ret); |
|
154 |
break; |
|
155 |
case 0x1b00000: |
|
156 |
ret = s->mctrl; |
|
157 |
MISC_DPRINTF("Read modem control %2.2x\n", ret); |
|
158 |
break; |
|
159 |
case 0x1f00000: |
|
160 |
MISC_DPRINTF("Read system control %2.2x\n", ret); |
|
161 |
break; |
|
162 |
case 0xa000000: |
|
163 |
MISC_DPRINTF("Read power management %2.2x\n", ret); |
|
164 |
break; |
|
165 |
} |
|
166 |
return ret; |
|
167 |
} |
|
168 |
|
|
169 |
static CPUReadMemoryFunc *slavio_misc_mem_read[3] = { |
|
170 |
slavio_misc_mem_readb, |
|
171 |
slavio_misc_mem_readb, |
|
172 |
slavio_misc_mem_readb, |
|
173 |
}; |
|
174 |
|
|
175 |
static CPUWriteMemoryFunc *slavio_misc_mem_write[3] = { |
|
176 |
slavio_misc_mem_writeb, |
|
177 |
slavio_misc_mem_writeb, |
|
178 |
slavio_misc_mem_writeb, |
|
179 |
}; |
|
180 |
|
|
181 |
static void slavio_misc_save(QEMUFile *f, void *opaque) |
|
182 |
{ |
|
183 |
MiscState *s = opaque; |
|
184 |
|
|
185 |
qemu_put_be32s(f, &s->irq); |
|
186 |
qemu_put_8s(f, &s->config); |
|
187 |
qemu_put_8s(f, &s->aux1); |
|
188 |
qemu_put_8s(f, &s->aux2); |
|
189 |
qemu_put_8s(f, &s->diag); |
|
190 |
qemu_put_8s(f, &s->mctrl); |
|
191 |
} |
|
192 |
|
|
193 |
static int slavio_misc_load(QEMUFile *f, void *opaque, int version_id) |
|
194 |
{ |
|
195 |
MiscState *s = opaque; |
|
196 |
|
|
197 |
if (version_id != 1) |
|
198 |
return -EINVAL; |
|
199 |
|
|
200 |
qemu_get_be32s(f, &s->irq); |
|
201 |
qemu_get_8s(f, &s->config); |
|
202 |
qemu_get_8s(f, &s->aux1); |
|
203 |
qemu_get_8s(f, &s->aux2); |
|
204 |
qemu_get_8s(f, &s->diag); |
|
205 |
qemu_get_8s(f, &s->mctrl); |
|
206 |
return 0; |
|
207 |
} |
|
208 |
|
|
209 |
void *slavio_misc_init(uint32_t base, int irq) |
|
210 |
{ |
|
211 |
int slavio_misc_io_memory; |
|
212 |
MiscState *s; |
|
213 |
|
|
214 |
s = qemu_mallocz(sizeof(MiscState)); |
|
215 |
if (!s) |
|
216 |
return NULL; |
|
217 |
|
|
218 |
slavio_misc_io_memory = cpu_register_io_memory(0, slavio_misc_mem_read, slavio_misc_mem_write, s); |
|
219 |
// Slavio control |
|
220 |
cpu_register_physical_memory(base + 0x1800000, MISC_MAXADDR, slavio_misc_io_memory); |
|
221 |
// AUX 1 |
|
222 |
cpu_register_physical_memory(base + 0x1900000, MISC_MAXADDR, slavio_misc_io_memory); |
|
223 |
// AUX 2 |
|
224 |
cpu_register_physical_memory(base + 0x1910000, MISC_MAXADDR, slavio_misc_io_memory); |
|
225 |
// Diagnostics |
|
226 |
cpu_register_physical_memory(base + 0x1a00000, MISC_MAXADDR, slavio_misc_io_memory); |
|
227 |
// Modem control |
|
228 |
cpu_register_physical_memory(base + 0x1b00000, MISC_MAXADDR, slavio_misc_io_memory); |
|
229 |
// System control |
|
230 |
cpu_register_physical_memory(base + 0x1f00000, MISC_MAXADDR, slavio_misc_io_memory); |
|
231 |
// Power management |
|
232 |
cpu_register_physical_memory(base + 0xa000000, MISC_MAXADDR, slavio_misc_io_memory); |
|
233 |
|
|
234 |
s->irq = irq; |
|
235 |
|
|
236 |
register_savevm("slavio_misc", base, 1, slavio_misc_save, slavio_misc_load, s); |
|
237 |
qemu_register_reset(slavio_misc_reset, s); |
|
238 |
slavio_misc_reset(s); |
|
239 |
return s; |
|
240 |
} |
b/hw/sun4m.c | ||
---|---|---|
37 | 37 |
// bits |
38 | 38 |
#define PHYS_JJ_IOMMU 0x10000000 /* I/O MMU */ |
39 | 39 |
#define PHYS_JJ_TCX_FB 0x50000000 /* TCX frame buffer */ |
40 |
#define PHYS_JJ_SLAVIO 0x70000000 /* Slavio base */ |
|
40 | 41 |
#define PHYS_JJ_ESPDMA 0x78400000 /* ESP DMA controller */ |
41 | 42 |
#define PHYS_JJ_ESP 0x78800000 /* ESP SCSI */ |
42 | 43 |
#define PHYS_JJ_ESP_IRQ 18 |
... | ... | |
55 | 56 |
#define PHYS_JJ_SER_IRQ 15 |
56 | 57 |
#define PHYS_JJ_FDC 0x71400000 /* Floppy */ |
57 | 58 |
#define PHYS_JJ_FLOPPY_IRQ 22 |
59 |
#define PHYS_JJ_ME_IRQ 30 /* Module error, power fail */ |
|
58 | 60 |
|
59 | 61 |
/* TSC handling */ |
60 | 62 |
|
... | ... | |
202 | 204 |
return iommu_translate_local(iommu, addr); |
203 | 205 |
} |
204 | 206 |
|
207 |
static void *slavio_misc; |
|
208 |
|
|
209 |
void qemu_system_powerdown(void) |
|
210 |
{ |
|
211 |
slavio_set_power_fail(slavio_misc, 1); |
|
212 |
} |
|
213 |
|
|
205 | 214 |
/* Sun4m hardware initialisation */ |
206 | 215 |
static void sun4m_init(int ram_size, int vga_ram_size, int boot_device, |
207 | 216 |
DisplayState *ds, const char **fd_filename, int snapshot, |
... | ... | |
230 | 239 |
slavio_serial_init(PHYS_JJ_SER, PHYS_JJ_SER_IRQ, serial_hds[1], serial_hds[0]); |
231 | 240 |
fdctrl_init(PHYS_JJ_FLOPPY_IRQ, 0, 1, PHYS_JJ_FDC, fd_table); |
232 | 241 |
esp_init(bs_table, PHYS_JJ_ESP_IRQ, PHYS_JJ_ESP, PHYS_JJ_ESPDMA); |
242 |
slavio_misc = slavio_misc_init(PHYS_JJ_SLAVIO, PHYS_JJ_ME_IRQ); |
|
233 | 243 |
|
234 | 244 |
prom_offset = ram_size + vram_size; |
235 | 245 |
|
b/hw/sun4u.c | ||
---|---|---|
1 |
/* |
|
2 |
* QEMU Sun4u System Emulator |
|
3 |
* |
|
4 |
* Copyright (c) 2005 Fabrice Bellard |
|
5 |
* |
|
6 |
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|
7 |
* of this software and associated documentation files (the "Software"), to deal |
|
8 |
* in the Software without restriction, including without limitation the rights |
|
9 |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
10 |
* copies of the Software, and to permit persons to whom the Software is |
|
11 |
* furnished to do so, subject to the following conditions: |
|
12 |
* |
|
13 |
* The above copyright notice and this permission notice shall be included in |
|
14 |
* all copies or substantial portions of the Software. |
|
15 |
* |
|
16 |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
17 |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
18 |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
|
19 |
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
20 |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
21 |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|
22 |
* THE SOFTWARE. |
|
23 |
*/ |
|
24 |
#include "vl.h" |
|
25 |
#include "m48t08.h" |
|
26 |
|
|
27 |
#define KERNEL_LOAD_ADDR 0x00004000 |
|
28 |
#define CMDLINE_ADDR 0x007ff000 |
|
29 |
#define INITRD_LOAD_ADDR 0x00800000 |
|
30 |
#define PROM_ADDR 0xffd00000 |
|
31 |
#define PROM_FILENAMEB "proll-sparc64.bin" |
|
32 |
#define PROM_FILENAMEE "proll-sparc64.elf" |
|
33 |
#define PHYS_JJ_EEPROM 0x71200000 /* m48t08 */ |
|
34 |
#define PHYS_JJ_IDPROM_OFF 0x1FD8 |
|
35 |
#define PHYS_JJ_EEPROM_SIZE 0x2000 |
|
36 |
// IRQs are not PIL ones, but master interrupt controller register |
|
37 |
// bits |
|
38 |
#define PHYS_JJ_MS_KBD 0x71000000 /* Mouse and keyboard */ |
|
39 |
#define PHYS_JJ_MS_KBD_IRQ 14 |
|
40 |
#define PHYS_JJ_SER 0x71100000 /* Serial */ |
|
41 |
#define PHYS_JJ_SER_IRQ 15 |
|
42 |
|
|
43 |
/* TSC handling */ |
|
44 |
|
|
45 |
uint64_t cpu_get_tsc() |
|
46 |
{ |
|
47 |
return qemu_get_clock(vm_clock); |
|
48 |
} |
|
49 |
|
|
50 |
int DMA_get_channel_mode (int nchan) |
|
51 |
{ |
|
52 |
return 0; |
|
53 |
} |
|
54 |
int DMA_read_memory (int nchan, void *buf, int pos, int size) |
|
55 |
{ |
|
56 |
return 0; |
|
57 |
} |
|
58 |
int DMA_write_memory (int nchan, void *buf, int pos, int size) |
|
59 |
{ |
|
60 |
return 0; |
|
61 |
} |
|
62 |
void DMA_hold_DREQ (int nchan) {} |
|
63 |
void DMA_release_DREQ (int nchan) {} |
|
64 |
void DMA_schedule(int nchan) {} |
|
65 |
void DMA_run (void) {} |
|
66 |
void DMA_init (int high_page_enable) {} |
|
67 |
void DMA_register_channel (int nchan, |
|
68 |
DMA_transfer_handler transfer_handler, |
|
69 |
void *opaque) |
|
70 |
{ |
|
71 |
} |
|
72 |
|
|
73 |
static void nvram_set_word (m48t08_t *nvram, uint32_t addr, uint16_t value) |
|
74 |
{ |
|
75 |
m48t08_write(nvram, addr++, (value >> 8) & 0xff); |
|
76 |
m48t08_write(nvram, addr++, value & 0xff); |
|
77 |
} |
|
78 |
|
|
79 |
static void nvram_set_lword (m48t08_t *nvram, uint32_t addr, uint32_t value) |
|
80 |
{ |
|
81 |
m48t08_write(nvram, addr++, value >> 24); |
|
82 |
m48t08_write(nvram, addr++, (value >> 16) & 0xff); |
|
83 |
m48t08_write(nvram, addr++, (value >> 8) & 0xff); |
|
84 |
m48t08_write(nvram, addr++, value & 0xff); |
|
85 |
} |
|
86 |
|
|
87 |
static void nvram_set_string (m48t08_t *nvram, uint32_t addr, |
|
88 |
const unsigned char *str, uint32_t max) |
|
89 |
{ |
|
90 |
unsigned int i; |
|
91 |
|
|
92 |
for (i = 0; i < max && str[i] != '\0'; i++) { |
|
93 |
m48t08_write(nvram, addr + i, str[i]); |
|
94 |
} |
|
95 |
m48t08_write(nvram, addr + max - 1, '\0'); |
|
96 |
} |
|
97 |
|
|
98 |
static m48t08_t *nvram; |
|
99 |
|
|
100 |
extern int nographic; |
|
101 |
|
|
102 |
static void nvram_init(m48t08_t *nvram, uint8_t *macaddr, const char *cmdline, |
|
103 |
int boot_device, uint32_t RAM_size, |
|
104 |
uint32_t kernel_size, |
|
105 |
int width, int height, int depth) |
|
106 |
{ |
|
107 |
unsigned char tmp = 0; |
|
108 |
int i, j; |
|
109 |
|
|
110 |
// Try to match PPC NVRAM |
|
111 |
nvram_set_string(nvram, 0x00, "QEMU_BIOS", 16); |
|
112 |
nvram_set_lword(nvram, 0x10, 0x00000001); /* structure v1 */ |
|
113 |
// NVRAM_size, arch not applicable |
|
114 |
m48t08_write(nvram, 0x2F, nographic & 0xff); |
|
115 |
nvram_set_lword(nvram, 0x30, RAM_size); |
|
116 |
m48t08_write(nvram, 0x34, boot_device & 0xff); |
|
117 |
nvram_set_lword(nvram, 0x38, KERNEL_LOAD_ADDR); |
|
118 |
nvram_set_lword(nvram, 0x3C, kernel_size); |
|
119 |
if (cmdline) { |
|
120 |
strcpy(phys_ram_base + CMDLINE_ADDR, cmdline); |
|
121 |
nvram_set_lword(nvram, 0x40, CMDLINE_ADDR); |
|
122 |
nvram_set_lword(nvram, 0x44, strlen(cmdline)); |
|
123 |
} |
|
124 |
// initrd_image, initrd_size passed differently |
|
125 |
nvram_set_word(nvram, 0x54, width); |
|
126 |
nvram_set_word(nvram, 0x56, height); |
|
127 |
nvram_set_word(nvram, 0x58, depth); |
|
128 |
|
|
129 |
// Sun4m specific use |
|
130 |
i = 0x1fd8; |
|
131 |
m48t08_write(nvram, i++, 0x01); |
|
132 |
m48t08_write(nvram, i++, 0x80); /* Sun4m OBP */ |
|
133 |
j = 0; |
|
134 |
m48t08_write(nvram, i++, macaddr[j++]); |
|
135 |
m48t08_write(nvram, i++, macaddr[j++]); |
|
136 |
m48t08_write(nvram, i++, macaddr[j++]); |
|
137 |
m48t08_write(nvram, i++, macaddr[j++]); |
|
138 |
m48t08_write(nvram, i++, macaddr[j++]); |
|
139 |
m48t08_write(nvram, i, macaddr[j]); |
|
140 |
|
|
141 |
/* Calculate checksum */ |
|
142 |
for (i = 0x1fd8; i < 0x1fe7; i++) { |
|
143 |
tmp ^= m48t08_read(nvram, i); |
|
144 |
} |
|
145 |
m48t08_write(nvram, 0x1fe7, tmp); |
|
146 |
} |
|
147 |
|
|
148 |
void pic_info() |
|
149 |
{ |
|
150 |
} |
|
151 |
|
|
152 |
void irq_info() |
|
153 |
{ |
|
154 |
} |
|
155 |
|
|
156 |
void pic_set_irq(int irq, int level) |
|
157 |
{ |
|
158 |
} |
|
159 |
|
|
160 |
void vga_update_display() |
|
161 |
{ |
|
162 |
} |
|
163 |
|
|
164 |
void vga_invalidate_display() |
|
165 |
{ |
|
166 |
} |
|
167 |
|
|
168 |
void vga_screen_dump(const char *filename) |
|
169 |
{ |
|
170 |
} |
|
171 |
|
|
172 |
void qemu_system_powerdown(void) |
|
173 |
{ |
|
174 |
} |
|
175 |
|
|
176 |
/* Sun4u hardware initialisation */ |
|
177 |
static void sun4u_init(int ram_size, int vga_ram_size, int boot_device, |
|
178 |
DisplayState *ds, const char **fd_filename, int snapshot, |
|
179 |
const char *kernel_filename, const char *kernel_cmdline, |
|
180 |
const char *initrd_filename) |
|
181 |
{ |
|
182 |
char buf[1024]; |
|
183 |
int ret, linux_boot; |
|
184 |
unsigned int i; |
|
185 |
long vram_size = 0x100000, prom_offset, initrd_size, kernel_size; |
|
186 |
|
|
187 |
linux_boot = (kernel_filename != NULL); |
|
188 |
|
|
189 |
/* allocate RAM */ |
|
190 |
cpu_register_physical_memory(0, ram_size, 0); |
|
191 |
|
|
192 |
nvram = m48t08_init(PHYS_JJ_EEPROM, PHYS_JJ_EEPROM_SIZE); |
|
193 |
// Slavio TTYA (base+4, Linux ttyS0) is the first Qemu serial device |
|
194 |
// Slavio TTYB (base+0, Linux ttyS1) is the second Qemu serial device |
|
195 |
slavio_serial_init(PHYS_JJ_SER, PHYS_JJ_SER_IRQ, serial_hds[1], serial_hds[0]); |
|
196 |
|
|
197 |
prom_offset = ram_size + vram_size; |
|
198 |
|
|
199 |
snprintf(buf, sizeof(buf), "%s/%s", bios_dir, PROM_FILENAMEE); |
|
200 |
ret = load_elf(buf, phys_ram_base + prom_offset); |
|
201 |
if (ret < 0) { |
|
202 |
snprintf(buf, sizeof(buf), "%s/%s", bios_dir, PROM_FILENAMEB); |
|
203 |
ret = load_image(buf, phys_ram_base + prom_offset); |
|
204 |
} |
|
205 |
if (ret < 0) { |
|
206 |
fprintf(stderr, "qemu: could not load prom '%s'\n", |
|
207 |
buf); |
|
208 |
exit(1); |
|
209 |
} |
|
210 |
cpu_register_physical_memory(PROM_ADDR, (ret + TARGET_PAGE_SIZE) & TARGET_PAGE_MASK, |
|
211 |
prom_offset | IO_MEM_ROM); |
|
212 |
|
|
213 |
kernel_size = 0; |
|
214 |
if (linux_boot) { |
|
215 |
kernel_size = load_elf(kernel_filename, phys_ram_base + KERNEL_LOAD_ADDR); |
|
216 |
if (kernel_size < 0) |
|
217 |
kernel_size = load_aout(kernel_filename, phys_ram_base + KERNEL_LOAD_ADDR); |
|
218 |
if (kernel_size < 0) |
|
219 |
kernel_size = load_image(kernel_filename, phys_ram_base + KERNEL_LOAD_ADDR); |
|
220 |
if (kernel_size < 0) { |
|
221 |
fprintf(stderr, "qemu: could not load kernel '%s'\n", |
|
222 |
kernel_filename); |
|
223 |
exit(1); |
|
224 |
} |
|
225 |
|
|
226 |
/* load initrd */ |
|
227 |
initrd_size = 0; |
|
228 |
if (initrd_filename) { |
|
229 |
initrd_size = load_image(initrd_filename, phys_ram_base + INITRD_LOAD_ADDR); |
|
230 |
if (initrd_size < 0) { |
|
231 |
fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", |
|
232 |
initrd_filename); |
|
233 |
exit(1); |
|
234 |
} |
|
235 |
} |
|
236 |
if (initrd_size > 0) { |
|
237 |
for (i = 0; i < 64 * TARGET_PAGE_SIZE; i += TARGET_PAGE_SIZE) { |
|
238 |
if (ldl_raw(phys_ram_base + KERNEL_LOAD_ADDR + i) |
|
239 |
== 0x48647253) { // HdrS |
|
240 |
stl_raw(phys_ram_base + KERNEL_LOAD_ADDR + i + 16, INITRD_LOAD_ADDR); |
|
241 |
stl_raw(phys_ram_base + KERNEL_LOAD_ADDR + i + 20, initrd_size); |
|
242 |
break; |
|
243 |
} |
|
244 |
} |
|
245 |
} |
|
246 |
} |
|
247 |
nvram_init(nvram, (uint8_t *)&nd_table[0].macaddr, kernel_cmdline, boot_device, ram_size, kernel_size, graphic_width, graphic_height, graphic_depth); |
|
248 |
} |
|
249 |
|
|
250 |
QEMUMachine sun4u_machine = { |
|
251 |
"sun4u", |
|
252 |
"Sun4u platform", |
|
253 |
sun4u_init, |
|
254 |
}; |
b/linux-user/main.c | ||
---|---|---|
552 | 552 |
env->pc = env->npc; |
553 | 553 |
env->npc = env->npc + 4; |
554 | 554 |
break; |
555 |
#ifndef TARGET_SPARC64 |
|
555 | 556 |
case TT_WIN_OVF: /* window overflow */ |
556 | 557 |
save_window(env); |
557 | 558 |
break; |
... | ... | |
569 | 570 |
queue_signal(info.si_signo, &info); |
570 | 571 |
} |
571 | 572 |
break; |
573 |
#else |
|
574 |
// XXX |
|
575 |
#endif |
|
572 | 576 |
case 0x100: // XXX, why do we get these? |
573 | 577 |
break; |
574 | 578 |
case EXCP_DEBUG: |
b/monitor.c | ||
---|---|---|
767 | 767 |
qemu_system_reset_request(); |
768 | 768 |
} |
769 | 769 |
|
770 |
static void do_system_powerdown(void) |
|
771 |
{ |
|
772 |
qemu_system_powerdown_request(); |
|
773 |
} |
|
774 |
|
|
770 | 775 |
#if defined(TARGET_I386) |
771 | 776 |
static void print_pte(uint32_t addr, uint32_t pte, uint32_t mask) |
772 | 777 |
{ |
... | ... | |
922 | 927 |
"keys", "send keys to the VM (e.g. 'sendkey ctrl-alt-f1')" }, |
923 | 928 |
{ "system_reset", "", do_system_reset, |
924 | 929 |
"", "reset the system" }, |
930 |
{ "system_powerdown", "", do_system_powerdown, |
|
931 |
"", "send system power down event" }, |
|
925 | 932 |
{ "sum", "ii", do_sum, |
926 | 933 |
"addr size", "compute the checksum of a memory region" }, |
927 | 934 |
{ NULL, NULL, }, |
b/qemu-doc.texi | ||
---|---|---|
42 | 42 |
@item PC (x86 processor) |
43 | 43 |
@item PREP (PowerPC processor) |
44 | 44 |
@item PowerMac (PowerPC processor, in progress) |
45 |
@item Sun4m (Sparc processor, in progress) |
|
45 |
@item Sun4m (32-bit Sparc processor) |
|
46 |
@item Sun4u (64-bit Sparc processor, in progress) |
|
46 | 47 |
@end itemize |
47 | 48 |
|
48 |
For user emulation, x86, PowerPC, ARM, and SPARC CPUs are supported.
|
|
49 |
For user emulation, x86, PowerPC, ARM, and Sparc32/64 CPUs are supported.
|
|
49 | 50 |
|
50 | 51 |
@chapter Installation |
51 | 52 |
|
... | ... | |
999 | 1000 |
More information is available at |
1000 | 1001 |
@url{http://jocelyn.mayer.free.fr/qemu-ppc/}. |
1001 | 1002 |
|
1002 |
@chapter Sparc System emulator invocation |
|
1003 |
@chapter Sparc32 System emulator invocation
|
|
1003 | 1004 |
|
1004 | 1005 |
Use the executable @file{qemu-system-sparc} to simulate a JavaStation |
1005 |
(sun4m architecture). The emulation is far from complete.
|
|
1006 |
(sun4m architecture). The emulation is somewhat complete.
|
|
1006 | 1007 |
|
1007 | 1008 |
QEMU emulates the following sun4m peripherals: |
1008 | 1009 |
|
1009 | 1010 |
@itemize @minus |
1010 |
@item
|
|
1011 |
@item |
|
1011 | 1012 |
IOMMU |
1012 | 1013 |
@item |
1013 | 1014 |
TCX Frame buffer |
... | ... | |
1016 | 1017 |
@item |
1017 | 1018 |
Non Volatile RAM M48T08 |
1018 | 1019 |
@item |
1019 |
Slave I/O: timers, interrupt controllers, Zilog serial ports |
|
1020 |
Slave I/O: timers, interrupt controllers, Zilog serial ports, keyboard |
|
1021 |
and power/reset logic |
|
1022 |
@item |
|
1023 |
ESP SCSI controller with hard disk and CD-ROM support |
|
1024 |
@item |
|
1025 |
Floppy drive |
|
1020 | 1026 |
@end itemize |
1021 | 1027 |
|
1028 |
The number of peripherals is fixed in the architecture. |
|
1029 |
|
|
1022 | 1030 |
QEMU uses the Proll, a PROM replacement available at |
1023 |
@url{http://people.redhat.com/zaitcev/linux/}. |
|
1031 |
@url{http://people.redhat.com/zaitcev/linux/}. The required |
|
1032 |
QEMU-specific patches are included with the sources. |
|
1033 |
|
|
1034 |
A sample Linux 2.6 series kernel and ram disk image are available on |
|
1035 |
the QEMU web site. Please note that currently neither Linux 2.4 |
|
1036 |
series, NetBSD, nor OpenBSD kernels work. |
|
1037 |
|
|
1038 |
@c man begin OPTIONS |
|
1039 |
|
|
1040 |
The following options are specific to the Sparc emulation: |
|
1041 |
|
|
1042 |
@table @option |
|
1043 |
|
|
1044 |
@item -g WxH |
|
1045 |
|
|
1046 |
Set the initial TCX graphic mode. The default is 1024x768. |
|
1047 |
|
|
1048 |
@end table |
Also available in: Unified diff