1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "dm/dmrpc.h"
4#include "known_devices.hpp"
5#include "pci_scan.hpp"
6
7#include <fcntl.h>
8#include <librpc/rpc_client.h>
9#include <mos/mm/mm_types.h>
10#include <mos/syscall/usermode.h>
11#include <mos/types.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <sys/mman.h>
16#include <sys/stat.h>
17#include <unistd.h>
18
19#define DEBUG 0
20
21// clang-format off
22#define debug_printf(fmt, ...) do { if (DEBUG) fprintf(stderr, fmt __VA_OPT__(,) __VA_ARGS__); } while (0)
23// clang-format on
24
25ptr_t mmio_base;
26static rpc_server_stub_t *dm;
27
28RPC_DECLARE_CLIENT(dm, DEVICE_MANAGER_RPCS_X)
29
30static void scan_callback(u8 bus, u8 device, u8 function, u16 vendor_id, u16 device_id, u8 base_class, u8 sub_class, u8 prog_if)
31{
32 const auto class_name = get_known_class_name(base_class, sub_class, prog_if);
33 debug_printf("PCI: %02x:%02x.%01x: [%04x:%04x] %s (%02x:%02x:%02x)\n", bus, device, function, vendor_id, device_id, class_name.c_str(), base_class, sub_class,
34 prog_if);
35
36 dm_register_device(server_stub: dm, vendorid: vendor_id, deviceid: device_id, busid: bus, devid: device, funcid: function, mmio_base);
37}
38
39typedef struct
40{
41 u64 base_address;
42 u16 segment_group_number;
43 u8 start_pci_bus_number;
44 u8 end_pci_bus_number;
45 u32 reserved;
46} __packed acpi_mcfg_base_addr_alloc_t;
47
48typedef struct
49{
50 char signature[4];
51 u32 length;
52 u8 revision;
53 u8 checksum;
54 char oem_id[6];
55 char oem_table_id[8];
56 u32 oem_revision;
57 u32 creator_id;
58 u32 creator_revision;
59 u8 reserved[8];
60 char content[];
61} acpi_mcfg_header_t;
62
63static const acpi_mcfg_header_t *mcfg_table;
64static const acpi_mcfg_base_addr_alloc_t *base_addr_alloc;
65static size_t n_base_addr_alloc;
66
67static bool read_mcfg_table(void)
68{
69 int fd = open(path: "/sys/acpi/MCFG", O_RDONLY);
70 if (fd < 0)
71 {
72 puts(string: "pci-daemon: failed to open /sys/acpi/MCFG");
73 return false;
74 }
75
76 mcfg_table = (acpi_mcfg_header_t *) mmap(NULL, size: 4 KB, PROT_READ, MAP_PRIVATE, fd: fd, offset: 0);
77 base_addr_alloc = (acpi_mcfg_base_addr_alloc_t *) &mcfg_table->content[0];
78 close(fd: fd);
79
80 n_base_addr_alloc = (mcfg_table->length - sizeof(acpi_mcfg_header_t)) / sizeof(acpi_mcfg_base_addr_alloc_t);
81
82 for (size_t i = 0; i < n_base_addr_alloc; i++)
83 {
84 const acpi_mcfg_base_addr_alloc_t *alloc = &base_addr_alloc[i];
85 debug_printf("pci-daemon: MCFG table: base_address=%llx\n", alloc->base_address);
86 debug_printf("pci-daemon: MCFG table: segment_group_number=%x\n", alloc->segment_group_number);
87 debug_printf("pci-daemon: MCFG table: start_pci_bus_number=%x\n", alloc->start_pci_bus_number);
88 debug_printf("pci-daemon: MCFG table: end_pci_bus_number=%x\n", alloc->end_pci_bus_number);
89 debug_printf("pci-daemon: MCFG table: reserved=%x\n", alloc->reserved);
90 }
91
92 if (n_base_addr_alloc > 1)
93 {
94 fputs(string: "pci-daemon: MCFG table: multiple base address allocators are not supported", stream: stderr);
95 return false;
96 }
97
98 mmio_base = base_addr_alloc->base_address;
99
100 // memory range
101 const ptr_t start = base_addr_alloc->base_address;
102 const ptr_t end = start + ((u32) base_addr_alloc->end_pci_bus_number - base_addr_alloc->start_pci_bus_number + 1) * 4 KB;
103 debug_printf("pci-daemon: PCI memory range: " PTR_FMT "-" PTR_FMT "\n", start, end);
104
105 // map the PCI memory range
106 const u64 size = ALIGN_UP_TO_PAGE(end - start);
107
108 fd_t memfd = open(path: "/sys/mem", O_RDWR);
109 if (memfd < 0)
110 {
111 puts(string: "pci-daemon: failed to open /sys/mem");
112 return false;
113 }
114
115 void *addr = mmap(addr: (void *) start, size: size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd: memfd, offset: start);
116 close(fd: memfd);
117
118 MOS_UNUSED(addr);
119
120 return true;
121}
122
123int main(int argc, char **argv)
124{
125 MOS_UNUSED(argc);
126 MOS_UNUSED(argv);
127
128 if (!read_mcfg_table())
129 {
130 puts(string: "pci-daemon: failed to read MCFG table");
131 return 1;
132 }
133
134 dm = rpc_client_create(MOS_DEVICE_MANAGER_SERVICE_NAME);
135 if (!dm)
136 {
137 printf(format: "pci-daemon: failed to connect to device manager\n");
138 return 1;
139 }
140
141 syscall_arch_syscall(nr: X86_SYSCALL_IOPL_ENABLE, arg1: 0, arg2: 0, arg3: 0, arg4: 0);
142 scan_pci(callback: scan_callback);
143
144 return 0;
145}
146