#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/clockintr.h>
#include <sys/proc.h>
#include <sys/exec.h>
#include <sys/sysctl.h>
#include <sys/timetc.h>
#include <machine/autoconf.h>
#include <machine/cpu.h>
#include <mips64/cache.h>
#include <mips64/mips_cpu.h>
#include <mips64/mips_opcode.h>
#include <uvm/uvm_extern.h>
#include <dev/clock_subr.h>
void
build_trampoline(vaddr_t addr, vaddr_t dest)
{
const uint32_t insns[] = {
0x3c1a0000,
0x675a0000,
0x001ad438,
0x675a0000,
0x001ad438,
0x675a0000,
0x03400008,
0x00000000
};
uint32_t *dst = (uint32_t *)addr;
const uint32_t *src = insns;
uint32_t a, b, c, d;
d = dest & 0xffff;
dest >>= 16;
if (d & 0x8000)
dest++;
c = dest & 0xffff;
dest >>= 16;
if (c & 0x8000)
dest++;
b = dest & 0xffff;
dest >>= 16;
if (b & 0x8000)
dest++;
a = dest & 0xffff;
*dst++ = *src++ | a;
if (b != 0)
*dst++ = *src++ | b;
else
src++;
*dst++ = *src++;
if (c != 0)
*dst++ = *src++ | c;
else
src++;
*dst++ = *src++;
if (d != 0)
*dst++ = *src++ | d;
else
src++;
*dst++ = *src++;
*dst++ = *src++;
}
register_t protosr = SR_FR_32 | SR_XX | SR_UX | SR_KSU_USER | SR_EXL |
SR_KX | SR_INT_ENAB;
void
setregs(struct proc *p, struct exec_package *pack, u_long stack,
struct ps_strings *arginfo)
{
struct cpu_info *ci = curcpu();
struct trapframe *tf = p->p_md.md_regs;
memset(tf, 0, sizeof *tf);
tf->sp = stack;
tf->pc = pack->ep_entry & ~3;
tf->t9 = pack->ep_entry & ~3;
tf->sr = protosr | (idle_mask & SR_INT_MASK);
if (CPU_HAS_FPU(ci))
p->p_md.md_flags &= ~MDP_FPUSED;
if (ci->ci_fpuproc == p)
ci->ci_fpuproc = NULL;
}
int
exec_md_map(struct proc *p, struct exec_package *pack)
{
#ifdef FPUEMUL
struct cpu_info *ci = curcpu();
vaddr_t va;
int rc;
if (CPU_HAS_FPU(ci))
return 0;
va = 0;
rc = uvm_map(&p->p_vmspace->vm_map, &va, PAGE_SIZE, NULL,
UVM_UNKNOWN_OFFSET, 0,
UVM_MAPFLAG(PROT_NONE, PROT_MASK, MAP_INHERIT_COPY,
MADV_NORMAL, UVM_FLAG_COPYONW));
if (rc != 0)
return rc;
#ifdef DEBUG
printf("%s: p %p fppgva %p\n", __func__, p, (void *)va);
#endif
p->p_md.md_fppgva = va;
#endif
return 0;
}
void
tlb_init(unsigned int tlbsize)
{
tlb_set_page_mask(TLB_PAGE_MASK);
tlb_set_wired(0);
tlb_flush(tlbsize);
#if UPAGES > 1
tlb_set_wired(UPAGES / 2);
#endif
}
void
tlb_asid_wrap(struct cpu_info *ci)
{
tlb_flush(ci->ci_hw.tlbsize);
#if defined(CPU_OCTEON)
Mips_InvalidateICache(ci, 0, ci->ci_l1inst.size);
#endif
}
void (*md_initclock)(void);
void (*md_startclock)(struct cpu_info *);
void (*md_triggerclock)(void);
extern todr_chip_handle_t todr_handle;
void
delay(int n)
{
int dly;
int p, c;
struct cpu_info *ci = curcpu();
uint32_t delayconst;
delayconst = ci->ci_delayconst;
if (delayconst == 0)
delayconst = bootcpu_hwinfo.clock / CP0_CYCLE_DIVIDER;
p = cp0_get_count();
dly = (delayconst / 1000000) * n;
while (dly > 0) {
c = cp0_get_count();
dly -= c - p;
p = c;
}
}
u_int cp0_get_timecount(struct timecounter *);
struct timecounter cp0_timecounter = {
.tc_get_timecount = cp0_get_timecount,
.tc_counter_mask = 0xffffffff,
.tc_frequency = 0,
.tc_name = "CP0",
.tc_quality = 0,
.tc_priv = NULL,
.tc_user = 0,
};
u_int
cp0_get_timecount(struct timecounter *tc)
{
return (cp0_get_count());
}
void
cp0_calibrate(struct cpu_info *ci)
{
struct timeval rtctime;
u_int first_cp0, second_cp0, cycles_per_sec;
int first_sec;
if (todr_handle == NULL)
return;
if (todr_gettime(todr_handle, &rtctime) != 0)
return;
first_sec = rtctime.tv_sec;
do {
first_cp0 = cp0_get_count();
if (todr_gettime(todr_handle, &rtctime) != 0)
return;
} while (rtctime.tv_sec == first_sec);
first_sec = rtctime.tv_sec;
do {
second_cp0 = cp0_get_count();
if (todr_gettime(todr_handle, &rtctime) != 0)
return;
} while (rtctime.tv_sec == first_sec);
cycles_per_sec = second_cp0 - first_cp0;
ci->ci_hw.clock = cycles_per_sec * CP0_CYCLE_DIVIDER;
ci->ci_delayconst = cycles_per_sec;
}
void
cpu_initclocks(void)
{
struct cpu_info *ci = curcpu();
tick = 1000000 / hz;
tick_nsec = 1000000000 / hz;
cp0_calibrate(ci);
#ifndef MULTIPROCESSOR
cpu_has_synced_cp0_count = 1;
#endif
if (cpu_setperf == NULL && cpu_has_synced_cp0_count) {
cp0_timecounter.tc_frequency =
(uint64_t)ci->ci_hw.clock / CP0_CYCLE_DIVIDER;
tc_init(&cp0_timecounter);
}
if (md_initclock != NULL)
(*md_initclock)();
}
void
cpu_startclock(void)
{
#ifdef DIAGNOSTIC
if (md_startclock == NULL)
panic("no clock");
#endif
(*md_startclock)(curcpu());
}
void
setstatclockrate(int newhz)
{
}
int
classify_insn(uint32_t insn)
{
InstFmt inst;
inst.word = insn;
switch (inst.JType.op) {
case OP_SPECIAL:
switch (inst.RType.func) {
case OP_JR:
return INSNCLASS_BRANCH;
case OP_JALR:
return INSNCLASS_CALL;
}
break;
case OP_BCOND:
switch (inst.IType.rt) {
case OP_BLTZ:
case OP_BLTZL:
case OP_BGEZ:
case OP_BGEZL:
return INSNCLASS_BRANCH;
case OP_BLTZAL:
case OP_BLTZALL:
case OP_BGEZAL:
case OP_BGEZALL:
return INSNCLASS_CALL;
}
break;
case OP_JAL:
return INSNCLASS_CALL;
case OP_J:
case OP_BEQ:
case OP_BEQL:
case OP_BNE:
case OP_BNEL:
case OP_BLEZ:
case OP_BLEZL:
case OP_BGTZ:
case OP_BGTZL:
return INSNCLASS_BRANCH;
case OP_COP1:
switch (inst.RType.rs) {
case OP_BC:
return INSNCLASS_BRANCH;
}
break;
}
return INSNCLASS_NEUTRAL;
}
void
unmap_startup(void)
{
extern uint32_t kernel_text[], endboot[];
uint32_t *word = kernel_text;
while (word < endboot)
*word++ = 0x00000034u;
}