diff options
Diffstat (limited to '')
| -rw-r--r-- | rsnix/src/ahci.rs | 8 | ||||
| -rw-r--r-- | rsnix/src/fat.rs | 130 | ||||
| -rw-r--r-- | rsnix/src/font.rs | 8 | ||||
| -rw-r--r-- | rsnix/src/gop.rs | 26 | ||||
| -rw-r--r-- | rsnix/src/interrupts.rs | 70 | ||||
| -rw-r--r-- | rsnix/src/memory.rs | 11 | ||||
| -rw-r--r-- | rsnix/src/ps2.rs | 10 | ||||
| -rw-r--r-- | rsnix/src/tty.rs | 1 | ||||
| -rw-r--r-- | rsnix/src/vt.rs | 8 |
9 files changed, 158 insertions, 114 deletions
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; | |||
| 54 | struct Disk { | 54 | struct 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 | ||
| 390 | fn disks() -> &'static mut [Option<Disk>; MAX_DISKS] { | ||
| 391 | unsafe { &mut *core::ptr::addr_of_mut!(DISKS) } | ||
| 392 | } | ||
| 393 | |||
| 392 | fn active() -> Option<&'static Disk> { | 394 | fn 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 { | |||
| 50 | static mut VOLS: Vec<Volume> = Vec::new(); | 49 | static mut VOLS: Vec<Volume> = Vec::new(); |
| 51 | static mut ACTIVE_VOL: usize = 0; | 50 | static 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. | ||
| 56 | fn vols() -> &'static mut Vec<Volume> { | ||
| 57 | unsafe { &mut *core::ptr::addr_of_mut!(VOLS) } | ||
| 58 | } | ||
| 59 | |||
| 60 | fn active_vol() -> &'static mut usize { | ||
| 61 | unsafe { &mut *core::ptr::addr_of_mut!(ACTIVE_VOL) } | ||
| 62 | } | ||
| 63 | |||
| 53 | pub fn volume_count() -> usize { | 64 | pub 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"). |
| 58 | pub fn uid(vol: usize) -> &'static str { | 69 | pub 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 | ||
| 64 | pub fn root_cluster(vol: usize) -> u32 { | 73 | pub 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). |
| 73 | fn select_vol(vol: usize) -> Result<(), ()> { | 80 | fn 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 | ||
| 99 | fn v() -> &'static mut Volume { | 106 | fn v() -> &'static mut Volume { |
| 100 | unsafe { VOLS.get_mut(ACTIVE_VOL) }.unwrap() | 107 | vols().get_mut(*active_vol()).unwrap() |
| 101 | } | 108 | } |
| 102 | 109 | ||
| 103 | fn spc() -> usize { | 110 | fn 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. | ||
| 737 | fn 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. | ||
| 774 | fn 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. |
| 734 | fn add_dir_entry(parent: u32, name: &str, is_dir: bool, cluster: u32) -> Result<(), ()> { | 799 | fn 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`). |
| 5 | include!(concat!(env!("OUT_DIR"), "/font_data.rs")); | 5 | include!(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). | ||
| 110 | fn 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. |
| 109 | pub fn dims() -> (usize, usize) { | 115 | pub 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. |
| 119 | pub fn rgb(r: u8, g: u8, b: u8) -> u32 { | 123 | pub 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 | ||
| 342 | pub(crate) fn blit_glyph(col: usize, row: usize, ch: u16, fg: (u8, u8, u8), bg: (u8, u8, u8)) { | 344 | pub(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. |
| 383 | pub(crate) fn shift_up_n(n: usize) { | 385 | pub(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 | ||
| 123 | impl KeyBuf { | 123 | impl 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 { | |||
| 297 | unsafe extern "C" fn isr_dispatch(frame: &IntFrame) { | 260 | unsafe 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). | ||
| 315 | fn timer_irq() { | ||
| 316 | eoi(0); | ||
| 317 | } | ||
| 318 | |||
| 319 | /// Keyboard IRQ — read scancode from port 0x60 and push to ring buffer. | ||
| 320 | fn 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. |
| 340 | pub 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). | ||
| 345 | pub 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. |
| 266 | fn kbd() -> &'static mut KbdState { | ||
| 267 | unsafe { &mut *core::ptr::addr_of_mut!(KBD) } | ||
| 268 | } | ||
| 269 | |||
| 266 | fn process_byte(byte: u8) -> Option<KeyEvent> { | 270 | fn 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 | ||
| 370 | fn update_modifiers(scancode: u8, pressed: bool, e0: bool) { | 374 | fn 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? |
| 395 | fn ctrl_held() -> bool { | 399 | fn 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}; | |||
| 20 | pub const LINE_BUF_SIZE: usize = 256; | 20 | pub 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) | ||
| 23 | pub enum LineInput { | 24 | pub 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 | ||
| 149 | fn rendered(r: usize, c: usize) -> &'static mut Cell { | 149 | fn 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 | ||
| 153 | fn put_cell(r: usize, c: usize, ch: u16) { | 153 | fn 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 | } |
