📄 code.c
字号:
/*
* Copyright 1999, 2000, 2001, 2002 Lucent Technologies Inc.
* All Rights Reserved.
* Information Sciences Research Center, Bell Labs.
*
* LUCENT TECHNOLOGIES DOES NOT CLAIM MERCHANTABILITY OF THIS SOFTWARE
* OR THE SUITABILITY OF THIS SOFTWARE FOR ANY PARTICULAR PURPOSE. The
* software is provided "as is" without expressed or implied warranty
* of any kind.
*
* These notices must be retained in any copies of any part of this
* software.
*
*/
/*
* Dynamic code generation
*
* Code generation must avoid pipeline hazards.
* Specific hazards for RM7000:
* setting EPC must followed by at least 4 integer instructions before ERET
* setting CP0 registers must be followed by at least 4 integer
* instructions before TLBWI.
*
* Important:
* If rm5230 is defined, then r5000 is also defined!
*/
#include "string.h"
#include "machine/cpu.h"
#include "diag.h"
#include "pebble.h"
#include "mem.h"
#include "log.h"
#include "op.h"
#include "../port/portal.h"
#define NPORTAL_RETURNS 18 /* number of different return portals */
/* processor depenent constants for avoiding pipeline hazards */
#if #cpu(rm7000)
#define MTC0_TO_TLB_OP 4 /* TLBR, TLBP, TLBWI */
#define TLBR_TO_MFC0 4
#define TLBP_TO_MFC0 4
#define TLBW_TO_BRANCH 8
#define TLBW_TO_ACCESS 4
#else
#if #cpu(rm5230)
#define MTC0_TO_TLB_OP 2 /* TLBR, TLBP, TLBWI */
#define TLBR_TO_MFC0 2
#define TLBP_TO_MFC0 2
#define TLBW_TO_BRANCH 2
#define TLBW_TO_ACCESS 2
#else
#if #cpu(r5000)
#define MTC0_TO_TLB_OP 2 /* TLBR, TLBP, TLBWI */
#define TLBR_TO_MFC0 3
#define TLBP_TO_MFC0 3
#define TLBW_TO_BRANCH 4
#define TLBW_TO_ACCESS 5
#else
#if defined(Cobalt)
#define MTC0_TO_TLB_OP 2 /* TLBR, TLBP, TLBWI */
#define TLBR_TO_MFC0 2
#define TLBP_TO_MFC0 2
#define TLBW_TO_BRANCH 2
#define TLBW_TO_ACCESS 2
#else
#error "invalid CPU type"
#endif
#endif
#endif
#endif
#ifdef Cobalt
/* new calling sequence: every parameter takes 8 bytes on the stack */
/* pass double word parameters in a single register */
#define SAVEREGS 32
#define PARAM_SIZE 8
#else
/* old calling sequence */
/* pass double word parameters in a register pair */
#define SAVEREGS 16
#define PARAM_SIZE 4
#endif
static int *portal_code = NULL; /* portals are created in this area */
/* then copied to dynamically allocated memory */
/*
* Generate a jump instruction.
* Note that the buffer address (p) is in virtual memory,
* while the actual portal code will be executed in kernel mode with physical
* addresses.
*/
static int *
gen_j(int *p, int *addr)
{
int *phys;
phys = PA_TO_KVA0((ulong)virt2phys(p));
if (((ulong)phys & JUMP_HI_MASK) !=
((ulong)addr & JUMP_HI_MASK))
panic("cannot generate jump instruction outside the region");
if ((ulong)p & 03)
panic("cannot generate jump intruction to unaligned address");
*p++ = J((ulong)addr >> 2);
return p;
}
static int *
gen_li(int *p, int rd, int val)
{
if (val == 0)
*p++ = ADDIU(rd,ZERO,0);
else if ((val & IMMASK) == 0)
/* low 16 bits are zero */
*p++ = LUI(rd, (val >> 16));
else if ((val & SEXTMASK) == SEXTMASK || (val & SEXTMASK) == 0)
/* a 16 bit value sign extended to 32 bits */
*p++ = ADDIU(rd,ZERO,val);
else if ((val & IMMASK) == val)
/* 16 high order bits are zero */
*p++ = ORI(rd,ZERO,val);
else {
/* no luck; must generate the value with two instructions */
*p++ = LUI(rd,(val >> 16));
*p++ = ORI(rd,rd,(val & IMMASK));
}
return p;
}
/* generate a constant in two instructions. necessary for cloning */
int *
gen_li_2inst(int *p, int rd, int val)
{
*p++ = LUI(rd,(val >> 16));
*p++ = ORI(rd,rd,(val & IMMASK));
return p;
}
static int *
gen_lw(int *p, int rd, ulong addr, int rs)
{
int hi_addr;
if (rd == rs)
panic("invalid register input to gen_lw");
hi_addr = addr >> 16;
/* will the low-order 16 bits of the address casue a sign extension? */
if (addr & 0x8000)
hi_addr -= 0xffff;
*p++ = LUI(rd, hi_addr);
if (rs != ZERO)
*p++ = ADD(rd, rd, rs);
*p++ = LW(rd, addr & IMMASK, rd);
return p;
}
#if 0
/* not used (yet) */
static int *
gen_la(int *p, int rd, int addr)
{
return gen_li(p, rd, addr);
}
#endif
/*
* Probe if TLB contains an entry matching the virtual address.
* If entry is found, erase it from TLB.
* The virtual address must be in the format of the TLBHI register
* (including ASID in lower bits).
* The register "addr_reg" is clobbered by this routine.
* The ASID field in TLBHI register may be set to zero by this routine
* and should be restored to its previous value.
*
* C0_INDEX must cleared to zero if we call gen_restrpc after this routine.
*/
static int *
gen_tlb_probe_erase(int *p, int addr_reg, int *label)
{
int *old_p;
*p++ = DMTC0(addr_reg, C0_TLBHI);
*p++ = MTC0(ZERO, C0_PAGEMASK);
old_p = p;
/* MTC0 must be followed by at least MTC0_TO_TLB_OP integer */
/* instructions before a TLBP */
while ((p - old_p) < MTC0_TO_TLB_OP)
*p++ = NOP;
*p++ = TLBP;
old_p = p;
/* TLBP must be followed by at least TLBP_TO_MFC0 integer */
/* instructions */
while ((p - old_p) < TLBP_TO_MFC0)
*p++ = NOP;
*p++ = MFC0(addr_reg, C0_INDEX);
*p++ = DMTC0(ZERO, C0_TLBHI);
*p++ = DMTC0(ZERO, C0_TLBLO0);
*p++ = DMTC0(ZERO, C0_TLBLO1);
old_p = p;
*p++ = BLTZ(addr_reg, label - (p+1));
*p++ = NOP;
/* MTC0 must be followed by at least MTC0_TO_TLB_OP integer */
/* instructions before a TLBWI */
while ((p - old_p) < MTC0_TO_TLB_OP)
*p++ = NOP;
*p++ = TLBWI;
old_p = p;
/* TLBWI must be followed by at least TLBW_TO_BRANCH integer */
/* instructions */
while ((p - old_p) < TLBW_TO_BRANCH)
*p++ = NOP;
return p;
}
/*
* Generate code to fill the portal number and the from ASID fields
* of the current log record. This code does not move the log record
* pointer. We assume that gen_write_log() will be called later to
* generate the rest of the log record write code.
* This code uses registers T0 and T1.
* "portal_no" is the register containing the portal number of the currently
* active portal.
*/
static int *
gen_write_log_portal(int *start, int portal_no)
{
int *p = start;
*p++ = MFC0(T1, C0_TLBHI);
p = gen_lw(p, T0, ((ulong)log) + LOG_DESC_HEAD*4, ZERO);
*p++ = SW(portal_no, LOG_ENTRY_PORTAL*4, T0);
*p++ = ANDI(T1, T1, TLBHI_PIDMASK);
*p++ = SW(T1, LOG_ENTRY_FROM_ASID*4, T0);
return p;
}
/*
* Generate code to fill the rest of the log record and advance the log
* record pointer.
* This code uses registers T0 through T4 (inclusive).
*/
static int *
gen_write_log(int *start, int treg, int base, int to_asid, int val1, int val2)
{
int *p = start;
p = gen_li(p, T0, (ulong)log);
*p++ = LW(T1, LOG_DESC_HEAD*4, T0);
*p++ = LW(T2, LOG_DESC_LOW*4, T0);
*p++ = LW(T3, LOG_DESC_HIGH*4, T0);
*p++ = MFC0(T4, C0_COUNT);
*p++ = SW(to_asid, LOG_ENTRY_TO_ASID*4, T1);
*p++ = SW(treg, LOG_ENTRY_THREAD*4, T1);
*p++ = SW(base, LOG_ENTRY_CALL_SP*4, T1);
*p++ = SW(SP, LOG_ENTRY_USER_SP*4, T1);
*p++ = SW(val1, LOG_ENTRY_P0*4, T1);
*p++ = SW(val2, LOG_ENTRY_P1*4, T1);
*p++ = SW(T4, LOG_ENTRY_TIME*4, T1);
*p++ = ADDIU(T1, T1, LOG_ENTRY_FIELDS*4);
*p++ = SLTU(T4, T1, T3);
*p++ = MOVZ(T1, T2, T4);
*p++ = SW(T1, LOG_DESC_HEAD*4, T0);
return p;
}
/*
* Generate portal return code
* Returned value: physical address of return portal
*/
static int *
gen_portal_rtn(int treg, int base, PortalStack stack_type,
PortalSave stack_save, uint is_window)
{
int *p, *old_p, *start, *phys_start;
int len, i;
int *label6, *label7;
static int *return_code = NULL;
DIAG(PORTAL_DIAG, ("gen_portal_rtn: stack_type=%d stack_save=%d is_window=%d\n",
stack_type, stack_save, is_window));
if (return_code == NULL)
return_code = (int *)malloc(MAX_PORTAL_LEN);
/* we must repeat code generation twice in order to compute */
/* forward branch offsets! */
label6 = label7 = return_code; /* for first iteration */
for (i = 0; i <= 1; i++) {
p = return_code;
if (PORTAL_LOG) {
p = gen_li(p, T7, -1);
*p++ = LW(T6, S_TLBHI*8, base);
p = gen_write_log_portal(p, T7);
*p++ = ANDI(T6, T6, TLBHI_PIDMASK);
p = gen_write_log(p, treg, base, T6, V0, V1);
}
if (is_window) {
*p++ = LD(T7, S_WIN_COUNT*8, base);
*p++ = LB(T6, 0, T7);
*p++ = LD(T8, S_WIN_VADDR*8, base);
*p++ = BNE(T6, ZERO, label7 - (p+1));
*p++ = ADDIU(T6, T6, -1);
p = gen_j(p, bad_win_rtn);
*p++ = NOP;
label7 = p;
*p++ = BNE(T6, ZERO, label6 - (p+1));
*p++ = SB(T6, 0, T7);
/* must clear TLB entry of a window with a zero */
/* access counter */
*p++ = LB(T5, NASID, T7);
p = gen_li(p, T4, 1);
/* was it loaded into the TLB at all? */
*p++ = BNE(T5, ZERO, label6 - (p+1));
*p++ = SB(T4, NASID, T7);
p = gen_tlb_probe_erase(p, T8, label6);
}
label6 = p;
if (stack_type == STACK_QUEUE) {
/* return stack to queue */
*p++ = LW(T3, THR_STACK_Q*4, treg);
*p++ = LW(T5, THR_STACK_DESC*4, treg);
*p++ = SW(T3, STACK_DESC_NEXT*4, T5);
*p++ = SW(T5, STACK_Q_HEAD*4, T3);
}
*p++ = LD(T2, S_TLBHI*8, base);
*p++ = LW(T4, S_STATUS*8, base);
*p++ = LD(T3, S_TLBLO0*8, base);
*p++ = LD(T5, S_EPC*8, base);
*p++ = MTC0(ZERO, C0_INDEX);
*p++ = DMTC0(T2, C0_TLBHI);
*p++ = DMTC0(T3, C0_TLBLO0);
*p++ = DADDIU(T6, T3, TLBLO0toLO1);
*p++ = DMTC0(T6, C0_TLBLO1);
*p++ = MTC0(ZERO, C0_PAGEMASK);
*p++ = MTC0(T4, C0_STATUS);
*p++ = DMTC0(T5, C0_EPC);
*p++ = LW(T1, S_STACK_DESC*8, base);
*p++ = LW(T2, S_STACK_Q*8, base);
*p++ = LW(T3, S_PORTAL_TBL*8, base);
*p++ = LD(T4, S_PTE*8, base);
*p++ = TLBWI;
old_p = p;
/* TLBWI must be followed by at least TLBW_TO_ACCESS integer */
/* instructions */
while ((p - old_p) < TLBW_TO_ACCESS)
*p++ = NOP;
*p++ = SW(T1, THR_STACK_DESC*4, treg);
*p++ = SW(T2, THR_STACK_Q*4, treg);
*p++ = SW(T3, THR_PORTAL_TBL*4, treg);
*p++ = SD(T4, THR_PTE*4, treg);
if (stack_save != SAVE_MINIMAL) {
*p++ = LD(S0, S_S0*8, base);
*p++ = LD(S1, S_S1*8, base);
*p++ = LD(S2, S_S2*8, base);
*p++ = LD(S3, S_S3*8, base);
*p++ = LD(S4, S_S4*8, base);
*p++ = LD(S5, S_S5*8, base);
*p++ = LD(S6, S_S6*8, base);
*p++ = LD(S7, S_S7*8, base);
*p++ = LD(S8, S_S8*8, base);
}
*p++ = LD(GP, S_GP*8, base);
*p++ = LD(SP, S_SP*8, base);
*p++ = LD(RA, S_RA*8, base);
if (stack_save == SAVE_FULL) {
*p++ = LD(T0, S_LO*8, base);
*p++ = LD(T1, S_HI*8, base);
*p++ = LD(AT, S_AT*8, base);
*p++ = LD(V0, S_V0*8, base);
*p++ = LD(V1, S_V1*8, base);
*p++ = MTLO(T0);
*p++ = MTHI(T1);
*p++ = LD(A0, S_A0*8, base);
*p++ = LD(A1, S_A1*8, base);
*p++ = LD(A2, S_A2*8, base);
*p++ = LD(A3, S_A3*8, base);
*p++ = LD(T0, S_T0*8, base);
*p++ = LD(T1, S_T1*8, base);
*p++ = LD(T2, S_T2*8, base);
*p++ = LD(T3, S_T3*8, base);
*p++ = LD(T4, S_T4*8, base);
*p++ = LD(T5, S_T5*8, base);
*p++ = LD(T6, S_T6*8, base);
*p++ = LD(T7, S_T7*8, base);
*p++ = LD(T8, S_T8*8, base);
*p++ = LD(T9, S_T9*8, base);
}
*p++ = DADDIU(base, base, 8*S_REGS);
*p++ = SW(base, THR_CALL_SP*4, treg);
*p++ = ERET;
}
/* pad resulting code to 8 byte boundary */
while ((uint)p & 07)
*p++ = NOP;
len = p - return_code;
if (len*sizeof(int) > MAX_PORTAL_LEN)
panic("portal code overflow");
start = (int *)aligned_malloc(len*sizeof(int));
memcpy((char *)start, (char *)return_code, len*sizeof(int));
phys_start = (int *)PA_TO_KVA0((ulong)virt2phys(start));
clean_cache(phys_start, len*sizeof(int));
clean_cache(start, len*sizeof(int));
DIAG(PORTAL_DIAG, ("gen_portal_rtn: start at %p\n", phys_start));
return (Portal)phys_start;
}
static int *
lookup_portal_rtn(PortalStack stack_type, PortalSave stack_save,
uint window_reg, uchar window_type)
{
int is_window;
int i;
static int *portal_rtn[NPORTAL_RETURNS];
/* note: '>' and '=' window types are not handled by return portal! */
is_window = (window_reg != 0) && (window_type == 'w');
/* convert the combination of <stack_type, stack_save, is_window> */
/* to an index */
i = (int)stack_type * 6 + (int)stack_save * 3 + is_window;
if (i < 0 || i >= NPORTAL_RETURNS)
panic("lookup_portal_rtn: invalid lookup table index");
if (portal_rtn[i] == NULL)
portal_rtn[i] =
gen_portal_rtn(K0, K1, stack_type, stack_save, is_window);
return portal_rtn[i];
}
/*
* assumption: C0_INDEX is zero on entry
*
* must not change register T9, which is set by the gen_pushreg routine
*/
static int *
gen_savereg(int *p, int treg, int base,
PortalStack stack_type, PortalSave stack_save,
uint asid_reg, uint window_reg, uchar window_type,
int new_asid, int *last_mtc)
{
int *start, *label, *label9, *label10;
int i;
if (stack_save == SAVE_FULL) {
*p++ = SD(AT, S_AT*8, base);
*p++ = SD(V0, S_V0*8, base);
*p++ = SD(V1, S_V1*8, base);
*p++ = SD(A0, S_A0*8, base);
*p++ = MFLO(V0);
*p++ = SD(A1, S_A1*8, base);
*p++ = SD(A2, S_A2*8, base);
*p++ = SD(A3, S_A3*8, base);
*p++ = SD(T0, S_T0*8, base);
*p++ = MFHI(V1);
*p++ = SD(T1, S_T1*8, base);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -