[arch][vax] initial stab at booting on a vax
Booting on a real MicroVAX 3100/40 via netboot and on simh emulating a micrvax 3900. Doesn't fully work, lots of stuff is stubbed out, but it starts to run and hits unimplemented bits and stops.
This commit is contained in:
57
arch/vax/arch.c
Normal file
57
arch/vax/arch.c
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Travis Geiselbrecht
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <lk/trace.h>
|
||||
#include <lk/debug.h>
|
||||
#include <stdint.h>
|
||||
#include <arch/ops.h>
|
||||
#include <arch/vax.h>
|
||||
|
||||
#define LOCAL_TRACE 0
|
||||
|
||||
void arch_early_init(void) {
|
||||
// set the top level exception handler
|
||||
//riscv_csr_write(mtvec, (uintptr_t)&riscv_exception_entry);
|
||||
|
||||
// mask all exceptions, just in case
|
||||
//riscv_csr_clear(mstatus, RISCV_STATUS_MIE);
|
||||
//riscv_csr_clear(mie, RISCV_MIE_MTIE | RISCV_MIE_MSIE | RISCV_MIE_SEIE | RISCV_MIE_MEIE);
|
||||
|
||||
// enable cycle counter (disabled for now, unimplemented on sifive-e)
|
||||
//riscv_csr_set(mcounteren, 1);
|
||||
}
|
||||
|
||||
void arch_init(void) {
|
||||
// print some arch info
|
||||
//dprintf(INFO, "RISCV: mvendorid %#lx marchid %#lx mimpid %#lx mhartid %#lx\n",
|
||||
// riscv_csr_read(mvendorid), riscv_csr_read(marchid),
|
||||
// riscv_csr_read(mimpid), riscv_csr_read(mhartid));
|
||||
//dprintf(INFO, "RISCV: misa %#lx\n", riscv_csr_read(misa));
|
||||
|
||||
// enable external interrupts
|
||||
//riscv_csr_set(mie, RISCV_MIE_MEIE);
|
||||
}
|
||||
|
||||
void arch_idle(void) {
|
||||
// disabled for now, QEMU seems to have some trouble emulating wfi properly
|
||||
// also have trouble breaking into sifive-e board with openocd when wfi
|
||||
// __asm__ volatile("wfi");
|
||||
}
|
||||
|
||||
void arch_chain_load(void *entry, ulong arg0, ulong arg1, ulong arg2, ulong arg3) {
|
||||
PANIC_UNIMPLEMENTED;
|
||||
}
|
||||
|
||||
/* unimplemented cache operations */
|
||||
void arch_disable_cache(uint flags) { PANIC_UNIMPLEMENTED; }
|
||||
void arch_enable_cache(uint flags) { PANIC_UNIMPLEMENTED; }
|
||||
|
||||
void arch_clean_cache_range(addr_t start, size_t len) { PANIC_UNIMPLEMENTED; }
|
||||
void arch_clean_invalidate_cache_range(addr_t start, size_t len) { PANIC_UNIMPLEMENTED; }
|
||||
void arch_invalidate_cache_range(addr_t start, size_t len) { PANIC_UNIMPLEMENTED; }
|
||||
void arch_sync_cache_range(addr_t start, size_t len) { PANIC_UNIMPLEMENTED; }
|
||||
0
arch/vax/asm.S
Normal file
0
arch/vax/asm.S
Normal file
71
arch/vax/include/arch/arch_ops.h
Normal file
71
arch/vax/include/arch/arch_ops.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Travis Geiselbrecht
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <lk/compiler.h>
|
||||
#include <lk/debug.h>
|
||||
#include <arch/vax.h>
|
||||
|
||||
static inline void arch_enable_ints(void) {
|
||||
// set the IPL to 0
|
||||
mtpr(PR_IPL, 0);
|
||||
}
|
||||
|
||||
static inline void arch_disable_ints(void) {
|
||||
// set the IPL to 31
|
||||
mtpr(PR_IPL, 31);
|
||||
}
|
||||
|
||||
static inline bool arch_ints_disabled(void) {
|
||||
uint32_t ipl = mfpr(PR_IPL);
|
||||
return ipl > 0;
|
||||
}
|
||||
|
||||
static inline int atomic_add(volatile int *ptr, int val) {
|
||||
// XXX not actually atomic
|
||||
int oldval = *ptr;
|
||||
*ptr += val;
|
||||
return oldval;
|
||||
//return __atomic_fetch_add(ptr, val, __ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
static inline int atomic_or(volatile int *ptr, int val) {
|
||||
return __atomic_fetch_or(ptr, val, __ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
static inline int atomic_and(volatile int *ptr, int val) {
|
||||
return __atomic_fetch_and(ptr, val, __ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
static inline int atomic_swap(volatile int *ptr, int val) {
|
||||
return __atomic_exchange_n(ptr, val, __ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
/* use a global pointer to store the current_thread */
|
||||
extern struct thread *_current_thread;
|
||||
|
||||
static inline struct thread *get_current_thread(void) {
|
||||
return _current_thread;
|
||||
}
|
||||
|
||||
static inline void set_current_thread(struct thread *t) {
|
||||
_current_thread = t;
|
||||
}
|
||||
|
||||
static inline uint32_t arch_cycle_count(void) {
|
||||
uint32_t count = 0;
|
||||
|
||||
//__asm__("rdcycle %0" : "=r"(count));
|
||||
//count = riscv_csr_read(mcycle);
|
||||
return count;
|
||||
}
|
||||
|
||||
static inline uint arch_curr_cpu_num(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
43
arch/vax/include/arch/arch_thread.h
Normal file
43
arch/vax/include/arch/arch_thread.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Travis Geiselbrecht
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#if 0
|
||||
struct riscv32_context_switch_frame {
|
||||
uint32_t ra; // return address (x1)
|
||||
uint32_t sp; // stack pointer (x2)
|
||||
uint32_t tp; // thread pointer (x4)
|
||||
|
||||
uint32_t s0; // x8-x9
|
||||
uint32_t s1;
|
||||
|
||||
uint32_t s2; // x18-x27
|
||||
uint32_t s3;
|
||||
uint32_t s4;
|
||||
uint32_t s5;
|
||||
uint32_t s6;
|
||||
uint32_t s7;
|
||||
uint32_t s8;
|
||||
uint32_t s9;
|
||||
uint32_t s10;
|
||||
uint32_t s11;
|
||||
};
|
||||
|
||||
struct arch_thread {
|
||||
struct riscv32_context_switch_frame cs_frame;
|
||||
};
|
||||
|
||||
void riscv32_context_switch(struct riscv32_context_switch_frame *oldcs,
|
||||
struct riscv32_context_switch_frame *newcs);
|
||||
#endif
|
||||
|
||||
struct arch_thread {
|
||||
};
|
||||
|
||||
16
arch/vax/include/arch/defines.h
Normal file
16
arch/vax/include/arch/defines.h
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Travis Geiselbrecht
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#define PAGE_SIZE 512
|
||||
#define PAGE_SIZE_SHIFT 9
|
||||
|
||||
// XXX is this right?
|
||||
#define CACHE_LINE 32
|
||||
|
||||
#define ARCH_DEFAULT_STACK_SIZE 1024
|
||||
77
arch/vax/include/arch/riscv.h
Normal file
77
arch/vax/include/arch/riscv.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Travis Geiselbrecht
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#define RISCV_STATUS_SIE (1u << 2)
|
||||
#define RISCV_STATUS_MIE (1u << 3)
|
||||
#define RISCV_STATUS_MPIE (1u << 7)
|
||||
#define RISCV_STATUS_MPP_MASK (3u << 11)
|
||||
|
||||
#define RISCV_MIE_MSIE (1u << 3)
|
||||
#define RISCV_MIE_MTIE (1u << 7)
|
||||
#define RISCV_MIE_SEIE (1u << 9)
|
||||
#define RISCV_MIE_MEIE (1u << 11)
|
||||
|
||||
#define RISCV_MIP_MSIP (1u << 3)
|
||||
#define RISCV_MIP_MTIP (1u << 7)
|
||||
#define RISCV_MIP_MEIP (1u << 11)
|
||||
|
||||
#define RISCV_MCAUSE_INT (1u << 31)
|
||||
|
||||
#define riscv_csr_clear(csr, bits) \
|
||||
({ \
|
||||
ulong __val = bits; \
|
||||
__asm__ volatile( \
|
||||
"csrc " #csr ", %0" \
|
||||
:: "rK" (__val) \
|
||||
: "memory"); \
|
||||
})
|
||||
|
||||
#define riscv_csr_read_clear(csr, bits) \
|
||||
({ \
|
||||
ulong __val = bits; \
|
||||
ulong __val_out; \
|
||||
__asm__ volatile( \
|
||||
"csrrc %0, " #csr ", %1" \
|
||||
: "=r"(__val_out) \
|
||||
: "rK" (__val) \
|
||||
: "memory"); \
|
||||
__val_out; \
|
||||
})
|
||||
|
||||
#define riscv_csr_set(csr, bits) \
|
||||
({ \
|
||||
ulong __val = bits; \
|
||||
__asm__ volatile( \
|
||||
"csrs " #csr ", %0" \
|
||||
:: "rK" (__val) \
|
||||
: "memory"); \
|
||||
})
|
||||
|
||||
#define riscv_csr_read(csr) \
|
||||
({ \
|
||||
ulong __val; \
|
||||
__asm__ volatile( \
|
||||
"csrr %0, " #csr \
|
||||
: "=r" (__val) \
|
||||
:: "memory"); \
|
||||
__val; \
|
||||
})
|
||||
|
||||
#define riscv_csr_write(csr, val) \
|
||||
({ \
|
||||
ulong __val = (ulong)val; \
|
||||
__asm__ volatile( \
|
||||
"csrw " #csr ", %0" \
|
||||
:: "rK" (__val) \
|
||||
: "memory"); \
|
||||
__val; \
|
||||
})
|
||||
|
||||
void riscv_exception_entry(void);
|
||||
enum handler_return riscv_timer_exception(void);
|
||||
58
arch/vax/include/arch/spinlock.h
Normal file
58
arch/vax/include/arch/spinlock.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Travis Geiselbrecht
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <arch/ops.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#if WITH_SMP
|
||||
#error vax does not support SMP
|
||||
#endif
|
||||
|
||||
#define SPIN_LOCK_INITIAL_VALUE (0)
|
||||
|
||||
typedef unsigned int spin_lock_t;
|
||||
|
||||
typedef unsigned long spin_lock_saved_state_t;
|
||||
typedef unsigned int spin_lock_save_flags_t;
|
||||
|
||||
static inline void arch_spin_lock(spin_lock_t *lock) {
|
||||
*lock = 1;
|
||||
}
|
||||
|
||||
static inline int arch_spin_trylock(spin_lock_t *lock) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void arch_spin_unlock(spin_lock_t *lock) {
|
||||
*lock = 0;
|
||||
}
|
||||
|
||||
static inline void arch_spin_lock_init(spin_lock_t *lock) {
|
||||
*lock = SPIN_LOCK_INITIAL_VALUE;
|
||||
}
|
||||
|
||||
static inline bool arch_spin_lock_held(spin_lock_t *lock) {
|
||||
return *lock != 0;
|
||||
}
|
||||
|
||||
/* default arm flag is to just disable plain irqs */
|
||||
#define ARCH_DEFAULT_SPIN_LOCK_FLAG_INTERRUPTS 0
|
||||
|
||||
static inline void
|
||||
arch_interrupt_save(spin_lock_saved_state_t *statep, spin_lock_save_flags_t flags) {
|
||||
/* disable interrupts by clearing the MIE bit while atomically saving the old state */
|
||||
//*statep = riscv_csr_read_clear(mstatus, RISCV_STATUS_MIE) & RISCV_STATUS_MIE;
|
||||
}
|
||||
|
||||
static inline void
|
||||
arch_interrupt_restore(spin_lock_saved_state_t old_state, spin_lock_save_flags_t flags) {
|
||||
/* drop the old MIE flag into the status register */
|
||||
//riscv_csr_set(mstatus, old_state);
|
||||
}
|
||||
|
||||
8
arch/vax/include/arch/vax.h
Normal file
8
arch/vax/include/arch/vax.h
Normal file
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Travis Geiselbrecht
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
#include <arch/vax/mtpr.h>
|
||||
188
arch/vax/include/arch/vax/mtpr.h
Normal file
188
arch/vax/include/arch/vax/mtpr.h
Normal file
@@ -0,0 +1,188 @@
|
||||
/* $NetBSD: mtpr.h,v 1.23 2017/05/22 17:12:11 ragge Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1994 Ludd, University of Lule}, Sweden.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* All bugs are subject to removal without further notice */
|
||||
|
||||
#ifndef _VAX_MTPR_H_
|
||||
#define _VAX_MTPR_H_
|
||||
|
||||
/******************************************************************************
|
||||
|
||||
Processor register numbers in the VAX /IC
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
|
||||
#define PR_KSP 0 /* Kernel Stack Pointer */
|
||||
#define PR_ESP 1 /* Executive Stack Pointer */
|
||||
#define PR_SSP 2 /* Supervisor Stack Pointer */
|
||||
#define PR_USP 3 /* User Stack Pointer */
|
||||
#define PR_ISP 4 /* Interrupt Stack Pointer */
|
||||
|
||||
#define PR_P0BR 8 /* P0 Base Register */
|
||||
#define PR_P0LR 9 /* P0 Length Register */
|
||||
#define PR_P1BR 10 /* P1 Base Register */
|
||||
#define PR_P1LR 11 /* P1 Length Register */
|
||||
#define PR_SBR 12 /* System Base Register */
|
||||
#define PR_SLR 13 /* System Limit Register */
|
||||
#define PR_PCBB 16 /* Process Control Block Base */
|
||||
#define PR_SCBB 17 /* System Control Block Base */
|
||||
#define PR_IPL 18 /* Interrupt Priority Level */
|
||||
#define PR_ASTLVL 19 /* AST Level */
|
||||
#define PR_SIRR 20 /* Software Interrupt Request */
|
||||
#define PR_SISR 21 /* Software Interrupt Summary */
|
||||
#define PR_IPIR 22 /* KA820 Interprocessor register */
|
||||
#define PR_MCSR 23 /* Machine Check Status Register 11/750 */
|
||||
#define PR_ICCS 24 /* Interval Clock Control */
|
||||
#define PR_NICR 25 /* Next Interval Count */
|
||||
#define PR_ICR 26 /* Interval Count */
|
||||
#define PR_TODR 27 /* Time Of Year (optional) */
|
||||
#define PR_CSRS 28 /* Console Storage R/S */
|
||||
#define PR_CSRD 29 /* Console Storage R/D */
|
||||
#define PR_CSTS 30 /* Console Storage T/S */
|
||||
#define PR_CSTD 31 /* Console Storage T/D */
|
||||
#define PR_RXCS 32 /* Console Receiver C/S */
|
||||
#define PR_RXDB 33 /* Console Receiver D/B */
|
||||
#define PR_TXCS 34 /* Console Transmit C/S */
|
||||
#define PR_TXDB 35 /* Console Transmit D/B */
|
||||
#define PR_TBDR 36 /* Translation Buffer Group Disable Register 11/750 */
|
||||
#define PR_CADR 37 /* Cache Disable Register 11/750 */
|
||||
#define PR_MCESR 38 /* Machiune Check Error Summary Register 11/750 */
|
||||
#define PR_CAER 39 /* Cache Error Register 11/750 */
|
||||
#define PR_ACCS 40 /* Accelerator control register */
|
||||
#define PR_SAVISP 41 /* Console Saved ISP */
|
||||
#define PR_SAVPC 42 /* Console Saved PC */
|
||||
#define PR_SAVPSL 43 /* Console Saved PSL */
|
||||
#define PR_WCSA 44 /* WCS Address */
|
||||
#define PR_WCSB 45 /* WCS Data */
|
||||
#define PR_SBIFS 48 /* SBI Fault/Status */
|
||||
#define PR_SBIS 49 /* SBI Silo */
|
||||
#define PR_SBISC 50 /* SBI Silo Comparator */
|
||||
#define PR_SBIMT 51 /* SBI Silo Maintenance */
|
||||
#define PR_SBIER 52 /* SBI Error Register */
|
||||
#define PR_SBITA 53 /* SBI Timeout Address Register */
|
||||
#define PR_SBIQC 54 /* SBI Quadword Clear */
|
||||
#define PR_IUR 55 /* Initialize Unibus Register 11/750 */
|
||||
#define PR_MAPEN 56 /* Memory Management Enable */
|
||||
#define PR_TBIA 57 /* Trans. Buf. Invalidate All */
|
||||
#define PR_TBIS 58 /* Trans. Buf. Invalidate Single */
|
||||
#define PR_TBDATA 59 /* Translation Buffer Data */
|
||||
#define PR_MBRK 60 /* Microprogram Break */
|
||||
#define PR_PMR 61 /* Performance Monnitor Enable */
|
||||
#define PR_SID 62 /* System ID Register */
|
||||
#define PR_TBCHK 63 /* Translation Buffer Check */
|
||||
|
||||
#define PR_PAMACC 64 /* Physical Address Memory Map Access (KA86) */
|
||||
#define PR_PAMLOC 65 /* Physical Address Memory Map Location (KA86) */
|
||||
#define PR_CSWP 66 /* Cache Sweep (KA86) */
|
||||
#define PR_MDECC 67 /* MBOX Data Ecc Register (KA86) */
|
||||
#define PR_MENA 68 /* MBOX Error Enable Register (KA86) */
|
||||
#define PR_MDCTL 69 /* MBOX Data Control Register (KA86) */
|
||||
#define PR_MCCTL 70 /* MBOX Mcc Control Register (KA86) */
|
||||
#define PR_MERG 71 /* MBOX Error Generator Register (KA86) */
|
||||
#define PR_CRBT 72 /* Console Reboot (KA86) */
|
||||
#define PR_DFI 73 /* Diagnostic Fault Insertion Register (KA86) */
|
||||
#define PR_EHSR 74 /* Error Handling Status Register (KA86) */
|
||||
#define PR_STXCS 76 /* Console Storage C/S (KA86) */
|
||||
#define PR_STXDB 77 /* Console Storage D/B (KA86) */
|
||||
#define PR_ESPA 78 /* EBOX Scratchpad Address (KA86) */
|
||||
#define PR_ESPD 79 /* EBOX Scratchpad Data (KA86) */
|
||||
|
||||
#define PR_RXCS1 80 /* Serial-Line Unit 1 Receive CSR (KA820) */
|
||||
#define PR_RXDB1 81 /* Serial-Line Unit 1 Receive Data Buffer (KA820) */
|
||||
#define PR_TXCS1 82 /* Serial-Line Unit 1 Transmit CSR (KA820) */
|
||||
#define PR_TXDB1 83 /* Serial-Line Unit 1 Transmit Data Buffer (KA820) */
|
||||
#define PR_RXCS2 84 /* Serial-Line Unit 2 Receive CSR (KA820) */
|
||||
#define PR_RXDB2 85 /* Serial-Line Unit 2 Receive Data Buffer (KA820) */
|
||||
#define PR_TXCS2 86 /* Serial-Line Unit 2 Transmit CSR (KA820) */
|
||||
#define PR_TXDB2 87 /* Serial-Line Unit 2 Transmit Data Buffer (KA820) */
|
||||
#define PR_RXCS3 88 /* Serial-Line Unit 3 Receive CSR (KA820) */
|
||||
#define PR_RXDB3 89 /* Serial-Line Unit 3 Receive Data Buffer (KA820) */
|
||||
#define PR_TXCS3 90 /* Serial-Line Unit 3 Transmit CSR (KA820) */
|
||||
#define PR_TXDB3 91 /* Serial-Line Unit 3 Transmit Data Buffer (KA820) */
|
||||
#define PR_RXCD 92 /* Receive Console Data from another CPU (KA820) */
|
||||
#define PR_CACHEX 93 /* Cache invalidate Register (KA820) */
|
||||
#define PR_BINID 94 /* VAXBI node ID Register (KA820) */
|
||||
#define PR_BISTOP 95 /* VAXBI Stop Register (KA820) */
|
||||
|
||||
#define PR_BCBTS 113 /* Backup Cache Tag Store (KA670) */
|
||||
#define PR_BCP1TS 114 /* Primary Tag Store 1st half (KA670) */
|
||||
#define PR_BCP2TS 115 /* Primary Tag Store 2st half (KA670) */
|
||||
#define PR_BCRFR 116 /* Refresh Register (KA670) */
|
||||
#define PR_BCIDX 117 /* Index Register (KA670) */
|
||||
#define PR_BCSTS 118 /* Status (KA670) */
|
||||
#define PR_BCCTL 119 /* Control Register (KA670) */
|
||||
#define PR_BCERR 120 /* Error Address (KA670) */
|
||||
#define PR_BCFBTS 121 /* Flush backup tag store (KA670) */
|
||||
#define PR_BCFPTS 122 /* Flush primary tag store (KA670) */
|
||||
|
||||
#define PR_VINTSR 123 /* vector i/f error status (KA43/KA46) */
|
||||
#define PR_PCTAG 124 /* primary cache tag store (KA43/KA46) */
|
||||
#define PR_PCIDX 125 /* primary cache index (KA43/KA46) */
|
||||
#define PR_PCERR 126 /* primary cache error address (KA43/KA46) */
|
||||
#define PR_PCSTS 127 /* primary cache status (KA43/KA46) */
|
||||
|
||||
#define PR_VPSR 144 /* Vector processor status register */
|
||||
#define PR_VAER 145 /* Vector arithmetic error register */
|
||||
#define PR_VMAC 146 /* Vector memory activity register */
|
||||
#define PR_VTBIA 147 /* Vector TBIA */
|
||||
#define PR_VSAR 148 /* Vector state address register */
|
||||
#define PR_VIADR 157 /* Vector indirect address register */
|
||||
#define PR_VIDLO 158 /* Vector indirect data low */
|
||||
#define PR_VIDHI 159 /* Vector indirect data high */
|
||||
|
||||
/* Definitions for AST */
|
||||
#define AST_NO 4
|
||||
#define AST_OK 3
|
||||
|
||||
#ifndef _LOCORE
|
||||
|
||||
typedef unsigned int register_t;
|
||||
|
||||
static inline void
|
||||
mtpr(register_t val, int reg)
|
||||
{
|
||||
__asm volatile (
|
||||
"mtpr %0,%1"
|
||||
: /* No output */
|
||||
: "g" (val), "g" (reg)
|
||||
: "memory");
|
||||
}
|
||||
|
||||
static inline register_t
|
||||
mfpr(int reg)
|
||||
{
|
||||
register_t __val;
|
||||
__asm volatile (
|
||||
"mfpr %1,%0"
|
||||
: "=g" (__val)
|
||||
: "g" (reg));
|
||||
return __val;
|
||||
}
|
||||
#endif /* _LOCORE */
|
||||
|
||||
#endif /* _VAX_MTPR_H_ */
|
||||
118
arch/vax/linker.ld
Normal file
118
arch/vax/linker.ld
Normal file
@@ -0,0 +1,118 @@
|
||||
OUTPUT_FORMAT("elf32-vax", "elf32-vax", "elf32-vax")
|
||||
OUTPUT_ARCH(vax)
|
||||
|
||||
ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
. = %KERNEL_BASE% + %KERNEL_LOAD_OFFSET%;
|
||||
|
||||
_start = .;
|
||||
|
||||
/* text/read-only data */
|
||||
/* set the load address to physical MEMBASE */
|
||||
.text : AT(%MEMBASE% + %KERNEL_LOAD_OFFSET%) {
|
||||
KEEP(*(.text.boot.vectab1))
|
||||
KEEP(*(.text.boot.vectab2))
|
||||
KEEP(*(.text.boot))
|
||||
*(.text* .sram.text.glue_7* .gnu.linkonce.t.*)
|
||||
}
|
||||
|
||||
.interp : { *(.interp) }
|
||||
.hash : { *(.hash) }
|
||||
.dynsym : { *(.dynsym) }
|
||||
.dynstr : { *(.dynstr) }
|
||||
.rel.text : { *(.rel.text) *(.rel.gnu.linkonce.t*) }
|
||||
.rela.text : { *(.rela.text) *(.rela.gnu.linkonce.t*) }
|
||||
.rel.data : { *(.rel.data) *(.rel.gnu.linkonce.d*) }
|
||||
.rela.data : { *(.rela.data) *(.rela.gnu.linkonce.d*) }
|
||||
.rel.rodata : { *(.rel.rodata) *(.rel.gnu.linkonce.r*) }
|
||||
.rela.rodata : { *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
|
||||
.rel.got : { *(.rel.got) }
|
||||
.rela.got : { *(.rela.got) }
|
||||
.rel.ctors : { *(.rel.ctors) }
|
||||
.rela.ctors : { *(.rela.ctors) }
|
||||
.rel.dtors : { *(.rel.dtors) }
|
||||
.rela.dtors : { *(.rela.dtors) }
|
||||
.rel.init : { *(.rel.init) }
|
||||
.rela.init : { *(.rela.init) }
|
||||
.rel.fini : { *(.rel.fini) }
|
||||
.rela.fini : { *(.rela.fini) }
|
||||
.rel.bss : { *(.rel.bss) }
|
||||
.rela.bss : { *(.rela.bss) }
|
||||
.rel.plt : { *(.rel.plt) }
|
||||
.rela.plt : { *(.rela.plt) }
|
||||
.init : { *(.init) } =0x9090
|
||||
.plt : { *(.plt) }
|
||||
|
||||
/* .ARM.exidx is sorted, so has to go in its own output section. */
|
||||
__exidx_start = .;
|
||||
.ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) }
|
||||
__exidx_end = .;
|
||||
|
||||
.rodata : ALIGN(4) {
|
||||
__rodata_start = .;
|
||||
__fault_handler_table_start = .;
|
||||
KEEP(*(.rodata.fault_handler_table))
|
||||
__fault_handler_table_end = .;
|
||||
*(.rodata .rodata.* .gnu.linkonce.r.*)
|
||||
}
|
||||
|
||||
/*
|
||||
* extra linker scripts tend to insert sections just after .rodata,
|
||||
* so we want to make sure this symbol comes after anything inserted above,
|
||||
* but not aligned to the next section necessarily.
|
||||
*/
|
||||
.dummy_post_rodata : {
|
||||
__rodata_end = .;
|
||||
}
|
||||
|
||||
.data : ALIGN(512) {
|
||||
/* writable data */
|
||||
__data_start_rom = .;
|
||||
/* in one segment binaries, the rom data address is on top of the ram data address */
|
||||
__data_start = .;
|
||||
*(.data .data.* .gnu.linkonce.d.*)
|
||||
}
|
||||
|
||||
.ctors : ALIGN(4) {
|
||||
__ctor_list = .;
|
||||
KEEP(*(.ctors .init_array))
|
||||
__ctor_end = .;
|
||||
}
|
||||
.dtors : ALIGN(4) {
|
||||
__dtor_list = .;
|
||||
KEEP(*(.dtors .fini_array))
|
||||
__dtor_end = .;
|
||||
}
|
||||
.got : { *(.got.plt) *(.got) }
|
||||
.dynamic : { *(.dynamic) }
|
||||
|
||||
/*
|
||||
* extra linker scripts tend to insert sections just after .data,
|
||||
* so we want to make sure this symbol comes after anything inserted above,
|
||||
* but not aligned to the next section necessarily.
|
||||
*/
|
||||
.dummy_post_data : {
|
||||
__data_end = .;
|
||||
}
|
||||
|
||||
/* uninitialized data (in same segment as writable data) */
|
||||
.bss : ALIGN(4) {
|
||||
KEEP(*(.bss.prebss.*))
|
||||
. = ALIGN(4);
|
||||
__bss_start = .;
|
||||
*(.bss .bss.*)
|
||||
*(.gnu.linkonce.b.*)
|
||||
*(COMMON)
|
||||
. = ALIGN(4);
|
||||
__bss_end = .;
|
||||
}
|
||||
|
||||
_end = .;
|
||||
|
||||
. = %KERNEL_BASE% + %MEMSIZE%;
|
||||
_end_of_ram = .;
|
||||
|
||||
/* Strip unnecessary stuff */
|
||||
/DISCARD/ : { *(.comment .note .eh_frame) }
|
||||
}
|
||||
55
arch/vax/rules.mk
Normal file
55
arch/vax/rules.mk
Normal file
@@ -0,0 +1,55 @@
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
MODULE := $(LOCAL_DIR)
|
||||
|
||||
MODULE_SRCS += $(LOCAL_DIR)/start.S
|
||||
MODULE_SRCS += $(LOCAL_DIR)/arch.c
|
||||
MODULE_SRCS += $(LOCAL_DIR)/asm.S
|
||||
MODULE_SRCS += $(LOCAL_DIR)/thread.c
|
||||
|
||||
GLOBAL_DEFINES += \
|
||||
SMP_MAX_CPUS=1
|
||||
|
||||
# set the default toolchain to vax-linux
|
||||
ifndef TOOLCHAIN_PREFIX
|
||||
TOOLCHAIN_PREFIX := vax-linux-
|
||||
endif
|
||||
|
||||
WITH_LINKER_GC ?= 0
|
||||
|
||||
cc-option = $(shell if test -z "`$(1) $(2) -S -o /dev/null -xc /dev/null 2>&1`"; \
|
||||
then echo "$(2)"; else echo "$(3)"; fi ;)
|
||||
|
||||
ARCH_COMPILEFLAGS :=
|
||||
ARCH_OPTFLAGS := -O2
|
||||
ARCH_LDFLAGS := -z max-page-size=512
|
||||
|
||||
LIBGCC := $(shell $(TOOLCHAIN_PREFIX)gcc $(GLOBAL_COMPILEFLAGS) $(ARCH_COMPILEFLAGS) $(GLOBAL_CFLAGS) -print-libgcc-file-name)
|
||||
$(info LIBGCC = $(LIBGCC))
|
||||
|
||||
KERNEL_BASE ?= $(MEMBASE)
|
||||
KERNEL_LOAD_OFFSET ?= 0
|
||||
ROMBASE ?= 0
|
||||
|
||||
GLOBAL_DEFINES += \
|
||||
ROMBASE=$(ROMBASE) \
|
||||
MEMBASE=$(MEMBASE) \
|
||||
MEMSIZE=$(MEMSIZE)
|
||||
|
||||
# potentially generated files that should be cleaned out with clean make rule
|
||||
GENERATED += \
|
||||
$(BUILDDIR)/linker.ld
|
||||
|
||||
# rules for generating the linker
|
||||
$(BUILDDIR)/linker.ld: $(LOCAL_DIR)/linker.ld $(wildcard arch/*.ld) linkerscript.phony
|
||||
@echo generating $@
|
||||
@$(MKDIR)
|
||||
$(NOECHO)sed "s/%ROMBASE%/$(ROMBASE)/;s/%MEMBASE%/$(MEMBASE)/;s/%MEMSIZE%/$(MEMSIZE)/;s/%KERNEL_BASE%/$(KERNEL_BASE)/;s/%KERNEL_LOAD_OFFSET%/$(KERNEL_LOAD_OFFSET)/;s/%VECTOR_BASE_PHYS%/$(VECTOR_BASE_PHYS)/" < $< > $@.tmp
|
||||
@$(call TESTANDREPLACEFILE,$@.tmp,$@)
|
||||
|
||||
linkerscript.phony:
|
||||
.PHONY: linkerscript.phony
|
||||
|
||||
LINKER_SCRIPT += $(BUILDDIR)/linker.ld
|
||||
|
||||
include make/module.mk
|
||||
62
arch/vax/start.S
Normal file
62
arch/vax/start.S
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Travis Geiselbrecht
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
.section .text.boot
|
||||
|
||||
.globl _start
|
||||
_start:
|
||||
nop
|
||||
nop
|
||||
|
||||
// save our bootargs
|
||||
moval bootargs_end,%sp
|
||||
pushr $0x1fff
|
||||
|
||||
// see if we need to be relocated
|
||||
movab _start, %r0 // where we are
|
||||
movl $_start, %r1 // where we want to be
|
||||
cmpl %r0,%r1
|
||||
beql relocated
|
||||
|
||||
// compute the copy length
|
||||
subl3 %r1, $__data_end, %r2
|
||||
|
||||
// copy us down to the final location
|
||||
1:
|
||||
movb (%r0)+,(%r1)+
|
||||
sobgtr %r2,1b
|
||||
|
||||
// zero bss
|
||||
subl3 $_end, $__data_end, %r2
|
||||
1:
|
||||
movb $0,(%r1)+
|
||||
sobgtr %r2,1b
|
||||
|
||||
// branch to our new spot
|
||||
movl $relocated, %r0
|
||||
jmp (%r0)
|
||||
|
||||
relocated:
|
||||
// set the stack to the end of us
|
||||
moval stack_end,%sp
|
||||
|
||||
// branch into main and we're done
|
||||
calls $0, lk_main
|
||||
halt
|
||||
|
||||
.section .data
|
||||
.align 4
|
||||
.globl bootargs
|
||||
bootargs:
|
||||
.skip 13*4
|
||||
bootargs_end:
|
||||
|
||||
.section .bss
|
||||
.align 4
|
||||
stack:
|
||||
.skip 1024
|
||||
stack_end:
|
||||
68
arch/vax/thread.c
Normal file
68
arch/vax/thread.c
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Travis Geiselbrecht
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <lk/debug.h>
|
||||
#include <lk/trace.h>
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <kernel/thread.h>
|
||||
#include <arch/vax.h>
|
||||
|
||||
#define LOCAL_TRACE 0
|
||||
|
||||
struct thread *_current_thread;
|
||||
|
||||
static void initial_thread_func(void) __NO_RETURN;
|
||||
static void initial_thread_func(void) {
|
||||
DEBUG_ASSERT(arch_ints_disabled());
|
||||
|
||||
thread_t *ct = get_current_thread();
|
||||
|
||||
#if LOCAL_TRACE
|
||||
LTRACEF("thread %p calling %p with arg %p\n", ct, ct->entry, ct->arg);
|
||||
dump_thread(ct);
|
||||
#endif
|
||||
|
||||
/* release the thread lock that was implicitly held across the reschedule */
|
||||
spin_unlock(&thread_lock);
|
||||
arch_enable_ints();
|
||||
|
||||
int ret = ct->entry(ct->arg);
|
||||
|
||||
LTRACEF("thread %p exiting with %d\n", ct, ret);
|
||||
|
||||
thread_exit(ret);
|
||||
}
|
||||
|
||||
void arch_thread_initialize(thread_t *t) {
|
||||
LTRACEF("t %p (%s)\n", t, t->name);
|
||||
|
||||
/* zero out the thread context */
|
||||
memset(&t->arch, 0, sizeof(t->arch));
|
||||
|
||||
//t->arch.cs_frame.sp = (vaddr_t)t->stack + t->stack_size;
|
||||
//t->arch.cs_frame.ra = (vaddr_t)&initial_thread_func;
|
||||
}
|
||||
|
||||
void arch_context_switch(thread_t *oldthread, thread_t *newthread) {
|
||||
DEBUG_ASSERT(arch_ints_disabled());
|
||||
|
||||
LTRACEF("old %p (%s), new %p (%s)\n", oldthread, oldthread->name, newthread, newthread->name);
|
||||
|
||||
PANIC_UNIMPLEMENTED;
|
||||
//riscv32_context_switch(&oldthread->arch.cs_frame, &newthread->arch.cs_frame);
|
||||
}
|
||||
|
||||
void arch_dump_thread(thread_t *t) {
|
||||
if (t->state != THREAD_RUNNING) {
|
||||
dprintf(INFO, "\tarch: ");
|
||||
//dprintf(INFO, "sp 0x%x\n", t->arch.cs_frame.sp);
|
||||
}
|
||||
}
|
||||
|
||||
11
lib/libc/string/arch/vax/rules.mk
Normal file
11
lib/libc/string/arch/vax/rules.mk
Normal file
@@ -0,0 +1,11 @@
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
ASM_STRING_OPS := #bcopy bzero memcpy memmove memset
|
||||
|
||||
MODULE_SRCS += \
|
||||
#$(LOCAL_DIR)/memcpy.S \
|
||||
#$(LOCAL_DIR)/memset.S
|
||||
|
||||
# filter out the C implementation
|
||||
C_STRING_OPS := $(filter-out $(ASM_STRING_OPS),$(C_STRING_OPS))
|
||||
|
||||
39
platform/vax/console.c
Normal file
39
platform/vax/console.c
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Travis Geiselbrecht
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
#include <lk/debug.h>
|
||||
#include <platform.h>
|
||||
#include <platform/debug.h>
|
||||
|
||||
// todo: move to .S file
|
||||
unsigned int rom_putchar_addr = 0x20040068;
|
||||
extern int rom_putchar(int c);
|
||||
|
||||
// select the above routine
|
||||
int (*putchar_func)(int c) = &rom_putchar;
|
||||
|
||||
void platform_dputc(char c) {
|
||||
if (c == '\n')
|
||||
putchar_func('\r');
|
||||
putchar_func(c);
|
||||
}
|
||||
|
||||
int platform_dgetc(char *c, bool wait) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// crash time versions, for the moment use the above
|
||||
void platform_pputc(char c) {
|
||||
platform_dputc(c);
|
||||
}
|
||||
|
||||
int platform_pgetc(char *c, bool wait) {
|
||||
return platform_dgetc(c, wait);
|
||||
}
|
||||
|
||||
|
||||
|
||||
20
platform/vax/init.c
Normal file
20
platform/vax/init.c
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Travis Geiselbrecht
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
#include <lk/debug.h>
|
||||
#include <platform.h>
|
||||
#include <platform/timer.h>
|
||||
|
||||
// stubbed out time
|
||||
static lk_time_t t = 0;
|
||||
lk_time_t current_time() {
|
||||
return ++t;
|
||||
}
|
||||
|
||||
lk_bigtime_t current_time_hires() {
|
||||
return (++t) * 1000;
|
||||
}
|
||||
10
platform/vax/rom.S
Normal file
10
platform/vax/rom.S
Normal file
@@ -0,0 +1,10 @@
|
||||
.text
|
||||
|
||||
.globl rom_putchar
|
||||
.type rom_putchar@function
|
||||
rom_putchar:
|
||||
.word 0x0004 # save r2
|
||||
movl 4(%ap), %r2
|
||||
jsb *rom_putchar_addr
|
||||
ret
|
||||
|
||||
53
platform/vax/rules.mk
Normal file
53
platform/vax/rules.mk
Normal file
@@ -0,0 +1,53 @@
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
MODULE := $(LOCAL_DIR)
|
||||
|
||||
ARCH := vax
|
||||
|
||||
#MODULE_DEPS := \
|
||||
lib/bio \
|
||||
lib/cbuf \
|
||||
lib/watchdog \
|
||||
dev/cache/pl310 \
|
||||
dev/interrupt/arm_gic \
|
||||
dev/timer/arm_cortex_a9
|
||||
|
||||
MODULE_SRCS += \
|
||||
$(LOCAL_DIR)/console.c \
|
||||
$(LOCAL_DIR)/init.c \
|
||||
$(LOCAL_DIR)/rom.S \
|
||||
$(LOCAL_DIR)/timer.c \
|
||||
|
||||
# $(LOCAL_DIR)/clocks.c \
|
||||
$(LOCAL_DIR)/debug.c \
|
||||
$(LOCAL_DIR)/fpga.c \
|
||||
$(LOCAL_DIR)/gpio.c \
|
||||
$(LOCAL_DIR)/platform.c \
|
||||
$(LOCAL_DIR)/qspi.c \
|
||||
$(LOCAL_DIR)/spiflash.c \
|
||||
$(LOCAL_DIR)/start.S \
|
||||
$(LOCAL_DIR)/swdt.c \
|
||||
$(LOCAL_DIR)/uart.c \
|
||||
|
||||
MEMBASE := 0x00000000
|
||||
MEMSIZE ?= 0x00800000 # default to 8MB
|
||||
KERNEL_LOAD_OFFSET := 0x00100000 # loaded 1MB into physical space
|
||||
|
||||
# put our kernel at 0x80000000 once we get the mmu running
|
||||
#KERNEL_BASE = 0x80000000
|
||||
KERNEL_BASE = 0
|
||||
|
||||
#LINKER_SCRIPT += \
|
||||
# $(BUILDDIR)/system-onesegment.ld
|
||||
|
||||
# python script to generate the zynq's bootrom bootheader
|
||||
#MKBOOTHEADER := $(LOCAL_DIR)/mkbootheader.py
|
||||
#EXTRA_BUILDDEPS += $(BOOTHEADERBIN)
|
||||
#GENERATED += $(BOOTHEADERBIN)
|
||||
|
||||
#$(BOOTHEADERBIN): $(OUTBIN) $(MKBOOTHEADER)
|
||||
# @$(MKDIR)
|
||||
# $(NOECHO)echo generating $@; \
|
||||
# $(MKBOOTHEADER) $(OUTBIN) $@
|
||||
|
||||
include make/module.mk
|
||||
18
platform/vax/timer.c
Normal file
18
platform/vax/timer.c
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Travis Geiselbrecht
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
#include <lk/debug.h>
|
||||
#include <lk/err.h>
|
||||
#include <platform.h>
|
||||
#include <platform/timer.h>
|
||||
|
||||
// stubbed out timer
|
||||
status_t platform_set_periodic_timer(platform_timer_callback callback, void *arg, lk_time_t interval) {
|
||||
return NO_ERROR;
|
||||
PANIC_UNIMPLEMENTED;
|
||||
}
|
||||
|
||||
6
project/vax-test.mk
Normal file
6
project/vax-test.mk
Normal file
@@ -0,0 +1,6 @@
|
||||
# main project for vim2
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
TARGET := vax
|
||||
|
||||
include project/virtual/test.mk
|
||||
9
target/vax/rules.mk
Normal file
9
target/vax/rules.mk
Normal file
@@ -0,0 +1,9 @@
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
GLOBAL_INCLUDES += \
|
||||
$(LOCAL_DIR)/include
|
||||
|
||||
PLATFORM := vax
|
||||
|
||||
#include make/module.mk
|
||||
|
||||
Reference in New Issue
Block a user