| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * Extensible Firmware Interface | 
 | 3 |  * | 
 | 4 |  * Based on Extensible Firmware Interface Specification version 0.9 April 30, 1999 | 
 | 5 |  * | 
 | 6 |  * Copyright (C) 1999 VA Linux Systems | 
 | 7 |  * Copyright (C) 1999 Walt Drummond <drummond@valinux.com> | 
 | 8 |  * Copyright (C) 1999-2003 Hewlett-Packard Co. | 
 | 9 |  *	David Mosberger-Tang <davidm@hpl.hp.com> | 
 | 10 |  *	Stephane Eranian <eranian@hpl.hp.com> | 
| Bjorn Helgaas | 32e62c6 | 2006-05-05 17:19:50 -0600 | [diff] [blame] | 11 |  * (c) Copyright 2006 Hewlett-Packard Development Company, L.P. | 
 | 12 |  *	Bjorn Helgaas <bjorn.helgaas@hp.com> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 13 |  * | 
 | 14 |  * All EFI Runtime Services are not implemented yet as EFI only | 
 | 15 |  * supports physical mode addressing on SoftSDV. This is to be fixed | 
 | 16 |  * in a future version.  --drummond 1999-07-20 | 
 | 17 |  * | 
 | 18 |  * Implemented EFI runtime services and virtual mode calls.  --davidm | 
 | 19 |  * | 
 | 20 |  * Goutham Rao: <goutham.rao@intel.com> | 
 | 21 |  *	Skip non-WB memory and ignore empty memory ranges. | 
 | 22 |  */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 23 | #include <linux/module.h> | 
 | 24 | #include <linux/kernel.h> | 
 | 25 | #include <linux/init.h> | 
 | 26 | #include <linux/types.h> | 
 | 27 | #include <linux/time.h> | 
 | 28 | #include <linux/efi.h> | 
| Zou Nan hai | a7956113 | 2006-12-07 09:51:35 -0800 | [diff] [blame] | 29 | #include <linux/kexec.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 30 |  | 
 | 31 | #include <asm/io.h> | 
 | 32 | #include <asm/kregs.h> | 
 | 33 | #include <asm/meminit.h> | 
 | 34 | #include <asm/pgtable.h> | 
 | 35 | #include <asm/processor.h> | 
 | 36 | #include <asm/mca.h> | 
 | 37 |  | 
 | 38 | #define EFI_DEBUG	0 | 
 | 39 |  | 
 | 40 | extern efi_status_t efi_call_phys (void *, ...); | 
 | 41 |  | 
 | 42 | struct efi efi; | 
 | 43 | EXPORT_SYMBOL(efi); | 
 | 44 | static efi_runtime_services_t *runtime; | 
| Zou Nan hai | a7956113 | 2006-12-07 09:51:35 -0800 | [diff] [blame] | 45 | static unsigned long mem_limit = ~0UL, max_addr = ~0UL, min_addr = 0UL; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 46 |  | 
 | 47 | #define efi_call_virt(f, args...)	(*(f))(args) | 
 | 48 |  | 
 | 49 | #define STUB_GET_TIME(prefix, adjust_arg)							  \ | 
 | 50 | static efi_status_t										  \ | 
 | 51 | prefix##_get_time (efi_time_t *tm, efi_time_cap_t *tc)						  \ | 
 | 52 | {												  \ | 
 | 53 | 	struct ia64_fpreg fr[6];								  \ | 
 | 54 | 	efi_time_cap_t *atc = NULL;								  \ | 
 | 55 | 	efi_status_t ret;									  \ | 
 | 56 | 												  \ | 
 | 57 | 	if (tc)											  \ | 
 | 58 | 		atc = adjust_arg(tc);								  \ | 
 | 59 | 	ia64_save_scratch_fpregs(fr);								  \ | 
 | 60 | 	ret = efi_call_##prefix((efi_get_time_t *) __va(runtime->get_time), adjust_arg(tm), atc); \ | 
 | 61 | 	ia64_load_scratch_fpregs(fr);								  \ | 
 | 62 | 	return ret;										  \ | 
 | 63 | } | 
 | 64 |  | 
 | 65 | #define STUB_SET_TIME(prefix, adjust_arg)							\ | 
 | 66 | static efi_status_t										\ | 
 | 67 | prefix##_set_time (efi_time_t *tm)								\ | 
 | 68 | {												\ | 
 | 69 | 	struct ia64_fpreg fr[6];								\ | 
 | 70 | 	efi_status_t ret;									\ | 
 | 71 | 												\ | 
 | 72 | 	ia64_save_scratch_fpregs(fr);								\ | 
 | 73 | 	ret = efi_call_##prefix((efi_set_time_t *) __va(runtime->set_time), adjust_arg(tm));	\ | 
 | 74 | 	ia64_load_scratch_fpregs(fr);								\ | 
 | 75 | 	return ret;										\ | 
 | 76 | } | 
 | 77 |  | 
 | 78 | #define STUB_GET_WAKEUP_TIME(prefix, adjust_arg)						\ | 
 | 79 | static efi_status_t										\ | 
 | 80 | prefix##_get_wakeup_time (efi_bool_t *enabled, efi_bool_t *pending, efi_time_t *tm)		\ | 
 | 81 | {												\ | 
 | 82 | 	struct ia64_fpreg fr[6];								\ | 
 | 83 | 	efi_status_t ret;									\ | 
 | 84 | 												\ | 
 | 85 | 	ia64_save_scratch_fpregs(fr);								\ | 
 | 86 | 	ret = efi_call_##prefix((efi_get_wakeup_time_t *) __va(runtime->get_wakeup_time),	\ | 
 | 87 | 				adjust_arg(enabled), adjust_arg(pending), adjust_arg(tm));	\ | 
 | 88 | 	ia64_load_scratch_fpregs(fr);								\ | 
 | 89 | 	return ret;										\ | 
 | 90 | } | 
 | 91 |  | 
 | 92 | #define STUB_SET_WAKEUP_TIME(prefix, adjust_arg)						\ | 
 | 93 | static efi_status_t										\ | 
 | 94 | prefix##_set_wakeup_time (efi_bool_t enabled, efi_time_t *tm)					\ | 
 | 95 | {												\ | 
 | 96 | 	struct ia64_fpreg fr[6];								\ | 
 | 97 | 	efi_time_t *atm = NULL;									\ | 
 | 98 | 	efi_status_t ret;									\ | 
 | 99 | 												\ | 
 | 100 | 	if (tm)											\ | 
 | 101 | 		atm = adjust_arg(tm);								\ | 
 | 102 | 	ia64_save_scratch_fpregs(fr);								\ | 
 | 103 | 	ret = efi_call_##prefix((efi_set_wakeup_time_t *) __va(runtime->set_wakeup_time),	\ | 
 | 104 | 				enabled, atm);							\ | 
 | 105 | 	ia64_load_scratch_fpregs(fr);								\ | 
 | 106 | 	return ret;										\ | 
 | 107 | } | 
 | 108 |  | 
 | 109 | #define STUB_GET_VARIABLE(prefix, adjust_arg)						\ | 
 | 110 | static efi_status_t									\ | 
 | 111 | prefix##_get_variable (efi_char16_t *name, efi_guid_t *vendor, u32 *attr,		\ | 
 | 112 | 		       unsigned long *data_size, void *data)				\ | 
 | 113 | {											\ | 
 | 114 | 	struct ia64_fpreg fr[6];							\ | 
 | 115 | 	u32 *aattr = NULL;									\ | 
 | 116 | 	efi_status_t ret;								\ | 
 | 117 | 											\ | 
 | 118 | 	if (attr)									\ | 
 | 119 | 		aattr = adjust_arg(attr);						\ | 
 | 120 | 	ia64_save_scratch_fpregs(fr);							\ | 
 | 121 | 	ret = efi_call_##prefix((efi_get_variable_t *) __va(runtime->get_variable),	\ | 
 | 122 | 				adjust_arg(name), adjust_arg(vendor), aattr,		\ | 
 | 123 | 				adjust_arg(data_size), adjust_arg(data));		\ | 
 | 124 | 	ia64_load_scratch_fpregs(fr);							\ | 
 | 125 | 	return ret;									\ | 
 | 126 | } | 
 | 127 |  | 
 | 128 | #define STUB_GET_NEXT_VARIABLE(prefix, adjust_arg)						\ | 
 | 129 | static efi_status_t										\ | 
 | 130 | prefix##_get_next_variable (unsigned long *name_size, efi_char16_t *name, efi_guid_t *vendor)	\ | 
 | 131 | {												\ | 
 | 132 | 	struct ia64_fpreg fr[6];								\ | 
 | 133 | 	efi_status_t ret;									\ | 
 | 134 | 												\ | 
 | 135 | 	ia64_save_scratch_fpregs(fr);								\ | 
 | 136 | 	ret = efi_call_##prefix((efi_get_next_variable_t *) __va(runtime->get_next_variable),	\ | 
 | 137 | 				adjust_arg(name_size), adjust_arg(name), adjust_arg(vendor));	\ | 
 | 138 | 	ia64_load_scratch_fpregs(fr);								\ | 
 | 139 | 	return ret;										\ | 
 | 140 | } | 
 | 141 |  | 
 | 142 | #define STUB_SET_VARIABLE(prefix, adjust_arg)						\ | 
 | 143 | static efi_status_t									\ | 
 | 144 | prefix##_set_variable (efi_char16_t *name, efi_guid_t *vendor, unsigned long attr,	\ | 
 | 145 | 		       unsigned long data_size, void *data)				\ | 
 | 146 | {											\ | 
 | 147 | 	struct ia64_fpreg fr[6];							\ | 
 | 148 | 	efi_status_t ret;								\ | 
 | 149 | 											\ | 
 | 150 | 	ia64_save_scratch_fpregs(fr);							\ | 
 | 151 | 	ret = efi_call_##prefix((efi_set_variable_t *) __va(runtime->set_variable),	\ | 
 | 152 | 				adjust_arg(name), adjust_arg(vendor), attr, data_size,	\ | 
 | 153 | 				adjust_arg(data));					\ | 
 | 154 | 	ia64_load_scratch_fpregs(fr);							\ | 
 | 155 | 	return ret;									\ | 
 | 156 | } | 
 | 157 |  | 
 | 158 | #define STUB_GET_NEXT_HIGH_MONO_COUNT(prefix, adjust_arg)					\ | 
 | 159 | static efi_status_t										\ | 
 | 160 | prefix##_get_next_high_mono_count (u32 *count)							\ | 
 | 161 | {												\ | 
 | 162 | 	struct ia64_fpreg fr[6];								\ | 
 | 163 | 	efi_status_t ret;									\ | 
 | 164 | 												\ | 
 | 165 | 	ia64_save_scratch_fpregs(fr);								\ | 
 | 166 | 	ret = efi_call_##prefix((efi_get_next_high_mono_count_t *)				\ | 
 | 167 | 				__va(runtime->get_next_high_mono_count), adjust_arg(count));	\ | 
 | 168 | 	ia64_load_scratch_fpregs(fr);								\ | 
 | 169 | 	return ret;										\ | 
 | 170 | } | 
 | 171 |  | 
 | 172 | #define STUB_RESET_SYSTEM(prefix, adjust_arg)					\ | 
 | 173 | static void									\ | 
 | 174 | prefix##_reset_system (int reset_type, efi_status_t status,			\ | 
 | 175 | 		       unsigned long data_size, efi_char16_t *data)		\ | 
 | 176 | {										\ | 
 | 177 | 	struct ia64_fpreg fr[6];						\ | 
 | 178 | 	efi_char16_t *adata = NULL;						\ | 
 | 179 | 										\ | 
 | 180 | 	if (data)								\ | 
 | 181 | 		adata = adjust_arg(data);					\ | 
 | 182 | 										\ | 
 | 183 | 	ia64_save_scratch_fpregs(fr);						\ | 
 | 184 | 	efi_call_##prefix((efi_reset_system_t *) __va(runtime->reset_system),	\ | 
 | 185 | 			  reset_type, status, data_size, adata);		\ | 
 | 186 | 	/* should not return, but just in case... */				\ | 
 | 187 | 	ia64_load_scratch_fpregs(fr);						\ | 
 | 188 | } | 
 | 189 |  | 
 | 190 | #define phys_ptr(arg)	((__typeof__(arg)) ia64_tpa(arg)) | 
 | 191 |  | 
 | 192 | STUB_GET_TIME(phys, phys_ptr) | 
 | 193 | STUB_SET_TIME(phys, phys_ptr) | 
 | 194 | STUB_GET_WAKEUP_TIME(phys, phys_ptr) | 
 | 195 | STUB_SET_WAKEUP_TIME(phys, phys_ptr) | 
 | 196 | STUB_GET_VARIABLE(phys, phys_ptr) | 
 | 197 | STUB_GET_NEXT_VARIABLE(phys, phys_ptr) | 
 | 198 | STUB_SET_VARIABLE(phys, phys_ptr) | 
 | 199 | STUB_GET_NEXT_HIGH_MONO_COUNT(phys, phys_ptr) | 
 | 200 | STUB_RESET_SYSTEM(phys, phys_ptr) | 
 | 201 |  | 
 | 202 | #define id(arg)	arg | 
 | 203 |  | 
 | 204 | STUB_GET_TIME(virt, id) | 
 | 205 | STUB_SET_TIME(virt, id) | 
 | 206 | STUB_GET_WAKEUP_TIME(virt, id) | 
 | 207 | STUB_SET_WAKEUP_TIME(virt, id) | 
 | 208 | STUB_GET_VARIABLE(virt, id) | 
 | 209 | STUB_GET_NEXT_VARIABLE(virt, id) | 
 | 210 | STUB_SET_VARIABLE(virt, id) | 
 | 211 | STUB_GET_NEXT_HIGH_MONO_COUNT(virt, id) | 
 | 212 | STUB_RESET_SYSTEM(virt, id) | 
 | 213 |  | 
 | 214 | void | 
 | 215 | efi_gettimeofday (struct timespec *ts) | 
 | 216 | { | 
 | 217 | 	efi_time_t tm; | 
 | 218 |  | 
 | 219 | 	memset(ts, 0, sizeof(ts)); | 
 | 220 | 	if ((*efi.get_time)(&tm, NULL) != EFI_SUCCESS) | 
 | 221 | 		return; | 
 | 222 |  | 
 | 223 | 	ts->tv_sec = mktime(tm.year, tm.month, tm.day, tm.hour, tm.minute, tm.second); | 
 | 224 | 	ts->tv_nsec = tm.nanosecond; | 
 | 225 | } | 
 | 226 |  | 
 | 227 | static int | 
| Christoph Lameter | 66888a6 | 2006-12-04 14:58:35 -0800 | [diff] [blame] | 228 | is_memory_available (efi_memory_desc_t *md) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 229 | { | 
 | 230 | 	if (!(md->attribute & EFI_MEMORY_WB)) | 
 | 231 | 		return 0; | 
 | 232 |  | 
 | 233 | 	switch (md->type) { | 
 | 234 | 	      case EFI_LOADER_CODE: | 
 | 235 | 	      case EFI_LOADER_DATA: | 
 | 236 | 	      case EFI_BOOT_SERVICES_CODE: | 
 | 237 | 	      case EFI_BOOT_SERVICES_DATA: | 
 | 238 | 	      case EFI_CONVENTIONAL_MEMORY: | 
 | 239 | 		return 1; | 
 | 240 | 	} | 
 | 241 | 	return 0; | 
 | 242 | } | 
 | 243 |  | 
| Tony Luck | d8c97d5 | 2005-09-08 12:39:59 -0700 | [diff] [blame] | 244 | typedef struct kern_memdesc { | 
 | 245 | 	u64 attribute; | 
 | 246 | 	u64 start; | 
 | 247 | 	u64 num_pages; | 
 | 248 | } kern_memdesc_t; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 249 |  | 
| Tony Luck | d8c97d5 | 2005-09-08 12:39:59 -0700 | [diff] [blame] | 250 | static kern_memdesc_t *kern_memmap; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 251 |  | 
| Bjorn Helgaas | 80851ef | 2006-01-08 01:04:13 -0800 | [diff] [blame] | 252 | #define efi_md_size(md)	(md->num_pages << EFI_PAGE_SHIFT) | 
 | 253 |  | 
 | 254 | static inline u64 | 
 | 255 | kmd_end(kern_memdesc_t *kmd) | 
 | 256 | { | 
 | 257 | 	return (kmd->start + (kmd->num_pages << EFI_PAGE_SHIFT)); | 
 | 258 | } | 
 | 259 |  | 
 | 260 | static inline u64 | 
 | 261 | efi_md_end(efi_memory_desc_t *md) | 
 | 262 | { | 
 | 263 | 	return (md->phys_addr + efi_md_size(md)); | 
 | 264 | } | 
 | 265 |  | 
 | 266 | static inline int | 
 | 267 | efi_wb(efi_memory_desc_t *md) | 
 | 268 | { | 
 | 269 | 	return (md->attribute & EFI_MEMORY_WB); | 
 | 270 | } | 
 | 271 |  | 
 | 272 | static inline int | 
 | 273 | efi_uc(efi_memory_desc_t *md) | 
 | 274 | { | 
 | 275 | 	return (md->attribute & EFI_MEMORY_UC); | 
 | 276 | } | 
 | 277 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 278 | static void | 
| Tony Luck | d8c97d5 | 2005-09-08 12:39:59 -0700 | [diff] [blame] | 279 | walk (efi_freemem_callback_t callback, void *arg, u64 attr) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 280 | { | 
| Tony Luck | d8c97d5 | 2005-09-08 12:39:59 -0700 | [diff] [blame] | 281 | 	kern_memdesc_t *k; | 
 | 282 | 	u64 start, end, voff; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 283 |  | 
| Tony Luck | d8c97d5 | 2005-09-08 12:39:59 -0700 | [diff] [blame] | 284 | 	voff = (attr == EFI_MEMORY_WB) ? PAGE_OFFSET : __IA64_UNCACHED_OFFSET; | 
 | 285 | 	for (k = kern_memmap; k->start != ~0UL; k++) { | 
 | 286 | 		if (k->attribute != attr) | 
 | 287 | 			continue; | 
 | 288 | 		start = PAGE_ALIGN(k->start); | 
 | 289 | 		end = (k->start + (k->num_pages << EFI_PAGE_SHIFT)) & PAGE_MASK; | 
 | 290 | 		if (start < end) | 
 | 291 | 			if ((*callback)(start + voff, end + voff, arg) < 0) | 
 | 292 | 				return; | 
 | 293 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 294 | } | 
 | 295 |  | 
 | 296 | /* | 
 | 297 |  * Walks the EFI memory map and calls CALLBACK once for each EFI memory descriptor that | 
 | 298 |  * has memory that is available for OS use. | 
 | 299 |  */ | 
 | 300 | void | 
 | 301 | efi_memmap_walk (efi_freemem_callback_t callback, void *arg) | 
 | 302 | { | 
| Tony Luck | d8c97d5 | 2005-09-08 12:39:59 -0700 | [diff] [blame] | 303 | 	walk(callback, arg, EFI_MEMORY_WB); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 304 | } | 
 | 305 |  | 
 | 306 | /* | 
| Tony Luck | d8c97d5 | 2005-09-08 12:39:59 -0700 | [diff] [blame] | 307 |  * Walks the EFI memory map and calls CALLBACK once for each EFI memory descriptor that | 
 | 308 |  * has memory that is available for uncached allocator. | 
| Jes Sorensen | f14f75b | 2005-06-21 17:15:02 -0700 | [diff] [blame] | 309 |  */ | 
| Tony Luck | d8c97d5 | 2005-09-08 12:39:59 -0700 | [diff] [blame] | 310 | void | 
 | 311 | efi_memmap_walk_uc (efi_freemem_callback_t callback, void *arg) | 
| Jes Sorensen | f14f75b | 2005-06-21 17:15:02 -0700 | [diff] [blame] | 312 | { | 
| Tony Luck | d8c97d5 | 2005-09-08 12:39:59 -0700 | [diff] [blame] | 313 | 	walk(callback, arg, EFI_MEMORY_UC); | 
| Jes Sorensen | f14f75b | 2005-06-21 17:15:02 -0700 | [diff] [blame] | 314 | } | 
 | 315 |  | 
| Jes Sorensen | f14f75b | 2005-06-21 17:15:02 -0700 | [diff] [blame] | 316 | /* | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 317 |  * Look for the PAL_CODE region reported by EFI and maps it using an | 
 | 318 |  * ITR to enable safe PAL calls in virtual mode.  See IA-64 Processor | 
 | 319 |  * Abstraction Layer chapter 11 in ADAG | 
 | 320 |  */ | 
 | 321 |  | 
 | 322 | void * | 
 | 323 | efi_get_pal_addr (void) | 
 | 324 | { | 
 | 325 | 	void *efi_map_start, *efi_map_end, *p; | 
 | 326 | 	efi_memory_desc_t *md; | 
 | 327 | 	u64 efi_desc_size; | 
 | 328 | 	int pal_code_count = 0; | 
 | 329 | 	u64 vaddr, mask; | 
 | 330 |  | 
 | 331 | 	efi_map_start = __va(ia64_boot_param->efi_memmap); | 
 | 332 | 	efi_map_end   = efi_map_start + ia64_boot_param->efi_memmap_size; | 
 | 333 | 	efi_desc_size = ia64_boot_param->efi_memdesc_size; | 
 | 334 |  | 
 | 335 | 	for (p = efi_map_start; p < efi_map_end; p += efi_desc_size) { | 
 | 336 | 		md = p; | 
 | 337 | 		if (md->type != EFI_PAL_CODE) | 
 | 338 | 			continue; | 
 | 339 |  | 
 | 340 | 		if (++pal_code_count > 1) { | 
 | 341 | 			printk(KERN_ERR "Too many EFI Pal Code memory ranges, dropped @ %lx\n", | 
 | 342 | 			       md->phys_addr); | 
 | 343 | 			continue; | 
 | 344 | 		} | 
 | 345 | 		/* | 
 | 346 | 		 * The only ITLB entry in region 7 that is used is the one installed by | 
 | 347 | 		 * __start().  That entry covers a 64MB range. | 
 | 348 | 		 */ | 
 | 349 | 		mask  = ~((1 << KERNEL_TR_PAGE_SHIFT) - 1); | 
 | 350 | 		vaddr = PAGE_OFFSET + md->phys_addr; | 
 | 351 |  | 
 | 352 | 		/* | 
 | 353 | 		 * We must check that the PAL mapping won't overlap with the kernel | 
 | 354 | 		 * mapping. | 
 | 355 | 		 * | 
 | 356 | 		 * PAL code is guaranteed to be aligned on a power of 2 between 4k and | 
 | 357 | 		 * 256KB and that only one ITR is needed to map it. This implies that the | 
 | 358 | 		 * PAL code is always aligned on its size, i.e., the closest matching page | 
 | 359 | 		 * size supported by the TLB. Therefore PAL code is guaranteed never to | 
 | 360 | 		 * cross a 64MB unless it is bigger than 64MB (very unlikely!).  So for | 
 | 361 | 		 * now the following test is enough to determine whether or not we need a | 
 | 362 | 		 * dedicated ITR for the PAL code. | 
 | 363 | 		 */ | 
 | 364 | 		if ((vaddr & mask) == (KERNEL_START & mask)) { | 
 | 365 | 			printk(KERN_INFO "%s: no need to install ITR for PAL code\n", | 
 | 366 | 			       __FUNCTION__); | 
 | 367 | 			continue; | 
 | 368 | 		} | 
 | 369 |  | 
 | 370 | 		if (md->num_pages << EFI_PAGE_SHIFT > IA64_GRANULE_SIZE) | 
 | 371 | 			panic("Woah!  PAL code size bigger than a granule!"); | 
 | 372 |  | 
 | 373 | #if EFI_DEBUG | 
 | 374 | 		mask  = ~((1 << IA64_GRANULE_SHIFT) - 1); | 
 | 375 |  | 
 | 376 | 		printk(KERN_INFO "CPU %d: mapping PAL code [0x%lx-0x%lx) into [0x%lx-0x%lx)\n", | 
 | 377 | 			smp_processor_id(), md->phys_addr, | 
 | 378 | 			md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT), | 
 | 379 | 			vaddr & mask, (vaddr & mask) + IA64_GRANULE_SIZE); | 
 | 380 | #endif | 
 | 381 | 		return __va(md->phys_addr); | 
 | 382 | 	} | 
| Horms | 9473252 | 2007-02-05 10:17:38 +0900 | [diff] [blame] | 383 | 	printk(KERN_WARNING "%s: no PAL-code memory-descriptor found\n", | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 384 | 	       __FUNCTION__); | 
 | 385 | 	return NULL; | 
 | 386 | } | 
 | 387 |  | 
 | 388 | void | 
 | 389 | efi_map_pal_code (void) | 
 | 390 | { | 
 | 391 | 	void *pal_vaddr = efi_get_pal_addr (); | 
 | 392 | 	u64 psr; | 
 | 393 |  | 
 | 394 | 	if (!pal_vaddr) | 
 | 395 | 		return; | 
 | 396 |  | 
 | 397 | 	/* | 
 | 398 | 	 * Cannot write to CRx with PSR.ic=1 | 
 | 399 | 	 */ | 
 | 400 | 	psr = ia64_clear_ic(); | 
 | 401 | 	ia64_itr(0x1, IA64_TR_PALCODE, GRANULEROUNDDOWN((unsigned long) pal_vaddr), | 
 | 402 | 		 pte_val(pfn_pte(__pa(pal_vaddr) >> PAGE_SHIFT, PAGE_KERNEL)), | 
 | 403 | 		 IA64_GRANULE_SHIFT); | 
 | 404 | 	ia64_set_psr(psr);		/* restore psr */ | 
 | 405 | 	ia64_srlz_i(); | 
 | 406 | } | 
 | 407 |  | 
 | 408 | void __init | 
 | 409 | efi_init (void) | 
 | 410 | { | 
 | 411 | 	void *efi_map_start, *efi_map_end; | 
 | 412 | 	efi_config_table_t *config_tables; | 
 | 413 | 	efi_char16_t *c16; | 
 | 414 | 	u64 efi_desc_size; | 
| Zou Nan hai | 9d78f43 | 2006-02-07 11:35:46 +0800 | [diff] [blame] | 415 | 	char *cp, vendor[100] = "unknown"; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 416 | 	int i; | 
 | 417 |  | 
 | 418 | 	/* it's too early to be able to use the standard kernel command line support... */ | 
| Alon Bar-Lev | a8d91b8 | 2007-02-12 00:54:12 -0800 | [diff] [blame] | 419 | 	for (cp = boot_command_line; *cp; ) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 420 | 		if (memcmp(cp, "mem=", 4) == 0) { | 
| Zou Nan hai | 9d78f43 | 2006-02-07 11:35:46 +0800 | [diff] [blame] | 421 | 			mem_limit = memparse(cp + 4, &cp); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 422 | 		} else if (memcmp(cp, "max_addr=", 9) == 0) { | 
| Zou Nan hai | 9d78f43 | 2006-02-07 11:35:46 +0800 | [diff] [blame] | 423 | 			max_addr = GRANULEROUNDDOWN(memparse(cp + 9, &cp)); | 
| Zou Nan hai | a7956113 | 2006-12-07 09:51:35 -0800 | [diff] [blame] | 424 | 		} else if (memcmp(cp, "min_addr=", 9) == 0) { | 
 | 425 | 			min_addr = GRANULEROUNDDOWN(memparse(cp + 9, &cp)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 426 | 		} else { | 
 | 427 | 			while (*cp != ' ' && *cp) | 
 | 428 | 				++cp; | 
 | 429 | 			while (*cp == ' ') | 
 | 430 | 				++cp; | 
 | 431 | 		} | 
 | 432 | 	} | 
| Zou Nan hai | a7956113 | 2006-12-07 09:51:35 -0800 | [diff] [blame] | 433 | 	if (min_addr != 0UL) | 
 | 434 | 		printk(KERN_INFO "Ignoring memory below %luMB\n", min_addr >> 20); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 435 | 	if (max_addr != ~0UL) | 
 | 436 | 		printk(KERN_INFO "Ignoring memory above %luMB\n", max_addr >> 20); | 
 | 437 |  | 
 | 438 | 	efi.systab = __va(ia64_boot_param->efi_systab); | 
 | 439 |  | 
 | 440 | 	/* | 
 | 441 | 	 * Verify the EFI Table | 
 | 442 | 	 */ | 
 | 443 | 	if (efi.systab == NULL) | 
 | 444 | 		panic("Woah! Can't find EFI system table.\n"); | 
 | 445 | 	if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) | 
 | 446 | 		panic("Woah! EFI system table signature incorrect\n"); | 
 | 447 | 	if ((efi.systab->hdr.revision ^ EFI_SYSTEM_TABLE_REVISION) >> 16 != 0) | 
 | 448 | 		printk(KERN_WARNING "Warning: EFI system table major version mismatch: " | 
 | 449 | 		       "got %d.%02d, expected %d.%02d\n", | 
 | 450 | 		       efi.systab->hdr.revision >> 16, efi.systab->hdr.revision & 0xffff, | 
 | 451 | 		       EFI_SYSTEM_TABLE_REVISION >> 16, EFI_SYSTEM_TABLE_REVISION & 0xffff); | 
 | 452 |  | 
 | 453 | 	config_tables = __va(efi.systab->tables); | 
 | 454 |  | 
 | 455 | 	/* Show what we know for posterity */ | 
 | 456 | 	c16 = __va(efi.systab->fw_vendor); | 
 | 457 | 	if (c16) { | 
| Zou Nan hai | ecdd5da | 2006-02-07 11:25:55 +0800 | [diff] [blame] | 458 | 		for (i = 0;i < (int) sizeof(vendor) - 1 && *c16; ++i) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 459 | 			vendor[i] = *c16++; | 
 | 460 | 		vendor[i] = '\0'; | 
 | 461 | 	} | 
 | 462 |  | 
 | 463 | 	printk(KERN_INFO "EFI v%u.%.02u by %s:", | 
 | 464 | 	       efi.systab->hdr.revision >> 16, efi.systab->hdr.revision & 0xffff, vendor); | 
 | 465 |  | 
| Bjorn Helgaas | b2c99e3 | 2006-03-26 01:37:08 -0800 | [diff] [blame] | 466 | 	efi.mps        = EFI_INVALID_TABLE_ADDR; | 
 | 467 | 	efi.acpi       = EFI_INVALID_TABLE_ADDR; | 
 | 468 | 	efi.acpi20     = EFI_INVALID_TABLE_ADDR; | 
 | 469 | 	efi.smbios     = EFI_INVALID_TABLE_ADDR; | 
 | 470 | 	efi.sal_systab = EFI_INVALID_TABLE_ADDR; | 
 | 471 | 	efi.boot_info  = EFI_INVALID_TABLE_ADDR; | 
 | 472 | 	efi.hcdp       = EFI_INVALID_TABLE_ADDR; | 
 | 473 | 	efi.uga        = EFI_INVALID_TABLE_ADDR; | 
 | 474 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 475 | 	for (i = 0; i < (int) efi.systab->nr_tables; i++) { | 
 | 476 | 		if (efi_guidcmp(config_tables[i].guid, MPS_TABLE_GUID) == 0) { | 
| Bjorn Helgaas | b2c99e3 | 2006-03-26 01:37:08 -0800 | [diff] [blame] | 477 | 			efi.mps = config_tables[i].table; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 478 | 			printk(" MPS=0x%lx", config_tables[i].table); | 
 | 479 | 		} else if (efi_guidcmp(config_tables[i].guid, ACPI_20_TABLE_GUID) == 0) { | 
| Bjorn Helgaas | b2c99e3 | 2006-03-26 01:37:08 -0800 | [diff] [blame] | 480 | 			efi.acpi20 = config_tables[i].table; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 481 | 			printk(" ACPI 2.0=0x%lx", config_tables[i].table); | 
 | 482 | 		} else if (efi_guidcmp(config_tables[i].guid, ACPI_TABLE_GUID) == 0) { | 
| Bjorn Helgaas | b2c99e3 | 2006-03-26 01:37:08 -0800 | [diff] [blame] | 483 | 			efi.acpi = config_tables[i].table; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 484 | 			printk(" ACPI=0x%lx", config_tables[i].table); | 
 | 485 | 		} else if (efi_guidcmp(config_tables[i].guid, SMBIOS_TABLE_GUID) == 0) { | 
| Bjorn Helgaas | b2c99e3 | 2006-03-26 01:37:08 -0800 | [diff] [blame] | 486 | 			efi.smbios = config_tables[i].table; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 487 | 			printk(" SMBIOS=0x%lx", config_tables[i].table); | 
 | 488 | 		} else if (efi_guidcmp(config_tables[i].guid, SAL_SYSTEM_TABLE_GUID) == 0) { | 
| Bjorn Helgaas | b2c99e3 | 2006-03-26 01:37:08 -0800 | [diff] [blame] | 489 | 			efi.sal_systab = config_tables[i].table; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 490 | 			printk(" SALsystab=0x%lx", config_tables[i].table); | 
 | 491 | 		} else if (efi_guidcmp(config_tables[i].guid, HCDP_TABLE_GUID) == 0) { | 
| Bjorn Helgaas | b2c99e3 | 2006-03-26 01:37:08 -0800 | [diff] [blame] | 492 | 			efi.hcdp = config_tables[i].table; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 493 | 			printk(" HCDP=0x%lx", config_tables[i].table); | 
 | 494 | 		} | 
 | 495 | 	} | 
 | 496 | 	printk("\n"); | 
 | 497 |  | 
 | 498 | 	runtime = __va(efi.systab->runtime); | 
 | 499 | 	efi.get_time = phys_get_time; | 
 | 500 | 	efi.set_time = phys_set_time; | 
 | 501 | 	efi.get_wakeup_time = phys_get_wakeup_time; | 
 | 502 | 	efi.set_wakeup_time = phys_set_wakeup_time; | 
 | 503 | 	efi.get_variable = phys_get_variable; | 
 | 504 | 	efi.get_next_variable = phys_get_next_variable; | 
 | 505 | 	efi.set_variable = phys_set_variable; | 
 | 506 | 	efi.get_next_high_mono_count = phys_get_next_high_mono_count; | 
 | 507 | 	efi.reset_system = phys_reset_system; | 
 | 508 |  | 
 | 509 | 	efi_map_start = __va(ia64_boot_param->efi_memmap); | 
 | 510 | 	efi_map_end   = efi_map_start + ia64_boot_param->efi_memmap_size; | 
 | 511 | 	efi_desc_size = ia64_boot_param->efi_memdesc_size; | 
 | 512 |  | 
 | 513 | #if EFI_DEBUG | 
 | 514 | 	/* print EFI memory map: */ | 
 | 515 | 	{ | 
 | 516 | 		efi_memory_desc_t *md; | 
 | 517 | 		void *p; | 
 | 518 |  | 
 | 519 | 		for (i = 0, p = efi_map_start; p < efi_map_end; ++i, p += efi_desc_size) { | 
 | 520 | 			md = p; | 
 | 521 | 			printk("mem%02u: type=%u, attr=0x%lx, range=[0x%016lx-0x%016lx) (%luMB)\n", | 
 | 522 | 			       i, md->type, md->attribute, md->phys_addr, | 
 | 523 | 			       md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT), | 
 | 524 | 			       md->num_pages >> (20 - EFI_PAGE_SHIFT)); | 
 | 525 | 		} | 
 | 526 | 	} | 
 | 527 | #endif | 
 | 528 |  | 
 | 529 | 	efi_map_pal_code(); | 
 | 530 | 	efi_enter_virtual_mode(); | 
 | 531 | } | 
 | 532 |  | 
 | 533 | void | 
 | 534 | efi_enter_virtual_mode (void) | 
 | 535 | { | 
 | 536 | 	void *efi_map_start, *efi_map_end, *p; | 
 | 537 | 	efi_memory_desc_t *md; | 
 | 538 | 	efi_status_t status; | 
 | 539 | 	u64 efi_desc_size; | 
 | 540 |  | 
 | 541 | 	efi_map_start = __va(ia64_boot_param->efi_memmap); | 
 | 542 | 	efi_map_end   = efi_map_start + ia64_boot_param->efi_memmap_size; | 
 | 543 | 	efi_desc_size = ia64_boot_param->efi_memdesc_size; | 
 | 544 |  | 
 | 545 | 	for (p = efi_map_start; p < efi_map_end; p += efi_desc_size) { | 
 | 546 | 		md = p; | 
 | 547 | 		if (md->attribute & EFI_MEMORY_RUNTIME) { | 
 | 548 | 			/* | 
 | 549 | 			 * Some descriptors have multiple bits set, so the order of | 
 | 550 | 			 * the tests is relevant. | 
 | 551 | 			 */ | 
 | 552 | 			if (md->attribute & EFI_MEMORY_WB) { | 
 | 553 | 				md->virt_addr = (u64) __va(md->phys_addr); | 
 | 554 | 			} else if (md->attribute & EFI_MEMORY_UC) { | 
 | 555 | 				md->virt_addr = (u64) ioremap(md->phys_addr, 0); | 
 | 556 | 			} else if (md->attribute & EFI_MEMORY_WC) { | 
 | 557 | #if 0 | 
 | 558 | 				md->virt_addr = ia64_remap(md->phys_addr, (_PAGE_A | _PAGE_P | 
 | 559 | 									   | _PAGE_D | 
 | 560 | 									   | _PAGE_MA_WC | 
 | 561 | 									   | _PAGE_PL_0 | 
 | 562 | 									   | _PAGE_AR_RW)); | 
 | 563 | #else | 
 | 564 | 				printk(KERN_INFO "EFI_MEMORY_WC mapping\n"); | 
 | 565 | 				md->virt_addr = (u64) ioremap(md->phys_addr, 0); | 
 | 566 | #endif | 
 | 567 | 			} else if (md->attribute & EFI_MEMORY_WT) { | 
 | 568 | #if 0 | 
 | 569 | 				md->virt_addr = ia64_remap(md->phys_addr, (_PAGE_A | _PAGE_P | 
 | 570 | 									   | _PAGE_D | _PAGE_MA_WT | 
 | 571 | 									   | _PAGE_PL_0 | 
 | 572 | 									   | _PAGE_AR_RW)); | 
 | 573 | #else | 
 | 574 | 				printk(KERN_INFO "EFI_MEMORY_WT mapping\n"); | 
 | 575 | 				md->virt_addr = (u64) ioremap(md->phys_addr, 0); | 
 | 576 | #endif | 
 | 577 | 			} | 
 | 578 | 		} | 
 | 579 | 	} | 
 | 580 |  | 
 | 581 | 	status = efi_call_phys(__va(runtime->set_virtual_address_map), | 
 | 582 | 			       ia64_boot_param->efi_memmap_size, | 
 | 583 | 			       efi_desc_size, ia64_boot_param->efi_memdesc_version, | 
 | 584 | 			       ia64_boot_param->efi_memmap); | 
 | 585 | 	if (status != EFI_SUCCESS) { | 
 | 586 | 		printk(KERN_WARNING "warning: unable to switch EFI into virtual mode " | 
 | 587 | 		       "(status=%lu)\n", status); | 
 | 588 | 		return; | 
 | 589 | 	} | 
 | 590 |  | 
 | 591 | 	/* | 
 | 592 | 	 * Now that EFI is in virtual mode, we call the EFI functions more efficiently: | 
 | 593 | 	 */ | 
 | 594 | 	efi.get_time = virt_get_time; | 
 | 595 | 	efi.set_time = virt_set_time; | 
 | 596 | 	efi.get_wakeup_time = virt_get_wakeup_time; | 
 | 597 | 	efi.set_wakeup_time = virt_set_wakeup_time; | 
 | 598 | 	efi.get_variable = virt_get_variable; | 
 | 599 | 	efi.get_next_variable = virt_get_next_variable; | 
 | 600 | 	efi.set_variable = virt_set_variable; | 
 | 601 | 	efi.get_next_high_mono_count = virt_get_next_high_mono_count; | 
 | 602 | 	efi.reset_system = virt_reset_system; | 
 | 603 | } | 
 | 604 |  | 
 | 605 | /* | 
 | 606 |  * Walk the EFI memory map looking for the I/O port range.  There can only be one entry of | 
 | 607 |  * this type, other I/O port ranges should be described via ACPI. | 
 | 608 |  */ | 
 | 609 | u64 | 
 | 610 | efi_get_iobase (void) | 
 | 611 | { | 
 | 612 | 	void *efi_map_start, *efi_map_end, *p; | 
 | 613 | 	efi_memory_desc_t *md; | 
 | 614 | 	u64 efi_desc_size; | 
 | 615 |  | 
 | 616 | 	efi_map_start = __va(ia64_boot_param->efi_memmap); | 
 | 617 | 	efi_map_end   = efi_map_start + ia64_boot_param->efi_memmap_size; | 
 | 618 | 	efi_desc_size = ia64_boot_param->efi_memdesc_size; | 
 | 619 |  | 
 | 620 | 	for (p = efi_map_start; p < efi_map_end; p += efi_desc_size) { | 
 | 621 | 		md = p; | 
 | 622 | 		if (md->type == EFI_MEMORY_MAPPED_IO_PORT_SPACE) { | 
 | 623 | 			if (md->attribute & EFI_MEMORY_UC) | 
 | 624 | 				return md->phys_addr; | 
 | 625 | 		} | 
 | 626 | 	} | 
 | 627 | 	return 0; | 
 | 628 | } | 
 | 629 |  | 
| Bjorn Helgaas | 32e62c6 | 2006-05-05 17:19:50 -0600 | [diff] [blame] | 630 | static struct kern_memdesc * | 
 | 631 | kern_memory_descriptor (unsigned long phys_addr) | 
 | 632 | { | 
 | 633 | 	struct kern_memdesc *md; | 
 | 634 |  | 
 | 635 | 	for (md = kern_memmap; md->start != ~0UL; md++) { | 
 | 636 | 		if (phys_addr - md->start < (md->num_pages << EFI_PAGE_SHIFT)) | 
 | 637 | 			 return md; | 
 | 638 | 	} | 
| Keith Owens | e037cda | 2006-07-17 15:41:59 +1000 | [diff] [blame] | 639 | 	return NULL; | 
| Bjorn Helgaas | 32e62c6 | 2006-05-05 17:19:50 -0600 | [diff] [blame] | 640 | } | 
 | 641 |  | 
| Bjorn Helgaas | 80851ef | 2006-01-08 01:04:13 -0800 | [diff] [blame] | 642 | static efi_memory_desc_t * | 
 | 643 | efi_memory_descriptor (unsigned long phys_addr) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 644 | { | 
 | 645 | 	void *efi_map_start, *efi_map_end, *p; | 
 | 646 | 	efi_memory_desc_t *md; | 
 | 647 | 	u64 efi_desc_size; | 
 | 648 |  | 
 | 649 | 	efi_map_start = __va(ia64_boot_param->efi_memmap); | 
 | 650 | 	efi_map_end   = efi_map_start + ia64_boot_param->efi_memmap_size; | 
 | 651 | 	efi_desc_size = ia64_boot_param->efi_memdesc_size; | 
 | 652 |  | 
 | 653 | 	for (p = efi_map_start; p < efi_map_end; p += efi_desc_size) { | 
 | 654 | 		md = p; | 
 | 655 |  | 
 | 656 | 		if (phys_addr - md->phys_addr < (md->num_pages << EFI_PAGE_SHIFT)) | 
| Bjorn Helgaas | 80851ef | 2006-01-08 01:04:13 -0800 | [diff] [blame] | 657 | 			 return md; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 658 | 	} | 
| Keith Owens | e037cda | 2006-07-17 15:41:59 +1000 | [diff] [blame] | 659 | 	return NULL; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 660 | } | 
 | 661 |  | 
| Bjorn Helgaas | 80851ef | 2006-01-08 01:04:13 -0800 | [diff] [blame] | 662 | u32 | 
 | 663 | efi_mem_type (unsigned long phys_addr) | 
 | 664 | { | 
 | 665 | 	efi_memory_desc_t *md = efi_memory_descriptor(phys_addr); | 
 | 666 |  | 
 | 667 | 	if (md) | 
 | 668 | 		return md->type; | 
 | 669 | 	return 0; | 
 | 670 | } | 
 | 671 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 672 | u64 | 
 | 673 | efi_mem_attributes (unsigned long phys_addr) | 
 | 674 | { | 
| Bjorn Helgaas | 80851ef | 2006-01-08 01:04:13 -0800 | [diff] [blame] | 675 | 	efi_memory_desc_t *md = efi_memory_descriptor(phys_addr); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 676 |  | 
| Bjorn Helgaas | 80851ef | 2006-01-08 01:04:13 -0800 | [diff] [blame] | 677 | 	if (md) | 
 | 678 | 		return md->attribute; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 679 | 	return 0; | 
 | 680 | } | 
 | 681 | EXPORT_SYMBOL(efi_mem_attributes); | 
 | 682 |  | 
| Bjorn Helgaas | 32e62c6 | 2006-05-05 17:19:50 -0600 | [diff] [blame] | 683 | u64 | 
 | 684 | efi_mem_attribute (unsigned long phys_addr, unsigned long size) | 
| Bjorn Helgaas | 80851ef | 2006-01-08 01:04:13 -0800 | [diff] [blame] | 685 | { | 
| Bjorn Helgaas | 136939a | 2006-03-26 01:37:05 -0800 | [diff] [blame] | 686 | 	unsigned long end = phys_addr + size; | 
| Bjorn Helgaas | 80851ef | 2006-01-08 01:04:13 -0800 | [diff] [blame] | 687 | 	efi_memory_desc_t *md = efi_memory_descriptor(phys_addr); | 
| Bjorn Helgaas | 32e62c6 | 2006-05-05 17:19:50 -0600 | [diff] [blame] | 688 | 	u64 attr; | 
 | 689 |  | 
 | 690 | 	if (!md) | 
 | 691 | 		return 0; | 
| Bjorn Helgaas | 80851ef | 2006-01-08 01:04:13 -0800 | [diff] [blame] | 692 |  | 
| Bjorn Helgaas | 136939a | 2006-03-26 01:37:05 -0800 | [diff] [blame] | 693 | 	/* | 
| Bjorn Helgaas | 32e62c6 | 2006-05-05 17:19:50 -0600 | [diff] [blame] | 694 | 	 * EFI_MEMORY_RUNTIME is not a memory attribute; it just tells | 
 | 695 | 	 * the kernel that firmware needs this region mapped. | 
| Bjorn Helgaas | 136939a | 2006-03-26 01:37:05 -0800 | [diff] [blame] | 696 | 	 */ | 
| Bjorn Helgaas | 32e62c6 | 2006-05-05 17:19:50 -0600 | [diff] [blame] | 697 | 	attr = md->attribute & ~EFI_MEMORY_RUNTIME; | 
| Bjorn Helgaas | 80851ef | 2006-01-08 01:04:13 -0800 | [diff] [blame] | 698 | 	do { | 
| Bjorn Helgaas | 136939a | 2006-03-26 01:37:05 -0800 | [diff] [blame] | 699 | 		unsigned long md_end = efi_md_end(md); | 
 | 700 |  | 
 | 701 | 		if (end <= md_end) | 
| Bjorn Helgaas | 32e62c6 | 2006-05-05 17:19:50 -0600 | [diff] [blame] | 702 | 			return attr; | 
| Bjorn Helgaas | 80851ef | 2006-01-08 01:04:13 -0800 | [diff] [blame] | 703 |  | 
 | 704 | 		md = efi_memory_descriptor(md_end); | 
| Bjorn Helgaas | 32e62c6 | 2006-05-05 17:19:50 -0600 | [diff] [blame] | 705 | 		if (!md || (md->attribute & ~EFI_MEMORY_RUNTIME) != attr) | 
| Bjorn Helgaas | 136939a | 2006-03-26 01:37:05 -0800 | [diff] [blame] | 706 | 			return 0; | 
| Bjorn Helgaas | 80851ef | 2006-01-08 01:04:13 -0800 | [diff] [blame] | 707 | 	} while (md); | 
 | 708 | 	return 0; | 
 | 709 | } | 
 | 710 |  | 
| Bjorn Helgaas | 32e62c6 | 2006-05-05 17:19:50 -0600 | [diff] [blame] | 711 | u64 | 
 | 712 | kern_mem_attribute (unsigned long phys_addr, unsigned long size) | 
 | 713 | { | 
 | 714 | 	unsigned long end = phys_addr + size; | 
 | 715 | 	struct kern_memdesc *md; | 
 | 716 | 	u64 attr; | 
 | 717 |  | 
 | 718 | 	/* | 
 | 719 | 	 * This is a hack for ioremap calls before we set up kern_memmap. | 
 | 720 | 	 * Maybe we should do efi_memmap_init() earlier instead. | 
 | 721 | 	 */ | 
 | 722 | 	if (!kern_memmap) { | 
 | 723 | 		attr = efi_mem_attribute(phys_addr, size); | 
 | 724 | 		if (attr & EFI_MEMORY_WB) | 
 | 725 | 			return EFI_MEMORY_WB; | 
 | 726 | 		return 0; | 
 | 727 | 	} | 
 | 728 |  | 
 | 729 | 	md = kern_memory_descriptor(phys_addr); | 
 | 730 | 	if (!md) | 
 | 731 | 		return 0; | 
 | 732 |  | 
 | 733 | 	attr = md->attribute; | 
 | 734 | 	do { | 
 | 735 | 		unsigned long md_end = kmd_end(md); | 
 | 736 |  | 
 | 737 | 		if (end <= md_end) | 
 | 738 | 			return attr; | 
 | 739 |  | 
 | 740 | 		md = kern_memory_descriptor(md_end); | 
 | 741 | 		if (!md || md->attribute != attr) | 
 | 742 | 			return 0; | 
 | 743 | 	} while (md); | 
 | 744 | 	return 0; | 
 | 745 | } | 
 | 746 | EXPORT_SYMBOL(kern_mem_attribute); | 
 | 747 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 748 | int | 
| Bjorn Helgaas | 136939a | 2006-03-26 01:37:05 -0800 | [diff] [blame] | 749 | valid_phys_addr_range (unsigned long phys_addr, unsigned long size) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 750 | { | 
| Bjorn Helgaas | 32e62c6 | 2006-05-05 17:19:50 -0600 | [diff] [blame] | 751 | 	u64 attr; | 
 | 752 |  | 
 | 753 | 	/* | 
 | 754 | 	 * /dev/mem reads and writes use copy_to_user(), which implicitly | 
 | 755 | 	 * uses a granule-sized kernel identity mapping.  It's really | 
 | 756 | 	 * only safe to do this for regions in kern_memmap.  For more | 
 | 757 | 	 * details, see Documentation/ia64/aliasing.txt. | 
 | 758 | 	 */ | 
 | 759 | 	attr = kern_mem_attribute(phys_addr, size); | 
 | 760 | 	if (attr & EFI_MEMORY_WB || attr & EFI_MEMORY_UC) | 
 | 761 | 		return 1; | 
 | 762 | 	return 0; | 
| Bjorn Helgaas | 80851ef | 2006-01-08 01:04:13 -0800 | [diff] [blame] | 763 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 764 |  | 
| Bjorn Helgaas | 80851ef | 2006-01-08 01:04:13 -0800 | [diff] [blame] | 765 | int | 
| Lennert Buytenhek | 06c67be | 2006-07-10 04:45:27 -0700 | [diff] [blame] | 766 | valid_mmap_phys_addr_range (unsigned long pfn, unsigned long size) | 
| Bjorn Helgaas | 80851ef | 2006-01-08 01:04:13 -0800 | [diff] [blame] | 767 | { | 
| Bjorn Helgaas | 32e62c6 | 2006-05-05 17:19:50 -0600 | [diff] [blame] | 768 | 	/* | 
 | 769 | 	 * MMIO regions are often missing from the EFI memory map. | 
 | 770 | 	 * We must allow mmap of them for programs like X, so we | 
 | 771 | 	 * currently can't do any useful validation. | 
 | 772 | 	 */ | 
 | 773 | 	return 1; | 
 | 774 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 775 |  | 
| Bjorn Helgaas | 32e62c6 | 2006-05-05 17:19:50 -0600 | [diff] [blame] | 776 | pgprot_t | 
 | 777 | phys_mem_access_prot(struct file *file, unsigned long pfn, unsigned long size, | 
 | 778 | 		     pgprot_t vma_prot) | 
 | 779 | { | 
 | 780 | 	unsigned long phys_addr = pfn << PAGE_SHIFT; | 
 | 781 | 	u64 attr; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 782 |  | 
| Bjorn Helgaas | 32e62c6 | 2006-05-05 17:19:50 -0600 | [diff] [blame] | 783 | 	/* | 
 | 784 | 	 * For /dev/mem mmap, we use user mappings, but if the region is | 
 | 785 | 	 * in kern_memmap (and hence may be covered by a kernel mapping), | 
 | 786 | 	 * we must use the same attribute as the kernel mapping. | 
 | 787 | 	 */ | 
 | 788 | 	attr = kern_mem_attribute(phys_addr, size); | 
 | 789 | 	if (attr & EFI_MEMORY_WB) | 
 | 790 | 		return pgprot_cacheable(vma_prot); | 
 | 791 | 	else if (attr & EFI_MEMORY_UC) | 
 | 792 | 		return pgprot_noncached(vma_prot); | 
 | 793 |  | 
 | 794 | 	/* | 
 | 795 | 	 * Some chipsets don't support UC access to memory.  If | 
 | 796 | 	 * WB is supported, we prefer that. | 
 | 797 | 	 */ | 
 | 798 | 	if (efi_mem_attribute(phys_addr, size) & EFI_MEMORY_WB) | 
 | 799 | 		return pgprot_cacheable(vma_prot); | 
 | 800 |  | 
 | 801 | 	return pgprot_noncached(vma_prot); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 802 | } | 
 | 803 |  | 
 | 804 | int __init | 
 | 805 | efi_uart_console_only(void) | 
 | 806 | { | 
 | 807 | 	efi_status_t status; | 
 | 808 | 	char *s, name[] = "ConOut"; | 
 | 809 | 	efi_guid_t guid = EFI_GLOBAL_VARIABLE_GUID; | 
 | 810 | 	efi_char16_t *utf16, name_utf16[32]; | 
 | 811 | 	unsigned char data[1024]; | 
 | 812 | 	unsigned long size = sizeof(data); | 
 | 813 | 	struct efi_generic_dev_path *hdr, *end_addr; | 
 | 814 | 	int uart = 0; | 
 | 815 |  | 
 | 816 | 	/* Convert to UTF-16 */ | 
 | 817 | 	utf16 = name_utf16; | 
 | 818 | 	s = name; | 
 | 819 | 	while (*s) | 
 | 820 | 		*utf16++ = *s++ & 0x7f; | 
 | 821 | 	*utf16 = 0; | 
 | 822 |  | 
 | 823 | 	status = efi.get_variable(name_utf16, &guid, NULL, &size, data); | 
 | 824 | 	if (status != EFI_SUCCESS) { | 
 | 825 | 		printk(KERN_ERR "No EFI %s variable?\n", name); | 
 | 826 | 		return 0; | 
 | 827 | 	} | 
 | 828 |  | 
 | 829 | 	hdr = (struct efi_generic_dev_path *) data; | 
 | 830 | 	end_addr = (struct efi_generic_dev_path *) ((u8 *) data + size); | 
 | 831 | 	while (hdr < end_addr) { | 
 | 832 | 		if (hdr->type == EFI_DEV_MSG && | 
 | 833 | 		    hdr->sub_type == EFI_DEV_MSG_UART) | 
 | 834 | 			uart = 1; | 
 | 835 | 		else if (hdr->type == EFI_DEV_END_PATH || | 
 | 836 | 			  hdr->type == EFI_DEV_END_PATH2) { | 
 | 837 | 			if (!uart) | 
 | 838 | 				return 0; | 
 | 839 | 			if (hdr->sub_type == EFI_DEV_END_ENTIRE) | 
 | 840 | 				return 1; | 
 | 841 | 			uart = 0; | 
 | 842 | 		} | 
 | 843 | 		hdr = (struct efi_generic_dev_path *) ((u8 *) hdr + hdr->length); | 
 | 844 | 	} | 
 | 845 | 	printk(KERN_ERR "Malformed %s value\n", name); | 
 | 846 | 	return 0; | 
 | 847 | } | 
| Tony Luck | d8c97d5 | 2005-09-08 12:39:59 -0700 | [diff] [blame] | 848 |  | 
| Tony Luck | d8c97d5 | 2005-09-08 12:39:59 -0700 | [diff] [blame] | 849 | /* | 
 | 850 |  * Look for the first granule aligned memory descriptor memory | 
 | 851 |  * that is big enough to hold EFI memory map. Make sure this | 
 | 852 |  * descriptor is atleast granule sized so it does not get trimmed | 
 | 853 |  */ | 
 | 854 | struct kern_memdesc * | 
 | 855 | find_memmap_space (void) | 
 | 856 | { | 
 | 857 | 	u64	contig_low=0, contig_high=0; | 
 | 858 | 	u64	as = 0, ae; | 
 | 859 | 	void *efi_map_start, *efi_map_end, *p, *q; | 
 | 860 | 	efi_memory_desc_t *md, *pmd = NULL, *check_md; | 
 | 861 | 	u64	space_needed, efi_desc_size; | 
 | 862 | 	unsigned long total_mem = 0; | 
 | 863 |  | 
 | 864 | 	efi_map_start = __va(ia64_boot_param->efi_memmap); | 
 | 865 | 	efi_map_end   = efi_map_start + ia64_boot_param->efi_memmap_size; | 
 | 866 | 	efi_desc_size = ia64_boot_param->efi_memdesc_size; | 
 | 867 |  | 
 | 868 | 	/* | 
 | 869 | 	 * Worst case: we need 3 kernel descriptors for each efi descriptor | 
 | 870 | 	 * (if every entry has a WB part in the middle, and UC head and tail), | 
 | 871 | 	 * plus one for the end marker. | 
 | 872 | 	 */ | 
 | 873 | 	space_needed = sizeof(kern_memdesc_t) * | 
 | 874 | 		(3 * (ia64_boot_param->efi_memmap_size/efi_desc_size) + 1); | 
 | 875 |  | 
 | 876 | 	for (p = efi_map_start; p < efi_map_end; pmd = md, p += efi_desc_size) { | 
 | 877 | 		md = p; | 
 | 878 | 		if (!efi_wb(md)) { | 
 | 879 | 			continue; | 
 | 880 | 		} | 
 | 881 | 		if (pmd == NULL || !efi_wb(pmd) || efi_md_end(pmd) != md->phys_addr) { | 
 | 882 | 			contig_low = GRANULEROUNDUP(md->phys_addr); | 
 | 883 | 			contig_high = efi_md_end(md); | 
 | 884 | 			for (q = p + efi_desc_size; q < efi_map_end; q += efi_desc_size) { | 
 | 885 | 				check_md = q; | 
 | 886 | 				if (!efi_wb(check_md)) | 
 | 887 | 					break; | 
 | 888 | 				if (contig_high != check_md->phys_addr) | 
 | 889 | 					break; | 
 | 890 | 				contig_high = efi_md_end(check_md); | 
 | 891 | 			} | 
 | 892 | 			contig_high = GRANULEROUNDDOWN(contig_high); | 
 | 893 | 		} | 
| Christoph Lameter | 66888a6 | 2006-12-04 14:58:35 -0800 | [diff] [blame] | 894 | 		if (!is_memory_available(md) || md->type == EFI_LOADER_DATA) | 
| Tony Luck | d8c97d5 | 2005-09-08 12:39:59 -0700 | [diff] [blame] | 895 | 			continue; | 
 | 896 |  | 
 | 897 | 		/* Round ends inward to granule boundaries */ | 
 | 898 | 		as = max(contig_low, md->phys_addr); | 
 | 899 | 		ae = min(contig_high, efi_md_end(md)); | 
 | 900 |  | 
| Zou Nan hai | a7956113 | 2006-12-07 09:51:35 -0800 | [diff] [blame] | 901 | 		/* keep within max_addr= and min_addr= command line arg */ | 
 | 902 | 		as = max(as, min_addr); | 
| Tony Luck | d8c97d5 | 2005-09-08 12:39:59 -0700 | [diff] [blame] | 903 | 		ae = min(ae, max_addr); | 
 | 904 | 		if (ae <= as) | 
 | 905 | 			continue; | 
 | 906 |  | 
 | 907 | 		/* avoid going over mem= command line arg */ | 
 | 908 | 		if (total_mem + (ae - as) > mem_limit) | 
 | 909 | 			ae -= total_mem + (ae - as) - mem_limit; | 
 | 910 |  | 
 | 911 | 		if (ae <= as) | 
 | 912 | 			continue; | 
 | 913 |  | 
 | 914 | 		if (ae - as > space_needed) | 
 | 915 | 			break; | 
 | 916 | 	} | 
 | 917 | 	if (p >= efi_map_end) | 
 | 918 | 		panic("Can't allocate space for kernel memory descriptors"); | 
 | 919 |  | 
 | 920 | 	return __va(as); | 
 | 921 | } | 
 | 922 |  | 
 | 923 | /* | 
 | 924 |  * Walk the EFI memory map and gather all memory available for kernel | 
 | 925 |  * to use.  We can allocate partial granules only if the unavailable | 
 | 926 |  * parts exist, and are WB. | 
 | 927 |  */ | 
 | 928 | void | 
 | 929 | efi_memmap_init(unsigned long *s, unsigned long *e) | 
 | 930 | { | 
| Keith Owens | e037cda | 2006-07-17 15:41:59 +1000 | [diff] [blame] | 931 | 	struct kern_memdesc *k, *prev = NULL; | 
| Tony Luck | d8c97d5 | 2005-09-08 12:39:59 -0700 | [diff] [blame] | 932 | 	u64	contig_low=0, contig_high=0; | 
 | 933 | 	u64	as, ae, lim; | 
 | 934 | 	void *efi_map_start, *efi_map_end, *p, *q; | 
 | 935 | 	efi_memory_desc_t *md, *pmd = NULL, *check_md; | 
 | 936 | 	u64	efi_desc_size; | 
 | 937 | 	unsigned long total_mem = 0; | 
 | 938 |  | 
 | 939 | 	k = kern_memmap = find_memmap_space(); | 
 | 940 |  | 
 | 941 | 	efi_map_start = __va(ia64_boot_param->efi_memmap); | 
 | 942 | 	efi_map_end   = efi_map_start + ia64_boot_param->efi_memmap_size; | 
 | 943 | 	efi_desc_size = ia64_boot_param->efi_memdesc_size; | 
 | 944 |  | 
 | 945 | 	for (p = efi_map_start; p < efi_map_end; pmd = md, p += efi_desc_size) { | 
 | 946 | 		md = p; | 
 | 947 | 		if (!efi_wb(md)) { | 
 | 948 | 			if (efi_uc(md) && (md->type == EFI_CONVENTIONAL_MEMORY || | 
 | 949 | 				    	   md->type == EFI_BOOT_SERVICES_DATA)) { | 
 | 950 | 				k->attribute = EFI_MEMORY_UC; | 
 | 951 | 				k->start = md->phys_addr; | 
 | 952 | 				k->num_pages = md->num_pages; | 
 | 953 | 				k++; | 
 | 954 | 			} | 
 | 955 | 			continue; | 
 | 956 | 		} | 
 | 957 | 		if (pmd == NULL || !efi_wb(pmd) || efi_md_end(pmd) != md->phys_addr) { | 
 | 958 | 			contig_low = GRANULEROUNDUP(md->phys_addr); | 
 | 959 | 			contig_high = efi_md_end(md); | 
 | 960 | 			for (q = p + efi_desc_size; q < efi_map_end; q += efi_desc_size) { | 
 | 961 | 				check_md = q; | 
 | 962 | 				if (!efi_wb(check_md)) | 
 | 963 | 					break; | 
 | 964 | 				if (contig_high != check_md->phys_addr) | 
 | 965 | 					break; | 
 | 966 | 				contig_high = efi_md_end(check_md); | 
 | 967 | 			} | 
 | 968 | 			contig_high = GRANULEROUNDDOWN(contig_high); | 
 | 969 | 		} | 
| Christoph Lameter | 66888a6 | 2006-12-04 14:58:35 -0800 | [diff] [blame] | 970 | 		if (!is_memory_available(md)) | 
| Tony Luck | d8c97d5 | 2005-09-08 12:39:59 -0700 | [diff] [blame] | 971 | 			continue; | 
 | 972 |  | 
 | 973 | 		/* | 
 | 974 | 		 * Round ends inward to granule boundaries | 
 | 975 | 		 * Give trimmings to uncached allocator | 
 | 976 | 		 */ | 
 | 977 | 		if (md->phys_addr < contig_low) { | 
 | 978 | 			lim = min(efi_md_end(md), contig_low); | 
 | 979 | 			if (efi_uc(md)) { | 
 | 980 | 				if (k > kern_memmap && (k-1)->attribute == EFI_MEMORY_UC && | 
 | 981 | 				    kmd_end(k-1) == md->phys_addr) { | 
 | 982 | 					(k-1)->num_pages += (lim - md->phys_addr) >> EFI_PAGE_SHIFT; | 
 | 983 | 				} else { | 
 | 984 | 					k->attribute = EFI_MEMORY_UC; | 
 | 985 | 					k->start = md->phys_addr; | 
 | 986 | 					k->num_pages = (lim - md->phys_addr) >> EFI_PAGE_SHIFT; | 
 | 987 | 					k++; | 
 | 988 | 				} | 
 | 989 | 			} | 
 | 990 | 			as = contig_low; | 
 | 991 | 		} else | 
 | 992 | 			as = md->phys_addr; | 
 | 993 |  | 
 | 994 | 		if (efi_md_end(md) > contig_high) { | 
 | 995 | 			lim = max(md->phys_addr, contig_high); | 
 | 996 | 			if (efi_uc(md)) { | 
 | 997 | 				if (lim == md->phys_addr && k > kern_memmap && | 
 | 998 | 				    (k-1)->attribute == EFI_MEMORY_UC && | 
 | 999 | 				    kmd_end(k-1) == md->phys_addr) { | 
 | 1000 | 					(k-1)->num_pages += md->num_pages; | 
 | 1001 | 				} else { | 
 | 1002 | 					k->attribute = EFI_MEMORY_UC; | 
 | 1003 | 					k->start = lim; | 
 | 1004 | 					k->num_pages = (efi_md_end(md) - lim) >> EFI_PAGE_SHIFT; | 
 | 1005 | 					k++; | 
 | 1006 | 				} | 
 | 1007 | 			} | 
 | 1008 | 			ae = contig_high; | 
 | 1009 | 		} else | 
 | 1010 | 			ae = efi_md_end(md); | 
 | 1011 |  | 
| Zou Nan hai | a7956113 | 2006-12-07 09:51:35 -0800 | [diff] [blame] | 1012 | 		/* keep within max_addr= and min_addr= command line arg */ | 
 | 1013 | 		as = max(as, min_addr); | 
| Tony Luck | d8c97d5 | 2005-09-08 12:39:59 -0700 | [diff] [blame] | 1014 | 		ae = min(ae, max_addr); | 
 | 1015 | 		if (ae <= as) | 
 | 1016 | 			continue; | 
 | 1017 |  | 
 | 1018 | 		/* avoid going over mem= command line arg */ | 
 | 1019 | 		if (total_mem + (ae - as) > mem_limit) | 
 | 1020 | 			ae -= total_mem + (ae - as) - mem_limit; | 
 | 1021 |  | 
 | 1022 | 		if (ae <= as) | 
 | 1023 | 			continue; | 
 | 1024 | 		if (prev && kmd_end(prev) == md->phys_addr) { | 
 | 1025 | 			prev->num_pages += (ae - as) >> EFI_PAGE_SHIFT; | 
 | 1026 | 			total_mem += ae - as; | 
 | 1027 | 			continue; | 
 | 1028 | 		} | 
 | 1029 | 		k->attribute = EFI_MEMORY_WB; | 
 | 1030 | 		k->start = as; | 
 | 1031 | 		k->num_pages = (ae - as) >> EFI_PAGE_SHIFT; | 
 | 1032 | 		total_mem += ae - as; | 
 | 1033 | 		prev = k++; | 
 | 1034 | 	} | 
 | 1035 | 	k->start = ~0L; /* end-marker */ | 
 | 1036 |  | 
 | 1037 | 	/* reserve the memory we are using for kern_memmap */ | 
 | 1038 | 	*s = (u64)kern_memmap; | 
 | 1039 | 	*e = (u64)++k; | 
 | 1040 | } | 
| Khalid Aziz | be37912 | 2005-09-19 15:42:36 -0700 | [diff] [blame] | 1041 |  | 
 | 1042 | void | 
 | 1043 | efi_initialize_iomem_resources(struct resource *code_resource, | 
 | 1044 | 			       struct resource *data_resource) | 
 | 1045 | { | 
 | 1046 | 	struct resource *res; | 
 | 1047 | 	void *efi_map_start, *efi_map_end, *p; | 
 | 1048 | 	efi_memory_desc_t *md; | 
 | 1049 | 	u64 efi_desc_size; | 
 | 1050 | 	char *name; | 
 | 1051 | 	unsigned long flags; | 
 | 1052 |  | 
 | 1053 | 	efi_map_start = __va(ia64_boot_param->efi_memmap); | 
 | 1054 | 	efi_map_end   = efi_map_start + ia64_boot_param->efi_memmap_size; | 
 | 1055 | 	efi_desc_size = ia64_boot_param->efi_memdesc_size; | 
 | 1056 |  | 
 | 1057 | 	res = NULL; | 
 | 1058 |  | 
 | 1059 | 	for (p = efi_map_start; p < efi_map_end; p += efi_desc_size) { | 
 | 1060 | 		md = p; | 
 | 1061 |  | 
 | 1062 | 		if (md->num_pages == 0) /* should not happen */ | 
 | 1063 | 			continue; | 
 | 1064 |  | 
 | 1065 | 		flags = IORESOURCE_MEM; | 
 | 1066 | 		switch (md->type) { | 
 | 1067 |  | 
 | 1068 | 			case EFI_MEMORY_MAPPED_IO: | 
 | 1069 | 			case EFI_MEMORY_MAPPED_IO_PORT_SPACE: | 
 | 1070 | 				continue; | 
 | 1071 |  | 
 | 1072 | 			case EFI_LOADER_CODE: | 
 | 1073 | 			case EFI_LOADER_DATA: | 
 | 1074 | 			case EFI_BOOT_SERVICES_DATA: | 
 | 1075 | 			case EFI_BOOT_SERVICES_CODE: | 
 | 1076 | 			case EFI_CONVENTIONAL_MEMORY: | 
 | 1077 | 				if (md->attribute & EFI_MEMORY_WP) { | 
 | 1078 | 					name = "System ROM"; | 
 | 1079 | 					flags |= IORESOURCE_READONLY; | 
 | 1080 | 				} else { | 
 | 1081 | 					name = "System RAM"; | 
 | 1082 | 				} | 
 | 1083 | 				break; | 
 | 1084 |  | 
 | 1085 | 			case EFI_ACPI_MEMORY_NVS: | 
 | 1086 | 				name = "ACPI Non-volatile Storage"; | 
 | 1087 | 				flags |= IORESOURCE_BUSY; | 
 | 1088 | 				break; | 
 | 1089 |  | 
 | 1090 | 			case EFI_UNUSABLE_MEMORY: | 
 | 1091 | 				name = "reserved"; | 
 | 1092 | 				flags |= IORESOURCE_BUSY | IORESOURCE_DISABLED; | 
 | 1093 | 				break; | 
 | 1094 |  | 
 | 1095 | 			case EFI_RESERVED_TYPE: | 
 | 1096 | 			case EFI_RUNTIME_SERVICES_CODE: | 
 | 1097 | 			case EFI_RUNTIME_SERVICES_DATA: | 
 | 1098 | 			case EFI_ACPI_RECLAIM_MEMORY: | 
 | 1099 | 			default: | 
 | 1100 | 				name = "reserved"; | 
 | 1101 | 				flags |= IORESOURCE_BUSY; | 
 | 1102 | 				break; | 
 | 1103 | 		} | 
 | 1104 |  | 
| Panagiotis Issaris | baf47fb | 2005-11-09 02:08:42 +0100 | [diff] [blame] | 1105 | 		if ((res = kzalloc(sizeof(struct resource), GFP_KERNEL)) == NULL) { | 
| Khalid Aziz | be37912 | 2005-09-19 15:42:36 -0700 | [diff] [blame] | 1106 | 			printk(KERN_ERR "failed to alocate resource for iomem\n"); | 
 | 1107 | 			return; | 
 | 1108 | 		} | 
 | 1109 |  | 
 | 1110 | 		res->name = name; | 
 | 1111 | 		res->start = md->phys_addr; | 
 | 1112 | 		res->end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1; | 
 | 1113 | 		res->flags = flags; | 
 | 1114 |  | 
 | 1115 | 		if (insert_resource(&iomem_resource, res) < 0) | 
 | 1116 | 			kfree(res); | 
 | 1117 | 		else { | 
 | 1118 | 			/* | 
 | 1119 | 			 * We don't know which region contains | 
 | 1120 | 			 * kernel data so we try it repeatedly and | 
 | 1121 | 			 * let the resource manager test it. | 
 | 1122 | 			 */ | 
 | 1123 | 			insert_resource(res, code_resource); | 
 | 1124 | 			insert_resource(res, data_resource); | 
| Zou Nan hai | a7956113 | 2006-12-07 09:51:35 -0800 | [diff] [blame] | 1125 | #ifdef CONFIG_KEXEC | 
 | 1126 |                         insert_resource(res, &efi_memmap_res); | 
 | 1127 |                         insert_resource(res, &boot_param_res); | 
 | 1128 | 			if (crashk_res.end > crashk_res.start) | 
 | 1129 | 				insert_resource(res, &crashk_res); | 
 | 1130 | #endif | 
| Khalid Aziz | be37912 | 2005-09-19 15:42:36 -0700 | [diff] [blame] | 1131 | 		} | 
 | 1132 | 	} | 
 | 1133 | } | 
| Zou Nan hai | a7956113 | 2006-12-07 09:51:35 -0800 | [diff] [blame] | 1134 |  | 
 | 1135 | #ifdef CONFIG_KEXEC | 
 | 1136 | /* find a block of memory aligned to 64M exclude reserved regions | 
 | 1137 |    rsvd_regions are sorted | 
 | 1138 |  */ | 
 | 1139 | unsigned long | 
 | 1140 | kdump_find_rsvd_region (unsigned long size, | 
 | 1141 | 		struct rsvd_region *r, int n) | 
 | 1142 | { | 
 | 1143 |   int i; | 
 | 1144 |   u64 start, end; | 
 | 1145 |   u64 alignment = 1UL << _PAGE_SIZE_64M; | 
 | 1146 |   void *efi_map_start, *efi_map_end, *p; | 
 | 1147 |   efi_memory_desc_t *md; | 
 | 1148 |   u64 efi_desc_size; | 
 | 1149 |  | 
 | 1150 |   efi_map_start = __va(ia64_boot_param->efi_memmap); | 
 | 1151 |   efi_map_end   = efi_map_start + ia64_boot_param->efi_memmap_size; | 
 | 1152 |   efi_desc_size = ia64_boot_param->efi_memdesc_size; | 
 | 1153 |  | 
 | 1154 |   for (p = efi_map_start; p < efi_map_end; p += efi_desc_size) { | 
 | 1155 | 	  md = p; | 
 | 1156 | 	  if (!efi_wb(md)) | 
 | 1157 | 		  continue; | 
 | 1158 | 	  start = ALIGN(md->phys_addr, alignment); | 
 | 1159 | 	  end = efi_md_end(md); | 
 | 1160 | 	  for (i = 0; i < n; i++) { | 
 | 1161 | 		if (__pa(r[i].start) >= start && __pa(r[i].end) < end) { | 
 | 1162 | 			if (__pa(r[i].start) > start + size) | 
 | 1163 | 				return start; | 
 | 1164 | 			start = ALIGN(__pa(r[i].end), alignment); | 
 | 1165 | 			if (i < n-1 && __pa(r[i+1].start) < start + size) | 
 | 1166 | 				continue; | 
 | 1167 | 			else | 
 | 1168 | 				break; | 
 | 1169 | 		} | 
 | 1170 | 	  } | 
 | 1171 | 	  if (end > start + size) | 
 | 1172 | 		return start; | 
 | 1173 |   } | 
 | 1174 |  | 
 | 1175 |   printk(KERN_WARNING "Cannot reserve 0x%lx byte of memory for crashdump\n", | 
 | 1176 | 	size); | 
 | 1177 |   return ~0UL; | 
 | 1178 | } | 
 | 1179 | #endif |