| /* |
| * bootstub 32 bit entry setting routings |
| * |
| * Copyright (C) 2008-2010 Intel Corporation. |
| * Author: Alek Du <alek.du@intel.com> |
| * |
| * 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 "sfi.h" |
| |
| #define bs_printk(x) { if (! *(int *)SPI_UART_SUPPRESSION) bs_spi_printk(x);} |
| |
| extern int no_uart_used; |
| |
| 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 void setup_boot_params(struct boot_params *bp, struct setup_header *sh) |
| { |
| u8 *initramfs; |
| int nr_entries; |
| |
| memset(bp, 0, sizeof (struct boot_params)); |
| 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.cmd_line_ptr = CMDLINE_OFFSET; |
| bp->hdr.cmdline_size = strnlen((const char *)CMDLINE_OFFSET, CMDLINE_SIZE); |
| bp->hdr.type_of_loader = 0xff; //bootstub is unknown bootloader for kernel :) |
| bp->hdr.ramdisk_size = *(u32 *)INITRD_SIZE_OFFSET; |
| bp->hdr.ramdisk_image = (bp->alt_mem_k*1024 - bp->hdr.ramdisk_size) & 0xFFFFF000; |
| bp->hdr.hardware_subarch = X86_SUBARCH_MRST; |
| initramfs = (u8 *)BZIMAGE_OFFSET + *(u32 *)BZIMAGE_SIZE_OFFSET; |
| 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"); |
| } |
| 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_e820(bp); |
| } |
| } |
| |
| static int get_32bit_entry(unsigned char *ptr) |
| { |
| 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 *eax, u32 *ebx, u32 *ecx, u32 *edx) |
| { |
| *eax = op; |
| *ecx = 0; |
| asm volatile("cpuid" |
| : "=a" (*eax), |
| "=b" (*ebx), |
| "=c" (*ecx), |
| "=d" (*edx) |
| : "0" (*eax), "2" (*ecx)); |
| } |
| |
| enum cpuid_regs { |
| CR_EAX = 0, |
| CR_ECX, |
| CR_EDX, |
| CR_EBX |
| }; |
| |
| int mid_identify_cpu(void) |
| { |
| u32 regs[4]; |
| |
| cpuid(1, ®s[CR_EAX], ®s[CR_EBX], ®s[CR_ECX], ®s[CR_EDX]); |
| |
| 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; |
| 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_VALLEYVIEW2: |
| case MID_CPU_CHIP_OTHER: |
| default: |
| no_uart_used = 1; |
| } |
| } |
| } |
| |
| int bootstub(void) |
| { |
| setup_spi(); |
| bs_printk("Bootstub Version: 1.3 ...\n"); |
| setup_boot_params((struct boot_params *)BOOT_PARAMS_OFFSET, |
| (struct setup_header *)SETUP_HEADER_OFFSET); |
| bs_printk("Jump to kernel 32bit entry ...\n"); |
| return get_32bit_entry((unsigned char *)BZIMAGE_OFFSET); |
| } |