使用Rust在树莓派上编写操作系统 - 05 - 驱动GPIO和UART

概述

  • 既然我们在上一章教程中已经准备好了基础设施——启用了安全全局变量——那么现在就可以为第一个真实设备添加驱动程序了。
  • 现在我们可以离开神奇的QEMU控制台,来使用真正的UART设备了,就像正经的嵌入式开发者那样。

需要注意的新增代码

  • 这是我们首次在真实的硬件上运行代码。
    • 因此,现在的构建编译分为RPi3RPi4两个版本。
    • 默认情况下,所有Makefile项都以RPi3为目标进行构建。
    • 如果需要以RPi4为构建目标,需要在每个构建项前面添加BSP=rpi4。例如:
      -BSP=rpi4 make
      -BSP=rpi4 make doc
    • 不过QEMU目前还不支持RPi4,因此BSP=rpi4 make qemu将无法正常工作。
  • 添加了driver::interface::DeviceDrivertrait,以从内核代码中抽象出BSP驱动程序实现。
  • 驱动程序存储在src/bsp/device_driver中,并且可以在BSP之间重用。
    • 我们引入了GPIO驱动,实现了对RPi的PL011 UART引脚的多路复用(pinmux即,将信号从SoC内部路由到实际的硬件引脚)。
      • 请注意此驱动程序如何区分RPi3RPi4。由于二者使用了不同的硬件,所以我们也需要在软件中考虑这种不同。
    • 其中的关键在于PL011Uart驱动:它实现了console::interface::*trait,而且从现在开始,它被用作主系统的控制台输出。
  • src/bsp/raspberrypi/memory.rs中的BSP现在具有一个内存映射。在特定情况下,映射包含了树莓派的MMIO地址,用于实例化相应设备的驱动程序。
  • 我们也修改了panic!处理程序,以使其不再依赖println!,panic现在使用的全局共享实例UART,不过遇到异常时,该实例有可能正处于锁定状态(由于目前使用的是NullLock实现,并不会出现这种锁的问题,但是如果是使用一个真实锁的实现,将会出现这种问题)。
    • 所以,为了避免变量被锁占用,现在的代码将会创建一个新的UART驱动实例,重新初始化设备,并使用这个新实例进行打印。这增加了系统在挂起之前能够打印出最后的重要消息的机会。

从SD卡启动

需要注意的是,在使用SD卡启动时,RPi3和RPi4的准备步骤有所不同。

通用步骤

  1. 创建一个名为bootFAT32格式分区。
  2. 在SD卡上,新建一个名为config.txt的文件,内容如下:
1
2
arm_64bit=1
init_uart_clock=48000000

Pi 3

  1. Raspberry Pi固件仓库中的下列文件复制到SD卡上:
  1. 执行make

Pi 4

  1. Raspberry Pi固件仓库中的下列文件复制到SD卡上:
  1. 执行BSP=rpi4 make

Note: 如果上述步骤在你的RPi4上不起作用,请尝试将SD卡上的start4.elf重命名为start.elf(即去掉4)。

通用步骤(续)

  1. 将文件kernel8.img复制到SD卡上,插回RPi。
  2. 执行miniterm项,就会在主机上打开UART设备:
1
$ make miniterm

注意Miniterm的默认串行设备名称为/dev/ttyUSB0。对于不同操作系统的宿主机,设备名称可能会有所不同。例如,在macOS上,名称可能类似于/dev/tty.usbserial-0001。这种情况下,需要明确给出名称:

1
$ DEV_SERIAL=/dev/tty.usbserial-0001 make miniterm
  1. 将USB串口线连接到宿主机PC。
  • 按照本教程初始的README中的接线图连接引脚。
  • 请确认没有连接USB串口线的电源引脚,仅连接了RX/TX和GND三个引脚。
  1. 将RPi接上电源电源线(miniUSB电源)并观察输出:
1
2
3
4
5
6
7
8
9
10
11
Miniterm 1.0

[MT] ⏳ Waiting for /dev/ttyUSB0
[MT] ✅ Serial connected
[0] mingo version 0.5.0
[1] Booting on: Raspberry Pi 3
[2] Drivers loaded:
1. BCM GPIO
2. BCM PL011 UART
[3] Chars written: 117
[4] Echoing input now
  1. 使用ctrl-c键退出。

与上一章代码的区别

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
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
diff -uNr 04_safe_globals/Cargo.toml 05_drivers_gpio_uart/Cargo.toml
--- 04_safe_globals/Cargo.toml
+++ 05_drivers_gpio_uart/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "mingo"
-version = "0.4.0"
+version = "0.5.0"
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
edition = "2018"

@@ -9,8 +9,8 @@

[features]
default = []
-bsp_rpi3 = []
-bsp_rpi4 = []
+bsp_rpi3 = ["tock-registers"]
+bsp_rpi4 = ["tock-registers"]

[[bin]]
name = "kernel"
@@ -22,6 +22,9 @@

[dependencies]

+# Optional dependencies
+tock-registers = { version = "0.7.x", default-features = false, features = ["register_types"], optional = true }
+
# Platform specific dependencies
[target.'cfg(target_arch = "aarch64")'.dependencies]
cortex-a = { version = "6.x.x" }

diff -uNr 04_safe_globals/Makefile 05_drivers_gpio_uart/Makefile
--- 04_safe_globals/Makefile
+++ 05_drivers_gpio_uart/Makefile
@@ -11,6 +11,9 @@
# Default to the RPi3.
BSP ?= rpi3

+# Default to a serial device name that is common in Linux.
+DEV_SERIAL ?= /dev/ttyUSB0
+


##--------------------------------------------------------------------------------------------------
@@ -72,6 +75,7 @@

EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE)
EXEC_TEST_DISPATCH = ruby ../common/tests/dispatch.rb
+EXEC_MINITERM = ruby ../common/serial/miniterm.rb

##------------------------------------------------------------------------------
## Dockerization
@@ -80,17 +84,25 @@
DOCKER_CMD = docker run -t --rm -v $(shell pwd):/work/tutorial -w /work/tutorial
DOCKER_CMD_INTERACT = $(DOCKER_CMD) -i
DOCKER_ARG_DIR_COMMON = -v $(shell pwd)/../common:/work/common
+DOCKER_ARG_DEV = --privileged -v /dev:/dev

DOCKER_QEMU = $(DOCKER_CMD_INTERACT) $(DOCKER_IMAGE)
DOCKER_TOOLS = $(DOCKER_CMD) $(DOCKER_IMAGE)
DOCKER_TEST = $(DOCKER_CMD) $(DOCKER_ARG_DIR_COMMON) $(DOCKER_IMAGE)

+# Dockerize commands, which require USB device passthrough, only on Linux.
+ifeq ($(shell uname -s),Linux)
+ DOCKER_CMD_DEV = $(DOCKER_CMD_INTERACT) $(DOCKER_ARG_DEV)
+
+ DOCKER_MINITERM = $(DOCKER_CMD_DEV) $(DOCKER_ARG_DIR_COMMON) $(DOCKER_IMAGE)
+endif
+


##--------------------------------------------------------------------------------------------------
## Targets
##--------------------------------------------------------------------------------------------------
-.PHONY: all $(KERNEL_ELF) $(KERNEL_BIN) doc qemu clippy clean readelf objdump nm check
+.PHONY: all $(KERNEL_ELF) $(KERNEL_BIN) doc qemu miniterm clippy clean readelf objdump nm check

all: $(KERNEL_BIN)

@@ -130,6 +142,12 @@
endif

##------------------------------------------------------------------------------
+## Connect to the target's serial
+##------------------------------------------------------------------------------
+miniterm:
+ @$(DOCKER_MINITERM) $(EXEC_MINITERM) $(DEV_SERIAL)
+
+##------------------------------------------------------------------------------
## Run clippy
##------------------------------------------------------------------------------
clippy:

diff -uNr 04_safe_globals/src/_arch/aarch64/cpu.rs 05_drivers_gpio_uart/src/_arch/aarch64/cpu.rs
--- 04_safe_globals/src/_arch/aarch64/cpu.rs
+++ 05_drivers_gpio_uart/src/_arch/aarch64/cpu.rs
@@ -17,6 +17,17 @@
// Public Code
//--------------------------------------------------------------------------------------------------

+pub use asm::nop;
+
+/// Spin for `n` cycles.
+#[cfg(feature = "bsp_rpi3")]
+#[inline(always)]
+pub fn spin_for_cycles(n: usize) {
+ for _ in 0..n {
+ asm::nop();
+ }
+}
+
/// Pause execution on the core.
#[inline(always)]
pub fn wait_forever() -> ! {

diff -uNr 04_safe_globals/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs 05_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs
--- 04_safe_globals/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs
+++ 05_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
+
+//! GPIO Driver.
+
+use crate::{
+ bsp::device_driver::common::MMIODerefWrapper, driver, synchronization,
+ synchronization::NullLock,
+};
+use tock_registers::{
+ interfaces::{ReadWriteable, Writeable},
+ register_bitfields, register_structs,
+ registers::ReadWrite,
+};
+
+//--------------------------------------------------------------------------------------------------
+// Private Definitions
+//--------------------------------------------------------------------------------------------------
+
+// GPIO registers.
+//
+// Descriptions taken from
+// - https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf
+// - https://datasheets.raspberrypi.org/bcm2711/bcm2711-peripherals.pdf
+register_bitfields! {
+ u32,
+
+ /// GPIO Function Select 1
+ GPFSEL1 [
+ /// Pin 15
+ FSEL15 OFFSET(15) NUMBITS(3) [
+ Input = 0b000,
+ Output = 0b001,
+ AltFunc0 = 0b100 // PL011 UART RX
+
+ ],
+
+ /// Pin 14
+ FSEL14 OFFSET(12) NUMBITS(3) [
+ Input = 0b000,
+ Output = 0b001,
+ AltFunc0 = 0b100 // PL011 UART TX
+ ]
+ ],
+
+ /// GPIO Pull-up/down Register
+ ///
+ /// BCM2837 only.
+ GPPUD [
+ /// Controls the actuation of the internal pull-up/down control line to ALL the GPIO pins.
+ PUD OFFSET(0) NUMBITS(2) [
+ Off = 0b00,
+ PullDown = 0b01,
+ PullUp = 0b10
+ ]
+ ],
+
+ /// GPIO Pull-up/down Clock Register 0
+ ///
+ /// BCM2837 only.
+ GPPUDCLK0 [
+ /// Pin 15
+ PUDCLK15 OFFSET(15) NUMBITS(1) [
+ NoEffect = 0,
+ AssertClock = 1
+ ],
+
+ /// Pin 14
+ PUDCLK14 OFFSET(14) NUMBITS(1) [
+ NoEffect = 0,
+ AssertClock = 1
+ ]
+ ],
+
+ /// GPIO Pull-up / Pull-down Register 0
+ ///
+ /// BCM2711 only.
+ GPIO_PUP_PDN_CNTRL_REG0 [
+ /// Pin 15
+ GPIO_PUP_PDN_CNTRL15 OFFSET(30) NUMBITS(2) [
+ NoResistor = 0b00,
+ PullUp = 0b01
+ ],
+
+ /// Pin 14
+ GPIO_PUP_PDN_CNTRL14 OFFSET(28) NUMBITS(2) [
+ NoResistor = 0b00,
+ PullUp = 0b01
+ ]
+ ]
+}
+
+register_structs! {
+ #[allow(non_snake_case)]
+ RegisterBlock {
+ (0x00 => _reserved1),
+ (0x04 => GPFSEL1: ReadWrite<u32, GPFSEL1::Register>),
+ (0x08 => _reserved2),
+ (0x94 => GPPUD: ReadWrite<u32, GPPUD::Register>),
+ (0x98 => GPPUDCLK0: ReadWrite<u32, GPPUDCLK0::Register>),
+ (0x9C => _reserved3),
+ (0xE4 => GPIO_PUP_PDN_CNTRL_REG0: ReadWrite<u32, GPIO_PUP_PDN_CNTRL_REG0::Register>),
+ (0xE8 => @END),
+ }
+}
+
+/// Abstraction for the associated MMIO registers.
+type Registers = MMIODerefWrapper<RegisterBlock>;
+
+//--------------------------------------------------------------------------------------------------
+// Public Definitions
+//--------------------------------------------------------------------------------------------------
+
+pub struct GPIOInner {
+ registers: Registers,
+}
+
+// Export the inner struct so that BSPs can use it for the panic handler.
+pub use GPIOInner as PanicGPIO;
+
+/// Representation of the GPIO HW.
+pub struct GPIO {
+ inner: NullLock<GPIOInner>,
+}
+
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
+impl GPIOInner {
+ /// Create an instance.
+ ///
+ /// # Safety
+ ///
+ /// - The user must ensure to provide a correct MMIO start address.
+ pub const unsafe fn new(mmio_start_addr: usize) -> Self {
+ Self {
+ registers: Registers::new(mmio_start_addr),
+ }
+ }
+
+ /// Disable pull-up/down on pins 14 and 15.
+ #[cfg(feature = "bsp_rpi3")]
+ fn disable_pud_14_15_bcm2837(&mut self) {
+ use crate::cpu;
+
+ // Make an educated guess for a good delay value (Sequence described in the BCM2837
+ // peripherals PDF).
+ //
+ // - According to Wikipedia, the fastest Pi3 clocks around 1.4 GHz.
+ // - The Linux 2837 GPIO driver waits 1 µs between the steps.
+ //
+ // So lets try to be on the safe side and default to 2000 cycles, which would equal 1 µs
+ // would the CPU be clocked at 2 GHz.
+ const DELAY: usize = 2000;
+
+ self.registers.GPPUD.write(GPPUD::PUD::Off);
+ cpu::spin_for_cycles(DELAY);
+
+ self.registers
+ .GPPUDCLK0
+ .write(GPPUDCLK0::PUDCLK15::AssertClock + GPPUDCLK0::PUDCLK14::AssertClock);
+ cpu::spin_for_cycles(DELAY);
+
+ self.registers.GPPUD.write(GPPUD::PUD::Off);
+ self.registers.GPPUDCLK0.set(0);
+ }
+
+ /// Disable pull-up/down on pins 14 and 15.
+ #[cfg(feature = "bsp_rpi4")]
+ fn disable_pud_14_15_bcm2711(&mut self) {
+ self.registers.GPIO_PUP_PDN_CNTRL_REG0.write(
+ GPIO_PUP_PDN_CNTRL_REG0::GPIO_PUP_PDN_CNTRL15::PullUp
+ + GPIO_PUP_PDN_CNTRL_REG0::GPIO_PUP_PDN_CNTRL14::PullUp,
+ );
+ }
+
+ /// Map PL011 UART as standard output.
+ ///
+ /// TX to pin 14
+ /// RX to pin 15
+ pub fn map_pl011_uart(&mut self) {
+ // Select the UART on pins 14 and 15.
+ self.registers
+ .GPFSEL1
+ .modify(GPFSEL1::FSEL15::AltFunc0 + GPFSEL1::FSEL14::AltFunc0);
+
+ // Disable pull-up/down on pins 14 and 15.
+ #[cfg(feature = "bsp_rpi3")]
+ self.disable_pud_14_15_bcm2837();
+
+ #[cfg(feature = "bsp_rpi4")]
+ self.disable_pud_14_15_bcm2711();
+ }
+}
+
+impl GPIO {
+ /// Create an instance.
+ ///
+ /// # Safety
+ ///
+ /// - The user must ensure to provide a correct MMIO start address.
+ pub const unsafe fn new(mmio_start_addr: usize) -> Self {
+ Self {
+ inner: NullLock::new(GPIOInner::new(mmio_start_addr)),
+ }
+ }
+
+ /// Concurrency safe version of `GPIOInner.map_pl011_uart()`
+ pub fn map_pl011_uart(&self) {
+ self.inner.lock(|inner| inner.map_pl011_uart())
+ }
+}
+
+//------------------------------------------------------------------------------
+// OS Interface Code
+//------------------------------------------------------------------------------
+use synchronization::interface::Mutex;
+
+impl driver::interface::DeviceDriver for GPIO {
+ fn compatible(&self) -> &'static str {
+ "BCM GPIO"
+ }
+}

diff -uNr 04_safe_globals/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs 05_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
--- 04_safe_globals/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
+++ 05_drivers_gpio_uart/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs
@@ -0,0 +1,408 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
+
+//! PL011 UART driver.
+//!
+//! # Resources
+//!
+//! - <https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf>
+//! - <https://developer.arm.com/documentation/ddi0183/latest>
+
+use crate::{
+ bsp::device_driver::common::MMIODerefWrapper, console, cpu, driver, synchronization,
+ synchronization::NullLock,
+};
+use core::fmt;
+use tock_registers::{
+ interfaces::{Readable, Writeable},
+ register_bitfields, register_structs,
+ registers::{ReadOnly, ReadWrite, WriteOnly},
+};
+
+//--------------------------------------------------------------------------------------------------
+// Private Definitions
+//--------------------------------------------------------------------------------------------------
+
+// PL011 UART registers.
+//
+// Descriptions taken from "PrimeCell UART (PL011) Technical Reference Manual" r1p5.
+register_bitfields! {
+ u32,
+
+ /// Flag Register.
+ FR [
+ /// Transmit FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
+ /// Line Control Register, LCR_H.
+ ///
+ /// - If the FIFO is disabled, this bit is set when the transmit holding register is empty.
+ /// - If the FIFO is enabled, the TXFE bit is set when the transmit FIFO is empty.
+ /// - This bit does not indicate if there is data in the transmit shift register.
+ TXFE OFFSET(7) NUMBITS(1) [],
+
+ /// Transmit FIFO full. The meaning of this bit depends on the state of the FEN bit in the
+ /// LCR_H Register.
+ ///
+ /// - If the FIFO is disabled, this bit is set when the transmit holding register is full.
+ /// - If the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full.
+ TXFF OFFSET(5) NUMBITS(1) [],
+
+ /// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
+ /// LCR_H Register.
+ ///
+ /// If the FIFO is disabled, this bit is set when the receive holding register is empty. If
+ /// the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty.
+
+ /// Receive FIFO empty. The meaning of this bit depends on the state of the FEN bit in the
+ /// LCR_H Register.
+ ///
+ /// - If the FIFO is disabled, this bit is set when the receive holding register is empty.
+ /// - If the FIFO is enabled, the RXFE bit is set when the receive FIFO is empty.
+ RXFE OFFSET(4) NUMBITS(1) [],
+
+ /// UART busy. If this bit is set to 1, the UART is busy transmitting data. This bit remains
+ /// set until the complete byte, including all the stop bits, has been sent from the shift
+ /// register.
+ ///
+ /// This bit is set as soon as the transmit FIFO becomes non-empty, regardless of whether
+ /// the UART is enabled or not.
+ BUSY OFFSET(3) NUMBITS(1) []
+ ],
+
+ /// Integer Baud Rate Divisor.
+ IBRD [
+ /// The integer baud rate divisor.
+ BAUD_DIVINT OFFSET(0) NUMBITS(16) []
+ ],
+
+ /// Fractional Baud Rate Divisor.
+ FBRD [
+ /// The fractional baud rate divisor.
+ BAUD_DIVFRAC OFFSET(0) NUMBITS(6) []
+ ],
+
+ /// Line Control Register.
+ LCR_H [
+ /// Word length. These bits indicate the number of data bits transmitted or received in a
+ /// frame.
+ #[allow(clippy::enum_variant_names)]
+ WLEN OFFSET(5) NUMBITS(2) [
+ FiveBit = 0b00,
+ SixBit = 0b01,
+ SevenBit = 0b10,
+ EightBit = 0b11
+ ],
+
+ /// Enable FIFOs:
+ ///
+ /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become 1-byte-deep holding
+ /// registers.
+ ///
+ /// 1 = Transmit and receive FIFO buffers are enabled (FIFO mode).
+ FEN OFFSET(4) NUMBITS(1) [
+ FifosDisabled = 0,
+ FifosEnabled = 1
+ ]
+ ],
+
+ /// Control Register.
+ CR [
+ /// Receive enable. If this bit is set to 1, the receive section of the UART is enabled.
+ /// Data reception occurs for either UART signals or SIR signals depending on the setting of
+ /// the SIREN bit. When the UART is disabled in the middle of reception, it completes the
+ /// current character before stopping.
+ RXE OFFSET(9) NUMBITS(1) [
+ Disabled = 0,
+ Enabled = 1
+ ],
+
+ /// Transmit enable. If this bit is set to 1, the transmit section of the UART is enabled.
+ /// Data transmission occurs for either UART signals, or SIR signals depending on the
+ /// setting of the SIREN bit. When the UART is disabled in the middle of transmission, it
+ /// completes the current character before stopping.
+ TXE OFFSET(8) NUMBITS(1) [
+ Disabled = 0,
+ Enabled = 1
+ ],
+
+ /// UART enable:
+ ///
+ /// 0 = UART is disabled. If the UART is disabled in the middle of transmission or
+ /// reception, it completes the current character before stopping.
+ ///
+ /// 1 = The UART is enabled. Data transmission and reception occurs for either UART signals
+ /// or SIR signals depending on the setting of the SIREN bit
+ UARTEN OFFSET(0) NUMBITS(1) [
+ /// If the UART is disabled in the middle of transmission or reception, it completes the
+ /// current character before stopping.
+ Disabled = 0,
+ Enabled = 1
+ ]
+ ],
+
+ /// Interrupt Clear Register.
+ ICR [
+ /// Meta field for all pending interrupts.
+ ALL OFFSET(0) NUMBITS(11) []
+ ]
+}
+
+register_structs! {
+ #[allow(non_snake_case)]
+ pub RegisterBlock {
+ (0x00 => DR: ReadWrite<u32>),
+ (0x04 => _reserved1),
+ (0x18 => FR: ReadOnly<u32, FR::Register>),
+ (0x1c => _reserved2),
+ (0x24 => IBRD: WriteOnly<u32, IBRD::Register>),
+ (0x28 => FBRD: WriteOnly<u32, FBRD::Register>),
+ (0x2c => LCR_H: WriteOnly<u32, LCR_H::Register>),
+ (0x30 => CR: WriteOnly<u32, CR::Register>),
+ (0x34 => _reserved3),
+ (0x44 => ICR: WriteOnly<u32, ICR::Register>),
+ (0x48 => @END),
+ }
+}
+
+/// Abstraction for the associated MMIO registers.
+type Registers = MMIODerefWrapper<RegisterBlock>;
+
+#[derive(PartialEq)]
+enum BlockingMode {
+ Blocking,
+ NonBlocking,
+}
+
+//--------------------------------------------------------------------------------------------------
+// Public Definitions
+//--------------------------------------------------------------------------------------------------
+
+pub struct PL011UartInner {
+ registers: Registers,
+ chars_written: usize,
+ chars_read: usize,
+}
+
+// Export the inner struct so that BSPs can use it for the panic handler.
+pub use PL011UartInner as PanicUart;
+
+/// Representation of the UART.
+pub struct PL011Uart {
+ inner: NullLock<PL011UartInner>,
+}
+
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
+impl PL011UartInner {
+ /// Create an instance.
+ ///
+ /// # Safety
+ ///
+ /// - The user must ensure to provide a correct MMIO start address.
+ pub const unsafe fn new(mmio_start_addr: usize) -> Self {
+ Self {
+ registers: Registers::new(mmio_start_addr),
+ chars_written: 0,
+ chars_read: 0,
+ }
+ }
+
+ /// Set up baud rate and characteristics.
+ ///
+ /// This results in 8N1 and 921_600 baud.
+ ///
+ /// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
+ /// `(48_000_000 / 16) / 921_600 = 3.2552083`.
+ ///
+ /// This means the integer part is `3` and goes into the `IBRD`.
+ /// The fractional part is `0.2552083`.
+ ///
+ /// `FBRD` calculation according to the PL011 Technical Reference Manual:
+ /// `INTEGER((0.2552083 * 64) + 0.5) = 16`.
+ ///
+ /// Therefore, the generated baud rate divider is: `3 + 16/64 = 3.25`. Which results in a
+ /// genrated baud rate of `48_000_000 / (16 * 3.25) = 923_077`.
+ ///
+ /// Error = `((923_077 - 921_600) / 921_600) * 100 = 0.16modulo`.
+ pub fn init(&mut self) {
+ // Execution can arrive here while there are still characters queued in the TX FIFO and
+ // actively being sent out by the UART hardware. If the UART is turned off in this case,
+ // those queued characters would be lost.
+ //
+ // For example, this can happen during runtime on a call to panic!(), because panic!()
+ // initializes its own UART instance and calls init().
+ //
+ // Hence, flush first to ensure all pending characters are transmitted.
+ self.flush();
+
+ // Turn the UART off temporarily.
+ self.registers.CR.set(0);
+
+ // Clear all pending interrupts.
+ self.registers.ICR.write(ICR::ALL::CLEAR);
+
+ // From the PL011 Technical Reference Manual:
+ //
+ // The LCR_H, IBRD, and FBRD registers form the single 30-bit wide LCR Register that is
+ // updated on a single write strobe generated by a LCR_H write. So, to internally update the
+ // contents of IBRD or FBRD, a LCR_H write must always be performed at the end.
+ //
+ // Set the baud rate, 8N1 and FIFO enabled.
+ self.registers.IBRD.write(IBRD::BAUD_DIVINT.val(3));
+ self.registers.FBRD.write(FBRD::BAUD_DIVFRAC.val(16));
+ self.registers
+ .LCR_H
+ .write(LCR_H::WLEN::EightBit + LCR_H::FEN::FifosEnabled);
+
+ // Turn the UART on.
+ self.registers
+ .CR
+ .write(CR::UARTEN::Enabled + CR::TXE::Enabled + CR::RXE::Enabled);
+ }
+
+ /// Send a character.
+ fn write_char(&mut self, c: char) {
+ // Spin while TX FIFO full is set, waiting for an empty slot.
+ while self.registers.FR.matches_all(FR::TXFF::SET) {
+ cpu::nop();
+ }
+
+ // Write the character to the buffer.
+ self.registers.DR.set(c as u32);
+
+ self.chars_written += 1;
+ }
+
+ /// Block execution until the last buffered character has been physically put on the TX wire.
+ fn flush(&self) {
+ // Spin until the busy bit is cleared.
+ while self.registers.FR.matches_all(FR::BUSY::SET) {
+ cpu::nop();
+ }
+ }
+
+ /// Retrieve a character.
+ fn read_char_converting(&mut self, blocking_mode: BlockingMode) -> Option<char> {
+ // If RX FIFO is empty,
+ if self.registers.FR.matches_all(FR::RXFE::SET) {
+ // immediately return in non-blocking mode.
+ if blocking_mode == BlockingMode::NonBlocking {
+ return None;
+ }
+
+ // Otherwise, wait until a char was received.
+ while self.registers.FR.matches_all(FR::RXFE::SET) {
+ cpu::nop();
+ }
+ }
+
+ // Read one character.
+ let mut ret = self.registers.DR.get() as u8 as char;
+
+ // Convert carrige return to newline.
+ if ret == '\r' {
+ ret = '\n'
+ }
+
+ // Update statistics.
+ self.chars_read += 1;
+
+ Some(ret)
+ }
+}
+
+/// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are
+/// used to implement the `kernel`'s `print!` and `println!` macros. By implementing `write_str()`,
+/// we get `write_fmt()` automatically.
+///
+/// The function takes an `&mut self`, so it must be implemented for the inner struct.
+///
+/// See [`src/print.rs`].
+///
+/// [`src/print.rs`]: ../../print/index.html
+impl fmt::Write for PL011UartInner {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ for c in s.chars() {
+ self.write_char(c);
+ }
+
+ Ok(())
+ }
+}
+
+impl PL011Uart {
+ /// Create an instance.
+ ///
+ /// # Safety
+ ///
+ /// - The user must ensure to provide a correct MMIO start address.
+ pub const unsafe fn new(mmio_start_addr: usize) -> Self {
+ Self {
+ inner: NullLock::new(PL011UartInner::new(mmio_start_addr)),
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// OS Interface Code
+//------------------------------------------------------------------------------
+use synchronization::interface::Mutex;
+
+impl driver::interface::DeviceDriver for PL011Uart {
+ fn compatible(&self) -> &'static str {
+ "BCM PL011 UART"
+ }
+
+ unsafe fn init(&self) -> Result<(), &'static str> {
+ self.inner.lock(|inner| inner.init());
+
+ Ok(())
+ }
+}
+
+impl console::interface::Write for PL011Uart {
+ /// Passthrough of `args` to the `core::fmt::Write` implementation, but guarded by a Mutex to
+ /// serialize access.
+ fn write_char(&self, c: char) {
+ self.inner.lock(|inner| inner.write_char(c));
+ }
+
+ fn write_fmt(&self, args: core::fmt::Arguments) -> fmt::Result {
+ // Fully qualified syntax for the call to `core::fmt::Write::write:fmt()` to increase
+ // readability.
+ self.inner.lock(|inner| fmt::Write::write_fmt(inner, args))
+ }
+
+ fn flush(&self) {
+ // Spin until TX FIFO empty is set.
+ self.inner.lock(|inner| inner.flush());
+ }
+}
+
+impl console::interface::Read for PL011Uart {
+ fn read_char(&self) -> char {
+ self.inner
+ .lock(|inner| inner.read_char_converting(BlockingMode::Blocking).unwrap())
+ }
+
+ fn clear_rx(&self) {
+ // Read from the RX FIFO until it is indicating empty.
+ while self
+ .inner
+ .lock(|inner| inner.read_char_converting(BlockingMode::NonBlocking))
+ .is_some()
+ {}
+ }
+}
+
+impl console::interface::Statistics for PL011Uart {
+ fn chars_written(&self) -> usize {
+ self.inner.lock(|inner| inner.chars_written)
+ }
+
+ fn chars_read(&self) -> usize {
+ self.inner.lock(|inner| inner.chars_read)
+ }
+}

diff -uNr 04_safe_globals/src/bsp/device_driver/bcm.rs 05_drivers_gpio_uart/src/bsp/device_driver/bcm.rs
--- 04_safe_globals/src/bsp/device_driver/bcm.rs
+++ 05_drivers_gpio_uart/src/bsp/device_driver/bcm.rs
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
+
+//! BCM driver top level.
+
+mod bcm2xxx_gpio;
+mod bcm2xxx_pl011_uart;
+
+pub use bcm2xxx_gpio::*;
+pub use bcm2xxx_pl011_uart::*;

diff -uNr 04_safe_globals/src/bsp/device_driver/common.rs 05_drivers_gpio_uart/src/bsp/device_driver/common.rs
--- 04_safe_globals/src/bsp/device_driver/common.rs
+++ 05_drivers_gpio_uart/src/bsp/device_driver/common.rs
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2020-2021 Andre Richter <andre.o.richter@gmail.com>
+
+//! Common device driver code.
+
+use core::{marker::PhantomData, ops};
+
+//--------------------------------------------------------------------------------------------------
+// Public Definitions
+//--------------------------------------------------------------------------------------------------
+
+pub struct MMIODerefWrapper<T> {
+ start_addr: usize,
+ phantom: PhantomData<fn() -> T>,
+}
+
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
+impl<T> MMIODerefWrapper<T> {
+ /// Create an instance.
+ pub const unsafe fn new(start_addr: usize) -> Self {
+ Self {
+ start_addr,
+ phantom: PhantomData,
+ }
+ }
+}
+
+impl<T> ops::Deref for MMIODerefWrapper<T> {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ unsafe { &*(self.start_addr as *const _) }
+ }
+}

diff -uNr 04_safe_globals/src/bsp/device_driver.rs 05_drivers_gpio_uart/src/bsp/device_driver.rs
--- 04_safe_globals/src/bsp/device_driver.rs
+++ 05_drivers_gpio_uart/src/bsp/device_driver.rs
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
+
+//! Device driver.
+
+#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))]
+mod bcm;
+mod common;
+
+#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))]
+pub use bcm::*;

diff -uNr 04_safe_globals/src/bsp/raspberrypi/console.rs 05_drivers_gpio_uart/src/bsp/raspberrypi/console.rs
--- 04_safe_globals/src/bsp/raspberrypi/console.rs
+++ 05_drivers_gpio_uart/src/bsp/raspberrypi/console.rs
@@ -4,113 +4,34 @@

//! BSP console facilities.

-use crate::{console, synchronization, synchronization::NullLock};
+use super::memory;
+use crate::{bsp::device_driver, console};
use core::fmt;

//--------------------------------------------------------------------------------------------------
-// Private Definitions
+// Public Code
//--------------------------------------------------------------------------------------------------

-/// A mystical, magical device for generating QEMU output out of the void.
+/// In case of a panic, the panic handler uses this function to take a last shot at printing
+/// something before the system is halted.
///
-/// The mutex protected part.
-struct QEMUOutputInner {
- chars_written: usize,
-}
-
-//--------------------------------------------------------------------------------------------------
-// Public Definitions
-//--------------------------------------------------------------------------------------------------
-
-/// The main struct.
-pub struct QEMUOutput {
- inner: NullLock<QEMUOutputInner>,
-}
-
-//--------------------------------------------------------------------------------------------------
-// Global instances
-//--------------------------------------------------------------------------------------------------
-
-static QEMU_OUTPUT: QEMUOutput = QEMUOutput::new();
-
-//--------------------------------------------------------------------------------------------------
-// Private Code
-//--------------------------------------------------------------------------------------------------
-
-impl QEMUOutputInner {
- const fn new() -> QEMUOutputInner {
- QEMUOutputInner { chars_written: 0 }
- }
-
- /// Send a character.
- fn write_char(&mut self, c: char) {
- unsafe {
- core::ptr::write_volatile(0x3F20_1000 as *mut u8, c as u8);
- }
-
- self.chars_written += 1;
- }
-}
-
-/// Implementing `core::fmt::Write` enables usage of the `format_args!` macros, which in turn are
-/// used to implement the `kernel`'s `print!` and `println!` macros. By implementing `write_str()`,
-/// we get `write_fmt()` automatically.
+/// We try to init panic-versions of the GPIO and the UART. The panic versions are not protected
+/// with synchronization primitives, which increases chances that we get to print something, even
+/// when the kernel's default GPIO or UART instances happen to be locked at the time of the panic.
///
-/// The function takes an `&mut self`, so it must be implemented for the inner struct.
+/// # Safety
///
-/// See [`src/print.rs`].
-///
-/// [`src/print.rs`]: ../../print/index.html
-impl fmt::Write for QEMUOutputInner {
- fn write_str(&mut self, s: &str) -> fmt::Result {
- for c in s.chars() {
- // Convert newline to carrige return + newline.
- if c == '\n' {
- self.write_char('\r')
- }
-
- self.write_char(c);
- }
-
- Ok(())
- }
-}
-
-//--------------------------------------------------------------------------------------------------
-// Public Code
-//--------------------------------------------------------------------------------------------------
-
-impl QEMUOutput {
- /// Create a new instance.
- pub const fn new() -> QEMUOutput {
- QEMUOutput {
- inner: NullLock::new(QEMUOutputInner::new()),
- }
- }
+/// - Use only for printing during a panic.
+pub unsafe fn panic_console_out() -> impl fmt::Write {
+ let mut panic_gpio = device_driver::PanicGPIO::new(memory::map::mmio::GPIO_START);
+ let mut panic_uart = device_driver::PanicUart::new(memory::map::mmio::PL011_UART_START);
+
+ panic_gpio.map_pl011_uart();
+ panic_uart.init();
+ panic_uart
}

/// Return a reference to the console.
pub fn console() -> &'static impl console::interface::All {
- &QEMU_OUTPUT
-}
-
-//------------------------------------------------------------------------------
-// OS Interface Code
-//------------------------------------------------------------------------------
-use synchronization::interface::Mutex;
-
-/// Passthrough of `args` to the `core::fmt::Write` implementation, but guarded by a Mutex to
-/// serialize access.
-impl console::interface::Write for QEMUOutput {
- fn write_fmt(&self, args: core::fmt::Arguments) -> fmt::Result {
- // Fully qualified syntax for the call to `core::fmt::Write::write:fmt()` to increase
- // readability.
- self.inner.lock(|inner| fmt::Write::write_fmt(inner, args))
- }
-}
-
-impl console::interface::Statistics for QEMUOutput {
- fn chars_written(&self) -> usize {
- self.inner.lock(|inner| inner.chars_written)
- }
+ &super::PL011_UART
}

diff -uNr 04_safe_globals/src/bsp/raspberrypi/driver.rs 05_drivers_gpio_uart/src/bsp/raspberrypi/driver.rs
--- 04_safe_globals/src/bsp/raspberrypi/driver.rs
+++ 05_drivers_gpio_uart/src/bsp/raspberrypi/driver.rs
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
+
+//! BSP driver support.
+
+use crate::driver;
+
+//--------------------------------------------------------------------------------------------------
+// Private Definitions
+//--------------------------------------------------------------------------------------------------
+
+/// Device Driver Manager type.
+struct BSPDriverManager {
+ device_drivers: [&'static (dyn DeviceDriver + Sync); 2],
+}
+
+//--------------------------------------------------------------------------------------------------
+// Global instances
+//--------------------------------------------------------------------------------------------------
+
+static BSP_DRIVER_MANAGER: BSPDriverManager = BSPDriverManager {
+ device_drivers: [&super::GPIO, &super::PL011_UART],
+};
+
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
+/// Return a reference to the driver manager.
+pub fn driver_manager() -> &'static impl driver::interface::DriverManager {
+ &BSP_DRIVER_MANAGER
+}
+
+//------------------------------------------------------------------------------
+// OS Interface Code
+//------------------------------------------------------------------------------
+use driver::interface::DeviceDriver;
+
+impl driver::interface::DriverManager for BSPDriverManager {
+ fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)] {
+ &self.device_drivers[..]
+ }
+
+ fn post_device_driver_init(&self) {
+ // Configure PL011Uart's output pins.
+ super::GPIO.map_pl011_uart();
+ }
+}

diff -uNr 04_safe_globals/src/bsp/raspberrypi/memory.rs 05_drivers_gpio_uart/src/bsp/raspberrypi/memory.rs
--- 04_safe_globals/src/bsp/raspberrypi/memory.rs
+++ 05_drivers_gpio_uart/src/bsp/raspberrypi/memory.rs
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
+
+//! BSP Memory Management.
+
+//--------------------------------------------------------------------------------------------------
+// Public Definitions
+//--------------------------------------------------------------------------------------------------
+
+/// The board's physical memory map.
+#[rustfmt::skip]
+pub(super) mod map {
+
+ pub const GPIO_OFFSET: usize = 0x0020_0000;
+ pub const UART_OFFSET: usize = 0x0020_1000;
+
+ /// Physical devices.
+ #[cfg(feature = "bsp_rpi3")]
+ pub mod mmio {
+ use super::*;
+
+ pub const START: usize = 0x3F00_0000;
+ pub const GPIO_START: usize = START + GPIO_OFFSET;
+ pub const PL011_UART_START: usize = START + UART_OFFSET;
+ }
+
+ /// Physical devices.
+ #[cfg(feature = "bsp_rpi4")]
+ pub mod mmio {
+ use super::*;
+
+ pub const START: usize = 0xFE00_0000;
+ pub const GPIO_START: usize = START + GPIO_OFFSET;
+ pub const PL011_UART_START: usize = START + UART_OFFSET;
+ }
+}

diff -uNr 04_safe_globals/src/bsp/raspberrypi.rs 05_drivers_gpio_uart/src/bsp/raspberrypi.rs
--- 04_safe_globals/src/bsp/raspberrypi.rs
+++ 05_drivers_gpio_uart/src/bsp/raspberrypi.rs
@@ -6,3 +6,33 @@

pub mod console;
pub mod cpu;
+pub mod driver;
+pub mod memory;
+
+//--------------------------------------------------------------------------------------------------
+// Global instances
+//--------------------------------------------------------------------------------------------------
+use super::device_driver;
+
+static GPIO: device_driver::GPIO =
+ unsafe { device_driver::GPIO::new(memory::map::mmio::GPIO_START) };
+
+static PL011_UART: device_driver::PL011Uart =
+ unsafe { device_driver::PL011Uart::new(memory::map::mmio::PL011_UART_START) };
+
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
+/// Board identification.
+pub fn board_name() -> &'static str {
+ #[cfg(feature = "bsp_rpi3")]
+ {
+ "Raspberry Pi 3"
+ }
+
+ #[cfg(feature = "bsp_rpi4")]
+ {
+ "Raspberry Pi 4"
+ }
+}

diff -uNr 04_safe_globals/src/bsp.rs 05_drivers_gpio_uart/src/bsp.rs
--- 04_safe_globals/src/bsp.rs
+++ 05_drivers_gpio_uart/src/bsp.rs
@@ -4,6 +4,8 @@

//! Conditional reexporting of Board Support Packages.

+mod device_driver;
+
#[cfg(any(feature = "bsp_rpi3", feature = "bsp_rpi4"))]
mod raspberrypi;


diff -uNr 04_safe_globals/src/console.rs 05_drivers_gpio_uart/src/console.rs
--- 04_safe_globals/src/console.rs
+++ 05_drivers_gpio_uart/src/console.rs
@@ -14,8 +14,25 @@

/// Console write functions.
pub trait Write {
+ /// Write a single character.
+ fn write_char(&self, c: char);
+
/// Write a Rust format string.
fn write_fmt(&self, args: fmt::Arguments) -> fmt::Result;
+
+ /// Block until the last buffered character has been physically put on the TX wire.
+ fn flush(&self);
+ }
+
+ /// Console read functions.
+ pub trait Read {
+ /// Read a single character.
+ fn read_char(&self) -> char {
+ ' '
+ }
+
+ /// Clear RX buffers, if any.
+ fn clear_rx(&self);
}

/// Console statistics.
@@ -24,8 +41,13 @@
fn chars_written(&self) -> usize {
0
}
+
+ /// Return the number of characters read.
+ fn chars_read(&self) -> usize {
+ 0
+ }
}

/// Trait alias for a full-fledged console.
- pub trait All = Write + Statistics;
+ pub trait All = Write + Read + Statistics;
}

diff -uNr 04_safe_globals/src/cpu.rs 05_drivers_gpio_uart/src/cpu.rs
--- 04_safe_globals/src/cpu.rs
+++ 05_drivers_gpio_uart/src/cpu.rs
@@ -13,4 +13,7 @@
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
//--------------------------------------------------------------------------------------------------
-pub use arch_cpu::wait_forever;
+pub use arch_cpu::{nop, wait_forever};
+
+#[cfg(feature = "bsp_rpi3")]
+pub use arch_cpu::spin_for_cycles;

diff -uNr 04_safe_globals/src/driver.rs 05_drivers_gpio_uart/src/driver.rs
--- 04_safe_globals/src/driver.rs
+++ 05_drivers_gpio_uart/src/driver.rs
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>
+
+//! Driver support.
+
+//--------------------------------------------------------------------------------------------------
+// Public Definitions
+//--------------------------------------------------------------------------------------------------
+
+/// Driver interfaces.
+pub mod interface {
+ /// Device Driver functions.
+ pub trait DeviceDriver {
+ /// Return a compatibility string for identifying the driver.
+ fn compatible(&self) -> &'static str;
+
+ /// Called by the kernel to bring up the device.
+ ///
+ /// # Safety
+ ///
+ /// - During init, drivers might do stuff with system-wide impact.
+ unsafe fn init(&self) -> Result<(), &'static str> {
+ Ok(())
+ }
+ }
+
+ /// Device driver management functions.
+ ///
+ /// The `BSP` is supposed to supply one global instance.
+ pub trait DriverManager {
+ /// Return a slice of references to all `BSP`-instantiated drivers.
+ ///
+ /// # Safety
+ ///
+ /// - The order of devices is the order in which `DeviceDriver::init()` is called.
+ fn all_device_drivers(&self) -> &[&'static (dyn DeviceDriver + Sync)];
+
+ /// Initialization code that runs after driver init.
+ ///
+ /// For example, device driver code that depends on other drivers already being online.
+ fn post_device_driver_init(&self);
+ }
+}

diff -uNr 04_safe_globals/src/main.rs 05_drivers_gpio_uart/src/main.rs
--- 04_safe_globals/src/main.rs
+++ 05_drivers_gpio_uart/src/main.rs
@@ -104,6 +104,8 @@
//! - It is implemented in `src/_arch/__arch_name__/cpu/boot.s`.
//! 2. Once finished with architectural setup, the arch code calls `kernel_init()`.

+#![allow(clippy::upper_case_acronyms)]
+#![feature(const_fn_fn_ptr_basics)]
#![feature(format_args_nl)]
#![feature(global_asm)]
#![feature(panic_info_message)]
@@ -114,6 +116,7 @@
mod bsp;
mod console;
mod cpu;
+mod driver;
mod panic_wait;
mod print;
mod synchronization;
@@ -123,16 +126,54 @@
/// # Safety
///
/// - Only a single core must be active and running this function.
+/// - The init calls in this function must appear in the correct order.
unsafe fn kernel_init() -> ! {
- use console::interface::Statistics;
+ use driver::interface::DriverManager;

- println!("[0] Hello from Rust!");
+ for i in bsp::driver::driver_manager().all_device_drivers().iter() {
+ if let Err(x) = i.init() {
+ panic!("Error loading driver: {}: {}", i.compatible(), x);
+ }
+ }
+ bsp::driver::driver_manager().post_device_driver_init();
+ // println! is usable from here on.
+
+ // Transition from unsafe to safe.
+ kernel_main()
+}
+
+/// The main function running after the early init.
+fn kernel_main() -> ! {
+ use bsp::console::console;
+ use console::interface::All;
+ use driver::interface::DriverManager;
+
+ println!(
+ "[0] {} version {}",
+ env!("CARGO_PKG_NAME"),
+ env!("CARGO_PKG_VERSION")
+ );
+ println!("[1] Booting on: {}", bsp::board_name());
+
+ println!("[2] Drivers loaded:");
+ for (i, driver) in bsp::driver::driver_manager()
+ .all_device_drivers()
+ .iter()
+ .enumerate()
+ {
+ println!(" {}. {}", i + 1, driver.compatible());
+ }

println!(
- "[1] Chars written: {}",
+ "[3] Chars written: {}",
bsp::console::console().chars_written()
);
+ println!("[4] Echoing input now");

- println!("[2] Stopping here.");
- cpu::wait_forever()
+ // Discard any spurious received characters before going into echo mode.
+ console().clear_rx();
+ loop {
+ let c = bsp::console::console().read_char();
+ bsp::console::console().write_char(c);
+ }
}

diff -uNr 04_safe_globals/src/panic_wait.rs 05_drivers_gpio_uart/src/panic_wait.rs
--- 04_safe_globals/src/panic_wait.rs
+++ 05_drivers_gpio_uart/src/panic_wait.rs
@@ -4,15 +4,35 @@

//! A panic handler that infinitely waits.

-use crate::{cpu, println};
-use core::panic::PanicInfo;
+use crate::{bsp, cpu};
+use core::{fmt, panic::PanicInfo};
+
+//--------------------------------------------------------------------------------------------------
+// Private Code
+//--------------------------------------------------------------------------------------------------
+
+fn _panic_print(args: fmt::Arguments) {
+ use fmt::Write;
+
+ unsafe { bsp::console::panic_console_out().write_fmt(args).unwrap() };
+}
+
+/// Prints with a newline - only use from the panic handler.
+///
+/// Carbon copy from <https://doc.rust-lang.org/src/std/macros.rs.html>
+#[macro_export]
+macro_rules! panic_println {
+ ($($arg:tt)*) => ({
+ _panic_print(format_args_nl!($($arg)*));
+ })
+}

#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
if let Some(args) = info.message() {
- println!("\nKernel panic: {}", args);
+ panic_println!("\nKernel panic: {}", args);
} else {
- println!("\nKernel panic!");
+ panic_println!("\nKernel panic!");
}

cpu::wait_forever()

diff -uNr 04_safe_globals/tests/boot_test_string.rb 05_drivers_gpio_uart/tests/boot_test_string.rb
--- 04_safe_globals/tests/boot_test_string.rb
+++ 05_drivers_gpio_uart/tests/boot_test_string.rb
@@ -1,3 +1,3 @@
# frozen_string_literal: true

-EXPECTED_PRINT = 'Stopping here'
+EXPECTED_PRINT = 'Echoing input now'

评论

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×