1 /*
2 * Copyright 2019-2022, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Augustin Cavalier <waddlesplash>
7 */
8
9 #include <debug.h>
10 #include <kernel/vm/vm.h>
11 #include <PCI.h>
12
13 extern "C" {
14 #include "nvme.h"
15 #include "nvme_log.h"
16 #include "nvme_mem.h"
17 #include "nvme_pci.h"
18 }
19
20
21 static pci_module_info* sPCIModule = NULL;
22
23
24 // #pragma mark - memory
25
26
27 int
nvme_mem_init()28 nvme_mem_init()
29 {
30 /* nothing to do */
31 return 0;
32 }
33
34
35 void
nvme_mem_cleanup()36 nvme_mem_cleanup()
37 {
38 /* nothing to do */
39 }
40
41
42 void*
nvme_mem_alloc_node(size_t size,size_t align,unsigned int node_id,phys_addr_t * paddr)43 nvme_mem_alloc_node(size_t size, size_t align, unsigned int node_id,
44 phys_addr_t* paddr)
45 {
46 size = ROUNDUP(size, B_PAGE_SIZE);
47
48 virtual_address_restrictions virtualRestrictions = {};
49
50 physical_address_restrictions physicalRestrictions = {};
51 physicalRestrictions.alignment = align;
52
53 void* address;
54 area_id area = create_area_etc(B_SYSTEM_TEAM, "nvme physical buffer",
55 size, B_CONTIGUOUS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
56 0, 0, &virtualRestrictions, &physicalRestrictions, &address);
57 if (area < 0)
58 return NULL;
59
60 if (paddr != NULL)
61 *paddr = nvme_mem_vtophys(address);
62 return address;
63 }
64
65
66 void*
nvme_malloc_node(size_t size,size_t align,unsigned int node_id)67 nvme_malloc_node(size_t size, size_t align, unsigned int node_id)
68 {
69 return nvme_mem_alloc_node(size, align, node_id, NULL);
70 }
71
72
73 void
nvme_free(void * addr)74 nvme_free(void* addr)
75 {
76 delete_area(area_for(addr));
77 }
78
79
80 phys_addr_t
nvme_mem_vtophys(void * vaddr)81 nvme_mem_vtophys(void* vaddr)
82 {
83 physical_entry entry;
84 status_t status = get_memory_map((void*)vaddr, 1, &entry, 1);
85 if (status != B_OK) {
86 panic("nvme: get_memory_map failed for %p: %s\n",
87 (void*)vaddr, strerror(status));
88 return NVME_VTOPHYS_ERROR;
89 }
90
91 return entry.address;
92 }
93
94
95 // #pragma mark - PCI
96
97
98 int
nvme_pci_init()99 nvme_pci_init()
100 {
101 status_t status = get_module(B_PCI_MODULE_NAME,
102 (module_info**)&sPCIModule);
103 return status;
104 }
105
106
107 int
nvme_pcicfg_read32(struct pci_device * dev,uint32_t * value,uint32_t offset)108 nvme_pcicfg_read32(struct pci_device* dev, uint32_t* value, uint32_t offset)
109 {
110 *value = sPCIModule->read_pci_config(dev->bus, dev->dev, dev->func, offset,
111 sizeof(*value));
112 return 0;
113 }
114
115
116 int
nvme_pcicfg_write32(struct pci_device * dev,uint32_t value,uint32_t offset)117 nvme_pcicfg_write32(struct pci_device* dev, uint32_t value, uint32_t offset)
118 {
119 sPCIModule->write_pci_config(dev->bus, dev->dev, dev->func, offset,
120 sizeof(value), value);
121 return 0;
122 }
123
124
125 void
nvme_pcicfg_get_bar_addr_len(void * devhandle,unsigned int bar,uint64_t * _addr,uint64_t * _size)126 nvme_pcicfg_get_bar_addr_len(void* devhandle, unsigned int bar,
127 uint64_t* _addr, uint64_t* _size)
128 {
129 struct pci_device* dev = (struct pci_device*)devhandle;
130 pci_info* info = (pci_info*)dev->pci_info;
131
132 uint64 addr = info->u.h0.base_registers[bar];
133 uint64 size = info->u.h0.base_register_sizes[bar];
134 if ((info->u.h0.base_register_flags[bar] & PCI_address_type) == PCI_address_type_64) {
135 addr |= (uint64)info->u.h0.base_registers[bar + 1] << 32;
136 size |= (uint64)info->u.h0.base_register_sizes[bar + 1] << 32;
137 }
138
139 *_addr = addr;
140 *_size = size;
141 }
142
143
144 int
nvme_pcicfg_map_bar(void * devhandle,unsigned int bar,bool read_only,void ** mapped_addr)145 nvme_pcicfg_map_bar(void* devhandle, unsigned int bar, bool read_only,
146 void** mapped_addr)
147 {
148 uint64 addr, size;
149 nvme_pcicfg_get_bar_addr_len(devhandle, bar, &addr, &size);
150
151 area_id area = map_physical_memory("nvme mapped bar", (phys_addr_t)addr, (size_t)size,
152 B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | (read_only ? 0 : B_KERNEL_WRITE_AREA),
153 mapped_addr);
154 if (area < B_OK)
155 return area;
156
157 return 0;
158 }
159
160
161 int
nvme_pcicfg_map_bar_write_combine(void * devhandle,unsigned int bar,void ** mapped_addr)162 nvme_pcicfg_map_bar_write_combine(void* devhandle, unsigned int bar,
163 void** mapped_addr)
164 {
165 status_t status = nvme_pcicfg_map_bar(devhandle, bar, false, mapped_addr);
166 if (status != 0)
167 return status;
168
169 // Turn on write combining for the area
170 status = vm_set_area_memory_type(area_for(*mapped_addr),
171 nvme_mem_vtophys(*mapped_addr), B_WRITE_COMBINING_MEMORY);
172 if (status != 0)
173 nvme_pcicfg_unmap_bar(devhandle, bar, *mapped_addr);
174 return status;
175 }
176
177
178 int
nvme_pcicfg_unmap_bar(void * devhandle,unsigned int bar,void * addr)179 nvme_pcicfg_unmap_bar(void* devhandle, unsigned int bar, void* addr)
180 {
181 return delete_area(area_for(addr));
182 }
183
184
185 // #pragma mark - logging
186
187
188 void
nvme_log(enum nvme_log_level level,const char * format,...)189 nvme_log(enum nvme_log_level level, const char *format, ...)
190 {
191 va_list ap;
192
193 va_start(ap, format);
194 nvme_vlog(level, format, ap);
195 va_end(ap);
196 }
197
198
199 void
nvme_vlog(enum nvme_log_level level,const char * format,va_list ap)200 nvme_vlog(enum nvme_log_level level, const char *format, va_list ap)
201 {
202 dvprintf(format, ap);
203 }
204