Skip to content
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

A minimal Rust based sdmmc driver for odroidc4 #273

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open

Conversation

Cheng-Li1
Copy link

@Cheng-Li1 Cheng-Li1 commented Oct 21, 2024

This pull request integrates the SDMMC driver for Lions OS into the SDDF. The driver has been tested with the existing file system and is capable of performing read/write operations. It serves as a foundation for further protocol layer enhancements and performance optimizations.

Driver structure:
sdmmc_hal(SDMMC Hardware (Physical/Platform) Layer): Interacts with the specific hardware registers of the controller to send and receive commands and data. It is hardware-specific and may only be reusable with similar hardware platforms (e.g., hardware layer for odroid C4 may only be reused for other Amlogic devices).

sdmmc_protocol(SDMMC Protocol Layer): Implements the SD card and eMMC protocols, handling tasks such as card initialization, data transfer setup, and command management. It does not interact directly with hardware registers and can be reused across all platforms using SDMMC host controllers. However, it provides a framework of interfaces that a sdmmc_hal should implement. This protocol layer is written in safe Rust, ensuring memory safety without the use of unsafe blocks.

main.rs: Contains the event loop that orchestrates the driver, leveraging both sdmmc_hal and sdmmc_protocol. This is where OS-specific logic is integrated with the SDMMC driver.

Note:

  1. The SDMMC driver is functional and capable of block-level read/write operations. The protocol layer has been implemented minimally, providing the essential functionality required for data transfers. (not include card initialization, since without UHS-I support, resetting the card only results in slower speed)

  2. Future work depends on enabling voltage control for UHS-I speed modes, which requires a voltage regulator or GPIO driver. These components are not yet available, so the driver currently operates without UHS-I support.

  3. This pull request represents the first iteration of the SDMMC driver, with planned enhancements for improved protocol handling and performance optimization in upcoming versions.

@alwin-joshy
Copy link
Contributor

One quick thing, you should run cargo fmt on it.

Copy link
Contributor

@midnightveil midnightveil left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a review of just local behaviour.

Thoughts on overall design I'll do probably later but my thoughts is that this is a leaky abstraction and doesn't benefit from Async rust at all in its current state and would be easier without it - the part where you want to manually implement a future is where you are reading the done/not done registers, not in the big state machine where you want an async fn

pub async fn read_block(self, blockcnt: u32, start_idx: u64, destination: u64) -> (Result<(), SdmmcHalError>, Option<SdmmcProtocol<'a, T>>) {
let mut cmd: SdmmcCmd;
let mut res: Result<(), SdmmcHalError>;
// TODO: Figure out a way to support cards with 4 KB sector size
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They don't exist, only legacy SD cards support block sizes not 512; any recent (i.e. anything you can buy these days and probably the past however long) is mandated to only support 512. Refer to the SD card specification.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with that, the comment should be deleted as sdcard can support block size specified by user, except legacy sdcard.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other way around -- modern SD cards can only support 512 sectors, legacy ones can specify any between 1-2048 (depending on the card status info max & min read/write lengths)

image

image

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My mistake.

pub const MMC_RSP_R7: u32 = MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE;


/// Program async Rust can be very dangerous if you do not know what is happening understand the hood
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what the point of comments like this are.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remind myself what I am doing is how dangerous it is😅, should adjust that.

@@ -0,0 +1,3 @@
#![no_std] // Don't link the standard library
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is obvious.

const CLK_CO_PHASE_270: u32 = 3 << 8;
const CLK_TX_PHASE_000: u32 = 0 << 10;
const CLK_TX_PHASE_180: u32 = 2 << 10;
const CLK_ALWAYS_ON: u32 = 1 << 24;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the point of using rust with it's nice data type support (i.e. ability to write good versions of bitfield structs) and then just using raw bitshifts?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am planning to use some kind of bit list structure on the protocol layer. On the hardware layer, as I need to directly write to the hardware registers, I am wondering if it is necessary to rely on another crate for those low-level operations as the hardware layer is almost directly from uboot, would this bare metal way make it easier to move from legacy C to Rust?


// STATUS register masks and flags
const STATUS_MASK: u32 = 0xFFFF; // GENMASK(15, 0)
const STATUS_ERR_MASK: u32 = 0x1FFF; // GENMASK(12, 0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is surely less understandable than the C version... no?

(You could write a const fn that is the equivalent of GENMASK, or use full data type)


const INTERRUPT: sel4_microkit::Channel = sel4_microkit::Channel::new(1);

const SDCARD_SECTOR_SIZE: u32 = 512;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You've defined this twice?

let meson_hal: &mut MesonSdmmcRegisters = MesonSdmmcRegisters::new();
let mut protocol: SdmmcProtocol<'static, MesonSdmmcRegisters> = SdmmcProtocol::new(meson_hal);
let mut irq_to_enable = InterruptType::Success as u32 | InterruptType::Error as u32;
// Should always succeed, at least for odroid C4
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assert it?

// TODO: The MAX_BLOCK_PER_TRANSFER is got by hackily get the defines in hardware layer which is wrong, check that to get properly from protocol layer
request.count_to_do = core::cmp::min(request.count as u32, sdmmc_hal::meson_gx_mmc::MAX_BLOCK_PER_TRANSFER);
if let Some(sdmmc) = self.sdmmc.take() {
self.future = Some(Box::pin(sdmmc.write_block(request.count_to_do as u32, request.block_number as u64 + request.success_count as u64, request.io_or_offset + request.success_count as u64 * SDCARD_SECTOR_SIZE as u64)));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait why are we allocating in the command processing loop?

pub io_or_offset: u64,
pub block_number: u32,
pub count: u16,
// I suggest use u32 here and change the count to use u32 in sddf_blk
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 billion * 4096 bytes is a lot of data?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot 2024-10-22 123859
I think in more recent versions of sdcard and sd host, they try to support 32 bit block count? And setting the block count to 32 bit should not cause any issue for host or card that does not support 32 bit block count as well.


uint8_t blk_enqueue_resp_helper(uint8_t status, uint16_t success, uint32_t id) {
// It would be better if we do not use int but use int8_t
if (blk_enqueue_resp(queue_handle, status, success, id) == 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this do the same as the inner functions?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's most likely because bindgen can't convert the header and so we have to have some wrapper functions.

I'm not too much of a fan of this though, we should just be able to have an object that only includes <sddf/blk/queue.h> etc for the Rust bindings.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have not had much luck with header-only APIs and bindgen in the past.

@Cheng-Li1 did you try bindgen before adding these wrappers?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I admit I have never used this before. But I guess if I use this bindgen, Ineed to keep track of the queue_handle in the Rust code?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would be the problem with that?

@Cheng-Li1
Copy link
Author

This is a review of just local behaviour.

Thoughts on overall design I'll do probably later but my thoughts is that this is a leaky abstraction and doesn't benefit from Async rust at all in its current state and would be easier without it - the part where you want to manually implement a future is where you are reading the done/not done registers, not in the big state machine where you want an async fn

I don't agree with the suggestion to implement the entire state machine as the future. In Rust, a future is designed to return Poll::Pending or Poll::Ready(result), and once it returns a result, it is considered complete. Putting the logic of returning results inside state machine is too messy and make debugging much harder. My design should be easier to understand, debug, and add new features than a big state machine.

Comment on lines +180 to +186
if clock_freq > 16000000 {
clk = SD_EMMC_CLKSRC_DIV2;
clk_src = CLK_SRC_DIV2;
} else {
clk = SD_EMMC_CLKSRC_24M;
clk_src = CLK_SRC_24M;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Rust, if statements are expressions:

let (clk, clk_src) = if frequency > 16_000_000 {
  (SD_EMMC_CLKSRC_DIV2, CLK_SRC_DIV2)
} else {
  (SD_EMMC_CLKSRC_24M, CLK_SRC_24M)
};

@midnightveil
Copy link
Contributor

midnightveil commented Oct 21, 2024

I don't agree with the suggestion to implement the entire state machine as the future. In Rust, a future is designed to return Poll::Pending or Poll::Ready(result), and once it returns a result, it is considered complete. Putting the logic of returning results inside state machine is too messy and make debugging much harder. My design should be easier to understand, debug, and add new features than a big state machine.

If I'm not mistaken... this is what I was saying was bad? You'd written a state machine inside the future, rather than using the syntax sugar of async fn. The manual impl Future should only be necessary when you need to block waiting for the registers to change.

You've written one for the whole sending a command thing.

}

#[protection_domain(heap_size = 0x10000)]
fn init() -> HandlerImpl<'static, MesonSdmmcRegisters> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nowhere in this initialisation process do you actually follow the specification for initialising the SD card?

I guess if you assume that U-Boot has done so already, but this isn't always the case, especially on platforms like the iMX8 where it doesn't have to boot off the SD card (or has multiple MMCs - eMMC and SD card). My driver does this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is what I have said in the note, as I cannot control the voltage. I will not be able to init the card by myself. If someone boots the system from an eMMC but want to use this driver to use sdcard, it will not work because I do not have any way to control the voltage to boot the sdcard.

Copy link
Contributor

@midnightveil midnightveil Oct 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not talking about the voltages et al, I'm talking about the command sending flow.

There's a whole initialisation sequence for sending commands? Refer to my driver

Without it you can't initialise the SD layer on secondary MMC devices, even if the voltage is turned on (which at least for the imx8 it is).

(Also, hopefully this should support hotplug, where that's definitely necessary)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The init of the card is easy to implement, providing I can control the voltage in the hardware layer, which I cannot do right now. For reset, it is also easy to implement but unnecessary because the card is already in sdcard high speed mode, reset does not have any benefits as I know.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not talking about the voltages et al, I'm talking about the command sending flow.

There's a whole initialisation sequence for sending commands? Refer to my driver

Without it you can't initialise the SD layer on secondary MMC devices, even if the voltage is turned on (which at least for the imx8 it is).

(Also, hopefully this should support hotplug, where that's definitely necessary)

No need to worry about hotplugging right now. I am pretty sure it would be supported in the future. If people really want to have reset even when doing it makes no obvious benefits, I can implement that as well.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reset even when doing it makes no obvious benefits

We should not be relying on U-Boot to reset the device properly. We should be doing device reset ourselves.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, but let me provide a bit more context about this. The card setup function is always on my plan as to improve the read/write speed to above 100MB/s, a card setup would be definitely be needed. I was planning to add the card setup at the second iteration, when we have some way to change voltage. But since we do not want to completely rely on uboot set up the device for us, I can do it ahead. However, one thing to understand is that, whether I implement it or not we are all relying on uboot to set up the voltage, so we are still depending on uboot to set up the device, more or less.

@Cheng-Li1
Copy link
Author

Hello, I can see a lot of reviews are about the comments on the code, which I did not pay much attention to. I am fixing those now. Thanks for the input.

@Cheng-Li1
Copy link
Author

If I'm not mistaken... this is what I was saying was bad? You'd written a state machine inside the future, rather than using the syntax sugar of async fn. The manual impl Future should only be necessary when you need to block waiting for the registers to change.

You've written one for the whole sending a command thing.

I am a bit confused by your claim. As I know, one would need to implement a future object as the basic building block for other async functions that call it. I would hope for more clarification on this one. My understanding is, while you can build complex async functions by composing other async functions, at the end of the chain, there must be some base futures that are not created using async fn and await.

@midnightveil
Copy link
Contributor

midnightveil commented Oct 21, 2024

I am a bit confused by your claim. As I know, one would need to implement a future object as the basic building block for other async functions that call it. I would hope for more clarification on this one. My understanding is, while you can build complex async functions by composing other async functions, at the end of the chain, there must be some base futures that are not created using async fn and await.

Yes, but what I'm saying is you've implemented:

impl Future for SdMmcFuture {
	fn poll(...) {
        match state {
			StateA => {
				// do stuffA
			    return Pending;
            },
            StateB => {
                // do stuffB
                return Pending;
			},
			StateC => {
				// do stuffC
				return Done(error or OK)
			}
	}
}

When it would make more sense for

// these don't /really/ need to be async, but if you need to send CMD5 for APP_CMDs before..
async fn send_command() { ... }
async fn get_response() { ... }

struct IrqWaiter;
impl Future for IrqWater {
	fn poll() {
		if (look at registers show done) {
			return Done
		else if (show error) {
			return Done with error
		} else {
			return Pending;
		}
	}
}

fn wait_for_irq(...) {
	return IrqWaiter { };
}

async fn mmc_send_command(...) {
	send_command(...).await?;
    wait_for_irq().await?;
    get_response().await?;
}

This was roughly how my existing driver worked?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hope we don't need to store this in the repository, I'll talk to Nick but if we do it should not be in the driver code.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe in board folder?

$(CC) -c $(CFLAGS) $< -o $@

blk/sdmmc/meson/libsddfblk.a: blk/sdmmc/meson/sddf_helper.o |blk/sdmmc/meson
ar rcs $@ $<
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ar rcs $@ $<
$(AR) rcs $@ $<

const INTERRUPT: sel4_microkit::Channel = sel4_microkit::Channel::new(1);

const SDCARD_SECTOR_SIZE: u32 = 512;
const SDDF_TRANSFER_SIZE: u32 = 4096;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't be defined here, should be part of the bindings.

const SDDF_TRANSFER_SIZE: u32 = 4096;
const SDDF_TO_REAL_SECTOR: u32 = SDDF_TRANSFER_SIZE / SDCARD_SECTOR_SIZE;

const RETRY_CHANCE: u16 = 5;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain this retries thing?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When sdcard encounters an error, like time out, crc error or other kinds of error, the sdmmc driver should retry a few times before reporting back an error in case the error is only temporary. I think uboot does this as well.

@@ -15,3 +15,5 @@ ci_build/
.#*
zig-out/
.zig-cache/
target/
Cargo.lock
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should commit the lockfiles for reproducibility. Is that right @midnightveil ?

Copy link
Contributor

@midnightveil midnightveil Oct 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. Though if we end up with a lock file per example it might bloat the git history/reposize a lot.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this? Can we keep all the building to the Makefile?

Copy link
Contributor

@midnightveil midnightveil Oct 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the easiest way if we are building through cargo I believe (annoyingly), though you could build everything by invoking rustc directly (this would make it harder to pull in dependencies such as sel4-rust though)

https://public-docs.ferrocene.dev/main/user-manual/rustc/executable.html

(build.rs isn't the build script it just modifies how cargo builds. I think you can do most of this with environment variables but...)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the rust-sel4 examples I don't see a build.rs for every crate

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From memory when I used the rust-sel4 project, I don't think I needed a build.rs either

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect this is because Cheng wanted to link against the sddf helper wrappers -- It's only because it wants to modify the link paths to link against that binary.

I suspect most of the rust-sel4 projects don't explicitly link against C code in that way.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I had to do pretty much the same thing in SMOS when linking against the sDDF and couldn't find a better way of doing it. IMO it's not a big deal to have it.

@Ivan-Velickovic
Copy link
Collaborator

Hmm, doesn't seem to compile for me:

error: failed to run custom build command for `sel4-sys v0.1.0 (https://github.com/seL4/rust-sel4.git?rev=d3790bfd15512e18659f9491c319867fabf9552d#d3790bfd)`

Caused by:
  process didn't exit successfully: `/Users/ivanv/ts/sddf/examples/blk/build/blk/sdmmc/meson/debug/build/sel4-sys-08eeb9ebbd413fc3/build-script-main` (exit status: 101)
  --- stdout
  cargo:rerun-if-env-changed=SEL4_INCLUDE_DIRS
  cargo:rerun-if-changed=/Users/ivanv/ts/microkit-sdk-1.4.1/board/odroidc4/debug/include
  cargo:rerun-if-env-changed=SEL4_INCLUDE_DIRS
  cargo:rerun-if-changed=/Users/ivanv/ts/microkit-sdk-1.4.1/board/odroidc4/debug/include
  cargo:rerun-if-env-changed=SEL4_INCLUDE_DIRS
  cargo:rerun-if-changed=/Users/ivanv/ts/microkit-sdk-1.4.1/board/odroidc4/debug/include
  cargo:rerun-if-env-changed=SEL4_INCLUDE_DIRS
  cargo:rerun-if-changed=/Users/ivanv/ts/microkit-sdk-1.4.1/board/odroidc4/debug/include
  cargo:rerun-if-env-changed=SEL4_INCLUDE_DIRS
  cargo:rerun-if-changed=/Users/ivanv/ts/microkit-sdk-1.4.1/board/odroidc4/debug/include
  cargo:rerun-if-env-changed=SEL4_INCLUDE_DIRS
  cargo:rerun-if-changed=/Users/ivanv/ts/microkit-sdk-1.4.1/board/odroidc4/debug/include

  --- stderr
  thread 'main' panicked at /Users/ivanv/.cargo/git/checkouts/rust-sel4-a4edf3b9624837c3/d3790bf/crates/sel4/sys/build/c.rs:69:10:
  called `Result::unwrap()` on an `Err` value: ClangDiagnostic("error: version 'minimal' in target triple 'aarch64-sel4-microkit-minimal' is invalid\n")
  note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
make[1]: *** [sdmmc_driver.elf] Error 101
make: *** [/Users/ivanv/ts/sddf/examples/blk/build/loader.img] Error 2

@Cheng-Li1
Copy link
Author

called Result::unwrap() on an Err value: ClangDiagnostic("error: version 'minimal' in target triple 'aarch64-sel4-microkit-minimal' is invalid\n")

Hi, Ivan. It looks like the build script of sel4 crate pass target 'aarch64-sel4-microkit-minimal' (which is suppose to be the Rust program's target) to clang, which causes this error. Could you try building this example https://github.com/seL4/rust-microkit-demo to see if it builds? I want to narrow done the problem because the example builds on my machine.

@Cheng-Li1
Copy link
Author

When it would make more sense for

// these don't /really/ need to be async, but if you need to send CMD5 for APP_CMDs before..
async fn send_command() { ... }
async fn get_response() { ... }

struct IrqWaiter;
impl Future for IrqWater {
	fn poll() {
		if (look at registers show done) {
			return Done
		else if (show error) {
			return Done with error
		} else {
			return Pending;
		}
	}
}

fn wait_for_irq(...) {
	return IrqWaiter { };
}

async fn mmc_send_command(...) {
	send_command(...).await?;
    wait_for_irq().await?;
    get_response().await?;
}

Hello, let me summarise the suggestion in case there is any misunderstanding. So you suggest I adopt a different kind of base future design that is simpler and requires a function from the hardware layer that checks if a command has finished or not. Let me explain my thought: I think there is no need for a simpler base future as the current one that encapsulates send command and receive response logic is already simple enough. Right now, the receive_response function checks if the cmd has finished, if so return the result. There is no need to separate another function to specifically check if the cmd has finished or not. Personally, I think whether the future is designed in your proposed way or my way is more like a design choice and does not have much influence on performance or correctness.

@Cheng-Li1
Copy link
Author

Personally, I think whether the future is designed in your proposed way or my way is more like a design choice and does not have much influence on performance or correctness.

But I do agree to put the whole send cmd and receive response logic into a separate async function like fn send_cmd_and_receive_resp can make the code look better.

Copy link
Contributor

@alwin-joshy alwin-joshy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few inital comments. don't fully understand the async stuff yet so will need to play around with it a little.

edition = "2021"

[dependencies]
sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", rev = "d3790bfd15512e18659f9491c319867fabf9552d", package = "sel4-microkit" }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be pinned to a particular commit?

I see the advantage that it means it will build more deterministically, but if we do end up including the Carfo.lock, I think we should just point it to main so there's less chance it gets out of date.

Also, I think the package = "sel4-microkit" isn't needed.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right😂, package = "sel4-microkit" is not needed as cargo will look for the right package on its own. At that time when I am writing Cargo.toml I did not know.

// Specify the path where the C library is located
println!("cargo:rustc-link-search=native=./");

// Link the C library (static or dynamic). Adjust "static" or "dylib" as needed.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment could be improved

println!("cargo:rustc-link-lib=static=sddfblk");

// If you need to specify the include directory for C headers:
// println!("cargo:include=path/to/your/c/include");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just delete it if you don't need it.

sdmmc_driver.elf: $(BUILD_DIR) blk/sdmmc/meson/libsddfblk.a
cp blk/sdmmc/meson/libsddfblk.a $(abspath ${SDMMC_DRIVER_DIR})
@cd $(abspath ${SDMMC_DRIVER_DIR}) && \
echo "Building sdmmc_driver.elf for board $(MICROKIT_BOARD)..." && \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably don't want debug prints in the Make recipe

const IRQ_TXD_ERR: u32 = 1 << 8;
const IRQ_DESC_ERR: u32 = 1 << 9;
const IRQ_RESP_ERR: u32 = 1 << 10;
// Equivalent to (IRQ_RXD_ERR_MASK | IRQ_TXD_ERR | IRQ_DESC_ERR | IRQ_RESP_ERR)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary comment

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I had to do pretty much the same thing in SMOS when linking against the sDDF and couldn't find a better way of doing it. IMO it's not a big deal to have it.


/// Program async Rust can be very dangerous if you do not know what is happening understand the hood
/// Power up and power off cannot be properly implemented if I do not have access to control gpio/ regulator and timer
pub trait SdmmcHardware {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably so that a concrete implementation can implement a susbet of the full functionality of the trait, right?

Not clear if this is something that we actually want or not...

}

// TODO: Change it to use the sleep function provided by the hardware layer
let mut retry: u32 = 1000000;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a lot of retries... Is there a particular reason you chose this number?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a busy poll retry, we could poll infinitely if we trust the device to be correct. I will add a comment to explain this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not clear if this is something that we actually want or not...

From the designer's perspective, not all the functions defined in traits will need to be implemented for the driver to work. There will be a function to ask for the hardware layer to provide all of its abilities and the protocol layer to determine if those satisfy the minimal requirement to get the driver working and if so, protocol layer try to achieve the maximum speed possible based on the capabilities provided. I would hope to have more clarification on this one.

@midnightveil
Copy link
Contributor

Personally, I think whether the future is designed in your proposed way or my way is more like a design choice and does not have much influence on performance or correctness.

This is the whole point, to make a good design? (Which then should be performant and correct). There are a lot of ways to do things badly that can still be performant or appear correct.

@Cheng-Li1
Copy link
Author

This is the whole point, to make a good design? (Which then should be performant and correct). There are a lot of ways to do things badly that can still be performant or appear correct.

Exactly, I definitely do not tolerate any possible wrong code that appears right or designs that are simply bad or unscalable. I understand right now, despite my efforts, there are a lot of places in my code that may have such issues. I will keep working on those.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants