diff options
author | Aidan MacDonald <amachronic@protonmail.com> | 2021-05-11 13:28:43 +0100 |
---|---|---|
committer | Aidan MacDonald <amachronic@protonmail.com> | 2021-05-12 10:35:20 +0000 |
commit | 3f26fcf34001197ed267fa1ad549095aae49c88e (patch) | |
tree | c81ab3298a349d1156e905d467492b603236f74d /firmware/target/mips | |
parent | cc22df198d0ccb64dfdfe0c2f247f7d86b7fd750 (diff) | |
download | rockbox-3f26fcf34001197ed267fa1ad549095aae49c88e.tar.gz rockbox-3f26fcf34001197ed267fa1ad549095aae49c88e.zip |
FiiO M3K: New bootloaderbootloader_fiiom3k_v1
SPL and UCL-compressed bootloader are now packed into one output,
bootloader.m3k, eliminating the separate SPL build phase.
The Rockbox bootloader now has a recovery menu, accessible by
holding VOL+ when booting, that lets you back up, restore, and
update the bootloader from the device.
Change-Id: I642c6e5fb83587a013ab2fbfd1adab439561ced2
Diffstat (limited to 'firmware/target/mips')
16 files changed, 546 insertions, 406 deletions
diff --git a/firmware/target/mips/ingenic_x1000/boot.lds b/firmware/target/mips/ingenic_x1000/boot.lds index 81468a95fc..c274d69aab 100644 --- a/firmware/target/mips/ingenic_x1000/boot.lds +++ b/firmware/target/mips/ingenic_x1000/boot.lds | |||
@@ -1,5 +1 @@ | |||
1 | #ifdef BOOTLOADER_SPL | #include "app.lds" | |
2 | # include "spl.lds" | ||
3 | #else | ||
4 | # include "app.lds" | ||
5 | #endif | ||
diff --git a/firmware/target/mips/ingenic_x1000/crt0.S b/firmware/target/mips/ingenic_x1000/crt0.S index b717f96692..b36500b462 100644 --- a/firmware/target/mips/ingenic_x1000/crt0.S +++ b/firmware/target/mips/ingenic_x1000/crt0.S | |||
@@ -34,33 +34,6 @@ | |||
34 | .section .init.text | 34 | .section .init.text |
35 | 35 | ||
36 | _start: | 36 | _start: |
37 | /* Clear data watchpoint */ | ||
38 | mtc0 zero, C0_WATCHLO | ||
39 | mtc0 zero, C0_WATCHHI | ||
40 | |||
41 | /* Set BEV, ERL, mask interrupts */ | ||
42 | li v0, 0x40fc04 | ||
43 | mtc0 v0, C0_Status | ||
44 | |||
45 | /* Set Cause_IV to 1 (use special interrupt vector) */ | ||
46 | li v0, M_CauseIV | ||
47 | mtc0 v0, C0_Cause | ||
48 | |||
49 | /* Set CPU_MODE and BUS_MODE to 1 in CPM_OPCR (Ingenic does this) */ | ||
50 | lui v0, 0xb000 | ||
51 | lw v1, 0x24(v0) | ||
52 | ori v1, v1, 0x22 | ||
53 | sw v1, 0x24(v0) | ||
54 | |||
55 | /* Enable kseg0 cacheability */ | ||
56 | li v0, 3 | ||
57 | mtc0 v0, C0_Config | ||
58 | nop | ||
59 | |||
60 | /* According to ingenic: "enable idx-store-data cache insn" */ | ||
61 | li v0, 0x20000000 | ||
62 | mtc0 v0, C0_ErrCtl | ||
63 | |||
64 | /* Cache init */ | 37 | /* Cache init */ |
65 | li v0, 0x80000000 | 38 | li v0, 0x80000000 |
66 | ori v1, v0, 0x4000 | 39 | ori v1, v0, 0x4000 |
@@ -80,7 +53,6 @@ _cache_loop: | |||
80 | mtc0 v0, C0_Config, 7 | 53 | mtc0 v0, C0_Config, 7 |
81 | nop | 54 | nop |
82 | 55 | ||
83 | #ifndef BOOTLOADER_SPL | ||
84 | /* Copy IRAM from BSS to low memory. */ | 56 | /* Copy IRAM from BSS to low memory. */ |
85 | la t0, _iramcopy | 57 | la t0, _iramcopy |
86 | la t1, _iramstart | 58 | la t1, _iramstart |
@@ -91,7 +63,6 @@ _iram_loop: | |||
91 | addiu t0, 4 | 63 | addiu t0, 4 |
92 | bne t1, t2, _iram_loop | 64 | bne t1, t2, _iram_loop |
93 | sw t3, -4(t1) | 65 | sw t3, -4(t1) |
94 | #endif | ||
95 | 66 | ||
96 | /* Clear the BSS segment (needed to zero-initialize C static values) */ | 67 | /* Clear the BSS segment (needed to zero-initialize C static values) */ |
97 | la t0, _bssbegin | 68 | la t0, _bssbegin |
@@ -103,7 +74,6 @@ _bss_loop: | |||
103 | sw zero, -4(t0) | 74 | sw zero, -4(t0) |
104 | _bss_done: | 75 | _bss_done: |
105 | 76 | ||
106 | #ifndef BOOTLOADER_SPL | ||
107 | /* Set stack pointer and clear the stack */ | 77 | /* Set stack pointer and clear the stack */ |
108 | la sp, stackend | 78 | la sp, stackend |
109 | la t0, stackbegin | 79 | la t0, stackbegin |
@@ -120,13 +90,11 @@ _irqstack_loop: | |||
120 | addiu t0, 4 | 90 | addiu t0, 4 |
121 | bne t0, k0, _irqstack_loop | 91 | bne t0, k0, _irqstack_loop |
122 | sw t1, -4(t0) | 92 | sw t1, -4(t0) |
123 | #endif | ||
124 | 93 | ||
125 | /* Jump to C code */ | 94 | /* Jump to C code */ |
126 | j main | 95 | j main |
127 | nop | 96 | nop |
128 | 97 | ||
129 | #ifndef BOOTLOADER_SPL | ||
130 | /* Exception entry points */ | 98 | /* Exception entry points */ |
131 | .section .vectors.1, "ax", %progbits | 99 | .section .vectors.1, "ax", %progbits |
132 | j tlb_refill_handler | 100 | j tlb_refill_handler |
@@ -260,6 +228,5 @@ _exception_return: | |||
260 | lw sp, 0x80(sp) | 228 | lw sp, 0x80(sp) |
261 | eret | 229 | eret |
262 | nop | 230 | nop |
263 | #endif | ||
264 | 231 | ||
265 | .set pop | 232 | .set pop |
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/boot.make b/firmware/target/mips/ingenic_x1000/fiiom3k/boot.make new file mode 100644 index 0000000000..77b23167c2 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/boot.make | |||
@@ -0,0 +1,30 @@ | |||
1 | # __________ __ ___. | ||
2 | # Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
3 | # Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
4 | # Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
5 | # Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
6 | # \/ \/ \/ \/ \/ | ||
7 | # $Id$ | ||
8 | # | ||
9 | |||
10 | include $(ROOTDIR)/lib/microtar/microtar.make | ||
11 | |||
12 | .SECONDEXPANSION: | ||
13 | |||
14 | $(BUILDDIR)/spl.m3k: $(BUILDDIR)/spl.bin | ||
15 | $(call PRINTS,MKSPL $(@F))$(TOOLSDIR)/mkspl-x1000 -type=nand -ppb=2 -bpp=2 $< $@ | ||
16 | |||
17 | $(BUILDDIR)/bootloader.ucl: $(BUILDDIR)/bootloader.bin | ||
18 | $(call PRINTS,UCLPACK $(@F))$(TOOLSDIR)/uclpack --nrv2e -9 $< $@ >/dev/null | ||
19 | |||
20 | .PHONY: $(BUILDDIR)/bootloader-info.txt | ||
21 | $(BUILDDIR)/bootloader-info.txt: | ||
22 | $(call PRINTS,GEN $(@F))echo $(SVNVERSION) > $@ | ||
23 | |||
24 | $(BUILDDIR)/$(BINARY): $(BUILDDIR)/spl.m3k \ | ||
25 | $(BUILDDIR)/bootloader.ucl \ | ||
26 | $(BUILDDIR)/bootloader-info.txt | ||
27 | $(call PRINTS,TAR $(@F))tar -C $(BUILDDIR) \ | ||
28 | --numeric-owner --no-acls --no-xattrs --no-selinux \ | ||
29 | --mode=0644 --owner=0 --group=0 \ | ||
30 | -cf $@ $(call full_path_subst,$(BUILDDIR)/%,%,$^) | ||
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c index 154785ee0b..10a58ace38 100644 --- a/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c | |||
@@ -19,192 +19,265 @@ | |||
19 | * | 19 | * |
20 | ****************************************************************************/ | 20 | ****************************************************************************/ |
21 | 21 | ||
22 | #include "installer.h" | 22 | #include "installer-fiiom3k.h" |
23 | #include "nand-x1000.h" | 23 | #include "nand-x1000.h" |
24 | #include "system.h" | 24 | #include "system.h" |
25 | #include "core_alloc.h" | 25 | #include "core_alloc.h" |
26 | #include "file.h" | 26 | #include "file.h" |
27 | #include "microtar.h" | ||
28 | #include <stdint.h> | ||
29 | #include <string.h> | ||
30 | #include <stdio.h> | ||
27 | 31 | ||
28 | #define INSTALL_SUCCESS 0 | 32 | #define IMAGE_SIZE (128 * 1024) |
29 | #define ERR_FLASH_OPEN_FAILED (-1) | 33 | #define TAR_SIZE (256 * 1024) |
30 | #define ERR_FLASH_ENABLE_WP_FAILED (-2) | 34 | |
31 | #define ERR_FLASH_DISABLE_WP_FAILED (-3) | 35 | static int flash_prepare(void) |
32 | #define ERR_FLASH_ERASE_FAILED (-4) | ||
33 | #define ERR_FLASH_WRITE_FAILED (-5) | ||
34 | #define ERR_FLASH_READ_FAILED (-6) | ||
35 | #define ERR_OUT_OF_MEMORY (-7) | ||
36 | #define ERR_CANNOT_READ_FILE (-8) | ||
37 | #define ERR_CANNOT_WRITE_FILE (-9) | ||
38 | #define ERR_WRONG_SIZE (-10) | ||
39 | |||
40 | #define BOOT_IMAGE_SIZE (128 * 1024) | ||
41 | |||
42 | static int install_from_buffer(const void* buf) | ||
43 | { | 36 | { |
44 | int status = INSTALL_SUCCESS; | ||
45 | int mf_id, dev_id; | 37 | int mf_id, dev_id; |
38 | int rc; | ||
39 | |||
40 | rc = nand_open(); | ||
41 | if(rc < 0) | ||
42 | return INSTALL_ERR_FLASH(NAND_OPEN, rc); | ||
46 | 43 | ||
47 | if(nand_open()) | 44 | rc = nand_identify(&mf_id, &dev_id); |
48 | return ERR_FLASH_OPEN_FAILED; | 45 | if(rc < 0) { |
49 | if(nand_identify(&mf_id, &dev_id)) { | 46 | nand_close(); |
50 | status = ERR_FLASH_OPEN_FAILED; | 47 | return INSTALL_ERR_FLASH(NAND_IDENTIFY, rc); |
51 | goto _exit; | ||
52 | } | 48 | } |
53 | 49 | ||
54 | if(nand_enable_writes(true)) { | 50 | return INSTALL_SUCCESS; |
55 | status = ERR_FLASH_DISABLE_WP_FAILED; | 51 | } |
56 | goto _exit; | 52 | |
53 | static void flash_finish(void) | ||
54 | { | ||
55 | /* Ensure writes are always disabled when we finish. | ||
56 | * Errors are safe to ignore here, there's nothing we could do anyway. */ | ||
57 | nand_enable_writes(false); | ||
58 | nand_close(); | ||
59 | } | ||
60 | |||
61 | static int flash_img_read(uint8_t* buffer) | ||
62 | { | ||
63 | int rc = flash_prepare(); | ||
64 | if(rc < 0) | ||
65 | goto error; | ||
66 | |||
67 | rc = nand_read(0, IMAGE_SIZE, buffer); | ||
68 | if(rc < 0) { | ||
69 | rc = INSTALL_ERR_FLASH(NAND_READ, rc); | ||
70 | goto error; | ||
57 | } | 71 | } |
58 | 72 | ||
59 | if(nand_erase(0, BOOT_IMAGE_SIZE)) { | 73 | error: |
60 | status = ERR_FLASH_ERASE_FAILED; | 74 | flash_finish(); |
61 | goto _exit; | 75 | return rc; |
76 | } | ||
77 | |||
78 | static int flash_img_write(const uint8_t* buffer) | ||
79 | { | ||
80 | int rc = flash_prepare(); | ||
81 | if(rc < 0) | ||
82 | goto error; | ||
83 | |||
84 | rc = nand_enable_writes(true); | ||
85 | if(rc < 0) { | ||
86 | rc = INSTALL_ERR_FLASH(NAND_ENABLE_WRITES, rc); | ||
87 | goto error; | ||
62 | } | 88 | } |
63 | 89 | ||
64 | if(nand_write(0, BOOT_IMAGE_SIZE, (const uint8_t*)buf)) { | 90 | rc = nand_erase(0, IMAGE_SIZE); |
65 | status = ERR_FLASH_WRITE_FAILED; | 91 | if(rc < 0) { |
66 | goto _exit; | 92 | rc = INSTALL_ERR_FLASH(NAND_ERASE, rc); |
93 | goto error; | ||
67 | } | 94 | } |
68 | 95 | ||
69 | if(nand_enable_writes(false)) { | 96 | rc = nand_write(0, IMAGE_SIZE, buffer); |
70 | status = ERR_FLASH_ENABLE_WP_FAILED; | 97 | if(rc < 0) { |
71 | goto _exit; | 98 | rc = INSTALL_ERR_FLASH(NAND_WRITE, rc); |
99 | goto error; | ||
72 | } | 100 | } |
73 | 101 | ||
74 | _exit: | 102 | error: |
75 | nand_close(); | 103 | flash_finish(); |
76 | return status; | 104 | return rc; |
77 | } | 105 | } |
78 | 106 | ||
79 | static int dump_to_buffer(void* buf) | 107 | static int patch_img(mtar_t* tar, uint8_t* buffer, const char* filename, |
108 | size_t patch_offset, size_t patch_size) | ||
80 | { | 109 | { |
81 | int status = INSTALL_SUCCESS; | 110 | /* Seek to file */ |
82 | int mf_id, dev_id; | 111 | mtar_header_t h; |
83 | 112 | int rc = mtar_find(tar, filename, &h); | |
84 | if(nand_open()) | 113 | if(rc != MTAR_ESUCCESS) { |
85 | return ERR_FLASH_OPEN_FAILED; | 114 | rc = INSTALL_ERR_MTAR(TAR_FIND, rc); |
86 | if(nand_identify(&mf_id, &dev_id)) { | 115 | return rc; |
87 | status = ERR_FLASH_OPEN_FAILED; | ||
88 | goto _exit; | ||
89 | } | 116 | } |
90 | 117 | ||
91 | if(nand_read(0, BOOT_IMAGE_SIZE, (uint8_t*)buf)) { | 118 | /* We need a normal file */ |
92 | status = ERR_FLASH_READ_FAILED; | 119 | if(h.type != 0 && h.type != MTAR_TREG) |
93 | goto _exit; | 120 | return INSTALL_ERR_BAD_FORMAT; |
121 | |||
122 | /* Check size does not exceed patch area */ | ||
123 | if(h.size > patch_size) | ||
124 | return INSTALL_ERR_BAD_FORMAT; | ||
125 | |||
126 | /* Read data directly into patch area, fill unused bytes with 0xff */ | ||
127 | memset(&buffer[patch_offset], 0xff, patch_size); | ||
128 | rc = mtar_read_data(tar, &buffer[patch_offset], h.size); | ||
129 | if(rc != MTAR_ESUCCESS) { | ||
130 | rc = INSTALL_ERR_MTAR(TAR_READ, rc); | ||
131 | return rc; | ||
94 | } | 132 | } |
95 | 133 | ||
96 | _exit: | 134 | return INSTALL_SUCCESS; |
97 | nand_close(); | ||
98 | return status; | ||
99 | } | 135 | } |
100 | 136 | ||
101 | int install_bootloader(const char* path) | 137 | int install_boot(const char* srcfile) |
102 | { | 138 | { |
103 | /* Allocate memory to hold image */ | 139 | int rc; |
104 | size_t bufsize = BOOT_IMAGE_SIZE + CACHEALIGN_SIZE - 1; | 140 | mtar_t* tar = NULL; |
105 | int handle = core_alloc("boot_image", bufsize); | 141 | int handle = -1; |
106 | if(handle < 0) | ||
107 | return ERR_OUT_OF_MEMORY; | ||
108 | |||
109 | int status = INSTALL_SUCCESS; | ||
110 | void* buffer = core_get_data(handle); | ||
111 | CACHEALIGN_BUFFER(buffer, bufsize); | ||
112 | 142 | ||
113 | /* Open the boot image */ | 143 | /* Allocate enough memory for image and tar state */ |
114 | int fd = open(path, O_RDONLY); | 144 | size_t bufsize = IMAGE_SIZE + sizeof(mtar_t) + 2*CACHEALIGN_SIZE; |
115 | if(fd < 0) { | 145 | handle = core_alloc("boot_image", bufsize); |
116 | status = ERR_CANNOT_READ_FILE; | 146 | if(handle < 0) { |
117 | goto _exit; | 147 | rc = INSTALL_ERR_OUT_OF_MEMORY; |
148 | goto error; | ||
118 | } | 149 | } |
119 | 150 | ||
120 | /* Check file size */ | 151 | uint8_t* buffer = core_get_data(handle); |
121 | off_t fsize = filesize(fd); | 152 | |
122 | if(fsize != BOOT_IMAGE_SIZE) { | 153 | /* Tar state alloc */ |
123 | status = ERR_WRONG_SIZE; | 154 | CACHEALIGN_BUFFER(buffer, bufsize); |
124 | goto _exit; | 155 | tar = (mtar_t*)buffer; |
125 | } | 156 | memset(tar, 0, sizeof(tar)); |
157 | |||
158 | /* Image buffer alloc */ | ||
159 | buffer += sizeof(mtar_t); | ||
160 | CACHEALIGN_BUFFER(buffer, bufsize); | ||
161 | |||
162 | /* Read the flash -- we need an existing image to patch */ | ||
163 | rc = flash_img_read(buffer); | ||
164 | if(rc < 0) | ||
165 | goto error; | ||
126 | 166 | ||
127 | /* Read the file into the buffer */ | 167 | /* Open the tarball */ |
128 | ssize_t cnt = read(fd, buffer, BOOT_IMAGE_SIZE); | 168 | rc = mtar_open(tar, srcfile, "r"); |
129 | if(cnt != BOOT_IMAGE_SIZE) { | 169 | if(rc != MTAR_ESUCCESS) { |
130 | status = ERR_CANNOT_READ_FILE; | 170 | rc = INSTALL_ERR_MTAR(TAR_OPEN, rc); |
131 | goto _exit; | 171 | goto error; |
132 | } | 172 | } |
133 | 173 | ||
134 | /* Perform the installation */ | 174 | /* Extract the needed files & patch 'em in */ |
135 | status = install_from_buffer(buffer); | 175 | rc = patch_img(tar, buffer, "spl.m3k", 0, 12 * 1024); |
176 | if(rc < 0) | ||
177 | goto error; | ||
136 | 178 | ||
137 | _exit: | 179 | rc = patch_img(tar, buffer, "bootloader.ucl", 0x6800, 102 * 1024); |
138 | if(fd >= 0) | 180 | if(rc < 0) |
139 | close(fd); | 181 | goto error; |
140 | core_free(handle); | 182 | |
141 | return status; | 183 | /* Flash the new image */ |
184 | rc = flash_img_write(buffer); | ||
185 | if(rc < 0) | ||
186 | goto error; | ||
187 | |||
188 | rc = INSTALL_SUCCESS; | ||
189 | |||
190 | error: | ||
191 | if(tar && tar->close) | ||
192 | mtar_close(tar); | ||
193 | if(handle >= 0) | ||
194 | core_free(handle); | ||
195 | return rc; | ||
142 | } | 196 | } |
143 | 197 | ||
144 | /* Dump the current bootloader to a file */ | 198 | int backup_boot(const char* destfile) |
145 | int dump_bootloader(const char* path) | ||
146 | { | 199 | { |
147 | /* Allocate memory to hold image */ | 200 | int rc; |
148 | size_t bufsize = BOOT_IMAGE_SIZE + CACHEALIGN_SIZE - 1; | 201 | int handle = -1; |
149 | int handle = core_alloc("boot_image", bufsize); | ||
150 | if(handle < 0) | ||
151 | return -1; | ||
152 | |||
153 | /* Read data from flash */ | ||
154 | int fd = -1; | 202 | int fd = -1; |
155 | void* buffer = core_get_data(handle); | 203 | size_t bufsize = IMAGE_SIZE + CACHEALIGN_SIZE - 1; |
204 | handle = core_alloc("boot_image", bufsize); | ||
205 | if(handle < 0) { | ||
206 | rc = INSTALL_ERR_OUT_OF_MEMORY; | ||
207 | goto error; | ||
208 | } | ||
209 | |||
210 | uint8_t* buffer = core_get_data(handle); | ||
156 | CACHEALIGN_BUFFER(buffer, bufsize); | 211 | CACHEALIGN_BUFFER(buffer, bufsize); |
157 | int status = dump_to_buffer(buffer); | ||
158 | if(status) | ||
159 | goto _exit; | ||
160 | 212 | ||
161 | /* Open file */ | 213 | rc = flash_img_read(buffer); |
162 | fd = open(path, O_CREAT|O_TRUNC|O_WRONLY); | 214 | if(rc < 0) |
215 | goto error; | ||
216 | |||
217 | fd = open(destfile, O_CREAT|O_TRUNC|O_WRONLY); | ||
163 | if(fd < 0) { | 218 | if(fd < 0) { |
164 | status = ERR_CANNOT_WRITE_FILE; | 219 | rc = INSTALL_ERR_FILE_IO; |
165 | goto _exit; | 220 | goto error; |
166 | } | 221 | } |
167 | 222 | ||
168 | /* Write data to file */ | 223 | ssize_t cnt = write(fd, buffer, IMAGE_SIZE); |
169 | ssize_t cnt = write(fd, buffer, BOOT_IMAGE_SIZE); | 224 | if(cnt != IMAGE_SIZE) { |
170 | if(cnt != BOOT_IMAGE_SIZE) { | 225 | rc = INSTALL_ERR_FILE_IO; |
171 | status = ERR_CANNOT_WRITE_FILE; | 226 | goto error; |
172 | goto _exit; | ||
173 | } | 227 | } |
174 | 228 | ||
175 | _exit: | 229 | error: |
176 | if(fd >= 0) | 230 | if(fd >= 0) |
177 | close(fd); | 231 | close(fd); |
178 | core_free(handle); | 232 | if(handle >= 0) |
179 | return status; | 233 | core_free(handle); |
234 | return rc; | ||
180 | } | 235 | } |
181 | 236 | ||
182 | const char* installer_strerror(int rc) | 237 | int restore_boot(const char* srcfile) |
183 | { | 238 | { |
184 | switch(rc) { | 239 | int rc; |
185 | case INSTALL_SUCCESS: | 240 | int handle = -1; |
186 | return "Success"; | 241 | int fd = -1; |
187 | case ERR_FLASH_OPEN_FAILED: | 242 | size_t bufsize = IMAGE_SIZE + CACHEALIGN_SIZE - 1; |
188 | return "Can't open flash device"; | 243 | handle = core_alloc("boot_image", bufsize); |
189 | case ERR_FLASH_ENABLE_WP_FAILED: | 244 | if(handle < 0) { |
190 | return "Couldn't re-enable write protect"; | 245 | rc = INSTALL_ERR_OUT_OF_MEMORY; |
191 | case ERR_FLASH_DISABLE_WP_FAILED: | 246 | goto error; |
192 | return "Can't disable write protect"; | ||
193 | case ERR_FLASH_ERASE_FAILED: | ||
194 | return "Flash erase failed"; | ||
195 | case ERR_FLASH_WRITE_FAILED: | ||
196 | return "Flash write error"; | ||
197 | case ERR_FLASH_READ_FAILED: | ||
198 | return "Flash read error"; | ||
199 | case ERR_OUT_OF_MEMORY: | ||
200 | return "Out of memory"; | ||
201 | case ERR_CANNOT_READ_FILE: | ||
202 | return "Error reading file"; | ||
203 | case ERR_CANNOT_WRITE_FILE: | ||
204 | return "Error writing file"; | ||
205 | case ERR_WRONG_SIZE: | ||
206 | return "Wrong file size"; | ||
207 | default: | ||
208 | return "Unknown error"; | ||
209 | } | 247 | } |
248 | |||
249 | uint8_t* buffer = core_get_data(handle); | ||
250 | CACHEALIGN_BUFFER(buffer, bufsize); | ||
251 | |||
252 | fd = open(srcfile, O_RDONLY); | ||
253 | if(fd < 0) { | ||
254 | rc = INSTALL_ERR_FILE_NOT_FOUND; | ||
255 | goto error; | ||
256 | } | ||
257 | |||
258 | off_t fsize = filesize(fd); | ||
259 | if(fsize != IMAGE_SIZE) { | ||
260 | rc = INSTALL_ERR_BAD_FORMAT; | ||
261 | goto error; | ||
262 | } | ||
263 | |||
264 | ssize_t cnt = read(fd, buffer, IMAGE_SIZE); | ||
265 | if(cnt != IMAGE_SIZE) { | ||
266 | rc = INSTALL_ERR_FILE_IO; | ||
267 | goto error; | ||
268 | } | ||
269 | |||
270 | close(fd); | ||
271 | fd = -1; | ||
272 | |||
273 | rc = flash_img_write(buffer); | ||
274 | if(rc < 0) | ||
275 | goto error; | ||
276 | |||
277 | error: | ||
278 | if(fd >= 0) | ||
279 | close(fd); | ||
280 | if(handle >= 0) | ||
281 | core_free(handle); | ||
282 | return rc; | ||
210 | } | 283 | } |
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.h b/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.h new file mode 100644 index 0000000000..eb700e6689 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.h | |||
@@ -0,0 +1,51 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2021 Aidan MacDonald | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #ifndef __INSTALLER_FIIOM3K_H__ | ||
23 | #define __INSTALLER_FIIOM3K_H__ | ||
24 | |||
25 | #include <stddef.h> | ||
26 | |||
27 | #define INSTALL_SUCCESS 0 | ||
28 | #define INSTALL_ERR_OUT_OF_MEMORY (-1) | ||
29 | #define INSTALL_ERR_FILE_NOT_FOUND (-2) | ||
30 | #define INSTALL_ERR_FILE_IO (-3) | ||
31 | #define INSTALL_ERR_BAD_FORMAT (-4) | ||
32 | #define INSTALL_ERR_NAND_OPEN (-5) | ||
33 | #define INSTALL_ERR_NAND_IDENTIFY (-6) | ||
34 | #define INSTALL_ERR_NAND_READ (-7) | ||
35 | #define INSTALL_ERR_NAND_ENABLE_WRITES (-8) | ||
36 | #define INSTALL_ERR_NAND_ERASE (-9) | ||
37 | #define INSTALL_ERR_NAND_WRITE (-10) | ||
38 | #define INSTALL_ERR_TAR_OPEN (-11) | ||
39 | #define INSTALL_ERR_TAR_FIND (-12) | ||
40 | #define INSTALL_ERR_TAR_READ (-13) | ||
41 | #define INSTALL_ERR_MTAR(x,y) ((INSTALL_ERR_##x)*100 + (y)) | ||
42 | #define INSTALL_ERR_FLASH(x,y) ((INSTALL_ERR_##x)*100 + (y)) | ||
43 | |||
44 | /* Install the Rockbox bootloader from a bootloader.m3k image */ | ||
45 | extern int install_boot(const char* srcfile); | ||
46 | |||
47 | /* Backup or restore the bootloader from a raw NAND image */ | ||
48 | extern int backup_boot(const char* destfile); | ||
49 | extern int restore_boot(const char* srcfile); | ||
50 | |||
51 | #endif /* __INSTALLER_FIIOM3K_H__ */ | ||
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c index bdd0ffc2e0..efea5aa323 100644 --- a/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c | |||
@@ -25,6 +25,11 @@ | |||
25 | #include "system.h" | 25 | #include "system.h" |
26 | #include <string.h> | 26 | #include <string.h> |
27 | 27 | ||
28 | /* Available boot options */ | ||
29 | #define BOOTOPTION_ROCKBOX 0 | ||
30 | #define BOOTOPTION_ORIG_FW 1 | ||
31 | #define BOOTOPTION_RECOVERY 2 | ||
32 | |||
28 | /* Boot select button state must remain stable for this duration | 33 | /* Boot select button state must remain stable for this duration |
29 | * before the choice will be accepted. Currently 100ms. | 34 | * before the choice will be accepted. Currently 100ms. |
30 | */ | 35 | */ |
@@ -56,7 +61,7 @@ const struct spl_boot_option spl_boot_options[] = { | |||
56 | */ | 61 | */ |
57 | .nand_addr = 0x6800, | 62 | .nand_addr = 0x6800, |
58 | .nand_size = 0x19800, | 63 | .nand_size = 0x19800, |
59 | .load_addr = X1000_DRAM_BASE - 8, /* first 8 bytes are bootloader ID */ | 64 | .load_addr = X1000_DRAM_END - 0x19800, |
60 | .exec_addr = X1000_DRAM_BASE, | 65 | .exec_addr = X1000_DRAM_BASE, |
61 | .cmdline = NULL, | 66 | .cmdline = NULL, |
62 | }, | 67 | }, |
@@ -80,7 +85,7 @@ const struct spl_boot_option spl_boot_options[] = { | |||
80 | 85 | ||
81 | void spl_error(void) | 86 | void spl_error(void) |
82 | { | 87 | { |
83 | const int pin = (1 << 24); | 88 | const uint32_t pin = (1 << 24); |
84 | 89 | ||
85 | /* Turn on button light */ | 90 | /* Turn on button light */ |
86 | jz_clr(GPIO_INT(GPIO_C), pin); | 91 | jz_clr(GPIO_INT(GPIO_C), pin); |
@@ -105,6 +110,10 @@ int spl_get_boot_option(void) | |||
105 | 110 | ||
106 | uint32_t pin = 1, lastpin = 0; | 111 | uint32_t pin = 1, lastpin = 0; |
107 | uint32_t deadline = 0; | 112 | uint32_t deadline = 0; |
113 | /* Iteration count guards against unlikely case of broken buttons | ||
114 | * which never stabilize; if this occurs, we always boot Rockbox. */ | ||
115 | int iter_count = 0; | ||
116 | const int max_iter_count = 30; | ||
108 | 117 | ||
109 | /* Configure the button GPIOs as inputs */ | 118 | /* Configure the button GPIOs as inputs */ |
110 | gpio_config(GPIO_A, pinmask, GPIO_INPUT); | 119 | gpio_config(GPIO_A, pinmask, GPIO_INPUT); |
@@ -116,19 +125,18 @@ int spl_get_boot_option(void) | |||
116 | if(pin != lastpin) { | 125 | if(pin != lastpin) { |
117 | /* This will always be set on the first iteration */ | 126 | /* This will always be set on the first iteration */ |
118 | deadline = __ost_read32() + BTN_STABLE_TIME; | 127 | deadline = __ost_read32() + BTN_STABLE_TIME; |
128 | iter_count += 1; | ||
119 | } | 129 | } |
120 | } while(__ost_read32() < deadline); | 130 | } while(iter_count < max_iter_count && __ost_read32() < deadline); |
121 | |||
122 | /* Play button boots original firmware */ | ||
123 | if(pin == (1 << 17)) | ||
124 | return SPL_BOOTOPT_ORIG_FW; | ||
125 | |||
126 | /* Volume up boots recovery */ | ||
127 | if(pin == (1 << 19)) | ||
128 | return SPL_BOOTOPT_RECOVERY; | ||
129 | 131 | ||
130 | /* Default is to boot Rockbox */ | 132 | if(iter_count < max_iter_count && (pin & (1 << 17))) { |
131 | return SPL_BOOTOPT_ROCKBOX; | 133 | if(pin & (1 << 19)) |
134 | return BOOTOPTION_RECOVERY; /* Play+Volume Up */ | ||
135 | else | ||
136 | return BOOTOPTION_ORIG_FW; /* Play */ | ||
137 | } else { | ||
138 | return BOOTOPTION_ROCKBOX; /* Volume Up or no buttons */ | ||
139 | } | ||
132 | } | 140 | } |
133 | 141 | ||
134 | void spl_handle_pre_boot(int bootopt) | 142 | void spl_handle_pre_boot(int bootopt) |
@@ -145,7 +153,7 @@ void spl_handle_pre_boot(int bootopt) | |||
145 | /* System clock setup -- common to Rockbox and FiiO firmware | 153 | /* System clock setup -- common to Rockbox and FiiO firmware |
146 | * ---- | 154 | * ---- |
147 | * CPU at 1 GHz, L2 cache at 500 MHz | 155 | * CPU at 1 GHz, L2 cache at 500 MHz |
148 | * AHB0 and AHB2 and 200 MHz | 156 | * AHB0 and AHB2 at 200 MHz |
149 | * PCLK at 100 MHz | 157 | * PCLK at 100 MHz |
150 | * DDR at 200 MHz | 158 | * DDR at 200 MHz |
151 | */ | 159 | */ |
@@ -153,7 +161,7 @@ void spl_handle_pre_boot(int bootopt) | |||
153 | clk_set_ccr_mux(CLKMUX_SCLK_A(APLL) | CLKMUX_CPU(SCLK_A) | | 161 | clk_set_ccr_mux(CLKMUX_SCLK_A(APLL) | CLKMUX_CPU(SCLK_A) | |
154 | CLKMUX_AHB0(SCLK_A) | CLKMUX_AHB2(SCLK_A)); | 162 | CLKMUX_AHB0(SCLK_A) | CLKMUX_AHB2(SCLK_A)); |
155 | 163 | ||
156 | if(bootopt == SPL_BOOTOPT_ROCKBOX) { | 164 | if(bootopt == BOOTOPTION_ROCKBOX) { |
157 | /* We don't use MPLL in Rockbox, so switch DDR memory to APLL */ | 165 | /* We don't use MPLL in Rockbox, so switch DDR memory to APLL */ |
158 | clk_set_ddr(X1000_CLK_SCLK_A, 5); | 166 | clk_set_ddr(X1000_CLK_SCLK_A, 5); |
159 | 167 | ||
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/spl-target.h b/firmware/target/mips/ingenic_x1000/fiiom3k/spl-target.h deleted file mode 100644 index ac90508f44..0000000000 --- a/firmware/target/mips/ingenic_x1000/fiiom3k/spl-target.h +++ /dev/null | |||
@@ -1,29 +0,0 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2021 Aidan MacDonald | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #ifndef __SPL_TARGET_H__ | ||
23 | #define __SPL_TARGET_H__ | ||
24 | |||
25 | #define SPL_DDR_MEMORYSIZE 64 | ||
26 | #define SPL_DDR_AUTOSR_EN 1 | ||
27 | #define SPL_DDR_NEED_BYPASS 1 | ||
28 | |||
29 | #endif /* __SPL_TARGET_H__ */ | ||
diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000-err.h b/firmware/target/mips/ingenic_x1000/nand-x1000-err.h deleted file mode 100644 index 869fe73ac9..0000000000 --- a/firmware/target/mips/ingenic_x1000/nand-x1000-err.h +++ /dev/null | |||
@@ -1,27 +0,0 @@ | |||
1 | #ifndef __NAND_X1000_ERR_H__ | ||
2 | #define __NAND_X1000_ERR_H__ | ||
3 | |||
4 | /* Error codes which can be returned by the nand-x1000 API. These codes are | ||
5 | * also used by host-side tools, so we define them here to avoid polluting | ||
6 | * the namespace with useless X1000 APIs. */ | ||
7 | #define NANDERR_CHIP_UNSUPPORTED (-1) | ||
8 | #define NANDERR_WRITE_PROTECTED (-2) | ||
9 | #define NANDERR_UNALIGNED_ADDRESS (-3) | ||
10 | #define NANDERR_UNALIGNED_LENGTH (-4) | ||
11 | #define NANDERR_READ_FAILED (-5) | ||
12 | #define NANDERR_ECC_FAILED (-6) | ||
13 | #define NANDERR_ERASE_FAILED (-7) | ||
14 | #define NANDERR_PROGRAM_FAILED (-8) | ||
15 | #define NANDERR_COMMAND_FAILED (-9) | ||
16 | #define NANDERR_OTHER (-99) | ||
17 | |||
18 | /* TEMPORARY -- compatibility hack for jztool's sake. | ||
19 | * This will go away once the new bootloader gets merged */ | ||
20 | #define NAND_SUCCESS 0 | ||
21 | #define NAND_ERR_UNKNOWN_CHIP NANDERR_CHIP_UNSUPPORTED | ||
22 | #define NAND_ERR_UNALIGNED NANDERR_UNALIGNED_ADDRESS | ||
23 | #define NAND_ERR_WRITE_PROTECT NANDERR_WRITE_PROTECTED | ||
24 | #define NAND_ERR_CONTROLLER NANDERR_OTHER | ||
25 | #define NAND_ERR_COMMAND NANDERR_COMMAND_FAILED | ||
26 | |||
27 | #endif /* __NAND_X1000_ERR_H__ */ | ||
diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000.c b/firmware/target/mips/ingenic_x1000/nand-x1000.c index 1770324fb3..fbac824789 100644 --- a/firmware/target/mips/ingenic_x1000/nand-x1000.c +++ b/firmware/target/mips/ingenic_x1000/nand-x1000.c | |||
@@ -200,11 +200,8 @@ static int nand_rdwr(bool write, uint32_t addr, uint32_t size, uint8_t* buf) | |||
200 | return NAND_SUCCESS; | 200 | return NAND_SUCCESS; |
201 | if(write && !nand_drv.write_enabled) | 201 | if(write && !nand_drv.write_enabled) |
202 | return NAND_ERR_WRITE_PROTECT; | 202 | return NAND_ERR_WRITE_PROTECT; |
203 | /* FIXME: re-enable this check after merging new SPL+bootloader. | 203 | if((uint32_t)buf & (CACHEALIGN_SIZE - 1)) |
204 | * It's only necessary for DMA, which is currently not used, but it's a | 204 | return NAND_ERR_UNALIGNED; |
205 | * good practice anyway. Disable for now due to SPL complications. */ | ||
206 | /*if((uint32_t)buf & (CACHEALIGN_SIZE - 1)) | ||
207 | return NAND_ERR_UNALIGNED;*/ | ||
208 | 205 | ||
209 | addr >>= nand_drv.chip_data->log2_page_size; | 206 | addr >>= nand_drv.chip_data->log2_page_size; |
210 | size >>= nand_drv.chip_data->log2_page_size; | 207 | size >>= nand_drv.chip_data->log2_page_size; |
diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000.h b/firmware/target/mips/ingenic_x1000/nand-x1000.h index 6c415b1170..f5db0bbfa5 100644 --- a/firmware/target/mips/ingenic_x1000/nand-x1000.h +++ b/firmware/target/mips/ingenic_x1000/nand-x1000.h | |||
@@ -36,7 +36,14 @@ | |||
36 | #include <stdint.h> | 36 | #include <stdint.h> |
37 | #include <stdbool.h> | 37 | #include <stdbool.h> |
38 | #include <stddef.h> | 38 | #include <stddef.h> |
39 | #include "nand-x1000-err.h" | 39 | |
40 | /* Error codes which can be returned by the NAND API */ | ||
41 | #define NAND_SUCCESS 0 | ||
42 | #define NAND_ERR_UNKNOWN_CHIP (-1) | ||
43 | #define NAND_ERR_UNALIGNED (-2) | ||
44 | #define NAND_ERR_WRITE_PROTECT (-3) | ||
45 | #define NAND_ERR_CONTROLLER (-4) | ||
46 | #define NAND_ERR_COMMAND (-5) | ||
40 | 47 | ||
41 | /* Chip supports quad I/O for page read/write */ | 48 | /* Chip supports quad I/O for page read/write */ |
42 | #define NANDCHIP_FLAG_QUAD 0x01 | 49 | #define NANDCHIP_FLAG_QUAD 0x01 |
diff --git a/firmware/target/mips/ingenic_x1000/spl-start.S b/firmware/target/mips/ingenic_x1000/spl-start.S new file mode 100644 index 0000000000..58346fe750 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/spl-start.S | |||
@@ -0,0 +1,97 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2021 Aidan MacDonald | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include "config.h" | ||
23 | #include "mips.h" | ||
24 | |||
25 | .text | ||
26 | .extern spl_main | ||
27 | .global _spl_start | ||
28 | |||
29 | .set push | ||
30 | .set mips32 | ||
31 | .set noreorder | ||
32 | .set noat | ||
33 | |||
34 | .section .init.spl | ||
35 | |||
36 | _spl_start: | ||
37 | /* Clear data watchpoint */ | ||
38 | mtc0 zero, C0_WATCHLO | ||
39 | mtc0 zero, C0_WATCHHI | ||
40 | |||
41 | /* Set BEV, ERL, mask interrupts */ | ||
42 | li v0, 0x40fc04 | ||
43 | mtc0 v0, C0_Status | ||
44 | |||
45 | /* Set Cause_IV to 1 (use special interrupt vector) */ | ||
46 | li v0, M_CauseIV | ||
47 | mtc0 v0, C0_Cause | ||
48 | |||
49 | /* Set CPU_MODE and BUS_MODE to 1 in CPM_OPCR (Ingenic does this) */ | ||
50 | lui v0, 0xb000 | ||
51 | lw v1, 0x24(v0) | ||
52 | ori v1, v1, 0x22 | ||
53 | sw v1, 0x24(v0) | ||
54 | |||
55 | /* Enable kseg0 cacheability */ | ||
56 | li v0, 3 | ||
57 | mtc0 v0, C0_Config | ||
58 | nop | ||
59 | |||
60 | /* According to ingenic: "enable idx-store-data cache insn" */ | ||
61 | li v0, 0x20000000 | ||
62 | mtc0 v0, C0_ErrCtl | ||
63 | |||
64 | /* Cache init */ | ||
65 | li v0, 0x80000000 | ||
66 | ori v1, v0, 0x4000 | ||
67 | mtc0 zero, C0_TAGLO | ||
68 | mtc0 zero, C0_TAGHI | ||
69 | _cache_loop: | ||
70 | cache ICIndexStTag, 0(v0) | ||
71 | cache DCIndexStTag, 0(v0) | ||
72 | addiu v0, v0, 32 | ||
73 | bne v0, v1, _cache_loop | ||
74 | nop | ||
75 | |||
76 | /* Invalidate BTB */ | ||
77 | mfc0 v0, C0_Config, 7 | ||
78 | nop | ||
79 | ori v0, v0, 2 | ||
80 | mtc0 v0, C0_Config, 7 | ||
81 | nop | ||
82 | |||
83 | /* Clear the BSS segment (needed to zero-initialize C static values) */ | ||
84 | la t0, _bssbegin | ||
85 | la t1, _bssend | ||
86 | beq t0, t1, _bss_done | ||
87 | _bss_loop: | ||
88 | addiu t0, 4 | ||
89 | bne t0, t1, _bss_loop | ||
90 | sw zero, -4(t0) | ||
91 | _bss_done: | ||
92 | |||
93 | /* Jump to C code */ | ||
94 | j spl_main | ||
95 | nop | ||
96 | |||
97 | .set pop | ||
diff --git a/firmware/target/mips/ingenic_x1000/spl-x1000-defs.h b/firmware/target/mips/ingenic_x1000/spl-x1000-defs.h deleted file mode 100644 index 1d9f120ee2..0000000000 --- a/firmware/target/mips/ingenic_x1000/spl-x1000-defs.h +++ /dev/null | |||
@@ -1,66 +0,0 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2021 Aidan MacDonald | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #ifndef __SPL_X1000_DEFS_H__ | ||
23 | #define __SPL_X1000_DEFS_H__ | ||
24 | |||
25 | #include <stdint.h> | ||
26 | |||
27 | #ifdef __cplusplus | ||
28 | extern "C" { | ||
29 | #endif | ||
30 | |||
31 | #define SPL_CMD_BOOT 0 | ||
32 | #define SPL_CMD_FLASH_READ 1 | ||
33 | #define SPL_CMD_FLASH_WRITE 2 | ||
34 | |||
35 | #define SPL_BOOTOPT_CHOOSE 0 | ||
36 | #define SPL_BOOTOPT_ROCKBOX 1 | ||
37 | #define SPL_BOOTOPT_ORIG_FW 2 | ||
38 | #define SPL_BOOTOPT_RECOVERY 3 | ||
39 | #define SPL_BOOTOPT_NONE 4 | ||
40 | |||
41 | #define SPL_FLAG_SKIP_INIT (1 << 0) | ||
42 | |||
43 | #define SPL_MAX_SIZE (12 * 1024) | ||
44 | #define SPL_LOAD_ADDRESS 0xf4001000 | ||
45 | #define SPL_EXEC_ADDRESS 0xf4001800 | ||
46 | #define SPL_ARGUMENTS_ADDRESS 0xf40011f0 | ||
47 | #define SPL_STATUS_ADDRESS 0xf40011e0 | ||
48 | #define SPL_BUFFER_ADDRESS 0xa0004000 | ||
49 | |||
50 | struct x1000_spl_arguments { | ||
51 | uint32_t command; | ||
52 | uint32_t param1; | ||
53 | uint32_t param2; | ||
54 | uint32_t flags; | ||
55 | }; | ||
56 | |||
57 | struct x1000_spl_status { | ||
58 | int err_code; | ||
59 | int reserved; | ||
60 | }; | ||
61 | |||
62 | #ifdef __cplusplus | ||
63 | } | ||
64 | #endif | ||
65 | |||
66 | #endif /* __SPL_X1000_DEFS_H__ */ | ||
diff --git a/firmware/target/mips/ingenic_x1000/spl-x1000.c b/firmware/target/mips/ingenic_x1000/spl-x1000.c index 6505cabbef..07453f6182 100644 --- a/firmware/target/mips/ingenic_x1000/spl-x1000.c +++ b/firmware/target/mips/ingenic_x1000/spl-x1000.c | |||
@@ -20,7 +20,6 @@ | |||
20 | ****************************************************************************/ | 20 | ****************************************************************************/ |
21 | 21 | ||
22 | #include "spl-x1000.h" | 22 | #include "spl-x1000.h" |
23 | #include "spl-target.h" | ||
24 | #include "clk-x1000.h" | 23 | #include "clk-x1000.h" |
25 | #include "nand-x1000.h" | 24 | #include "nand-x1000.h" |
26 | #include "system.h" | 25 | #include "system.h" |
@@ -29,16 +28,15 @@ | |||
29 | #include "x1000/ddrc.h" | 28 | #include "x1000/ddrc.h" |
30 | #include "x1000/ddrc_apb.h" | 29 | #include "x1000/ddrc_apb.h" |
31 | #include "x1000/ddrphy.h" | 30 | #include "x1000/ddrphy.h" |
31 | #include "ucl_decompress.h" | ||
32 | 32 | ||
33 | struct x1000_spl_arguments* const spl_arguments = | 33 | #ifdef FIIO_M3K |
34 | (struct x1000_spl_arguments*)SPL_ARGUMENTS_ADDRESS; | 34 | # define SPL_DDR_MEMORYSIZE 64 |
35 | 35 | # define SPL_DDR_AUTOSR_EN 1 | |
36 | struct x1000_spl_status* const spl_status = | 36 | # define SPL_DDR_NEED_BYPASS 1 |
37 | (struct x1000_spl_status*)SPL_STATUS_ADDRESS; | 37 | #else |
38 | 38 | # error "please add SPL memory definitions" | |
39 | /* defined to be Linux compatible; Rockbox needs no arguments so there | 39 | #endif |
40 | * is no harm in passing them and we save a little code size */ | ||
41 | typedef void(*entry_fn)(int, char**, int, int); | ||
42 | 40 | ||
43 | /* Note: This is based purely on disassembly of the SPL from the FiiO M3K. | 41 | /* Note: This is based purely on disassembly of the SPL from the FiiO M3K. |
44 | * The code there is somewhat generic and corresponds roughly to Ingenic's | 42 | * The code there is somewhat generic and corresponds roughly to Ingenic's |
@@ -243,83 +241,70 @@ static int nandread(uint32_t addr, uint32_t size, void* buffer) | |||
243 | return rc; | 241 | return rc; |
244 | } | 242 | } |
245 | 243 | ||
246 | static int nandwrite(uint32_t addr, uint32_t size, const void* buffer) | 244 | /* Entry point function type, defined to be Linux compatible. */ |
247 | { | 245 | typedef void(*entry_fn)(int, char**, int, int); |
248 | int rc; | ||
249 | int mf_id, dev_id; | ||
250 | 246 | ||
251 | if((rc = nand_open())) | 247 | /* Kernel command line arguments */ |
252 | return rc; | 248 | static char* argv[2]; |
253 | if((rc = nand_identify(&mf_id, &dev_id))) { | ||
254 | nand_close(); | ||
255 | return rc; | ||
256 | } | ||
257 | 249 | ||
258 | if((rc = nand_enable_writes(true))) | 250 | /* This variable is defined by the maskrom. It's simply the level of the |
259 | goto _end; | 251 | * boot_sel[2:0] pins (GPIOs B28-30) at boot time. Meaning of the bits: |
252 | * | ||
253 | * boot_sel[2] boot_sel[1] boot_sel[0] Description | ||
254 | * ----------------------------------------------------------------- | ||
255 | * 1 X X EXCLK is 26 MHz | ||
256 | * 0 X X EXCLK is 24 MHz | ||
257 | * X 1 1 Boot from SFC0 | ||
258 | * X 0 1 Boot from MSC0 | ||
259 | * X 1 0 Boot from USB 2.0 device | ||
260 | * ----------------------------------------------------------------- | ||
261 | * Source: X1000 PM pg. 687, "XBurst Boot ROM Specification" | ||
262 | */ | ||
263 | extern const uint32_t boot_sel; | ||
260 | 264 | ||
261 | if((rc = nand_erase(addr, size))) | 265 | void spl_main(void) |
262 | goto _end1; | 266 | { |
267 | int opt_index; | ||
268 | uint8_t* load_addr; | ||
269 | const struct spl_boot_option* opt; | ||
263 | 270 | ||
264 | rc = nand_write(addr, size, (const uint8_t*)buffer); | 271 | /* Basic hardware init */ |
272 | init(); | ||
265 | 273 | ||
266 | _end1: | 274 | /* If doing a USB boot, host PC will upload 2nd stage itself, |
267 | /* an error here is very unlikely, so ignore it */ | 275 | * we should not load anything from flash or change clocks. */ |
268 | nand_enable_writes(false); | 276 | if((boot_sel & 3) == 2) |
277 | return; | ||
269 | 278 | ||
270 | _end: | 279 | /* Get the boot option */ |
271 | nand_close(); | 280 | opt_index = spl_get_boot_option(); |
272 | return rc; | 281 | opt = &spl_boot_options[opt_index]; |
273 | } | 282 | load_addr = (uint8_t*)opt->load_addr; |
274 | 283 | ||
275 | /* Kernel command line arguments */ | 284 | /* Set up hardware, load stuff from flash */ |
276 | static char* argv[2]; | 285 | spl_handle_pre_boot(opt_index); |
286 | if(nandread(opt->nand_addr, opt->nand_size, load_addr)) | ||
287 | spl_error(); | ||
277 | 288 | ||
278 | void main(void) | 289 | if(!opt->cmdline) { |
279 | { | 290 | /* No command line => we are booting Rockbox, decompress bootloader. |
280 | if(!(SPL_ARGUMENTS->flags & SPL_FLAG_SKIP_INIT)) | 291 | * In the case of Rockbox, load binary directly to exec address */ |
281 | init(); | 292 | uint32_t out_size = X1000_DRAM_END - opt->exec_addr; |
282 | 293 | int rc = ucl_unpack(load_addr, opt->nand_size, | |
283 | switch(SPL_ARGUMENTS->command) { | 294 | (uint8_t*)opt->exec_addr, &out_size); |
284 | case SPL_CMD_BOOT: { | 295 | if(rc != UCL_E_OK) |
285 | int option = SPL_ARGUMENTS->param1; | ||
286 | if(option == SPL_BOOTOPT_CHOOSE) | ||
287 | option = spl_get_boot_option(); | ||
288 | if(option == SPL_BOOTOPT_NONE) | ||
289 | return; | ||
290 | |||
291 | const struct spl_boot_option* opt = &spl_boot_options[option-1]; | ||
292 | if(nandread(opt->nand_addr, opt->nand_size, (void*)opt->load_addr)) | ||
293 | spl_error(); | 296 | spl_error(); |
294 | |||
295 | /* Let target handle necessary pre-boot setup */ | ||
296 | spl_handle_pre_boot(option); | ||
297 | |||
298 | /* Reading the Linux command line from the bootloader is handled by | ||
299 | * arch/mips/xburst/core/prom.c -- see Ingenic kernel sources. | ||
300 | * | ||
301 | * Rockbox doesn't use arguments, but passing them does not hurt and it | ||
302 | * saves an unnecessary branch. | ||
303 | */ | ||
304 | entry_fn entry = (entry_fn)opt->exec_addr; | ||
305 | argv[0] = 0; | ||
306 | argv[1] = (char*)opt->cmdline; | ||
307 | |||
308 | commit_discard_idcache(); | ||
309 | entry(2, argv, 0, 0); | ||
310 | __builtin_unreachable(); | ||
311 | } | 297 | } |
312 | 298 | ||
313 | case SPL_CMD_FLASH_READ: | 299 | /* Reading the Linux command line from the bootloader is handled by |
314 | SPL_STATUS->err_code = nandread(SPL_ARGUMENTS->param1, | 300 | * arch/mips/xburst/core/prom.c -- see Ingenic kernel sources. It's |
315 | SPL_ARGUMENTS->param2, | 301 | * simply an (int argc, char* argv[]) thing. |
316 | (void*)SPL_BUFFER_ADDRESS); | 302 | */ |
317 | return; | 303 | entry_fn entry = (entry_fn)opt->exec_addr; |
318 | 304 | argv[0] = 0; | |
319 | case SPL_CMD_FLASH_WRITE: | 305 | argv[1] = (char*)opt->cmdline; |
320 | SPL_STATUS->err_code = nandwrite(SPL_ARGUMENTS->param1, | 306 | |
321 | SPL_ARGUMENTS->param2, | 307 | commit_discard_idcache(); |
322 | (const void*)SPL_BUFFER_ADDRESS); | 308 | entry(2, argv, 0, 0); |
323 | return; | 309 | __builtin_unreachable(); |
324 | } | ||
325 | } | 310 | } |
diff --git a/firmware/target/mips/ingenic_x1000/spl-x1000.h b/firmware/target/mips/ingenic_x1000/spl-x1000.h index d2255a8d05..062cb40f88 100644 --- a/firmware/target/mips/ingenic_x1000/spl-x1000.h +++ b/firmware/target/mips/ingenic_x1000/spl-x1000.h | |||
@@ -22,10 +22,7 @@ | |||
22 | #ifndef __SPL_X1000_H__ | 22 | #ifndef __SPL_X1000_H__ |
23 | #define __SPL_X1000_H__ | 23 | #define __SPL_X1000_H__ |
24 | 24 | ||
25 | #include "spl-x1000-defs.h" | 25 | #include <stdint.h> |
26 | |||
27 | #define SPL_ARGUMENTS ((struct x1000_spl_arguments*)SPL_ARGUMENTS_ADDRESS) | ||
28 | #define SPL_STATUS ((struct x1000_spl_status*)SPL_STATUS_ADDRESS) | ||
29 | 26 | ||
30 | struct spl_boot_option { | 27 | struct spl_boot_option { |
31 | uint32_t nand_addr; | 28 | uint32_t nand_addr; |
@@ -35,15 +32,13 @@ struct spl_boot_option { | |||
35 | const char* cmdline; /* for Linux */ | 32 | const char* cmdline; /* for Linux */ |
36 | }; | 33 | }; |
37 | 34 | ||
38 | /* Defined by target, indices are 0 = ROCKBOX, 1 = ORIG_FW, etc... */ | 35 | /* Defined by target, order is not important */ |
39 | extern const struct spl_boot_option spl_boot_options[]; | 36 | extern const struct spl_boot_option spl_boot_options[]; |
40 | 37 | ||
41 | /* Called on a fatal error */ | 38 | /* Called on a fatal error */ |
42 | extern void spl_error(void) __attribute__((noreturn)); | 39 | extern void spl_error(void) __attribute__((noreturn)); |
43 | 40 | ||
44 | /* When SPL boots with SPL_BOOTOPTION_CHOOSE, this function is invoked | 41 | /* Invoked by SPL main routine to determine the boot option */ |
45 | * to let the target figure out the boot option based on buttons the | ||
46 | * user is pressing */ | ||
47 | extern int spl_get_boot_option(void); | 42 | extern int spl_get_boot_option(void); |
48 | 43 | ||
49 | /* Do any setup/initialization needed for the given boot option, this | 44 | /* Do any setup/initialization needed for the given boot option, this |
diff --git a/firmware/target/mips/ingenic_x1000/spl.lds b/firmware/target/mips/ingenic_x1000/spl.lds index 36ae8c34d1..e932bd9c2e 100644 --- a/firmware/target/mips/ingenic_x1000/spl.lds +++ b/firmware/target/mips/ingenic_x1000/spl.lds | |||
@@ -3,8 +3,8 @@ | |||
3 | 3 | ||
4 | OUTPUT_FORMAT("elf32-littlemips") | 4 | OUTPUT_FORMAT("elf32-littlemips") |
5 | OUTPUT_ARCH(MIPS) | 5 | OUTPUT_ARCH(MIPS) |
6 | ENTRY(_start) | 6 | ENTRY(_spl_start) |
7 | STARTUP(target/mips/ingenic_x1000/crt0.o) | 7 | STARTUP(target/mips/ingenic_x1000/spl-start.o) |
8 | 8 | ||
9 | MEMORY { | 9 | MEMORY { |
10 | /* First 4k of TCSM is used by mask ROM for stack + variables, | 10 | /* First 4k of TCSM is used by mask ROM for stack + variables, |
@@ -15,9 +15,12 @@ MEMORY { | |||
15 | 15 | ||
16 | SECTIONS | 16 | SECTIONS |
17 | { | 17 | { |
18 | /* Mask ROM variables, addresses found by disassembly */ | ||
19 | boot_sel = X1000_TCSM_BASE + 0x1ec; | ||
20 | |||
18 | .text : | 21 | .text : |
19 | { | 22 | { |
20 | *(.init.text); | 23 | *(.init.spl); |
21 | *(.text*); | 24 | *(.text*); |
22 | *(.icode*); | 25 | *(.icode*); |
23 | } > TCSM | 26 | } > TCSM |
diff --git a/firmware/target/mips/ingenic_x1000/x1000boot.make b/firmware/target/mips/ingenic_x1000/x1000boot.make new file mode 100644 index 0000000000..e69b6a1439 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/x1000boot.make | |||
@@ -0,0 +1,53 @@ | |||
1 | # __________ __ ___. | ||
2 | # Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
3 | # Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
4 | # Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
5 | # Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
6 | # \/ \/ \/ \/ \/ | ||
7 | # $Id$ | ||
8 | # | ||
9 | |||
10 | INCLUDES += -I$(APPSDIR) | ||
11 | SRC += $(call preprocess, $(APPSDIR)/SOURCES) | ||
12 | |||
13 | LDSDEP := $(FIRMDIR)/export/cpu.h $(FIRMDIR)/export/config/$(MODELNAME).h | ||
14 | |||
15 | BOOTLDS := $(FIRMDIR)/target/$(CPU)/$(MANUFACTURER)/boot.lds | ||
16 | BOOTLINK := $(BUILDDIR)/boot.link | ||
17 | |||
18 | SPLLDS := $(FIRMDIR)/target/$(CPU)/$(MANUFACTURER)/spl.lds | ||
19 | SPLLINK := $(BUILDDIR)/spl.link | ||
20 | |||
21 | CLEANOBJS += $(BUILDDIR)/bootloader.* $(BUILDDIR)/spl.* | ||
22 | |||
23 | include $(FIRMDIR)/target/$(CPU)/$(MANUFACTURER)/$(MODELNAME)/boot.make | ||
24 | |||
25 | .SECONDEXPANSION: | ||
26 | |||
27 | $(BOOTLINK): $(BOOTLDS) $(LDSDEP) | ||
28 | $(call PRINTS,PP $(@F)) | ||
29 | $(call preprocess2file,$<,$@,) | ||
30 | |||
31 | $(BUILDDIR)/bootloader.elf: $$(OBJ) $(FIRMLIB) $(CORE_LIBS) $$(BOOTLINK) | ||
32 | $(call PRINTS,LD $(@F))$(CC) $(GCCOPTS) -Os -nostdlib -o $@ $(OBJ) \ | ||
33 | -L$(BUILDDIR)/firmware -lfirmware \ | ||
34 | -L$(BUILDDIR)/lib $(call a2lnk, $(CORE_LIBS)) \ | ||
35 | -lgcc -T$(BOOTLINK) $(GLOBAL_LDOPTS) \ | ||
36 | -Wl,--gc-sections -Wl,-Map,$(BUILDDIR)/bootloader.map | ||
37 | |||
38 | $(BUILDDIR)/bootloader.bin: $(BUILDDIR)/bootloader.elf | ||
39 | $(call PRINTS,OC $(@F))$(call objcopy,$<,$@) | ||
40 | |||
41 | $(SPLLINK): $(SPLLDS) $(LDSDEP) | ||
42 | $(call PRINTS,PP $(@F)) | ||
43 | $(call preprocess2file,$<,$@,) | ||
44 | |||
45 | $(BUILDDIR)/spl.elf: $$(OBJ) $(FIRMLIB) $(CORE_LIBS) $$(SPLLINK) | ||
46 | $(call PRINTS,LD $(@F))$(CC) $(GCCOPTS) -Os -nostdlib -o $@ $(OBJ) \ | ||
47 | -L$(BUILDDIR)/firmware -lfirmware \ | ||
48 | -L$(BUILDDIR)/lib $(call a2lnk, $(CORE_LIBS)) \ | ||
49 | -lgcc -T$(SPLLINK) $(GLOBAL_LDOPTS) \ | ||
50 | -Wl,--gc-sections -Wl,-Map,$(BUILDDIR)/spl.map | ||
51 | |||
52 | $(BUILDDIR)/spl.bin: $(BUILDDIR)/spl.elf | ||
53 | $(call PRINTS,OC $(@F))$(call objcopy,$<,$@) | ||