124 lines
2.7 KiB
C
124 lines
2.7 KiB
C
#ifndef _E2K_ASM_GETSP_ADJ_H
|
|
#define _E2K_ASM_GETSP_ADJ_H
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <asm/thread_info.h>
|
|
|
|
/*
|
|
* bug #101468: if user allocated more than 4Gb of stack then %cr1_hi.ussz
|
|
* field would overflow. In this case we remember all such over/underflows
|
|
* in software and apply corresponding corrections to %usd.lo.base manually
|
|
* from exc_last_wish handler.
|
|
*
|
|
* All under/overflows are kept in a single list.
|
|
*/
|
|
struct getsp_adj {
|
|
struct list_head list_entry;
|
|
unsigned long frame_index;
|
|
int correction;
|
|
};
|
|
|
|
|
|
static inline int __copy_getsp_adj(struct list_head *dst,
|
|
const struct list_head *src)
|
|
{
|
|
const struct getsp_adj *p;
|
|
struct getsp_adj *new;
|
|
|
|
list_for_each_entry(p, src, list_entry) {
|
|
new = kmalloc(sizeof(*new), GFP_KERNEL);
|
|
if (!new)
|
|
return -ENOMEM;
|
|
|
|
new->correction = p->correction;
|
|
new->frame_index = p->frame_index;
|
|
list_add_tail(&new->list_entry, dst);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline int copy_getsp_adj(struct thread_info *new_ti,
|
|
const struct thread_info *old_ti)
|
|
{
|
|
return __copy_getsp_adj(&new_ti->getsp_adj, &old_ti->getsp_adj);
|
|
}
|
|
|
|
static inline void free_getsp_adj(struct list_head *getsp_adj_list)
|
|
{
|
|
struct getsp_adj *p, *tmp;
|
|
|
|
list_for_each_entry_safe(p, tmp, getsp_adj_list, list_entry) {
|
|
list_del(&p->list_entry);
|
|
kfree(p);
|
|
}
|
|
}
|
|
|
|
static inline s64 getsp_adj_get_correction(unsigned long frame)
|
|
{
|
|
unsigned long frame_index = frame - (u64) CURRENT_PCS_BASE();
|
|
struct getsp_adj *p;
|
|
|
|
list_for_each_entry(p, ¤t_thread_info()->getsp_adj, list_entry) {
|
|
if (p->frame_index == frame_index)
|
|
return (s64) p->correction;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline int getsp_adj_set_correction(int correction, unsigned long frame)
|
|
{
|
|
unsigned long frame_index = frame - (u64) CURRENT_PCS_BASE();
|
|
struct getsp_adj *new, *p;
|
|
|
|
list_for_each_entry(p, ¤t_thread_info()->getsp_adj, list_entry) {
|
|
if (p->frame_index == frame_index) {
|
|
if (correction) {
|
|
p->correction = correction;
|
|
} else {
|
|
list_del(&p->list_entry);
|
|
kfree(p);
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (!correction)
|
|
return 0;
|
|
|
|
new = kmalloc(sizeof(*new), GFP_KERNEL);
|
|
if (!new)
|
|
return -ENOMEM;
|
|
|
|
new->correction = correction;
|
|
new->frame_index = frame_index;
|
|
list_add(&new->list_entry, ¤t_thread_info()->getsp_adj);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline void getsp_adj_apply(struct pt_regs *regs)
|
|
{
|
|
unsigned long frame, frame_index;
|
|
struct getsp_adj *p;
|
|
|
|
frame = AS(regs->stacks.pcsp_lo).base + AS(regs->stacks.pcsp_hi).ind;
|
|
frame_index = frame - (u64) CURRENT_PCS_BASE();
|
|
|
|
list_for_each_entry(p, ¤t_thread_info()->getsp_adj, list_entry) {
|
|
if (p->frame_index == frame_index)
|
|
goto found;
|
|
}
|
|
|
|
return;
|
|
|
|
found:
|
|
AS(regs->stacks.usd_lo).base += p->correction * 0x100000000ULL;
|
|
list_del(&p->list_entry);
|
|
kfree(p);
|
|
}
|
|
|
|
#endif
|