-
Notifications
You must be signed in to change notification settings - Fork 7
/
versatilepb.nothing
585 lines (516 loc) · 20.6 KB
/
versatilepb.nothing
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
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
;; -*- mode: scheme -*-
;; Copyright (C) 2012-2015 Tony Garnock-Jones <[email protected]>
;;
;; This file is part of pi-nothing.
;;
;; pi-nothing 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 3 of the License,
;; or (at your option) any later version.
;;
;; pi-nothing 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 pi-nothing. If not, see <http://www.gnu.org/licenses/>.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Interrupt table
;; TODO: Limitation on the linker means this routine has to be early
;; in the image, so that the offset to
;; sys:exception-handler-undefined-instruction etc isn't too large to
;; fit in an instruction. See the label-linker instance in
;; `indirect-immediate` in mach-arm7.rkt. A more flexible linker would
;; be able to put the needed value in the data quasisegment, like the
;; non-label branch does.
(define (configure-interrupt-table)
(let ((mutable vec 0))
(while (<u vec 8)
(! (<< vec 2) #xe59ff018) ;; LDR PC, [PC, #24]
(set! vec (+ vec 1))))
(! #x20 interrupt:reset)
(! #x24 sys:exception-handler-undefined-instruction)
(! sys:interrupt-vector-undefined-instruction interrupt:undefined-instruction)
(! #x28 sys:exception-handler-swi)
(! sys:interrupt-vector-swi interrupt:swi)
(! #x2c sys:exception-handler-prefetch-abort)
(! sys:interrupt-vector-prefetch-abort interrupt:prefetch-abort)
(! #x30 sys:exception-handler-data-abort)
(! sys:interrupt-vector-data-abort interrupt:data-abort)
(! #x34 interrupt:unused)
(! #x38 sys:exception-handler-irq)
(! sys:interrupt-vector-irq interrupt:irq)
(! #x3c sys:exception-handler-fiq)
(! sys:interrupt-vector-fiq interrupt:fiq))
(define (cpu-enable-interrupts)
(sys:set-cpsr (binand (sys:get-cpsr) #xffffff3f))) ;; clear I and F bits
(define (cpu-disable-interrupts)
(sys:set-cpsr (binor (sys:get-cpsr) #xc0))) ;; set I and F bits
(define (debug:halt)
(puts (data #"debug:halt\n\0"))
(cpu-disable-interrupts)
(while (= 0 0) (sys:wait-for-interrupt))) ;; doesn't actually suspend if pending interrupt still uncleared.
(define (interrupt:reset)
(puts (data #"interrupt:reset\n\0"))
(debug:halt)
0)
(define (interrupt:undefined-instruction)
(puts (data #"interrupt:undefined-instruction\n\0"))
(debug:halt)
0) ;; either 0 or 4 depending on whether to skip the insn or retry it
(define (interrupt:swi)
(puts (data #"interrupt:swi\n\0"))
(debug:halt)
0) ;; will return immediately after the SWI
(define (interrupt:prefetch-abort)
(puts (data #"interrupt:prefetch-abort\n\0"))
(debug:halt)
0) ;; either 0 or 4 depending on whether to skip the insn or retry it
(define (interrupt:data-abort)
(puts (data #"interrupt:data-abort\n\0"))
(debug:halt)
0) ;; either 4 or 8 depending on whether to skip the insn or retry it
(define (interrupt:unused)
(puts (data #":unused\n\0"))
(debug:halt)
0)
(define (interrupt:irq)
;; (puts (data #"interrupt:irq\n\0"))
(?volatile (+ vic0 vic-address)) ;; dummy read -- see vic-nonvectored-irq-status
(sys:set-spsr (binor (sys:get-spsr) #xc0)) ;; disable interrupts when we return from this handler.
;; ^ we do this because we don't know which interrupt caused us
;; here, so we can't clear the condition and will loop forever.
;; We leave the clearing of the condition to user code.
;; (debug:halt)
4) ;; not zero! otherwise we end up skipping an instruction
(define (interrupt:fiq)
(puts (data #"interrupt:fiq\n\0"))
(debug:halt)
4) ;; not zero! otherwise we end up skipping an instruction
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(struct uart ((dr word)
(rsr-ecr word)
(reserved1 byte 16)
(fr word)
(reserved2 byte 4)
(lpr word)
(ibrd word)
(fbrd word)
(lcr-h word)
(cr word)
(ifls word)
(imsc word)
(raw-interrupt-status word) ;; a.k.a. RIS
(masked-interrupt-status word) ;; a.k.a. MIS
(icr word)
(dmacr word)))
(const uart0 #x101f1000)
(const uart-flag-rx-fifo-empty #x10)
(const uart-flag-tx-fifo-full #x20)
;; Tells the UART to signal an interrupt. Still need to enable
;; interrupts at the VIC and at the CPU.
(define (uart-enable-interrupt)
(let ((old (? (+ uart0 uart-imsc))))
(puts (data #"UART-IMSC: \0")) (putx old) (newline)
(! (+ uart0 uart-imsc) (binor old (<< 1 4))))) ;; RXIM
(define (uart-interrupt-status)
(? (+ uart0 uart-masked-interrupt-status)))
(define (division-by-zero a b)
(puts (data #"Division by zero\0"))
(debug:halt))
(define (putc ch)
(while (<> (binand (? (+ uart0 uart-fr)) uart-flag-tx-fifo-full) 0))
(! (+ uart0 uart-dr) ch))
(define (poll-for-char)
(if (<> (binand (? (+ uart0 uart-fr)) uart-flag-rx-fifo-empty) 0)
-1
(? (+ uart0 uart-dr))))
(define (getc)
(let ((mutable ch -1))
(while (= ch -1)
(set! ch (poll-for-char)))
ch))
(define (puts p)
(let ((ch (?byte p)))
(when (<> ch 0)
(putc ch)
(puts (+ p 1)))))
(define (putn* i)
(if (<> i 0)
(begin (putn* (/ i 10))
(putc (+ (% i 10) 48)))
0))
(define (putn i)
(if (<> i 0)
(putn* i)
(putc 48)))
(define (putx n)
(let ((mutable shift 28))
(while (>=s shift 0)
(let ((digit (binand 15 (>>u n shift))))
(putc (+ digit (if (>=u digit 10)
55 ;; 65 - 10
48)))
(set! shift (- shift 4))))))
(define (newline)
(putc 13)
(putc 10))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(const N_PCI_BASE_ADDRESS_REGISTERS 6)
(const PCI_HEADER_TYPE_GENERAL #x00)
(const PCI_HEADER_TYPE_BRIDGE #x01)
(const PCI_HEADER_TYPE_CARDBUS #x02)
(const PCI_HEADER_TYPE_MULTI_FUNCTION_MASK #x80)
(const PCI_HEADER_TYPE_MASK #x7f)
(const PCI_CLASS_LEGACY #x00) ;; Device was built prior definition of the class code field
(const PCI_CLASS_STORAGE #x01) ;; Mass Storage Controller
(const PCI_CLASS_NETWORK #x02) ;; Network Controller
(const PCI_CLASS_DISPLAY #x03) ;; Display Controller
(const PCI_CLASS_MULTIMEDIA #x04) ;; Multimedia Controller
(const PCI_CLASS_MEMORY #x05) ;; Memory Controller
(const PCI_CLASS_BRIDGE #x06) ;; Bridge Device
(const PCI_CLASS_SIMPLE_COMM #x07) ;; Simple Communication Controllers
(const PCI_CLASS_BASE_SYSTEM_PERIPHERAL #x08) ;; Base System Peripherals
(const PCI_CLASS_INPUT #x09) ;; Input Devices
(const PCI_CLASS_DOCK #x0A) ;; Docking Stations
(const PCI_CLASS_PROCESSOR #x0B) ;; Processors
(const PCI_CLASS_SERIAL_BUS #x0C) ;; Serial Bus Controllers
(const PCI_CLASS_WIRELESS #x0D) ;; Wireless Controllers
(const PCI_CLASS_INTELLIGENT_IO #x0E) ;; Intelligent I/O Controllers
(const PCI_CLASS_SATELLITE_COMM #x0F) ;; Satellite Communication Controllers
(const PCI_CLASS_CRYPTO #x10) ;; Encryption/Decryption Controllers
(const PCI_CLASS_DASP #x11) ;; Data Acquisition and Signal Processing Controllers
;; others reserved
(const PCI_CLASS_MISC #xFF) ;; Device does not fit any defined class.
;; N.B.: This lines up with struct pci-device below!
(const PCI_REGISTER_VENDOR_ID 0)
(const PCI_REGISTER_DEVICE_ID 2)
(const PCI_REGISTER_COMMAND 4)
(const PCI_REGISTER_STATUS 6)
(const PCI_REGISTER_REVISION_ID 8)
(const PCI_REGISTER_PROG_IF 9)
(const PCI_REGISTER_SUBCLASS 10)
(const PCI_REGISTER_CLASS_CODE 11)
(const PCI_REGISTER_CACHE_LINE_SIZE 12)
(const PCI_REGISTER_LATENCY_TIMER 13)
(const PCI_REGISTER_HEADER_TYPE 14)
(const PCI_REGISTER_BIST 15)
(const PCI_REGISTER_BAR0 16)
(const PCI_REGISTER_BAR1 20)
(const PCI_REGISTER_BAR2 24)
(const PCI_REGISTER_BAR3 28)
(const PCI_REGISTER_BAR4 32)
(const PCI_REGISTER_BAR5 36)
(const PCI_REGISTER_CARDBUS_CIS_POINTER 40)
(const PCI_REGISTER_SUBSYSTEM_VENDOR_ID 44)
(const PCI_REGISTER_SUBSYSTEM_ID 46)
(const PCI_REGISTER_EXPANSION_ROM_BASE_ADDRESS 48)
(const PCI_REGISTER_CAPABILITIES_POINTER 52)
(const PCI_REGISTER_INTERRUPT_LINE 60)
(const PCI_REGISTER_INTERRUPT_PIN 61)
(const PCI_REGISTER_MIN_GRANT 62)
(const PCI_REGISTER_MAX_LATENCY 63)
(const PCI_COMMAND_IO_SPACE #x0001)
(const PCI_COMMAND_MEMORY_SPACE #x0002)
(const PCI_COMMAND_BUS_MASTER #x0004)
(const PCI_COMMAND_SPECIAL_CYCLES #x0008)
(const PCI_COMMAND_MEMORY_WRITE_AND_INVALIDATE_ENABLE #x0010)
(const PCI_COMMAND_VGA_PALETTE_SNOOP #x0020)
(const PCI_COMMAND_PARITY_ERROR_RESPONSE #x0040)
(const PCI_COMMAND_SERR_ENABLE #x0100)
(const PCI_COMMAND_FAST_BACKTOBACK_ENABLE #x0200)
(const PCI_COMMAND_INTERRUPT_DISABLE #x0400)
(const PCI_STATUS_INTERRUPT #x0008)
(const PCI_STATUS_CAPABILITIES_LIST #x0010)
(const PCI_STATUS_66_MHZ_CAPABLE #x0020)
(const PCI_STATUS_FAST_BACKTOBACK_CAPABLE #x0080)
(const PCI_STATUS_MASTER_DATA_PARITY_ERROR #x0100)
(const PCI_STATUS_DEVSEL_MASK #x0600)
(const PCI_STATUS_DEVSEL_FAST #x0000)
(const PCI_STATUS_DEVSEL_MEDIUM #x0200)
(const PCI_STATUS_DEVSEL_SLOW #x0400)
(const PCI_STATUS_SIGNALED_TARGET_ABORT #x0800)
(const PCI_STATUS_RECEIVED_TARGET_ABORT #x1000)
(const PCI_STATUS_RECEIVED_MASTER_ABORT #x2000)
(const PCI_STATUS_SIGNALED_SYSTEM_ERROR #x4000)
(const PCI_STATUS_DETECTED_PARITY_ERROR #x8000)
(struct pci-device ((vendor-id word) ;; vendor ID in low 16 bits, device ID in high 16 bits
(status-command word)
(class-code-rev-id word)
(bist word)
(bars word 6) ;; TODO: would be nice to reuse N_PCI_BASE_ADDRESS_REGISTERS here
(cardbus-cis-pointer word)
(subsystem-id word) ;; vendor ID in low 16 bits, subsystem ID proper in high
(expansion-rom-base word)
(unused word)
(reserved word)
(max-lat-min-gnt-interrupt-pin-interrupt-line word)))
(define (?pci slot offset)
(? (+ #x42000000 (<< slot 11) offset)))
(define (!pci slot offset value)
(! (+ #x42000000 (<< slot 11) offset) value))
(define (find-self-slot)
;; http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0224i/Cacdijji.html
(let ((mutable i 11))
(while (<u i 32)
(when (= (? (+ #x41000000 (<< i 11)))
#x030010ee) ;; Vendor/device for PB926EJ-S
(return i))
(set! i (+ i 1)))))
(define (configure-pci-slot slot)
(let ((vendor-id (?pci slot pci-device-vendor-id)))
(when (<> vendor-id -1)
(puts (data #"Slot number \0")) (putn slot) (putc 32) (putx vendor-id) (newline)
(puts (data #"Class/Rev \0")) (putx (?pci slot pci-device-class-code-rev-id)) (newline)
(let ((mutable bar-num 0))
(while (<u bar-num N_PCI_BASE_ADDRESS_REGISTERS)
(puts (data #"BAR \0")) (putn bar-num) (putc 32)
(putx (?pci slot (+ pci-device-bars (<< bar-num 4))))
(newline)
(set! bar-num (+ bar-num 1))))
)))
(define (configure-pci)
;; http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0224i/Cacdijji.html
(if (<> (binand 1 (? #x10000044)) 0)
(let ((self-slot (find-self-slot)))
(puts (data #"PCI configuration\n\n\0"))
(puts (data #"Self-slot: \0")) (putn self-slot) (newline)
(! #x1000100c self-slot) ;; set PCI_SELFID register
;; TODO: should the following bus master config happen at
;; #x41000000 instead of #x42000000?
(!pci self-slot pci-device-status-command
(binor (?pci self-slot pci-device-status-command)
PCI_COMMAND_BUS_MASTER)) ;; "set bit 2"
;; Now scan the bus
(let ((mutable i 11))
(while (<u i 32)
(configure-pci-slot i)
(set! i (+ i 1)))))
(puts (data #"PCI not present on board\n\0"))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; VIC, Vectored Interrupt Controller
;; http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0273a/index.html
(struct vic ((irq-status word)
(fiq-status word)
(raw-interrupt-status word)
(int-select word)
(int-enable word)
(int-enable-clear word)
(software-interrupt word)
(software-interrupt-clear word)
(protection-enable word)
(software-priority-mask word)
(priority-daisy word)))
(const vic-vector-address-base #x100) ;; 32 word-size regs here, then unused
(const vic-vector-priority-base #x200) ;; 32 word-size regs here, then unused
(const vic-address #xf00) ;; one word-size reg here
(const vic-peripheral-id-byte0 #xfe0) ;; one byte-size reg here; low byte
(const vic-peripheral-id-byte1 #xfe4) ;; one byte-size reg here
(const vic-peripheral-id-byte2 #xfe8) ;; one byte-size reg here
(const vic-peripheral-id-byte3 #xfec) ;; one byte-size reg here; high byte
(const vic-primecell-id-byte0 #xff0) ;; one byte-size reg here; low byte
(const vic-primecell-id-byte1 #xff4) ;; one byte-size reg here
(const vic-primecell-id-byte2 #xff8) ;; one byte-size reg here
(const vic-primecell-id-byte3 #xffc) ;; one byte-size reg here; high byte
(const vic0 #x10140000)
(define (vic-enable-all-interrupts)
(! (+ vic0 vic-int-enable) #xffffffff))
(define (configure-vic)
(vic-enable-all-interrupts)
;; Enable VIC protection, disables access to the VIC from user mode:
(! (+ vic0 vic-protection-enable) (binor (? (+ vic0 vic-protection-enable)) 1)))
(define (check-vic-irq-status)
;; See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0273a/CJAECAJB.html
;; We did the dummy read of vic-address in interrupt:irq.
(puts (data #"VICIRQSTATUS:\0"))
(putx (? (+ vic0 vic-irq-status)))
(putc 32)
(! (+ vic0 vic-address) 0)) ;; Clears the intterupt in the VIC, apparently?
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Timer, SP804; two individual channels
;; http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0271d/index.html
;; http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0271d/Babgabfg.html
;; https://singpolyma.net/2012/02/writing-a-simple-os-kernel-hardware-interrupts/
(struct timer ((load word)
(value word)
(control word) ;; only 8 bits used
(intclr word)
(raw-interrupt-status word)
(masked-interrupt-status word)
(background-load word)))
;; There are two register groups, one per free-running-clock; one at
;; offset 0, the other at offset 32.
(const timer-frc0-base #x00)
(const timer-frc1-base #x20)
;; Control word bits (really, control byte)
(const timer-control-enabled #x80)
(const timer-control-periodic #x40) ;; set = periodic; clear = free-running
(const timer-control-interrupt-enabled #x20)
(const timer-control-prescale-1 #x00) ;; }
(const timer-control-prescale-16 #x04) ;; }-- bits ....XX.. control prescaler
(const timer-control-prescale-256 #x08) ;; } (N.B. 11 -> undefined)
(const timer-control-32bit #x02) ;; set = 32-bit counter; clear = 16-bit
(const timer-control-oneshot #x01) ;; set = oneshot; clear = wrapping
(const timer0 #x101e2000)
(define (configure-timer)
;; Configure frc0 as a regular one-second tick with an interrupt
;; configured
(let ((base (+ timer0 timer-frc0-base)))
(! (+ base timer-control) 0) ;; disable temporarily
(! (+ base timer-load) 1000000) ;; microseconds apparently
(! (+ base timer-control)
(binor timer-control-enabled
timer-control-periodic
timer-control-interrupt-enabled
timer-control-prescale-1
timer-control-32bit)))
;; Configure frc1 as a free-running counter (of microseconds,
;; presumably) with no interrupt configured
(let ((base (+ timer0 timer-frc1-base)))
(! (+ base timer-control) 0) ;; disable temporarily
(! (+ base timer-load) 0) ;; microseconds apparently
(! (+ base timer-control)
(binor timer-control-enabled
timer-control-prescale-1
timer-control-32bit))))
(define (check-timer)
(let ((base (+ timer0 timer-frc0-base)))
(when (<> (? (+ base timer-masked-interrupt-status)) 0)
(! (+ base timer-intclr) 0)
(puts (data #"Tick \0"))
(putn (- 0 (? (+ timer0 timer-frc1-base timer-value))))
(putc 32)
(putn (rtc-value))
(newline))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; PL031 Real-Time Clock
;; http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0224b/i1006087.html
(struct rtc ((data word)
(match word)
(load word)
(control word)
(interrupt-mask-set-and-clear word)
(raw-interrupt-status word)
(masked-interrupt-status word)
(intclr word)))
(const rtc0 #x101e8000)
(define (configure-rtc)
(! (+ rtc0 rtc-interrupt-mask-set-and-clear) 0) ;; mask out the match interrupt - we don't want it
(! (+ rtc0 rtc-control) 1) ;; enable the RTC. (System reset needed to disable again!)
)
(define (rtc-value)
(? (+ rtc0 rtc-data)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(const ATAG_NONE #x00000000)
(const ATAG_CORE #x54410001)
(const ATAG_MEM #x54410002)
(const ATAG_VIDEOTEXT #x54410003)
(const ATAG_RAMDISK #x54410004)
(const ATAG_INITRD2 #x54420005) ;; yes, really, not #x54410005. Presumably that was INITRD1?
(const ATAG_SERIAL #x54410006)
(const ATAG_REVISION #x54410007)
(const ATAG_VIDEOLFB #x54410008)
(const ATAG_CMDLINE #x54410009)
(define (dump-atag-word base field-number description-str)
(putc 32)
(putc 32)
(putx (? (+ base (<< (+ field-number 2) 2))))
(putc 32)
(puts description-str)
(newline))
(define (dump-atag-core base)
(dump-atag-word base 0 (data #"flags\0"))
(dump-atag-word base 1 (data #"pagesize\0"))
(dump-atag-word base 2 (data #"rootdev\0")))
(define (dump-atag-mem base)
(dump-atag-word base 0 (data #"size\0"))
(dump-atag-word base 1 (data #"start\0")))
(define (dump-atags base)
(let ((size (? base))
(tag (? (+ base 4))))
(puts (data #"ATAG #x\0"))
(putx tag)
(puts (data #" size \0"))
(putn size)
(newline)
(when (= tag ATAG_CORE) (dump-atag-core base))
(when (= tag ATAG_MEM) (dump-atag-mem base))
(when (<> tag ATAG_NONE)
(dump-atags (+ base (<< size 2))))))
(define (enable-caches)
;; . Instruction caches (bit 12)
;; . Branch prediction (bit 11)
;; . Data caches (bit 2)
(sys:set-sctlr (binor (sys:get-sctlr) #x1804)))
;; Drastically simplified.
(define (store-page-table-entry! pagetable-base virtaddr physaddr flags)
(let ((entry-num (>>u virtaddr 20)))
(! (+ pagetable-base (<< entry-num 2))
(binor (<< (>>u physaddr 20) 20)
flags
2))))
(define (configure-mmu-identity-mapping)
;; Let's arbitrarily (!) place our root page table at #x4000. Qemu
;; loads versatilepb images to #x10000, and Raspberry Pi images load
;; to #x8000, so we have just enough room for our 16k page table at
;; #x4000 on a Raspberry Pi.
(let ((pagetable-base #x4000))
(let ((mutable addr 0))
(while (<u addr #x40000000)
(let ((flags (if (<> (binand addr #x20000000) 0)
#x00000000 ;; nothing, i.e. not cacheable
#x0000000c))) ;; C and B, i.e. cacheable
(store-page-table-entry! pagetable-base addr addr flags)
(store-page-table-entry! pagetable-base (+ addr #x40000000) (+ addr #x40000000) flags)
(store-page-table-entry! pagetable-base (+ addr #x80000000) (+ addr #x80000000) flags)
(store-page-table-entry! pagetable-base (+ addr #xC0000000) (+ addr #xC0000000) flags))
(set! addr (+ addr #x100000))))
(sys:set-mmu-domains #xffffffff) ;; All Master mode, no checks
(sys:set-ttbr0 pagetable-base)
(sys:set-ttbr1 pagetable-base)
(sys:set-ttbcr 0)
;; N.B. Instruction cache must be disabled at the moment MMU is enabled!
(sys:set-sctlr (binor (sys:get-sctlr) 1))))
(define (main always-zero machine-type atags-base) ;; Linux kernel startup conventions
(configure-mmu-identity-mapping)
(enable-caches) ;; After MMU enable, to make sure Icache is disabled when enabling MMU
(configure-interrupt-table)
(configure-timer)
(configure-rtc)
(configure-vic)
(uart-enable-interrupt)
(configure-pci)
(newline)
(puts (data #"Hello, world!\n\0"))
(puts (data #"Machine type \0"))
(putn machine-type)
(newline)
(dump-atags atags-base)
;; (putx (? #x00)) (newline)
;; (putx (? #x04)) (newline)
;; (putx (? #x08)) (newline)
;; (putx (? #x0c)) (newline)
;; (putx (? #x10)) (newline)
;; (putx (? #x14)) (newline)
;; (putx (? #x18)) (newline)
;; (putx (? #x1c)) (newline)
;; (putx (? #x20)) (newline)
;; (putx (? #x24)) (newline)
;; (putx (? #x28)) (newline)
;; (putx (? #x2c)) (newline)
(while (= 0 0)
;; Now (re)enable interrupts:
(cpu-enable-interrupts)
(sys:wait-for-interrupt)
;; Interrupts disabled at this point. Will be reenabled at the top
;; of the loop.
(check-vic-irq-status)
(check-timer)
(when (<> (uart-interrupt-status) 0)
;; Clear the interrupt:
(! (+ uart0 uart-icr) (<< 1 4))
(let ((ch (getc)))
(puts (data #"You typed: \0"))
(putc ch)
(newline)))))