// Called by _start. Physical addressing is in effect, but this code is // linked assuming virtual addresses: only PC-relative references actually // work. A small stack is available. void boot_start() { // Clear some bootdata values bootdata->initrd_size = 0; // Work out phys->virt offset bootdata->phys_to_virt = (uint32_t)&_start - bootdata->rom_base; // Print all the info we received from the assembly entry point DBGSTR("Pycorn bootstrap entered\n"); DBGINT("rom base: ", bootdata->rom_base); DBGINT("machine type: ", bootdata->machtype); DBGINT("taglist ptr: ", bootdata->taglist_ptr); DBGINT("phys->virt: ", bootdata->phys_to_virt); // Work out section sizes uint32_t text_size = &__text_end__ - &__text_start__; uint32_t data_size = &__data_end__ - &__data_start__; uint32_t bss_size = &__bss_end__ - &__bss_start__; uint32_t heap_size = &__heap_end__ - &__heap_start__; uint32_t stack_size = &__stack_end__ - &__stack_start__; uint32_t img_size = text_size + data_size; // Parse atags int r = parse_atags(); DBGINT("parse_atags returned ", r); (void)r; // Work out where the first free page after the image is. // We will use this as the starting location to allocate pages, so there // had better be some megabytes of memory here. This will change later to // a less stupid allocator. bootdata->next_free_page = bootdata->rom_base + img_size; DBGINT("first free page: ", bootdata->next_free_page); // Allocate page directory DBGSTR("Allocate page directory\n"); bootdata->page_directory = alloc_pages_zero(PAGEDIR_SIZE, PAGEDIR_SIZE); DBGINT("page directory: ", bootdata->page_directory); // Set MMU base address DBGSTR("Set MMU base address\n"); mmu_set_base(bootdata->page_directory); // Allocate and map page table mappings // Page tables are placed linearly at a fixed location to make // it possible to find them again later without having to remember // where they are. // This is kinda scary as we are bootstrapping :) DBGSTR("Allocate and map page table mappings\n"); int ptbl_section = (virtaddr)(&__page_tbl_start__) >> SECTION_SHIFT; physaddr ptbl_map = get_page_table(ptbl_section, 1); virtaddr ptbl_address = (virtaddr)(&__page_tbl_start__) + (ptbl_section * PAGETABLE_SIZE); map_pages(ptbl_address, ptbl_address + (PAGE_SIZE * PTBLS_PER_PAGE), ptbl_map | PTB_RW | PTB_CACHE | PTB_BUFF | PTB_EXT); // Map page directory DBGSTR("Map page directory\n"); map_pages((virtaddr)&__page_dir_virt__, (virtaddr)(&__page_dir_virt__ + PAGEDIR_SIZE), bootdata->page_directory | PTB_RW | PTB_CACHE | PTB_BUFF | PTB_EXT); // Map text section of image DBGSTR("Map text section\n"); map_pages((virtaddr)&__text_start__, (virtaddr)&__text_end__, bootdata->rom_base | PTB_ROM | PTB_CACHE | PTB_BUFF | PTB_EXT); // Map data section of image DBGSTR("Map data section\n"); physaddr data_phys = bootdata->rom_base + text_size; map_pages((virtaddr)&__data_start__, (virtaddr)&__data_end__, data_phys | PTB_RW | PTB_CACHE | PTB_BUFF | PTB_EXT); // Allocate and map bss section DBGSTR("Allocate and map bss\n"); physaddr bss_phys = alloc_pages_zero(bss_size, PAGE_SIZE); map_pages((virtaddr)&__bss_start__, (virtaddr)&__bss_end__, bss_phys | PTB_RW | PTB_CACHE | PTB_BUFF | PTB_EXT); // Allocate and map heap section DBGSTR("Allocate and map heap\n"); physaddr heap_phys = alloc_pages_zero(heap_size, PAGE_SIZE); map_pages((virtaddr)&__heap_start__, (virtaddr)&__heap_end__, heap_phys | PTB_RW | PTB_CACHE | PTB_BUFF | PTB_EXT); // Allocate and map stack section DBGSTR("Allocate and map stack\n"); physaddr stack_phys = alloc_pages_zero(stack_size, PAGE_SIZE); map_pages((virtaddr)&__stack_start__, (virtaddr)&__stack_end__, stack_phys | PTB_RW | PTB_CACHE | PTB_BUFF | PTB_EXT); // Map debug UART - we assume no more than a page is needed DBGSTR("Mapping debug UART\n"); map_pages((virtaddr)&__dbg_serial_virt__, (virtaddr)(&__dbg_serial_virt__ + PAGE_SIZE), (physaddr)&__dbg_serial_phys__ | PTB_RW | PTB_EXT); // Map boot data page DBGSTR("Mapping boot data\n"); map_pages((virtaddr)&__bootdata_virt__, (virtaddr)(&__bootdata_virt__ + PAGE_SIZE), (physaddr)bootdata | PTB_RW | PTB_CACHE | PTB_BUFF | PTB_EXT); // Map the initrd if there was one if (bootdata->initrd_size) { // The initrd address may not be a page multiple as u-boot has // its own header on the file, so we need to align it. physaddr map_start = PAGEALIGN_DOWN(bootdata->initrd_phys); uint32_t map_len = PAGEALIGN_UP(bootdata->initrd_phys + bootdata->initrd_size) - map_start; // We also need to calculate the offset and offset the virtual // address by the matching amount. uint32_t offset = bootdata->initrd_phys - map_start; bootdata->initrd_virt = (virtaddr)&__initrd_map_start__ + offset; DBGSTR("Map initrd\n"); map_pages((virtaddr)&__initrd_map_start__, (virtaddr)(&__initrd_map_start__ + map_len), map_start | PTB_ROM | PTB_CACHE | PTB_BUFF | PTB_EXT); } // Self-map MMU enabling code // The page which contains the MMU enable function must be mapped // with phys==virt address, otherwise bad stuff happens. We do this // by stuffing in a 1MB section mapping for this address, which may // overwrite an actual page table mapping (it's saved and restored // later on). DBGSTR("Self-map MMU enabling code\n"); mmu_enable_func mmu_enable_phys = &mmu_enable - bootdata->phys_to_virt; physaddr selfmap_addr = (physaddr)mmu_enable_phys; int selfmap_index = selfmap_addr >> SECTION_SHIFT; selfmap_addr = selfmap_index << SECTION_SHIFT; uint32_t *pgd = (uint32_t *)bootdata->page_directory; uint32_t old_pde = pgd[selfmap_index]; pgd[selfmap_index] = selfmap_addr | PGD_ROM | PGD_SECTION; // Enable MMU. This doesn't return, it goes to boot_after_mmu. DBGSTR("Enable MMU\n"); mmu_enable_phys(selfmap_index, old_pde, &boot_after_mmu); }
void kernel_main(uint32_t boot_dev, uint32_t arm_m_type, uint32_t atags) { atag_cmd_line = (void *)0; _atags = atags; _arm_m_type = arm_m_type; UNUSED(boot_dev); // First use the serial console stdout_putc = uart_putc; stderr_putc = uart_putc; stream_putc = def_stream_putc; // Dump ATAGS parse_atags(atags, atag_cb); int result = fb_init(); if(result == 0) puts("Successfully set up frame buffer"); else { puts("Error setting up framebuffer:"); puthex(result); } // Switch to the framebuffer for output stdout_putc = split_putc; printf("Welcome to Rpi bootloader\n"); printf("ARM system type is %x\n", arm_m_type); if(atag_cmd_line != (void *)0) printf("Command line: %s\n", atag_cmd_line); struct usb_hcd *usb_hcd; dwc_usb_init(&usb_hcd, DWC_USB_BASE); struct block_device *sd_dev; if(sd_card_init(&sd_dev) == 0) read_mbr(sd_dev, (void*)0, (void*)0); // List devices printf("MAIN: device list: "); char **devs = vfs_get_device_list(); while(*devs) printf("%s ", *devs++); printf("\n"); // Look for a boot configuration file, starting with the default device, // then iterating through all devices FILE *f = (void*)0; // Default device char **fname = boot_cfg_names; char *found_cfg; while(*fname) { f = fopen(*fname, "r"); if(f) { found_cfg = *fname; break; } fname++; } if(!f) { // Try other devices char **dev = vfs_get_device_list(); while(*dev) { int dev_len = strlen(*dev); fname = boot_cfg_names; while(*fname) { int fname_len = strlen(*fname); char *new_str = (char *)malloc(dev_len + fname_len + 3); new_str[0] = 0; strcat(new_str, "("); strcat(new_str, *dev); strcat(new_str, ")"); strcat(new_str, *fname); f = fopen(new_str, "r"); if(f) { found_cfg = new_str; break; } free(new_str); fname++; } if(f) break; dev++; } } if(!f) { printf("MAIN: No bootloader configuration file found\n"); } else { printf("MAIN: Found bootloader configuration: %s\n", found_cfg); char *buf = (char *)malloc(f->len + 1); buf[f->len] = 0; // null terminate fread(buf, 1, f->len, f); fclose(f); cfg_parse(buf); } }