Serenity Operating System
1/*
2 * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <AK/Types.h>
8#include <LibTest/TestCase.h>
9#include <assert.h>
10#include <errno.h>
11#include <fcntl.h>
12#include <inttypes.h>
13#include <stdio.h>
14#include <string.h>
15#include <sys/mman.h>
16#include <sys/stat.h>
17#include <unistd.h>
18
19static u8 read_buffer[0x100000];
20
21static ALWAYS_INLINE bool mem_chunk(int fd, u64 base, u64 length)
22{
23 u64 mmoffset = base % sysconf(_SC_PAGESIZE);
24 void* mmp = mmap(NULL, mmoffset + length, PROT_READ, MAP_SHARED, fd, base - mmoffset);
25 if (mmp == MAP_FAILED)
26 return false;
27 if (munmap(mmp, mmoffset + length) < 0)
28 perror("munmap");
29 return true;
30}
31
32enum class ReadResult {
33 SeekFailure,
34 ReadFailure,
35 ReadSuccess,
36};
37
38static ALWAYS_INLINE ReadResult read_chunk(int fd, u64 base, u64 length)
39{
40 VERIFY(length <= sizeof(read_buffer));
41 auto rs = lseek(fd, base, SEEK_SET);
42 if (rs < 0) {
43 fprintf(stderr, "Couldn't seek to offset %" PRIi64 " while verifying: %s\n", base, strerror(errno));
44 return ReadResult::SeekFailure;
45 }
46 if (read(fd, read_buffer, length) < 0)
47 return ReadResult::ReadFailure;
48 return ReadResult::ReadSuccess;
49}
50
51TEST_CASE(test_memory_access_device_read)
52{
53 int rc = geteuid();
54 EXPECT_EQ(rc, 0);
55
56 int fd = open("/dev/mem", O_RDONLY);
57 EXPECT(fd >= 0);
58
59 // FIXME: This is expected to work on QEMU machines (both 440FX and Q35),
60 // however, it will be much nicer to have some sort of a node in the ProcFS
61 // to expose physical memory ranges (e820 memory map).
62
63 auto read_result = read_chunk(fd, 0x0, 0x100000);
64 EXPECT_EQ(read_result, ReadResult::ReadFailure);
65
66 read_result = read_chunk(fd, 0xe0000, 0x100000 - 0xe0000);
67 EXPECT_EQ(read_result, ReadResult::ReadSuccess);
68
69 read_result = read_chunk(fd, 0x100000, 0x200000 - 0x100000);
70 EXPECT_EQ(read_result, ReadResult::ReadFailure);
71
72 read_result = read_chunk(fd, 0xf0000, 70000);
73 EXPECT_EQ(read_result, ReadResult::ReadFailure);
74
75 read_result = read_chunk(fd, 0xfffc0000, 16384);
76 EXPECT_EQ(read_result, ReadResult::ReadSuccess);
77
78 read_result = read_chunk(fd, 0xfffc0000, 0x100000);
79 EXPECT_EQ(read_result, ReadResult::ReadFailure);
80}
81
82TEST_CASE(test_memory_access_device_mmap)
83{
84 int rc = geteuid();
85 EXPECT_EQ(rc, 0);
86
87 int fd = open("/dev/mem", O_RDONLY);
88 EXPECT(fd >= 0);
89
90 // FIXME: This is expected to work on QEMU machines (both 440FX and Q35),
91 // however, it will be much nicer to have some sort of a node in the ProcFS
92 // to expose physical memory ranges (e820 memory map).
93
94 auto mmap_result = mem_chunk(fd, 0xe0000, 0x100000 - 0xe0000);
95 EXPECT_EQ(mmap_result, true);
96
97 mmap_result = mem_chunk(fd, 0x100000, 0x200000 - 0x100000);
98 EXPECT_EQ(mmap_result, false);
99
100 mmap_result = mem_chunk(fd, 0xf0000, 70000);
101 EXPECT_EQ(mmap_result, false);
102
103 mmap_result = mem_chunk(fd, 0xfffc0000, 16384);
104 EXPECT_EQ(mmap_result, true);
105
106 mmap_result = mem_chunk(fd, 0xfffc0000, 0x100000);
107 EXPECT_EQ(mmap_result, false);
108}