/* * x86_64-cb/bootboot.c * * Copyright (C) 2017 - 2021 bzt (bztsrc@gitlab) * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * This file is part of the BOOTBOOT Protocol package. * @brief Boot loader for the x86_64 coreboot payload * * Memory map * 0h - 1000h reserved by coreboot * 1000h - 2000h SMP trampoline * 2000h - 3000h bootboot structure * 3000h - 4000h environment * 4000h - 29000h paging tables * 29000h - 69000h core stacks, 1k for each core * 69000h - A0000h free memory * A0000h - F0000h reserved by coreboot * F0000h - FFFF0h special IMD records, see arch_write_tables() in coreboot/src/arch/x86/tables.c * 100000h -300000h libpayload, this code, and heap * 300000h - ? initrd */ #define BBDEBUG 1 //#define BBNOIDEFALLBACK 1 #include #include #if IS_ENABLED(CONFIG_LP_CBFS) # include #endif #if IS_ENABLED(CONFIG_LP_STORAGE) # include #endif #if IS_ENABLED(CONFIG_LP_USB) # include # include #endif #include "tinf.h" #include "../dist/bootboot.h" #define PAGESIZE 4096 #if BBDEBUG #define DBG(fmt, ...) do{printf(fmt,__VA_ARGS__); }while(0); #else #define DBG(fmt, ...) #endif #define INITRD_BASE ((CONFIG_LP_BASE_ADDRESS + 1024*1024 + CONFIG_LP_HEAP_SIZE + PAGESIZE-1) & ~(PAGESIZE-1)) extern void ap_trampoline(); #if __WORDSIZE == 64 extern void bsp64_init(uint64_t apicid); #else extern void bsp_init(); #endif #define send_ipi(a,m,v) do { \ while(*((volatile uint32_t*)((uintptr_t)lapic_addr + 0x300)) & (1 << 12)) __asm__ __volatile__ ("pause" : : : "memory"); \ *((volatile uint32_t*)((uintptr_t)lapic_addr + 0x310)) = (*((volatile uint32_t*)((uintptr_t)lapic_addr + 0x310)) & \ 0x00ffffff) | (a << 24); \ *((volatile uint32_t*)((uintptr_t)lapic_addr + 0x300)) = (*((volatile uint32_t*)((uintptr_t)lapic_addr + 0x300)) & m) | v; \ } while(0) /*** ELF64 defines and structs ***/ #define ELFMAG "\177ELF" #define SELFMAG 4 #define EI_CLASS 4 /* File class byte index */ #define ELFCLASS64 2 /* 64-bit objects */ #define EI_DATA 5 /* Data encoding byte index */ #define ELFDATA2LSB 1 /* 2's complement, little endian */ #define PT_LOAD 1 /* Loadable program segment */ #define EM_X86_64 62 /* AMD x86-64 architecture */ typedef struct { unsigned char e_ident[16];/* Magic number and other info */ uint16_t e_type; /* Object file type */ uint16_t e_machine; /* Architecture */ uint32_t e_version; /* Object file version */ uint64_t e_entry; /* Entry point virtual address */ uint64_t e_phoff; /* Program header table file offset */ uint64_t e_shoff; /* Section header table file offset */ uint32_t e_flags; /* Processor-specific flags */ uint16_t e_ehsize; /* ELF header size in bytes */ uint16_t e_phentsize; /* Program header table entry size */ uint16_t e_phnum; /* Program header table entry count */ uint16_t e_shentsize; /* Section header table entry size */ uint16_t e_shnum; /* Section header table entry count */ uint16_t e_shstrndx; /* Section header string table index */ } Elf64_Ehdr; typedef struct { uint32_t p_type; /* Segment type */ uint32_t p_flags; /* Segment flags */ uint64_t p_offset; /* Segment file offset */ uint64_t p_vaddr; /* Segment virtual address */ uint64_t p_paddr; /* Segment physical address */ uint64_t p_filesz; /* Segment size in file */ uint64_t p_memsz; /* Segment size in memory */ uint64_t p_align; /* Segment alignment */ } Elf64_Phdr; typedef struct { uint32_t sh_name; /* Section name (string tbl index) */ uint32_t sh_type; /* Section type */ uint64_t sh_flags; /* Section flags */ uint64_t sh_addr; /* Section virtual addr at execution */ uint64_t sh_offset; /* Section file offset */ uint64_t sh_size; /* Section size in bytes */ uint32_t sh_link; /* Link to another section */ uint32_t sh_info; /* Additional section information */ uint64_t sh_addralign; /* Section alignment */ uint64_t sh_entsize; /* Entry size if section holds table */ } Elf64_Shdr; typedef struct { uint32_t st_name; /* Symbol name (string tbl index) */ uint8_t st_info; /* Symbol type and binding */ uint8_t st_other; /* Symbol visibility */ uint16_t st_shndx; /* Section index */ uint64_t st_value; /* Symbol value */ uint64_t st_size; /* Symbol size */ } Elf64_Sym; /*** PE32+ defines and structs ***/ #define MZ_MAGIC 0x5a4d /* "MZ" */ #define PE_MAGIC 0x00004550 /* "PE\0\0" */ #define IMAGE_FILE_MACHINE_AMD64 0x8664 /* AMD x86_64 architecture */ #define PE_OPT_MAGIC_PE32PLUS 0x020b /* PE32+ format */ typedef struct { uint16_t magic; /* MZ magic */ uint16_t reserved[29]; /* reserved */ uint32_t peaddr; /* address of pe header */ } mz_hdr; typedef struct { uint32_t magic; /* PE magic */ uint16_t machine; /* machine type */ uint16_t sections; /* number of sections */ uint32_t timestamp; /* time_t */ uint32_t sym_table; /* symbol table offset */ uint32_t numsym; /* number of symbols */ uint16_t opt_hdr_size; /* size of optional header */ uint16_t flags; /* flags */ uint16_t file_type; /* file type, PE32PLUS magic */ uint8_t ld_major; /* linker major version */ uint8_t ld_minor; /* linker minor version */ uint32_t text_size; /* size of text section(s) */ uint32_t data_size; /* size of data section(s) */ uint32_t bss_size; /* size of bss section(s) */ int32_t entry_point; /* file offset of entry point */ int32_t code_base; /* relative code addr in ram */ } pe_hdr; typedef struct { uint32_t iszero; /* if this is not zero, then iszero+nameoffs gives UTF-8 string */ uint32_t nameoffs; int32_t value; /* value of the symbol */ uint16_t section; /* section it belongs to */ uint16_t type; /* symbol type */ uint8_t storclass; /* storage class */ uint8_t auxsyms; /* number of pe_sym records following */ } pe_sym; /*** other defines and structs ***/ typedef struct { uint32_t type[4]; uint8_t uuid[16]; uint64_t start; uint64_t end; uint64_t flags; uint8_t name[72]; } efipart_t; typedef struct { char jmp[3]; char oem[8]; uint16_t bps; uint8_t spc; uint16_t rsc; uint8_t nf; uint8_t nr0; uint8_t nr1; uint16_t ts16; uint8_t media; uint16_t spf16; uint16_t spt; uint16_t nh; uint32_t hs; uint32_t ts32; uint32_t spf32; uint32_t flg; uint32_t rc; char vol[6]; char fst[8]; char dmy[20]; char fst2[8]; } __attribute__((packed)) bpb_t; typedef struct { char name[8]; char ext[3]; char attr[9]; uint16_t ch; uint32_t attr2; uint16_t cl; uint32_t size; } __attribute__((packed)) fatdir_t; /** * return type for fs drivers */ typedef struct { uint8_t *ptr; uint32_t size; } file_t; /*** common variables ***/ extern struct sysinfo_t lib_sysinfo; file_t initrd; // initrd file descriptor file_t core; // kernel file descriptor BOOTBOOT *bootboot = (BOOTBOOT*)0x2000; char *environment = (char*)0x3000; uint64_t *paging = (uint64_t*)0x4000; int reqwidth = 1024, reqheight = 768; char *kernelname="sys/core"; unsigned char *kne, nosmp=0; // alternative environment name char *cfgname="sys/config"; uint64_t mm_addr = BOOTBOOT_MMIO; // virtual addresses uint64_t fb_addr = BOOTBOOT_FB; uint64_t bb_addr = BOOTBOOT_INFO; uint64_t env_addr= BOOTBOOT_ENV; uint64_t core_addr=BOOTBOOT_CORE; uint64_t entrypoint=0, lapic_addr=0, initstack = 1024; uint16_t lapic_ids[1024]; /** * SHA-256 */ typedef struct { uint8_t d[64]; uint32_t l; uint32_t b[2]; uint32_t s[8]; } SHA256_CTX; #define SHA_ADD(a,b,c) if(a>0xffffffff-(c))b++;a+=c; #define SHA_ROTL(a,b) (((a)<<(b))|((a)>>(32-(b)))) #define SHA_ROTR(a,b) (((a)>>(b))|((a)<<(32-(b)))) #define SHA_CH(x,y,z) (((x)&(y))^(~(x)&(z))) #define SHA_MAJ(x,y,z) (((x)&(y))^((x)&(z))^((y)&(z))) #define SHA_EP0(x) (SHA_ROTR(x,2)^SHA_ROTR(x,13)^SHA_ROTR(x,22)) #define SHA_EP1(x) (SHA_ROTR(x,6)^SHA_ROTR(x,11)^SHA_ROTR(x,25)) #define SHA_SIG0(x) (SHA_ROTR(x,7)^SHA_ROTR(x,18)^((x)>>3)) #define SHA_SIG1(x) (SHA_ROTR(x,17)^SHA_ROTR(x,19)^((x)>>10)) static uint32_t sha256_k[64]={ 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 }; void sha256_t(SHA256_CTX *ctx) { uint32_t a,b,c,d,e,f,g,h,i,j,t1,t2,m[64]; for(i=0,j=0;i<16;i++,j+=4) m[i]=(ctx->d[j]<<24)|(ctx->d[j+1]<<16)|(ctx->d[j+2]<<8)|(ctx->d[j+3]); for(;i<64;i++) m[i]=SHA_SIG1(m[i-2])+m[i-7]+SHA_SIG0(m[i-15])+m[i-16]; a=ctx->s[0];b=ctx->s[1];c=ctx->s[2];d=ctx->s[3]; e=ctx->s[4];f=ctx->s[5];g=ctx->s[6];h=ctx->s[7]; for(i=0;i<64;i++) { t1=h+SHA_EP1(e)+SHA_CH(e,f,g)+sha256_k[i]+m[i]; t2=SHA_EP0(a)+SHA_MAJ(a,b,c);h=g;g=f;f=e;e=d+t1;d=c;c=b;b=a;a=t1+t2; } ctx->s[0]+=a;ctx->s[1]+=b;ctx->s[2]+=c;ctx->s[3]+=d; ctx->s[4]+=e;ctx->s[5]+=f;ctx->s[6]+=g;ctx->s[7]+=h; } void SHA256_Init(SHA256_CTX *ctx) { ctx->l=0;ctx->b[0]=ctx->b[1]=0; ctx->s[0]=0x6a09e667;ctx->s[1]=0xbb67ae85;ctx->s[2]=0x3c6ef372;ctx->s[3]=0xa54ff53a; ctx->s[4]=0x510e527f;ctx->s[5]=0x9b05688c;ctx->s[6]=0x1f83d9ab;ctx->s[7]=0x5be0cd19; } void SHA256_Update(SHA256_CTX *ctx, const void *data, int len) { uint8_t *d=(uint8_t *)data; for(;len--;d++) { ctx->d[ctx->l++]=*d; if(ctx->l==64) {sha256_t(ctx);SHA_ADD(ctx->b[0],ctx->b[1],512);ctx->l=0;} } } void SHA256_Final(unsigned char *h, SHA256_CTX *ctx) { uint32_t i=ctx->l; ctx->d[i++]=0x80; if(ctx->l<56) {while(i<56) ctx->d[i++]=0x00;} else {while(i<64) ctx->d[i++]=0x00;sha256_t(ctx);memset(ctx->d,0,56);} SHA_ADD(ctx->b[0],ctx->b[1],ctx->l*8); ctx->d[63]=ctx->b[0];ctx->d[62]=ctx->b[0]>>8;ctx->d[61]=ctx->b[0]>>16;ctx->d[60]=ctx->b[0]>>24; ctx->d[59]=ctx->b[1];ctx->d[58]=ctx->b[1]>>8;ctx->d[57]=ctx->b[1]>>16;ctx->d[56]=ctx->b[1]>>24; sha256_t(ctx); for(i=0;i<4;i++) { h[i] =(ctx->s[0]>>(24-i*8)); h[i+4] =(ctx->s[1]>>(24-i*8)); h[i+8] =(ctx->s[2]>>(24-i*8)); h[i+12]=(ctx->s[3]>>(24-i*8)); h[i+16]=(ctx->s[4]>>(24-i*8)); h[i+20]=(ctx->s[5]>>(24-i*8)); h[i+24]=(ctx->s[6]>>(24-i*8)); h[i+28]=(ctx->s[7]>>(24-i*8)); } } /** * precalculated CRC32c lookup table for polynomial 0x1EDC6F41 (castagnoli-crc) */ uint32_t crc32c_lookup[256]={ 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL, 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL, 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL, 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L, 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L, 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL, 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL, 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L, 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L, 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L, 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L, 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L, 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L, 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L, 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L, 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L, 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL, 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L, 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L, 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL, 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL, 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L, 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL, 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L, 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL, 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL, 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L, 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL, 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL, 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L, 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L, 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L, 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L, 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL, 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL, 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L }; uint32_t crc32_calc(char *start,int length) { uint32_t crc32_val=0; while(length--) crc32_val=(crc32_val>>8)^crc32c_lookup[(crc32_val&0xff)^(unsigned char)*start++]; return crc32_val; } /** * function to convert ascii to number */ int atoi(unsigned char*c) { int r=0; while(*c>='0'&&*c<='9') { r*=10; r+=*c-'0'; c++; } return r; } /** * convert ascii octal number to binary number */ int octbin(unsigned char *str,int size) { int s=0; unsigned char *c=str; while(size-->0){ s*=8; s+=*c-'0'; c++; } return s; } /** * convert ascii hex number to binary number */ int hexbin(unsigned char *str, int size) { int v=0; while(size-->0){ v <<= 4; if(*str>='0' && *str<='9') v += (int)((unsigned char)(*str)-'0'); else if(*str >= 'A' && *str <= 'F') v += (int)((unsigned char)(*str)-'A'+10); str++; } return v; } /** * print panic and halt */ void panic(char *str) { printf("BOOTBOOT-PANIC: %s\n", str); halt(); } /** * Read a line from console */ int ReadLine(unsigned char *buf, int l) { int i=0; char c; while(1) { c=getchar(); if(c=='\n' || c=='\r') { break; } else if(c==8) { if(i) i--; buf[i]=0; continue; } else if(c==27) { buf[0]=0; return 0; } else if(c && i> 8, 0x1F4); outb(start >> 16, 0x1F5); outb((start >> 24) | 0xE0, 0x1F6); outb(0x20, 0x1F7); // cmd 0x20 - read sectors while((inb(0x1F7) & 0xC0) != 0x40); insl(0x1F0, buf, 512/4); } return count; } #endif return 0; } // get filesystem drivers for initrd #include "fs.h" /** * Parse FS0:\BOOTBOOT\CONFIG or /sys/config */ void ParseEnvironment(uint8_t *env) { uint8_t *end=env+PAGESIZE, *start=env; DBG(" * Environment @%p %d bytes\n",env,env ? (int)strlen((char*)env) : 0); env--; env[PAGESIZE]=0; kne=NULL; while(envstart && env[-1]!=' '&&env[-1]!='\t'&&env[-1]!='\r'&&env[-1]!='\n') continue; // parse screen dimensions if(!memcmp(env,"screen=",7)){ env+=7; reqwidth=atoi(env); while(envfb_width=lib_sysinfo.framebuffer.x_resolution; bootboot->fb_height=lib_sysinfo.framebuffer.y_resolution; bootboot->fb_scanline=lib_sysinfo.framebuffer.bytes_per_line; bootboot->fb_ptr=(uint64_t)lib_sysinfo.framebuffer.physical_address; bootboot->fb_size=lib_sysinfo.framebuffer.y_resolution * lib_sysinfo.framebuffer.bytes_per_line; bootboot->fb_type=(!lib_sysinfo.framebuffer.blue_mask_pos ? FB_ARGB : ( !lib_sysinfo.framebuffer.red_mask_pos ? FB_ABGR : ( lib_sysinfo.framebuffer.blue_mask_pos == 8 ? FB_RGBA : FB_BGRA ))); DBG(" * Screen %d x %d, scanline %d, fb @%llx %d bytes, type %d %s\n", bootboot->fb_width, bootboot->fb_height, bootboot->fb_scanline, bootboot->fb_ptr, bootboot->fb_size,bootboot->fb_type, bootboot->fb_type==FB_ARGB?"ARGB":(bootboot->fb_type==FB_ABGR?"ABGR":( bootboot->fb_type==FB_RGBA?"RGBA":"BGRA"))); } /** * Locate and load the kernel in initrd */ void LoadCore() { uint64_t bss = 0, ptr; uint32_t r = 0; entrypoint=0; core.ptr=NULL; while(core.ptr==NULL && fsdrivers[r]!=NULL) { core=(*fsdrivers[r++])(initrd.ptr,kernelname); } if(kne!=NULL) *kne='\n'; // scan for the first executable if(core.ptr==NULL || core.size==0) { DBG(" * Autodetecting kernel%s\n",""); core.size=0; r=initrd.size; core.ptr=initrd.ptr; while(r-->0) { Elf64_Ehdr *ehdr=(Elf64_Ehdr *)(core.ptr); pe_hdr *pehdr=(pe_hdr*)(core.ptr + ((mz_hdr*)(core.ptr))->peaddr); if((!memcmp(ehdr->e_ident,ELFMAG,SELFMAG)||!memcmp(ehdr->e_ident,"OS/Z",4))&& ehdr->e_ident[EI_CLASS]==ELFCLASS64&& ehdr->e_ident[EI_DATA]==ELFDATA2LSB&& ehdr->e_machine==EM_X86_64&& ehdr->e_phnum>0){ core.size=1; break; } if(((mz_hdr*)(core.ptr))->magic==MZ_MAGIC && ((mz_hdr*)(core.ptr))->peaddr<65536 && pehdr->magic == PE_MAGIC && pehdr->machine == IMAGE_FILE_MACHINE_AMD64 && pehdr->file_type == PE_OPT_MAGIC_PE32PLUS) { core.size=1; break; } core.ptr++; } } if(core.ptr==NULL || core.size==0) { panic("Kernel not found in initrd"); } else { Elf64_Ehdr *ehdr=(Elf64_Ehdr *)(core.ptr); pe_hdr *pehdr=(pe_hdr*)(core.ptr + ((mz_hdr*)(core.ptr))->peaddr); if((!memcmp(ehdr->e_ident,ELFMAG,SELFMAG)||!memcmp(ehdr->e_ident,"OS/Z",4))&& ehdr->e_ident[EI_CLASS]==ELFCLASS64&& ehdr->e_ident[EI_DATA]==ELFDATA2LSB&& ehdr->e_machine==EM_X86_64&& ehdr->e_phnum>0){ DBG(" * Parsing ELF64 @%p\n",core.ptr); Elf64_Phdr *phdr=(Elf64_Phdr *)((uint8_t *)ehdr+ehdr->e_phoff); for(r=0;re_phnum;r++){ if(phdr->p_type==PT_LOAD && (phdr->p_vaddr >> 30) == 0x3FFFFFFFF) { core.ptr += phdr->p_offset; // hack to keep symtab and strtab for shared libraries core.size = phdr->p_filesz + (ehdr->e_type==3?0x4000:0); bss = phdr->p_memsz - core.size; core_addr = phdr->p_vaddr; entrypoint = ehdr->e_entry; break; } phdr=(Elf64_Phdr *)((uint8_t *)phdr+ehdr->e_phentsize); } if(ehdr->e_shoff > 0) { Elf64_Shdr *shdr=(Elf64_Shdr *)((uint8_t *)ehdr + ehdr->e_shoff), *sym_sh = NULL, *str_sh = NULL; Elf64_Shdr *strt=(Elf64_Shdr *)((uint8_t *)shdr+(uint64_t)ehdr->e_shstrndx*(uint64_t)ehdr->e_shentsize); Elf64_Sym *sym = NULL, *s; char *strtable = (char *)ehdr + strt->sh_offset; uint32_t strsz = 0, syment = 0, i; for(i = 0; i < ehdr->e_shnum; i++){ /* checking shdr->sh_type is not enough, there can be multiple SHT_STRTAB records... */ if(!memcmp(strtable + shdr->sh_name, ".symtab", 8)) sym_sh = shdr; if(!memcmp(strtable + shdr->sh_name, ".strtab", 8)) str_sh = shdr; shdr = (Elf64_Shdr *)((uint8_t *)shdr + ehdr->e_shentsize); } if(str_sh && sym_sh) { strtable = (char *)ehdr + str_sh->sh_offset; strsz = str_sh->sh_size; sym = (Elf64_Sym *)((uint8_t*)ehdr + sym_sh->sh_offset); syment = sym_sh->sh_entsize; if(str_sh->sh_offset && strsz > 0 && sym_sh->sh_offset && syment > 0) for(s = sym, i = 0; i<(strtable-(char*)sym)/syment && s->st_name < strsz; i++, s++) { if(!memcmp(strtable + s->st_name, "bootboot", 9)) bb_addr = s->st_value; if(!memcmp(strtable + s->st_name, "environment", 12)) env_addr = s->st_value; if(!memcmp(strtable + s->st_name, "mmio", 5)) mm_addr = s->st_value; if(!memcmp(strtable + s->st_name, "fb", 3)) fb_addr = s->st_value; if(!memcmp(strtable + s->st_name, "initstack", 10)) initstack = s->st_value; } } } } else if(((mz_hdr*)(core.ptr))->magic==MZ_MAGIC && ((mz_hdr*)(core.ptr))->peaddr<65536 && pehdr->magic == PE_MAGIC && pehdr->machine == IMAGE_FILE_MACHINE_AMD64 && pehdr->file_type == PE_OPT_MAGIC_PE32PLUS && (pehdr->code_base & 0xC0000000)) { DBG(" * Parsing PE32+ @%p\n",core.ptr); core.size = (pehdr->entry_point-pehdr->code_base) + pehdr->text_size + pehdr->data_size; bss = pehdr->bss_size; core_addr = (int64_t)pehdr->code_base; entrypoint = (int64_t)pehdr->entry_point; if(pehdr->sym_table > 0 && pehdr->numsym > 0) { pe_sym *s; char *strtable = (char *)pehdr + pehdr->sym_table + pehdr->numsym * 18 + 4, *name; uint32_t i; for(i = 0; i < pehdr->numsym; i++) { s = (pe_sym*)((uint8_t *)pehdr + pehdr->sym_table + i * 18); name = !s->iszero ? (char*)&s->iszero : strtable + s->nameoffs; if(!memcmp(name, "bootboot", 9)) bb_addr = (int64_t)s->value; if(!memcmp(name, "environment", 12)) env_addr = (int64_t)s->value; if(!memcmp(name, "mmio", 5)) mm_addr = (int64_t)s->value; if(!memcmp(name, "fb", 3)) fb_addr = (int64_t)s->value; if(!memcmp(name, "initstack", 10)) initstack = (int64_t)s->value; i += s->auxsyms; } } } } if(core.ptr==NULL || core.size<2 || entrypoint==0 || (core_addr&(PAGESIZE-1)) || (bb_addr>>30)!=0x3FFFFFFFF || (bb_addr & (PAGESIZE-1)) || (env_addr>>30)!=0x3FFFFFFFF || (env_addr&(PAGESIZE-1)) || (fb_addr>>30)!=0x3FFFFFFFF || (mm_addr & (PAGESIZE-1)) || (mm_addr>>30)!=0x3FFFFFFFF || (fb_addr & (1024*1024*2-1))) panic("Kernel is not a valid executable"); if(core.size+bss > 16*1024*1024) panic("Kernel is too big"); if(initstack < 1024) initstack = 1024; if(initstack > 16384) initstack = 16384; // create core segment ptr = ((bootboot->initrd_ptr+bootboot->initrd_size) + PAGESIZE - 1) & ~(PAGESIZE-1); memcpy((void*)(uintptr_t)ptr, core.ptr, core.size); core.ptr=(uint8_t*)(uintptr_t)ptr; if(bss>0) memset(core.ptr + core.size, 0, bss); core.size += bss; DBG(" * fb @%llx\n", fb_addr); DBG(" * bootboot @%llx\n", bb_addr); DBG(" * environment @%llx\n", env_addr); DBG(" * Entry point @%llx, text @%p %d bytes\n",entrypoint, core.ptr, core.size); if(initstack != 1024) DBG(" * Stack size %d bytes per core\n", (int)initstack); core.size = (core.size+PAGESIZE-1)&~(PAGESIZE-1); } /** * Add a mapping to paging tables */ int freep = 24; void MapPage(uint64_t virt, uint64_t phys) { int i,j; j = (virt>>(9+12)) & 0x1FF; if(!paging[22*512 + j] || (paging[22*512 + j] & 0xFF) == 0x83) { if(freep == 37) return; paging[22*512 + j]=(uint64_t)((uintptr_t)paging+freep*PAGESIZE+3); freep++; } i = (paging[22*512 + j] - (uint64_t)((uintptr_t)paging)) >> 12; j = (virt>>(12)) & 0x1FF; paging[i*512 + j] = phys | 1; } /** * bootboot entry point, run only on BSP core */ int main(void) { unsigned int ret=0, i, dsk, bad_madt=0; uint8_t *pe, *ptr; uint32_t np, sp, r; unsigned char *data; #if IS_ENABLED(CONFIG_LP_SERIAL_CONSOLE) /* ridiculous, libpayload is a huge library, yet has no function to initialize serial port properly... */ __asm__ __volatile__( "movl %0, %%edx;" "xorb %%al, %%al;outb %%al, %%dx;" /* IER int off */ "movb $0x80, %%al;addb $2,%%dl;outb %%al, %%dx;" /* LCR set divisor mode */ "movb $1, %%al;subb $3,%%dl;outb %%al, %%dx;" /* DLL divisor lo 115200 */ "xorb %%al, %%al;incb %%dl;outb %%al, %%dx;" /* DLH divisor hi */ "incb %%dl;outb %%al, %%dx;" /* FCR fifo off */ "movb $0x43, %%al;incb %%dl;outb %%al, %%dx;" /* LCR 8N1, break on */ "movb $0x8, %%al;incb %%dl;outb %%al, %%dx;" /* MCR Aux out 2 */ "xorb %%al, %%al;subb $4,%%dl;inb %%dx, %%al" /* clear receiver/transmitter */ : : "a"(CONFIG_LP_SERIAL_IOBASE + 1): ); #endif video_init(); #if IS_ENABLED(CONFIG_LP_STORAGE) storage_initialize(); ahcicount = storage_device_count(); #endif #if IS_ENABLED(CONFIG_LP_USB) usb_initialize(); usb_poll(); // this calls usbdisk_create() if it detects any USB storages #endif video_console_clear(); printf("Booting OS...\n"); memset(bootboot, 0, 2*PAGESIZE); memcpy(bootboot->magic, BOOTBOOT_MAGIC, 4); bootboot->protocol = PROTOCOL_DYNAMIC | LOADER_COREBOOT; bootboot->size = 128; __asm__ __volatile__ ( "movl $1, %%eax;" "cpuid;" "shrl $4, %%eax;" "shrl $24, %%ebx;" "movw %%bx,%0;" "movl %%eax,%1;" : "=b"(bootboot->bspid),"=a"(ret) : : ); /* check processor */ __asm__ __volatile__ ( "xorl %%edx, %%edx;" "movl $0x80000001, %%eax;" "cpuid;" "movl %%edx,%0;" : "=d"(i) : : ); if((ret & 0xFFFF) < 0x0600 || !(i & (1<<29))) panic("Hardware not supported"); /* we check this here as early as possible, because GetLFB() can't change the resolution */ if(!lib_sysinfo.framebuffer.physical_address || !lib_sysinfo.framebuffer.bytes_per_line) panic("coreboot compiled without LINEAR_FRAMEBUFFER"); if(lib_sysinfo.framebuffer.bits_per_pixel != 32) panic("not 32 bit pixel format, no framebuffer"); DBG(" * Locate initrd in Flash ROM%s\n",""); /* if it's a Flashmap area */ initrd.ptr = 0; initrd.size = 0; struct fmap *fm = (struct fmap*)lib_sysinfo.fmap_cache; if(fm && !memcmp(fm->signature, FMAP_SIGNATURE, 8) && fm->nareas > 0) for(i = 0; i < fm->nareas; i++) if(!memcmp(fm->areas[i].name, "INITRD", 7)) { initrd.ptr = (uint8_t*)lib_sysinfo.fmap_cache + fm->areas[i].offset; initrd.size = fm->areas[i].size; break; } #if IS_ENABLED(CONFIG_LP_CBFS) /* if it's on the CBFS filesystem in the COREBOOT Flashmap area */ if(!initrd.ptr || !initrd.size) { size_t s; initrd.ptr = cbfs_map("bootboot/initrd", &s); if(initrd.ptr && s > 1 && s < 16*1024*1024) initrd.size = s; else initrd.ptr = NULL; } #endif // skip optional ROM header if(initrd.ptr && initrd.ptr[0]==0x55 && initrd.ptr[1]==0xAA && initrd.ptr[8]=='I') { initrd.ptr += 32; initrd.size -= 32; } #if IS_ENABLED(CONFIG_LP_SERIAL_CONSOLE) /* try to receive the initrd from serial line */ if(!initrd.ptr || !initrd.size) { while(serial_havechar()) serial_getchar(); serial_putchar(3); serial_putchar(3); serial_putchar(3); mdelay(10); if(serial_havechar()) { // we got response from raspbootcom or USBImager sp=serial_getchar(); sp|=serial_getchar()<<8; sp|=serial_getchar()<<16; sp|=serial_getchar()<<24; if(sp>0 && sp127) np=127; // read GPT entries if(!disk_read(dsk, *((uint32_t*)(pe+72)), (np*sp+511)/512, pe)) continue; efipart_t *part=NULL; // first, look for a partition with bootable flag for(r=0;rtype[0]==0 && part->type[1]==0 && part->type[2]==0 && part->type[3]==0) || part->start==0) { r=np; break; } // EFI_PART_USED_BY_OS? if(part->flags&4) break; } // if none, look for specific partition types if(part==NULL || r>=np) { for(r=0;rtype[0]==0 && part->type[1]==0 && part->type[2]==0 && part->type[3]==0) || part->start==0) { r=np; break; } // ESP? if((part->type[0]==0xC12A7328 && part->type[1]==0x11D2F81F) || // or OS/Z root partition for this architecture? (part->type[0]==0x5A2F534F && (part->type[1]&0xFFFF)==0x8664 && part->type[3]==0x746F6F72)) break; } } if(part && rstart, end = part->end; if(!disk_read(dsk, start, 1, pe)) continue; //is it a FAT partition? bpb_t *bpb=(bpb_t*)pe; if(!memcmp((void*)bpb->fst,"FAT16",5) || !memcmp((void*)bpb->fst2,"FAT32",5)) { // locate BOOTBOOT directory uint64_t data_sec, root_sec, clu=0, cclu=0, s, s2, s3; fatdir_t *dir; uint32_t *fat32=(uint32_t*)(pe+512); uint16_t *fat16=(uint16_t*)fat32; uint8_t *ptr; data_sec=root_sec=((bpb->spf16?bpb->spf16:bpb->spf32)*bpb->nf)+bpb->rsc; s=(bpb->nr0+(bpb->nr1<<8))*sizeof(fatdir_t); if(bpb->spf16>0) { data_sec+=(s+511)>>9; } else { root_sec+=(bpb->rc-2)*bpb->spc; } s3=bpb->spc*512; // load fat table r=disk_read(dsk, start+1, (bpb->spf16?bpb->spf16:bpb->spf32)+bpb->rsc, pe + 512); if(!r) continue; pe+=(r+1)*512; // load root directory r=disk_read(dsk, start+root_sec, s/512+1, pe); if(!r) continue; dir=(fatdir_t*)pe; while(dir->name[0]!=0 && memcmp(dir->name,"BOOTBOOT ",11)) dir++; if(dir->name[0]!='B') continue; r=disk_read(dsk, start+(dir->cl+(dir->ch<<16)-2)*bpb->spc+data_sec, bpb->spc, pe); if(!r) continue; dir=(fatdir_t*)pe; // locate environment and initrd while(dir->name[0]!=0) { if(!memcmp(dir->name,"CONFIG ",11)) { s=dir->sizesize:PAGESIZE; // round up to cluster size cclu=dir->cl+(dir->ch<<16); ptr=(uint8_t*)environment; while(s>0) { s2=s>s3?s3:s; r=disk_read(dsk, start+(cclu-2)*bpb->spc+data_sec,s2<512?1:(s2+511)/512,ptr); cclu=bpb->spf16>0?fat16[cclu]:fat32[cclu]; ptr+=s2; s-=s2; } } else if(!memcmp(dir->name,fn,11)) { clu=dir->cl+(dir->ch<<16); initrd.size=dir->size; } dir++; } // if initrd not found, try architecture specific name if(clu==0) { dir=(fatdir_t*)pe; while(dir->name[0]!=0) { if(!memcmp(dir->name,"X86_64 ",11)) { clu=dir->cl+(dir->ch<<16); initrd.size=dir->size; break; } dir++; } } // walk through cluster chain to load initrd if(clu!=0 && initrd.size!=0) { initrd.ptr=ptr=(uint8_t*)INITRD_BASE; s=initrd.size; while(s>0) { s2=s>s3?s3:s; r=disk_read(dsk, start+(clu-2)*bpb->spc+data_sec,s2<512?1:(s2+511)/512,ptr); clu=bpb->spf16>0?fat16[clu]:fat32[clu]; ptr+=s2; s-=s2; } } } else { // initrd is on the entire partition r=disk_read(dsk, start,end-start,(uint8_t*)INITRD_BASE); if(!r) continue; initrd.ptr=(uint8_t*)INITRD_BASE; initrd.size=r*512; } } } } if(!initrd.ptr || !initrd.size) panic("Initrd not found"); data = initrd.ptr == (uint8_t*)INITRD_BASE ? (uint8_t*)(((uintptr_t)initrd.ptr+initrd.size+PAGESIZE-1) & ~(PAGESIZE-1)) : (uint8_t*)INITRD_BASE; // check if initrd is gzipped if(initrd.ptr[0]==0x1f && initrd.ptr[1]==0x8b) { unsigned char *addr,f; int len=0, r; TINF_DATA d; DBG(" * Gzip compressed initrd @%p %d bytes\n",initrd.ptr,initrd.size); // skip gzip header addr=initrd.ptr+2; if(*addr++!=8) goto gzerr; f=*addr++; addr+=6; if(f&4) { r=*addr++; r+=(*addr++ << 8); addr+=r; } if(f&8) { while(*addr++ != 0); } if(f&16) { while(*addr++ != 0); } if(f&2) addr+=2; d.source = addr; memcpy(&len,initrd.ptr+initrd.size-4,4); // decompress d.bitcount = 0; d.bfinal = 0; d.btype = -1; d.curlen = 0; d.dest = data; d.destSize = len; do { r = uzlib_uncompress(&d); } while (!r); if (r != TINF_DONE) { gzerr: panic("Unable to uncompress"); } // swap initrd.ptr with the uncompressed buffer initrd.ptr=data; initrd.size=len; } #if IS_ENABLED(CONFIG_LP_LZMA) // check if initrd is compressed with xz if(initrd.ptr[0] == 0xFD && initrd.ptr[1] == '7' && initrd.ptr[2] == 'z' && initrd.ptr[3] == 'X' && initrd.ptr[4] == 'Z') { DBG(" * Xz (lzma) compressed initrd @%p %d bytes\n",initrd.ptr,initrd.size); initrd.size = ulzma(initrd.ptr, data); initrd.ptr = data; if(initrd.size < 1) goto gzerr; } #endif #if IS_ENABLED(CONFIG_LP_LZ4) // check if initrd is compressed with lz4 if(initrd.ptr[0] == 0x04 && initrd.ptr[1] == 0x22 && initrd.ptr[2] == 0x4D && initrd.ptr[3] == 0x18) { DBG(" * Lz4 compressed initrd @%p %d bytes\n",initrd.ptr,initrd.size); initrd.size = ulz4f(initrd.ptr, data); initrd.ptr = data; if(initrd.size < 1) goto gzerr; } #endif DBG(" * Initrd loaded @%p %d bytes\n",initrd.ptr,initrd.size); bootboot->initrd_ptr = (uint64_t)(uintptr_t)initrd.ptr; bootboot->initrd_size = initrd.size; if(!environment[0]) { // if there's no environment file on boot partition, find it inside the INITRD file_t ret; i=0; ret.ptr=NULL; ret.size=0; while(ret.ptr==NULL && fsdrivers[i]!=NULL) { ret=(*fsdrivers[i++])(initrd.ptr,cfgname); } if(ret.ptr!=NULL) memcpy(environment, ret.ptr, ret.size 1 && s < PAGESIZE-1) memcpy(environment, ptr, s); } #endif ParseEnvironment((uint8_t*)environment); GetLFB(); DBG(" * System tables%s\n",""); for(data = (unsigned char*)phys_to_virt(0x000f0000); data < (unsigned char*)phys_to_virt(0x00100000); data += 16) { if(!memcmp(data, "RSD PTR ", 8)) bootboot->arch.x86_64.acpi_ptr = *((uint64_t*)(data + 24)) ? *((uint64_t*)(data + 24)) : (uint64_t)(*((uint32_t*)(data + 16))); if(!memcmp(data, "_SM_", 4)) bootboot->arch.x86_64.smbi_ptr = (uint64_t)(uintptr_t)data; if(!memcmp(data, "_MP_", 4)) bootboot->arch.x86_64.mp_ptr = (uint64_t)(uintptr_t)data; } #if IS_ENABLED(CONFIG_LP_NVRAM) struct tm t; rtc_read_clock(&t); bootboot->datetime[0]=dec2bcd((t.tm_year+1900)/100); bootboot->datetime[1]=dec2bcd((t.tm_year+1900)%100); bootboot->datetime[2]=dec2bcd(t.tm_mon+1); bootboot->datetime[3]=dec2bcd(t.tm_mday); bootboot->datetime[4]=dec2bcd(t.tm_hour); bootboot->datetime[5]=dec2bcd(t.tm_min); bootboot->datetime[6]=dec2bcd(t.tm_sec); bootboot->datetime[7]=t.tm_isdst; DBG(" * System time %d-%02d-%02d %02d:%02d:%02d GMT%s%d:%02d %s\n", t.tm_year+1900,t.tm_mon+1,t.tm_mday,t.tm_hour,t.tm_min,t.tm_sec, bootboot->timezone>=0?"+":"",bootboot->timezone/60,bootboot->timezone%60, t.tm_isdst?"summertime":""); #endif // locate sys/core LoadCore(); /* Symmetric Multi Processing support */ memset(lapic_ids, 0xFF, sizeof(lapic_ids)); ptr = (uint8_t*)(uintptr_t)bootboot->arch.x86_64.acpi_ptr; if(ptr && (ptr[0]=='X' || ptr[0]=='R') && ptr[1]=='S' && ptr[2]=='D' && ptr[3]=='T') { pe = ptr; ptr += 36; // iterate on ACPI table pointers for(r = *((uint32_t*)(pe + 4)); ptr < pe + r; ptr += pe[0] == 'X' ? 8 : 4) { data = (uint8_t*)(uintptr_t)(pe[0] == 'X' ? *((uint64_t*)ptr) : *((uint32_t*)ptr)); if(!memcmp(data, "APIC", 4)) { // found MADT, iterate on its variable length entries lapic_addr = (uint64_t)(*((uint32_t*)(data+0x24))); for(r = *((uint32_t*)(data + 4)), ptr = data + 44, i = 0; ptr < data + r && i < (int)(sizeof(lapic_ids)/sizeof(lapic_ids[0])); ptr += ptr[1]) { switch(ptr[0]) { case 0: // found Processor Local APIC if((ptr[4] & 1) && ptr[3] != 0xFF && lapic_ids[(int)ptr[3]] == 0xFFFF) lapic_ids[(int)ptr[3]] = i++; else bad_madt++; break; case 5: lapic_addr = *((uint64_t*)(ptr+4)); break; // found 64 bit Local APIC Address } } if(i && lapic_ids[bootboot->bspid] != 0xFFFF) bootboot->numcores = i; break; } } } // disable PIC and NMI __asm__ __volatile__ ( "movb $0xFF, %%al; outb %%al, $0x21; outb %%al, $0xA1;" // disable PIC "inb $0x70, %%al; orb $0x80, %%al; outb %%al, $0x70;" // disable NMI : : :); if(!nosmp && bootboot->numcores > 1 && lapic_addr) { DBG(" * SMP numcores %d%s\n", bootboot->numcores, bad_madt ? " (bad MADT)" : ""); memcpy((uint8_t*)0x1000, (void*)&ap_trampoline, 128); // enable Local APIC *((volatile uint32_t*)((uintptr_t)lapic_addr + 0x0D0)) = (1 << 24); *((volatile uint32_t*)((uintptr_t)lapic_addr + 0x0E0)) = 0xFFFFFFFF; *((volatile uint32_t*)((uintptr_t)lapic_addr + 0x0F0)) = *((volatile uint32_t*)((uintptr_t)lapic_addr + 0x0F0)) | 0x100; *((volatile uint32_t*)((uintptr_t)lapic_addr + 0x080)) = 0; // make sure we use the correct Local APIC ID for the BSP bootboot->bspid = *((volatile uint32_t*)((uintptr_t)lapic_addr + 0x20)) >> 24; #if 0 // use broadcast IPI if MADT is okay (no bcast id and all CPUs enabled) if(!bad_madt) { // send Broadcast INIT IPI *((volatile uint32_t*)((uintptr_t)lapic_addr + 0x300)) = 0x0C4500; mdelay(10); // wait 10 msec // send Broadcast STARTUP IPI *((volatile uint32_t*)((uintptr_t)lapic_addr + 0x300)) = 0x0C4601;// start at 0100:0000h udelay(200); // wait 10 msec // send second SIPI *((volatile uint32_t*)((uintptr_t)lapic_addr + 0x300)) = 0x0C4601; udelay(200); // wait 10 msec } else #endif { // supports up to 255 cores (lapicid 255 is bcast address), requires x2APIC to have more for(i = 0; i < 255; i++) { if(i == bootboot->bspid || lapic_ids[i] == 0xFFFF) continue; *((volatile uint32_t*)((uintptr_t)lapic_addr + 0x280)) = 0; // clear APIC errors r = *((volatile uint32_t*)((uintptr_t)lapic_addr + 0x280)); send_ipi(i, 0xfff00000, 0x00C500); // trigger INIT IPI udelay(200); send_ipi(i, 0xfff00000, 0x008500); // deassert INIT IPI } mdelay(10); // wait 10 msec for(i = 0; i < 255; i++) { if(i == bootboot->bspid || lapic_ids[i] == 0xFFFF) continue; *((volatile uint8_t*)0x1011) = 0; send_ipi(i, 0xfff0f800, 0x004601); // trigger SIPI, start at 0100:0000h for(r = 250; !*((volatile uint8_t*)0x1011) && r > 0; r--) udelay(200);// wait for AP with 50 msec timeout if(!*((volatile uint8_t*)0x1011)) { send_ipi(i, 0xfff0f800, 0x004601); mdelay(50); } } } } else { bootboot->numcores = 1; lapic_addr = 0; lapic_ids[bootboot->bspid] = 0; } /* Create paging tables */ DBG(" * Pagetables PML4 @%p\n", (void*)paging); memset(paging, 0, (37+(bootboot->numcores*initstack+PAGESIZE-1)/PAGESIZE)*PAGESIZE); //PML4 paging[0]=(uint64_t)((uintptr_t)paging+PAGESIZE)+3; // pointer to 2M PDPE (16G RAM identity mapped) paging[511]=(uint64_t)((uintptr_t)paging+20*PAGESIZE)+3; // pointer to 4k PDPE (core mapped at -2M) //identity mapping //2M PDPE for(i=0;i<16;i++) paging[512+i]=(uint64_t)((uintptr_t)paging+(3+i)*PAGESIZE+3); //first 2M mapped per page paging[3*512]=(uint64_t)((uintptr_t)paging+2*PAGESIZE+3); for(i=0;i<512;i++) paging[2*512+i]=(uint64_t)(i*PAGESIZE+3); //2M PDE for(i=1;i<512*16;i++) paging[3*512+i]=(uint64_t)((i<<21)+0x83); //kernel mapping //4k PDPE paging[20*512+511]=(uint64_t)((uintptr_t)paging+22*PAGESIZE+3); //4k PDE r = (fb_addr>>(9+12)) & 0x1FF; for(i=0;r+i<511 && i<63;i++) paging[22*512+r+i]=(uint64_t)(((uintptr_t)(bootboot->fb_ptr)+(i<<21))+0x83); // map framebuffer paging[22*512+511]=(uint64_t)((uintptr_t)paging+23*PAGESIZE+3); //4k PT //dynamically map these. Main struct, environment string and code segment for(i=0;i<(core.size/PAGESIZE);i++) MapPage(core_addr + i*PAGESIZE, (uint64_t)((uintptr_t)core.ptr+i*PAGESIZE+3)); MapPage(bb_addr, (uint64_t)((uintptr_t)bootboot)+1); MapPage(env_addr, (uint64_t)((uintptr_t)environment)+1); // stack at the top of the memory for(i=0; i<((bootboot->numcores*initstack+PAGESIZE-1)/PAGESIZE); i++) { if(paging[23*512+511-i]) panic("Stack smash"); paging[23*512+511-i]=(uint64_t)((uintptr_t)paging+(37+i)*PAGESIZE+3); // core stacks } /* Get memory map */ uint64_t srt, end, ldrend = (uintptr_t)paging + (37+(bootboot->numcores*initstack+PAGESIZE-1)/PAGESIZE)*PAGESIZE; uint64_t iniend = (uint64_t)(uintptr_t)core.ptr + core.size, a, b; MMapEnt *mmapent=(MMapEnt *)&(bootboot->mmap), *sort; for (i = 0; (int)i < lib_sysinfo.n_memranges; i++) { srt = lib_sysinfo.memrange[i].base; end = srt + lib_sysinfo.memrange[i].size; srt = (srt + PAGESIZE-1) & ~(PAGESIZE-1); end &= ~(PAGESIZE-1); r = lib_sysinfo.memrange[i].type == CB_MEM_RAM || lib_sysinfo.memrange[i].type == CB_MEM_TABLE ? MMAP_FREE : ( lib_sysinfo.memrange[i].type == CB_MEM_ACPI || lib_sysinfo.memrange[i].type == CB_MEM_NVS ? MMAP_ACPI : MMAP_USED); /* exclude two intervals: 0 - ldrend and INITRD_BASE - iniend */ // +--------+ // +--------------+ if(end <= ldrend) continue; // +--------+ // +--------------+ if(srt < ldrend && ldrend < end) srt = ldrend; // +---+ // +--------+ if(srt >= INITRD_BASE && end <= iniend) continue; // +----------+ // +--------+ if(srt > INITRD_BASE && srt < iniend && iniend < end) srt = iniend; else // +----------+ // +--------+ if(srt < INITRD_BASE && end > INITRD_BASE && end < iniend) end = iniend; else // +------------------+ // +--------+ if(srt < INITRD_BASE && end > iniend) { // split into two regions mmapent->ptr = srt; mmapent->size = (INITRD_BASE - srt) | (r & 0xF); // bubble up record. It's okay to use an ineffective, but simple sort here, because there are no // more records than a couple hundred, and in most scenearios they are already sorted, meaning this // loop will never run at all. But in the unlikely event they aren't sorted, this will fix that. for(sort = mmapent - 1; sort >= (MMapEnt *)&(bootboot->mmap) && sort[0].ptr > sort[1].ptr; sort--) { a = sort[0].ptr; b = sort[0].size; sort[0].ptr = sort[1].ptr; sort[0].size = sort[1].size; sort[1].ptr = a; sort[1].size = b; } mmapent++; bootboot->size += sizeof(MMapEnt); srt = iniend; } if(srt >= end) continue; // merge contiguous areas */ if(bootboot->size > 128 && MMapEnt_Type(mmapent) == r && MMapEnt_Ptr(mmapent) + MMapEnt_Size(mmapent) == srt) { mmapent->size += end - srt; } else { mmapent->ptr = srt; mmapent->size = (end - srt) | (r & 0xF); for(sort = mmapent - 1; sort >= (MMapEnt *)&(bootboot->mmap) && sort[0].ptr > sort[1].ptr; sort--) { a = sort[0].ptr; b = sort[0].size; sort[0].ptr = sort[1].ptr; sort[0].size = sort[1].size; sort[1].ptr = a; sort[1].size = b; } mmapent++; bootboot->size += sizeof(MMapEnt); } } DBG(" * Memory Map @%lx %d bytes\n",(uintptr_t)&(bootboot->mmap), bootboot->size - 128); /* clear the screen */ for(np=sp=0;spfb_height;sp++) { r=np; for(i=0;ifb_width;i+=2,r+=8) *((uint64_t*)(uintptr_t)(bootboot->fb_ptr + r))=0; np+=bootboot->fb_scanline; } /* continue in Assembly, enable long mode and jump to kernel's entry point */ #if __WORDSIZE == 64 bsp64_init(bootboot->bspid); #else bsp_init(); #endif return 0; }