-
Notifications
You must be signed in to change notification settings - Fork 27
treewide: nuke old repo and replace with rewrite from @phip1611 #41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
d3473f7 to
a43ff67
Compare
Wasabi375
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First off I want to say that this looks great and I'm all for merging this. Thanks for the great work.
That said, I'm not a maintainer here so please feel free to ignore my comments (especially the style based ones if you don't agree).
thanks, your review was already very helpful! |
9a3ca6a to
05934be
Compare
|
@phil-opp @mkroening This PR is now in a state that fullfills my personal high CI, coding and API standards including excellent documentation. We have a basic integration test in QEMU and I tested everything on real hardware! I am very happy to maintain this crate from now on and I'd be very happy to get the corresponding admin rights to publish this crate as well as maintain the repository. PS: This is not a hostile takeover but a friendly offer to improve the ecosystem! :) I hope we can find a path forward thats works for everyone and that we can publish this soon. If you could give me repository admin rights, I can set the necessary changes for the new CI jobs. PPS: I didn't have a chance to test the MMIO backend in a VM or real hardware. But I'm confident it should work. I have a unit test for it tho. |
b6cadd0 to
880be9e
Compare
6feb766 to
e0e4418
Compare
## TL;DR
As discussed [0], this is my 100% rewrite of the crate. It is 100% spec
compliant, highly configurable (usable in kernel drivers) while still
preserving a very simple entry-level abstraction that "just works".
**It also runs on real hardware now!** The original code wasn't mature
enough for all the caveats. Further, we have one common abstraction for
x86 port I/O and MMIO.
## Background
As part of a learning project, I conducted an in-depth exploration of
UART 16550 devices and driver development. During this process, I
identified a key limitation in the current crate: the inability to
configure the baud rate. This functionality is essential for real
hardware usage, and addressing it was my primary motivation.
As I continued, I recognized additional areas for improvement.
Incremental changes quickly proved insufficient to meet the standards
of code quality, completeness, and maintainability I aimed for.
Consequently, I undertook a full rewrite, resulting in a driver that
is fully spec-compliant while maintaining seamless and idiomatic Rust
integration.
While I could release this as a separate crate, I prefer not to fragment
the ecosystem - especially given that rust-osdev already maintains a
crate in this space. **I fully acknowledge that this presents a "take it
or leave it" ("friss oder stirb") scenario** 😞 . That said, I am eager to
collaborate toward a solution that serves the community effectively,
and I am confident this rewrite provides a strong, forward-looking
foundation.
## Notes about Real Hardware Tests
To properly operate with real hardware, I had to specifically
investigate the behavior and especially adapt try_send_bytes()
and test_loopback().
Further, the source and the destination must agree on the baud
rate. Tests in VMs would behave differently (have fewer caveats),
especially as the baud rate is mostly irrelevant there.
## About API and Implementation
Please look into `lib.rs`, I suggest using `cargo doc`. A small except from README is also here:
[0] https://rust-osdev.zulipchat.com/#narrow/channel/426430-general/topic/uart.2016550.20.2F.20serial/near/566528969
This is a basic integration test that runs the code in a VM machine that uses a x86 port I/O backed UART16550.
This enables to compile the crate normally even on non-x86. We could work around using combinations of #[cfg] and #[doc], but this would be very ugly.
This is best practice in Rust. It is a private API helper anyway that just must be public to a certain degree.
mkroening
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, this is great!
While I did not take a close look at every single line of code, this looks extremely solid to me.
This does not break anything with Hermit (hermit-os/kernel#2191), and I have created an issue for adding 16550 UART device support on AArch64 and RISC-V (hermit-os/kernel#2192).
I have a few comments, but nothing blocking. I would be personally fine with merging this, though @phil-opp would need to create a team for this repo and add you.
I don't know whether @phil-opp would prefer some attribution somewhere in the new repo.
| struct DummyGlobalAlloc; | ||
|
|
||
| #[global_allocator] | ||
| static GLOBAL_ALLOCATOR: DummyGlobalAlloc = DummyGlobalAlloc; | ||
|
|
||
| unsafe impl GlobalAlloc for DummyGlobalAlloc { | ||
| unsafe fn alloc(&self, layout: Layout) -> *mut u8 { | ||
| panic!("unsupported! layout={layout:?}"); | ||
| } | ||
|
|
||
| unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { | ||
| panic!("unsupported! ptr={ptr:?}, layout={layout:?}"); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might make sense to not build alloc and remove the global allocator and the anyhow crate that requires it.
As far as I know, creating anyhow errors requires boxing, so handling errors without an allocator is not possible anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah yes make sense! thanks :)
| @@ -0,0 +1,16 @@ | |||
| [package] | |||
| name = "kernel" | |||
| version = "0.1.0" | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is not meant for publication, we could remove the version, which then implies publish = false.
| authors = [ | ||
| "Philipp Schuster <phip1611@gmail.com>", | ||
| ] | ||
| resolver = "3" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is implied by edition = "2024". If this was intentional, you can leave it, of course.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah I think there was a weird situation in another project where this was needed. I'm not too sure about the context right now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe some situation with workspaces? I have not tested this myself, but the docs read like it should work to me. If you prefer being explicit, though, that's fine with me. 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would really appreciate some integration with embedded-io. This could also be an optional dependency if you prefer that.
The implementation could look something like this:
impl<B: Backend> embedded_io::ErrorType for Uart16550<B> {
type Error = Infallible;
}
impl<B: Backend> embedded_io::Write for Uart16550<B> {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
loop {
if let Ok(n) = self.try_send_bytes(buf) {
return Ok(n);
}
hint::spin_loop()
}
}
fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
impl<B: Backend> embedded_io::WriteReady for Uart16550<B> {
fn write_ready(&mut self) -> Result<bool, Self::Error> {
let lsr = self.lsr();
let msr = self.msr();
let mcr = self.mcr();
if !lsr.contains(LSR::THR_EMPTY) {
return Ok(false);
}
if !mcr.contains(MCR::LOOP_BACK) && !msr.contains(MSR::CTS) {
return Ok(false);
}
Ok(true)
}
}
impl<B: Backend> embedded_io::Read for Uart16550<B> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
loop {
let n = self.receive_bytes(buf);
if n > 0 {
return Ok(n);
}
hint::spin_loop();
}
}
}
impl<B: Backend> embedded_io::ReadReady for Uart16550<B> {
fn read_ready(&mut self) -> Result<bool, Self::Error> {
let lsr = self.lsr();
Ok(lsr.contains(LSR::DATA_READY))
}
}Let me know if you would rather
- integrate these changes into this PR yourselves,
- have me create a PR against your PR, or
- have me create a PR after your PR is merged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
great idea!
have me create a PR after your PR is merged.
would be better for reviewability I guess :)
| #![deny( | ||
| clippy::all, | ||
| clippy::cargo, | ||
| clippy::nursery, | ||
| clippy::must_use_candidate, | ||
| clippy::missing_safety_doc, | ||
| clippy::undocumented_unsafe_blocks, | ||
| clippy::needless_pass_by_value | ||
| )] | ||
| #![deny(missing_docs)] | ||
| #![deny(missing_debug_implementations)] | ||
| #![deny(rustdoc::all)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we make these warnings and enforce them in the CI? For me, development is easier if the code does compile, even if it is still imperfect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we make these warnings and enforce them in the CI? For me, development is easier if the code does compile, even if it is still imperfect.
Hm, matter of taste of course. :) I use this in all my projects and am very happy with that. I don't have a strong opinion tho
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you prefer this, this is fine to me. I will adjust accordingly. :D
Thanks :)
Awesome!
Sure! Please let me know what the best option is. |
TL;DR
As discussed [0], this is my 100% rewrite of the crate. It is 100% spec
complete, highly configurable (usable in kernel drivers) while still
preserving a very simple entry-level abstraction that "just works".
It also runs on real hardware now! The original code wasn't mature
enough for all the caveats. Further, we have one common abstraction for
x86 port I/O and MMIO.
Background
As part of a learning project, I conducted an in-depth exploration of
UART 16550 devices and driver development. During this process, I
identified a key limitation in the current crate: the inability to
configure the baud rate. This functionality is essential for real
hardware usage, and addressing it was my primary motivation.
As I continued, I recognized additional areas for improvement.
Incremental changes quickly proved insufficient to meet the standards
of code quality, completeness, and maintainability I aimed for.
Consequently, I undertook a full rewrite, resulting in a driver that
is fully spec-compliant while maintaining seamless and idiomatic Rust
integration.
While I could release this as a separate crate, I prefer not to fragment
the ecosystem - especially given that rust-osdev already maintains a
crate in this space. I fully acknowledge that this presents a "take it
or leave it" ("friss oder stirb") scenario 😞 . That said, I am eager to
collaborate toward a solution that serves the community effectively,
and I am confident this rewrite provides a strong, forward-looking
foundation.
Notes about Real Hardware Tests
To properly operate with real hardware, I had to specifically
investigate the behavior and especially adapt try_send_bytes()
and test_loopback().
Further, the source and the destination must agree on the baud
rate. Tests in VMs would behave differently (have fewer caveats),
especially as the baud rate is mostly irrelevant there.
About API and Implementation
Please look into
lib.rs, I suggest usingcargo doc. A small except from README is also here:Example (Minimalistic)
lib.rsExample (More low-level control)
lib.rsHints for Reviewers
I tested this on real hardware, in QEMU, and in Cloud Hypervisor. I
suggest checking this out and using
cargo doc --opento get a niceoverview.
I didn't use a LLM to write the code, only for this cover letter
(commit message) to improve it.
Steps to Undraft
[0] https://rust-osdev.zulipchat.com/#narrow/channel/426430-general/topic/uart.2016550.20.2F.20serial/near/566528969
Closes #37 #38 #30