mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-14 12:37:32 +00:00
[POWERPC] cell: handle SPE kernel mappings that cross segment boundaries
Currently, we have a possibilty that the SLBs setup during context switch don't cover the entirety of the necessary lscsa and code regions, if these regions cross a segment boundary. This change checks the start and end of each region, and inserts a SLB entry for each, if unique. We also remove the assumption that the spu_save_code and spu_restore_code reside in the same segment, by using the specific code array for save and restore. Signed-off-by: Jeremy Kerr <jk@ozlabs.org> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
parent
f6eb7d7ffe
commit
684bd61401
3 changed files with 52 additions and 13 deletions
|
@ -274,20 +274,56 @@ static void __spu_kernel_slb(void *addr, struct spu_slb *slb)
|
||||||
slb->esid = (ea & ESID_MASK) | SLB_ESID_V;
|
slb->esid = (ea & ESID_MASK) | SLB_ESID_V;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given an array of @nr_slbs SLB entries, @slbs, return non-zero if the
|
||||||
|
* address @new_addr is present.
|
||||||
|
*/
|
||||||
|
static inline int __slb_present(struct spu_slb *slbs, int nr_slbs,
|
||||||
|
void *new_addr)
|
||||||
|
{
|
||||||
|
unsigned long ea = (unsigned long)new_addr;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < nr_slbs; i++)
|
||||||
|
if (!((slbs[i].esid ^ ea) & ESID_MASK))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setup the SPU kernel SLBs, in preparation for a context save/restore. We
|
* Setup the SPU kernel SLBs, in preparation for a context save/restore. We
|
||||||
* need to map both the context save area, and the save/restore code.
|
* need to map both the context save area, and the save/restore code.
|
||||||
|
*
|
||||||
|
* Because the lscsa and code may cross segment boundaires, we check to see
|
||||||
|
* if mappings are required for the start and end of each range. We currently
|
||||||
|
* assume that the mappings are smaller that one segment - if not, something
|
||||||
|
* is seriously wrong.
|
||||||
*/
|
*/
|
||||||
void spu_setup_kernel_slbs(struct spu *spu, struct spu_lscsa *lscsa, void *code)
|
void spu_setup_kernel_slbs(struct spu *spu, struct spu_lscsa *lscsa,
|
||||||
|
void *code, int code_size)
|
||||||
{
|
{
|
||||||
struct spu_slb code_slb, lscsa_slb;
|
struct spu_slb slbs[4];
|
||||||
|
int i, nr_slbs = 0;
|
||||||
|
/* start and end addresses of both mappings */
|
||||||
|
void *addrs[] = {
|
||||||
|
lscsa, (void *)lscsa + sizeof(*lscsa) - 1,
|
||||||
|
code, code + code_size - 1
|
||||||
|
};
|
||||||
|
|
||||||
__spu_kernel_slb(lscsa, &lscsa_slb);
|
/* check the set of addresses, and create a new entry in the slbs array
|
||||||
__spu_kernel_slb(code, &code_slb);
|
* if there isn't already a SLB for that address */
|
||||||
|
for (i = 0; i < ARRAY_SIZE(addrs); i++) {
|
||||||
|
if (__slb_present(slbs, nr_slbs, addrs[i]))
|
||||||
|
continue;
|
||||||
|
|
||||||
spu_load_slb(spu, 0, &lscsa_slb);
|
__spu_kernel_slb(addrs[i], &slbs[nr_slbs]);
|
||||||
if (lscsa_slb.esid != code_slb.esid)
|
nr_slbs++;
|
||||||
spu_load_slb(spu, 1, &code_slb);
|
}
|
||||||
|
|
||||||
|
/* Add the set of SLBs */
|
||||||
|
for (i = 0; i < nr_slbs; i++)
|
||||||
|
spu_load_slb(spu, i, &slbs[i]);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(spu_setup_kernel_slbs);
|
EXPORT_SYMBOL_GPL(spu_setup_kernel_slbs);
|
||||||
|
|
||||||
|
|
|
@ -691,7 +691,8 @@ static inline void resume_mfc_queue(struct spu_state *csa, struct spu *spu)
|
||||||
out_be64(&priv2->mfc_control_RW, MFC_CNTL_RESUME_DMA_QUEUE);
|
out_be64(&priv2->mfc_control_RW, MFC_CNTL_RESUME_DMA_QUEUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void setup_mfc_slbs(struct spu_state *csa, struct spu *spu)
|
static inline void setup_mfc_slbs(struct spu_state *csa, struct spu *spu,
|
||||||
|
unsigned int *code, int code_size)
|
||||||
{
|
{
|
||||||
/* Save, Step 47:
|
/* Save, Step 47:
|
||||||
* Restore, Step 30.
|
* Restore, Step 30.
|
||||||
|
@ -708,7 +709,7 @@ static inline void setup_mfc_slbs(struct spu_state *csa, struct spu *spu)
|
||||||
* translation is desired by OS environment).
|
* translation is desired by OS environment).
|
||||||
*/
|
*/
|
||||||
spu_invalidate_slbs(spu);
|
spu_invalidate_slbs(spu);
|
||||||
spu_setup_kernel_slbs(spu, csa->lscsa, &spu_save_code);
|
spu_setup_kernel_slbs(spu, csa->lscsa, code, code_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void set_switch_active(struct spu_state *csa, struct spu *spu)
|
static inline void set_switch_active(struct spu_state *csa, struct spu *spu)
|
||||||
|
@ -1835,7 +1836,8 @@ static void save_lscsa(struct spu_state *prev, struct spu *spu)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
resume_mfc_queue(prev, spu); /* Step 46. */
|
resume_mfc_queue(prev, spu); /* Step 46. */
|
||||||
setup_mfc_slbs(prev, spu); /* Step 47. */
|
/* Step 47. */
|
||||||
|
setup_mfc_slbs(prev, spu, spu_save_code, sizeof(spu_save_code));
|
||||||
set_switch_active(prev, spu); /* Step 48. */
|
set_switch_active(prev, spu); /* Step 48. */
|
||||||
enable_interrupts(prev, spu); /* Step 49. */
|
enable_interrupts(prev, spu); /* Step 49. */
|
||||||
save_ls_16kb(prev, spu); /* Step 50. */
|
save_ls_16kb(prev, spu); /* Step 50. */
|
||||||
|
@ -1940,7 +1942,8 @@ static void restore_lscsa(struct spu_state *next, struct spu *spu)
|
||||||
setup_spu_status_part1(next, spu); /* Step 27. */
|
setup_spu_status_part1(next, spu); /* Step 27. */
|
||||||
setup_spu_status_part2(next, spu); /* Step 28. */
|
setup_spu_status_part2(next, spu); /* Step 28. */
|
||||||
restore_mfc_rag(next, spu); /* Step 29. */
|
restore_mfc_rag(next, spu); /* Step 29. */
|
||||||
setup_mfc_slbs(next, spu); /* Step 30. */
|
/* Step 30. */
|
||||||
|
setup_mfc_slbs(next, spu, spu_restore_code, sizeof(spu_restore_code));
|
||||||
set_spu_npc(next, spu); /* Step 31. */
|
set_spu_npc(next, spu); /* Step 31. */
|
||||||
set_signot1(next, spu); /* Step 32. */
|
set_signot1(next, spu); /* Step 32. */
|
||||||
set_signot2(next, spu); /* Step 33. */
|
set_signot2(next, spu); /* Step 33. */
|
||||||
|
|
|
@ -201,8 +201,8 @@ int spu_irq_class_0_bottom(struct spu *spu);
|
||||||
int spu_irq_class_1_bottom(struct spu *spu);
|
int spu_irq_class_1_bottom(struct spu *spu);
|
||||||
void spu_irq_setaffinity(struct spu *spu, int cpu);
|
void spu_irq_setaffinity(struct spu *spu, int cpu);
|
||||||
|
|
||||||
void spu_setup_kernel_slbs(struct spu *spu,
|
void spu_setup_kernel_slbs(struct spu *spu, struct spu_lscsa *lscsa,
|
||||||
struct spu_lscsa *lscsa, void *code);
|
void *code, int code_size);
|
||||||
|
|
||||||
#ifdef CONFIG_KEXEC
|
#ifdef CONFIG_KEXEC
|
||||||
void crash_register_spus(struct list_head *list);
|
void crash_register_spus(struct list_head *list);
|
||||||
|
|
Loading…
Reference in a new issue