#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: sh3_machdep.c,v 1.114 2025/03/16 15:34:59 riastradh Exp $");
#include "opt_ddb.h"
#include "opt_kgdb.h"
#include "opt_memsize.h"
#include "opt_kstack_debug.h"
#include "opt_ptrace.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/exec.h>
#include <sys/kernel.h>
#include <sys/mount.h>
#include <sys/proc.h>
#include <sys/signalvar.h>
#include <sys/ras.h>
#include <sys/syscallargs.h>
#include <sys/ucontext.h>
#include <sys/cpu.h>
#include <sys/bus.h>
#ifdef KGDB
#include <sys/kgdb.h>
#ifndef KGDB_DEVNAME
#define KGDB_DEVNAME "nodev"
#endif
const char kgdb_devname[] = KGDB_DEVNAME;
#endif
#include <uvm/uvm.h>
#include <sh3/cache.h>
#include <sh3/clock.h>
#include <sh3/exception.h>
#include <sh3/locore.h>
#include <sh3/mmu.h>
#include <sh3/pcb.h>
#include <sh3/intr.h>
#include <sh3/ubcreg.h>
struct cpu_info cpu_info_store;
int cpu_arch;
int cpu_product;
struct vm_map *phys_map;
struct pcb *curpcb;
#if !defined(IOM_RAM_BEGIN)
#error "define IOM_RAM_BEGIN"
#elif (IOM_RAM_BEGIN & SH3_P1SEG_BASE) != 0
#error "IOM_RAM_BEGIN is physical address. not P1 address."
#endif
#define VBR (uint8_t *)SH3_PHYS_TO_P1SEG(IOM_RAM_BEGIN)
vaddr_t ram_start = SH3_PHYS_TO_P1SEG(IOM_RAM_BEGIN);
extern char sh_vector_generic[], sh_vector_generic_end[];
extern char sh_vector_interrupt[], sh_vector_interrupt_end[];
#ifdef SH3
extern char sh3_vector_tlbmiss[], sh3_vector_tlbmiss_end[];
#endif
#ifdef SH4
extern char sh4_vector_tlbmiss[], sh4_vector_tlbmiss_end[];
#endif
uint32_t dumpmag = 0x8fca0101;
int dumpsize;
long dumplo;
void
sh_cpu_init(int arch, int product)
{
cpu_arch = arch;
cpu_product = product;
#if defined(SH3) && defined(SH4)
sh_devreg_init();
#endif
sh_cache_init();
sh_mmu_init();
machine_clock_init();
curcpu()->ci_idepth = -1;
intc_init();
memcpy(VBR + 0x100, sh_vector_generic,
sh_vector_generic_end - sh_vector_generic);
#ifdef SH3
if (CPU_IS_SH3)
memcpy(VBR + 0x400, sh3_vector_tlbmiss,
sh3_vector_tlbmiss_end - sh3_vector_tlbmiss);
#endif
#ifdef SH4
if (CPU_IS_SH4)
memcpy(VBR + 0x400, sh4_vector_tlbmiss,
sh4_vector_tlbmiss_end - sh4_vector_tlbmiss);
#endif
memcpy(VBR + 0x600, sh_vector_interrupt,
sh_vector_interrupt_end - sh_vector_interrupt);
if (!SH_HAS_UNIFIED_CACHE)
sh_icache_sync_all();
__asm volatile("ldc %0, vbr" :: "r"(VBR));
__sh_switch_resume = CPU_IS_SH3 ? sh3_switch_resume : sh4_switch_resume;
uvm_md_init();
#if defined(PTRACE_HOOKS) || defined(DDB)
_reg_write_2(SH_(BBRA), 0);
_reg_write_2(SH_(BBRB), 0);
#ifdef SH3
if (CPU_IS_SH3) {
_reg_write_4(SH3_BRCR, (UBC_CTL_A_AFTER_INSN
| SH3_UBC_CTL_A_MASK_ASID));
_reg_write_4(SH3_BAMRA, 0x00000000);
}
#endif
#ifdef SH4
if (CPU_IS_SH4) {
_reg_write_2(SH4_BRCR, UBC_CTL_A_AFTER_INSN);
_reg_write_1(SH4_BAMRA, SH4_UBC_MASK_NONE | SH4_UBC_MASK_ASID);
}
#endif
#endif
}
void
sh_proc0_init(void)
{
struct switchframe *sf;
vaddr_t u;
u = uvm_pageboot_alloc(USPACE);
memset((void *)u, 0, USPACE);
uvm_lwp_setuarea(&lwp0, u);
curpcb = lwp_getpcb(&lwp0);
lwp0.l_md.md_pcb = curpcb;
sf = &curpcb->pcb_sf;
#ifdef KSTACK_DEBUG
memset((char *)(u + sizeof(struct pcb)), 0x5a,
PAGE_SIZE - sizeof(struct pcb));
memset((char *)(u + PAGE_SIZE), 0xa5, USPACE - PAGE_SIZE);
memset(sf, 0xb4, sizeof(struct switchframe));
#endif
sf->sf_r6_bank = u + PAGE_SIZE;
sf->sf_r7_bank = sf->sf_r15 = u + USPACE;
__asm volatile("ldc %0, r6_bank" :: "r"(sf->sf_r6_bank));
__asm volatile("ldc %0, r7_bank" :: "r"(sf->sf_r7_bank));
lwp0.l_md.md_regs = (struct trapframe *)sf->sf_r6_bank - 1;
}
void
sh_startup(void)
{
vaddr_t minaddr, maxaddr;
char pbuf[9];
const char *model = cpu_getmodel();
printf("%s%s", copyright, version);
if (*model != '\0')
printf("%s\n", model);
#ifdef DEBUG
printf("general exception handler:\t%d byte\n",
sh_vector_generic_end - sh_vector_generic);
printf("TLB miss exception handler:\t%d byte\n",
#if defined(SH3) && defined(SH4)
CPU_IS_SH3 ? sh3_vector_tlbmiss_end - sh3_vector_tlbmiss :
sh4_vector_tlbmiss_end - sh4_vector_tlbmiss
#elif defined(SH3)
sh3_vector_tlbmiss_end - sh3_vector_tlbmiss
#elif defined(SH4)
sh4_vector_tlbmiss_end - sh4_vector_tlbmiss
#endif
);
printf("interrupt exception handler:\t%d byte\n",
sh_vector_interrupt_end - sh_vector_interrupt);
#endif
format_bytes(pbuf, sizeof(pbuf), ctob(physmem));
printf("total memory = %s\n", pbuf);
minaddr = 0;
phys_map = uvm_km_suballoc(kernel_map, &minaddr, &maxaddr,
VM_PHYS_SIZE, 0, false, NULL);
format_bytes(pbuf, sizeof(pbuf), ptoa(uvm_availmem(false)));
printf("avail memory = %s\n", pbuf);
}
void
cpu_dumpconf(void)
{
}
void
dumpsys(void)
{
}
void *
getframe(const struct lwp *l, int sig, int *onstack)
{
const struct proc *p = l->l_proc;
const stack_t *sigstk = &l->l_sigstk;
*onstack = (sigstk->ss_flags & (SS_DISABLE | SS_ONSTACK)) == 0
&& (SIGACTION(p, sig).sa_flags & SA_ONSTACK) != 0;
if (*onstack)
return ((char *)sigstk->ss_sp + sigstk->ss_size);
else
return ((void *)l->l_md.md_regs->tf_r15);
}
void
sendsig_siginfo(const ksiginfo_t *ksi, const sigset_t *mask)
{
struct lwp *l = curlwp;
struct proc *p = l->l_proc;
struct sigacts *ps = p->p_sigacts;
struct trapframe *tf = l->l_md.md_regs;
int sig = ksi->ksi_signo, error;
sig_t catcher = SIGACTION(p, sig).sa_handler;
struct sigframe_siginfo *fp, frame;
int onstack;
fp = getframe(l, sig, &onstack);
--fp;
memset(&frame, 0, sizeof(frame));
frame.sf_si._info = ksi->ksi_info;
frame.sf_uc.uc_link = l->l_ctxlink;
frame.sf_uc.uc_sigmask = *mask;
frame.sf_uc.uc_flags = _UC_SIGMASK;
frame.sf_uc.uc_flags |= (l->l_sigstk.ss_flags & SS_ONSTACK)
? _UC_SETSTACK : _UC_CLRSTACK;
sendsig_reset(l, sig);
mutex_exit(p->p_lock);
cpu_getmcontext(l, &frame.sf_uc.uc_mcontext, &frame.sf_uc.uc_flags);
error = copyout(&frame, fp, sizeof(frame));
mutex_enter(p->p_lock);
if (error != 0) {
sigexit(l, SIGILL);
}
tf->tf_r4 = sig;
tf->tf_r5 = (int)&fp->sf_si;
tf->tf_r6 = (int)&fp->sf_uc;
tf->tf_spc = (int)catcher;
tf->tf_r15 = (int)fp;
tf->tf_pr = (int)ps->sa_sigdesc[sig].sd_tramp;
if (onstack)
l->l_sigstk.ss_flags |= SS_ONSTACK;
}
void
cpu_getmcontext(struct lwp *l, mcontext_t *mcp, unsigned int *flags)
{
const struct trapframe *tf = l->l_md.md_regs;
__greg_t *gr = mcp->__gregs;
__greg_t ras_pc;
gr[_REG_GBR] = tf->tf_gbr;
gr[_REG_PC] = tf->tf_spc;
gr[_REG_SR] = tf->tf_ssr;
gr[_REG_MACL] = tf->tf_macl;
gr[_REG_MACH] = tf->tf_mach;
gr[_REG_PR] = tf->tf_pr;
gr[_REG_R14] = tf->tf_r14;
gr[_REG_R13] = tf->tf_r13;
gr[_REG_R12] = tf->tf_r12;
gr[_REG_R11] = tf->tf_r11;
gr[_REG_R10] = tf->tf_r10;
gr[_REG_R9] = tf->tf_r9;
gr[_REG_R8] = tf->tf_r8;
gr[_REG_R7] = tf->tf_r7;
gr[_REG_R6] = tf->tf_r6;
gr[_REG_R5] = tf->tf_r5;
gr[_REG_R4] = tf->tf_r4;
gr[_REG_R3] = tf->tf_r3;
gr[_REG_R2] = tf->tf_r2;
gr[_REG_R1] = tf->tf_r1;
gr[_REG_R0] = tf->tf_r0;
gr[_REG_R15] = tf->tf_r15;
if ((ras_pc = (__greg_t)ras_lookup(l->l_proc,
(void *) gr[_REG_PC])) != -1)
gr[_REG_PC] = ras_pc;
*flags |= (_UC_CPU|_UC_TLSBASE);
memset(&mcp->__fpregs, 0, sizeof (mcp->__fpregs));
}
int
cpu_mcontext_validate(struct lwp *l, const mcontext_t *mcp)
{
struct trapframe *tf = l->l_md.md_regs;
const __greg_t *gr = mcp->__gregs;
if (((tf->tf_ssr ^ gr[_REG_SR]) & PSL_USERSTATIC) != 0)
return EINVAL;
return 0;
}
int
cpu_setmcontext(struct lwp *l, const mcontext_t *mcp, unsigned int flags)
{
struct trapframe *tf = l->l_md.md_regs;
const __greg_t *gr = mcp->__gregs;
struct proc *p = l->l_proc;
int error;
if ((flags & _UC_CPU) != 0) {
error = cpu_mcontext_validate(l, mcp);
if (error)
return error;
tf->tf_spc = gr[_REG_PC];
tf->tf_ssr = gr[_REG_SR];
tf->tf_macl = gr[_REG_MACL];
tf->tf_mach = gr[_REG_MACH];
tf->tf_pr = gr[_REG_PR];
tf->tf_r14 = gr[_REG_R14];
tf->tf_r13 = gr[_REG_R13];
tf->tf_r12 = gr[_REG_R12];
tf->tf_r11 = gr[_REG_R11];
tf->tf_r10 = gr[_REG_R10];
tf->tf_r9 = gr[_REG_R9];
tf->tf_r8 = gr[_REG_R8];
tf->tf_r7 = gr[_REG_R7];
tf->tf_r6 = gr[_REG_R6];
tf->tf_r5 = gr[_REG_R5];
tf->tf_r4 = gr[_REG_R4];
tf->tf_r3 = gr[_REG_R3];
tf->tf_r2 = gr[_REG_R2];
tf->tf_r1 = gr[_REG_R1];
tf->tf_r0 = gr[_REG_R0];
tf->tf_r15 = gr[_REG_R15];
if (flags & _UC_TLSBASE)
lwp_setprivate(l, (void *)(uintptr_t)gr[_REG_GBR]);
}
#if 0
if (flags & _UC_FPU) {
;
}
#endif
mutex_enter(p->p_lock);
if (flags & _UC_SETSTACK)
l->l_sigstk.ss_flags |= SS_ONSTACK;
if (flags & _UC_CLRSTACK)
l->l_sigstk.ss_flags &= ~SS_ONSTACK;
mutex_exit(p->p_lock);
return (0);
}
void
setregs(struct lwp *l, struct exec_package *pack, vaddr_t stack)
{
struct trapframe *tf;
l->l_md.md_flags &= ~(MDL_USEDFPU | MDL_SSTEP);
tf = l->l_md.md_regs;
memset(tf, 0, sizeof(*tf));
tf->tf_ssr = PSL_USERSET;
tf->tf_spc = pack->ep_entry;
tf->tf_pr = 0;
tf->tf_gbr = 0;
tf->tf_macl = 0;
tf->tf_mach = 0;
tf->tf_r0 = 0;
tf->tf_r1 = 0;
tf->tf_r2 = 0;
tf->tf_r3 = 0;
if (ufetch_int((void *)stack, (u_int *)&tf->tf_r4) != 0)
tf->tf_r4 = -1;
tf->tf_r5 = stack + 4;
tf->tf_r6 = stack + 4 * tf->tf_r4 + 8;
tf->tf_r7 = 0;
tf->tf_r8 = 0;
tf->tf_r9 = l->l_proc->p_psstrp;
tf->tf_r10 = 0;
tf->tf_r11 = 0;
tf->tf_r12 = 0;
tf->tf_r13 = 0;
tf->tf_r14 = 0;
tf->tf_r15 = stack;
}
void
cpu_reset(void)
{
_cpu_exception_suspend();
_reg_write_4(SH_(EXPEVT), EXPEVT_RESET_MANUAL);
#ifndef __lint__
goto *(void *)0xa0000000;
#endif
}
int
cpu_lwp_setprivate(lwp_t *l, void *addr)
{
l->l_md.md_regs->tf_gbr = (int)addr;
return 0;
}