summaryrefslogtreecommitdiffstats
path: root/purgatory/arch/ia64/purgatory-ia64.c
blob: e138e9ca315d45942c9a742be8c04ef72009b268 (plain)
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
/*
 * purgatory:  setup code
 *
 * Copyright (C) 2005-2006  Zou Nan hai (nanhai.zou@intel.com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation (version 2 of the License).
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#include <purgatory.h>
#include <stdint.h>
#include <string.h>
#include "purgatory-ia64.h"

#define PAGE_OFFSET             0xe000000000000000UL

#define EFI_PAGE_SHIFT          12
#define EFI_PAGE_SIZE		(1UL<<EFI_PAGE_SHIFT)
#define EFI_PAGE_ALIGN(x)	((x + EFI_PAGE_SIZE - 1)&~(EFI_PAGE_SIZE-1))
/* Memory types: */
#define EFI_RESERVED_TYPE                0
#define EFI_LOADER_CODE                  1
#define EFI_LOADER_DATA                  2
#define EFI_BOOT_SERVICES_CODE           3
#define EFI_BOOT_SERVICES_DATA           4
#define EFI_RUNTIME_SERVICES_CODE        5
#define EFI_RUNTIME_SERVICES_DATA        6
#define EFI_CONVENTIONAL_MEMORY          7
#define EFI_UNUSABLE_MEMORY              8
#define EFI_ACPI_RECLAIM_MEMORY          9
#define EFI_ACPI_MEMORY_NVS             10
#define EFI_MEMORY_MAPPED_IO            11
#define EFI_MEMORY_MAPPED_IO_PORT_SPACE 12
#define EFI_PAL_CODE                    13
#define EFI_MAX_MEMORY_TYPE             14

typedef struct {
        uint64_t signature;
        uint32_t revision;
        uint32_t headersize;
        uint32_t crc32;
        uint32_t reserved;
} efi_table_hdr_t;

typedef struct {
        efi_table_hdr_t hdr;
        unsigned long get_time;
        unsigned long set_time;
        unsigned long get_wakeup_time;
        unsigned long set_wakeup_time;
        unsigned long set_virtual_address_map;
        unsigned long convert_pointer;
        unsigned long get_variable;
        unsigned long get_next_variable;
        unsigned long set_variable;
        unsigned long get_next_high_mono_count;
        unsigned long reset_system;
} efi_runtime_services_t;

typedef struct {
        efi_table_hdr_t hdr;
        unsigned long fw_vendor;          /* physical addr of
					     CHAR16 vendor string */
        uint32_t fw_revision;
        unsigned long con_in_handle;
        unsigned long con_in;
        unsigned long con_out_handle;
        unsigned long con_out;
        unsigned long stderr_handle;
        unsigned long stderr;
        unsigned long runtime;
        unsigned long boottime;
        unsigned long nr_tables;
        unsigned long tables;
} efi_system_table_t;

struct ia64_boot_param {
        uint64_t command_line;             /* physical address of
					      command linearguments */
        uint64_t efi_systab;               /* physical address of
					      EFI system table */
        uint64_t efi_memmap;               /* physical address of
					      EFI memory map */
        uint64_t efi_memmap_size;          /* size of EFI memory map */
        uint64_t efi_memdesc_size;         /* size of an EFI memory map
					      descriptor */
        uint32_t efi_memdesc_version;      /* memory descriptor version */
        struct {
                uint16_t num_cols;         /* number of columns on console
					      output device */
                uint16_t num_rows;         /* number of rows on console
					      output device */
                uint16_t orig_x;           /* cursor's x position */
                uint16_t orig_y;           /* cursor's y position */
        } console_info;
        uint64_t fpswa;                    /* physical address of
					      the fpswa interface */
        uint64_t initrd_start;
        uint64_t initrd_size;

        uint64_t vmcode_start;
        uint64_t vmcode_size;
};

typedef struct {
        uint32_t type;
        uint32_t pad;
        uint64_t phys_addr;
        uint64_t virt_addr;
        uint64_t num_pages;
        uint64_t attribute;
} efi_memory_desc_t;

struct loaded_segment {
        unsigned long start;
        unsigned long end;
};

struct kexec_boot_params {
	uint64_t vmcode_base;
	uint64_t vmcode_size;
	uint64_t ramdisk_base;
	uint64_t ramdisk_size;
	uint64_t command_line;
	uint64_t command_line_len;
	uint64_t efi_memmap_base;
	uint64_t efi_memmap_size;
	uint64_t boot_param_base;
	struct loaded_segment *loaded_segments;
	unsigned long loaded_segments_num;
};

void
setup_arch(void)
{
	reset_vga();
}

inline unsigned long PA(unsigned long addr)
{
	return addr & 0x0fffffffffffffffLL;
}

void
patch_efi_memmap(struct kexec_boot_params *params,
		struct ia64_boot_param *boot_param)
{
	void *dest = (void *)params->efi_memmap_base;
	void *src  = (void *)boot_param->efi_memmap;
	uint64_t orig_type;
	efi_memory_desc_t *src_md, *dst_md;
	void *src_end = src + boot_param->efi_memmap_size;
	unsigned long i;
	for (; src < src_end; src += boot_param->efi_memdesc_size,
	     dest += boot_param->efi_memdesc_size) {
		unsigned long mstart, mend;
		src_md = src;
		dst_md = dest;
		if (src_md->num_pages == 0)
			continue;
		mstart = src_md->phys_addr;
		mend = src_md->phys_addr +
			(src_md->num_pages << EFI_PAGE_SHIFT);
		*dst_md = *src_md;
		if (src_md->type == EFI_LOADER_DATA)
			dst_md->type = EFI_CONVENTIONAL_MEMORY;
		/* segments are already sorted and aligned to 4K */
		orig_type = dst_md->type;
		for (i = 0; i < params->loaded_segments_num; i++) {
			struct loaded_segment *seg;
			unsigned long start_pages, mid_pages, end_pages;

			seg = &params->loaded_segments[i];
			if (seg->start < mstart || seg->start >= mend)
				continue;

			while (seg->end > mend && src < src_end) {
				src += boot_param->efi_memdesc_size;
				src_md = src;
				/* TODO check contig and attribute here */
				mend = src_md->phys_addr +
					(src_md->num_pages << EFI_PAGE_SHIFT);
			}
                        if (seg->end < mend && src < src_end) {
                                void *src_next;
                                efi_memory_desc_t *src_next_md;
                                src_next = src + boot_param->efi_memdesc_size;
                                src_next_md = src_next;
                                if (src_next_md->type ==
                                    EFI_CONVENTIONAL_MEMORY) {
				        /* TODO check contig and attribute */
                                        src += boot_param->efi_memdesc_size;
                                        src_md = src;
				        mend = src_md->phys_addr +
					        (src_md->num_pages <<
                                                 EFI_PAGE_SHIFT);
                                }

                        }
			start_pages = (seg->start - mstart) >> EFI_PAGE_SHIFT;
			mid_pages = (seg->end - seg->start) >> EFI_PAGE_SHIFT;
			end_pages = (mend - seg->end) >> EFI_PAGE_SHIFT;
			if (start_pages) {
				dst_md->num_pages = start_pages;
				dest += boot_param->efi_memdesc_size;
				dst_md = dest;
				*dst_md = *src_md;
			}
			dst_md->phys_addr = seg->start;
			dst_md->num_pages = mid_pages;
			dst_md->type = EFI_LOADER_DATA;
			if (!end_pages)
				break;
			dest += boot_param->efi_memdesc_size;
			dst_md = dest;
			*dst_md = *src_md;
			dst_md->phys_addr = seg->end;
			dst_md->num_pages = end_pages;
			dst_md->type = orig_type;
			mstart = seg->end;
		}
	}

	boot_param->efi_memmap_size = dest - (void *)params->efi_memmap_base;
}

void
flush_icache_range(char *start, unsigned long len)
{
	unsigned long i, addr;
	addr = (unsigned long)start & ~31UL;
	len += (unsigned long)start - addr;
	for (i = 0;i < len; i += 32)
	  asm volatile("fc.i %0"::"r"(start + i):"memory");
	asm volatile (";;sync.i;;":::"memory");
	asm volatile ("srlz.i":::"memory");
}

extern char __dummy_efi_function[], __dummy_efi_function_end[];


void
ia64_env_setup(struct ia64_boot_param *boot_param,
	struct kexec_boot_params *params)
{
	unsigned long len;
        efi_system_table_t *systab;
        efi_runtime_services_t *runtime;
	unsigned long *set_virtual_address_map;
	char *command_line = (char *)params->command_line;
	uint64_t command_line_len = params->command_line_len;
	struct ia64_boot_param *new_boot_param =
	(struct ia64_boot_param *) params->boot_param_base;
	memcpy(new_boot_param, boot_param, 4096);

	/*
	 * patch efi_runtime->set_virtual_address_map to a dummy function
	 *
	 * The EFI specification mandates that set_virtual_address_map only
	 * takes effect the first time that it is called, and that
	 * subsequent calls will return error.  By replacing it with a
	 * dummy function the new OS can think it is calling it again
	 * without either the OS or any buggy EFI implementations getting
	 * upset.
	 *
	 * Note: as the EFI specification says that set_virtual_address_map
	 * will only take affect the first time it is called, the mapping
	 * can't be updated, and thus mapping of the old and new OS really
	 * needs to be the same.
	 */
	len = __dummy_efi_function_end - __dummy_efi_function;
	memcpy(command_line + command_line_len,
		__dummy_efi_function, len);
	systab = (efi_system_table_t *)new_boot_param->efi_systab;
	runtime = (efi_runtime_services_t *)PA(systab->runtime);
	set_virtual_address_map =
		(unsigned long *)PA(runtime->set_virtual_address_map);
	*(set_virtual_address_map) =
		(unsigned long)(command_line + command_line_len);
	flush_icache_range(command_line + command_line_len, len);

	patch_efi_memmap(params, new_boot_param);

	new_boot_param->efi_memmap = params->efi_memmap_base;
	new_boot_param->command_line = params->command_line;
	new_boot_param->console_info.orig_x = 0;
	new_boot_param->console_info.orig_y = 0;
	new_boot_param->initrd_start = params->ramdisk_base;
	new_boot_param->initrd_size =  params->ramdisk_size;
	new_boot_param->vmcode_start = params->vmcode_base;
	new_boot_param->vmcode_size =  params->vmcode_size;
}

/* This function can be used to execute after the SHA256 verification. */
void post_verification_setup_arch(void)
{
	/* Nothing for now */
}