summaryrefslogtreecommitdiffstats
path: root/rsnix
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--rsnix/Cargo.toml3
-rw-r--r--rsnix/src/ahci.rs8
-rw-r--r--rsnix/src/fat.rs130
-rw-r--r--rsnix/src/font.rs8
-rw-r--r--rsnix/src/gop.rs26
-rw-r--r--rsnix/src/interrupts.rs70
-rw-r--r--rsnix/src/memory.rs11
-rw-r--r--rsnix/src/ps2.rs10
-rw-r--r--rsnix/src/tty.rs1
-rw-r--r--rsnix/src/vt.rs8
10 files changed, 161 insertions, 114 deletions
diff --git a/rsnix/Cargo.toml b/rsnix/Cargo.toml
index d8cd26b..5225b1a 100644
--- a/rsnix/Cargo.toml
+++ b/rsnix/Cargo.toml
@@ -2,3 +2,6 @@
2name = "rsnix" 2name = "rsnix"
3version.workspace = true 3version.workspace = true
4edition.workspace = true 4edition.workspace = true
5
6[lints]
7workspace = true
diff --git a/rsnix/src/ahci.rs b/rsnix/src/ahci.rs
index 427345d..9a49431 100644
--- a/rsnix/src/ahci.rs
+++ b/rsnix/src/ahci.rs
@@ -54,7 +54,6 @@ const ATA_WRITE_DMA_EXT: u8 = 0x35;
54struct Disk { 54struct Disk {
55 port: usize, // port register base = ABAR + 0x100 55 port: usize, // port register base = ABAR + 0x100
56 clb: usize, // command list, 1024 B (32 × 32 B), 256 B aligned 56 clb: usize, // command list, 1024 B (32 × 32 B), 256 B aligned
57 fis: usize, // FIS receive area, 256 B aligned
58 ct: usize, // command table, 256 B, 128 B aligned 57 ct: usize, // command table, 256 B, 128 B aligned
59 buf: usize, // 4 KiB DMA data buffer (page aligned) 58 buf: usize, // 4 KiB DMA data buffer (page aligned)
60 sectors: u64, // total sectors (LBA48 from IDENTIFY) 59 sectors: u64, // total sectors (LBA48 from IDENTIFY)
@@ -175,7 +174,6 @@ fn start_port(abar: usize, port_idx: u32) -> Option<Disk> {
175 let mut d = Disk { 174 let mut d = Disk {
176 port, 175 port,
177 clb, 176 clb,
178 fis,
179 ct, 177 ct,
180 buf, 178 buf,
181 sectors: 0, 179 sectors: 0,
@@ -389,8 +387,12 @@ fn issue(d: &Disk, cmd: u8, lba: u64, count: u32, write: bool) -> Result<(), ()>
389 Ok(()) 387 Ok(())
390} 388}
391 389
390fn disks() -> &'static mut [Option<Disk>; MAX_DISKS] {
391 unsafe { &mut *core::ptr::addr_of_mut!(DISKS) }
392}
393
392fn active() -> Option<&'static Disk> { 394fn active() -> Option<&'static Disk> {
393 unsafe { DISKS.get(ACTIVE).and_then(|d| d.as_ref()) } 395 disks().get(unsafe { ACTIVE }).and_then(|d| d.as_ref())
394} 396}
395 397
396/// DMA `count` sectors starting at `lba` into the selected disk's buffer. 398/// DMA `count` sectors starting at `lba` into the selected disk's buffer.
diff --git a/rsnix/src/fat.rs b/rsnix/src/fat.rs
index 52aa641..13d19df 100644
--- a/rsnix/src/fat.rs
+++ b/rsnix/src/fat.rs
@@ -38,7 +38,6 @@ struct Volume {
38 uid: String, // unique mount name: "LABEL-XXXXXXXX" 38 uid: String, // unique mount name: "LABEL-XXXXXXXX"
39 sectors_per_cluster: u8, 39 sectors_per_cluster: u8,
40 reserved: u16, 40 reserved: u16,
41 fat_count: u8,
42 fat_size: u32, // sectors per FAT 41 fat_size: u32, // sectors per FAT
43 root_cluster: u32, 42 root_cluster: u32,
44 first_data: u32, // LBA of cluster 2 43 first_data: u32, // LBA of cluster 2
@@ -50,28 +49,36 @@ struct Volume {
50static mut VOLS: Vec<Volume> = Vec::new(); 49static mut VOLS: Vec<Volume> = Vec::new();
51static mut ACTIVE_VOL: usize = 0; 50static mut ACTIVE_VOL: usize = 0;
52 51
52/// Raw-pointer accessors for the volume statics. The kernel is
53/// single-threaded, so `&'static` refs are sound; the `addr_of_mut!`
54/// round-trip keeps `static_mut_refs` (warn today, deny in edition
55/// 2024) quiet.
56fn vols() -> &'static mut Vec<Volume> {
57 unsafe { &mut *core::ptr::addr_of_mut!(VOLS) }
58}
59
60fn active_vol() -> &'static mut usize {
61 unsafe { &mut *core::ptr::addr_of_mut!(ACTIVE_VOL) }
62}
63
53pub fn volume_count() -> usize { 64pub fn volume_count() -> usize {
54 unsafe { VOLS.len() } 65 vols().len()
55} 66}
56 67
57/// Unique mount name of volume `vol` ("RSDOS-5BD1E55F"). 68/// Unique mount name of volume `vol` ("RSDOS-5BD1E55F").
58pub fn uid(vol: usize) -> &'static str { 69pub fn uid(vol: usize) -> &'static str {
59 unsafe { VOLS.get(vol) } 70 vols().get(vol).map(|v| v.uid.as_str()).unwrap_or("")
60 .map(|v| v.uid.as_str())
61 .unwrap_or("")
62} 71}
63 72
64pub fn root_cluster(vol: usize) -> u32 { 73pub fn root_cluster(vol: usize) -> u32 {
65 unsafe { VOLS.get(vol) } 74 vols().get(vol).map(|v| v.root_cluster).unwrap_or(0)
66 .map(|v| v.root_cluster)
67 .unwrap_or(0)
68} 75}
69 76
70/// Point all FAT operations at volume `vol`; also selects the backing 77/// Point all FAT operations at volume `vol`; also selects the backing
71/// AHCI disk. Called by every public entry point (single-threaded 78/// AHCI disk. Called by every public entry point (single-threaded
72/// kernel, so a process-wide "current volume" is fine). 79/// kernel, so a process-wide "current volume" is fine).
73fn select_vol(vol: usize) -> Result<(), ()> { 80fn select_vol(vol: usize) -> Result<(), ()> {
74 let disk = unsafe { VOLS.get(vol) }.map(|v| v.disk).ok_or(())?; 81 let disk = vols().get(vol).map(|v| v.disk).ok_or(())?;
75 unsafe { 82 unsafe {
76 ACTIVE_VOL = vol; 83 ACTIVE_VOL = vol;
77 } 84 }
@@ -97,7 +104,7 @@ fn write_sector(lba: u64, data: &[u8; SECTOR]) -> Result<(), ()> {
97// --- cluster math ---------------------------------------------------------- 104// --- cluster math ----------------------------------------------------------
98 105
99fn v() -> &'static mut Volume { 106fn v() -> &'static mut Volume {
100 unsafe { VOLS.get_mut(ACTIVE_VOL) }.unwrap() 107 vols().get_mut(*active_vol()).unwrap()
101} 108}
102 109
103fn spc() -> usize { 110fn spc() -> usize {
@@ -351,13 +358,13 @@ fn scan_dir(dir_cluster: u32) -> Result<Vec<DirItem>, ()> {
351 return Err(()); // loop protection on a corrupt chain 358 return Err(()); // loop protection on a corrupt chain
352 } 359 }
353 read_cluster(c, &mut buf)?; 360 read_cluster(c, &mut buf)?;
354 let mut done = false;
355 for (i, e) in buf.chunks_exact(32).enumerate() { 361 for (i, e) in buf.chunks_exact(32).enumerate() {
356 match e[0] { 362 match e[0] {
357 0x00 => { 363 // 0x00 ends the entries in THIS cluster, not the whole
358 done = true; 364 // directory: a dir that spills into a second cluster keeps
359 break; 365 // its 0x00 marker in the first cluster, so we must keep
360 } 366 // walking the chain to see the rest.
367 0x00 => break,
361 0xE5 => lfn.clear(), // deleted entry; drop any partial LFN 368 0xE5 => lfn.clear(), // deleted entry; drop any partial LFN
362 _ => { 369 _ => {
363 let attr = e[11]; 370 let attr = e[11];
@@ -415,9 +422,6 @@ fn scan_dir(dir_cluster: u32) -> Result<Vec<DirItem>, ()> {
415 } 422 }
416 } 423 }
417 } 424 }
418 if done {
419 break;
420 }
421 cur = next_cluster(c).ok(); 425 cur = next_cluster(c).ok();
422 } 426 }
423 Ok(items) 427 Ok(items)
@@ -729,10 +733,77 @@ fn to_8_3(name: &str) -> Option<([u8; 11], bool)> {
729 Some((sfn, needs)) 733 Some((sfn, needs))
730} 734}
731 735
736/// Collect the raw short names already present in a directory.
737fn dir_short_names(parent: u32) -> Result<Vec<[u8; 11]>, ()> {
738 let cs = cluster_size();
739 let mut buf = vec![0u8; cs];
740 let mut out = Vec::new();
741 let mut cur: Option<u32> = Some(parent);
742 let mut guard = 0;
743 while let Some(c) = cur {
744 guard += 1;
745 if guard > 1_000_000 {
746 return Err(()); // loop protection on a corrupt chain
747 }
748 read_cluster(c, &mut buf)?;
749 for e in buf.chunks_exact(32) {
750 match e[0] {
751 // Same rule as scan_dir: 0x00 ends this cluster's entries
752 // but the directory may continue in the next cluster.
753 0x00 => break,
754 0xE5 => {}
755 _ => {
756 if e[11] != 0x0F {
757 let mut sfn = [0u8; 11];
758 sfn.copy_from_slice(&e[0..11]);
759 out.push(sfn);
760 }
761 }
762 }
763 }
764 cur = next_cluster(c).ok();
765 }
766 Ok(out)
767}
768
769/// Make `base` unique within a directory: on collision, append a numeric
770/// ~N tail (DISK-TE~1, DISK-TE~2, …) — the standard FAT short-name
771/// disambiguation that fsck and other OSes expect. Without it, two files
772/// whose long names share an 8.3 prefix would get the same short name and
773/// fsck reports them as duplicate directory entries.
774fn unique_short_name(base: &[u8; 11], used: &[[u8; 11]]) -> [u8; 11] {
775 if !used.contains(base) {
776 return *base;
777 }
778 let mut name_len = 0;
779 while name_len < 8 && base[name_len] != b' ' {
780 name_len += 1;
781 }
782 for n in 1..=999u32 {
783 let tail = alloc::format!("~{}", n);
784 let tb = tail.as_bytes();
785 let keep = name_len.saturating_sub(tb.len());
786 let mut cand = [b' '; 11];
787 cand[..keep].copy_from_slice(&base[..keep]);
788 cand[keep..keep + tb.len()].copy_from_slice(tb);
789 cand[8..11].copy_from_slice(&base[8..11]);
790 if !used.contains(&cand) {
791 return cand;
792 }
793 }
794 *base // pathological; keep the colliding name
795}
796
732/// Write the LFN entries (if any) + SFN entry for `name` into the parent 797/// Write the LFN entries (if any) + SFN entry for `name` into the parent
733/// directory, extending its chain with a fresh zeroed cluster if needed. 798/// directory, extending its chain with a fresh zeroed cluster if needed.
734fn add_dir_entry(parent: u32, name: &str, is_dir: bool, cluster: u32) -> Result<(), ()> { 799fn add_dir_entry(parent: u32, name: &str, is_dir: bool, cluster: u32) -> Result<(), ()> {
735 let (sfn, needs_lfn) = to_8_3(name).ok_or(())?; 800 let (base_sfn, mut needs_lfn) = to_8_3(name).ok_or(())?;
801 // Short names must be unique within the directory; a plain truncation
802 // collides when two long names share an 8.3 prefix.
803 let sfn = unique_short_name(&base_sfn, &dir_short_names(parent)?);
804 if sfn != base_sfn {
805 needs_lfn = true; // keep the original name via an LFN entry
806 }
736 let lfn_chars: Vec<u16> = name.encode_utf16().collect(); 807 let lfn_chars: Vec<u16> = name.encode_utf16().collect();
737 let groups = if needs_lfn { 808 let groups = if needs_lfn {
738 lfn_chars.len().div_ceil(13) 809 lfn_chars.len().div_ceil(13)
@@ -810,6 +881,18 @@ fn add_dir_entry(parent: u32, name: &str, is_dir: bool, cluster: u32) -> Result<
810 write_cluster(c, &buf)?; 881 write_cluster(c, &buf)?;
811 return Ok(()); 882 return Ok(());
812 } 883 }
884 // Chain full: pack this cluster before extending. A mid-chain
885 // 0x00 would look like the end of the directory to other FAT
886 // readers (Windows, mtools stop at the first 0x00), hiding every
887 // entry written into the new cluster. Marking the free slots as
888 // 0xE5 (deleted) keeps them skippable while the chain walk finds
889 // the continuation.
890 for i in 0..slots {
891 if buf[i * 32] == 0x00 {
892 buf[i * 32] = 0xE5;
893 }
894 }
895 write_cluster(c, &buf)?;
813 // Chain full: extend with a fresh zeroed cluster and retry. 896 // Chain full: extend with a fresh zeroed cluster and retry.
814 match next_cluster(c) { 897 match next_cluster(c) {
815 Ok(n) => cur = Some(n), 898 Ok(n) => cur = Some(n),
@@ -910,10 +993,8 @@ pub fn init() -> bool {
910 match try_mount(d) { 993 match try_mount(d) {
911 Some(volume) => { 994 Some(volume) => {
912 let uid = volume.uid.clone(); 995 let uid = volume.uid.clone();
913 let vol = unsafe { 996 vols().push(volume);
914 VOLS.push(volume); 997 let vol = vols().len() - 1;
915 VOLS.len() - 1
916 };
917 // Re-sync the FSInfo free-cluster count (may be stale 998 // Re-sync the FSInfo free-cluster count (may be stale
918 // from earlier sessions). 999 // from earlier sessions).
919 if select_vol(vol).is_ok() { 1000 if select_vol(vol).is_ok() {
@@ -1026,7 +1107,7 @@ fn try_mount(d: usize) -> Option<Volume> {
1026 let mut uid = alloc::format!("{}-{:08X}", label, serial); 1107 let mut uid = alloc::format!("{}-{:08X}", label, serial);
1027 let mut n = 2u32; 1108 let mut n = 2u32;
1028 loop { 1109 loop {
1029 let taken = unsafe { VOLS.iter() }.any(|v| v.uid == uid); 1110 let taken = vols().iter().any(|v| v.uid == uid);
1030 if !taken { 1111 if !taken {
1031 break; 1112 break;
1032 } 1113 }
@@ -1038,7 +1119,6 @@ fn try_mount(d: usize) -> Option<Volume> {
1038 uid, 1119 uid,
1039 sectors_per_cluster: spc, 1120 sectors_per_cluster: spc,
1040 reserved, 1121 reserved,
1041 fat_count,
1042 fat_size, 1122 fat_size,
1043 root_cluster, 1123 root_cluster,
1044 first_data, 1124 first_data,
diff --git a/rsnix/src/font.rs b/rsnix/src/font.rs
index e188513..35327e7 100644
--- a/rsnix/src/font.rs
+++ b/rsnix/src/font.rs
@@ -1,5 +1,5 @@
1/// 8×16 bitmap font. 1// 8×16 bitmap font.
2/// 2//
3/// Glyphs are generated at build time by `build.rs` from the system PSF2 font 3// Glyphs are generated at build time by `build.rs` from the system PSF2 font
4/// (`/usr/share/kbd/consolefonts/lat1-16.psfu.gz`). 4// (`/usr/share/kbd/consolefonts/lat1-16.psfu.gz`).
5include!(concat!(env!("OUT_DIR"), "/font_data.rs")); 5include!(concat!(env!("OUT_DIR"), "/font_data.rs"));
diff --git a/rsnix/src/gop.rs b/rsnix/src/gop.rs
index 77cc514..0d24d7d 100644
--- a/rsnix/src/gop.rs
+++ b/rsnix/src/gop.rs
@@ -105,23 +105,25 @@ pub fn clear_screen() {
105 crate::vt::clear_all(); 105 crate::vt::clear_all();
106} 106}
107 107
108/// Raw-pointer accessor for the framebuffer static (single-threaded
109/// kernel; keeps `static_mut_refs` quiet).
110fn fb() -> &'static mut Option<Framebuffer> {
111 unsafe { &mut *core::ptr::addr_of_mut!(FB) }
112}
113
108/// (cols, rows) of the text surface, from the current framebuffer mode. 114/// (cols, rows) of the text surface, from the current framebuffer mode.
109pub fn dims() -> (usize, usize) { 115pub fn dims() -> (usize, usize) {
110 unsafe { 116 match fb().as_ref() {
111 match FB.as_ref() { 117 Some(f) => (f.width / 8, f.height / 16),
112 Some(f) => (f.width / 8, f.height / 16), 118 None => (0, 0),
113 None => (0, 0),
114 }
115 } 119 }
116} 120}
117 121
118/// Pack an (r, g, b) colour for the framebuffer's pixel format. 122/// Pack an (r, g, b) colour for the framebuffer's pixel format.
119pub fn rgb(r: u8, g: u8, b: u8) -> u32 { 123pub fn rgb(r: u8, g: u8, b: u8) -> u32 {
120 unsafe { 124 match fb().as_ref() {
121 match FB.as_ref() { 125 Some(f) if f.bgr => ((r as u32) << 16) | ((g as u32) << 8) | b as u32,
122 Some(f) if f.bgr => ((r as u32) << 16) | ((g as u32) << 8) | b as u32, 126 _ => ((b as u32) << 16) | ((g as u32) << 8) | r as u32,
123 _ => ((b as u32) << 16) | ((g as u32) << 8) | r as u32,
124 }
125 } 127 }
126} 128}
127 129
@@ -340,7 +342,7 @@ fn unicode_glyph_bits(ch: u16) -> Option<[u8; 16]> {
340} 342}
341 343
342pub(crate) fn blit_glyph(col: usize, row: usize, ch: u16, fg: (u8, u8, u8), bg: (u8, u8, u8)) { 344pub(crate) fn blit_glyph(col: usize, row: usize, ch: u16, fg: (u8, u8, u8), bg: (u8, u8, u8)) {
343 if let Some(fb) = unsafe { FB.as_ref() } { 345 if let Some(fb) = fb().as_ref() {
344 let glyph = font::GLYPH_8X16; 346 let glyph = font::GLYPH_8X16;
345 let data = if ch < 256 { 347 let data = if ch < 256 {
346 match glyph.get(ch as usize) { 348 match glyph.get(ch as usize) {
@@ -381,7 +383,7 @@ pub(crate) fn blit_glyph(col: usize, row: usize, ch: u16, fg: (u8, u8, u8), bg:
381/// Shift the framebuffer content up `n` text rows and clear the exposed 383/// Shift the framebuffer content up `n` text rows and clear the exposed
382/// bottom rows. `n` batches deferred scrolls into one push. 384/// bottom rows. `n` batches deferred scrolls into one push.
383pub(crate) fn shift_up_n(n: usize) { 385pub(crate) fn shift_up_n(n: usize) {
384 if let Some(fb) = unsafe { FB.as_ref() } { 386 if let Some(fb) = fb().as_ref() {
385 fb.shift_up_n(n); 387 fb.shift_up_n(n);
386 } 388 }
387} 389}
diff --git a/rsnix/src/interrupts.rs b/rsnix/src/interrupts.rs
index 66dc59f..cbd234e 100644
--- a/rsnix/src/interrupts.rs
+++ b/rsnix/src/interrupts.rs
@@ -98,7 +98,7 @@ fn pit_set_rate(hz: u64) {
98 let divisor = (PIT_FREQ / hz) as u16; 98 let divisor = (PIT_FREQ / hz) as u16;
99 unsafe { 99 unsafe {
100 // Mode 2, channel 0, lobyte/hibyte, binary 100 // Mode 2, channel 0, lobyte/hibyte, binary
101 asm!("out dx, al", in("dx") PIT_CMD, in("al") 0b00_11_010_0u8, 101 asm!("out dx, al", in("dx") PIT_CMD, in("al") 0b0011_0100u8,
102 options(nostack, preserves_flags)); 102 options(nostack, preserves_flags));
103 asm!("out dx, al", in("dx") PIT_CH0, in("al") (divisor & 0xFF) as u8, 103 asm!("out dx, al", in("dx") PIT_CH0, in("al") (divisor & 0xFF) as u8,
104 options(nostack, preserves_flags)); 104 options(nostack, preserves_flags));
@@ -122,47 +122,10 @@ unsafe impl Sync for KeyBuf {}
122 122
123impl KeyBuf { 123impl KeyBuf {
124 const fn new() -> Self { 124 const fn new() -> Self {
125 // UnsafeCell requires const initialisation in a const ctx. 125 // Inline-const array repeat (stable since 1.79): each element is a
126 // Build an array the long way — one cell at a time. 126 // fresh `UnsafeCell::new(0)` value, so no interior-mutable const.
127 const NEW_CELL: core::cell::UnsafeCell<u8> = core::cell::UnsafeCell::new(0);
128 // `[NEW_CELL; 256]` needs Clone, which UnsafeCell doesn't have.
129 // Use a union trick or just mark the whole buffer init with zeroes.
130 // SAFETY: we initialise via the const-fn path in init().
131 Self { 127 Self {
132 buf: [ 128 buf: [const { core::cell::UnsafeCell::new(0) }; 256],
133 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
134 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
135 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
136 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
137 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
138 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
139 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
140 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
141 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
142 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
143 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
144 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
145 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
146 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
147 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
148 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
149 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
150 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
151 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
152 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
153 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
154 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
155 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
156 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
157 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
158 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
159 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
160 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
161 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
162 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
163 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
164 NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL, NEW_CELL,
165 ],
166 head: AtomicU16::new(0), 129 head: AtomicU16::new(0),
167 tail: AtomicU16::new(0), 130 tail: AtomicU16::new(0),
168 } 131 }
@@ -297,7 +260,10 @@ struct IntFrame {
297unsafe extern "C" fn isr_dispatch(frame: &IntFrame) { 260unsafe extern "C" fn isr_dispatch(frame: &IntFrame) {
298 unsafe { 261 unsafe {
299 match frame.vector { 262 match frame.vector {
300 32 => eoi(0), 263 32 => {
264 TICKS.fetch_add(1, Ordering::Relaxed);
265 eoi(0);
266 }
301 33 => { 267 33 => {
302 let byte: u8; 268 let byte: u8;
303 core::arch::asm!("in al, dx", out("al") byte, in("dx") 0x60u16, 269 core::arch::asm!("in al, dx", out("al") byte, in("dx") 0x60u16,
@@ -311,22 +277,6 @@ unsafe extern "C" fn isr_dispatch(frame: &IntFrame) {
311 } 277 }
312} 278}
313 279
314/// Timer IRQ (PIT channel 0 ~ 100 Hz).
315fn timer_irq() {
316 eoi(0);
317}
318
319/// Keyboard IRQ — read scancode from port 0x60 and push to ring buffer.
320fn keyboard_irq() {
321 let byte: u8;
322 unsafe {
323 asm!("in al, dx", out("al") byte, in("dx") 0x60,
324 options(nostack, preserves_flags));
325 }
326 KEYBUF.push(byte);
327 eoi(1);
328}
329
330// --------------------------------------------------------------------------- 280// ---------------------------------------------------------------------------
331// Public API 281// Public API
332// --------------------------------------------------------------------------- 282// ---------------------------------------------------------------------------
@@ -348,7 +298,7 @@ pub fn init() {
348 let cs = cs as u16; 298 let cs = cs as u16;
349 299
350 // Build the IDT. 300 // Build the IDT.
351 let idt_ptr = IDT.as_mut_ptr(); 301 let idt_ptr = core::ptr::addr_of_mut!(IDT[0]);
352 let spurious = isr_stub_spurious as *const () as u64; 302 let spurious = isr_stub_spurious as *const () as u64;
353 303
354 // Fill all 256 entries with the spurious handler. 304 // Fill all 256 entries with the spurious handler.
@@ -370,7 +320,7 @@ pub fn init() {
370 ]; 320 ];
371 321
372 // Load IDT. 322 // Load IDT.
373 asm!("lidt [{}]", in(reg) &IDTR, options(nostack, preserves_flags)); 323 asm!("lidt [{}]", in(reg) core::ptr::addr_of!(IDTR), options(nostack, preserves_flags));
374 324
375 // Remap PIC and mask everything. 325 // Remap PIC and mask everything.
376 remap_pic(); 326 remap_pic();
diff --git a/rsnix/src/memory.rs b/rsnix/src/memory.rs
index c80edab..54ff7be 100644
--- a/rsnix/src/memory.rs
+++ b/rsnix/src/memory.rs
@@ -289,7 +289,7 @@ pub fn capture_memory_map(bs: &BootServices) -> Result<usize, Status> {
289 let buf_size = buf_size.div_ceil(desc_size) * desc_size; 289 let buf_size = buf_size.div_ceil(desc_size) * desc_size;
290 290
291 // If our static buffer is too small, fall back to pool (rare) 291 // If our static buffer is too small, fall back to pool (rare)
292 if buf_size > unsafe { MAP_WORK_BUF.len() } { 292 if buf_size > unsafe { (*core::ptr::addr_of!(MAP_WORK_BUF)).len() } {
293 return Err(EFI_BUFFER_TOO_SMALL); 293 return Err(EFI_BUFFER_TOO_SMALL);
294 } 294 }
295 295
@@ -337,7 +337,12 @@ pub fn capture_memory_map(bs: &BootServices) -> Result<usize, Status> {
337} 337}
338 338
339/// Retry ExitBootServices with a fresh memory map. 339/// Retry ExitBootServices with a fresh memory map.
340pub fn refresh_and_exit(bs: &BootServices, image_handle: Handle) -> Result<usize, Status> { 340///
341/// # Safety
342///
343/// `image_handle` must be the valid EFI image handle this kernel was
344/// started with (opaque firmware pointer; never dereferenced here).
345pub unsafe fn refresh_and_exit(bs: &BootServices, image_handle: Handle) -> Result<usize, Status> {
341 let key = capture_memory_map(bs)?; 346 let key = capture_memory_map(bs)?;
342 let status = unsafe { (bs.exit_boot_services)(image_handle, key) }; 347 let status = unsafe { (bs.exit_boot_services)(image_handle, key) };
343 if status == EFI_SUCCESS { 348 if status == EFI_SUCCESS {
@@ -591,7 +596,7 @@ pub fn init_page_allocator() -> bool {
591 596
592 // The bitmap itself lives in the heap (one allocation, never freed). 597 // The bitmap itself lives in the heap (one allocation, never freed).
593 // `bytes` >= 1 and align 16 is a power of two, so this cannot fail. 598 // `bytes` >= 1 and align 16 is a power of two, so this cannot fail.
594 let layout = unsafe { Layout::from_size_align_unchecked(bytes, 16) }; 599 let layout = Layout::from_size_align_unchecked(bytes, 16);
595 let bm = ALLOCATOR.alloc(layout); 600 let bm = ALLOCATOR.alloc(layout);
596 if bm.is_null() { 601 if bm.is_null() {
597 return false; 602 return false;
diff --git a/rsnix/src/ps2.rs b/rsnix/src/ps2.rs
index 4747813..35848b6 100644
--- a/rsnix/src/ps2.rs
+++ b/rsnix/src/ps2.rs
@@ -263,8 +263,12 @@ pub fn try_poll_key() -> Option<KeyEvent> {
263 263
264/// Process a single scancode byte. Returns Some(KeyEvent) when a visible 264/// Process a single scancode byte. Returns Some(KeyEvent) when a visible
265/// event happens; returns None for modifier changes, releases, and prefixes. 265/// event happens; returns None for modifier changes, releases, and prefixes.
266fn kbd() -> &'static mut KbdState {
267 unsafe { &mut *core::ptr::addr_of_mut!(KBD) }
268}
269
266fn process_byte(byte: u8) -> Option<KeyEvent> { 270fn process_byte(byte: u8) -> Option<KeyEvent> {
267 let state = unsafe { &mut KBD }; 271 let state = kbd();
268 272
269 if byte == 0xE0 { 273 if byte == 0xE0 {
270 state.got_e0 = true; 274 state.got_e0 = true;
@@ -368,7 +372,7 @@ fn process_e0(byte: u8) -> Option<KeyEvent> {
368} 372}
369 373
370fn update_modifiers(scancode: u8, pressed: bool, e0: bool) { 374fn update_modifiers(scancode: u8, pressed: bool, e0: bool) {
371 let state = unsafe { &mut KBD }; 375 let state = kbd();
372 match (scancode, e0) { 376 match (scancode, e0) {
373 (0x2A, false) => set_mod(state, MOD_LSHIFT, pressed), 377 (0x2A, false) => set_mod(state, MOD_LSHIFT, pressed),
374 (0x36, false) => set_mod(state, MOD_RSHIFT, pressed), 378 (0x36, false) => set_mod(state, MOD_RSHIFT, pressed),
@@ -393,6 +397,6 @@ fn set_mod(state: &mut KbdState, bit: u8, pressed: bool) {
393 397
394/// Is either Ctrl key held? 398/// Is either Ctrl key held?
395fn ctrl_held() -> bool { 399fn ctrl_held() -> bool {
396 let state = unsafe { &mut KBD }; 400 let state = kbd();
397 state.modifiers & (MOD_LCTRL | MOD_RCTRL) != 0 401 state.modifiers & (MOD_LCTRL | MOD_RCTRL) != 0
398} 402}
diff --git a/rsnix/src/tty.rs b/rsnix/src/tty.rs
index b6b6cc8..81a7a24 100644
--- a/rsnix/src/tty.rs
+++ b/rsnix/src/tty.rs
@@ -20,6 +20,7 @@ use crate::{gop, serial};
20pub const LINE_BUF_SIZE: usize = 256; 20pub const LINE_BUF_SIZE: usize = 256;
21 21
22/// Result of one canonical line-input session. 22/// Result of one canonical line-input session.
23#[allow(clippy::large_enum_variant)] // stack buffer by design (no heap churn)
23pub enum LineInput { 24pub enum LineInput {
24 Line(strbuf::String<LINE_BUF_SIZE>), 25 Line(strbuf::String<LINE_BUF_SIZE>),
25 Interrupted, // ^C 26 Interrupted, // ^C
diff --git a/rsnix/src/vt.rs b/rsnix/src/vt.rs
index 59378f3..2cfb0f3 100644
--- a/rsnix/src/vt.rs
+++ b/rsnix/src/vt.rs
@@ -146,8 +146,8 @@ fn cell(r: usize, c: usize) -> &'static mut Cell {
146 &mut cells()[idx(r, c)] 146 &mut cells()[idx(r, c)]
147} 147}
148 148
149fn rendered(r: usize, c: usize) -> &'static mut Cell { 149fn params() -> &'static mut [usize; 4] {
150 &mut rendered_cells()[idx(r, c)] 150 unsafe { &mut *core::ptr::addr_of_mut!(PARAMS) }
151} 151}
152 152
153fn put_cell(r: usize, c: usize, ch: u16) { 153fn put_cell(r: usize, c: usize, ch: u16) {
@@ -204,14 +204,14 @@ fn put_byte(b: u8) {
204 CUR = CUR.saturating_mul(10).saturating_add((b - b'0') as usize); 204 CUR = CUR.saturating_mul(10).saturating_add((b - b'0') as usize);
205 } 205 }
206 b';' => { 206 b';' => {
207 if NPARAMS < PARAMS.len() { 207 if NPARAMS < params().len() {
208 PARAMS[NPARAMS] = CUR; 208 PARAMS[NPARAMS] = CUR;
209 NPARAMS += 1; 209 NPARAMS += 1;
210 } 210 }
211 CUR = 0; 211 CUR = 0;
212 } 212 }
213 _ => { 213 _ => {
214 if NPARAMS < PARAMS.len() { 214 if NPARAMS < params().len() {
215 PARAMS[NPARAMS] = CUR; 215 PARAMS[NPARAMS] = CUR;
216 NPARAMS += 1; 216 NPARAMS += 1;
217 } 217 }