-
-
Notifications
You must be signed in to change notification settings - Fork 79
Implement Index and Bank fields #274
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
Open
martin-hughes
wants to merge
2
commits into
rust-osdev:main
Choose a base branch
from
martin-hughes:issue-212-no-tests
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1374,6 +1374,33 @@ where | |
| let (_, data) = namespace.search(&data_name, &context.current_scope)?; | ||
| (index, data) | ||
| }; | ||
|
|
||
| if let Object::FieldUnit(ref data_fu) = *data { | ||
| if data_fu.flags.access_type_bytes()? < FieldFlags(field_flags).access_type_bytes()? { | ||
| // On ACPICA this causes reads/writes to be truncated to the width of | ||
| // the data register. | ||
| // | ||
| // The issue comes from this part of the spec: | ||
| // "The value written to the IndexName register is defined to be a byte | ||
| // offset that is aligned on an AccessType boundary." | ||
| // | ||
| // Consider the case where the index field is WordAcc but the data | ||
| // register is ByteAcc. Only even numbers could be written to the index | ||
| // register - multiples of width of word (2 bytes). But to access the | ||
| // high byte of the word for the field, we'd need to write an odd number | ||
| // to the index register. Which is not compatible with the spec. | ||
| // | ||
| // The ASL writer shouldn't have allowed this. And it seems that most | ||
| // uses of IndexField are ByteAcc all around. So whatever strange | ||
| // behaviour we allow is probably OK. | ||
| // | ||
| // But warn the user, just in case. | ||
| warn!("Data field access width is smaller than normal field width at {:?}", start_pc); | ||
| } | ||
| } else { | ||
| warn!("Wrong data field type in IndexField: {:?}", data); | ||
| }; | ||
|
|
||
| let kind = FieldUnitKind::Index { index, data }; | ||
| self.parse_field_list(&mut context, kind, start_pc, pkg_length, field_flags)?; | ||
| } | ||
|
|
@@ -2374,17 +2401,19 @@ where | |
| Output::Integer(value) => value, | ||
| }; | ||
|
|
||
| let read_region = match field.kind { | ||
| FieldUnitKind::Normal { ref region } => region, | ||
| // FieldUnitKind::Bank { ref region, ref bank, bank_value } => { | ||
| FieldUnitKind::Bank { .. } => { | ||
| // TODO: put the bank_value in the bank | ||
| todo!(); | ||
| let (read_region, index_field_idx) = match field.kind { | ||
| FieldUnitKind::Normal { ref region } => (region, 0), | ||
| FieldUnitKind::Bank { ref region, ref bank, bank_value } => { | ||
| let Object::FieldUnit(ref bank) = **bank else { panic!() }; | ||
| assert!(matches!(bank.kind, FieldUnitKind::Normal { .. })); | ||
| self.do_field_write(bank, Object::Integer(bank_value).wrap())?; | ||
| (region, 0) | ||
| } | ||
| // FieldUnitKind::Index { ref index, ref data } => { | ||
| FieldUnitKind::Index { .. } => { | ||
| // TODO: configure the correct index | ||
| todo!(); | ||
| FieldUnitKind::Index { index: _, ref data } => { | ||
| let Object::FieldUnit(ref data) = **data else { panic!() }; | ||
| let FieldUnitKind::Normal { region } = &data.kind else { panic!() }; | ||
| let reg_idx = field.bit_index / 8; | ||
| (region, reg_idx) | ||
| } | ||
| }; | ||
| let Object::OpRegion(ref read_region) = **read_region else { panic!() }; | ||
|
|
@@ -2404,7 +2433,27 @@ where | |
| / access_width_bits; | ||
| let mut read_so_far = 0; | ||
| for i in 0..native_accesses_needed { | ||
| let aligned_offset = object::align_down(field.bit_index + i * access_width_bits, access_width_bits); | ||
| // Advance the read pointer. For Index fields, this also means updating the Index | ||
| // register. | ||
| let aligned_offset = match field.kind { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This block isn't ideal - but the alternatives didn't seem great so I stuck with this slightly awkward code. I considered:
Ultimately I don't think these functions are going to be extended much in future, so there was no need to go for a gold-plated solution. |
||
| FieldUnitKind::Normal { .. } | FieldUnitKind::Bank { .. } => { | ||
| object::align_down(field.bit_index + i * access_width_bits, access_width_bits) | ||
| } | ||
| FieldUnitKind::Index { ref index, ref data } => { | ||
| // Update index register | ||
| let Object::FieldUnit(ref index) = **index else { panic!() }; | ||
| let Object::FieldUnit(ref data) = **data else { panic!() }; | ||
| self.do_field_write( | ||
| index, | ||
| Object::Integer((index_field_idx + i * (access_width_bits / 8)) as u64).wrap(), | ||
| )?; | ||
|
|
||
| // The offset is always that of the data register, as we always read from the | ||
| // base of the data register. | ||
| data.bit_index | ||
| } | ||
| }; | ||
|
|
||
| let raw = self.do_native_region_read(read_region, aligned_offset / 8, access_width_bits / 8)?; | ||
| let src_index = if i == 0 { field.bit_index % access_width_bits } else { 0 }; | ||
| let remaining_length = field.bit_length - read_so_far; | ||
|
|
@@ -2434,17 +2483,23 @@ where | |
| }; | ||
| let access_width_bits = field.flags.access_type_bytes()? * 8; | ||
|
|
||
| let write_region = match field.kind { | ||
| FieldUnitKind::Normal { ref region } => region, | ||
| // FieldUnitKind::Bank { ref region, ref bank, bank_value } => { | ||
| FieldUnitKind::Bank { .. } => { | ||
| // TODO: put the bank_value in the bank | ||
| todo!(); | ||
| // In this tuple: | ||
| // - write_region is the region that the data will be written to. | ||
| // - index_field_idx is the initial index to write into the Index register of an index | ||
| // field. For all other field types it is unused and set to zero. | ||
| let (write_region, index_field_idx) = match field.kind { | ||
| FieldUnitKind::Normal { ref region } => (region, 0), | ||
| FieldUnitKind::Bank { ref region, ref bank, bank_value } => { | ||
| let Object::FieldUnit(ref bank) = **bank else { panic!() }; | ||
| assert!(matches!(bank.kind, FieldUnitKind::Normal { .. })); | ||
| self.do_field_write(bank, Object::Integer(bank_value).wrap())?; | ||
| (region, 0) | ||
| } | ||
| // FieldUnitKind::Index { ref index, ref data } => { | ||
| FieldUnitKind::Index { .. } => { | ||
| // TODO: configure the correct index | ||
| todo!(); | ||
| FieldUnitKind::Index { index: _, ref data } => { | ||
| let Object::FieldUnit(ref data) = **data else { panic!() }; | ||
| let FieldUnitKind::Normal { region: data_region } = &data.kind else { panic!() }; | ||
| let reg_idx = field.bit_index / 8; | ||
| (data_region, reg_idx) | ||
| } | ||
| }; | ||
| let Object::OpRegion(ref write_region) = **write_region else { panic!() }; | ||
|
|
@@ -2459,7 +2514,26 @@ where | |
| let mut written_so_far = 0; | ||
|
|
||
| for i in 0..native_accesses_needed { | ||
| let aligned_offset = object::align_down(field.bit_index + i * access_width_bits, access_width_bits); | ||
| // Advance the write pointer... For normal and bank fields this is straightforward. For | ||
| // Index fields, this involves updating the index register. | ||
| let aligned_offset = match field.kind { | ||
| FieldUnitKind::Normal { .. } | FieldUnitKind::Bank { .. } => { | ||
| object::align_down(field.bit_index + i * access_width_bits, access_width_bits) | ||
| } | ||
| FieldUnitKind::Index { ref index, ref data } => { | ||
| // Update index register | ||
| let Object::FieldUnit(ref index) = **index else { panic!() }; | ||
| let Object::FieldUnit(ref data) = **data else { panic!() }; | ||
| self.do_field_write( | ||
| index, | ||
| Object::Integer((index_field_idx + i * (access_width_bits / 8)) as u64).wrap(), | ||
| )?; | ||
|
|
||
| // The offset is always that of the data register, as we always read from the | ||
| // base of the data register. | ||
| data.bit_index | ||
| } | ||
| }; | ||
| let dst_index = if i == 0 { field.bit_index % access_width_bits } else { 0 }; | ||
|
|
||
| /* | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| // Test operations on "Bank" fields | ||
|
|
||
| use aml_test_tools::handlers::std_test_handler::{ | ||
| Command, | ||
| construct_std_handler, | ||
| create_mutex, | ||
| read_u16, | ||
| write_u8, | ||
| write_u16, | ||
| }; | ||
|
|
||
| mod test_infra; | ||
|
|
||
| #[test] | ||
| fn test_basic_bank_store_and_load() { | ||
| // This is a straightforward test of banked fields. | ||
| // Internally: Apart from setting the bank index beforehand, the field read/write is identical | ||
| // to normal fields. So this test is probably sufficient testing of banked fields. | ||
| const AML: &str = r#"DefinitionBlock("%FN%", "DSDT", 1, "RSACPI", "BNKFLD", 1) { | ||
| OperationRegion(MEM, SystemMemory, 0x40000, 0x1000) | ||
| Field(MEM, ByteAcc, NoLock, Preserve) { | ||
| INDX, 8 | ||
| } | ||
|
|
||
| BankField (MEM, INDX, 0, WordAcc, NoLock, Preserve) { | ||
| OFFSET (0x02), // Prevent aliasing INDX | ||
| A, 16, | ||
| B, 16, | ||
| C, 16 | ||
| } | ||
|
|
||
| BankField (MEM, INDX, 1, WordAcc, NoLock, Preserve) { | ||
| OFFSET (0x02), | ||
| D, 16, | ||
| E, 16, | ||
| F, 16 | ||
| } | ||
|
|
||
| Method(MAIN, 0, NotSerialized) { | ||
| C = 0xA55A | ||
| D = C | ||
| E = 0x5AA5 | ||
| A = E | ||
| Return (0) | ||
| } | ||
| } | ||
| "#; | ||
|
|
||
| const EXPECTED_COMMANDS: &[Command] = &[ | ||
| create_mutex(), | ||
| // C = 0xA55A | ||
| write_u8(0x40000, 0), // Select bank 0. | ||
| write_u16(0x40006, 0xA55A), // Write the value to C. | ||
| // D = C | ||
| write_u8(0x40000, 0), // Select bank 0. | ||
| read_u16(0x40006, 0xA55A), // Read the value from C. | ||
| write_u8(0x40000, 1), // Select bank 1. | ||
| write_u16(0x40002, 0xA55A), // Write the value to D. | ||
| // E = 0x5AA5 | ||
| write_u8(0x40000, 1), // Select bank 1. | ||
| write_u16(0x40004, 0x5AA5), // Write the value to E. | ||
| // A = E | ||
| write_u8(0x40000, 1), // Select bank 1. | ||
| read_u16(0x40004, 0x5AA5), // Read from E | ||
| write_u8(0x40000, 0), // Select bank 0. | ||
| write_u16(0x40002, 0x5AA5), // Write the value to A. | ||
| ]; | ||
|
|
||
| let handler = construct_std_handler(EXPECTED_COMMANDS.to_vec()); | ||
| test_infra::run_aml_test(AML, handler); | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 wondered whether to just
panic!, but this weird case runs in ACPICA, it just gives odd results. Since the supposed "reference implementation" supports itacpiought to as well, but it's weird and unlikely enough it's I felt it worth a warning.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.
supportspermits