From 4f648142c89eea87a6353051e67aacb784d4205f Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Wed, 4 Feb 2026 17:34:37 +0200 Subject: [PATCH] sifive: initial support for hifive unleashed a00 --- Cargo.lock | 17 ++ .../riscv64/sifive-hifive-unleashed-a00.dts | 248 ++++++++++++++++++ etc/raw-dtb/sifive-hifive-unleashed-a00.dtb | Bin 0 -> 4979 bytes etc/raw-dtb/sifive-hifive-unleashed-a00.dts | 248 ++++++++++++++++++ kernel/Cargo.toml | 1 + kernel/driver/bsp/sifive/Cargo.toml | 17 ++ kernel/driver/bsp/sifive/src/clock.rs | 121 +++++++++ kernel/driver/bsp/sifive/src/lib.rs | 6 + kernel/driver/bsp/sifive/src/uart.rs | 228 ++++++++++++++++ kernel/lib/device-api/src/clock/mod.rs | 2 + kernel/libk/libk-mm/src/phys/manager.rs | 12 +- kernel/libk/libk-mm/src/phys/mod.rs | 4 + kernel/src/main.rs | 1 + lib/qemu/src/riscv64.rs | 10 +- xtask/src/env.rs | 3 +- xtask/src/qemu.rs | 31 ++- 16 files changed, 934 insertions(+), 15 deletions(-) create mode 100644 etc/dtb/riscv64/sifive-hifive-unleashed-a00.dts create mode 100644 etc/raw-dtb/sifive-hifive-unleashed-a00.dtb create mode 100644 etc/raw-dtb/sifive-hifive-unleashed-a00.dts create mode 100644 kernel/driver/bsp/sifive/Cargo.toml create mode 100644 kernel/driver/bsp/sifive/src/clock.rs create mode 100644 kernel/driver/bsp/sifive/src/lib.rs create mode 100644 kernel/driver/bsp/sifive/src/uart.rs diff --git a/Cargo.lock b/Cargo.lock index f0fd3318..7570960a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2723,6 +2723,22 @@ dependencies = [ "yggdrasil-abi", ] +[[package]] +name = "ygg_driver_bsp_sifive" +version = "0.1.0" +dependencies = [ + "bytemuck", + "device-api", + "device-tree", + "futures-util", + "libk", + "libk-mm", + "libk-util", + "log", + "tock-registers", + "yggdrasil-abi", +] + [[package]] name = "ygg_driver_fat32" version = "0.1.0" @@ -3053,6 +3069,7 @@ dependencies = [ "ygg_driver_bsp_bcm283x", "ygg_driver_bsp_jh7110", "ygg_driver_bsp_riscv", + "ygg_driver_bsp_sifive", "ygg_driver_fat32", "ygg_driver_input", "ygg_driver_net_core", diff --git a/etc/dtb/riscv64/sifive-hifive-unleashed-a00.dts b/etc/dtb/riscv64/sifive-hifive-unleashed-a00.dts new file mode 100644 index 00000000..c3176f52 --- /dev/null +++ b/etc/dtb/riscv64/sifive-hifive-unleashed-a00.dts @@ -0,0 +1,248 @@ +/dts-v1/; + +/ { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "sifive,hifive-unleashed-a00"; + model = "SiFive HiFive Unleashed A00"; + + chosen { + linux,initrd-end = <0x00 0x87688400>; + linux,initrd-start = <0x00 0x84200000>; + stdout-path = "/soc/serial@10010000"; + }; + + aliases { + serial0 = "/soc/serial@10010000"; + serial1 = "/soc/serial@10011000"; + ethernet0 = "/soc/ethernet@10090000"; + }; + + gpio-restart { + compatible = "gpio-restart"; + gpios = <0x07 0x0a 0x01>; + }; + + cpus { + #address-cells = <0x01>; + #size-cells = <0x00>; + timebase-frequency = <0xf4240>; + + cpu0: cpu@0 { + device_type = "cpu"; + reg = <0x00>; + status = "okay"; + compatible = "riscv"; + riscv,isa-extensions = "i", "m", "a", "c", "zicntr", "zicsr", "zifencei", "zihpm", "sdtrig"; + riscv,isa-base = "rv64i"; + riscv,isa = "rv64imac_zicntr_zicsr_zifencei_zihpm_sdtrig"; + + cpu0_intc: interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x04>; + }; + }; + + cpu1: cpu@1 { + device_type = "cpu"; + reg = <0x01>; + status = "okay"; + compatible = "riscv"; + riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "zicntr", "zicsr", "zifencei", "zihpm", "sdtrig"; + riscv,isa-base = "rv64i"; + riscv,isa = "rv64imafdc_zicntr_zicsr_zifencei_zihpm_sdtrig"; + mmu-type = "riscv,sv39"; + + cpu1_intc: interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x03>; + }; + }; + }; + + memory@80000000 { + device_type = "memory"; + reg = <0x00 0x80000000 0x00 0x8000000>; + }; + + rtcclk: rtcclk { + #clock-cells = <0x00>; + compatible = "fixed-clock"; + clock-frequency = <0xf4240>; + clock-output-names = "rtcclk"; + phandle = <0x02>; + }; + + hfclk: hfclk { + #clock-cells = <0x00>; + compatible = "fixed-clock"; + clock-frequency = <0x1fca055>; + clock-output-names = "hfclk"; + phandle = <0x01>; + }; + + soc { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "simple-bus"; + ranges; + + uart0: serial@10010000 { + interrupts = <0x04>; + interrupt-parent = <&plic>; + clocks = <&prci 0x03>; + reg = <0x00 0x10010000 0x00 0x1000>; + compatible = "sifive,uart0"; + }; + + uart1: serial@10011000 { + interrupts = <0x05>; + interrupt-parent = <&plic>; + clocks = <&prci 0x03>; + reg = <0x00 0x10011000 0x00 0x1000>; + compatible = "sifive,uart0"; + }; + + // pwm@10021000 { + // #pwm-cells = <0x00>; + // clocks = <0x05 0x03>; + // interrupts = <0x2e 0x2f 0x30 0x31>; + // interrupt-parent = <0x06>; + // reg = <0x00 0x10021000 0x00 0x1000>; + // compatible = "sifive,pwm0"; + // }; + + // pwm@10020000 { + // #pwm-cells = <0x00>; + // clocks = <0x05 0x03>; + // interrupts = <0x2a 0x2b 0x2c 0x2d>; + // interrupt-parent = <0x06>; + // reg = <0x00 0x10020000 0x00 0x1000>; + // compatible = "sifive,pwm0"; + // }; + + // ethernet@10090000 { + // #size-cells = <0x00>; + // #address-cells = <0x01>; + // local-mac-address = [52 54 00 12 34 56]; + // clock-names = "pclk", "hclk"; + // clocks = <0x05 0x02 0x05 0x02>; + // interrupts = <0x35>; + // interrupt-parent = <0x06>; + // phy-handle = <0x08>; + // phy-mode = "gmii"; + // reg-names = "control"; + // reg = <0x00 0x10090000 0x00 0x2000 0x00 0x100a0000 0x00 0x1000>; + // compatible = "sifive,fu540-c000-gem"; + + // ethernet-phy@0 { + // reg = <0x00>; + // phandle = <0x08>; + // }; + // }; + + // spi@10040000 { + // compatible = "sifive,spi0"; + // reg = <0x00 0x10040000 0x00 0x1000>; + // interrupt-parent = <0x06>; + // interrupts = <0x33>; + // clocks = <0x05 0x03>; + // #address-cells = <0x01>; + // #size-cells = <0x00>; + + // flash@0 { + // compatible = "jedec,spi-nor"; + // reg = <0x00>; + // spi-max-frequency = <0x2faf080>; + // m25p,fast-read; + // spi-tx-bus-width = <0x04>; + // spi-rx-bus-width = <0x04>; + // }; + // }; + + // spi@10050000 { + // compatible = "sifive,spi0"; + // reg = <0x00 0x10050000 0x00 0x1000>; + // interrupt-parent = <0x06>; + // interrupts = <0x06>; + // clocks = <0x05 0x03>; + // #address-cells = <0x01>; + // #size-cells = <0x00>; + + // mmc@0 { + // compatible = "mmc-spi-slot"; + // reg = <0x00>; + // spi-max-frequency = <0x1312d00>; + // voltage-ranges = <0xce4 0xce4>; + // disable-wp; + // }; + // }; + + // cache-controller@2010000 { + // compatible = "sifive,fu540-c000-ccache"; + // cache-block-size = <0x40>; + // cache-level = <0x02>; + // cache-sets = <0x400>; + // cache-size = <0x200000>; + // cache-unified; + // interrupt-parent = <0x06>; + // interrupts = <0x01 0x02 0x03>; + // reg = <0x00 0x2010000 0x00 0x1000>; + // }; + + // dma@3000000 { + // compatible = "sifive,fu540-c000-pdma"; + // reg = <0x00 0x3000000 0x00 0x100000>; + // interrupt-parent = <0x06>; + // interrupts = <0x17 0x18 0x19 0x1a 0x1b 0x1c 0x1d 0x1e>; + // #dma-cells = <0x01>; + // }; + + gpio: gpio@10060000 { + compatible = "sifive,gpio0"; + interrupt-parent = <&plic>; + interrupts = <0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13 0x14 0x15 0x16>; + reg = <0x00 0x10060000 0x00 0x1000>; + gpio-controller; + #gpio-cells = <0x02>; + interrupt-controller; + #interrupt-cells = <0x02>; + clocks = <0x05 0x03>; + phandle = <0x07>; + }; + + plic: interrupt-controller@c000000 { + phandle = <0x06>; + riscv,ndev = <0x35>; + reg = <0x00 0xc000000 0x00 0x4000000>; + interrupts-extended = <0x04 0x0b 0x03 0x0b 0x03 0x09>; + interrupt-controller; + compatible = "sifive,plic-1.0.0", "riscv,plic0"; + #interrupt-cells = <0x01>; + }; + + prci: clock-controller@10000000 { + compatible = "sifive,fu540-c000-prci"; + reg = <0x00 0x10000000 0x00 0x1000>; + clocks = <&hfclk>, <&rtcclk>; + #clock-cells = <0x01>; + phandle = <0x05>; + }; + + // otp@10070000 { + // compatible = "sifive,fu540-c000-otp"; + // reg = <0x00 0x10070000 0x00 0x1000>; + // fuse-count = <0x1000>; + // }; + + clint: clint@2000000 { + interrupts-extended = <0x04 0x03 0x04 0x07 0x03 0x03 0x03 0x07>; + reg = <0x00 0x2000000 0x00 0x10000>; + compatible = "sifive,clint0", "riscv,clint0"; + }; + }; +}; \ No newline at end of file diff --git a/etc/raw-dtb/sifive-hifive-unleashed-a00.dtb b/etc/raw-dtb/sifive-hifive-unleashed-a00.dtb new file mode 100644 index 0000000000000000000000000000000000000000..8a567ddd3d5ddb648922dd07eb92e4786c124c10 GIT binary patch literal 4979 zcmb_gJC7Vi5bjyv02{;cexs8vvG9%Av+ux`0~P`cIY0<72@=+LruTO6yqKB2he;e5 zArT-l0tn0xKqBN1ASVt8A+$tZgt~R@yI{F z8S`jkj5%e@;!o(00v-k|0?-CuVR%i3yMF?Y-i7{d!1I6sKyk`Y{5gylnsl6Q+LeiP z;k3wX)J$v=M#CYdWgYpgJ6<1T) z1aOT98Nn58ICIvs`Q`R^2=F4lTiKu|I6~LE5 z%cJ;$6UMHo@#}0@{j9(f zAtc2eA9MdCfIWS${vWLZL8HB#+slWltLMY9uIc%(qrHSl@58Z44(3OTHS>O?K+=u_ z)Q{ujV4h^IaqVHi=Mv+UX7l7}Wc&Z`W6g8*G4-mC#m~9T%X(*YiYEyT(_$NKEZrPQ zLR&;Hd3%KvG!zatkyE#EoVl!~4(j`WlW4?o&dkC%-A1*FvoaP&<$Keg8{*V^#><+w zUF{AgV^t-x??_kh_qFrTL0!y`GhWubF6Nc&3*`>OPS1Wdn(;)9&a2FZ7g0U#_v^Tx z=Pj5on|>bl2d`qxeR!V)=P;&y#Np;9`MY9@QplikjoZ(x zbW4f#b83U?Abtlnh;E2Fm<`od&gy+a4Vc+5cnf2;efBNB;<@8l=H6H)$Ukp0jgdrK z9Ee>&r6;fCqxTWap%~{MwM`EL=w9}LZF>=GjsY0+aJGJzKYO-;L)z_a`|s5!3%z~v z<{&}w{(Xg98mztju6bnby*TqcxbFr>D;}kZoF|Pv4C(TXIYWBye2D+oXL@$+3o#f_ zFG~aMz}mrM8+n?Vl4^y<_r@WAgD7nY0~afv8fi=Ny9g^?>-_TsaD5Anr|WCOFh;b( z4V$|&6R4lUYO>=mV)cpWzl_o;X|9)cm*bfMOmBRI%vALxxEJ02Y>#4 z#m}P){+jUyzhX>Xer@ox$LIaBe#d@c_DH*}U-qka`}!r%V;^R*U+N!dA-&RMCEkMj z`z2WAG3{dVJ<7u`4upoO;9LB3zKElEVta3((Fy+!rW7aVY4m&KXe>)H*Z1|;z()Y} z{TCQhm*Sr|_71;nmN4t)(FJ;*A#>MRM9xaS>0L&7gf3?XyYCL_43az=on(+_Zvni$ z_u5MZ)@~o2Oe&Bj;qGic2b#ur$=gM_PXXwwX8_Lv`2E8Ce*vKTk1*Cf1boNyfWE92 zIzEZHujgbLdL1gGuYcs{_}1XrXT@QaISk-Ar!1Z=zFD~op1(%`j{+V8;5*2$2JQ0Y zB6lZEHtyXJ@QzX&mNC}2lJmxF&fM(H-Ks9IZqR0l(_U}!5$hltc-XvNsW<9-<_LYR z^_#{@o#4jVn3!l>OTIqm;|vi+A(7^oY0nZsW1#j?8pjHXclsz$8h7RRc(yb) zqDtf9kr?w_rf`2=CN?v1nO9MpUd*gnZqmyrdN#`%vm7M}zOx&T)={y6PyT8W6$v<9 zD9dSEP1~@Da!fhWxV9fn@u9k7mVLNIT5UH>VmH&+UTAkJYuYro7x7PoaK@kK(@^GI zGNn!A(+Z=Iygk%zx3*}~vSFc=}9 ZB}0Q#Fx_5Bi?pqi(ETH47s(ch`4>Vy&ZqzY literal 0 HcmV?d00001 diff --git a/etc/raw-dtb/sifive-hifive-unleashed-a00.dts b/etc/raw-dtb/sifive-hifive-unleashed-a00.dts new file mode 100644 index 00000000..7501ea9a --- /dev/null +++ b/etc/raw-dtb/sifive-hifive-unleashed-a00.dts @@ -0,0 +1,248 @@ +/dts-v1/; + +/ { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "sifive,hifive-unleashed-a00"; + model = "SiFive HiFive Unleashed A00"; + + chosen { + linux,initrd-end = <0x00 0x87688400>; + linux,initrd-start = <0x00 0x84200000>; + stdout-path = "/soc/serial@10010000"; + }; + + aliases { + serial0 = "/soc/serial@10010000"; + serial1 = "/soc/serial@10011000"; + ethernet0 = "/soc/ethernet@10090000"; + }; + + gpio-restart { + compatible = "gpio-restart"; + gpios = <0x07 0x0a 0x01>; + }; + + cpus { + #address-cells = <0x01>; + #size-cells = <0x00>; + timebase-frequency = <0xf4240>; + + cpu@0 { + device_type = "cpu"; + reg = <0x00>; + status = "okay"; + compatible = "riscv"; + riscv,isa-extensions = "i", "m", "a", "c", "zicntr", "zicsr", "zifencei", "zihpm", "sdtrig"; + riscv,isa-base = "rv64i"; + riscv,isa = "rv64imac_zicntr_zicsr_zifencei_zihpm_sdtrig"; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x04>; + }; + }; + + cpu@1 { + device_type = "cpu"; + reg = <0x01>; + status = "okay"; + compatible = "riscv"; + riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "zicntr", "zicsr", "zifencei", "zihpm", "sdtrig"; + riscv,isa-base = "rv64i"; + riscv,isa = "rv64imafdc_zicntr_zicsr_zifencei_zihpm_sdtrig"; + mmu-type = "riscv,sv39"; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x03>; + }; + }; + }; + + memory@80000000 { + device_type = "memory"; + reg = <0x00 0x80000000 0x00 0x8000000>; + }; + + rtcclk { + #clock-cells = <0x00>; + compatible = "fixed-clock"; + clock-frequency = <0xf4240>; + clock-output-names = "rtcclk"; + phandle = <0x02>; + }; + + hfclk { + #clock-cells = <0x00>; + compatible = "fixed-clock"; + clock-frequency = <0x1fca055>; + clock-output-names = "hfclk"; + phandle = <0x01>; + }; + + soc { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "simple-bus"; + ranges; + + serial@10010000 { + interrupts = <0x04>; + interrupt-parent = <0x06>; + clocks = <0x05 0x03>; + reg = <0x00 0x10010000 0x00 0x1000>; + compatible = "sifive,uart0"; + }; + + serial@10011000 { + interrupts = <0x05>; + interrupt-parent = <0x06>; + clocks = <0x05 0x03>; + reg = <0x00 0x10011000 0x00 0x1000>; + compatible = "sifive,uart0"; + }; + + pwm@10021000 { + #pwm-cells = <0x00>; + clocks = <0x05 0x03>; + interrupts = <0x2e 0x2f 0x30 0x31>; + interrupt-parent = <0x06>; + reg = <0x00 0x10021000 0x00 0x1000>; + compatible = "sifive,pwm0"; + }; + + pwm@10020000 { + #pwm-cells = <0x00>; + clocks = <0x05 0x03>; + interrupts = <0x2a 0x2b 0x2c 0x2d>; + interrupt-parent = <0x06>; + reg = <0x00 0x10020000 0x00 0x1000>; + compatible = "sifive,pwm0"; + }; + + ethernet@10090000 { + #size-cells = <0x00>; + #address-cells = <0x01>; + local-mac-address = [52 54 00 12 34 56]; + clock-names = "pclk", "hclk"; + clocks = <0x05 0x02 0x05 0x02>; + interrupts = <0x35>; + interrupt-parent = <0x06>; + phy-handle = <0x08>; + phy-mode = "gmii"; + reg-names = "control"; + reg = <0x00 0x10090000 0x00 0x2000 0x00 0x100a0000 0x00 0x1000>; + compatible = "sifive,fu540-c000-gem"; + + ethernet-phy@0 { + reg = <0x00>; + phandle = <0x08>; + }; + }; + + spi@10040000 { + compatible = "sifive,spi0"; + reg = <0x00 0x10040000 0x00 0x1000>; + interrupt-parent = <0x06>; + interrupts = <0x33>; + clocks = <0x05 0x03>; + #address-cells = <0x01>; + #size-cells = <0x00>; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0x00>; + spi-max-frequency = <0x2faf080>; + m25p,fast-read; + spi-tx-bus-width = <0x04>; + spi-rx-bus-width = <0x04>; + }; + }; + + spi@10050000 { + compatible = "sifive,spi0"; + reg = <0x00 0x10050000 0x00 0x1000>; + interrupt-parent = <0x06>; + interrupts = <0x06>; + clocks = <0x05 0x03>; + #address-cells = <0x01>; + #size-cells = <0x00>; + + mmc@0 { + compatible = "mmc-spi-slot"; + reg = <0x00>; + spi-max-frequency = <0x1312d00>; + voltage-ranges = <0xce4 0xce4>; + disable-wp; + }; + }; + + cache-controller@2010000 { + compatible = "sifive,fu540-c000-ccache"; + cache-block-size = <0x40>; + cache-level = <0x02>; + cache-sets = <0x400>; + cache-size = <0x200000>; + cache-unified; + interrupt-parent = <0x06>; + interrupts = <0x01 0x02 0x03>; + reg = <0x00 0x2010000 0x00 0x1000>; + }; + + dma@3000000 { + compatible = "sifive,fu540-c000-pdma"; + reg = <0x00 0x3000000 0x00 0x100000>; + interrupt-parent = <0x06>; + interrupts = <0x17 0x18 0x19 0x1a 0x1b 0x1c 0x1d 0x1e>; + #dma-cells = <0x01>; + }; + + gpio@10060000 { + compatible = "sifive,gpio0"; + interrupt-parent = <0x06>; + interrupts = <0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13 0x14 0x15 0x16>; + reg = <0x00 0x10060000 0x00 0x1000>; + gpio-controller; + #gpio-cells = <0x02>; + interrupt-controller; + #interrupt-cells = <0x02>; + clocks = <0x05 0x03>; + phandle = <0x07>; + }; + + interrupt-controller@c000000 { + phandle = <0x06>; + riscv,ndev = <0x35>; + reg = <0x00 0xc000000 0x00 0x4000000>; + interrupts-extended = <0x04 0x0b 0x03 0x0b 0x03 0x09>; + interrupt-controller; + compatible = "sifive,plic-1.0.0", "riscv,plic0"; + #interrupt-cells = <0x01>; + }; + + clock-controller@10000000 { + compatible = "sifive,fu540-c000-prci"; + reg = <0x00 0x10000000 0x00 0x1000>; + clocks = <0x01 0x02>; + #clock-cells = <0x01>; + phandle = <0x05>; + }; + + otp@10070000 { + compatible = "sifive,fu540-c000-otp"; + reg = <0x00 0x10070000 0x00 0x1000>; + fuse-count = <0x1000>; + }; + + clint@2000000 { + interrupts-extended = <0x04 0x03 0x04 0x07 0x03 0x03 0x03 0x07>; + reg = <0x00 0x2000000 0x00 0x10000>; + compatible = "sifive,clint0", "riscv,clint0"; + }; + }; +}; diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 0cea6ecc..84668d7e 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -67,6 +67,7 @@ ygg_driver_bsp_arm.path = "driver/bsp/arm" # PrimeCell components ygg_driver_bsp_riscv.path = "driver/bsp/riscv" ygg_driver_net_stmmac.path = "driver/net/stmmac" ygg_driver_bsp_jh7110.path = "driver/bsp/jh7110" +ygg_driver_bsp_sifive.path = "driver/bsp/sifive" [target.'cfg(target_arch = "x86_64")'.dependencies] yboot-proto.workspace = true diff --git a/kernel/driver/bsp/sifive/Cargo.toml b/kernel/driver/bsp/sifive/Cargo.toml new file mode 100644 index 00000000..584d96da --- /dev/null +++ b/kernel/driver/bsp/sifive/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "ygg_driver_bsp_sifive" +version = "0.1.0" +edition = "2024" + +[dependencies] +device-tree.workspace = true +device-api.workspace = true +yggdrasil-abi.workspace = true +libk-mm.workspace = true +libk-util.workspace = true +libk.workspace = true + +tock-registers.workspace = true +log.workspace = true +bytemuck.workspace = true +futures-util.workspace = true diff --git a/kernel/driver/bsp/sifive/src/clock.rs b/kernel/driver/bsp/sifive/src/clock.rs new file mode 100644 index 00000000..624111c1 --- /dev/null +++ b/kernel/driver/bsp/sifive/src/clock.rs @@ -0,0 +1,121 @@ +use alloc::sync::Arc; +use device_api::{ + clock::{ClockController, ClockHandle, Hertz}, + device::Device, +}; +use device_tree::{ + DeviceTreePropertyRead, TProp, + driver::{DeviceTreeClockController, Node, ProbeContext, device_tree_driver}, +}; +use libk::error::Error; +use libk_mm::device::DeviceMemoryIo; +use libk_util::sync::IrqSafeSpinlock; +use tock_registers::{interfaces::Readable, register_structs, registers::ReadWrite}; + +const CLK_COREPLL: u32 = 0; +const CLK_DDRPLL: u32 = 1; +const CLK_GEMGXLPLL: u32 = 2; +const CLK_TLCLK: u32 = 3; + +register_structs! { + Regs { + (0x000 => hfxosccfg: ReadWrite), + (0x004 => corepllcfg0: ReadWrite), + (0x008 => _0), + (0x00C => ddrpllcfg0: ReadWrite), + (0x010 => ddrpllcfg1: ReadWrite), + (0x014 => _1), + (0x01C => gemgxlpllcfg0: ReadWrite), + (0x020 => gemgxlpllcfg1: ReadWrite), + (0x024 => coreclksel: ReadWrite), + (0x028 => deviceresetreg: ReadWrite), + (0x02C => _2), + (0x100 => @END), + } +} + +struct Prci { + clk_osc: ClockHandle, + clk_rtc: ClockHandle, + regs: IrqSafeSpinlock>, +} + +impl Device for Prci { + fn display_name(&self) -> &str { + "sifive,fu540-c000-prci" + } +} + +impl ClockController for Prci { + fn clock_rate(&self, clock: Option) -> Result { + let _ = &self.clk_rtc; + let regs = self.regs.lock(); + match clock { + Some(CLK_TLCLK) => { + // 0 = CORE_PLL + // 1 = HFCLK + let coreclksel = regs.coreclksel.get() & 1 != 0; + let glcm = if coreclksel { + self.clk_osc.rate()? + } else { + todo!() + }; + Ok(glcm / 2) + } + Some(_) => todo!(), + None => Err(Error::InvalidArgument), + } + } + + fn set_clock_rate(&self, _clock: Option, _rate: Hertz) -> Result { + Err(Error::NotImplemented) + } + + fn enable_clock(&self, clock: Option) -> Result<(), Error> { + match clock { + Some(CLK_GEMGXLPLL) | Some(CLK_DDRPLL) | Some(CLK_COREPLL) => todo!(), + Some(CLK_TLCLK) => Ok(()), + _ => Err(Error::InvalidArgument), + } + } + + fn disable_clock(&self, _clock: Option) -> Result<(), Error> { + Err(Error::NotImplemented) + } +} + +impl DeviceTreeClockController for Prci { + fn map_clock(self: Arc, property: &TProp, offset: usize) -> Option<(ClockHandle, usize)> { + let clock = property.read_cell(offset, 1)? as u32; + Some(( + ClockHandle { + parent: self.clone(), + clock: Some(clock), + }, + 1, + )) + } +} + +device_tree_driver! { + compatible: ["sifive,fu540-c000-prci"], + driver: { + fn probe(&self, node: &Arc, context: &mut ProbeContext) -> Option> { + let base = node.map_base(context, 0)?; + let clk_osc = node.clock(0)?; + let clk_rtc = node.clock(1)?; + + let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?; + + let prci = Arc::new(Prci { + regs: IrqSafeSpinlock::new(regs), + clk_osc, + clk_rtc + }); + + node.make_clock_controller(prci.clone()); + + Some(prci) + } + } +} diff --git a/kernel/driver/bsp/sifive/src/lib.rs b/kernel/driver/bsp/sifive/src/lib.rs new file mode 100644 index 00000000..49917ddb --- /dev/null +++ b/kernel/driver/bsp/sifive/src/lib.rs @@ -0,0 +1,6 @@ +#![no_std] + +extern crate alloc; + +mod clock; +mod uart; diff --git a/kernel/driver/bsp/sifive/src/uart.rs b/kernel/driver/bsp/sifive/src/uart.rs new file mode 100644 index 00000000..702a2614 --- /dev/null +++ b/kernel/driver/bsp/sifive/src/uart.rs @@ -0,0 +1,228 @@ +use alloc::sync::Arc; +use device_api::{ + clock::{ClockHandle, Hertz}, + device::{Device, DeviceInitContext}, + interrupt::{InterruptHandler, IrqHandle, IrqVector}, +}; +use device_tree::driver::{Node, ProbeContext, device_tree_driver}; +use libk::{ + debug::{self, DebugSink}, + device::manager::DEVICE_REGISTRY, + error::Error, + vfs::{Terminal, TerminalInput, TerminalOutput}, +}; +use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo}; +use libk_util::{OneTimeInit, sync::IrqSafeSpinlock}; +use tock_registers::{ + interfaces::{Readable, Writeable}, + register_bitfields, register_structs, + registers::{ReadOnly, ReadWrite, WriteOnly}, +}; +use yggdrasil_abi::io::{TerminalOptions, TerminalOutputOptions}; + +register_bitfields! { + u32, + txctrl [ + txen OFFSET(0) NUMBITS(1) [], + nstop OFFSET(1) NUMBITS(1) [ + One = 0, + Two = 1 + ], + txcnt OFFSET(16) NUMBITS(3) [], + ], + rxctrl [ + rxen OFFSET(0) NUMBITS(1) [], + rxcnt OFFSET(16) NUMBITS(3) [], + ], + interrupt [ + txwm OFFSET(0) NUMBITS(1) [], + rxwm OFFSET(1) NUMBITS(1) [], + ], +} + +register_structs! { + Regs { + (0x00 => txdata: WriteOnly), + (0x04 => rxdata: ReadOnly), + (0x08 => txctrl: ReadWrite), + (0x0C => rxctrl: ReadWrite), + (0x10 => ie: ReadWrite), + (0x14 => ip: ReadOnly), + (0x18 => div: ReadWrite), + (0x1C => _0), + (0x20 => @END), + } +} + +struct Inner { + clock: ClockHandle, + regs: IrqSafeSpinlock>, +} + +struct Uart { + name: &'static str, + irq: IrqHandle, + clock: ClockHandle, + base: PhysicalAddress, + inner: OneTimeInit>>, +} + +impl Regs { + fn set_baud_rate(&self, input_clk: Hertz, baud: u32) -> Result<(), Error> { + let div = Hertz::divider(input_clk, Hertz::from(baud)).ok_or(Error::InvalidArgument)?; + if div >= (1 << 20) || div == 0 { + return Err(Error::InvalidArgument); + } + let div = div - 1; + self.div.set(div); + + Ok(()) + } + + fn baud_rate(&self, input_clk: Hertz) -> u32 { + let div = self.div.get() + 1; + (input_clk / div).0 as u32 + } + + fn write(&self, byte: u8) { + while !self.ip.matches_all(interrupt::txwm::SET) { + core::hint::spin_loop(); + } + self.txdata.set(byte as u32); + } +} + +impl Device for Uart { + unsafe fn init(self: Arc, _cx: DeviceInitContext) -> Result<(), Error> { + let regs = unsafe { DeviceMemoryIo::::map(self.base, Default::default()) }?; + + self.clock.enable()?; + let input_clk = self.clock.rate()?; + + { + let _guard = debug::MuteGuard::acquire(); + + regs.txctrl.write(txctrl::txen::CLEAR); + regs.rxctrl.write(rxctrl::rxen::CLEAR); + regs.set_baud_rate(input_clk, 115200)?; + regs.txctrl.write(txctrl::txen::SET + txctrl::txcnt.val(3)); + regs.rxctrl.write(rxctrl::rxen::SET + rxctrl::rxcnt.val(0)); + } + + let input = TerminalInput::with_capacity(64)?; + let output = Inner { + regs: IrqSafeSpinlock::new(regs), + clock: self.clock.clone(), + }; + + let terminal = self.inner.init(Arc::new(Terminal::from_parts( + TerminalOptions::const_default(), + input, + output, + ))); + + DEVICE_REGISTRY + .serial_terminal + .register(terminal.clone(), Some(self.clone())) + .ok(); + + Ok(()) + } + + unsafe fn init_irq(self: Arc) -> Result<(), Error> { + self.irq.register(self.clone())?; + self.irq.enable()?; + let regs = self.inner.get().output().regs.lock(); + regs.ie.write(interrupt::rxwm::SET); + Ok(()) + } + + fn display_name(&self) -> &str { + self.name + } +} + +impl InterruptHandler for Uart { + fn handle_irq(self: Arc, _vector: IrqVector) -> bool { + let terminal = self.inner.get(); + let byte = { + let regs = terminal.output().regs.lock(); + if regs.ip.matches_all(interrupt::rxwm::SET) { + regs.rxdata.get() as u8 + } else { + return false; + } + }; + terminal.write_to_input(byte); + true + } +} + +impl DebugSink for Uart { + fn putc(&self, c: u8) -> Result<(), Error> { + self.inner.get().putc_to_output(c) + } + fn puts(&self, s: &str) -> Result<(), Error> { + self.inner.get().write_to_output(s.as_bytes())?; + Ok(()) + } + fn supports_control_sequences(&self) -> bool { + true + } +} + +impl TerminalOutput for Inner { + fn write(&self, byte: u8, options: &TerminalOutputOptions) -> Result<(), Error> { + let regs = self.regs.lock(); + if byte == b'\n' && options.contains(TerminalOutputOptions::NL_TO_CRNL) { + regs.write(b'\r'); + } + regs.write(byte); + Ok(()) + } + fn write_multiple( + &self, + bytes: &[u8], + options: &TerminalOutputOptions, + ) -> Result { + let regs = self.regs.lock(); + for &byte in bytes { + if byte == b'\n' && options.contains(TerminalOutputOptions::NL_TO_CRNL) { + regs.write(b'\r'); + } + regs.write(byte); + } + Ok(bytes.len()) + } + + fn baud_rate(&self) -> u32 { + let input_clk = self.clock.rate().unwrap(); + self.regs.lock().baud_rate(input_clk) + } + fn set_baud_rate(&self, baud: u32) -> Result<(), Error> { + let input_clk = self.clock.rate().unwrap(); + self.regs.lock().set_baud_rate(input_clk, baud) + } +} + +device_tree_driver! { + compatible: ["sifive,uart0"], + driver: { + fn probe(&self, node: &Arc, context: &mut ProbeContext) -> Option> { + let name = node.name().unwrap_or("uart"); + let base = node.map_base(context, 0)?; + let clock = node.clock(0)?; + let irq = node.interrupt(0)?; + + let uart = Arc::new(Uart { + name, + irq, + clock, + base, + inner: OneTimeInit::new() + }); + + Some(uart) + } + } +} diff --git a/kernel/lib/device-api/src/clock/mod.rs b/kernel/lib/device-api/src/clock/mod.rs index 7235f249..b4fbd8ab 100644 --- a/kernel/lib/device-api/src/clock/mod.rs +++ b/kernel/lib/device-api/src/clock/mod.rs @@ -7,11 +7,13 @@ pub use freq::{Hertz, IntoHertz}; mod freq; +#[derive(Clone)] pub struct ClockHandle { pub parent: Arc, pub clock: Option, } +#[derive(Clone)] pub struct ResetHandle { pub parent: Arc, pub reset: Option, diff --git a/kernel/libk/libk-mm/src/phys/manager.rs b/kernel/libk/libk-mm/src/phys/manager.rs index dd6bb975..0cca9ead 100644 --- a/kernel/libk/libk-mm/src/phys/manager.rs +++ b/kernel/libk/libk-mm/src/phys/manager.rs @@ -132,7 +132,10 @@ impl PhysicalMemoryManager { /// Allocates a contiguous range of physical pages, marking it as used with `usage` pub fn alloc_contiguous_pages(&mut self, count: usize) -> Result { - 'l0: for i in self.last_free_bit..self.page_count { + if count >= self.page_count { + return Err(Error::OutOfMemory); + } + 'l0: for i in self.last_free_bit..self.page_count - count { for j in 0..count { if self.is_alloc(i + j) { continue 'l0; @@ -164,8 +167,13 @@ impl PhysicalMemoryManager { /// `addr` must be a page-aligned physical address previously allocated by this implementation. pub unsafe fn free_page(&mut self, page: PhysicalAddress) { let page = page.try_into_usize().unwrap(); - assert!(page >= self.offset); + if page < self.offset { + panic!("Physical page below managed area: {page:#x}"); + } let index = (page - self.offset) / L3_PAGE_SIZE; + if index >= self.page_count { + panic!("Physical page above managed area: {page:#x}"); + } STATS.used_pages.fetch_sub(1, Ordering::Relaxed); diff --git a/kernel/libk/libk-mm/src/phys/mod.rs b/kernel/libk/libk-mm/src/phys/mod.rs index 186ba5ed..ab74ad9e 100644 --- a/kernel/libk/libk-mm/src/phys/mod.rs +++ b/kernel/libk/libk-mm/src/phys/mod.rs @@ -204,6 +204,10 @@ pub unsafe fn init_from_iter + Clone>( ArchitectureImpl::halt(); } + /* + 000000:?:libk_mm::phys:207: page_bitmap_phys_base=0x80060000 + 000000:?:libk_mm::phys:208: total_count=32768 + */ let mut manager = PhysicalMemoryManager::new( page_bitmap_phys_base, phys_start diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 2a0f843e..a727a99b 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -80,6 +80,7 @@ cfg_if::cfg_if! { } else if #[cfg(target_arch = "riscv64")] { extern crate ygg_driver_bsp_riscv; extern crate ygg_driver_bsp_jh7110; + extern crate ygg_driver_bsp_sifive; extern crate ygg_driver_net_stmmac; } diff --git a/lib/qemu/src/riscv64.rs b/lib/qemu/src/riscv64.rs index 7f48de03..1d9c982b 100644 --- a/lib/qemu/src/riscv64.rs +++ b/lib/qemu/src/riscv64.rs @@ -10,6 +10,7 @@ pub enum Cpu { #[derive(Debug)] pub enum Machine { Virt, + SiFiveU, } #[derive(Debug)] @@ -18,7 +19,7 @@ pub struct QemuRiscv64; #[derive(Debug)] pub enum Image { OpenSBI { - bios: PathBuf, + bios: Option, kernel: PathBuf, initrd: PathBuf, }, @@ -29,6 +30,7 @@ impl IntoArgs for Machine { command.arg("-M"); match self { Self::Virt => command.arg("virt"), + Self::SiFiveU => command.arg("sifive_u"), }; } } @@ -54,8 +56,10 @@ impl IntoArgs for Image { command.arg(kernel); command.arg("-initrd"); command.arg(initrd); - command.arg("-bios"); - command.arg(bios); + if let Some(bios) = bios { + command.arg("-bios"); + command.arg(bios); + } } } } diff --git a/xtask/src/env.rs b/xtask/src/env.rs index 1785f8fc..66a8c081 100644 --- a/xtask/src/env.rs +++ b/xtask/src/env.rs @@ -80,7 +80,7 @@ pub enum Board { raspi4b, // RISC-V boards - jh7110, + sifive_u, } #[derive(Debug)] @@ -228,6 +228,7 @@ impl Board { pub fn device_tree(&self) -> Option<&str> { match self { Self::raspi4b => Some("aarch64/bcm2711-rpi-4-b"), + Self::sifive_u => Some("riscv64/sifive-hifive-unleashed-a00"), _ => None, } } diff --git a/xtask/src/qemu.rs b/xtask/src/qemu.rs index b86eb49c..cda7d925 100644 --- a/xtask/src/qemu.rs +++ b/xtask/src/qemu.rs @@ -236,16 +236,29 @@ fn run_riscv64( qemu.override_qemu(qemu_bin); } let bios = env.workspace_root.join("boot/riscv/fw_jump.bin"); + + match env.board { + Board::sifive_u => qemu + .with_machine(riscv64::Machine::SiFiveU) + .with_boot_image(riscv64::Image::OpenSBI { + kernel, + initrd, + bios: None, + }), + Board::virt | Board::default => qemu + .with_machine(riscv64::Machine::Virt) + .with_boot_image(riscv64::Image::OpenSBI { + kernel, + initrd, + bios: Some(bios), + }) + .with_memory_megabytes(1024) + .with_cpu(riscv64::Cpu::Rv64), + e => return Err(Error::UnsupportedEmulation(e)), + }; + qemu.with_serial(QemuSerialTarget::MonStdio) - .with_machine(riscv64::Machine::Virt) - .with_cpu(riscv64::Cpu::Rv64) - .with_memory_megabytes(1024) - .disable_display() - .with_boot_image(riscv64::Image::OpenSBI { - kernel, - initrd, - bios, - }); + .disable_display(); for device in devices { qemu.with_device(device);