xref: /DragonOS/kernel/src/common/glib.h (revision 77799ccaaca276fe127448d169f0e035837cce44)
1 //
2 // 内核全局通用库
3 // Created by longjin on 2022/1/22.
4 //
5 
6 #pragma once
7 
8 // 引入对bool类型的支持
9 #include <stdbool.h>
10 #include <DragonOS/stdint.h>
11 #include <common/stddef.h>
12 #include <arch/arch.h>
13 #include <common/compiler.h>
14 #include <common/list.h>
15 
16 #include <asm/asm.h>
17 
18 /**
19  * @brief 根据结构体变量内某个成员变量member的基地址,计算出该结构体变量的基地址
20  * @param ptr 指向结构体变量内的成员变量member的指针
21  * @param type 成员变量所在的结构体
22  * @param member 成员变量名
23  *
24  * 方法:使用ptr减去结构体内的偏移,得到结构体变量的基地址
25  */
26 #define container_of(ptr, type, member)                                     \
27     ({                                                                      \
28         typeof(((type *)0)->member) *p = (ptr);                             \
29         (type *)((unsigned long)p - (unsigned long)&(((type *)0)->member)); \
30     })
31 
32 // 定义类型的缩写
33 typedef unsigned char uchar;
34 typedef unsigned short ushort;
35 typedef unsigned int uint;
36 typedef unsigned long ul;
37 typedef unsigned long long int ull;
38 typedef long long int ll;
39 
40 #define ABS(x) ((x) > 0 ? (x) : -(x)) // 绝对值
41 // 最大最小值
42 #define max(x, y) ((x > y) ? (x) : (y))
43 #define min(x, y) ((x < y) ? (x) : (y))
44 
45 // 遮罩高32bit
46 #define MASK_HIGH_32bit(x) (x & (0x00000000ffffffffUL))
47 
48 // 四舍五入成整数
49 ul round(double x)
50 {
51     return (ul)(x + 0.5);
52 }
53 
54 /**
55  * @brief 地址按照align进行对齐
56  *
57  * @param addr
58  * @param _align
59  * @return ul 对齐后的地址
60  */
61 static __always_inline ul ALIGN(const ul addr, const ul _align)
62 {
63     return (ul)((addr + _align - 1) & (~(_align - 1)));
64 }
65 
66 void *memset(void *dst, unsigned char C, ul size)
67 {
68 
69     int d0, d1;
70     unsigned long tmp = C * 0x0101010101010101UL;
71     __asm__ __volatile__("cld	\n\t"
72                          "rep	\n\t"
73                          "stosq	\n\t"
74                          "testb	$4, %b3	\n\t"
75                          "je	1f	\n\t"
76                          "stosl	\n\t"
77                          "1:\ttestb	$2, %b3	\n\t"
78                          "je	2f\n\t"
79                          "stosw	\n\t"
80                          "2:\ttestb	$1, %b3	\n\t"
81                          "je	3f	\n\t"
82                          "stosb	\n\t"
83                          "3:	\n\t"
84                          : "=&c"(d0), "=&D"(d1)
85                          : "a"(tmp), "q"(size), "0"(size / 8), "1"(dst)
86                          : "memory");
87     return dst;
88 }
89 
90 void *memset_c(void *dst, uint8_t c, size_t count)
91 {
92     uint8_t *xs = (uint8_t *)dst;
93 
94     while (count--)
95         *xs++ = c;
96 
97     return dst;
98 }
99 
100 /**
101  * @brief 内存拷贝函数
102  *
103  * @param dst 目标数组
104  * @param src 源数组
105  * @param Num 字节数
106  * @return void*
107  */
108 static void *memcpy(void *dst, const void *src, long Num)
109 {
110     int d0 = 0, d1 = 0, d2 = 0;
111     __asm__ __volatile__("cld	\n\t"
112                          "rep	\n\t"
113                          "movsq	\n\t"
114                          "testb	$4,%b4	\n\t"
115                          "je	1f	\n\t"
116                          "movsl	\n\t"
117                          "1:\ttestb	$2,%b4	\n\t"
118                          "je	2f	\n\t"
119                          "movsw	\n\t"
120                          "2:\ttestb	$1,%b4	\n\t"
121                          "je	3f	\n\t"
122                          "movsb	\n\t"
123                          "3:	\n\t"
124                          : "=&c"(d0), "=&D"(d1), "=&S"(d2)
125                          : "0"(Num / 8), "q"(Num), "1"(dst), "2"(src)
126                          : "memory");
127     return dst;
128 }
129 
130 // 从io口读入8个bit
131 unsigned char io_in8(unsigned short port)
132 {
133     unsigned char ret = 0;
134     __asm__ __volatile__("inb	%%dx,	%0	\n\t"
135                          "mfence			\n\t"
136                          : "=a"(ret)
137                          : "d"(port)
138                          : "memory");
139     return ret;
140 }
141 
142 // 从io口读入32个bit
143 unsigned int io_in32(unsigned short port)
144 {
145     unsigned int ret = 0;
146     __asm__ __volatile__("inl	%%dx,	%0	\n\t"
147                          "mfence			\n\t"
148                          : "=a"(ret)
149                          : "d"(port)
150                          : "memory");
151     return ret;
152 }
153 
154 // 输出8个bit到输出端口
155 void io_out8(unsigned short port, unsigned char value)
156 {
157     __asm__ __volatile__("outb	%0,	%%dx	\n\t"
158                          "mfence			\n\t"
159                          :
160                          : "a"(value), "d"(port)
161                          : "memory");
162 }
163 
164 // 输出32个bit到输出端口
165 void io_out32(unsigned short port, unsigned int value)
166 {
167     __asm__ __volatile__("outl	%0,	%%dx	\n\t"
168                          "mfence			\n\t"
169                          :
170                          : "a"(value), "d"(port)
171                          : "memory");
172 }
173 
174 /**
175  * @brief 从端口读入n个word到buffer
176  *
177  */
178 #define io_insw(port, buffer, nr)                                                 \
179     __asm__ __volatile__("cld;rep;insw;mfence;" ::"d"(port), "D"(buffer), "c"(nr) \
180                          : "memory")
181 
182 /**
183  * @brief 从输出buffer中的n个word到端口
184  *
185  */
186 #define io_outsw(port, buffer, nr)                                                 \
187     __asm__ __volatile__("cld;rep;outsw;mfence;" ::"d"(port), "S"(buffer), "c"(nr) \
188                          : "memory")
189 
190 /**
191  * @brief 验证地址空间是否为用户地址空间
192  *
193  * @param addr_start 地址起始值
194  * @param length 地址长度
195  * @return true
196  * @return false
197  */
198 bool verify_area(uint64_t addr_start, uint64_t length)
199 {
200     if ((addr_start + length) <= 0x00007fffffffffffUL) // 用户程序可用的的地址空间应<= 0x00007fffffffffffUL
201         return true;
202     else
203         return false;
204 }
205 
206 /**
207  * @brief 从用户空间搬运数据到内核空间
208  *
209  * @param dst 目的地址
210  * @param src 源地址
211  * @param size 搬运的大小
212  * @return uint64_t
213  */
214 static inline uint64_t copy_from_user(void *dst, void *src, uint64_t size)
215 {
216     uint64_t tmp0, tmp1;
217     if (!verify_area((uint64_t)src, size))
218         return 0;
219 
220     /**
221      * @brief 先每次搬运8 bytes,剩余就直接一个个byte搬运
222      *
223      */
224     asm volatile("rep   \n\t"
225                  "movsq  \n\t"
226                  "movq %3, %0   \n\t"
227                  "rep   \n\t"
228                  "movsb \n\t"
229                  : "=&c"(size), "=&D"(tmp0), "=&S"(tmp1)
230                  : "r"(size & 7), "0"(size >> 3), "1"(dst), "2"(src)
231                  : "memory");
232     return size;
233 }
234 
235 /**
236  * @brief 从内核空间搬运数据到用户空间
237  *
238  * @param dst 目的地址
239  * @param src 源地址
240  * @param size 搬运的大小
241  * @return uint64_t
242  */
243 static inline uint64_t copy_to_user(void *dst, void *src, uint64_t size)
244 {
245     if (verify_area((uint64_t)src, size))
246         return 0;
247 
248     /**
249      * @brief 先每次搬运8 bytes,剩余就直接一个个byte搬运
250      *
251      */
252     // todo:编译有bug
253     // asm volatile("rep   \n\t"
254     //              "movsq  \n\t"
255     //              "movq %3, %0   \n\t"
256     //              "rep   \n\t"
257     //              "movsb \n\t"
258     //              : "=&c"(size), "=&D"(tmp0), "=&S"(tmp1)
259     //              : "r"(size & 7), "0"(size >> 3), "1"(dst), "2"(src)
260     //              : "memory");
261     memcpy(dst, src, size);
262 
263     return size;
264 }
265 
266 /**
267  * @brief 这个函数让蜂鸣器发声,目前仅用于真机调试。未来将移除,请勿依赖此函数。
268  *
269  * @param times 发声循环多少遍
270  */
271 void __experimental_beep(uint64_t times);
272 
273 /**
274  * @brief 往指定地址写入8字节
275  * 防止由于编译器优化导致不支持的内存访问类型(尤其是在mmio的时候)
276  *
277  * @param vaddr 虚拟地址
278  * @param value 要写入的值
279  */
280 static __always_inline void __write8b(uint64_t vaddr, uint64_t value)
281 {
282     asm volatile("movq %%rdx, 0(%%rax)" ::"a"(vaddr), "d"(value)
283                  : "memory");
284 }
285 
286 /**
287  * @brief 往指定地址写入4字节
288  * 防止由于编译器优化导致不支持的内存访问类型(尤其是在mmio的时候)
289  *
290  * @param vaddr 虚拟地址
291  * @param value 要写入的值
292  */
293 static __always_inline void __write4b(uint64_t vaddr, uint32_t value)
294 {
295     asm volatile("movl %%edx, 0(%%rax)" ::"a"(vaddr), "d"(value)
296                  : "memory");
297 }
298 
299 /**
300  * @brief 从指定地址读取8字节
301  * 防止由于编译器优化导致不支持的内存访问类型(尤其是在mmio的时候)
302  *
303  * @param vaddr 虚拟地址
304  * @return uint64_t 读取到的值
305  */
306 static __always_inline uint64_t __read8b(uint64_t vaddr)
307 {
308     uint64_t retval;
309     asm volatile("movq 0(%%rax), %0"
310                  : "=r"(retval)
311                  : "a"(vaddr)
312                  : "memory");
313     return retval;
314 }
315 
316 /**
317  * @brief 从指定地址读取4字节
318  * 防止由于编译器优化导致不支持的内存访问类型(尤其是在mmio的时候)
319  *
320  * @param vaddr 虚拟地址
321  * @return uint64_t 读取到的值
322  */
323 static __always_inline uint32_t __read4b(uint64_t vaddr)
324 {
325     uint32_t retval;
326     asm volatile("movl 0(%%rax), %0"
327                  : "=d"(retval)
328                  : "a"(vaddr)
329                  : "memory");
330     return retval;
331 }
332 
333 /**
334  * @brief 将数据从src搬运到dst,并能正确处理地址重叠的问题
335  *
336  * @param dst 目标地址指针
337  * @param src 源地址指针
338  * @param size 大小
339  * @return void* 指向目标地址的指针
340  */
341 void *memmove(void *dst, const void *src, uint64_t size);