summaryrefslogtreecommitdiffstats
path: root/kernel/arch/sparc/prom/memory.c
blob: 3f263a64857d5962c7738ec72b6c9bca16b7d353 (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
/* memory.c: Prom routine for acquiring various bits of information
 *           about RAM on the machine, both virtual and physical.
 *
 * Copyright (C) 1995, 2008 David S. Miller (davem@davemloft.net)
 * Copyright (C) 1997 Michael A. Griffith (grif@acm.org)
 */

#include <linux/kernel.h>
#include <linux/sort.h>
#include <linux/init.h>

#include <asm/openprom.h>
#include <asm/oplib.h>
#include <asm/page.h>

static int __init prom_meminit_v0(void)
{
	struct linux_mlist_v0 *p;
	int index;

	index = 0;
	for (p = *(romvec->pv_v0mem.v0_available); p; p = p->theres_more) {
		sp_banks[index].base_addr = (unsigned long) p->start_adr;
		sp_banks[index].num_bytes = p->num_bytes;
		index++;
	}

	return index;
}

static int __init prom_meminit_v2(void)
{
	struct linux_prom_registers reg[64];
	phandle node;
	int size, num_ents, i;

	node = prom_searchsiblings(prom_getchild(prom_root_node), "memory");
	size = prom_getproperty(node, "available", (char *) reg, sizeof(reg));
	num_ents = size / sizeof(struct linux_prom_registers);

	for (i = 0; i < num_ents; i++) {
		sp_banks[i].base_addr = reg[i].phys_addr;
		sp_banks[i].num_bytes = reg[i].reg_size;
	}

	return num_ents;
}

static int sp_banks_cmp(const void *a, const void *b)
{
	const struct sparc_phys_banks *x = a, *y = b;

	if (x->base_addr > y->base_addr)
		return 1;
	if (x->base_addr < y->base_addr)
		return -1;
	return 0;
}

/* Initialize the memory lists based upon the prom version. */
void __init prom_meminit(void)
{
	int i, num_ents = 0;

	switch (prom_vers) {
	case PROM_V0:
		num_ents = prom_meminit_v0();
		break;

	case PROM_V2:
	case PROM_V3:
		num_ents = prom_meminit_v2();
		break;

	default:
		break;
	}
	sort(sp_banks, num_ents, sizeof(struct sparc_phys_banks),
	     sp_banks_cmp, NULL);

	/* Sentinel.  */
	sp_banks[num_ents].base_addr = 0xdeadbeef;
	sp_banks[num_ents].num_bytes = 0;

	for (i = 0; i < num_ents; i++)
		sp_banks[i].num_bytes &= PAGE_MASK;
}
ess, int rw, int mmu_idx) { CRISCPU *cpu = CRIS_CPU(cs); cs->exception_index = 0xaa; cpu->env.pregs[PR_EDA] = address; cpu_dump_state(cs, stderr, fprintf, 0); return 1; } #else /* !CONFIG_USER_ONLY */ static void cris_shift_ccs(CPUCRISState *env) { uint32_t ccs; /* Apply the ccs shift. */ ccs = env->pregs[PR_CCS]; ccs = ((ccs & 0xc0000000) | ((ccs << 12) >> 2)) & ~0x3ff; env->pregs[PR_CCS] = ccs; } int cris_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, int mmu_idx) { CRISCPU *cpu = CRIS_CPU(cs); CPUCRISState *env = &cpu->env; struct cris_mmu_result res; int prot, miss; int r = -1; target_ulong phy; qemu_log_mask(CPU_LOG_MMU, "%s addr=%" VADDR_PRIx " pc=%x rw=%x\n", __func__, address, env->pc, rw); miss = cris_mmu_translate(&res, env, address & TARGET_PAGE_MASK, rw, mmu_idx, 0); if (miss) { if (cs->exception_index == EXCP_BUSFAULT) { cpu_abort(cs, "CRIS: Illegal recursive bus fault." "addr=%" VADDR_PRIx " rw=%d\n", address, rw); } env->pregs[PR_EDA] = address; cs->exception_index = EXCP_BUSFAULT; env->fault_vector = res.bf_vec; r = 1; } else { /* * Mask off the cache selection bit. The ETRAX busses do not * see the top bit. */ phy = res.phy & ~0x80000000; prot = res.prot; tlb_set_page(cs, address & TARGET_PAGE_MASK, phy, prot, mmu_idx, TARGET_PAGE_SIZE); r = 0; } if (r > 0) { qemu_log_mask(CPU_LOG_MMU, "%s returns %d irqreq=%x addr=%" VADDR_PRIx " phy=%x vec=%x" " pc=%x\n", __func__, r, cs->interrupt_request, address, res.phy, res.bf_vec, env->pc); } return r; } void crisv10_cpu_do_interrupt(CPUState *cs) { CRISCPU *cpu = CRIS_CPU(cs); CPUCRISState *env = &cpu->env; int ex_vec = -1; D_LOG("exception index=%d interrupt_req=%d\n", cs->exception_index, cs->interrupt_request); if (env->dslot) { /* CRISv10 never takes interrupts while in a delay-slot. */ cpu_abort(cs, "CRIS: Interrupt on delay-slot\n"); } assert(!(env->pregs[PR_CCS] & PFIX_FLAG)); switch (cs->exception_index) { case EXCP_BREAK: /* These exceptions are genereated by the core itself. ERP should point to the insn following the brk. */ ex_vec = env->trap_vector; env->pregs[PRV10_BRP] = env->pc; break; case EXCP_NMI: /* NMI is hardwired to vector zero. */ ex_vec = 0; env->pregs[PR_CCS] &= ~M_FLAG_V10; env->pregs[PRV10_BRP] = env->pc; break; case EXCP_BUSFAULT: cpu_abort(cs, "Unhandled busfault"); break; default: /* The interrupt controller gives us the vector. */ ex_vec = env->interrupt_vector; /* Normal interrupts are taken between TB's. env->pc is valid here. */ env->pregs[PR_ERP] = env->pc; break; } if (env->pregs[PR_CCS] & U_FLAG) { /* Swap stack pointers. */ env->pregs[PR_USP] = env->regs[R_SP]; env->regs[R_SP] = env->ksp; } /* Now that we are in kernel mode, load the handlers address. */ env->pc = cpu_ldl_code(env, env->pregs[PR_EBP] + ex_vec * 4); env->locked_irq = 1; env->pregs[PR_CCS] |= F_FLAG_V10; /* set F. */ qemu_log_mask(CPU_LOG_INT, "%s isr=%x vec=%x ccs=%x pid=%d erp=%x\n", __func__, env->pc, ex_vec, env->pregs[PR_CCS], env->pregs[PR_PID], env->pregs[PR_ERP]); } void cris_cpu_do_interrupt(CPUState *cs) { CRISCPU *cpu = CRIS_CPU(cs); CPUCRISState *env = &cpu->env; int ex_vec = -1; D_LOG("exception index=%d interrupt_req=%d\n", cs->exception_index, cs->interrupt_request); switch (cs->exception_index) { case EXCP_BREAK: /* These exceptions are genereated by the core itself. ERP should point to the insn following the brk. */ ex_vec = env->trap_vector; env->pregs[PR_ERP] = env->pc; break; case EXCP_NMI: /* NMI is hardwired to vector zero. */ ex_vec = 0; env->pregs[PR_CCS] &= ~M_FLAG_V32; env->pregs[PR_NRP] = env->pc; break; case EXCP_BUSFAULT: ex_vec = env->fault_vector; env->pregs[PR_ERP] = env->pc; break; default: /* The interrupt controller gives us the vector. */ ex_vec = env->interrupt_vector; /* Normal interrupts are taken between TB's. env->pc is valid here. */ env->pregs[PR_ERP] = env->pc; break; } /* Fill in the IDX field. */ env->pregs[PR_EXS] = (ex_vec & 0xff) << 8; if (env->dslot) { D_LOG("excp isr=%x PC=%x ds=%d SP=%x" " ERP=%x pid=%x ccs=%x cc=%d %x\n", ex_vec, env->pc, env->dslot, env->regs[R_SP], env->pregs[PR_ERP], env->pregs[PR_PID], env->pregs[PR_CCS], env->cc_op, env->cc_mask); /* We loose the btarget, btaken state here so rexec the branch. */ env->pregs[PR_ERP] -= env->dslot; /* Exception starts with dslot cleared. */ env->dslot = 0; } if (env->pregs[PR_CCS] & U_FLAG) { /* Swap stack pointers. */ env->pregs[PR_USP] = env->regs[R_SP]; env->regs[R_SP] = env->ksp; } /* Apply the CRIS CCS shift. Clears U if set. */ cris_shift_ccs(env); /* Now that we are in kernel mode, load the handlers address. This load may not fault, real hw leaves that behaviour as undefined. */ env->pc = cpu_ldl_code(env, env->pregs[PR_EBP] + ex_vec * 4); /* Clear the excption_index to avoid spurios hw_aborts for recursive bus faults. */ cs->exception_index = -1; D_LOG("%s isr=%x vec=%x ccs=%x pid=%d erp=%x\n", __func__, env->pc, ex_vec, env->pregs[PR_CCS], env->pregs[PR_PID], env->pregs[PR_ERP]); } hwaddr cris_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { CRISCPU *cpu = CRIS_CPU(cs); uint32_t phy = addr; struct cris_mmu_result res; int miss; miss = cris_mmu_translate(&res, &cpu->env, addr, 0, 0, 1); /* If D TLB misses, try I TLB. */ if (miss) { miss = cris_mmu_translate(&res, &cpu->env, addr, 2, 0, 1); } if (!miss) { phy = res.phy; } D(fprintf(stderr, "%s %x -> %x\n", __func__, addr, phy)); return phy; } #endif bool cris_cpu_exec_interrupt(CPUState *cs, int interrupt_request) { CPUClass *cc = CPU_GET_CLASS(cs); CRISCPU *cpu = CRIS_CPU(cs); CPUCRISState *env = &cpu->env; bool ret = false; if (interrupt_request & CPU_INTERRUPT_HARD && (env->pregs[PR_CCS] & I_FLAG) && !env->locked_irq) { cs->exception_index = EXCP_IRQ; cc->do_interrupt(cs); ret = true; } if (interrupt_request & CPU_INTERRUPT_NMI) { unsigned int m_flag_archval; if (env->pregs[PR_VR] < 32) { m_flag_archval = M_FLAG_V10; } else { m_flag_archval = M_FLAG_V32; } if ((env->pregs[PR_CCS] & m_flag_archval)) { cs->exception_index = EXCP_NMI; cc->do_interrupt(cs); ret = true; } } return ret; }