linux/arch/e2k/3p/global_sp.c

1594 lines
46 KiB
C

#include <linux/slab.h>
#include <linux/mman.h>
#include <linux/pagemap.h>
#include <asm/e2k_api.h>
#include <asm/3p.h>
#include <asm/e2k_ptypes.h>
#include <asm/e2k_debug.h>
#include <asm/pgalloc.h>
#include <asm/hw_stacks.h>
#include <asm/processor.h>
#include <asm/console.h>
#include <asm/hw_stacks.h>
#include <asm/cpu_regs_access.h>
#undef DEBUG_TRAP_CELLAR
#undef DbgTC
#define DEBUG_TRAP_CELLAR 0 /* DEBUG_TRAP_CELLAR */
#define DbgTC(...) DebugPrint(DEBUG_TRAP_CELLAR ,##__VA_ARGS__)
/*
* The problems of multithreading
*
* I. PROBLEM
* If a stack pointer is written to a global variable seen by other threads
* there is a problemm accessing it from those threads. There are two
* possibilities:
* 1) 'psl' of the reading thread is greater or equal to 'psl' stored in
* the pointer. In this case the thread will access not the stack from
* which this pointer originated but rather from its own stack (as a
* consequence of having the highest part of stack address stored in the
* 'SBR' per-thread register).
* There is also a possibility that although psl is valid (from the point
* of view of that thread) the actual address is not - then reading will
* generate exc_page_miss or something like that and the process will be
* killed with SIGSEGV.
* 2) 'psl' of the reading thread is less than 'psl' stored in the pointer.
* The thread receives exc_illegal_operand and the process is terminated
* with SIGILL.
*
* II. SOLUTION
* So the solution is to replace SAP's written to global variables with some
* invalid value and let the interrupt handler deal with everything. But
* the written SAP might be pointing to another SAP which points to array
* of SAP's etc - thus it's impossible to follow all stack pointers in all
* threads (since that chain of pointers may change at *any* time).
*
* There are some subtleties to consider:
*
* 1. Since stack addresses are reused, two identical pointers to a stack
* can point to different values (and even to variables with different
* types). So to catch such accesses (which as we assume are invalid)
* the 'age' of a pointer must be keeped track of.
*
* 2. Lifetime of any given stack pointer written to global variable
* is unknown - any thread can read it to a local register which can live
* forever. So the 'age' of pointers grows indefinitly.
*
* 3. It is enough to increase the 'age' on every function return -
* since this is the only case of a pointer invalidation which is not
* handled by hardware.
* This can be optimized further - it is enough to increase the 'age'
* only on the returns for which there is a global pointer pointing to
* the released stack frame.
*
* 4. So every access to another thread's stack must generate an interrupt.
* But half-speculative loads can only generate exc_page_miss, so for example
* just clearing read/write bits in AP is not an option - half-speculative
* loads from such AP's will only write diagnostic (DP).
*
* In current implementation there is an area starting at START_VAL
* (START_VAL == thread_info->multithread_address) which all created AP's
* are pointing to. Every new AP created from SAP (when that SAP is written
* to a global variable or to another thread's stack) is calculated like this:
* AP.size = SAP.size
* AP.index = SAP.index
* AP.base = START_VAL + index;
* index += AP.size
* So the address in AP is acting as 'age'.
*
* 5. Since the address in AP is acting as 'age', when we run out of space
* in that area we cannot do anythyng anymore. The size of the area is defined
* at compile time in MAX_MULTITHREAD_SIZE.
*
* 6. Changing SAP's to AP's is required only when multithreading. So until
* there is a second thread we only keep track of globals holding pointers to
* stack but do not substitute SAP to AP.
*
* 7. When SAP is changed to AP we mark ALL frames in current stack to
* generate exc_last_wish on return. This because informations and pointers
* in our stack can change at any time, and the other threads accessing
* our stack must be informed about it.
*
* Actually this is an overkill - it is enough to mark only those frames
* to which that SAP *might point in a future*. So if the SAP is pointing to
* int (4 bytes) then we know that there will not be any lists in the future
* pointing to other frames and it is enough to mark with 'last wish' only
* the frame to which that SAP is pointing.
*
* 8. A couple of words about 'age'.
* In current implementation when last wish interrupt is generated, we add
* to the globals list a new element with type "TYPE_BOUND" which stores
* thread ID, psl and time.
* thread ID == current->pid
* psl == PUSD.psl
* time == index
* where 'index' is the same as in 4).
*
* This information is enough to check whether access by some SAP converted
* to AP is valid.
*
*
* And it would of great help to have hardware support for storing thread
* number in SAP (even better - store both thread number AND number of
* last_wish interrupts in SAP). Then this would be SO MUCH faster and
* simpler...
*/
/*
* To check validation of stack pointers which was written in global
* for multi_threading
*/
#define IS_THIS_THREAD(x) (x->type == TYPE_GLOBAL && current->pid == x->pid)
#define IS_MULTITHREADING (WAS_MULTITHREADING)
typedef u32 Syllabe_t;
typedef struct {
unsigned mdl : 4;
unsigned lng : 3;
unsigned nop : 3;
unsigned lm : 1;
unsigned x : 1;
unsigned s : 1;
unsigned sh : 1;
unsigned c : 2; // it is mask now
unsigned cd : 2;
unsigned pl : 2;
unsigned ale : 6;
unsigned al : 6;
} HS_syllable_fields_t;
#undef DEBUG_SP
//#define DEBUG_SP
#ifdef DEBUG_SP
#define CHECK_SIZE(x, zz) \
if ((p_psl - l_psl)* sizeof(e2k_mem_crs_t) \
> AS_STRUCT(regs->stacks.pcsp_hi).ind) { \
printk("CHECK_SIZE %s p_psl=%ld l_psl=%ld ind = %ld \n", \
x, p_psl, l_psl, AS_STRUCT(regs->stacks.pcsp_hi).ind); \
return zz; \
}
#else /* !DEBUG_SP */
#define CHECK_SIZE(x, zz)
#endif /* DEBUG_SP */
typedef union HS_syllable_union {
HS_syllable_fields_t fields;
Syllabe_t word;
} HS_syllable_struct_t;
#define AL(w) (((HS_syllable_fields_t*)&w)->al)
#define ALE(w) (((HS_syllable_fields_t*)&w)->ale)
#define PL(w) (((HS_syllable_fields_t*)&w)->pl)
#define Cd(w) (((HS_syllable_fields_t*)&w)->cd)
#define C(w) (((HS_syllable_fields_t*)&w)->c)
#define SH(w) (((HS_syllable_fields_t*)&w)->sh)
#define S(w) (((HS_syllable_fields_t*)&w)->s)
#define lm(w) (((HS_syllable_fields_t*)&w)->lm)
#define NOP_CNT(w) (((HS_syllable_fields_t*)&w)->nop)
#define LNG(w) ((((HS_syllable_fields_t*)&w)->lng)+1)
#define HS_LNG(w) (((HS_syllable_fields_t*)&w)->lng)
#define MDL(w) (((HS_syllable_fields_t*)&w)->mdl)
typedef struct
{
unsigned ctcond : 9;
unsigned xxx : 1;
unsigned ctop : 2;
unsigned aa : 4;
unsigned alc : 2;
unsigned abp : 2;
unsigned xx : 1;
unsigned abn : 2;
unsigned abg : 2;
unsigned x : 1;
unsigned vfdi : 1;
unsigned srp : 1;
unsigned bap : 1;
unsigned eap : 1;
unsigned ipd : 2;
} SS_syllable_fields_t;
typedef union SS_syllable_union {
SS_syllable_fields_t fields;
Syllabe_t word;
} SS_syllable_struct_t;
#define SSIPD(w) (((SS_syllable_fields_t*)&w)->ipd)
#define SSEAP(w) (((SS_syllable_fields_t*)&w)->eap)
#define SSBAP(w) (((SS_syllable_fields_t*)&w)->bap)
#define SSSRP(w) (((SS_syllable_fields_t*)&w)->srp)
#define SSVFDI(w) (((SS_syllable_fields_t*)&w)->vfdi)
#define SSABG(w) (((SS_syllable_fields_t*)&w)->abg)
#define SSABN(w) (((SS_syllable_fields_t*)&w)->abn)
#define SSAA(w) (((SS_syllable_fields_t*)&w)->aa)
#define SSCTOP(w) (((SS_syllable_fields_t*)&w)->ctop)
#define SSCTCOND(w) (((SS_syllable_fields_t*)&w)->ctcond)
typedef struct {
unsigned param : 28;
unsigned opc : 4;
} CS1_syllable_fields_t;
typedef union CS1_syllable_union {
CS1_syllable_fields_t fields;
Syllabe_t word;
} CS1_syllable_struct_t;
int gsp_is_return(struct pt_regs *regs)
{
e2k_rwp_struct_t tir_lo;
u64 ip;
HS_syllable_struct_t hs;
SS_syllable_struct_t ss;
int ctop;
/* Pointer on the instruction that caused the exception
* is located in corresponding TIR register.
*/
tir_lo.E2K_RWP_reg = regs->trap->TIR_lo;
ip = tir_lo.E2K_RWP_base;
DbgTC("IP = %llx\n", ip);
/* We need to read Header Syllabe of instruction interrupted
* to determine general instruction structure
*/
if (get_user(AS_WORD(hs), (Syllabe_t *) ip) == -EFAULT) {
return 0;
}
DbgTC("HS = %x\n", AS_WORD(hs));
/* Check presence of Stub Syllabe */
if (S(hs)) {
DbgTC("SS does exist\n");
} else {
DbgTC("SS doesn't exist\n");
return 0;
}
/* Stub Syllabe encodes different short fragment of command */
if (get_user(AS_WORD(ss),
(Syllabe_t *) (ip + sizeof (HS_syllable_struct_t))) == -EFAULT) {
return 0;
}
DbgTC("SS = %x\n", AS_WORD(ss));
/* CTOP field encodes CTPR register in use */
ctop = SSCTOP(ss);
/* RETURN always uses CTPR3 */
if (ctop != 3) {
DbgTC("SS.CTOP !=3 !!! SS.CTOP = %d\n", ctop);
return 0;
} else {
DbgTC("SS.CTOP == 3\n");
}
/* CTPR3.opc field should match RETURN operation indicator */
if (AS_STRUCT(regs->ctpr3).opc != RETURN_CT_OPC) {
DbgTC("ctpr3.opc != RETURN_CT_OPC\n");
return 0;
} else {
DbgTC("ctpr3.opc == RETURN_CT_OPC\n");
}
return 1;
}
static int __set_last_wish_all(e2k_mem_crs_t *frame,
unsigned long real_frame_addr,
unsigned long corrected_frame_addr, int flags, void *arg)
{
int *skip = (int *) arg;
e2k_cr1_lo_t cr1_lo = frame->cr1_lo;
if (*skip) {
--(*skip);
return 0;
}
if (AS(cr1_lo).pm)
return 0;
AS(cr1_lo).lw = 1;
if (flags & PCF_FLUSH_NEEDED)
NATIVE_FLUSHC;
DbgTC("crs 0x%lx\n", real_frame_addr);
return put_cr1_lo(cr1_lo, real_frame_addr, 0);
}
/*
* set last_wish for all procedures which psl < l_psl
*/
static long set_last_wish_all(struct pt_regs *regs, int l_psl, int p_psl)
{
int skip = p_psl - l_psl;
long ret;
if (p_psl == l_psl)
AS(regs->crs.cr1_lo).lw = 1;
ret = parse_chain_stack(PCS_USER, NULL, __set_last_wish_all, &skip);
return (IS_ERR_VALUE(ret)) ? ret : 0;
}
#undef GET_IP
#define GET_IP (AS(regs->crs.cr0_hi).ip << E2K_ALIGN_INS)
/* change SAP to AP with "rw" == 0 and unique address */
/* for type TYPE_INIT */
#define START_VAL current_thread_info()->multithread_address
#define GET_CURR_ADDRESS(x) (x->lcl_psl + START_VAL)
#define WAS_MODIFIED(x) (x->old_address)
#define INCR_CURR_ADDRESS(entry, x) (entry->lcl_psl = entry->lcl_psl + x + \
(1 + ~(x & 0xf) & 0xf)*(!!(x & 0xf)))
#define IS_CHANGED_ADDRESS(entry, x) \
(x >= START_VAL && x < GET_CURR_ADDRESS(entry))
#define MAX_MULTITHREAD_SIZE (PAGE_SIZE * 100)
#define CAN_INCR_CURR_ADDRESS(entry, x) (entry->lcl_psl + x + (1 + ~(x & \
0xf) & 0xf)*(!!(x & 0xf)) < MAX_MULTITHREAD_SIZE)
/* for type TYPE_BOUND */
#define GET_TIME(x) (x->global_p)
e2k_addr_t get_valid_address(e2k_addr_t address, global_store_t **record);
void down_read_lock_multithread(void);
void up_read_lock_multithread(void);
static void print_all_records(void);
global_store_t* get_init_record(void)
{
global_store_t *entry = current_thread_info()->g_list;
while (entry != NULL)
{
if (entry->type == TYPE_INIT) {
return entry;
}
entry = entry->next;
}
return NULL;
}
/*
* create new record and change SAP to AP
* addr - address in stack ( was readed SAP)
*/
static void create_new_record(pt_regs_t *regs,e2k_addr_t addr,
long multithread_addr)
{
global_store_t *list = current_thread_info()->g_list;
global_store_t *new, *last;
global_store_t *init = get_init_record();
register unsigned long tmp_lo, tmp_hi;
union {e2k_rwsap_lo_struct_t sap_lo; e2k_rwap_lo_struct_t ap_lo;} lo;
union {e2k_rwsap_hi_struct_t sap_hi; e2k_rwap_hi_struct_t ap_hi;} hi;
unsigned long address;
unsigned long base;
e2k_addr_t usbr;
global_store_t *pnt_record;
int tag_lo, tag_hi;
DbgTC("addr=0x%lx multithread_addr=0x%lx\n",
addr, multithread_addr);
if (get_valid_address(multithread_addr, &pnt_record)==0) {
DbgTC("bad multithread_addr=%lx\n",
multithread_addr);
return;
}
new = (global_store_t *) kmalloc(sizeof(global_store_t), GFP_ATOMIC);
if (!new) {
DbgTC("no memory\n");
return;
}
new->lcl_psl = 0;
new->global_p = 0;
new->next = NULL;
/* for multithreading support */
new->type = TYPE_GLOBAL;
new->pid = pnt_record->pid;
new->sbr = pnt_record->sbr;
new->word1 = 0;
new->word2 = 0;
new->old_address = 0;
usbr = regs->stacks.top;
NATIVE_LOAD_TAGGED_QWORD_AND_TAGS(addr, AS_WORD(lo.sap_lo),
AS_WORD(hi.sap_hi), tag_lo, tag_hi);
address = GET_CURR_ADDRESS(init);
if (!CAN_INCR_CURR_ADDRESS(init, AS_STRUCT(hi.sap_hi).size)) {
pr_info(" create_new_record very many SAP IND=0x%lx "
"MAX=0x%lx CURR_SIZE=0x%x\n",
GET_CURR_ADDRESS(init) - START_VAL,
MAX_MULTITHREAD_SIZE, AS_STRUCT(hi.sap_hi).size);
return;
}
INCR_CURR_ADDRESS(init, AS_STRUCT(hi.sap_hi).size);
base = AS_STRUCT(lo.sap_lo).base;
new->word2 = AS_WORD(hi.sap_hi);
/* field base for SAP and AP is different */
AS_STRUCT(lo.ap_lo).base = address;
AS_AP_STRUCT(lo.ap_lo).itag = E2K_AP_ITAG;
tmp_lo = AS_WORD(lo.ap_lo);
tmp_hi = AS_WORD(hi.ap_hi);
NATIVE_STORE_TAGGED_QWORD((e2k_addr_t)addr, tmp_lo, tmp_hi,
E2K_AP_LO_ETAG, E2K_AP_HI_ETAG);
new->new_address = address;
new->old_address = base;
if (list == NULL) {
new->prev = NULL;
current_thread_info()->g_list = list = new;
} else {
last = list;
/* semaphore is required */
while (last->next != NULL)
last = last->next;
new->prev = last;
last->next = new;
}
}
/*
* If the result of loadQ (operand - marked AP) is SAP
* than for different thread must be changed to new marked AP
* (if address of SAP is valid or NULL othervise)
*/
void change_sap(int cnt, pt_regs_t *regs, e2k_addr_t addr,
long multithread_addr)
{
struct trap_pt_regs *trap = regs->trap;
trap_cellar_t *tcellar = trap->tcellar;
e2k_addr_t usbr;
usbr = regs->stacks.top;
addr -= 8; /* pointed to second word */
DbgTC("change_sap :cnt=%d addr=0x%lx usbr=0x%lx GET_IP=%llx tag(addr)=%x tag(addr+8)=%x pid=%d\n",
cnt, addr, usbr, GET_IP,
NATIVE_LOAD_TAGD(addr),
NATIVE_LOAD_TAGD(addr+8),
current->pid);
if (!WAS_MULTITHREADING) {
return;
}
if (DEBUG_TRAP_CELLAR) {
print_all_TC(tcellar, trap->tc_count);
}
DbgTC("change_sap *addr=0x%lx *(addr + 8)=0x%lx tag(addr)=%x tag(addr+8)=%x\n",
*(long *)addr, *(long *)(addr + 8),
NATIVE_LOAD_TAGD(addr), NATIVE_LOAD_TAGD((addr+8)));
down_read_lock_multithread();
if (IS_SAP_LO(addr) && IS_SAP_HI(addr + 8)) {
/* change SAP => AP */
create_new_record(regs, addr, multithread_addr);
}
up_read_lock_multithread();
}
static int change_sap_to_ap(trap_cellar_t *tcellar, struct pt_regs *regs,
global_store_t* record, int no_check)
{
e2k_rwsap_lo_struct_t sap_lo;
e2k_rwsap_hi_struct_t sap_hi;
e2k_rwap_lo_struct_t ap_lo;
unsigned int l_psl;
unsigned long address;
unsigned long base;
unsigned long *pnt;
global_store_t *init = get_init_record();
DbgTC("change_sap_to_ap():no_check=%d record=%px CURR_ADDRESS=0x%lx"
" WAS_MODIFIED(record)=%lx sbr=%lx\n",
no_check, record, GET_CURR_ADDRESS(init),
WAS_MODIFIED(record), (regs == NULL) ? 0 : regs->stacks.top);
/* Verify if the data is SAP */
if (record->global_p && !WAS_MODIFIED(record) &&
(no_check || (IS_SAP_LO(&(tcellar[0].data)) &&
IS_SAP_HI(&(tcellar[1].data)))) ) {
register unsigned long tmp_hi, tmp_lo;
AS_WORD(sap_lo) = tcellar[0].data;
AS_WORD(sap_hi) = tcellar[1].data;
record->word1 = AS_WORD(sap_lo);
record->word2 = AS_WORD(sap_hi);
l_psl = AS_STRUCT(sap_lo).psl;
AS_STRUCT(sap_lo).psl = 0;
sap_lo.E2K_RWSAP_lo_itag = AP_ITAG;
address = GET_CURR_ADDRESS(init);
if (!CAN_INCR_CURR_ADDRESS(init, AS_STRUCT(sap_hi).size)) {
pr_info(" change_sap_to_ap very many SAP IND=0x%lx "
"MAX=0x%lx CURR_SIZE=0x%x\n",
GET_CURR_ADDRESS(init) - START_VAL,
MAX_MULTITHREAD_SIZE, AS_STRUCT(sap_hi).size);
return 0;
}
INCR_CURR_ADDRESS(init, AS_STRUCT(sap_hi).size);
base = AS_STRUCT(sap_lo).base;
/* field base for SAP and AP is different */
AS_WORD(ap_lo) = AS_WORD(sap_lo);
AS_STRUCT(ap_lo).base = address;
record->new_address = address;
/* in tcellar base already increased by regs->stacks.top */
record->old_address = base;
AS_AP_STRUCT(ap_lo).itag = E2K_AP_ITAG;
tmp_lo = AS_WORD(ap_lo);
tmp_hi = AS_WORD(sap_hi);
if (!no_check) {
pnt = &tcellar[0].data;
NATIVE_STORE_TAGGED_WORD((e2k_addr_t)pnt, tmp_lo,
E2K_AP_LO_ETAG,
TAGGED_MEM_STORE_REC_OPC, 2);
pnt = &tcellar[1].data;
NATIVE_STORE_TAGGED_WORD((e2k_addr_t)pnt, tmp_hi,
E2K_AP_HI_ETAG,
TAGGED_MEM_STORE_REC_OPC, 5);
} else {
pnt = (unsigned long*)(record->global_p);
NATIVE_STORE_TAGGED_QWORD((e2k_addr_t)pnt, tmp_lo,
tmp_hi, E2K_AP_LO_ETAG, E2K_AP_HI_ETAG);
}
if (!no_check && IS_AP_LO(&(tcellar[0].data)) &&
IS_AP_HI(&(tcellar[1].data))) {
DbgTC("change_sap_to_ap(): now is AP\n");
}
DbgTC("SAP.base=%lx AP.base=%lx psl=%d tcellar[0].data=%lx "
"tcellar[1].data=%lx CURR_SIZE=%x\n",
base, address, l_psl,
tcellar[0].data, tcellar[0 +1].data,
AS_STRUCT(sap_hi).size);
return 1;
}
return 0;
}
global_store_t *first_record(void)
{
global_store_t *new;
new = (global_store_t *) kmalloc(sizeof(global_store_t), GFP_ATOMIC);
if (!new) {
DbgTC("no memory\n");
return NULL;
}
NUM_THREAD(new) = 0; /* (x->orig_psr_lw) */
new->lcl_psl = 0;
new->global_p = 0;
new->next = NULL;
new->pid = current->pid;
new->old_address = 0;
new->prev = NULL;
new->type = TYPE_INIT;
current_thread_info()->g_list = new;
return new;
}
static void init_g_list(int from)
{
global_store_t *entry = current_thread_info()->g_list;
DbgTC("pid=%d, list %px START_VAL=%lx from=%d\n",
current->pid, entry, START_VAL, from);
if (!START_VAL) {
START_VAL = (e2k_addr_t) vm_mmap_notkillable(NULL, 0L,
MAX_MULTITHREAD_SIZE,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, 0L);
}
if (entry == NULL) {
/* added new record to have common date for all threads */
entry = first_record();
}
if (entry != NULL) {
NUM_THREAD(entry) += from;
}
if (!current_thread_info()->lock) {
current_thread_info()->lock = (struct rw_semaphore *)
kmalloc(sizeof(struct rw_semaphore), GFP_ATOMIC);
init_rwsem(current_thread_info()->lock);
}
if (!current_thread_info()->lock) {
DbgTC("no memory\n");
}
}
/*
* search all globals pointed to SAP and change it to AP
* only for this thread
*/
void mark_all_global_sp(struct pt_regs * regs, pid_t pid)
{
struct trap_pt_regs *trap = regs->trap;
global_store_t *entry = current_thread_info()->g_list;
trap_cellar_t tcellar[2];
int curr_count;
DbgTC("pid=%d, list %px START_VAL=%lx\n",
pid, entry, START_VAL);
init_g_list(1);
entry = current_thread_info()->g_list;
if (trap) {
curr_count = trap->curr_cnt;
trap->curr_cnt = 0;
}
while (entry != NULL)
{
if (entry->pid == pid && entry->type == TYPE_GLOBAL) {
tcellar[0].data = entry->word1;
tcellar[1].data = entry->word2;
change_sap_to_ap(tcellar, regs, entry, 1);
}
entry = entry->next;
}
if (trap)
trap->curr_cnt = curr_count;
DbgTC("exitting.\n");
}
static int is_valid_addr(global_store_t *entry, e2k_addr_t address,
e2k_addr_t* offset)
{
e2k_rwsap_hi_struct_t sap_hi;
long size ;
DbgTC("address=%lx entry->new_address=%lx entry=%px\n",
address, entry->new_address, entry);
AS_WORD(sap_hi) = entry->word2;
size = AS_STRUCT(sap_hi).size;
*offset = 0;
if (entry->new_address <= address &&
entry->new_address + size > address) {
*offset = address - entry->new_address;
DbgTC("size=%lx\n", size);
return 1;
}
return 0;
}
/*
* check that is address now in stack
*/
e2k_addr_t is_correct_entry(global_store_t *record)
{
global_store_t *entry = current_thread_info()->g_list;
DbgTC("is_correct_entry new_address=%lx record->pid=%d "
" record->lcl_psl=%d\n",
record->new_address, record->pid, record->lcl_psl);
while (entry != NULL)
{
if (entry->type == TYPE_BOUND && entry->pid == record->pid &&
record->lcl_psl == entry->lcl_psl) {
DbgTC("GET_TIME(entry)=0x%lx \n",
GET_TIME(entry));
if (GET_TIME(entry) > record->new_address) {
return 0;
} else {
/* very old return */
return 1;
}
}
entry = entry->next;
}
return 1;
}
e2k_addr_t get_valid_address(e2k_addr_t address, global_store_t **record)
{
global_store_t *entry = current_thread_info()->g_list;
e2k_addr_t offset;
DbgTC("address=%lx\n", address);
while (entry != NULL)
{
if (entry->type == TYPE_GLOBAL &&
is_valid_addr(entry, address, &offset)) {
if (!is_correct_entry(entry)) {
return 0;
}
*record = entry;
DbgTC("address=%lx old_address=%lx"
" pid =%d \n",
address, entry->old_address + offset, entry->pid);
return entry->old_address + offset;
}
entry = entry->next;
}
return 0;
}
static void set_new_bound(int psl, struct pt_regs *regs)
{
global_store_t *entry = current_thread_info()->g_list;
global_store_t *new, *last, *list;
global_store_t *init = get_init_record();
if (!WAS_MULTITHREADING) {
return;
}
DbgTC("set_new_bound: psl=%d sbr=0x%lx GET_CURR_ADDRESS=%lx\n",
psl, regs->stacks.top, GET_CURR_ADDRESS(init));
while (entry != NULL) {
if (entry->type == TYPE_BOUND && entry->pid == current->pid &&
entry->lcl_psl == psl) {
/* update this record */
GET_TIME(entry) = GET_CURR_ADDRESS(init); /* as time */
entry->sbr = regs->stacks.top;
DbgTC("set_new_bound(): update entry=%px\n", entry);
return;
}
entry = entry->next;
}
new = (global_store_t *) kmalloc(sizeof(global_store_t), GFP_ATOMIC);
if (!new) {
DbgTC("no memory\n");
return;
}
DbgTC("new entry=%px\n",new);
new->pid = current->pid;
new->lcl_psl = psl;
new->next = NULL;
new->type = TYPE_BOUND;
new->sbr = regs->stacks.top;
GET_TIME(new) = GET_CURR_ADDRESS(init); /* as time */
INCR_CURR_ADDRESS(init, 16);
list = current_thread_info()->g_list;
if (list == NULL) {
new->prev = NULL;
current_thread_info()->g_list = list = new;
} else {
last = list;
while (last->next != NULL)
last = last->next;
new->prev = last;
last->next = new;
}
}
/*
* This code must done :
* check that address was changed (by change_sap_to_ap())
* old address is valid
* access to old address can call page_fault
* change tcellar (old address => address)
* change vma && address
* result 0 - this address invalid
* 1 - changed address
* 2 - value of address may be changed
* 3 - unknown addr
*/
int interpreted_ap_code(struct pt_regs *regs, struct vm_area_struct **vma,
e2k_addr_t *address)
{
struct trap_pt_regs *trap = regs->trap;
trap_cellar_t *tcellar = trap->tcellar;
int curr_count = trap->curr_cnt;
int tc_count = trap->tc_count /3;
unsigned long addr = *address;
e2k_addr_t old_address;
e2k_rwap_lo_struct_t ap_lo;
register unsigned long tmp;
struct vm_area_struct *new_vma;
tc_opcode_t opcode;
int i, count =-1;
global_store_t *init = get_init_record();
global_store_t *pnt_record = NULL;
DbgTC("interpreted_ap_op addr=%lx curr_count=%d tc_count=%d\n",
addr, curr_count, tc_count);
/* find our count */
for(i = curr_count; i < tc_count; i++ ) {
AW(opcode) = AS(tcellar[i].condition).opcode;
if (tcellar[i].address == addr) {
count = i;
break;
}
}
if (count == -1) {
DbgTC("interpreted_ap_op not find count\n");
return 3;
}
AW(opcode) = AS(tcellar[count].condition).opcode;
if (!IS_CHANGED_ADDRESS(init, addr)) {
DbgTC("interpreted_ap_op not changed address\n");
return 3;
}
DbgTC("interpreted_ap_op count=%d\n", count);
if (DEBUG_TRAP_CELLAR)
print_all_TC(trap->tcellar, trap->tc_count);
/* Verify if the data is AP */
if (0 && AS(opcode).fmt == 5 &&
(!IS_AP_LO(&(tcellar[count].data)) &&
!IS_AP_HI(&(tcellar[count].data))) ) {
DbgTC("interpreted_ap_op not AP IS_AP_LO =%d IS_AP_HI=%d\n",
IS_AP_LO(&(tcellar[count].data)),
IS_AP_HI(&(tcellar[count].data)));
return 2;
}
old_address = get_valid_address(*address, &pnt_record);
DbgTC("interpreted_ap_op old_address=%lx pid=%d curr->pid=%d fmt=%d\n",
old_address,
(old_address)?pnt_record->pid:-1,
current->pid, AS(opcode).fmt);
if (old_address == 0) {
return 0;
}
tcellar[count].address = old_address;
// print_user_address_ptes(current->mm, old_address);
DbgTC("interpreted_ap_op old_address=%lx *old_address=%lx\n",
old_address, *(long *)old_address);
*address = old_address;
if (IS_AP_LO(&(tcellar[count].data))) {
AS_WORD(ap_lo) = tcellar[count].data;
AS_STRUCT(ap_lo).base = old_address;
tmp = AS_WORD(ap_lo);
NATIVE_STORE_VALUE_WITH_TAG(&tcellar[count].data, tmp,
E2K_AP_LO_ETAG);
}
new_vma = find_vma(current->mm, old_address);
DbgTC("interpreted_ap_op tcellar[%d].data=%lx new_vma=%px\n",
count, tcellar[count].data, new_vma);
if (!new_vma) {
return 0;
}
/* To garantee that old_address has pte */
/* TODO*/
if (AS(opcode).fmt == 5) {
if (current->pid != pnt_record->pid) {
return 2;
} else {
return 1;
}
}
return 1;
}
static void delete_list(global_store_t *entry)
{
global_store_t *prev, *next;
prev = entry->prev;
next = entry->next;
if (prev != NULL && next != NULL) {
prev->next = next;
next->prev = prev;
} else {
if (prev != NULL && next == NULL) {
prev->next = NULL;
} else {
if (prev == NULL && next != NULL) {
next->prev = NULL;
current_thread_info()->g_list = next;
} else {
current_thread_info()->g_list = NULL;
}
}
}
}
void free_global_multithread(void)
{
global_store_t *entry = current_thread_info()->g_list;
global_store_t *advance_entry;
DbgTC("free_global_multithread(): pid=%d, NUM_THREAD=%d\n",
current->pid, NUM_THREAD(entry));
NUM_THREAD(entry)--;
if (NUM_THREAD(entry) == 0) {
while (entry != NULL) {
advance_entry = entry->next;
delete_list(entry);
kfree((void *) entry);
entry = advance_entry;
}
}
if (current_thread_info()->lock) {
kfree((void *)current_thread_info()->lock);
}
}
void free_global_sp(void)
{
global_store_t *entry;
global_store_t *advance_entry;
struct mm_struct *mm;
mm = current->mm;
if (!mm) {
return;
}
down_write(&mm->mmap_sem);
entry = current_thread_info()->g_list;
DbgTC("pid=%d, list %px\n", current->pid, entry);
while (entry != NULL)
{
advance_entry = entry->next; /* to avoid re-usage of */
/* an entry after kfree() */
/* entry->global_p == 0 is special record */
if (entry->global_p && IS_THIS_THREAD(entry)) {
delete_list(entry);
kfree((void *) entry);
DbgTC("Entry %px freed\n", entry);
}
entry = advance_entry;
}
if (current_thread_info()->g_list) {
free_global_multithread();
}
up_write(&mm->mmap_sem);
DbgTC("exitting.\n");
}
struct change_lw_args {
bool set;
int skip;
int orig_psr_lw;
};
static int __change_last_wish_one(e2k_mem_crs_t *frame,
unsigned long real_frame_addr,
unsigned long corrected_frame_addr, int flags, void *arg)
{
struct change_lw_args *args = (struct change_lw_args *) arg;
int *skip = (int *) &args->skip;
bool set = args->set;
e2k_cr1_lo_t cr1_lo = frame->cr1_lo;
if (*skip) {
--(*skip);
return 0;
}
DbgTC("crs = 0x%lx\n", corrected_frame_addr);
if (AS(cr1_lo).pm)
return -EINVAL;
if (set) {
args->orig_psr_lw = AS(cr1_lo).lw;
if (AS(cr1_lo).lw) {
DbgTC("lw was already set in memory\n");
return 1;
}
AS(cr1_lo).lw = 1;
if (flags & PCF_FLUSH_NEEDED)
NATIVE_FLUSHC;
if (put_cr1_lo(cr1_lo, real_frame_addr, 0))
return -EFAULT;
DbgTC("lw was cleared in memory, is SET now\n");
} else {
if (!AS(cr1_lo).lw) {
DbgTC("lw was already cleared in memory\n");
return 1;
}
DbgTC("lw was set in memory\n");
if (!args->orig_psr_lw) {
AS(cr1_lo).lw = 0;
if (flags & PCF_FLUSH_NEEDED)
NATIVE_FLUSHC;
if (put_cr1_lo(cr1_lo, real_frame_addr, 0))
return -EFAULT;
DbgTC("lw is CLEARED\n");
} else {
DbgTC("lw remain SET\n");
}
}
return 1;
}
static void new_record(global_store_t *list, struct pt_regs *regs,
e2k_addr_t global, unsigned int l_psl, unsigned int p_psl)
{
struct trap_pt_regs *trap = regs->trap;
global_store_t *new, *last;
trap_cellar_t *tcellar = trap->tcellar;
long tc_count = trap->curr_cnt;
new = (global_store_t *) kmalloc(sizeof(global_store_t), GFP_ATOMIC);
if (!new) {
DbgTC("no memory\n");
return;
}
new->lcl_psl = l_psl;
new->global_p = global;
new->next = NULL;
/* for multithreading support */
new->type = TYPE_GLOBAL;
new->pid = current->pid;
new->word1 = tcellar[tc_count].data;
new->word2 = tcellar[tc_count + 1].data;
new->old_address = 0;
if (IS_MULTITHREADING) {
DbgTC("global=%lx new=%lx pid=%d\n",
global, new, new->pid);
change_sap_to_ap(tcellar, regs, new, 0);
}
if (list == NULL) {
new->prev = NULL;
current_thread_info()->g_list = list = new;
} else {
last = list;
/* semaphore is required */
while (last->next != NULL)
last = last->next;
new->prev = last;
last->next = new;
}
if (l_psl == p_psl) {
e2k_psr_t psr;
AS_WORD(psr) = AS_STRUCT(regs->crs.cr1_lo).psr;
if (AS_STRUCT(psr).lw) {
DbgTC("lw was already set in CR1"
" l_psl=%d pid=%d\n", l_psl, current->pid);
} else {
DbgTC("lw was cleared in CR1 "
"l_psl=%d pid=%d\n", l_psl, current->pid);
AS_STRUCT(psr).lw = 1;
AS_STRUCT(regs->crs.cr1_lo).psr = AS_WORD(psr);
DbgTC("lw is SET\n");
}
new->orig_psr_lw = AS_STRUCT(psr).lw;
} else {
struct change_lw_args args;
long ret;
DbgTC("l_psl != p_psl\n");
CHECK_SIZE("new_record", new)
args.set = true;
args.skip = p_psl - l_psl;
ret = parse_chain_stack(PCS_USER, NULL, __change_last_wish_one, &args);
if (!IS_ERR_VALUE(ret))
new->orig_psr_lw = args.orig_psr_lw;
}
if (DEBUG_TRAP_CELLAR) {
print_all_records();
}
}
static void update_record(global_store_t *record,
struct pt_regs *regs,
unsigned int l_psl,
unsigned int p_psl)
{
struct trap_pt_regs *trap = regs->trap;
unsigned int old_psl;
e2k_psr_t psr;
trap_cellar_t *tcellar = trap->tcellar;
long tc_count = trap->curr_cnt;
if (record == NULL) {
DbgTC("record pointer is NULL\n");
return;
}
DbgTC("record pointer is %px\n", record);
/* for multithreading support */
record->pid = current->pid;
record->word1 = tcellar[tc_count].data;
record->word2 = tcellar[tc_count + 1].data;
record->old_address = 0;
old_psl = record->lcl_psl;
if (old_psl == l_psl) {
DbgTC("new local has the same psl.\n");
return; /* do nothing */
}
/* Clear old last wish */
if (old_psl == p_psl) {
AS_WORD(psr) = AS_STRUCT(regs->crs.cr1_lo).psr;
if (AS_STRUCT(psr).lw) {
DbgTC("lw was set in CR1\n");
if (!record->orig_psr_lw) {
AS_STRUCT(psr).lw = 0;
AS_STRUCT(regs->crs.cr1_lo).psr = AS_WORD(psr);
DbgTC("lw is CLEARED\n");
} else {
DbgTC("lw remain SET\n");
}
} else {
DbgTC("lw was already cleared in CR1\n");
}
} else {
struct change_lw_args args;
long ret;
DbgTC("l_psl != p_psl\n");
CHECK_SIZE("update_record",record);
args.set = false;
args.skip = p_psl - l_psl;
args.orig_psr_lw = record->orig_psr_lw;
ret = parse_chain_stack(PCS_USER, NULL, __change_last_wish_one, &args);
if (IS_ERR_VALUE(ret))
return;
}
/* Set new last wish */
if (l_psl == p_psl) {
AS_WORD(psr) = AS_STRUCT(regs->crs.cr1_lo).psr;
if (AS_STRUCT(psr).lw) {
DbgTC("lw was already set in CR1\n");
} else {
DbgTC("lw was cleared in CR1\n");
AS_STRUCT(psr).lw = 1;
AS_STRUCT(regs->crs.cr1_lo).psr = AS_WORD(psr);
DbgTC("lw is SET\n");
}
record->orig_psr_lw = AS_STRUCT(psr).lw;
} else {
struct change_lw_args args;
long ret;
DbgTC("l_psl != p_psl\n");
CHECK_SIZE("update_record1",record);
args.set = true;
args.skip = p_psl - l_psl;
ret = parse_chain_stack(PCS_USER, NULL, __change_last_wish_one, &args);
if (!IS_ERR_VALUE(ret))
record->orig_psr_lw = args.orig_psr_lw;
}
/* semaphore is required */
record->lcl_psl = l_psl;
}
void down_read_lock_multithread(void)
{
struct rw_semaphore *lock = current_thread_info()->lock;
if (WAS_MULTITHREADING && lock) {
down_read(lock);
}
}
void up_read_lock_multithread(void)
{
struct rw_semaphore *lock = current_thread_info()->lock;
if (WAS_MULTITHREADING && lock) {
up_read(lock);
}
}
/*
* do_global_sp()
*/
int do_global_sp(struct pt_regs *regs, trap_cellar_t *tcellar)
{
e2k_addr_t global; /* address of the GLOBAL */
unsigned int l_psl; /* PSL of the LOCAL */
unsigned int p_psl; /* PSL of the interrupted */
/* user procedure */
e2k_rwsap_lo_struct_t sap_lo;
e2k_pusd_lo_t pusd_lo;
int res;
global_store_t *list, *record;
/* Verify if the data is SAP */
if ( IS_SAP_LO(&(tcellar[0].data)) &&
IS_SAP_HI(&(tcellar[1].data)) ) {
global = tcellar[0].address;
AS_WORD(sap_lo) = tcellar[0].data;
l_psl = AS_STRUCT(sap_lo).psl;
DbgTC("SAP global addr = %lx, local psl = %d\n",
global, l_psl);
DbgTC("SAP global tcellar[0].data = %lx, tcellar[1].data = %lx\n",
tcellar[0].data, tcellar[1].data);
DbgTC("SAP global *global = %lx, *global+8 = %lx\n",
*(long*)global, *(long*)(global+8));
DbgTC("SAP IS_SAP_LO=%d IS_SAP_HI=%d\n",
IS_SAP_LO(global), IS_SAP_HI(global+8));
} else {
DbgTC("The data isn't SAP: data0 = %lx, data1 = %lx\n",
tcellar[0].data, tcellar[1].data);
return 1;
}
if (! user_mode(regs)) {
DbgTC("exception happened in kernel mode. "
"Exiting.\n");
return 1;
}
AS_WORD(pusd_lo) = AS_WORD(regs->stacks.usd_lo);
if (AS_STRUCT(pusd_lo).p) {
DbgTC("protected mode detected gd_lo=0x%llx gd_hi=0x%llx PID=%d\n",
AS_WORD(read_GD_lo_reg()), AS_WORD(read_GD_hi_reg()),
current->pid);
p_psl = AS_STRUCT(pusd_lo).psl;
/* NOTICE: correction for OS entrance */
p_psl--;
DbgTC("interrupted procedure psl = %d\n",
p_psl);
} else {
DbgTC("NON-protected mode detected. "
"Exiting.\n");
return 1;
}
res = gsp_is_return(regs);
if (WAS_MULTITHREADING) {
if (set_last_wish_all(regs, l_psl, p_psl + res)) {
DbgTC("error in set_last_wish_all\n");
return 1;
}
}
/* check if the local is allowed to store in the global */
if (!res) {
if (l_psl > p_psl) {
DbgTC("l_psl > p_psl Exiting.\n");
return 1;
};
} else if (l_psl > (p_psl + 1)) {
/* Easy case. No need to do anything */
DbgTC("RETURN case. l_psl > p_psl + 1. Exiting.\n");
return 1;
} else if (l_psl == (p_psl + 1)) {
/* This is ugly patch against the case when LOAD/STORE to
* a global is located in the same VLIW with CT (control
* transfer) instruction associated with RETURN
*/
list = current_thread_info()->g_list;
/* Most un-pleasant case, when stored local belongs to
* current procedure being executing on the CPU
*/
DbgTC("RETURN case. l_psl == (p_psl + 1)\n");
for (record = list; record != NULL; record = record->next) {
if (record->global_p == global)
break;
}
if (!WAS_MULTITHREADING && record != NULL) {
unsigned int old_psl = record->lcl_psl;
DbgTC("RETURN case. record != NULL, old_psl=%d, p_psl=%d\n",
old_psl, p_psl);
/* Clear old last wish */
if (old_psl == p_psl) {
e2k_psr_t psr;
AW(psr) = AS(regs->crs.cr1_lo).psr;
if (AS(psr).lw && !record->orig_psr_lw) {
AS(psr).lw = 0;
AS(regs->crs.cr1_lo).psr = AW(psr);
}
} else if (WAS_MULTITHREADING) {
if (set_last_wish_all(regs, l_psl, p_psl)) {
DbgTC("error in set_last_wish_all\n");
return 1;
}
} else {
struct change_lw_args args;
long ret;
CHECK_SIZE("do_global_sp ", 1);
args.set = false;
args.skip = p_psl - l_psl;
args.orig_psr_lw = record->orig_psr_lw;
ret = parse_chain_stack(PCS_USER, NULL,
__change_last_wish_one, &args);
if (IS_ERR_VALUE(ret))
return 1;
}
/* Delete the record from list */
delete_list(record);
kfree((void *) record);
};
/* Clear the global with Null Pointer */
DbgTC("RETURN case. Clearing of global with addr = %lx\n",
global);
if (!IS_MULTITHREADING) {
E2K_STORE_NULLPTR_QWORD(global);
return 1;
}
}
/* The list is defined as one entry per global.
* If the address matched an existing entry - just update the record.
* If not - create new one.
* Update will also move the last_wish flag on a new location in CS.
*/
init_g_list(0);
list = current_thread_info()->g_list;
/*
* Look for the first (the only) record in list that contains
* this global's pointer
*/
for (record = list; record != NULL; record = record->next)
{
if (record->global_p == global)
break;
}
if (IS_MULTITHREADING || record == NULL) {
DbgTC(""
" record == NULL, call for new_record().\n");
new_record(list, regs, global, l_psl, p_psl);
} else {
DbgTC(""
" record == NULL, call for update_record().\n");
update_record(record, regs, l_psl, p_psl);
}
return 0;
}
static void print_global_records(global_store_t *entry)
{
e2k_rwsap_hi_struct_t sap_hi;
long size;
AS_WORD(sap_hi) = entry->word2;
size = AS_STRUCT(sap_hi).size;
printk("GLOBAL pid=%d global=0x%lx psl=%d entry=%px\n",
entry->pid, entry->global_p, entry->lcl_psl, entry);
printk(" new_address=0x%lx old_address=0x%lx"
" sbr=0x%lx size=%lx\n",
entry->new_address, entry->old_address,
entry->sbr, size);
}
static void print_init_records(global_store_t *entry)
{
pr_info("INIT pid=%d GET_CURR_IND=0x%x NUM_THREAD=%d\n",
entry->pid, entry->lcl_psl, NUM_THREAD(entry));
}
static void print_bound_records(global_store_t *entry)
{
printk("BOUND pid=%d TIME(CURR_IND)=0x%lx entry=%px\n",
entry->pid, GET_TIME(entry), entry);
printk(" sbr=0x%lx lcl_psl=%d\n",
entry->sbr, entry->lcl_psl);
}
static void print_all_records(void)
{
global_store_t *entry = current_thread_info()->g_list;
DbgTC("print_all_records entry %px \n", entry);
while (entry != NULL)
{
switch (entry->type)
{
case TYPE_INIT:
print_init_records(entry);
break;
case TYPE_BOUND:
print_bound_records(entry);
break;
case TYPE_GLOBAL:
print_global_records(entry);
break;
default:
printk("UNKNOWN entry=%px entry->type =%d\n",
entry, entry->type);
break;
}
entry = entry->next;
}
}
int delete_records(unsigned int psl_from)
{
e2k_addr_t global; /* address of the GLOBAL */
/* user procedure */
unsigned int g_psl = 0;
global_store_t *entry, *advance_entry;
struct mm_struct *mm;
struct vm_area_struct *vma = NULL;
DbgTC("Deleting records with psl >= %d\n", psl_from);
if (current_thread_info()->g_list == NULL) {
DbgTC("list of globals is empty. Exiting.\n");
return 1;
}
mm = current->mm;
down_read(&mm->mmap_sem);
entry = current_thread_info()->g_list;
while (entry != NULL)
{
advance_entry = entry->next; /* to avoid re-usage of */
/* an entry after kfree() */
if (entry->lcl_psl >= psl_from && IS_THIS_THREAD(entry)) {
global = entry->global_p;
vma = find_vma(mm, global);
if (!vma || ((vma->vm_start) > global)) {
printk(KERN_NOTICE "delete_records(): global %lx "
" belongs to an unmapped area\n", global);
} else if ( IS_SAP_LO(global) &&
IS_SAP_HI(global + sizeof(e2k_rwsap_lo_struct_t)) ) {
e2k_rwsap_lo_struct_t sap_lo;
AS_WORD(sap_lo) = *(u64 *) global;
g_psl = AS_STRUCT(sap_lo).psl;
DbgTC("SAP global addr LO = %lx, "
" HI = %lx, "
"global psl = %d\n", global,
global + sizeof (e2k_rwsap_lo_struct_t), g_psl);
if (g_psl >= psl_from)
E2K_STORE_NULLPTR_QWORD(global);
} else {
DbgTC("The data isn't SAP: data0 = %llx, data1 = %llx\n",
*(u64 *) global,
*(u64 *) (global + sizeof(u64)));
}
delete_list(entry);
kfree((void *) entry);
DbgTC("Entry %px freed\n", entry);
}
entry = advance_entry;
}
up_read(&mm->mmap_sem);
return 0;
}
int lw_global_sp(struct pt_regs *regs)
{
e2k_addr_t global; /* address of the GLOBAL */
unsigned int g_psl = 0; /* PSL of the GLOBAL */
unsigned int p_psl; /* PSL of the interrupted */
/* user procedure */
e2k_pusd_lo_t pusd_lo;
global_store_t *entry, *advance_entry;
struct mm_struct *mm;
struct vm_area_struct *vma = NULL;
AS_WORD(pusd_lo) = AS_WORD(regs->stacks.usd_lo);
if (AS_STRUCT(pusd_lo).p) {
DbgTC("protected mode detected\n");
p_psl = AS_STRUCT(pusd_lo).psl;
/* NOTICE: correction for OS entrance */
p_psl--;
DbgTC("interrupted procedure psl = %d\n",
p_psl);
} else {
DbgTC("NON-protected mode detected. "
"Exiting.\n");
return 1;
}
if (current_thread_info()->g_list == NULL) {
DbgTC("list of globals is empty. Exiting.\n");
return 1;
}
down_read_lock_multithread();
mm = current->mm;
down_read(&mm->mmap_sem);
entry = current_thread_info()->g_list;
while (entry != NULL)
{
advance_entry = entry->next; /* to avoid re-usage of */
/* an entry after kfree() */
if (entry->lcl_psl == (p_psl+1) && IS_THIS_THREAD(entry)) {
/* lms fires LWE after the CT */
global = entry->global_p;
vma = find_vma(mm, global);
if (!vma || ((vma->vm_start) > global)) {
DbgTC(KERN_NOTICE "lw_global_sp(): global %lx belongs to an"
" unmapped area\n", global);
} else if ( IS_SAP_LO(global) &&
IS_SAP_HI(global + sizeof(e2k_rwsap_lo_struct_t)) ) {
e2k_rwsap_lo_struct_t sap_lo;
AS_WORD(sap_lo) = *(u64 *) global;
g_psl = AS_STRUCT(sap_lo).psl;
DbgTC("SAP global addr LO = %lx, "
" HI = %lx, "
"global psl = %d\n", global,
global + sizeof (e2k_rwsap_lo_struct_t), g_psl);
if (!WAS_MULTITHREADING && g_psl > p_psl)
E2K_STORE_NULLPTR_QWORD(global);
} else {
DbgTC("The data isn't SAP: data0 = %llx, data1 = %llx\n",
*(u64 *) global,
*(u64 *) (global + sizeof(u64)));
}
if (!WAS_MULTITHREADING) {
delete_list(entry);
kfree((void *) entry);
DbgTC("Entry %px freed\n", entry);
}
};
entry = advance_entry;
}
set_new_bound(p_psl+1, regs);
up_read(&mm->mmap_sem);
up_read_lock_multithread();
return 0;
}