bf0afa44067d39362bba446367103e33c2b470ed
[rpi-open-firmware.git] / arm_chainloader / loader.cc
1 /*=============================================================================
2 Copyright (C) 2016-2017 Authors of rpi-open-firmware
3 All rights reserved.
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 FILE DESCRIPTION
16 Second stage bootloader.
17
18 =============================================================================*/
19
20 #include <drivers/fatfs/ff.h>
21 #include <chainloader.h>
22 #include <drivers/mailbox.hpp>
23 #include <drivers/block_device.hpp>
24 #include <libfdt.h>
25 #include <memory_map.h>
26
27 #define logf(fmt, ...) printf("[LDR:%s]: " fmt, __FUNCTION__, ##__VA_ARGS__);
28
29 FATFS g_BootVolumeFs;
30
31 #define ROOT_VOLUME_PREFIX "0:"
32 #define DTB_LOAD_ADDRESS 0xF000000
33 #define KERNEL_LOAD_ADDRESS 0x2000000
34
35 typedef void (*linux_t)(uint32_t, uint32_t, void*);
36
37 static_assert((MEM_USABLE_START+0x800000) < KERNEL_LOAD_ADDRESS,
38 "memory layout would not allow for kernel to be loaded at KERNEL_LOAD_ADDRESS, please check memory_map.h");
39
40 struct LoaderImpl {
41 inline bool file_exists(const char* path) {
42 return f_stat(path, NULL) == FR_OK;
43 }
44
45 size_t read_file(const char* path, uint8_t*& dest, bool should_alloc = true) {
46 /* ensure file exists first */
47 if(!file_exists(path))
48 panic("attempted to read %s, but it does not exist", path);
49
50 /* read entire file into buffer */
51 FIL fp;
52 f_open(&fp, path, FA_READ);
53
54 unsigned int len = f_size(&fp);
55
56 if(should_alloc) {
57 /*
58 * since this can be used for strings, there's no harm in reserving an
59 * extra byte for the null terminator and appending it.
60 */
61 uint8_t* buffer = new uint8_t[len + 1];
62 dest = buffer;
63 buffer[len] = 0;
64 }
65
66 logf("%s: reading %d bytes to 0x%X (allocated=%d) ...\n", path, len, (unsigned int)dest, should_alloc);
67
68 f_read(&fp, dest, len, &len);
69 f_close(&fp);
70
71 return len;
72 }
73
74 uint8_t* load_fdt(const char* filename, uint8_t* cmdline) {
75 /* read device tree blob */
76 uint8_t* fdt = reinterpret_cast<uint8_t*>(DTB_LOAD_ADDRESS);
77 size_t sz = read_file(filename, fdt, false);
78 logf("FDT loaded at %X\n", (unsigned int) fdt);
79
80 void* v_fdt = reinterpret_cast<void*>(fdt);
81
82 int res;
83
84 if ((res = fdt_check_header(v_fdt)) != 0) {
85 panic("fdt blob invalid, fdt_check_header returned %d", res);
86 }
87
88 /* pass in command line args */
89 res = fdt_open_into(v_fdt, v_fdt, sz + 4096);
90
91 int node = fdt_path_offset(v_fdt, "/chosen");
92 if (node < 0)
93 panic("no chosen node in fdt");
94
95 res = fdt_setprop(v_fdt, node, "bootargs", cmdline, strlen((char*) cmdline) + 1);
96
97 /* pass in a memory map, skipping first meg for bootcode */
98 int memory = fdt_path_offset(v_fdt, "/memory");
99 if(memory < 0)
100 panic("no memory node in fdt");
101
102 /* start the memory map at 1M/16 and grow continuous for 256M
103 * TODO: does this disrupt I/O? */
104
105 char dtype[] = "memory";
106 uint8_t memmap[] = { 0x00, 0x00, 0x01, 0x00, 0x30, 0x00, 0x00, 0x00 };
107 res = fdt_setprop(v_fdt, memory, "reg", (void*) memmap, sizeof(memmap));
108
109 logf("(valid) fdt loaded at 0x%X\n", (unsigned int)fdt);
110
111 return fdt;
112 }
113
114 void teardown_hardware() {
115 BlockDevice* bd = get_sdhost_device();
116 if (bd)
117 bd->stop();
118 }
119
120 LoaderImpl() {
121 logf("Mounting boot partitiion ...\n");
122 FRESULT r = f_mount(&g_BootVolumeFs, ROOT_VOLUME_PREFIX, 1);
123 if (r != FR_OK) {
124 panic("failed to mount boot partition, error: %d", (int)r);
125 }
126 logf("Boot partition mounted!\n");
127
128 /* read the command-line null-terminated */
129 uint8_t* cmdline;
130 size_t cmdlen = read_file("cmdline.txt", cmdline);
131
132 logf("kernel cmdline: %s\n", cmdline);
133
134 /* load flat device tree */
135 uint8_t* fdt = load_fdt("rpi.dtb", cmdline);
136
137 /* once the fdt contains the cmdline, it is not needed */
138 delete[] cmdline;
139
140 /* read the kernel as a function pointer at fixed address */
141 uint8_t* zImage = reinterpret_cast<uint8_t*>(KERNEL_LOAD_ADDRESS);
142 linux_t kernel = reinterpret_cast<linux_t>(zImage);
143
144 size_t ksize = read_file("zImage", zImage, false);
145 logf("zImage loaded at 0x%X\n", (unsigned int)kernel);
146
147 /* flush the cache */
148 logf("Flushing....\n")
149 for (uint8_t* i = zImage; i < zImage + ksize; i += 32) {
150 __asm__ __volatile__ ("mcr p15,0,%0,c7,c10,1" : : "r" (i) : "memory");
151 }
152
153 /* the eMMC card in particular needs to be reset */
154 teardown_hardware();
155
156 /* fire away -- this should never return */
157 logf("Jumping to the Linux kernel...\n");
158 kernel(0, ~0, fdt);
159 }
160 };
161
162 static LoaderImpl STATIC_APP g_Loader {};
This page took 0.084699 seconds and 4 git commands to generate.