1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
|
/*
* S390 purgatory
*
* Copyright IBM Corp. 2011
*
* Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
*/
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "../../../kexec/kexec-sha256.h"
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define MAX(x, y) ((x) > (y) ? (x) : (y))
extern struct sha256_region sha256_regions[SHA256_REGIONS];
unsigned long crash_base = (unsigned long) -1;
unsigned long crash_size = (unsigned long) -1;
/*
* Implement memcpy using the mvcle instruction
*/
static void memcpy_fast(void *target, void *src, unsigned long size)
{
register unsigned long __target asm("2") = (unsigned long) target;
register unsigned long __size1 asm("3") = size;
register unsigned long __src asm("4") = (unsigned long) src;
register unsigned long __size2 asm("5") = size;
asm volatile (
"0: mvcle %0,%2,0\n"
" jo 0b\n"
: "+d" (__target), "+d" (__size1), "+d" (__src), "+d" (__size2)
:
: "cc", "memory"
);
}
/*
* Swap memory areas
*/
static void memswap(void *addr1, void *addr2, unsigned long size)
{
unsigned long off, copy_len;
static char buf[1024];
for (off = 0; off < size; off += sizeof(buf)) {
copy_len = MIN(size - off, sizeof(buf));
memcpy_fast(buf, (void *) addr2 + off, copy_len);
memcpy_fast(addr2 + off, addr1 + off, copy_len);
memcpy_fast(addr1 + off, buf, copy_len);
}
}
/*
* Nothing to do
*/
void setup_arch(void)
{
}
/*
* Do swap of [crash base - crash base + size] with [0 - crash size]
*
* We swap all kexec segments except of purgatory. The rest is copied
* from [0 - crash size] to [crash base - crash base + size].
* We use [0x2000 - 0x10000] for purgatory. This area is never used
* by s390 Linux kernels.
*
* This functions assumes that the sha256_regions[] is sorted.
*/
void post_verification_setup_arch(void)
{
unsigned long start, len, last = crash_base + 0x10000;
struct sha256_region *ptr, *end;
end = &sha256_regions[sizeof(sha256_regions)/sizeof(sha256_regions[0])];
for (ptr = sha256_regions; ptr < end; ptr++) {
if (!ptr->start)
continue;
start = MAX(ptr->start, crash_base + 0x10000);
len = ptr->len - (start - ptr->start);
memcpy_fast((void *) last, (void *) last - crash_base,
start - last);
memswap((void *) start - crash_base, (void *) start, len);
last = start + len;
}
memcpy_fast((void *) last, (void *) last - crash_base,
crash_base + crash_size - last);
memcpy_fast((void *) crash_base, (void *) 0, 0x2000);
}
|