Greg Kroah-Hartman | b244131 | 2017-11-01 15:07:57 +0100 | [diff] [blame^] | 1 | // SPDX-License-Identifier: GPL-2.0 |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2 | /* |
| 3 | * PAL & SAL emulation. |
| 4 | * |
| 5 | * Copyright (C) 1998-2001 Hewlett-Packard Co |
| 6 | * David Mosberger-Tang <davidm@hpl.hp.com> |
| 7 | */ |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 8 | |
| 9 | #ifdef CONFIG_PCI |
| 10 | # include <linux/pci.h> |
| 11 | #endif |
| 12 | |
| 13 | #include <linux/efi.h> |
| 14 | #include <asm/io.h> |
| 15 | #include <asm/pal.h> |
| 16 | #include <asm/sal.h> |
David Howells | c140d87 | 2012-03-28 18:30:02 +0100 | [diff] [blame] | 17 | #include <asm/setup.h> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 18 | |
| 19 | #include "ssc.h" |
| 20 | |
| 21 | #define MB (1024*1024UL) |
| 22 | |
| 23 | #define SIMPLE_MEMMAP 1 |
| 24 | |
| 25 | #if SIMPLE_MEMMAP |
| 26 | # define NUM_MEM_DESCS 4 |
| 27 | #else |
| 28 | # define NUM_MEM_DESCS 16 |
| 29 | #endif |
| 30 | |
| 31 | static char fw_mem[( sizeof(struct ia64_boot_param) |
| 32 | + sizeof(efi_system_table_t) |
| 33 | + sizeof(efi_runtime_services_t) |
| 34 | + 1*sizeof(efi_config_table_t) |
| 35 | + sizeof(struct ia64_sal_systab) |
| 36 | + sizeof(struct ia64_sal_desc_entry_point) |
| 37 | + NUM_MEM_DESCS*(sizeof(efi_memory_desc_t)) |
| 38 | + 1024)] __attribute__ ((aligned (8))); |
| 39 | |
| 40 | #define SECS_PER_HOUR (60 * 60) |
| 41 | #define SECS_PER_DAY (SECS_PER_HOUR * 24) |
| 42 | |
| 43 | /* Compute the `struct tm' representation of *T, |
| 44 | offset OFFSET seconds east of UTC, |
| 45 | and store year, yday, mon, mday, wday, hour, min, sec into *TP. |
| 46 | Return nonzero if successful. */ |
| 47 | int |
| 48 | offtime (unsigned long t, efi_time_t *tp) |
| 49 | { |
| 50 | const unsigned short int __mon_yday[2][13] = |
| 51 | { |
| 52 | /* Normal years. */ |
| 53 | { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, |
| 54 | /* Leap years. */ |
| 55 | { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } |
| 56 | }; |
| 57 | long int days, rem, y; |
| 58 | const unsigned short int *ip; |
| 59 | |
| 60 | days = t / SECS_PER_DAY; |
| 61 | rem = t % SECS_PER_DAY; |
| 62 | while (rem < 0) { |
| 63 | rem += SECS_PER_DAY; |
| 64 | --days; |
| 65 | } |
| 66 | while (rem >= SECS_PER_DAY) { |
| 67 | rem -= SECS_PER_DAY; |
| 68 | ++days; |
| 69 | } |
| 70 | tp->hour = rem / SECS_PER_HOUR; |
| 71 | rem %= SECS_PER_HOUR; |
| 72 | tp->minute = rem / 60; |
| 73 | tp->second = rem % 60; |
| 74 | /* January 1, 1970 was a Thursday. */ |
| 75 | y = 1970; |
| 76 | |
| 77 | # define DIV(a, b) ((a) / (b) - ((a) % (b) < 0)) |
| 78 | # define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400)) |
| 79 | # define __isleap(year) \ |
| 80 | ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) |
| 81 | |
| 82 | while (days < 0 || days >= (__isleap (y) ? 366 : 365)) { |
| 83 | /* Guess a corrected year, assuming 365 days per year. */ |
| 84 | long int yg = y + days / 365 - (days % 365 < 0); |
| 85 | |
| 86 | /* Adjust DAYS and Y to match the guessed year. */ |
| 87 | days -= ((yg - y) * 365 + LEAPS_THRU_END_OF (yg - 1) |
| 88 | - LEAPS_THRU_END_OF (y - 1)); |
| 89 | y = yg; |
| 90 | } |
| 91 | tp->year = y; |
| 92 | ip = __mon_yday[__isleap(y)]; |
| 93 | for (y = 11; days < (long int) ip[y]; --y) |
| 94 | continue; |
| 95 | days -= ip[y]; |
| 96 | tp->month = y + 1; |
| 97 | tp->day = days + 1; |
| 98 | return 1; |
| 99 | } |
| 100 | |
| 101 | extern void pal_emulator_static (void); |
| 102 | |
| 103 | /* Macro to emulate SAL call using legacy IN and OUT calls to CF8, CFC etc.. */ |
| 104 | |
| 105 | #define BUILD_CMD(addr) ((0x80000000 | (addr)) & ~3) |
| 106 | |
| 107 | #define REG_OFFSET(addr) (0x00000000000000FF & (addr)) |
| 108 | #define DEVICE_FUNCTION(addr) (0x000000000000FF00 & (addr)) |
| 109 | #define BUS_NUMBER(addr) (0x0000000000FF0000 & (addr)) |
| 110 | |
| 111 | static efi_status_t |
| 112 | fw_efi_get_time (efi_time_t *tm, efi_time_cap_t *tc) |
| 113 | { |
| 114 | #if defined(CONFIG_IA64_HP_SIM) || defined(CONFIG_IA64_GENERIC) |
| 115 | struct { |
| 116 | int tv_sec; /* must be 32bits to work */ |
| 117 | int tv_usec; |
| 118 | } tv32bits; |
| 119 | |
| 120 | ssc((unsigned long) &tv32bits, 0, 0, 0, SSC_GET_TOD); |
| 121 | |
| 122 | memset(tm, 0, sizeof(*tm)); |
| 123 | offtime(tv32bits.tv_sec, tm); |
| 124 | |
| 125 | if (tc) |
| 126 | memset(tc, 0, sizeof(*tc)); |
| 127 | #else |
| 128 | # error Not implemented yet... |
| 129 | #endif |
| 130 | return EFI_SUCCESS; |
| 131 | } |
| 132 | |
| 133 | static void |
| 134 | efi_reset_system (int reset_type, efi_status_t status, unsigned long data_size, efi_char16_t *data) |
| 135 | { |
| 136 | #if defined(CONFIG_IA64_HP_SIM) || defined(CONFIG_IA64_GENERIC) |
| 137 | ssc(status, 0, 0, 0, SSC_EXIT); |
| 138 | #else |
| 139 | # error Not implemented yet... |
| 140 | #endif |
| 141 | } |
| 142 | |
| 143 | static efi_status_t |
| 144 | efi_unimplemented (void) |
| 145 | { |
| 146 | return EFI_UNSUPPORTED; |
| 147 | } |
| 148 | |
| 149 | static struct sal_ret_values |
| 150 | sal_emulator (long index, unsigned long in1, unsigned long in2, |
| 151 | unsigned long in3, unsigned long in4, unsigned long in5, |
| 152 | unsigned long in6, unsigned long in7) |
| 153 | { |
| 154 | long r9 = 0; |
| 155 | long r10 = 0; |
| 156 | long r11 = 0; |
| 157 | long status; |
| 158 | |
| 159 | /* |
| 160 | * Don't do a "switch" here since that gives us code that |
| 161 | * isn't self-relocatable. |
| 162 | */ |
| 163 | status = 0; |
| 164 | if (index == SAL_FREQ_BASE) { |
Jiri Slaby | 0745d19 | 2012-03-08 21:01:16 +0100 | [diff] [blame] | 165 | if (in1 == SAL_FREQ_BASE_PLATFORM) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 166 | r9 = 200000000; |
Jiri Slaby | 0745d19 | 2012-03-08 21:01:16 +0100 | [diff] [blame] | 167 | else if (in1 == SAL_FREQ_BASE_INTERVAL_TIMER) { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 168 | /* |
| 169 | * Is this supposed to be the cr.itc frequency |
| 170 | * or something platform specific? The SAL |
| 171 | * doc ain't exactly clear on this... |
| 172 | */ |
| 173 | r9 = 700000000; |
Jiri Slaby | 0745d19 | 2012-03-08 21:01:16 +0100 | [diff] [blame] | 174 | } else if (in1 == SAL_FREQ_BASE_REALTIME_CLOCK) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 175 | r9 = 1; |
Jiri Slaby | 0745d19 | 2012-03-08 21:01:16 +0100 | [diff] [blame] | 176 | else |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 177 | status = -1; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 178 | } else if (index == SAL_SET_VECTORS) { |
| 179 | ; |
| 180 | } else if (index == SAL_GET_STATE_INFO) { |
| 181 | ; |
| 182 | } else if (index == SAL_GET_STATE_INFO_SIZE) { |
| 183 | ; |
| 184 | } else if (index == SAL_CLEAR_STATE_INFO) { |
| 185 | ; |
| 186 | } else if (index == SAL_MC_RENDEZ) { |
| 187 | ; |
| 188 | } else if (index == SAL_MC_SET_PARAMS) { |
| 189 | ; |
| 190 | } else if (index == SAL_CACHE_FLUSH) { |
| 191 | ; |
| 192 | } else if (index == SAL_CACHE_INIT) { |
| 193 | ; |
| 194 | #ifdef CONFIG_PCI |
| 195 | } else if (index == SAL_PCI_CONFIG_READ) { |
| 196 | /* |
| 197 | * in1 contains the PCI configuration address and in2 |
| 198 | * the size of the read. The value that is read is |
| 199 | * returned via the general register r9. |
| 200 | */ |
| 201 | outl(BUILD_CMD(in1), 0xCF8); |
| 202 | if (in2 == 1) /* Reading byte */ |
| 203 | r9 = inb(0xCFC + ((REG_OFFSET(in1) & 3))); |
| 204 | else if (in2 == 2) /* Reading word */ |
| 205 | r9 = inw(0xCFC + ((REG_OFFSET(in1) & 2))); |
| 206 | else /* Reading dword */ |
| 207 | r9 = inl(0xCFC); |
| 208 | status = PCIBIOS_SUCCESSFUL; |
| 209 | } else if (index == SAL_PCI_CONFIG_WRITE) { |
| 210 | /* |
| 211 | * in1 contains the PCI configuration address, in2 the |
| 212 | * size of the write, and in3 the actual value to be |
| 213 | * written out. |
| 214 | */ |
| 215 | outl(BUILD_CMD(in1), 0xCF8); |
| 216 | if (in2 == 1) /* Writing byte */ |
| 217 | outb(in3, 0xCFC + ((REG_OFFSET(in1) & 3))); |
| 218 | else if (in2 == 2) /* Writing word */ |
| 219 | outw(in3, 0xCFC + ((REG_OFFSET(in1) & 2))); |
| 220 | else /* Writing dword */ |
| 221 | outl(in3, 0xCFC); |
| 222 | status = PCIBIOS_SUCCESSFUL; |
| 223 | #endif /* CONFIG_PCI */ |
| 224 | } else if (index == SAL_UPDATE_PAL) { |
| 225 | ; |
| 226 | } else { |
| 227 | status = -1; |
| 228 | } |
| 229 | return ((struct sal_ret_values) {status, r9, r10, r11}); |
| 230 | } |
| 231 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 232 | struct ia64_boot_param * |
| 233 | sys_fw_init (const char *args, int arglen) |
| 234 | { |
| 235 | efi_system_table_t *efi_systab; |
| 236 | efi_runtime_services_t *efi_runtime; |
| 237 | efi_config_table_t *efi_tables; |
| 238 | struct ia64_sal_systab *sal_systab; |
| 239 | efi_memory_desc_t *efi_memmap, *md; |
| 240 | unsigned long *pal_desc, *sal_desc; |
| 241 | struct ia64_sal_desc_entry_point *sal_ed; |
| 242 | struct ia64_boot_param *bp; |
| 243 | unsigned char checksum = 0; |
| 244 | char *cp, *cmd_line; |
| 245 | int i = 0; |
| 246 | # define MAKE_MD(typ, attr, start, end) \ |
| 247 | do { \ |
| 248 | md = efi_memmap + i++; \ |
| 249 | md->type = typ; \ |
| 250 | md->pad = 0; \ |
| 251 | md->phys_addr = start; \ |
| 252 | md->virt_addr = 0; \ |
| 253 | md->num_pages = (end - start) >> 12; \ |
| 254 | md->attribute = attr; \ |
| 255 | } while (0) |
| 256 | |
| 257 | memset(fw_mem, 0, sizeof(fw_mem)); |
| 258 | |
| 259 | pal_desc = (unsigned long *) &pal_emulator_static; |
| 260 | sal_desc = (unsigned long *) &sal_emulator; |
| 261 | |
| 262 | cp = fw_mem; |
| 263 | efi_systab = (void *) cp; cp += sizeof(*efi_systab); |
| 264 | efi_runtime = (void *) cp; cp += sizeof(*efi_runtime); |
| 265 | efi_tables = (void *) cp; cp += sizeof(*efi_tables); |
| 266 | sal_systab = (void *) cp; cp += sizeof(*sal_systab); |
| 267 | sal_ed = (void *) cp; cp += sizeof(*sal_ed); |
| 268 | efi_memmap = (void *) cp; cp += NUM_MEM_DESCS*sizeof(*efi_memmap); |
| 269 | bp = (void *) cp; cp += sizeof(*bp); |
| 270 | cmd_line = (void *) cp; |
| 271 | |
| 272 | if (args) { |
| 273 | if (arglen >= 1024) |
| 274 | arglen = 1023; |
| 275 | memcpy(cmd_line, args, arglen); |
| 276 | } else { |
| 277 | arglen = 0; |
| 278 | } |
| 279 | cmd_line[arglen] = '\0'; |
| 280 | |
Roel Kluin | b17de36 | 2007-11-06 22:13:53 +0100 | [diff] [blame] | 281 | memset(efi_systab, 0, sizeof(*efi_systab)); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 282 | efi_systab->hdr.signature = EFI_SYSTEM_TABLE_SIGNATURE; |
Bjorn Helgaas | 873ec74 | 2007-05-08 00:29:57 -0700 | [diff] [blame] | 283 | efi_systab->hdr.revision = ((1 << 16) | 00); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 284 | efi_systab->hdr.headersize = sizeof(efi_systab->hdr); |
| 285 | efi_systab->fw_vendor = __pa("H\0e\0w\0l\0e\0t\0t\0-\0P\0a\0c\0k\0a\0r\0d\0\0"); |
| 286 | efi_systab->fw_revision = 1; |
| 287 | efi_systab->runtime = (void *) __pa(efi_runtime); |
| 288 | efi_systab->nr_tables = 1; |
| 289 | efi_systab->tables = __pa(efi_tables); |
| 290 | |
| 291 | efi_runtime->hdr.signature = EFI_RUNTIME_SERVICES_SIGNATURE; |
| 292 | efi_runtime->hdr.revision = EFI_RUNTIME_SERVICES_REVISION; |
| 293 | efi_runtime->hdr.headersize = sizeof(efi_runtime->hdr); |
Luck, Tony | 4484009 | 2013-06-20 11:15:39 -0700 | [diff] [blame] | 294 | efi_runtime->get_time = (void *)__pa(&fw_efi_get_time); |
| 295 | efi_runtime->set_time = (void *)__pa(&efi_unimplemented); |
| 296 | efi_runtime->get_wakeup_time = (void *)__pa(&efi_unimplemented); |
| 297 | efi_runtime->set_wakeup_time = (void *)__pa(&efi_unimplemented); |
| 298 | efi_runtime->set_virtual_address_map = (void *)__pa(&efi_unimplemented); |
| 299 | efi_runtime->get_variable = (void *)__pa(&efi_unimplemented); |
| 300 | efi_runtime->get_next_variable = (void *)__pa(&efi_unimplemented); |
| 301 | efi_runtime->set_variable = (void *)__pa(&efi_unimplemented); |
| 302 | efi_runtime->get_next_high_mono_count = (void *)__pa(&efi_unimplemented); |
| 303 | efi_runtime->reset_system = (void *)__pa(&efi_reset_system); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 304 | |
| 305 | efi_tables->guid = SAL_SYSTEM_TABLE_GUID; |
| 306 | efi_tables->table = __pa(sal_systab); |
| 307 | |
| 308 | /* fill in the SAL system table: */ |
| 309 | memcpy(sal_systab->signature, "SST_", 4); |
| 310 | sal_systab->size = sizeof(*sal_systab); |
| 311 | sal_systab->sal_rev_minor = 1; |
| 312 | sal_systab->sal_rev_major = 0; |
| 313 | sal_systab->entry_count = 1; |
| 314 | |
| 315 | #ifdef CONFIG_IA64_GENERIC |
| 316 | strcpy(sal_systab->oem_id, "Generic"); |
| 317 | strcpy(sal_systab->product_id, "IA-64 system"); |
| 318 | #endif |
| 319 | |
| 320 | #ifdef CONFIG_IA64_HP_SIM |
| 321 | strcpy(sal_systab->oem_id, "Hewlett-Packard"); |
| 322 | strcpy(sal_systab->product_id, "HP-simulator"); |
| 323 | #endif |
| 324 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 325 | /* fill in an entry point: */ |
| 326 | sal_ed->type = SAL_DESC_ENTRY_POINT; |
| 327 | sal_ed->pal_proc = __pa(pal_desc[0]); |
| 328 | sal_ed->sal_proc = __pa(sal_desc[0]); |
| 329 | sal_ed->gp = __pa(sal_desc[1]); |
| 330 | |
| 331 | for (cp = (char *) sal_systab; cp < (char *) efi_memmap; ++cp) |
| 332 | checksum += *cp; |
| 333 | |
| 334 | sal_systab->checksum = -checksum; |
| 335 | |
| 336 | #if SIMPLE_MEMMAP |
| 337 | /* simulate free memory at physical address zero */ |
| 338 | MAKE_MD(EFI_BOOT_SERVICES_DATA, EFI_MEMORY_WB, 0*MB, 1*MB); |
| 339 | MAKE_MD(EFI_PAL_CODE, EFI_MEMORY_WB, 1*MB, 2*MB); |
| 340 | MAKE_MD(EFI_CONVENTIONAL_MEMORY, EFI_MEMORY_WB, 2*MB, 130*MB); |
| 341 | MAKE_MD(EFI_CONVENTIONAL_MEMORY, EFI_MEMORY_WB, 4096*MB, 4128*MB); |
| 342 | #else |
| 343 | MAKE_MD( 4, 0x9, 0x0000000000000000, 0x0000000000001000); |
| 344 | MAKE_MD( 7, 0x9, 0x0000000000001000, 0x000000000008a000); |
| 345 | MAKE_MD( 4, 0x9, 0x000000000008a000, 0x00000000000a0000); |
| 346 | MAKE_MD( 5, 0x8000000000000009, 0x00000000000c0000, 0x0000000000100000); |
| 347 | MAKE_MD( 7, 0x9, 0x0000000000100000, 0x0000000004400000); |
| 348 | MAKE_MD( 2, 0x9, 0x0000000004400000, 0x0000000004be5000); |
| 349 | MAKE_MD( 7, 0x9, 0x0000000004be5000, 0x000000007f77e000); |
| 350 | MAKE_MD( 6, 0x8000000000000009, 0x000000007f77e000, 0x000000007fb94000); |
| 351 | MAKE_MD( 6, 0x8000000000000009, 0x000000007fb94000, 0x000000007fb95000); |
| 352 | MAKE_MD( 6, 0x8000000000000009, 0x000000007fb95000, 0x000000007fc00000); |
| 353 | MAKE_MD(13, 0x8000000000000009, 0x000000007fc00000, 0x000000007fc3a000); |
| 354 | MAKE_MD( 7, 0x9, 0x000000007fc3a000, 0x000000007fea0000); |
| 355 | MAKE_MD( 5, 0x8000000000000009, 0x000000007fea0000, 0x000000007fea8000); |
| 356 | MAKE_MD( 7, 0x9, 0x000000007fea8000, 0x000000007feab000); |
| 357 | MAKE_MD( 5, 0x8000000000000009, 0x000000007feab000, 0x000000007ffff000); |
| 358 | MAKE_MD( 7, 0x9, 0x00000000ff400000, 0x0000000104000000); |
| 359 | #endif |
| 360 | |
| 361 | bp->efi_systab = __pa(&fw_mem); |
| 362 | bp->efi_memmap = __pa(efi_memmap); |
| 363 | bp->efi_memmap_size = NUM_MEM_DESCS*sizeof(efi_memory_desc_t); |
| 364 | bp->efi_memdesc_size = sizeof(efi_memory_desc_t); |
| 365 | bp->efi_memdesc_version = 1; |
| 366 | bp->command_line = __pa(cmd_line); |
| 367 | bp->console_info.num_cols = 80; |
| 368 | bp->console_info.num_rows = 25; |
| 369 | bp->console_info.orig_x = 0; |
| 370 | bp->console_info.orig_y = 24; |
| 371 | bp->fpswa = 0; |
| 372 | |
| 373 | return bp; |
| 374 | } |