/* * Copyright 2002-04 Guido Guenther * * based on arcboot/ext2load/loader.c * */ #include #include #include #include #include #include #include #include #include #include #include #define KSEG0ADDR(addr) (((addr) & 0x1fffffff) | 0x80000000) #define ANSI_CLEAR "\033[2J" extern void* __kernel_start; extern void* __kernel_end; extern void* __rd_start; extern void* __rd_end; /* convert an offset in the kernel image to an address in the loaded tftpboot image */ static void* offset2addr(unsigned long offset) { void* address = (void*)((unsigned long)&(__kernel_start) + offset); return address; } /* copy program segments to the locations the kernel expects */ static unsigned long CopyProgramSegments32(Elf32_Ehdr * header) { int idx; int loaded = 0; Elf32_Phdr *segment, *segments; size_t size = header->e_phentsize * header->e_phnum; unsigned long kernel_end=0L; if (size <= 0) prom_fatal("No program segments\n\r"); segments = malloc(size); if (segments == NULL) prom_fatal("Cannot allocate memory for segment headers\n\r"); segments = (Elf32_Phdr*)offset2addr(header->e_phoff); segment = segments; for (idx = 0; idx < header->e_phnum; idx++) { if (segment->p_type == PT_LOAD) { printf ("Loading program segment %u at 0x%x, size = 0x%x\n\r", idx + 1, KSEG0ADDR(segment->p_vaddr), segment->p_filesz); memcpy((void *)segment->p_vaddr, offset2addr(segment->p_offset), segment->p_filesz); /* determine the highest address used by the kernel's memory image */ if( kernel_end < segment->p_vaddr + segment->p_memsz ) { kernel_end = segment->p_vaddr + segment->p_memsz; } size = segment->p_memsz - segment->p_filesz; if (size > 0) { printf ("Zeroing memory at 0x%x, size = 0x%x\n\r", (KSEG0ADDR(segment->p_vaddr + segment->p_filesz)), size); memset((void *) (KSEG0ADDR(segment-> p_vaddr + segment->p_filesz)), 0, size); } loaded = 1; } segment = (Elf32_Phdr *) (((char *) segment) + header->e_phentsize); } if (!loaded) prom_fatal("No loadable program segments found\n\r"); free(segments); return kernel_end; } static unsigned long CopyProgramSegments64(Elf64_Ehdr * header) { int idx; int loaded = 0; Elf64_Phdr *segment, *segments; unsigned long size = header->e_phentsize * header->e_phnum; unsigned long kernel_end=0L; if (size <= 0) prom_fatal("No program segments\n\r"); segments = malloc(size); if (segments == NULL) prom_fatal("Cannot allocate memory for segment headers\n\r"); segments = (Elf64_Phdr*)offset2addr(header->e_phoff); segment = segments; for (idx = 0; idx < header->e_phnum; idx++) { if (segment->p_type == PT_LOAD) { printf ("Loading program segment %u at 0x%x, " "size = 0x%lx %lx\n\r", idx + 1, (int)KSEG0ADDR(segment->p_vaddr), (long)(segment->p_filesz>>32), (long)(segment->p_filesz&0xffffffff)); memcpy((void *)(long)(segment->p_vaddr), offset2addr(segment->p_offset), segment->p_filesz); /* determine the highest address used by the kernel's memory image */ if( kernel_end < segment->p_vaddr + segment->p_memsz ) { kernel_end = segment->p_vaddr + segment->p_memsz; } size = (unsigned long)segment->p_memsz - (unsigned long)segment->p_filesz; if (size > 0) { printf ("Zeroing memory at 0x%lx, size = 0x%lx\n\r", (KSEG0ADDR((unsigned long)segment->p_vaddr + (unsigned long)segment->p_filesz)), size); memset((void *) (KSEG0ADDR((unsigned long)segment->p_vaddr + (unsigned long)segment->p_filesz)), 0, size); } loaded = 1; } segment = (Elf64_Phdr *) (((char *) segment) + header->e_phentsize); } if (!loaded) prom_fatal("No loadable program segments found\n\r"); free(segments); return kernel_end; } static unsigned long CopyKernel(unsigned long* kernel_end) { Elf32_Ehdr *header = (Elf32_Ehdr*)offset2addr(0L); Elf64_Ehdr *header64 = (Elf64_Ehdr*)header; if (memcmp(&(header->e_ident[EI_MAG0]), ELFMAG, SELFMAG) != 0) prom_fatal("Not an ELF file\n\r"); if (header->e_ident[EI_CLASS] == ELFCLASS32) { if (header->e_ident[EI_DATA] != ELFDATA2MSB) prom_fatal("Not a big-endian file\n\r"); if (header->e_ident[EI_VERSION] != EV_CURRENT) prom_fatal("Wrong ELF version\n\r"); if (header->e_type != ET_EXEC) prom_fatal("Not an executable file\n\r"); if (header->e_machine != EM_MIPS) prom_fatal("Unsupported machine type\n\r"); if (header->e_version != EV_CURRENT) prom_fatal("Wrong ELF version\n\r"); (*kernel_end) = CopyProgramSegments32(header); printf("Kernel entry point is 0x%lx\n\r", ((unsigned long) KSEG0ADDR(header->e_entry))); return KSEG0ADDR(header->e_entry); } else if (header->e_ident[EI_CLASS] == ELFCLASS64) { if (header64->e_ident[EI_DATA] != ELFDATA2MSB) prom_fatal("Not a big-endian file\n\r"); if (header64->e_ident[EI_VERSION] != EV_CURRENT) prom_fatal("Wrong ELF version\n\r"); if (header64->e_type != ET_EXEC) prom_fatal("Not an executable file\n\r"); if (header64->e_machine != EM_MIPS) prom_fatal("Unsupported machine type\n\r"); if (header64->e_version != EV_CURRENT) prom_fatal("Wrong ELF version\n\r"); (*kernel_end) = CopyProgramSegments64(header64); printf("Kernel entry point is 0x%lx\n\r", ((unsigned long)KSEG0ADDR(header64->e_entry))); return KSEG0ADDR(header64->e_entry); } else prom_fatal("Neither an ELF32 nor an ELF64 kernel\n\r"); return 0L; } static void copyRamdisk(void* rd_vaddr, void* rd_start, unsigned long rd_size) { printf("Copying initrd from 0x%p to 0x%p (0x%lx bytes)...\n\r", rd_start, rd_vaddr, rd_size); memcpy(rd_vaddr, rd_start, rd_size); printf("Initrd copied.\n\r"); } void _start(long argc, char * argv[], char * envp[]) { char* nargv[3]; int nargc,i; char argv_rd[128]; /* passed to the kernel on its commandline */ unsigned long kernel_end = 0L; unsigned long rd_size= ((char*)&__rd_end) - ((char*)&__rd_start); char* rd_vaddr=NULL; void (*kernel_entry)(int argc, char * argv[], char * envp[]); /* Print identification */ #if (SUBARCH == IP22) printf(ANSI_CLEAR "\n\rtip22: IP22 Linux tftpboot loader " __ARCSBOOT_VERSION__ "\n\r"); #elif (SUBARCH == IP32) printf(ANSI_CLEAR "\n\rtip32: IP32 Linux tftpboot loader " __ARCSBOOT_VERSION__ "\n\r"); #endif prom_init_malloc(); /* copy kernel and ramdisk to its load addresses */ #ifdef DEBUG printf("Embedded kernel image starts 0x%p, ends 0x%p\n\r", &__kernel_start, &__kernel_end); printf("Embedded ramdisk image starts 0x%p, ends 0x%p\n\r", &__rd_start, &__rd_end); #endif kernel_entry = (void (*)(int, char *[], char *[]))CopyKernel(&kernel_end); /* align to page boundary */ #if (SUBARCH != IP22) rd_vaddr = (char*)(((kernel_end + PAGE_SIZE) / PAGE_SIZE ) * PAGE_SIZE); #else rd_vaddr = (char*)0x88800000; #endif copyRamdisk( rd_vaddr, (char*)&__rd_start, rd_size); /* tell the kernel about the ramdisk */ sprintf(argv_rd, "rd_start=0x%p rd_size=0x%lx", rd_vaddr, rd_size); nargv[0] = argv[0]; nargv[1] = argv_rd; nargc=2; for(i=1; i < argc; i++) { if( !memcmp(argv[i],"append=",7) ) break; } if( i < argc ) { /* we're asked to pass s.th. to the kernel */ nargv[2] = argv[i]+7; nargc++; } #ifdef DEBUG printf("Arguments passed to kernel:\n\r"); for(i = 0; i < nargc; i++ ) printf("%u: %s\n\r", i, nargv[i]); prom_wait("\n\r--- Debug: press to boot kernel ---"); #endif /* Finally jump into the kernel */ printf("Starting kernel...\n\r"); prom_flush_cache_all(); if( kernel_entry ) (*kernel_entry)(nargc ,nargv, envp); else printf("Invalid kernel entry NULL\n\r"); /* Not likely to get back here in a functional state, * but what the heck */ prom_wait("\n\r--- Press to restart ---"); prom_restart(); }