diff --git a/src/cmd/asm/internal/arch/arch.go b/src/cmd/asm/internal/arch/arch.go index 19b4785b2ad8c8..e239ecd4762d6b 100644 --- a/src/cmd/asm/internal/arch/arch.go +++ b/src/cmd/asm/internal/arch/arch.go @@ -471,9 +471,6 @@ func archMips64(linkArch *obj.LinkArch) *Arch { for i := mips.REG_FCR0; i <= mips.REG_FCR31; i++ { register[obj.Rconv(i)] = int16(i) } - for i := mips.REG_W0; i <= mips.REG_W31; i++ { - register[obj.Rconv(i)] = int16(i) - } register["HI"] = mips.REG_HI register["LO"] = mips.REG_LO // Pseudo-registers. @@ -491,7 +488,6 @@ func archMips64(linkArch *obj.LinkArch) *Arch { "FCR": true, "M": true, "R": true, - "W": true, } instructions := make(map[string]obj.As) diff --git a/src/cmd/asm/internal/arch/mips.go b/src/cmd/asm/internal/arch/mips.go index 5d71f40fbea6f8..79fb7cf02e018d 100644 --- a/src/cmd/asm/internal/arch/mips.go +++ b/src/cmd/asm/internal/arch/mips.go @@ -63,10 +63,6 @@ func mipsRegisterNumber(name string, n int16) (int16, bool) { if 0 <= n && n <= 31 { return mips.REG_R0 + n, true } - case "W": - if 0 <= n && n <= 31 { - return mips.REG_W0 + n, true - } } return 0, false } diff --git a/src/cmd/asm/internal/asm/testdata/mips.s b/src/cmd/asm/internal/asm/testdata/mips.s index f65eba07ba338f..7136d686d758a5 100644 --- a/src/cmd/asm/internal/asm/testdata/mips.s +++ b/src/cmd/asm/internal/asm/testdata/mips.s @@ -429,11 +429,6 @@ label4: CLZ R1, R2 // 70221020 CLO R1, R2 // 70221021 - WSBH R1, R2 // 7c0110a0 - - SEB R1, R2 // 7c011420 - SEH R1, R2 // 7c011620 - // to (Hi, Lo) MADD R2, R1 // 70220000 MSUB R2, R1 // 70220004 diff --git a/src/cmd/asm/internal/asm/testdata/mips64.s b/src/cmd/asm/internal/asm/testdata/mips64.s index ea4bb80aecbce5..e2f91a4500b455 100644 --- a/src/cmd/asm/internal/asm/testdata/mips64.s +++ b/src/cmd/asm/internal/asm/testdata/mips64.s @@ -587,50 +587,8 @@ label4: CALL foo(SB) RET foo(SB) - // unary operation NEGW R1, R2 // 00011023 NEGV R1, R2 // 0001102f - - WSBH R1, R2 // 7c0110a0 - DSBH R1, R2 // 7c0110a4 - DSHD R1, R2 // 7c011164 - - SEB R1, R2 // 7c011420 - SEH R1, R2 // 7c011620 - - RET - -// MSA VMOVI - VMOVB $511, W0 // 7b0ff807 - VMOVH $24, W23 // 7b20c5c7 - VMOVW $-24, W15 // 7b5f43c7 - VMOVD $-511, W31 // 7b700fc7 - - VMOVB (R0), W8 // 78000220 - VMOVB 511(R3), W0 // 79ff1820 - VMOVB -512(R12), W21 // 7a006560 - VMOVH (R24), W12 // 7800c321 - VMOVH 110(R19), W8 // 78379a21 - VMOVH -70(R12), W3 // 7bdd60e1 - VMOVW (R3), W31 // 78001fe2 - VMOVW 64(R20), W16 // 7810a422 - VMOVW -104(R17), W24 // 7be68e22 - VMOVD (R3), W2 // 780018a3 - VMOVD 128(R23), W19 // 7810bce3 - VMOVD -256(R31), W0 // 7be0f823 - - VMOVB W8, (R0) // 78000224 - VMOVB W0, 511(R3) // 79ff1824 - VMOVB W21, -512(R12) // 7a006564 - VMOVH W12, (R24) // 7800c325 - VMOVH W8, 110(R19) // 78379a25 - VMOVH W3, -70(R12) // 7bdd60e5 - VMOVW W31, (R3) // 78001fe6 - VMOVW W16, 64(R20) // 7810a426 - VMOVW W24, -104(R17) // 7be68e26 - VMOVD W2, (R3) // 780018a7 - VMOVD W19, 128(R23) // 7810bce7 - VMOVD W0, -256(R31) // 7be0f827 RET // END diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go index bb1d08e76d4e1c..edeffc3d91c385 100644 --- a/src/cmd/dist/build.go +++ b/src/cmd/dist/build.go @@ -1743,6 +1743,7 @@ var cgoEnabled = map[string]bool{ "windows/arm64": true, "noos/thumb": false, "noos/riscv64": false, + "noos/mips64": false, } // List of platforms that are marked as broken ports. diff --git a/src/cmd/internal/obj/mips/a.out.go b/src/cmd/internal/obj/mips/a.out.go index cd6131332ac834..95d260733447fe 100644 --- a/src/cmd/internal/obj/mips/a.out.go +++ b/src/cmd/internal/obj/mips/a.out.go @@ -43,7 +43,6 @@ const ( NSYM = 50 NREG = 32 /* number of general registers */ NFREG = 32 /* number of floating point registers */ - NWREG = 32 /* number of MSA registers */ ) const ( @@ -181,41 +180,6 @@ const ( REG_FCR30 REG_FCR31 - // MSA registers - // The lower bits of W registers are alias to F registers - REG_W0 // must be a multiple of 32 - REG_W1 - REG_W2 - REG_W3 - REG_W4 - REG_W5 - REG_W6 - REG_W7 - REG_W8 - REG_W9 - REG_W10 - REG_W11 - REG_W12 - REG_W13 - REG_W14 - REG_W15 - REG_W16 - REG_W17 - REG_W18 - REG_W19 - REG_W20 - REG_W21 - REG_W22 - REG_W23 - REG_W24 - REG_W25 - REG_W26 - REG_W27 - REG_W28 - REG_W29 - REG_W30 - REG_W31 - REG_HI REG_LO @@ -253,8 +217,6 @@ func init() { f(REG_F0, REG_F31, 32) // For 32-bit MIPS, compiler only uses even numbered registers -- see cmd/compile/internal/ssa/gen/MIPSOps.go MIPSDWARFRegisters[REG_HI] = 64 MIPSDWARFRegisters[REG_LO] = 65 - // The lower bits of W registers are alias to F registers - f(REG_W0, REG_W31, 32) } const ( @@ -281,7 +243,6 @@ const ( C_FREG C_FCREG C_MREG /* special processor register */ - C_WREG /* MSA registers */ C_HI C_LO C_ZCON @@ -394,8 +355,6 @@ const ( AROTRV ASC ASCV - ASEB - ASEH ASGT ASGTU ASLL @@ -417,7 +376,6 @@ const ( ATLBWR ATNE AWORD - AWSBH AXOR /* 64-bit */ @@ -437,8 +395,6 @@ const ( AADDVU ASUBV ASUBVU - ADSBH - ADSHD /* 64-bit FP */ ATRUNCFV @@ -451,12 +407,6 @@ const ( AMOVVF AMOVVD - /* MSA */ - AVMOVB - AVMOVH - AVMOVW - AVMOVD - ALAST // aliases @@ -482,7 +432,4 @@ func init() { if REG_FCR0%32 != 0 { panic("REG_FCR0 is not a multiple of 32") } - if REG_W0%32 != 0 { - panic("REG_W0 is not a multiple of 32") - } } diff --git a/src/cmd/internal/obj/mips/anames.go b/src/cmd/internal/obj/mips/anames.go index d86e37ff83e1b5..079b62913e137e 100644 --- a/src/cmd/internal/obj/mips/anames.go +++ b/src/cmd/internal/obj/mips/anames.go @@ -82,8 +82,6 @@ var Anames = []string{ "ROTRV", "SC", "SCV", - "SEB", - "SEH", "SGT", "SGTU", "SLL", @@ -105,7 +103,6 @@ var Anames = []string{ "TLBWR", "TNE", "WORD", - "WSBH", "XOR", "MOVV", "MOVVL", @@ -123,8 +120,6 @@ var Anames = []string{ "ADDVU", "SUBV", "SUBVU", - "DSBH", - "DSHD", "TRUNCFV", "TRUNCDV", "TRUNCFW", @@ -134,9 +129,5 @@ var Anames = []string{ "MOVDV", "MOVVF", "MOVVD", - "VMOVB", - "VMOVH", - "VMOVW", - "VMOVD", "LAST", } diff --git a/src/cmd/internal/obj/mips/anames0.go b/src/cmd/internal/obj/mips/anames0.go index c30069673092ba..c56d34eaf533a1 100644 --- a/src/cmd/internal/obj/mips/anames0.go +++ b/src/cmd/internal/obj/mips/anames0.go @@ -10,7 +10,6 @@ var cnames0 = []string{ "FREG", "FCREG", "MREG", - "WREG", "HI", "LO", "ZCON", diff --git a/src/cmd/internal/obj/mips/asm0.go b/src/cmd/internal/obj/mips/asm0.go index 2804073db17a8e..b3302d91a76bf3 100644 --- a/src/cmd/internal/obj/mips/asm0.go +++ b/src/cmd/internal/obj/mips/asm0.go @@ -254,7 +254,6 @@ var optab = []Optab{ {AMOVV, C_REG, C_NONE, C_LO, 21, 4, 0, sys.MIPS64, 0}, {AMUL, C_REG, C_REG, C_NONE, 22, 4, 0, 0, 0}, - {AMUL, C_REG, C_REG, C_REG, 22, 4, 0, 0, 0}, {AMULV, C_REG, C_REG, C_NONE, 22, 4, 0, sys.MIPS64, 0}, {AADD, C_ADD0CON, C_REG, C_REG, 4, 4, 0, 0, 0}, @@ -377,14 +376,6 @@ var optab = []Optab{ {ATEQ, C_SCON, C_NONE, C_REG, 15, 4, 0, 0, 0}, {ACMOVT, C_REG, C_NONE, C_REG, 17, 4, 0, 0, 0}, - {AVMOVB, C_SCON, C_NONE, C_WREG, 56, 4, 0, sys.MIPS64, 0}, - {AVMOVB, C_ADDCON, C_NONE, C_WREG, 56, 4, 0, sys.MIPS64, 0}, - {AVMOVB, C_SOREG, C_NONE, C_WREG, 57, 4, 0, sys.MIPS64, 0}, - {AVMOVB, C_WREG, C_NONE, C_SOREG, 58, 4, 0, sys.MIPS64, 0}, - - {AWSBH, C_REG, C_NONE, C_REG, 59, 4, 0, 0, 0}, - {ADSBH, C_REG, C_NONE, C_REG, 59, 4, 0, sys.MIPS64, 0}, - {ABREAK, C_REG, C_NONE, C_SEXT, 7, 4, REGSB, sys.MIPS64, 0}, /* really CACHE instruction */ {ABREAK, C_REG, C_NONE, C_SAUTO, 7, 4, REGSP, sys.MIPS64, 0}, {ABREAK, C_REG, C_NONE, C_SOREG, 7, 4, REGZERO, sys.MIPS64, 0}, @@ -585,9 +576,6 @@ func (c *ctxt0) aclass(a *obj.Addr) int { if REG_FCR0 <= a.Reg && a.Reg <= REG_FCR31 { return C_FCREG } - if REG_W0 <= a.Reg && a.Reg <= REG_W31 { - return C_WREG - } if a.Reg == REG_LO { return C_LO } @@ -1063,11 +1051,6 @@ func buildop(ctxt *obj.Link) { case AMOVVL: opset(AMOVVR, r0) - case AVMOVB: - opset(AVMOVH, r0) - opset(AVMOVW, r0) - opset(AVMOVD, r0) - case AMOVW, AMOVD, AMOVF, @@ -1104,13 +1087,6 @@ func buildop(ctxt *obj.Link) { case ATEQ: opset(ATNE, r0) - - case AWSBH: - opset(ASEB, r0) - opset(ASEH, r0) - - case ADSBH: - opset(ADSHD, r0) } } } @@ -1167,14 +1143,6 @@ func OP_JMP(op uint32, i uint32) uint32 { return op | i&0x3FFFFFF } -func OP_VI10(op uint32, df uint32, s10 int32, wd uint32, minor uint32) uint32 { - return 0x1e<<26 | (op&7)<<23 | (df&3)<<21 | uint32(s10&0x3FF)<<11 | (wd&31)<<6 | minor&0x3F -} - -func OP_VMI10(s10 int32, rs uint32, wd uint32, minor uint32, df uint32) uint32 { - return 0x1e<<26 | uint32(s10&0x3FF)<<16 | (rs&31)<<11 | (wd&31)<<6 | (minor&15)<<2 | df&3 -} - func (c *ctxt0) asmout(p *obj.Prog, o *Optab, out []uint32) { o1 := uint32(0) o2 := uint32(0) @@ -1406,17 +1374,8 @@ func (c *ctxt0) asmout(p *obj.Prog, o *Optab, out []uint32) { } o1 = OP_RRR(a, REGZERO, p.From.Reg, REGZERO) - case 22: /* mul r1,r2 [r3]*/ - if p.To.Reg != obj.REG_NONE { - r := p.Reg - if r == obj.REG_NONE { - r = p.To.Reg - } - a := SP(3, 4) | 2 /* mul */ - o1 = OP_RRR(a, p.From.Reg, r, p.To.Reg) - } else { - o1 = OP_RRR(c.oprrr(p.As), p.From.Reg, p.Reg, REGZERO) - } + case 22: /* mul r1,r2 */ + o1 = OP_RRR(c.oprrr(p.As), p.From.Reg, p.Reg, REGZERO) case 23: /* add $lcon,r1,r2 ==> lu+or+add */ v := c.regoff(&p.From) @@ -1680,22 +1639,6 @@ func (c *ctxt0) asmout(p *obj.Prog, o *Optab, out []uint32) { rel.Sym = p.From.Sym rel.Add = p.From.Offset rel.Type = objabi.R_ADDRMIPSTLS - - case 56: /* vmov{b,h,w,d} $scon, wr */ - - v := c.regoff(&p.From) - o1 = OP_VI10(110, c.twobitdf(p.As), v, uint32(p.To.Reg), 7) - - case 57: /* vld $soreg, wr */ - v := c.lsoffset(p.As, c.regoff(&p.From)) - o1 = OP_VMI10(v, uint32(p.From.Reg), uint32(p.To.Reg), 8, c.twobitdf(p.As)) - - case 58: /* vst wr, $soreg */ - v := c.lsoffset(p.As, c.regoff(&p.To)) - o1 = OP_VMI10(v, uint32(p.To.Reg), uint32(p.From.Reg), 9, c.twobitdf(p.As)) - - case 59: - o1 = OP_RRR(c.oprrr(p.As), p.From.Reg, REGZERO, p.To.Reg) } out[0] = o1 @@ -1896,16 +1839,6 @@ func (c *ctxt0) oprrr(a obj.As) uint32 { return SP(3, 4) | OP(0, 0) case AMSUB: return SP(3, 4) | OP(0, 4) - case AWSBH: - return SP(3, 7) | OP(20, 0) - case ADSBH: - return SP(3, 7) | OP(20, 4) - case ADSHD: - return SP(3, 7) | OP(44, 4) - case ASEB: - return SP(3, 7) | OP(132, 0) - case ASEH: - return SP(3, 7) | OP(196, 0) } if a < 0 { @@ -2097,43 +2030,3 @@ func vshift(a obj.As) bool { } return false } - -// MSA Two-bit Data Format Field Encoding -func (c *ctxt0) twobitdf(a obj.As) uint32 { - switch a { - case AVMOVB: - return 0 - case AVMOVH: - return 1 - case AVMOVW: - return 2 - case AVMOVD: - return 3 - default: - c.ctxt.Diag("unsupported data format %v", a) - } - return 0 -} - -// MSA Load/Store offset have to be multiple of size of data format -func (c *ctxt0) lsoffset(a obj.As, o int32) int32 { - var mod int32 - switch a { - case AVMOVB: - mod = 1 - case AVMOVH: - mod = 2 - case AVMOVW: - mod = 4 - case AVMOVD: - mod = 8 - default: - c.ctxt.Diag("unsupported instruction:%v", a) - } - - if o%mod != 0 { - c.ctxt.Diag("invalid offset for %v: %d is not a multiple of %d", a, o, mod) - } - - return o / mod -} diff --git a/src/cmd/internal/obj/mips/list0.go b/src/cmd/internal/obj/mips/list0.go index f734e21ede1a8f..addf9f70d8a224 100644 --- a/src/cmd/internal/obj/mips/list0.go +++ b/src/cmd/internal/obj/mips/list0.go @@ -59,9 +59,6 @@ func rconv(r int) string { if REG_FCR0 <= r && r <= REG_FCR31 { return fmt.Sprintf("FCR%d", r-REG_FCR0) } - if REG_W0 <= r && r <= REG_W31 { - return fmt.Sprintf("W%d", r-REG_W0) - } if r == REG_HI { return "HI" } diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go index aff7a24ea50e01..f82dd7126e61f3 100644 --- a/src/cmd/link/internal/ld/deadcode.go +++ b/src/cmd/link/internal/ld/deadcode.go @@ -90,6 +90,11 @@ func (d *deadcodePass) init() { first, last = -14, 479 case sys.RISCV64: first, last = 1, 1023 + case sys.MIPS64: + first, last = 1, 5 + // runtime.unhandledExternalInterrupt is also a handler. + // Must be called somewhere in the code, otherwise it + // gets removed without error and relocs point to 0x0. } for i := first; i <= last; i++ { names = append(names, InterruptHandler(i)) diff --git a/src/cmd/link/internal/mips64/asm.go b/src/cmd/link/internal/mips64/asm.go index e82d9861841109..d690a626dba6a0 100644 --- a/src/cmd/link/internal/mips64/asm.go +++ b/src/cmd/link/internal/mips64/asm.go @@ -61,7 +61,76 @@ var ( gotSymIndex uint64 ) +func lookupFuncSym(ldr *loader.Loader, name string) loader.Sym { + if s := ldr.Lookup(name, sym.SymVerABI0); s != 0 && ldr.SymType(s) == sym.STEXT { + return s + } + if s := ldr.Lookup(name, sym.SymVerABIInternal); s != 0 && ldr.SymType(s) == sym.STEXT { + return s + } + return 0 +} + +func gentext_noos(ctxt *ld.Link, ldr *loader.Loader) { + vectors := ldr.CreateSymForUpdate("runtime.vectors", sym.SymVerABI0) + vectors.SetType(sym.STEXT) + vectors.SetReachable(true) + vectors.SetAlign(8) + + unhandledInterrupt := ldr.Lookup("runtime.unhandledExternalInterrupt", sym.SymVerABI0) + if unhandledInterrupt == 0 { + ld.Errorf(nil, "runtime.unhandledExternalInterrupt not defined") + } + + // search for user defined ISRs: //go:linkname functionName IRQ%d_Handler + // This code tries to keep runtime.vectors small, by cutting off all irq + // at the end that point to runtime.unhandledExternalInterrupt. + var irqHandlers [5]loader.Sym + irqNum := 1 + for i := 1; i < len(irqHandlers); i++ { + s := lookupFuncSym(ldr, ld.InterruptHandler(i)) + if s == 0 { + irqHandlers[i] = unhandledInterrupt + } else { + irqHandlers[i] = s + irqNum = i + 1 + } + } + + vectors.AddUint64(ctxt.Arch, uint64(irqNum)) + relocs := vectors.AddRelocs(irqNum - 1) + for i, s := range irqHandlers[1:irqNum] { + ldr.MakeSymbolUpdater(s).SetReachable(true) + rel := relocs.At(i) + rel.SetSym(s) + rel.SetType(objabi.R_ADDR) + rel.SetSiz(8) + rel.SetOff(int32(vectors.AddUint64(ctxt.Arch, 0))) + } + + ctxt.Textp = append(ctxt.Textp, vectors.Sym()) + + // move the entry symbol at the beggining of the text segment + entry := lookupFuncSym(ldr, *ld.FlagEntrySymbol) + if entry == 0 { + ld.Errorf(nil, "cannot find entry function: %s", *ld.FlagEntrySymbol) + } + for i, s := range ctxt.Textp { + if s == entry { + copy(ctxt.Textp[1:], ctxt.Textp[:i]) + ctxt.Textp[0] = s + return + } + } + ldr.Errorf(entry, "cannot find symbol in ctxt.Textp") +} + func gentext(ctxt *ld.Link, ldr *loader.Loader) { + if ctxt.HeadType == objabi.Hnoos { + gentext_noos(ctxt, ldr) + return + } + if *ld.FlagD || ctxt.Target.IsExternal() { return } diff --git a/src/cmd/link/internal/mips64/obj.go b/src/cmd/link/internal/mips64/obj.go index 193ad1f27110b8..e2d77d6f307c8e 100644 --- a/src/cmd/link/internal/mips64/obj.go +++ b/src/cmd/link/internal/mips64/obj.go @@ -105,6 +105,17 @@ func archinit(ctxt *ld.Link) { if *ld.FlagTextAddr == -1 { *ld.FlagTextAddr = ld.Rnd(0x10000, *ld.FlagRound) + int64(ld.HEADR) } + + case objabi.Hnoos: + *ld.FlagD = true + ld.Elfinit(ctxt) + ld.HEADR = ld.ELFRESERVE + if *ld.FlagTextAddr == -1 { + *ld.FlagTextAddr = 0 + } + if *ld.FlagRound == -1 { + *ld.FlagRound = 8 + } } dynSymCount = 0 diff --git a/src/embedded/arch/r4000/systim/systim_mips64.go b/src/embedded/arch/r4000/systim/systim_mips64.go new file mode 100644 index 00000000000000..723a21d20662df --- /dev/null +++ b/src/embedded/arch/r4000/systim/systim_mips64.go @@ -0,0 +1,45 @@ +// Current implementation has limitations: +// - Overflows at 2^56 nanoseconds (834 days) because 8 bits are used to +// improve precision of hz2ns variable. +// - Calls between nanotime must not be more than 2^32 nanoseconds apart, +// otherwise overflow detection fails. Since the GC calls nanoseconds +// frequently, it's currently not a issue. +package systim + +import ( + "embedded/rtos" + "internal/cpu/r4000/creg" + _ "unsafe" // for linkname +) + +var hz2ns int64 // 56.8 fixed-point integer +var lastTicks int64 + +// Setups COUNT register to work as system timer. +func Setup(clkhz int64) { + hz2ns = 2 * 1e9 + hz2ns = hz2ns << 8 + hz2ns /= clkhz + + setAlarm(nanotime()) // init lastTicks and alarm + + rtos.SetSystemTimer(nanotime, setAlarm) +} + +//go:nosplit +func nanotime() int64 { + // TODO handle overflow in interrupt + ticks32 := int64(creg.COUNT.Load()) + if ticks32 < lastTicks&0xffff_ffff { + lastTicks += (1 << 32) + } + lastTicks = (lastTicks & ^0xffff_ffff) | ticks32 + + return (lastTicks * hz2ns) >> 8 +} + +//go:nosplit +func setAlarm(ns int64) { + // Only needed for a tickless implementation. As long as curcpuSleep is + // a noop, there is no need to implement this. +} diff --git a/src/embedded/mmio/asm_mips64.s b/src/embedded/mmio/asm_mips64.s new file mode 100644 index 00000000000000..df679af2a735b6 --- /dev/null +++ b/src/embedded/mmio/asm_mips64.s @@ -0,0 +1,54 @@ +#include "textflag.h" + + +TEXT ·load64(SB),NOSPLIT,$0-16 + MOVV addr+0(FP), R4 + MOVV (R4), R5 + MOVV R5, ret+8(FP) + RET + +TEXT ·load32(SB),NOSPLIT,$0-12 + MOVV addr+0(FP), R4 + MOVWU (R4), R5 + MOVW R5, ret+8(FP) + RET + +TEXT ·load16(SB),NOSPLIT,$0-10 + MOVV addr+0(FP), R4 + MOVHU (R4), R5 + MOVH R5, ret+8(FP) + RET + +TEXT ·load8(SB),NOSPLIT,$0-9 + MOVV addr+0(FP), R4 + MOVBU (R4), R5 + MOVB R5, ret+8(FP) + RET + +TEXT ·store64(SB),NOSPLIT,$0-16 + MOVV addr+0(FP), R4 + MOVV v+8(FP), R5 + MOVV R5, (R4) + RET + +TEXT ·store32(SB),NOSPLIT,$0-12 + MOVV addr+0(FP), R4 + MOVWU v+8(FP), R5 + MOVW R5, (R4) + RET + +TEXT ·store16(SB),NOSPLIT,$0-10 + MOVV addr+0(FP), R4 + MOVHU v+8(FP), R5 + MOVH R5, (R4) + RET + +TEXT ·store8(SB),NOSPLIT,$0-9 + MOVV addr+0(FP), R4 + MOVBU v+8(FP), R5 + MOVB R5, (R4) + RET + +TEXT ·MB(SB),NOSPLIT,$0 + SYNC + RET diff --git a/src/embedded/rtos/asm_mips64.s b/src/embedded/rtos/asm_mips64.s new file mode 100644 index 00000000000000..31b19f9a1389b6 --- /dev/null +++ b/src/embedded/rtos/asm_mips64.s @@ -0,0 +1,6 @@ +#include "textflag.h" + +// func publicationBarrier() +TEXT ·publicationBarrier(SB),NOSPLIT|NOFRAME,$0-0 + SYNC + RET diff --git a/src/embedded/rtos/irq_noos_mips64.go b/src/embedded/rtos/irq_noos_mips64.go new file mode 100644 index 00000000000000..b6d27a9d338ee8 --- /dev/null +++ b/src/embedded/rtos/irq_noos_mips64.go @@ -0,0 +1,12 @@ +package rtos + +const ( + intPrioHighest = 0 + intPrioHigh = 0 + intPrioSysTimer = 0 + intPrioMid = 0 + intPrioSysCall = 0 + intPrioLow = 0 + intPrioLowest = 0 + intPrioCurrent = -1 +) diff --git a/src/internal/cpu/cpu_mips64x.go b/src/internal/cpu/cpu_mips64x.go index c452ffd8b30216..af1b2318a308d6 100644 --- a/src/internal/cpu/cpu_mips64x.go +++ b/src/internal/cpu/cpu_mips64x.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build mips64 || mips64le +//go:build (mips64 || mips64le) && !noos package cpu diff --git a/src/internal/cpu/cpu_noos_mips64.go b/src/internal/cpu/cpu_noos_mips64.go new file mode 100644 index 00000000000000..e998699fe04154 --- /dev/null +++ b/src/internal/cpu/cpu_noos_mips64.go @@ -0,0 +1,9 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +const CacheLinePadSize = 32 + +func doinit() {} diff --git a/src/internal/cpu/r4000/creg/creg_mips64.go b/src/internal/cpu/r4000/creg/creg_mips64.go new file mode 100644 index 00000000000000..633bd78e2ef574 --- /dev/null +++ b/src/internal/cpu/r4000/creg/creg_mips64.go @@ -0,0 +1,82 @@ +// Package creg provides access to the control registers. +// Page references refer to Vr4300 User' Manual (7th edition) +package creg + +type CP0Reg uint + +// System Control Processor (CP0) registers (p.46) +const ( + // memory management registers + INDEX CP0Reg = 0 + RANDOM CP0Reg = 1 + WIRED CP0Reg = 6 + PRID CP0Reg = 15 + CONFIG CP0Reg = 16 + LLADDR CP0Reg = 17 + ENTRY_LO_0 CP0Reg = 2 + ENTRY_LO_1 CP0Reg = 3 + ENTRY_HI CP0Reg = 10 + PAGE_MASK CP0Reg = 5 + CACHE_TAG_LO CP0Reg = 28 + CACHE_TAG_HI CP0Reg = 29 + + // exception processing registers + CONTEXT CP0Reg = 4 + BAD_VADDR CP0Reg = 8 + COUNT CP0Reg = 9 + COMPARE CP0Reg = 11 + STATUS CP0Reg = 12 + CAUSE CP0Reg = 13 + EPC CP0Reg = 14 + WATCH_LO CP0Reg = 18 + WATCH_HI CP0Reg = 19 + XCONTEXT CP0Reg = 20 + PARITY_ERR CP0Reg = 26 + CACHE_ERR CP0Reg = 27 + ERROR_EPC CP0Reg = 30 +) + +// Status register bits (p.166) +const ( + IE = 0x001 << 0 // interrupt enable + EXL = 0x001 << 1 // exception level + ERL = 0x001 << 2 // error level + KSU = 0x003 << 3 // kernel/supervisor/user mode + UX = 0x001 << 5 // user mode 64-bit addressing and operations + SX = 0x001 << 6 // supervisor mode 64-bit addressing and operations + KX = 0x001 << 7 // kernel mode 64-bit addressing + IM = 0x0ff << 8 // interrupt mask + DS = 0x1ff << 16 // diagnostic status + RE = 0x001 << 25 // reverse-endian + FR = 0x001 << 26 // floating-point registers + RP = 0x001 << 27 // reduced power + CU = 0x00f << 28 // coprocessor usability +) + +// Cause register bits (p.171) +const ( + IP_SW0 = 0x01 << 8 + IP_SW1 = 0x02 << 8 + IP_SWMASK = 0x03 << 8 + IP_EXTMASK = 0x7C << 8 + IP_TIMER = 0x80 << 8 +) + +func (r CP0Reg) Load() uint32 { return mfc0(r) } +func (r CP0Reg) Store(val uint32) { mtc0(r, val) } + +func (r CP0Reg) LoadBits(mask uint32) uint32 { + return r.Load() & mask +} +func (r CP0Reg) StoreBits(mask uint32, val uint32) { + r.Store((r.Load() & ^mask) | (val & mask)) +} +func (r CP0Reg) SetBits(mask uint32) { + r.Store((r.Load() | mask)) +} +func (r CP0Reg) ClearBits(mask uint32) { + r.Store((r.Load() & ^mask)) +} + +func mtc0(creg CP0Reg, val uint32) +func mfc0(creg CP0Reg) uint32 diff --git a/src/internal/cpu/r4000/creg/creg_mips64.s b/src/internal/cpu/r4000/creg/creg_mips64.s new file mode 100644 index 00000000000000..c2dd1354fefd5b --- /dev/null +++ b/src/internal/cpu/r4000/creg/creg_mips64.s @@ -0,0 +1,215 @@ +#include "textflag.h" + +TEXT ·mtc0(SB),NOSPLIT,$0-12 + MOVV creg+0(FP), R4 + MOVWU val+8(FP), R5 + SLL $4, R4 + MOVV $_mtc0(SB), R6 + ADDU R6, R4 + JMP (R4) + + +TEXT _mtc0(SB),NOSPLIT|NOFRAME,$0 + MOVW R5, M(0) + RET + WORD $0x0 + MOVW R5, M(1) + RET + WORD $0x0 + MOVW R5, M(2) + RET + WORD $0x0 + MOVW R5, M(3) + RET + WORD $0x0 + MOVW R5, M(4) + RET + WORD $0x0 + MOVW R5, M(5) + RET + WORD $0x0 + MOVW R5, M(6) + RET + WORD $0x0 + MOVW R5, M(7) + RET + WORD $0x0 + MOVW R5, M(8) + RET + WORD $0x0 + MOVW R5, M(9) + RET + WORD $0x0 + MOVW R5, M(10) + RET + WORD $0x0 + MOVW R5, M(11) + RET + WORD $0x0 + MOVW R5, M(12) + RET + WORD $0x0 + MOVW R5, M(13) + RET + WORD $0x0 + MOVW R5, M(14) + RET + WORD $0x0 + MOVW R5, M(15) + RET + WORD $0x0 + MOVW R5, M(16) + RET + WORD $0x0 + MOVW R5, M(17) + RET + WORD $0x0 + MOVW R5, M(18) + RET + WORD $0x0 + MOVW R5, M(19) + RET + WORD $0x0 + MOVW R5, M(20) + RET + WORD $0x0 + MOVW R5, M(21) + RET + WORD $0x0 + MOVW R5, M(22) + RET + WORD $0x0 + MOVW R5, M(23) + RET + WORD $0x0 + MOVW R5, M(24) + RET + WORD $0x0 + MOVW R5, M(25) + RET + WORD $0x0 + MOVW R5, M(26) + RET + WORD $0x0 + MOVW R5, M(27) + RET + WORD $0x0 + MOVW R5, M(28) + RET + WORD $0x0 + MOVW R5, M(29) + RET + WORD $0x0 + MOVW R5, M(30) + RET + WORD $0x0 + MOVW R5, M(31) + RET + WORD $0x0 + + +TEXT ·mfc0(SB),NOSPLIT,$0-12 + MOVV creg+0(FP), R4 + SLL $4, R4 + MOVV $_mfc0(SB), R6 + ADDU R6, R4 + JMP (R4) + + +TEXT _mfc0(SB),NOSPLIT|NOFRAME,$0 + MOVW M(0), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(1), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(2), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(3), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(4), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(5), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(6), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(7), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(8), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(9), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(10), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(11), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(12), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(13), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(14), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(15), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(16), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(17), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(18), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(19), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(20), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(21), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(22), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(23), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(24), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(25), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(26), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(27), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(28), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(29), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(30), R5 + MOVW R5, ret+8(FP) + RET + MOVW M(31), R5 + MOVW R5, ret+8(FP) + RET diff --git a/src/internal/platform/zosarch.go b/src/internal/platform/zosarch.go index 9f9de5bf29fbd9..060e0567b1916a 100644 --- a/src/internal/platform/zosarch.go +++ b/src/internal/platform/zosarch.go @@ -46,6 +46,7 @@ var List = []OSArch{ {"netbsd", "arm64"}, {"noos", "riscv64"}, {"noos", "thumb"}, + {"noos", "mips64"}, {"openbsd", "386"}, {"openbsd", "amd64"}, {"openbsd", "arm"}, @@ -103,6 +104,7 @@ var distInfo = map[OSArch]osArchInfo{ {"netbsd", "arm64"}: {CgoSupported: true}, {"noos", "riscv64"}: {}, {"noos", "thumb"}: {}, + {"noos", "mips64"}: {}, {"openbsd", "386"}: {CgoSupported: true}, {"openbsd", "amd64"}: {CgoSupported: true}, {"openbsd", "arm"}: {CgoSupported: true}, diff --git a/src/runtime/asm_mips64.h b/src/runtime/asm_mips64.h new file mode 100644 index 00000000000000..9bf7c97f0e774b --- /dev/null +++ b/src/runtime/asm_mips64.h @@ -0,0 +1,104 @@ +// Copyright (c) 1995 Cygnus Support +// +// The authors hereby grant permission to use, copy, modify, distribute, and +// license this software and its documentation for any purpose, provided that +// existing copyright notices are retained in all copies and that this notice is +// included verbatim in any distributions. No written agreement, license, or +// royalty fee is required for any of the authorized uses. Modifications to this +// software may be copyrighted by their authors and need not follow the +// licensing terms described here, provided that the new terms are clearly +// indicated on the first page of each file where they apply. + + +// Useful memory constants +#define KSEG0 0xFFFFFFFF80000000 +#define KSEG1 0xFFFFFFFFA0000000 + +// Standard Co-Processor 0 registers +#define C0_INDEX 0 // Index of TLB Entry +#define C0_ENTRYLO0 2 // TLB entry's first PFN +#define C0_ENTRYLO1 3 // TLB entry's second PFN +#define C0_PAGEMASK 5 // Size of TLB Entries +#define C0_COUNT 9 // Timer Count Register +#define C0_ENTRYHI 10 // VPN and ASID of two TLB entry +#define C0_COMPARE 11 // Timer Compare Register +#define C0_SR 12 // Status Register +#define C0_CAUSE 13 // last exception description +#define C0_EPC 14 // Exception error address +#define C0_PRID 15 // Processor Revision ID +#define C0_CONFIG 16 // CPU configuration +#define C0_WATCHLO 18 // Watchpoint + +// Standard Processor Revision ID Register field offsets +#define PR_IMP 8 + +// Standard Config Register field offsets +#define CR_DB 4 +#define CR_IB 5 +#define CR_DC 6 +#define CR_IC 9 +#define CR_SC 17 +#define CR_SS 20 +#define CR_SB 22 + +// Standard Status Register bitmasks +#define SR_CU1 0x20000000 // Mark CP1 as usable +#define SR_FR 0x04000000 // Enable MIPS III FP registers +#define SR_BEV 0x00400000 // Controls location of exception vectors +#define SR_PE 0x00100000 // Mark soft reset (clear parity error) + +#define SR_KX 0x00000080 // Kernel extended addressing enabled +#define SR_SX 0x00000040 // Supervisor extended addressing enabled +#define SR_UX 0x00000020 // User extended addressing enabled + +#define SR_ERL 0x00000004 // Error level +#define SR_EXL 0x00000002 // Exception level +#define SR_IE 0x00000001 // Interrupts enabled + +// Standard Cause Register bitmasks +#define CAUSE_EXC_MASK (0x1F << 2) +#define CAUSE_EXC_INTERRUPT (0 << 2) +#define CAUSE_EXC_SYSCALL (8 << 2) +#define CAUSE_EXC_BREAKPOINT (9 << 2) +#define CAUSE_EXC_COPROCESSOR (11 << 2) + +// Masks for Status Registers IM bits and Cause Registers IP bits +#define INTR_MASK (0xFF << 8) +#define INTR_SW0 (0x01 << 8) +#define INTR_SW1 (0x02 << 8) +#define INTR_SW (0x03 << 8) +#define INTR_EXT (0x7C << 8) +#define INTR_TIMER (0x80 << 8) + +// BREAK is overloaded CACHE opcode. Register number specifies the cache op. +#define CACHE BREAK +// Prepend NOOP to ERET to avert CP0 hazards +#define ERET NOOP; NOOP; WORD $0x42000018 + +// Individual cache operations +#define INDEX_INVALIDATE_I R00 +#define INDEX_WRITEBACK_INVALIDATE_D R01 +#define INDEX_INVALIDATE_SI R02 +#define INDEX_WRITEBACK_INVALIDATE_SD R03 +#define INDEX_LOAD_TAG_I R04 +#define INDEX_LOAD_TAG_D R05 +#define INDEX_LOAD_TAG_SI R06 +#define INDEX_LOAD_TAG_SD R07 +#define INDEX_STORE_TAG_I R08 +#define INDEX_STORE_TAG_D R09 +#define INDEX_STORE_TAG_SI R10 +#define INDEX_STORE_TAG_SD R11 +#define CREATE_DIRTY_EXCLUSIVE_D R13 +#define CREATE_DIRTY_EXCLUSIVE_SD R15 +#define HIT_INVALIDATE_I R16 +#define HIT_INVALIDATE_D R17 +#define HIT_INVALIDATE_SI R18 +#define HIT_INVALIDATE_SD R19 +#define CACHE_FILL_I R20 +#define HIT_WRITEBACK_INVALIDATE_D R21 +#define HIT_WRITEBACK_INVALIDATE_SD R23 +#define HIT_WRITEBACK_I R24 +#define HIT_WRITEBACK_D R25 +#define HIT_WRITEBACK_SD R27 +#define HIT_SET_VIRTUAL_SI R30 +#define HIT_SET_VIRTUAL_SD R31 diff --git a/src/runtime/asm_mips64x.s b/src/runtime/asm_mips64x.s index 80cd87c4af335f..772df493a8f94f 100644 --- a/src/runtime/asm_mips64x.s +++ b/src/runtime/asm_mips64x.s @@ -11,6 +11,8 @@ #define REGCTXT R22 +#ifndef GOOS_noos + TEXT runtime·rt0_go(SB),NOSPLIT|TOPFRAME,$0 // R29 = stack; R4 = argc; R5 = argv @@ -74,6 +76,8 @@ nocgo: MOVV R0, 1(R0) RET +#endif + DATA runtime·mainPC+0(SB)/8,$runtime·main(SB) GLOBL runtime·mainPC(SB),RODATA,$8 diff --git a/src/runtime/const_noos_n64.go b/src/runtime/const_noos_n64.go new file mode 100644 index 00000000000000..fede0e0af49d10 --- /dev/null +++ b/src/runtime/const_noos_n64.go @@ -0,0 +1,36 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// TODO: Do not use GOTARGET tags in runtime. Come up with something smarter +// based on some kind of memory description similar to the -M linker option. + +//go:build noos && n64 + +package runtime + +const ( + _OS = 0 + noos = true + noosScaleDown = 2 // must be power of 2 + noosStackCacheSize = 16 * 1024 + noosNumStackOrders = 3 + noosHeapAddrBits = 23 // enough for 8 MiB K210 SRAM + noosLogHeapArenaBytes = 17 // 128 KiB + noosArenaBaseOffset = 0x0 + noosMinPhysPageSize = 256 + noosSpanSetInitSpineCap = 64 + noosStackMin = 2048 + noosStackSystem = 0 + noosStackGuard = 928 + noosFinBlockSize = 2 * 1024 + noosSweepMinHeapDistance = 8 * 1024 + noosDefaultHeapMinimum = 64 * 1024 + noosMemoryLimitHeapGoalHeadroom = 1 << 18 + noosGCSweepBlockEntries = 1024 + noosGCSweepBufInitSpineCap = 64 + noosGCBitsChunkBytes = 16 * 1024 + noosSemTabSize = 113 + noosGOGC = 50 + noosTimeHistMaxBucketBits = 47 +) diff --git a/src/runtime/cpuflags.go b/src/runtime/cpuflags.go index bbe93c5bea2d7e..1f562ea73eb6e3 100644 --- a/src/runtime/cpuflags.go +++ b/src/runtime/cpuflags.go @@ -17,8 +17,6 @@ const ( offsetX86HasRDTSCP = unsafe.Offsetof(cpu.X86.HasRDTSCP) offsetARMHasIDIVA = unsafe.Offsetof(cpu.ARM.HasIDIVA) - - offsetMIPS64XHasMSA = unsafe.Offsetof(cpu.MIPS64X.HasMSA) ) var ( diff --git a/src/runtime/memclr_mips64x.s b/src/runtime/memclr_mips64x.s index cf3a9c4ab4fb36..77cdcceb07f18b 100644 --- a/src/runtime/memclr_mips64x.s +++ b/src/runtime/memclr_mips64x.s @@ -4,7 +4,6 @@ //go:build mips64 || mips64le -#include "go_asm.h" #include "textflag.h" // See memclrNoHeapPointers Go doc for important implementation constraints. @@ -15,60 +14,6 @@ TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT,$0-16 MOVV n+8(FP), R2 ADDV R1, R2, R4 - // if less than 16 bytes or no MSA, do words check - SGTU $16, R2, R3 - BNE R3, no_msa - MOVBU internal∕cpu·MIPS64X+const_offsetMIPS64XHasMSA(SB), R3 - BEQ R3, R0, no_msa - - VMOVB $0, W0 - - SGTU $128, R2, R3 - BEQ R3, msa_large - - AND $15, R2, R5 - XOR R2, R5, R6 - ADDVU R1, R6 - -msa_small: - VMOVB W0, (R1) - ADDVU $16, R1 - SGTU R6, R1, R3 - BNE R3, R0, msa_small - BEQ R5, R0, done - VMOVB W0, -16(R4) - JMP done - -msa_large: - AND $127, R2, R5 - XOR R2, R5, R6 - ADDVU R1, R6 - -msa_large_loop: - VMOVB W0, (R1) - VMOVB W0, 16(R1) - VMOVB W0, 32(R1) - VMOVB W0, 48(R1) - VMOVB W0, 64(R1) - VMOVB W0, 80(R1) - VMOVB W0, 96(R1) - VMOVB W0, 112(R1) - - ADDVU $128, R1 - SGTU R6, R1, R3 - BNE R3, R0, msa_large_loop - BEQ R5, R0, done - VMOVB W0, -128(R4) - VMOVB W0, -112(R4) - VMOVB W0, -96(R4) - VMOVB W0, -80(R4) - VMOVB W0, -64(R4) - VMOVB W0, -48(R4) - VMOVB W0, -32(R4) - VMOVB W0, -16(R4) - JMP done - -no_msa: // if less than 8 bytes, do one byte at a time SGTU $8, R2, R3 BNE R3, out diff --git a/src/runtime/mksizeclasses_mcu.go b/src/runtime/mksizeclasses_mcu.go index 1c92eed35c6d75..b2b7d874f58373 100644 --- a/src/runtime/mksizeclasses_mcu.go +++ b/src/runtime/mksizeclasses_mcu.go @@ -52,7 +52,7 @@ func main() { fmt.Fprintln(&b, "// Code generated by mksizeclasses_mcu.go; DO NOT EDIT.") fmt.Fprintln(&b, "//go:generate go run mksizeclasses_mcu.go") fmt.Fprintln(&b) - fmt.Fprintln(&b, "//go:build noos && thumb") + fmt.Fprintln(&b, "//go:build noos && (thumb || mips)") fmt.Fprintln(&b) fmt.Fprintln(&b, "package runtime") classes := makeClasses() diff --git a/src/runtime/mksizeclasses_mcu64.go b/src/runtime/mksizeclasses_mcu64.go index a1828575c2c658..88dd1400ac2946 100644 --- a/src/runtime/mksizeclasses_mcu64.go +++ b/src/runtime/mksizeclasses_mcu64.go @@ -52,7 +52,7 @@ func main() { fmt.Fprintln(&b, "// Code generated by mksizeclasses_mcu64.go; DO NOT EDIT.") fmt.Fprintln(&b, "//go:generate go run mksizeclasses_mcu64.go") fmt.Fprintln(&b) - fmt.Fprintln(&b, "//go:build noos && riscv64") + fmt.Fprintln(&b, "//go:build noos && (riscv64 || mips64)") fmt.Fprintln(&b) fmt.Fprintln(&b, "package runtime") classes := makeClasses() diff --git a/src/runtime/os_noos_mips64.go b/src/runtime/os_noos_mips64.go new file mode 100644 index 00000000000000..6c7fd3c1c33c62 --- /dev/null +++ b/src/runtime/os_noos_mips64.go @@ -0,0 +1,12 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runtime + +//go:nosplit +func cputicks() int64 { + // Currently cputicks() is used in blocking profiler and to seed fastrand(). + // nanotime() is a poor approximation of CPU ticks that is enough for the profiler. + return nanotime() +} diff --git a/src/runtime/rt0_noos_mips64.go b/src/runtime/rt0_noos_mips64.go new file mode 100644 index 00000000000000..7e215abf957a31 --- /dev/null +++ b/src/runtime/rt0_noos_mips64.go @@ -0,0 +1,34 @@ +package runtime + +import "unsafe" + +const pallocMin = 20 * 1024 +const signalStackSize = 4096 + +//go:nosplit +func meminit(freeStart, freeEnd, nodmaStart, nodmaEnd uintptr) { + freeSize := freeEnd - freeStart + nodmaSize := nodmaEnd - nodmaStart + size := freeSize + nodmaSize + + // Estimate the space needed for non-heap allocations + palloc := int((freeSize>>(pageShift))*unsafe.Sizeof(emptymspan) + pallocMin) + + // We prefer the non-DMA memory for non-heap objects to preserve as much + // as possible of the DMA capable memory for heap allocations + pallocInFree := max(palloc-int(nodmaSize), 0) + + // Reduce the arena by the remainder of the non-heap space that did not fit in + // the non-DMA memory, properly align the arena + arenaSize := freeSize - uintptr(pallocInFree) + arenaSize &= ^(uintptr(heapArenaBytes) - 1) + arenaStart := freeEnd - arenaSize + + noosMem.free.start = freeStart + noosMem.free.end = freeEnd + noosMem.nodma.start = nodmaStart + noosMem.nodma.end = nodmaEnd + noosMem.arenaStart = arenaStart + noosMem.arenaSize = arenaSize + noosMem.size = size +} diff --git a/src/runtime/rt0_noos_mips64.s b/src/runtime/rt0_noos_mips64.s new file mode 100644 index 00000000000000..c9a375a30a975c --- /dev/null +++ b/src/runtime/rt0_noos_mips64.s @@ -0,0 +1,153 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "go_asm.h" +#include "go_tls.h" +#include "funcdata.h" +#include "textflag.h" +#include "asm_mips64.h" + +TEXT _rt0_mips64_noos(SB),NOSPLIT|NOFRAME,$0 + JMP ·rt0_target(SB) + +TEXT runtime·_rt0_mips64_noos1(SB),NOSPLIT|NOFRAME,$0 + // Clear .bss, .noptrbss and unallocated memory. + SUBU $16, R29 + + MOVW $runtime·bss(SB), R4 + MOVW $runtime·ebss(SB), R5 + SUB R4, R5 + MOVV R4, 8(R29) + MOVV R5, 16(R29) + JAL runtime·memclrNoHeapPointers(SB) + + MOVW $runtime·noptrbss(SB), R4 + MOVW $runtime·enoptrbss(SB), R5 + SUB R4, R5 + MOVV R4, 8(R29) + MOVV R5, 16(R29) + JAL runtime·memclrNoHeapPointers(SB) + + MOVW $runtime·end(SB), R4 + MOVW $runtime·ramend(SB), R5 + SUB R4, R5 + MOVV R4, 8(R29) + MOVV R5, 16(R29) + JAL runtime·memclrNoHeapPointers(SB) + + ADDU $16, R29 + + // Load interrupt vector + MOVW $runtime·intvector(SB), R8 + MOVW $0xa0000000, R9 + MOVW $4, R10 +loop: + MOVW (R8), R11 + MOVW R11, 0(R9) + MOVW R11, 0x80(R9) + MOVW R11, 0x100(R9) + MOVW R11, 0x180(R9) + CACHE HIT_INVALIDATE_I, 0(R9) + CACHE HIT_INVALIDATE_I, 0x80(R9) + CACHE HIT_INVALIDATE_I, 0x100(R9) + CACHE HIT_INVALIDATE_I, 0x180(R9) + ADD $4, R8 + ADD $4, R9 + ADDU $-1, R10 + BGTZ R10,loop + + JMP runtime·rt0_go(SB) + + +TEXT runtime·rt0_go(SB),NOSPLIT|NOFRAME|TOPFRAME,$0 + // setup main stack in cpu0.gh + MOVV $runtime·cpu0(SB), R8 // gh is the first field of the cpuctx struct + MOVV $runtime·ramend(SB), R29 // main stack starts at the end of memory + SUB $16, R29 + MOVV R29, (g_stack+stack_hi)(R8) + SUB $const_signalStackSize, R29, R9 + MOVV R9, (g_stack+stack_lo)(R8) + ADD $const_stackGuard, R9 + MOVV R9, g_stackguard0(R8) + MOVV R9, g_stackguard1(R8) + + // set up m0 (bootstrap thread), temporarily use cpu0.gh as g + MOVV $runtime·m0(SB), R9 + MOVV R8, m_g0(R9) // m0.g0 = cpu0.gh + MOVV R9, g_m(R8) // cpu0.gh.m = m0 + + MOVV R8, g // set g to gh + + JAL runtime·check(SB) + JAL runtime·osinit(SB) + + // initialize noosMem + MOVV $runtime·end(SB), R8 + MOVV $runtime·ramend(SB), R9 + MOVV $runtime·nodmastart(SB), R10 + MOVV $runtime·nodmaend(SB), R11 + SUB $const_signalStackSize, R9 + + SUB $32, R29 + MOVV R8, 8(R29) + MOVV R9, 16(R29) + MOVV R10, 24(R29) + MOVV R11, 32(R29) + JAL runtime·meminit(SB) + ADD $32, R29 + + // initialize noos tasker and Go scheduler + JAL runtime·taskerinit(SB) + JAL runtime·schedinit(SB) + + // allocate g0 for m0 and leave gh + SUB $16, R29 + MOVW $(2*const_stackMin), R4 + MOVW R4, 8(R29) + JAL runtime·malg(SB) + MOVV 16(R29), R8 // newg in R8 + ADD $16, R29 + + // stackguard check during newproc requires valid stackguard1 but + // malg sets it to 0xFFFFFFFF (mstart fixes this but is called later) + MOVV g_stackguard0(R8), R9 + MOVV R9, g_stackguard1(R8) + + MOVV $runtime·m0(SB), R9 + MOVV R8, m_g0(R9) // m0.g0 = newg + MOVV R9, g_m(R8) // newg.m = m0 + + // newg stack pointer to R29 + MOVV (g_stack+stack_hi)(R8), R29 + + // newg to g + MOVV g, R10 + MOVV R8, g + + // fix cpu0.gh, cpu0.mh + ADD $cpuctx_mh, R10, R9 // R10 points to cpu0 (and to cpu0.gh at the same time) + MOVV R10, m_g0(R9) // cpu0.mh.g0 = cpu0.gh + MOVV R10, m_gsignal(R9) // cpu0.mh.gsignal = cpu0.gh (to easily check for handler mode) + MOVV R9, g_m(R10) // cpu0.gh.m = cpu0.mh + + // create a new goroutine to start program + SUB $16, R29 + MOVV $runtime·mainPC(SB), R8 + MOVV R8, 8(R29) // arg 1: fn + MOVV $0, R8 + MOVV R8, 0(R29) // dummy LR + JAL runtime·newproc(SB) + ADD $16, R29 + + // enable interrupts + // TODO where to enable interupts correctly? + MOVW R0, M(C0_COMPARE) + MOVW M(C0_SR), R8 + OR $(SR_IE|INTR_SW|INTR_EXT), R8 + MOVW R8, M(C0_SR) + + // start this M + JAL runtime·mstart(SB) + + UNDEF diff --git a/src/runtime/sizeclasses_mcu64.go b/src/runtime/sizeclasses_mcu64.go index 6809a55bf9420d..777f3b5f165fea 100644 --- a/src/runtime/sizeclasses_mcu64.go +++ b/src/runtime/sizeclasses_mcu64.go @@ -1,7 +1,7 @@ // Code generated by mksizeclasses_mcu64.go; DO NOT EDIT. //go:generate go run mksizeclasses_mcu64.go -//go:build noos && riscv64 +//go:build noos && (riscv64 || mips64) package runtime diff --git a/src/runtime/sys_noos_mips64.s b/src/runtime/sys_noos_mips64.s new file mode 100644 index 00000000000000..912b8aade44b64 --- /dev/null +++ b/src/runtime/sys_noos_mips64.s @@ -0,0 +1,149 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "go_asm.h" +#include "go_tls.h" +#include "textflag.h" +#include "syscall_noos.h" + +// if you add new syscall you must check sysMaxArgs in tasker_noos_*.s + +// syscalls allowed for low priority interrupt handlers +DATA runtime·syscalls+(SYS_nanotime*8)(SB)/8, $·sysnanotime(SB) +DATA runtime·syscalls+(SYS_irqctl*8)(SB)/8, $·sysirqctl(SB) +DATA runtime·syscalls+(SYS_setprivlevel*8)(SB)/8, $·syssetprivlevel(SB) +DATA runtime·syscalls+(SYS_write*8)(SB)/8, $·syswrite(SB) +DATA runtime·syscalls+(SYS_cachemaint*8)(SB)/8, $·syscachemaint(SB) + +// syscalls disallowed for low priority interrupt handlers +DATA runtime·syscalls+(SYS_setsystim1*8)(SB)/8, $·syssetsystim1(SB) +DATA runtime·syscalls+(SYS_setsyswriter1*8)(SB)/8, $·syssetsyswriter1(SB) +DATA runtime·syscalls+(SYS_newosproc*8)(SB)/8, $·sysnewosproc(SB) +DATA runtime·syscalls+(SYS_exitThread*8)(SB)/8, $·sysexitThread(SB) +DATA runtime·syscalls+(SYS_futexsleep*8)(SB)/8, $·sysfutexsleep(SB) +DATA runtime·syscalls+(SYS_futexwakeup*8)(SB)/8, $·sysfutexwakeup(SB) +DATA runtime·syscalls+(SYS_osyield*8)(SB)/8, $·curcpuSchedule(SB) +DATA runtime·syscalls+(SYS_nanosleep*8)(SB)/8, $·sysnanosleep(SB) +DATA runtime·syscalls+(SYS_reset*8)(SB)/8, $·sysreset(SB) + +GLOBL runtime·syscalls(SB), RODATA, $(SYS_NUM*8) + +// func nanotime() int64 +TEXT ·nanotime(SB),NOSPLIT|NOFRAME,$0-8 + MOVV $SYS_nanotime, R8 + MOVV $(0+8), R9 + MOVV $8, R10 + SYSCALL + RET + +// func irqctl(irq, ctl, ctxid int) (enabled, prio, errno int) +TEXT ·irqctl(SB),NOSPLIT|NOFRAME,$0-48 + MOVV $SYS_irqctl, R8 + MOVV $(24+8), R9 + MOVV $24, R10 + SYSCALL + RET + +// func setprivlevel(newlevel int) (oldlevel, errno int) +TEXT ·setprivlevel(SB),NOSPLIT|NOFRAME,$0-24 + MOVV $SYS_setprivlevel, R8 + MOVV $(8+8), R9 + MOVV $16, R10 + SYSCALL + RET + +// func write(fd uintptr, p unsafe.Pointer, n int32) int32 +TEXT ·write(SB),NOSPLIT|NOFRAME,$0-32 + MOVV $SYS_write, R8 + MOVV $(24+8), R9 + MOVV $8, R10 + SYSCALL + RET + +// func cachemaint(op int, p unsafe.Pointer, size int) +TEXT ·cachemaint(SB),NOSPLIT|NOFRAME,$0-24 + MOVV $SYS_cachemaint, R8 + MOVV $(24+8), R9 + MOVV $0, R10 + SYSCALL + RET + +// func setsystim1() +TEXT ·setsystim1(SB),NOSPLIT|NOFRAME,$0-0 + MOVV $SYS_setsystim1, R8 + MOVV $(0+8), R9 + MOVV $0, R10 + SYSCALL + RET + + +// func setsyswriter1() +TEXT ·setsyswriter1(SB),NOSPLIT|NOFRAME,$0-0 + MOVV $SYS_setsyswriter1, R8 + MOVV $(0+8), R9 + MOVV $0, R10 + SYSCALL + RET + + +// func newosproc(mp *m) +TEXT ·newosproc(SB),NOSPLIT|NOFRAME,$0-8 + MOVV $SYS_newosproc, R8 + MOVV $(8+8), R9 + MOVV $0, R10 + SYSCALL + RET + +// func exitThread(wait *atomic.Uint32) +TEXT ·exitThread(SB),NOSPLIT|NOFRAME,$0-8 + MOVV $SYS_exitThread, R8 + MOVV $(8+8), R9 + MOVV $0, R10 + SYSCALL + RET + +// func futexsleep(addr *uint32, val uint32, ns int64) +TEXT ·futexsleep(SB),NOSPLIT|NOFRAME,$0-24 + MOVV $SYS_futexsleep, R8 + MOVV $(24+8), R9 + MOVV $0, R10 + SYSCALL + RET + +// func futexwakeup(addr *uint32, cnt uint32) +TEXT ·futexwakeup(SB),NOSPLIT|NOFRAME,$0-16 + MOVV $SYS_futexwakeup, R8 + MOVV $(16+8), R9 + MOVV $0, R10 + SYSCALL + RET + +// func osyield() +TEXT ·osyield(SB),NOSPLIT|NOFRAME,$0-0 + MOVV $SYS_osyield, R8 + MOVV $(0+8), R9 + MOVV $0, R10 + SYSCALL + RET + +// func nanosleep(ns int64) +TEXT ·nanosleep(SB),NOSPLIT|NOFRAME,$0-8 + MOVV $SYS_nanosleep, R8 + MOVV $(8+8), R9 + MOVV $0, R10 + SYSCALL + RET + +// func reset(level int, addr unsafe.Pointer) bool +TEXT ·reset(SB),NOSPLIT|NOFRAME,$0-16 + MOVV $SYS_reset, R8 + MOVV $(16+8), R9 + MOVV $0, R10 + SYSCALL + RET + +// func exit(r int32) +TEXT ·exit(SB),NOSPLIT|NOFRAME,$0-8 + NOOP + JMP -1(PC) diff --git a/src/runtime/target_noos_n64.go b/src/runtime/target_noos_n64.go new file mode 100644 index 00000000000000..d1e1ddf00b8f3a --- /dev/null +++ b/src/runtime/target_noos_n64.go @@ -0,0 +1,11 @@ +//go:build noos && n64 + +package runtime + +import "github.com/clktmr/n64/machine" + +//go:nowritebarrierrec +//go:nosplit +func targetDefaultWrite(fd int, p []byte) int { + return machine.DefaultWrite(fd, p) +} diff --git a/src/runtime/target_noos_n64.s b/src/runtime/target_noos_n64.s new file mode 100644 index 00000000000000..0a2adae93e22ff --- /dev/null +++ b/src/runtime/target_noos_n64.s @@ -0,0 +1,10 @@ +#include "go_asm.h" +#include "go_tls.h" +#include "funcdata.h" +#include "textflag.h" + +TEXT ·rt0_target(SB),NOSPLIT|NOFRAME,$0 + JMP machine·rt0(SB) + +TEXT ·unhandledExcepton_target(SB),NOSPLIT|NOFRAME,$0 + JMP machine·unhandledException(SB) diff --git a/src/runtime/tasker_noos.go b/src/runtime/tasker_noos.go index 1cf23fa93efb0f..a835d3c4f26506 100644 --- a/src/runtime/tasker_noos.go +++ b/src/runtime/tasker_noos.go @@ -75,8 +75,10 @@ import ( // Tasker code does not use FPU so the architecture specific context switch // code can avoid saving/restoring FPU context if not need. +var dummyNanoseconds int64 + //go:nosplit -func dummyNanotime() int64 { return 1 } +func dummyNanotime() int64 { dummyNanoseconds += 100; return dummyNanoseconds } //go:nosplit func dummySetalarm(ns int64) {} diff --git a/src/runtime/tasker_noos_mips64.go b/src/runtime/tasker_noos_mips64.go new file mode 100644 index 00000000000000..2db46964caa005 --- /dev/null +++ b/src/runtime/tasker_noos_mips64.go @@ -0,0 +1,96 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runtime + +import ( + "internal/abi" + "internal/cpu/r4000/creg" + "unsafe" +) + +// See saveGPRS and saveFPRS +const ( + numGPRS = 28 + numFPRS = 33 +) + +type mOS struct { + // thread context + gprs [numGPRS]uintptr + fprs [numFPRS]float64 + sp, fp, ra, epc uintptr +} + +var ( + cpu0 cpuctx + pcpu0 = &cpu0 +) + +//go:nowritebarrierrec +//go:nosplit +func taskerinit() { + *(*uintptr)(unsafe.Pointer(&cpu0.t)) = uintptr(unsafe.Pointer(&thetasker)) + cpu0.exe.set(getg().m) + allcpu := (*slice)(unsafe.Pointer(&thetasker.allcpu)) + *(*uintptr)(unsafe.Pointer(&allcpu.array)) = uintptr(unsafe.Pointer(&pcpu0)) + allcpu.len = 1 + allcpu.cap = 1 +} + +//go:nosplit +func curcpuSleep() {} + +//go:nosplit +func (cpu *cpuctx) newwork() { + creg.CAUSE.SetBits(creg.IP_SW0) +} + +//go:nosplit +func curcpuWakeup() {} + +//go:nosplit +func curcpuSavectxSched() {} + +//go:nosplit +func curcpuSavectxCall() {} + +//go:nosplit +func cpuid() int { + // for now only single CPU is supported + return 0 +} + +//go:nosplit +func archnewm(m *m) { + m.epc = abi.FuncPCABI0(mstart) + m.sp = m.g0.stack.hi + m.fp = uintptr(unsafe.Pointer(m.g0)) + m.ra = 1 // smallCtx flag +} + +//go:nosplit +func curcpuSchedule() { + // syscall still needs to duffcopy it's result back. mark for schedule + // and call scheduler after duffcopy in ISR. + curcpu().schedule = true +} + +//go:nowritebarrierrec +//go:nosplit +func defaultWrite(fd int, p []byte) int { + return targetDefaultWrite(fd, p) +} + +// only called from ISR, do not use +func enterScheduler() +func saveGPRs() +func restoreGPRs() +func saveFPRs() +func restoreFPRs() +func unhandledExternalInterrupt() + +// syscalls unsupported by mips64 +func syssetprivlevel(newlevel int) (oldlevel, errno int) { return } +func sysirqctl(irq, ctl, ctxid int) (enabled, prio, errno int) { return } diff --git a/src/runtime/tasker_noos_mips64.s b/src/runtime/tasker_noos_mips64.s new file mode 100644 index 00000000000000..dcc9e7d36707a5 --- /dev/null +++ b/src/runtime/tasker_noos_mips64.s @@ -0,0 +1,603 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Implementation of the exception handler for the tasker. +// +// There are different levels of context that we save on preemption: +// - goroutine (sp, fp): Needed to return to the preempted goroutine +// from the "signal" goroutine, if we didn't schedule. Saved in g.sched. +// - exception (lr, status, epc): Registers changed by the exception, must be +// restored at the end of the exception. Saved on "signal" stack, i.e. might +// be nested. +// - thread (sp, fp, lr, epc): Needed to schedule a goroutine. If we are +// going to call the scheduler, the goroutine context is moved into the thread +// context. Saved in m.mOS. +// - full (gprs, fprs): Full context saves all registers. Needed on arbitrary +// preemption, i.e. interrupts. These will be saved on the "signal" stack or +// in m.mOS. External interrupts will choose the former, to allow nested +// interrupts. Software interrupts will choose the latter, to allow +// scheduling. +// +// There are some conditions that affect how the exception is handled: +// - fromHandler: This flag is set when the exception preempted the "signal" +// goroutine, i.e. we are in a nested exception. A nested exception must not +// enter the scheduler, but always return to the caller stack. Therefore it's +// also only allowed to call fast syscalls, which are guaranteed to not call +// the scheduler. This flag is encoded in bit 0 of the exception context's +// _mepc value. +// - smallCtx: This flag indicates that we didn't save the gprs/fprs. We don't +// need to save them if preemption happened at function entry, i.e. syscalls. +// The caller won't care about these because of the Go ABI0. This flag is +// encoded in bit 0 of the exception context's _lr value. +// - fast syscall: Fast syscalls are guaranteed to not enter the scheduler. We +// need only to save the goroutine's and exception's context. + +#include "go_asm.h" +#include "go_tls.h" +#include "textflag.h" +#include "syscall_noos.h" + +#include "asm_mips64.h" + +#define sysMaxArgs (48+8) + +// Exception Context +#define _lr (0*8) +#define _mstatus (1*8) +#define _mepc (2*8) +#define excCtxSize (3*8) + + +// This will be copied into the processor's general exception vector. Since the +// general exception vector is limited in size, it just jumps to the actual +// implementation. +TEXT runtime·intvector(SB),NOSPLIT|NOFRAME,$0 + JMP ·exceptionHandler(SB) + + +// Main exception handler. If necessary saves goroutine context and switches +// stach to "signal" goroutine. Saves exceptions context. Masks pending +// interrupts in preparation for nested interrupts. Lastly evaluates the +// exception cause and calls the appropriate function. +// +// R26 and R27 are free, see runtime/asm_mips64x.s +// +// Interrupt exceptions are disabled by EXL=1 but other exceptions can +// still occur. +// +// Only syscalls and interrupts are handled at the moment, all other exceptions +// are fatal. +TEXT runtime·exceptionHandler(SB),NOSPLIT|NOFRAME,$0 + // Determine caller stack + MOVV $·cpu0(SB), R26 + BNE R26, g, fromThread + MOVV $1, R27 // fromHandler flag + JMP fromHandler + +fromThread: + MOVV $0, R27 + + // Save goroutine context in g.sched + MOVV R29, (g_sched+gobuf_sp)(R26) + MOVV g, (g_sched+gobuf_g)(R26) + MOVV (g_stack+stack_hi)(R26), R29 + + // Switch to "signal" goroutine + MOVV R26, g + +fromHandler: + // Save exception context on ISR stack + SUB $excCtxSize, R29 + OR $1, R31, R26 // Encode smallCtx flag in lr + MOVV R26, _lr(R29) // R29 is now free for use + MOVV M(C0_SR), R26 + MOVV R26, _mstatus(R29) + MOVV M(C0_EPC), R26 + OR R27, R26 // Encode fromHandler flag in EPC + MOVV R26, _mepc(R29) + + // Mask pending interrupts. Otherwise they will cause an exception loop + // as soon as we allow nested interrupts. + MOVV M(C0_CAUSE), R26 + MOVV $INTR_EXT, R27 // nesteding only allowed for external interrupts + AND R27, R26 + NOR R26, R26 + MOVV M(C0_SR), R27 + AND R27, R26 + MOVV R26, M(C0_SR) + + // Branch depending on exception cause + MOVV M(C0_CAUSE), R26 + AND $CAUSE_EXC_MASK, R26 + + MOVV $CAUSE_EXC_SYSCALL, R27 + BNE R26, R27, 2(PC) + JMP ·syscallHandler(SB) + + MOVV $CAUSE_EXC_INTERRUPT, R27 + BNE R26, R27, fatal + + MOVV M(C0_CAUSE), R26 + AND $INTR_MASK, R26 + + AND $INTR_EXT, R26, R27 + BEQ R27, R0, 2(PC) + JMP ·externalInterruptHandler(SB) + + AND $INTR_SW0, R26, R27 + BEQ R27, R0, 2(PC) + JMP ·softwareInterruptHandler(SB) + +fatal: + JMP ·unhandledExcepton_target(SB) + + +// Execute a system call. First unsets exception level to allow nested +// interrupts. Forwards epc by one instruction, otherwise the syscall is +// triggered again on ERET. Saves thread context if we might schedule, i.e. not +// a fast syscall and not nested exception. Duffcopies the syscall arguments +// from caller stack to "signal" stack. Jump to syscall handler. Duffcopy +// results back. It the syscall called curcpuSchedule, jump to enterScheduler. +// Otherwise restore exception and goroutine context. +// +// System call is like an oridnary function call, so all registers are caller +// save (Go ABI0). Meaning we don't need to save GPRs and are free to use them. +// The tiny wrapper over SYSCALL instruction adds additional parameters in +// R8-R10 registers: +// +// R8: syscall number +// R9: argument data size on the stack (+8 for caller return address) +// R10: return data size on the stack +TEXT runtime·syscallHandler(SB),NOSPLIT|NOFRAME,$0 + // Allow nested interrupts. Reminder: Don't use R26 or R27 when + // exceptions are enabled. + MOVV M(C0_SR), R26 + AND $~SR_EXL, R26 + MOVV R26, M(C0_SR) + + MOVV _mepc(R29), R2 + ADD $4, R2 // Don't execute syscall instruction again + MOVV R2, _mepc(R29) + + MOVV $SYS_LAST_FAST, R4 + SUB R4, R8, R4 // R4 <= 0: fast syscall + + // If fromHandler skip saving thread context + AND $1, R2 // fromHandler flag + BNE R2, R0, currentStack + + MOVV (g_sched+gobuf_sp)(g), R1 // duffcopy src thread + BLEZ R4, duffcopy // fast syscall + + // Save thread context in mOS + // Needs to be done before the syscall, cpuctx.exe might be nil + // afterwards (e.g. in nanosleep) + MOVV (cpuctx_exe)(g), R2 + + MOVV _lr(R29), R3 + MOVV R3, (m_mOS+mOS_ra)(R2) + + MOVV _mepc(R29), R3 + AND $~1, R3 // Remove fromHandler flag from epc + MOVV R3, (m_mOS+mOS_epc)(R2) + + MOVV (g_sched+gobuf_sp)(g), R3 + MOVV R3, (m_mOS+mOS_sp)(R2) + MOVV (g_sched+gobuf_g)(g), R3 + MOVV R3, (m_mOS+mOS_fp)(R2) + + JMP duffcopy + +currentStack: + BGTZ R4, badSyscall // slow syscall from handler + ADD $excCtxSize, R29, R1 // duffcopy src handler + +duffcopy: + // 3 extra registers to preserve src, dst and size of result + SUB $sysMaxArgs+3*8, R29 + + // Copy arguments from the caller's stack + MOVV $·duffcopy+2048(SB), R3 + SLL $1, R9 + SUB R9, R3 + MOVV R29, R2 // duffcopy dst + JAL (R3) + + // Save data needed to copy the return values back to the caller's stack + MOVV R1, (sysMaxArgs+0*8)(R29) + MOVV R2, (sysMaxArgs+1*8)(R29) + MOVV R10, (sysMaxArgs+2*8)(R29) + + // Call the service routine + MOVV $·syscalls(SB), R9 + SLL $3, R8 + ADD R8, R9 + MOVV (R9), R9 + JAL (R9) + + // Copy the return values back to the caller's stack + MOVV (sysMaxArgs+2*8)(R29), R10 + BEQ R0, R10, nothingToCopy + MOVV (sysMaxArgs+0*8)(R29), R2 // duffcopy dst + MOVV (sysMaxArgs+1*8)(R29), R1 // duffcopy src + MOVV $·duffcopy+2048(SB), R3 + SLL $1, R10 + SUB R10, R3 + JAL (R3) + +nothingToCopy: + ADD $sysMaxArgs+3*8, R29 + + // Run the scheduler if the syscall wants it + MOVB cpuctx_schedule(g), R8 // Always false if nested or fast syscall + BEQ R8, R0, 2(PC) + JMP ·enterScheduler(SB) + + // Restore ctx of caller + MOVV _lr(R29), R1 + AND $~1, R1, R31 // Remove smallCtx flag from lr + MOVV _mstatus(R29), R1 + MOVV R1, M(C0_SR) + MOVV _mepc(R29), R1 + AND $1, R1, R2 + AND $~1, R1 // Remove fromHandler flag from epc + MOVV R1, M(C0_EPC) + + ADD $excCtxSize, R29 + + BNE R2, R0, return + + MOVV $·cpu0(SB), R1 + MOVV (g_sched+gobuf_sp)(R1), R29 + MOVV (g_sched+gobuf_g)(R1), g + +return: + ERET + +badSyscall: + BREAK + + +// An interrupt was caused by software, particularly SW0. This can happen on +// any instruction. Saves full context in the thread context, enables nested +// interrupts and jumps to enterScheduler. +// +// TODO do we really need to save full context? SW_INT is only set in newwork() +TEXT runtime·softwareInterruptHandler(SB),NOSPLIT|NOFRAME,$0 + MOVV (cpuctx_exe)(g), R26 + MOVV $(m_mOS+mOS_gprs)(R26), R26 + JAL ·saveGPRs(SB) + + // Save thread context in mOS + MOVV (cpuctx_exe)(g), R27 + + MOVV _lr(R29), R26 + AND $~1, R26 // Remove smallCtx flag from lr + MOVV R26, (m_mOS+mOS_ra)(R27) + + MOVV _mepc(R29), R26 + AND $~1, R26 // Remove fromHandler flag from epc + MOVV R26, (m_mOS+mOS_epc)(R27) + + MOVV (g_sched+gobuf_sp)(g), R26 + MOVV R26, (m_mOS+mOS_sp)(R27) + MOVV (g_sched+gobuf_g)(g), R26 + MOVV R26, (m_mOS+mOS_fp)(R27) + + // Enable nested interrupts + MOVV M(C0_SR), R26 + AND $~SR_EXL, R26 + MOVV R26, M(C0_SR) + + // Clear pending bit and enter the scheduler. + MOVV M(C0_CAUSE), R26 + AND $~INTR_SW0, R26 + MOVV R26, M(C0_CAUSE) + + JMP ·enterScheduler(SB) + + +// Calls the scheduler and switches to the newly scheduled goroutine. Assumes +// nested interrupts are enabled. +TEXT runtime·enterScheduler(SB),NOSPLIT|NOFRAME,$0 + // Enter scheduler + MOVB R0, cpuctx_schedule(g) + JAL ·curcpuRunScheduler(SB) + + // Clear cpuctx.newexe + MOVB R0, (cpuctx_newexe)(g) + + // Restore mstatus from exception context + MOVV _mstatus(R29), R1 + MOVV R1, M(C0_SR) + ADD $excCtxSize, R29 + + // Disable nested interrupts + MOVV M(C0_SR), R8 + OR $SR_EXL, R8 + MOVV R8, M(C0_SR) + + MOVV (cpuctx_exe)(g), R27 + MOVV (m_mOS+mOS_ra)(R27), R9 + + AND $1, R9 // smallCtx flag + BNE R0, R9, smallCtx + + MOVV $(m_mOS+mOS_gprs)(R27), R26 + JAL ·restoreGPRs(SB) + // Only use R26, R27 from here + +smallCtx: + MOVV (m_mOS+mOS_sp)(R27), R29 + MOVV (m_mOS+mOS_fp)(R27), g + MOVV (m_mOS+mOS_ra)(R27), R31 + MOVV (m_mOS+mOS_epc)(R27), R26 + MOVV R26, M(C0_EPC) + MOVV $~1, R27 + AND R27, R31 // Remove smallCtx flag + + ERET + + +// Calls a user implementation for a pending external interrupt. External +// interrupt can happen anytime, saves full context. Context will be saved on +// the ISR stack, to allow nested interrupts be supported. Calculates the +// runtime.vector offset from the interrupt number and calls it. Restores +// context and returns. +// TODO save fprs? +TEXT runtime·externalInterruptHandler(SB),NOSPLIT|NOFRAME,$0 + SUB $const_numGPRS*8, R29 + MOVV R29, R26 + JAL ·saveGPRs(SB) + + // Context is saved. Enable nested interrupts. Don't use R26, R27. + MOVV M(C0_SR), R27 + AND $~SR_EXL, R27 + MOVV R27, M(C0_SR) + + // External interrupts are handled by the application. We need to call + // one of the registered handlers. + MOVV M(C0_CAUSE), R8 + AND $INTR_EXT, R8 + + SRL $8, R8 // INTR_EXT in lsb + MOVV $1, R9 + +findVector: + // Find and handle only the first pending interrupt. If there are + // multiple interrupts pending, the interrupt handler will run again. + AND $1, R8, R10 + BNE R10, R0, callVector + ADD $1, R9 + SRL $1, R8 + JMP findVector + +callVector: + SLL $3, R9, R8 // irq vector offset + + // Get interrupt vector + MOVV $runtime·vectors(SB), R10 + MOVV (R10), R11 + SUB R9, R11 + BLEZ R11, fatal + ADD R8, R10 + MOVV (R10), R10 + + JAL (R10) + + // External interrupts can't be cleared by clearing the bits in cause + // register. The user handler must have cleared the interrupt at this + // point in the external source. + + // Disable nested interrupts + MOVV M(C0_SR), R8 + OR $SR_EXL, R8 + MOVV R8, M(C0_SR) + + // Restore context of caller + MOVV R29, R26 + JAL ·restoreGPRs(SB) + // Only use R26, R27 from here + ADD $const_numGPRS*8, R29 + + MOVV _mstatus(R29), R26 + MOVV R26, M(C0_SR) + MOVV _lr(R29), R26 + MOVV $~1, R27 + AND R27, R26, R31 // Remove smallCtx flag from lr + MOVV _mepc(R29), R26 + MOVV $~1, R27 + AND R26, R27 // Remove fromHandler flag from EPC + MOVV R27, M(C0_EPC) + + MOVV $1, R27 + AND R26, R27 + + ADD $excCtxSize, R29 + + // Don't switch stacks yet if we were called from handler + BNE R27, R0, return + + MOVV $·cpu0(SB), R26 + MOVV (g_sched+gobuf_sp)(R26), R29 + MOVV (g_sched+gobuf_g)(R26), g + +return: + ERET + +fatal: + JMP ·unhandledExternalInterrupt(SB) + + +// Do not remove. Required by the linker, runtime.vectors defaults to this. +TEXT ·unhandledExternalInterrupt(SB),NOSPLIT|NOFRAME,$0 + BREAK + + +// R26 must point to where gprs will be stored. +TEXT ·saveGPRs(SB),NOSPLIT|NOFRAME,$0 + MOVV R1, 0(R26) + MOVV R2, 8(R26) + MOVV R3, 16(R26) + MOVV R4, 24(R26) + MOVV R5, 32(R26) + MOVV R6, 40(R26) + MOVV R7, 48(R26) + MOVV R8, 56(R26) + MOVV R9, 64(R26) + MOVV R10, 72(R26) + MOVV R11, 80(R26) + MOVV R12, 88(R26) + MOVV R13, 96(R26) + MOVV R14, 104(R26) + MOVV R15, 112(R26) + MOVV R16, 120(R26) + MOVV R17, 128(R26) + MOVV R18, 136(R26) + MOVV R19, 144(R26) + MOVV R20, 152(R26) + MOVV R21, 160(R26) + MOVV R22, 168(R26) + MOVV R23, 176(R26) + MOVV R24, 184(R26) + MOVV R25, 192(R26) + MOVV RSB, 200(R26) + MOVV HI, R1 + MOVV R1, 208(R26) + MOVV LO, R1 + MOVV R1, 216(R26) + RET + + +// R26 must point to stored gprs. Only use R26, R27 after restoring. Be +// especially careful and look at the disassembly; The assembler might decide +// to use R16-R23 for you. +TEXT ·restoreGPRs(SB),NOSPLIT|NOFRAME,$0 + MOVV 216(R26), R1 + MOVV R1, LO + MOVV 208(R26), R1 + MOVV R1, HI + MOVV 200(R26), RSB + MOVV 192(R26), R25 + MOVV 184(R26), R24 + MOVV 176(R26), R23 + MOVV 168(R26), R22 + MOVV 160(R26), R21 + MOVV 152(R26), R20 + MOVV 144(R26), R19 + MOVV 136(R26), R18 + MOVV 128(R26), R17 + MOVV 120(R26), R16 + MOVV 112(R26), R15 + MOVV 104(R26), R14 + MOVV 96(R26), R13 + MOVV 88(R26), R12 + MOVV 80(R26), R11 + MOVV 72(R26), R10 + MOVV 64(R26), R9 + MOVV 56(R26), R8 + MOVV 48(R26), R7 + MOVV 40(R26), R6 + MOVV 32(R26), R5 + MOVV 24(R26), R4 + MOVV 16(R26), R3 + MOVV 8(R26), R2 + MOVV 0(R26), R1 + RET + + +// Might clobber some gprs! +// R26 must point to where fprs will be stored. +TEXT ·saveFPRs(SB),NOSPLIT|NOFRAME,$0 + #ifndef GOMIPS64_softfloat + MOVV FCR31, R1 + MOVV R1, 0(R26) + MOVD F0, 8(R26) + MOVD F1, 16(R26) + MOVD F2, 24(R26) + MOVD F3, 32(R26) + MOVD F4, 40(R26) + MOVD F5, 48(R26) + MOVD F6, 56(R26) + MOVD F7, 64(R26) + MOVD F8, 72(R26) + MOVD F9, 80(R26) + MOVD F10, 88(R26) + MOVD F11, 96(R26) + MOVD F12, 104(R26) + MOVD F13, 112(R26) + MOVD F14, 120(R26) + MOVD F15, 128(R26) + MOVD F16, 136(R26) + MOVD F17, 144(R26) + MOVD F18, 152(R26) + MOVD F19, 160(R26) + MOVD F20, 168(R26) + MOVD F21, 176(R26) + MOVD F22, 184(R26) + MOVD F23, 192(R26) + MOVD F24, 200(R26) + MOVD F25, 208(R26) + MOVD F26, 216(R26) + MOVD F27, 224(R26) + MOVD F28, 232(R26) + MOVD F29, 240(R26) + MOVD F30, 248(R26) + MOVD F31, 256(R26) + #endif + RET + + +// Might clobber some gprs! +// R26 must point to stored fprs. +TEXT ·restoreFPRs(SB),NOSPLIT|NOFRAME,$0 + #ifndef GOMIPS64_softfloat + MOVD 256(R26), F31 + MOVD 248(R26), F30 + MOVD 240(R26), F29 + MOVD 232(R26), F28 + MOVD 224(R26), F27 + MOVD 216(R26), F26 + MOVD 208(R26), F25 + MOVD 200(R26), F24 + MOVD 192(R26), F23 + MOVD 184(R26), F22 + MOVD 176(R26), F21 + MOVD 168(R26), F20 + MOVD 160(R26), F19 + MOVD 152(R26), F18 + MOVD 144(R26), F17 + MOVD 136(R26), F16 + MOVD 128(R26), F15 + MOVD 120(R26), F14 + MOVD 112(R26), F13 + MOVD 104(R26), F12 + MOVD 96(R26), F11 + MOVD 88(R26), F10 + MOVD 80(R26), F9 + MOVD 72(R26), F8 + MOVD 64(R26), F7 + MOVD 56(R26), F6 + MOVD 48(R26), F5 + MOVD 40(R26), F4 + MOVD 32(R26), F3 + MOVD 24(R26), F2 + MOVD 16(R26), F1 + MOVD 8(R26), F0 + MOVV 0(R26), R1 + MOVV R1, FCR31 + #endif + RET + + +// syscalls not supported by mips64 + +// func sysreset(level int, addr unsafe.Pointer) bool +TEXT ·sysreset(SB),NOSPLIT|NOFRAME,$0-12 + NOP + + +// func syscachemaint(op int, p unsafe.Pointer, size int) +TEXT ·syscachemaint(SB),NOSPLIT,$0-12 + NOP diff --git a/src/runtime/tls_mips64x.s b/src/runtime/tls_mips64x.s index ec2748e5b22f03..1e7aac99c9f207 100644 --- a/src/runtime/tls_mips64x.s +++ b/src/runtime/tls_mips64x.s @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build mips64 || mips64le +//go:build (mips64 || mips64le) && !noos #include "go_asm.h" #include "go_tls.h" diff --git a/src/runtime/tls_noos_mips64.s b/src/runtime/tls_noos_mips64.s new file mode 100644 index 00000000000000..a64e501ca9a421 --- /dev/null +++ b/src/runtime/tls_noos_mips64.s @@ -0,0 +1,11 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +TEXT runtime·save_g(SB),NOSPLIT|NOFRAME,$0-0 + RET + +TEXT runtime·load_g(SB),NOSPLIT|NOFRAME,$0-0 + RET diff --git a/src/syscall/syscall_noos.go b/src/syscall/syscall_noos.go index cb6942372ff78c..294e71cf71ecbd 100644 --- a/src/syscall/syscall_noos.go +++ b/src/syscall/syscall_noos.go @@ -19,9 +19,9 @@ type Error struct{ s string } var ( ENOTSUP = &Error{"operation not supported"} - EINVAL = &Error{"operation not supported"} - ENOENT = &Error{"invalid argument"} - EACCES = &Error{"no such file or directory"} + EINVAL = &Error{"invalid argument"} + ENOENT = &Error{"no such file or directory"} + EACCES = &Error{"permission denied"} EPERM = &Error{"operation not permitted"} EEXIST = &Error{"file exists"} ENOTEMPTY = &Error{"directory not empty"} @@ -33,7 +33,7 @@ var ( ENOSPC = &Error{"no space left on device"} EBADF = &Error{"bad file descriptor"} ECANCELED = &Error{"operation canceled"} - EINTR = &Error{"interrupt"} // for os package only + EINTR = &Error{"interrupt"} // for os package only //ENOPROTOOPT = &Error{"protocol not available"} // for net package only )