mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-08-16 17:00:35 +03:00
LibEDID: Fix handling extension maps
We weren't properly iterating the extension blocks and thought we encountered an unexpected extension map block, when we really should have just skipped over it.
This commit is contained in:
parent
18fc54fc34
commit
a821aa5f50
Notes:
sideshowbarker
2024-07-17 20:17:50 +09:00
Author: https://github.com/tomuta Commit: https://github.com/SerenityOS/serenity/commit/a821aa5f50e Pull-request: https://github.com/SerenityOS/serenity/pull/12116
@ -366,6 +366,148 @@ TEST_CASE(edid2)
|
||||
}
|
||||
}
|
||||
|
||||
// This EDID has extension maps
|
||||
static const u8 edid_extension_maps[] = {
|
||||
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x4d, 0x29, 0x48, 0x44,
|
||||
0x01, 0x00, 0x00, 0x00, 0x0a, 0x0d, 0x01, 0x03, 0x80, 0x50, 0x2d, 0x78,
|
||||
0x0a, 0x0d, 0xc9, 0xa0, 0x57, 0x47, 0x98, 0x27, 0x12, 0x48, 0x4c, 0x20,
|
||||
0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x1d, 0x80, 0x18, 0x71, 0x1c,
|
||||
0x16, 0x20, 0x58, 0x2c, 0x25, 0x00, 0x20, 0xc2, 0x31, 0x00, 0x00, 0x9e,
|
||||
0x8c, 0x0a, 0xd0, 0x8a, 0x20, 0xe0, 0x2d, 0x10, 0x10, 0x3e, 0x96, 0x00,
|
||||
0x13, 0x8e, 0x21, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x48,
|
||||
0x44, 0x4d, 0x49, 0x20, 0x54, 0x56, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b, 0x3d, 0x0f, 0x2e, 0x08, 0x02, 0x00,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x03, 0xf1, 0xf0, 0x02, 0x02, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x0c, 0x02, 0x03, 0x1e, 0xf1, 0x4a, 0x85, 0x04, 0x10,
|
||||
0x02, 0x01, 0x06, 0x14, 0x12, 0x16, 0x13, 0x23, 0x09, 0x07, 0x07, 0x83,
|
||||
0x01, 0x00, 0x00, 0x66, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x80, 0x01, 0x1d,
|
||||
0x00, 0x72, 0x51, 0xd0, 0x1e, 0x20, 0x6e, 0x28, 0x55, 0x00, 0xc4, 0x8e,
|
||||
0x21, 0x00, 0x00, 0x1e, 0xd6, 0x09, 0x80, 0xa0, 0x20, 0xe0, 0x2d, 0x10,
|
||||
0x10, 0x60, 0x22, 0x00, 0x12, 0x8e, 0x21, 0x08, 0x08, 0x18, 0x8c, 0x0a,
|
||||
0xd0, 0x90, 0x20, 0x40, 0x31, 0x20, 0x0c, 0x40, 0x55, 0x00, 0xc4, 0x8e,
|
||||
0x21, 0x00, 0x00, 0x18, 0x01, 0x1d, 0x80, 0xd0, 0x72, 0x1c, 0x16, 0x20,
|
||||
0x10, 0x2c, 0x25, 0x80, 0xc4, 0x8e, 0x21, 0x00, 0x00, 0x9e, 0x8c, 0x0a,
|
||||
0xa0, 0x14, 0x51, 0xf0, 0x16, 0x00, 0x26, 0x7c, 0x43, 0x00, 0x13, 0x8e,
|
||||
0x21, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5,
|
||||
0x02, 0x03, 0x04, 0xf1, 0xf3, 0x39, 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40,
|
||||
0x58, 0x2c, 0x45, 0x00, 0xc4, 0x8e, 0x21, 0x00, 0x00, 0x1e, 0x8c, 0x0a,
|
||||
0xa0, 0x20, 0x51, 0x20, 0x18, 0x10, 0x18, 0x7e, 0x23, 0x00, 0xc4, 0x8e,
|
||||
0x21, 0x00, 0x00, 0x98, 0x01, 0x1d, 0x00, 0xbc, 0x52, 0xd0, 0x1e, 0x20,
|
||||
0xb8, 0x28, 0x55, 0x40, 0xc4, 0x8e, 0x21, 0x00, 0x00, 0x1e, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf
|
||||
};
|
||||
|
||||
TEST_CASE(edid_extension_maps)
|
||||
{
|
||||
auto edid_load_result = EDID::Parser::from_bytes({ edid_extension_maps, sizeof(edid_extension_maps) });
|
||||
EXPECT(!edid_load_result.is_error());
|
||||
auto edid = edid_load_result.release_value();
|
||||
EXPECT(edid.legacy_manufacturer_id() == "SII");
|
||||
|
||||
{
|
||||
static constexpr struct {
|
||||
unsigned block_id;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned refresh_rate;
|
||||
} expected_detailed_timings[] = {
|
||||
{ 0, 1920, 1080, 60 },
|
||||
{ 0, 720, 480, 60 },
|
||||
{ 2, 1280, 720, 60 },
|
||||
{ 2, 640, 480, 60 },
|
||||
{ 2, 720, 576, 50 },
|
||||
{ 2, 1920, 1080, 50 },
|
||||
{ 2, 1440, 480, 60 },
|
||||
{ 3, 1920, 1080, 60 },
|
||||
{ 3, 1440, 576, 50 },
|
||||
{ 3, 1280, 720, 50 }
|
||||
};
|
||||
static constexpr size_t expected_detailed_timings_count = sizeof(expected_detailed_timings) / sizeof(expected_detailed_timings[0]);
|
||||
size_t detailed_timings_found = 0;
|
||||
auto result = edid.for_each_detailed_timing([&](auto& detailed_timing, unsigned block_id) {
|
||||
EXPECT(detailed_timings_found < expected_detailed_timings_count);
|
||||
auto& expected_timings = expected_detailed_timings[detailed_timings_found];
|
||||
EXPECT(block_id == expected_timings.block_id);
|
||||
EXPECT(detailed_timing.horizontal_addressable_pixels() == expected_timings.width);
|
||||
EXPECT(detailed_timing.vertical_addressable_lines() == expected_timings.height);
|
||||
EXPECT(detailed_timing.refresh_rate().lround() == expected_timings.refresh_rate);
|
||||
detailed_timings_found++;
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
EXPECT(!result.is_error());
|
||||
EXPECT(result.value() == IterationDecision::Continue);
|
||||
EXPECT(detailed_timings_found == expected_detailed_timings_count);
|
||||
}
|
||||
}
|
||||
|
||||
static const u8 edid_1_0[] = {
|
||||
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x34, 0x38, 0xc2, 0x0b,
|
||||
0x7b, 0x00, 0x00, 0x00, 0x0f, 0x0a, 0x01, 0x00, 0x28, 0x20, 0x18, 0x32,
|
||||
0xe8, 0x7e, 0x4e, 0x9e, 0x57, 0x45, 0x98, 0x24, 0x10, 0x47, 0x4f, 0xa4,
|
||||
0x42, 0x01, 0x31, 0x59, 0x45, 0x59, 0x61, 0x59, 0x71, 0x4f, 0x81, 0x80,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xf9, 0x15, 0x20, 0xf8, 0x30, 0x58,
|
||||
0x1f, 0x20, 0x20, 0x40, 0x13, 0x00, 0x40, 0xf0, 0x10, 0x00, 0x00, 0x1e,
|
||||
0xa4, 0x1a, 0x20, 0x10, 0x31, 0x58, 0x24, 0x20, 0x2f, 0x55, 0x33, 0x00,
|
||||
0x40, 0xf0, 0x10, 0x00, 0x00, 0x1e, 0x30, 0x2a, 0x00, 0x98, 0x51, 0x00,
|
||||
0x2a, 0x40, 0x30, 0x70, 0x13, 0x00, 0x40, 0xf0, 0x10, 0x00, 0x00, 0x1e,
|
||||
0xea, 0x24, 0x00, 0x60, 0x41, 0x00, 0x28, 0x30, 0x30, 0x60, 0x13, 0x00,
|
||||
0x40, 0xf0, 0x10, 0x00, 0x00, 0x1e, 0x00, 0x72
|
||||
};
|
||||
|
||||
TEST_CASE(edid_1_0)
|
||||
{
|
||||
auto edid_load_result = EDID::Parser::from_bytes({ edid_1_0, sizeof(edid_1_0) });
|
||||
EXPECT(!edid_load_result.is_error());
|
||||
auto edid = edid_load_result.release_value();
|
||||
EXPECT(edid.legacy_manufacturer_id() == "MAX");
|
||||
EXPECT(edid.serial_number() == 123);
|
||||
|
||||
{
|
||||
static constexpr struct {
|
||||
unsigned block_id;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned refresh_rate;
|
||||
} expected_detailed_timings[] = {
|
||||
{ 0, 800, 600, 85 },
|
||||
{ 0, 800, 600, 100 },
|
||||
{ 0, 1280, 1024, 60 },
|
||||
{ 0, 1024, 768, 85 }
|
||||
};
|
||||
static constexpr size_t expected_detailed_timings_count = sizeof(expected_detailed_timings) / sizeof(expected_detailed_timings[0]);
|
||||
size_t detailed_timings_found = 0;
|
||||
auto result = edid.for_each_detailed_timing([&](auto& detailed_timing, unsigned block_id) {
|
||||
EXPECT(detailed_timings_found < expected_detailed_timings_count);
|
||||
auto& expected_timings = expected_detailed_timings[detailed_timings_found];
|
||||
EXPECT(block_id == expected_timings.block_id);
|
||||
EXPECT(detailed_timing.horizontal_addressable_pixels() == expected_timings.width);
|
||||
EXPECT(detailed_timing.vertical_addressable_lines() == expected_timings.height);
|
||||
EXPECT(detailed_timing.refresh_rate().lround() == expected_timings.refresh_rate);
|
||||
detailed_timings_found++;
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
EXPECT(!result.is_error());
|
||||
EXPECT(result.value() == IterationDecision::Continue);
|
||||
EXPECT(detailed_timings_found == expected_detailed_timings_count);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(dmt_find_std_id)
|
||||
{
|
||||
auto* dmt = EDID::DMT::find_timing_by_std_id(0xd1, 0xf);
|
||||
|
@ -141,7 +141,7 @@ struct [[gnu::packed]] EDID {
|
||||
u8 checksum;
|
||||
};
|
||||
|
||||
enum ExtensionBlockTag : u8 {
|
||||
enum class ExtensionBlockTag : u8 {
|
||||
CEA_861 = 0x2,
|
||||
VideoTimingBlock = 0x10,
|
||||
DisplayInformation = 0x40,
|
||||
@ -220,8 +220,10 @@ public:
|
||||
ErrorOr<IterationDecision> for_each_dtd(Function<IterationDecision(Parser::DetailedTiming const&)> callback) const
|
||||
{
|
||||
u8 dtd_start = m_edid.read_host(&m_block->cea861extension.dtd_start_offset);
|
||||
if (dtd_start <= 4)
|
||||
if (dtd_start < 4) {
|
||||
// dtd_start == 4 means there are no data blocks, but there are still DTDs
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
|
||||
if (dtd_start > offsetof(Definitions::ExtensionBlock, checksum) - sizeof(Definitions::DetailedTiming))
|
||||
return Error::from_string_literal("CEA 861 extension block has invalid DTD list"sv);
|
||||
@ -488,35 +490,39 @@ ErrorOr<IterationDecision> Parser::for_each_extension_block(Function<IterationDe
|
||||
if (sizeof(Definitions::EDID) + (size_t)raw_extension_block_count * sizeof(Definitions::ExtensionBlock) > m_bytes.size())
|
||||
return Error::from_string_literal("Truncated EDID");
|
||||
|
||||
auto validate_block_checksum = [&](Definitions::ExtensionBlock const& extension_map) {
|
||||
auto validate_block_checksum = [&](Definitions::ExtensionBlock const& block) {
|
||||
u8 checksum = 0x0;
|
||||
auto* bytes = (u8 const*)&extension_map;
|
||||
for (size_t i = 0; i < sizeof(extension_map); i++)
|
||||
auto* bytes = (u8 const*)█
|
||||
for (size_t i = 0; i < sizeof(block); i++)
|
||||
checksum += bytes[i];
|
||||
|
||||
return checksum == 0;
|
||||
};
|
||||
|
||||
size_t offset = sizeof(Definitions::EDID);
|
||||
auto* raw_extension_blocks = (Definitions::ExtensionBlock const*)(m_bytes.data() + offset);
|
||||
auto* raw_extension_blocks = (Definitions::ExtensionBlock const*)(m_bytes.data() + sizeof(Definitions::EDID));
|
||||
Definitions::ExtensionBlock const* current_extension_map = nullptr;
|
||||
|
||||
unsigned raw_index = 0;
|
||||
if (m_revision <= 3) {
|
||||
if (raw_extension_block_count > 1) {
|
||||
current_extension_map = &raw_extension_blocks[0];
|
||||
if (read_host(¤t_extension_map->tag) != Definitions::ExtensionBlockTag::ExtensionBlockMap)
|
||||
raw_index++;
|
||||
if (read_host(¤t_extension_map->tag) != (u8)Definitions::ExtensionBlockTag::ExtensionBlockMap)
|
||||
return Error::from_string_literal("Did not find extension map at block 1"sv);
|
||||
if (!validate_block_checksum(*current_extension_map))
|
||||
return Error::from_string_literal("Extension block map checksum mismatch"sv);
|
||||
}
|
||||
} else if (read_host(&raw_extension_blocks[0].tag) == Definitions::ExtensionBlockTag::ExtensionBlockMap) {
|
||||
} else if (read_host(&raw_extension_blocks[0].tag) == (u8)Definitions::ExtensionBlockTag::ExtensionBlockMap) {
|
||||
current_extension_map = &raw_extension_blocks[0];
|
||||
raw_index++;
|
||||
}
|
||||
|
||||
for (unsigned raw_index = 0; raw_index < raw_extension_block_count; raw_index++) {
|
||||
for (; raw_index < raw_extension_block_count; raw_index++) {
|
||||
auto& raw_block = raw_extension_blocks[raw_index];
|
||||
u8 tag = read_host(&raw_block.tag);
|
||||
|
||||
if (current_extension_map && raw_index == 127) {
|
||||
if (tag != Definitions::ExtensionBlockTag::ExtensionBlockMap)
|
||||
if (tag != (u8)Definitions::ExtensionBlockTag::ExtensionBlockMap)
|
||||
return Error::from_string_literal("Did not find extension map at block 128"sv);
|
||||
current_extension_map = &raw_extension_blocks[127];
|
||||
if (!validate_block_checksum(*current_extension_map))
|
||||
@ -524,17 +530,16 @@ ErrorOr<IterationDecision> Parser::for_each_extension_block(Function<IterationDe
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tag == Definitions::ExtensionBlockTag::ExtensionBlockMap)
|
||||
if (tag == (u8)Definitions::ExtensionBlockTag::ExtensionBlockMap)
|
||||
return Error::from_string_literal("Unexpected extension map encountered"sv);
|
||||
|
||||
if (!validate_block_checksum(raw_block))
|
||||
return Error::from_string_literal("Extension block checksum mismatch"sv);
|
||||
|
||||
size_t offset = (u8 const*)&raw_block - m_bytes.data();
|
||||
IterationDecision decision = callback(raw_index + 1, tag, raw_block.block.revision, m_bytes.slice(offset, sizeof(Definitions::ExtensionBlock)));
|
||||
if (decision != IterationDecision::Continue)
|
||||
return decision;
|
||||
|
||||
offset += sizeof(Definitions::ExtensionBlock);
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
@ -1033,7 +1038,7 @@ ErrorOr<IterationDecision> Parser::for_each_detailed_timing(Function<IterationDe
|
||||
|
||||
Optional<Error> extension_error;
|
||||
auto result = for_each_extension_block([&](u8 block_id, u8 tag, u8, ReadonlyBytes bytes) {
|
||||
if (tag != Definitions::ExtensionBlockTag::CEA_861)
|
||||
if (tag != (u8)Definitions::ExtensionBlockTag::CEA_861)
|
||||
return IterationDecision::Continue;
|
||||
|
||||
CEA861ExtensionBlock cea861(*this, (Definitions::ExtensionBlock const*)bytes.data());
|
||||
@ -1077,7 +1082,7 @@ ErrorOr<IterationDecision> Parser::for_each_short_video_descriptor(Function<Iter
|
||||
{
|
||||
Optional<Error> extension_error;
|
||||
auto result = for_each_extension_block([&](u8 block_id, u8 tag, u8, ReadonlyBytes bytes) {
|
||||
if (tag != Definitions::ExtensionBlockTag::CEA_861)
|
||||
if (tag != (u8)Definitions::ExtensionBlockTag::CEA_861)
|
||||
return IterationDecision::Continue;
|
||||
|
||||
CEA861ExtensionBlock cea861(*this, (Definitions::ExtensionBlock const*)bytes.data());
|
||||
@ -1113,7 +1118,7 @@ ErrorOr<IterationDecision> Parser::for_each_display_descriptor(Function<Iteratio
|
||||
|
||||
Optional<Error> extension_error;
|
||||
auto result = for_each_extension_block([&](u8, u8 tag, u8, ReadonlyBytes bytes) {
|
||||
if (tag != Definitions::ExtensionBlockTag::CEA_861)
|
||||
if (tag != (u8)Definitions::ExtensionBlockTag::CEA_861)
|
||||
return IterationDecision::Continue;
|
||||
|
||||
CEA861ExtensionBlock cea861(*this, (Definitions::ExtensionBlock const*)bytes.data());
|
||||
|
Loading…
Reference in New Issue
Block a user