aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/ubicom32/files/arch/ubicom32/kernel/head.S
blob: 0c60504af54b2ce870d13c7f4e824a4412414e9d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
/*
 * arch/ubicom32/kernel/head.S
 *	<TODO: Replace with short file description>
 *
 * (C) Copyright 2009, Ubicom, Inc.
 *
 * This file is part of the Ubicom32 Linux Kernel Port.
 *
 * The Ubicom32 Linux Kernel Port is free software: you can redistribute
 * it and/or modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation, either version 2 of the
 * License, or (at your option) any later version.
 *
 * The Ubicom32 Linux Kernel Port is distributed in the hope that it
 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 * the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with the Ubicom32 Linux Kernel Port.  If not,
 * see <http://www.gnu.org/licenses/>.
 *
 * Ubicom32 implementation derived from (with many thanks):
 *   arch/m68knommu
 *   arch/blackfin
 *   arch/parisc
 */
#include <linux/sys.h>
#include <linux/linkage.h>
#include <asm/asm-offsets.h>
#include <asm/page_offset.h>
#define __ASM__
#include <asm/ip5000.h>


#define SRC_AN A3
#define DST_AN A4

#define PARAM_DN D0
#define TMP_DN D15
#define TMP2_DN D14

/*
 * The following code is placed at the start of the Linux section of memory.
 * This is the primary entry point for Linux.
 *
 * However, we also want the syscall entry/exit code to be at a fixed address.
 * So we take the primary entry point and reserve 16 bytes.  That address is
 * where the system_call entry point exists.  This 16 bytes basically allows
 * us to jump around the system_call entry point code to the actual startup
 * code.
 *
 * Linux Memory Map (see vlinux.lds.S):
 * 0x40400000 - Primary Entry Point for Linux (jump around code below).
 * 0x40400010 - Old syscall Entry Point.
 */

	.sect	.skip_syscall, "ax", @progbits
	.global	__skip_syscall_section
__skip_syscall_section:
	moveai		A3, #%hi(_start)
	lea.1		A3, %lo(_start)(A3)
	ret		A3
/*
 * __os_node_offset contains the offset from KERNELBASE to the os_node, it is
 * not intended to be used by anything except the boot code.
 */
__os_node_offset:
.long	(_os_node - KERNELSTART)

.text
.global	_start

/*
 * start()
 *	This is the start of the Linux kernel.
 */
_start:
	move.4		SCRATCHPAD1, #0


/*
 * Setup the range registers... the loader has setup a few, but we will go ahead
 * and correct them for our own limits. Note that once set these are never
 * changed again.  The ranges are as follows
 *
 *  D_RANGE0 - io block (set up by loaded)
 *
 *  I_RANGE0 and D_RANGE1 - kernel/ultra loader address space bottom of ocm-> top
 *	of ram typically 0x3ffc0000 - 0x440000000
 *  I_RANGE1 - kernel / userspace transition area (aka syscalls, context switches)
 *	typically 0x3FFC0030 - ~0x3FFC0200
 *  I_RANGE2 / D_RANGE2 - slab area
 *	typically 0x40A00000 - ~0x44000000
 *  I_RANGE3
 *	old system call interface if enabled.
 *
 *   D_RANGE3, D_RANGE4 - unused.
 */
	moveai		SRC_AN, #%hi(PAGE_OFFSET_RAW)
	lea.4		SRC_AN, %lo(PAGE_OFFSET_RAW)(SRC_AN)
	move.4		D_RANGE1_LO, SRC_AN
	move.4		I_RANGE0_LO, SRC_AN

; don't try to calculate I_RANGE_HI, see below
;	moveai		SRC_AN, #%hi(___init_end-4)
;	lea.4		SRC_AN, %lo(___init_end-4)(SRC_AN)
;	move.4		I_RANGE0_HI, SRC_AN

	moveai		SRC_AN, #%hi(SDRAMSTART + CONFIG_MIN_RAMSIZE-4)
	lea.4		SRC_AN, %lo(SDRAMSTART + CONFIG_MIN_RAMSIZE-4)(SRC_AN)
	move.4		D_RANGE1_HI, SRC_AN

; for now allow the whole ram to be executable as well so we don't run into problems
; once we load user more code.
	move.4		I_RANGE0_HI, SRC_AN

#ifdef CONFIG_PROTECT_KERNEL
; when kernel protection is enabled, we only open up syscall and non kernel text
; for userspace apps, for now only irange registers registers 1 and 2 are used for userspace.

	;; syscall range
	moveai		SRC_AN, #%hi(__syscall_text_run_begin)
	lea.4		SRC_AN, %lo(__syscall_text_run_begin)(SRC_AN)
	move.4		I_RANGE1_LO, SRC_AN
	moveai		SRC_AN, #%hi(__syscall_text_run_end)
	lea.4		SRC_AN, %lo(__syscall_text_run_end)(SRC_AN)
	move.4		I_RANGE1_HI, SRC_AN

	;; slab instructions
	moveai		SRC_AN, #%hi(_edata)
	lea.4		SRC_AN, %lo(_edata)(SRC_AN)
	move.4		I_RANGE2_LO, SRC_AN
	;; End of DDR is already in range0 hi so just copy it.
	move.4		I_RANGE2_HI, I_RANGE0_HI

#ifdef CONFIG_OLD_40400010_SYSTEM_CALL
	;; create a small hole for old syscall location
	moveai		SRC_AN, #%hi(0x40400000)
	lea.4		I_RANGE3_LO, 0x10(SRC_AN)
	lea.4		I_RANGE3_HI, 0x14(SRC_AN)
#endif
	;; slab data (same as slab instructions but starting a little earlier).
	moveai		SRC_AN, #%hi(_data_protection_end)
	lea.4		SRC_AN, %lo(_data_protection_end)(SRC_AN)
	move.4		D_RANGE2_LO, SRC_AN
	move.4		D_RANGE2_HI, I_RANGE0_HI

;; enable ranges
	;; skip I_RANGE0_EN
	move.4		I_RANGE1_EN, #-1
	move.4		I_RANGE2_EN, #-1
#ifdef CONFIG_OLD_40400010_SYSTEM_CALL
	move.4		I_RANGE3_EN, #-1
#else
	move.4		I_RANGE3_EN, #0
#endif
	;; skip D_RANGE0_EN or D_RANGE1_EN
	move.4		D_RANGE2_EN, #-1
	move.4		D_RANGE3_EN, #0
	move.4		D_RANGE4_EN, #0
#endif

;
; If __ocm_free_begin is smaller than __ocm_free_end the
; setup OCM text and data ram banks properly
;
	moveai		DST_AN, #%hi(__ocm_free_begin)
	lea.4		TMP_DN, %lo(__ocm_free_begin)(DST_AN)
	moveai		DST_AN, #%hi(__ocm_free_end)
	lea.4		TMP2_DN, %lo(__ocm_free_end)(DST_AN)
	sub.4		#0, TMP2_DN, TMP_DN
	jmple.f		2f
	moveai		DST_AN, #%hi(__data_begin)
	lea.4		TMP_DN, %lo(__data_begin)(DST_AN)
	moveai		DST_AN, #%hi(OCMSTART)
	lea.4		TMP2_DN, %lo(OCMSTART)(DST_AN)
	sub.4		TMP_DN, TMP_DN, TMP2_DN
	lsr.4		TMP_DN, TMP_DN, #15
	lsl.4		TMP_DN, #1, TMP_DN
	moveai		DST_AN, #%hi(OCMC_BASE)
	add.4		OCMC_BANK_MASK(DST_AN), #-1, TMP_DN
	pipe_flush	0
2:
;
; Load .ocm_text
;
	moveai          DST_AN, #%hi(__ocm_text_run_end)
	lea.4           TMP_DN, %lo(__ocm_text_run_end)(DST_AN)
	moveai          DST_AN, #%hi(__ocm_text_run_begin)
	lea.4           DST_AN, %lo(__ocm_text_run_begin)(DST_AN)
	moveai          SRC_AN, #%hi(__ocm_text_load_begin)
	lea.4           SRC_AN, %lo(__ocm_text_load_begin)(SRC_AN)
	jmpt.t          2f

1:      move.4          (DST_AN)4++, (SRC_AN)4++

2:      sub.4           #0, DST_AN, TMP_DN
	jmpne.t         1b
;
; Load .syscall_text
;
	moveai          DST_AN, #%hi(__syscall_text_run_end)
	lea.4           TMP_DN, %lo(__syscall_text_run_end)(DST_AN)
	moveai          DST_AN, #%hi(__syscall_text_run_begin)
	lea.4           DST_AN, %lo(__syscall_text_run_begin)(DST_AN)
	moveai          SRC_AN, #%hi(__syscall_text_load_begin)
	lea.4           SRC_AN, %lo(__syscall_text_load_begin)(SRC_AN)
	jmpt.t          2f

1:	move.4          (DST_AN)4++, (SRC_AN)4++

2:	sub.4           #0, DST_AN, TMP_DN
	jmpne.t         1b

;
; Load .ocm_data
;
	moveai          DST_AN, #%hi(__ocm_data_run_end)
	lea.4           TMP_DN, %lo(__ocm_data_run_end)(DST_AN)
	moveai          DST_AN, #%hi(__ocm_data_run_begin)
	lea.4           DST_AN, %lo(__ocm_data_run_begin)(DST_AN)
	moveai          SRC_AN, #%hi(__ocm_data_load_begin)
	lea.4           SRC_AN, %lo(__ocm_data_load_begin)(SRC_AN)
	jmpt.t          2f

1:      move.4          (DST_AN)4++, (SRC_AN)4++

2:      sub.4           #0, DST_AN, TMP_DN
	jmpne.t         1b

; Clear .bss
;
	moveai		SRC_AN, #%hi(_ebss)
	lea.4		TMP_DN, %lo(_ebss)(SRC_AN)
	moveai		DST_AN, #%hi(_sbss)
	lea.4		DST_AN, %lo(_sbss)(DST_AN)
	jmpt.t		2f

1:	move.4		(DST_AN)4++, #0

2:	sub.4		#0, DST_AN, TMP_DN
	jmpne.t		1b

; save our parameter to devtree (after clearing .bss)
	moveai		DST_AN, #%hi(devtree)
	lea.4		DST_AN, %lo(devtree)(DST_AN)
	move.4		(DST_AN), PARAM_DN

	moveai		sp, #%hi(init_thread_union)
	lea.4		sp, %lo(init_thread_union)(sp)
	movei		TMP_DN, #ASM_THREAD_SIZE
	add.4		sp, sp, TMP_DN
	move.4		-4(sp)++, #0 ; nesting level = 0
	move.4		-4(sp)++, #1 ; KERNEL_THREAD

;; ip3k-elf-gdb backend now sets scratchpad3 to 1 when either continue
;; or single step commands are issued. scratchpad3 is set to 0 when the
;; debugger detaches from the board.
	move.4		TMP_DN, scratchpad3
	lsl.4		TMP_DN, TMP_DN, #0x0
	jmpeq.f		_jump_to_start_kernel
_ok_to_set_break_points_in_linux:
;; THREAD_STALL
	move.4		mt_dbg_active_clr,#-1
;; stalling the threads isn't instantaneous.. need to flush the pipe.
	pipe_flush	0
	pipe_flush	0

_jump_to_start_kernel:
	moveai		SRC_AN, #%hi(start_kernel)
	lea.4		SRC_AN, %lo(start_kernel)(SRC_AN)
	ret		SRC_AN