| /* |
| * bootstub 32 bit entry setting routings |
| * |
| * Copyright (C) 2008-2010 Intel Corporation. |
| * Author: Alek Du <[email protected]> |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms and conditions of the GNU General Public License, |
| * version 2, as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope 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., |
| * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| */ |
| |
| #include "types.h" |
| #include "bootstub.h" |
| #include "bootparam.h" |
| #include "spi-uart.h" |
| #include "ssp-uart.h" |
| #include "mb.h" |
| #include "sfi.h" |
| #include <bootimg.h> |
| |
| #include <stdint.h> |
| #include <stddef.h> |
| #include "imr_toc.h" |
| |
| #define PAGE_SIZE_MASK 0xFFF |
| #define MASK_1K 0x3FF |
| #define PAGE_ALIGN_FWD(x) ((x + PAGE_SIZE_MASK) & ~PAGE_SIZE_MASK) |
| #define PAGE_ALIGN_BACK(x) ((x) & ~PAGE_SIZE_MASK) |
| |
| #define IMR_START_ADDRESS(x) (((x) & 0xFFFFFFFC) << 8) |
| #define IMR_END_ADDRESS(x) ((x == 0) ? (x) : ((((x) & 0xFFFFFFFC) << 8) | MASK_1K)) |
| |
| #define IMR6_START_ADDRESS IMR_START_ADDRESS(*((u32 *)0xff108160)) |
| #define IMR6_END_ADDRESS IMR_END_ADDRESS(*((u32 *)0xff108164)) |
| #define IMR7_START_ADDRESS IMR_START_ADDRESS(*((u32 *)0xff108170)) |
| #define IMR7_END_ADDRESS IMR_END_ADDRESS(*((u32 *)0xff108174)) |
| |
| #define FATAL_HANG() { asm("cli"); while (1) { asm("nop"); } } |
| |
| extern int no_uart_used; |
| |
| extern imr_toc_t imr6_toc; |
| static u32 imr7_size; |
| |
| static u32 sps_load_adrs; |
| |
| static memory_map_t mb_mmap[E820MAX]; |
| u32 mb_magic, mb_info; |
| |
| struct gdt_ptr { |
| u16 len; |
| u32 ptr; |
| } __attribute__((packed)); |
| |
| static void *memcpy(void *dest, const void *src, size_t count) |
| { |
| char *tmp = dest; |
| const char *s = src; |
| size_t _count = count / 4; |
| |
| while (_count--) { |
| *(long *)tmp = *(long *)s; |
| tmp += 4; |
| s += 4; |
| } |
| count %= 4; |
| while (count--) |
| *tmp++ = *s++; |
| return dest; |
| } |
| |
| static void *memset(void *s, unsigned char c, size_t count) |
| { |
| char *xs = s; |
| size_t _count = count / 4; |
| unsigned long _c = c << 24 | c << 16 | c << 8 | c; |
| |
| while (_count--) { |
| *(long *)xs = _c; |
| xs += 4; |
| } |
| count %= 4; |
| while (count--) |
| *xs++ = c; |
| return s; |
| } |
| |
| static size_t strnlen(const char *s, size_t maxlen) |
| { |
| const char *es = s; |
| while (*es && maxlen) { |
| es++; |
| maxlen--; |
| } |
| |
| return (es - s); |
| } |
| |
| static const char *strnchr(const char *s, int c, size_t maxlen) |
| { |
| int i; |
| for (i = 0; i < maxlen && *s != c; s++, i++) |
| ; |
| return s; |
| } |
| |
| int strncmp(const char *cs, const char *ct, size_t count) |
| { |
| unsigned char c1, c2; |
| |
| while (count) { |
| c1 = *cs++; |
| c2 = *ct++; |
| if (c1 != c2) |
| return c1 < c2 ? -1 : 1; |
| if (!c1) |
| break; |
| count--; |
| } |
| return 0; |
| } |
| |
| static inline int is_image_aosp(unsigned char *magic) |
| { |
| return !strncmp((char *)magic, (char *)BOOT_MAGIC, sizeof(BOOT_MAGIC)-1); |
| } |
| |
| static void setup_boot_params(struct boot_params *bp, struct setup_header *sh) |
| { |
| bp->screen_info.orig_video_mode = 0; |
| bp->screen_info.orig_video_lines = 0; |
| bp->screen_info.orig_video_cols = 0; |
| bp->alt_mem_k = 128*1024; // hard coded 128M mem here, since SFI will override it |
| memcpy(&bp->hdr, sh, sizeof (struct setup_header)); |
| bp->hdr.type_of_loader = 0xff; //bootstub is unknown bootloader for kernel :) |
| bp->hdr.hardware_subarch = X86_SUBARCH_MRST; |
| } |
| |
| static u32 bzImage_setup(struct boot_params *bp, struct setup_header *sh) |
| { |
| void *cmdline = (void *)BOOT_CMDLINE_OFFSET; |
| struct boot_img_hdr *aosp = (struct boot_img_hdr *)AOSP_HEADER_ADDRESS; |
| size_t cmdline_len, extra_cmdline_len; |
| u8 *initramfs, *ptr; |
| |
| if (is_image_aosp(aosp->magic)) { |
| ptr = (u8*)aosp->kernel_addr; |
| cmdline_len = strnlen((const char *)aosp->cmdline, sizeof(aosp->cmdline)); |
| extra_cmdline_len = strnlen((const char *)aosp->extra_cmdline, sizeof(aosp->extra_cmdline)); |
| |
| /* |
| * Copy the command + extra command line to be after bootparams |
| * so that it won't be overwritten by the kernel executable. |
| */ |
| memset(cmdline, 0, sizeof(aosp->cmdline) + sizeof(aosp->extra_cmdline)); |
| memcpy(cmdline, (const void *)aosp->cmdline, cmdline_len); |
| memcpy(cmdline + cmdline_len, (const void *)aosp->extra_cmdline, extra_cmdline_len); |
| |
| bp->hdr.ramdisk_size = aosp->ramdisk_size; |
| |
| initramfs = (u8 *)aosp->ramdisk_addr; |
| } else { |
| ptr = (u8*)BZIMAGE_OFFSET; |
| cmdline_len = strnlen((const char *)CMDLINE_OFFSET, CMDLINE_SIZE); |
| /* |
| * Copy the command line to be after bootparams so that it won't be |
| * overwritten by the kernel executable. |
| */ |
| memset(cmdline, 0, CMDLINE_SIZE); |
| memcpy(cmdline, (const void *)CMDLINE_OFFSET, cmdline_len); |
| |
| bp->hdr.ramdisk_size = *(u32 *)INITRD_SIZE_OFFSET; |
| |
| initramfs = (u8 *)BZIMAGE_OFFSET + *(u32 *)BZIMAGE_SIZE_OFFSET; |
| } |
| |
| bp->hdr.cmd_line_ptr = BOOT_CMDLINE_OFFSET; |
| bp->hdr.cmdline_size = cmdline_len; |
| #ifndef BUILD_RAMDUMP |
| bp->hdr.ramdisk_image = (bp->alt_mem_k*1024 - bp->hdr.ramdisk_size) & 0xFFFFF000; |
| |
| if (*initramfs) { |
| bs_printk("Relocating initramfs to high memory ...\n"); |
| memcpy((u8*)bp->hdr.ramdisk_image, initramfs, bp->hdr.ramdisk_size); |
| } else { |
| bs_printk("Won't relocate initramfs, are you in SLE?\n"); |
| } |
| #else |
| bp->hdr.ramdisk_image = (u32) initramfs; |
| #endif |
| |
| while (1){ |
| if (*(u32 *)ptr == SETUP_SIGNATURE && *(u32 *)(ptr+4) == 0) |
| break; |
| ptr++; |
| } |
| ptr+=4; |
| return (((unsigned int)ptr+511)/512)*512; |
| } |
| |
| static inline void cpuid(u32 op, u32 regs[4]) |
| { |
| __asm__ volatile ( |
| "mov %%ebx, %%edi\n" |
| "cpuid\n" |
| "xchg %%edi, %%ebx\n" |
| : "=a"(regs[0]), "=D"(regs[1]), "=c"(regs[2]), "=d"(regs[3]) |
| : "a"(op) |
| ); |
| } |
| |
| enum cpuid_regs { |
| CR_EAX = 0, |
| CR_ECX, |
| CR_EDX, |
| CR_EBX |
| }; |
| |
| int mid_identify_cpu(void) |
| { |
| u32 regs[4]; |
| |
| cpuid(1, regs); |
| |
| switch ( regs[CR_EAX] & CPUID_MASK ) { |
| |
| case PENWELL_FAMILY: |
| return MID_CPU_CHIP_PENWELL; |
| case CLOVERVIEW_FAMILY: |
| return MID_CPU_CHIP_CLOVERVIEW; |
| case VALLEYVIEW2_FAMILY: |
| return MID_CPU_CHIP_VALLEYVIEW2; |
| case TANGIER_FAMILY: |
| return MID_CPU_CHIP_TANGIER; |
| case ANNIEDALE_FAMILY: |
| return MID_CPU_CHIP_ANNIEDALE; |
| default: |
| return MID_CPU_CHIP_OTHER; |
| } |
| } |
| |
| static void setup_spi(void) |
| { |
| if (!(*(int *)SPI_TYPE)) { |
| switch ( mid_identify_cpu() ) { |
| |
| case MID_CPU_CHIP_PENWELL: |
| *(int *)SPI_TYPE = SPI_1; |
| bs_printk("PNW detected\n"); |
| break; |
| |
| case MID_CPU_CHIP_CLOVERVIEW: |
| *(int *)SPI_TYPE = SPI_1; |
| bs_printk("CLV detected\n"); |
| break; |
| |
| case MID_CPU_CHIP_TANGIER: |
| *(int *)SPI_TYPE = SPI_2; |
| bs_printk("MRD detected\n"); |
| break; |
| |
| case MID_CPU_CHIP_ANNIEDALE: |
| *(int *)SPI_TYPE = SPI_2; |
| bs_printk("ANN detected\n"); |
| break; |
| |
| case MID_CPU_CHIP_VALLEYVIEW2: |
| case MID_CPU_CHIP_OTHER: |
| default: |
| no_uart_used = 1; |
| } |
| } |
| } |
| |
| static void setup_gdt(void) |
| { |
| static const u64 boot_gdt[] __attribute__((aligned(16))) = { |
| /* CS: code, read/execute, 4 GB, base 0 */ |
| [GDT_ENTRY_BOOT_CS] = GDT_ENTRY(0xc09b, 0, 0xfffff), |
| /* DS: data, read/write, 4 GB, base 0 */ |
| [GDT_ENTRY_BOOT_DS] = GDT_ENTRY(0xc093, 0, 0xfffff), |
| }; |
| static struct gdt_ptr gdt; |
| |
| gdt.len = sizeof(boot_gdt)-1; |
| gdt.ptr = (u32)&boot_gdt; |
| |
| asm volatile("lgdtl %0" : : "m" (gdt)); |
| } |
| |
| static void setup_idt(void) |
| { |
| static const struct gdt_ptr null_idt = {0, 0}; |
| asm volatile("lidtl %0" : : "m" (null_idt)); |
| } |
| |
| static void vxe_fw_setup(void) |
| { |
| u8 *vxe_fw_image; |
| u32 vxe_fw_size; |
| u32 vxe_fw_load_adrs; |
| |
| vxe_fw_size = *(u32*)VXE_FW_SIZE_OFFSET; |
| /* do we have a VXE FW image? */ |
| if (vxe_fw_size == 0) |
| return; |
| |
| /* Do we have enough room to load the image? */ |
| if (vxe_fw_size > imr6_toc.entries[IMR_TOC_ENTRY_VXE_FW].size) { |
| bs_printk("FATAL ERROR: VXE FW image size is too large for IMR\n"); |
| FATAL_HANG(); |
| } |
| |
| vxe_fw_image = (u8 *)( |
| BZIMAGE_OFFSET |
| + *(u32 *)BZIMAGE_SIZE_OFFSET |
| + *(u32 *)INITRD_SIZE_OFFSET |
| ); |
| |
| vxe_fw_load_adrs = IMR6_START_ADDRESS + imr6_toc.entries[IMR_TOC_ENTRY_VXE_FW].start_offset; |
| memcpy((u8 *)vxe_fw_load_adrs, vxe_fw_image, vxe_fw_size); |
| } |
| |
| static void load_imr_toc(u32 imr, u32 imrsize, imr_toc_t *toc, u32 tocsize) |
| { |
| if (imr == 0 || imrsize == 0 || toc == NULL || tocsize == 0 || imrsize < tocsize ) |
| { |
| bs_printk("FATAL ERROR: TOC size is too large for IMR\n"); |
| FATAL_HANG(); |
| } |
| memcpy((u8 *)imr, (u8 *)toc, tocsize); |
| } |
| |
| |
| static u32 xen_multiboot_setup(void) |
| { |
| u32 *magic, *xen_image, i; |
| char *src, *dst; |
| u32 xen_size; |
| u32 xen_jump_adrs; |
| static module_t modules[3]; |
| static multiboot_info_t mb = { |
| .flags = MBI_CMDLINE | MBI_MODULES | MBI_MEMMAP | MBI_DRIVES, |
| .mmap_addr = (u32)mb_mmap, |
| .mods_count = 3, |
| .mods_addr = (u32)modules, |
| }; |
| |
| xen_size = *(u32 *)XEN_SIZE_OFFSET; |
| /* do we have a xen image? */ |
| if (xen_size == 0) { |
| return 0; |
| } |
| |
| /* Compute the actual offset of the Xen image */ |
| xen_image = (u32*)( |
| BZIMAGE_OFFSET |
| + *(u32 *)BZIMAGE_SIZE_OFFSET |
| + *(u32 *)INITRD_SIZE_OFFSET |
| + *(u32 *)VXE_FW_SIZE_OFFSET |
| + *(u32 *)SEC_PLAT_SVCS_SIZE_OFFSET |
| ); |
| |
| /* the multiboot signature should be located in the first 8192 bytes */ |
| for (magic = xen_image; magic < xen_image + 2048; magic++) |
| if (*magic == MULTIBOOT_HEADER_MAGIC) |
| break; |
| if (*magic != MULTIBOOT_HEADER_MAGIC) { |
| return 0; |
| } |
| |
| mb.cmdline = (u32)strnchr((char *)CMDLINE_OFFSET, '$', CMDLINE_SIZE) + 1; |
| dst = (char *)mb.cmdline + strnlen((const char *)mb.cmdline, CMDLINE_SIZE) - 1; |
| *dst = ' '; |
| dst++; |
| src = (char *)CMDLINE_OFFSET; |
| for (i = 0 ;i < strnlen((const char *)CMDLINE_OFFSET, CMDLINE_SIZE);i++) { |
| if (!strncmp(src, "capfreq=", 8)) { |
| while (*src != ' ' && *src != 0) { |
| *dst = *src; |
| dst++; |
| src++; |
| } |
| break; |
| } |
| src++; |
| } |
| |
| /* fill in the multiboot module information: dom0 kernel + initrd + Platform Services Image */ |
| modules[0].mod_start = BZIMAGE_OFFSET; |
| modules[0].mod_end = BZIMAGE_OFFSET + *(u32 *)BZIMAGE_SIZE_OFFSET; |
| modules[0].string = CMDLINE_OFFSET; |
| |
| modules[1].mod_start = modules[0].mod_end ; |
| modules[1].mod_end = modules[1].mod_start + *(u32 *)INITRD_SIZE_OFFSET; |
| modules[1].string = 0; |
| |
| modules[2].mod_start = sps_load_adrs; |
| modules[2].mod_end = modules[2].mod_start + *(u32 *)SEC_PLAT_SVCS_SIZE_OFFSET; |
| modules[2].string = 0; |
| |
| mb.drives_addr = IMR6_START_ADDRESS + imr6_toc.entries[IMR_TOC_ENTRY_XEN_EXTRA].start_offset; |
| mb.drives_length = imr6_toc.entries[IMR_TOC_ENTRY_XEN_EXTRA].size; |
| |
| for(i = 0; i < E820MAX; i++) |
| if (!mb_mmap[i].size) |
| break; |
| mb.mmap_length = i * sizeof(memory_map_t); |
| |
| /* relocate xen to start address */ |
| if (xen_size > imr7_size) { |
| bs_printk("FATAL ERROR: Xen image size is too large for IMR\n"); |
| FATAL_HANG(); |
| } |
| xen_jump_adrs = IMR7_START_ADDRESS; |
| memcpy((u8 *)xen_jump_adrs, xen_image, xen_size); |
| |
| mb_info = (u32)&mb; |
| mb_magic = MULTIBOOT_BOOTLOADER_MAGIC; |
| |
| return (u32)xen_jump_adrs; |
| } |
| |
| static void sec_plat_svcs_setup(void) |
| { |
| u8 *sps_image; |
| u32 sps_size; |
| |
| sps_size = PAGE_ALIGN_FWD(*(u32*)SEC_PLAT_SVCS_SIZE_OFFSET); |
| /* do we have a SPS image? */ |
| if (sps_size == 0) |
| return; |
| |
| /* Do we have enough room to load the image? */ |
| if (sps_size > imr7_size) { |
| bs_printk("FATAL ERROR: SPS image size is too large for IMR\n"); |
| FATAL_HANG(); |
| } |
| |
| sps_image = (u8 *)( |
| BZIMAGE_OFFSET |
| + *(u32 *)BZIMAGE_SIZE_OFFSET |
| + *(u32 *)INITRD_SIZE_OFFSET |
| + *(u32 *)VXE_FW_SIZE_OFFSET |
| ); |
| |
| /* load SPS image (with assumed CHAABI Mailboxes suffixed) */ |
| /* at bottom of IMR7 */ |
| /* Must be page-aligned or Xen will panic */ |
| sps_load_adrs = PAGE_ALIGN_BACK(IMR7_START_ADDRESS + imr7_size - sps_size); |
| memcpy((u8 *)sps_load_adrs, sps_image, sps_size); |
| |
| /* reduce remaining size for Xen image size check */ |
| imr7_size -= sps_size; |
| } |
| |
| int bootstub(void) |
| { |
| u32 jmp; |
| struct boot_img_hdr *aosp = (struct boot_img_hdr *)AOSP_HEADER_ADDRESS; |
| struct boot_params *bp = (struct boot_params *)BOOT_PARAMS_OFFSET; |
| struct setup_header *sh; |
| u32 imr_size; |
| int nr_entries; |
| |
| if (is_image_aosp(aosp->magic)) { |
| sh = (struct setup_header *)((unsigned int)aosp->kernel_addr + \ |
| (unsigned int)offsetof(struct boot_params,hdr)); |
| /* disable the bs_printk through SPI/UART */ |
| *(int *)SPI_UART_SUPPRESSION = 1; |
| *(int *)SPI_TYPE = SPI_2; |
| } else |
| sh = (struct setup_header *)SETUP_HEADER_OFFSET; |
| |
| setup_idt(); |
| setup_gdt(); |
| setup_spi(); |
| bs_printk("Bootstub Version: 1.4 ...\n"); |
| |
| memset(bp, 0, sizeof (struct boot_params)); |
| |
| if (mid_identify_cpu() == MID_CPU_CHIP_VALLEYVIEW2) { |
| nr_entries = get_e820_by_bios(bp->e820_map); |
| bp->e820_entries = (nr_entries > 0) ? nr_entries : 0; |
| } else { |
| sfi_setup_mmap(bp, mb_mmap); |
| } |
| |
| if ((mid_identify_cpu() != MID_CPU_CHIP_TANGIER) && (mid_identify_cpu() != MID_CPU_CHIP_ANNIEDALE)) { |
| if ((IMR6_END_ADDRESS > IMR6_START_ADDRESS) && (IMR7_END_ADDRESS > IMR7_START_ADDRESS)) { |
| imr_size = PAGE_ALIGN_FWD(IMR6_END_ADDRESS - IMR6_START_ADDRESS); |
| load_imr_toc(IMR6_START_ADDRESS, imr_size, &imr6_toc, sizeof(imr6_toc)); |
| vxe_fw_setup(); |
| sfi_add_e820_entry(bp, mb_mmap, IMR6_START_ADDRESS, imr_size, E820_RESERVED); |
| |
| imr7_size = PAGE_ALIGN_FWD(IMR7_END_ADDRESS - IMR7_START_ADDRESS); |
| sec_plat_svcs_setup(); |
| sfi_add_e820_entry(bp, mb_mmap, IMR7_START_ADDRESS, imr7_size, E820_RESERVED); |
| } else { |
| *(u32 *)XEN_SIZE_OFFSET = 0; /* Don't allow Xen to boot */ |
| } |
| } else { |
| *(u32 *)XEN_SIZE_OFFSET = 0; /* Don't allow Xen to boot */ |
| } |
| |
| setup_boot_params(bp, sh); |
| |
| jmp = xen_multiboot_setup(); |
| if (!jmp) { |
| bs_printk("Using bzImage to boot\n"); |
| jmp = bzImage_setup(bp, sh); |
| } else |
| bs_printk("Using multiboot image to boot\n"); |
| |
| bs_printk("Jump to kernel 32bit entry\n"); |
| return jmp; |
| } |
| |
| void bs_printk(const char *str) |
| { |
| if (*(int *)SPI_UART_SUPPRESSION) |
| return; |
| |
| switch (*(int *)SPI_TYPE) { |
| |
| case SPI_1: |
| bs_spi_printk(str); |
| break; |
| |
| case SPI_2: |
| bs_ssp_printk(str); |
| break; |
| } |
| } |